vibespot 1.0.6 → 1.0.8
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.
- package/dist/index.js +61 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/ui/docs/docs.css +977 -0
- package/ui/docs/docs.js +268 -0
- package/ui/docs/index.html +1242 -0
- package/ui/index.html +8 -0
- package/ui/styles.css +1 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/utils/fs.ts","../src/utils/shell.ts","../src/utils/config.ts","../src/utils/claude-oauth.ts","../src/hubspot/api.ts","../src/hubspot/fetcher.ts","../src/ai/prompts.ts","../src/server/session/types.ts","../src/server/project-git.ts","../src/server/session/state.ts","../src/server/session/templates.ts","../src/server/session/store.ts","../src/server/session/disk.ts","../src/server/session/index.ts","../src/server/session.ts","../src/hubl/renderer.ts","../src/server/preview.ts","../src/server/log.ts","../src/server/ai-parser.ts","../src/server/ai-prompts.ts","../src/server/ai-engines.ts","../src/server/route-helpers.ts","../src/server/routes/upload-files.ts","../src/server/agent/engine-adapter.ts","../src/server/agent/prompts/intent-analyzer.ts","../src/server/agent/stages/intent-analyzer.ts","../src/server/agent/prompts/page-architect.ts","../src/server/agent/stages/page-architect.ts","../src/server/agent/types.ts","../src/server/agent/prompts/module-developer.ts","../src/server/agent/stages/module-developer.ts","../src/server/agent/stages/validator.ts","../src/server/agent/pipeline.ts","../src/server/ai-handler.ts","../src/ai/design-extractor.ts","../src/server/agent/stages/brandvoice-extractor.ts","../src/server/agent/stages/context-extractor.ts","../src/index.ts","../src/cli/program.ts","../src/commands/wizard.ts","../src/cli/banner.ts","../src/cli/theme.ts","../src/wizard/preflight.ts","../src/utils/detect.ts","../src/prompts/prompter.ts","../src/wizard/source.ts","../src/wizard/theme-setup.ts","../src/hubspot/theme-scaffold.ts","../src/wizard/conversion.ts","../src/ai/claude-code.ts","../src/ai/claude-api.ts","../src/ai/gemini-cli.ts","../src/ai/codex-cli.ts","../src/wizard/uploader.ts","../src/server/auto-fix.ts","../src/hubspot/uploader.ts","../src/wizard/next-steps.ts","../src/commands/init.ts","../src/commands/convert.ts","../src/commands/upload.ts","../src/commands/doctor.ts","../src/commands/vibe.ts","../src/server/server.ts","../src/server/process-manager.ts","../src/server/routes/setup.ts","../src/server/routes/settings.ts","../src/server/routes/claude-oauth.ts","../src/server/routes/themes.ts","../src/server/routes/templates.ts","../src/server/routes/modules.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport function readFile(path: string): string {\n return readFileSync(path, \"utf-8\");\n}\n\nexport function writeFile(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, \"utf-8\");\n}\n\nexport function fileExists(path: string): boolean {\n return existsSync(path);\n}\n\nexport function ensureDir(path: string): void {\n mkdirSync(path, { recursive: true });\n}\n\nexport function resolveAsset(name: string): string {\n // In built package, assets/ is at the package root\n // During dev, it's relative to the project root\n const paths = [\n join(import.meta.dirname, \"../../assets\", name),\n join(import.meta.dirname, \"../assets\", name),\n join(process.cwd(), \"assets\", name),\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n throw new Error(`Asset not found: ${name}`);\n}\n\n/** Read version from package.json (cached after first call). */\nlet _version = \"\";\nexport function getVersion(): string {\n if (_version) return _version;\n const candidates = [\n join(import.meta.dirname, \"../../package.json\"),\n join(import.meta.dirname, \"../package.json\"),\n join(process.cwd(), \"package.json\"),\n ];\n for (const p of candidates) {\n if (existsSync(p)) {\n try {\n const pkg = JSON.parse(readFileSync(p, \"utf-8\"));\n if (pkg.name === \"vibespot\" && pkg.version) {\n _version = pkg.version;\n return _version;\n }\n } catch { /* try next */ }\n }\n }\n _version = \"dev\";\n return _version;\n}\n\n/** Read CHANGELOG.md content (cached after first call). */\nlet _changelog = \"\";\nexport function getChangelog(): string {\n if (_changelog) return _changelog;\n const candidates = [\n join(import.meta.dirname, \"../../CHANGELOG.md\"),\n join(import.meta.dirname, \"../CHANGELOG.md\"),\n join(process.cwd(), \"CHANGELOG.md\"),\n ];\n for (const p of candidates) {\n if (existsSync(p)) {\n try {\n _changelog = readFileSync(p, \"utf-8\");\n return _changelog;\n } catch { /* try next */ }\n }\n }\n return \"\";\n}\n","import { execSync, type ExecSyncOptions } from \"node:child_process\";\n\nexport interface ShellResult {\n stdout: string;\n stderr: string;\n success: boolean;\n}\n\nexport function run(\n command: string,\n options: ExecSyncOptions = {}\n): ShellResult {\n try {\n const stdout = execSync(command, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120_000,\n ...options,\n }).trim();\n return { stdout, stderr: \"\", success: true };\n } catch (err: unknown) {\n const e = err as { stdout?: Buffer | string; stderr?: Buffer | string };\n const stdout = (e.stdout ?? \"\").toString().trim();\n const stderr = (e.stderr ?? \"\").toString().trim();\n return { stdout, stderr, success: false };\n }\n}\n\nexport function runOrThrow(\n command: string,\n options: ExecSyncOptions = {}\n): string {\n return execSync(command, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120_000,\n ...options,\n }).trim();\n}\n\nexport function runPassthrough(\n command: string,\n options: ExecSyncOptions = {}\n): boolean {\n try {\n execSync(command, {\n stdio: \"inherit\",\n timeout: 300_000,\n ...options,\n });\n return true;\n } catch {\n return false;\n }\n}\n","import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { chmodSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"./fs.js\";\n\nexport type AIEngineType =\n | \"claude-code\"\n | \"anthropic-api\"\n | \"claude-oauth\"\n | \"openai-api\"\n | \"gemini-cli\"\n | \"gemini-api\"\n | \"codex-cli\"\n // Legacy value — migrated to \"anthropic-api\" on load\n | \"api\";\n\nexport interface HubSpotAccountConfig {\n portalId: string;\n portalName: string;\n personalAccessKey: string;\n dataCenter: \"na1\" | \"eu1\";\n addedAt: string;\n}\n\nexport interface VibeSpotConfig {\n aiEngine?: AIEngineType;\n anthropicApiKey?: string;\n openaiApiKey?: string;\n geminiApiKey?: string;\n claudeCodeModel?: string;\n anthropicApiModel?: string;\n openaiApiModel?: string;\n lastThemePath?: string;\n lastSourcePath?: string;\n // HubSpot account management\n hubspotAccounts?: HubSpotAccountConfig[];\n activeHubSpotAccount?: string; // portalId\n hubspotUploadMode?: \"api\" | \"cli\"; // default \"api\"\n // CLI tool toggles — only enabled tools get checked on settings load\n enabledCLITools?: string[];\n // Agentic pipeline — multi-stage decomposed AI generation\n agenticMode?: boolean; // undefined = never prompted, true/false = user choice\n agenticConcurrency?: number; // max parallel module generation calls (default 3)\n}\n\nconst CONFIG_DIR = join(homedir(), \".vibespot\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.json\");\n\nexport function loadConfig(): VibeSpotConfig {\n if (!fileExists(CONFIG_PATH)) return {};\n\n try {\n const raw = JSON.parse(readFile(CONFIG_PATH));\n // Migrate legacy \"api\" engine type\n if (raw.aiEngine === \"api\") {\n raw.aiEngine = \"anthropic-api\";\n }\n return raw;\n } catch {\n return {};\n }\n}\n\n/**\n * Get the API key for a given engine, checking config first then env vars.\n */\nexport function getApiKeyForEngine(engine: AIEngineType, config?: VibeSpotConfig): string | undefined {\n const c = config || loadConfig();\n switch (engine) {\n case \"anthropic-api\":\n case \"api\":\n return c.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n case \"openai-api\":\n return c.openaiApiKey || process.env.OPENAI_API_KEY;\n case \"gemini-api\":\n return c.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY;\n default:\n return undefined;\n }\n}\n\n/**\n * Mask an API key for display (show first 7 + last 4 chars).\n */\nexport function maskApiKey(key: string): string {\n if (key.length <= 12) return \"***\";\n return key.slice(0, 7) + \"...\" + key.slice(-4);\n}\n\nexport function saveConfig(config: VibeSpotConfig): void {\n const existing = loadConfig();\n const merged = { ...existing, ...config };\n writeFile(CONFIG_PATH, JSON.stringify(merged, null, 2));\n // Restrict file permissions — config contains API keys\n if (process.platform !== \"win32\") {\n try { chmodSync(CONFIG_PATH, 0o600); } catch { /* ignore */ }\n }\n}\n\nexport function getConfigDir(): string {\n return CONFIG_DIR;\n}\n\n// ---------------------------------------------------------------------------\n// HubSpot account helpers\n// ---------------------------------------------------------------------------\n\nexport function getActiveHubSpotAccount(): HubSpotAccountConfig | null {\n const config = loadConfig();\n if (!config.hubspotAccounts?.length) return null;\n const activeId = config.activeHubSpotAccount;\n if (activeId) {\n const found = config.hubspotAccounts.find((a) => a.portalId === activeId);\n if (found) return found;\n }\n // Fallback to first account\n return config.hubspotAccounts[0] || null;\n}\n\nexport function addHubSpotAccount(\n pak: string,\n portalId: string,\n portalName: string,\n dataCenter: \"na1\" | \"eu1\",\n): void {\n const config = loadConfig();\n const accounts = config.hubspotAccounts || [];\n\n // Replace if same portalId exists\n const idx = accounts.findIndex((a) => a.portalId === portalId);\n const entry: HubSpotAccountConfig = {\n portalId,\n portalName,\n personalAccessKey: pak,\n dataCenter,\n addedAt: new Date().toISOString(),\n };\n\n if (idx >= 0) {\n accounts[idx] = entry;\n } else {\n accounts.push(entry);\n }\n\n saveConfig({\n hubspotAccounts: accounts,\n activeHubSpotAccount: portalId,\n } as VibeSpotConfig);\n}\n\nexport function removeHubSpotAccount(portalId: string): void {\n const config = loadConfig();\n const accounts = (config.hubspotAccounts || []).filter((a) => a.portalId !== portalId);\n const update: Partial<VibeSpotConfig> = { hubspotAccounts: accounts };\n\n // If we removed the active account, switch to the first remaining\n if (config.activeHubSpotAccount === portalId) {\n update.activeHubSpotAccount = accounts[0]?.portalId || undefined;\n }\n\n saveConfig(update as VibeSpotConfig);\n}\n\nexport function setActiveHubSpotAccount(portalId: string): void {\n saveConfig({ activeHubSpotAccount: portalId } as VibeSpotConfig);\n}\n\nexport function getHubSpotPak(): string | null {\n const acct = getActiveHubSpotAccount();\n return acct?.personalAccessKey || null;\n}\n\n// ---------------------------------------------------------------------------\n// CLI tool toggle helpers\n// ---------------------------------------------------------------------------\n\nexport function isCliToolEnabled(toolId: string): boolean {\n const config = loadConfig();\n return config.enabledCLITools?.includes(toolId) ?? false;\n}\n\nexport function setCliToolEnabled(toolId: string, enabled: boolean): void {\n const config = loadConfig();\n const tools = new Set(config.enabledCLITools || []);\n if (enabled) tools.add(toolId);\n else tools.delete(toolId);\n saveConfig({ enabledCLITools: [...tools] } as VibeSpotConfig);\n}\n","/**\n * Claude OAuth token manager — stores and auto-refreshes OAuth tokens.\n *\n * Users obtain a token via `claude setup-token` (Claude Code CLI) and paste it\n * into the vibespot settings UI. Tokens are stored in ~/.vibespot/claude-oauth.json.\n *\n * The access token (sk-ant-oat01-...) has an 8-hour lifetime and is auto-refreshed\n * when within 5 minutes of expiry.\n */\n\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { chmodSync, unlinkSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"./fs.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst CLIENT_ID = \"9d1c250a-e61b-44d9-88ed-5944d1962f5e\";\nconst TOKEN_ENDPOINT = \"https://console.anthropic.com/v1/oauth/token\";\nconst TOKEN_FILE = join(homedir(), \".vibespot\", \"claude-oauth.json\");\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes\n\n// ---------------------------------------------------------------------------\n// OAuth headers required for OAT tokens on every API call\n// ---------------------------------------------------------------------------\n\nexport const OAUTH_EXTRA_HEADERS: Record<string, string> = {\n \"user-agent\": \"claude-cli/2.1.75\",\n \"x-app\": \"cli\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n};\n\n/** System prompt prefix required for OAT tokens — must be a separate block. */\nexport const OAUTH_SYSTEM_PREFIX = \"You are Claude Code, Anthropic's official CLI for Claude.\";\n\n// ---------------------------------------------------------------------------\n// Token types\n// ---------------------------------------------------------------------------\n\ninterface OAuthTokens {\n access_token: string;\n refresh_token: string;\n expires_at: number; // Unix ms\n}\n\n// ---------------------------------------------------------------------------\n// Token persistence\n// ---------------------------------------------------------------------------\n\nfunction loadTokens(): OAuthTokens | null {\n if (!fileExists(TOKEN_FILE)) return null;\n try {\n return JSON.parse(readFile(TOKEN_FILE));\n } catch {\n return null;\n }\n}\n\nfunction saveTokens(tokens: OAuthTokens): void {\n writeFile(TOKEN_FILE, JSON.stringify(tokens, null, 2));\n if (process.platform !== \"win32\") {\n try { chmodSync(TOKEN_FILE, 0o600); } catch { /* ignore */ }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Token refresh (with concurrency guard)\n// ---------------------------------------------------------------------------\n\nlet refreshPromise: Promise<void> | null = null;\n\nasync function refreshAccessToken(refresh_token: string): Promise<void> {\n const resp = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"refresh_token\",\n refresh_token,\n client_id: CLIENT_ID,\n }),\n });\n\n if (!resp.ok) {\n clearOAuthTokens();\n throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n }\n\n const data = await resp.json();\n saveTokens({\n access_token: data.access_token,\n refresh_token: data.refresh_token || refresh_token,\n expires_at: Date.now() + (data.expires_in || 28800) * 1000,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Save an initial OAuth token (from `claude setup-token` or manual paste).\n */\nexport function saveInitialToken(accessToken: string, refreshToken: string = \"\"): void {\n saveTokens({\n access_token: accessToken,\n refresh_token: refreshToken,\n // Default 8h expiry — will be corrected on first refresh\n expires_at: Date.now() + 28800 * 1000,\n });\n}\n\n/**\n * Check if a valid (non-expired) OAuth token exists.\n */\nexport function hasValidOAuthToken(): boolean {\n const tokens = loadTokens();\n if (!tokens) return false;\n return tokens.expires_at > Date.now();\n}\n\n/**\n * Get a valid access token, auto-refreshing if needed.\n * Returns null if no token is stored or refresh fails.\n */\nexport async function getValidAccessToken(): Promise<string | null> {\n const tokens = loadTokens();\n if (!tokens) return null;\n\n // Still valid and not near expiry\n if (tokens.expires_at - Date.now() > REFRESH_BUFFER_MS) {\n return tokens.access_token;\n }\n\n // Need to refresh — use concurrency guard\n if (tokens.refresh_token) {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken(tokens.refresh_token).finally(() => {\n refreshPromise = null;\n });\n }\n\n try {\n await refreshPromise;\n } catch {\n return null;\n }\n\n const refreshed = loadTokens();\n return refreshed?.access_token ?? null;\n }\n\n // No refresh token — return current token even if expiring\n return tokens.access_token;\n}\n\n/**\n * Get token info for status display.\n */\nexport function getOAuthTokenInfo(): { expiresAt: string } | null {\n const tokens = loadTokens();\n if (!tokens) return null;\n return { expiresAt: new Date(tokens.expires_at).toISOString() };\n}\n\n/**\n * Clear stored OAuth tokens (logout).\n */\nexport function clearOAuthTokens(): void {\n if (fileExists(TOKEN_FILE)) {\n try { unlinkSync(TOKEN_FILE); } catch { /* ignore */ }\n }\n}\n","/**\n * HubSpot CMS Source Code API v3 client.\n * Direct HTTP calls — no HubSpot CLI dependency.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { basename } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface HubSpotApiError {\n status: number;\n message: string;\n category?: string;\n detail?: string;\n}\n\nexport interface UploadResult {\n success: boolean;\n path: string;\n error?: HubSpotApiError;\n}\n\nexport interface FileMetadata {\n id: string;\n name: string;\n folder: boolean;\n path: string;\n createdAt?: number;\n updatedAt?: number;\n children?: (FileMetadata | string)[];\n}\n\nexport interface AccountInfo {\n portalId: string;\n portalName: string;\n dataCenter: \"na1\" | \"eu1\";\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** HubSpot API base URL — same for all regions. Cloudflare routes by token. */\nconst BASE_URL = \"https://api.hubapi.com\";\nconst MAX_RETRIES = 3;\nconst RETRY_DELAY_MS = 1000;\n/** Refresh access token 5 minutes before expiry. */\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Token exchange — PAKs are exchanged for short-lived access tokens\n// ---------------------------------------------------------------------------\n\ninterface CachedToken {\n accessToken: string;\n expiresAt: number;\n hubId: number;\n hubName: string;\n}\n\nconst tokenCache = new Map<string, CachedToken>();\n\n/** Exchange a PAK for a short-lived OAuth access token via HubSpot's localdevauth endpoint. */\nasync function exchangePakForToken(pak: string): Promise<CachedToken> {\n const cached = tokenCache.get(pak);\n if (cached && cached.expiresAt - Date.now() > TOKEN_REFRESH_BUFFER_MS) {\n return cached;\n }\n\n const resp = await fetch(`${BASE_URL}/localdevauth/v1/auth/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ encodedOAuthRefreshToken: pak }),\n });\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n resp.status === 401 || resp.status === 403\n ? \"Invalid or expired Personal Access Key\"\n : `Token exchange failed (${resp.status}): ${body.slice(0, 200)}`,\n );\n }\n\n const data = await resp.json() as Record<string, unknown>;\n const token: CachedToken = {\n accessToken: data.oauthAccessToken as string,\n expiresAt: data.expiresAtMillis as number,\n hubId: data.hubId as number,\n hubName: (data.hubName as string) || \"\",\n };\n\n tokenCache.set(pak, token);\n return token;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function authHeaders(pak: string): Promise<Record<string, string>> {\n const { accessToken } = await exchangePakForToken(pak);\n return {\n Authorization: `Bearer ${accessToken}`,\n };\n}\n\n/** Encode a remote path for use in HubSpot API URLs — encode each segment but keep slashes. */\nfunction encodePath(remotePath: string): string {\n return remotePath.replace(/^\\/+/, \"\").split(\"/\").filter(Boolean).map(encodeURIComponent).join(\"/\");\n}\n\n/** Detect data center from PAK prefix. */\nexport function detectDataCenterFromPak(pak: string): \"na1\" | \"eu1\" {\n // Modern PAK format: pat-eu1-... or pat-na1-...\n if (pak.startsWith(\"pat-eu1-\")) return \"eu1\";\n if (pak.startsWith(\"pat-na1-\")) return \"na1\";\n // Legacy base64 prefix check\n if (pak.startsWith(\"CiRldTE\")) return \"eu1\";\n return \"na1\";\n}\n\n/** Sleep helper for retry backoff. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Parse error response body into HubSpotApiError. */\nasync function parseErrorResponse(resp: Response, fallbackPath?: string): Promise<HubSpotApiError> {\n let message = `HTTP ${resp.status}`;\n let category: string | undefined;\n let detail: string | undefined;\n\n try {\n const body = await resp.json() as Record<string, unknown>;\n if (body.message && typeof body.message === \"string\") message = body.message;\n if (body.category && typeof body.category === \"string\") category = body.category;\n if (body.errors && Array.isArray(body.errors) && body.errors.length > 0) {\n const firstError = body.errors[0] as Record<string, unknown>;\n detail = firstError.message as string || JSON.stringify(firstError);\n }\n } catch {\n try {\n const text = await resp.text();\n if (text) message = text.slice(0, 500);\n } catch { /* ignore */ }\n }\n\n return {\n status: resp.status,\n message: fallbackPath ? `${message} (${fallbackPath})` : message,\n category,\n detail,\n };\n}\n\n/** Make a fetch request with retry logic for rate limits and transient errors. */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n retries = MAX_RETRIES,\n): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n const resp = await fetch(url, options);\n\n if (resp.status === 429 || (resp.status >= 500 && attempt < retries)) {\n const delay = RETRY_DELAY_MS * Math.pow(2, attempt);\n await sleep(delay);\n continue;\n }\n\n return resp;\n }\n\n // Should never reach here, but TypeScript needs it\n return fetch(url, options);\n}\n\n// ---------------------------------------------------------------------------\n// API Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a Personal Access Key by fetching account info.\n * Returns portal ID, name, and data center.\n */\nexport async function validatePak(pak: string): Promise<AccountInfo> {\n // Exchange PAK for access token — this validates the key\n const token = await exchangePakForToken(pak);\n\n // Use the access token to get account details\n const url = `${BASE_URL}/account-info/v3/details`;\n const resp = await fetchWithRetry(url, { headers: await authHeaders(pak) });\n\n if (!resp.ok) {\n const err = await parseErrorResponse(resp);\n throw new Error(`Failed to get account info: ${err.message}`);\n }\n\n const data = await resp.json() as Record<string, unknown>;\n const portalId = String(data.portalId || token.hubId || \"\");\n const portalName = token.hubName || (data.uiDomain as string) || portalId;\n\n return {\n portalId,\n portalName,\n dataCenter: detectDataCenterFromPak(pak),\n };\n}\n\n/**\n * Upload a single file to the HubSpot CMS Design Manager.\n * Uses PUT /cms/v3/source-code/{env}/content/{path} with multipart form-data.\n */\nexport async function uploadFile(\n pak: string,\n remotePath: string,\n localFilePath: string,\n): Promise<UploadResult> {\n const fileContent = readFileSync(localFilePath);\n const fileName = basename(localFilePath);\n\n // Build multipart form data\n const formData = new FormData();\n const blob = new Blob([fileContent]);\n formData.append(\"file\", blob, fileName);\n\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"PUT\",\n headers: await authHeaders(pak),\n body: formData,\n });\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n return { success: false, path: remotePath, error };\n }\n\n return { success: true, path: remotePath };\n}\n\n/**\n * Delete a file or directory from the HubSpot CMS Design Manager.\n */\nexport async function deleteFile(pak: string, remotePath: string): Promise<void> {\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"DELETE\",\n headers: await authHeaders(pak),\n });\n\n if (!resp.ok && resp.status !== 404) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to delete ${remotePath}: ${error.message}`);\n }\n}\n\n/**\n * Download a file from the HubSpot CMS Design Manager.\n */\nexport async function downloadFile(pak: string, remotePath: string): Promise<Buffer> {\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"GET\",\n headers: await authHeaders(pak),\n });\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to download ${remotePath}: ${error.message}`);\n }\n\n const arrayBuffer = await resp.arrayBuffer();\n return Buffer.from(arrayBuffer);\n}\n\n/**\n * Get metadata for a file or directory in the HubSpot CMS Design Manager.\n * Returns null if the path doesn't exist (404).\n */\nexport async function getMetadata(pak: string, remotePath: string): Promise<FileMetadata | null> {\n const url = `${BASE_URL}/cms/v3/source-code/published/metadata/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"GET\",\n headers: await authHeaders(pak),\n });\n\n if (resp.status === 404) return null;\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to get metadata for ${remotePath}: ${error.message}`);\n }\n\n return (await resp.json()) as FileMetadata;\n}\n\n/**\n * List children of a directory in the HubSpot CMS Design Manager.\n * Returns empty array if the directory doesn't exist.\n */\nexport async function listDirectory(pak: string, remotePath: string): Promise<FileMetadata[]> {\n const meta = await getMetadata(pak, remotePath);\n if (!meta) return [];\n if (!meta.folder) return [meta];\n return meta.children || [];\n}\n\n/**\n * List root-level folders in the HubSpot CMS Design Manager.\n * Tries multiple API approaches since the metadata endpoint doesn't support root listing.\n */\nexport async function listRootFolders(pak: string): Promise<FileMetadata[]> {\n const headers = await authHeaders(pak);\n\n // Try different URL patterns for root metadata\n const urlVariants = [\n `${BASE_URL}/cms/v3/source-code/published/metadata`, // no trailing slash\n `${BASE_URL}/cms/v3/source-code/published/metadata/`, // trailing slash\n `${BASE_URL}/designmanager/v1/portals/content/listing`, // Design Manager API\n ];\n\n for (const url of urlVariants) {\n try {\n const resp = await fetch(url, { method: \"GET\", headers });\n if (resp.ok) {\n const data = await resp.json() as Record<string, unknown>;\n // Could be { children: [...] } or { objects: [...] } or direct array\n const children = (data.children || data.objects || (Array.isArray(data) ? data : null)) as FileMetadata[] | null;\n if (children && children.length > 0) {\n return children.filter((c: FileMetadata) => c.folder);\n }\n }\n } catch { /* try next */ }\n }\n\n return [];\n}\n","/**\n * Theme fetcher — downloads an existing theme from HubSpot\n * via the CMS Source Code API v3. Replaces `hs cms fetch`.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { getMetadata, downloadFile, type FileMetadata } from \"./api.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchThemeOptions {\n onFile?: (path: string) => void;\n concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Recursive directory listing\n// ---------------------------------------------------------------------------\n\n/** Recursively list all files under a remote path. */\nasync function listFilesRecursive(pak: string, remotePath: string): Promise<string[]> {\n const meta = await getMetadata(pak, remotePath);\n if (!meta) return [];\n\n if (!meta.folder) return [meta.path || remotePath];\n\n const files: string[] = [];\n const rawChildren = meta.children || [];\n\n for (const child of rawChildren) {\n // HubSpot API returns children as plain strings (folder/file names) or as objects\n const childName = typeof child === \"string\" ? child : (child as FileMetadata).name;\n if (!childName) continue;\n const childPath = `${remotePath}/${childName}`;\n\n if (typeof child === \"string\") {\n // String children — need to fetch metadata to determine if folder or file\n files.push(...(await listFilesRecursive(pak, childPath)));\n } else {\n const childMeta = child as FileMetadata;\n if (childMeta.folder) {\n files.push(...(await listFilesRecursive(pak, childMeta.path || childPath)));\n } else {\n files.push(childMeta.path || childPath);\n }\n }\n }\n\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// Parallel download\n// ---------------------------------------------------------------------------\n\nasync function parallelMap<T>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const i = index++;\n await fn(items[i]);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n}\n\n// ---------------------------------------------------------------------------\n// Main fetch function\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch (download) an entire theme from HubSpot to a local directory.\n */\nexport async function fetchTheme(\n pak: string,\n themeName: string,\n targetPath: string,\n opts: FetchThemeOptions = {},\n): Promise<void> {\n const concurrency = opts.concurrency ?? 5;\n\n // List all files in the remote theme\n const remoteFiles = await listFilesRecursive(pak, themeName);\n\n if (remoteFiles.length === 0) {\n throw new Error(`Theme \"${themeName}\" not found on HubSpot or is empty`);\n }\n\n // Download all files in parallel\n mkdirSync(targetPath, { recursive: true });\n\n await parallelMap(remoteFiles, concurrency, async (remotePath) => {\n // Strip theme name prefix to get relative path\n const relativePath = remotePath.startsWith(themeName + \"/\")\n ? remotePath.slice(themeName.length + 1)\n : remotePath;\n\n const localPath = join(targetPath, relativePath);\n\n // Ensure directory exists\n mkdirSync(dirname(localPath), { recursive: true });\n\n // Download and write\n const content = await downloadFile(pak, remotePath);\n writeFileSync(localPath, content);\n\n opts.onFile?.(relativePath);\n });\n}\n","import { readFile, resolveAsset } from \"../utils/fs.js\";\n\n// In-memory cache for guide files (~90KB total, read once)\nconst guideCache = new Map<string, string>();\n\nfunction cachedAsset(name: string): string {\n let val = guideCache.get(name);\n if (val !== undefined) return val;\n try { val = readFile(resolveAsset(name)); } catch { val = \"\"; }\n guideCache.set(name, val);\n return val;\n}\n\nexport function getConversionGuide(): string {\n return cachedAsset(\"conversion-guide.md\") || \"Conversion guide not found. Using built-in rules.\";\n}\n\nexport function getDesignGuide(): string {\n return cachedAsset(\"design-guide.md\");\n}\n\nexport function getContentGuide(): string {\n return cachedAsset(\"content-guide.md\");\n}\n\nexport function getHubspotRules(): string {\n return cachedAsset(\"hubspot-rules.md\");\n}\n\nexport function getHumanifyGuide(): string {\n return cachedAsset(\"humanify-guide.md\");\n}\n\n/**\n * Extract page-type-specific section from page-types.md.\n * Returns only the relevant section for the given page type.\n */\nexport function getPageTypeGuide(pageType: string): string {\n const fullGuide = cachedAsset(\"page-types.md\");\n if (!fullGuide) return \"\";\n\n // Map page type to section header\n const sectionHeaders: Record<string, string> = {\n landing_page: \"## Landing Page\",\n blog_post: \"## Blog Post\",\n website_page: \"## Website Page\",\n module_only: \"## Module Only\",\n };\n\n const header = sectionHeaders[pageType];\n if (!header) return \"\";\n\n const startIdx = fullGuide.indexOf(header);\n if (startIdx < 0) return \"\";\n\n // Find the next section (## at start of line) after this one\n const afterHeader = fullGuide.indexOf(\"\\n## \", startIdx + header.length);\n const section = afterHeader >= 0\n ? fullGuide.slice(startIdx, afterHeader).trim()\n : fullGuide.slice(startIdx).trim();\n\n return section;\n}\n\nexport function buildSystemPrompt(conversionGuide: string): string {\n return `You are a HubSpot CMS expert converting React/Tailwind pages to native HubSpot modules.\n\n## Rules\nFollow the conversion guide below EXACTLY. Key rules:\n- Use \"type\": \"text\" (NEVER \"textarea\")\n- NEVER use \"name\": \"name\" (it's reserved) — use alternatives like \"item_name\"\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\" on the group\n- All CSS classes must use a unique prefix to avoid theme conflicts\n- Every dnd_section needs padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true\n- Use template_css and template_js variables (resolved in base.html, not child templates)\n- Add .body-wrapper:has(.my-page) CSS fix + JS fallback for body background\n- Add scoped overrides with !important for headings, paragraphs, links to defeat theme-overrides.css\n- Use IntersectionObserver for scroll animations (add .visible class)\n- Convert Tailwind utilities to vanilla CSS with BEM naming\n- Convert React hooks to vanilla JS (no React, no npm packages)\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide\n${conversionGuide}`;\n}\n\nexport function buildAnalyzePrompt(componentList: string): string {\n return `Analyze these React components and create a module map.\n\nFor each component, return a JSON object with:\n- name: HubSpot module name (e.g., \"My Hero\")\n- description: Brief description of the section\n- hasRepeaters: true if it contains .map() or array iteration\n- hasInteractivity: true if it has JS-driven behavior (carousel, accordion, etc.)\n\nComponents:\n${componentList}\n\nReturn ONLY valid JSON array, no markdown fences.`;\n}\n\nexport function buildModulePrompt(\n componentSource: string,\n moduleName: string,\n cssVars: string\n): string {\n return `Convert this React component to a HubSpot module named \"${moduleName}\".\n\nReturn a JSON object with these keys:\n- fieldsJson: complete fields.json content (as JSON string)\n- metaJson: complete meta.json content (as JSON string)\n- moduleHtml: complete module.html content (HubL template)\n- moduleCss: complete module.css content (vanilla CSS)\n- moduleJs: module.js content if interactive behavior needed, or null\n\nDesign system CSS variables available:\n${cssVars}\n\nReact component source:\n${componentSource}\n\nReturn ONLY valid JSON, no markdown fences.`;\n}\n\nexport function buildCssPrompt(\n indexCss: string,\n tailwindConfig: string,\n pagePrefix: string\n): string {\n return `Create a shared CSS file for a HubSpot CMS landing page.\n\nExtract the design system from the source CSS and Tailwind config below.\nUse the class prefix \".${pagePrefix}-\" for all custom classes.\n\nRequirements:\n- CSS custom properties for all colors, spacing\n- Page wrapper styles (.${pagePrefix}-page) with !important font/color\n- .body-wrapper:has(.${pagePrefix}-page) background fix\n- .body-wrapper.${pagePrefix}-page-active JS fallback\n- Scoped overrides: .${pagePrefix}-page h1-h6, p, a with !important\n- .dnd-section padding: 0 !important and .row-fluid max-width: 100%\n- Form overrides for dark themes\n- Utility classes (container, section, grid, glass)\n- Scroll animation classes (.${pagePrefix}-scroll-animate / .visible)\n- Mobile breakpoint at 767px\n- Responsive grid fallbacks\n\nSource CSS:\n${indexCss}\n\nTailwind config:\n${tailwindConfig}\n\nReturn ONLY the CSS content, no markdown fences.`;\n}\n\nexport function buildJsPrompt(\n hooksSource: string,\n interactiveComponents: string,\n pagePrefix: string\n): string {\n return `Create a shared vanilla JS file for a HubSpot CMS landing page.\n\nConvert the React hooks and interactive components below to plain JavaScript.\nUse the class prefix \"${pagePrefix}-\" to match the CSS.\n\nRequirements:\n- IIFE wrapper with \"use strict\"\n- initBodyWrapper: add \"${pagePrefix}-page-active\" class to .body-wrapper\n- initScrollAnimations: IntersectionObserver for .${pagePrefix}-scroll-animate → .visible\n- Convert any carousels, accordions, typing animations to vanilla JS\n- DOMContentLoaded / readyState check\n\nReact hooks source:\n${hooksSource}\n\nInteractive component sources:\n${interactiveComponents}\n\nReturn ONLY the JavaScript content, no markdown fences.`;\n}\n\nexport function buildTemplatePrompt(\n moduleNames: string[],\n themeName: string,\n pagePrefix: string\n): string {\n return `Create a HubSpot page template that assembles these modules:\n\n${moduleNames.map((n, i) => `${i + 1}. ${n}.module`).join(\"\\n\")}\n\nTemplate requirements:\n- templateType: page, isAvailableForNewContent: true\n- extends \"./layouts/base.html\"\n- set template_css = \"../../css/${pagePrefix}-theme.css\"\n- set template_js = \"../../js/${pagePrefix}-animations.js\"\n- Empty header and footer blocks (modules handle them)\n- Wrap content in <div class=\"${pagePrefix}-page\">\n- Each module in its own dnd_section with padding zeroed and full_width=true\n- dnd_area label: \"${themeName} Landing Page\"\n\nReturn ONLY the template HTML content, no markdown fences.`;\n}\n","/**\n * Type definitions for the vibe coding session system.\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PipelineMetadata {\n steps: { step: string; label: string; decisions?: string[] }[];\n modules: { name: string; status: \"complete\" | \"failed\" }[];\n stats: { modulesGenerated: number; modulesUnchanged: number; modulesFailed: number; durationMs: number };\n}\n\nexport interface ChatMessage {\n role: \"user\" | \"assistant\";\n content: string;\n timestamp: number;\n pipeline?: PipelineMetadata;\n}\n\nexport type PageType = \"landing_page\" | \"blog_post\" | \"website_page\" | \"module_only\";\n\nexport interface TemplateEntry {\n id: string; // e.g. \"lp-main\", \"blog-post\"\n label: string; // \"Main Landing Page\"\n pageType: PageType;\n templateFile: string; // \"templates/lp-main.html\"\n modules: ModuleFiles[];\n moduleOrder: string[];\n sharedCss: string;\n sharedJs: string;\n template: string; // HubL template content\n messages: ChatMessage[]; // per-template chat history\n}\n\nexport interface SessionAsset {\n id: string;\n filename: string;\n originalName: string;\n type: \"image\" | \"document\";\n usage: \"asset\" | \"context\";\n mimeType: string;\n size: number;\n addedAt: string;\n extractedText?: string;\n}\n\nexport interface VibeSession {\n id: string;\n themePath: string;\n themeName: string;\n\n // Multi-template support\n templates: TemplateEntry[];\n activeTemplateId: string;\n brandAssets?: {\n styleguide?: string;\n brandvoice?: string;\n humanify?: boolean;\n themeContext?: string;\n };\n assets?: SessionAsset[];\n\n // Legacy flat fields — kept for backward compat, redirected to active template\n messages: ChatMessage[];\n modules: ModuleFiles[];\n sharedCss: string;\n sharedJs: string;\n template: string;\n moduleOrder: string[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface SessionIndexEntry {\n id: string;\n themeName: string;\n updatedAt: number;\n moduleCount: number;\n templateCount: number;\n}\n\nexport interface SessionSnapshot {\n modules: ReadonlyArray<Readonly<ModuleFiles>>;\n moduleOrder: ReadonlyArray<string>;\n sharedCss: string;\n sharedJs: string;\n messages: ReadonlyArray<Readonly<ChatMessage>>;\n themeName: string;\n themePath: string;\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string };\n}\n\nexport interface FieldDef {\n name: string;\n default?: unknown;\n children?: FieldDef[];\n}\n","/**\n * Per-project local git — auto-commits, version history, rollback.\n * All operations are local-only. Git is optional; if unavailable,\n * every function degrades gracefully (returns null / false / []).\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\n\nexport interface GitCommitInfo {\n hash: string; // short hash (7 chars)\n fullHash: string; // full SHA\n message: string; // first line of commit message\n timestamp: number; // unix epoch ms\n date: string; // ISO string for display\n}\n\n// ---------------------------------------------------------------------------\n// Git availability (cached)\n// ---------------------------------------------------------------------------\n\n/** Validate a git hash to prevent shell injection — must be hex only. */\nfunction isValidHash(hash: string): boolean {\n return /^[0-9a-f]{4,40}$/i.test(hash);\n}\n\nlet gitAvailableCache: boolean | null = null;\n\nexport function isGitAvailable(): boolean {\n if (gitAvailableCache !== null) return gitAvailableCache;\n const result = run(\"git --version\");\n gitAvailableCache = result.success;\n return gitAvailableCache;\n}\n\n// ---------------------------------------------------------------------------\n// Repo initialization\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure a git repo exists in the theme directory.\n * Creates .gitignore, .vibespot/ dir, and initial commit if needed.\n * Safe to call multiple times (idempotent).\n */\nexport function ensureGitRepo(themePath: string): boolean {\n if (!isGitAvailable()) return false;\n\n // Already initialized\n if (existsSync(join(themePath, \".git\"))) {\n ensureVibeSpotDir(themePath);\n return true;\n }\n\n // Init repo\n const init = run(\"git init\", { cwd: themePath });\n if (!init.success) {\n console.warn(`[project-git] git init failed in ${themePath}: ${init.stderr}`);\n return false;\n }\n\n // Create .gitignore\n writeGitIgnore(themePath);\n\n // Create .vibespot/ dir for chat persistence\n ensureVibeSpotDir(themePath);\n\n // Initial commit\n run(\"git add -A\", { cwd: themePath });\n run('git commit -m \"Initial theme\"', { cwd: themePath });\n\n return true;\n}\n\nfunction ensureVibeSpotDir(themePath: string): void {\n const dir = join(themePath, \".vibespot\");\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\nfunction writeGitIgnore(themePath: string): void {\n const gitignorePath = join(themePath, \".gitignore\");\n const lines = [\".vibespot/\", \"node_modules/\", \"\"];\n writeFileSync(gitignorePath, lines.join(\"\\n\"), \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Committing\n// ---------------------------------------------------------------------------\n\n/**\n * Stage all changes and commit with the given message.\n * Returns the short commit hash, or null if nothing to commit or error.\n */\nexport function commitThemeState(themePath: string, message: string): string | null {\n if (!isGitAvailable()) return null;\n if (!existsSync(join(themePath, \".git\"))) return null;\n\n // Stage everything\n run(\"git add -A\", { cwd: themePath });\n\n // Check if there are staged changes\n const diff = run(\"git diff --cached --quiet\", { cwd: themePath });\n if (diff.success) return null; // exit 0 = nothing staged\n\n // Truncate message to 72 chars\n const truncated = message.length > 72\n ? message.slice(0, 69) + \"...\"\n : message;\n\n // Commit (use -- to prevent message from being interpreted as flags)\n const commitResult = run(`git commit -m \"${truncated.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n if (!commitResult.success) {\n console.warn(`[project-git] commit failed: ${commitResult.stderr}`);\n return null;\n }\n\n // Get short hash\n const hashResult = run(\"git rev-parse --short HEAD\", { cwd: themePath });\n return hashResult.success ? hashResult.stdout : null;\n}\n\n/**\n * Stage only specific files and commit with a template-scoped message.\n * Used for per-template version history so rollback doesn't affect other templates.\n */\nexport function commitTemplateState(\n themePath: string,\n templateId: string,\n message: string,\n filePaths: string[]\n): string | null {\n if (!isGitAvailable()) return null;\n if (!existsSync(join(themePath, \".git\"))) return null;\n\n // Stage only the specified paths\n for (const fp of filePaths) {\n const fullPath = join(themePath, fp);\n if (existsSync(fullPath)) {\n run(`git add \"${fp}\"`, { cwd: themePath });\n }\n }\n\n // Check if there are staged changes\n const diff = run(\"git diff --cached --quiet\", { cwd: themePath });\n if (diff.success) return null; // nothing staged\n\n // Build prefixed message: [templateId] user message\n const prefix = `[${templateId}] `;\n const maxMsg = 72 - prefix.length;\n const truncated = message.length > maxMsg\n ? message.slice(0, maxMsg - 3) + \"...\"\n : message;\n const fullMessage = prefix + truncated;\n\n const commitResult = run(`git commit -m \"${fullMessage.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n if (!commitResult.success) {\n console.warn(`[project-git] template commit failed: ${commitResult.stderr}`);\n return null;\n }\n\n const hashResult = run(\"git rev-parse --short HEAD\", { cwd: themePath });\n return hashResult.success ? hashResult.stdout : null;\n}\n\n// ---------------------------------------------------------------------------\n// History\n// ---------------------------------------------------------------------------\n\n/**\n * Get commit history (most recent first).\n */\nexport function getHistory(themePath: string, limit: number = 50): GitCommitInfo[] {\n if (!isGitAvailable()) return [];\n if (!existsSync(join(themePath, \".git\"))) return [];\n\n const result = run(\n `git log --pretty=format:\"%h|%H|%s|%at\" -n ${limit}`,\n { cwd: themePath }\n );\n if (!result.success || !result.stdout.trim()) return [];\n\n const commits: GitCommitInfo[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n const parts = line.split(\"|\");\n if (parts.length < 4) continue;\n const timestamp = parseInt(parts[3], 10) * 1000;\n commits.push({\n hash: parts[0],\n fullHash: parts[1],\n message: parts[2],\n timestamp,\n date: new Date(timestamp).toISOString(),\n });\n }\n return commits;\n}\n\n/**\n * Get commit history filtered to a specific template (by commit message prefix).\n */\nexport function getTemplateHistory(\n themePath: string,\n templateId: string,\n limit: number = 50\n): GitCommitInfo[] {\n if (!isGitAvailable()) return [];\n if (!existsSync(join(themePath, \".git\"))) return [];\n\n const escapedId = templateId.replace(/[[\\]\\\\]/g, \"\\\\$&\");\n const result = run(\n `git log --grep=\"\\\\[${escapedId}\\\\]\" --pretty=format:\"%h|%H|%s|%at\" -n ${limit}`,\n { cwd: themePath }\n );\n if (!result.success || !result.stdout.trim()) return [];\n\n const commits: GitCommitInfo[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n const parts = line.split(\"|\");\n if (parts.length < 4) continue;\n const timestamp = parseInt(parts[3], 10) * 1000;\n commits.push({\n hash: parts[0],\n fullHash: parts[1],\n message: parts[2],\n timestamp,\n date: new Date(timestamp).toISOString(),\n });\n }\n return commits;\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\n/**\n * Restore theme files to a specific commit's state.\n * Creates a new commit (\"Rollback to: <original message>\") to keep linear history.\n * Does NOT touch .vibespot/ (gitignored) — chat is preserved.\n */\nexport function rollbackToCommit(\n themePath: string,\n commitHash: string\n): { success: boolean; error?: string } {\n if (!isGitAvailable()) return { success: false, error: \"Git not available\" };\n if (!existsSync(join(themePath, \".git\"))) return { success: false, error: \"Not a git repo\" };\n\n if (!isValidHash(commitHash)) return { success: false, error: \"Invalid commit hash\" };\n\n // Verify commit exists\n const verify = run(`git cat-file -t ${commitHash}`, { cwd: themePath });\n if (!verify.success || verify.stdout.trim() !== \"commit\") {\n return { success: false, error: `Commit ${commitHash} not found` };\n }\n\n // Get original commit message\n const msgResult = run(`git log --format=\"%s\" -1 ${commitHash}`, { cwd: themePath });\n const origMessage = msgResult.success ? msgResult.stdout : commitHash;\n\n // Restore all files from that commit\n const checkout = run(`git checkout ${commitHash} -- .`, { cwd: themePath });\n if (!checkout.success) {\n return { success: false, error: `Checkout failed: ${checkout.stderr}` };\n }\n\n // Commit the rollback\n const rollbackMsg = `Rollback to: ${origMessage}`.slice(0, 72);\n run(`git commit -m \"${rollbackMsg.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n\n return { success: true };\n}\n\n/**\n * Restore only specific template files to a commit's state.\n * Other templates' files are left untouched.\n */\nexport function rollbackTemplateToCommit(\n themePath: string,\n templateId: string,\n commitHash: string,\n filePaths: string[]\n): { success: boolean; error?: string } {\n if (!isGitAvailable()) return { success: false, error: \"Git not available\" };\n if (!existsSync(join(themePath, \".git\"))) return { success: false, error: \"Not a git repo\" };\n\n if (!isValidHash(commitHash)) return { success: false, error: \"Invalid commit hash\" };\n\n // Verify commit exists\n const verify = run(`git cat-file -t ${commitHash}`, { cwd: themePath });\n if (!verify.success || verify.stdout.trim() !== \"commit\") {\n return { success: false, error: `Commit ${commitHash} not found` };\n }\n\n // Get original commit message\n const msgResult = run(`git log --format=\"%s\" -1 ${commitHash}`, { cwd: themePath });\n const origMessage = msgResult.success ? msgResult.stdout : commitHash;\n\n // Restore only the specified paths from that commit\n let restored = 0;\n for (const fp of filePaths) {\n const checkout = run(`git checkout ${commitHash} -- \"${fp}\"`, { cwd: themePath });\n if (checkout.success) restored++;\n // Skip paths that didn't exist at that commit (git errors silently ignored)\n }\n\n if (restored === 0) {\n return { success: false, error: \"No files could be restored from that commit\" };\n }\n\n // Stage and commit the scoped rollback\n run(\"git add -A\", { cwd: themePath });\n const prefix = `[${templateId}] `;\n const rollbackMsg = `${prefix}Rollback to: ${origMessage}`.slice(0, 72);\n run(`git commit -m \"${rollbackMsg.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n\n return { success: true };\n}\n","/**\n * State mutation functions for the active session.\n */\n\nimport { readFileSync, existsSync, writeFileSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles, GeneratedAssets } from \"../../ai/engine.js\";\nimport type { ChatMessage, TemplateEntry, SessionAsset, FieldDef } from \"./types.js\";\nimport { getSession, saveSession } from \"./store.js\";\nimport { getActiveTemplate } from \"./templates.js\";\n\n// ---------------------------------------------------------------------------\n// Flat-field sync helpers (exported for use by templates.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Sync flat session fields from a template (compatibility layer).\n * Existing code reads session.modules, session.messages, etc.\n * This keeps those in sync with the active template.\n */\nexport function syncFlatFieldsFromTemplate(tpl: TemplateEntry): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = tpl.modules;\n activeSession.moduleOrder = tpl.moduleOrder;\n activeSession.sharedCss = tpl.sharedCss;\n activeSession.sharedJs = tpl.sharedJs;\n activeSession.template = tpl.template;\n activeSession.messages = tpl.messages;\n}\n\n/**\n * Sync changes from flat session fields back to the active template.\n * Called after any mutation to session.modules/sharedCss/etc.\n */\nexport function syncFlatFieldsToTemplate(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const tpl = getActiveTemplate();\n if (!tpl) return;\n tpl.modules = activeSession.modules;\n tpl.moduleOrder = activeSession.moduleOrder;\n tpl.sharedCss = activeSession.sharedCss;\n tpl.sharedJs = activeSession.sharedJs;\n tpl.template = activeSession.template;\n tpl.messages = activeSession.messages;\n}\n\n// ---------------------------------------------------------------------------\n// Message management\n// ---------------------------------------------------------------------------\n\nexport function addMessage(role: \"user\" | \"assistant\", content: string, pipeline?: import(\"./types.js\").PipelineMetadata): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const msg: import(\"./types.js\").ChatMessage = { role, content, timestamp: Date.now() };\n if (pipeline) msg.pipeline = pipeline;\n activeSession.messages.push(msg);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n saveChatToTheme();\n}\n\nexport function addSessionAsset(asset: SessionAsset): void {\n const activeSession = getSession();\n if (!activeSession) return;\n if (!activeSession.assets) activeSession.assets = [];\n activeSession.assets.push(asset);\n activeSession.updatedAt = Date.now();\n saveSession();\n}\n\n// ---------------------------------------------------------------------------\n// Module mutations\n// ---------------------------------------------------------------------------\n\n/**\n * Update session with newly generated/modified modules.\n * Merges new modules into existing state (updates existing, adds new).\n */\nexport function updateModules(assets: Partial<GeneratedAssets>): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n if (assets.sharedCss !== undefined) activeSession.sharedCss = assets.sharedCss;\n if (assets.sharedJs !== undefined) activeSession.sharedJs = assets.sharedJs;\n if (assets.template !== undefined) activeSession.template = assets.template;\n\n if (assets.modules) {\n for (const newMod of assets.modules) {\n const newNameLower = newMod.moduleName.toLowerCase();\n const idx = activeSession.modules.findIndex(\n (m) => m.moduleName.toLowerCase() === newNameLower\n );\n if (idx >= 0) {\n activeSession.modules[idx] = newMod;\n } else {\n activeSession.modules.push(newMod);\n if (!activeSession.moduleOrder.some((n) => n.toLowerCase() === newNameLower)) {\n activeSession.moduleOrder.push(newMod.moduleName);\n }\n }\n }\n }\n\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Reorder modules (used by drag-and-drop in the UI).\n */\nexport function reorderModules(newOrder: string[]): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.moduleOrder = newOrder;\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Remove a module by name.\n */\nexport function removeModule(moduleName: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = activeSession.modules.filter(\n (m) => m.moduleName !== moduleName\n );\n activeSession.moduleOrder = activeSession.moduleOrder.filter(\n (n) => n !== moduleName\n );\n\n // Also remove from all templates\n for (const tpl of activeSession.templates) {\n tpl.modules = tpl.modules.filter((m) => m.moduleName !== moduleName);\n tpl.moduleOrder = tpl.moduleOrder.filter((n) => n !== moduleName);\n }\n\n // Delete module directory from disk\n if (activeSession.themePath) {\n const modDir = join(activeSession.themePath, \"modules\", `${moduleName}.module`);\n if (existsSync(modDir)) rmSync(modDir, { recursive: true, force: true });\n }\n\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Detach a module from the current template (remove from moduleOrder only).\n * The module data stays in the session so it can be re-added later.\n */\nexport function detachModule(moduleName: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.moduleOrder = activeSession.moduleOrder.filter(\n (n) => n !== moduleName\n );\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Update a single field value in a module's fields.json.\n * Used by the field editor sidebar.\n */\nexport function updateFieldValue(\n moduleName: string,\n fieldPath: string,\n value: unknown\n): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n const mod = activeSession.modules.find((m) => m.moduleName === moduleName);\n if (!mod) return;\n\n try {\n const fields = JSON.parse(mod.fieldsJson);\n setFieldDefault(fields, fieldPath, value);\n mod.fieldsJson = JSON.stringify(fields, null, 2);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n } catch {\n // Invalid JSON — skip\n }\n}\n\n/**\n * Get modules in display order.\n */\nexport function getOrderedModules(): ModuleFiles[] {\n const activeSession = getSession();\n if (!activeSession) return [];\n\n const ordered: ModuleFiles[] = [];\n for (const name of activeSession.moduleOrder) {\n const mod = activeSession.modules.find((m) => m.moduleName === name);\n if (mod) ordered.push(mod);\n }\n\n // Append any modules not in the order list\n for (const mod of activeSession.modules) {\n if (!activeSession.moduleOrder.includes(mod.moduleName)) {\n ordered.push(mod);\n }\n }\n\n return ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Chat persistence — store chat in theme directory\n// ---------------------------------------------------------------------------\n\n/**\n * Persist chat to {themePath}/.vibespot/chat.json (gitignored).\n */\nexport function saveChatToTheme(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n try {\n const chatDir = join(activeSession.themePath, \".vibespot\");\n mkdirSync(chatDir, { recursive: true });\n const chatData = {\n sessionId: activeSession.id,\n themeName: activeSession.themeName,\n messages: activeSession.messages,\n updatedAt: Date.now(),\n };\n writeFileSync(join(chatDir, \"chat.json\"), JSON.stringify(chatData, null, 2), \"utf-8\");\n } catch {\n // Non-critical — don't block on chat persistence errors\n }\n}\n\n/**\n * Load chat history from a theme's .vibespot/chat.json.\n */\nexport function loadChatFromTheme(themePath: string): ChatMessage[] {\n const chatPath = join(themePath, \".vibespot\", \"chat.json\");\n if (!existsSync(chatPath)) return [];\n try {\n const data = JSON.parse(readFileSync(chatPath, \"utf-8\"));\n return Array.isArray(data.messages) ? data.messages : [];\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set a default value at a dot-separated field path in a fields.json array.\n */\nfunction setFieldDefault(fields: FieldDef[], path: string, value: unknown): void {\n const parts = path.split(\".\");\n const fieldName = parts[0];\n const field = fields.find((f: FieldDef) => f.name === fieldName);\n if (!field) return;\n\n if (parts.length === 1) {\n field.default = value;\n } else if (field.children) {\n setFieldDefault(field.children, parts.slice(1).join(\".\"), value);\n }\n}\n","/**\n * Multi-template management functions.\n */\n\nimport { existsSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { VibeSession, TemplateEntry, PageType } from \"./types.js\";\nimport { getSession } from \"./store.js\";\nimport { syncFlatFieldsFromTemplate } from \"./state.js\";\n\n// ---------------------------------------------------------------------------\n// Template management\n// ---------------------------------------------------------------------------\n\n/**\n * Migrate a legacy flat session (v0.3.0) into the templates array.\n * Called when loading a session that has modules but no templates.\n */\nexport function migrateSession(session: VibeSession): void {\n if (session.templates && session.templates.length > 0) return;\n if (!session.modules || session.modules.length === 0) {\n session.templates = [];\n session.activeTemplateId = \"\";\n return;\n }\n\n const templateId = `lp-${session.themeName}`;\n const entry: TemplateEntry = {\n id: templateId,\n label: `${session.themeName} Landing Page`,\n pageType: \"landing_page\",\n templateFile: `templates/lp-${session.themeName}.html`,\n modules: [...session.modules],\n moduleOrder: [...session.moduleOrder],\n sharedCss: session.sharedCss || \"\",\n sharedJs: session.sharedJs || \"\",\n template: session.template || \"\",\n messages: [...session.messages],\n };\n\n session.templates = [entry];\n session.activeTemplateId = templateId;\n}\n\n/**\n * Get the active template entry, or null if none set.\n */\nexport function getActiveTemplate(): TemplateEntry | null {\n const activeSession = getSession();\n if (!activeSession) return null;\n if (!activeSession.activeTemplateId || !activeSession.templates?.length) return null;\n return activeSession.templates.find((t) => t.id === activeSession!.activeTemplateId) || null;\n}\n\n/**\n * Set the active template by ID. Syncs flat fields from the template.\n */\nexport function setActiveTemplate(templateId: string): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const tpl = activeSession.templates.find((t) => t.id === templateId);\n if (!tpl) return false;\n\n activeSession.activeTemplateId = templateId;\n syncFlatFieldsFromTemplate(tpl);\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Create a new template entry and add it to the session.\n */\nexport function addTemplate(pageType: PageType, label: string): TemplateEntry {\n const activeSession = getSession();\n if (!activeSession) throw new Error(\"No active session\");\n\n const slug = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const prefix = pageType === \"blog_post\" ? \"bp\" :\n pageType === \"website_page\" ? \"wp\" :\n pageType === \"module_only\" ? \"mo\" : \"lp\";\n const id = `${prefix}-${slug}`;\n\n const entry: TemplateEntry = {\n id,\n label,\n pageType,\n templateFile: pageType === \"module_only\" ? \"\" : `templates/${id}.html`,\n modules: [],\n moduleOrder: [],\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n messages: [],\n };\n\n activeSession.templates.push(entry);\n activeSession.activeTemplateId = id;\n syncFlatFieldsFromTemplate(entry);\n activeSession.updatedAt = Date.now();\n return entry;\n}\n\n/**\n * Clone an existing template, deep-copying modules and state.\n */\nexport function cloneTemplate(templateId: string, newLabel?: string): TemplateEntry | null {\n const activeSession = getSession();\n if (!activeSession) return null;\n const source = activeSession.templates.find((t) => t.id === templateId);\n if (!source) return null;\n\n const label = newLabel || `${source.label} (Copy)`;\n const slug = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const prefix = source.pageType === \"blog_post\" ? \"bp\" :\n source.pageType === \"website_page\" ? \"wp\" :\n source.pageType === \"module_only\" ? \"mo\" : \"lp\";\n const id = `${prefix}-${slug}`;\n\n const entry: TemplateEntry = {\n id,\n label,\n pageType: source.pageType,\n templateFile: source.pageType === \"module_only\" ? \"\" : `templates/${id}.html`,\n modules: source.modules.map((m) => ({ ...m })), // shallow copy each module\n moduleOrder: [...source.moduleOrder],\n sharedCss: source.sharedCss,\n sharedJs: source.sharedJs,\n template: source.template,\n messages: [], // start fresh chat for clone\n };\n\n activeSession.templates.push(entry);\n activeSession.activeTemplateId = id;\n syncFlatFieldsFromTemplate(entry);\n activeSession.updatedAt = Date.now();\n return entry;\n}\n\n/**\n * Rename a template's display label.\n */\nexport function renameTemplate(templateId: string, newLabel: string): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const tpl = activeSession.templates.find((t) => t.id === templateId);\n if (!tpl) return false;\n tpl.label = newLabel;\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Remove a template by ID.\n * If deleteModules is true, also remove modules that are exclusive to this\n * template (not used by any other template) from session and disk.\n */\nexport function removeTemplate(templateId: string, deleteModules = false): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const idx = activeSession.templates.findIndex((t) => t.id === templateId);\n if (idx < 0) return false;\n\n const removed = activeSession.templates.splice(idx, 1)[0];\n\n // Delete template file(s) from disk\n if (activeSession.themePath) {\n const templatesDir = join(activeSession.themePath, \"templates\");\n const filename = `${removed.id}.html`;\n const filePath = join(templatesDir, filename);\n if (existsSync(filePath)) rmSync(filePath, { force: true });\n // Also remove blog listing template if applicable\n if (removed.pageType === \"blog_post\") {\n const listingPath = join(templatesDir, `${removed.id}-listing.html`);\n if (existsSync(listingPath)) rmSync(listingPath, { force: true });\n }\n }\n\n // Delete exclusive modules from disk + remaining templates\n if (deleteModules && removed.modules.length > 0) {\n // Collect module names still used by other templates\n const usedElsewhere = new Set<string>();\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) usedElsewhere.add(mod.moduleName);\n }\n // Also check flat session modules\n for (const mod of activeSession.modules) usedElsewhere.add(mod.moduleName);\n\n const exclusiveModules = removed.modules\n .map((m) => m.moduleName)\n .filter((name) => !usedElsewhere.has(name));\n\n // Remove from disk\n if (activeSession.themePath && exclusiveModules.length > 0) {\n const modulesDir = join(activeSession.themePath, \"modules\");\n for (const name of exclusiveModules) {\n const modDir = join(modulesDir, `${name}.module`);\n if (existsSync(modDir)) rmSync(modDir, { recursive: true, force: true });\n }\n }\n }\n\n // If we removed the active template, switch to the first remaining one\n if (activeSession.activeTemplateId === templateId) {\n if (activeSession.templates.length > 0) {\n setActiveTemplate(activeSession.templates[0].id);\n } else {\n activeSession.activeTemplateId = \"\";\n activeSession.modules = [];\n activeSession.moduleOrder = [];\n activeSession.sharedCss = \"\";\n activeSession.sharedJs = \"\";\n activeSession.template = \"\";\n activeSession.messages = [];\n }\n }\n\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Get deduplicated modules across all templates (the module library).\n */\nexport function getModuleLibrary(): Array<{ module: ModuleFiles; usedIn: string[] }> {\n const activeSession = getSession();\n if (!activeSession) return [];\n const map = new Map<string, { module: ModuleFiles; usedIn: string[] }>();\n\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) {\n const existing = map.get(mod.moduleName);\n if (existing) {\n existing.usedIn.push(tpl.label);\n } else {\n map.set(mod.moduleName, { module: mod, usedIn: [tpl.label] });\n }\n }\n }\n\n return Array.from(map.values());\n}\n","/**\n * Session CRUD, index management, and the global activeSession variable.\n */\n\nimport { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync, rmSync, renameSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ensureGitRepo } from \"../project-git.js\";\nimport type { VibeSession, SessionIndexEntry } from \"./types.js\";\nimport { migrateSession } from \"./templates.js\";\n\n// ---------------------------------------------------------------------------\n// Session management\n// ---------------------------------------------------------------------------\n\nconst SESSIONS_DIR = join(homedir(), \".vibespot\", \"sessions\");\nconst INDEX_PATH = join(SESSIONS_DIR, \"_index.json\");\n\nlet _indexCache: SessionIndexEntry[] | null = null;\n\nfunction readIndex(): SessionIndexEntry[] {\n if (_indexCache) return _indexCache;\n try {\n if (!existsSync(INDEX_PATH)) return rebuildIndex();\n _indexCache = JSON.parse(readFileSync(INDEX_PATH, \"utf-8\"));\n return _indexCache!;\n } catch {\n return rebuildIndex();\n }\n}\n\nfunction writeIndex(entries: SessionIndexEntry[]): void {\n _indexCache = entries;\n try {\n mkdirSync(SESSIONS_DIR, { recursive: true });\n writeFileSync(INDEX_PATH, JSON.stringify(entries), \"utf-8\");\n } catch { /* non-critical */ }\n}\n\nfunction rebuildIndex(): SessionIndexEntry[] {\n if (!existsSync(SESSIONS_DIR)) return [];\n const entries: SessionIndexEntry[] = [];\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n const templates = data.templates || [];\n entries.push({\n id: data.id,\n themeName: data.themeName,\n updatedAt: data.updatedAt,\n moduleCount: templates.reduce((n: number, t: any) => n + (t.modules?.length || 0), 0),\n templateCount: templates.length,\n });\n } catch { /* skip corrupt files */ }\n }\n _indexCache = entries;\n writeIndex(entries);\n return entries;\n}\n\nfunction upsertIndex(session: VibeSession): void {\n const entries = readIndex();\n const templates = session.templates || [];\n const entry: SessionIndexEntry = {\n id: session.id,\n themeName: session.themeName,\n updatedAt: session.updatedAt,\n moduleCount: templates.reduce((n, t) => n + (t.modules?.length || 0), 0),\n templateCount: templates.length,\n };\n const idx = entries.findIndex((e) => e.id === session.id);\n if (idx >= 0) entries[idx] = entry;\n else entries.push(entry);\n writeIndex(entries);\n}\n\nfunction removeFromIndex(sessionId: string): void {\n const entries = readIndex().filter((e) => e.id !== sessionId);\n writeIndex(entries);\n}\n\nfunction removeFromIndexByTheme(themeName: string): void {\n const entries = readIndex().filter((e) => e.themeName !== themeName);\n writeIndex(entries);\n}\n\nlet activeSession: VibeSession | null = null;\n\nexport function getSession(): VibeSession | null {\n return activeSession;\n}\n\nfunction generateId(): string {\n return `vibe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function createSession(themePath: string, themeName: string): VibeSession {\n const session: VibeSession = {\n id: generateId(),\n themePath,\n themeName,\n templates: [],\n activeTemplateId: \"\",\n messages: [],\n modules: [],\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n moduleOrder: [],\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n activeSession = session;\n ensureGitRepo(themePath);\n return session;\n}\n\n// ---------------------------------------------------------------------------\n// Persistence — save/load sessions across restarts\n// ---------------------------------------------------------------------------\n\nexport function saveSession(): void {\n if (!activeSession) return;\n\n mkdirSync(SESSIONS_DIR, { recursive: true });\n const filePath = join(SESSIONS_DIR, `${activeSession.id}.json`);\n writeFileSync(filePath, JSON.stringify(activeSession, null, 2), \"utf-8\");\n upsertIndex(activeSession);\n}\n\nexport function loadSession(sessionId: string): VibeSession | null {\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n if (!existsSync(filePath)) return null;\n\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n\n // Ensure templates array exists (backward compat with v0.3.0 sessions)\n if (!data.templates) data.templates = [];\n if (!data.activeTemplateId) data.activeTemplateId = \"\";\n\n // Migrate flat fields into templates if needed\n migrateSession(data);\n\n activeSession = data;\n return data;\n } catch {\n return null;\n }\n}\n\nexport function listSessions(): Array<{ id: string; themeName: string; updatedAt: number; moduleCount: number; templateCount: number }> {\n if (!existsSync(SESSIONS_DIR)) return [];\n return readIndex();\n}\n\nexport function deleteSession(sessionId: string, deleteFiles = false): void {\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n\n // Read the session to get themeName (needed to find sibling sessions)\n let themeName = \"\";\n if (deleteFiles) {\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n themeName = data.themeName || \"\";\n if (data.themePath && existsSync(data.themePath)) {\n rmSync(data.themePath, { recursive: true, force: true });\n }\n } catch { /* ignore */ }\n } else {\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n themeName = data.themeName || \"\";\n } catch { /* ignore */ }\n }\n\n try {\n if (existsSync(filePath)) rmSync(filePath);\n } catch { /* ignore */ }\n\n // Also delete all other sessions for the same theme (prevents ghost entries)\n if (themeName && existsSync(SESSIONS_DIR)) {\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n if (data.themeName === themeName) {\n rmSync(join(SESSIONS_DIR, f));\n }\n } catch { /* ignore */ }\n }\n removeFromIndexByTheme(themeName);\n } else {\n removeFromIndex(sessionId);\n }\n\n if (activeSession?.id === sessionId) {\n activeSession = null;\n }\n}\n\n/**\n * Rename a project: update themeName in all sessions, rename disk folder,\n * rename CSS/JS files, and update the session index.\n */\nexport function renameSession(sessionId: string, newName: string): { ok: boolean; error?: string } {\n // Load the session to get the current theme name\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n if (!existsSync(filePath)) return { ok: false, error: \"Session not found\" };\n\n let session: VibeSession;\n try {\n session = JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return { ok: false, error: \"Failed to read session\" };\n }\n\n const oldName = session.themeName;\n if (oldName === newName) return { ok: true };\n\n const oldPath = session.themePath;\n const newPath = join(dirname(oldPath), newName);\n\n // Rename the folder on disk\n if (existsSync(oldPath)) {\n if (existsSync(newPath)) return { ok: false, error: \"A project with that name already exists\" };\n try {\n renameSync(oldPath, newPath);\n } catch (err) {\n return { ok: false, error: `Failed to rename folder: ${err instanceof Error ? err.message : String(err)}` };\n }\n\n // Rename CSS/JS files inside the new folder\n const cssOld = join(newPath, \"css\", `${oldName}-theme.css`);\n const cssNew = join(newPath, \"css\", `${newName}-theme.css`);\n if (existsSync(cssOld)) try { renameSync(cssOld, cssNew); } catch { /* non-critical */ }\n\n const jsOld = join(newPath, \"js\", `${oldName}-animations.js`);\n const jsNew = join(newPath, \"js\", `${newName}-animations.js`);\n if (existsSync(jsOld)) try { renameSync(jsOld, jsNew); } catch { /* non-critical */ }\n\n // Update theme.json label/name\n const themeJsonPath = join(newPath, \"theme.json\");\n if (existsSync(themeJsonPath)) {\n try {\n const themeData = JSON.parse(readFileSync(themeJsonPath, \"utf-8\"));\n themeData.label = newName;\n themeData.name = newName;\n writeFileSync(themeJsonPath, JSON.stringify(themeData, null, 2), \"utf-8\");\n } catch { /* non-critical */ }\n }\n }\n\n // Update all sessions that reference this theme\n if (existsSync(SESSIONS_DIR)) {\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n if (data.themeName === oldName) {\n data.themeName = newName;\n data.themePath = newPath;\n data.updatedAt = Date.now();\n writeFileSync(join(SESSIONS_DIR, f), JSON.stringify(data, null, 2), \"utf-8\");\n }\n } catch { /* skip corrupt files */ }\n }\n }\n\n // Update the in-memory active session\n if (activeSession && activeSession.themeName === oldName) {\n activeSession.themeName = newName;\n activeSession.themePath = newPath;\n activeSession.updatedAt = Date.now();\n }\n\n // Rebuild the index\n rebuildIndex();\n\n return { ok: true };\n}\n","/**\n * Theme disk I/O — reading from and writing to theme directories.\n */\n\nimport { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { ChatMessage, TemplateEntry, PageType } from \"./types.js\";\nimport { getSession } from \"./store.js\";\nimport { getOrderedModules, syncFlatFieldsFromTemplate, syncFlatFieldsToTemplate, loadChatFromTheme } from \"./state.js\";\nimport { getActiveTemplate, migrateSession } from \"./templates.js\";\nimport { ensureGitRepo } from \"../project-git.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction safeRead(filePath: string): string {\n try {\n return readFileSync(filePath, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get modules in display order for a specific template entry.\n */\nfunction getOrderedModulesFrom(tpl: TemplateEntry): ModuleFiles[] {\n const ordered: ModuleFiles[] = [];\n for (const name of tpl.moduleOrder) {\n const mod = tpl.modules.find((m) => m.moduleName === name);\n if (mod) ordered.push(mod);\n }\n for (const mod of tpl.modules) {\n if (!tpl.moduleOrder.includes(mod.moduleName)) {\n ordered.push(mod);\n }\n }\n return ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Template file parsing\n// ---------------------------------------------------------------------------\n\ninterface ParsedTemplate {\n id: string;\n label: string;\n pageType: PageType;\n moduleNames: string[];\n templateContent: string;\n filename: string;\n}\n\n/**\n * Parse a single HubL template file to extract metadata and module references.\n */\nfunction parseTemplateFile(filePath: string, filename: string): ParsedTemplate | null {\n const content = safeRead(filePath);\n if (!content) return null;\n\n // Skip scaffold placeholder and listing templates\n if (filename === \"home.html\" || filename.endsWith(\"-listing.html\")) return null;\n\n // Infer ID from filename (strip .html)\n const id = filename.replace(/\\.html$/, \"\");\n\n // Infer pageType from prefix\n let pageType: PageType = \"landing_page\";\n if (id.startsWith(\"bp-\")) pageType = \"blog_post\";\n else if (id.startsWith(\"wp-\")) pageType = \"website_page\";\n else if (id.startsWith(\"mo-\")) pageType = \"module_only\";\n\n // Extract label from HubL comment metadata\n let label = id;\n const labelMatch = content.match(/<!--[\\s\\S]*?label:\\s*\"?([^\"\\n]+)\"?\\s*[\\s\\S]*?-->/);\n if (labelMatch) {\n label = labelMatch[1].trim();\n }\n\n // Extract module references from dnd_module tags\n const moduleRe = /dnd_module\\s+path=[\"']\\.\\.\\/modules\\/(.+?)\\.module[\"']/g;\n const moduleNames: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = moduleRe.exec(content)) !== null) {\n moduleNames.push(match[1]);\n }\n\n return { id, label, pageType, moduleNames, templateContent: content, filename };\n}\n\n/**\n * Scan the templates directory and create TemplateEntry objects for each template file.\n * Returns an empty array if no valid template files are found.\n */\nfunction scanTemplateFiles(\n templatesDir: string,\n modulesByName: Map<string, ModuleFiles>,\n sharedCss: string,\n sharedJs: string,\n chatMessages: ChatMessage[],\n): TemplateEntry[] {\n if (!existsSync(templatesDir)) return [];\n\n const entries: TemplateEntry[] = [];\n const allFiles = readdirSync(templatesDir).filter(\n (f) => f.endsWith(\".html\") && f !== \"home.html\"\n );\n\n // Filter out layout files (in subdirectories) — only direct children\n for (const filename of allFiles) {\n const filePath = join(templatesDir, filename);\n const parsed = parseTemplateFile(filePath, filename);\n if (!parsed) continue;\n if (parsed.moduleNames.length === 0 && entries.length > 0) continue; // skip empty templates if we have others\n\n // Build module list for this template\n const templateModules: ModuleFiles[] = [];\n const templateOrder: string[] = [];\n for (const name of parsed.moduleNames) {\n const mod = modulesByName.get(name);\n if (mod) {\n templateModules.push(mod);\n templateOrder.push(name);\n }\n }\n\n entries.push({\n id: parsed.id,\n label: parsed.label,\n pageType: parsed.pageType,\n templateFile: `templates/${parsed.filename}`,\n modules: templateModules,\n moduleOrder: templateOrder,\n sharedCss,\n sharedJs,\n template: parsed.templateContent,\n messages: entries.length === 0 ? [...chatMessages] : [], // chat history goes to first template\n });\n }\n\n return entries;\n}\n\n// ---------------------------------------------------------------------------\n// Scan modules from disk (for loading existing themes)\n// ---------------------------------------------------------------------------\n\n/**\n * Scan a theme directory on disk and load existing modules into the session.\n */\nexport function scanThemeFromDisk(themePath: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n // Load persisted chat from theme directory (if session has no messages yet)\n const chatFromDisk = loadChatFromTheme(themePath);\n if (chatFromDisk.length > 0 && activeSession.messages.length === 0) {\n activeSession.messages = chatFromDisk;\n }\n\n // Ensure git repo exists (handles themes created before this feature)\n ensureGitRepo(themePath);\n\n const modulesDir = join(themePath, \"modules\");\n if (!existsSync(modulesDir)) return;\n\n const entries = readdirSync(modulesDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.endsWith(\".module\")) continue;\n\n const modDir = join(modulesDir, entry.name);\n const moduleName = entry.name.replace(/\\.module$/, \"\");\n\n const mod: ModuleFiles = {\n moduleName,\n fieldsJson: safeRead(join(modDir, \"fields.json\")),\n metaJson: safeRead(join(modDir, \"meta.json\")),\n moduleHtml: safeRead(join(modDir, \"module.html\")),\n moduleCss: safeRead(join(modDir, \"module.css\")),\n moduleJs: safeRead(join(modDir, \"module.js\")) || undefined,\n };\n\n if (mod.fieldsJson && mod.moduleHtml) {\n activeSession.modules.push(mod);\n activeSession.moduleOrder.push(moduleName);\n }\n }\n\n // Load shared CSS/JS\n const cssDir = join(themePath, \"css\");\n const jsDir = join(themePath, \"js\");\n let sharedCss = \"\";\n let sharedJs = \"\";\n\n if (existsSync(cssDir)) {\n const cssFiles = readdirSync(cssDir).filter(\n (f) => f.endsWith(\"-theme.css\")\n );\n if (cssFiles.length > 0) {\n sharedCss = safeRead(join(cssDir, cssFiles[0]));\n activeSession.sharedCss = sharedCss;\n }\n }\n\n if (existsSync(jsDir)) {\n const jsFiles = readdirSync(jsDir).filter(\n (f) => f.endsWith(\"-animations.js\")\n );\n if (jsFiles.length > 0) {\n sharedJs = safeRead(join(jsDir, jsFiles[0]));\n activeSession.sharedJs = sharedJs;\n }\n }\n\n // Load brand assets from .vibespot/ directory\n const sgPath = join(themePath, \".vibespot\", \"styleguide.md\");\n const bvPath = join(themePath, \".vibespot\", \"brandvoice.md\");\n const tcPath = join(themePath, \".vibespot\", \"theme-context.md\");\n if (existsSync(sgPath) || existsSync(bvPath) || existsSync(tcPath)) {\n if (!activeSession.brandAssets) activeSession.brandAssets = {};\n if (existsSync(sgPath)) activeSession.brandAssets.styleguide = safeRead(sgPath);\n if (existsSync(bvPath)) activeSession.brandAssets.brandvoice = safeRead(bvPath);\n if (existsSync(tcPath)) activeSession.brandAssets.themeContext = safeRead(tcPath);\n }\n\n // Scan template files and create per-template TemplateEntry objects\n const templatesDir = join(themePath, \"templates\");\n const modulesByName = new Map(activeSession.modules.map((m) => [m.moduleName, m]));\n const parsedTemplates = scanTemplateFiles(templatesDir, modulesByName, sharedCss, sharedJs, activeSession.messages);\n\n if (parsedTemplates.length > 0) {\n activeSession.templates = parsedTemplates;\n activeSession.activeTemplateId = parsedTemplates[0].id;\n\n // Reorder flat modules to match the first template's order\n const firstOrder = parsedTemplates[0].moduleOrder;\n if (firstOrder.length > 0) {\n const loadedNames = new Set(activeSession.moduleOrder);\n const validOrder = firstOrder.filter((n) => loadedNames.has(n));\n for (const n of activeSession.moduleOrder) {\n if (!validOrder.includes(n)) validOrder.push(n);\n }\n activeSession.moduleOrder = validOrder;\n }\n\n syncFlatFieldsFromTemplate(parsedTemplates[0]);\n } else {\n // No template files found — fall back to legacy single-template migration\n if (!activeSession.templates) activeSession.templates = [];\n if (!activeSession.activeTemplateId) activeSession.activeTemplateId = \"\";\n migrateSession(activeSession);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Write modules back to disk (for upload)\n// ---------------------------------------------------------------------------\n\nexport function writeModulesToDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n const themePath = activeSession.themePath;\n\n // Collect all modules from all templates (deduplicated by name)\n const allModules = new Map<string, ModuleFiles>();\n if (activeSession.templates.length > 0) {\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) {\n allModules.set(mod.moduleName, mod);\n }\n }\n }\n // Also include flat session modules (backward compat / active template)\n for (const mod of activeSession.modules) {\n allModules.set(mod.moduleName, mod);\n }\n\n // Pre-create all module directories in one pass\n const modulesBaseDir = join(themePath, \"modules\");\n mkdirSync(modulesBaseDir, { recursive: true });\n for (const mod of allModules.values()) {\n mkdirSync(join(modulesBaseDir, `${mod.moduleName}.module`), { recursive: true });\n }\n\n for (const mod of allModules.values()) {\n const modDir = join(modulesBaseDir, `${mod.moduleName}.module`);\n writeFileSync(join(modDir, \"fields.json\"), mod.fieldsJson, \"utf-8\");\n writeFileSync(join(modDir, \"meta.json\"), mod.metaJson, \"utf-8\");\n writeFileSync(join(modDir, \"module.html\"), mod.moduleHtml, \"utf-8\");\n writeFileSync(join(modDir, \"module.css\"), mod.moduleCss, \"utf-8\");\n if (mod.moduleJs) {\n writeFileSync(join(modDir, \"module.js\"), mod.moduleJs, \"utf-8\");\n }\n }\n\n // Write shared CSS/JS\n if (activeSession.sharedCss) {\n const cssDir = join(themePath, \"css\");\n mkdirSync(cssDir, { recursive: true });\n writeFileSync(\n join(cssDir, `${activeSession.themeName}-theme.css`),\n activeSession.sharedCss,\n \"utf-8\"\n );\n }\n\n if (activeSession.sharedJs) {\n const jsDir = join(themePath, \"js\");\n mkdirSync(jsDir, { recursive: true });\n writeFileSync(\n join(jsDir, `${activeSession.themeName}-animations.js`),\n activeSession.sharedJs,\n \"utf-8\"\n );\n }\n\n // Write page templates for all templates in the session\n const templatesDir = join(themePath, \"templates\");\n mkdirSync(templatesDir, { recursive: true });\n\n // Remove scaffold home.html once real templates exist — it's an empty shell\n // that shows up as a usable template in HubSpot but has no modules.\n const scaffoldHome = join(templatesDir, \"home.html\");\n const hasRealTemplates = activeSession.templates.length > 0 || activeSession.modules.length > 0;\n if (hasRealTemplates && existsSync(scaffoldHome)) {\n rmSync(scaffoldHome, { force: true });\n }\n\n // Track which template files we're writing so we can clean up stale ones\n const activeTemplateFiles = new Set<string>();\n\n if (activeSession.templates.length > 0) {\n for (const tpl of activeSession.templates) {\n if (tpl.pageType === \"module_only\") continue; // No template for module-only\n if (tpl.modules.length === 0) continue;\n\n const templateContent = tpl.template || generateTemplateForEntry(tpl);\n const annotated = ensureTemplateAnnotations(templateContent, tpl.label, tpl.pageType);\n const filename = `${tpl.id}.html`;\n writeFileSync(join(templatesDir, filename), annotated, \"utf-8\");\n activeTemplateFiles.add(filename);\n\n // For blog posts, also generate a listing template\n if (tpl.pageType === \"blog_post\") {\n writeBlogListingTemplate(templatesDir, tpl);\n activeTemplateFiles.add(`${tpl.id}-listing.html`);\n }\n }\n } else if (activeSession.modules.length > 0) {\n // Legacy fallback: single template from flat fields\n const template = activeSession.template || generateTemplateFromModules();\n const annotated = ensureTemplateAnnotations(template, `${activeSession.themeName} Landing Page`);\n const filename = `lp-${activeSession.themeName}.html`;\n writeFileSync(join(templatesDir, filename), annotated, \"utf-8\");\n activeTemplateFiles.add(filename);\n }\n\n // Clean up stale lp-*.html template files that are no longer in the session\n try {\n for (const file of readdirSync(templatesDir)) {\n if (file.startsWith(\"lp-\") && file.endsWith(\".html\") && !activeTemplateFiles.has(file)) {\n rmSync(join(templatesDir, file), { force: true });\n }\n }\n } catch { /* non-critical */ }\n\n // Patch base.html to load template_js (animations won't work without this)\n patchBaseTemplate();\n\n // Populate theme.json with proper metadata\n updateThemeJson();\n}\n\n// ---------------------------------------------------------------------------\n// Reload from disk\n// ---------------------------------------------------------------------------\n\n/**\n * Clear in-memory modules and re-scan from disk.\n * Used after a git rollback to sync session state with restored files.\n */\nexport function reloadModulesFromDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = [];\n activeSession.moduleOrder = [];\n activeSession.sharedCss = \"\";\n activeSession.sharedJs = \"\";\n activeSession.template = \"\";\n scanThemeFromDisk(activeSession.themePath);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Reload only the active template's modules from disk.\n * Used after a scoped rollback to avoid affecting other templates.\n */\nexport function reloadActiveTemplateFromDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const tpl = getActiveTemplate();\n if (!tpl) return;\n\n const themePath = activeSession.themePath;\n const modulesDir = join(themePath, \"modules\");\n\n // Reload modules that belong to this template\n tpl.modules = [];\n for (const name of tpl.moduleOrder) {\n const modDir = join(modulesDir, `${name}.module`);\n if (!existsSync(modDir)) continue;\n const mod: ModuleFiles = {\n moduleName: name,\n fieldsJson: safeRead(join(modDir, \"fields.json\")),\n metaJson: safeRead(join(modDir, \"meta.json\")),\n moduleHtml: safeRead(join(modDir, \"module.html\")),\n moduleCss: safeRead(join(modDir, \"module.css\")),\n moduleJs: safeRead(join(modDir, \"module.js\")) || undefined,\n };\n if (mod.fieldsJson && mod.moduleHtml) {\n tpl.modules.push(mod);\n }\n }\n\n // Reload template file\n if (tpl.templateFile) {\n const tplPath = join(themePath, tpl.templateFile);\n if (existsSync(tplPath)) {\n tpl.template = safeRead(tplPath);\n }\n }\n\n // Sync flat fields from the updated template\n syncFlatFieldsFromTemplate(tpl);\n activeSession.updatedAt = Date.now();\n}\n\n// ---------------------------------------------------------------------------\n// Theme metadata — populate theme.json + validate template annotations\n// ---------------------------------------------------------------------------\n\n/**\n * Patch base.html to load template_js (the boilerplate only loads template_css).\n * Without this, shared animations JS never runs and scroll-animate elements stay invisible.\n */\nfunction patchBaseTemplate(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const basePath = join(activeSession.themePath, \"templates\", \"layouts\", \"base.html\");\n if (!existsSync(basePath)) return;\n\n try {\n let content = readFileSync(basePath, \"utf-8\");\n // Already patched?\n if (content.includes(\"template_js\")) return;\n\n // Insert {% if template_js %} block right after the main.js require line\n const mainJsLine = '{{ require_js(get_asset_url(\"../../js/main.js\")) }}';\n if (content.includes(mainJsLine)) {\n content = content.replace(\n mainJsLine,\n mainJsLine + '\\n {% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}'\n );\n } else {\n // Fallback: insert before standard_footer_includes\n content = content.replace(\n \"{{ standard_footer_includes }}\",\n '{% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}\\n {{ standard_footer_includes }}'\n );\n }\n\n writeFileSync(basePath, content, \"utf-8\");\n } catch {\n // Non-critical — the generated template has its own require_js fallback\n }\n}\n\n/**\n * Update theme.json with proper name/label and ensure template annotations.\n * Called during writeModulesToDisk.\n */\nfunction updateThemeJson(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const themeJsonPath = join(activeSession.themePath, \"theme.json\");\n if (!existsSync(themeJsonPath)) return;\n\n try {\n const themeData = JSON.parse(readFileSync(themeJsonPath, \"utf-8\"));\n themeData.label = activeSession.themeName;\n themeData.name = activeSession.themeName;\n writeFileSync(themeJsonPath, JSON.stringify(themeData, null, 2), \"utf-8\");\n } catch {\n // Non-critical\n }\n}\n\n/**\n * Ensure the page template has proper HubSpot annotations.\n */\nfunction ensureTemplateAnnotations(templateContent: string, label: string, pageType: PageType = \"landing_page\"): string {\n // Check if annotations already exist\n if (templateContent.includes(\"templateType\")) return templateContent;\n\n const templateType = pageType === \"blog_post\" ? \"blog_post\" : \"page\";\n const annotations = `<!--\n templateType: ${templateType}\n isAvailableForNewContent: true\n label: \"${label}\"\n-->\\n`;\n return annotations + templateContent;\n}\n\n// ---------------------------------------------------------------------------\n// Template generation — build page templates from module data\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HubSpot page template for a specific TemplateEntry.\n */\nfunction generateTemplateForEntry(tpl: TemplateEntry): string {\n if (tpl.modules.length === 0) return \"\";\n\n const activeSession = getSession()!;\n const name = activeSession.themeName;\n const ordered = getOrderedModulesFrom(tpl);\n\n const sections = ordered.map((mod) => {\n return ` {% dnd_section padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true %}\n {% dnd_module path=\"../modules/${mod.moduleName}.module\" %}\n {% end_dnd_module %}\n {% end_dnd_section %}`;\n }).join(\"\\n\\n\");\n\n const templateType = tpl.pageType === \"blog_post\" ? \"blog_post\" : \"page\";\n\n return `<!--\n templateType: ${templateType}\n isAvailableForNewContent: true\n label: \"${tpl.label}\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% set template_css = \"../../css/${name}-theme.css\" %}\n{% set template_js = \"../../js/${name}-animations.js\" %}\n\n{% block header %}\n{% endblock header %}\n\n{% block body %}\n<div class=\"${name}-page\">\n {% dnd_area \"main_content\" label=\"${tpl.label}\" %}\n\n${sections}\n\n {% end_dnd_area %}\n</div>\n{{ require_js(get_asset_url(\"../../js/${name}-animations.js\")) }}\n{% endblock body %}\n\n{% block footer %}\n{% endblock footer %}\n`;\n}\n\n/**\n * Write a blog listing template alongside a blog post template.\n */\nfunction writeBlogListingTemplate(templatesDir: string, tpl: TemplateEntry): void {\n const listingContent = `<!--\n templateType: blog_listing\n isAvailableForNewContent: true\n label: \"${tpl.label} - Listing\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% block body %}\n<div class=\"blog-listing\">\n <h1>{{ group.public_title }}</h1>\n {% for content in contents %}\n <article class=\"blog-listing__post\">\n <h2><a href=\"{{ content.absolute_url }}\">{{ content.name }}</a></h2>\n {% if content.featured_image %}\n <img src=\"{{ content.featured_image }}\" alt=\"{{ content.featured_image_alt_text }}\">\n {% endif %}\n <p>{{ content.post_summary|truncatewords(30) }}</p>\n <span class=\"blog-listing__date\">{{ content.publish_date|datetimeformat('%B %d, %Y') }}</span>\n </article>\n {% endfor %}\n {% if next_page_num %}\n <a href=\"{{ next_page_url }}\">Next Page</a>\n {% endif %}\n</div>\n{% endblock body %}\n`;\n writeFileSync(\n join(templatesDir, `${tpl.id}-listing.html`),\n listingContent,\n \"utf-8\"\n );\n}\n\n/**\n * Build a HubSpot page template that assembles all modules in display order.\n * Legacy — used when no templates array exists (flat session).\n */\nfunction generateTemplateFromModules(): string {\n const activeSession = getSession();\n if (!activeSession || activeSession.modules.length === 0) return \"\";\n\n const name = activeSession.themeName;\n const ordered = getOrderedModules();\n\n const sections = ordered.map((mod) => {\n return ` {% dnd_section padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true %}\n {% dnd_module path=\"../modules/${mod.moduleName}.module\" %}\n {% end_dnd_module %}\n {% end_dnd_section %}`;\n }).join(\"\\n\\n\");\n\n return `<!--\n templateType: page\n isAvailableForNewContent: true\n label: \"${name} Landing Page\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% set template_css = \"../../css/${name}-theme.css\" %}\n{% set template_js = \"../../js/${name}-animations.js\" %}\n\n{% block header %}\n{% endblock header %}\n\n{% block body %}\n<div class=\"${name}-page\">\n {% dnd_area \"main_content\" label=\"${name} Landing Page\" %}\n\n${sections}\n\n {% end_dnd_area %}\n</div>\n{{ require_js(get_asset_url(\"../../js/${name}-animations.js\")) }}\n{% endblock body %}\n\n{% block footer %}\n{% endblock footer %}\n`;\n}\n","export * from \"./types.js\";\nexport * from \"./store.js\";\nexport * from \"./state.js\";\nexport * from \"./disk.js\";\nexport * from \"./templates.js\";\n","/**\n * Re-export shim — session logic lives in session/ directory.\n * Kept for ESM import compatibility (NodeNext resolves ./session.js to file, not session/index.js).\n */\nexport * from \"./session/index.js\";\n","/**\n * Lightweight HubL subset renderer for local preview.\n *\n * Supports the constructs that AI-generated HubSpot modules actually use:\n * {{ module.field }} — variable access\n * {{ module.group.child }} — nested access\n * {% if module.field %}...{% endif %} — conditionals (+ {% else %})\n * {% for item in module.list %}...{% endfor %} — loops\n * {{ item.field }} — loop variable access\n *\n * get_asset_url(\"assets/...\") is resolved to /theme-assets/ for local preview.\n * Everything else (require_css, dnd_area, etc.) is stripped.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FieldDef {\n name: string;\n type: string;\n default?: unknown;\n children?: FieldDef[];\n occurrence?: { min: number; max: number };\n tab?: string;\n}\n\nexport interface RenderContext {\n module: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a render context from a fields.json array, using each field's default.\n */\nexport function buildContextFromFields(fields: FieldDef[]): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const field of fields) {\n if (field.type === \"group\" && field.occurrence && Array.isArray(field.default)) {\n // Repeater group — default is an array of objects\n result[field.name] = field.default;\n } else if (field.type === \"group\" && field.children) {\n // Nested group (e.g. styles) — recurse into children\n result[field.name] = buildContextFromFields(field.children);\n } else {\n result[field.name] = field.default ?? \"\";\n }\n }\n\n return result;\n}\n\n/**\n * Render a HubL template string with the given context.\n * Returns plain HTML suitable for browser rendering.\n */\nexport function renderHubL(template: string, context: RenderContext): string {\n let output = template;\n\n // 1. Strip HubSpot-only directives that don't apply in preview\n output = stripDirectives(output);\n\n // 2. Process {% for %} loops (must come before if/expressions)\n output = processForLoops(output, context);\n\n // 3. Process {% if %} / {% else %} / {% endif %} conditionals\n output = processConditionals(output, context);\n\n // 4. Resolve {{ expression }} variable references\n output = resolveExpressions(output, context);\n\n // 5. Clean up any remaining unresolved tags\n output = cleanupRemaining(output);\n\n return output;\n}\n\n/**\n * Assemble a full preview HTML page from rendered modules + CSS/JS.\n */\nexport function assemblePreview(opts: {\n renderedModules: string[];\n sharedCss?: string;\n moduleCssArray: string[];\n sharedJs?: string;\n moduleJsArray: string[];\n}): string {\n const styleBlocks = [\n opts.sharedCss || \"\",\n ...opts.moduleCssArray,\n ]\n .filter(Boolean)\n .map((css) => `<style>${css}</style>`)\n .join(\"\\n\");\n\n const scriptBlocks = [\n opts.sharedJs || \"\",\n ...opts.moduleJsArray,\n ]\n .filter(Boolean)\n .map((js) => `<script>${js}</script>`)\n .join(\"\\n\");\n\n const body = opts.renderedModules.join(\"\\n\");\n\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\">\n${styleBlocks}\n<style>\nhtml{scroll-behavior:smooth}\n.vsp-img-wrap{position:relative;display:inline-block}\n.vsp-img-badge{position:absolute;top:8px;right:8px;background:rgba(0,0,0,.75);color:#fff;font:500 11px/1 -apple-system,sans-serif;padding:5px 8px;border-radius:4px;pointer-events:none;opacity:.85;white-space:nowrap;z-index:10}\n</style>\n</head>\n<body>\n${body}\n${scriptBlocks}\n<script>\n// Anchor link handler — smooth scroll to module sections\ndocument.addEventListener('click',function(e){\n var a=e.target.closest('a[href^=\"#\"]');\n if(!a)return;\n var id=a.getAttribute('href').slice(1);\n if(!id)return;\n var el=document.getElementById(id);\n if(el){\n e.preventDefault();\n el.scrollIntoView({behavior:'smooth',block:'start'});\n }\n});\n// Placeholder image badges\ndocument.querySelectorAll('img').forEach(function(img){\n var src=img.src||img.getAttribute('src')||'';\n if(src.indexOf('placehold')!==-1){\n var w=document.createElement('span');\n w.className='vsp-img-wrap';\n img.parentNode.insertBefore(w,img);\n w.appendChild(img);\n var b=document.createElement('span');\n b.className='vsp-img-badge';\n b.textContent='Placeholder — replace in HubSpot';\n w.appendChild(b);\n }\n});\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n// Pre-compiled regex patterns for stripDirectives (avoid recompilation per render)\nconst RE_REQUIRE_TAG = /\\{%[-\\s]*require_(css|js)\\b.*?%\\}/gs;\nconst RE_END_REQUIRE_TAG = /\\{%[-\\s]*end_require_(css|js)\\s*%\\}/gs;\nconst RE_REQUIRE_EXPR = /\\{\\{[-\\s]*require_(css|js)\\(.*?\\)\\s*\\}\\}/gs;\nconst RE_GET_ASSET_URL = /\\{\\{[-\\s]*get_asset_url\\([\"'](?:[^\"'\\/]+\\/)?assets\\/(.*?)[\"']\\)\\s*\\}\\}/gs;\nconst RE_GET_ASSET_URL_STRIP = /\\{\\{[-\\s]*get_asset_url\\(.*?\\)\\s*\\}\\}/gs;\nconst RE_DND_TAGS = /\\{%[-\\s]*(end_)?(dnd_area|dnd_section|dnd_column|dnd_row|dnd_module)\\b.*?%\\}/gs;\nconst RE_MODULE_TAG = /\\{%[-\\s]*module\\b.*?%\\}/gs;\nconst RE_TEMPLATE_TAGS = /\\{%[-\\s]*(extends|block|endblock|set)\\b.*?%\\}/gs;\nconst RE_ANNOTATIONS = /\\{#.*?#\\}/gs;\nconst RE_CONTENT_VARS = /\\{\\{[-\\s]*content\\.\\w+.*?\\}\\}/gs;\n// Match only INNERMOST if/endif blocks (body must not contain other {% if %} tags).\n// The while-loop peels layers from inside out, resolving nested conditionals correctly.\nconst RE_IF_PATTERN = /\\{%[-\\s]*if\\s+(.*?)\\s*-?%\\}((?:(?!\\{%[-\\s]*if\\s)[\\s\\S])*?)\\{%[-\\s]*endif\\s*-?%\\}/g;\n\n/**\n * Strip HubSpot-specific directives that have no meaning in local preview.\n */\nfunction stripDirectives(tpl: string): string {\n tpl = tpl.replace(RE_REQUIRE_TAG, \"\");\n tpl = tpl.replace(RE_END_REQUIRE_TAG, \"\");\n tpl = tpl.replace(RE_REQUIRE_EXPR, \"\");\n // Resolve get_asset_url(\"assets/filename\") → /theme-assets/filename for preview\n RE_GET_ASSET_URL.lastIndex = 0;\n tpl = tpl.replace(RE_GET_ASSET_URL, (_match, filename) => `/theme-assets/${filename}`);\n // Strip any remaining get_asset_url() calls with non-standard paths\n RE_GET_ASSET_URL_STRIP.lastIndex = 0;\n tpl = tpl.replace(RE_GET_ASSET_URL_STRIP, \"\");\n tpl = tpl.replace(RE_DND_TAGS, \"\");\n tpl = tpl.replace(RE_MODULE_TAG, \"\");\n tpl = tpl.replace(RE_TEMPLATE_TAGS, \"\");\n tpl = tpl.replace(RE_ANNOTATIONS, \"\");\n tpl = tpl.replace(RE_CONTENT_VARS, \"\");\n return tpl;\n}\n\n/**\n * Process {% for VAR in PATH %}...{% endfor %} loops.\n * Uses balanced tag matching to handle nested for-loops correctly.\n */\nfunction processForLoops(tpl: string, context: RenderContext): string {\n let result = tpl;\n let safety = 0;\n\n while (safety < 30) {\n safety++;\n const match = findOutermostFor(result);\n if (!match) break;\n\n const { varName, iterExpr, body, start, end } = match;\n const items = resolveIterable(iterExpr, context);\n\n let rendered = \"\";\n if (Array.isArray(items)) {\n rendered = items\n .map((item, index) => {\n const loopContext: RenderContext = {\n ...context,\n [varName]: item,\n loop: { index: index + 1, index0: index, first: index === 0, last: index === items.length - 1, length: items.length },\n };\n\n let out = processForLoops(body, loopContext);\n out = processConditionals(out, loopContext);\n out = resolveExpressions(out, loopContext);\n return out;\n })\n .join(\"\");\n }\n\n result = result.slice(0, start) + rendered + result.slice(end);\n }\n\n return result;\n}\n\n/**\n * Find the first outermost {% for %}...{% endfor %} block with balanced nesting.\n */\nfunction findOutermostFor(tpl: string): { varName: string; iterExpr: string; body: string; start: number; end: number } | null {\n const openTag = /\\{%[-\\s]*for\\s+(\\w+)\\s+in\\s+([\\w.]+(?:\\([^)]*\\))?(?:\\|[\\w(),\"' ]+)*)\\s*-?%\\}/g;\n const forOrEndfor = /\\{%[-\\s]*(for\\s|endfor)\\s*.*?-?%\\}/g;\n\n const firstOpen = openTag.exec(tpl);\n if (!firstOpen) return null;\n\n const varName = firstOpen[1];\n const iterExpr = firstOpen[2];\n const bodyStart = firstOpen.index + firstOpen[0].length;\n\n // Find matching endfor by counting nesting depth\n forOrEndfor.lastIndex = bodyStart;\n let depth = 1;\n let m: RegExpExecArray | null;\n\n while ((m = forOrEndfor.exec(tpl)) !== null) {\n if (m[1].startsWith(\"for\")) {\n depth++;\n } else {\n depth--;\n if (depth === 0) {\n const body = tpl.slice(bodyStart, m.index);\n return { varName, iterExpr, body, start: firstOpen.index, end: m.index + m[0].length };\n }\n }\n }\n\n return null; // Unmatched for-loop\n}\n\n/**\n * Process {% if EXPR %}...{% else %}...{% endif %} conditionals.\n * Supports {% elif %} as well.\n */\nfunction processConditionals(tpl: string, context: RenderContext): string {\n // Process from innermost out\n let result = tpl;\n let safety = 0;\n\n while (RE_IF_PATTERN.test(result) && safety < 50) {\n safety++;\n result = result.replace(RE_IF_PATTERN, (_match, condition: string, body: string) => {\n // Split on {% else %} and {% elif %}\n const elseMatch = body.split(/\\{%[-\\s]*else\\s*-?%\\}/);\n const ifBody = elseMatch[0];\n const elseBody = elseMatch[1] || \"\";\n\n // Check for {% elif %} (treat as nested if-else)\n const elifParts = ifBody.split(/\\{%[-\\s]*elif\\s+(.*?)\\s*-?%\\}/);\n\n if (elifParts.length > 1) {\n // Has elif branches\n if (evaluateCondition(condition, context)) {\n return elifParts[0];\n }\n // Check elif branches\n for (let i = 1; i < elifParts.length; i += 2) {\n const elifCondition = elifParts[i];\n const elifBody = elifParts[i + 1] || \"\";\n if (evaluateCondition(elifCondition, context)) {\n return elifBody;\n }\n }\n return elseBody;\n }\n\n if (evaluateCondition(condition, context)) {\n return ifBody;\n }\n return elseBody;\n });\n\n RE_IF_PATTERN.lastIndex = 0;\n }\n\n return result;\n}\n\n/**\n * Resolve all {{ expression }} references in the template.\n */\nfunction resolveExpressions(tpl: string, context: RenderContext): string {\n return tpl.replace(/\\{\\{[-\\s]*(.*?)[-\\s]*\\}\\}/g, (_match, expr: string) => {\n const trimmed = expr.trim();\n\n // Handle filters: {{ value|filter }}\n const filterParts = trimmed.split(\"|\");\n const path = filterParts[0].trim();\n\n let value = resolvePath(context, path);\n\n // Apply basic filters\n for (let i = 1; i < filterParts.length; i++) {\n value = applyFilter(value, filterParts[i].trim());\n }\n\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"object\") return JSON.stringify(value);\n // Strip literal \\n sequences that AI sometimes puts in field defaults\n let str = String(value);\n str = str.replace(/\\\\n/g, \" \").replace(/\\n/g, \" \");\n return str;\n });\n}\n\n/**\n * Clean up any remaining HubL tags that weren't handled.\n */\nfunction cleanupRemaining(tpl: string): string {\n // Remove any remaining {% ... %} tags\n tpl = tpl.replace(/\\{%.*?%\\}/gs, \"\");\n // Remove any remaining {{ ... }} that reference unknown paths\n tpl = tpl.replace(/\\{\\{.*?\\}\\}/gs, \"\");\n return tpl;\n}\n\n/**\n * Resolve an iterable expression for {% for %} loops.\n * Handles dotted paths (module.services) and range(start, end) calls.\n */\nfunction resolveIterable(expr: string, context: RenderContext): unknown {\n // Handle range(start, end) — with possible filter on args\n const rangeMatch = expr.match(/^range\\(\\s*(.+?)\\s*,\\s*(.+?)\\s*\\)$/);\n if (rangeMatch) {\n const start = resolveNumericArg(rangeMatch[1], context);\n const end = resolveNumericArg(rangeMatch[2], context);\n const arr: number[] = [];\n for (let i = start; i < end; i++) arr.push(i);\n return arr;\n }\n\n // Handle split('...') filter: \"value|split('\\n')\"\n const splitMatch = expr.match(/^(.+?)\\|split\\(['\"](.+?)['\"]\\)$/);\n if (splitMatch) {\n const val = resolvePath(context, splitMatch[1].trim());\n if (typeof val === \"string\") return val.split(splitMatch[2]);\n return [];\n }\n\n return resolvePath(context, expr);\n}\n\n/**\n * Resolve a numeric argument that may be a literal, a path, or a path|filter.\n */\nfunction resolveNumericArg(arg: string, context: RenderContext): number {\n const trimmed = arg.trim();\n\n // Apply filters (e.g. \"item.rating|int\")\n const filterParts = trimmed.split(\"|\");\n const path = filterParts[0].trim();\n\n // Literal number\n if (!isNaN(Number(path))) return Number(path);\n\n // Path lookup\n let value = resolvePath(context, path);\n for (let i = 1; i < filterParts.length; i++) {\n value = applyFilter(value, filterParts[i].trim());\n }\n return Number(value) || 0;\n}\n\n/**\n * Resolve a dot-path expression against a context object.\n * E.g. \"module.styles.bg_color.color\" → context.module.styles.bg_color.color\n */\nfunction resolvePath(context: RenderContext, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = context;\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n/**\n * Evaluate a simple condition expression.\n * Supports: path truthiness, \"not path\", \"path == value\", \"path != value\"\n */\nfunction evaluateCondition(expr: string, context: RenderContext): boolean {\n const trimmed = expr.trim();\n\n // Handle \"not\" prefix\n if (trimmed.startsWith(\"not \")) {\n return !evaluateCondition(trimmed.slice(4), context);\n }\n\n // Handle \"and\" / \"or\"\n if (trimmed.includes(\" and \")) {\n return trimmed.split(\" and \").every((part) => evaluateCondition(part, context));\n }\n if (trimmed.includes(\" or \")) {\n return trimmed.split(\" or \").some((part) => evaluateCondition(part, context));\n }\n\n // Handle comparison operators\n const eqMatch = trimmed.match(/^(.+?)\\s*(==|!=|>=|<=|>|<)\\s*(.+)$/);\n if (eqMatch) {\n const left = resolvePath(context, eqMatch[1].trim());\n const operator = eqMatch[2];\n let right: unknown = eqMatch[3].trim();\n\n // Parse right side: string literal, number, or path\n if (\n (typeof right === \"string\" && right.startsWith('\"') && right.endsWith('\"')) ||\n (typeof right === \"string\" && right.startsWith(\"'\") && right.endsWith(\"'\"))\n ) {\n right = (right as string).slice(1, -1);\n } else if (!isNaN(Number(right))) {\n right = Number(right);\n } else {\n right = resolvePath(context, right as string);\n }\n\n switch (operator) {\n case \"==\": return left == right;\n case \"!=\": return left != right;\n case \">\": return Number(left) > Number(right);\n case \"<\": return Number(left) < Number(right);\n case \">=\": return Number(left) >= Number(right);\n case \"<=\": return Number(left) <= Number(right);\n }\n }\n\n // Simple truthiness\n const value = resolvePath(context, trimmed);\n return isTruthy(value);\n}\n\n/**\n * Check HubL-style truthiness (empty strings and 0 are falsy).\n */\nfunction isTruthy(value: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (value === \"\") return false;\n if (value === 0) return false;\n if (value === false) return false;\n if (Array.isArray(value) && value.length === 0) return false;\n return true;\n}\n\n/**\n * Apply a basic HubL filter.\n */\nfunction applyFilter(value: unknown, filter: string): unknown {\n const str = value === null || value === undefined ? \"\" : String(value);\n\n // Handle filters with arguments: truncate(100), default(\"fallback\")\n const argMatch = filter.match(/^(\\w+)\\((.*)\\)$/);\n const filterName = argMatch ? argMatch[1] : filter;\n const filterArg = argMatch ? argMatch[2].replace(/^[\"']|[\"']$/g, \"\") : undefined;\n\n switch (filterName) {\n case \"escape\":\n case \"e\":\n return str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n case \"lower\":\n return str.toLowerCase();\n case \"upper\":\n return str.toUpperCase();\n case \"capitalize\":\n return str.charAt(0).toUpperCase() + str.slice(1);\n case \"trim\":\n return str.trim();\n case \"truncate\":\n if (filterArg) {\n const len = parseInt(filterArg, 10);\n return str.length > len ? str.slice(0, len) + \"...\" : str;\n }\n return str;\n case \"default\":\n return isTruthy(value) ? value : (filterArg ?? \"\");\n case \"length\":\n if (Array.isArray(value)) return value.length;\n return str.length;\n case \"join\":\n if (Array.isArray(value)) return value.join(filterArg ?? \", \");\n return str;\n case \"int\":\n case \"float\":\n return Number(str) || 0;\n case \"abs\":\n return Math.abs(Number(str));\n case \"round\":\n return Math.round(Number(str));\n default:\n // Unknown filter — pass through\n return value;\n }\n}\n","/**\n * Preview builder — assembles rendered HubL modules into a full HTML page.\n */\n\nimport {\n renderHubL,\n buildContextFromFields,\n assemblePreview,\n type FieldDef,\n} from \"../hubl/renderer.js\";\nimport { getSession, getOrderedModules } from \"./session.js\";\n\n/**\n * Extract CSS custom property values from shared CSS.\n * Returns defaults for dark theme if properties aren't found.\n */\nfunction extractThemeColors(sharedCss: string | undefined): {\n bg: string;\n surface: string;\n text: string;\n textMuted: string;\n border: string;\n} {\n if (!sharedCss) {\n return { bg: \"#0f0f14\", surface: \"#1a1a20\", text: \"#ffffff\", textMuted: \"#666\", border: \"#333\" };\n }\n\n const extract = (names: string[], fallback: string): string => {\n for (const name of names) {\n // Match --name: #hex or --name: rgb(...) etc.\n const re = new RegExp(`${name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*:\\\\s*([^;})]+)`, \"i\");\n const m = sharedCss.match(re);\n if (m) return m[1].trim();\n }\n return fallback;\n };\n\n const bg = extract([\"--bg\", \"--background\", \"--color-bg\", \"--bg-primary\", \"--body-bg\"], \"#0f0f14\");\n const surface = extract([\"--surface\", \"--bg-secondary\", \"--card-bg\", \"--color-surface\"], bg);\n const text = extract([\"--text\", \"--color-text\", \"--text-primary\", \"--fg\", \"--foreground\"], \"#ffffff\");\n const textMuted = extract([\"--text-muted\", \"--text-secondary\", \"--muted\", \"--color-text-muted\"], \"#666\");\n const border = extract([\"--border\", \"--border-color\", \"--color-border\"], \"#333\");\n\n return { bg, surface, text, textMuted, border };\n}\n\n/**\n * Build a full preview HTML page from the current session state.\n * Each module's HubL template is rendered with its fields.json defaults,\n * then assembled with shared CSS/JS into a complete page.\n */\nexport function buildPreviewHtml(): string {\n const session = getSession();\n if (!session) {\n return welcomePreview();\n }\n\n const modules = getOrderedModules();\n const moduleOrder = session.moduleOrder || [];\n\n // Nothing to show yet — no modules and no pending order\n if (modules.length === 0 && moduleOrder.length === 0) {\n return welcomePreview();\n }\n\n const renderedModules: string[] = [];\n const moduleCssArray: string[] = [];\n const moduleJsArray: string[] = [];\n const renderedNames = new Set<string>();\n\n for (const mod of modules) {\n // Skip template-like content that was accidentally stored as a module\n if (mod.moduleHtml.includes(\"dnd_area\") || mod.moduleHtml.includes(\"extends \")) {\n continue;\n }\n\n // Build context from fields.json defaults\n let context: { module: Record<string, unknown> };\n try {\n const fields: FieldDef[] = JSON.parse(mod.fieldsJson);\n context = { module: buildContextFromFields(fields) };\n } catch {\n context = { module: {} };\n }\n\n // Render HubL template with context\n const rendered = renderHubL(mod.moduleHtml, context);\n\n // Wrap each module in a container with id + data attribute for anchor links\n const anchorId = mod.moduleName.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n renderedModules.push(\n `<div class=\"vibespot-module\" id=\"${anchorId}\" data-module=\"${mod.moduleName}\">${rendered}</div>`\n );\n renderedNames.add(mod.moduleName);\n\n if (mod.moduleCss) moduleCssArray.push(mod.moduleCss);\n if (mod.moduleJs) moduleJsArray.push(mod.moduleJs);\n }\n\n // Render skeleton placeholders for modules in moduleOrder that aren't generated yet\n const theme = extractThemeColors(session.sharedCss);\n for (const name of moduleOrder) {\n if (!renderedNames.has(name)) {\n const anchorId = name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n renderedModules.push(\n `<div class=\"vibespot-module vibespot-module--pending\" id=\"${anchorId}\" data-module=\"${name}\">\n <div class=\"vibespot-placeholder\">\n <div class=\"vibespot-placeholder__name\">${name}</div>\n </div>\n </div>`\n );\n }\n }\n\n // Add placeholder CSS for pending modules — uses theme colors\n const placeholderCss = `body{background:${theme.bg}}.vibespot-placeholder{display:flex;align-items:center;justify-content:center;min-height:200px;padding:3rem;background:${theme.surface};border:1px dashed ${theme.border};border-radius:12px;margin:1rem 0}.vibespot-placeholder__name{font-size:1.5rem;font-weight:600;font-family:system-ui,sans-serif;color:${theme.textMuted};letter-spacing:.5px;animation:vp-fade 2s ease-in-out infinite}@keyframes vp-fade{0%,100%{opacity:.3}50%{opacity:.8}}`;\n\n return assemblePreview({\n renderedModules,\n sharedCss: session.sharedCss,\n moduleCssArray: [placeholderCss, ...moduleCssArray],\n sharedJs: session.sharedJs,\n moduleJsArray,\n });\n}\n\n/**\n * Static welcome screen — shown before first generation.\n */\nfunction welcomePreview(): 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\">\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n background: #0f0f14;\n color: #888;\n }\n .welcome {\n text-align: center;\n padding: 2rem;\n }\n .welcome__wave {\n font-size: 4rem;\n margin-bottom: 1.2rem;\n opacity: 0.4;\n animation: float 3s ease-in-out infinite;\n }\n @keyframes float {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-6px); }\n }\n .welcome__title {\n font-size: 1.4rem;\n font-weight: 600;\n color: #bbb;\n letter-spacing: 0.5px;\n margin-bottom: 0.4rem;\n }\n .welcome__sub {\n font-size: 1rem;\n color: #666;\n font-weight: 300;\n }\n</style>\n</head>\n<body>\n<div class=\"welcome\">\n <div class=\"welcome__wave\">\\u224B</div>\n <div class=\"welcome__title\">vibeSpot</div>\n <div class=\"welcome__sub\">Build Something Great</div>\n</div>\n</body>\n</html>`;\n}\n\n/**\n * Build a preview HTML page for a single module (used by the dashboard module library).\n */\nexport function buildModulePreviewHtml(moduleName: string): string {\n const session = getSession();\n if (!session) return \"\";\n\n // Find the module across all templates\n let mod: typeof session.modules[0] | undefined;\n for (const tpl of session.templates) {\n mod = tpl.modules.find((m) => m.moduleName === moduleName);\n if (mod) break;\n }\n // Fallback: check flat modules array\n if (!mod) {\n mod = session.modules.find((m) => m.moduleName === moduleName);\n }\n if (!mod) return \"\";\n\n let context: { module: Record<string, unknown> };\n try {\n const fields: FieldDef[] = JSON.parse(mod.fieldsJson);\n context = { module: buildContextFromFields(fields) };\n } catch {\n context = { module: {} };\n }\n\n const rendered = renderHubL(mod.moduleHtml, context);\n\n return assemblePreview({\n renderedModules: [\n `<div class=\"vibespot-module\" data-module=\"${mod.moduleName}\">${rendered}</div>`,\n ],\n sharedCss: session.sharedCss,\n moduleCssArray: mod.moduleCss ? [mod.moduleCss] : [],\n sharedJs: session.sharedJs,\n moduleJsArray: mod.moduleJs ? [mod.moduleJs] : [],\n });\n}\n\n// Note: The generating screen (spinner + rotating messages) is now\n// rendered client-side in preview.js to avoid an extra server round-trip.\n","/**\n * Structured logging helper.\n * Writes to both console and a rotating log file at ~/.vibespot/logs/.\n * Log files are named vibespot-YYYY-MM-DD.log and auto-pruned after 7 days.\n */\n\nimport { appendFileSync, mkdirSync, readdirSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst LOG_DIR = join(homedir(), \".vibespot\", \"logs\");\nconst MAX_AGE_DAYS = 7;\n\nlet logDirReady = false;\nlet pruned = false;\n\nfunction ensureLogDir(): void {\n if (logDirReady) return;\n try {\n mkdirSync(LOG_DIR, { recursive: true });\n logDirReady = true;\n } catch {\n // If we can't create the dir, file logging silently disabled\n }\n}\n\nfunction pruneOldLogs(): void {\n if (pruned) return;\n pruned = true;\n try {\n const cutoff = Date.now() - MAX_AGE_DAYS * 86400_000;\n for (const f of readdirSync(LOG_DIR)) {\n if (!f.startsWith(\"vibespot-\") || !f.endsWith(\".log\")) continue;\n // Extract date from filename: vibespot-YYYY-MM-DD.log\n const dateStr = f.slice(9, 19);\n const ts = new Date(dateStr).getTime();\n if (ts && ts < cutoff) {\n try { unlinkSync(join(LOG_DIR, f)); } catch { /* ignore */ }\n }\n }\n } catch { /* ignore */ }\n}\n\nfunction todayFile(): string {\n const d = new Date();\n const date = d.toISOString().slice(0, 10);\n return join(LOG_DIR, `vibespot-${date}.log`);\n}\n\nfunction timestamp(): string {\n return new Date().toISOString().slice(11, 23); // HH:mm:ss.SSS\n}\n\nfunction writeToFile(level: string, line: string): void {\n ensureLogDir();\n if (!logDirReady) return;\n if (!pruned) pruneOldLogs();\n try {\n appendFileSync(todayFile(), `${timestamp()} ${level} ${line}\\n`);\n } catch { /* ignore — don't break the app over logging */ }\n}\n\nexport const log = {\n info(context: string, message: string, data?: Record<string, unknown>): void {\n const line = data\n ? `[${context}] ${message} ${JSON.stringify(data)}`\n : `[${context}] ${message}`;\n console.log(line);\n writeToFile(\"INFO\", line);\n },\n\n warn(context: string, message: string, data?: Record<string, unknown>): void {\n const line = data\n ? `[${context}] ${message} ${JSON.stringify(data)}`\n : `[${context}] ${message}`;\n console.warn(line);\n writeToFile(\"WARN\", line);\n },\n\n error(context: string, message: string, err?: unknown): void {\n const errMsg = err instanceof Error ? err.message : err ? String(err) : \"\";\n const line = errMsg\n ? `[${context}] ${message}: ${errMsg}`\n : `[${context}] ${message}`;\n console.error(line);\n writeToFile(\"ERROR\", line);\n },\n};\n","/**\n * AI response parser — extracts vibespot-modules JSON from AI responses.\n */\n\nimport { updateModules } from \"./session.js\";\nimport type { ModuleFiles } from \"../ai/engine.js\";\nimport { log } from \"./log.js\";\n\n/**\n * Try JSON.parse, and if it fails, attempt to repair common AI JSON issues\n * (unescaped quotes inside string values) and retry.\n */\nexport function tryParseJSON(raw: string): unknown | null {\n try {\n return JSON.parse(raw);\n } catch {\n // Fall through to repair\n }\n\n let repaired = raw;\n let lastPos = -1;\n for (let attempt = 0; attempt < 20; attempt++) {\n try {\n return JSON.parse(repaired);\n } catch (err) {\n if (!(err instanceof SyntaxError)) return null;\n const posMatch = /position (\\d+)/.exec(err.message);\n if (!posMatch) return null;\n const pos = parseInt(posMatch[1], 10);\n if (pos <= lastPos) return null;\n lastPos = pos;\n const searchStart = Math.max(0, pos - 5);\n const nearSlice = repaired.slice(searchStart, pos + 1);\n const lastQuote = nearSlice.lastIndexOf('\"');\n if (lastQuote === -1) return null;\n const absPos = searchStart + lastQuote;\n if (absPos > 0 && repaired[absPos - 1] === \"\\\\\") return null;\n repaired = repaired.slice(0, absPos) + '\\\\\"' + repaired.slice(absPos + 1);\n }\n }\n return null;\n}\n\n/**\n * Attempt to salvage a truncated JSON response by finding complete module objects.\n */\nexport function tryRepairTruncatedJSON(raw: string): Record<string, unknown> | null {\n const modulesIdx = raw.indexOf('\"modules\"');\n if (modulesIdx === -1) return null;\n\n const arrayStart = raw.indexOf(\"[\", modulesIdx);\n if (arrayStart === -1) return null;\n\n let lastCompleteModule = -1;\n let braceDepth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = arrayStart + 1; i < raw.length; i++) {\n const ch = raw[i];\n if (escaped) { escaped = false; continue; }\n if (ch === \"\\\\\") { escaped = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n\n if (ch === \"{\") braceDepth++;\n if (ch === \"}\") {\n braceDepth--;\n if (braceDepth === 0) {\n lastCompleteModule = i;\n }\n }\n }\n\n if (lastCompleteModule === -1) return null;\n\n const upToLastModule = raw.slice(0, lastCompleteModule + 1);\n const repaired = upToLastModule + \"]}\";\n\n const jsonStr = repaired.trimStart().startsWith(\"{\") ? repaired : \"{\" + repaired;\n return tryParseJSON(jsonStr) as Record<string, unknown> | null;\n}\n\n/**\n * Convert a raw module object from AI JSON into a ModuleFiles entry.\n */\nfunction toModuleFiles(m: Record<string, unknown>): ModuleFiles {\n return {\n moduleName: String(m.moduleName || \"\"),\n fieldsJson: typeof m.fieldsJson === \"string\"\n ? m.fieldsJson\n : JSON.stringify(m.fieldsJson, null, 2),\n metaJson: typeof m.metaJson === \"string\"\n ? m.metaJson\n : JSON.stringify(m.metaJson, null, 2),\n moduleHtml: String(m.moduleHtml || \"\"),\n moduleCss: String(m.moduleCss || \"\"),\n moduleJs: m.moduleJs ? String(m.moduleJs) : undefined,\n };\n}\n\n/**\n * Parse vibespot-modules JSON blocks from an AI response and update the session.\n */\nexport function parseAndApplyModules(\n response: string,\n onWarning?: (warning: string) => void\n): void {\n let modulesApplied = false;\n let match;\n\n // Look for ```vibespot-modules ... ``` blocks\n const blockPattern = /```vibespot-modules\\s*\\n?([\\s\\S]*?)```/g;\n\n while ((match = blockPattern.exec(response)) !== null) {\n try {\n log.info(\"parse\", \"Found vibespot-modules block\", { length: match[1].length });\n const data = tryParseJSON(match[1]);\n if (!data || typeof data !== \"object\") {\n log.warn(\"parse\", \"tryParseJSON returned non-object\", { result: typeof data });\n throw new Error(\"Invalid JSON after repair\");\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules)) {\n updateModules({\n modules: obj.modules.map((m: Record<string, unknown>) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n }\n } catch (err) {\n log.warn(\"parse\", \"Failed to parse vibespot-modules block\", { error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n // Also try to find standalone JSON that looks like module data\n if (!modulesApplied) {\n const jsonPattern = /```(?:json)?\\s*\\n([\\s\\S]*?)```/g;\n while ((match = jsonPattern.exec(response)) !== null) {\n if (!match[1].includes('\"modules\"')) continue;\n try {\n const data = tryParseJSON(match[1]);\n if (!data || typeof data !== \"object\") throw new Error(\"Invalid JSON after repair\");\n const obj = data as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules)) {\n updateModules({\n modules: obj.modules.map((m: Record<string, unknown>) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n }\n } catch (err) {\n log.warn(\"parse\", \"Failed to parse JSON module block\", { error: err instanceof Error ? err.message : String(err) });\n }\n }\n }\n\n // Handle truncated responses (hit max_tokens — unclosed code fence)\n if (!modulesApplied) {\n const fenceCount = (response.match(/```/g) || []).length;\n if (fenceCount % 2 !== 0 && response.includes('\"modules\"')) {\n log.info(\"parse\", \"Detected truncated response (odd fence count), attempting salvage\");\n const lastFenceIdx = response.lastIndexOf(\"```\");\n let truncated = response.slice(lastFenceIdx + 3);\n truncated = truncated.replace(/^[\\w-]*\\s*\\n?/, \"\");\n const salvaged = tryRepairTruncatedJSON(truncated);\n if (salvaged) {\n const obj = salvaged as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules) && obj.modules.length > 0) {\n log.info(\"parse\", \"Salvaged modules from truncated response\", { count: obj.modules.length });\n updateModules({\n modules: (obj.modules as Record<string, unknown>[]).map((m) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n if (onWarning) {\n onWarning(\"Response was truncated — some modules may be incomplete. Try sending your request again for the full set.\");\n }\n }\n }\n }\n }\n\n // Warn user if the response looked like it should contain modules but parsing failed\n if (!modulesApplied) {\n log.info(\"parse\", \"No modules applied\", {\n responseLength: response.length,\n hasVibespot: response.includes(\"vibespot-modules\"),\n hasModules: response.includes('\"modules\"'),\n fenceCount: (response.match(/```/g) || []).length,\n });\n const hasModuleRef = response.includes(\"vibespot-modules\") || response.includes('\"modules\"');\n const describesProse = /\\bmodule|modul/i.test(response) &&\n (/\\bcreated?\\b|\\berstellt\\b|\\bgenerat/i.test(response) || /\\|.*\\|.*\\|/m.test(response));\n\n if (hasModuleRef || describesProse) {\n const msg = hasModuleRef\n ? \"Module changes could not be applied — the AI response contained invalid JSON. Try sending your request again.\"\n : \"The AI described modules but did not include the required structured data. Try sending your request again.\";\n log.warn(\"parse\", msg);\n if (onWarning) {\n onWarning(msg);\n }\n }\n }\n}\n","/**\n * AI prompt construction — system prompts, state context, message building.\n */\n\nimport { getConversionGuide, getDesignGuide, getContentGuide, getHubspotRules, getPageTypeGuide, getHumanifyGuide } from \"../ai/prompts.js\";\nimport {\n getSession,\n getActiveTemplate,\n getModuleLibrary,\n} from \"./session.js\";\nimport type { UploadedFileContext } from \"./routes/upload-files.js\";\n\n// Multimodal content block types (compatible with Anthropic/OpenAI APIs)\nexport type ContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"image\"; source: { type: \"base64\"; media_type: string; data: string } };\n\nexport type MultimodalMessage = {\n role: \"user\" | \"assistant\";\n content: string | ContentBlock[];\n};\n\n/**\n * Get the active template's page type and brand assets from the session.\n */\nexport function getPromptContext(): { pageType?: string; brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean } } {\n const session = getSession();\n if (!session) return {};\n const tpl = getActiveTemplate();\n return {\n pageType: tpl?.pageType,\n brandAssets: session.brandAssets,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic prompt caching — system prompt as block array with cache_control\n// ---------------------------------------------------------------------------\n\nexport interface SystemPromptBlock {\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n}\n\n/**\n * Build the system prompt as an array of blocks for Anthropic API engines.\n * Static reference guides are marked with cache_control for prompt caching.\n */\nexport function buildVibeSystemPromptBlocks(\n conversionGuide: string,\n themeName: string,\n editMode: boolean = false,\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): SystemPromptBlock[] {\n // Block 1: Core instructions (semi-static — varies by themeName)\n const core = buildCoreInstructions(themeName, editMode);\n const blocks: SystemPromptBlock[] = [{ type: \"text\", text: core }];\n\n // Block 2: Reference guides — CACHED (identical across all calls)\n if (editMode) {\n const guides = `## HubSpot CMS Rules\\n${getHubspotRules()}\\n\\n## Conversion Guide Reference\\n${conversionGuide}`;\n blocks.push({ type: \"text\", text: guides, cache_control: { type: \"ephemeral\" } });\n } else {\n const guides = `## Design Quality\n- Use modern, clean design with proper spacing and typography\n- Include responsive CSS (mobile breakpoint at 767px)\n- Add scroll animation classes where appropriate\n- Use CSS custom properties for the design system\n- Make content editable through fields.json (headlines, text, colors, images, links)\n\n## Scroll Animation CSS Fallback (IMPORTANT)\nWhen using scroll-animate classes (opacity: 0 → visible), you MUST include a CSS-only fallback animation in sharedCss that auto-reveals elements after a delay. This ensures content is visible even if the JS file fails to load:\n\\`\\`\\`css\n@keyframes scroll-animate-fallback {\n to { opacity: 1; transform: none; }\n}\n.scroll-animate {\n animation: scroll-animate-fallback 0.1s 3s forwards;\n}\n.scroll-animate.visible {\n animation: none;\n}\n\\`\\`\\`\nThis makes elements appear after 3 seconds if JS never adds the .visible class. Once JS runs normally and adds .visible, the animation is cancelled.\n\n## Design Guide\n${getDesignGuide()}\n\n## Content & Copywriting Guide\n${getContentGuide()}\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}`;\n blocks.push({ type: \"text\", text: guides, cache_control: { type: \"ephemeral\" } });\n }\n\n // Block 3: Dynamic content (changes per session/template — NOT cached)\n const dynamic = buildDynamicSection(pageType, brandAssets);\n if (dynamic) {\n blocks.push({ type: \"text\", text: dynamic });\n }\n\n // Block 4: Format reminder\n blocks.push({\n type: \"text\",\n text: `## REMINDER — Output Format (CRITICAL)\\nYour response MUST contain a \\`\\`\\`vibespot-modules code block with the full JSON. Without this block, no modules will be created. Do NOT respond with only text, tables, or descriptions. The JSON block is mandatory.`,\n });\n\n return blocks;\n}\n\n/** Build the dynamic (per-session) prompt sections. */\nfunction buildDynamicSection(\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): string {\n const parts: string[] = [];\n\n if (pageType) {\n const section = getPageTypeGuide(pageType);\n if (section) parts.push(`## Page Type Context\\n${section}`);\n }\n\n if (brandAssets?.styleguide) {\n parts.push(`## Brand Style Guide\\n${brandAssets.styleguide}`);\n }\n if (brandAssets?.brandvoice) {\n parts.push(`## Brand Voice\\n${brandAssets.brandvoice}`);\n }\n if (brandAssets?.humanify !== false) {\n const humanifyGuide = getHumanifyGuide();\n if (humanifyGuide) parts.push(`## Anti-AI Copy Rules (Humanify)\\n${humanifyGuide}`);\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/** Build the core instructions block (reused by both string and block builders). */\nfunction buildCoreInstructions(themeName: string, editMode: boolean): string {\n return `You are vibeSpot, an AI that builds HubSpot CMS landing pages from natural language descriptions.\n\n## Your Role\nYou generate native HubSpot CMS modules directly from user descriptions. Every module you create is immediately compatible with HubSpot's drag-and-drop page editor.\n\n## Output Format — CRITICAL\nYou MUST include a \\`\\`\\`vibespot-modules code block with ALL module data as JSON. This is the ONLY way modules get created. A text summary, table, or description of modules does NOT work — you must output the actual JSON.\n\n\\`\\`\\`vibespot-modules\n{\n \"modules\": [\n {\n \"moduleName\": \"Hero\",\n \"fieldsJson\": \"...\",\n \"metaJson\": \"...\",\n \"moduleHtml\": \"...\",\n \"moduleCss\": \"...\",\n \"moduleJs\": null\n }\n ],\n \"sharedCss\": \"...\",\n \"sharedJs\": \"...\"\n}\n\\`\\`\\`\n\nNEVER respond with only a text summary. The vibespot-modules JSON block is mandatory.\n\n## Rules\n- fieldsJson, metaJson must be valid JSON strings\n- moduleHtml uses HubL template syntax ({{ module.field_name }})\n- moduleCss is vanilla CSS (no Tailwind, no Sass)\n- moduleJs is optional vanilla JS (wrapped in IIFE)\n- NEVER use CDN imports (@import url(), <link> to external CDNs like Google Fonts, cdnjs, unpkg, jsdelivr)\n- For fonts, use system font stacks with good fallbacks. Define them as CSS custom properties in sharedCss:\n --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-mono: 'SF Mono', SFMono-Regular, Consolas, monospace;\n- All assets must be self-contained — no external HTTP requests in CSS or HTML\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER put literal \\\\n newline sequences in field default values — use plain text without line breaks\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- All CSS classes must use a unique prefix \"${themeName}-\" to avoid theme conflicts\n- Use BEM naming: ${themeName}-module__element--modifier\n- metaJson must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 } and iterate with {% for %}\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder image\", \"width\": 800, \"height\": 600 }\n\n## Images & Media\n- Users can upload images that get placed in the theme's assets/ folder automatically\n- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url(\"${themeName}/assets/filename.ext\") }}\n- IMPORTANT: get_asset_url() paths must include the theme name prefix \"${themeName}/\" because HubSpot resolves from the Design Manager root\n- For background images with uploaded assets: style=\"background-image: url('{{ get_asset_url(\\\\\"${themeName}/assets/filename.ext\\\\\") }}')\"\n- For images without an uploaded asset, use image fields (type \"image\") with placehold.co defaults\n- ALWAYS use image fields (type \"image\") so users can swap images in the page editor\n- Use placehold.co URLs as defaults so the preview looks complete (e.g. https://placehold.co/800x600/1a1a2e/ffffff?text=Hero+Image)\n- In module.html, render images with: <img src=\"{{ module.field_name.src }}\" alt=\"{{ module.field_name.alt }}\" width=\"{{ module.field_name.width }}\" height=\"{{ module.field_name.height }}\">\n- For background images in CSS, use inline styles: style=\"background-image: url('{{ module.field_name.src }}')\"\n- Size placeholders appropriately for their context (hero: 1920x800, cards: 600x400, icons: 200x200, avatars: 150x150)\n- If the user's intent is ambiguous (design reference vs page asset), ask them to clarify\n\n## Navigation & Anchor Links\n- For anchor links, add an id attribute directly on the module's root element in module.html:\n <section id=\"pricing\" class=\"...\"> (NOT on an external wrapper — HubSpot's dnd system strips those)\n- The id should be the moduleName lowercased with spaces replaced by hyphens (e.g. \"Pricing Cards\" → id=\"pricing-cards\")\n- For navigation/menu modules, use anchor links that match these ids: e.g. href=\"#features\"\n- Always include smooth scrolling behavior in navigation link clicks\n- For nav modules, make menu items editable via a repeater group with \"label\" (text) and \"anchor\" (text) fields` +\n (editMode\n ? `\n\n## When modifying existing modules\nThe current template's modules are listed in page order in the user message. This sequence forms the page narrative.\n\n- **Modify**: When the user references an existing module by name or describes changes to existing content, update that module's code. Keep the moduleName unchanged.\n- **Add**: When the user asks for a new section, create a new module and insert it at the narratively correct position. Consider the page flow: navigation → hero → content sections → social proof → CTA → footer.\n- **Rearrange**: When the user asks to reorder sections, include a \"moduleOrder\" array in the vibespot-modules JSON with the new sequence of module names.\n- **Remove**: When the user asks to remove a section, omit it from the output.\n- **Preserve**: Always include ALL modules you want to keep (modified + unchanged) in your output. Modules omitted from the output will be removed from the page.\n- **Design consistency**: Match the existing theme's design language — reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`\n : \"\");\n}\n\n/**\n * Build the system prompt for vibe coding mode.\n */\nexport function buildVibeSystemPrompt(\n conversionGuide: string,\n themeName: string,\n editMode: boolean = false,\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean }\n): string {\n const core = `You are vibeSpot, an AI that builds HubSpot CMS landing pages from natural language descriptions.\n\n## Your Role\nYou generate native HubSpot CMS modules directly from user descriptions. Every module you create is immediately compatible with HubSpot's drag-and-drop page editor.\n\n## Output Format — CRITICAL\nYou MUST include a \\`\\`\\`vibespot-modules code block with ALL module data as JSON. This is the ONLY way modules get created. A text summary, table, or description of modules does NOT work — you must output the actual JSON.\n\n\\`\\`\\`vibespot-modules\n{\n \"modules\": [\n {\n \"moduleName\": \"Hero\",\n \"fieldsJson\": \"...\",\n \"metaJson\": \"...\",\n \"moduleHtml\": \"...\",\n \"moduleCss\": \"...\",\n \"moduleJs\": null\n }\n ],\n \"sharedCss\": \"...\",\n \"sharedJs\": \"...\"\n}\n\\`\\`\\`\n\nNEVER respond with only a text summary. The vibespot-modules JSON block is mandatory.\n\n## Rules\n- fieldsJson, metaJson must be valid JSON strings\n- moduleHtml uses HubL template syntax ({{ module.field_name }})\n- moduleCss is vanilla CSS (no Tailwind, no Sass)\n- moduleJs is optional vanilla JS (wrapped in IIFE)\n- NEVER use CDN imports (@import url(), <link> to external CDNs like Google Fonts, cdnjs, unpkg, jsdelivr)\n- For fonts, use system font stacks with good fallbacks. Define them as CSS custom properties in sharedCss:\n --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-mono: 'SF Mono', SFMono-Regular, Consolas, monospace;\n- All assets must be self-contained — no external HTTP requests in CSS or HTML\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER put literal \\\\n newline sequences in field default values — use plain text without line breaks\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- All CSS classes must use a unique prefix \"${themeName}-\" to avoid theme conflicts\n- Use BEM naming: ${themeName}-module__element--modifier\n- metaJson must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 } and iterate with {% for %}\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder image\", \"width\": 800, \"height\": 600 }\n\n## Images & Media\n- Users can upload images that get placed in the theme's assets/ folder automatically\n- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url(\"${themeName}/assets/filename.ext\") }}\n- IMPORTANT: get_asset_url() paths must include the theme name prefix \"${themeName}/\" because HubSpot resolves from the Design Manager root\n- For background images with uploaded assets: style=\"background-image: url('{{ get_asset_url(\\\\\"${themeName}/assets/filename.ext\\\\\") }}')\"\n- For images without an uploaded asset, use image fields (type \"image\") with placehold.co defaults\n- ALWAYS use image fields (type \"image\") so users can swap images in the page editor\n- Use placehold.co URLs as defaults so the preview looks complete (e.g. https://placehold.co/800x600/1a1a2e/ffffff?text=Hero+Image)\n- In module.html, render images with: <img src=\"{{ module.field_name.src }}\" alt=\"{{ module.field_name.alt }}\" width=\"{{ module.field_name.width }}\" height=\"{{ module.field_name.height }}\">\n- For background images in CSS, use inline styles: style=\"background-image: url('{{ module.field_name.src }}')\"\n- Size placeholders appropriately for their context (hero: 1920x800, cards: 600x400, icons: 200x200, avatars: 150x150)\n- If the user's intent is ambiguous (design reference vs page asset), ask them to clarify\n\n## Navigation & Anchor Links\n- For anchor links, add an id attribute directly on the module's root element in module.html:\n <section id=\"pricing\" class=\"...\"> (NOT on an external wrapper — HubSpot's dnd system strips those)\n- The id should be the moduleName lowercased with spaces replaced by hyphens (e.g. \"Pricing Cards\" → id=\"pricing-cards\")\n- For navigation/menu modules, use anchor links that match these ids: e.g. href=\"#features\"\n- Always include smooth scrolling behavior in navigation link clicks\n- For nav modules, make menu items editable via a repeater group with \"label\" (text) and \"anchor\" (text) fields\n\n## When modifying existing modules\nThe current template's modules are listed in page order in the user message. This sequence forms the page narrative.\n\n- **Modify**: When the user references an existing module by name or describes changes to existing content, update that module's code. Keep the moduleName unchanged.\n- **Add**: When the user asks for a new section, create a new module and insert it at the narratively correct position. Consider the page flow: navigation → hero → content sections → social proof → CTA → footer.\n- **Rearrange**: When the user asks to reorder sections, include a \"moduleOrder\" array in the vibespot-modules JSON with the new sequence of module names.\n- **Remove**: When the user asks to remove a section, omit it from the output.\n- **Preserve**: Always include ALL modules you want to keep (modified + unchanged) in your output. Modules omitted from the output will be removed from the page.\n- **Design consistency**: Match the existing theme's design language — reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`;\n\n const pageTypeSection = pageType ? getPageTypeGuide(pageType) : \"\";\n const pageTypePrompt = pageTypeSection ? `\\n\\n## Page Type Context\\n${pageTypeSection}` : \"\";\n\n // Brand assets are included in both creation and edit modes for design consistency\n let brandPrompt = \"\";\n if (brandAssets?.styleguide) {\n brandPrompt += `\\n\\n## Brand Style Guide\\n${brandAssets.styleguide}`;\n }\n if (brandAssets?.brandvoice) {\n brandPrompt += `\\n\\n## Brand Voice\\n${brandAssets.brandvoice}`;\n }\n if (brandAssets?.humanify !== false) {\n const humanifyGuide = getHumanifyGuide();\n if (humanifyGuide) {\n brandPrompt += `\\n\\n## Anti-AI Copy Rules (Humanify)\\n${humanifyGuide}`;\n }\n }\n\n const formatReminder = `\n\n## REMINDER — Output Format (CRITICAL)\nYour response MUST contain a \\`\\`\\`vibespot-modules code block with the full JSON. Without this block, no modules will be created. Do NOT respond with only text, tables, or descriptions. The JSON block is mandatory.`;\n\n if (editMode) {\n return core + pageTypePrompt + brandPrompt + `\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}` + formatReminder;\n }\n\n return core + pageTypePrompt + brandPrompt + `\n\n## Design Quality\n- Use modern, clean design with proper spacing and typography\n- Include responsive CSS (mobile breakpoint at 767px)\n- Add scroll animation classes where appropriate\n- Use CSS custom properties for the design system\n- Make content editable through fields.json (headlines, text, colors, images, links)\n\n## Scroll Animation CSS Fallback (IMPORTANT)\nWhen using scroll-animate classes (opacity: 0 → visible), you MUST include a CSS-only fallback animation in sharedCss that auto-reveals elements after a delay. This ensures content is visible even if the JS file fails to load:\n\\`\\`\\`css\n@keyframes scroll-animate-fallback {\n to { opacity: 1; transform: none; }\n}\n.scroll-animate {\n animation: scroll-animate-fallback 0.1s 3s forwards;\n}\n.scroll-animate.visible {\n animation: none;\n}\n\\`\\`\\`\nThis makes elements appear after 3 seconds if JS never adds the .visible class. Once JS runs normally and adds .visible, the animation is cancelled.\n\n## Design Guide\n${getDesignGuide()}\n\n## Content & Copywriting Guide\n${getContentGuide()}\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}` + formatReminder;\n}\n\n/**\n * Build a summary of the current module state for inclusion in user messages.\n */\nexport function buildStateContext(): string {\n const session = getSession()!;\n const parts: string[] = [];\n const modules = session.modules;\n const total = modules.length;\n\n if (total > 0) {\n // Page narrative summary — helps AI understand the page structure\n parts.push(\"\\n\\n## Page Narrative (module sequence)\\n\");\n parts.push(`This template has ${total} module${total === 1 ? \"\" : \"s\"} in this order:\\n`);\n for (let i = 0; i < total; i++) {\n parts.push(`${i + 1}. ${modules[i].moduleName}\\n`);\n }\n parts.push(`\\nWhen the user asks to modify this page, decide whether to MODIFY existing modules, ADD new ones at the right narrative position, REARRANGE the sequence, or REMOVE sections. Always include ALL modules you want to keep in your output.\\n`);\n\n // Detailed module state\n parts.push(\"\\n## Current Module State\\n\");\n for (let i = 0; i < total; i++) {\n const mod = modules[i];\n parts.push(`\\n### ${i + 1}/${total}: ${mod.moduleName}.module\\n`);\n parts.push(`**fields.json:**\\n\\`\\`\\`json\\n${mod.fieldsJson}\\n\\`\\`\\`\\n`);\n parts.push(`**module.html:**\\n\\`\\`\\`html\\n${mod.moduleHtml}\\n\\`\\`\\`\\n`);\n parts.push(`**module.css:**\\n\\`\\`\\`css\\n${mod.moduleCss}\\n\\`\\`\\`\\n`);\n if (mod.moduleJs) {\n parts.push(`**module.js:**\\n\\`\\`\\`js\\n${mod.moduleJs}\\n\\`\\`\\`\\n`);\n }\n }\n if (session.sharedCss) {\n parts.push(`\\n### Shared CSS\\n\\`\\`\\`css\\n${session.sharedCss}\\n\\`\\`\\`\\n`);\n }\n if (session.sharedJs) {\n parts.push(`\\n### Shared JS\\n\\`\\`\\`js\\n${session.sharedJs}\\n\\`\\`\\`\\n`);\n }\n }\n\n const library = getModuleLibrary();\n const currentModuleNames = new Set(session.modules.map((m) => m.moduleName));\n const otherModules = library.filter((e) => !currentModuleNames.has(e.module.moduleName));\n if (otherModules.length > 0) {\n parts.push(\"\\n\\n## Available modules in this theme (reusable)\\n\");\n for (const entry of otherModules) {\n parts.push(`- ${entry.module.moduleName} (used in: ${entry.usedIn.join(\", \")})\\n`);\n }\n parts.push(\"\\nThe user can ask to reuse any of these modules by name.\\n\");\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build the messages array with state context appended to the latest user message.\n * When fileContexts are provided, the last message uses multimodal content blocks.\n */\nexport function buildMessagesWithContext(\n userMessage: string,\n fileContexts?: UploadedFileContext[]\n): MultimodalMessage[] {\n const session = getSession()!;\n\n // Build history from session messages, but exclude the latest user message\n // if it matches the one we're about to send (it was already added to the\n // session by the WebSocket handler before generation starts).\n let history = session.messages.slice(-20);\n if (\n history.length > 0 &&\n history[history.length - 1].role === \"user\" &&\n history[history.length - 1].content === userMessage\n ) {\n history = history.slice(0, -1);\n }\n\n const messages: MultimodalMessage[] =\n history.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const stateContext = buildStateContext();\n\n // Build asset manifest if there are any uploaded assets in the session\n let assetManifest = \"\";\n if (session.assets?.length) {\n const imageAssets = session.assets.filter((a) => a.type === \"image\" && a.usage === \"asset\");\n if (imageAssets.length > 0) {\n assetManifest = `\\n\\n## Available Theme Assets\\nThese images are in the theme's assets/ folder. Reference them with get_asset_url(\"${session.themeName}/assets/filename\"):\\n${imageAssets.map((a) => `- ${a.filename} (${a.originalName}) → get_asset_url(\"${session.themeName}/assets/${a.filename}\")`).join(\"\\n\")}`;\n }\n }\n\n let textContent = userMessage;\n if (stateContext) textContent += `\\n\\n---\\n${stateContext}`;\n if (assetManifest) textContent += assetManifest;\n\n // Format reminder — placed at the end of user content so the model sees it\n // right before it starts generating. This combats the \"forgot the format\" problem.\n textContent += `\\n\\n---\\nRemember: respond with a \\`\\`\\`vibespot-modules JSON block containing ALL modules. No text-only responses.`;\n\n // Add document text from attachments\n const hasFiles = fileContexts && fileContexts.length > 0;\n if (hasFiles) {\n for (const fc of fileContexts) {\n if (fc.type === \"document\" && fc.extractedText) {\n textContent += `\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`;\n }\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n textContent += `\\n\\n[Uploaded image: ${fc.originalName} → available as get_asset_url(\"${fc.assetPath}\")]`;\n }\n }\n }\n\n // Build multimodal content if there are image attachments\n const imageFiles = hasFiles ? fileContexts.filter((fc) => fc.type === \"image\" && fc.base64) : [];\n\n if (imageFiles.length > 0) {\n const contentBlocks: ContentBlock[] = [];\n // Add image blocks first so the AI \"sees\" them\n for (const img of imageFiles) {\n contentBlocks.push({\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: img.mimeType,\n data: img.base64!,\n },\n });\n }\n // Add the text content\n contentBlocks.push({ type: \"text\", text: textContent });\n messages.push({ role: \"user\", content: contentBlocks });\n } else {\n messages.push({ role: \"user\", content: textContent });\n }\n\n return messages;\n}\n","/**\n * AI engine implementations — streaming handlers for each supported engine.\n */\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport { spawn } from \"node:child_process\";\nimport { getConversionGuide } from \"../ai/prompts.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { getValidAccessToken, OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } from \"../utils/claude-oauth.js\";\nimport { getSession } from \"./session.js\";\nimport { buildVibeSystemPrompt, buildVibeSystemPromptBlocks, buildStateContext, buildMessagesWithContext, getPromptContext, type SystemPromptBlock, type MultimodalMessage } from \"./ai-prompts.js\";\nimport { log } from \"./log.js\";\nimport type { UploadedFileContext } from \"./routes/upload-files.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy-loaded Anthropic SDK\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<typeof import(\"@anthropic-ai/sdk\").default> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\n// ---------------------------------------------------------------------------\n// File context helper (for CLI engines that don't support vision)\n// ---------------------------------------------------------------------------\n\nfunction buildFileContextText(fileContexts?: UploadedFileContext[]): string {\n if (!fileContexts?.length) return \"\";\n const parts: string[] = [];\n for (const fc of fileContexts) {\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n parts.push(`\\n[Uploaded image: ${fc.originalName} → use get_asset_url(\"${fc.assetPath}\")]`);\n }\n if (fc.type === \"document\" && fc.extractedText) {\n parts.push(`\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`);\n }\n }\n return parts.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// CLI status messages (shown while waiting for buffered CLI output)\n// ---------------------------------------------------------------------------\n\nexport const CLI_STATUS_MESSAGES = [\n \"Analyzing your request...\",\n \"Reading the conversion guide...\",\n \"Planning module structure...\",\n \"Generating HTML templates...\",\n \"Writing CSS styles...\",\n \"Creating field definitions...\",\n \"Building module metadata...\",\n \"Assembling theme assets...\",\n \"Polishing the output...\",\n \"Almost there — hang tight...\",\n];\n\n// ---------------------------------------------------------------------------\n// Anthropic Streaming API (shared helper + public wrappers)\n// ---------------------------------------------------------------------------\n\nconst RATE_LIMIT_DELAYS = [10, 20, 40, 60, 120]; // seconds\n\n/**\n * Shared streaming helper for all Anthropic-based engines.\n * Accepts a pre-built SDK client and system prompt (string or blocks).\n */\nasync function _streamAnthropic(\n client: InstanceType<Awaited<ReturnType<typeof getAnthropicSDK>>>,\n system: string | SystemPromptBlock[],\n messages: MultimodalMessage[],\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n): Promise<void> {\n for (let attempt = 0; ; attempt++) {\n try {\n let fullResponse = \"\";\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n try {\n const stream = client.messages.stream({\n model,\n max_tokens: 48000,\n system: system as any,\n messages: messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[],\n });\n\n for await (const event of stream) {\n if (\n event.type === \"content_block_delta\" &&\n event.delta.type === \"text_delta\"\n ) {\n const text = event.delta.text;\n fullResponse += text;\n onChunk(text);\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n return;\n } catch (err: unknown) {\n const status = (err as { status?: number }).status;\n const errType = (err as { error?: { type?: string } }).error?.type;\n const is429 = status === 429\n || errType === \"rate_limit_error\"\n || (err instanceof Error && err.message.includes(\"429\"));\n\n if (!is429 || attempt >= RATE_LIMIT_DELAYS.length) throw err;\n\n const wait = RATE_LIMIT_DELAYS[attempt];\n log.warn(\"ai-engine\", `Rate limited (429), attempt ${attempt + 1}/${RATE_LIMIT_DELAYS.length} — waiting ${wait}s`);\n if (onStatus) onStatus(`Rate limited by Anthropic API — retrying in ${wait}s...`);\n await new Promise((r) => setTimeout(r, wait * 1000));\n if (onStatus) onStatus(\"Retrying...\");\n }\n }\n}\n\n/** Prepare common Anthropic call context (messages, system prompt blocks). */\nfunction prepareAnthropicContext(userMessage: string, themeName: string, fileContexts?: UploadedFileContext[]) {\n const conversionGuide = getConversionGuide();\n const session = getSession()!;\n const editMode = session.modules.length > 0;\n const messages = buildMessagesWithContext(userMessage, fileContexts);\n const ctx = getPromptContext();\n const systemBlocks = buildVibeSystemPromptBlocks(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n return { messages, systemBlocks, conversionGuide, editMode };\n}\n\nexport async function streamWithAnthropicAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ apiKey });\n const { messages, systemBlocks } = prepareAnthropicContext(userMessage, themeName, fileContexts);\n\n log.info(\"anthropic\", \"API call\", {\n model,\n systemBlockCount: systemBlocks.length,\n cachedBlocks: systemBlocks.filter((b) => b.cache_control).length,\n messageCount: messages.length,\n });\n\n await _streamAnthropic(client, systemBlocks, messages, model, onChunk, onStatus, onFinish);\n}\n\n// ---------------------------------------------------------------------------\n// Claude OAuth Streaming API — uses OAuth token + required headers\n// ---------------------------------------------------------------------------\n\nexport async function streamWithClaudeOAuth(\n userMessage: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const accessToken = await getValidAccessToken();\n if (!accessToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n authToken: accessToken,\n defaultHeaders: OAUTH_EXTRA_HEADERS,\n } as any);\n\n const { messages, systemBlocks } = prepareAnthropicContext(userMessage, themeName, fileContexts);\n\n // Prepend required Claude Code system prefix as separate first block\n const oauthBlocks: SystemPromptBlock[] = [\n { type: \"text\", text: OAUTH_SYSTEM_PREFIX },\n ...systemBlocks,\n ];\n\n log.info(\"anthropic-oauth\", \"API call\", {\n model,\n systemBlockCount: oauthBlocks.length,\n cachedBlocks: oauthBlocks.filter((b) => b.cache_control).length,\n messageCount: messages.length,\n });\n\n await _streamAnthropic(client, oauthBlocks, messages, model, onChunk, onStatus, onFinish);\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI Streaming API (uses native fetch — no npm dependency)\n// ---------------------------------------------------------------------------\n\nexport async function streamWithOpenAIAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const editMode = getSession()!.modules.length > 0;\n const messages = buildMessagesWithContext(userMessage, fileContexts);\n const ctx = getPromptContext();\n\n // Convert multimodal messages to OpenAI format\n const openaiMessages = messages.map((m) => {\n if (typeof m.content === \"string\") return m;\n // Convert Anthropic-style content blocks to OpenAI format\n return {\n role: m.role,\n content: m.content.map((block) => {\n if (block.type === \"text\") return { type: \"text\" as const, text: block.text };\n return {\n type: \"image_url\" as const,\n image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` },\n };\n }),\n };\n });\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n max_tokens: 48000,\n stream: true,\n messages: [\n { role: \"system\", content: buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets) },\n ...openaiMessages,\n ],\n }),\n });\n\n if (!response.ok) {\n const err = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${err}`);\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n let fullResponse = \"\";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") break;\n\n try {\n const parsed = JSON.parse(data);\n const delta = parsed.choices?.[0]?.delta?.content;\n if (delta) {\n fullResponse += delta;\n onChunk(delta);\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n}\n\n// ---------------------------------------------------------------------------\n// Gemini Streaming API (uses native fetch — no npm dependency)\n// ---------------------------------------------------------------------------\n\nexport async function streamWithGeminiAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const session = getSession()!;\n const editMode = session.modules.length > 0;\n const stateContext = buildStateContext();\n const ctx = getPromptContext();\n\n const contents: Array<{ role: string; parts: Array<{ text?: string; inlineData?: { mimeType: string; data: string } }> }> = [];\n\n for (const m of session.messages.slice(-20)) {\n contents.push({\n role: m.role === \"assistant\" ? \"model\" : \"user\",\n parts: [{ text: m.content }],\n });\n }\n\n let userContent = stateContext\n ? `${userMessage}\\n\\n---\\n${stateContext}`\n : userMessage;\n\n // Add document text from file contexts\n if (fileContexts?.length) {\n for (const fc of fileContexts) {\n if (fc.type === \"document\" && fc.extractedText) {\n userContent += `\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`;\n }\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n userContent += `\\n\\n[Uploaded image: ${fc.originalName} → available as get_asset_url(\"${fc.assetPath}\")]`;\n }\n }\n }\n\n const userParts: Array<{ text?: string; inlineData?: { mimeType: string; data: string } }> = [];\n\n // Add image parts for Gemini vision\n if (fileContexts?.length) {\n for (const fc of fileContexts) {\n if (fc.type === \"image\" && fc.base64) {\n userParts.push({ inlineData: { mimeType: fc.mimeType, data: fc.base64 } });\n }\n }\n }\n\n userParts.push({ text: userContent });\n contents.push({ role: \"user\", parts: userParts });\n\n const model = \"gemini-2.5-flash\";\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n systemInstruction: { parts: [{ text: buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets) }] },\n contents,\n generationConfig: { maxOutputTokens: 48000 },\n }),\n });\n\n if (!response.ok) {\n const err = await response.text();\n throw new Error(`Gemini API error (${response.status}): ${err}`);\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n let fullResponse = \"\";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n\n try {\n const parsed = JSON.parse(data);\n const text = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n if (text) {\n fullResponse += text;\n onChunk(text);\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n}\n\n// ---------------------------------------------------------------------------\n// CLI subprocess helper — sends prompt via stdin to avoid shell arg limits\n// ---------------------------------------------------------------------------\n\nexport function spawnCLI(\n bin: string,\n args: string[],\n prompt: string,\n onChunk?: (chunk: string) => void\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const env = { ...process.env };\n delete env.CLAUDECODE;\n\n const child = spawn(bin, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n fn();\n };\n\n child.stdout.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n stdout += chunk;\n if (onChunk) onChunk(chunk);\n });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) =>\n settle(() => reject(new Error(`${bin} failed to start: ${err.message}`)))\n );\n\n child.on(\"close\", (code) => {\n settle(() => {\n if (code !== 0) {\n reject(new Error(\n `${bin} exited with code ${code}.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n (stdout ? `Output: ${stdout.slice(0, 500)}` : \"No output\")\n ));\n } else {\n resolve(stdout);\n }\n });\n });\n\n // Write prompt to stdin with backpressure handling for large prompts\n child.stdin.on(\"error\", () => {});\n const ok = child.stdin.write(prompt);\n if (!ok) {\n // Buffer is full — wait for drain before ending\n child.stdin.once(\"drain\", () => child.stdin.end());\n } else {\n child.stdin.end();\n }\n\n const timer = setTimeout(() => {\n child.kill();\n settle(() => reject(new Error(\n `${bin} timed out after 5 minutes.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n `Partial output (${stdout.length} chars): ${stdout.slice(0, 500)}`\n )));\n }, 300_000);\n\n // Clear timeout when process exits normally\n child.on(\"close\", () => clearTimeout(timer));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Claude Code subprocess\n// ---------------------------------------------------------------------------\n\nexport async function generateWithClaudeCode(\n userMessage: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const config = loadConfig();\n const editMode = getSession()!.modules.length > 0;\n const ctx = getPromptContext();\n\n let prompt = buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n prompt += \"\\n\\n## User Request\\n\" + userMessage;\n prompt += buildStateContext();\n prompt += buildFileContextText(fileContexts);\n prompt += \"\\n\\n---\\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.\";\n\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n\n const heartbeat = setInterval(() => {\n statusIndex++;\n const msg = CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)];\n sendStatus(msg);\n }, 6000);\n\n try {\n const result = await spawnCLI(\"claude\", args, prompt, (chunk) => {\n onChunk(chunk);\n });\n if (onFinish) onFinish(result);\n } finally {\n clearInterval(heartbeat);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generic CLI subprocess (Gemini CLI, Codex CLI)\n// ---------------------------------------------------------------------------\n\nexport async function generateWithCLI(\n cli: \"gemini\" | \"codex\",\n userMessage: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const editMode = getSession()!.modules.length > 0;\n const ctx = getPromptContext();\n\n let prompt = buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n prompt += \"\\n\\n## User Request\\n\" + userMessage;\n prompt += buildStateContext();\n prompt += buildFileContextText(fileContexts);\n prompt += \"\\n\\n---\\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.\";\n\n let bin: string;\n let args: string[];\n if (cli === \"gemini\") {\n bin = \"gemini\";\n args = [];\n } else {\n bin = \"codex\";\n args = [\"exec\", \"--full-auto\"];\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n\n const heartbeat = setInterval(() => {\n statusIndex++;\n const msg = CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)];\n sendStatus(msg);\n }, 6000);\n\n try {\n const result = await spawnCLI(bin, args, prompt, (chunk) => {\n onChunk(chunk);\n });\n if (onFinish) onFinish(result);\n } finally {\n clearInterval(heartbeat);\n }\n}\n","/**\n * Shared helpers for route handlers.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nexport function jsonResponse(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n}\n\nexport function readBody(req: IncomingMessage, callback: (body: string) => void): void {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk) => chunks.push(chunk));\n req.on(\"end\", () => callback(Buffer.concat(chunks).toString(\"utf-8\")));\n}\n\n/**\n * Read request body and parse as JSON with error handling.\n * Returns 400 with an error message if parsing fails.\n */\nexport function readJsonBody<T = Record<string, unknown>>(\n req: IncomingMessage,\n res: ServerResponse,\n callback: (data: T) => void\n): void {\n readBody(req, (body) => {\n try {\n callback(JSON.parse(body || \"{}\") as T);\n } catch {\n jsonResponse(res, 400, { error: \"Invalid JSON in request body\" });\n }\n });\n}\n","/**\n * File upload route — handles images (page assets) and documents (AI context).\n *\n * Images are copied to theme/assets/ and sent to AI as vision input.\n * Documents (PDF, DOCX, MD, TXT) are text-extracted for AI context only.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { createWriteStream, mkdirSync, existsSync, readFileSync, copyFileSync } from \"node:fs\";\nimport { join, extname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport Busboy from \"busboy\";\nimport { jsonResponse } from \"../route-helpers.js\";\nimport { getSession, addSessionAsset, type SessionAsset } from \"../session.js\";\nimport { log } from \"../log.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB\n\nconst IMAGE_MIMES = new Set([\n \"image/png\", \"image/jpeg\", \"image/jpg\", \"image/svg+xml\",\n \"image/webp\", \"image/gif\",\n]);\n\nconst DOCUMENT_MIMES = new Set([\n \"application/pdf\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"text/markdown\", \"text/plain\",\n]);\n\nconst SUPPORTED_MIMES = new Set([...IMAGE_MIMES, ...DOCUMENT_MIMES]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Sanitize a filename for safe filesystem use. */\nfunction sanitizeFilename(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9._-]/g, \"_\")\n .replace(/_{2,}/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase();\n}\n\n/** Deduplicate filename if it already exists in the target directory. */\nfunction deduplicateFilename(dir: string, name: string): string {\n if (!existsSync(join(dir, name))) return name;\n const ext = extname(name);\n const base = name.slice(0, -ext.length || undefined);\n let counter = 1;\n while (existsSync(join(dir, `${base}-${counter}${ext}`))) counter++;\n return `${base}-${counter}${ext}`;\n}\n\n/** Extract text from a PDF file using pdf-parse. */\nasync function extractPdfText(filePath: string): Promise<string> {\n const pdfParse = (await import(\"pdf-parse\")).default;\n const buffer = readFileSync(filePath);\n const data = await pdfParse(buffer);\n return data.text;\n}\n\n/** Extract text from a DOCX file using mammoth. */\nasync function extractDocxText(filePath: string): Promise<string> {\n const mammoth = await import(\"mammoth\");\n const result = await mammoth.extractRawText({ path: filePath });\n return result.value;\n}\n\n/** Read plain text or markdown file. */\nfunction extractPlainText(filePath: string): string {\n return readFileSync(filePath, \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Route handler\n// ---------------------------------------------------------------------------\n\nexport function handleFileUploadRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 400, { error: \"No active session\" });\n return;\n }\n\n const contentType = req.headers[\"content-type\"] || \"\";\n if (!contentType.includes(\"multipart/form-data\")) {\n jsonResponse(res, 400, { error: \"Expected multipart/form-data\" });\n return;\n }\n\n const results: SessionAsset[] = [];\n const errors: string[] = [];\n let fileCount = 0;\n const writePromises: Promise<void>[] = [];\n\n const bb = Busboy({ headers: req.headers, limits: { fileSize: MAX_FILE_SIZE, files: 10 } });\n\n bb.on(\"file\", (fieldname, fileStream, info) => {\n const { filename: originalName, mimeType } = info;\n fileCount++;\n\n if (!SUPPORTED_MIMES.has(mimeType)) {\n errors.push(`Unsupported file type: ${originalName} (${mimeType})`);\n fileStream.resume(); // drain the stream\n return;\n }\n\n const isImage = IMAGE_MIMES.has(mimeType);\n const sanitized = sanitizeFilename(originalName);\n const id = randomUUID();\n\n // Determine storage path\n let targetDir: string;\n let finalFilename: string;\n\n if (isImage) {\n // Images go to theme/assets/\n targetDir = join(session.themePath, \"assets\");\n mkdirSync(targetDir, { recursive: true });\n finalFilename = deduplicateFilename(targetDir, sanitized);\n } else {\n // Documents go to .vibespot/uploads/ (context only)\n targetDir = join(session.themePath, \".vibespot\", \"uploads\");\n mkdirSync(targetDir, { recursive: true });\n finalFilename = `${id}-${sanitized}`;\n }\n\n const targetPath = join(targetDir, finalFilename);\n const writeStream = createWriteStream(targetPath);\n let fileSize = 0;\n let truncated = false;\n\n fileStream.on(\"data\", (chunk: Buffer) => {\n fileSize += chunk.length;\n });\n\n fileStream.on(\"limit\", () => {\n truncated = true;\n errors.push(`File too large (>10MB): ${originalName}`);\n });\n\n fileStream.pipe(writeStream);\n\n // Track each file write as a promise so we can await them all\n writePromises.push(new Promise<void>((resolve) => {\n writeStream.on(\"finish\", () => {\n if (!truncated) {\n const asset: SessionAsset = {\n id,\n filename: finalFilename,\n originalName,\n type: isImage ? \"image\" : \"document\",\n usage: isImage ? \"asset\" : \"context\",\n mimeType,\n size: fileSize,\n addedAt: new Date().toISOString(),\n };\n results.push(asset);\n addSessionAsset(asset);\n }\n resolve();\n });\n writeStream.on(\"error\", () => {\n errors.push(`Failed to write: ${originalName}`);\n resolve();\n });\n }));\n });\n\n bb.on(\"finish\", async () => {\n // Wait for all file writes to complete before responding\n await Promise.all(writePromises);\n\n // Extract text from documents\n for (const asset of results) {\n if (asset.type === \"document\") {\n const filePath = join(session.themePath, \".vibespot\", \"uploads\", asset.filename);\n try {\n if (asset.mimeType === \"application/pdf\") {\n asset.extractedText = await extractPdfText(filePath);\n } else if (asset.mimeType === \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\") {\n asset.extractedText = await extractDocxText(filePath);\n } else {\n asset.extractedText = extractPlainText(filePath);\n }\n log.info(\"upload\", `Extracted text from ${asset.originalName} (${asset.extractedText.length} chars)`);\n } catch (err) {\n log.warn(\"upload\", `Failed to extract text from ${asset.originalName}: ${err}`);\n asset.extractedText = `[Could not extract text from ${asset.originalName}]`;\n }\n }\n }\n\n if (fileCount === 0) {\n jsonResponse(res, 400, { error: \"No files uploaded\" });\n return;\n }\n\n jsonResponse(res, 200, {\n files: results.map((a) => ({\n id: a.id,\n filename: a.filename,\n originalName: a.originalName,\n type: a.type,\n usage: a.usage,\n size: a.size,\n })),\n errors: errors.length > 0 ? errors : undefined,\n });\n });\n\n bb.on(\"error\", (err) => {\n log.error(\"upload\", `Busboy error: ${err}`);\n jsonResponse(res, 500, { error: \"Upload failed\" });\n });\n\n req.pipe(bb);\n}\n\n// ---------------------------------------------------------------------------\n// Get uploaded asset data (for AI context)\n// ---------------------------------------------------------------------------\n\nexport interface UploadedFileContext {\n id: string;\n filename: string;\n originalName: string;\n type: \"image\" | \"document\";\n usage: \"asset\" | \"context\";\n /** Base64-encoded image data (for vision API) */\n base64?: string;\n mimeType: string;\n /** Extracted text content (for documents) */\n extractedText?: string;\n /** Asset path for get_asset_url() references */\n assetPath?: string;\n}\n\n/** Load full context for uploaded files by their IDs. */\nexport function getFileContexts(fileIds: string[]): UploadedFileContext[] {\n const session = getSession();\n if (!session?.assets) return [];\n\n return fileIds\n .map((id) => {\n const asset = session.assets!.find((a) => a.id === id);\n if (!asset) return null;\n\n const ctx: UploadedFileContext = {\n id: asset.id,\n filename: asset.filename,\n originalName: asset.originalName,\n type: asset.type,\n usage: asset.usage,\n mimeType: asset.mimeType,\n };\n\n if (asset.type === \"image\") {\n // Load base64 for vision API\n const imgPath = join(session.themePath, \"assets\", asset.filename);\n if (existsSync(imgPath)) {\n ctx.base64 = readFileSync(imgPath).toString(\"base64\");\n }\n ctx.assetPath = `${session.themeName}/assets/${asset.filename}`;\n } else if (asset.type === \"document\") {\n // Load extracted text\n ctx.extractedText = asset.extractedText;\n }\n\n return ctx;\n })\n .filter((c): c is UploadedFileContext => c !== null);\n}\n\n/** Get the asset manifest (list of all image assets) for the AI system prompt. */\nexport function getAssetManifest(): string[] {\n const session = getSession();\n if (!session?.assets) return [];\n return session.assets\n .filter((a) => a.type === \"image\" && a.usage === \"asset\")\n .map((a) => a.filename);\n}\n","/**\n * Engine adapter for agentic pipeline — structured output via each provider's\n * native mechanism (Anthropic tool_use, OpenAI json_schema, Gemini responseSchema)\n * for API engines, or prompt-based JSON extraction for CLI engines.\n *\n * This is a lower-level API than the streaming functions in ai-engines.ts.\n * It accepts custom system prompts and structured output schemas, returning\n * parsed JSON or raw text.\n */\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport { spawnCLI } from \"../ai-engines.js\";\nimport { tryParseJSON, tryRepairTruncatedJSON } from \"../ai-parser.js\";\nimport { loadConfig } from \"../../utils/config.js\";\nimport { OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } from \"../../utils/claude-oauth.js\";\nimport { log } from \"../log.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MultimodalContent {\n type: \"text\";\n text: string;\n}\n\nexport interface AgentMessage {\n role: \"user\" | \"assistant\";\n content: string | MultimodalContent[];\n}\n\nexport interface StructuredOutputSpec {\n /** JSON Schema for the expected output */\n schema: Record<string, unknown>;\n /** Name for the schema / tool (used by Anthropic tool_use, OpenAI json_schema) */\n name: string;\n}\n\n/** System prompt block with optional cache control (for Anthropic prompt caching). */\nexport interface SystemPromptBlock {\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n}\n\nexport interface AgentCallOptions {\n systemPrompt: string;\n /** When provided, used instead of systemPrompt for Anthropic engines (enables prompt caching). */\n systemBlocks?: SystemPromptBlock[];\n messages: AgentMessage[];\n structuredOutput?: StructuredOutputSpec;\n maxTokens?: number;\n onChunk?: (chunk: string) => void;\n onStatus?: (status: string) => void;\n}\n\nexport type AgentCallResult =\n | { type: \"structured\"; data: unknown }\n | { type: \"text\"; text: string };\n\nexport type AgentEngine =\n | \"anthropic-api\"\n | \"claude-oauth\"\n | \"openai-api\"\n | \"gemini-api\"\n | \"claude-code\"\n | \"gemini-cli\"\n | \"codex-cli\";\n\n// ---------------------------------------------------------------------------\n// Rate limit retry delays (shared with ai-engines.ts pattern)\n// ---------------------------------------------------------------------------\n\nconst RATE_LIMIT_DELAYS = [10, 20, 40, 60, 120]; // seconds\n\nasync function withRateLimitRetry<T>(\n fn: () => Promise<T>,\n onStatus?: (status: string) => void,\n): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await fn();\n } catch (err: unknown) {\n const status = (err as { status?: number }).status;\n const errType = (err as { error?: { type?: string } }).error?.type;\n const is429 =\n status === 429 ||\n errType === \"rate_limit_error\" ||\n (err instanceof Error && err.message.includes(\"429\"));\n\n if (!is429 || attempt >= RATE_LIMIT_DELAYS.length) throw err;\n\n const wait = RATE_LIMIT_DELAYS[attempt];\n log.warn(\n \"agent-adapter\",\n `Rate limited (429), attempt ${attempt + 1}/${RATE_LIMIT_DELAYS.length} — waiting ${wait}s`,\n );\n if (onStatus) onStatus(`Rate limited — retrying in ${wait}s...`);\n await new Promise((r) => setTimeout(r, wait * 1000));\n if (onStatus) onStatus(\"Retrying...\");\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Post-processing: ensure fieldsJson / metaJson are strings, not objects\n// ---------------------------------------------------------------------------\n\nfunction stringifyJsonFields(data: unknown): unknown {\n if (data && typeof data === \"object\" && !Array.isArray(data)) {\n const obj = data as Record<string, unknown>;\n for (const key of [\"fieldsJson\", \"metaJson\"]) {\n if (obj[key] && typeof obj[key] === \"object\") {\n obj[key] = JSON.stringify(obj[key]);\n }\n }\n }\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic Adapter — structured output via tool_use\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<\n typeof import(\"@anthropic-ai/sdk\").default\n> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n extraHeaders?: Record<string, string>,\n systemPrefix?: string,\n): Promise<AgentCallResult> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n apiKey,\n ...(extraHeaders ? { defaultHeaders: extraHeaders } : {}),\n });\n\n const messages =\n opts.messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[];\n\n // Resolve system prompt: prefer blocks (with cache control), fall back to string\n let system: string | SystemPromptBlock[] = opts.systemPrompt;\n if (opts.systemBlocks) {\n system = systemPrefix\n ? [{ type: \"text\" as const, text: systemPrefix }, ...opts.systemBlocks]\n : opts.systemBlocks;\n } else if (systemPrefix) {\n system = [\n { type: \"text\" as const, text: systemPrefix },\n { type: \"text\" as const, text: opts.systemPrompt },\n ];\n }\n\n if (opts.structuredOutput) {\n // Use tool_use to enforce structured output\n const tool: Anthropic.Tool = {\n name: opts.structuredOutput.name,\n description: `Return the result as structured JSON matching the ${opts.structuredOutput.name} schema.`,\n input_schema:\n opts.structuredOutput.schema as Anthropic.Tool.InputSchema,\n };\n\n return withRateLimitRetry(async () => {\n const response = await client.messages.create({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n tools: [tool],\n tool_choice: { type: \"tool\", name: opts.structuredOutput!.name },\n });\n\n // Extract tool_use input from the response\n for (const block of response.content) {\n if (block.type === \"tool_use\") {\n return {\n type: \"structured\" as const,\n data: stringifyJsonFields(block.input),\n };\n }\n }\n\n // Fallback: no tool_use block found — return text\n const textParts = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text);\n return { type: \"text\" as const, text: textParts.join(\"\") };\n }, opts.onStatus);\n }\n\n // Non-structured: regular text generation\n return withRateLimitRetry(async () => {\n let fullText = \"\";\n const stream = client.messages.stream({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n });\n\n for await (const event of stream) {\n if (\n event.type === \"content_block_delta\" &&\n event.delta.type === \"text_delta\"\n ) {\n fullText += event.delta.text;\n if (opts.onChunk) opts.onChunk(event.delta.text);\n }\n }\n\n return { type: \"text\" as const, text: fullText };\n }, opts.onStatus);\n}\n\n/**\n * OAuth variant — uses authToken (Bearer) instead of apiKey, adds required headers + system prefix.\n */\nasync function callAnthropicOAuth(\n accessToken: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n authToken: accessToken,\n defaultHeaders: OAUTH_EXTRA_HEADERS,\n } as any);\n\n const messages =\n opts.messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[];\n\n // Build system with OAuth prefix + optional cache blocks\n let system: string | SystemPromptBlock[];\n if (opts.systemBlocks) {\n system = [\n { type: \"text\" as const, text: OAUTH_SYSTEM_PREFIX },\n ...opts.systemBlocks,\n ];\n } else {\n system = [\n { type: \"text\" as const, text: OAUTH_SYSTEM_PREFIX },\n { type: \"text\" as const, text: opts.systemPrompt },\n ];\n }\n\n if (opts.structuredOutput) {\n const tool: Anthropic.Tool = {\n name: opts.structuredOutput.name,\n description: `Return the result as structured JSON matching the ${opts.structuredOutput.name} schema.`,\n input_schema: opts.structuredOutput.schema as Anthropic.Tool.InputSchema,\n };\n\n return withRateLimitRetry(async () => {\n const response = await client.messages.create({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n tools: [tool],\n tool_choice: { type: \"tool\", name: opts.structuredOutput!.name },\n });\n\n for (const block of response.content) {\n if (block.type === \"tool_use\") {\n return { type: \"structured\" as const, data: stringifyJsonFields(block.input) };\n }\n }\n\n const textParts = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text);\n return { type: \"text\" as const, text: textParts.join(\"\") };\n }, opts.onStatus);\n }\n\n return withRateLimitRetry(async () => {\n let fullText = \"\";\n const stream = client.messages.stream({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n });\n\n for await (const event of stream) {\n if (event.type === \"content_block_delta\" && event.delta.type === \"text_delta\") {\n fullText += event.delta.text;\n if (opts.onChunk) opts.onChunk(event.delta.text);\n }\n }\n\n return { type: \"text\" as const, text: fullText };\n }, opts.onStatus);\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI Adapter — structured output via response_format json_schema\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively add `additionalProperties: false` to all object-type schemas\n * (required by OpenAI's structured output).\n */\nfunction addAdditionalPropertiesFalse(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...schema };\n if (result.type === \"object\") {\n result.additionalProperties = false;\n if (\n result.properties &&\n typeof result.properties === \"object\"\n ) {\n const props: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(\n result.properties as Record<string, unknown>,\n )) {\n props[k] =\n v && typeof v === \"object\"\n ? addAdditionalPropertiesFalse(v as Record<string, unknown>)\n : v;\n }\n result.properties = props;\n }\n }\n if (result.items && typeof result.items === \"object\") {\n result.items = addAdditionalPropertiesFalse(\n result.items as Record<string, unknown>,\n );\n }\n return result;\n}\n\nasync function callOpenAI(\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const openaiMessages = [\n { role: \"system\", content: opts.systemPrompt },\n ...opts.messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === \"string\"\n ? m.content\n : m.content.map((b) => ({ type: \"text\" as const, text: b.text })),\n })),\n ];\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: opts.maxTokens || 16000,\n messages: openaiMessages,\n };\n\n if (opts.structuredOutput) {\n body.response_format = {\n type: \"json_schema\",\n json_schema: {\n name: opts.structuredOutput.name,\n strict: true,\n schema: addAdditionalPropertiesFalse(opts.structuredOutput.schema),\n },\n };\n }\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const err = await response.text();\n const status = response.status;\n if (status === 429) {\n const error = new Error(`OpenAI rate limit: ${err}`);\n (error as unknown as { status: number }).status = 429;\n throw error;\n }\n throw new Error(`OpenAI API error (${status}): ${err}`);\n }\n\n const json = await response.json();\n const content = json.choices?.[0]?.message?.content || \"\";\n\n if (opts.structuredOutput) {\n try {\n return {\n type: \"structured\",\n data: stringifyJsonFields(JSON.parse(content)),\n };\n } catch {\n log.warn(\"agent-adapter\", \"OpenAI structured output parse failed, returning raw text\");\n return { type: \"text\", text: content };\n }\n }\n\n return { type: \"text\", text: content };\n}\n\n// ---------------------------------------------------------------------------\n// Gemini Adapter — structured output via responseMimeType + responseSchema\n// ---------------------------------------------------------------------------\n\nasync function callGemini(\n apiKey: string,\n _model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const model = _model || \"gemini-2.5-flash\";\n\n // Gemini uses \"user\" / \"model\" roles\n const contents = opts.messages.map((m) => ({\n role: m.role === \"assistant\" ? \"model\" : \"user\",\n parts:\n typeof m.content === \"string\"\n ? [{ text: m.content }]\n : m.content.map((b) => ({ text: b.text })),\n }));\n\n const body: Record<string, unknown> = {\n systemInstruction: { parts: [{ text: opts.systemPrompt }] },\n contents,\n generationConfig: {\n maxOutputTokens: opts.maxTokens || 16000,\n ...(opts.structuredOutput\n ? {\n responseMimeType: \"application/json\",\n responseSchema: opts.structuredOutput.schema,\n }\n : {}),\n },\n };\n\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const err = await response.text();\n const status = response.status;\n if (status === 429) {\n const error = new Error(`Gemini rate limit: ${err}`);\n (error as unknown as { status: number }).status = 429;\n throw error;\n }\n throw new Error(`Gemini API error (${status}): ${err}`);\n }\n\n const json = await response.json();\n const text = json.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n\n if (opts.structuredOutput) {\n try {\n return {\n type: \"structured\",\n data: stringifyJsonFields(JSON.parse(text)),\n };\n } catch {\n log.warn(\"agent-adapter\", \"Gemini structured output parse failed, returning raw text\");\n return { type: \"text\", text };\n }\n }\n\n return { type: \"text\", text };\n}\n\n// ---------------------------------------------------------------------------\n// CLI Adapter — structured output via prompt instructions + post-parse\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve CLI binary and args for a given engine.\n */\nfunction resolveCLIBinary(engine: AgentEngine): { bin: string; args: string[] } {\n switch (engine) {\n case \"claude-code\": {\n const config = loadConfig();\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n return { bin: \"claude\", args };\n }\n case \"gemini-cli\":\n return { bin: \"gemini\", args: [] };\n case \"codex-cli\":\n return { bin: \"codex\", args: [\"exec\", \"--full-auto\"] };\n default:\n throw new Error(`Not a CLI engine: ${engine}`);\n }\n}\n\n/**\n * Build a flat prompt string from AgentCallOptions for CLI engines.\n * CLI engines receive a single string via stdin — no separate system/user messages.\n */\nfunction buildCLIPrompt(opts: AgentCallOptions): string {\n const parts: string[] = [opts.systemPrompt];\n\n for (const msg of opts.messages) {\n const role = msg.role === \"user\" ? \"User\" : \"Assistant\";\n const text =\n typeof msg.content === \"string\"\n ? msg.content\n : msg.content.map((b) => b.text).join(\"\\n\");\n parts.push(`\\n\\n## ${role}\\n${text}`);\n }\n\n if (opts.structuredOutput) {\n const schemaDesc = describeSchema(opts.structuredOutput.schema);\n parts.push(`\\n\\n## Output Format — CRITICAL\nRespond with a JSON code block. Wrap your JSON in \\`\\`\\`json fences. No prose or explanation before or after the code block.\n\nThe JSON must match this structure:\n${schemaDesc}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Generate a human-readable schema description from a JSON Schema object.\n */\nfunction describeSchema(schema: Record<string, unknown>, indent = 0): string {\n const pad = \" \".repeat(indent);\n const props = schema.properties as Record<string, Record<string, unknown>> | undefined;\n const required = (schema.required as string[]) || [];\n\n if (!props) return `${pad}${JSON.stringify(schema)}`;\n\n const lines: string[] = [\"{\"];\n for (const [key, prop] of Object.entries(props)) {\n const req = required.includes(key) ? \" (required)\" : \"\";\n const type = prop.type || \"any\";\n const desc = prop.description ? ` — ${prop.description}` : \"\";\n const enumVals = prop.enum ? ` [${(prop.enum as string[]).join(\", \")}]` : \"\";\n\n if (type === \"array\" && prop.items) {\n const itemType = (prop.items as Record<string, unknown>).type || \"object\";\n lines.push(`${pad} \"${key}\": ${type}<${itemType}>${req}${desc}${enumVals}`);\n } else if (type === \"object\" && prop.properties) {\n lines.push(`${pad} \"${key}\": ${describeSchema(prop as Record<string, unknown>, indent + 1)}${req}${desc}`);\n } else {\n lines.push(`${pad} \"${key}\": ${type}${req}${desc}${enumVals}`);\n }\n }\n lines.push(`${pad}}`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Extract JSON from CLI output that may contain prose, markdown fences, or raw JSON.\n * Tries multiple strategies to find valid JSON anywhere in the output.\n */\nfunction extractJSON(output: string): unknown | null {\n const trimmed = output.trim();\n\n // Strategy 1: Direct parse (output is raw JSON with no surrounding text)\n const direct = tryParseJSON(trimmed);\n if (direct && typeof direct === \"object\") return direct;\n\n // Strategy 2: Extract from markdown fenced code blocks (```json ... ``` or ``` ... ```)\n const fenceMatch = trimmed.match(/```(?:json|vibespot-modules)?\\s*\\n([\\s\\S]*?)```/i);\n if (fenceMatch) {\n const fenced = fenceMatch[1].trim();\n const parsed = tryParseJSON(fenced);\n if (parsed && typeof parsed === \"object\") return parsed;\n const repaired = tryRepairTruncatedJSON(fenced);\n if (repaired && typeof repaired === \"object\") return repaired;\n }\n\n // Strategy 3: Find the outermost { ... } in the output (greedy brace matching)\n const firstBrace = trimmed.indexOf(\"{\");\n const lastBrace = trimmed.lastIndexOf(\"}\");\n if (firstBrace !== -1 && lastBrace > firstBrace) {\n const braceContent = trimmed.slice(firstBrace, lastBrace + 1);\n const parsed = tryParseJSON(braceContent);\n if (parsed && typeof parsed === \"object\") return parsed;\n const repaired = tryRepairTruncatedJSON(braceContent);\n if (repaired && typeof repaired === \"object\") return repaired;\n }\n\n // Strategy 4: Try repair on the full output (may be truncated JSON with leading text stripped)\n const repaired = tryRepairTruncatedJSON(trimmed);\n if (repaired && typeof repaired === \"object\") return repaired;\n\n return null;\n}\n\n/**\n * Call an AI agent via CLI subprocess with prompt-based JSON extraction.\n */\nasync function callAgentCLI(\n engine: AgentEngine,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const { bin, args } = resolveCLIBinary(engine);\n const prompt = buildCLIPrompt(opts);\n\n const rawOutput = await spawnCLI(bin, args, prompt, opts.onChunk);\n\n if (!opts.structuredOutput) {\n return { type: \"text\", text: rawOutput };\n }\n\n // Extract JSON from CLI output using multiple strategies\n const parsed = extractJSON(rawOutput);\n if (parsed) {\n return {\n type: \"structured\",\n data: stringifyJsonFields(parsed as Record<string, unknown>),\n };\n }\n\n log.warn(\"agent-cli\", `${engine}: failed to parse structured output, returning text`, {\n outputPreview: rawOutput.slice(0, 500),\n outputLength: rawOutput.length,\n });\n return { type: \"text\", text: rawOutput };\n}\n\n// ---------------------------------------------------------------------------\n// Unified entry points\n// ---------------------------------------------------------------------------\n\nconst API_ENGINES = new Set([\"anthropic-api\", \"claude-oauth\", \"openai-api\", \"gemini-api\"]);\n\n/**\n * Call an AI agent via API with optional structured output enforcement.\n */\nexport async function callAgentAPI(\n engine: AgentEngine,\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n log.info(\"agent-adapter\", `${engine} API call`, {\n model,\n structured: !!opts.structuredOutput,\n schemaName: opts.structuredOutput?.name,\n systemPromptLength: opts.systemPrompt.length,\n messageCount: opts.messages.length,\n });\n\n switch (engine) {\n case \"anthropic-api\":\n return callAnthropic(apiKey, model, opts);\n case \"claude-oauth\": {\n // Resolve fresh OAuth token at call time (auto-refreshes if needed)\n const { getValidAccessToken } = await import(\"../../utils/claude-oauth.js\");\n const oauthToken = await getValidAccessToken();\n if (!oauthToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n return callAnthropicOAuth(oauthToken, model, opts);\n }\n case \"openai-api\":\n return callOpenAI(apiKey, model, opts);\n case \"gemini-api\":\n return callGemini(apiKey, model, opts);\n default:\n throw new Error(`Unsupported API engine: ${engine}`);\n }\n}\n\n/**\n * Unified agent call dispatcher — routes to API or CLI adapter based on engine type.\n * Stages should call this instead of callAgentAPI directly.\n */\nexport async function callAgent(\n engine: AgentEngine,\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n if (API_ENGINES.has(engine)) {\n return callAgentAPI(engine, apiKey, model, opts);\n }\n\n log.info(\"agent-adapter\", `${engine} CLI call`, {\n structured: !!opts.structuredOutput,\n schemaName: opts.structuredOutput?.name,\n systemPromptLength: opts.systemPrompt.length,\n messageCount: opts.messages.length,\n });\n\n return callAgentCLI(engine, model, opts);\n}\n\n/**\n * Check if an engine type supports the agentic pipeline.\n * All engine types now support agentic mode — CLI engines use\n * prompt-based JSON extraction instead of native structured output.\n */\nexport function isAgenticCapable(\n engine: string,\n): engine is AgentEngine {\n return (\n engine === \"anthropic-api\" ||\n engine === \"claude-oauth\" ||\n engine === \"openai-api\" ||\n engine === \"gemini-api\" ||\n engine === \"claude-code\" ||\n engine === \"gemini-cli\" ||\n engine === \"codex-cli\"\n );\n}\n\n/**\n * Check if an engine is a CLI engine (subprocess-based).\n */\nexport function isCLIEngine(engine: string): boolean {\n return engine === \"claude-code\" || engine === \"gemini-cli\" || engine === \"codex-cli\";\n}\n","/**\n * Prompt builder for Stage 1: Intent Analyzer.\n * ~1.5K tokens — intent classification rules only. No guides.\n */\n\nexport function buildIntentAnalyzerPrompt(\n themeName: string,\n moduleNames: string[],\n libraryModuleNames: { name: string; usedIn: string[] }[],\n themeContext?: string,\n): string {\n const moduleList =\n moduleNames.length > 0\n ? `Current template modules (in page order):\\n${moduleNames.map((n, i) => `${i + 1}. ${n}`).join(\"\\n\")}`\n : \"No modules yet (new page).\";\n\n const libraryList =\n libraryModuleNames.length > 0\n ? `\\n\\nModule library (reusable from other templates):\\n${libraryModuleNames.map((m) => `- ${m.name} (used in: ${m.usedIn.join(\", \")})`).join(\"\\n\")}`\n : \"\";\n\n const contextSection = themeContext\n ? `\\n\\n## Product Context\\n${themeContext}`\n : \"\";\n\n return `You are the Intent Analyzer for vibeSpot, a HubSpot CMS page builder.\n\nYour job: classify the user's request and plan which modules need work. You do NOT generate module code — you only plan.\n\n## Theme: \"${themeName}\"\n\n${moduleList}${libraryList}${contextSection}\n\n## Classification Rules\n\n1. **create** — User wants a new page from scratch (e.g., \"build me a landing page for...\")\n2. **modify** — User wants to change existing modules (e.g., \"make the hero button red\", \"update the pricing\")\n3. **add** — User wants new modules added to the existing page (e.g., \"add a testimonials section\")\n4. **remove** — User wants modules removed (e.g., \"remove the footer\")\n5. **rearrange** — User wants to reorder modules (e.g., \"move pricing above features\")\n6. **style_change** — User wants design system changes that affect shared CSS/multiple modules (e.g., \"change the color scheme to blue\")\n7. **question** — User is asking a question, not requesting changes (e.g., \"what modules do I have?\"). Provide the answer directly.\n\n## Key Rules\n\n- For **modify**: list only the modules that actually need changes in \\`affectedModules\\`. Everything else goes in \\`unchangedModules\\`.\n- For **add**: new modules go in \\`newModules\\` with a descriptive name, brief description, and position index (0-based).\n- For **reuse**: if the user references a module from the library, put it in \\`reuseModules\\` with the source template name. Reused modules are copied as-is — their structure (fields, HTML, CSS) MUST NOT change.\n- For **style_change**: set \\`designSystemChanges: true\\`. All modules become affected since they need the updated design system.\n- For **question**: set \\`intent: \"question\"\\` and provide the answer in the \\`answer\\` field. The pipeline will short-circuit.\n- When the user references \"the rest of the page\", \"match the page style\", \"consistent with other sections\", or similar cross-module language, they want the target module to match the shared design system. Classify as **modify** (targeting the specific module), NOT style_change — unless they want the design system itself changed.\n- \\`guidesNeeded\\` determines which reference guides downstream stages receive. Only include what's actually needed:\n - \"design\" — for new pages, layout changes, design system work\n - \"content\" — for new pages, content-heavy changes\n - \"conversion\" — for any module code generation\n - \"hubspot_rules\" — for any module code generation\n - \"humanify\" — when generating user-facing copy\n\n## Conversation Context\n\nYou receive recent chat history (up to 3 prior exchanges). Use it to resolve:\n- **Back-references**: \"same section\", \"that module\", \"the one above\" → look at which module was modified in the previous turn\n- **Corrections**: \"I meant the hero\", \"no, the stakes section\", \"I was referencing X\" → the user is correcting YOUR previous classification. Re-apply the PREVIOUS request to the correct module. This is NOT a question — it's a \"modify\" intent.\n- **Follow-ups**: \"now make it bigger\", \"also add a CTA\" → applies to the module(s) from the previous turn\n\nCRITICAL: When the user corrects a misclassification (e.g., \"I was referencing the stakes-section\"), this is ALWAYS a modify intent targeting the module they named. NEVER classify corrections as \"question\".\n\n## Compound Requests\n\nIf the user asks for multiple things (e.g., \"make hero taller AND add testimonials\"), capture ALL parts:\n- Affected existing modules in \\`affectedModules\\`\n- New modules in \\`newModules\\`\n- Set the broadest applicable intent (prefer \"modify\" + newModules over splitting)`;\n}\n\n/** JSON Schema for PipelinePlan (used for structured output). */\nexport const INTENT_ANALYZER_SCHEMA = {\n type: \"object\",\n properties: {\n intent: {\n type: \"string\",\n enum: [\n \"create\",\n \"modify\",\n \"add\",\n \"remove\",\n \"rearrange\",\n \"style_change\",\n \"question\",\n ],\n },\n affectedModules: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Names of existing modules that need changes\",\n },\n unchangedModules: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Names of existing modules that stay as-is\",\n },\n newModules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n description: { type: \"string\" },\n position: { type: \"number\" },\n },\n required: [\"name\", \"description\", \"position\"],\n },\n description: \"New modules to create\",\n },\n reuseModules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n sourceTemplate: { type: \"string\" },\n position: { type: \"number\" },\n },\n required: [\"name\", \"sourceTemplate\", \"position\"],\n },\n description: \"Modules to copy from the library (immutable structure)\",\n },\n guidesNeeded: {\n type: \"array\",\n items: {\n type: \"string\",\n enum: [\n \"design\",\n \"content\",\n \"conversion\",\n \"hubspot_rules\",\n \"humanify\",\n ],\n },\n },\n designSystemChanges: {\n type: \"boolean\",\n description: \"True if shared CSS / design system needs regeneration\",\n },\n answer: {\n type: \"string\",\n description:\n 'For \"question\" intent only — the answer to return directly',\n },\n },\n required: [\n \"intent\",\n \"affectedModules\",\n \"unchangedModules\",\n \"newModules\",\n \"guidesNeeded\",\n \"designSystemChanges\",\n ],\n} as const;\n","/**\n * Stage 1: Intent Analyzer\n * Fast classification of user request. ~1.5K token system prompt.\n * Returns a PipelinePlan telling downstream stages what to do.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { PipelinePlan, PipelineEvent } from \"../types.js\";\nimport type { SessionSnapshot } from \"../../session/types.js\";\nimport {\n buildIntentAnalyzerPrompt,\n INTENT_ANALYZER_SCHEMA,\n} from \"../prompts/intent-analyzer.js\";\nimport { log } from \"../../log.js\";\n\nexport async function runIntentAnalyzer(\n userMessage: string,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n onEvent: (event: PipelineEvent) => void,\n libraryModules: { name: string; usedIn: string[] }[],\n): Promise<PipelinePlan> {\n onEvent({\n type: \"agent_step\",\n step: \"analyzing\",\n label: \"Analyzing your request...\",\n });\n\n const moduleNames = snapshot.modules.map((m) => m.moduleName);\n\n const systemPrompt = buildIntentAnalyzerPrompt(\n snapshot.themeName,\n moduleNames,\n libraryModules,\n snapshot.brandAssets?.themeContext,\n );\n\n // Build messages with recent conversation history for context resolution\n // (e.g., \"same section\", \"that module\", corrections like \"I meant the hero\")\n const messages: { role: \"user\" | \"assistant\"; content: string }[] = [];\n const recentMessages = snapshot.messages.slice(-6); // Last 3 exchanges max\n for (const msg of recentMessages) {\n if (msg.role === \"user\" || msg.role === \"assistant\") {\n // Truncate long assistant messages to just the first line (narrative summary)\n const content =\n msg.role === \"assistant\" && msg.content.length > 300\n ? msg.content.slice(0, 300) + \"...\"\n : msg.content;\n messages.push({ role: msg.role, content });\n }\n }\n // Always end with the current user message\n messages.push({ role: \"user\", content: userMessage });\n\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages,\n structuredOutput: {\n schema: INTENT_ANALYZER_SCHEMA as unknown as Record<string, unknown>,\n name: \"pipeline_plan\",\n },\n maxTokens: 2000,\n });\n\n if (result.type !== \"structured\") {\n log.warn(\"intent-analyzer\", \"Did not get structured output, falling back\");\n // Fallback: treat as full page create/modify\n const isNew = snapshot.modules.length === 0;\n return {\n intent: isNew ? \"create\" : \"modify\",\n affectedModules: isNew ? [] : moduleNames,\n unchangedModules: [],\n newModules: [],\n guidesNeeded: [\"design\", \"content\", \"conversion\", \"hubspot_rules\", \"humanify\"],\n designSystemChanges: isNew,\n };\n }\n\n const plan = result.data as PipelinePlan;\n\n // Ensure arrays exist\n plan.affectedModules = plan.affectedModules || [];\n plan.unchangedModules = plan.unchangedModules || [];\n plan.newModules = plan.newModules || [];\n plan.guidesNeeded = plan.guidesNeeded || [];\n\n log.info(\"intent-analyzer\", \"Plan\", {\n intent: plan.intent,\n affected: plan.affectedModules.length,\n unchanged: plan.unchangedModules.length,\n new: plan.newModules.length,\n reuse: plan.reuseModules?.length || 0,\n designSystem: plan.designSystemChanges,\n });\n\n onEvent({\n type: \"agent_decision\",\n step: \"analyzing\",\n decision: formatDecision(plan),\n });\n\n return plan;\n}\n\nfunction formatDecision(plan: PipelinePlan): string {\n const parts: string[] = [`Intent: ${plan.intent}`];\n\n if (plan.affectedModules.length > 0) {\n parts.push(`Modifying: ${plan.affectedModules.join(\", \")}`);\n }\n if (plan.unchangedModules.length > 0) {\n parts.push(`Unchanged: ${plan.unchangedModules.join(\", \")}`);\n }\n if (plan.newModules.length > 0) {\n parts.push(\n `New: ${plan.newModules.map((m) => m.name).join(\", \")}`,\n );\n }\n if (plan.reuseModules?.length) {\n parts.push(\n `Reuse: ${plan.reuseModules.map((m) => `${m.name} from ${m.sourceTemplate}`).join(\", \")}`,\n );\n }\n if (plan.designSystemChanges) {\n parts.push(\"Design system changes: yes\");\n }\n\n return parts.join(\" | \");\n}\n","/**\n * Prompt builders for Stage 2: Design System + Module Planner.\n *\n * Stage 2 is split into two sequential calls:\n * 2a: Design System — creates :root variables, shared CSS, shared JS\n * 2b: Module Planner — receives the CSS, plans modules with content briefs\n *\n * This split ensures the design system is complete and correct before\n * module developers reference it.\n */\n\nimport type { SystemPromptBlock } from \"../engine-adapter.js\";\n\n// ---------------------------------------------------------------------------\n// Stage 2a: Design System\n// ---------------------------------------------------------------------------\n\nexport function buildDesignSystemPrompt(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; themeContext?: string },\n): string {\n const parts: string[] = [];\n\n parts.push(`You are the Design System Architect for vibeSpot, a HubSpot CMS page builder.\n\nYour job: create a complete, production-ready CSS design system for a landing page theme. You produce the :root custom properties, shared utility/component CSS, and optional shared JS (scroll animations). Downstream agents will use YOUR CSS classes and variables to build individual modules.\n\n## Theme: \"${themeName}\"\n\n## Output Requirements\n\n### cssVariables\nA flat object mapping CSS custom property names to values. Every variable your CSS references MUST be defined here. Include ALL of these categories:\n\n**Colors** (at minimum):\n- --${themeName}-color-bg: page background\n- --${themeName}-color-surface: card/section background\n- --${themeName}-color-dark: dark section background\n- --${themeName}-color-dark-surface: card bg inside dark sections\n- --${themeName}-color-text: primary text color\n- --${themeName}-color-text-inverse: text on dark backgrounds\n- --${themeName}-color-text-muted: secondary/muted text\n- --${themeName}-color-primary: primary brand color\n- --${themeName}-color-primary-dark: darker variant for hover states\n- --${themeName}-color-accent: accent/highlight color\n- --${themeName}-color-accent-light: light tint for pill/badge backgrounds\n- --${themeName}-color-border: default border color\n- --${themeName}-color-border-hover: border on hover\n\n**Typography**:\n- --${themeName}-font-display: display/heading font stack (system fonts only)\n- --${themeName}-font-body: body text font stack (system fonts only)\n- --${themeName}-size-h1 through --${themeName}-size-h3: heading sizes using clamp()\n- --${themeName}-size-body, --${themeName}-size-lg, --${themeName}-size-small, --${themeName}-size-label\n- --${themeName}-leading-tight, --${themeName}-leading-snug, --${themeName}-leading-body: line heights\n- --${themeName}-tracking-tight, --${themeName}-tracking-wide: letter spacing\n\n**Spacing**:\n- --${themeName}-space-xs through --${themeName}-space-xl, --${themeName}-space-section\n- --${themeName}-max-width: content max-width (1152-1280px)\n\n**Effects**:\n- --${themeName}-radius-sm, --${themeName}-radius-md, --${themeName}-radius-lg, --${themeName}-radius-full\n- --${themeName}-shadow-card-hover, --${themeName}-shadow-button\n- --${themeName}-transition-fast, --${themeName}-transition-base, --${themeName}-transition-slow\n\n### sharedCss\nComplete CSS file content. MUST include:\n1. A \\`:root {}\\` block with ALL variables from cssVariables\n2. Reset (box-sizing, margin, padding)\n3. Body styles referencing your variables\n4. Typography rules (h1-h6, p)\n5. Layout utilities (.${themeName}-container, .${themeName}-section, .${themeName}-section--dark)\n6. Grid system (.${themeName}-grid, .${themeName}-grid--2/3/4 with responsive breakpoints)\n7. Card component (.${themeName}-card with hover lift)\n8. Button component (.${themeName}-btn, .${themeName}-btn--primary, .${themeName}-btn--secondary)\n CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus — HubSpot overrides link hover styles\n9. Pill/badge (.${themeName}-pill)\n10. Decorative elements (at least one background treatment: grid pattern, noise, gradient orb)\n11. Scroll animation CSS ([data-animate], [data-animate-stagger]) with 3s CSS-only fallback\n12. Section label (.${themeName}-label) — uppercase, letter-spacing, accent color\n13. Stat number styling\n14. Responsive mobile styles (@media max-width: 767px)\n\n### sharedJs (optional)\nIntersectionObserver-based scroll animation JS. Wrap in IIFE.\n\n## CSS Rules — CRITICAL\n- All classes MUST use prefix \"${themeName}-\"\n- Use BEM naming: ${themeName}-module__element--modifier\n- Use system font stacks ONLY (no Google Fonts @import, no external CDN)\n- Every var() reference in CSS must have a matching declaration in :root\n- No Tailwind, no Sass, no PostCSS\n- Use clamp() for fluid typography sizing\n\n## Font Strategy\nUse system font stacks that approximate the desired aesthetic. Pick TWO stacks:\n- Display: for headings (e.g., Georgia, \"Times New Roman\", serif for editorial)\n- Body: for text (e.g., system-ui, -apple-system, \"Segoe UI\", sans-serif)\n\nGood system font stacks by style:\n| Style | Display Stack | Body Stack |\n|-------|--------------|------------|\n| Editorial | Georgia, Cambria, \"Times New Roman\", serif | system-ui, -apple-system, \"Segoe UI\", sans-serif |\n| Modern | system-ui, -apple-system, sans-serif | \"Segoe UI\", Roboto, sans-serif |\n| Warm | Optima, Candara, \"Noto Sans\", sans-serif | \"Trebuchet MS\", system-ui, sans-serif |\n| Monospace/Tech | \"SF Mono\", \"Cascadia Code\", \"Fira Code\", monospace | system-ui, sans-serif |\n| Geometric | Futura, \"Century Gothic\", \"Trebuchet MS\", sans-serif | system-ui, sans-serif |`);\n\n parts.push(`\\n\\n## Design Guide\\n${getArchitectDesignSummary()}`);\n\n if (brandAssets?.styleguide) {\n parts.push(`\\n\\n## Brand Style Guide\\n${brandAssets.styleguide}`);\n }\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build design system prompt as blocks with cache control.\n * The design guide summary is static and cached.\n */\nexport function buildDesignSystemPromptBlocks(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; themeContext?: string },\n): SystemPromptBlock[] {\n // Build core prompt without the design guide (pass empty brandAssets to skip dynamic parts)\n const full = buildDesignSystemPrompt(themeName);\n // Split at the design guide marker\n const marker = \"\\n\\n## Design Guide\\n\";\n const markerIdx = full.indexOf(marker);\n\n if (markerIdx === -1) {\n // Fallback: no split possible, return as single block\n return [{ type: \"text\", text: full }];\n }\n\n const corePart = full.slice(0, markerIdx);\n const designGuide = `## Design Guide\\n${getArchitectDesignSummary()}`;\n\n const blocks: SystemPromptBlock[] = [\n { type: \"text\", text: corePart },\n { type: \"text\", text: designGuide, cache_control: { type: \"ephemeral\" } },\n ];\n\n // Dynamic brand assets\n const dynamicParts: string[] = [];\n if (brandAssets?.styleguide) dynamicParts.push(`## Brand Style Guide\\n${brandAssets.styleguide}`);\n if (brandAssets?.themeContext) dynamicParts.push(`## Product Context\\n${brandAssets.themeContext}`);\n if (dynamicParts.length > 0) {\n blocks.push({ type: \"text\", text: dynamicParts.join(\"\\n\\n\") });\n }\n\n return blocks;\n}\n\n/** JSON Schema for Design System output. */\nexport const DESIGN_SYSTEM_SCHEMA = {\n type: \"object\",\n properties: {\n cssVariables: {\n type: \"object\",\n description: \"CSS custom property name → value map. Every var() used in sharedCss must be defined here.\",\n },\n sharedCss: {\n type: \"string\",\n description: \"Complete shared CSS file. MUST start with :root {} block defining all cssVariables, followed by reset, typography, layout, components, animations, and responsive styles.\",\n },\n sharedJs: {\n type: \"string\",\n description: \"Optional shared JS for scroll animations (IntersectionObserver). Wrap in IIFE. Empty string if not needed.\",\n },\n aesthetic: {\n type: \"string\",\n description: \"Brief description of the chosen aesthetic direction (e.g., 'dark luxury with warm gold accents')\",\n },\n },\n required: [\"cssVariables\", \"sharedCss\", \"aesthetic\"],\n} as const;\n\n// ---------------------------------------------------------------------------\n// Stage 2b: Module Planner\n// ---------------------------------------------------------------------------\n\nexport function buildModulePlannerPrompt(\n themeName: string,\n sharedCss: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n guidesNeeded?: string[],\n): string {\n const parts: string[] = [];\n\n parts.push(`You are the Module Planner for vibeSpot, a HubSpot CMS page builder.\n\nYour job: plan the modules for a landing page. You define what each module contains (content brief) and how it should be laid out. You do NOT write module code — downstream Module Developers handle that.\n\nThe Design System has already been created. Your module plans MUST reference the existing CSS classes and variables.\n\n## Theme: \"${themeName}\"\n\n## Available CSS Classes\nThe following shared CSS classes are available for modules to use. Reference these in your layoutNotes:\n\n\\`\\`\\`css\n${sharedCss}\n\\`\\`\\`\n\n## Output Rules\n- Module names: descriptive, title-case (e.g., \"Hero Banner\", \"Pricing Cards\")\n- Content briefs: describe the actual copy/content each module needs (headlines, body text, CTAs, stats)\n- Layout notes: describe the visual layout using the available CSS classes above\n- Reference specific CSS classes from the shared CSS in your layout notes (e.g., \"Use ${themeName}-grid--3 for card layout, ${themeName}-section--dark for background\")\n- moduleOrder: list module names in the order they should appear on the page`);\n\n if (!guidesNeeded || guidesNeeded.includes(\"content\")) {\n parts.push(`\\n\\n## Content & Copywriting Guide\\n${getArchitectContentSummary()}`);\n }\n\n if (brandAssets?.brandvoice) {\n parts.push(`\\n\\n## Brand Voice\\n${brandAssets.brandvoice}`);\n }\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n parts.push(`\\n\\n## Anti-AI Copy Rules\\n${getArchitectHumanifySummary()}`);\n }\n\n return parts.join(\"\");\n}\n\n/** JSON Schema for Module Planner output. */\nexport const MODULE_PLANNER_SCHEMA = {\n type: \"object\",\n properties: {\n modules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Module name in title-case\" },\n description: { type: \"string\", description: \"What this module does\" },\n contentBrief: { type: \"string\", description: \"Specific content: headlines, body copy, stats, CTAs\" },\n layoutNotes: { type: \"string\", description: \"Visual layout approach referencing shared CSS classes\" },\n },\n required: [\"name\", \"description\", \"contentBrief\", \"layoutNotes\"],\n },\n },\n moduleOrder: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Module names in page display order\",\n },\n narrative: {\n type: \"string\",\n description: \"Brief description of the page story/flow\",\n },\n },\n required: [\"modules\", \"moduleOrder\", \"narrative\"],\n} as const;\n\n// ---------------------------------------------------------------------------\n// Legacy export — kept for backward compatibility with pipeline imports\n// ---------------------------------------------------------------------------\n\n/** @deprecated Use DESIGN_SYSTEM_SCHEMA + MODULE_PLANNER_SCHEMA instead */\nexport const PAGE_ARCHITECT_SCHEMA = DESIGN_SYSTEM_SCHEMA;\n\n/** @deprecated Use buildDesignSystemPrompt + buildModulePlannerPrompt instead */\nexport function buildPageArchitectPrompt(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n guidesNeeded?: string[],\n): string {\n return buildDesignSystemPrompt(themeName, brandAssets);\n}\n\n// ---------------------------------------------------------------------------\n// Guide summaries (shared by both prompts)\n// ---------------------------------------------------------------------------\n\nfunction getArchitectDesignSummary(): string {\n return `### Design Philosophy\nYou are a senior UI designer. Every page must look professionally designed, not like AI output.\nAvoid \"AI slop\": purple gradients on white, cookie-cutter card grids, no personality.\n\nBefore designing, decide on:\n- **Aesthetic direction**: minimal editorial? bold brutalist? warm organic? luxury dark-mode? Pick one and commit.\n- **One memorable element**: unusual layout, clever animation, striking color, unexpected typography.\n- **Audience**: the audience shapes the vibe. A SaaS dashboard ≠ a restaurant landing page.\n\nWhen a user gives a simple prompt like \"build me a landing page for a coffee shop,\" internally expand it:\n- Pick an aesthetic (warm, editorial, slightly vintage)\n- Pick specific colors (cream bg #faf7f2, espresso #3c1e0e, gold accent #c4956a)\n- Decide hero style (full-bleed image background, overlaid text)\n- Choose layout approach (asymmetric sections, large visual areas)\n- Add texture (subtle paper/grain noise overlay)\n- Set animations (scroll-triggered reveals)\nThe user gives the \"what,\" you decide the \"how it should look and feel.\"\n\n### Typography Scale\nInclude these in the CSS custom properties:\n\\`\\`\\`\nh1: clamp(2.5rem, 5vw, 4.5rem) /* Hero headlines — BIG */\nh2: clamp(1.75rem, 3vw, 3rem) /* Section headings */\nh3: clamp(1.25rem, 2vw, 1.75rem) /* Card titles, subheadings */\nbody: 1rem - 1.125rem /* 16-18px body text */\nsmall: 0.875rem /* Captions, labels */\nline-height: 1.1-1.2 for headings, 1.5-1.7 for body\nletter-spacing: -0.02em to -0.04em for large headings (tighter = more premium)\n\\`\\`\\`\n\n### Color Palettes\nPick a dominant (70%), secondary (25%), accent (5%). Ensure WCAG AA contrast (4.5:1 body, 3:1 large text).\n\n\\`\\`\\`\nDARK LUXURY: --bg: #0a0a0a; --surface: #141414; --text: #e8e8e8; --primary: #c9a84c; --accent: #e8d5a3\nWARM EARTH: --bg: #faf7f2; --surface: #f0ebe3; --text: #2d2418; --primary: #8b5e3c; --accent: #c4956a\nCOOL MINIMAL: --bg: #fafafa; --surface: #f1f1f1; --text: #1a1a1a; --primary: #0055ff; --accent: #00c4ff\nFOREST: --bg: #0f1a0f; --surface: #1a2e1a; --text: #d4e8d0; --primary: #4ade80; --accent: #22c55e\nEDITORIAL CREAM:--bg: #fffdf5; --surface: #f5f0e8; --text: #1c1917; --primary: #dc2626; --accent: #f97316\nNOIR: --bg: #000000; --surface: #111111; --text: #ffffff; --primary: #ffffff; --accent: #666666\n\\`\\`\\`\n\n### Layout Patterns\n1. **Split hero**: Content left, visual right (50/50 or 60/40)\n2. **Full-bleed hero**: Edge-to-edge background with centered content overlay\n3. **Bento grid**: Asymmetric grid with mixed card sizes (span-2, span-1)\n4. **Staggered/offset**: Content blocks not perfectly aligned, adds dynamism\n5. **Overlapping elements**: Cards/images that break grid lines, overlap sections\n6. **Scroll-based reveal**: Content appears as you scroll\n\n### Background Treatments (pick 1-2 per page)\nInclude these in shared CSS:\n- **Subtle grid pattern**: linear-gradient with thin lines at 60px intervals\n- **Noise texture overlay**: SVG feTurbulence filter at 0.03 opacity, fixed position\n- **Gradient orb/blob**: 600px radial-gradient circle, blurred 80px, absolute positioned\n- **Radial gradient on sections**: radial-gradient(ellipse at top, rgba(primary, 0.05), transparent 70%)\n- **Background alternation**: alternate section backgrounds every 2-3 sections to create visual \"chapters\"\n\n### Micro-Interactions (include in shared CSS)\n- **Card hover**: translateY(-4px) + box-shadow 20px 40px rgba(0,0,0,0.1), transition 0.3s ease\n- **Button hover**: translateY(-1px) + box-shadow 4px 12px rgba(primary, 0.3), transition 0.2s\n- **Link underline**: pseudo-element width 0 → 100% on hover, transition 0.3s\n- **Scroll animations**: data-animate elements start opacity:0 translateY(20px), animate to visible via IntersectionObserver. Include CSS fallback: elements become visible after 3s even if JS fails.\n- **Stagger children**: transition-delay: calc(var(--index) * 100ms)\n\n### Component Requirements\n- **Hero**: Visually dominant headline (largest on page), subheading with lower contrast, clear CTA with hover, visual interest (gradient/image/pattern/animation), min 80vh. Every hero needs a \"wow.\"\n- **Navigation**: Sticky with backdrop-blur-md bg-white/80, logo left, CTA right, active state indicator, smooth transition on scroll (shrink, shadow, bg change)\n- **Cards**: Subtle border OR shadow (not both heavy), rounded-xl to rounded-2xl, consistent padding, hover lift. Optional: subtle gradient border with pseudo-element\n- **Buttons**: Primary filled + secondary outlined/ghost, generous padding (px-6 py-3 min). CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus/:active — HubSpot overrides link hover styles\n- **Footer**: Darker than page, multi-column (3-4 cols), stacked on mobile, subtle separator from main content\n\n### Spacing\n- Section padding: 80-128px vertical\n- Content max-width: 1152-1280px centered\n- Card padding: 24-32px, gap: 24-32px\n- Between heading and body: 16-24px\n- Generous whitespace = premium. Cramped = amateur.\n- Mobile: always responsive, use clamp() for fluid sizing\n\n### Quality Checklist\n- [ ] Color palette has personality (not generic blue/purple on white)\n- [ ] Typography scale is consistent (headings use clamp(), body 16-18px)\n- [ ] Spacing is generous (sections have 80px+ padding)\n- [ ] At least one \"wow\" element (animation, unusual layout, bold color)\n- [ ] Backgrounds aren't flat (subtle pattern, gradient, or texture)\n- [ ] Hover states exist (cards lift, buttons shift, links animate)\n- [ ] Scroll animations present with CSS fallback\n- [ ] Mobile responsive (works at 375px)\n- [ ] Contrast ratios pass WCAG AA\n- [ ] Page feels cohesive (one aesthetic direction, not a Frankenstein)\n\n### Anti-Patterns\n\n| Don't | Do Instead |\n|-------|-----------|\n| Purple gradient on white | Choose a palette with personality |\n| Symmetric 3-col grids for everything | Mix layouts: bento, split, offset, overlapping |\n| Flat white/gray backgrounds | Add subtle texture, gradient, or pattern |\n| Tiny padding between sections | Use 80-128px for breathing room |\n| All animations same speed | Stagger with increasing delays |\n| Skip hover/focus states | Every interactive element needs feedback |\n| Use \\`<br>\\` tags for spacing | Use proper margin/padding |\n| Put everything in a shadowed card | Vary: full-bleed, contained, floating |`;\n}\n\nfunction getArchitectContentSummary(): string {\n return `### Mandatory Page Sections (generate all)\n1. **Navigation Bar** — Logo, 4-5 nav links, CTA button, sticky on scroll\n2. **Hero** — Badge/pill, primary headline, subheadline, primary + secondary CTA, trust signals, visual element\n3. **Social Proof Bar** — Logo strip of 4-6 clients OR stats bar (compact, py-8)\n4. **Features/Services** — Section label + headline, 3-6 cards with icon/title/description/metric\n5. **How It Works** — 3-4 numbered steps with titles, descriptions, visuals, connected flow\n6. **Testimonials** — At least 3 with full quotes, names, roles, ratings\n7. **Pricing/Value** — Pricing tiers or key metrics in large text with context + CTA\n8. **FAQ** — 4-6 real questions with specific, helpful answers\n9. **Final CTA** — Strong headline, subtext, primary + secondary buttons, visually distinct\n10. **Footer** — Brand name, 3-4 link columns with 3-5 links each, contact info, social icons, copyright\n\n### Optional Sections (include 1-2 when they fit)\n- Comparison table (\"Us vs. Them\")\n- Case study highlight\n- Team/About strip\n- Blog/Resource teasers\n- Partners/Integrations logo grid\n\n### Headline Rules — The \"Bar Test\"\nEvery headline should pass this test: if you shouted it across a bar, would someone turn their head?\n\n| Don't | Do Instead |\n|-------|-----------|\n| \"Our Services\" | \"What We Actually Do\" |\n| \"How It Works\" | \"Unclogged in 3 Steps\" |\n| \"Pricing\" | \"Cheaper Than Your Uber Eats Habit\" |\n| \"Testimonials\" | \"Don't Take Our Word For It\" |\n| \"Get Started\" | \"Blocked Drain? Text Us a Photo.\" |\n| \"Features\" | \"Everything You Get, Nothing You Don't\" |\n\n### CTA Button Copy\nNever use \"Submit\" or \"Learn More.\" Tie CTAs to specific outcomes:\n\"Book Now — From €49 →\" · \"Start Free Trial · No Card Required\" · \"Get My Custom Quote in 10 Min\" · \"Join 2,000+ Happy Customers\"\n\n### Minimum Content Quantities\n\n| Element | Min | Why |\n|---------|-----|-----|\n| Testimonials | 3 | One looks fake, two looks thin |\n| Feature cards | 4 | Three is a wireframe |\n| FAQ items | 4 | Fewer looks like hiding something |\n| Process steps | 3 | Natural narrative arc |\n| Stats/metrics | 3 | Singles look accidental |\n| Footer columns | 3 | Fewer = side project |\n| Nav links | 4-5 | Establishes depth |\n| CTA repetitions | 3 | Hero, mid-page, closing |\n\n### Business Type Content Templates\n\n**Local Service** (plumber, electrician, cleaner): Hero = pain point + speed promise. Must-have: service area, response time, pricing. CTAs: phone, WhatsApp, booking. Stats: response time, jobs done, satisfaction.\n\n**SaaS/Tech Product**: Hero = outcome-first (\"Save 10hrs/week\"). Must-have: feature grid, integration logos, product visual. CTAs: free trial, demo. Stats: performance, customer count, uptime.\n\n**Restaurant/Food**: Hero = sensory/emotional (\"Farm-to-table since 2019\"). Must-have: menu highlights with prices, hours, location. CTAs: reserve, order, menu. Stats: years open, dishes served.\n\n**E-commerce/DTC**: Hero = benefit + social proof (\"Join 50K+ happy sleepers\"). Must-have: features, comparison, guarantee. CTAs: shop, add to cart, \"Try risk-free.\" Stats: units sold, return rate.\n\n**Agency/Consultancy**: Hero = expertise + outcome (\"Scaled 40+ brands past €1M\"). Must-have: services, case studies, process. CTAs: book call, see cases, get proposal. Stats: clients, revenue, years.\n\n### Content Density — Never Leave Empty Space\nAt every viewport-height (100vh), the user should see:\n- At least one piece of **specific data** (number, price, time, rating)\n- At least one piece of **social proof** (quote, logo, rating, customer count)\n- At least one **visual element** (icon, illustration, decorative shape, gradient block)\n- A clear sense of **what section they're in** (label + headline visible)\n\nEvery card must contain ALL of: icon/visual, title (3-6 words), description (2-3 sentences with specific detail), optional link/metric. Never generate a card that is just a title and one sentence.\n\n### Content Rhythm & Visual Pacing\nAlternate section density — don't make every section the same weight:\n- HERO: Full, rich, attention-grabbing (100vh)\n- TRUST BAR: Compact (py-8 to py-12)\n- FEATURES: Dense, multi-card grid (tall section)\n- HOW IT WORKS: Medium, 3-4 steps with breathing room\n- TESTIMONIALS: Dense, 3+ cards\n- PRICING: Medium, 2-3 focused cards\n- FAQ: Compact (accordion saves space)\n- FINAL CTA: Full width, bold, short (50vh max)\n- FOOTER: Dense with links, compact\n\nAlternate backgrounds every 2-3 sections to create visual \"chapters.\" Sprinkle trust signals throughout (not just one section).\n\n### Body Copy Rules\n- Never write generic filler. Every sentence needs a SPECIFIC detail.\n- Invent plausible specifics: neighborhood names, \"48 hours\" not \"quickly\", \"€49\" not \"affordable\"\n- Keep paragraphs to 2-3 sentences max\n- Aim for 6th-grade reading level\n- Include section labels (UPPERCASE, letter-spacing 0.1em, accent color, 2-3 words) above every headline`;\n}\n\nfunction getArchitectHumanifySummary(): string {\n return `### Banned Punctuation\n- **Em dashes (—)**: NEVER use. Biggest AI tell. Replace with periods, commas, or parentheses.\n- **Semicolons**: Feel academic, not conversational. Use periods instead.\n- **Exclamation marks**: One per page maximum. Zero is ideal for B2B.\n\n### Banned Words\n**HARD BANNED (always rewrite):**\ndelve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone\n\n**SOFT BANNED (rewrite unless truly specific):**\nseamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)\n\n### Banned Openers\n\"In today's\", \"In an era\", \"In the realm\", \"Whether you're\", \"Are you tired\", \"Imagine a world\", \"Picture this\", \"Here's the thing\", \"Let's face it\", \"Look no further\", \"Say goodbye to\", \"Gone are the days\", \"It's no secret\", \"At its core\", \"At the end of the day\", \"When it comes to\"\n\n### Banned Closers\n\"The future of [X] is here\", \"Your journey starts here\", \"Join the revolution\", \"Experience the difference\", \"See what's possible\", \"Ready to take the next step\"\n\n### Banned Structures\n- \"It's not about X, it's about Y\" (second biggest AI tell after em dashes)\n- \"It's not just X, it's Y\"\n- \"[X]. Here's why.\" / \"[X]. And it matters.\"\n- \"Despite the challenges\"\n- Tricolon abuse (\"Fast, reliable, revolutionary\") — once per page max\n\n### Positive Rules\n- Be concrete, not abstract: \"42 minutes\" not \"fast\", \"€29/month\" not \"affordable\"\n- Use plain short words: use > utilize, start > commence, help > facilitate\n- Vary sentence length aggressively: mix 3-word, 12-word, and 25-word sentences\n- Front-load the benefit in the first 5 words\n- Write like you'd explain it in a bar — if you wouldn't say it holding a beer, rewrite it`;\n}\n","/**\n * Stage 2: Design System + Module Planner\n *\n * Split into two sequential agent calls:\n * 2a: Design System — creates :root vars, shared CSS, shared JS\n * 2b: Module Planner — plans modules using the finalized CSS\n *\n * This ensures module developers get a complete, working design system.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { PipelinePlan, PageBlueprint, DesignSystemOutput, PipelineEvent } from \"../types.js\";\nimport type { SessionSnapshot } from \"../../session/types.js\";\nimport {\n buildDesignSystemPrompt,\n buildDesignSystemPromptBlocks,\n buildModulePlannerPrompt,\n DESIGN_SYSTEM_SCHEMA,\n MODULE_PLANNER_SCHEMA,\n} from \"../prompts/page-architect.js\";\nimport { log } from \"../../log.js\";\n\nexport async function runPageArchitect(\n userMessage: string,\n plan: PipelinePlan,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n onEvent: (event: PipelineEvent) => void,\n): Promise<PageBlueprint> {\n // -------------------------------------------------------------------------\n // Stage 2a: Design System\n // -------------------------------------------------------------------------\n\n onEvent({\n type: \"agent_step\",\n step: \"designing\",\n label: \"Creating design system...\",\n });\n\n const isAnthropicEngine = engine === \"anthropic-api\" || engine === \"claude-oauth\";\n const designPrompt = buildDesignSystemPrompt(\n snapshot.themeName,\n snapshot.brandAssets,\n );\n const designBlocks = isAnthropicEngine\n ? buildDesignSystemPromptBlocks(snapshot.themeName, snapshot.brandAssets)\n : undefined;\n\n let designUserContent = `## User Request\\n${userMessage}`;\n if (snapshot.modules.length > 0 && plan.designSystemChanges) {\n designUserContent += `\\n\\n## Current Shared CSS (update this)\\n\\`\\`\\`css\\n${snapshot.sharedCss}\\n\\`\\`\\``;\n }\n\n const designResult = await callAgent(engine, apiKey, model, {\n systemPrompt: designPrompt,\n systemBlocks: designBlocks,\n messages: [{ role: \"user\", content: designUserContent }],\n structuredOutput: {\n schema: DESIGN_SYSTEM_SCHEMA as unknown as Record<string, unknown>,\n name: \"design_system\",\n },\n maxTokens: 16000,\n });\n\n let designSystem: DesignSystemOutput;\n\n if (designResult.type !== \"structured\") {\n log.warn(\"page-architect\", \"Design system: did not get structured output, using fallback\");\n designSystem = {\n cssVariables: {},\n sharedCss: snapshot.sharedCss || \"\",\n sharedJs: snapshot.sharedJs || \"\",\n aesthetic: \"default\",\n };\n } else {\n designSystem = designResult.data as DesignSystemOutput;\n log.info(\"page-architect\", \"Design system created\", {\n aesthetic: designSystem.aesthetic,\n varCount: Object.keys(designSystem.cssVariables || {}).length,\n cssLength: designSystem.sharedCss?.length || 0,\n });\n }\n\n // Ensure :root block exists in sharedCss\n let sharedCss = designSystem.sharedCss || \"\";\n const vars = designSystem.cssVariables;\n if (vars && typeof vars === \"object\" && Object.keys(vars).length > 0) {\n if (!sharedCss.includes(\":root\")) {\n const varLines = Object.entries(vars)\n .map(([k, v]) => ` ${k.startsWith(\"--\") ? k : `--${k}`}: ${v};`)\n .join(\"\\n\");\n sharedCss = `:root {\\n${varLines}\\n}\\n\\n${sharedCss}`;\n }\n }\n\n // Detect if the user requested web fonts that couldn't be used\n const fontNotes: string[] = [];\n const webFontPattern = /\\b(Montserrat|Inter|Poppins|Raleway|Playfair|Lato|Roboto|Open\\s?Sans|Nunito|Merriweather|Oswald|Source\\s?Sans|Fira\\s?Sans|Work\\s?Sans|Manrope|Plus\\s?Jakarta)\\b/gi;\n const requestedFonts = [...new Set((userMessage.match(webFontPattern) || []).map((f) => f.trim()))];\n if (requestedFonts.length > 0) {\n const usedFonts = requestedFonts.filter((f) =>\n sharedCss.toLowerCase().includes(f.toLowerCase()),\n );\n const droppedFonts = requestedFonts.filter((f) => !usedFonts.includes(f));\n if (droppedFonts.length > 0) {\n fontNotes.push(\n `Note: ${droppedFonts.join(\", \")} not available — HubSpot modules use system font stacks (no external font imports allowed)`,\n );\n }\n }\n\n const decisionParts = [\n `Design system: ${designSystem.aesthetic || \"created\"} | ${Object.keys(vars || {}).length} variables, ${sharedCss.length} chars CSS`,\n ...fontNotes,\n ];\n\n onEvent({\n type: \"agent_decision\",\n step: \"designing\",\n decision: decisionParts.join(\"\\n\"),\n });\n\n // Emit design system ready so the preview can start showing themed placeholders\n onEvent({\n type: \"design_system_ready\",\n sharedCss,\n sharedJs: designSystem.sharedJs || \"\",\n aesthetic: designSystem.aesthetic || \"\",\n });\n\n // -------------------------------------------------------------------------\n // Stage 2b: Module Planner\n // -------------------------------------------------------------------------\n\n onEvent({\n type: \"agent_step\",\n step: \"designing\",\n label: \"Planning modules...\",\n });\n\n const plannerPrompt = buildModulePlannerPrompt(\n snapshot.themeName,\n sharedCss,\n snapshot.brandAssets,\n plan.guidesNeeded,\n );\n\n let plannerUserContent = `## User Request\\n${userMessage}`;\n if (plan.newModules.length > 0) {\n plannerUserContent += `\\n\\n## Planned Modules\\n${plan.newModules.map((m, i) => `${i + 1}. **${m.name}** — ${m.description}`).join(\"\\n\")}`;\n }\n if (snapshot.modules.length > 0 && !plan.designSystemChanges) {\n plannerUserContent += `\\n\\n## Existing Modules (keeping)\\n${snapshot.modules.map((m) => `- ${m.moduleName}`).join(\"\\n\")}`;\n }\n\n const plannerResult = await callAgent(engine, apiKey, model, {\n systemPrompt: plannerPrompt,\n messages: [{ role: \"user\", content: plannerUserContent }],\n structuredOutput: {\n schema: MODULE_PLANNER_SCHEMA as unknown as Record<string, unknown>,\n name: \"module_plan\",\n },\n maxTokens: 8000,\n });\n\n let modulePlan: { modules: PageBlueprint[\"modules\"]; moduleOrder: string[]; narrative: string };\n\n if (plannerResult.type !== \"structured\") {\n log.warn(\"page-architect\", \"Module planner: did not get structured output, using fallback\");\n modulePlan = {\n modules: plan.newModules.map((m) => ({\n name: m.name,\n description: m.description,\n contentBrief: \"Generate appropriate content\",\n layoutNotes: \"Use responsive layout\",\n })),\n moduleOrder: plan.newModules.map((m) => m.name),\n narrative: \"Page generated from user request\",\n };\n } else {\n modulePlan = plannerResult.data as typeof modulePlan;\n log.info(\"page-architect\", \"Module plan\", {\n moduleCount: modulePlan.modules.length,\n });\n }\n\n onEvent({\n type: \"agent_decision\",\n step: \"designing\",\n decision: `Page: ${modulePlan.narrative} | ${modulePlan.modules.length} modules planned`,\n });\n\n // -------------------------------------------------------------------------\n // Assemble full blueprint\n // -------------------------------------------------------------------------\n\n return {\n designSystem: {\n cssVariables: designSystem.cssVariables || {},\n sharedCss,\n sharedJs: designSystem.sharedJs,\n },\n modules: modulePlan.modules,\n moduleOrder: modulePlan.moduleOrder,\n narrative: modulePlan.narrative,\n };\n}\n","/**\n * Shared types for the agentic pipeline.\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { SessionSnapshot } from \"../session/types.js\";\n\n// ---------------------------------------------------------------------------\n// Stage 1 output: Intent Analyzer\n// ---------------------------------------------------------------------------\n\nexport interface PipelinePlan {\n intent:\n | \"create\"\n | \"modify\"\n | \"add\"\n | \"remove\"\n | \"rearrange\"\n | \"style_change\"\n | \"question\";\n affectedModules: string[];\n unchangedModules: string[];\n newModules: { name: string; description: string; position: number }[];\n reuseModules?: {\n name: string;\n sourceTemplate: string;\n position: number;\n }[];\n guidesNeeded: (\n | \"design\"\n | \"content\"\n | \"conversion\"\n | \"hubspot_rules\"\n | \"humanify\"\n )[];\n designSystemChanges: boolean;\n answer?: string; // For \"question\" intent — short-circuits the pipeline\n}\n\n// ---------------------------------------------------------------------------\n// Stage 2a output: Design System\n// ---------------------------------------------------------------------------\n\nexport interface DesignSystemOutput {\n cssVariables: Record<string, string>;\n sharedCss: string;\n sharedJs?: string;\n aesthetic: string;\n}\n\n// ---------------------------------------------------------------------------\n// Stage 2 combined output: Page Architect (Design System + Module Plan)\n// ---------------------------------------------------------------------------\n\nexport interface PageBlueprint {\n designSystem: {\n cssVariables: Record<string, string>;\n sharedCss: string;\n sharedJs?: string;\n };\n modules: {\n name: string;\n description: string;\n contentBrief: string;\n layoutNotes: string;\n }[];\n moduleOrder: string[];\n narrative: string;\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline events (emitted to WebSocket)\n// ---------------------------------------------------------------------------\n\nexport type PipelineStep =\n | \"analyzing\"\n | \"designing\"\n | \"developing\"\n | \"quality_check\";\n\nexport type ModuleStatus =\n | \"queued\"\n | \"generating\"\n | \"validating\"\n | \"retrying\"\n | \"complete\"\n | \"failed\";\n\nexport type PipelineEvent =\n | { type: \"agent_step\"; step: PipelineStep; label: string }\n | { type: \"agent_decision\"; step: string; decision: string }\n | {\n type: \"module_progress\";\n module: string;\n status: ModuleStatus;\n current: number;\n total: number;\n moduleFiles?: ModuleFiles;\n }\n | {\n type: \"design_system_ready\";\n sharedCss: string;\n sharedJs: string;\n aesthetic: string;\n }\n | {\n type: \"blueprint_ready\";\n moduleOrder: string[];\n sharedCss: string;\n sharedJs?: string;\n }\n | { type: \"module_stream\"; module: string; content: string }\n | {\n type: \"pipeline_complete\";\n modulesGenerated: number;\n modulesUnchanged: number;\n durationMs: number;\n answer?: string;\n }\n | {\n type: \"pipeline_partial\";\n succeeded: string[];\n failed: string[];\n durationMs: number;\n };\n\n// ---------------------------------------------------------------------------\n// Pipeline result (returned by orchestrator)\n// ---------------------------------------------------------------------------\n\nexport interface PipelineResult {\n modules: ModuleFiles[];\n moduleOrder: string[];\n sharedCss: string;\n sharedJs: string;\n assistantMessage: string;\n stats: {\n modulesGenerated: number;\n modulesUnchanged: number;\n modulesFailed: number;\n durationMs: number;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Module spec (passed to Stage 3 Module Developer)\n// ---------------------------------------------------------------------------\n\nexport interface ModuleSpec {\n name: string;\n description: string;\n contentBrief: string;\n layoutNotes: string;\n existingCode?: ModuleFiles; // Present when modifying an existing module\n}\n\n// ---------------------------------------------------------------------------\n// Concurrency limiter helper\n// ---------------------------------------------------------------------------\n\nexport function createConcurrencyLimiter(maxConcurrent: number) {\n let running = 0;\n const queue: Array<() => void> = [];\n\n return async function limit<T>(fn: () => Promise<T>): Promise<T> {\n if (running >= maxConcurrent) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n running++;\n try {\n return await fn();\n } finally {\n running--;\n if (queue.length > 0) {\n queue.shift()!();\n }\n }\n };\n}\n","/**\n * Prompt builder for Stage 3: Module Developer.\n * Output format rules (~2K) + conversion guide (~20K) + HubSpot rules (~20K) = ~42K tokens.\n * Design/content guides excluded — those decisions were made in Stage 2.\n */\n\nimport {\n getConversionGuide,\n getHubspotRules,\n} from \"../../../ai/prompts.js\";\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { SystemPromptBlock } from \"../engine-adapter.js\";\n\nexport function buildModuleDeveloperPrompt(\n themeName: string,\n sharedCss: string,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n): string {\n const parts: string[] = [];\n\n parts.push(`You are a Module Developer for vibeSpot, a HubSpot CMS page builder.\n\nYour job: generate ONE HubSpot CMS module. You receive a module specification and must produce the complete module code.\n\n## Theme: \"${themeName}\"\n\n## Output Rules — CRITICAL\nYou produce a single module with these fields:\n- **moduleName**: Exact module name (title-case, e.g., \"Hero Banner\")\n- **fieldsJson**: Valid JSON string — the module's fields.json content\n- **metaJson**: Valid JSON string — must include host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- **moduleHtml**: HubL template ({{ module.field_name }} syntax)\n- **moduleCss**: Vanilla CSS (no Tailwind, no Sass, no CDN imports)\n- **moduleJs**: Optional vanilla JS wrapped in IIFE, or null\n\n## CSS Rules\n- All CSS classes must use prefix \"${themeName}-\"\n- Use BEM naming: ${themeName}-moduleName__element--modifier\n- Reference the theme's CSS custom properties (shown below)\n- No CDN imports (@import url(), external <link> tags)\n- Use system font stacks — no Google Fonts\n\n## Field Rules\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER use \"name\": \"label\" (reserved) — use \"section_label\" instead\n- NEVER put literal \\\\n in field defaults\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder\", \"width\": 800, \"height\": 600 }\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 }\n\n## Images & Assets\n- Use get_asset_url(\"${themeName}/assets/filename.ext\") for uploaded assets\n- For placeholder images, use image fields with placehold.co defaults\n- Size placeholders appropriately (hero: 1920x800, cards: 600x400, icons: 200x200)\n\n## Navigation & Anchors\n- Add id attribute on module root element: id=\"module-name-lowercased\"\n- For nav modules, use anchor links (#features, #pricing, etc.)\n- Include smooth scroll behavior in nav click handlers\n\n## metaJson Template\n{ \"host_template_types\": [\"PAGE\"], \"is_available_for_new_content\": true }`);\n\n if (sharedCss) {\n parts.push(`\\n\\n## Theme Shared CSS (use these custom properties)\\n\\`\\`\\`css\\n${sharedCss}\\n\\`\\`\\``);\n }\n\n if (!guidesNeeded || guidesNeeded.includes(\"hubspot_rules\")) {\n parts.push(`\\n\\n## HubSpot CMS Rules\\n${getHubspotRules()}`);\n }\n\n if (!guidesNeeded || guidesNeeded.includes(\"conversion\")) {\n parts.push(`\\n\\n## Conversion Guide\\n${getConversionGuide()}`);\n }\n\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n parts.push(`\\n\\n## Anti-AI Copy Rules\\n${getModuleDevHumanifySummary()}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build the module developer prompt as an array of blocks with cache control.\n * Static reference guides (conversion guide + HubSpot rules) are marked for caching.\n * These are identical across all parallel module generation calls in a batch.\n */\nexport function buildModuleDeveloperPromptBlocks(\n themeName: string,\n sharedCss: string,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n): SystemPromptBlock[] {\n const blocks: SystemPromptBlock[] = [];\n\n // Block 1: Core instructions (varies by themeName + sharedCss)\n let core = buildModuleDeveloperPrompt(themeName, \"\", [], brandAssets ? { ...brandAssets, humanify: false } : undefined);\n if (sharedCss) {\n core += `\\n\\n## Theme Shared CSS (use these custom properties)\\n\\`\\`\\`css\\n${sharedCss}\\n\\`\\`\\``;\n }\n blocks.push({ type: \"text\", text: core });\n\n // Block 2: Reference guides — CACHED (identical across all module calls)\n const guideParts: string[] = [];\n if (!guidesNeeded || guidesNeeded.includes(\"hubspot_rules\")) {\n guideParts.push(`## HubSpot CMS Rules\\n${getHubspotRules()}`);\n }\n if (!guidesNeeded || guidesNeeded.includes(\"conversion\")) {\n guideParts.push(`## Conversion Guide\\n${getConversionGuide()}`);\n }\n if (guideParts.length > 0) {\n blocks.push({ type: \"text\", text: guideParts.join(\"\\n\\n\"), cache_control: { type: \"ephemeral\" } });\n }\n\n // Block 3: Dynamic content (brand assets, humanify)\n const dynamicParts: string[] = [];\n if (brandAssets?.themeContext) {\n dynamicParts.push(`## Product Context\\n${brandAssets.themeContext}`);\n }\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n dynamicParts.push(`## Anti-AI Copy Rules\\n${getModuleDevHumanifySummary()}`);\n }\n if (dynamicParts.length > 0) {\n blocks.push({ type: \"text\", text: dynamicParts.join(\"\\n\\n\") });\n }\n\n return blocks;\n}\n\n/**\n * Condensed humanify guide for Stage 3 (~2K chars).\n * Keeps: banned punctuation, full banned word list, banned openers/closers/structures,\n * positive rules, testimonial rules. Drops: full examples, explanations, sniff test.\n */\nfunction getModuleDevHumanifySummary(): string {\n return `### Banned Punctuation\n- **Em dashes (—)**: NEVER use. Replace with periods, commas, or parentheses. Hyphens for compounds fine.\n- **Semicolons**: Use periods instead in marketing copy.\n- **Exclamation marks**: One per page max. Zero ideal for B2B.\n\n### Banned Words\n**HARD BANNED:**\ndelve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone\n\n**SOFT BANNED (rewrite unless truly earned):**\nseamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)\n\n### Banned Openers\nNever start a heading or paragraph with: \"In today's\", \"In an era\", \"In the realm\", \"Whether you're\", \"Are you tired\", \"Imagine a world\", \"Picture this\", \"Here's the thing\", \"Let's face it\", \"Look no further\", \"Say goodbye to\", \"Gone are the days\", \"It's no secret\", \"At its core\", \"At the end of the day\", \"When it comes to\"\n\n### Banned Closers\nNever end with: \"The future of [X] is here\", \"Your journey starts here\", \"Join the revolution\", \"Experience the difference\", \"See what's possible\", \"Ready to take the next step\"\n\n### Banned Structures\n- \"It's not about X, it's about Y\" — just state Y\n- \"It's not just X, it's Y\" — just state Y\n- \"[X]. Here's why.\" / \"[X]. And it matters.\"\n- \"Despite the challenges\"\n- Tricolon abuse (\"Fast, reliable, revolutionary\") — max once per page\n\n### Positive Writing Rules\n- Be concrete: \"42 minutes\" not \"fast\", \"€29/month\" not \"affordable\", \"2,847 teams\" not \"thousands\"\n- Use plain words: use > utilize, start > commence, help > facilitate, enough > sufficient\n- Vary sentence length: mix 3-word, 12-word, and 25-word sentences\n- Front-load the benefit in the first 5 words\n- Write like you'd say it in a bar. If you wouldn't say it holding a beer, rewrite it.\n- One adjective per noun max. Zero is often better.\n\n### Testimonial Rules\n- Include a specific problem that was solved\n- Include a concrete detail (time saved, money saved, specific task)\n- Keep slightly imperfect (fragments OK, mild hedging like \"honestly didn't think\")\n- Full names, specific roles (not \"John D., CEO\")\n- Never start with \"This product is...\" — start with the person's situation\n- Vary length and voice across testimonials`;\n}\n\n/**\n * Build the user message for a single module generation call.\n */\nexport function buildModuleUserMessage(\n userMessage: string,\n spec: { name: string; description: string; contentBrief: string; layoutNotes: string },\n existingCode?: ModuleFiles,\n): string {\n const parts: string[] = [];\n\n parts.push(`## User Request\\n${userMessage}`);\n\n parts.push(`\\n\\n## Module Specification\n- **Name**: ${spec.name}\n- **Description**: ${spec.description}\n- **Content Brief**: ${spec.contentBrief}\n- **Layout Notes**: ${spec.layoutNotes}`);\n\n if (existingCode) {\n parts.push(`\\n\\n## Existing Module Code (modify this)\n**fields.json:**\n\\`\\`\\`json\n${existingCode.fieldsJson}\n\\`\\`\\`\n\n**module.html:**\n\\`\\`\\`html\n${existingCode.moduleHtml}\n\\`\\`\\`\n\n**module.css:**\n\\`\\`\\`css\n${existingCode.moduleCss}\n\\`\\`\\``);\n\n if (existingCode.moduleJs) {\n parts.push(`\\n**module.js:**\n\\`\\`\\`js\n${existingCode.moduleJs}\n\\`\\`\\``);\n }\n }\n\n return parts.join(\"\");\n}\n\n/** JSON Schema for a single ModuleFiles (used for structured output). */\nexport const MODULE_DEVELOPER_SCHEMA = {\n type: \"object\",\n properties: {\n moduleName: { type: \"string\" },\n fieldsJson: {\n type: \"string\",\n description: \"Complete fields.json content as a JSON string\",\n },\n metaJson: {\n type: \"string\",\n description: \"Complete meta.json content as a JSON string\",\n },\n moduleHtml: {\n type: \"string\",\n description: \"Complete module.html HubL template content\",\n },\n moduleCss: {\n type: \"string\",\n description: \"Complete module.css vanilla CSS content\",\n },\n moduleJs: {\n type: \"string\",\n description: \"Optional module.js vanilla JS content, or empty string if not needed\",\n },\n },\n required: [\n \"moduleName\",\n \"fieldsJson\",\n \"metaJson\",\n \"moduleHtml\",\n \"moduleCss\",\n ],\n} as const;\n","/**\n * Stage 3: Module Developer (Parallel)\n * One API call per module, run with concurrency limiter.\n * ~42K token system prompt (conversion guide + HubSpot rules).\n */\n\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { ModuleSpec, PipelineEvent } from \"../types.js\";\nimport { createConcurrencyLimiter } from \"../types.js\";\nimport {\n buildModuleDeveloperPrompt,\n buildModuleDeveloperPromptBlocks,\n buildModuleUserMessage,\n MODULE_DEVELOPER_SCHEMA,\n} from \"../prompts/module-developer.js\";\nimport { log } from \"../../log.js\";\n\nexport interface ModuleDevResult {\n moduleName: string;\n module?: ModuleFiles;\n error?: string;\n}\n\nexport async function runModuleDeveloper(\n userMessage: string,\n specs: ModuleSpec[],\n sharedCss: string,\n themeName: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n concurrency: number,\n onEvent: (event: PipelineEvent) => void,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): Promise<ModuleDevResult[]> {\n onEvent({\n type: \"agent_step\",\n step: \"developing\",\n label: `Generating ${specs.length} module${specs.length === 1 ? \"\" : \"s\"}...`,\n });\n\n const isAnthropicEngine = engine === \"anthropic-api\" || engine === \"claude-oauth\";\n const systemPrompt = buildModuleDeveloperPrompt(\n themeName,\n sharedCss,\n guidesNeeded,\n brandAssets,\n );\n const systemBlocks = isAnthropicEngine\n ? buildModuleDeveloperPromptBlocks(themeName, sharedCss, guidesNeeded, brandAssets)\n : undefined;\n\n const limit = createConcurrencyLimiter(concurrency);\n const total = specs.length;\n\n const promises = specs.map((spec, index) =>\n limit(async (): Promise<ModuleDevResult> => {\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"generating\",\n current: index + 1,\n total,\n });\n\n let lastError = \"\";\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n if (attempt > 0) {\n log.warn(\"module-developer\", `${spec.name}: retrying after failure (attempt ${attempt + 1})`);\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"retrying\",\n current: index + 1,\n total,\n });\n }\n\n const module = await generateSingleModule(\n userMessage,\n spec,\n systemPrompt,\n engine,\n apiKey,\n model,\n 0,\n systemBlocks,\n );\n\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"complete\",\n current: index + 1,\n total,\n moduleFiles: module,\n });\n\n return { moduleName: spec.name, module };\n } catch (err) {\n lastError =\n err instanceof Error ? err.message\n : typeof err === \"object\" && err !== null ? JSON.stringify(err)\n : String(err);\n log.error(\"module-developer\", `Failed: ${spec.name} (attempt ${attempt + 1})`, {\n error: lastError,\n });\n }\n }\n\n // Both attempts failed\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"failed\",\n current: index + 1,\n total,\n });\n\n return { moduleName: spec.name, error: lastError };\n }),\n );\n\n const results = await Promise.allSettled(promises);\n\n return results.map((r) => {\n if (r.status === \"fulfilled\") return r.value;\n return {\n moduleName: \"unknown\",\n error: r.reason instanceof Error ? r.reason.message : String(r.reason),\n };\n });\n}\n\nasync function generateSingleModule(\n userMessage: string,\n spec: ModuleSpec,\n systemPrompt: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n retryCount = 0,\n systemBlocks?: import(\"../engine-adapter.js\").SystemPromptBlock[],\n): Promise<ModuleFiles> {\n const userContent = buildModuleUserMessage(\n userMessage,\n spec,\n spec.existingCode,\n );\n\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n systemBlocks,\n messages: [{ role: \"user\", content: userContent }],\n structuredOutput: {\n schema: MODULE_DEVELOPER_SCHEMA as unknown as Record<string, unknown>,\n name: \"module_output\",\n },\n maxTokens: 16000,\n });\n\n if (result.type !== \"structured\") {\n if (retryCount < 2) {\n log.warn(\n \"module-developer\",\n `${spec.name}: no structured output, retry ${retryCount + 1}`,\n );\n return generateSingleModule(\n userMessage,\n spec,\n systemPrompt,\n engine,\n apiKey,\n model,\n retryCount + 1,\n systemBlocks,\n );\n }\n throw new Error(\n `Module \"${spec.name}\" failed to produce structured output after ${retryCount + 1} attempts`,\n );\n }\n\n const data = result.data as Record<string, unknown>;\n\n // Ensure fieldsJson and metaJson are strings\n const fieldsJson =\n typeof data.fieldsJson === \"string\"\n ? data.fieldsJson\n : JSON.stringify(data.fieldsJson, null, 2);\n const metaJson =\n typeof data.metaJson === \"string\"\n ? data.metaJson\n : JSON.stringify(data.metaJson, null, 2);\n\n return {\n moduleName: spec.name, // Always use canonical spec name, never AI-generated casing\n fieldsJson,\n metaJson,\n moduleHtml: String(data.moduleHtml || \"\"),\n moduleCss: String(data.moduleCss || \"\"),\n moduleJs: data.moduleJs ? String(data.moduleJs) : undefined,\n };\n}\n","/**\n * Stage 4: Validator + Assembler\n * Mostly rule-based code — catches errors before they reach the user or HubSpot.\n * Reuses patterns from auto-fix.ts.\n */\n\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { PipelineEvent } from \"../types.js\";\nimport { tryParseJSON } from \"../../ai-parser.js\";\nimport { log } from \"../../log.js\";\n\nexport interface ValidationIssue {\n module: string;\n field: string;\n message: string;\n autoFixed: boolean;\n}\n\nexport interface ValidationResult {\n module: ModuleFiles;\n issues: ValidationIssue[];\n valid: boolean;\n}\n\n/**\n * Validate and auto-fix a set of generated modules.\n */\nexport function validateModules(\n modules: ModuleFiles[],\n themeName: string,\n onEvent: (event: PipelineEvent) => void,\n): ValidationResult[] {\n onEvent({\n type: \"agent_step\",\n step: \"quality_check\",\n label: \"Quality check...\",\n });\n\n return modules.map((mod) => {\n const issues: ValidationIssue[] = [];\n let fixedModule = { ...mod };\n\n // --- JSON parsability ---\n fixedModule.fieldsJson = validateAndFixJson(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n \"fieldsJson\",\n issues,\n );\n fixedModule.metaJson = validateAndFixJson(\n fixedModule.metaJson,\n fixedModule.moduleName,\n \"metaJson\",\n issues,\n );\n\n // --- Reserved field names ---\n fixedModule.fieldsJson = fixReservedFieldNames(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n issues,\n );\n\n // --- Deprecated field types ---\n fixedModule.fieldsJson = fixDeprecatedFieldTypes(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n issues,\n );\n\n // --- CDN import stripping ---\n fixedModule.moduleCss = stripCdnImports(\n fixedModule.moduleCss,\n fixedModule.moduleName,\n \"moduleCss\",\n issues,\n );\n\n // --- CSS prefix auto-fix ---\n fixedModule.moduleCss = fixCssPrefix(\n fixedModule.moduleCss,\n fixedModule.moduleName,\n themeName,\n issues,\n );\n fixedModule.moduleHtml = fixHtmlClassPrefix(\n fixedModule.moduleHtml,\n fixedModule.moduleName,\n themeName,\n issues,\n );\n\n // --- HubL basic checks + auto-fix ---\n fixedModule.moduleHtml = fixHublSyntax(\n fixedModule.moduleHtml,\n fixedModule.moduleName,\n issues,\n );\n\n // --- metaJson required fields ---\n fixedModule.metaJson = ensureMetaFields(\n fixedModule.metaJson,\n fixedModule.moduleName,\n issues,\n );\n\n const valid = issues.every((i) => i.autoFixed);\n\n if (issues.length > 0) {\n log.info(\"validator\", `${fixedModule.moduleName}: ${issues.length} issues`, {\n autoFixed: issues.filter((i) => i.autoFixed).length,\n unfixed: issues.filter((i) => !i.autoFixed).length,\n });\n }\n\n return { module: fixedModule, issues, valid };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Validators\n// ---------------------------------------------------------------------------\n\nfunction validateAndFixJson(\n jsonStr: string,\n moduleName: string,\n field: string,\n issues: ValidationIssue[],\n): string {\n if (!jsonStr || jsonStr.trim() === \"\") {\n issues.push({\n module: moduleName,\n field,\n message: `Empty ${field}`,\n autoFixed: field === \"metaJson\", // metaJson can be auto-generated\n });\n if (field === \"metaJson\") {\n return JSON.stringify({\n host_template_types: [\"PAGE\"],\n is_available_for_new_content: true,\n });\n }\n return jsonStr;\n }\n\n const parsed = tryParseJSON(jsonStr);\n if (parsed === null) {\n issues.push({\n module: moduleName,\n field,\n message: `Invalid JSON in ${field}`,\n autoFixed: false,\n });\n }\n return jsonStr;\n}\n\nfunction fixReservedFieldNames(\n fieldsJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n let fixed = fieldsJson;\n\n // \"name\": \"name\" → \"name\": \"item_name\"\n const namePattern = /\"name\"\\s*:\\s*\"name\"/g;\n if (namePattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"name\" is a reserved field name → renamed to \"item_name\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"name\"\\s*:\\s*\"name\"/g, '\"name\": \"item_name\"');\n }\n\n // \"name\": \"label\" → \"name\": \"section_label\"\n const labelPattern = /\"name\"\\s*:\\s*\"label\"/g;\n if (labelPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"label\" is a reserved field name → renamed to \"section_label\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"name\"\\s*:\\s*\"label\"/g, '\"name\": \"section_label\"');\n }\n\n return fixed;\n}\n\nfunction fixDeprecatedFieldTypes(\n fieldsJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n let fixed = fieldsJson;\n\n const textareaPattern = /\"type\"\\s*:\\s*\"textarea\"/g;\n if (textareaPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"textarea\" is deprecated → changed to \"text\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"type\"\\s*:\\s*\"textarea\"/g, '\"type\": \"text\"');\n }\n\n return fixed;\n}\n\nfunction stripCdnImports(\n css: string,\n moduleName: string,\n field: string,\n issues: ValidationIssue[],\n): string {\n if (!css) return css;\n let fixed = css;\n\n // @import url(...) for external fonts\n const importPattern = /@import\\s+url\\([^)]*(?:fonts\\.googleapis|cdnjs|unpkg|jsdelivr)[^)]*\\)\\s*;?/gi;\n if (importPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field,\n message: \"CDN @import removed (external imports not allowed)\",\n autoFixed: true,\n });\n fixed = fixed.replace(importPattern, \"/* CDN import removed */\");\n }\n\n return fixed;\n}\n\n/** Classes that should NOT be prefixed (framework/HubSpot/utility). */\nconst SKIP_CLASSES = new Set([\n \"visible\", \"active\", \"scroll-animate\", \"hidden\", \"open\", \"closed\",\n \"fade-in\", \"fade-out\", \"is-active\", \"is-open\", \"is-visible\",\n]);\nfunction shouldSkipClass(name: string): boolean {\n return (\n SKIP_CLASSES.has(name) ||\n name.startsWith(\"body-wrapper\") ||\n name.startsWith(\"dnd-\") ||\n name.startsWith(\"row-\") ||\n name.startsWith(\"hs-\") ||\n name.startsWith(\"hs_\")\n );\n}\n\n/**\n * Auto-fix CSS classes that don't use the theme prefix.\n * Adds `themeName-` prefix to unprefixed class selectors in CSS and returns the fixed CSS.\n */\nfunction fixCssPrefix(\n css: string,\n moduleName: string,\n themeName: string,\n issues: ValidationIssue[],\n): string {\n if (!css) return css;\n\n const prefix = themeName + \"-\";\n\n // Collect unprefixed class names (deduplicated, ordered by first occurrence)\n const classPattern = /\\.([a-zA-Z][\\w-]*)/g;\n const unprefixedSet = new Set<string>();\n let match;\n\n while ((match = classPattern.exec(css)) !== null) {\n const className = match[1];\n if (!className.startsWith(prefix) && !shouldSkipClass(className)) {\n unprefixedSet.add(className);\n }\n }\n\n if (unprefixedSet.size <= 3) return css; // minor — don't bother\n\n // Replace each unprefixed class with the prefixed version\n let fixed = css;\n for (const name of unprefixedSet) {\n // Replace in selectors: .className → .prefix-className\n // Use word boundary to avoid partial matches\n const selectorRe = new RegExp(`\\\\.${escapeRegex(name)}(?=[\\\\s,{:+~>\\\\[\\\\]])`, \"g\");\n fixed = fixed.replace(selectorRe, `.${prefix}${name}`);\n }\n\n if (fixed !== css) {\n issues.push({\n module: moduleName,\n field: \"moduleCss\",\n message: `${unprefixedSet.size} CSS classes auto-prefixed with \"${prefix}\"`,\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\n/**\n * Auto-fix class references in HTML to match prefixed CSS classes.\n */\nfunction fixHtmlClassPrefix(\n html: string,\n moduleName: string,\n themeName: string,\n issues: ValidationIssue[],\n): string {\n if (!html) return html;\n\n const prefix = themeName + \"-\";\n\n // Find all class=\"...\" attributes and check for unprefixed classes\n const classAttrRe = /class=\"([^\"]*)\"/g;\n let anyFixed = false;\n\n const fixed = html.replace(classAttrRe, (fullMatch, classValue: string) => {\n const classes = classValue.split(/\\s+/);\n let changed = false;\n const newClasses = classes.map((cls: string) => {\n if (cls && !cls.startsWith(prefix) && !shouldSkipClass(cls) && /^[a-zA-Z][\\w-]*$/.test(cls)) {\n changed = true;\n return prefix + cls;\n }\n return cls;\n });\n if (changed) {\n anyFixed = true;\n return `class=\"${newClasses.join(\" \")}\"`;\n }\n return fullMatch;\n });\n\n if (anyFixed) {\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `HTML class references auto-prefixed with \"${prefix}\"`,\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction fixHublSyntax(\n html: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n if (!html) return html;\n let fixed = html;\n\n // Two-pass approach to handle unbalanced HubL tags:\n // Pass 1: collect all tags in order to find orphans\n // Pass 2: apply fixes\n\n const tagPattern = /\\{%[-~]?\\s*(if|for|block|macro|endif|endfor|endblock|endmacro)\\b[^%]*%\\}/g;\n const tags: { tag: string; isOpen: boolean; baseTag: string; start: number; end: number }[] = [];\n let match;\n\n while ((match = tagPattern.exec(fixed)) !== null) {\n const tag = match[1];\n const isOpen = !tag.startsWith(\"end\");\n const baseTag = isOpen ? tag : tag.replace(\"end\", \"\");\n tags.push({ tag, isOpen, baseTag, start: match.index, end: match.index + match[0].length });\n }\n\n // Match openers to closers using a stack\n const stack: number[] = []; // indices into tags[]\n const orphanClosers: number[] = []; // indices of unmatched closers\n\n for (let i = 0; i < tags.length; i++) {\n if (tags[i].isOpen) {\n stack.push(i);\n } else {\n // Find matching opener on the stack (search from top)\n let found = -1;\n for (let j = stack.length - 1; j >= 0; j--) {\n if (tags[stack[j]].baseTag === tags[i].baseTag) {\n found = j;\n break;\n }\n }\n if (found !== -1) {\n stack.splice(found, 1);\n } else {\n orphanClosers.push(i);\n }\n }\n }\n\n // Remove orphan closing tags (replace in reverse order to keep positions valid)\n if (orphanClosers.length > 0) {\n for (let i = orphanClosers.length - 1; i >= 0; i--) {\n const t = tags[orphanClosers[i]];\n fixed =\n fixed.slice(0, t.start) +\n `<!-- removed orphan {% ${t.tag} %} -->` +\n fixed.slice(t.end);\n }\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `Removed ${orphanClosers.length} orphan closing tag${orphanClosers.length === 1 ? \"\" : \"s\"} with no matching opener`,\n autoFixed: true,\n });\n }\n\n // Append missing closing tags for unclosed openers (stack has unmatched openers)\n if (stack.length > 0) {\n const unclosed = stack.map((i) => tags[i].baseTag);\n const closers = unclosed\n .reverse()\n .map((tag) => `{% end${tag} %}`)\n .join(\"\\n\");\n fixed = `${fixed}\\n${closers}`;\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `Added ${unclosed.length} missing closing tag${unclosed.length === 1 ? \"\" : \"s\"}: ${unclosed.map((t) => `{% end${t} %}`).join(\", \")}`,\n autoFixed: true,\n });\n }\n\n // Fix now() → local_dt\n if (/\\bnow\\(\\)/.test(fixed)) {\n fixed = fixed.replace(/\\bnow\\(\\)/g, \"local_dt\");\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: \"Replaced now() with local_dt (now() is not valid HubL)\",\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\nfunction ensureMetaFields(\n metaJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n const parsed = tryParseJSON(metaJson);\n if (!parsed || typeof parsed !== \"object\") return metaJson;\n\n const obj = parsed as Record<string, unknown>;\n let changed = false;\n\n if (!obj.host_template_types) {\n obj.host_template_types = [\"PAGE\"];\n changed = true;\n }\n if (obj.is_available_for_new_content === undefined) {\n obj.is_available_for_new_content = true;\n changed = true;\n }\n\n if (changed) {\n issues.push({\n module: moduleName,\n field: \"metaJson\",\n message: \"Added missing meta.json required fields\",\n autoFixed: true,\n });\n return JSON.stringify(obj, null, 2);\n }\n\n return metaJson;\n}\n","/**\n * Agentic Pipeline Orchestrator\n *\n * Runs the 4-stage pipeline:\n * 1. Intent Analyzer — classify request, plan modules\n * 2. Page Architect — design system + module specs (new pages / design changes only)\n * 3. Module Developer — parallel per-module generation\n * 4. Validator — rule-based checks + auto-fix\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { SessionSnapshot } from \"../session/types.js\";\nimport type { AgentEngine } from \"./engine-adapter.js\";\nimport { isCLIEngine } from \"./engine-adapter.js\";\nimport type {\n PipelineEvent,\n PipelineResult,\n ModuleSpec,\n PageBlueprint,\n} from \"./types.js\";\nimport { runIntentAnalyzer } from \"./stages/intent-analyzer.js\";\nimport { runPageArchitect } from \"./stages/page-architect.js\";\nimport { runModuleDeveloper } from \"./stages/module-developer.js\";\nimport { validateModules } from \"./stages/validator.js\";\nimport { log } from \"../log.js\";\nimport { execSync } from \"node:child_process\";\n\nexport { isAgenticCapable, isCLIEngine } from \"./engine-adapter.js\";\n\n/**\n * Run the full agentic pipeline for a user message.\n *\n * @param userMessage The user's chat message\n * @param snapshot Immutable copy of session state at pipeline start\n * @param engine Which API engine to use\n * @param apiKey API key for the engine\n * @param model Model ID\n * @param concurrency Max parallel module generation calls\n * @param onEvent Callback for pipeline progress events (WebSocket)\n * @param libraryModules Modules available for reuse from other templates\n */\nexport async function runAgentPipeline(\n userMessage: string,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n concurrency: number,\n onEvent: (event: PipelineEvent) => void,\n libraryModules: { name: string; usedIn: string[] }[],\n): Promise<PipelineResult> {\n const startTime = Date.now();\n\n // All engines use the configured concurrency (default 20, cap in ai-handler)\n const effectiveConcurrency = concurrency;\n\n if (isCLIEngine(engine)) {\n const binMap: Record<string, string> = {\n \"claude-code\": \"claude\",\n \"gemini-cli\": \"gemini\",\n \"codex-cli\": \"codex\",\n };\n const bin = binMap[engine];\n if (bin) {\n try {\n execSync(`command -v ${bin}`, { stdio: \"ignore\" });\n } catch {\n throw new Error(\n `CLI engine \"${engine}\" requires \"${bin}\" to be installed and on your PATH.`,\n );\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 1: Intent Analyzer\n // -----------------------------------------------------------------------\n\n const plan = await runIntentAnalyzer(\n userMessage,\n snapshot,\n engine,\n apiKey,\n model,\n onEvent,\n libraryModules,\n );\n\n // Short-circuit for questions\n if (plan.intent === \"question\" && plan.answer) {\n const durationMs = Date.now() - startTime;\n onEvent({\n type: \"pipeline_complete\",\n modulesGenerated: 0,\n modulesUnchanged: snapshot.modules.length,\n durationMs,\n answer: plan.answer,\n });\n return {\n modules: [...snapshot.modules],\n moduleOrder: snapshot.moduleOrder as string[],\n sharedCss: snapshot.sharedCss,\n sharedJs: snapshot.sharedJs,\n assistantMessage: plan.answer,\n stats: {\n modulesGenerated: 0,\n modulesUnchanged: snapshot.modules.length,\n modulesFailed: 0,\n durationMs,\n },\n };\n }\n\n // -----------------------------------------------------------------------\n // Stage 2: Page Architect (new pages or design system changes)\n // -----------------------------------------------------------------------\n\n let blueprint: PageBlueprint | null = null;\n let sharedCss = snapshot.sharedCss;\n let sharedJs = snapshot.sharedJs;\n\n const needsArchitect =\n plan.intent === \"create\" || plan.designSystemChanges;\n\n if (needsArchitect) {\n // Stage 2 now runs two sequential calls:\n // 2a: Design System (CSS vars + shared CSS/JS) — emits design_system_ready\n // 2b: Module Planner (module specs + order) — uses the finalized CSS\n blueprint = await runPageArchitect(\n userMessage,\n plan,\n snapshot,\n engine,\n apiKey,\n model,\n onEvent,\n );\n // sharedCss already has :root block merged by the stage\n sharedCss = blueprint.designSystem.sharedCss || sharedCss;\n sharedJs = blueprint.designSystem.sharedJs || sharedJs;\n\n // Notify client of module order for incremental preview placeholders\n onEvent({\n type: \"blueprint_ready\",\n moduleOrder: blueprint.moduleOrder,\n sharedCss,\n sharedJs,\n });\n }\n\n // -----------------------------------------------------------------------\n // Build module specs for Stage 3\n // -----------------------------------------------------------------------\n\n const moduleSpecs: ModuleSpec[] = [];\n\n // New modules from the plan\n if (blueprint) {\n for (const bpMod of blueprint.modules) {\n moduleSpecs.push({\n name: bpMod.name,\n description: bpMod.description,\n contentBrief: bpMod.contentBrief,\n layoutNotes: bpMod.layoutNotes,\n });\n }\n } else {\n // No blueprint — build specs from plan\n for (const newMod of plan.newModules) {\n moduleSpecs.push({\n name: newMod.name,\n description: newMod.description,\n contentBrief: \"Generate appropriate content based on the user request\",\n layoutNotes: \"Use responsive layout matching the existing design system\",\n });\n }\n\n // Affected existing modules (modifications)\n for (const modName of plan.affectedModules) {\n const existing = snapshot.modules.find(\n (m) => m.moduleName === modName,\n );\n if (existing) {\n moduleSpecs.push({\n name: modName,\n description: `Modify existing module: ${modName}`,\n contentBrief: \"Apply the user's requested changes\",\n layoutNotes: \"Preserve existing layout unless changes are requested\",\n existingCode: existing,\n });\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 3: Module Developer (parallel)\n // -----------------------------------------------------------------------\n\n let generatedModules: ModuleFiles[] = [];\n let failedModules: string[] = [];\n\n if (moduleSpecs.length > 0) {\n const devResults = await runModuleDeveloper(\n userMessage,\n moduleSpecs,\n sharedCss,\n snapshot.themeName,\n engine,\n apiKey,\n model,\n effectiveConcurrency,\n onEvent,\n plan.guidesNeeded,\n snapshot.brandAssets,\n );\n\n for (const r of devResults) {\n if (r.module) {\n generatedModules.push(r.module);\n } else {\n failedModules.push(r.moduleName);\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 4: Quality Check\n // -----------------------------------------------------------------------\n\n let validationResults: import(\"./stages/validator.js\").ValidationResult[] | null = null;\n\n if (generatedModules.length > 0) {\n validationResults = validateModules(\n generatedModules,\n snapshot.themeName,\n onEvent,\n );\n\n // Replace generated modules with validated/auto-fixed versions\n generatedModules = validationResults.map((r) => r.module);\n\n // Log quality check summary with details\n const totalIssues = validationResults.reduce(\n (sum, r) => sum + r.issues.length,\n 0,\n );\n if (totalIssues > 0) {\n const autoFixed = validationResults.reduce(\n (sum, r) => sum + r.issues.filter((i) => i.autoFixed).length,\n 0,\n );\n log.info(\"pipeline\", `Quality check: ${totalIssues} issues, ${autoFixed} auto-fixed`);\n\n // Build detailed issue list for the user\n const issueDetails = validationResults\n .flatMap((r) => r.issues)\n .map((i) => `${i.autoFixed ? \"✓\" : \"⚠\"} ${i.module}: ${i.message}`)\n .join(\"\\n\");\n\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: `${totalIssues} issues found, ${autoFixed} auto-fixed\\n${issueDetails}`,\n });\n } else {\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: \"All modules passed quality checks\",\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Assemble final module list\n // -----------------------------------------------------------------------\n\n const finalModules = assembleModuleList(\n snapshot,\n plan,\n generatedModules,\n blueprint,\n libraryModules,\n );\n\n // Build module order (reconciles any missing modules automatically)\n const moduleOrder = buildModuleOrder(\n snapshot,\n plan,\n blueprint,\n finalModules,\n );\n\n // Warn if moduleOrder was missing modules (reconciled in buildModuleOrder)\n if (blueprint?.moduleOrder?.length) {\n const blueprintSet = new Set(blueprint.moduleOrder);\n const missing = finalModules\n .filter((m) => !blueprintSet.has(m.moduleName))\n .map((m) => m.moduleName);\n if (missing.length > 0) {\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: `⚠ ${missing.length} module${missing.length === 1 ? \"\" : \"s\"} missing from page order — auto-inserted: ${missing.join(\", \")}`,\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Build assistant message\n // -----------------------------------------------------------------------\n\n const durationMs = Date.now() - startTime;\n const modulesGenerated = generatedModules.length;\n const modulesUnchanged = plan.unchangedModules.length;\n\n const validationIssues = validationResults\n ? validationResults.flatMap((r) => r.issues)\n : [];\n\n const assistantMessage = buildAssistantMessage(\n plan,\n modulesGenerated,\n modulesUnchanged,\n failedModules,\n durationMs,\n blueprint,\n validationIssues,\n );\n\n // -----------------------------------------------------------------------\n // Emit completion event\n // -----------------------------------------------------------------------\n\n if (failedModules.length > 0) {\n onEvent({\n type: \"pipeline_partial\",\n succeeded: generatedModules.map((m) => m.moduleName),\n failed: failedModules,\n durationMs,\n });\n } else {\n onEvent({\n type: \"pipeline_complete\",\n modulesGenerated,\n modulesUnchanged,\n durationMs,\n });\n }\n\n return {\n modules: finalModules,\n moduleOrder,\n sharedCss,\n sharedJs,\n assistantMessage,\n stats: {\n modulesGenerated,\n modulesUnchanged,\n modulesFailed: failedModules.length,\n durationMs,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Assemble the final module list by combining generated, unchanged, and reused modules.\n */\nfunction assembleModuleList(\n snapshot: SessionSnapshot,\n plan: { unchangedModules: string[]; reuseModules?: { name: string; sourceTemplate: string; position: number }[] },\n generatedModules: ModuleFiles[],\n blueprint: PageBlueprint | null,\n libraryModules: { name: string; usedIn: string[]; module?: ModuleFiles }[],\n): ModuleFiles[] {\n const result: ModuleFiles[] = [];\n const added = new Set<string>();\n\n // Add generated modules\n for (const mod of generatedModules) {\n result.push(mod);\n added.add(mod.moduleName);\n }\n\n // Add unchanged modules from snapshot\n for (const name of plan.unchangedModules) {\n if (added.has(name)) continue;\n const existing = snapshot.modules.find((m) => m.moduleName === name);\n if (existing) {\n result.push(existing as ModuleFiles);\n added.add(name);\n }\n }\n\n // Add reused modules from library\n if (plan.reuseModules) {\n for (const reuse of plan.reuseModules) {\n if (added.has(reuse.name)) continue;\n const libEntry = libraryModules.find(\n (l) => l.name === reuse.name && (l as { module?: ModuleFiles }).module,\n );\n if (libEntry && (libEntry as { module?: ModuleFiles }).module) {\n result.push((libEntry as { module: ModuleFiles }).module);\n added.add(reuse.name);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Build the final module order.\n */\nfunction buildModuleOrder(\n snapshot: SessionSnapshot,\n plan: { intent: string; newModules: { name: string; position: number }[]; reuseModules?: { name: string; position: number }[] },\n blueprint: PageBlueprint | null,\n finalModules: ModuleFiles[],\n): string[] {\n // If blueprint provides order, use it — but reconcile with actual modules\n if (blueprint?.moduleOrder?.length) {\n const order = [...blueprint.moduleOrder];\n // Append any generated modules missing from the blueprint order\n // (AI sometimes drops modules from moduleOrder while still generating them)\n const orderSet = new Set(order);\n for (const mod of finalModules) {\n if (!orderSet.has(mod.moduleName)) {\n // Insert before footer if present, otherwise append\n const footerIdx = order.findIndex(\n (n) => n.toLowerCase().includes(\"footer\"),\n );\n if (footerIdx !== -1) {\n order.splice(footerIdx, 0, mod.moduleName);\n } else {\n order.push(mod.moduleName);\n }\n orderSet.add(mod.moduleName);\n log.warn(\n \"pipeline\",\n `Module \"${mod.moduleName}\" missing from blueprint order — inserted`,\n );\n }\n }\n return order;\n }\n\n // For create intent, use the order from finalModules\n if (plan.intent === \"create\") {\n return finalModules.map((m) => m.moduleName);\n }\n\n // Start with existing order\n const order = [...(snapshot.moduleOrder as string[])];\n\n // Insert new modules at their specified positions\n const insertions = [\n ...plan.newModules.map((m) => ({ name: m.name, position: m.position })),\n ...(plan.reuseModules || []).map((m) => ({\n name: m.name,\n position: m.position,\n })),\n ].sort((a, b) => a.position - b.position);\n\n for (const ins of insertions) {\n const pos = Math.min(ins.position, order.length);\n order.splice(pos, 0, ins.name);\n }\n\n // Filter to only modules that exist in finalModules\n const moduleNames = new Set(finalModules.map((m) => m.moduleName));\n return order.filter((name) => moduleNames.has(name));\n}\n\nfunction buildAssistantMessage(\n plan: { intent: string; affectedModules: string[]; newModules: { name: string }[] },\n modulesGenerated: number,\n modulesUnchanged: number,\n failedModules: string[],\n durationMs: number,\n blueprint: PageBlueprint | null,\n validationIssues: { module: string; message: string; autoFixed: boolean }[],\n): string {\n const seconds = Math.round(durationMs / 1000);\n const parts: string[] = [];\n\n if (plan.intent === \"create\") {\n parts.push(\n `Created ${modulesGenerated} module${modulesGenerated === 1 ? \"\" : \"s\"} in ${seconds}s.`,\n );\n } else if (plan.intent === \"modify\" || plan.intent === \"style_change\") {\n parts.push(\n `Updated ${modulesGenerated} module${modulesGenerated === 1 ? \"\" : \"s\"} in ${seconds}s.`,\n );\n if (modulesUnchanged > 0) {\n parts.push(`${modulesUnchanged} module${modulesUnchanged === 1 ? \"\" : \"s\"} unchanged.`);\n }\n } else if (plan.intent === \"add\") {\n const newNames = plan.newModules.map((m) => m.name).join(\", \");\n parts.push(`Added ${newNames} in ${seconds}s.`);\n } else if (plan.intent === \"remove\") {\n parts.push(`Removed modules in ${seconds}s.`);\n } else if (plan.intent === \"rearrange\") {\n parts.push(`Rearranged modules in ${seconds}s.`);\n }\n\n // Add narrative summary from blueprint\n if (blueprint?.narrative) {\n parts.push(`\\n\\n${blueprint.narrative}`);\n }\n\n if (failedModules.length > 0) {\n parts.push(\n `\\n\\n**Failed:** ${failedModules.join(\", \")}. You can retry these individually.`,\n );\n }\n\n // Add validation details\n const unfixed = validationIssues.filter((i) => !i.autoFixed);\n const fixed = validationIssues.filter((i) => i.autoFixed);\n if (fixed.length > 0 || unfixed.length > 0) {\n const valParts: string[] = [];\n if (fixed.length > 0) {\n valParts.push(`**Auto-fixed:** ${fixed.map((i) => `${i.module}: ${i.message}`).join(\", \")}`);\n }\n if (unfixed.length > 0) {\n valParts.push(`**Warnings:** ${unfixed.map((i) => `${i.module}: ${i.message}`).join(\", \")}`);\n }\n parts.push(`\\n\\n${valParts.join(\"\\n\")}`);\n }\n\n return parts.join(\"\");\n}\n","/**\n * AI handler coordinator for vibe coding mode.\n * Dispatches to engine implementations, manages generation state,\n * and delegates response parsing. Supports both single-call and\n * agentic pipeline modes.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { loadConfig, getApiKeyForEngine, type AIEngineType } from \"../utils/config.js\";\nimport { getSession, addMessage, saveSession, updateModules, reorderModules, getModuleLibrary, getActiveTemplate } from \"./session.js\";\nimport { parseAndApplyModules } from \"./ai-parser.js\";\nimport { log } from \"./log.js\";\nimport {\n streamWithAnthropicAPI,\n streamWithClaudeOAuth,\n streamWithOpenAIAPI,\n streamWithGeminiAPI,\n generateWithClaudeCode,\n generateWithCLI,\n} from \"./ai-engines.js\";\nimport { hasValidOAuthToken, getValidAccessToken } from \"../utils/claude-oauth.js\";\nimport { getFileContexts } from \"./routes/upload-files.js\";\nimport { runAgentPipeline, isAgenticCapable, isCLIEngine } from \"./agent/pipeline.js\";\nimport type { AgentEngine } from \"./agent/engine-adapter.js\";\nimport type { PipelineEvent, PipelineResult } from \"./agent/types.js\";\nimport type { SessionSnapshot, PipelineMetadata } from \"./session/types.js\";\n\n// ---------------------------------------------------------------------------\n// Parse warning callback — set by the WebSocket handler\n// ---------------------------------------------------------------------------\n\nlet parseWarningCallback: ((warning: string) => void) | null = null;\n\nexport function setParseWarningCallback(cb: ((warning: string) => void) | null): void {\n parseWarningCallback = cb;\n}\n\n// ---------------------------------------------------------------------------\n// Generation lock — prevents session switching while AI is generating\n// ---------------------------------------------------------------------------\n\nlet generatingSessionId: string | null = null;\n\nexport function isGenerating(): boolean {\n return generatingSessionId !== null;\n}\n\n// ---------------------------------------------------------------------------\n// Finish response — save message and parse modules\n// ---------------------------------------------------------------------------\n\nfunction finishResponse(fullResponse: string): void {\n if (generatingSessionId) {\n const current = getSession();\n if (!current || current.id !== generatingSessionId) {\n log.warn(\"ai-handler\", \"Session changed during generation — discarding AI output\");\n return;\n }\n }\n addMessage(\"assistant\", fullResponse);\n parseAndApplyModules(fullResponse, parseWarningCallback || undefined);\n saveSession();\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Stream an AI response for a chat message.\n * Calls onChunk with text fragments as they arrive.\n * After the full response, parses module JSON blocks and updates the session.\n */\nexport async function handleGenerateStream(\n userMessage: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n fileIds?: string[]\n): Promise<void> {\n const session = getSession();\n if (!session) throw new Error(\"No active session\");\n\n const capturedSessionId = session.id;\n generatingSessionId = capturedSessionId;\n\n // Load file contexts for any attached files\n const fileContexts = fileIds?.length ? getFileContexts(fileIds) : undefined;\n\n try {\n const config = loadConfig();\n const engine = config.aiEngine || detectDefaultEngine();\n\n switch (engine) {\n case \"anthropic-api\":\n case \"api\": {\n const apiKey = getApiKeyForEngine(\"anthropic-api\", config);\n if (!apiKey) throw new Error(\"Anthropic API key not configured. Open Settings to add one.\");\n await streamWithAnthropicAPI(userMessage, apiKey, session.themeName,\n config.anthropicApiModel || \"claude-sonnet-4-6\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"claude-oauth\": {\n await streamWithClaudeOAuth(userMessage, session.themeName,\n config.anthropicApiModel || \"claude-sonnet-4-6\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"openai-api\": {\n const apiKey = getApiKeyForEngine(\"openai-api\", config);\n if (!apiKey) throw new Error(\"OpenAI API key not configured. Open Settings to add one.\");\n await streamWithOpenAIAPI(userMessage, apiKey, session.themeName,\n config.openaiApiModel || \"gpt-4o\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"gemini-api\": {\n const apiKey = getApiKeyForEngine(\"gemini-api\", config);\n if (!apiKey) throw new Error(\"Gemini API key not configured. Open Settings to add one.\");\n await streamWithGeminiAPI(userMessage, apiKey, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"claude-code\":\n await generateWithClaudeCode(userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n case \"gemini-cli\":\n await generateWithCLI(\"gemini\", userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n case \"codex-cli\":\n await generateWithCLI(\"codex\", userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n default:\n throw new Error(`Unknown AI engine: ${engine}. Open Settings to configure one.`);\n }\n } finally {\n generatingSessionId = null;\n parseWarningCallback = null;\n }\n}\n\n/**\n * Detect the best available engine when none is configured.\n */\nfunction detectDefaultEngine(): AIEngineType {\n const config = loadConfig();\n if (hasValidOAuthToken()) return \"claude-oauth\";\n if (config.anthropicApiKey || process.env.ANTHROPIC_API_KEY) return \"anthropic-api\";\n if (config.openaiApiKey || process.env.OPENAI_API_KEY) return \"openai-api\";\n if (config.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY) return \"gemini-api\";\n try { execSync(\"claude --version\", { stdio: \"pipe\" }); return \"claude-code\"; } catch {}\n try { execSync(\"gemini --version\", { stdio: \"pipe\" }); return \"gemini-cli\"; } catch {}\n try { execSync(\"codex --version\", { stdio: \"pipe\" }); return \"codex-cli\"; } catch {}\n throw new Error(\"No AI engine available. Open Settings to configure one.\");\n}\n\n/**\n * Non-streaming generation (used by REST API fallback).\n */\nexport async function handleGenerate(userMessage: string): Promise<string> {\n let fullResponse = \"\";\n await handleGenerateStream(userMessage, (chunk) => {\n fullResponse += chunk;\n });\n return fullResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Agentic pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * Take an immutable snapshot of the current session state for the agentic pipeline.\n */\nfunction takeSnapshot(): SessionSnapshot {\n const session = getSession()!;\n const tpl = getActiveTemplate();\n const modules = tpl ? [...tpl.modules] : [...session.modules];\n const moduleOrder = tpl ? [...tpl.moduleOrder] : [...session.moduleOrder];\n\n return {\n modules,\n moduleOrder,\n sharedCss: tpl?.sharedCss || session.sharedCss,\n sharedJs: tpl?.sharedJs || session.sharedJs,\n messages: [...session.messages],\n themeName: session.themeName,\n themePath: session.themePath,\n brandAssets: session.brandAssets ? { ...session.brandAssets } : undefined,\n };\n}\n\n/**\n * Resolve the API engine type and key/model for agentic pipeline.\n */\nexport function resolveAgenticEngine(config: ReturnType<typeof loadConfig>): {\n engine: AgentEngine;\n apiKey: string;\n model: string;\n} {\n const engineType = config.aiEngine || detectDefaultEngine();\n\n if (!isAgenticCapable(engineType)) {\n throw new Error(\"Agentic pipeline is not available for this engine.\");\n }\n\n // CLI engines don't need an API key\n if (isCLIEngine(engineType)) {\n let model = \"\";\n if (engineType === \"claude-code\") {\n model = config.claudeCodeModel || \"\";\n }\n return { engine: engineType as AgentEngine, apiKey: \"\", model };\n }\n\n // Claude OAuth resolves its token at call time in the engine adapter\n let apiKey: string | undefined;\n if (engineType === \"claude-oauth\") {\n if (!hasValidOAuthToken()) {\n throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n }\n apiKey = \"oauth\"; // Token resolved fresh in engine adapter (auto-refresh)\n } else {\n apiKey = getApiKeyForEngine(engineType, config);\n }\n if (!apiKey) {\n throw new Error(`API key not configured for ${engineType}. Open Settings to add one.`);\n }\n\n let model: string;\n switch (engineType) {\n case \"anthropic-api\":\n case \"claude-oauth\":\n model = config.anthropicApiModel || \"claude-sonnet-4-6\";\n break;\n case \"openai-api\":\n model = config.openaiApiModel || \"gpt-4o\";\n break;\n case \"gemini-api\":\n model = \"gemini-2.5-flash\";\n break;\n default:\n model = \"\";\n }\n\n return { engine: engineType as AgentEngine, apiKey, model };\n}\n\n/**\n * Run the agentic pipeline for a user message.\n * Returns the PipelineResult. The caller (WebSocket handler in server.ts)\n * is responsible for applying the result to the session and committing.\n */\nexport async function handleAgenticGenerate(\n userMessage: string,\n onEvent: (event: PipelineEvent) => void,\n fileIds?: string[],\n): Promise<PipelineResult> {\n const session = getSession();\n if (!session) throw new Error(\"No active session\");\n\n const capturedSessionId = session.id;\n generatingSessionId = capturedSessionId;\n\n try {\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n const concurrency = config.agenticConcurrency || 20;\n\n const snapshot = takeSnapshot();\n\n // Build library module list for intent analyzer\n const library = getModuleLibrary();\n const currentModuleNames = new Set(\n snapshot.modules.map((m) => m.moduleName),\n );\n const libraryModules = library\n .filter((e) => !currentModuleNames.has(e.module.moduleName))\n .map((e) => ({ name: e.module.moduleName, usedIn: e.usedIn }));\n\n const result = await runAgentPipeline(\n userMessage,\n snapshot,\n engine,\n apiKey,\n model,\n concurrency,\n onEvent,\n libraryModules,\n );\n\n // Verify session hasn't changed during generation\n const current = getSession();\n if (!current || current.id !== capturedSessionId) {\n log.warn(\"ai-handler\", \"Session changed during agentic generation — discarding output\");\n throw new Error(\"Session changed during generation\");\n }\n\n return result;\n } finally {\n generatingSessionId = null;\n }\n}\n\n/**\n * Apply a pipeline result to the current session.\n * Called by the WebSocket handler after successful pipeline execution.\n */\nexport function applyPipelineResult(result: PipelineResult, pipelineMeta?: PipelineMetadata): void {\n // Update modules in the session (merges new + updates existing)\n updateModules({\n modules: result.modules,\n sharedCss: result.sharedCss,\n sharedJs: result.sharedJs,\n });\n\n // Set the module order from the pipeline result\n reorderModules(result.moduleOrder);\n\n // Add assistant message to chat history with pipeline metadata\n addMessage(\"assistant\", result.assistantMessage, pipelineMeta);\n saveSession();\n}\n\n/**\n * Check if agentic mode should be used for the current configuration.\n */\nexport function shouldUseAgenticMode(): {\n useAgentic: boolean;\n needsPrompt: boolean;\n reason?: string;\n} {\n const config = loadConfig();\n const engine = config.aiEngine || detectDefaultEngine();\n\n if (!isAgenticCapable(engine)) {\n return {\n useAgentic: false,\n needsPrompt: false,\n reason: \"Agentic pipeline is not available for this engine.\",\n };\n }\n\n if (config.agenticMode === undefined) {\n return { useAgentic: false, needsPrompt: true };\n }\n\n return { useAgentic: config.agenticMode, needsPrompt: false };\n}\n","/**\n * AI-powered design extraction — analyzes a theme's CSS/HTML/fields\n * and generates a structured design system document (styleguide).\n *\n * Supports all configured AI engines: Anthropic API, OpenAI API, Gemini API,\n * Claude Code CLI, Gemini CLI, Codex CLI.\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { resolveAsset, readFile } from \"../utils/fs.js\";\nimport { loadConfig, getApiKeyForEngine, type AIEngineType } from \"../utils/config.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy-loaded Anthropic SDK\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<typeof import(\"@anthropic-ai/sdk\").default> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\n// ---------------------------------------------------------------------------\n// Theme file collection\n// ---------------------------------------------------------------------------\n\nconst MAX_CONTENT_CHARS = 80_000;\n\nfunction safeRead(path: string): string {\n try { return readFileSync(path, \"utf-8\"); } catch { return \"\"; }\n}\n\n/**\n * Collect CSS, HTML, and fields.json from a theme directory.\n * Prioritizes CSS (most design-relevant), then HTML, then fields.\n */\nexport function collectThemeFiles(themePath: string): string {\n const parts: string[] = [];\n let totalChars = 0;\n\n function addSection(label: string, content: string): boolean {\n if (!content.trim()) return true;\n const section = `\\n### ${label}\\n\\`\\`\\`\\n${content}\\n\\`\\`\\`\\n`;\n if (totalChars + section.length > MAX_CONTENT_CHARS) return false;\n parts.push(section);\n totalChars += section.length;\n return true;\n }\n\n // Theme metadata\n const themeJson = safeRead(join(themePath, \"theme.json\"));\n if (themeJson) addSection(\"theme.json\", themeJson);\n\n // Shared CSS files (highest priority for design tokens)\n const cssDir = join(themePath, \"css\");\n if (existsSync(cssDir)) {\n for (const f of readdirSync(cssDir).filter((f) => f.endsWith(\".css\"))) {\n if (!addSection(`css/${f}`, safeRead(join(cssDir, f)))) break;\n }\n }\n\n // Module CSS files\n const modulesDir = join(themePath, \"modules\");\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const css = safeRead(join(modPath, \"module.css\"));\n if (css && !addSection(`modules/${dir}/module.css`, css)) break;\n }\n }\n\n // Module HTML templates (for component patterns)\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const html = safeRead(join(modPath, \"module.html\"));\n if (html && !addSection(`modules/${dir}/module.html`, html)) break;\n }\n }\n\n // Module fields.json (for content structure)\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const fields = safeRead(join(modPath, \"fields.json\"));\n if (fields && !addSection(`modules/${dir}/fields.json`, fields)) break;\n }\n }\n\n return parts.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Extraction prompt\n// ---------------------------------------------------------------------------\n\nlet _extractionPrompt = \"\";\nfunction getExtractionPrompt(): string {\n if (!_extractionPrompt) {\n try { _extractionPrompt = readFile(resolveAsset(\"extraction-prompt.md\")); } catch { _extractionPrompt = \"\"; }\n }\n return _extractionPrompt;\n}\n\n// ---------------------------------------------------------------------------\n// CLI subprocess helper\n// ---------------------------------------------------------------------------\n\nfunction spawnCLIForExtraction(bin: string, args: string[], prompt: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const env = { ...process.env };\n delete env.CLAUDECODE; // prevent recursion if running inside Claude Code\n\n const child = spawn(bin, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) =>\n reject(new Error(`${bin} failed to start: ${err.message}`))\n );\n\n child.on(\"close\", (code) => {\n if (code === 0 || stdout.trim()) resolve(stdout.trim());\n else reject(new Error(`${bin} exited with code ${code}: ${stderr.trim()}`));\n });\n\n child.stdin.write(prompt);\n child.stdin.end();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main extraction function\n// ---------------------------------------------------------------------------\n\nexport interface ExtractionProgress {\n status: string;\n}\n\n/**\n * Extract a design system document from a theme directory using AI.\n * Uses the currently configured AI engine.\n * Returns a markdown string suitable for saving as a styleguide.\n */\nexport async function extractDesignContext(\n themePath: string,\n onProgress?: (p: ExtractionProgress) => void,\n): Promise<string> {\n onProgress?.({ status: \"Collecting theme files...\" });\n const themeContent = collectThemeFiles(themePath);\n if (!themeContent.trim()) {\n throw new Error(\"No CSS, HTML, or fields.json files found in theme.\");\n }\n\n const systemPrompt = getExtractionPrompt();\n if (!systemPrompt) {\n throw new Error(\"Extraction prompt not found (assets/extraction-prompt.md).\");\n }\n\n const userMessage = `Analyze this HubSpot CMS theme and extract the design system:\\n${themeContent}`;\n\n onProgress?.({ status: \"Analyzing design patterns...\" });\n\n const config = loadConfig();\n const engine = (config.aiEngine || \"anthropic-api\") as AIEngineType;\n let text = \"\";\n\n switch (engine) {\n // ----- API engines -----\n case \"anthropic-api\":\n case \"api\": {\n const apiKey = getApiKeyForEngine(\"anthropic-api\");\n if (!apiKey) throw new Error(\"Anthropic API key not configured. Open Settings to add one.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ apiKey });\n const response = await client.messages.create({\n model: config.anthropicApiModel || \"claude-sonnet-4-6\",\n max_tokens: 8000,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userMessage }],\n });\n text = response.content\n .filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n break;\n }\n\n case \"claude-oauth\": {\n const { getValidAccessToken, OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } = await import(\"../utils/claude-oauth.js\");\n const accessToken = await getValidAccessToken();\n if (!accessToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ authToken: accessToken, defaultHeaders: OAUTH_EXTRA_HEADERS } as any);\n const response = await client.messages.create({\n model: config.anthropicApiModel || \"claude-sonnet-4-6\",\n max_tokens: 8000,\n system: [\n { type: \"text\", text: OAUTH_SYSTEM_PREFIX },\n { type: \"text\", text: systemPrompt },\n ] as any,\n messages: [{ role: \"user\", content: userMessage }],\n });\n text = response.content\n .filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n break;\n }\n\n case \"openai-api\": {\n const apiKey = getApiKeyForEngine(\"openai-api\");\n if (!apiKey) throw new Error(\"OpenAI API key not configured. Open Settings to add one.\");\n\n const resp = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({\n model: config.openaiApiModel || \"gpt-4o\",\n max_tokens: 8000,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userMessage },\n ],\n }),\n });\n if (!resp.ok) throw new Error(`OpenAI API error: ${resp.status} ${await resp.text()}`);\n const data = await resp.json() as { choices: { message: { content: string } }[] };\n text = data.choices?.[0]?.message?.content || \"\";\n break;\n }\n\n case \"gemini-api\": {\n const apiKey = getApiKeyForEngine(\"gemini-api\");\n if (!apiKey) throw new Error(\"Gemini API key not configured. Open Settings to add one.\");\n\n const model = config.geminiApiModel || \"gemini-2.5-flash\";\n const resp = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n system_instruction: { parts: [{ text: systemPrompt }] },\n contents: [{ role: \"user\", parts: [{ text: userMessage }] }],\n generationConfig: { maxOutputTokens: 8000 },\n }),\n },\n );\n if (!resp.ok) throw new Error(`Gemini API error: ${resp.status} ${await resp.text()}`);\n const data = await resp.json() as { candidates: { content: { parts: { text: string }[] } }[] };\n text = data.candidates?.[0]?.content?.parts?.map((p) => p.text).join(\"\") || \"\";\n break;\n }\n\n // ----- CLI engines -----\n case \"claude-code\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n text = await spawnCLIForExtraction(\"claude\", args, combinedPrompt);\n break;\n }\n\n case \"gemini-cli\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n text = await spawnCLIForExtraction(\"gemini\", [], combinedPrompt);\n break;\n }\n\n case \"codex-cli\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n text = await spawnCLIForExtraction(\"codex\", [], combinedPrompt);\n break;\n }\n\n default:\n throw new Error(`Unknown AI engine: ${engine}. Open Settings to configure one.`);\n }\n\n if (!text.trim()) {\n throw new Error(\"AI returned empty response.\");\n }\n\n onProgress?.({ status: \"Design extraction complete.\" });\n return text;\n}\n","/**\n * Brand Voice Extractor\n *\n * Lightweight AI call that analyzes rendered preview HTML to extract writing\n * style, tone, and voice guidelines. Runs on-demand from the brand assets panel.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport { log } from \"../../log.js\";\n\n/**\n * Extract a brand voice guide from the rendered preview HTML.\n * The preview has all HubL resolved to actual field default values, so the AI\n * sees real copy instead of `{{ module.field_name }}` placeholders.\n *\n * Returns a markdown string, or null if extraction fails.\n */\nexport async function extractBrandvoice(\n previewHtml: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n): Promise<string | null> {\n if (!previewHtml || previewHtml.length < 50) return null;\n\n const systemPrompt = `You are a brand strategist. Analyze the rendered landing page HTML below and extract a concise brand voice guide. The HTML contains the actual text content with all template variables resolved to their default values.\n\nReturn a markdown document with these sections (skip any section where the content provides no signal):\n\n## Tone\nOverall communication style (e.g., confident but approachable, technical but clear, playful, authoritative).\n\n## Voice Characteristics\n3-5 bullet points describing how this brand \"sounds\" (e.g., \"Uses short, punchy sentences\", \"Addresses reader directly with 'you'\").\n\n## Vocabulary\n- **Preferred words**: Terms used repeatedly or intentionally\n- **Avoided patterns**: Any notable absences or anti-patterns\n\n## Sentence Style\nTypical sentence length, structure, use of questions, imperatives, etc.\n\n## Dos and Don'ts\n3-4 practical rules for writing in this voice (e.g., \"Do: Lead with benefits, not features\", \"Don't: Use jargon without context\").\n\nKeep it actionable — this guide will be fed to AI to maintain consistent copy across pages.`;\n\n try {\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages: [{ role: \"user\", content: previewHtml }],\n maxTokens: 1000,\n });\n\n const text = result.type === \"text\" ? result.text : JSON.stringify(result.data);\n if (!text || text.trim().length < 20) return null;\n\n log.info(\"brandvoice-extractor\", `Extracted brand voice (${text.length} chars)`);\n return text.trim();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(\"brandvoice-extractor\", `Brand voice extraction failed: ${msg}`);\n return null;\n }\n}\n","/**\n * Post-pipeline stage: Theme Context Extractor\n *\n * Lightweight AI call that extracts a product/company brief from the rendered\n * preview HTML. Runs in the background after pipeline completion — never blocks\n * the preview or user interaction.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport { log } from \"../../log.js\";\n\n/**\n * Extract a product/company context brief from the rendered preview HTML.\n * The preview has all HubL resolved to actual field default values, so the AI\n * sees real copy instead of `{{ module.field_name }}` placeholders.\n *\n * Returns a markdown string, or null if extraction fails or produces nothing useful.\n */\nexport async function extractThemeContext(\n previewHtml: string,\n existingContext: string | undefined,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n): Promise<string | null> {\n if (!previewHtml || previewHtml.length < 50) return null;\n\n const existingNote = existingContext\n ? `\\n\\nExisting product context (update if the new content adds info, keep what's still accurate):\\n${existingContext}`\n : \"\";\n\n const systemPrompt = `You are a content analyst. Extract a concise product/company brief from the rendered landing page HTML below. The HTML contains the actual text content (headings, paragraphs, button labels, image alt text, etc.) with all template variables resolved to their default values.\n\nReturn a markdown document with these sections (skip any section where the content provides no information):\n\n## Product / Company\nName, one-line description, and what it does.\n\n## Value Propositions\nKey benefits or features (bulleted list).\n\n## Target Audience\nWho this product/service is for.\n\n## Tone & Voice\nCommunication style (e.g., professional, casual, technical, friendly).\n\n## Key Terminology\nSpecific terms, product names, or branded language used consistently.\n\nKeep it concise — this brief is used as context for AI-generated content on other pages in the same theme.${existingNote}`;\n\n try {\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages: [{ role: \"user\", content: previewHtml }],\n maxTokens: 1000,\n });\n\n const text = result.type === \"text\" ? result.text : JSON.stringify(result.data);\n if (!text || text.trim().length < 20) return null;\n\n log.info(\"context-extractor\", `Extracted theme context (${text.length} chars)`);\n return text.trim();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(\"context-extractor\", `Theme context extraction failed: ${msg}`);\n return null;\n }\n}\n","import { buildProgram } from \"./cli/program.js\";\n\nconst program = buildProgram();\nprogram.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import { Command } from \"commander\";\nimport { wizardCommand } from \"../commands/wizard.js\";\nimport { initCommand } from \"../commands/init.js\";\nimport { convertCommand } from \"../commands/convert.js\";\nimport { uploadCommand } from \"../commands/upload.js\";\nimport { doctorCommand } from \"../commands/doctor.js\";\nimport { vibeCommand } from \"../commands/vibe.js\";\nimport { getVersion } from \"../utils/fs.js\";\n\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"vibespot\")\n .description(\n \"AI-powered HubSpot CMS landing page builder\"\n )\n .version(getVersion())\n .action(vibeCommand);\n\n program\n .command(\"wizard\")\n .description(\"Classic CLI wizard — step-by-step conversion flow\")\n .action(wizardCommand);\n\n program\n .command(\"init\")\n .description(\"Check and install required tools\")\n .action(initCommand);\n\n program\n .command(\"convert\")\n .description(\"Convert a React project to HubSpot modules\")\n .action(convertCommand);\n\n program\n .command(\"upload\")\n .description(\"Upload theme to HubSpot\")\n .action(uploadCommand);\n\n program\n .command(\"doctor\")\n .description(\"Diagnose environment issues\")\n .action(doctorCommand);\n\n return program;\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runPreflight } from \"../wizard/preflight.js\";\nimport { setupSource } from \"../wizard/source.js\";\nimport { setupTheme } from \"../wizard/theme-setup.js\";\nimport { runConversion } from \"../wizard/conversion.js\";\nimport { runUpload } from \"../wizard/uploader.js\";\nimport { showNextSteps } from \"../wizard/next-steps.js\";\nimport { saveConfig } from \"../utils/config.js\";\n\nexport async function wizardCommand(): Promise<void> {\n printBanner();\n\n // Step 1: Preflight checks\n const preflight = await runPreflight();\n\n // Step 2: Source setup\n const source = await setupSource();\n saveConfig({ lastSourcePath: source.sourceDir });\n\n // Step 3: Theme setup\n const themeInfo = await setupTheme();\n saveConfig({ lastThemePath: themeInfo.themePath });\n\n // Step 4: AI conversion\n await runConversion({\n aiEngine: preflight.aiEngine,\n model: preflight.model,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n });\n\n // Step 5: Upload\n await runUpload(themeInfo.themePath);\n\n // Step 6: Next steps + optional cleanup\n await showNextSteps({\n portalId: preflight.portalId,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n wasCloned: source.wasCloned,\n });\n}\n","import { theme } from \"./theme.js\";\nimport { getVersion } from \"../utils/fs.js\";\n\nexport function printBanner() {\n const v = theme.vibes;\n const o = theme.accent; // HubSpot orange for \"Spot\"\n const m = theme.muted;\n\n // Block-pixel ASCII art: \"vibe ≋ Spot\"\n const lines = [\n `${v(\"██ ██ ██ █████ ▄▄▄▄▄\")}${v(\" ≋≋≋≋≋≋≋≋ \")}${o(\"▄▄▄▄▄ █████ ▄▄▄▄ ▀▀██▀▀\")}`,\n `${v(\"██ ██ ██ ██ ██ ██ \")}${v(\" ≋≋≋≋≋≋ \")}${o(\"██ ██ ██ ██ ██ ██ \")}`,\n `${v(\"██ ██ ██ █████ ████ \")}${v(\" ≋≋≋≋ \")}${o(\"▀▀▀▄ █████ ██ ██ ██ \")}`,\n `${v(\" █▄▄█▀ ██ ██ ██ ██ \")}${v(\" ≋≋≋≋≋≋ \")}${o(\" ██ ██ ██ ██ ██ \")}`,\n `${v(\" ▀▀▀ ██ █████ ▀▀▀▀▀\")}${v(\" ≋≋≋≋≋≋≋≋ \")}${o(\"▀▀▀▀ ██ ▀▀▀▀ ██ \")}`,\n ];\n\n console.log();\n for (const line of lines) {\n console.log(` ${line}`);\n }\n console.log();\n console.log(` ${m(\"AI-powered HubSpot Landing Pages\")} ${theme.dim(`v${getVersion()}`)}`);\n console.log();\n}\n","import chalk from \"chalk\";\n\nexport const palette = {\n accent: \"#FF7A59\",\n accentBright: \"#FF9A7A\",\n success: \"#00BDA5\",\n info: \"#0066FF\",\n warn: \"#FFB020\",\n error: \"#E23D2D\",\n muted: \"#8B8D91\",\n vibes: \"#00BDD6\",\n};\n\nconst noColor = !!process.env.NO_COLOR;\n\nfunction hex(color: string) {\n return noColor ? chalk : chalk.hex(color);\n}\n\nexport const theme = {\n accent: hex(palette.accent),\n accentBright: hex(palette.accentBright),\n success: hex(palette.success),\n info: hex(palette.info),\n warn: hex(palette.warn),\n error: hex(palette.error),\n muted: hex(palette.muted),\n vibes: hex(palette.vibes),\n heading: noColor ? chalk.bold : chalk.bold.hex(palette.accent),\n command: hex(palette.accentBright),\n dim: chalk.dim,\n bold: chalk.bold,\n};\n","import {\n detectNode,\n detectGit,\n detectHubSpotCLI,\n detectClaudeCode,\n detectGeminiCLI,\n detectCodexCLI,\n detectHubSpotAuth,\n hasAnthropicKey,\n nodeVersionOk,\n} from \"../utils/detect.js\";\nimport { hasValidOAuthToken } from \"../utils/claude-oauth.js\";\nimport { run, runPassthrough } from \"../utils/shell.js\";\nimport { saveConfig, loadConfig, getHubSpotPak, getActiveHubSpotAccount, addHubSpotAccount, type AIEngineType } from \"../utils/config.js\";\nimport { validatePak } from \"../hubspot/api.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport interface PreflightResult {\n aiEngine: AIEngineType;\n model?: string;\n portalId: string;\n portalName: string;\n}\n\nexport async function runPreflight(): Promise<PreflightResult> {\n await ui.intro(\"Checking your environment\");\n\n // Node.js\n const node = detectNode();\n if (!node.found) {\n ui.logError(\"Node.js not found. Install it from https://nodejs.org\");\n process.exit(1);\n }\n if (!nodeVersionOk(node.version)) {\n ui.logError(\n `Node.js ${node.version} is too old. Version 18+ required. Update at https://nodejs.org`\n );\n process.exit(1);\n }\n ui.logSuccess(`Node.js v${node.version}`);\n\n // Git\n const git = detectGit();\n if (!git.found) {\n ui.logError(\"Git not found. Install it from https://git-scm.com\");\n process.exit(1);\n }\n ui.logSuccess(`Git ${git.version}`);\n\n // HubSpot connection\n const config = loadConfig();\n const useApi = config.hubspotUploadMode !== \"cli\";\n let portalId = \"\";\n let portalName = \"\";\n\n if (useApi) {\n // API mode — check for PAK in config\n let pak = getHubSpotPak();\n const acct = getActiveHubSpotAccount();\n\n if (!pak) {\n ui.logWarn(\"No HubSpot account connected\");\n await ui.note(\n \"You need a Personal Access Key to deploy themes.\\n\" +\n \"Create one at: https://app.hubspot.com/l/personal-access-key\\n\" +\n \"Make sure the Content scope is enabled.\",\n \"HubSpot connection required\"\n );\n\n const key = await ui.text({\n message: \"Paste your Personal Access Key:\",\n placeholder: \"pat-na1-...\",\n validate: (v) => v.trim() ? undefined : \"Key is required\",\n });\n\n const s = await ui.spinner();\n s.start(\"Validating key...\");\n try {\n const info = await validatePak(key);\n addHubSpotAccount(key, info.portalId, info.portalName, info.dataCenter);\n pak = key;\n portalId = info.portalId;\n portalName = info.portalName;\n s.stop(`Connected to ${info.portalName} (${info.portalId})`);\n } catch (err) {\n s.stop(\"Validation failed\");\n ui.logError(`Invalid key: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n } else {\n portalId = acct?.portalId || \"\";\n portalName = acct?.portalName || \"\";\n ui.logSuccess(\n `HubSpot${portalName ? `: ${portalName}` : \"\"}${portalId ? ` (${portalId})` : \"\"} — API mode`\n );\n }\n } else {\n // CLI mode — require hs CLI\n let hs = detectHubSpotCLI();\n if (!hs.found) {\n ui.logWarn(\"HubSpot CLI not found\");\n const install = await ui.confirm({ message: \"Install HubSpot CLI globally?\" });\n if (!install) {\n ui.logError(\"HubSpot CLI is required in CLI mode. Install: npm install -g @hubspot/cli\");\n process.exit(1);\n }\n const s = await ui.spinner();\n s.start(\"Installing HubSpot CLI...\");\n const result = run(\"npm install -g @hubspot/cli\");\n if (!result.success) {\n s.stop(\"Failed\");\n ui.logError(\"Try: npm install -g @hubspot/cli\");\n process.exit(1);\n }\n hs = detectHubSpotCLI();\n s.stop(`HubSpot CLI v${hs.version} installed`);\n } else {\n ui.logSuccess(`HubSpot CLI v${hs.version}`);\n }\n\n let auth = detectHubSpotAuth();\n if (!auth.authenticated) {\n ui.logWarn(\"HubSpot not authenticated\");\n const doAuth = await ui.confirm({ message: \"Run `hs init` now?\" });\n if (!doAuth) {\n ui.logError(\"Run `hs init` manually.\");\n process.exit(1);\n }\n const s = await ui.spinner();\n s.start(\"Waiting for HubSpot authentication...\");\n const authOk = runPassthrough(\"hs init\");\n if (!authOk) {\n s.stop(\"Authentication failed\");\n process.exit(1);\n }\n auth = detectHubSpotAuth();\n s.stop(`Connected to portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`);\n } else {\n ui.logSuccess(\n `HubSpot portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`\n );\n }\n portalId = auth.portalId;\n portalName = auth.portalName;\n }\n\n // AI Engine selection\n const claude = detectClaudeCode();\n const gemini = detectGeminiCLI();\n const codex = detectCodexCLI();\n const hasKey = hasAnthropicKey();\n\n const engineLabels: Record<AIEngineType, string> = {\n \"claude-code\": \"Claude Code\",\n \"api\": \"Anthropic API\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-api\": \"Gemini API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"codex-cli\": \"OpenAI Codex\",\n };\n\n const hasOAuth = hasValidOAuthToken();\n\n let aiEngine: AIEngineType;\n const lastUsed = config.aiEngine;\n\n // Always build list of available engines\n const available: { value: AIEngineType; label: string; hint: string }[] = [];\n\n if (claude.found) {\n available.push({\n value: \"claude-code\",\n label: \"Claude Code\",\n hint: lastUsed === \"claude-code\"\n ? \"last used — recommended\"\n : \"uses your existing Claude subscription — recommended\",\n });\n }\n if (hasOAuth) {\n available.push({\n value: \"claude-oauth\",\n label: \"Claude (OAuth)\",\n hint: lastUsed === \"claude-oauth\"\n ? \"last used\"\n : \"uses your Claude Pro/Max subscription via OAuth\",\n });\n }\n if (gemini.found) {\n available.push({\n value: \"gemini-cli\",\n label: \"Gemini CLI\",\n hint: lastUsed === \"gemini-cli\"\n ? \"last used\"\n : \"uses your existing Gemini setup\",\n });\n }\n if (codex.found) {\n available.push({\n value: \"codex-cli\",\n label: \"OpenAI Codex\",\n hint: lastUsed === \"codex-cli\"\n ? \"last used\"\n : \"uses your existing OpenAI setup\",\n });\n }\n if (hasKey) {\n available.push({\n value: \"api\",\n label: \"Anthropic API\",\n hint: lastUsed === \"api\"\n ? \"last used\"\n : \"uses your API key\",\n });\n }\n\n // Sort last-used engine to the top\n if (lastUsed) {\n available.sort((a, b) =>\n a.value === lastUsed ? -1 : b.value === lastUsed ? 1 : 0\n );\n }\n\n if (available.length === 1) {\n // Only one option — use it automatically\n aiEngine = available[0].value;\n ui.logSuccess(`AI engine: ${engineLabels[aiEngine]} (auto-detected)`);\n } else if (available.length > 1) {\n // Multiple available — always ask\n aiEngine = await ui.select({\n message: \"Choose your AI engine:\",\n options: available,\n });\n } else {\n // None available — guide the user\n await ui.note(\n \"You need an AI coding assistant to power the conversion.\\n\\n\" +\n `${theme.bold(\"Option 1:\")} Install Claude Code ${theme.muted(\"(recommended)\")}\\n` +\n \" https://claude.ai/code\\n\\n\" +\n `${theme.bold(\"Option 2:\")} Install Gemini CLI\\n` +\n \" https://github.com/google-gemini/gemini-cli\\n\\n\" +\n `${theme.bold(\"Option 3:\")} Install OpenAI Codex\\n` +\n \" https://github.com/openai/codex\\n\\n\" +\n `${theme.bold(\"Option 4:\")} Set an Anthropic API key\\n` +\n \" export ANTHROPIC_API_KEY=sk-ant-...\\n\" +\n \" (get one at https://console.anthropic.com)\",\n \"AI engine required\"\n );\n\n aiEngine = await ui.select({\n message: \"Which will you set up?\",\n options: [\n {\n value: \"claude-code\" as const,\n label: \"Claude Code\",\n hint: \"I'll install it now\",\n },\n {\n value: \"gemini-cli\" as const,\n label: \"Gemini CLI\",\n hint: \"I'll install it now\",\n },\n {\n value: \"codex-cli\" as const,\n label: \"OpenAI Codex\",\n hint: \"I'll install it now\",\n },\n {\n value: \"api\" as const,\n label: \"Anthropic API\",\n hint: \"I'll enter my key\",\n },\n ],\n });\n\n if (aiEngine === \"api\") {\n const key = await ui.text({\n message: \"Enter your Anthropic API key:\",\n placeholder: \"sk-ant-api03-...\",\n validate: (v) =>\n v.startsWith(\"sk-ant-\") ? undefined : \"Key should start with sk-ant-\",\n });\n process.env.ANTHROPIC_API_KEY = key;\n saveConfig({ anthropicApiKey: key });\n }\n }\n\n // Model selection for Claude Code\n let model: string | undefined;\n if (aiEngine === \"claude-code\") {\n model = await ui.select({\n message: \"Which model?\",\n options: [\n { value: \"sonnet\", label: \"Sonnet\", hint: \"fast, recommended\" },\n { value: \"opus\", label: \"Opus\", hint: \"most capable\" },\n { value: \"haiku\", label: \"Haiku\", hint: \"fastest, cheapest\" },\n ],\n });\n }\n\n saveConfig({ aiEngine });\n\n await ui.outro(\"Environment ready!\");\n\n return {\n aiEngine,\n model,\n portalId,\n portalName,\n };\n}\n","import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { readFileSync, existsSync, readdirSync } from \"node:fs\";\nimport { run } from \"./shell.js\";\nimport { loadConfig, maskApiKey, isCliToolEnabled, getActiveHubSpotAccount, type AIEngineType, type HubSpotAccountConfig } from \"./config.js\";\nimport { detectDataCenterFromPak } from \"../hubspot/api.js\";\nimport { hasValidOAuthToken, getOAuthTokenInfo } from \"./claude-oauth.js\";\n\nconst whichCmd = process.platform === \"win32\" ? \"where\" : \"which\";\n\nexport interface ToolInfo {\n name: string;\n found: boolean;\n version: string;\n path: string;\n}\n\nexport function detectNode(): ToolInfo {\n const result = run(\"node --version\");\n return {\n name: \"Node.js\",\n found: result.success,\n version: result.stdout.replace(/^v/, \"\"),\n path: run(`${whichCmd} node`).stdout,\n };\n}\n\nexport function detectGit(): ToolInfo {\n const result = run(\"git --version\");\n return {\n name: \"Git\",\n found: result.success,\n version: result.stdout.replace(\"git version \", \"\"),\n path: run(`${whichCmd} git`).stdout,\n };\n}\n\nexport function detectHubSpotCLI(): ToolInfo {\n const result = run(\"hs --version\");\n return {\n name: \"HubSpot CLI\",\n found: result.success,\n version: result.stdout,\n path: run(`${whichCmd} hs`).stdout,\n };\n}\n\nexport interface CLIToolInfo extends ToolInfo {\n authenticated: boolean;\n authDetail: string;\n}\n\nexport function detectClaudeCode(): CLIToolInfo {\n const result = run(\"claude --version\");\n if (!result.success) {\n return { name: \"Claude Code\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Check for Claude Code auth by looking for credentials\n // Claude Code stores OAuth tokens in ~/.claude/ — if the dir exists with credentials, it's authenticated\n const claudeDir = join(homedir(), \".claude\");\n let authenticated = false;\n let authDetail = \"Not signed in — run `claude` to authenticate\";\n\n try {\n if (existsSync(claudeDir)) {\n // If .claude dir exists with auth files, consider it authenticated\n const files = readdirSync(claudeDir);\n const hasAuth = files.some(f =>\n f.includes(\"credentials\") || f.includes(\"auth\") || f.includes(\"token\") || f === \".credentials.json\"\n );\n if (hasAuth || files.length > 2) {\n // Has some config — likely authenticated\n authenticated = true;\n authDetail = \"Authenticated\";\n }\n }\n } catch { /* ignore */ }\n\n return {\n name: \"Claude Code\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} claude`).stdout,\n authenticated,\n authDetail,\n };\n}\n\nexport function detectDataCenter(portalId: string): string {\n try {\n const configPath = join(homedir(), \".hscli\", \"config.yml\");\n if (!existsSync(configPath)) return \"na1\";\n\n const config = readFileSync(configPath, \"utf-8\");\n\n // Find the account block matching this portal ID\n const accountIdx = config.indexOf(`accountId: ${portalId}`);\n if (accountIdx === -1) return \"na1\";\n\n // Look for personalAccessKey after this account entry\n const keyIdx = config.indexOf(\"personalAccessKey:\", accountIdx);\n if (keyIdx === -1) return \"na1\";\n\n // Extract the key value (next non-empty trimmed line after the label)\n const keySection = config.slice(keyIdx, keyIdx + 300);\n const keyMatch = keySection.match(/personalAccessKey:[\\s>-]*\\n\\s+(\\S+)/);\n if (!keyMatch) return \"na1\";\n\n // CiRldTE = base64 prefix for \"eu1\" datacenter in HubSpot personal access keys\n if (keyMatch[1].startsWith(\"CiRldTE\")) return \"eu1\";\n } catch {\n // Fall through to default\n }\n return \"na1\";\n}\n\nexport interface HubSpotAccount {\n name: string;\n portalId: string;\n authType: string;\n isDefault: boolean;\n}\n\nexport function detectHubSpotAuth(): {\n authenticated: boolean;\n portalName: string;\n portalId: string;\n accounts: HubSpotAccount[];\n} {\n const result = run(\"hs accounts list\");\n if (!result.success || !result.stdout) {\n return { authenticated: false, portalName: \"\", portalId: \"\", accounts: [] };\n }\n\n // Parse all accounts from the table\n const accounts: HubSpotAccount[] = [];\n let defaultName = \"\";\n let defaultId = \"\";\n\n // Default account line: \"Account: name [standard] (123456)\"\n const defaultMatch = result.stdout.match(/Account:\\s*(.+?)\\s*\\((\\d+)\\)/);\n if (defaultMatch) {\n defaultName = defaultMatch[1].trim();\n defaultId = defaultMatch[2].trim();\n }\n\n // Parse table rows: \"name [standard] 123456 personalaccesskey\"\n const lines = result.stdout.split(\"\\n\");\n for (const line of lines) {\n const tableMatch = line.match(/^\\s*(.+?)\\s+(\\d{5,})\\s+(.*)/);\n if (tableMatch && !/Account ID/i.test(line) && !/^-+$/.test(line.trim()) && !/^Name\\s/i.test(line.trim())) {\n const name = tableMatch[1].trim();\n const portalId = tableMatch[2].trim();\n const authType = tableMatch[3]?.trim() || \"unknown\";\n accounts.push({\n name,\n portalId,\n authType,\n isDefault: portalId === defaultId,\n });\n }\n }\n\n if (defaultMatch) {\n return {\n authenticated: true,\n portalName: defaultName,\n portalId: defaultId,\n accounts,\n };\n }\n\n // Fallback: at least one account in table\n if (accounts.length > 0) {\n return {\n authenticated: true,\n portalName: accounts[0].name,\n portalId: accounts[0].portalId,\n accounts,\n };\n }\n\n return {\n authenticated: result.stdout.length > 0,\n portalName: \"\",\n portalId: \"\",\n accounts: [],\n };\n}\n\nexport function detectGeminiCLI(): CLIToolInfo {\n const result = run(\"gemini --version\");\n if (!result.success) {\n return { name: \"Gemini CLI\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Gemini CLI uses Google Cloud auth — check for application default credentials\n const adcPath = join(homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\");\n const hasAdc = existsSync(adcPath);\n // Also check GOOGLE_API_KEY / GEMINI_API_KEY env vars\n const hasEnvKey = !!(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GOOGLE_AI_API_KEY);\n const authenticated = hasAdc || hasEnvKey;\n\n return {\n name: \"Gemini CLI\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} gemini`).stdout,\n authenticated,\n authDetail: authenticated ? \"Authenticated\" : \"Run `gemini` to sign in with Google\",\n };\n}\n\nexport function detectCodexCLI(): CLIToolInfo {\n const result = run(\"codex --version\");\n if (!result.success) {\n return { name: \"OpenAI Codex CLI\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Codex CLI supports OAuth (stored in ~/.codex/auth.json) or OPENAI_API_KEY\n const hasKey = !!(process.env.OPENAI_API_KEY);\n let hasOAuth = false;\n try {\n const authFile = join(homedir(), \".codex\", \"auth.json\");\n if (existsSync(authFile)) {\n const content = readFileSync(authFile, \"utf-8\");\n hasOAuth = content.length > 10; // non-empty auth file\n }\n } catch { /* ignore */ }\n\n const authenticated = hasKey || hasOAuth;\n const detail = hasOAuth ? \"Authenticated (OAuth)\" : hasKey ? \"Authenticated (API key)\" : \"Not authenticated\";\n return {\n name: \"OpenAI Codex CLI\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} codex`).stdout,\n authenticated,\n authDetail: detail,\n };\n}\n\nexport function detectGitHubCLI(): ToolInfo {\n const result = run(\"gh --version\");\n return {\n name: \"GitHub CLI\",\n found: result.success,\n version: result.stdout.split(\"\\n\")[0]?.replace(\"gh version \", \"\").split(\" \")[0] || \"\",\n path: run(`${whichCmd} gh`).stdout,\n };\n}\n\nexport function detectGitHubAuth(): { authenticated: boolean; username: string } {\n const result = run(\"gh auth status 2>&1\");\n if (!result.success && !result.stdout) {\n return { authenticated: false, username: \"\" };\n }\n // gh auth status outputs to stderr, but our run() captures both\n const output = result.stdout || result.stderr || \"\";\n const match = output.match(/Logged in to github\\.com.*account\\s+(\\S+)/);\n if (match) {\n return { authenticated: true, username: match[1] };\n }\n // Alternate pattern: \"account borismichel\"\n const altMatch = output.match(/account\\s+(\\S+)/);\n if (altMatch && output.includes(\"Logged in\")) {\n return { authenticated: true, username: altMatch[1] };\n }\n return { authenticated: output.includes(\"Logged in\"), username: \"\" };\n}\n\nexport function hasAnthropicKey(): boolean {\n return !!process.env.ANTHROPIC_API_KEY;\n}\n\nexport function nodeVersionOk(version: string): boolean {\n const major = parseInt(version.split(\".\")[0], 10);\n return major >= 18;\n}\n\nexport function hsCliVersionOk(version: string): boolean {\n const major = parseInt(version.split(\".\")[0], 10);\n return !isNaN(major) && major >= 8;\n}\n\n// ---------------------------------------------------------------------------\n// Comprehensive environment status (used by GET /api/settings/status)\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Config-based HubSpot auth (for API mode — no CLI subprocess)\n// ---------------------------------------------------------------------------\n\nexport function detectHubSpotAuthFromConfig(): {\n authenticated: boolean;\n portalName: string;\n portalId: string;\n dataCenter: string;\n accounts: HubSpotAccount[];\n uploadMode: \"api\" | \"cli\";\n} {\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n const configAccounts = config.hubspotAccounts || [];\n\n const accounts: HubSpotAccount[] = configAccounts.map((a) => ({\n name: a.portalName,\n portalId: a.portalId,\n authType: \"personalaccesskey\",\n isDefault: a.portalId === (config.activeHubSpotAccount || configAccounts[0]?.portalId),\n }));\n\n const active = getActiveHubSpotAccount();\n\n return {\n authenticated: !!active,\n portalName: active?.portalName || \"\",\n portalId: active?.portalId || \"\",\n dataCenter: active ? active.dataCenter : \"na1\",\n accounts,\n uploadMode,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Comprehensive environment status (used by GET /api/settings/status)\n// ---------------------------------------------------------------------------\n\nexport interface EnvironmentStatus {\n tools: {\n node: ToolInfo;\n git: ToolInfo;\n hubspot: ToolInfo & { authenticated: boolean; portalName: string; portalId: string; dataCenter: string; accounts: HubSpotAccount[]; uploadMode: \"api\" | \"cli\" };\n github: ToolInfo & { authenticated: boolean; username: string };\n claudeCode: CLIToolInfo;\n claudeOAuth: { authenticated: boolean; expiresAt?: string };\n geminiCli: CLIToolInfo;\n codexCli: CLIToolInfo;\n };\n apiKeys: {\n anthropic: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n openai: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n gemini: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n };\n activeEngine: AIEngineType | null;\n availableEngines: AIEngineType[];\n enabledCLITools: string[];\n}\n\nconst DISABLED_CLI: CLIToolInfo = { name: \"\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Disabled\" };\n\nexport function detectEnvironment(): EnvironmentStatus {\n const config = loadConfig();\n\n const node = detectNode();\n const git = detectGit();\n\n // HubSpot: API mode uses config, CLI mode uses hs CLI\n const hsUploadMode = config.hubspotUploadMode || \"api\";\n let hsInfo: ToolInfo & { authenticated: boolean; portalName: string; portalId: string; dataCenter: string; accounts: HubSpotAccount[]; uploadMode: \"api\" | \"cli\" };\n\n if (hsUploadMode === \"cli\") {\n const hs = detectHubSpotCLI();\n const hsAuth = hs.found ? detectHubSpotAuth() : { authenticated: false, portalName: \"\", portalId: \"\", accounts: [] as HubSpotAccount[] };\n const dc = hsAuth.portalId ? detectDataCenter(hsAuth.portalId) : \"na1\";\n hsInfo = { ...hs, ...hsAuth, dataCenter: dc, uploadMode: \"cli\" };\n } else {\n // API mode — read from vibespot config, no subprocess\n const apiAuth = detectHubSpotAuthFromConfig();\n hsInfo = {\n name: \"HubSpot API\",\n found: true, // always available (built-in)\n version: \"v3\",\n path: \"\",\n ...apiAuth,\n };\n }\n\n // GitHub CLI — always checked (lightweight)\n const gh = detectGitHubCLI();\n const ghAuth = gh.found ? detectGitHubAuth() : { authenticated: false, username: \"\" };\n\n // Claude OAuth token check\n const claudeOAuth = {\n authenticated: hasValidOAuthToken(),\n expiresAt: getOAuthTokenInfo()?.expiresAt,\n };\n\n // AI CLI tools — only check if enabled in config (lazy loading)\n const enabledTools = config.enabledCLITools || [];\n const claude = isCliToolEnabled(\"claude-code\") ? detectClaudeCode() : { ...DISABLED_CLI, name: \"Claude Code\" };\n const gemini = isCliToolEnabled(\"gemini-cli\") ? detectGeminiCLI() : { ...DISABLED_CLI, name: \"Gemini CLI\" };\n const codex = isCliToolEnabled(\"codex-cli\") ? detectCodexCLI() : { ...DISABLED_CLI, name: \"OpenAI Codex CLI\" };\n\n // Determine API key status\n function keyStatus(configKey: string | undefined, ...envVars: string[]): { configured: boolean; masked: string; source: \"config\" | \"env\" | null } {\n if (configKey) return { configured: true, masked: maskApiKey(configKey), source: \"config\" };\n for (const v of envVars) {\n if (process.env[v]) return { configured: true, masked: maskApiKey(process.env[v]!), source: \"env\" };\n }\n return { configured: false, masked: \"\", source: null };\n }\n\n const anthropicKey = keyStatus(config.anthropicApiKey, \"ANTHROPIC_API_KEY\");\n const openaiKey = keyStatus(config.openaiApiKey, \"OPENAI_API_KEY\");\n const geminiKey = keyStatus(config.geminiApiKey, \"GEMINI_API_KEY\", \"GOOGLE_AI_API_KEY\");\n\n // Build available engines — CLI tools must be enabled + authenticated\n const available: AIEngineType[] = [];\n if (claude.found && claude.authenticated) available.push(\"claude-code\");\n if (claudeOAuth.authenticated) available.push(\"claude-oauth\");\n if (anthropicKey.configured) available.push(\"anthropic-api\");\n if (openaiKey.configured) available.push(\"openai-api\");\n if (gemini.found && gemini.authenticated) available.push(\"gemini-cli\");\n if (geminiKey.configured) available.push(\"gemini-api\");\n if (codex.found && codex.authenticated) available.push(\"codex-cli\");\n\n return {\n tools: {\n node,\n git,\n hubspot: hsInfo,\n github: { ...gh, ...ghAuth },\n claudeCode: claude,\n claudeOAuth,\n geminiCli: gemini,\n codexCli: codex,\n },\n apiKeys: {\n anthropic: anthropicKey,\n openai: openaiKey,\n gemini: geminiKey,\n },\n activeEngine: config.aiEngine || null,\n availableEngines: available,\n enabledCLITools: enabledTools,\n };\n}\n","import * as p from \"@clack/prompts\";\nimport { theme } from \"../cli/theme.js\";\n\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\nexport function handleCancel(value: unknown): void {\n if (p.isCancel(value)) {\n p.cancel(theme.muted(\"Operation cancelled.\"));\n process.exit(0);\n }\n}\n\nexport async function intro(title: string): Promise<void> {\n p.intro(theme.heading(title));\n}\n\nexport async function outro(message: string): Promise<void> {\n p.outro(theme.success(message));\n}\n\nexport async function note(message: string, title?: string): Promise<void> {\n p.note(message, title ? theme.heading(title) : undefined);\n}\n\nexport async function text(opts: {\n message: string;\n placeholder?: string;\n defaultValue?: string;\n validate?: (value: string) => string | undefined;\n}): Promise<string> {\n const result = await p.text({\n message: theme.accent(opts.message),\n placeholder: opts.placeholder,\n defaultValue: opts.defaultValue,\n validate: opts.validate,\n });\n handleCancel(result);\n return result as string;\n}\n\nexport async function confirm(opts: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean> {\n const result = await p.confirm({\n message: theme.accent(opts.message),\n initialValue: opts.initialValue ?? true,\n });\n handleCancel(result);\n return result as boolean;\n}\n\nexport async function select<T extends string>(opts: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n}): Promise<T> {\n const result = await p.select({\n message: theme.accent(opts.message),\n options: opts.options,\n });\n handleCancel(result);\n return result as T;\n}\n\nexport async function spinner(): Promise<{\n start: (msg: string) => void;\n stop: (msg: string) => void;\n message: (msg: string) => void;\n}> {\n const s = p.spinner();\n return {\n start: (msg: string) => s.start(theme.muted(msg)),\n stop: (msg: string) => s.stop(theme.success(msg)),\n message: (msg: string) => s.message(theme.muted(msg)),\n };\n}\n\nexport function log(message: string): void {\n p.log.info(message);\n}\n\nexport function logSuccess(message: string): void {\n p.log.success(theme.success(message));\n}\n\nexport function logWarn(message: string): void {\n p.log.warn(theme.warn(message));\n}\n\nexport function logError(message: string): void {\n p.log.error(theme.error(message));\n}\n\nexport function logStep(message: string): void {\n p.log.step(theme.accent(message));\n}\n","import { readdirSync, statSync } from \"node:fs\";\nimport { join, basename, extname } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport { fileExists, readFile } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport interface ComponentInfo {\n name: string;\n path: string;\n description: string;\n}\n\nexport interface SourceAnalysis {\n sourceDir: string;\n wasCloned: boolean;\n components: ComponentInfo[];\n hasTailwind: boolean;\n cssVarCount: number;\n fonts: string[];\n interactions: string[];\n}\n\nfunction findComponents(dir: string): ComponentInfo[] {\n const components: ComponentInfo[] = [];\n\n // Common locations for landing page components\n const searchDirs = [\n join(dir, \"src/components/landing\"),\n join(dir, \"src/components/sections\"),\n join(dir, \"src/components\"),\n join(dir, \"src/pages\"),\n join(dir, \"app/components\"),\n join(dir, \"components\"),\n ];\n\n for (const searchDir of searchDirs) {\n if (!fileExists(searchDir)) continue;\n\n try {\n const files = readdirSync(searchDir);\n for (const file of files) {\n const filePath = join(searchDir, file);\n const stat = statSync(filePath);\n if (!stat.isFile()) continue;\n\n const ext = extname(file);\n if (![\".tsx\", \".jsx\"].includes(ext)) continue;\n\n // Skip utility/UI components\n const name = basename(file, ext);\n if (name.startsWith(\"ui\") || name === \"index\") continue;\n\n // Read the file to extract a description\n const content = readFile(filePath);\n const desc = describeComponent(name, content);\n\n components.push({ name, path: filePath, description: desc });\n }\n } catch {\n // Directory read failed, skip\n }\n }\n\n return components;\n}\n\nfunction describeComponent(name: string, content: string): string {\n const hints: string[] = [];\n\n if (/carousel|slider|swiper|embla/i.test(content)) hints.push(\"carousel\");\n if (/accordion|collapsible|expand/i.test(content)) hints.push(\"accordion\");\n if (/form|submit|input.*email/i.test(content)) hints.push(\"form\");\n if (/nav|navigation|menu/i.test(content)) hints.push(\"navigation\");\n if (/hero|headline|tagline/i.test(content)) hints.push(\"hero\");\n if (/footer|copyright/i.test(content)) hints.push(\"footer\");\n if (/testimonial|quote|review/i.test(content)) hints.push(\"testimonials\");\n if (/pricing|plan|tier/i.test(content)) hints.push(\"pricing\");\n if (/faq|question.*answer/i.test(content)) hints.push(\"FAQ\");\n if (/feature|benefit|advantage/i.test(content)) hints.push(\"features\");\n if (/contact|get.in.touch/i.test(content)) hints.push(\"contact\");\n if (/cta|call.to.action/i.test(content)) hints.push(\"CTA\");\n if (/team|member|bio/i.test(content)) hints.push(\"team\");\n\n if (hints.length === 0) {\n // Fallback: infer from component name\n const readable = name\n .replace(/Section$/, \"\")\n .replace(/([A-Z])/g, \" $1\")\n .trim();\n return readable;\n }\n\n return hints.join(\", \");\n}\n\nfunction analyzeCSS(dir: string): { varCount: number; fonts: string[] } {\n const cssFiles = [\n join(dir, \"src/index.css\"),\n join(dir, \"src/globals.css\"),\n join(dir, \"src/app/globals.css\"),\n join(dir, \"app/globals.css\"),\n ];\n\n let varCount = 0;\n const fonts: string[] = [];\n\n for (const cssFile of cssFiles) {\n if (!fileExists(cssFile)) continue;\n\n const content = readFile(cssFile);\n const varMatches = content.match(/--[\\w-]+:/g);\n if (varMatches) varCount += varMatches.length;\n\n const fontMatches = content.match(\n /font-family:\\s*['\"]([^'\"]+)['\"]/g\n );\n if (fontMatches) {\n for (const m of fontMatches) {\n const font = m.match(/['\"]([^'\"]+)['\"]/)?.[1];\n if (font && !fonts.includes(font)) fonts.push(font);\n }\n }\n\n const importMatches = content.match(\n /@import\\s+url\\([^)]*fonts\\.googleapis\\.com[^)]*family=([^&)]+)/g\n );\n if (importMatches) {\n for (const m of importMatches) {\n const font = m.match(/family=([^&)]+)/)?.[1]?.replace(/\\+/g, \" \");\n if (font && !fonts.includes(font)) fonts.push(font);\n }\n }\n }\n\n return { varCount, fonts };\n}\n\nfunction detectInteractions(dir: string): string[] {\n const interactions: string[] = [];\n\n const hooksDir = join(dir, \"src/hooks\");\n if (fileExists(hooksDir)) {\n try {\n const hooks = readdirSync(hooksDir);\n for (const hook of hooks) {\n if (/scroll/i.test(hook)) interactions.push(\"Scroll animations\");\n if (/intersection/i.test(hook)) interactions.push(\"Scroll animations\");\n }\n } catch {\n // Ignore\n }\n }\n\n // Check landing components for patterns\n const componentDir = join(dir, \"src/components/landing\");\n if (fileExists(componentDir)) {\n try {\n const files = readdirSync(componentDir);\n for (const file of files) {\n if (!file.endsWith(\".tsx\") && !file.endsWith(\".jsx\")) continue;\n const content = readFile(join(componentDir, file));\n if (/carousel|embla|swiper/i.test(content) && !interactions.includes(\"Carousel\"))\n interactions.push(\"Carousel\");\n if (/accordion|collapsible/i.test(content) && !interactions.includes(\"Accordion\"))\n interactions.push(\"Accordion\");\n if (/typing|typewriter/i.test(content) && !interactions.includes(\"Typing animation\"))\n interactions.push(\"Typing animation\");\n if (/parallax|requestAnimationFrame/i.test(content) && !interactions.includes(\"Parallax\"))\n interactions.push(\"Parallax\");\n }\n } catch {\n // Ignore\n }\n }\n\n if (interactions.length === 0) {\n interactions.push(\"Scroll animations\");\n }\n\n return interactions;\n}\n\n/**\n * Headless source analysis — called from the vibe server (no interactive prompts).\n * Clones the repo if it's a URL, then analyzes components.\n */\nexport function analyzeSource(input: string): SourceAnalysis {\n let sourceDir: string;\n let wasCloned = false;\n\n if (input.startsWith(\"http\") || input.startsWith(\"git@\")) {\n wasCloned = true;\n const repoName =\n basename(input.replace(/\\.git$/, \"\")) || \"react-source\";\n sourceDir = join(process.cwd(), \"workspace\", repoName);\n\n if (!fileExists(sourceDir)) {\n const result = run(`git clone --depth 1 \"${input}\" \"${sourceDir}\"`);\n if (!result.success) {\n throw new Error(`Failed to clone ${input}: ${result.stderr}`);\n }\n }\n } else {\n sourceDir = input;\n if (!fileExists(sourceDir)) {\n throw new Error(`Directory not found: ${sourceDir}`);\n }\n }\n\n const components = findComponents(sourceDir);\n const hasTailwind =\n fileExists(join(sourceDir, \"tailwind.config.ts\")) ||\n fileExists(join(sourceDir, \"tailwind.config.js\"));\n const { varCount, fonts } = analyzeCSS(sourceDir);\n const interactions = detectInteractions(sourceDir);\n\n return {\n sourceDir,\n wasCloned,\n components,\n hasTailwind,\n cssVarCount: varCount,\n fonts,\n interactions,\n };\n}\n\nexport async function setupSource(): Promise<SourceAnalysis> {\n await ui.intro(\"Source Project\");\n\n const input = await ui.text({\n message: \"GitHub URL or local path to your React project:\",\n placeholder: \"https://github.com/user/my-lovable-page\",\n validate: (v) => {\n if (!v.trim()) return \"Please enter a URL or path\";\n return undefined;\n },\n });\n\n let sourceDir: string;\n let wasCloned = false;\n\n if (input.startsWith(\"http\") || input.startsWith(\"git@\")) {\n wasCloned = true;\n // Clone from GitHub\n const repoName =\n basename(input.replace(/\\.git$/, \"\")) || \"react-source\";\n sourceDir = join(process.cwd(), \"workspace\", repoName);\n\n if (fileExists(sourceDir)) {\n // Already cloned from a previous run — reuse it\n ui.logSuccess(`Using existing clone: ${theme.dim(sourceDir)}`);\n } else {\n const s = await ui.spinner();\n s.start(\"Cloning repository...\");\n\n const result = run(`git clone --depth 1 \"${input}\" \"${sourceDir}\"`);\n if (!result.success) {\n s.stop(\"Clone failed\");\n ui.logError(\n `Failed to clone ${input}. Check the URL and your access permissions.`\n );\n process.exit(1);\n }\n\n s.stop(`Cloned to ${theme.dim(sourceDir)}`);\n }\n } else {\n sourceDir = input;\n if (!fileExists(sourceDir)) {\n ui.logError(`Directory not found: ${sourceDir}`);\n process.exit(1);\n }\n ui.logSuccess(`Using local source: ${theme.dim(sourceDir)}`);\n }\n\n // Analyze\n const s = await ui.spinner();\n s.start(\"Analyzing project structure...\");\n\n const components = findComponents(sourceDir);\n const hasTailwind =\n fileExists(join(sourceDir, \"tailwind.config.ts\")) ||\n fileExists(join(sourceDir, \"tailwind.config.js\"));\n const { varCount, fonts } = analyzeCSS(sourceDir);\n const interactions = detectInteractions(sourceDir);\n\n s.stop(`Found ${components.length} landing page components`);\n\n if (components.length === 0) {\n ui.logWarn(\n \"No components found. Make sure the React source has .tsx/.jsx files in src/components/\"\n );\n process.exit(1);\n }\n\n // Show summary\n const componentList = components\n .map((c, i) => ` ${theme.dim(`${i + 1}.`)} ${theme.bold(c.name)} ${theme.muted(`— ${c.description}`)}`)\n .join(\"\\n\");\n\n const cssInfo = hasTailwind\n ? `Tailwind + custom CSS (${varCount} variables)`\n : `Custom CSS (${varCount} variables)`;\n const fontInfo = fonts.length > 0 ? fonts.join(\", \") : \"System fonts\";\n const jsInfo = interactions.join(\", \");\n\n await ui.note(\n `${componentList}\\n\\n CSS: ${cssInfo}\\n JS: ${jsInfo}\\n Font: ${fontInfo}`,\n `${components.length} components detected`\n );\n\n const ok = await ui.confirm({ message: \"Does this look right?\" });\n if (!ok) {\n ui.logError(\"Please adjust your source directory and try again.\");\n process.exit(0);\n }\n\n await ui.outro(\"Source analyzed!\");\n\n return {\n sourceDir,\n wasCloned,\n components,\n hasTailwind,\n cssVarCount: varCount,\n fonts,\n interactions,\n };\n}\n","import { join } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport { fileExists, readFile, writeFile, ensureDir } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport { loadConfig, getHubSpotPak } from \"../utils/config.js\";\nimport { createThemeScaffold } from \"../hubspot/theme-scaffold.js\";\nimport { fetchTheme } from \"../hubspot/fetcher.js\";\n\nexport interface ThemeInfo {\n themePath: string;\n themeName: string;\n}\n\nexport async function setupTheme(): Promise<ThemeInfo> {\n await ui.intro(\"HubSpot Theme Setup\");\n\n const choice = await ui.select({\n message: \"Do you have an existing HubSpot theme?\",\n options: [\n {\n value: \"fetch\" as const,\n label: \"Fetch my existing theme from HubSpot\",\n hint: \"downloads your current theme\",\n },\n {\n value: \"create\" as const,\n label: \"Start fresh (HubSpot Boilerplate)\",\n hint: \"creates a new starter theme\",\n },\n ],\n });\n\n let themeName: string;\n let themePath: string;\n\n const workspaceDir = join(process.cwd(), \"workspace\");\n ensureDir(workspaceDir);\n\n if (choice === \"fetch\") {\n themeName = await ui.text({\n message: \"What's your theme name in HubSpot?\",\n placeholder: \"My-Company-Theme\",\n validate: (v) =>\n v.trim() ? undefined : \"Theme name is required\",\n });\n\n themePath = join(workspaceDir, themeName);\n\n const s = await ui.spinner();\n s.start(\"Fetching theme from HubSpot...\");\n\n const config = loadConfig();\n const pak = getHubSpotPak();\n\n if (config.hubspotUploadMode === \"cli\" || !pak) {\n // CLI fallback\n const result = run(`hs cms fetch \"${themeName}\" \"${themePath}\"`);\n if (!result.success) {\n s.stop(\"Fetch failed\");\n ui.logError(\n `Could not fetch theme \"${themeName}\". Check the name in HubSpot Design Manager.`\n );\n process.exit(1);\n }\n } else {\n // API mode\n try {\n await fetchTheme(pak, themeName, themePath);\n } catch (err) {\n s.stop(\"Fetch failed\");\n ui.logError(\n `Could not fetch theme \"${themeName}\": ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n }\n\n s.stop(`Theme fetched: ${theme.dim(themePath)}`);\n } else {\n themeName = await ui.text({\n message: \"Name for your new theme:\",\n placeholder: \"my-theme\",\n defaultValue: \"my-theme\",\n });\n\n themePath = join(workspaceDir, themeName);\n\n const s = await ui.spinner();\n s.start(\"Creating theme...\");\n\n try {\n createThemeScaffold(themePath, themeName);\n } catch (err) {\n s.stop(\"Creation failed\");\n ui.logError(\n `Could not create theme \"${themeName}\": ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n\n s.stop(`Theme created: ${theme.dim(themePath)}`);\n }\n\n // Validate and patch\n await ui.intro(\"Checking theme compatibility\");\n\n const baseHtmlPath = join(themePath, \"templates/layouts/base.html\");\n if (!fileExists(baseHtmlPath)) {\n ui.logError(\n `base.html not found at ${baseHtmlPath}. Your theme may have a different structure.`\n );\n process.exit(1);\n }\n ui.logSuccess(\"base.html found\");\n\n // Check for template_css and template_js support\n let baseHtml = readFile(baseHtmlPath);\n let patched = false;\n\n if (!baseHtml.includes(\"template_css\")) {\n ui.logWarn(\"Missing template_css support in base.html\");\n\n // Find the line with require_css for theme-overrides or the last require_css\n const cssInsertPoint =\n baseHtml.indexOf(\"theme-overrides.css\") !== -1\n ? baseHtml.indexOf(\n \"{{\",\n baseHtml.lastIndexOf(\"\\n\", baseHtml.indexOf(\"theme-overrides.css\"))\n )\n : baseHtml.lastIndexOf(\"require_css\");\n\n if (cssInsertPoint > 0) {\n const insertBefore = baseHtml.lastIndexOf(\"\\n\", cssInsertPoint);\n const block = `\\n {% if template_css %}\\n {{ require_css(get_asset_url(template_css)) }}\\n {% endif %}`;\n baseHtml =\n baseHtml.slice(0, insertBefore) + block + baseHtml.slice(insertBefore);\n patched = true;\n }\n } else {\n ui.logSuccess(\"template_css support\");\n }\n\n if (!baseHtml.includes(\"template_js\")) {\n ui.logWarn(\"Missing template_js support in base.html\");\n\n // Find the line with require_js for main.js or the last require_js\n const jsLine = baseHtml.indexOf(\"require_js\");\n if (jsLine > 0) {\n const lineEnd = baseHtml.indexOf(\"\\n\", jsLine);\n // Find the end of the require_js line (including closing tags)\n const nextLine = baseHtml.indexOf(\"\\n\", lineEnd + 1);\n const block = `\\n {% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}`;\n const insertAt =\n baseHtml.indexOf(\"}}\", jsLine) + 2 + baseHtml.slice(baseHtml.indexOf(\"}}\", jsLine) + 2).indexOf(\"\\n\") + 1;\n baseHtml =\n baseHtml.slice(0, baseHtml.indexOf(\"\\n\", baseHtml.indexOf(\"}}\", jsLine) + 2)) +\n block +\n baseHtml.slice(baseHtml.indexOf(\"\\n\", baseHtml.indexOf(\"}}\", jsLine) + 2));\n patched = true;\n }\n } else {\n ui.logSuccess(\"template_js support\");\n }\n\n if (patched) {\n const s = await ui.spinner();\n s.start(\"Patching base.html...\");\n writeFile(baseHtmlPath, baseHtml);\n s.stop(\"base.html patched with template_css/template_js support\");\n }\n\n // Check .hsignore\n const hsignorePath = join(themePath, \".hsignore\");\n if (fileExists(hsignorePath)) {\n const hsignore = readFile(hsignorePath);\n if (!hsignore.includes(\"docs/\")) {\n writeFile(hsignorePath, hsignore + \"\\ndocs/\\n\");\n ui.logSuccess(\"Added docs/ to .hsignore\");\n }\n } else {\n writeFile(hsignorePath, \"docs/\\n*.md\\nnode_modules/\\n.git\\n\");\n ui.logSuccess(\"Created .hsignore\");\n }\n\n await ui.outro(\"Theme ready!\");\n\n return { themePath, themeName };\n}\n","/**\n * Theme scaffold generator — replaces `hs cms theme create`.\n * Creates the standard HubSpot theme directory structure locally.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Create a minimal HubSpot theme directory structure.\n * Produces the same layout as `hs cms theme create` but without\n * boilerplate modules/templates (vibespot generates those via AI).\n */\nexport function createThemeScaffold(themePath: string, themeName: string): void {\n // Create directories\n mkdirSync(themePath, { recursive: true });\n mkdirSync(join(themePath, \"templates\"), { recursive: true });\n mkdirSync(join(themePath, \"modules\"), { recursive: true });\n mkdirSync(join(themePath, \"css\"), { recursive: true });\n mkdirSync(join(themePath, \"js\"), { recursive: true });\n mkdirSync(join(themePath, \"images\"), { recursive: true });\n mkdirSync(join(themePath, \"assets\"), { recursive: true });\n\n // theme.json — required by HubSpot\n const themeJson = {\n label: themeName,\n preview_path: \"./templates/home.html\",\n screenshot_path: \"./images/template-previews/home.png\",\n enable_domain_stylesheets: false,\n version: \"1.0.0\",\n author: {\n name: \"vibeSpot\",\n url: \"https://github.com/borismichel/vibespot\",\n },\n };\n writeFileSync(join(themePath, \"theme.json\"), JSON.stringify(themeJson, null, 2) + \"\\n\");\n\n // fields.json — empty theme-level fields\n writeFileSync(join(themePath, \"fields.json\"), \"[]\\n\");\n\n // Placeholder landing page template — gets replaced once AI generates real modules.\n // Marked isAvailableForNewContent: false so it won't appear in HubSpot as a usable template.\n const landingTemplate = `<!--\n templateType: page\n isAvailableForNewContent: false\n label: ${themeName} (placeholder)\n screenshotPath: ../images/template-previews/home.png\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% block body %}\n{% dnd_area \"main_content\"\n label=\"Main Content\",\n class=\"body-container body-container--${themeName}\"\n%}\n{% end_dnd_area %}\n{% endblock body %}\n`;\n writeFileSync(join(themePath, \"templates\", \"home.html\"), landingTemplate);\n\n // Base layout\n const baseLayout = `<!--\n templateType: none\n isAvailableForNewContent: false\n label: Base Layout\n-->\n<!DOCTYPE html>\n<html lang=\"{{ html_lang }}\" {{ html_lang_dir }}>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n {% if template_css %}\n {{ require_css(get_asset_url(template_css)) }}\n {% endif %}\n {{ standard_header_includes }}\n</head>\n<body>\n {% block body %}{% endblock body %}\n {% if template_js %}\n {{ require_js(get_asset_url(template_js)) }}\n {% endif %}\n {{ standard_footer_includes }}\n</body>\n</html>\n`;\n mkdirSync(join(themePath, \"templates\", \"layouts\"), { recursive: true });\n writeFileSync(join(themePath, \"templates\", \"layouts\", \"base.html\"), baseLayout);\n}\n","import { join } from \"node:path\";\nimport { readdirSync, rmSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets } from \"../ai/engine.js\";\nimport type { AIEngineType } from \"../utils/config.js\";\nimport { ClaudeCodeEngine } from \"../ai/claude-code.js\";\nimport { ClaudeAPIEngine } from \"../ai/claude-api.js\";\nimport { GeminiCLIEngine } from \"../ai/gemini-cli.js\";\nimport { CodexCLIEngine } from \"../ai/codex-cli.js\";\nimport { getConversionGuide } from \"../ai/prompts.js\";\nimport { readFile, writeFile, fileExists } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nfunction createEngine(type: AIEngineType, model?: string): AIEngine {\n switch (type) {\n case \"claude-code\":\n return new ClaudeCodeEngine(model);\n case \"gemini-cli\":\n return new GeminiCLIEngine();\n case \"codex-cli\":\n return new CodexCLIEngine();\n case \"api\":\n return new ClaudeAPIEngine();\n }\n}\n\nexport async function runConversion(opts: {\n aiEngine: AIEngineType;\n model?: string;\n sourceDir: string;\n themePath: string;\n}): Promise<GeneratedAssets> {\n await ui.intro(\"Converting React to HubSpot Modules\");\n\n await ui.note(\n \"AI will now analyze your React code and create\\nHubSpot-native modules. This takes 2-5 minutes.\",\n \"AI Conversion\"\n );\n\n const engine = createEngine(opts.aiEngine, opts.model);\n\n const conversionGuide = getConversionGuide();\n\n const s = await ui.spinner();\n s.start(\"Starting AI conversion...\");\n\n const startTime = Date.now();\n\n const result = await engine.convert({\n sourceDir: opts.sourceDir,\n themePath: opts.themePath,\n conversionGuide,\n onProgress: (step, detail) => {\n if (step === \"created\") {\n ui.logSuccess(detail);\n } else {\n s.message(detail);\n }\n },\n });\n\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);\n s.stop(`AI conversion complete (${elapsed}s)`);\n\n // Validate and auto-fix all known HubSpot issues before upload\n const fixes = validateAndFix(opts.themePath);\n for (const fix of fixes) {\n ui.logSuccess(`Auto-fixed: ${fix}`);\n }\n\n // Show conversion checklist\n const checklist = buildChecklist(opts.themePath, result);\n const lines: string[] = [];\n for (const item of checklist) {\n const icon = item.passed ? \"\\u2705\" : \"\\u274c\";\n const severity = !item.passed ? (item.critical ? \" (CRITICAL)\" : \" (cosmetic)\") : \"\";\n lines.push(`${icon} ${item.label}${severity}`);\n }\n const passed = checklist.filter((c) => c.passed).length;\n lines.push(`\\n${passed}/${checklist.length} checks passed`);\n await ui.note(lines.join(\"\\n\"), \"Conversion Checklist\");\n\n const criticalFailures = checklist.filter((c) => !c.passed && c.critical);\n const cosmeticFailures = checklist.filter((c) => !c.passed && !c.critical);\n\n if (criticalFailures.length > 0) {\n ui.logError(\n `${criticalFailures.length} critical issue(s) — upload will likely fail:\\n` +\n criticalFailures.map((c) => ` - ${c.label}`).join(\"\\n\")\n );\n const proceed = await ui.confirm({\n message: \"Continue with upload anyway?\",\n initialValue: false,\n });\n if (!proceed) {\n throw new Error(\"Conversion aborted due to critical checklist failures.\");\n }\n } else if (cosmeticFailures.length > 0) {\n ui.logWarn(\n `${cosmeticFailures.length} non-critical issue(s) — page will work but may look incomplete:\\n` +\n cosmeticFailures.map((c) => ` - ${c.label}`).join(\"\\n\")\n );\n }\n\n // Offer to clean up log file\n const logPath = join(opts.themePath, \"..\", \"vibespot-conversion.log\");\n if (fileExists(logPath)) {\n const keepLog = await ui.confirm({\n message: \"Keep conversion log file for debugging?\",\n initialValue: false,\n });\n if (!keepLog) {\n rmSync(logPath);\n } else {\n ui.logSuccess(`Log saved: ${logPath}`);\n }\n }\n\n await ui.outro(\"Files ready for upload!\");\n\n return result;\n}\n\n/**\n * Run all validation and auto-fix routines before upload.\n * Returns a list of human-readable fix descriptions.\n */\nexport function validateAndFix(themePath: string): string[] {\n const fixes: string[] = [];\n\n // 1. Template annotations\n validateTemplates(themePath);\n\n // 2. Module meta.json\n validateModuleMeta(themePath);\n\n // 3. Fix fields.json issues in all modules\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n\n const moduleName = entry.replace(\".module\", \"\");\n let content = readFile(fieldsPath);\n let changed = false;\n\n // 3a. \"textarea\" → \"text\"\n if (content.includes('\"textarea\"')) {\n content = content.replace(/\"textarea\"/g, '\"text\"');\n changed = true;\n fixes.push(`${moduleName}: \"textarea\" → \"text\"`);\n }\n\n // 3b. \"name\": \"name\" → \"name\": \"item_name\"\n if (/\"name\":\\s*\"name\"/.test(content)) {\n content = content.replace(/\"name\":\\s*\"name\"/g, '\"name\": \"item_name\"');\n changed = true;\n fixes.push(`${moduleName}: reserved field name \"name\" → \"item_name\"`);\n }\n\n // 3c. Fix \"choice\" fields — choices must be [[\"value\",\"Label\"]] not [\"value\"]\n // 3d. Fix \"link\" fields — default must be { url: { href, type }, open_in_new_tab, no_follow }\n try {\n const fields = JSON.parse(content);\n let jsonFixed = false;\n if (fixChoiceFields(fields)) {\n jsonFixed = true;\n fixes.push(`${moduleName}: fixed choice field format`);\n }\n if (fixLinkFields(fields)) {\n jsonFixed = true;\n fixes.push(`${moduleName}: fixed link field default value`);\n }\n if (jsonFixed) {\n content = JSON.stringify(fields, null, 2) + \"\\n\";\n changed = true;\n }\n } catch {\n fixes.push(`${moduleName}: fields.json has invalid JSON — manual fix needed`);\n }\n\n if (changed) writeFile(fieldsPath, content);\n\n // 3d. Fix now() in module.html\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (fileExists(htmlPath)) {\n let html = readFile(htmlPath);\n if (html.includes(\"now()\")) {\n html = html.replace(/now\\(\\)/g, \"local_dt\");\n writeFile(htmlPath, html);\n fixes.push(`${moduleName}: now() → local_dt`);\n }\n }\n }\n }\n\n // 4. Remove HubDB templates (requires CMS Hub Pro/Enterprise)\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\")) continue;\n const filePath = join(templatesDir, file);\n const content = readFile(filePath);\n if (content.includes(\"hubdb_table\") || content.includes(\"hubdb_table_rows\")) {\n rmSync(filePath);\n fixes.push(`Removed ${file} (HubDB requires CMS Hub Pro/Enterprise)`);\n }\n }\n }\n\n return fixes;\n}\n\n/** Recursively fix choice fields: convert string[] choices to [value, Label][] */\nfunction fixChoiceFields(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"choice\" && Array.isArray(f.choices)) {\n const needsFix = f.choices.some((c: unknown) => typeof c === \"string\");\n if (needsFix) {\n f.choices = (f.choices as unknown[]).map((c: unknown) => {\n if (typeof c === \"string\") {\n const label = c.charAt(0).toUpperCase() + c.slice(1);\n return [c, label];\n }\n return c;\n });\n fixed = true;\n }\n }\n\n // Recurse into group children\n if (Array.isArray(f.children)) {\n if (fixChoiceFields(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\n/** Recursively fix link fields: default must be { url: { href, type }, open_in_new_tab, no_follow } */\nfunction fixLinkFields(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"link\") {\n const def = f.default;\n // Fix if default is a string, missing, or doesn't have the required url.href structure\n const needsFix =\n typeof def === \"string\" ||\n def === undefined ||\n def === null ||\n (typeof def === \"object\" && !(def as Record<string, unknown>).url);\n\n if (needsFix) {\n const href = typeof def === \"string\" ? def : \"\";\n f.default = {\n url: { href, type: \"EXTERNAL\" },\n open_in_new_tab: false,\n no_follow: false,\n };\n fixed = true;\n }\n }\n\n // Recurse into group children\n if (Array.isArray(f.children)) {\n if (fixLinkFields(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\ninterface CheckItem {\n label: string;\n passed: boolean;\n /** If true, failure blocks upload. If false, it's cosmetic / nice-to-have. */\n critical: boolean;\n}\n\nfunction buildChecklist(themePath: string, result: GeneratedAssets): CheckItem[] {\n const items: CheckItem[] = [];\n\n // Modules — critical: upload will fail with zero modules\n const moduleCount = result.modules.length;\n items.push({\n label: `Modules created (${moduleCount})`,\n passed: moduleCount > 0,\n critical: true,\n });\n\n // fields.json — critical: invalid fields cause upload deserialization errors\n let fieldsOk = true;\n for (const m of result.modules) {\n if (m.fieldsJson.includes('\"textarea\"') || /\"name\":\\s*\"name\"/.test(m.fieldsJson)) {\n fieldsOk = false;\n break;\n }\n }\n items.push({\n label: \"fields.json valid (no textarea, no reserved names)\",\n passed: moduleCount > 0 && fieldsOk,\n critical: true,\n });\n\n // module.html — critical: modules won't render without it\n const allHaveHtml = result.modules.every((m) => m.moduleHtml.length > 0);\n items.push({\n label: \"module.html created for each module\",\n passed: moduleCount > 0 && allHaveHtml,\n critical: true,\n });\n\n // module.css — not critical (page works but looks unstyled)\n const missingCss = result.modules.filter((m) => !m.moduleCss).map((m) => m.moduleName);\n const allHaveCss = missingCss.length === 0;\n items.push({\n label: allHaveCss\n ? \"module.css created for each module\"\n : `module.css missing for: ${missingCss.join(\", \")}`,\n passed: moduleCount > 0 && allHaveCss,\n critical: false,\n });\n\n // Style tab — not critical (modules work without style tab)\n const hasStyleTab = result.modules.some((m) => m.fieldsJson.includes('\"STYLE\"'));\n items.push({\n label: \"Style tab fields (color pickers)\",\n passed: hasStyleTab,\n critical: false,\n });\n\n // Shared CSS — not critical (modules have their own CSS)\n items.push({\n label: \"Shared CSS with design system variables\",\n passed: result.sharedCss.length > 50,\n critical: false,\n });\n\n // Shared JS — not critical (page works without animations)\n items.push({\n label: \"Shared JS for scroll animations\",\n passed: result.sharedJs.length > 50,\n critical: false,\n });\n\n // Page template — critical: no template means no page in HubSpot\n items.push({\n label: \"Page template with dnd_area\",\n passed: result.template.length > 0 && result.template.includes(\"dnd_area\"),\n critical: true,\n });\n\n // Template annotations — critical: template won't appear in picker\n const templatesDir = join(themePath, \"templates\");\n let templateAnnotated = false;\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\") && /templateType\\s*:\\s*page/i.test(content)) {\n templateAnnotated = true;\n break;\n }\n }\n }\n items.push({\n label: \"Template annotations (templateType: page)\",\n passed: templateAnnotated,\n critical: true,\n });\n\n return items;\n}\n\n/**\n * Ensure all templates in templates/ have the required HubSpot annotations.\n * Without `templateType: page` and `isAvailableForNewContent: true`,\n * the template won't appear in HubSpot's template picker.\n */\nexport function validateTemplates(themePath: string): void {\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return;\n\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n\n const filePath = join(templatesDir, file);\n let content = readFile(filePath);\n\n // Skip files that don't look like page templates\n if (!content.includes(\"dnd_area\") && !content.includes(\"extends\")) continue;\n\n const hasTemplateType = /templateType\\s*:\\s*page/i.test(content);\n const hasAvailable = /isAvailableForNewContent\\s*:\\s*true/i.test(content);\n\n if (hasTemplateType && hasAvailable) continue;\n\n // Build the annotation block\n const label = file.replace(\".html\", \"\").replace(/[-_]/g, \" \").replace(/\\b\\w/g, c => c.toUpperCase());\n\n if (content.includes(\"<!--\") && content.indexOf(\"-->\") < 200) {\n // Has an existing comment block at the top — patch it\n const commentEnd = content.indexOf(\"-->\");\n let annotation = content.slice(0, commentEnd);\n\n if (!hasTemplateType) {\n annotation += \"\\n templateType: page\";\n }\n if (!hasAvailable) {\n annotation += \"\\n isAvailableForNewContent: true\";\n }\n if (!/label\\s*:/i.test(annotation)) {\n annotation += `\\n label: ${label}`;\n }\n\n content = annotation + content.slice(commentEnd);\n } else {\n // No annotation block — prepend one\n const block = `<!--\\n templateType: page\\n isAvailableForNewContent: true\\n label: ${label}\\n-->\\n`;\n content = block + content;\n }\n\n writeFile(filePath, content);\n ui.logSuccess(`Template \"${file}\" — annotations verified`);\n }\n\n}\n\n/**\n * Ensure all module meta.json files have the required fields for\n * landing page compatibility.\n */\nexport function validateModuleMeta(themePath: string): void {\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const metaPath = join(modulesDir, entry, \"meta.json\");\n if (!fileExists(metaPath)) continue;\n\n try {\n const meta = JSON.parse(readFile(metaPath));\n let changed = false;\n\n if (!meta.host_template_types || !meta.host_template_types.includes(\"PAGE\")) {\n meta.host_template_types = [\"PAGE\"];\n changed = true;\n }\n if (!meta.is_available_for_new_content) {\n meta.is_available_for_new_content = true;\n changed = true;\n }\n\n if (changed) {\n writeFile(metaPath, JSON.stringify(meta, null, 2) + \"\\n\");\n }\n } catch {\n // Skip malformed meta.json\n }\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join, basename } from \"node:path\";\nimport { readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide, getHubspotRules } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\n/** Boilerplate modules from `hs cms theme create` — used to distinguish AI-generated modules. */\nconst BOILERPLATE_MODULES = new Set([\n \"button.module\",\n \"card.module\",\n \"menu.module\",\n \"pricing-card.module\",\n \"social-follow.module\",\n]);\n\n/** Boilerplate templates from `hs cms theme create`. */\nconst BOILERPLATE_TEMPLATES = new Set([\n \"about.html\",\n \"blog-index.html\",\n \"blog-post.html\",\n \"contact.html\",\n \"home.html\",\n \"hubdb.html\",\n \"landing-page.html\",\n \"pricing.html\",\n \"qa-test.html\",\n \"base.html\",\n]);\n\nexport class ClaudeCodeEngine implements AIEngine {\n private model?: string;\n private reported = new Set<string>();\n private moduleCount = 0;\n private expectedModules = 0;\n\n constructor(model?: string) {\n this.model = model;\n }\n\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n // Reset progress tracking\n this.reported.clear();\n this.moduleCount = 0;\n this.expectedModules = 0;\n\n // Count source components to estimate expected modules\n const sourceComponents = this.countSourceComponents(sourceDir);\n\n // Snapshot existing files so we can detect what Claude actually created\n const existingModules = this.listModules(themePath);\n const existingCss = this.listDir(join(themePath, \"css\"));\n const existingJs = this.listDir(join(themePath, \"js\"));\n const existingTemplates = this.listDir(join(themePath, \"templates\"));\n\n // Build the prompt for Claude Code\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", `Starting Claude Code (${sourceComponents} source components found)...`);\n\n // Run Claude Code with real-time progress tracking\n let stdout = \"\";\n let stderr = \"\";\n\n // Poll the filesystem every 3s to show progress\n const progressInterval = setInterval(() => {\n this.reportProgress(themePath, existingModules, existingCss, existingJs, existingTemplates, onProgress);\n }, 3000);\n\n try {\n await new Promise<void>((resolve, reject) => {\n // Strip CLAUDECODE env var to allow running from inside a Claude Code session\n const env = { ...process.env };\n delete env.CLAUDECODE;\n\n const args = [\n \"--print\",\n \"--max-turns\", \"50\",\n \"--allowedTools\", \"Read,Glob,Grep,Write,Edit,Bash\",\n ];\n if (this.model) args.push(\"--model\", this.model);\n\n const child = spawn(\"claude\", args, {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n shell: true,\n });\n\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Claude Code failed to start: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(\n `Claude Code exited with code ${code}.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n (stdout ? `Output: ${stdout.slice(0, 500)}` : \"No output\")\n ));\n } else {\n resolve();\n }\n });\n\n // Handle stdin errors (EPIPE if claude exits before prompt is fully written)\n child.stdin.on(\"error\", () => {});\n\n // Send prompt via stdin and close\n child.stdin.write(prompt);\n child.stdin.end();\n\n // 30 min timeout\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Claude Code timed out after 30 minutes\"));\n }, 1_800_000);\n });\n } finally {\n clearInterval(progressInterval);\n }\n\n // Write full log to workspace for debugging\n const logPath = join(themePath, \"..\", \"vibespot-conversion.log\");\n try {\n const timestamp = new Date().toISOString();\n const logContent = [\n `=== vibeSpot Conversion Log ===`,\n `Timestamp: ${timestamp}`,\n `Source: ${sourceDir}`,\n `Theme: ${themePath}`,\n `Model: ${this.model || \"default\"}`,\n ``,\n `=== PROMPT SENT ===`,\n prompt.slice(0, 500) + \"\\n... (truncated, full guide follows)\",\n ``,\n `=== CLAUDE CODE STDOUT ===`,\n stdout || \"(empty)\",\n ``,\n `=== CLAUDE CODE STDERR ===`,\n stderr || \"(empty)\",\n ``,\n ].join(\"\\n\");\n writeFileSync(logPath, logContent, \"utf-8\");\n onProgress(\"status\", `Log written to ${basename(logPath)}`);\n } catch {\n // Non-critical — don't fail if log can't be written\n }\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n // Scan the theme directory for what Claude Code created\n const result = this.scanGeneratedFiles(themePath);\n\n // Validate that new files were actually created\n const newModules = result.modules.filter(\n (m) => !existingModules.has(m.moduleName + \".module\")\n );\n\n if (newModules.length === 0) {\n const outputPreview = stdout.slice(0, 1500) || \"(no output)\";\n const stderrPreview = stderr.slice(0, 500);\n throw new Error(\n \"Claude Code did not create any new module files.\\n\\n\" +\n \"This usually means the model described the conversion instead of using Write tool to create files.\\n\\n\" +\n \"Possible causes:\\n\" +\n \" - Model didn't use Write tool (just printed text)\\n\" +\n \" - Claude Code hit a rate limit or API error\\n\" +\n \" - The source directory was not accessible\\n\\n\" +\n `Source: ${opts.sourceDir}\\n` +\n `Theme: ${themePath}\\n` +\n (stderrPreview ? `\\nStderr:\\n${stderrPreview}\\n` : \"\") +\n `\\nClaude output:\\n${outputPreview}`\n );\n }\n\n return result;\n }\n\n /** Poll filesystem and emit \"created\" events for newly detected files. */\n private reportProgress(\n themePath: string,\n existingModules: Set<string>,\n existingCss: Set<string>,\n existingJs: Set<string>,\n existingTemplates: Set<string>,\n onProgress: (step: string, detail: string) => void,\n ): void {\n let newItems = 0;\n\n // Check for new CSS files\n const currentCss = this.listDir(join(themePath, \"css\"));\n for (const f of currentCss) {\n if (existingCss.has(f) || !f.endsWith(\".css\")) continue;\n const key = `css:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Shared CSS (${f})`);\n newItems++;\n }\n }\n\n // Check for new JS files\n const currentJs = this.listDir(join(themePath, \"js\"));\n for (const f of currentJs) {\n if (existingJs.has(f) || !f.endsWith(\".js\")) continue;\n const key = `js:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Shared JS (${f})`);\n newItems++;\n }\n }\n\n // Try to detect expected module count from template file (once it exists)\n if (this.expectedModules === 0) {\n this.expectedModules = this.detectExpectedModules(themePath, existingTemplates);\n }\n\n // Check for new modules\n const currentModules = this.listModules(themePath);\n for (const mod of currentModules) {\n if (existingModules.has(mod)) continue;\n const key = `module:${mod}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n this.moduleCount++;\n const counter = this.expectedModules > 0\n ? `[${this.moduleCount}/${this.expectedModules}]`\n : `[${this.moduleCount}]`;\n onProgress(\"created\", `Module ${counter}: ${mod.replace(\".module\", \"\")}`);\n newItems++;\n }\n }\n\n // Check for new templates\n const currentTemplates = this.listDir(join(themePath, \"templates\"));\n for (const f of currentTemplates) {\n if (existingTemplates.has(f) || !f.endsWith(\".html\")) continue;\n const key = `template:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Page template (${f})`);\n newItems++;\n }\n }\n\n // Update spinner status text (only if no new items were just logged)\n if (newItems === 0) {\n if (this.moduleCount > 0) {\n const of = this.expectedModules > 0 ? `/${this.expectedModules}` : \"\";\n onProgress(\"status\", `${this.moduleCount}${of} modules created, conversion continuing...`);\n } else if (this.reported.size > 0) {\n onProgress(\"status\", \"Shared assets created, building modules...\");\n } else {\n onProgress(\"status\", \"Claude Code is analyzing source files...\");\n }\n }\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `You are converting a React landing page to native HubSpot CMS modules.\n\nSOURCE DIRECTORY: ${sourceDir}\nTHEME DIRECTORY: ${themePath}\n\nIMPORTANT — YOU MUST CREATE REAL FILES:\nYou have access to Write, Edit, Read, Glob, Grep, and Bash tools. You MUST use the Write tool to create each file. Do NOT just describe or list what files should be created — actually call the Write tool for every single file. If you do not call Write, no files will be created and the conversion will fail.\n\nSTEP-BY-STEP PROCESS:\n1. Use Glob to find all .tsx/.jsx files in ${sourceDir}/src/\n2. Use Read to read each component file and understand the page structure\n3. Use Write to create a shared CSS file at ${themePath}/css/<name>-theme.css\n - Include CSS custom properties, design system variables, utility classes\n - Add theme-override countermeasures (.body-wrapper:has(), scoped !important overrides)\n4. Use Write to create a shared JS file at ${themePath}/js/<name>-animations.js\n - Convert React hooks to vanilla JS (IntersectionObserver for scroll animations)\n - IIFE wrapper, DOMContentLoaded setup\n5. For EACH visual section of the page, use Write to create ALL FOUR files:\n a. ${themePath}/modules/<name>.module/fields.json\n - Editable fields for the section content\n - NEVER use \"textarea\" type (use \"text\" instead)\n - NEVER use \"name\" as a field name (use \"item_name\" instead)\n - Add a \"styles\" group with \"tab\": \"STYLE\" containing color pickers\n b. ${themePath}/modules/<name>.module/meta.json\n - Must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n c. ${themePath}/modules/<name>.module/module.html\n - HubL template that renders the section (convert JSX to HubL)\n d. ${themePath}/modules/<name>.module/module.css\n - REQUIRED — complete vanilla CSS for this section\n - Must include: layout, spacing, colors, typography, backgrounds, gradients, shadows, borders, hover effects, responsive breakpoints\n - Convert ALL Tailwind classes to BEM-style CSS. Do NOT skip this file.\n6. Use Write to create a page template at ${themePath}/templates/lp-<name>.html\n - Annotation: templateType: page, isAvailableForNewContent: true\n - Extends \"./layouts/base.html\"\n - Sets template_css and template_js variables\n - Wraps modules in dnd_area with dnd_section containers\n7. Read ${themePath}/templates/layouts/base.html and ensure it supports template_css and template_js variables\n\nCSS QUALITY: The converted page must visually match the original React page. Every module.css must be self-contained with complete styling for that section.\n\nDo NOT run hs upload — I will handle that separately.\n\nHUBSPOT CMS RULES:\n${getHubspotRules()}\n\nCONVERSION GUIDE:\n${guide}`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n // Find shared CSS (any non-boilerplate CSS in css/)\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n // Find shared JS (any non-boilerplate JS in js/)\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n // Find new template (prefer lp-* or non-boilerplate templates)\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n // First pass: look for lp-* templates (AI-generated naming convention)\n for (const file of readdirSync(templatesDir)) {\n if (file.startsWith(\"lp-\") && file.endsWith(\".html\")) {\n result.template = readFile(join(templatesDir, file));\n break;\n }\n }\n // Second pass: any non-boilerplate template with dnd_area\n if (!result.template) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !BOILERPLATE_TEMPLATES.has(file) &&\n !file.startsWith(\"system\")\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n // Third pass: fall back to any template with dnd_area\n if (!result.template) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n }\n\n // Scan modules/\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n // Only count modules that have at least fields.json and module.html\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n\n /** List module directories in modules/ */\n private listModules(themePath: string): Set<string> {\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return new Set();\n return new Set(\n readdirSync(modulesDir).filter((e) => e.endsWith(\".module\"))\n );\n }\n\n /** List files in a directory */\n private listDir(dir: string): Set<string> {\n if (!fileExists(dir)) return new Set();\n return new Set(readdirSync(dir));\n }\n\n /** Detect expected module count from template file (counts dnd_module references) */\n private detectExpectedModules(themePath: string, existingTemplates: Set<string>): number {\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return 0;\n\n for (const file of readdirSync(templatesDir)) {\n if (existingTemplates.has(file)) continue;\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n\n try {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n const matches = content.match(/dnd_module/g);\n return matches ? matches.length : 0;\n }\n } catch {\n // Skip unreadable files\n }\n }\n return 0;\n }\n\n /** Count .tsx/.jsx component files in the source directory */\n private countSourceComponents(sourceDir: string): number {\n const srcDir = join(sourceDir, \"src\");\n if (!fileExists(srcDir)) return 0;\n return this.countComponentsRecursive(srcDir);\n }\n\n private countComponentsRecursive(dir: string): number {\n let count = 0;\n for (const entry of readdirSync(dir)) {\n const fullPath = join(dir, entry);\n try {\n const stat = statSync(fullPath);\n if (stat.isDirectory() && entry !== \"node_modules\" && entry !== \".git\") {\n count += this.countComponentsRecursive(fullPath);\n } else if (/\\.(tsx|jsx)$/.test(entry) && !entry.includes(\".test.\") && !entry.includes(\".spec.\")) {\n count++;\n }\n } catch {\n // Skip unreadable entries\n }\n }\n return count;\n }\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport { join, basename } from \"node:path\";\nimport { readdirSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport {\n buildSystemPrompt,\n buildCssPrompt,\n buildJsPrompt,\n buildModulePrompt,\n buildTemplatePrompt,\n} from \"./prompts.js\";\nimport { readFile, fileExists, writeFile, ensureDir } from \"../utils/fs.js\";\n\nexport class ClaudeAPIEngine implements AIEngine {\n private client: Anthropic;\n private model = \"claude-sonnet-4-6\";\n\n constructor(apiKey?: string) {\n this.client = new Anthropic({\n apiKey: apiKey || process.env.ANTHROPIC_API_KEY,\n });\n }\n\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, conversionGuide, onProgress } = opts;\n const systemPrompt = buildSystemPrompt(conversionGuide);\n\n // Determine a page prefix from the source dir name\n const dirName = basename(sourceDir) || \"page\";\n const pagePrefix = dirName\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 15);\n\n // Step 1: Generate shared CSS\n onProgress(\"css\", \"Analyzing design system...\");\n const indexCss = this.findAndReadCSS(sourceDir);\n const tailwindConfig = this.findAndReadTailwind(sourceDir);\n\n const cssContent = await this.complete(\n systemPrompt,\n buildCssPrompt(indexCss, tailwindConfig, pagePrefix)\n );\n const cssPath = join(themePath, \"css\", `${pagePrefix}-theme.css`);\n writeFile(cssPath, cssContent);\n onProgress(\"css-done\", `Created css/${pagePrefix}-theme.css`);\n\n // Step 2: Generate shared JS\n onProgress(\"js\", \"Creating shared JavaScript...\");\n const hooksSource = this.findAndReadHooks(sourceDir);\n const interactiveSource = this.findInteractiveComponents(sourceDir);\n\n const jsContent = await this.complete(\n systemPrompt,\n buildJsPrompt(hooksSource, interactiveSource, pagePrefix)\n );\n const jsPath = join(themePath, \"js\", `${pagePrefix}-animations.js`);\n writeFile(jsPath, jsContent);\n onProgress(\"js-done\", `Created js/${pagePrefix}-animations.js`);\n\n // Step 3: Generate modules\n onProgress(\"modules\", \"Building modules...\");\n const components = this.findComponents(sourceDir);\n const modules: ModuleFiles[] = [];\n\n for (let i = 0; i < components.length; i++) {\n const comp = components[i];\n const moduleName = comp.name\n .replace(/Section$/, \"\")\n .replace(/([A-Z])/g, \" $1\")\n .trim();\n\n onProgress(\n \"module\",\n `Building ${moduleName}.module (${i + 1}/${components.length})...`\n );\n\n const source = readFile(comp.path);\n const response = await this.complete(\n systemPrompt,\n buildModulePrompt(source, moduleName, `See css/${pagePrefix}-theme.css`)\n );\n\n try {\n const parsed = JSON.parse(response);\n const mod: ModuleFiles = {\n moduleName,\n fieldsJson: typeof parsed.fieldsJson === \"string\"\n ? parsed.fieldsJson\n : JSON.stringify(parsed.fieldsJson, null, 2),\n metaJson: typeof parsed.metaJson === \"string\"\n ? parsed.metaJson\n : JSON.stringify(parsed.metaJson, null, 2),\n moduleHtml: parsed.moduleHtml || \"\",\n moduleCss: parsed.moduleCss || \"\",\n moduleJs: parsed.moduleJs || undefined,\n };\n\n // Write module files\n const modDir = join(themePath, \"modules\", `${moduleName}.module`);\n ensureDir(modDir);\n writeFile(join(modDir, \"fields.json\"), mod.fieldsJson);\n writeFile(join(modDir, \"meta.json\"), mod.metaJson);\n writeFile(join(modDir, \"module.html\"), mod.moduleHtml);\n writeFile(join(modDir, \"module.css\"), mod.moduleCss);\n if (mod.moduleJs) writeFile(join(modDir, \"module.js\"), mod.moduleJs);\n\n modules.push(mod);\n onProgress(\"module-done\", `${moduleName}.module (${this.countFiles(mod)} files)`);\n } catch {\n onProgress(\"module-error\", `Failed to parse ${moduleName} — skipping`);\n }\n }\n\n // Step 4: Generate template\n onProgress(\"template\", \"Creating page template...\");\n const moduleNames = modules.map((m) => m.moduleName);\n const templateContent = await this.complete(\n systemPrompt,\n buildTemplatePrompt(moduleNames, dirName, pagePrefix)\n );\n\n const templatePath = join(\n themePath,\n \"templates\",\n `lp-${pagePrefix}.html`\n );\n writeFile(templatePath, templateContent);\n onProgress(\"template-done\", `Created templates/lp-${pagePrefix}.html`);\n\n return {\n sharedCss: cssContent,\n sharedJs: jsContent,\n template: templateContent,\n modules,\n };\n }\n\n private async complete(system: string, user: string): Promise<string> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: 8192,\n system,\n messages: [{ role: \"user\", content: user }],\n });\n\n const textBlock = response.content.find((b) => b.type === \"text\");\n return textBlock?.text || \"\";\n }\n\n private findAndReadCSS(dir: string): string {\n const paths = [\n join(dir, \"src/index.css\"),\n join(dir, \"src/globals.css\"),\n join(dir, \"src/app/globals.css\"),\n join(dir, \"app/globals.css\"),\n ];\n for (const p of paths) {\n if (fileExists(p)) return readFile(p);\n }\n return \"\";\n }\n\n private findAndReadTailwind(dir: string): string {\n const paths = [\n join(dir, \"tailwind.config.ts\"),\n join(dir, \"tailwind.config.js\"),\n join(dir, \"tailwind.config.mjs\"),\n ];\n for (const p of paths) {\n if (fileExists(p)) return readFile(p);\n }\n return \"\";\n }\n\n private findAndReadHooks(dir: string): string {\n const hooksDir = join(dir, \"src/hooks\");\n if (!fileExists(hooksDir)) return \"\";\n try {\n return readdirSync(hooksDir)\n .filter((f) => f.endsWith(\".ts\") || f.endsWith(\".tsx\"))\n .map((f) => `// ${f}\\n${readFile(join(hooksDir, f))}`)\n .join(\"\\n\\n\");\n } catch {\n return \"\";\n }\n }\n\n private findInteractiveComponents(dir: string): string {\n const components = this.findComponents(dir);\n const interactive: string[] = [];\n\n for (const comp of components) {\n const content = readFile(comp.path);\n if (\n /carousel|accordion|typing|parallax|embla|swiper|collapsible/i.test(\n content\n )\n ) {\n interactive.push(`// ${comp.name}\\n${content}`);\n }\n }\n\n return interactive.join(\"\\n\\n\");\n }\n\n private findComponents(dir: string): { name: string; path: string }[] {\n const searchDirs = [\n join(dir, \"src/components/landing\"),\n join(dir, \"src/components/sections\"),\n join(dir, \"src/components\"),\n ];\n\n for (const searchDir of searchDirs) {\n if (!fileExists(searchDir)) continue;\n try {\n return readdirSync(searchDir)\n .filter(\n (f) =>\n (f.endsWith(\".tsx\") || f.endsWith(\".jsx\")) &&\n !f.startsWith(\"ui\") &&\n f !== \"index.tsx\" &&\n f !== \"index.jsx\"\n )\n .map((f) => ({\n name: f.replace(/\\.(tsx|jsx)$/, \"\"),\n path: join(searchDir, f),\n }));\n } catch {\n continue;\n }\n }\n\n return [];\n }\n\n private countFiles(mod: ModuleFiles): number {\n let count = 3; // fields.json, meta.json, module.html always present\n if (mod.moduleCss) count++;\n if (mod.moduleJs) count++;\n return count;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\nexport class GeminiCLIEngine implements AIEngine {\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", \"Running Gemini CLI (this may take a few minutes)...\");\n\n // Use async spawn so the event loop stays free for spinner animation\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"gemini\", [\"-p\", prompt], {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env },\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Gemini CLI failed: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0 && stderr && !stdout) {\n reject(new Error(`Gemini CLI failed: ${stderr}`));\n } else {\n resolve();\n }\n });\n\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Gemini CLI timed out after 10 minutes\"));\n }, 600_000);\n });\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n return this.scanGeneratedFiles(themePath);\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `Read the conversion guide below, then convert the React landing page at ${sourceDir} into native HubSpot CMS modules for the theme at ${themePath}.\n\nINSTRUCTIONS:\n1. Analyze all .tsx/.jsx components in the React source\n2. Create a shared CSS file in css/ with design system variables, utilities, and theme-override countermeasures\n3. Create a shared JS file in js/ for scroll animations and interactive features\n4. For each visual section, create a HubSpot module in modules/ with fields.json, meta.json, module.html, module.css\n5. Add a styles group with \"tab\": \"STYLE\" and color pickers to each module\n6. Create a page template in templates/ that assembles all modules\n7. Make sure base.html supports template_css and template_js variables\n\nCONVERSION GUIDE:\n${guide}\n\nDo NOT run hs upload — I will handle that separately.\nCreate all files directly in the theme directory.`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n (file.includes(\"theme\") || file.includes(\"page\")) &&\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n (file.includes(\"animation\") || file.includes(\"page\")) &&\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\nexport class CodexCLIEngine implements AIEngine {\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", \"Running OpenAI Codex (this may take a few minutes)...\");\n\n // Use async spawn so the event loop stays free for spinner animation\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"codex\", [\"exec\", \"--full-auto\", prompt], {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env },\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Codex CLI failed: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0 && stderr && !stdout) {\n reject(new Error(`Codex CLI failed: ${stderr}`));\n } else {\n resolve();\n }\n });\n\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Codex CLI timed out after 10 minutes\"));\n }, 600_000);\n });\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n return this.scanGeneratedFiles(themePath);\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `Read the conversion guide below, then convert the React landing page at ${sourceDir} into native HubSpot CMS modules for the theme at ${themePath}.\n\nINSTRUCTIONS:\n1. Analyze all .tsx/.jsx components in the React source\n2. Create a shared CSS file in css/ with design system variables, utilities, and theme-override countermeasures\n3. Create a shared JS file in js/ for scroll animations and interactive features\n4. For each visual section, create a HubSpot module in modules/ with fields.json, meta.json, module.html, module.css\n5. Add a styles group with \"tab\": \"STYLE\" and color pickers to each module\n6. Create a page template in templates/ that assembles all modules\n7. Make sure base.html supports template_css and template_js variables\n\nCONVERSION GUIDE:\n${guide}\n\nDo NOT run hs upload — I will handle that separately.\nCreate all files directly in the theme directory.`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n (file.includes(\"theme\") || file.includes(\"page\")) &&\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n (file.includes(\"animation\") || file.includes(\"page\")) &&\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n}\n","import { join, basename } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport {\n parseUploadErrors,\n parseApiErrors,\n autoFixError,\n type UploadError,\n} from \"../server/auto-fix.js\";\nimport { loadConfig, getHubSpotPak } from \"../utils/config.js\";\nimport { uploadTheme, deleteFile } from \"../hubspot/uploader.js\";\n\n/** Count \"Uploaded file\" lines in hs cms upload output */\nfunction countUploadedFiles(output: string): number {\n return (output.match(/^Uploaded file /gm) || []).length;\n}\n\nexport async function runUpload(themePath: string): Promise<boolean> {\n await ui.intro(\"Uploading to HubSpot\");\n\n const themeName = basename(themePath) || themePath;\n const config = loadConfig();\n const pak = getHubSpotPak();\n const useApi = config.hubspotUploadMode !== \"cli\" && !!pak;\n const s = await ui.spinner();\n\n const MAX_RETRIES = 3;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n s.start(\n attempt === 1\n ? \"Uploading theme...\"\n : `Retrying upload (attempt ${attempt}/${MAX_RETRIES})...`\n );\n\n let errors: UploadError[] = [];\n let uploadedCount = 0;\n let success = false;\n\n if (useApi) {\n // API mode — parallel upload\n const result = await uploadTheme(pak!, themePath, themeName, {\n onFileComplete: () => { uploadedCount++; },\n });\n success = result.success;\n if (!success) {\n errors = parseApiErrors(result.errors);\n } else {\n uploadedCount = result.uploaded;\n }\n } else {\n // CLI mode\n const result = run(`hs cms upload \"${themePath}\" \"${themeName}\"`, {\n cwd: join(themePath, \"..\"),\n });\n const fullOutput = [result.stdout, result.stderr].filter(Boolean).join(\"\\n\");\n uploadedCount = countUploadedFiles(fullOutput);\n success = result.success;\n if (!success) {\n errors = parseUploadErrors(fullOutput);\n }\n }\n\n if (success) {\n s.stop(`All files uploaded! (${uploadedCount} files)`);\n await ui.outro(\"Upload complete!\");\n return true;\n }\n\n if (uploadedCount > 0) {\n s.stop(`${uploadedCount} files uploaded, but some errors occurred`);\n } else {\n s.stop(\"Upload failed\");\n }\n\n if (errors.length === 0) {\n ui.logError(\"Upload failed with unknown error.\");\n\n if (uploadedCount > 0) {\n ui.logWarn(\n \"Most files uploaded successfully. The theme may already be usable in HubSpot.\\n\" +\n \"You can check your HubSpot Design Manager to verify.\"\n );\n const proceed = await ui.confirm({\n message: \"Continue anyway (theme is likely uploaded)?\",\n initialValue: true,\n });\n if (proceed) return true;\n }\n\n if (attempt < MAX_RETRIES) {\n const retry = await ui.confirm({ message: \"Try uploading again?\" });\n if (!retry) break;\n continue;\n }\n break;\n }\n\n // Try to auto-fix known errors\n let anyFixed = false;\n for (const error of errors) {\n if (error.fixable) {\n const fixed = autoFixError(themePath, error);\n if (fixed) {\n ui.logSuccess(`Auto-fixed: ${error.message}`);\n anyFixed = true;\n } else {\n ui.logWarn(`Could not auto-fix: ${error.message}`);\n }\n } else {\n ui.logError(error.message);\n }\n }\n\n if (anyFixed && attempt < MAX_RETRIES) continue;\n\n if (uploadedCount > 0) {\n ui.logWarn(\n `${uploadedCount} files uploaded successfully despite errors.\\n` +\n \"The theme may work — check HubSpot Design Manager.\"\n );\n const proceed = await ui.confirm({\n message: \"Continue anyway?\",\n initialValue: true,\n });\n if (proceed) return true;\n }\n\n if (!anyFixed) {\n s.start(\"Cleaning up stuck modules...\");\n if (useApi) {\n try { await deleteFile(pak!, `${themeName}/modules`); } catch { /* ignore */ }\n } else {\n run(`hs cms delete \"${themeName}/modules\"`, { cwd: join(themePath, \"..\") });\n }\n s.stop(\"Cleaned up modules, retrying...\");\n }\n }\n\n ui.logError(\"Upload failed after multiple attempts.\");\n return false;\n}\n","/**\n * Auto-fix utilities for common HubSpot upload errors.\n * Shared between CLI wizard (uploader.ts) and web server (server.ts).\n */\n\nimport { join } from \"node:path\";\nimport { readdirSync, rmSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"../utils/fs.js\";\n\nexport interface UploadError {\n file: string;\n message: string;\n fixable: boolean;\n}\n\n/** Parse API upload errors into the standard UploadError interface. */\nexport function parseApiErrors(apiErrors: { file: string; status: number; message: string; category?: string; detail?: string }[]): UploadError[] {\n const errors: UploadError[] = [];\n\n for (const err of apiErrors) {\n const msg = `${err.message}${err.detail ? ` — ${err.detail}` : \"\"}`;\n let fixable = false;\n\n // Detect fixable error patterns from API response messages\n if (/textarea|unknown.*field.*type/i.test(msg)) fixable = true;\n if (/reserved.*name|missing field name|field null/i.test(msg)) fixable = true;\n if (/could not resolve.*now/i.test(msg)) fixable = true;\n if (/hubdb|do not have access/i.test(msg)) fixable = true;\n if (/invalid default value|link.*invalid|deserializ/i.test(msg)) fixable = true;\n if (/color.*invalid/i.test(msg)) fixable = true;\n\n errors.push({\n file: err.file || \"unknown\",\n message: msg,\n fixable,\n });\n }\n\n return errors;\n}\n\nexport function parseUploadErrors(output: string): UploadError[] {\n const errors: UploadError[] = [];\n\n if (/textarea.*not.*valid|unknown.*field.*type/i.test(output)) {\n const fileMatch = output.match(/(?:in|file:?)\\s+(\\S+fields\\.json)/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: '\"textarea\" is not a valid field type',\n fixable: true,\n });\n }\n\n if (/missing field name|field null/i.test(output)) {\n const fileMatch = output.match(/(?:in|file:?)\\s+(\\S+fields\\.json)/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: '\"name\" is a reserved field name',\n fixable: true,\n });\n }\n\n if (/could not resolve.*now/i.test(output)) {\n errors.push({\n file: \"module.html\",\n message: \"now() is not a valid HubL function\",\n fixable: true,\n });\n }\n\n if (/hubdb|do not have access to hubdb/i.test(output)) {\n errors.push({\n file: \"templates\",\n message: \"HubDB requires CMS Hub Pro/Enterprise\",\n fixable: true,\n });\n }\n\n if (/invalid default value|link.*field.*invalid/i.test(output)) {\n const fieldMatch = output.match(/field.*?(\\w+)\\s+has an invalid/i);\n errors.push({\n file: fieldMatch?.[1] || \"fields.json\",\n message: `Link field has invalid default value`,\n fixable: true,\n });\n }\n\n if (/failed to deserialize/i.test(output)) {\n const fileMatch = output.match(/file '([^']+)'/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: \"fields.json deserialization error\",\n fixable: true,\n });\n }\n\n if (/format for the color value is invalid/i.test(output)) {\n errors.push({\n file: \"fields.json\",\n message: \"Color field has invalid format (rgba/rgb/named — must be hex)\",\n fixable: true,\n });\n }\n\n return errors;\n}\n\nexport function applyAutoFixes(themePath: string): string[] {\n const fixes: string[] = [];\n if (fixTextareaFields(themePath)) fixes.push('textarea → text');\n if (fixReservedNames(themePath)) fixes.push('name → item_name');\n if (fixNowFunction(themePath)) fixes.push('now() → local_dt');\n if (fixHubDbTemplates(themePath)) fixes.push('Removed HubDB templates');\n if (fixLinkFieldDefaults(themePath)) fixes.push('Fixed link field defaults');\n if (fixColorFieldDefaults(themePath)) fixes.push('Fixed rgba/invalid color values → hex');\n if (fixCdnImports(themePath)) fixes.push('Stripped CDN @import statements');\n return fixes;\n}\n\nexport function autoFixError(themePath: string, error: UploadError): boolean {\n if (error.message.includes(\"textarea\")) return fixTextareaFields(themePath);\n if (error.message.includes(\"reserved field name\")) return fixReservedNames(themePath);\n if (error.message.includes(\"now()\")) return fixNowFunction(themePath);\n if (error.message.includes(\"HubDB\")) return fixHubDbTemplates(themePath);\n if (error.message.includes(\"invalid default value\") || error.message.includes(\"deserialization\"))\n return fixLinkFieldDefaults(themePath);\n if (error.message.includes(\"invalid format\") && error.message.includes(\"color\"))\n return fixColorFieldDefaults(themePath);\n return false;\n}\n\nexport function fixTextareaFields(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n let content = readFile(fieldsPath);\n if (content.includes('\"textarea\"')) {\n content = content.replace(/\"textarea\"/g, '\"text\"');\n writeFile(fieldsPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixReservedNames(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n let content = readFile(fieldsPath);\n if (/\"name\":\\s*\"name\"/g.test(content)) {\n content = content.replace(/\"name\":\\s*\"name\"/g, '\"name\": \"item_name\"');\n writeFile(fieldsPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixNowFunction(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (!fileExists(htmlPath)) continue;\n let content = readFile(htmlPath);\n if (content.includes(\"now()\")) {\n content = content.replace(/now\\(\\)/g, \"local_dt\");\n writeFile(htmlPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixHubDbTemplates(themePath: string): boolean {\n let fixed = false;\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return false;\n\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\")) continue;\n const filePath = join(templatesDir, file);\n const content = readFile(filePath);\n if (content.includes(\"hubdb_table\") || content.includes(\"hubdb_table_rows\")) {\n rmSync(filePath);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixLinkFieldDefaults(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n try {\n const fields = JSON.parse(readFile(fieldsPath));\n if (fixLinkFieldsRecursive(fields)) {\n writeFile(fieldsPath, JSON.stringify(fields, null, 2) + \"\\n\");\n fixed = true;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n return fixed;\n}\n\n/**\n * Strip external CDN @import statements from all CSS files.\n * Google Fonts and other CDN imports can fail in HubSpot's editor\n * and cause content to appear invisible.\n */\nexport function fixCdnImports(themePath: string): boolean {\n let fixed = false;\n\n // Check shared CSS files\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (!file.endsWith(\".css\")) continue;\n const filePath = join(cssDir, file);\n let content = readFile(filePath);\n const cleaned = content.replace(/@import\\s+url\\(['\"]?https?:\\/\\/[^)]+['\"]?\\)\\s*;?/gi, \"\");\n if (cleaned !== content) {\n writeFile(filePath, cleaned);\n fixed = true;\n }\n }\n }\n\n // Check module CSS files\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const cssPath = join(modulesDir, entry, \"module.css\");\n if (!fileExists(cssPath)) continue;\n let content = readFile(cssPath);\n const cleaned = content.replace(/@import\\s+url\\(['\"]?https?:\\/\\/[^)]+['\"]?\\)\\s*;?/gi, \"\");\n if (cleaned !== content) {\n writeFile(cssPath, cleaned);\n fixed = true;\n }\n }\n }\n\n // Check module HTML for <link> tags to external CDNs\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (!fileExists(htmlPath)) continue;\n let content = readFile(htmlPath);\n const cleaned = content.replace(/<link[^>]+href=['\"]https?:\\/\\/[^'\"]+['\"][^>]*>/gi, \"\");\n if (cleaned !== content) {\n writeFile(htmlPath, cleaned);\n fixed = true;\n }\n }\n }\n\n return fixed;\n}\n\n/**\n * Fix color fields that use rgba(), rgb(), named colors, or 3-digit hex.\n * HubSpot requires: { \"color\": \"#rrggbb\", \"opacity\": 100 }\n */\nexport function fixColorFieldDefaults(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n try {\n const fields = JSON.parse(readFile(fieldsPath));\n if (fixColorFieldsRecursive(fields)) {\n writeFile(fieldsPath, JSON.stringify(fields, null, 2) + \"\\n\");\n fixed = true;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n return fixed;\n}\n\nfunction fixColorFieldsRecursive(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"color\" && f.default && typeof f.default === \"object\") {\n const def = f.default as Record<string, unknown>;\n const colorVal = def.color;\n if (typeof colorVal === \"string\" && !isValidHexColor(colorVal)) {\n const converted = convertToHex(colorVal);\n if (converted) {\n def.color = converted.hex;\n // If the rgba had opacity, use that instead of the existing opacity\n if (converted.opacity !== undefined) {\n def.opacity = converted.opacity;\n }\n fixed = true;\n }\n }\n }\n\n if (Array.isArray(f.children)) {\n if (fixColorFieldsRecursive(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\nfunction isValidHexColor(color: string): boolean {\n return /^#[0-9a-fA-F]{6}$/.test(color);\n}\n\nfunction convertToHex(color: string): { hex: string; opacity?: number } | null {\n // 3-digit hex → 6-digit\n const hex3 = color.match(/^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/);\n if (hex3) {\n return { hex: `#${hex3[1]}${hex3[1]}${hex3[2]}${hex3[2]}${hex3[3]}${hex3[3]}` };\n }\n\n // rgba(r, g, b, a)\n const rgba = color.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*([\\d.]+))?\\s*\\)/i);\n if (rgba) {\n const r = Math.min(255, parseInt(rgba[1]));\n const g = Math.min(255, parseInt(rgba[2]));\n const b = Math.min(255, parseInt(rgba[3]));\n const hex = `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\n const opacity = rgba[4] !== undefined ? Math.round(parseFloat(rgba[4]) * 100) : undefined;\n return { hex, opacity };\n }\n\n // Named colors (common ones)\n const named: Record<string, string> = {\n white: \"#ffffff\", black: \"#000000\", red: \"#ff0000\", green: \"#008000\",\n blue: \"#0000ff\", yellow: \"#ffff00\", orange: \"#ffa500\", purple: \"#800080\",\n gray: \"#808080\", grey: \"#808080\", transparent: \"#000000\",\n };\n const lower = color.toLowerCase().trim();\n if (named[lower]) {\n return { hex: named[lower], opacity: lower === \"transparent\" ? 0 : undefined };\n }\n\n return null;\n}\n\nfunction fixLinkFieldsRecursive(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"link\") {\n const def = f.default;\n const needsFix =\n typeof def === \"string\" ||\n def === undefined ||\n def === null ||\n (typeof def === \"object\" && !(def as Record<string, unknown>).url);\n\n if (needsFix) {\n const href = typeof def === \"string\" ? def : \"\";\n f.default = {\n url: { href, type: \"EXTERNAL\" },\n open_in_new_tab: false,\n no_follow: false,\n };\n fixed = true;\n }\n }\n\n if (Array.isArray(f.children)) {\n if (fixLinkFieldsRecursive(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n","/**\n * Parallel theme uploader — walks a theme directory and uploads all files\n * to HubSpot via the CMS Source Code API v3.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { uploadFile, type HubSpotApiError, type UploadResult } from \"./api.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UploadFileError {\n file: string;\n status: number;\n message: string;\n category?: string;\n detail?: string;\n}\n\nexport interface UploadThemeResult {\n success: boolean;\n uploaded: number;\n failed: number;\n total: number;\n errors: UploadFileError[];\n}\n\nexport interface UploadThemeOptions {\n concurrency?: number;\n onFileStart?: (path: string) => void;\n onFileComplete?: (path: string) => void;\n onFileError?: (path: string, error: UploadFileError) => void;\n onProgress?: (completed: number, total: number) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Excluded directories\n// ---------------------------------------------------------------------------\n\nconst EXCLUDED_DIRS = new Set([\".git\", \"node_modules\", \".vibespot\", \".DS_Store\"]);\n\n// ---------------------------------------------------------------------------\n// File discovery\n// ---------------------------------------------------------------------------\n\n/** Recursively walk a directory and return all file paths. */\nfunction walkDir(dir: string): string[] {\n const files: string[] = [];\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (EXCLUDED_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith(\".\") && entry.name !== \".gitkeep\") continue;\n\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n files.push(...walkDir(fullPath));\n } else if (entry.isFile()) {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// Parallel upload\n// ---------------------------------------------------------------------------\n\n/** Run async tasks with bounded concurrency. */\nasync function parallelMap<T>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const i = index++;\n await fn(items[i]);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n}\n\n// ---------------------------------------------------------------------------\n// Main upload function\n// ---------------------------------------------------------------------------\n\n/**\n * Upload an entire theme directory to HubSpot.\n * Files are uploaded in parallel with configurable concurrency.\n */\nexport async function uploadTheme(\n pak: string,\n themePath: string,\n themeName: string,\n opts: UploadThemeOptions = {},\n): Promise<UploadThemeResult> {\n const concurrency = opts.concurrency ?? 5;\n\n // Discover all files\n const localFiles = walkDir(themePath);\n const total = localFiles.length;\n let uploaded = 0;\n let failed = 0;\n const errors: UploadFileError[] = [];\n\n await parallelMap(localFiles, concurrency, async (localPath) => {\n // Map local path to remote path: themeName/relative/path\n const rel = relative(themePath, localPath).replace(/\\\\/g, \"/\");\n const remotePath = `${themeName}/${rel}`;\n\n opts.onFileStart?.(rel);\n\n const result = await uploadFile(pak, remotePath, localPath);\n\n if (result.success) {\n uploaded++;\n opts.onFileComplete?.(rel);\n } else {\n failed++;\n const err: UploadFileError = {\n file: rel,\n status: result.error?.status || 0,\n message: result.error?.message || \"Unknown error\",\n category: result.error?.category,\n detail: result.error?.detail,\n };\n errors.push(err);\n opts.onFileError?.(rel, err);\n }\n\n opts.onProgress?.(uploaded + failed, total);\n });\n\n return {\n success: failed === 0,\n uploaded,\n failed,\n total,\n errors,\n };\n}\n\n/**\n * Delete a remote path (used for cleaning up stuck modules).\n */\nexport { deleteFile } from \"./api.js\";\n","import { execFileSync } from \"node:child_process\";\nimport { rmSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport { detectDataCenter } from \"../utils/detect.js\";\nimport { fileExists } from \"../utils/fs.js\";\n\nexport async function showNextSteps(opts: {\n portalId: string;\n sourceDir: string;\n themePath: string;\n wasCloned: boolean;\n}): Promise<void> {\n const { portalId, sourceDir, themePath, wasCloned } = opts;\n await ui.intro(\"You're all set!\");\n\n const dataCenter = detectDataCenter(portalId);\n const host =\n dataCenter === \"eu1\" ? \"app-eu1.hubspot.com\" : \"app.hubspot.com\";\n\n await ui.note(\n `Your React page has been converted and uploaded to HubSpot.\\n` +\n `The theme and modules are now in your account, but you still\\n` +\n `need to ${theme.bold(\"create a new landing page\")} that uses them.\\n\\n` +\n `Next steps:\\n\\n` +\n ` ${theme.bold(\"1.\")} Go to HubSpot ${theme.muted(\"→\")} Content ${theme.muted(\"→\")} Landing Pages ${theme.muted(\"→\")} Create\\n` +\n ` ${theme.bold(\"2.\")} Choose your uploaded theme from the theme picker\\n` +\n ` ${theme.bold(\"3.\")} Select the landing page template that was just created\\n` +\n ` ${theme.bold(\"4.\")} Your converted modules will appear — drag them onto the page\\n` +\n ` ${theme.bold(\"5.\")} Click each section to edit text, images, and colors\\n` +\n ` ${theme.bold(\"6.\")} Upload images via File Manager ${theme.muted(\"(Settings → Files)\")}\\n` +\n ` ${theme.bold(\"7.\")} Preview and publish!`,\n \"What's next\"\n );\n\n const openBrowser = await ui.confirm({\n message: \"Open HubSpot Landing Pages in your browser?\",\n });\n\n if (openBrowser) {\n const url = portalId\n ? `https://${host}/page-ui/${portalId}/management/pages/landing`\n : `https://${host}`;\n\n try {\n // Cross-platform browser open\n const platform = process.platform;\n if (platform === \"darwin\") {\n execFileSync(\"open\", [url], { stdio: \"ignore\" });\n } else if (platform === \"win32\") {\n execFileSync(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n } else {\n execFileSync(\"xdg-open\", [url], { stdio: \"ignore\" });\n }\n ui.logSuccess(\"Opening HubSpot Landing Pages...\");\n } catch {\n ui.log(`Open this URL in your browser: ${theme.info(url)}`);\n }\n }\n\n // Offer to clean up local directories\n const dirsToClean: { path: string; label: string }[] = [];\n if (wasCloned && fileExists(sourceDir)) {\n dirsToClean.push({ path: sourceDir, label: `Cloned source (${basename(sourceDir)})` });\n }\n if (fileExists(themePath)) {\n dirsToClean.push({ path: themePath, label: `Theme directory (${basename(themePath)})` });\n }\n\n if (dirsToClean.length > 0) {\n const cleanup = await ui.confirm({\n message: \"Clean up local working directories?\",\n });\n\n if (cleanup) {\n for (const dir of dirsToClean) {\n try {\n rmSync(dir.path, { recursive: true, force: true });\n ui.logSuccess(`Removed ${dir.label}`);\n } catch {\n ui.logWarn(`Could not remove ${dir.label} — delete manually if needed.`);\n }\n }\n }\n }\n\n await ui.outro(`Thanks for using hub${theme.vibes(\"Vibes\")}! ${theme.vibes(\"~\")}`);\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runPreflight } from \"../wizard/preflight.js\";\n\nexport async function initCommand(): Promise<void> {\n printBanner();\n await runPreflight();\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { setupSource } from \"../wizard/source.js\";\nimport { setupTheme } from \"../wizard/theme-setup.js\";\nimport { runConversion } from \"../wizard/conversion.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nexport async function convertCommand(): Promise<void> {\n printBanner();\n\n const config = loadConfig();\n\n if (!config.aiEngine) {\n ui.logError(\n \"AI engine not configured. Run `vibespot init` first or use the full wizard with `vibespot`.\"\n );\n process.exit(1);\n }\n\n const source = await setupSource();\n const themeInfo = await setupTheme();\n\n await runConversion({\n aiEngine: config.aiEngine,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n });\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runUpload } from \"../wizard/uploader.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nexport async function uploadCommand(): Promise<void> {\n printBanner();\n\n const config = loadConfig();\n\n if (!config.lastThemePath) {\n const path = await ui.text({\n message: \"Path to your HubSpot theme directory:\",\n placeholder: \"./my-theme\",\n validate: (v) => (v.trim() ? undefined : \"Path is required\"),\n });\n await runUpload(path);\n } else {\n const useLast = await ui.confirm({\n message: `Upload from ${config.lastThemePath}?`,\n });\n\n if (useLast) {\n await runUpload(config.lastThemePath);\n } else {\n const path = await ui.text({\n message: \"Path to your HubSpot theme directory:\",\n placeholder: \"./my-theme\",\n });\n await runUpload(path);\n }\n }\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport {\n detectNode,\n detectGit,\n detectHubSpotCLI,\n detectClaudeCode,\n detectGeminiCLI,\n detectCodexCLI,\n detectHubSpotAuth,\n nodeVersionOk,\n hsCliVersionOk,\n} from \"../utils/detect.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport async function doctorCommand(): Promise<void> {\n printBanner();\n await ui.intro(\"Environment Diagnostics\");\n\n let issues = 0;\n\n // Node.js\n const node = detectNode();\n if (!node.found) {\n ui.logError(\"Node.js — not installed\");\n ui.log(\" Install from https://nodejs.org\");\n issues++;\n } else if (!nodeVersionOk(node.version)) {\n ui.logWarn(`Node.js v${node.version} — too old (need 18+)`);\n ui.log(\" Update at https://nodejs.org\");\n issues++;\n } else {\n ui.logSuccess(`Node.js v${node.version}`);\n }\n\n // Git\n const git = detectGit();\n if (!git.found) {\n ui.logError(\"Git — not installed\");\n ui.log(\" Install from https://git-scm.com\");\n issues++;\n } else {\n ui.logSuccess(`Git ${git.version}`);\n }\n\n // HubSpot CLI (only needed for deployment)\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n ui.logWarn(\"HubSpot CLI — not installed (only needed for deployment)\");\n ui.log(\" Install: npm install -g @hubspot/cli\");\n } else if (!hsCliVersionOk(hs.version)) {\n ui.logWarn(`HubSpot CLI v${hs.version} — too old (need v8+)`);\n ui.log(\" Update: npm install -g @hubspot/cli@latest\");\n issues++;\n } else {\n ui.logSuccess(`HubSpot CLI v${hs.version}`);\n\n // HubSpot auth\n const auth = detectHubSpotAuth();\n if (!auth.authenticated) {\n ui.logWarn(\"HubSpot — not authenticated\");\n ui.log(\" Run: hs init\");\n } else {\n ui.logSuccess(\n `HubSpot portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`\n );\n }\n }\n\n // AI engines\n const claude = detectClaudeCode();\n if (claude.found) {\n ui.logSuccess(`Claude Code ${claude.version} at ${claude.path}`);\n } else {\n ui.log(theme.muted(\"Claude Code — not installed\"));\n }\n\n const gemini = detectGeminiCLI();\n if (gemini.found) {\n ui.logSuccess(`Gemini CLI ${gemini.version} at ${gemini.path}`);\n } else {\n ui.log(theme.muted(\"Gemini CLI — not installed\"));\n }\n\n const codex = detectCodexCLI();\n if (codex.found) {\n ui.logSuccess(`OpenAI Codex ${codex.version} at ${codex.path}`);\n } else {\n ui.log(theme.muted(\"OpenAI Codex — not installed\"));\n }\n\n // API keys\n const config = loadConfig();\n\n const anthropicKey = !!(config.anthropicApiKey || process.env.ANTHROPIC_API_KEY);\n const openaiKey = !!(config.openaiApiKey || process.env.OPENAI_API_KEY);\n const geminiKey = !!(config.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY);\n\n if (anthropicKey) ui.logSuccess(\"Anthropic API key configured\");\n else ui.log(theme.muted(\"Anthropic API key — not set\"));\n\n if (openaiKey) ui.logSuccess(\"OpenAI API key configured\");\n else ui.log(theme.muted(\"OpenAI API key — not set\"));\n\n if (geminiKey) ui.logSuccess(\"Google AI API key configured\");\n else ui.log(theme.muted(\"Google AI API key — not set\"));\n const engineLabels: Record<string, string> = {\n \"claude-code\": \"Claude Code\",\n \"api\": \"Anthropic API\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-api\": \"Gemini API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"codex-cli\": \"OpenAI Codex\",\n };\n if (config.aiEngine) {\n ui.logSuccess(`AI engine: ${engineLabels[config.aiEngine] || config.aiEngine}`);\n }\n if (config.lastThemePath) {\n ui.log(theme.muted(`Last theme: ${config.lastThemePath}`));\n }\n\n // No AI option available\n if (!claude.found && !gemini.found && !codex.found && !anthropicKey && !openaiKey && !geminiKey) {\n ui.logWarn(\"No AI engine available\");\n ui.log(\" Fastest: Set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY)\");\n ui.log(\" Or install: Claude Code — https://claude.ai/code\");\n ui.log(\" Gemini CLI — https://github.com/google-gemini/gemini-cli\");\n ui.log(\" Codex CLI — https://github.com/openai/codex\");\n issues++;\n }\n\n console.log();\n if (issues === 0) {\n await ui.outro(\"Everything looks good!\");\n } else {\n await ui.outro(\n theme.warn(`${issues} issue${issues > 1 ? \"s\" : \"\"} found — see above`)\n );\n }\n}\n","/**\n * `vibespot vibe` — Vibe coding mode.\n * Immediately starts a local server and opens the browser.\n * All setup happens in the web UI — zero CLI prompts.\n */\n\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { startServer } from \"../server/server.js\";\nimport { saveSession } from \"../server/session.js\";\n\nconst DEFAULT_PORT = 4200;\n\nexport async function vibeCommand(): Promise<void> {\n const accent = chalk.hex(\"#e8613a\");\n const dim = chalk.dim;\n\n console.log(\"\");\n console.log(accent(\" v vibeSpot\"));\n console.log(dim(\" Starting...\\n\"));\n\n const uiDir = resolveUiDir();\n if (!uiDir) {\n console.error(chalk.red(\" Could not find UI assets. Is the package installed correctly?\"));\n process.exit(1);\n }\n\n try {\n const { port, close } = await startServer({ port: DEFAULT_PORT, uiDir });\n const url = `http://localhost:${port}`;\n\n console.log(accent(` v ${url}`));\n console.log(dim(\" Press Ctrl+C to stop\\n\"));\n\n // Auto-open browser\n try {\n if (process.platform === \"darwin\") {\n execFileSync(\"open\", [url], { stdio: \"ignore\" });\n } else if (process.platform === \"win32\") {\n execFileSync(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n } else {\n execFileSync(\"xdg-open\", [url], { stdio: \"ignore\" });\n }\n } catch {\n // Browser open failed — user can open manually\n }\n\n // Keep running until Ctrl+C\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", () => {\n console.log(dim(\"\\n Saving session...\"));\n saveSession();\n close();\n console.log(dim(\" Goodbye!\\n\"));\n resolve();\n // Force exit after a short grace period — open connections\n // (WebSocket, keep-alive HTTP) can keep the process alive indefinitely.\n setTimeout(() => process.exit(0), 500);\n });\n });\n } catch (err) {\n console.error(chalk.red(` Failed to start: ${err instanceof Error ? err.message : String(err)}`));\n process.exit(1);\n }\n}\n\nfunction resolveUiDir(): string | null {\n const candidates = [\n join(import.meta.dirname, \"../../ui\"),\n join(import.meta.dirname, \"../ui\"),\n join(process.cwd(), \"ui\"),\n ];\n\n for (const dir of candidates) {\n if (existsSync(join(dir, \"index.html\"))) return dir;\n }\n\n return null;\n}\n","/**\n * Local development server for vibeSpot vibe coding mode.\n * Serves the UI, handles WebSocket connections, and manages AI interactions.\n */\n\nimport { createServer, IncomingMessage, ServerResponse } from \"node:http\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, extname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport {\n getSession,\n addMessage,\n getOrderedModules,\n updateModules,\n reorderModules,\n writeModulesToDisk,\n saveSession,\n getActiveTemplate,\n} from \"./session.js\";\nimport { commitThemeState, commitTemplateState, isGitAvailable } from \"./project-git.js\";\nimport { buildPreviewHtml, buildModulePreviewHtml } from \"./preview.js\";\nimport { handleGenerateStream, handleAgenticGenerate, applyPipelineResult, shouldUseAgenticMode, setParseWarningCallback, resolveAgenticEngine } from \"./ai-handler.js\";\nimport { loadConfig, getHubSpotPak, getActiveHubSpotAccount } from \"../utils/config.js\";\nimport { detectHubSpotAuth, detectDataCenter, detectHubSpotAuthFromConfig } from \"../utils/detect.js\";\nimport { applyAutoFixes, parseUploadErrors, parseApiErrors } from \"./auto-fix.js\";\nimport { startStreamingJob, startJobSafe, getJob, addJobListener, removeJobListener } from \"./process-manager.js\";\nimport { uploadTheme, type UploadFileError } from \"../hubspot/uploader.js\";\nimport { jsonResponse } from \"./route-helpers.js\";\nimport { getChangelog } from \"../utils/fs.js\";\n\n// Route modules\nimport {\n handleSetupInfoRoute,\n handleSetupCreateRoute,\n handleSetupFetchRoute,\n handleSetupOpenRoute,\n handleSetupResumeRoute,\n handleSetupApiKeyRoute,\n handleSetupRemoteThemesRoute,\n} from \"./routes/setup.js\";\nimport {\n handleSettingsStatusRoute,\n handleSettingsEngineRoute,\n handleSettingsApiKeyRoute,\n handleSettingsInstallRoute,\n handleSettingsHsAuthRoute,\n handleSettingsGhAuthRoute,\n handleSettingsHsSwitchRoute,\n handleSettingsGhLogoutRoute,\n handleSettingsCLIAuthRoute,\n handleSettingsHsModeRoute,\n handleSettingsCliToggleRoute,\n handleSettingsGenericRoute,\n handleSettingsJobRoute,\n} from \"./routes/settings.js\";\nimport {\n handleClaudeOAuthSaveRoute,\n handleClaudeOAuthStatusRoute,\n handleClaudeOAuthLogoutRoute,\n} from \"./routes/claude-oauth.js\";\nimport {\n handleThemesRoute,\n handleThemeSwitchRoute,\n handleDeleteLocalThemeRoute,\n handleRenameThemeRoute,\n} from \"./routes/themes.js\";\nimport {\n handleDashboardRoute,\n handleDownloadZipRoute,\n handleTemplatesRoute,\n handleTemplateActivateRoute,\n handleTemplateRenameRoute,\n handleTemplateCloneRoute,\n handleModuleLibraryRoute,\n handleAddModuleToTemplateRoute,\n handleBrandAssetsRoute,\n handleDesignExtractRoute,\n handleReferenceImportRoute,\n} from \"./routes/templates.js\";\nimport {\n handleSessionRoute,\n handleModulesRoute,\n handleReorderRoute,\n handleUploadRoute,\n handleFieldRoute,\n handleImportRoute,\n handleHistoryRoute,\n handleRollbackRoute,\n handleCodeUpdateRoute,\n} from \"./routes/modules.js\";\nimport { handleFileUploadRoute } from \"./routes/upload-files.js\";\n\n// ---------------------------------------------------------------------------\n// MIME types for static serving\n// ---------------------------------------------------------------------------\n\nconst MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".webp\": \"image/webp\",\n \".gif\": \"image/gif\",\n \".ico\": \"image/x-icon\",\n \".woff2\": \"font/woff2\",\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface ServerOptions {\n port: number;\n uiDir: string;\n}\n\nexport function startServer(opts: ServerOptions): Promise<{ port: number; close: () => void }> {\n const { port, uiDir } = opts;\n\n const server = createServer((req, res) => handleRequest(req, res, uiDir));\n\n // WebSocket server — upgrade on the same HTTP server\n const wss = new WebSocketServer({ server });\n wss.on(\"connection\", (ws) => handleWsConnection(ws));\n\n return new Promise((resolve, reject) => {\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n // Try next port\n server.listen(port + 1, () => {\n resolve({\n port: port + 1,\n close: () => { server.close(); wss.close(); },\n });\n });\n } else {\n reject(err);\n }\n });\n\n server.listen(port, () => {\n resolve({\n port,\n close: () => { server.close(); wss.close(); },\n });\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// HTTP request handler\n// ---------------------------------------------------------------------------\n\nfunction handleRequest(req: IncomingMessage, res: ServerResponse, uiDir: string): void {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const method = req.method || \"GET\";\n\n // Security headers\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n res.setHeader(\"X-Frame-Options\", \"DENY\");\n res.setHeader(\"X-XSS-Protection\", \"1; mode=block\");\n res.setHeader(\"Referrer-Policy\", \"strict-origin-when-cross-origin\");\n\n // API routes\n if (url.pathname.startsWith(\"/api/\")) {\n handleApiRoute(method, url.pathname, req, res);\n return;\n }\n\n // Preview route — returns rendered preview HTML\n if (url.pathname === \"/preview\") {\n const html = buildPreviewHtml();\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n return;\n }\n\n // Single-module preview (for dashboard module library)\n if (url.pathname === \"/module-preview\") {\n const moduleName = url.searchParams.get(\"module\") || \"\";\n const html = buildModulePreviewHtml(moduleName);\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html || \"<!-- module not found -->\");\n return;\n }\n\n // Theme assets — serve uploaded images for preview\n if (url.pathname.startsWith(\"/theme-assets/\")) {\n serveThemeAsset(url.pathname.slice(\"/theme-assets/\".length), res);\n return;\n }\n\n // Static files from ui/ directory\n serveStatic(url.pathname, uiDir, req, res);\n}\n\n// ---------------------------------------------------------------------------\n// API routes\n// ---------------------------------------------------------------------------\n\nfunction handleApiRoute(\n method: string,\n path: string,\n req: IncomingMessage,\n res: ServerResponse\n): void {\n // CORS — restrict to localhost origins only\n const origin = req.headers.origin || \"\";\n if (/^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/.test(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n }\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n switch (path) {\n case \"/api/session\":\n handleSessionRoute(method, res);\n break;\n\n case \"/api/modules\":\n handleModulesRoute(method, req, res);\n break;\n\n case \"/api/modules/reorder\":\n handleReorderRoute(req, res);\n break;\n\n case \"/api/modules/code\":\n handleCodeUpdateRoute(req, res);\n break;\n\n case \"/api/upload\":\n handleUploadRoute(res);\n break;\n\n case \"/api/upload-files\":\n if (method === \"POST\") handleFileUploadRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/field\":\n handleFieldRoute(req, res);\n break;\n\n case \"/api/import\":\n handleImportRoute(req, res);\n break;\n\n case \"/api/setup\":\n handleSetupInfoRoute(res);\n break;\n\n case \"/api/setup/create\":\n handleSetupCreateRoute(req, res);\n break;\n\n case \"/api/setup/fetch\":\n handleSetupFetchRoute(req, res);\n break;\n\n case \"/api/setup/open\":\n handleSetupOpenRoute(req, res);\n break;\n\n case \"/api/setup/resume\":\n handleSetupResumeRoute(req, res);\n break;\n\n case \"/api/setup/apikey\":\n handleSetupApiKeyRoute(req, res);\n break;\n\n case \"/api/setup/remote-themes\":\n if (method === \"GET\") handleSetupRemoteThemesRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n // Settings routes\n case \"/api/settings/status\":\n if (method === \"GET\") handleSettingsStatusRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/engine\":\n if (method === \"POST\") handleSettingsEngineRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/apikey\":\n if (method === \"POST\") handleSettingsApiKeyRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/install\":\n if (method === \"POST\") handleSettingsInstallRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-auth\":\n if (method === \"POST\") handleSettingsHsAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/gh-auth\":\n if (method === \"POST\") handleSettingsGhAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-switch\":\n if (method === \"POST\") handleSettingsHsSwitchRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/gh-logout\":\n if (method === \"POST\") handleSettingsGhLogoutRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/cli-auth\":\n if (method === \"POST\") handleSettingsCLIAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-mode\":\n if (method === \"POST\") handleSettingsHsModeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/cli-toggle\":\n if (method === \"POST\") handleSettingsCliToggleRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/save\":\n if (method === \"POST\") handleClaudeOAuthSaveRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/status\":\n if (method === \"GET\") handleClaudeOAuthStatusRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/logout\":\n if (method === \"POST\") handleClaudeOAuthLogoutRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings\":\n if (method === \"POST\") handleSettingsGenericRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/changelog\":\n if (method === \"GET\") {\n jsonResponse(res, 200, { changelog: getChangelog() });\n } else {\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n }\n break;\n\n case \"/api/themes\":\n handleThemesRoute(method, req, res);\n break;\n\n case \"/api/themes/switch\":\n if (method === \"POST\") handleThemeSwitchRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/themes/delete-local\":\n if (method === \"POST\") handleDeleteLocalThemeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/themes/rename\":\n if (method === \"POST\") handleRenameThemeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/history\":\n if (method === \"GET\") handleHistoryRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/rollback\":\n if (method === \"POST\") handleRollbackRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n // Dashboard & template routes\n case \"/api/dashboard\":\n if (method === \"GET\") handleDashboardRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates\":\n handleTemplatesRoute(method, req, res);\n break;\n\n case \"/api/templates/activate\":\n if (method === \"POST\") handleTemplateActivateRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates/rename\":\n if (method === \"POST\") handleTemplateRenameRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates/clone\":\n if (method === \"POST\") handleTemplateCloneRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/module-library\":\n if (method === \"GET\") handleModuleLibraryRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/brand-assets\":\n handleBrandAssetsRoute(method, req, res);\n break;\n\n case \"/api/brand-assets/extract\":\n if (method === \"POST\") handleDesignExtractRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/brand-assets/import-reference\":\n if (method === \"POST\") handleReferenceImportRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/download-zip\":\n if (method === \"GET\") handleDownloadZipRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n default:\n // Prefix match for job polling: /api/settings/job/:id\n if (path.startsWith(\"/api/settings/job/\") && method === \"GET\") {\n handleSettingsJobRoute(path, res);\n }\n // Prefix match for template add-module: /api/templates/:id/add-module\n else if (path.match(/^\\/api\\/templates\\/[^/]+\\/add-module$/) && method === \"POST\") {\n handleAddModuleToTemplateRoute(path, req, res);\n } else {\n jsonResponse(res, 404, { error: \"Not found\" });\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// WebSocket handler\n// ---------------------------------------------------------------------------\n\nfunction handleWsConnection(ws: WebSocket): void {\n ws.on(\"message\", async (data) => {\n let msg: { type: string; [key: string]: unknown };\n try {\n msg = JSON.parse(data.toString());\n } catch {\n ws.send(JSON.stringify({ type: \"error\", message: \"Invalid JSON\" }));\n return;\n }\n\n switch (msg.type) {\n case \"chat\": {\n const userMessage = String(msg.message || \"\");\n if (!userMessage.trim()) return;\n\n addMessage(\"user\", userMessage);\n saveSession();\n\n const fileIds = Array.isArray(msg.fileIds) ? msg.fileIds as string[] : undefined;\n\n // Check if agentic mode should be used\n const agenticCheck = shouldUseAgenticMode();\n\n // Notify frontend if agentic mode needs first-run prompt\n if (agenticCheck.needsPrompt) {\n ws.send(JSON.stringify({ type: \"agentic_prompt\" }));\n // Don't block — fall through to single-call mode for now.\n // User can choose agentic mode from the prompt, which saves to config.\n }\n\n try {\n if (agenticCheck.useAgentic) {\n // --- Agentic pipeline mode ---\n // Collect pipeline events for metadata persistence\n const pipelineSteps: { step: string; label: string; decisions?: string[] }[] = [];\n const pipelineModules: { name: string; status: \"complete\" | \"failed\" }[] = [];\n\n const result = await handleAgenticGenerate(\n userMessage,\n (event) => {\n // Don't send moduleFiles over WebSocket (too large)\n if (event.type === \"module_progress\" && event.moduleFiles) {\n const { moduleFiles, ...wsEvent } = event;\n ws.send(JSON.stringify(wsEvent));\n } else {\n ws.send(JSON.stringify(event));\n }\n\n // Collect events for metadata + incremental preview\n if (event.type === \"agent_step\") {\n pipelineSteps.push({ step: event.step, label: event.label });\n } else if (event.type === \"agent_decision\") {\n const last = pipelineSteps[pipelineSteps.length - 1];\n if (last) {\n if (!last.decisions) last.decisions = [];\n last.decisions.push(event.decision);\n }\n } else if (event.type === \"design_system_ready\") {\n // Design system created — push CSS to session for themed placeholders\n updateModules({ sharedCss: event.sharedCss, sharedJs: event.sharedJs });\n } else if (event.type === \"blueprint_ready\") {\n // Module plan ready — set order for incremental preview placeholders\n updateModules({ sharedCss: event.sharedCss, sharedJs: event.sharedJs });\n reorderModules(event.moduleOrder);\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n } else if (event.type === \"module_progress\" && event.status === \"complete\" && event.moduleFiles) {\n // Push completed module to session immediately for incremental preview\n updateModules({ modules: [{\n moduleName: event.module,\n fieldsJson: event.moduleFiles.fieldsJson,\n metaJson: event.moduleFiles.metaJson,\n moduleHtml: event.moduleFiles.moduleHtml,\n moduleCss: event.moduleFiles.moduleCss,\n moduleJs: event.moduleFiles.moduleJs,\n }] });\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n pipelineModules.push({ name: event.module, status: \"complete\" });\n } else if (event.type === \"module_progress\" && event.status === \"failed\") {\n pipelineModules.push({ name: event.module, status: \"failed\" });\n }\n },\n fileIds,\n );\n\n // Apply result to session with pipeline metadata\n applyPipelineResult(result, {\n steps: pipelineSteps,\n modules: pipelineModules,\n stats: result.stats,\n });\n\n } else {\n // --- Single-call mode (existing behavior) ---\n setParseWarningCallback((warning) => {\n ws.send(JSON.stringify({ type: \"parse_warning\", message: warning }));\n });\n\n await handleGenerateStream(\n userMessage,\n (chunk) => {\n ws.send(JSON.stringify({ type: \"stream\", content: chunk }));\n },\n (status) => {\n ws.send(JSON.stringify({ type: \"stream_status\", content: status }));\n },\n fileIds\n );\n }\n\n // Write modules to disk and commit for version history\n const currentSession = getSession();\n if (currentSession) {\n writeModulesToDisk();\n const activeTpl = getActiveTemplate();\n let commitHash: string | null = null;\n if (activeTpl) {\n const filePaths = activeTpl.moduleOrder.map((n: string) => `modules/${n}.module`);\n if (activeTpl.templateFile) filePaths.push(activeTpl.templateFile);\n if (activeTpl.sharedCss) filePaths.push(`css/${currentSession.themeName}-theme.css`);\n if (activeTpl.sharedJs) filePaths.push(`js/${currentSession.themeName}-animations.js`);\n commitHash = commitTemplateState(currentSession.themePath, activeTpl.id, userMessage, filePaths);\n } else {\n commitHash = commitThemeState(currentSession.themePath, userMessage);\n }\n if (commitHash) {\n ws.send(JSON.stringify({ type: \"version_created\", hash: commitHash }));\n }\n }\n\n // After generation, send updated preview\n ws.send(JSON.stringify({ type: \"generation_complete\" }));\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n\n // Suggest brand asset extraction if none exist yet\n {\n const sess = getSession();\n if (sess && agenticCheck.useAgentic && !sess.brandAssets?.styleguide && !sess.brandAssets?.brandvoice && !sess.brandAssets?.themeContext) {\n ws.send(JSON.stringify({ type: \"suggest_brand_extraction\" }));\n }\n }\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n break;\n }\n\n case \"extract_brand_assets\": {\n const session = getSession();\n if (!session) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No active session\" }));\n break;\n }\n\n // Fire-and-forget — run extraction in background, never block the UI\n (async () => {\n try {\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n\n // Extract theme context from rendered preview HTML\n const { buildPreviewHtml } = await import(\"./preview.js\");\n const previewHtml = buildPreviewHtml();\n if (!previewHtml || previewHtml.length < 50) return;\n\n const { extractThemeContext } = await import(\"./agent/stages/context-extractor.js\");\n const themeContext = await extractThemeContext(\n previewHtml,\n session.brandAssets?.themeContext,\n engine,\n apiKey,\n model,\n );\n\n const { mkdirSync, writeFileSync } = await import(\"node:fs\");\n\n if (themeContext) {\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.themeContext = themeContext;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n if (!existsSync(assetDir)) mkdirSync(assetDir, { recursive: true });\n writeFileSync(join(assetDir, \"theme-context.md\"), themeContext);\n\n saveSession();\n ws.send(JSON.stringify({ type: \"brand_asset_extracted\", assetType: \"themeContext\" }));\n }\n\n // Also extract styleguide if missing\n if (!session.brandAssets?.styleguide) {\n try {\n const { extractDesignContext } = await import(\"../ai/design-extractor.js\");\n const styleguide = await extractDesignContext(session.themePath);\n if (styleguide) {\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.styleguide = styleguide;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n if (!existsSync(assetDir)) mkdirSync(assetDir, { recursive: true });\n writeFileSync(join(assetDir, \"styleguide.md\"), styleguide);\n\n saveSession();\n ws.send(JSON.stringify({ type: \"brand_asset_extracted\", assetType: \"styleguide\" }));\n }\n } catch { /* non-critical */ }\n }\n\n ws.send(JSON.stringify({ type: \"brand_extraction_complete\" }));\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"brand_extraction_error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n })();\n break;\n }\n\n case \"start_upload\": {\n const session = getSession();\n if (!session) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No active session\" }));\n break;\n }\n\n try {\n writeModulesToDisk();\n\n // Apply auto-fixes before uploading\n const fixes = applyAutoFixes(session.themePath);\n if (fixes.length > 0) {\n ws.send(JSON.stringify({ type: \"upload_status\", phase: \"autofix\", fixes }));\n }\n\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (uploadMode === \"api\") {\n // --- API mode: direct HTTP uploads ---\n const pak = getHubSpotPak();\n if (!pak) {\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: \"No HubSpot account configured. Open Settings → HubSpot to add one.\",\n errors: [{ file: \"\", message: \"No HubSpot account configured\", fixable: false }],\n }));\n break;\n }\n\n ws.send(JSON.stringify({ type: \"upload_started\", jobId: \"api-upload\" }));\n\n const result = await uploadTheme(pak, session.themePath, session.themeName, {\n onFileStart: (path) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: `Uploading ${path}\\n` }));\n },\n onFileComplete: (path) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: ` ✓ ${path}\\n` }));\n },\n onFileError: (path, err) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: ` ✗ ${path}: ${err.message}\\n` }));\n },\n onProgress: (completed, total) => {\n ws.send(JSON.stringify({ type: \"upload_progress\", completed, total }));\n },\n });\n\n if (result.success) {\n const acct = getActiveHubSpotAccount();\n ws.send(JSON.stringify({\n type: \"upload_complete\",\n output: `Uploaded ${result.uploaded} files`,\n portalId: acct?.portalId || \"\",\n dataCenter: acct?.dataCenter || \"na1\",\n themeName: session.themeName,\n }));\n } else {\n const errors = parseApiErrors(result.errors);\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: result.errors.map((e) => `${e.file}: ${e.message}`).join(\"\\n\"),\n errors,\n }));\n }\n } else {\n // --- CLI mode: legacy hs cms upload subprocess ---\n const jobId = startStreamingJob(\n `hs cms upload \"${session.themePath}\" \"${session.themeName}\"`,\n \"Uploading to HubSpot\",\n { cwd: join(session.themePath, \"..\"), timeout: 180_000 }\n );\n\n ws.send(JSON.stringify({ type: \"upload_started\", jobId }));\n\n const chunkListener = (chunk: string) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk }));\n };\n addJobListener(jobId, chunkListener);\n\n const pollInterval = setInterval(() => {\n const job = getJob(jobId);\n if (!job || job.status === \"running\") return;\n\n clearInterval(pollInterval);\n removeJobListener(jobId, chunkListener);\n\n if (job.status === \"completed\") {\n const auth = detectHubSpotAuth();\n const dc = auth.portalId ? detectDataCenter(auth.portalId) : \"na1\";\n ws.send(JSON.stringify({\n type: \"upload_complete\",\n output: job.output,\n portalId: auth.portalId || \"\",\n dataCenter: dc,\n themeName: session.themeName,\n }));\n } else {\n const errors = parseUploadErrors(job.output);\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: job.output,\n errors,\n exitCode: job.exitCode,\n }));\n }\n }, 500);\n }\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n break;\n }\n\n case \"upload_fix_with_ai\": {\n const errorContext = String(msg.errorContext || \"\");\n if (!errorContext.trim()) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No error context provided\" }));\n break;\n }\n\n const fixPrompt = `The HubSpot upload (\"hs cms upload\") failed. Below is the upload log output containing the errors.\n\nIMPORTANT: Be verbose in your response. For each error:\n1. State exactly which file has the problem and what the error is\n2. Explain WHY this error occurs (e.g. \"HubSpot doesn't support textarea field type\" or \"field name 'name' is reserved in HubSpot modules\")\n3. Describe the specific fix you're applying (e.g. \"Changing field type from textarea to text\" or \"Renaming field from 'name' to 'item_name'\")\n4. Apply the fix to the module files\n\nCRITICAL: After fixing the reported errors, scan ALL other module files in the theme for the same issues. For example, if you fix \"name\" → \"item_name\" in one module, check every other module's fields.json for the same problem. Fix all occurrences, not just the ones in the error log.\n\nAfter fixing all errors, summarize the changes you made.\n\nUpload log:\n${errorContext}`;\n addMessage(\"user\", fixPrompt);\n saveSession();\n\n ws.send(JSON.stringify({ type: \"upload_fix_started\" }));\n\n try {\n await handleGenerateStream(fixPrompt, (chunk) => {\n // Stream to both the chat panel and the upload panel\n ws.send(JSON.stringify({ type: \"stream\", content: chunk }));\n ws.send(JSON.stringify({ type: \"upload_fix_stream\", content: chunk }));\n });\n\n // Write fixes to disk and commit\n const fixSession = getSession();\n if (fixSession) {\n writeModulesToDisk();\n const fixHash = commitThemeState(fixSession.themePath, \"AI fix: upload errors\");\n if (fixHash) {\n ws.send(JSON.stringify({ type: \"version_created\", hash: fixHash }));\n }\n }\n\n ws.send(JSON.stringify({ type: \"upload_fix_complete\" }));\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: err instanceof Error ? err.message : String(err),\n errors: [{ file: \"AI fix\", message: err instanceof Error ? err.message : String(err), fixable: false }],\n }));\n }\n break;\n }\n\n case \"ping\":\n ws.send(JSON.stringify({ type: \"pong\" }));\n break;\n\n default:\n ws.send(JSON.stringify({ type: \"error\", message: `Unknown type: ${msg.type}` }));\n }\n });\n\n // Send initial state\n const session = getSession();\n if (session) {\n const cfg = loadConfig();\n const engineLabels: Record<string, string> = {\n \"claude-code\": \"Claude Code\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"gemini-api\": \"Gemini API\",\n \"codex-cli\": \"Codex CLI\",\n \"api\": \"Anthropic API\",\n };\n const activeTpl = getActiveTemplate();\n ws.send(JSON.stringify({\n type: \"init\",\n sessionId: session.id,\n themeName: session.themeName,\n modules: getOrderedModules().map((m) => m.moduleName),\n messageCount: session.messages.length,\n messages: session.messages,\n gitAvailable: isGitAvailable(),\n engine: cfg.aiEngine ? engineLabels[cfg.aiEngine] || cfg.aiEngine : \"\",\n // Multi-template context\n templateId: activeTpl?.id || null,\n pageType: activeTpl?.pageType || null,\n templates: (session.templates || []).map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n })),\n }));\n } else {\n ws.send(JSON.stringify({ type: \"needs_setup\" }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Theme asset serving (uploaded images for preview)\n// ---------------------------------------------------------------------------\n\nfunction serveThemeAsset(filename: string, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"No session\");\n return;\n }\n const filePath = join(session.themePath, \"assets\", filename);\n if (!existsSync(filePath)) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"Asset not found\");\n return;\n }\n const ext = extname(filePath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n const buffer = readFileSync(filePath);\n res.writeHead(200, {\n \"Content-Type\": contentType,\n \"Cache-Control\": \"no-cache\",\n });\n res.end(buffer);\n}\n\n// ---------------------------------------------------------------------------\n// Static file serving\n// ---------------------------------------------------------------------------\n\nconst staticCache = new Map<string, { buffer: Buffer; etag: string; contentType: string }>();\n\nfunction serveStatic(pathname: string, uiDir: string, req: IncomingMessage, res: ServerResponse): void {\n // Default to index.html\n let filePath = pathname === \"/\" ? \"/index.html\" : pathname;\n const fullPath = join(uiDir, filePath);\n\n if (!existsSync(fullPath)) {\n // SPA fallback — serve index.html for unknown routes\n const indexPath = join(uiDir, \"index.html\");\n if (existsSync(indexPath)) {\n const content = readFileSync(indexPath);\n res.writeHead(200, { \"Content-Type\": \"text/html\", \"Cache-Control\": \"no-cache\" });\n res.end(content);\n } else {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"Not found\");\n }\n return;\n }\n\n const ext = extname(fullPath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n const isHtml = ext === \".html\";\n\n try {\n // Always re-read from disk to pick up changes during development\n const buffer = readFileSync(fullPath);\n const etag = '\"' + createHash(\"md5\").update(buffer).digest(\"hex\").slice(0, 16) + '\"';\n\n // Check If-None-Match for 304\n const clientEtag = req.headers[\"if-none-match\"];\n if (clientEtag === etag) {\n res.writeHead(304);\n res.end();\n return;\n }\n\n res.writeHead(200, {\n \"Content-Type\": contentType,\n \"Cache-Control\": \"no-cache\",\n \"ETag\": etag,\n });\n res.end(buffer);\n } catch {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal Server Error\");\n }\n}\n","/**\n * Background process manager for long-running CLI operations\n * (tool installation, OAuth flows, etc.)\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\n\nexport interface ProcessJob {\n id: string;\n command: string;\n description: string;\n status: \"running\" | \"completed\" | \"failed\";\n output: string;\n exitCode: number | null;\n startedAt: number;\n completedAt: number | null;\n}\n\nconst jobs = new Map<string, ProcessJob>();\n\nfunction _attachJobHandlers(child: ChildProcess, job: ProcessJob, timeout?: number): void {\n child.stdout?.on(\"data\", (d: Buffer) => {\n job.output += d.toString();\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n job.output += d.toString();\n });\n\n child.on(\"close\", (code) => {\n job.status = code === 0 ? \"completed\" : \"failed\";\n job.exitCode = code;\n job.completedAt = Date.now();\n });\n\n child.on(\"error\", (err) => {\n job.status = \"failed\";\n job.output += `\\nProcess error: ${err.message}`;\n job.completedAt = Date.now();\n });\n\n const t = timeout || 300_000;\n setTimeout(() => {\n if (job.status === \"running\") {\n child.kill();\n job.status = \"failed\";\n job.output += \"\\nProcess timed out\";\n job.completedAt = Date.now();\n }\n }, t);\n}\n\n/**\n * Start a job safely using an argument array (no shell interpolation).\n * Preferred over the string overload to prevent command injection.\n */\nexport function startJobSafe(\n cmd: string,\n args: string[],\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number; stdin?: string }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: ProcessJob = {\n id,\n command: `${cmd} ${args.join(\" \")}`,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n };\n\n jobs.set(id, job);\n\n const child: ChildProcess = spawn(cmd, args, {\n cwd: opts?.cwd,\n stdio: [opts?.stdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n // Windows needs shell to resolve .cmd/.bat from PATH; args array prevents injection\n shell: process.platform === \"win32\",\n });\n\n if (opts?.stdin && child.stdin) {\n child.stdin.write(opts.stdin);\n child.stdin.end();\n }\n\n _attachJobHandlers(child, job, opts?.timeout);\n return id;\n}\n\nexport function startJob(\n command: string,\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: ProcessJob = {\n id,\n command,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n };\n\n jobs.set(id, job);\n\n const parts = command.split(\" \");\n const child: ChildProcess = spawn(parts[0], parts.slice(1), {\n cwd: opts?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n shell: true,\n });\n\n _attachJobHandlers(child, job, opts?.timeout);\n return id;\n}\n\nexport function getJob(id: string): ProcessJob | undefined {\n return jobs.get(id);\n}\n\nexport function cleanupOldJobs(): void {\n const cutoff = Date.now() - 30 * 60 * 1000;\n for (const [id, job] of jobs) {\n if (job.completedAt && job.completedAt < cutoff) {\n jobs.delete(id);\n }\n }\n}\n\n// Clean up periodically\nsetInterval(cleanupOldJobs, 10 * 60 * 1000);\n\n// ---------------------------------------------------------------------------\n// Streaming jobs — same as regular jobs but also emit output chunks to listeners\n// ---------------------------------------------------------------------------\n\nexport interface StreamingJob extends ProcessJob {\n listeners: Set<(chunk: string) => void>;\n}\n\nexport function startStreamingJob(\n command: string,\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: StreamingJob = {\n id,\n command,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n listeners: new Set(),\n };\n\n jobs.set(id, job);\n\n const parts = command.split(\" \");\n const child: ChildProcess = spawn(parts[0], parts.slice(1), {\n cwd: opts?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n shell: true,\n });\n\n const emitChunk = (chunk: string) => {\n for (const listener of job.listeners) {\n try { listener(chunk); } catch { /* listener error — ignore */ }\n }\n };\n\n child.stdout?.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n job.output += chunk;\n emitChunk(chunk);\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n job.output += chunk;\n emitChunk(chunk);\n });\n\n child.on(\"close\", (code) => {\n job.status = code === 0 ? \"completed\" : \"failed\";\n job.exitCode = code;\n job.completedAt = Date.now();\n job.listeners.clear();\n });\n\n child.on(\"error\", (err) => {\n job.status = \"failed\";\n job.output += `\\nProcess error: ${err.message}`;\n job.completedAt = Date.now();\n job.listeners.clear();\n });\n\n // Timeout safety net\n const timeout = opts?.timeout || 300_000;\n setTimeout(() => {\n if (job.status === \"running\") {\n child.kill();\n job.status = \"failed\";\n job.output += \"\\nProcess timed out\";\n job.completedAt = Date.now();\n job.listeners.clear();\n }\n }, timeout);\n\n return id;\n}\n\nexport function addJobListener(jobId: string, listener: (chunk: string) => void): void {\n const job = jobs.get(jobId);\n if (!job || !(\"listeners\" in job)) return;\n\n const streamingJob = job as StreamingJob;\n\n // Send buffered output first\n if (streamingJob.output) {\n try { listener(streamingJob.output); } catch { /* ignore */ }\n }\n\n streamingJob.listeners.add(listener);\n}\n\nexport function removeJobListener(jobId: string, listener: (chunk: string) => void): void {\n const job = jobs.get(jobId);\n if (!job || !(\"listeners\" in job)) return;\n\n (job as StreamingJob).listeners.delete(listener);\n}\n","/**\n * Setup routes — onboarding flow in the browser.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readdirSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execFileSync, type ExecFileSyncOptions } from \"node:child_process\";\n\n// Windows needs shell to resolve .cmd/.bat from PATH; args array prevents injection\nconst _shellOpt: ExecFileSyncOptions = process.platform === \"win32\" ? { shell: true } : {};\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { loadConfig, getHubSpotPak } from \"../../utils/config.js\";\nimport { createThemeScaffold } from \"../../hubspot/theme-scaffold.js\";\nimport { fetchTheme } from \"../../hubspot/fetcher.js\";\nimport { getMetadata, listRootFolders } from \"../../hubspot/api.js\";\nimport {\n getSession,\n createSession,\n scanThemeFromDisk,\n saveSession,\n loadSession,\n listSessions,\n} from \"../session.js\";\nimport { isGenerating } from \"../ai-handler.js\";\nimport { detectEnvironment } from \"../../utils/detect.js\";\nimport { saveConfig } from \"../../utils/config.js\";\nimport { ensureDir } from \"../../utils/fs.js\";\n\nexport const WORKSPACE_DIR = join(homedir(), \"vibespot-themes\");\n\nlet _themeListCache: { data: Array<{ name: string; moduleCount: number }>; ts: number } | null = null;\nconst THEME_LIST_TTL = 5000;\n\nexport function getLocalThemes(): Array<{ name: string; moduleCount: number }> {\n if (_themeListCache && Date.now() - _themeListCache.ts < THEME_LIST_TTL) return _themeListCache.data;\n const themes: Array<{ name: string; moduleCount: number }> = [];\n if (existsSync(WORKSPACE_DIR)) {\n try {\n for (const entry of readdirSync(WORKSPACE_DIR, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const themeJson = join(WORKSPACE_DIR, entry.name, \"theme.json\");\n if (existsSync(themeJson)) {\n let moduleCount = 0;\n const modulesDir = join(WORKSPACE_DIR, entry.name, \"modules\");\n if (existsSync(modulesDir)) {\n try {\n moduleCount = readdirSync(modulesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory()).length;\n } catch { /* ignore */ }\n }\n themes.push({ name: entry.name, moduleCount });\n }\n }\n }\n } catch { /* ignore */ }\n }\n _themeListCache = { data: themes, ts: Date.now() };\n return themes;\n}\n\nexport function handleSetupInfoRoute(res: ServerResponse): void {\n const session = getSession();\n const env = detectEnvironment();\n\n let hsInstalled = false;\n try {\n execFileSync(\"hs\", [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", ..._shellOpt });\n hsInstalled = true;\n } catch { /* not installed */ }\n\n const sessions = listSessions()\n .sort((a, b) => b.updatedAt - a.updatedAt)\n .slice(0, 10);\n\n const localThemes = getLocalThemes();\n\n jsonResponse(res, 200, {\n hasActiveSession: !!session,\n activeSession: session ? {\n id: session.id,\n themeName: session.themeName,\n moduleCount: session.modules.length,\n } : null,\n hsInstalled,\n aiAvailable: env.availableEngines.length > 0,\n availableEngines: env.availableEngines,\n activeEngine: env.activeEngine,\n sessions,\n localThemes,\n });\n}\n\nexport function handleSetupCreateRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { name } = JSON.parse(body);\n if (!name || typeof name !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n const themeName = name\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const themePath = join(WORKSPACE_DIR, themeName);\n ensureDir(WORKSPACE_DIR);\n\n if (existsSync(themePath)) {\n rmSync(themePath, { recursive: true, force: true });\n }\n\n // Create theme scaffold locally (no CLI dependency)\n createThemeScaffold(themePath, themeName);\n\n createSession(themePath, themeName);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName,\n themePath,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupFetchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { name: rawName } = JSON.parse(body);\n if (!rawName || typeof rawName !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n // Strip leading/trailing slashes (HubSpot DM \"Copy path\" gives \"/theme-name\")\n const name = rawName.replace(/^\\/+|\\/+$/g, \"\");\n if (!name) {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n const pak = getHubSpotPak();\n const config = loadConfig();\n\n // Sanitize for local directory name (marketplace paths like @marketplace/Theme → _marketplace_Theme)\n const safeDirName = name.includes(\"/\") || name.includes(\"@\")\n ? name.replace(/[@/]/g, \"_\").replace(/_+/g, \"_\").replace(/^_|_$/g, \"\")\n : name;\n const themePath = join(WORKSPACE_DIR, safeDirName);\n ensureDir(WORKSPACE_DIR);\n\n if (config.hubspotUploadMode === \"cli\" || !pak) {\n // CLI fallback\n execFileSync(\"hs\", [\"cms\", \"fetch\", name, themePath], {\n encoding: \"utf-8\",\n stdio: \"pipe\",\n ..._shellOpt,\n });\n\n createSession(themePath, safeDirName);\n scanThemeFromDisk(themePath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: safeDirName,\n themePath,\n moduleCount: getSession()?.modules.length || 0,\n });\n } else {\n // API mode (default) — use original name for API, safe name for local\n fetchTheme(pak, name, themePath)\n .then(() => {\n createSession(themePath, safeDirName);\n scanThemeFromDisk(themePath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: safeDirName,\n themePath,\n moduleCount: getSession()?.modules.length || 0,\n });\n })\n .catch((err) => {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n } catch (err) {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n}\n\nexport function handleSetupOpenRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { path: themePath } = JSON.parse(body);\n if (!themePath || typeof themePath !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme path is required\" });\n return;\n }\n\n let fullPath = themePath;\n if (!existsSync(fullPath)) {\n fullPath = join(WORKSPACE_DIR, themePath);\n }\n if (!existsSync(fullPath)) {\n jsonResponse(res, 400, { error: `Theme folder not found: ${themePath}` });\n return;\n }\n\n const themeName = basename(fullPath);\n createSession(fullPath, themeName);\n scanThemeFromDisk(fullPath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName,\n themePath: fullPath,\n moduleCount: getSession()?.modules.length || 0,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupResumeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { sessionId } = JSON.parse(body);\n if (!sessionId || typeof sessionId !== \"string\") {\n jsonResponse(res, 400, { error: \"Session ID is required\" });\n return;\n }\n\n const session = loadSession(sessionId);\n if (!session) {\n jsonResponse(res, 404, { error: \"Session not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: session.themeName,\n themePath: session.themePath,\n moduleCount: session.modules.length,\n messageCount: session.messages.length,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupApiKeyRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { apiKey } = JSON.parse(body);\n if (!apiKey || typeof apiKey !== \"string\") {\n jsonResponse(res, 400, { error: \"API key is required\" });\n return;\n }\n\n saveConfig({ anthropicApiKey: apiKey });\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n/**\n * List themes available on HubSpot Design Manager.\n * Returns folders at the root level that look like themes (contain theme.json).\n */\nexport function handleSetupRemoteThemesRoute(res: ServerResponse): void {\n const pak = getHubSpotPak();\n if (!pak) {\n jsonResponse(res, 200, { themes: [], error: \"No HubSpot account connected\" });\n return;\n }\n\n (async () => {\n const folders = await listRootFolders(pak);\n\n if (folders.length === 0) {\n jsonResponse(res, 200, { themes: [] });\n return;\n }\n\n const themes: Array<{ name: string; path: string }> = [];\n\n // Check which folders have a theme.json (in parallel)\n const checks = folders.map(async (folder) => {\n const folderPath = folder.path || folder.name;\n try {\n const tjMeta = await getMetadata(pak, `${folderPath}/theme.json`);\n if (tjMeta && !tjMeta.folder) {\n themes.push({ name: folder.name, path: folderPath });\n }\n } catch { /* not a theme */ }\n });\n\n await Promise.all(checks);\n themes.sort((a, b) => a.name.localeCompare(b.name));\n\n const localThemes = getLocalThemes();\n const localNames = new Set(localThemes.map((t) => t.name));\n\n jsonResponse(res, 200, {\n themes: themes.map((t) => ({\n ...t,\n existsLocally: localNames.has(t.name),\n })),\n });\n })().catch((err) => {\n jsonResponse(res, 200, {\n themes: [],\n error: err instanceof Error ? err.message : String(err),\n });\n });\n}\n","/**\n * Settings routes — environment management, API keys, tool install, auth.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readFileSync, appendFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { loadConfig, saveConfig, getApiKeyForEngine, addHubSpotAccount, removeHubSpotAccount, setActiveHubSpotAccount, setCliToolEnabled, type AIEngineType, type HubSpotAccountConfig } from \"../../utils/config.js\";\nimport { listSessions } from \"../session.js\";\nimport { getLocalThemes } from \"./setup.js\";\nimport { detectEnvironment, detectHubSpotCLI, detectHubSpotAuth, detectGitHubCLI, detectGitHubAuth } from \"../../utils/detect.js\";\nimport { validatePak } from \"../../hubspot/api.js\";\nimport { getVersion } from \"../../utils/fs.js\";\nimport { startJob, startJobSafe, getJob } from \"../process-manager.js\";\n\n// ---------------------------------------------------------------------------\n// Live model catalog — fetched from provider APIs, cached 10 minutes\n// ---------------------------------------------------------------------------\n\ntype ModelEntry = { id: string; label: string };\nconst modelCache: { data: Record<string, ModelEntry[]>; ts: number } = { data: {}, ts: 0 };\nconst MODEL_CACHE_TTL = 10 * 60 * 1000;\n\nconst STATIC_MODELS: Record<string, ModelEntry[]> = {\n \"claude-code\": [\n { id: \"sonnet\", label: \"Claude Sonnet (default)\" },\n { id: \"opus\", label: \"Claude Opus\" },\n { id: \"haiku\", label: \"Claude Haiku\" },\n ],\n \"codex-cli\": [\n { id: \"o4-mini\", label: \"o4 Mini (default)\" },\n { id: \"o3\", label: \"o3\" },\n { id: \"gpt-4o\", label: \"GPT-4o\" },\n ],\n};\n\nasync function fetchAnthropicModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\"https://api.anthropic.com/v1/models\", {\n headers: { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" },\n });\n if (!resp.ok) return [];\n const data = await resp.json() as { data: { id: string; display_name: string }[] };\n return data.data\n .filter((m) => !m.id.startsWith(\"claude-3-\") && !m.id.startsWith(\"claude-2\"))\n .map((m) => ({ id: m.id, label: m.display_name }));\n}\n\nasync function fetchOpenAIModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (!resp.ok) return [];\n const data = await resp.json() as { data: { id: string }[] };\n const keep = /^(gpt-4o|gpt-4o-mini|o[1-4](-mini)?|o[1-4]-pro)$/;\n return data.data\n .filter((m) => keep.test(m.id))\n .sort((a, b) => a.id.localeCompare(b.id))\n .map((m) => ({ id: m.id, label: m.id }));\n}\n\nasync function fetchGeminiModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,\n );\n if (!resp.ok) return [];\n const data = await resp.json() as { models: { name: string; displayName: string }[] };\n return data.models\n .filter((m) => m.name.includes(\"gemini-2\"))\n .map((m) => ({ id: m.name.replace(\"models/\", \"\"), label: m.displayName }));\n}\n\nasync function getModelCatalog(): Promise<Record<string, ModelEntry[]>> {\n if (Date.now() - modelCache.ts < MODEL_CACHE_TTL && Object.keys(modelCache.data).length > 0) {\n return modelCache.data;\n }\n\n const config = loadConfig();\n const catalog: Record<string, ModelEntry[]> = { ...STATIC_MODELS };\n\n const jobs: Promise<void>[] = [];\n\n const anthropicKey = getApiKeyForEngine(\"anthropic-api\", config);\n if (anthropicKey) {\n jobs.push(\n fetchAnthropicModels(anthropicKey)\n .then((models) => {\n if (models.length) {\n catalog[\"anthropic-api\"] = models;\n catalog[\"claude-oauth\"] = models; // same model list\n }\n })\n .catch(() => {}),\n );\n }\n\n const openaiKey = getApiKeyForEngine(\"openai-api\", config);\n if (openaiKey) {\n jobs.push(\n fetchOpenAIModels(openaiKey)\n .then((models) => { if (models.length) catalog[\"openai-api\"] = models; })\n .catch(() => {}),\n );\n }\n\n const geminiKey = getApiKeyForEngine(\"gemini-api\", config);\n if (geminiKey) {\n jobs.push(\n fetchGeminiModels(geminiKey)\n .then((models) => {\n if (models.length) {\n catalog[\"gemini-api\"] = models;\n catalog[\"gemini-cli\"] = models;\n }\n })\n .catch(() => {}),\n );\n }\n\n await Promise.all(jobs);\n\n modelCache.data = catalog;\n modelCache.ts = Date.now();\n return catalog;\n}\n\nexport function handleSettingsStatusRoute(res: ServerResponse): void {\n const env = detectEnvironment();\n const config = loadConfig();\n\n const configPayload = {\n aiEngine: config.aiEngine || null,\n claudeCodeModel: config.claudeCodeModel || null,\n anthropicApiModel: config.anthropicApiModel || null,\n openaiApiModel: config.openaiApiModel || null,\n hubspotUploadMode: config.hubspotUploadMode || \"api\",\n hubspotAccounts: (config.hubspotAccounts || []).map((a: HubSpotAccountConfig) => ({\n portalId: a.portalId,\n portalName: a.portalName,\n dataCenter: a.dataCenter,\n })),\n activeHubSpotAccount: config.activeHubSpotAccount || null,\n enabledCLITools: config.enabledCLITools || [],\n agenticMode: config.agenticMode,\n agenticConcurrency: config.agenticConcurrency,\n };\n\n const sessionCount = listSessions().length;\n const localThemeCount = getLocalThemes().length;\n\n const version = getVersion();\n\n getModelCatalog().then((models) => {\n jsonResponse(res, 200, {\n version,\n environment: env,\n config: configPayload,\n models,\n sessionCount,\n localThemeCount,\n });\n }).catch(() => {\n jsonResponse(res, 200, {\n version,\n environment: env,\n config: configPayload,\n models: STATIC_MODELS,\n sessionCount,\n localThemeCount,\n });\n });\n}\n\nexport function handleSettingsEngineRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { engine, model } = JSON.parse(body);\n\n const validEngines: AIEngineType[] = [\n \"claude-code\", \"anthropic-api\", \"claude-oauth\", \"openai-api\", \"gemini-cli\", \"gemini-api\", \"codex-cli\",\n ];\n if (!validEngines.includes(engine)) {\n jsonResponse(res, 400, { error: `Invalid engine: ${engine}` });\n return;\n }\n\n const configUpdate: Record<string, unknown> = { aiEngine: engine };\n if (model) {\n switch (engine) {\n case \"claude-code\":\n configUpdate.claudeCodeModel = model;\n break;\n case \"anthropic-api\":\n case \"claude-oauth\":\n configUpdate.anthropicApiModel = model;\n break;\n case \"openai-api\":\n configUpdate.openaiApiModel = model;\n break;\n }\n }\n\n saveConfig(configUpdate as any);\n jsonResponse(res, 200, { ok: true, engine });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsApiKeyRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { provider, apiKey } = JSON.parse(body);\n\n if (!provider || typeof provider !== \"string\") {\n jsonResponse(res, 400, { error: \"provider is required\" });\n return;\n }\n\n if (!apiKey) {\n const configUpdate: Record<string, unknown> = {};\n switch (provider) {\n case \"anthropic\": configUpdate.anthropicApiKey = \"\"; break;\n case \"openai\": configUpdate.openaiApiKey = \"\"; break;\n case \"gemini\": configUpdate.geminiApiKey = \"\"; break;\n default:\n jsonResponse(res, 400, { error: `Unknown provider: ${provider}` });\n return;\n }\n saveConfig(configUpdate as any);\n jsonResponse(res, 200, { ok: true, provider, deleted: true });\n return;\n }\n\n const configUpdate: Record<string, unknown> = {};\n switch (provider) {\n case \"anthropic\": configUpdate.anthropicApiKey = apiKey; break;\n case \"openai\": configUpdate.openaiApiKey = apiKey; break;\n case \"gemini\": configUpdate.geminiApiKey = apiKey; break;\n default:\n jsonResponse(res, 400, { error: `Unknown provider: ${provider}` });\n return;\n }\n\n saveConfig(configUpdate as any);\n\n let autoSelectedEngine: string | null = null;\n const currentConfig = loadConfig();\n if (!currentConfig.aiEngine) {\n const engineMap: Record<string, string> = {\n anthropic: \"anthropic-api\",\n openai: \"openai-api\",\n gemini: \"gemini-api\",\n };\n const engine = engineMap[provider];\n if (engine) {\n saveConfig({ aiEngine: engine } as any);\n autoSelectedEngine = engine;\n }\n }\n\n jsonResponse(res, 200, { ok: true, provider, autoSelectedEngine });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsInstallRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { tool } = JSON.parse(body);\n\n const installCommands: Record<string, { cmd: string; desc: string }> = {\n hubspot: { cmd: \"npm install -g @hubspot/cli\", desc: \"Installing HubSpot CLI\" },\n claude: { cmd: \"npm install -g @anthropic-ai/claude-code\", desc: \"Installing Claude Code\" },\n gemini: { cmd: \"npm install -g @google/gemini-cli\", desc: \"Installing Gemini CLI\" },\n codex: { cmd: process.platform === \"darwin\" ? \"brew install --cask codex\" : \"npm install -g @openai/codex\", desc: \"Installing OpenAI Codex\" },\n gh: { cmd: process.platform === \"darwin\" ? \"brew install gh\" : \"npm install -g @cli/gh\", desc: \"Installing GitHub CLI\" },\n };\n\n const config = installCommands[tool];\n if (!config) {\n jsonResponse(res, 400, { error: `Unknown tool: ${tool}. Valid: ${Object.keys(installCommands).join(\", \")}` });\n return;\n }\n\n const jobId = startJob(config.cmd, config.desc, { timeout: 120_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsHsAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const parsed = JSON.parse(body || \"{}\");\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (parsed.personalAccessKey) {\n if (uploadMode === \"api\") {\n // API mode: validate PAK directly via HTTP, store in config\n validatePak(parsed.personalAccessKey).then((info) => {\n addHubSpotAccount(parsed.personalAccessKey, info.portalId, info.portalName, info.dataCenter);\n jsonResponse(res, 200, {\n ok: true,\n portalName: info.portalName,\n portalId: info.portalId,\n dataCenter: info.dataCenter,\n });\n }).catch((err) => {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n });\n return;\n } else {\n // CLI mode: use hs auth command\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\", needsInstall: true });\n return;\n }\n const jobId = startJobSafe(\n \"hs\", [\"auth\", `--pak=${parsed.personalAccessKey}`],\n \"Authenticating with HubSpot\",\n { timeout: 30_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n }\n\n // No key provided — check existing auth\n if (uploadMode === \"api\") {\n const accounts = config.hubspotAccounts || [];\n if (accounts.length > 0 && !parsed.force) {\n const active = accounts.find((a) => a.portalId === config.activeHubSpotAccount) || accounts[0];\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n portalName: active.portalName,\n portalId: active.portalId,\n });\n return;\n }\n } else {\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\", needsInstall: true });\n return;\n }\n const auth = detectHubSpotAuth();\n if (auth.authenticated && !parsed.force) {\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n portalName: auth.portalName,\n portalId: auth.portalId,\n });\n return;\n }\n }\n\n jsonResponse(res, 200, {\n needsKey: true,\n instructions: \"Create a personal access key in HubSpot\",\n url: \"https://app.hubspot.com/portal-recommend/l?slug=personal-access-key\",\n steps: [\n \"Click the link above to open HubSpot\",\n \"Select your account\",\n \"Create a Personal Access Key with CMS permissions\",\n \"Copy the key and paste it below\",\n ],\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsGhAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const parsed = JSON.parse(body || \"{}\");\n\n const gh = detectGitHubCLI();\n if (!gh.found) {\n jsonResponse(res, 400, { error: \"GitHub CLI not installed\", needsInstall: true });\n return;\n }\n\n const auth = detectGitHubAuth();\n if (auth.authenticated && !parsed.force) {\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n username: auth.username,\n });\n return;\n }\n\n if (parsed.token) {\n const jobId = startJobSafe(\n \"gh\", [\"auth\", \"login\", \"--with-token\"],\n \"Authenticating with GitHub\",\n { timeout: 30_000, stdin: parsed.token }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n\n const jobId = startJob(\n \"gh auth login --web --git-protocol https\",\n \"GitHub authentication (check your browser)\",\n { timeout: 300_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, browserAuthRequired: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsHsSwitchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { portalId, action } = JSON.parse(body);\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (uploadMode === \"api\") {\n // API mode: synchronous config updates (no subprocess)\n if (action === \"remove\" && portalId) {\n removeHubSpotAccount(portalId);\n jsonResponse(res, 200, { ok: true });\n return;\n }\n if (portalId) {\n setActiveHubSpotAccount(portalId);\n jsonResponse(res, 200, { ok: true });\n return;\n }\n } else {\n // CLI mode: use hs accounts commands\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\" });\n return;\n }\n // Validate portalId is numeric to prevent injection\n const safePortalId = String(portalId).replace(/[^0-9]/g, \"\");\n if (!safePortalId) {\n jsonResponse(res, 400, { error: \"Invalid portalId\" });\n return;\n }\n if (action === \"remove\") {\n const jobId = startJobSafe(\"hs\", [\"accounts\", \"remove\", safePortalId], `Removing HubSpot account ${safePortalId}`, { timeout: 15_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n if (safePortalId) {\n const jobId = startJobSafe(\"hs\", [\"accounts\", \"use\", safePortalId], `Switching to HubSpot account ${safePortalId}`, { timeout: 15_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n }\n\n jsonResponse(res, 400, { error: \"portalId required\" });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsGhLogoutRoute(res: ServerResponse): void {\n const jobId = startJob(\n \"gh auth logout --hostname github.com -y\",\n \"Logging out of GitHub\",\n { timeout: 15_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n}\n\nexport function handleSettingsCLIAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { cli, apiKey } = JSON.parse(body || \"{}\");\n\n switch (cli) {\n case \"claude\": {\n const jobId = startJob(\n \"CLAUDECODE= claude --print -p 'reply OK'\",\n \"Authenticating Claude Code (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"If Claude Code opens a browser window, complete the sign-in there.\" });\n break;\n }\n case \"gemini\": {\n const jobId = startJob(\n \"gemini -p 'reply OK'\",\n \"Authenticating Gemini CLI (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"If Gemini opens a browser window, complete the sign-in there.\" });\n break;\n }\n case \"codex\": {\n if (apiKey && apiKey.trim()) {\n const key = apiKey.trim();\n process.env.OPENAI_API_KEY = key;\n saveConfig({ openaiApiKey: key } as any);\n if (process.platform !== \"win32\") {\n // Sanitize key to prevent shell profile injection — only allow\n // alphanumeric chars, dashes, underscores, and dots (valid API key chars)\n const safeKey = /^[A-Za-z0-9_\\-.:]+$/.test(key) ? key : \"\";\n if (safeKey) {\n const profileLine = `export OPENAI_API_KEY=\"${safeKey}\"`;\n const shellProfile = process.env.SHELL?.includes(\"zsh\")\n ? join(homedir(), \".zshrc\")\n : join(homedir(), \".bashrc\");\n try {\n const existing = existsSync(shellProfile)\n ? readFileSync(shellProfile, \"utf-8\")\n : \"\";\n if (!existing.includes(\"OPENAI_API_KEY\")) {\n appendFileSync(shellProfile, `\\n# Added by vibeSpot\\n${profileLine}\\n`);\n }\n } catch { /* ignore profile write errors */ }\n }\n }\n jsonResponse(res, 200, { ok: true, message: \"API key saved\" });\n } else {\n const jobId = startJob(\n \"codex login\",\n \"Authenticating Codex CLI (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"Complete the sign-in in your browser.\" });\n }\n break;\n }\n default:\n jsonResponse(res, 400, { error: `Unknown CLI: ${cli}` });\n }\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// HubSpot upload mode toggle\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsHsModeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { mode } = JSON.parse(body);\n if (mode !== \"api\" && mode !== \"cli\") {\n jsonResponse(res, 400, { error: `Invalid mode: ${mode}. Must be \"api\" or \"cli\".` });\n return;\n }\n saveConfig({ hubspotUploadMode: mode } as any);\n jsonResponse(res, 200, { ok: true, mode });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// CLI tool toggle\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsCliToggleRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { toolId, enabled } = JSON.parse(body);\n if (!toolId || typeof enabled !== \"boolean\") {\n jsonResponse(res, 400, { error: \"toolId (string) and enabled (boolean) required\" });\n return;\n }\n setCliToolEnabled(toolId, enabled);\n jsonResponse(res, 200, { ok: true, toolId, enabled });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Generic settings save (used for agentic mode, etc.)\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsGenericRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const data = JSON.parse(body);\n const allowedKeys = [\"agenticMode\", \"agenticConcurrency\"];\n const update: Record<string, unknown> = {};\n\n for (const key of allowedKeys) {\n if (key in data) update[key] = data[key];\n }\n\n if (Object.keys(update).length === 0) {\n jsonResponse(res, 400, { error: \"No valid settings fields provided\" });\n return;\n }\n\n saveConfig(update as import(\"../../utils/config.js\").VibeSpotConfig);\n jsonResponse(res, 200, { ok: true, updated: Object.keys(update) });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Job polling\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsJobRoute(path: string, res: ServerResponse): void {\n const jobId = path.replace(\"/api/settings/job/\", \"\");\n if (!jobId) {\n jsonResponse(res, 400, { error: \"Job ID required\" });\n return;\n }\n\n const job = getJob(jobId);\n if (!job) {\n jsonResponse(res, 404, { error: \"Job not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n id: job.id,\n status: job.status,\n description: job.description,\n output: job.output,\n exitCode: job.exitCode,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n });\n}\n","/**\n * Claude OAuth routes — save/manage OAuth tokens for Claude Pro/Max access.\n * Users obtain tokens via `claude setup-token` (Claude Code CLI).\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { saveConfig, loadConfig } from \"../../utils/config.js\";\nimport {\n saveInitialToken,\n hasValidOAuthToken,\n getOAuthTokenInfo,\n clearOAuthTokens,\n} from \"../../utils/claude-oauth.js\";\n\n/**\n * POST /api/settings/claude-oauth/save\n * Save an OAuth token (from `claude setup-token` or manual paste).\n */\nexport function handleClaudeOAuthSaveRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { access_token, refresh_token } = JSON.parse(body);\n if (!access_token || typeof access_token !== \"string\") {\n jsonResponse(res, 400, { error: \"access_token is required\" });\n return;\n }\n\n saveInitialToken(access_token.trim(), (refresh_token || \"\").trim());\n\n // Auto-select claude-oauth as the active engine\n const config = loadConfig();\n if (!config.aiEngine || config.aiEngine !== \"claude-oauth\") {\n saveConfig({ aiEngine: \"claude-oauth\" } as any);\n }\n\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n/**\n * GET /api/settings/claude-oauth/status\n * Return current OAuth authentication status.\n */\nexport function handleClaudeOAuthStatusRoute(_req: IncomingMessage, res: ServerResponse): void {\n const authenticated = hasValidOAuthToken();\n const info = getOAuthTokenInfo();\n jsonResponse(res, 200, {\n authenticated,\n expiresAt: info?.expiresAt || null,\n });\n}\n\n/**\n * POST /api/settings/claude-oauth/logout\n * Clear stored OAuth tokens and reset engine if needed.\n */\nexport function handleClaudeOAuthLogoutRoute(_req: IncomingMessage, res: ServerResponse): void {\n try {\n clearOAuthTokens();\n\n const config = loadConfig();\n if (config.aiEngine === \"claude-oauth\") {\n saveConfig({ aiEngine: undefined } as any);\n }\n\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n","/**\n * Theme routes — list, switch, delete, rename themes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport {\n getSession,\n listSessions,\n loadSession,\n deleteSession,\n renameSession,\n saveSession,\n} from \"../session.js\";\nimport { WORKSPACE_DIR } from \"./setup.js\";\n\nexport function handleThemesRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n if (method === \"GET\") {\n const session = getSession();\n const sessions = listSessions()\n .sort((a, b) => b.updatedAt - a.updatedAt);\n\n jsonResponse(res, 200, {\n activeTheme: session\n ? { id: session.id, themeName: session.themeName }\n : null,\n sessions,\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { sessionId, deleteFiles } = JSON.parse(body);\n deleteSession(sessionId, deleteFiles);\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleThemeSwitchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { sessionId } = JSON.parse(body);\n const session = loadSession(sessionId);\n if (!session) {\n jsonResponse(res, 404, { error: \"Session not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: session.themeName,\n themePath: session.themePath,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleDeleteLocalThemeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { themeName } = JSON.parse(body);\n if (!themeName || typeof themeName !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n const themePath = join(WORKSPACE_DIR, themeName);\n if (!existsSync(themePath)) {\n jsonResponse(res, 404, { error: \"Theme not found on disk\" });\n return;\n }\n rmSync(themePath, { recursive: true, force: true });\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleRenameThemeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { sessionId, newName } = JSON.parse(body);\n if (!sessionId || !newName || typeof newName !== \"string\") {\n jsonResponse(res, 400, { error: \"sessionId and newName are required\" });\n return;\n }\n const sanitized = newName.toLowerCase().replace(/[^a-z0-9-]/g, \"-\").replace(/^-|-$/g, \"\").replace(/-{2,}/g, \"-\");\n if (!sanitized) {\n jsonResponse(res, 400, { error: \"Invalid name\" });\n return;\n }\n const result = renameSession(sessionId, sanitized);\n if (result.ok) {\n jsonResponse(res, 200, { ok: true, newName: sanitized });\n } else {\n jsonResponse(res, 400, { error: result.error });\n }\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n","/**\n * Dashboard & template routes — CRUD, activate, rename, module library, brand assets, download.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readFileSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { execFileSync, type ExecFileSyncOptions } from \"node:child_process\";\n\nconst _shellOpt: ExecFileSyncOptions = process.platform === \"win32\" ? { shell: true } : {};\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { log } from \"../log.js\";\nimport { getHubSpotPak } from \"../../utils/config.js\";\nimport {\n getSession,\n saveSession,\n getOrderedModules,\n getActiveTemplate,\n setActiveTemplate,\n addTemplate,\n removeTemplate,\n cloneTemplate,\n getModuleLibrary,\n renameTemplate,\n type PageType,\n} from \"../session.js\";\nimport { ensureDir, writeFile } from \"../../utils/fs.js\";\n\nexport function handleDashboardRoute(res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const library = getModuleLibrary();\n jsonResponse(res, 200, {\n themeName: session.themeName,\n themePath: session.themePath,\n templates: session.templates.map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n messageCount: t.messages.length,\n })),\n activeTemplateId: session.activeTemplateId,\n moduleLibrary: library.map((entry) => ({\n moduleName: entry.module.moduleName,\n usedIn: entry.usedIn,\n })),\n brandAssets: {\n hasStyleguide: !!session.brandAssets?.styleguide,\n hasBrandvoice: !!session.brandAssets?.brandvoice,\n hasThemeContext: !!session.brandAssets?.themeContext,\n humanify: session.brandAssets?.humanify !== false,\n },\n });\n}\n\nexport function handleDownloadZipRoute(res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const themePath = session.themePath;\n if (!existsSync(themePath)) {\n jsonResponse(res, 404, { error: \"Theme directory not found\" });\n return;\n }\n\n const themeName = session.themeName || \"theme\";\n const parentDir = join(themePath, \"..\");\n const folderName = basename(themePath);\n\n try {\n const zipFileName = `${themeName}.zip`;\n const tmpZip = join(parentDir, zipFileName);\n\n if (existsSync(tmpZip)) rmSync(tmpZip);\n\n execFileSync(\"zip\", [\n \"-r\", zipFileName, folderName,\n \"-x\", `${folderName}/.git/*`, `${folderName}/.vibespot/*`, `${folderName}/node_modules/*`,\n ], { cwd: parentDir, timeout: 30_000, ..._shellOpt });\n\n const zipData = readFileSync(tmpZip);\n rmSync(tmpZip);\n\n res.writeHead(200, {\n \"Content-Type\": \"application/zip\",\n \"Content-Disposition\": `attachment; filename=\"${zipFileName}\"`,\n \"Content-Length\": zipData.length,\n });\n res.end(zipData);\n } catch (err: any) {\n log.error(\"download-zip\", \"Failed to create zip archive\", err);\n jsonResponse(res, 500, { error: \"Failed to create zip archive\" });\n }\n}\n\nexport function handleTemplatesRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n jsonResponse(res, 200, {\n templates: session.templates.map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n })),\n activeTemplateId: session.activeTemplateId,\n });\n return;\n }\n\n if (method === \"POST\") {\n readBody(req, (body) => {\n try {\n const { pageType, label } = JSON.parse(body);\n if (!pageType || !label) {\n jsonResponse(res, 400, { error: \"pageType and label are required\" });\n return;\n }\n const validTypes: PageType[] = [\"landing_page\", \"blog_post\", \"website_page\", \"module_only\"];\n if (!validTypes.includes(pageType)) {\n jsonResponse(res, 400, { error: `Invalid pageType: ${pageType}` });\n return;\n }\n\n const entry = addTemplate(pageType, label);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n template: {\n id: entry.id,\n label: entry.label,\n pageType: entry.pageType,\n },\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { templateId, deleteModules } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const removed = removeTemplate(templateId, !!deleteModules);\n if (!removed) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleTemplateActivateRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const success = setActiveTemplate(templateId);\n if (!success) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n const session = getSession();\n jsonResponse(res, 200, {\n ok: true,\n modules: getOrderedModules().map((m) => m.moduleName),\n messageCount: session?.messages.length || 0,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleTemplateRenameRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId, newLabel } = JSON.parse(body);\n if (!templateId || !newLabel || typeof newLabel !== \"string\") {\n jsonResponse(res, 400, { error: \"templateId and newLabel are required\" });\n return;\n }\n const success = renameTemplate(templateId, newLabel.trim());\n if (!success) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, { ok: true, newLabel: newLabel.trim() });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleTemplateCloneRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId, label } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const entry = cloneTemplate(templateId, label);\n if (!entry) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, {\n ok: true,\n template: {\n id: entry.id,\n label: entry.label,\n pageType: entry.pageType,\n moduleCount: entry.modules.length,\n },\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleModuleLibraryRoute(res: ServerResponse): void {\n const library = getModuleLibrary();\n jsonResponse(res, 200, {\n modules: library.map((entry) => ({\n moduleName: entry.module.moduleName,\n usedIn: entry.usedIn,\n fieldsJson: entry.module.fieldsJson,\n })),\n });\n}\n\nexport function handleAddModuleToTemplateRoute(path: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n try {\n const { moduleName } = JSON.parse(body);\n if (!moduleName) {\n jsonResponse(res, 400, { error: \"moduleName is required\" });\n return;\n }\n\n const library = getModuleLibrary();\n const entry = library.find((e) => e.module.moduleName === moduleName);\n if (!entry) {\n jsonResponse(res, 404, { error: `Module \"${moduleName}\" not found in library` });\n return;\n }\n\n const modCopy = { ...entry.module };\n const existing = session.modules.find((m) => m.moduleName === modCopy.moduleName);\n if (!existing) {\n session.modules.push(modCopy);\n session.moduleOrder.push(modCopy.moduleName);\n session.updatedAt = Date.now();\n }\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleBrandAssetsRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n jsonResponse(res, 200, {\n styleguide: session.brandAssets?.styleguide || null,\n brandvoice: session.brandAssets?.brandvoice || null,\n themeContext: session.brandAssets?.themeContext || null,\n });\n return;\n }\n\n if (method === \"POST\") {\n readBody(req, (body) => {\n try {\n const { type, content } = JSON.parse(body);\n if (!type) {\n jsonResponse(res, 400, { error: \"type is required\" });\n return;\n }\n\n if (!session.brandAssets) session.brandAssets = {};\n\n if (type === \"humanify\") {\n session.brandAssets.humanify = content === \"on\";\n session.updatedAt = Date.now();\n saveSession();\n jsonResponse(res, 200, { ok: true });\n return;\n }\n\n if (!content) {\n jsonResponse(res, 400, { error: \"content is required\" });\n return;\n }\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}. Must be \"styleguide\", \"brandvoice\", or \"themeContext\"` });\n return;\n }\n\n const filename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n session.brandAssets[type] = content;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, filename), content);\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { type } = JSON.parse(body);\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}` });\n return;\n }\n\n if (session.brandAssets) {\n delete session.brandAssets[type];\n }\n session.updatedAt = Date.now();\n\n const delFilename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n const filePath = join(session.themePath, \".vibespot\", delFilename);\n if (existsSync(filePath)) rmSync(filePath);\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\n// ---------------------------------------------------------------------------\n// Brand asset extraction from theme\n// ---------------------------------------------------------------------------\n\n/** Save a single brand asset to session + disk. */\nfunction saveBrandAsset(\n session: ReturnType<typeof getSession>,\n type: \"styleguide\" | \"brandvoice\" | \"themeContext\",\n content: string,\n): void {\n if (!session) return;\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets[type] = content;\n session.updatedAt = Date.now();\n\n const filename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, filename), content);\n}\n\n/** Extract a single brand asset by type. */\nasync function extractSingleAsset(\n session: NonNullable<ReturnType<typeof getSession>>,\n type: \"styleguide\" | \"brandvoice\" | \"themeContext\",\n sourcePath?: string,\n): Promise<string | null> {\n if (type === \"styleguide\") {\n const { extractDesignContext } = await import(\"../../ai/design-extractor.js\");\n return extractDesignContext(sourcePath || session.themePath);\n }\n\n // brandvoice and themeContext need AI + rendered preview HTML\n const { resolveAgenticEngine } = await import(\"../ai-handler.js\");\n const { loadConfig } = await import(\"../../utils/config.js\");\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n\n const { buildPreviewHtml } = await import(\"../preview.js\");\n const previewHtml = buildPreviewHtml();\n if (!previewHtml || previewHtml.length < 50) return null;\n\n if (type === \"brandvoice\") {\n const { extractBrandvoice } = await import(\"../agent/stages/brandvoice-extractor.js\");\n return extractBrandvoice(previewHtml, engine, apiKey, model);\n }\n\n // themeContext\n const { extractThemeContext } = await import(\"../agent/stages/context-extractor.js\");\n return extractThemeContext(previewHtml, session.brandAssets?.themeContext, engine, apiKey, model);\n}\n\nexport function handleDesignExtractRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n (async () => {\n try {\n const parsed = body ? JSON.parse(body) : {};\n const type = parsed.type || \"styleguide\";\n const sourcePath = parsed.sourcePath;\n\n if (type === \"all\") {\n // Extract all three in parallel\n const types = [\"styleguide\", \"brandvoice\", \"themeContext\"] as const;\n const results = await Promise.allSettled(\n types.map((t) => extractSingleAsset(session, t, sourcePath)),\n );\n\n const extracted: Record<string, string | null> = {};\n for (let i = 0; i < types.length; i++) {\n const r = results[i];\n const content = r.status === \"fulfilled\" ? r.value : null;\n if (content) {\n saveBrandAsset(session, types[i], content);\n }\n extracted[types[i]] = content;\n }\n\n saveSession();\n jsonResponse(res, 200, { ok: true, type: \"all\", extracted });\n return;\n }\n\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}` });\n return;\n }\n\n const content = await extractSingleAsset(session, type, sourcePath);\n if (!content) {\n jsonResponse(res, 200, { ok: false, type, error: \"No content to extract from\" });\n return;\n }\n\n saveBrandAsset(session, type, content);\n saveSession();\n jsonResponse(res, 200, { ok: true, type, content });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n })();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Reference theme import — download from HubSpot + extract design\n// ---------------------------------------------------------------------------\n\nexport function handleReferenceImportRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n (async () => {\n try {\n const { source, themeName, localPath } = JSON.parse(body);\n\n let sourcePath: string;\n\n if (source === \"hubspot\") {\n // Download theme from HubSpot to temp location\n if (!themeName) {\n jsonResponse(res, 400, { error: \"themeName is required for HubSpot import\" });\n return;\n }\n const pak = getHubSpotPak();\n if (!pak) {\n jsonResponse(res, 400, { error: \"No HubSpot account connected\" });\n return;\n }\n\n // Strip leading/trailing slashes (HubSpot DM \"Copy path\" gives \"/@marketplace/Theme\")\n const cleanName = themeName.replace(/^\\/+|\\/+$/g, \"\");\n if (!cleanName) {\n jsonResponse(res, 400, { error: \"Invalid theme name\" });\n return;\n }\n\n // Sanitize for local directory name (replace @ and / with safe chars)\n const safeDirName = cleanName.replace(/[@/]/g, \"_\").replace(/_+/g, \"_\");\n const { homedir } = await import(\"node:os\");\n const refDir = join(homedir(), \"vibespot-themes\", \".references\", safeDirName);\n ensureDir(refDir);\n\n const { fetchTheme } = await import(\"../../hubspot/fetcher.js\");\n await fetchTheme(pak, cleanName, refDir);\n sourcePath = refDir;\n } else if (source === \"local\") {\n if (!localPath) {\n jsonResponse(res, 400, { error: \"localPath is required for local import\" });\n return;\n }\n if (!existsSync(localPath)) {\n jsonResponse(res, 400, { error: `Path not found: ${localPath}` });\n return;\n }\n sourcePath = localPath;\n } else {\n jsonResponse(res, 400, { error: \"source must be 'hubspot' or 'local'\" });\n return;\n }\n\n // Extract design context and save to current theme\n const { extractDesignContext } = await import(\"../../ai/design-extractor.js\");\n const styleguide = await extractDesignContext(sourcePath);\n\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.styleguide = styleguide;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, \"styleguide.md\"), styleguide);\n\n saveSession();\n jsonResponse(res, 200, { ok: true, styleguide, source: sourcePath });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n })();\n });\n}\n","/**\n * Module, field, import, session, upload, and history routes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { join } from \"node:path\";\nimport { jsonResponse, readBody, readJsonBody } from \"../route-helpers.js\";\nimport {\n getSession,\n getOrderedModules,\n removeModule,\n detachModule,\n reorderModules,\n updateFieldValue,\n saveSession,\n writeModulesToDisk,\n addMessage,\n reloadModulesFromDisk,\n reloadActiveTemplateFromDisk,\n getActiveTemplate,\n} from \"../session.js\";\nimport { isGenerating } from \"../ai-handler.js\";\nimport { applyAutoFixes } from \"../auto-fix.js\";\nimport { startStreamingJob } from \"../process-manager.js\";\nimport { analyzeSource } from \"../../wizard/source.js\";\nimport { commitThemeState, commitTemplateState, getHistory, getTemplateHistory, rollbackToCommit, rollbackTemplateToCommit, isGitAvailable } from \"../project-git.js\";\n\nexport function handleSessionRoute(method: string, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n jsonResponse(res, 200, {\n id: session.id,\n themeName: session.themeName,\n themePath: session.themePath,\n messageCount: session.messages.length,\n moduleCount: session.modules.length,\n moduleOrder: session.moduleOrder,\n });\n}\n\nexport function handleModulesRoute(\n method: string,\n req: IncomingMessage,\n res: ServerResponse\n): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n const ordered = getOrderedModules();\n jsonResponse(res, 200, {\n modules: ordered.map((m) => ({\n moduleName: m.moduleName,\n fieldsJson: m.fieldsJson,\n moduleHtml: m.moduleHtml,\n moduleCss: m.moduleCss,\n moduleJs: m.moduleJs || null,\n })),\n sharedCss: session.sharedCss,\n sharedJs: session.sharedJs,\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readJsonBody(req, res, (data: { moduleName: string; deleteEntirely?: boolean }) => {\n if (data.deleteEntirely) {\n removeModule(data.moduleName);\n } else {\n detachModule(data.moduleName);\n }\n saveSession();\n jsonResponse(res, 200, { ok: true });\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleCodeUpdateRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n try {\n const data = JSON.parse(body);\n\n // Shared CSS/JS\n if (data.shared) {\n if (data.shared === \"css\") {\n session.sharedCss = data.content;\n } else if (data.shared === \"js\") {\n session.sharedJs = data.content;\n } else {\n jsonResponse(res, 400, { error: \"Invalid shared type\" });\n return;\n }\n // Also update the active template's shared fields\n const tpl = getActiveTemplate();\n if (tpl) {\n if (data.shared === \"css\") tpl.sharedCss = data.content;\n else tpl.sharedJs = data.content;\n }\n session.updatedAt = Date.now();\n saveSession();\n writeModulesToDisk();\n jsonResponse(res, 200, { ok: true });\n return;\n }\n\n // Module file\n const { moduleName, fileType, content } = data;\n if (!moduleName || !fileType) {\n jsonResponse(res, 400, { error: \"moduleName and fileType required\" });\n return;\n }\n\n const mod = session.modules.find((m) => m.moduleName === moduleName);\n if (!mod) {\n jsonResponse(res, 404, { error: `Module \"${moduleName}\" not found` });\n return;\n }\n\n switch (fileType) {\n case \"html\": mod.moduleHtml = content; break;\n case \"css\": mod.moduleCss = content; break;\n case \"js\": mod.moduleJs = content || undefined; break;\n case \"fields\":\n // Validate JSON before saving\n try { JSON.parse(content); } catch {\n jsonResponse(res, 400, { error: \"Invalid JSON in fields.json\" });\n return;\n }\n mod.fieldsJson = content;\n break;\n default:\n jsonResponse(res, 400, { error: `Invalid fileType: ${fileType}` });\n return;\n }\n\n session.updatedAt = Date.now();\n saveSession();\n writeModulesToDisk();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: String(err) });\n }\n });\n}\n\nexport function handleReorderRoute(req: IncomingMessage, res: ServerResponse): void {\n readJsonBody(req, res, (data: { order: unknown }) => {\n if (Array.isArray(data.order)) {\n reorderModules(data.order);\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } else {\n jsonResponse(res, 400, { error: \"order must be an array\" });\n }\n });\n}\n\nexport async function handleUploadRoute(res: ServerResponse): Promise<void> {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n try {\n writeModulesToDisk();\n const fixes = applyAutoFixes(session.themePath);\n\n const jobId = startStreamingJob(\n `hs cms upload \"${session.themePath}\" \"${session.themeName}\"`,\n \"Uploading to HubSpot\",\n { cwd: join(session.themePath, \"..\"), timeout: 180_000 }\n );\n\n jsonResponse(res, 200, {\n ok: true,\n jobId,\n fixes,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: String(err) });\n }\n}\n\nexport function handleFieldRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { moduleName, fieldPath, value } = JSON.parse(body);\n updateFieldValue(moduleName, fieldPath, value);\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: String(err) });\n }\n });\n}\n\nexport function handleImportRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { url } = JSON.parse(body);\n if (!url || typeof url !== \"string\") {\n jsonResponse(res, 400, { error: \"url is required\" });\n return;\n }\n\n const analysis = analyzeSource(url);\n\n const componentSummary = analysis.components\n .map((c) => `- ${c.name}: ${c.description}`)\n .join(\"\\n\");\n\n const summary = {\n sourceDir: analysis.sourceDir,\n componentCount: analysis.components.length,\n components: analysis.components.map((c) => ({\n name: c.name,\n description: c.description,\n })),\n hasTailwind: analysis.hasTailwind,\n cssVarCount: analysis.cssVarCount,\n fonts: analysis.fonts,\n interactions: analysis.interactions,\n conversionPrompt: `Import and convert the React landing page from ${url} to native HubSpot modules.\n\nSource analysis found ${analysis.components.length} components:\n${componentSummary}\n\nDesign system: ${analysis.hasTailwind ? \"Tailwind CSS\" : \"Custom CSS\"}, ${analysis.cssVarCount} CSS variables\nFonts: ${analysis.fonts.length > 0 ? analysis.fonts.join(\", \") : \"System fonts\"}\nInteractions: ${analysis.interactions.join(\", \")}\n\nRead the React source files from ${analysis.sourceDir} and convert each component to a HubSpot module. Preserve the design, layout, colors, and content. Generate fields.json so marketers can edit all text, images, colors, and links in the HubSpot page editor.`,\n };\n\n jsonResponse(res, 200, summary);\n } catch (err) {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Version history routes\n// ---------------------------------------------------------------------------\n\nexport function handleHistoryRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n if (!isGitAvailable()) {\n jsonResponse(res, 200, { available: false, commits: [] });\n return;\n }\n\n const url = new URL(req.url || \"/\", \"http://localhost\");\n const templateId = url.searchParams.get(\"templateId\");\n\n const commits = templateId\n ? getTemplateHistory(session.themePath, templateId, 50)\n : getHistory(session.themePath, 50);\n jsonResponse(res, 200, { available: true, commits, filtered: !!templateId });\n}\n\nexport function handleRollbackRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const { hash, templateId } = JSON.parse(body);\n if (!hash || typeof hash !== \"string\") {\n jsonResponse(res, 400, { error: \"Commit hash is required\" });\n return;\n }\n\n addMessage(\"assistant\", `Rolled back to version ${hash.slice(0, 7)}.`);\n\n if (templateId) {\n const tpl = session.templates.find((t) => t.id === templateId);\n if (!tpl) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n const filePaths = tpl.moduleOrder.map((n) => `modules/${n}.module`);\n if (tpl.templateFile) filePaths.push(tpl.templateFile);\n\n const result = rollbackTemplateToCommit(session.themePath, templateId, hash, filePaths);\n if (!result.success) {\n jsonResponse(res, 500, { error: result.error || \"Rollback failed\" });\n return;\n }\n reloadActiveTemplateFromDisk();\n } else {\n const result = rollbackToCommit(session.themePath, hash);\n if (!result.success) {\n jsonResponse(res, 500, { error: result.error || \"Rollback failed\" });\n return;\n }\n reloadModulesFromDisk();\n }\n\n saveSession();\n jsonResponse(res, 200, {\n ok: true,\n modules: getOrderedModules().map((m) => m.moduleName),\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n"],"mappings":"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","names":["path","fileURLToPath","init_esm_shims","__esmMin","readFileSync","writeFileSync","mkdirSync","existsSync","dirname","join","readFile","path","writeFile","content","fileExists","ensureDir","resolveAsset","name","paths","p","getVersion","_version","candidates","pkg","getChangelog","_changelog","init_fs","__esmMin","init_esm_shims","execSync","run","command","options","err","e","stdout","stderr","runPassthrough","init_shell","__esmMin","init_esm_shims","config_exports","__export","addHubSpotAccount","getActiveHubSpotAccount","getApiKeyForEngine","getConfigDir","getHubSpotPak","isCliToolEnabled","loadConfig","maskApiKey","removeHubSpotAccount","saveConfig","setActiveHubSpotAccount","setCliToolEnabled","join","homedir","chmodSync","fileExists","CONFIG_PATH","raw","readFile","engine","config","c","key","merged","writeFile","CONFIG_DIR","activeId","found","a","pak","portalId","portalName","dataCenter","accounts","idx","entry","update","toolId","enabled","tools","init_config","__esmMin","init_esm_shims","init_fs","claude_oauth_exports","__export","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","clearOAuthTokens","getOAuthTokenInfo","getValidAccessToken","hasValidOAuthToken","saveInitialToken","join","homedir","chmodSync","unlinkSync","loadTokens","fileExists","TOKEN_FILE","readFile","saveTokens","tokens","writeFile","refreshAccessToken","refresh_token","resp","TOKEN_ENDPOINT","CLIENT_ID","data","accessToken","refreshToken","REFRESH_BUFFER_MS","refreshPromise","init_claude_oauth","__esmMin","init_esm_shims","init_fs","readFileSync","basename","exchangePakForToken","pak","cached","tokenCache","TOKEN_REFRESH_BUFFER_MS","resp","BASE_URL","body","data","token","authHeaders","accessToken","encodePath","remotePath","detectDataCenterFromPak","sleep","ms","resolve","parseErrorResponse","fallbackPath","message","category","detail","firstError","text","fetchWithRetry","url","options","retries","MAX_RETRIES","attempt","delay","RETRY_DELAY_MS","validatePak","err","portalId","portalName","uploadFile","localFilePath","fileContent","fileName","formData","blob","error","deleteFile","downloadFile","arrayBuffer","getMetadata","listRootFolders","headers","urlVariants","children","c","init_api","__esmMin","init_esm_shims","fetcher_exports","__export","fetchTheme","mkdirSync","writeFileSync","join","dirname","listFilesRecursive","pak","remotePath","meta","getMetadata","files","rawChildren","child","childName","childPath","childMeta","parallelMap","items","concurrency","fn","index","worker","i","workers","themeName","targetPath","opts","remoteFiles","relativePath","localPath","content","downloadFile","init_fetcher","__esmMin","init_esm_shims","init_api","cachedAsset","name","val","guideCache","readFile","resolveAsset","getConversionGuide","getDesignGuide","getContentGuide","getHubspotRules","getHumanifyGuide","getPageTypeGuide","pageType","fullGuide","header","startIdx","afterHeader","buildSystemPrompt","conversionGuide","buildModulePrompt","componentSource","moduleName","cssVars","buildCssPrompt","indexCss","tailwindConfig","pagePrefix","buildJsPrompt","hooksSource","interactiveComponents","buildTemplatePrompt","moduleNames","themeName","n","i","init_prompts","__esmMin","init_esm_shims","init_fs","init_types","__esmMin","init_esm_shims","existsSync","writeFileSync","mkdirSync","join","isValidHash","hash","isGitAvailable","gitAvailableCache","run","ensureGitRepo","themePath","ensureVibeSpotDir","init","writeGitIgnore","dir","gitignorePath","commitThemeState","message","truncated","commitResult","hashResult","commitTemplateState","templateId","filePaths","fp","fullPath","prefix","maxMsg","fullMessage","getHistory","limit","result","commits","line","parts","timestamp","getTemplateHistory","escapedId","rollbackToCommit","commitHash","verify","msgResult","origMessage","checkout","rollbackMsg","rollbackTemplateToCommit","restored","init_project_git","__esmMin","init_esm_shims","init_shell","readFileSync","existsSync","writeFileSync","mkdirSync","rmSync","join","syncFlatFieldsFromTemplate","tpl","activeSession","getSession","syncFlatFieldsToTemplate","getActiveTemplate","addMessage","role","content","pipeline","msg","saveChatToTheme","addSessionAsset","asset","saveSession","updateModules","assets","newMod","newNameLower","idx","m","n","reorderModules","newOrder","removeModule","moduleName","modDir","detachModule","updateFieldValue","fieldPath","value","mod","fields","setFieldDefault","getOrderedModules","ordered","name","chatDir","chatData","loadChatFromTheme","themePath","chatPath","data","path","parts","fieldName","field","f","init_state","__esmMin","init_esm_shims","init_store","init_templates","existsSync","rmSync","join","migrateSession","session","templateId","entry","getActiveTemplate","activeSession","getSession","setActiveTemplate","tpl","t","syncFlatFieldsFromTemplate","addTemplate","pageType","label","slug","id","cloneTemplate","newLabel","source","m","renameTemplate","removeTemplate","deleteModules","idx","removed","templatesDir","filename","filePath","listingPath","usedElsewhere","mod","exclusiveModules","name","modulesDir","modDir","getModuleLibrary","map","existing","init_templates","__esmMin","init_esm_shims","init_store","init_state","readFileSync","readdirSync","existsSync","writeFileSync","mkdirSync","rmSync","renameSync","join","dirname","homedir","readIndex","_indexCache","INDEX_PATH","rebuildIndex","writeIndex","entries","SESSIONS_DIR","f","data","templates","n","t","upsertIndex","session","entry","idx","e","removeFromIndex","sessionId","removeFromIndexByTheme","themeName","getSession","activeSession","generateId","createSession","themePath","ensureGitRepo","saveSession","filePath","loadSession","migrateSession","listSessions","deleteSession","deleteFiles","renameSession","newName","oldName","oldPath","newPath","err","cssOld","cssNew","jsOld","jsNew","themeJsonPath","themeData","init_store","__esmMin","init_esm_shims","init_project_git","init_templates","readFileSync","readdirSync","existsSync","writeFileSync","mkdirSync","rmSync","join","safeRead","filePath","getOrderedModulesFrom","tpl","ordered","name","mod","m","parseTemplateFile","filename","content","id","pageType","label","labelMatch","moduleRe","moduleNames","match","scanTemplateFiles","templatesDir","modulesByName","sharedCss","sharedJs","chatMessages","entries","allFiles","f","parsed","templateModules","templateOrder","scanThemeFromDisk","themePath","activeSession","getSession","chatFromDisk","loadChatFromTheme","ensureGitRepo","modulesDir","entry","modDir","moduleName","cssDir","jsDir","cssFiles","jsFiles","sgPath","bvPath","tcPath","parsedTemplates","firstOrder","loadedNames","validOrder","n","syncFlatFieldsFromTemplate","migrateSession","writeModulesToDisk","allModules","modulesBaseDir","scaffoldHome","activeTemplateFiles","templateContent","generateTemplateForEntry","annotated","ensureTemplateAnnotations","writeBlogListingTemplate","template","generateTemplateFromModules","file","patchBaseTemplate","updateThemeJson","reloadModulesFromDisk","syncFlatFieldsToTemplate","reloadActiveTemplateFromDisk","getActiveTemplate","tplPath","basePath","mainJsLine","themeJsonPath","themeData","sections","listingContent","getOrderedModules","init_disk","__esmMin","init_esm_shims","init_store","init_state","init_templates","init_project_git","init_session","__esmMin","init_esm_shims","init_types","init_store","init_state","init_disk","init_templates","init_session","__esmMin","init_esm_shims","buildContextFromFields","fields","result","field","renderHubL","template","context","output","stripDirectives","processForLoops","processConditionals","resolveExpressions","cleanupRemaining","assemblePreview","opts","styleBlocks","css","scriptBlocks","js","body","tpl","RE_REQUIRE_TAG","RE_END_REQUIRE_TAG","RE_REQUIRE_EXPR","RE_GET_ASSET_URL","_match","filename","RE_GET_ASSET_URL_STRIP","RE_DND_TAGS","RE_MODULE_TAG","RE_TEMPLATE_TAGS","RE_ANNOTATIONS","RE_CONTENT_VARS","safety","match","findOutermostFor","varName","iterExpr","start","end","items","resolveIterable","rendered","item","index","loopContext","out","openTag","forOrEndfor","firstOpen","bodyStart","depth","m","RE_IF_PATTERN","condition","elseMatch","ifBody","elseBody","elifParts","evaluateCondition","i","elifCondition","elifBody","expr","filterParts","path","value","resolvePath","applyFilter","str","rangeMatch","resolveNumericArg","arr","splitMatch","val","arg","parts","current","part","trimmed","eqMatch","left","operator","right","isTruthy","filter","argMatch","filterName","filterArg","len","init_renderer","__esmMin","init_esm_shims","preview_exports","__export","buildModulePreviewHtml","buildPreviewHtml","extractThemeColors","sharedCss","extract","names","fallback","name","re","m","bg","surface","text","textMuted","border","session","getSession","welcomePreview","modules","getOrderedModules","moduleOrder","renderedModules","moduleCssArray","moduleJsArray","renderedNames","mod","context","fields","buildContextFromFields","rendered","renderHubL","anchorId","theme","placeholderCss","assemblePreview","moduleName","tpl","init_preview","__esmMin","init_esm_shims","init_renderer","init_session","appendFileSync","mkdirSync","readdirSync","unlinkSync","join","homedir","ensureLogDir","logDirReady","LOG_DIR","pruneOldLogs","pruned","cutoff","MAX_AGE_DAYS","f","dateStr","ts","todayFile","date","timestamp","writeToFile","level","line","log","init_log","__esmMin","init_esm_shims","context","message","data","err","errMsg","tryParseJSON","raw","repaired","lastPos","attempt","err","posMatch","pos","searchStart","lastQuote","absPos","tryRepairTruncatedJSON","modulesIdx","arrayStart","lastCompleteModule","braceDepth","inString","escaped","i","ch","jsonStr","toModuleFiles","m","parseAndApplyModules","response","onWarning","modulesApplied","match","blockPattern","log","data","obj","updateModules","jsonPattern","lastFenceIdx","truncated","salvaged","hasModuleRef","describesProse","msg","init_ai_parser","__esmMin","init_esm_shims","init_session","init_log","getPromptContext","session","getSession","getActiveTemplate","buildVibeSystemPromptBlocks","conversionGuide","themeName","editMode","pageType","brandAssets","blocks","buildCoreInstructions","guides","getHubspotRules","getDesignGuide","getContentGuide","dynamic","buildDynamicSection","parts","section","getPageTypeGuide","humanifyGuide","getHumanifyGuide","buildVibeSystemPrompt","core","pageTypeSection","pageTypePrompt","brandPrompt","formatReminder","buildStateContext","modules","total","i","mod","library","getModuleLibrary","currentModuleNames","m","otherModules","e","entry","buildMessagesWithContext","userMessage","fileContexts","history","messages","stateContext","assetManifest","imageAssets","a","textContent","hasFiles","fc","imageFiles","contentBlocks","img","init_ai_prompts","__esmMin","init_esm_shims","init_prompts","init_session","spawn","getAnthropicSDK","_AnthropicCtor","buildFileContextText","fileContexts","parts","fc","_streamAnthropic","client","system","messages","model","onChunk","onStatus","onFinish","attempt","fullResponse","statusIndex","sendStatus","CLI_STATUS_MESSAGES","heartbeat","stream","event","text","err","status","errType","RATE_LIMIT_DELAYS","wait","log","r","prepareAnthropicContext","userMessage","themeName","conversionGuide","getConversionGuide","editMode","getSession","buildMessagesWithContext","ctx","getPromptContext","systemBlocks","buildVibeSystemPromptBlocks","streamWithAnthropicAPI","apiKey","AnthropicSDK","b","streamWithClaudeOAuth","accessToken","getValidAccessToken","OAUTH_EXTRA_HEADERS","oauthBlocks","OAUTH_SYSTEM_PREFIX","streamWithOpenAIAPI","openaiMessages","m","block","response","buildVibeSystemPrompt","reader","decoder","buffer","done","value","lines","line","data","delta","streamWithGeminiAPI","session","stateContext","buildStateContext","contents","userContent","userParts","url","spawnCLI","bin","args","prompt","resolve","reject","env","child","stdout","stderr","settled","settle","fn","d","chunk","code","timer","generateWithClaudeCode","config","loadConfig","msg","result","generateWithCLI","cli","init_ai_engines","__esmMin","init_esm_shims","init_prompts","init_config","init_claude_oauth","init_session","init_ai_prompts","init_log","jsonResponse","res","status","data","readBody","req","callback","chunks","chunk","readJsonBody","body","init_route_helpers","__esmMin","init_esm_shims","createWriteStream","mkdirSync","existsSync","readFileSync","join","extname","randomUUID","Busboy","sanitizeFilename","name","deduplicateFilename","dir","ext","base","counter","extractPdfText","filePath","pdfParse","buffer","extractDocxText","extractPlainText","handleFileUploadRoute","req","res","session","getSession","jsonResponse","results","errors","fileCount","writePromises","bb","MAX_FILE_SIZE","fieldname","fileStream","info","originalName","mimeType","SUPPORTED_MIMES","isImage","IMAGE_MIMES","sanitized","id","targetDir","finalFilename","targetPath","writeStream","fileSize","truncated","chunk","resolve","asset","addSessionAsset","log","err","a","getFileContexts","fileIds","ctx","imgPath","c","DOCUMENT_MIMES","init_upload_files","__esmMin","init_esm_shims","init_route_helpers","init_session","init_log","withRateLimitRetry","fn","onStatus","attempt","err","status","errType","RATE_LIMIT_DELAYS","wait","log","r","stringifyJsonFields","data","obj","key","getAnthropicSDK","_AnthropicCtor","callAnthropic","apiKey","model","opts","extraHeaders","systemPrefix","AnthropicSDK","client","messages","system","tool","response","block","b","fullText","stream","event","callAnthropicOAuth","accessToken","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","addAdditionalPropertiesFalse","schema","result","props","k","v","callOpenAI","openaiMessages","m","body","error","content","callGemini","_model","contents","url","text","resolveCLIBinary","engine","config","loadConfig","args","buildCLIPrompt","parts","msg","role","schemaDesc","describeSchema","indent","pad","required","lines","prop","req","type","desc","enumVals","itemType","extractJSON","output","trimmed","direct","tryParseJSON","fenceMatch","fenced","parsed","repaired","tryRepairTruncatedJSON","firstBrace","lastBrace","braceContent","callAgentCLI","bin","prompt","rawOutput","spawnCLI","callAgentAPI","getValidAccessToken","oauthToken","callAgent","API_ENGINES","isAgenticCapable","isCLIEngine","init_engine_adapter","__esmMin","init_esm_shims","init_ai_engines","init_ai_parser","init_config","init_claude_oauth","init_log","buildIntentAnalyzerPrompt","themeName","moduleNames","libraryModuleNames","themeContext","moduleList","n","i","libraryList","m","contextSection","INTENT_ANALYZER_SCHEMA","init_intent_analyzer","__esmMin","init_esm_shims","runIntentAnalyzer","userMessage","snapshot","engine","apiKey","model","onEvent","libraryModules","moduleNames","m","systemPrompt","buildIntentAnalyzerPrompt","messages","recentMessages","msg","content","result","callAgent","INTENT_ANALYZER_SCHEMA","log","isNew","plan","formatDecision","parts","init_intent_analyzer","__esmMin","init_esm_shims","init_engine_adapter","init_log","buildDesignSystemPrompt","themeName","brandAssets","parts","getArchitectDesignSummary","buildDesignSystemPromptBlocks","full","markerIdx","corePart","designGuide","blocks","dynamicParts","buildModulePlannerPrompt","sharedCss","guidesNeeded","getArchitectContentSummary","getArchitectHumanifySummary","DESIGN_SYSTEM_SCHEMA","MODULE_PLANNER_SCHEMA","init_page_architect","__esmMin","init_esm_shims","runPageArchitect","userMessage","plan","snapshot","engine","apiKey","model","onEvent","isAnthropicEngine","designPrompt","buildDesignSystemPrompt","designBlocks","buildDesignSystemPromptBlocks","designUserContent","designResult","callAgent","DESIGN_SYSTEM_SCHEMA","designSystem","log","sharedCss","vars","k","v","fontNotes","webFontPattern","requestedFonts","f","usedFonts","droppedFonts","decisionParts","plannerPrompt","buildModulePlannerPrompt","plannerUserContent","m","i","plannerResult","MODULE_PLANNER_SCHEMA","modulePlan","init_page_architect","__esmMin","init_esm_shims","init_engine_adapter","init_log","createConcurrencyLimiter","maxConcurrent","running","queue","fn","resolve","init_types","__esmMin","init_esm_shims","buildModuleDeveloperPrompt","themeName","sharedCss","guidesNeeded","brandAssets","parts","getHubspotRules","getConversionGuide","getModuleDevHumanifySummary","buildModuleDeveloperPromptBlocks","blocks","core","guideParts","dynamicParts","buildModuleUserMessage","userMessage","spec","existingCode","MODULE_DEVELOPER_SCHEMA","init_module_developer","__esmMin","init_esm_shims","init_prompts","runModuleDeveloper","userMessage","specs","sharedCss","themeName","engine","apiKey","model","concurrency","onEvent","guidesNeeded","brandAssets","isAnthropicEngine","systemPrompt","buildModuleDeveloperPrompt","systemBlocks","buildModuleDeveloperPromptBlocks","limit","createConcurrencyLimiter","total","promises","spec","index","lastError","attempt","log","module","generateSingleModule","err","r","retryCount","userContent","buildModuleUserMessage","result","callAgent","MODULE_DEVELOPER_SCHEMA","data","fieldsJson","metaJson","init_module_developer","__esmMin","init_esm_shims","init_engine_adapter","init_types","init_log","validateModules","modules","themeName","onEvent","mod","issues","fixedModule","validateAndFixJson","fixReservedFieldNames","fixDeprecatedFieldTypes","stripCdnImports","fixCssPrefix","fixHtmlClassPrefix","fixHublSyntax","ensureMetaFields","valid","i","log","jsonStr","moduleName","field","tryParseJSON","fieldsJson","fixed","css","importPattern","shouldSkipClass","name","SKIP_CLASSES","prefix","classPattern","unprefixedSet","match","className","selectorRe","escapeRegex","html","classAttrRe","anyFixed","fullMatch","classValue","classes","changed","newClasses","cls","s","tagPattern","tags","tag","isOpen","baseTag","stack","orphanClosers","found","j","t","unclosed","closers","metaJson","parsed","obj","init_validator","__esmMin","init_esm_shims","init_ai_parser","init_log","execSync","runAgentPipeline","userMessage","snapshot","engine","apiKey","model","concurrency","onEvent","libraryModules","startTime","effectiveConcurrency","isCLIEngine","bin","plan","runIntentAnalyzer","durationMs","blueprint","sharedCss","sharedJs","runPageArchitect","moduleSpecs","bpMod","newMod","modName","existing","m","generatedModules","failedModules","devResults","runModuleDeveloper","r","validationResults","validateModules","totalIssues","sum","autoFixed","i","log","issueDetails","finalModules","assembleModuleList","moduleOrder","buildModuleOrder","blueprintSet","missing","modulesGenerated","modulesUnchanged","validationIssues","assistantMessage","buildAssistantMessage","result","added","mod","name","reuse","libEntry","l","order","orderSet","footerIdx","n","insertions","a","b","ins","pos","moduleNames","seconds","parts","newNames","unfixed","fixed","valParts","init_pipeline","__esmMin","init_esm_shims","init_engine_adapter","init_intent_analyzer","init_page_architect","init_module_developer","init_validator","init_log","ai_handler_exports","__export","applyPipelineResult","handleAgenticGenerate","handleGenerate","handleGenerateStream","isGenerating","resolveAgenticEngine","setParseWarningCallback","shouldUseAgenticMode","execSync","cb","parseWarningCallback","generatingSessionId","finishResponse","fullResponse","current","getSession","log","addMessage","parseAndApplyModules","saveSession","userMessage","onChunk","onStatus","fileIds","session","fileContexts","getFileContexts","config","loadConfig","engine","detectDefaultEngine","apiKey","getApiKeyForEngine","streamWithAnthropicAPI","streamWithClaudeOAuth","streamWithOpenAIAPI","streamWithGeminiAPI","generateWithClaudeCode","generateWithCLI","hasValidOAuthToken","chunk","takeSnapshot","tpl","getActiveTemplate","modules","moduleOrder","engineType","isAgenticCapable","isCLIEngine","model","onEvent","capturedSessionId","concurrency","snapshot","library","getModuleLibrary","currentModuleNames","m","libraryModules","e","result","runAgentPipeline","pipelineMeta","updateModules","reorderModules","init_ai_handler","__esmMin","init_esm_shims","init_config","init_session","init_ai_parser","init_log","init_ai_engines","init_claude_oauth","init_upload_files","init_pipeline","design_extractor_exports","__export","collectThemeFiles","extractDesignContext","existsSync","readdirSync","readFileSync","join","spawn","getAnthropicSDK","_AnthropicCtor","safeRead","path","themePath","parts","totalChars","addSection","label","content","section","MAX_CONTENT_CHARS","themeJson","cssDir","f","modulesDir","dir","d","modPath","css","html","fields","getExtractionPrompt","_extractionPrompt","readFile","resolveAsset","spawnCLIForExtraction","bin","args","prompt","resolve","reject","env","child","stdout","stderr","err","code","onProgress","themeContent","systemPrompt","userMessage","config","loadConfig","engine","text","apiKey","getApiKeyForEngine","AnthropicSDK","block","getValidAccessToken","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","accessToken","resp","model","p","combinedPrompt","init_design_extractor","__esmMin","init_esm_shims","init_fs","init_config","brandvoice_extractor_exports","__export","extractBrandvoice","previewHtml","engine","apiKey","model","systemPrompt","result","callAgent","text","log","err","msg","init_brandvoice_extractor","__esmMin","init_esm_shims","init_engine_adapter","init_log","context_extractor_exports","__export","extractThemeContext","previewHtml","existingContext","engine","apiKey","model","systemPrompt","result","callAgent","text","log","err","msg","init_context_extractor","__esmMin","init_esm_shims","init_engine_adapter","init_log","init_esm_shims","init_esm_shims","Command","init_esm_shims","init_esm_shims","init_esm_shims","chalk","palette","noColor","hex","color","theme","init_fs","printBanner","v","theme","o","m","lines","line","getVersion","init_esm_shims","init_esm_shims","init_shell","init_config","init_claude_oauth","join","homedir","readFileSync","existsSync","readdirSync","whichCmd","detectNode","result","run","detectGit","detectHubSpotCLI","detectClaudeCode","claudeDir","authenticated","authDetail","files","f","detectDataCenter","portalId","configPath","config","accountIdx","keyIdx","keyMatch","detectHubSpotAuth","accounts","defaultName","defaultId","defaultMatch","lines","line","tableMatch","name","authType","detectGeminiCLI","adcPath","hasAdc","hasEnvKey","detectCodexCLI","hasKey","hasOAuth","authFile","detail","detectGitHubCLI","detectGitHubAuth","output","match","altMatch","hasAnthropicKey","nodeVersionOk","version","hsCliVersionOk","major","detectHubSpotAuthFromConfig","loadConfig","uploadMode","configAccounts","a","active","getActiveHubSpotAccount","DISABLED_CLI","detectEnvironment","node","git","hsUploadMode","hsInfo","hs","hsAuth","dc","gh","ghAuth","claudeOAuth","hasValidOAuthToken","getOAuthTokenInfo","enabledTools","claude","isCliToolEnabled","gemini","codex","keyStatus","configKey","envVars","maskApiKey","v","anthropicKey","openaiKey","geminiKey","available","init_claude_oauth","init_shell","init_config","init_api","init_esm_shims","p","handleCancel","value","theme","intro","title","outro","message","note","text","opts","result","confirm","select","spinner","s","msg","log","logSuccess","logWarn","logError","runPreflight","intro","node","detectNode","logError","nodeVersionOk","logSuccess","git","detectGit","config","loadConfig","useApi","portalId","portalName","pak","getHubSpotPak","acct","getActiveHubSpotAccount","logWarn","note","key","text","v","s","spinner","info","validatePak","addHubSpotAccount","err","hs","detectHubSpotCLI","confirm","run","auth","detectHubSpotAuth","runPassthrough","claude","detectClaudeCode","gemini","detectGeminiCLI","codex","detectCodexCLI","hasKey","hasAnthropicKey","engineLabels","hasOAuth","hasValidOAuthToken","aiEngine","lastUsed","available","a","b","select","theme","saveConfig","model","outro","init_esm_shims","init_shell","init_fs","readdirSync","statSync","join","basename","extname","findComponents","dir","components","searchDirs","join","searchDir","fileExists","files","readdirSync","file","filePath","statSync","ext","extname","name","basename","content","readFile","desc","describeComponent","hints","analyzeCSS","cssFiles","varCount","fonts","cssFile","varMatches","fontMatches","m","font","importMatches","detectInteractions","interactions","hooksDir","hooks","hook","componentDir","analyzeSource","input","sourceDir","wasCloned","repoName","result","run","hasTailwind","setupSource","intro","text","v","logSuccess","theme","s","spinner","logError","logWarn","componentList","c","i","cssInfo","fontInfo","jsInfo","note","confirm","outro","init_esm_shims","init_shell","init_fs","join","init_config","init_esm_shims","mkdirSync","writeFileSync","join","createThemeScaffold","themePath","themeName","themeJson","landingTemplate","baseLayout","init_fetcher","setupTheme","intro","choice","select","themeName","themePath","workspaceDir","join","ensureDir","text","v","s","spinner","config","loadConfig","pak","getHubSpotPak","run","logError","fetchTheme","err","theme","createThemeScaffold","baseHtmlPath","fileExists","logSuccess","baseHtml","readFile","patched","logWarn","cssInsertPoint","insertBefore","jsLine","lineEnd","nextLine","block","insertAt","writeFile","hsignorePath","hsignore","outro","init_esm_shims","join","readdirSync","rmSync","init_esm_shims","init_prompts","init_fs","spawn","join","basename","readdirSync","statSync","writeFileSync","BOILERPLATE_TEMPLATES","ClaudeCodeEngine","model","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","sourceComponents","existingModules","existingCss","join","existingJs","existingTemplates","prompt","stdout","stderr","progressInterval","resolve","reject","env","args","child","spawn","d","err","code","logPath","logContent","writeFileSync","basename","result","m","outputPreview","stderrPreview","newItems","currentCss","f","key","currentJs","currentModules","mod","counter","currentTemplates","of","getHubspotRules","cssDir","fileExists","file","readdirSync","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","statSync","moduleFiles","fj","mj","mh","mc","mjs","e","dir","matches","srcDir","count","fullPath","init_esm_shims","init_prompts","init_fs","Anthropic","join","basename","readdirSync","ClaudeAPIEngine","apiKey","opts","sourceDir","themePath","conversionGuide","onProgress","systemPrompt","buildSystemPrompt","dirName","pagePrefix","indexCss","tailwindConfig","cssContent","buildCssPrompt","cssPath","writeFile","hooksSource","interactiveSource","jsContent","buildJsPrompt","jsPath","components","modules","i","comp","moduleName","source","readFile","response","buildModulePrompt","parsed","mod","modDir","ensureDir","moduleNames","m","templateContent","buildTemplatePrompt","templatePath","system","user","b","dir","paths","p","fileExists","hooksDir","f","interactive","content","searchDirs","searchDir","count","init_esm_shims","init_prompts","init_fs","spawn","join","readdirSync","statSync","GeminiCLIEngine","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","prompt","resolve","reject","child","stdout","stderr","d","err","code","result","cssDir","fileExists","file","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","moduleFiles","fj","mj","mh","mc","mjs","init_esm_shims","init_prompts","init_fs","spawn","join","readdirSync","statSync","CodexCLIEngine","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","prompt","resolve","reject","child","stdout","stderr","d","err","code","result","cssDir","fileExists","file","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","moduleFiles","fj","mj","mh","mc","mjs","init_prompts","init_fs","createEngine","type","model","ClaudeCodeEngine","GeminiCLIEngine","CodexCLIEngine","ClaudeAPIEngine","runConversion","opts","intro","note","engine","conversionGuide","getConversionGuide","spinner","startTime","result","step","detail","logSuccess","elapsed","fixes","validateAndFix","fix","checklist","buildChecklist","lines","item","icon","severity","passed","c","criticalFailures","cosmeticFailures","logError","confirm","logWarn","logPath","join","fileExists","rmSync","outro","themePath","validateTemplates","validateModuleMeta","modulesDir","entry","readdirSync","fieldsPath","moduleName","content","readFile","changed","fields","jsonFixed","fixChoiceFields","fixLinkFields","writeFile","htmlPath","html","templatesDir","file","filePath","fixed","field","f","label","def","href","items","moduleCount","fieldsOk","m","allHaveHtml","missingCss","allHaveCss","hasStyleTab","templateAnnotated","hasTemplateType","hasAvailable","commentEnd","annotation","metaPath","meta","init_esm_shims","init_shell","join","basename","init_esm_shims","init_fs","join","readdirSync","rmSync","parseApiErrors","apiErrors","errors","err","msg","fixable","parseUploadErrors","output","fileMatch","fieldMatch","applyAutoFixes","themePath","fixes","fixTextareaFields","fixReservedNames","fixNowFunction","fixHubDbTemplates","fixLinkFieldDefaults","fixColorFieldDefaults","fixCdnImports","autoFixError","error","fixed","modulesDir","fileExists","entry","fieldsPath","content","readFile","writeFile","htmlPath","templatesDir","file","filePath","fields","fixLinkFieldsRecursive","cssDir","cleaned","cssPath","fixColorFieldsRecursive","field","f","def","colorVal","isValidHexColor","converted","convertToHex","color","hex3","rgba","r","g","b","hex","opacity","named","lower","href","init_config","init_esm_shims","init_api","readdirSync","join","relative","EXCLUDED_DIRS","walkDir","dir","files","entry","fullPath","parallelMap","items","concurrency","fn","index","worker","i","workers","uploadTheme","pak","themePath","themeName","opts","localFiles","total","uploaded","failed","errors","localPath","rel","remotePath","result","uploadFile","err","countUploadedFiles","output","runUpload","themePath","intro","themeName","basename","config","loadConfig","pak","getHubSpotPak","useApi","s","spinner","MAX_RETRIES","attempt","errors","uploadedCount","success","result","uploadTheme","parseApiErrors","run","join","fullOutput","parseUploadErrors","outro","logError","logWarn","confirm","anyFixed","error","autoFixError","logSuccess","deleteFile","init_esm_shims","execFileSync","rmSync","basename","init_fs","showNextSteps","opts","portalId","sourceDir","themePath","wasCloned","intro","host","detectDataCenter","note","theme","confirm","url","platform","execFileSync","logSuccess","log","dirsToClean","fileExists","basename","dir","rmSync","logWarn","outro","init_config","wizardCommand","printBanner","preflight","runPreflight","source","setupSource","saveConfig","themeInfo","setupTheme","runConversion","runUpload","showNextSteps","init_esm_shims","initCommand","printBanner","runPreflight","init_esm_shims","init_config","convertCommand","printBanner","config","loadConfig","logError","source","setupSource","themeInfo","setupTheme","runConversion","init_esm_shims","init_config","uploadCommand","printBanner","config","loadConfig","confirm","runUpload","path","text","v","init_esm_shims","init_config","doctorCommand","printBanner","intro","issues","node","detectNode","nodeVersionOk","logSuccess","logWarn","log","logError","git","detectGit","hs","detectHubSpotCLI","hsCliVersionOk","auth","detectHubSpotAuth","claude","detectClaudeCode","theme","gemini","detectGeminiCLI","codex","detectCodexCLI","config","loadConfig","anthropicKey","openaiKey","geminiKey","engineLabels","outro","init_esm_shims","join","existsSync","execFileSync","chalk","init_esm_shims","init_session","init_project_git","init_preview","init_ai_handler","init_config","createServer","readFileSync","existsSync","join","extname","createHash","WebSocketServer","init_esm_shims","spawn","jobs","_attachJobHandlers","child","job","timeout","d","code","err","startJobSafe","cmd","args","description","opts","id","startJob","command","parts","getJob","cleanupOldJobs","cutoff","startStreamingJob","emitChunk","chunk","listener","addJobListener","jobId","streamingJob","removeJobListener","init_route_helpers","init_fs","init_esm_shims","init_route_helpers","init_config","existsSync","readdirSync","rmSync","join","basename","homedir","execFileSync","init_fetcher","init_api","init_session","init_ai_handler","init_config","init_fs","_shellOpt","WORKSPACE_DIR","join","homedir","_themeListCache","THEME_LIST_TTL","getLocalThemes","themes","existsSync","entry","readdirSync","themeJson","moduleCount","modulesDir","e","handleSetupInfoRoute","res","session","getSession","env","detectEnvironment","hsInstalled","execFileSync","sessions","listSessions","b","localThemes","jsonResponse","handleSetupCreateRoute","req","readBody","body","isGenerating","name","themeName","themePath","ensureDir","rmSync","createThemeScaffold","createSession","saveSession","err","handleSetupFetchRoute","rawName","pak","getHubSpotPak","config","loadConfig","safeDirName","scanThemeFromDisk","fetchTheme","handleSetupOpenRoute","fullPath","basename","handleSetupResumeRoute","sessionId","loadSession","handleSetupApiKeyRoute","apiKey","saveConfig","handleSetupRemoteThemesRoute","folders","listRootFolders","checks","folder","folderPath","tjMeta","getMetadata","a","localNames","t","init_esm_shims","init_route_helpers","init_config","init_session","existsSync","readFileSync","appendFileSync","join","homedir","init_api","init_fs","modelCache","MODEL_CACHE_TTL","STATIC_MODELS","fetchAnthropicModels","apiKey","resp","m","fetchOpenAIModels","data","keep","a","b","fetchGeminiModels","getModelCatalog","config","loadConfig","catalog","jobs","anthropicKey","getApiKeyForEngine","models","openaiKey","geminiKey","handleSettingsStatusRoute","res","env","detectEnvironment","configPayload","sessionCount","listSessions","localThemeCount","getLocalThemes","version","getVersion","jsonResponse","handleSettingsEngineRoute","req","readBody","body","engine","model","configUpdate","saveConfig","err","handleSettingsApiKeyRoute","provider","autoSelectedEngine","handleSettingsInstallRoute","tool","installCommands","jobId","startJob","handleSettingsHsAuthRoute","parsed","uploadMode","validatePak","info","addHubSpotAccount","detectHubSpotCLI","startJobSafe","accounts","active","auth","detectHubSpotAuth","handleSettingsGhAuthRoute","detectGitHubCLI","detectGitHubAuth","handleSettingsHsSwitchRoute","portalId","action","removeHubSpotAccount","setActiveHubSpotAccount","safePortalId","handleSettingsGhLogoutRoute","handleSettingsCLIAuthRoute","cli","key","safeKey","profileLine","shellProfile","join","homedir","existsSync","readFileSync","appendFileSync","handleSettingsHsModeRoute","mode","handleSettingsCliToggleRoute","toolId","enabled","setCliToolEnabled","handleSettingsGenericRoute","allowedKeys","update","handleSettingsJobRoute","path","job","getJob","init_esm_shims","init_route_helpers","init_config","init_claude_oauth","handleClaudeOAuthSaveRoute","req","res","readBody","body","access_token","refresh_token","jsonResponse","saveInitialToken","config","loadConfig","saveConfig","err","handleClaudeOAuthStatusRoute","_req","authenticated","hasValidOAuthToken","info","getOAuthTokenInfo","handleClaudeOAuthLogoutRoute","clearOAuthTokens","init_esm_shims","init_route_helpers","init_session","existsSync","rmSync","join","handleThemesRoute","method","req","res","session","getSession","sessions","listSessions","a","b","jsonResponse","readBody","body","sessionId","deleteFiles","deleteSession","err","handleThemeSwitchRoute","loadSession","handleDeleteLocalThemeRoute","themeName","themePath","join","WORKSPACE_DIR","existsSync","rmSync","handleRenameThemeRoute","newName","sanitized","result","renameSession","init_esm_shims","init_route_helpers","init_log","init_config","init_session","init_fs","existsSync","readFileSync","rmSync","join","basename","execFileSync","_shellOpt","handleDashboardRoute","res","session","getSession","jsonResponse","library","getModuleLibrary","t","entry","handleDownloadZipRoute","themePath","themeName","parentDir","folderName","zipFileName","tmpZip","zipData","err","log","handleTemplatesRoute","method","req","readBody","body","pageType","label","addTemplate","saveSession","templateId","deleteModules","removeTemplate","handleTemplateActivateRoute","setActiveTemplate","getOrderedModules","m","handleTemplateRenameRoute","newLabel","renameTemplate","handleTemplateCloneRoute","cloneTemplate","handleModuleLibraryRoute","handleAddModuleToTemplateRoute","path","moduleName","e","modCopy","handleBrandAssetsRoute","type","content","filename","assetDir","ensureDir","writeFile","delFilename","filePath","saveBrandAsset","extractSingleAsset","sourcePath","extractDesignContext","resolveAgenticEngine","loadConfig","config","engine","apiKey","model","buildPreviewHtml","previewHtml","extractBrandvoice","extractThemeContext","handleDesignExtractRoute","parsed","types","results","extracted","i","r","handleReferenceImportRoute","source","localPath","pak","getHubSpotPak","cleanName","safeDirName","homedir","refDir","fetchTheme","styleguide","init_esm_shims","init_route_helpers","init_session","join","init_project_git","handleSessionRoute","method","res","session","getSession","jsonResponse","handleModulesRoute","req","ordered","getOrderedModules","m","readJsonBody","data","removeModule","detachModule","saveSession","handleCodeUpdateRoute","readBody","body","tpl","getActiveTemplate","writeModulesToDisk","moduleName","fileType","content","mod","err","handleReorderRoute","reorderModules","handleUploadRoute","fixes","applyAutoFixes","jobId","startStreamingJob","join","handleFieldRoute","fieldPath","value","updateFieldValue","handleImportRoute","url","analysis","analyzeSource","componentSummary","c","summary","handleHistoryRoute","isGitAvailable","templateId","commits","getTemplateHistory","getHistory","handleRollbackRoute","hash","addMessage","t","filePaths","n","result","rollbackTemplateToCommit","reloadActiveTemplateFromDisk","rollbackToCommit","reloadModulesFromDisk","init_upload_files","MIME_TYPES","startServer","opts","port","uiDir","server","createServer","req","res","handleRequest","wss","WebSocketServer","ws","handleWsConnection","resolve","reject","err","url","method","handleApiRoute","html","buildPreviewHtml","moduleName","buildModulePreviewHtml","serveThemeAsset","serveStatic","path","origin","handleSessionRoute","handleModulesRoute","handleReorderRoute","handleCodeUpdateRoute","handleUploadRoute","handleFileUploadRoute","jsonResponse","handleFieldRoute","handleImportRoute","handleSetupInfoRoute","handleSetupCreateRoute","handleSetupFetchRoute","handleSetupOpenRoute","handleSetupResumeRoute","handleSetupApiKeyRoute","handleSetupRemoteThemesRoute","handleSettingsStatusRoute","handleSettingsEngineRoute","handleSettingsApiKeyRoute","handleSettingsInstallRoute","handleSettingsHsAuthRoute","handleSettingsGhAuthRoute","handleSettingsHsSwitchRoute","handleSettingsGhLogoutRoute","handleSettingsCLIAuthRoute","handleSettingsHsModeRoute","handleSettingsCliToggleRoute","handleClaudeOAuthSaveRoute","handleClaudeOAuthStatusRoute","handleClaudeOAuthLogoutRoute","handleSettingsGenericRoute","getChangelog","handleThemesRoute","handleThemeSwitchRoute","handleDeleteLocalThemeRoute","handleRenameThemeRoute","handleHistoryRoute","handleRollbackRoute","handleDashboardRoute","handleTemplatesRoute","handleTemplateActivateRoute","handleTemplateRenameRoute","handleTemplateCloneRoute","handleModuleLibraryRoute","handleBrandAssetsRoute","handleDesignExtractRoute","handleReferenceImportRoute","handleDownloadZipRoute","handleSettingsJobRoute","handleAddModuleToTemplateRoute","data","msg","userMessage","addMessage","saveSession","fileIds","agenticCheck","shouldUseAgenticMode","pipelineSteps","pipelineModules","result","handleAgenticGenerate","event","moduleFiles","wsEvent","last","updateModules","reorderModules","getOrderedModules","applyPipelineResult","setParseWarningCallback","warning","handleGenerateStream","chunk","status","currentSession","getSession","writeModulesToDisk","activeTpl","getActiveTemplate","commitHash","filePaths","n","commitTemplateState","commitThemeState","m","sess","session","config","loadConfig","engine","apiKey","model","resolveAgenticEngine","previewHtml","extractThemeContext","themeContext","mkdirSync","writeFileSync","assetDir","join","existsSync","extractDesignContext","styleguide","fixes","applyAutoFixes","pak","getHubSpotPak","uploadTheme","completed","total","acct","getActiveHubSpotAccount","errors","parseApiErrors","e","jobId","startStreamingJob","chunkListener","addJobListener","pollInterval","job","getJob","removeJobListener","auth","detectHubSpotAuth","dc","detectDataCenter","parseUploadErrors","errorContext","fixPrompt","fixSession","fixHash","cfg","engineLabels","isGitAvailable","t","filename","filePath","ext","extname","contentType","buffer","readFileSync","serveStatic","pathname","uiDir","req","res","fullPath","join","existsSync","indexPath","content","readFileSync","ext","extname","contentType","MIME_TYPES","isHtml","buffer","etag","createHash","init_session","DEFAULT_PORT","vibeCommand","accent","chalk","dim","uiDir","resolveUiDir","port","close","startServer","url","execFileSync","resolve","saveSession","err","candidates","join","dir","existsSync","init_fs","buildProgram","program","Command","getVersion","vibeCommand","wizardCommand","initCommand","convertCommand","uploadCommand","doctorCommand","program","buildProgram","err"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/utils/fs.ts","../src/utils/shell.ts","../src/utils/config.ts","../src/utils/claude-oauth.ts","../src/hubspot/api.ts","../src/hubspot/fetcher.ts","../src/ai/prompts.ts","../src/server/session/types.ts","../src/server/project-git.ts","../src/server/session/state.ts","../src/server/session/templates.ts","../src/server/session/store.ts","../src/server/session/disk.ts","../src/server/session/index.ts","../src/server/session.ts","../src/hubl/renderer.ts","../src/server/preview.ts","../src/server/log.ts","../src/server/ai-parser.ts","../src/server/ai-prompts.ts","../src/server/ai-engines.ts","../src/server/route-helpers.ts","../src/server/routes/upload-files.ts","../src/server/agent/engine-adapter.ts","../src/server/agent/prompts/intent-analyzer.ts","../src/server/agent/stages/intent-analyzer.ts","../src/server/agent/prompts/page-architect.ts","../src/server/agent/stages/page-architect.ts","../src/server/agent/types.ts","../src/server/agent/prompts/module-developer.ts","../src/server/agent/stages/module-developer.ts","../src/server/agent/stages/validator.ts","../src/server/agent/pipeline.ts","../src/server/ai-handler.ts","../src/ai/design-extractor.ts","../src/server/agent/stages/brandvoice-extractor.ts","../src/server/agent/stages/context-extractor.ts","../src/index.ts","../src/cli/program.ts","../src/commands/wizard.ts","../src/cli/banner.ts","../src/cli/theme.ts","../src/wizard/preflight.ts","../src/utils/detect.ts","../src/prompts/prompter.ts","../src/wizard/source.ts","../src/wizard/theme-setup.ts","../src/hubspot/theme-scaffold.ts","../src/wizard/conversion.ts","../src/ai/claude-code.ts","../src/ai/claude-api.ts","../src/ai/gemini-cli.ts","../src/ai/codex-cli.ts","../src/wizard/uploader.ts","../src/server/auto-fix.ts","../src/hubspot/uploader.ts","../src/wizard/next-steps.ts","../src/commands/init.ts","../src/commands/convert.ts","../src/commands/upload.ts","../src/commands/doctor.ts","../src/commands/vibe.ts","../src/server/server.ts","../src/server/process-manager.ts","../src/server/routes/setup.ts","../src/server/routes/settings.ts","../src/server/routes/claude-oauth.ts","../src/server/routes/themes.ts","../src/server/routes/templates.ts","../src/server/routes/modules.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport function readFile(path: string): string {\n return readFileSync(path, \"utf-8\");\n}\n\nexport function writeFile(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, content, \"utf-8\");\n}\n\nexport function fileExists(path: string): boolean {\n return existsSync(path);\n}\n\nexport function ensureDir(path: string): void {\n mkdirSync(path, { recursive: true });\n}\n\nexport function resolveAsset(name: string): string {\n // In built package, assets/ is at the package root\n // During dev, it's relative to the project root\n const paths = [\n join(import.meta.dirname, \"../../assets\", name),\n join(import.meta.dirname, \"../assets\", name),\n join(process.cwd(), \"assets\", name),\n ];\n\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n\n throw new Error(`Asset not found: ${name}`);\n}\n\n/** Read version from package.json (cached after first call). */\nlet _version = \"\";\nexport function getVersion(): string {\n if (_version) return _version;\n const candidates = [\n join(import.meta.dirname, \"../../package.json\"),\n join(import.meta.dirname, \"../package.json\"),\n join(process.cwd(), \"package.json\"),\n ];\n for (const p of candidates) {\n if (existsSync(p)) {\n try {\n const pkg = JSON.parse(readFileSync(p, \"utf-8\"));\n if (pkg.name === \"vibespot\" && pkg.version) {\n _version = pkg.version;\n return _version;\n }\n } catch { /* try next */ }\n }\n }\n _version = \"dev\";\n return _version;\n}\n\n/** Read CHANGELOG.md content (cached after first call). */\nlet _changelog = \"\";\nexport function getChangelog(): string {\n if (_changelog) return _changelog;\n const candidates = [\n join(import.meta.dirname, \"../../CHANGELOG.md\"),\n join(import.meta.dirname, \"../CHANGELOG.md\"),\n join(process.cwd(), \"CHANGELOG.md\"),\n ];\n for (const p of candidates) {\n if (existsSync(p)) {\n try {\n _changelog = readFileSync(p, \"utf-8\");\n return _changelog;\n } catch { /* try next */ }\n }\n }\n return \"\";\n}\n","import { execSync, type ExecSyncOptions } from \"node:child_process\";\n\nexport interface ShellResult {\n stdout: string;\n stderr: string;\n success: boolean;\n}\n\nexport function run(\n command: string,\n options: ExecSyncOptions = {}\n): ShellResult {\n try {\n const stdout = execSync(command, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120_000,\n ...options,\n }).trim();\n return { stdout, stderr: \"\", success: true };\n } catch (err: unknown) {\n const e = err as { stdout?: Buffer | string; stderr?: Buffer | string };\n const stdout = (e.stdout ?? \"\").toString().trim();\n const stderr = (e.stderr ?? \"\").toString().trim();\n return { stdout, stderr, success: false };\n }\n}\n\nexport function runOrThrow(\n command: string,\n options: ExecSyncOptions = {}\n): string {\n return execSync(command, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 120_000,\n ...options,\n }).trim();\n}\n\nexport function runPassthrough(\n command: string,\n options: ExecSyncOptions = {}\n): boolean {\n try {\n execSync(command, {\n stdio: \"inherit\",\n timeout: 300_000,\n ...options,\n });\n return true;\n } catch {\n return false;\n }\n}\n","import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { chmodSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"./fs.js\";\n\nexport type AIEngineType =\n | \"claude-code\"\n | \"anthropic-api\"\n | \"claude-oauth\"\n | \"openai-api\"\n | \"gemini-cli\"\n | \"gemini-api\"\n | \"codex-cli\"\n // Legacy value — migrated to \"anthropic-api\" on load\n | \"api\";\n\nexport interface HubSpotAccountConfig {\n portalId: string;\n portalName: string;\n personalAccessKey: string;\n dataCenter: \"na1\" | \"eu1\";\n addedAt: string;\n}\n\nexport interface VibeSpotConfig {\n aiEngine?: AIEngineType;\n anthropicApiKey?: string;\n openaiApiKey?: string;\n geminiApiKey?: string;\n claudeCodeModel?: string;\n anthropicApiModel?: string;\n openaiApiModel?: string;\n lastThemePath?: string;\n lastSourcePath?: string;\n // HubSpot account management\n hubspotAccounts?: HubSpotAccountConfig[];\n activeHubSpotAccount?: string; // portalId\n hubspotUploadMode?: \"api\" | \"cli\"; // default \"api\"\n // CLI tool toggles — only enabled tools get checked on settings load\n enabledCLITools?: string[];\n // Agentic pipeline — multi-stage decomposed AI generation\n agenticMode?: boolean; // undefined = never prompted, true/false = user choice\n agenticConcurrency?: number; // max parallel module generation calls (default 3)\n}\n\nconst CONFIG_DIR = join(homedir(), \".vibespot\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.json\");\n\nexport function loadConfig(): VibeSpotConfig {\n if (!fileExists(CONFIG_PATH)) return {};\n\n try {\n const raw = JSON.parse(readFile(CONFIG_PATH));\n // Migrate legacy \"api\" engine type\n if (raw.aiEngine === \"api\") {\n raw.aiEngine = \"anthropic-api\";\n }\n return raw;\n } catch {\n return {};\n }\n}\n\n/**\n * Get the API key for a given engine, checking config first then env vars.\n */\nexport function getApiKeyForEngine(engine: AIEngineType, config?: VibeSpotConfig): string | undefined {\n const c = config || loadConfig();\n switch (engine) {\n case \"anthropic-api\":\n case \"api\":\n return c.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n case \"openai-api\":\n return c.openaiApiKey || process.env.OPENAI_API_KEY;\n case \"gemini-api\":\n return c.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY;\n default:\n return undefined;\n }\n}\n\n/**\n * Mask an API key for display (show first 7 + last 4 chars).\n */\nexport function maskApiKey(key: string): string {\n if (key.length <= 12) return \"***\";\n return key.slice(0, 7) + \"...\" + key.slice(-4);\n}\n\nexport function saveConfig(config: VibeSpotConfig): void {\n const existing = loadConfig();\n const merged = { ...existing, ...config };\n writeFile(CONFIG_PATH, JSON.stringify(merged, null, 2));\n // Restrict file permissions — config contains API keys\n if (process.platform !== \"win32\") {\n try { chmodSync(CONFIG_PATH, 0o600); } catch { /* ignore */ }\n }\n}\n\nexport function getConfigDir(): string {\n return CONFIG_DIR;\n}\n\n// ---------------------------------------------------------------------------\n// HubSpot account helpers\n// ---------------------------------------------------------------------------\n\nexport function getActiveHubSpotAccount(): HubSpotAccountConfig | null {\n const config = loadConfig();\n if (!config.hubspotAccounts?.length) return null;\n const activeId = config.activeHubSpotAccount;\n if (activeId) {\n const found = config.hubspotAccounts.find((a) => a.portalId === activeId);\n if (found) return found;\n }\n // Fallback to first account\n return config.hubspotAccounts[0] || null;\n}\n\nexport function addHubSpotAccount(\n pak: string,\n portalId: string,\n portalName: string,\n dataCenter: \"na1\" | \"eu1\",\n): void {\n const config = loadConfig();\n const accounts = config.hubspotAccounts || [];\n\n // Replace if same portalId exists\n const idx = accounts.findIndex((a) => a.portalId === portalId);\n const entry: HubSpotAccountConfig = {\n portalId,\n portalName,\n personalAccessKey: pak,\n dataCenter,\n addedAt: new Date().toISOString(),\n };\n\n if (idx >= 0) {\n accounts[idx] = entry;\n } else {\n accounts.push(entry);\n }\n\n saveConfig({\n hubspotAccounts: accounts,\n activeHubSpotAccount: portalId,\n } as VibeSpotConfig);\n}\n\nexport function removeHubSpotAccount(portalId: string): void {\n const config = loadConfig();\n const accounts = (config.hubspotAccounts || []).filter((a) => a.portalId !== portalId);\n const update: Partial<VibeSpotConfig> = { hubspotAccounts: accounts };\n\n // If we removed the active account, switch to the first remaining\n if (config.activeHubSpotAccount === portalId) {\n update.activeHubSpotAccount = accounts[0]?.portalId || undefined;\n }\n\n saveConfig(update as VibeSpotConfig);\n}\n\nexport function setActiveHubSpotAccount(portalId: string): void {\n saveConfig({ activeHubSpotAccount: portalId } as VibeSpotConfig);\n}\n\nexport function getHubSpotPak(): string | null {\n const acct = getActiveHubSpotAccount();\n return acct?.personalAccessKey || null;\n}\n\n// ---------------------------------------------------------------------------\n// CLI tool toggle helpers\n// ---------------------------------------------------------------------------\n\nexport function isCliToolEnabled(toolId: string): boolean {\n const config = loadConfig();\n return config.enabledCLITools?.includes(toolId) ?? false;\n}\n\nexport function setCliToolEnabled(toolId: string, enabled: boolean): void {\n const config = loadConfig();\n const tools = new Set(config.enabledCLITools || []);\n if (enabled) tools.add(toolId);\n else tools.delete(toolId);\n saveConfig({ enabledCLITools: [...tools] } as VibeSpotConfig);\n}\n","/**\n * Claude OAuth token manager — stores and auto-refreshes OAuth tokens.\n *\n * Users obtain a token via `claude setup-token` (Claude Code CLI) and paste it\n * into the vibespot settings UI. Tokens are stored in ~/.vibespot/claude-oauth.json.\n *\n * The access token (sk-ant-oat01-...) has an 8-hour lifetime and is auto-refreshed\n * when within 5 minutes of expiry.\n */\n\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { chmodSync, unlinkSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"./fs.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst CLIENT_ID = \"9d1c250a-e61b-44d9-88ed-5944d1962f5e\";\nconst TOKEN_ENDPOINT = \"https://console.anthropic.com/v1/oauth/token\";\nconst TOKEN_FILE = join(homedir(), \".vibespot\", \"claude-oauth.json\");\nconst REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes\n\n// ---------------------------------------------------------------------------\n// OAuth headers required for OAT tokens on every API call\n// ---------------------------------------------------------------------------\n\nexport const OAUTH_EXTRA_HEADERS: Record<string, string> = {\n \"user-agent\": \"claude-cli/2.1.75\",\n \"x-app\": \"cli\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n};\n\n/** System prompt prefix required for OAT tokens — must be a separate block. */\nexport const OAUTH_SYSTEM_PREFIX = \"You are Claude Code, Anthropic's official CLI for Claude.\";\n\n// ---------------------------------------------------------------------------\n// Token types\n// ---------------------------------------------------------------------------\n\ninterface OAuthTokens {\n access_token: string;\n refresh_token: string;\n expires_at: number; // Unix ms\n}\n\n// ---------------------------------------------------------------------------\n// Token persistence\n// ---------------------------------------------------------------------------\n\nfunction loadTokens(): OAuthTokens | null {\n if (!fileExists(TOKEN_FILE)) return null;\n try {\n return JSON.parse(readFile(TOKEN_FILE));\n } catch {\n return null;\n }\n}\n\nfunction saveTokens(tokens: OAuthTokens): void {\n writeFile(TOKEN_FILE, JSON.stringify(tokens, null, 2));\n if (process.platform !== \"win32\") {\n try { chmodSync(TOKEN_FILE, 0o600); } catch { /* ignore */ }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Token refresh (with concurrency guard)\n// ---------------------------------------------------------------------------\n\nlet refreshPromise: Promise<void> | null = null;\n\nasync function refreshAccessToken(refresh_token: string): Promise<void> {\n const resp = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n grant_type: \"refresh_token\",\n refresh_token,\n client_id: CLIENT_ID,\n }),\n });\n\n if (!resp.ok) {\n clearOAuthTokens();\n throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n }\n\n const data = await resp.json();\n saveTokens({\n access_token: data.access_token,\n refresh_token: data.refresh_token || refresh_token,\n expires_at: Date.now() + (data.expires_in || 28800) * 1000,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Save an initial OAuth token (from `claude setup-token` or manual paste).\n */\nexport function saveInitialToken(accessToken: string, refreshToken: string = \"\"): void {\n saveTokens({\n access_token: accessToken,\n refresh_token: refreshToken,\n // Default 8h expiry — will be corrected on first refresh\n expires_at: Date.now() + 28800 * 1000,\n });\n}\n\n/**\n * Check if a valid (non-expired) OAuth token exists.\n */\nexport function hasValidOAuthToken(): boolean {\n const tokens = loadTokens();\n if (!tokens) return false;\n return tokens.expires_at > Date.now();\n}\n\n/**\n * Get a valid access token, auto-refreshing if needed.\n * Returns null if no token is stored or refresh fails.\n */\nexport async function getValidAccessToken(): Promise<string | null> {\n const tokens = loadTokens();\n if (!tokens) return null;\n\n // Still valid and not near expiry\n if (tokens.expires_at - Date.now() > REFRESH_BUFFER_MS) {\n return tokens.access_token;\n }\n\n // Need to refresh — use concurrency guard\n if (tokens.refresh_token) {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken(tokens.refresh_token).finally(() => {\n refreshPromise = null;\n });\n }\n\n try {\n await refreshPromise;\n } catch {\n return null;\n }\n\n const refreshed = loadTokens();\n return refreshed?.access_token ?? null;\n }\n\n // No refresh token — return current token even if expiring\n return tokens.access_token;\n}\n\n/**\n * Get token info for status display.\n */\nexport function getOAuthTokenInfo(): { expiresAt: string } | null {\n const tokens = loadTokens();\n if (!tokens) return null;\n return { expiresAt: new Date(tokens.expires_at).toISOString() };\n}\n\n/**\n * Clear stored OAuth tokens (logout).\n */\nexport function clearOAuthTokens(): void {\n if (fileExists(TOKEN_FILE)) {\n try { unlinkSync(TOKEN_FILE); } catch { /* ignore */ }\n }\n}\n","/**\n * HubSpot CMS Source Code API v3 client.\n * Direct HTTP calls — no HubSpot CLI dependency.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { basename } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface HubSpotApiError {\n status: number;\n message: string;\n category?: string;\n detail?: string;\n}\n\nexport interface UploadResult {\n success: boolean;\n path: string;\n error?: HubSpotApiError;\n}\n\nexport interface FileMetadata {\n id: string;\n name: string;\n folder: boolean;\n path: string;\n createdAt?: number;\n updatedAt?: number;\n children?: (FileMetadata | string)[];\n}\n\nexport interface AccountInfo {\n portalId: string;\n portalName: string;\n dataCenter: \"na1\" | \"eu1\";\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** HubSpot API base URL — same for all regions. Cloudflare routes by token. */\nconst BASE_URL = \"https://api.hubapi.com\";\nconst MAX_RETRIES = 3;\nconst RETRY_DELAY_MS = 1000;\n/** Refresh access token 5 minutes before expiry. */\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Token exchange — PAKs are exchanged for short-lived access tokens\n// ---------------------------------------------------------------------------\n\ninterface CachedToken {\n accessToken: string;\n expiresAt: number;\n hubId: number;\n hubName: string;\n}\n\nconst tokenCache = new Map<string, CachedToken>();\n\n/** Exchange a PAK for a short-lived OAuth access token via HubSpot's localdevauth endpoint. */\nasync function exchangePakForToken(pak: string): Promise<CachedToken> {\n const cached = tokenCache.get(pak);\n if (cached && cached.expiresAt - Date.now() > TOKEN_REFRESH_BUFFER_MS) {\n return cached;\n }\n\n const resp = await fetch(`${BASE_URL}/localdevauth/v1/auth/refresh`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ encodedOAuthRefreshToken: pak }),\n });\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n resp.status === 401 || resp.status === 403\n ? \"Invalid or expired Personal Access Key\"\n : `Token exchange failed (${resp.status}): ${body.slice(0, 200)}`,\n );\n }\n\n const data = await resp.json() as Record<string, unknown>;\n const token: CachedToken = {\n accessToken: data.oauthAccessToken as string,\n expiresAt: data.expiresAtMillis as number,\n hubId: data.hubId as number,\n hubName: (data.hubName as string) || \"\",\n };\n\n tokenCache.set(pak, token);\n return token;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function authHeaders(pak: string): Promise<Record<string, string>> {\n const { accessToken } = await exchangePakForToken(pak);\n return {\n Authorization: `Bearer ${accessToken}`,\n };\n}\n\n/** Encode a remote path for use in HubSpot API URLs — encode each segment but keep slashes. */\nfunction encodePath(remotePath: string): string {\n return remotePath.replace(/^\\/+/, \"\").split(\"/\").filter(Boolean).map(encodeURIComponent).join(\"/\");\n}\n\n/** Detect data center from PAK prefix. */\nexport function detectDataCenterFromPak(pak: string): \"na1\" | \"eu1\" {\n // Modern PAK format: pat-eu1-... or pat-na1-...\n if (pak.startsWith(\"pat-eu1-\")) return \"eu1\";\n if (pak.startsWith(\"pat-na1-\")) return \"na1\";\n // Legacy base64 prefix check\n if (pak.startsWith(\"CiRldTE\")) return \"eu1\";\n return \"na1\";\n}\n\n/** Sleep helper for retry backoff. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Parse error response body into HubSpotApiError. */\nasync function parseErrorResponse(resp: Response, fallbackPath?: string): Promise<HubSpotApiError> {\n let message = `HTTP ${resp.status}`;\n let category: string | undefined;\n let detail: string | undefined;\n\n try {\n const body = await resp.json() as Record<string, unknown>;\n if (body.message && typeof body.message === \"string\") message = body.message;\n if (body.category && typeof body.category === \"string\") category = body.category;\n if (body.errors && Array.isArray(body.errors) && body.errors.length > 0) {\n const firstError = body.errors[0] as Record<string, unknown>;\n detail = firstError.message as string || JSON.stringify(firstError);\n }\n } catch {\n try {\n const text = await resp.text();\n if (text) message = text.slice(0, 500);\n } catch { /* ignore */ }\n }\n\n return {\n status: resp.status,\n message: fallbackPath ? `${message} (${fallbackPath})` : message,\n category,\n detail,\n };\n}\n\n/** Make a fetch request with retry logic for rate limits and transient errors. */\nasync function fetchWithRetry(\n url: string,\n options: RequestInit,\n retries = MAX_RETRIES,\n): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n const resp = await fetch(url, options);\n\n if (resp.status === 429 || (resp.status >= 500 && attempt < retries)) {\n const delay = RETRY_DELAY_MS * Math.pow(2, attempt);\n await sleep(delay);\n continue;\n }\n\n return resp;\n }\n\n // Should never reach here, but TypeScript needs it\n return fetch(url, options);\n}\n\n// ---------------------------------------------------------------------------\n// API Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a Personal Access Key by fetching account info.\n * Returns portal ID, name, and data center.\n */\nexport async function validatePak(pak: string): Promise<AccountInfo> {\n // Exchange PAK for access token — this validates the key\n const token = await exchangePakForToken(pak);\n\n // Use the access token to get account details\n const url = `${BASE_URL}/account-info/v3/details`;\n const resp = await fetchWithRetry(url, { headers: await authHeaders(pak) });\n\n if (!resp.ok) {\n const err = await parseErrorResponse(resp);\n throw new Error(`Failed to get account info: ${err.message}`);\n }\n\n const data = await resp.json() as Record<string, unknown>;\n const portalId = String(data.portalId || token.hubId || \"\");\n const portalName = token.hubName || (data.uiDomain as string) || portalId;\n\n return {\n portalId,\n portalName,\n dataCenter: detectDataCenterFromPak(pak),\n };\n}\n\n/**\n * Upload a single file to the HubSpot CMS Design Manager.\n * Uses PUT /cms/v3/source-code/{env}/content/{path} with multipart form-data.\n */\nexport async function uploadFile(\n pak: string,\n remotePath: string,\n localFilePath: string,\n): Promise<UploadResult> {\n const fileContent = readFileSync(localFilePath);\n const fileName = basename(localFilePath);\n\n // Build multipart form data\n const formData = new FormData();\n const blob = new Blob([fileContent]);\n formData.append(\"file\", blob, fileName);\n\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"PUT\",\n headers: await authHeaders(pak),\n body: formData,\n });\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n return { success: false, path: remotePath, error };\n }\n\n return { success: true, path: remotePath };\n}\n\n/**\n * Delete a file or directory from the HubSpot CMS Design Manager.\n */\nexport async function deleteFile(pak: string, remotePath: string): Promise<void> {\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"DELETE\",\n headers: await authHeaders(pak),\n });\n\n if (!resp.ok && resp.status !== 404) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to delete ${remotePath}: ${error.message}`);\n }\n}\n\n/**\n * Download a file from the HubSpot CMS Design Manager.\n */\nexport async function downloadFile(pak: string, remotePath: string): Promise<Buffer> {\n const url = `${BASE_URL}/cms/v3/source-code/published/content/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"GET\",\n headers: await authHeaders(pak),\n });\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to download ${remotePath}: ${error.message}`);\n }\n\n const arrayBuffer = await resp.arrayBuffer();\n return Buffer.from(arrayBuffer);\n}\n\n/**\n * Get metadata for a file or directory in the HubSpot CMS Design Manager.\n * Returns null if the path doesn't exist (404).\n */\nexport async function getMetadata(pak: string, remotePath: string): Promise<FileMetadata | null> {\n const url = `${BASE_URL}/cms/v3/source-code/published/metadata/${encodePath(remotePath)}`;\n\n const resp = await fetchWithRetry(url, {\n method: \"GET\",\n headers: await authHeaders(pak),\n });\n\n if (resp.status === 404) return null;\n\n if (!resp.ok) {\n const error = await parseErrorResponse(resp, remotePath);\n throw new Error(`Failed to get metadata for ${remotePath}: ${error.message}`);\n }\n\n return (await resp.json()) as FileMetadata;\n}\n\n/**\n * List children of a directory in the HubSpot CMS Design Manager.\n * Returns empty array if the directory doesn't exist.\n */\nexport async function listDirectory(pak: string, remotePath: string): Promise<FileMetadata[]> {\n const meta = await getMetadata(pak, remotePath);\n if (!meta) return [];\n if (!meta.folder) return [meta];\n return meta.children || [];\n}\n\n/**\n * List root-level folders in the HubSpot CMS Design Manager.\n * Tries multiple API approaches since the metadata endpoint doesn't support root listing.\n */\nexport async function listRootFolders(pak: string): Promise<FileMetadata[]> {\n const headers = await authHeaders(pak);\n\n // Try different URL patterns for root metadata\n const urlVariants = [\n `${BASE_URL}/cms/v3/source-code/published/metadata`, // no trailing slash\n `${BASE_URL}/cms/v3/source-code/published/metadata/`, // trailing slash\n `${BASE_URL}/designmanager/v1/portals/content/listing`, // Design Manager API\n ];\n\n for (const url of urlVariants) {\n try {\n const resp = await fetch(url, { method: \"GET\", headers });\n if (resp.ok) {\n const data = await resp.json() as Record<string, unknown>;\n // Could be { children: [...] } or { objects: [...] } or direct array\n const children = (data.children || data.objects || (Array.isArray(data) ? data : null)) as FileMetadata[] | null;\n if (children && children.length > 0) {\n return children.filter((c: FileMetadata) => c.folder);\n }\n }\n } catch { /* try next */ }\n }\n\n return [];\n}\n","/**\n * Theme fetcher — downloads an existing theme from HubSpot\n * via the CMS Source Code API v3. Replaces `hs cms fetch`.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { getMetadata, downloadFile, type FileMetadata } from \"./api.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchThemeOptions {\n onFile?: (path: string) => void;\n concurrency?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Recursive directory listing\n// ---------------------------------------------------------------------------\n\n/** Recursively list all files under a remote path. */\nasync function listFilesRecursive(pak: string, remotePath: string): Promise<string[]> {\n const meta = await getMetadata(pak, remotePath);\n if (!meta) return [];\n\n if (!meta.folder) return [meta.path || remotePath];\n\n const files: string[] = [];\n const rawChildren = meta.children || [];\n\n for (const child of rawChildren) {\n // HubSpot API returns children as plain strings (folder/file names) or as objects\n const childName = typeof child === \"string\" ? child : (child as FileMetadata).name;\n if (!childName) continue;\n const childPath = `${remotePath}/${childName}`;\n\n if (typeof child === \"string\") {\n // String children — need to fetch metadata to determine if folder or file\n files.push(...(await listFilesRecursive(pak, childPath)));\n } else {\n const childMeta = child as FileMetadata;\n if (childMeta.folder) {\n files.push(...(await listFilesRecursive(pak, childMeta.path || childPath)));\n } else {\n files.push(childMeta.path || childPath);\n }\n }\n }\n\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// Parallel download\n// ---------------------------------------------------------------------------\n\nasync function parallelMap<T>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const i = index++;\n await fn(items[i]);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n}\n\n// ---------------------------------------------------------------------------\n// Main fetch function\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch (download) an entire theme from HubSpot to a local directory.\n */\nexport async function fetchTheme(\n pak: string,\n themeName: string,\n targetPath: string,\n opts: FetchThemeOptions = {},\n): Promise<void> {\n const concurrency = opts.concurrency ?? 5;\n\n // List all files in the remote theme\n const remoteFiles = await listFilesRecursive(pak, themeName);\n\n if (remoteFiles.length === 0) {\n throw new Error(`Theme \"${themeName}\" not found on HubSpot or is empty`);\n }\n\n // Download all files in parallel\n mkdirSync(targetPath, { recursive: true });\n\n await parallelMap(remoteFiles, concurrency, async (remotePath) => {\n // Strip theme name prefix to get relative path\n const relativePath = remotePath.startsWith(themeName + \"/\")\n ? remotePath.slice(themeName.length + 1)\n : remotePath;\n\n const localPath = join(targetPath, relativePath);\n\n // Ensure directory exists\n mkdirSync(dirname(localPath), { recursive: true });\n\n // Download and write\n const content = await downloadFile(pak, remotePath);\n writeFileSync(localPath, content);\n\n opts.onFile?.(relativePath);\n });\n}\n","import { readFile, resolveAsset } from \"../utils/fs.js\";\n\n// In-memory cache for guide files (~90KB total, read once)\nconst guideCache = new Map<string, string>();\n\nfunction cachedAsset(name: string): string {\n let val = guideCache.get(name);\n if (val !== undefined) return val;\n try { val = readFile(resolveAsset(name)); } catch { val = \"\"; }\n guideCache.set(name, val);\n return val;\n}\n\nexport function getConversionGuide(): string {\n return cachedAsset(\"conversion-guide.md\") || \"Conversion guide not found. Using built-in rules.\";\n}\n\nexport function getDesignGuide(): string {\n return cachedAsset(\"design-guide.md\");\n}\n\nexport function getContentGuide(): string {\n return cachedAsset(\"content-guide.md\");\n}\n\nexport function getHubspotRules(): string {\n return cachedAsset(\"hubspot-rules.md\");\n}\n\nexport function getHumanifyGuide(): string {\n return cachedAsset(\"humanify-guide.md\");\n}\n\n/**\n * Extract page-type-specific section from page-types.md.\n * Returns only the relevant section for the given page type.\n */\nexport function getPageTypeGuide(pageType: string): string {\n const fullGuide = cachedAsset(\"page-types.md\");\n if (!fullGuide) return \"\";\n\n // Map page type to section header\n const sectionHeaders: Record<string, string> = {\n landing_page: \"## Landing Page\",\n blog_post: \"## Blog Post\",\n website_page: \"## Website Page\",\n module_only: \"## Module Only\",\n };\n\n const header = sectionHeaders[pageType];\n if (!header) return \"\";\n\n const startIdx = fullGuide.indexOf(header);\n if (startIdx < 0) return \"\";\n\n // Find the next section (## at start of line) after this one\n const afterHeader = fullGuide.indexOf(\"\\n## \", startIdx + header.length);\n const section = afterHeader >= 0\n ? fullGuide.slice(startIdx, afterHeader).trim()\n : fullGuide.slice(startIdx).trim();\n\n return section;\n}\n\nexport function buildSystemPrompt(conversionGuide: string): string {\n return `You are a HubSpot CMS expert converting React/Tailwind pages to native HubSpot modules.\n\n## Rules\nFollow the conversion guide below EXACTLY. Key rules:\n- Use \"type\": \"text\" (NEVER \"textarea\")\n- NEVER use \"name\": \"name\" (it's reserved) — use alternatives like \"item_name\"\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\" on the group\n- All CSS classes must use a unique prefix to avoid theme conflicts\n- Every dnd_section needs padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true\n- Use template_css and template_js variables (resolved in base.html, not child templates)\n- Add .body-wrapper:has(.my-page) CSS fix + JS fallback for body background\n- Add scoped overrides with !important for headings, paragraphs, links to defeat theme-overrides.css\n- Use IntersectionObserver for scroll animations (add .visible class)\n- Convert Tailwind utilities to vanilla CSS with BEM naming\n- Convert React hooks to vanilla JS (no React, no npm packages)\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide\n${conversionGuide}`;\n}\n\nexport function buildAnalyzePrompt(componentList: string): string {\n return `Analyze these React components and create a module map.\n\nFor each component, return a JSON object with:\n- name: HubSpot module name (e.g., \"My Hero\")\n- description: Brief description of the section\n- hasRepeaters: true if it contains .map() or array iteration\n- hasInteractivity: true if it has JS-driven behavior (carousel, accordion, etc.)\n\nComponents:\n${componentList}\n\nReturn ONLY valid JSON array, no markdown fences.`;\n}\n\nexport function buildModulePrompt(\n componentSource: string,\n moduleName: string,\n cssVars: string\n): string {\n return `Convert this React component to a HubSpot module named \"${moduleName}\".\n\nReturn a JSON object with these keys:\n- fieldsJson: complete fields.json content (as JSON string)\n- metaJson: complete meta.json content (as JSON string)\n- moduleHtml: complete module.html content (HubL template)\n- moduleCss: complete module.css content (vanilla CSS)\n- moduleJs: module.js content if interactive behavior needed, or null\n\nDesign system CSS variables available:\n${cssVars}\n\nReact component source:\n${componentSource}\n\nReturn ONLY valid JSON, no markdown fences.`;\n}\n\nexport function buildCssPrompt(\n indexCss: string,\n tailwindConfig: string,\n pagePrefix: string\n): string {\n return `Create a shared CSS file for a HubSpot CMS landing page.\n\nExtract the design system from the source CSS and Tailwind config below.\nUse the class prefix \".${pagePrefix}-\" for all custom classes.\n\nRequirements:\n- CSS custom properties for all colors, spacing\n- Page wrapper styles (.${pagePrefix}-page) with !important font/color\n- .body-wrapper:has(.${pagePrefix}-page) background fix\n- .body-wrapper.${pagePrefix}-page-active JS fallback\n- Scoped overrides: .${pagePrefix}-page h1-h6, p, a with !important\n- .dnd-section padding: 0 !important and .row-fluid max-width: 100%\n- Form overrides for dark themes\n- Utility classes (container, section, grid, glass)\n- Scroll animation classes (.${pagePrefix}-scroll-animate / .visible)\n- Mobile breakpoint at 767px\n- Responsive grid fallbacks\n\nSource CSS:\n${indexCss}\n\nTailwind config:\n${tailwindConfig}\n\nReturn ONLY the CSS content, no markdown fences.`;\n}\n\nexport function buildJsPrompt(\n hooksSource: string,\n interactiveComponents: string,\n pagePrefix: string\n): string {\n return `Create a shared vanilla JS file for a HubSpot CMS landing page.\n\nConvert the React hooks and interactive components below to plain JavaScript.\nUse the class prefix \"${pagePrefix}-\" to match the CSS.\n\nRequirements:\n- IIFE wrapper with \"use strict\"\n- initBodyWrapper: add \"${pagePrefix}-page-active\" class to .body-wrapper\n- initScrollAnimations: IntersectionObserver for .${pagePrefix}-scroll-animate → .visible\n- Convert any carousels, accordions, typing animations to vanilla JS\n- DOMContentLoaded / readyState check\n\nReact hooks source:\n${hooksSource}\n\nInteractive component sources:\n${interactiveComponents}\n\nReturn ONLY the JavaScript content, no markdown fences.`;\n}\n\nexport function buildTemplatePrompt(\n moduleNames: string[],\n themeName: string,\n pagePrefix: string\n): string {\n return `Create a HubSpot page template that assembles these modules:\n\n${moduleNames.map((n, i) => `${i + 1}. ${n}.module`).join(\"\\n\")}\n\nTemplate requirements:\n- templateType: page, isAvailableForNewContent: true\n- extends \"./layouts/base.html\"\n- set template_css = \"../../css/${pagePrefix}-theme.css\"\n- set template_js = \"../../js/${pagePrefix}-animations.js\"\n- Empty header and footer blocks (modules handle them)\n- Wrap content in <div class=\"${pagePrefix}-page\">\n- Each module in its own dnd_section with padding zeroed and full_width=true\n- dnd_area label: \"${themeName} Landing Page\"\n\nReturn ONLY the template HTML content, no markdown fences.`;\n}\n","/**\n * Type definitions for the vibe coding session system.\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PipelineMetadata {\n steps: { step: string; label: string; decisions?: string[] }[];\n modules: { name: string; status: \"complete\" | \"failed\" }[];\n stats: { modulesGenerated: number; modulesUnchanged: number; modulesFailed: number; durationMs: number };\n}\n\nexport interface ChatMessage {\n role: \"user\" | \"assistant\";\n content: string;\n timestamp: number;\n pipeline?: PipelineMetadata;\n}\n\nexport type PageType = \"landing_page\" | \"blog_post\" | \"website_page\" | \"module_only\";\n\nexport interface TemplateEntry {\n id: string; // e.g. \"lp-main\", \"blog-post\"\n label: string; // \"Main Landing Page\"\n pageType: PageType;\n templateFile: string; // \"templates/lp-main.html\"\n modules: ModuleFiles[];\n moduleOrder: string[];\n sharedCss: string;\n sharedJs: string;\n template: string; // HubL template content\n messages: ChatMessage[]; // per-template chat history\n}\n\nexport interface SessionAsset {\n id: string;\n filename: string;\n originalName: string;\n type: \"image\" | \"document\";\n usage: \"asset\" | \"context\";\n mimeType: string;\n size: number;\n addedAt: string;\n extractedText?: string;\n}\n\nexport interface VibeSession {\n id: string;\n themePath: string;\n themeName: string;\n\n // Multi-template support\n templates: TemplateEntry[];\n activeTemplateId: string;\n brandAssets?: {\n styleguide?: string;\n brandvoice?: string;\n humanify?: boolean;\n themeContext?: string;\n };\n assets?: SessionAsset[];\n\n // Legacy flat fields — kept for backward compat, redirected to active template\n messages: ChatMessage[];\n modules: ModuleFiles[];\n sharedCss: string;\n sharedJs: string;\n template: string;\n moduleOrder: string[];\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface SessionIndexEntry {\n id: string;\n themeName: string;\n updatedAt: number;\n moduleCount: number;\n templateCount: number;\n}\n\nexport interface SessionSnapshot {\n modules: ReadonlyArray<Readonly<ModuleFiles>>;\n moduleOrder: ReadonlyArray<string>;\n sharedCss: string;\n sharedJs: string;\n messages: ReadonlyArray<Readonly<ChatMessage>>;\n themeName: string;\n themePath: string;\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string };\n}\n\nexport interface FieldDef {\n name: string;\n default?: unknown;\n children?: FieldDef[];\n}\n","/**\n * Per-project local git — auto-commits, version history, rollback.\n * All operations are local-only. Git is optional; if unavailable,\n * every function degrades gracefully (returns null / false / []).\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\n\nexport interface GitCommitInfo {\n hash: string; // short hash (7 chars)\n fullHash: string; // full SHA\n message: string; // first line of commit message\n timestamp: number; // unix epoch ms\n date: string; // ISO string for display\n}\n\n// ---------------------------------------------------------------------------\n// Git availability (cached)\n// ---------------------------------------------------------------------------\n\n/** Validate a git hash to prevent shell injection — must be hex only. */\nfunction isValidHash(hash: string): boolean {\n return /^[0-9a-f]{4,40}$/i.test(hash);\n}\n\nlet gitAvailableCache: boolean | null = null;\n\nexport function isGitAvailable(): boolean {\n if (gitAvailableCache !== null) return gitAvailableCache;\n const result = run(\"git --version\");\n gitAvailableCache = result.success;\n return gitAvailableCache;\n}\n\n// ---------------------------------------------------------------------------\n// Repo initialization\n// ---------------------------------------------------------------------------\n\n/**\n * Ensure a git repo exists in the theme directory.\n * Creates .gitignore, .vibespot/ dir, and initial commit if needed.\n * Safe to call multiple times (idempotent).\n */\nexport function ensureGitRepo(themePath: string): boolean {\n if (!isGitAvailable()) return false;\n\n // Already initialized\n if (existsSync(join(themePath, \".git\"))) {\n ensureVibeSpotDir(themePath);\n return true;\n }\n\n // Init repo\n const init = run(\"git init\", { cwd: themePath });\n if (!init.success) {\n console.warn(`[project-git] git init failed in ${themePath}: ${init.stderr}`);\n return false;\n }\n\n // Create .gitignore\n writeGitIgnore(themePath);\n\n // Create .vibespot/ dir for chat persistence\n ensureVibeSpotDir(themePath);\n\n // Initial commit\n run(\"git add -A\", { cwd: themePath });\n run('git commit -m \"Initial theme\"', { cwd: themePath });\n\n return true;\n}\n\nfunction ensureVibeSpotDir(themePath: string): void {\n const dir = join(themePath, \".vibespot\");\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n}\n\nfunction writeGitIgnore(themePath: string): void {\n const gitignorePath = join(themePath, \".gitignore\");\n const lines = [\".vibespot/\", \"node_modules/\", \"\"];\n writeFileSync(gitignorePath, lines.join(\"\\n\"), \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Committing\n// ---------------------------------------------------------------------------\n\n/**\n * Stage all changes and commit with the given message.\n * Returns the short commit hash, or null if nothing to commit or error.\n */\nexport function commitThemeState(themePath: string, message: string): string | null {\n if (!isGitAvailable()) return null;\n if (!existsSync(join(themePath, \".git\"))) return null;\n\n // Stage everything\n run(\"git add -A\", { cwd: themePath });\n\n // Check if there are staged changes\n const diff = run(\"git diff --cached --quiet\", { cwd: themePath });\n if (diff.success) return null; // exit 0 = nothing staged\n\n // Truncate message to 72 chars\n const truncated = message.length > 72\n ? message.slice(0, 69) + \"...\"\n : message;\n\n // Commit (use -- to prevent message from being interpreted as flags)\n const commitResult = run(`git commit -m \"${truncated.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n if (!commitResult.success) {\n console.warn(`[project-git] commit failed: ${commitResult.stderr}`);\n return null;\n }\n\n // Get short hash\n const hashResult = run(\"git rev-parse --short HEAD\", { cwd: themePath });\n return hashResult.success ? hashResult.stdout : null;\n}\n\n/**\n * Stage only specific files and commit with a template-scoped message.\n * Used for per-template version history so rollback doesn't affect other templates.\n */\nexport function commitTemplateState(\n themePath: string,\n templateId: string,\n message: string,\n filePaths: string[]\n): string | null {\n if (!isGitAvailable()) return null;\n if (!existsSync(join(themePath, \".git\"))) return null;\n\n // Stage only the specified paths\n for (const fp of filePaths) {\n const fullPath = join(themePath, fp);\n if (existsSync(fullPath)) {\n run(`git add \"${fp}\"`, { cwd: themePath });\n }\n }\n\n // Check if there are staged changes\n const diff = run(\"git diff --cached --quiet\", { cwd: themePath });\n if (diff.success) return null; // nothing staged\n\n // Build prefixed message: [templateId] user message\n const prefix = `[${templateId}] `;\n const maxMsg = 72 - prefix.length;\n const truncated = message.length > maxMsg\n ? message.slice(0, maxMsg - 3) + \"...\"\n : message;\n const fullMessage = prefix + truncated;\n\n const commitResult = run(`git commit -m \"${fullMessage.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n if (!commitResult.success) {\n console.warn(`[project-git] template commit failed: ${commitResult.stderr}`);\n return null;\n }\n\n const hashResult = run(\"git rev-parse --short HEAD\", { cwd: themePath });\n return hashResult.success ? hashResult.stdout : null;\n}\n\n// ---------------------------------------------------------------------------\n// History\n// ---------------------------------------------------------------------------\n\n/**\n * Get commit history (most recent first).\n */\nexport function getHistory(themePath: string, limit: number = 50): GitCommitInfo[] {\n if (!isGitAvailable()) return [];\n if (!existsSync(join(themePath, \".git\"))) return [];\n\n const result = run(\n `git log --pretty=format:\"%h|%H|%s|%at\" -n ${limit}`,\n { cwd: themePath }\n );\n if (!result.success || !result.stdout.trim()) return [];\n\n const commits: GitCommitInfo[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n const parts = line.split(\"|\");\n if (parts.length < 4) continue;\n const timestamp = parseInt(parts[3], 10) * 1000;\n commits.push({\n hash: parts[0],\n fullHash: parts[1],\n message: parts[2],\n timestamp,\n date: new Date(timestamp).toISOString(),\n });\n }\n return commits;\n}\n\n/**\n * Get commit history filtered to a specific template (by commit message prefix).\n */\nexport function getTemplateHistory(\n themePath: string,\n templateId: string,\n limit: number = 50\n): GitCommitInfo[] {\n if (!isGitAvailable()) return [];\n if (!existsSync(join(themePath, \".git\"))) return [];\n\n const escapedId = templateId.replace(/[[\\]\\\\]/g, \"\\\\$&\");\n const result = run(\n `git log --grep=\"\\\\[${escapedId}\\\\]\" --pretty=format:\"%h|%H|%s|%at\" -n ${limit}`,\n { cwd: themePath }\n );\n if (!result.success || !result.stdout.trim()) return [];\n\n const commits: GitCommitInfo[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n const parts = line.split(\"|\");\n if (parts.length < 4) continue;\n const timestamp = parseInt(parts[3], 10) * 1000;\n commits.push({\n hash: parts[0],\n fullHash: parts[1],\n message: parts[2],\n timestamp,\n date: new Date(timestamp).toISOString(),\n });\n }\n return commits;\n}\n\n// ---------------------------------------------------------------------------\n// Rollback\n// ---------------------------------------------------------------------------\n\n/**\n * Restore theme files to a specific commit's state.\n * Creates a new commit (\"Rollback to: <original message>\") to keep linear history.\n * Does NOT touch .vibespot/ (gitignored) — chat is preserved.\n */\nexport function rollbackToCommit(\n themePath: string,\n commitHash: string\n): { success: boolean; error?: string } {\n if (!isGitAvailable()) return { success: false, error: \"Git not available\" };\n if (!existsSync(join(themePath, \".git\"))) return { success: false, error: \"Not a git repo\" };\n\n if (!isValidHash(commitHash)) return { success: false, error: \"Invalid commit hash\" };\n\n // Verify commit exists\n const verify = run(`git cat-file -t ${commitHash}`, { cwd: themePath });\n if (!verify.success || verify.stdout.trim() !== \"commit\") {\n return { success: false, error: `Commit ${commitHash} not found` };\n }\n\n // Get original commit message\n const msgResult = run(`git log --format=\"%s\" -1 ${commitHash}`, { cwd: themePath });\n const origMessage = msgResult.success ? msgResult.stdout : commitHash;\n\n // Restore all files from that commit\n const checkout = run(`git checkout ${commitHash} -- .`, { cwd: themePath });\n if (!checkout.success) {\n return { success: false, error: `Checkout failed: ${checkout.stderr}` };\n }\n\n // Commit the rollback\n const rollbackMsg = `Rollback to: ${origMessage}`.slice(0, 72);\n run(`git commit -m \"${rollbackMsg.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n\n return { success: true };\n}\n\n/**\n * Restore only specific template files to a commit's state.\n * Other templates' files are left untouched.\n */\nexport function rollbackTemplateToCommit(\n themePath: string,\n templateId: string,\n commitHash: string,\n filePaths: string[]\n): { success: boolean; error?: string } {\n if (!isGitAvailable()) return { success: false, error: \"Git not available\" };\n if (!existsSync(join(themePath, \".git\"))) return { success: false, error: \"Not a git repo\" };\n\n if (!isValidHash(commitHash)) return { success: false, error: \"Invalid commit hash\" };\n\n // Verify commit exists\n const verify = run(`git cat-file -t ${commitHash}`, { cwd: themePath });\n if (!verify.success || verify.stdout.trim() !== \"commit\") {\n return { success: false, error: `Commit ${commitHash} not found` };\n }\n\n // Get original commit message\n const msgResult = run(`git log --format=\"%s\" -1 ${commitHash}`, { cwd: themePath });\n const origMessage = msgResult.success ? msgResult.stdout : commitHash;\n\n // Restore only the specified paths from that commit\n let restored = 0;\n for (const fp of filePaths) {\n const checkout = run(`git checkout ${commitHash} -- \"${fp}\"`, { cwd: themePath });\n if (checkout.success) restored++;\n // Skip paths that didn't exist at that commit (git errors silently ignored)\n }\n\n if (restored === 0) {\n return { success: false, error: \"No files could be restored from that commit\" };\n }\n\n // Stage and commit the scoped rollback\n run(\"git add -A\", { cwd: themePath });\n const prefix = `[${templateId}] `;\n const rollbackMsg = `${prefix}Rollback to: ${origMessage}`.slice(0, 72);\n run(`git commit -m \"${rollbackMsg.replace(/\"/g, '\\\\\"')}\"`, { cwd: themePath });\n\n return { success: true };\n}\n","/**\n * State mutation functions for the active session.\n */\n\nimport { readFileSync, existsSync, writeFileSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles, GeneratedAssets } from \"../../ai/engine.js\";\nimport type { ChatMessage, TemplateEntry, SessionAsset, FieldDef } from \"./types.js\";\nimport { getSession, saveSession } from \"./store.js\";\nimport { getActiveTemplate } from \"./templates.js\";\n\n// ---------------------------------------------------------------------------\n// Flat-field sync helpers (exported for use by templates.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Sync flat session fields from a template (compatibility layer).\n * Existing code reads session.modules, session.messages, etc.\n * This keeps those in sync with the active template.\n */\nexport function syncFlatFieldsFromTemplate(tpl: TemplateEntry): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = tpl.modules;\n activeSession.moduleOrder = tpl.moduleOrder;\n activeSession.sharedCss = tpl.sharedCss;\n activeSession.sharedJs = tpl.sharedJs;\n activeSession.template = tpl.template;\n activeSession.messages = tpl.messages;\n}\n\n/**\n * Sync changes from flat session fields back to the active template.\n * Called after any mutation to session.modules/sharedCss/etc.\n */\nexport function syncFlatFieldsToTemplate(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const tpl = getActiveTemplate();\n if (!tpl) return;\n tpl.modules = activeSession.modules;\n tpl.moduleOrder = activeSession.moduleOrder;\n tpl.sharedCss = activeSession.sharedCss;\n tpl.sharedJs = activeSession.sharedJs;\n tpl.template = activeSession.template;\n tpl.messages = activeSession.messages;\n}\n\n// ---------------------------------------------------------------------------\n// Message management\n// ---------------------------------------------------------------------------\n\nexport function addMessage(role: \"user\" | \"assistant\", content: string, pipeline?: import(\"./types.js\").PipelineMetadata): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const msg: import(\"./types.js\").ChatMessage = { role, content, timestamp: Date.now() };\n if (pipeline) msg.pipeline = pipeline;\n activeSession.messages.push(msg);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n saveChatToTheme();\n}\n\nexport function addSessionAsset(asset: SessionAsset): void {\n const activeSession = getSession();\n if (!activeSession) return;\n if (!activeSession.assets) activeSession.assets = [];\n activeSession.assets.push(asset);\n activeSession.updatedAt = Date.now();\n saveSession();\n}\n\n// ---------------------------------------------------------------------------\n// Module mutations\n// ---------------------------------------------------------------------------\n\n/**\n * Update session with newly generated/modified modules.\n * Merges new modules into existing state (updates existing, adds new).\n */\nexport function updateModules(assets: Partial<GeneratedAssets>): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n if (assets.sharedCss !== undefined) activeSession.sharedCss = assets.sharedCss;\n if (assets.sharedJs !== undefined) activeSession.sharedJs = assets.sharedJs;\n if (assets.template !== undefined) activeSession.template = assets.template;\n\n if (assets.modules) {\n for (const newMod of assets.modules) {\n const newNameLower = newMod.moduleName.toLowerCase();\n const idx = activeSession.modules.findIndex(\n (m) => m.moduleName.toLowerCase() === newNameLower\n );\n if (idx >= 0) {\n activeSession.modules[idx] = newMod;\n } else {\n activeSession.modules.push(newMod);\n if (!activeSession.moduleOrder.some((n) => n.toLowerCase() === newNameLower)) {\n activeSession.moduleOrder.push(newMod.moduleName);\n }\n }\n }\n }\n\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Reorder modules (used by drag-and-drop in the UI).\n */\nexport function reorderModules(newOrder: string[]): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.moduleOrder = newOrder;\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Remove a module by name.\n */\nexport function removeModule(moduleName: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = activeSession.modules.filter(\n (m) => m.moduleName !== moduleName\n );\n activeSession.moduleOrder = activeSession.moduleOrder.filter(\n (n) => n !== moduleName\n );\n\n // Also remove from all templates\n for (const tpl of activeSession.templates) {\n tpl.modules = tpl.modules.filter((m) => m.moduleName !== moduleName);\n tpl.moduleOrder = tpl.moduleOrder.filter((n) => n !== moduleName);\n }\n\n // Delete module directory from disk\n if (activeSession.themePath) {\n const modDir = join(activeSession.themePath, \"modules\", `${moduleName}.module`);\n if (existsSync(modDir)) rmSync(modDir, { recursive: true, force: true });\n }\n\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Detach a module from the current template (remove from moduleOrder only).\n * The module data stays in the session so it can be re-added later.\n */\nexport function detachModule(moduleName: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.moduleOrder = activeSession.moduleOrder.filter(\n (n) => n !== moduleName\n );\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Update a single field value in a module's fields.json.\n * Used by the field editor sidebar.\n */\nexport function updateFieldValue(\n moduleName: string,\n fieldPath: string,\n value: unknown\n): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n const mod = activeSession.modules.find((m) => m.moduleName === moduleName);\n if (!mod) return;\n\n try {\n const fields = JSON.parse(mod.fieldsJson);\n setFieldDefault(fields, fieldPath, value);\n mod.fieldsJson = JSON.stringify(fields, null, 2);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n } catch {\n // Invalid JSON — skip\n }\n}\n\n/**\n * Get modules in display order.\n */\nexport function getOrderedModules(): ModuleFiles[] {\n const activeSession = getSession();\n if (!activeSession) return [];\n\n const ordered: ModuleFiles[] = [];\n for (const name of activeSession.moduleOrder) {\n const mod = activeSession.modules.find((m) => m.moduleName === name);\n if (mod) ordered.push(mod);\n }\n\n // Append any modules not in the order list\n for (const mod of activeSession.modules) {\n if (!activeSession.moduleOrder.includes(mod.moduleName)) {\n ordered.push(mod);\n }\n }\n\n return ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Chat persistence — store chat in theme directory\n// ---------------------------------------------------------------------------\n\n/**\n * Persist chat to {themePath}/.vibespot/chat.json (gitignored).\n */\nexport function saveChatToTheme(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n try {\n const chatDir = join(activeSession.themePath, \".vibespot\");\n mkdirSync(chatDir, { recursive: true });\n const chatData = {\n sessionId: activeSession.id,\n themeName: activeSession.themeName,\n messages: activeSession.messages,\n updatedAt: Date.now(),\n };\n writeFileSync(join(chatDir, \"chat.json\"), JSON.stringify(chatData, null, 2), \"utf-8\");\n } catch {\n // Non-critical — don't block on chat persistence errors\n }\n}\n\n/**\n * Load chat history from a theme's .vibespot/chat.json.\n */\nexport function loadChatFromTheme(themePath: string): ChatMessage[] {\n const chatPath = join(themePath, \".vibespot\", \"chat.json\");\n if (!existsSync(chatPath)) return [];\n try {\n const data = JSON.parse(readFileSync(chatPath, \"utf-8\"));\n return Array.isArray(data.messages) ? data.messages : [];\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Set a default value at a dot-separated field path in a fields.json array.\n */\nfunction setFieldDefault(fields: FieldDef[], path: string, value: unknown): void {\n const parts = path.split(\".\");\n const fieldName = parts[0];\n const field = fields.find((f: FieldDef) => f.name === fieldName);\n if (!field) return;\n\n if (parts.length === 1) {\n field.default = value;\n } else if (field.children) {\n setFieldDefault(field.children, parts.slice(1).join(\".\"), value);\n }\n}\n","/**\n * Multi-template management functions.\n */\n\nimport { existsSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { VibeSession, TemplateEntry, PageType } from \"./types.js\";\nimport { getSession } from \"./store.js\";\nimport { syncFlatFieldsFromTemplate } from \"./state.js\";\n\n// ---------------------------------------------------------------------------\n// Template management\n// ---------------------------------------------------------------------------\n\n/**\n * Migrate a legacy flat session (v0.3.0) into the templates array.\n * Called when loading a session that has modules but no templates.\n */\nexport function migrateSession(session: VibeSession): void {\n if (session.templates && session.templates.length > 0) return;\n if (!session.modules || session.modules.length === 0) {\n session.templates = [];\n session.activeTemplateId = \"\";\n return;\n }\n\n const templateId = `lp-${session.themeName}`;\n const entry: TemplateEntry = {\n id: templateId,\n label: `${session.themeName} Landing Page`,\n pageType: \"landing_page\",\n templateFile: `templates/lp-${session.themeName}.html`,\n modules: [...session.modules],\n moduleOrder: [...session.moduleOrder],\n sharedCss: session.sharedCss || \"\",\n sharedJs: session.sharedJs || \"\",\n template: session.template || \"\",\n messages: [...session.messages],\n };\n\n session.templates = [entry];\n session.activeTemplateId = templateId;\n}\n\n/**\n * Get the active template entry, or null if none set.\n */\nexport function getActiveTemplate(): TemplateEntry | null {\n const activeSession = getSession();\n if (!activeSession) return null;\n if (!activeSession.activeTemplateId || !activeSession.templates?.length) return null;\n return activeSession.templates.find((t) => t.id === activeSession!.activeTemplateId) || null;\n}\n\n/**\n * Set the active template by ID. Syncs flat fields from the template.\n */\nexport function setActiveTemplate(templateId: string): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const tpl = activeSession.templates.find((t) => t.id === templateId);\n if (!tpl) return false;\n\n activeSession.activeTemplateId = templateId;\n syncFlatFieldsFromTemplate(tpl);\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Create a new template entry and add it to the session.\n */\nexport function addTemplate(pageType: PageType, label: string): TemplateEntry {\n const activeSession = getSession();\n if (!activeSession) throw new Error(\"No active session\");\n\n const slug = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const prefix = pageType === \"blog_post\" ? \"bp\" :\n pageType === \"website_page\" ? \"wp\" :\n pageType === \"module_only\" ? \"mo\" : \"lp\";\n const id = `${prefix}-${slug}`;\n\n const entry: TemplateEntry = {\n id,\n label,\n pageType,\n templateFile: pageType === \"module_only\" ? \"\" : `templates/${id}.html`,\n modules: [],\n moduleOrder: [],\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n messages: [],\n };\n\n activeSession.templates.push(entry);\n activeSession.activeTemplateId = id;\n syncFlatFieldsFromTemplate(entry);\n activeSession.updatedAt = Date.now();\n return entry;\n}\n\n/**\n * Clone an existing template, deep-copying modules and state.\n */\nexport function cloneTemplate(templateId: string, newLabel?: string): TemplateEntry | null {\n const activeSession = getSession();\n if (!activeSession) return null;\n const source = activeSession.templates.find((t) => t.id === templateId);\n if (!source) return null;\n\n const label = newLabel || `${source.label} (Copy)`;\n const slug = label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const prefix = source.pageType === \"blog_post\" ? \"bp\" :\n source.pageType === \"website_page\" ? \"wp\" :\n source.pageType === \"module_only\" ? \"mo\" : \"lp\";\n const id = `${prefix}-${slug}`;\n\n const entry: TemplateEntry = {\n id,\n label,\n pageType: source.pageType,\n templateFile: source.pageType === \"module_only\" ? \"\" : `templates/${id}.html`,\n modules: source.modules.map((m) => ({ ...m })), // shallow copy each module\n moduleOrder: [...source.moduleOrder],\n sharedCss: source.sharedCss,\n sharedJs: source.sharedJs,\n template: source.template,\n messages: [], // start fresh chat for clone\n };\n\n activeSession.templates.push(entry);\n activeSession.activeTemplateId = id;\n syncFlatFieldsFromTemplate(entry);\n activeSession.updatedAt = Date.now();\n return entry;\n}\n\n/**\n * Rename a template's display label.\n */\nexport function renameTemplate(templateId: string, newLabel: string): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const tpl = activeSession.templates.find((t) => t.id === templateId);\n if (!tpl) return false;\n tpl.label = newLabel;\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Remove a template by ID.\n * If deleteModules is true, also remove modules that are exclusive to this\n * template (not used by any other template) from session and disk.\n */\nexport function removeTemplate(templateId: string, deleteModules = false): boolean {\n const activeSession = getSession();\n if (!activeSession) return false;\n const idx = activeSession.templates.findIndex((t) => t.id === templateId);\n if (idx < 0) return false;\n\n const removed = activeSession.templates.splice(idx, 1)[0];\n\n // Delete template file(s) from disk\n if (activeSession.themePath) {\n const templatesDir = join(activeSession.themePath, \"templates\");\n const filename = `${removed.id}.html`;\n const filePath = join(templatesDir, filename);\n if (existsSync(filePath)) rmSync(filePath, { force: true });\n // Also remove blog listing template if applicable\n if (removed.pageType === \"blog_post\") {\n const listingPath = join(templatesDir, `${removed.id}-listing.html`);\n if (existsSync(listingPath)) rmSync(listingPath, { force: true });\n }\n }\n\n // Delete exclusive modules from disk + remaining templates\n if (deleteModules && removed.modules.length > 0) {\n // Collect module names still used by other templates\n const usedElsewhere = new Set<string>();\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) usedElsewhere.add(mod.moduleName);\n }\n // Also check flat session modules\n for (const mod of activeSession.modules) usedElsewhere.add(mod.moduleName);\n\n const exclusiveModules = removed.modules\n .map((m) => m.moduleName)\n .filter((name) => !usedElsewhere.has(name));\n\n // Remove from disk\n if (activeSession.themePath && exclusiveModules.length > 0) {\n const modulesDir = join(activeSession.themePath, \"modules\");\n for (const name of exclusiveModules) {\n const modDir = join(modulesDir, `${name}.module`);\n if (existsSync(modDir)) rmSync(modDir, { recursive: true, force: true });\n }\n }\n }\n\n // If we removed the active template, switch to the first remaining one\n if (activeSession.activeTemplateId === templateId) {\n if (activeSession.templates.length > 0) {\n setActiveTemplate(activeSession.templates[0].id);\n } else {\n activeSession.activeTemplateId = \"\";\n activeSession.modules = [];\n activeSession.moduleOrder = [];\n activeSession.sharedCss = \"\";\n activeSession.sharedJs = \"\";\n activeSession.template = \"\";\n activeSession.messages = [];\n }\n }\n\n activeSession.updatedAt = Date.now();\n return true;\n}\n\n/**\n * Get deduplicated modules across all templates (the module library).\n */\nexport function getModuleLibrary(): Array<{ module: ModuleFiles; usedIn: string[] }> {\n const activeSession = getSession();\n if (!activeSession) return [];\n const map = new Map<string, { module: ModuleFiles; usedIn: string[] }>();\n\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) {\n const existing = map.get(mod.moduleName);\n if (existing) {\n existing.usedIn.push(tpl.label);\n } else {\n map.set(mod.moduleName, { module: mod, usedIn: [tpl.label] });\n }\n }\n }\n\n return Array.from(map.values());\n}\n","/**\n * Session CRUD, index management, and the global activeSession variable.\n */\n\nimport { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync, rmSync, renameSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ensureGitRepo } from \"../project-git.js\";\nimport type { VibeSession, SessionIndexEntry } from \"./types.js\";\nimport { migrateSession } from \"./templates.js\";\n\n// ---------------------------------------------------------------------------\n// Session management\n// ---------------------------------------------------------------------------\n\nconst SESSIONS_DIR = join(homedir(), \".vibespot\", \"sessions\");\nconst INDEX_PATH = join(SESSIONS_DIR, \"_index.json\");\n\nlet _indexCache: SessionIndexEntry[] | null = null;\n\nfunction readIndex(): SessionIndexEntry[] {\n if (_indexCache) return _indexCache;\n try {\n if (!existsSync(INDEX_PATH)) return rebuildIndex();\n _indexCache = JSON.parse(readFileSync(INDEX_PATH, \"utf-8\"));\n return _indexCache!;\n } catch {\n return rebuildIndex();\n }\n}\n\nfunction writeIndex(entries: SessionIndexEntry[]): void {\n _indexCache = entries;\n try {\n mkdirSync(SESSIONS_DIR, { recursive: true });\n writeFileSync(INDEX_PATH, JSON.stringify(entries), \"utf-8\");\n } catch { /* non-critical */ }\n}\n\nfunction rebuildIndex(): SessionIndexEntry[] {\n if (!existsSync(SESSIONS_DIR)) return [];\n const entries: SessionIndexEntry[] = [];\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n const templates = data.templates || [];\n entries.push({\n id: data.id,\n themeName: data.themeName,\n updatedAt: data.updatedAt,\n moduleCount: templates.reduce((n: number, t: any) => n + (t.modules?.length || 0), 0),\n templateCount: templates.length,\n });\n } catch { /* skip corrupt files */ }\n }\n _indexCache = entries;\n writeIndex(entries);\n return entries;\n}\n\nfunction upsertIndex(session: VibeSession): void {\n const entries = readIndex();\n const templates = session.templates || [];\n const entry: SessionIndexEntry = {\n id: session.id,\n themeName: session.themeName,\n updatedAt: session.updatedAt,\n moduleCount: templates.reduce((n, t) => n + (t.modules?.length || 0), 0),\n templateCount: templates.length,\n };\n const idx = entries.findIndex((e) => e.id === session.id);\n if (idx >= 0) entries[idx] = entry;\n else entries.push(entry);\n writeIndex(entries);\n}\n\nfunction removeFromIndex(sessionId: string): void {\n const entries = readIndex().filter((e) => e.id !== sessionId);\n writeIndex(entries);\n}\n\nfunction removeFromIndexByTheme(themeName: string): void {\n const entries = readIndex().filter((e) => e.themeName !== themeName);\n writeIndex(entries);\n}\n\nlet activeSession: VibeSession | null = null;\n\nexport function getSession(): VibeSession | null {\n return activeSession;\n}\n\nfunction generateId(): string {\n return `vibe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function createSession(themePath: string, themeName: string): VibeSession {\n const session: VibeSession = {\n id: generateId(),\n themePath,\n themeName,\n templates: [],\n activeTemplateId: \"\",\n messages: [],\n modules: [],\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n moduleOrder: [],\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n activeSession = session;\n ensureGitRepo(themePath);\n return session;\n}\n\n// ---------------------------------------------------------------------------\n// Persistence — save/load sessions across restarts\n// ---------------------------------------------------------------------------\n\nexport function saveSession(): void {\n if (!activeSession) return;\n\n mkdirSync(SESSIONS_DIR, { recursive: true });\n const filePath = join(SESSIONS_DIR, `${activeSession.id}.json`);\n writeFileSync(filePath, JSON.stringify(activeSession, null, 2), \"utf-8\");\n upsertIndex(activeSession);\n}\n\nexport function loadSession(sessionId: string): VibeSession | null {\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n if (!existsSync(filePath)) return null;\n\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n\n // Ensure templates array exists (backward compat with v0.3.0 sessions)\n if (!data.templates) data.templates = [];\n if (!data.activeTemplateId) data.activeTemplateId = \"\";\n\n // Migrate flat fields into templates if needed\n migrateSession(data);\n\n activeSession = data;\n return data;\n } catch {\n return null;\n }\n}\n\nexport function listSessions(): Array<{ id: string; themeName: string; updatedAt: number; moduleCount: number; templateCount: number }> {\n if (!existsSync(SESSIONS_DIR)) return [];\n return readIndex();\n}\n\nexport function deleteSession(sessionId: string, deleteFiles = false): void {\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n\n // Read the session to get themeName (needed to find sibling sessions)\n let themeName = \"\";\n if (deleteFiles) {\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n themeName = data.themeName || \"\";\n if (data.themePath && existsSync(data.themePath)) {\n rmSync(data.themePath, { recursive: true, force: true });\n }\n } catch { /* ignore */ }\n } else {\n try {\n const data = JSON.parse(readFileSync(filePath, \"utf-8\"));\n themeName = data.themeName || \"\";\n } catch { /* ignore */ }\n }\n\n try {\n if (existsSync(filePath)) rmSync(filePath);\n } catch { /* ignore */ }\n\n // Also delete all other sessions for the same theme (prevents ghost entries)\n if (themeName && existsSync(SESSIONS_DIR)) {\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n if (data.themeName === themeName) {\n rmSync(join(SESSIONS_DIR, f));\n }\n } catch { /* ignore */ }\n }\n removeFromIndexByTheme(themeName);\n } else {\n removeFromIndex(sessionId);\n }\n\n if (activeSession?.id === sessionId) {\n activeSession = null;\n }\n}\n\n/**\n * Rename a project: update themeName in all sessions, rename disk folder,\n * rename CSS/JS files, and update the session index.\n */\nexport function renameSession(sessionId: string, newName: string): { ok: boolean; error?: string } {\n // Load the session to get the current theme name\n const filePath = join(SESSIONS_DIR, sessionId + \".json\");\n if (!existsSync(filePath)) return { ok: false, error: \"Session not found\" };\n\n let session: VibeSession;\n try {\n session = JSON.parse(readFileSync(filePath, \"utf-8\"));\n } catch {\n return { ok: false, error: \"Failed to read session\" };\n }\n\n const oldName = session.themeName;\n if (oldName === newName) return { ok: true };\n\n const oldPath = session.themePath;\n const newPath = join(dirname(oldPath), newName);\n\n // Rename the folder on disk\n if (existsSync(oldPath)) {\n if (existsSync(newPath)) return { ok: false, error: \"A project with that name already exists\" };\n try {\n renameSync(oldPath, newPath);\n } catch (err) {\n return { ok: false, error: `Failed to rename folder: ${err instanceof Error ? err.message : String(err)}` };\n }\n\n // Rename CSS/JS files inside the new folder\n const cssOld = join(newPath, \"css\", `${oldName}-theme.css`);\n const cssNew = join(newPath, \"css\", `${newName}-theme.css`);\n if (existsSync(cssOld)) try { renameSync(cssOld, cssNew); } catch { /* non-critical */ }\n\n const jsOld = join(newPath, \"js\", `${oldName}-animations.js`);\n const jsNew = join(newPath, \"js\", `${newName}-animations.js`);\n if (existsSync(jsOld)) try { renameSync(jsOld, jsNew); } catch { /* non-critical */ }\n\n // Update theme.json label/name\n const themeJsonPath = join(newPath, \"theme.json\");\n if (existsSync(themeJsonPath)) {\n try {\n const themeData = JSON.parse(readFileSync(themeJsonPath, \"utf-8\"));\n themeData.label = newName;\n themeData.name = newName;\n writeFileSync(themeJsonPath, JSON.stringify(themeData, null, 2), \"utf-8\");\n } catch { /* non-critical */ }\n }\n }\n\n // Update all sessions that reference this theme\n if (existsSync(SESSIONS_DIR)) {\n for (const f of readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(\".json\") && f !== \"_index.json\")) {\n try {\n const data = JSON.parse(readFileSync(join(SESSIONS_DIR, f), \"utf-8\"));\n if (data.themeName === oldName) {\n data.themeName = newName;\n data.themePath = newPath;\n data.updatedAt = Date.now();\n writeFileSync(join(SESSIONS_DIR, f), JSON.stringify(data, null, 2), \"utf-8\");\n }\n } catch { /* skip corrupt files */ }\n }\n }\n\n // Update the in-memory active session\n if (activeSession && activeSession.themeName === oldName) {\n activeSession.themeName = newName;\n activeSession.themePath = newPath;\n activeSession.updatedAt = Date.now();\n }\n\n // Rebuild the index\n rebuildIndex();\n\n return { ok: true };\n}\n","/**\n * Theme disk I/O — reading from and writing to theme directories.\n */\n\nimport { readFileSync, readdirSync, existsSync, writeFileSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { ChatMessage, TemplateEntry, PageType } from \"./types.js\";\nimport { getSession } from \"./store.js\";\nimport { getOrderedModules, syncFlatFieldsFromTemplate, syncFlatFieldsToTemplate, loadChatFromTheme } from \"./state.js\";\nimport { getActiveTemplate, migrateSession } from \"./templates.js\";\nimport { ensureGitRepo } from \"../project-git.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction safeRead(filePath: string): string {\n try {\n return readFileSync(filePath, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/**\n * Get modules in display order for a specific template entry.\n */\nfunction getOrderedModulesFrom(tpl: TemplateEntry): ModuleFiles[] {\n const ordered: ModuleFiles[] = [];\n for (const name of tpl.moduleOrder) {\n const mod = tpl.modules.find((m) => m.moduleName === name);\n if (mod) ordered.push(mod);\n }\n for (const mod of tpl.modules) {\n if (!tpl.moduleOrder.includes(mod.moduleName)) {\n ordered.push(mod);\n }\n }\n return ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Template file parsing\n// ---------------------------------------------------------------------------\n\ninterface ParsedTemplate {\n id: string;\n label: string;\n pageType: PageType;\n moduleNames: string[];\n templateContent: string;\n filename: string;\n}\n\n/**\n * Parse a single HubL template file to extract metadata and module references.\n */\nfunction parseTemplateFile(filePath: string, filename: string): ParsedTemplate | null {\n const content = safeRead(filePath);\n if (!content) return null;\n\n // Skip scaffold placeholder and listing templates\n if (filename === \"home.html\" || filename.endsWith(\"-listing.html\")) return null;\n\n // Infer ID from filename (strip .html)\n const id = filename.replace(/\\.html$/, \"\");\n\n // Infer pageType from prefix\n let pageType: PageType = \"landing_page\";\n if (id.startsWith(\"bp-\")) pageType = \"blog_post\";\n else if (id.startsWith(\"wp-\")) pageType = \"website_page\";\n else if (id.startsWith(\"mo-\")) pageType = \"module_only\";\n\n // Extract label from HubL comment metadata\n let label = id;\n const labelMatch = content.match(/<!--[\\s\\S]*?label:\\s*\"?([^\"\\n]+)\"?\\s*[\\s\\S]*?-->/);\n if (labelMatch) {\n label = labelMatch[1].trim();\n }\n\n // Extract module references from dnd_module tags\n const moduleRe = /dnd_module\\s+path=[\"']\\.\\.\\/modules\\/(.+?)\\.module[\"']/g;\n const moduleNames: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = moduleRe.exec(content)) !== null) {\n moduleNames.push(match[1]);\n }\n\n return { id, label, pageType, moduleNames, templateContent: content, filename };\n}\n\n/**\n * Scan the templates directory and create TemplateEntry objects for each template file.\n * Returns an empty array if no valid template files are found.\n */\nfunction scanTemplateFiles(\n templatesDir: string,\n modulesByName: Map<string, ModuleFiles>,\n sharedCss: string,\n sharedJs: string,\n chatMessages: ChatMessage[],\n): TemplateEntry[] {\n if (!existsSync(templatesDir)) return [];\n\n const entries: TemplateEntry[] = [];\n const allFiles = readdirSync(templatesDir).filter(\n (f) => f.endsWith(\".html\") && f !== \"home.html\"\n );\n\n // Filter out layout files (in subdirectories) — only direct children\n for (const filename of allFiles) {\n const filePath = join(templatesDir, filename);\n const parsed = parseTemplateFile(filePath, filename);\n if (!parsed) continue;\n if (parsed.moduleNames.length === 0 && entries.length > 0) continue; // skip empty templates if we have others\n\n // Build module list for this template\n const templateModules: ModuleFiles[] = [];\n const templateOrder: string[] = [];\n for (const name of parsed.moduleNames) {\n const mod = modulesByName.get(name);\n if (mod) {\n templateModules.push(mod);\n templateOrder.push(name);\n }\n }\n\n entries.push({\n id: parsed.id,\n label: parsed.label,\n pageType: parsed.pageType,\n templateFile: `templates/${parsed.filename}`,\n modules: templateModules,\n moduleOrder: templateOrder,\n sharedCss,\n sharedJs,\n template: parsed.templateContent,\n messages: entries.length === 0 ? [...chatMessages] : [], // chat history goes to first template\n });\n }\n\n return entries;\n}\n\n// ---------------------------------------------------------------------------\n// Scan modules from disk (for loading existing themes)\n// ---------------------------------------------------------------------------\n\n/**\n * Scan a theme directory on disk and load existing modules into the session.\n */\nexport function scanThemeFromDisk(themePath: string): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n // Load persisted chat from theme directory (if session has no messages yet)\n const chatFromDisk = loadChatFromTheme(themePath);\n if (chatFromDisk.length > 0 && activeSession.messages.length === 0) {\n activeSession.messages = chatFromDisk;\n }\n\n // Ensure git repo exists (handles themes created before this feature)\n ensureGitRepo(themePath);\n\n const modulesDir = join(themePath, \"modules\");\n if (!existsSync(modulesDir)) return;\n\n const entries = readdirSync(modulesDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.endsWith(\".module\")) continue;\n\n const modDir = join(modulesDir, entry.name);\n const moduleName = entry.name.replace(/\\.module$/, \"\");\n\n const mod: ModuleFiles = {\n moduleName,\n fieldsJson: safeRead(join(modDir, \"fields.json\")),\n metaJson: safeRead(join(modDir, \"meta.json\")),\n moduleHtml: safeRead(join(modDir, \"module.html\")),\n moduleCss: safeRead(join(modDir, \"module.css\")),\n moduleJs: safeRead(join(modDir, \"module.js\")) || undefined,\n };\n\n if (mod.fieldsJson && mod.moduleHtml) {\n activeSession.modules.push(mod);\n activeSession.moduleOrder.push(moduleName);\n }\n }\n\n // Load shared CSS/JS\n const cssDir = join(themePath, \"css\");\n const jsDir = join(themePath, \"js\");\n let sharedCss = \"\";\n let sharedJs = \"\";\n\n if (existsSync(cssDir)) {\n const cssFiles = readdirSync(cssDir).filter(\n (f) => f.endsWith(\"-theme.css\")\n );\n if (cssFiles.length > 0) {\n sharedCss = safeRead(join(cssDir, cssFiles[0]));\n activeSession.sharedCss = sharedCss;\n }\n }\n\n if (existsSync(jsDir)) {\n const jsFiles = readdirSync(jsDir).filter(\n (f) => f.endsWith(\"-animations.js\")\n );\n if (jsFiles.length > 0) {\n sharedJs = safeRead(join(jsDir, jsFiles[0]));\n activeSession.sharedJs = sharedJs;\n }\n }\n\n // Load brand assets from .vibespot/ directory\n const sgPath = join(themePath, \".vibespot\", \"styleguide.md\");\n const bvPath = join(themePath, \".vibespot\", \"brandvoice.md\");\n const tcPath = join(themePath, \".vibespot\", \"theme-context.md\");\n if (existsSync(sgPath) || existsSync(bvPath) || existsSync(tcPath)) {\n if (!activeSession.brandAssets) activeSession.brandAssets = {};\n if (existsSync(sgPath)) activeSession.brandAssets.styleguide = safeRead(sgPath);\n if (existsSync(bvPath)) activeSession.brandAssets.brandvoice = safeRead(bvPath);\n if (existsSync(tcPath)) activeSession.brandAssets.themeContext = safeRead(tcPath);\n }\n\n // Scan template files and create per-template TemplateEntry objects\n const templatesDir = join(themePath, \"templates\");\n const modulesByName = new Map(activeSession.modules.map((m) => [m.moduleName, m]));\n const parsedTemplates = scanTemplateFiles(templatesDir, modulesByName, sharedCss, sharedJs, activeSession.messages);\n\n if (parsedTemplates.length > 0) {\n activeSession.templates = parsedTemplates;\n activeSession.activeTemplateId = parsedTemplates[0].id;\n\n // Reorder flat modules to match the first template's order\n const firstOrder = parsedTemplates[0].moduleOrder;\n if (firstOrder.length > 0) {\n const loadedNames = new Set(activeSession.moduleOrder);\n const validOrder = firstOrder.filter((n) => loadedNames.has(n));\n for (const n of activeSession.moduleOrder) {\n if (!validOrder.includes(n)) validOrder.push(n);\n }\n activeSession.moduleOrder = validOrder;\n }\n\n syncFlatFieldsFromTemplate(parsedTemplates[0]);\n } else {\n // No template files found — fall back to legacy single-template migration\n if (!activeSession.templates) activeSession.templates = [];\n if (!activeSession.activeTemplateId) activeSession.activeTemplateId = \"\";\n migrateSession(activeSession);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Write modules back to disk (for upload)\n// ---------------------------------------------------------------------------\n\nexport function writeModulesToDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n\n const themePath = activeSession.themePath;\n\n // Collect all modules from all templates (deduplicated by name)\n const allModules = new Map<string, ModuleFiles>();\n if (activeSession.templates.length > 0) {\n for (const tpl of activeSession.templates) {\n for (const mod of tpl.modules) {\n allModules.set(mod.moduleName, mod);\n }\n }\n }\n // Also include flat session modules (backward compat / active template)\n for (const mod of activeSession.modules) {\n allModules.set(mod.moduleName, mod);\n }\n\n // Pre-create all module directories in one pass\n const modulesBaseDir = join(themePath, \"modules\");\n mkdirSync(modulesBaseDir, { recursive: true });\n for (const mod of allModules.values()) {\n mkdirSync(join(modulesBaseDir, `${mod.moduleName}.module`), { recursive: true });\n }\n\n for (const mod of allModules.values()) {\n const modDir = join(modulesBaseDir, `${mod.moduleName}.module`);\n writeFileSync(join(modDir, \"fields.json\"), mod.fieldsJson, \"utf-8\");\n writeFileSync(join(modDir, \"meta.json\"), mod.metaJson, \"utf-8\");\n writeFileSync(join(modDir, \"module.html\"), mod.moduleHtml, \"utf-8\");\n writeFileSync(join(modDir, \"module.css\"), mod.moduleCss, \"utf-8\");\n if (mod.moduleJs) {\n writeFileSync(join(modDir, \"module.js\"), mod.moduleJs, \"utf-8\");\n }\n }\n\n // Write shared CSS/JS\n if (activeSession.sharedCss) {\n const cssDir = join(themePath, \"css\");\n mkdirSync(cssDir, { recursive: true });\n writeFileSync(\n join(cssDir, `${activeSession.themeName}-theme.css`),\n activeSession.sharedCss,\n \"utf-8\"\n );\n }\n\n if (activeSession.sharedJs) {\n const jsDir = join(themePath, \"js\");\n mkdirSync(jsDir, { recursive: true });\n writeFileSync(\n join(jsDir, `${activeSession.themeName}-animations.js`),\n activeSession.sharedJs,\n \"utf-8\"\n );\n }\n\n // Write page templates for all templates in the session\n const templatesDir = join(themePath, \"templates\");\n mkdirSync(templatesDir, { recursive: true });\n\n // Remove scaffold home.html once real templates exist — it's an empty shell\n // that shows up as a usable template in HubSpot but has no modules.\n const scaffoldHome = join(templatesDir, \"home.html\");\n const hasRealTemplates = activeSession.templates.length > 0 || activeSession.modules.length > 0;\n if (hasRealTemplates && existsSync(scaffoldHome)) {\n rmSync(scaffoldHome, { force: true });\n }\n\n // Track which template files we're writing so we can clean up stale ones\n const activeTemplateFiles = new Set<string>();\n\n if (activeSession.templates.length > 0) {\n for (const tpl of activeSession.templates) {\n if (tpl.pageType === \"module_only\") continue; // No template for module-only\n if (tpl.modules.length === 0) continue;\n\n const templateContent = tpl.template || generateTemplateForEntry(tpl);\n const annotated = ensureTemplateAnnotations(templateContent, tpl.label, tpl.pageType);\n const filename = `${tpl.id}.html`;\n writeFileSync(join(templatesDir, filename), annotated, \"utf-8\");\n activeTemplateFiles.add(filename);\n\n // For blog posts, also generate a listing template\n if (tpl.pageType === \"blog_post\") {\n writeBlogListingTemplate(templatesDir, tpl);\n activeTemplateFiles.add(`${tpl.id}-listing.html`);\n }\n }\n } else if (activeSession.modules.length > 0) {\n // Legacy fallback: single template from flat fields\n const template = activeSession.template || generateTemplateFromModules();\n const annotated = ensureTemplateAnnotations(template, `${activeSession.themeName} Landing Page`);\n const filename = `lp-${activeSession.themeName}.html`;\n writeFileSync(join(templatesDir, filename), annotated, \"utf-8\");\n activeTemplateFiles.add(filename);\n }\n\n // Clean up stale lp-*.html template files that are no longer in the session\n try {\n for (const file of readdirSync(templatesDir)) {\n if (file.startsWith(\"lp-\") && file.endsWith(\".html\") && !activeTemplateFiles.has(file)) {\n rmSync(join(templatesDir, file), { force: true });\n }\n }\n } catch { /* non-critical */ }\n\n // Patch base.html to load template_js (animations won't work without this)\n patchBaseTemplate();\n\n // Populate theme.json with proper metadata\n updateThemeJson();\n}\n\n// ---------------------------------------------------------------------------\n// Reload from disk\n// ---------------------------------------------------------------------------\n\n/**\n * Clear in-memory modules and re-scan from disk.\n * Used after a git rollback to sync session state with restored files.\n */\nexport function reloadModulesFromDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n activeSession.modules = [];\n activeSession.moduleOrder = [];\n activeSession.sharedCss = \"\";\n activeSession.sharedJs = \"\";\n activeSession.template = \"\";\n scanThemeFromDisk(activeSession.themePath);\n activeSession.updatedAt = Date.now();\n syncFlatFieldsToTemplate();\n}\n\n/**\n * Reload only the active template's modules from disk.\n * Used after a scoped rollback to avoid affecting other templates.\n */\nexport function reloadActiveTemplateFromDisk(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const tpl = getActiveTemplate();\n if (!tpl) return;\n\n const themePath = activeSession.themePath;\n const modulesDir = join(themePath, \"modules\");\n\n // Reload modules that belong to this template\n tpl.modules = [];\n for (const name of tpl.moduleOrder) {\n const modDir = join(modulesDir, `${name}.module`);\n if (!existsSync(modDir)) continue;\n const mod: ModuleFiles = {\n moduleName: name,\n fieldsJson: safeRead(join(modDir, \"fields.json\")),\n metaJson: safeRead(join(modDir, \"meta.json\")),\n moduleHtml: safeRead(join(modDir, \"module.html\")),\n moduleCss: safeRead(join(modDir, \"module.css\")),\n moduleJs: safeRead(join(modDir, \"module.js\")) || undefined,\n };\n if (mod.fieldsJson && mod.moduleHtml) {\n tpl.modules.push(mod);\n }\n }\n\n // Reload template file\n if (tpl.templateFile) {\n const tplPath = join(themePath, tpl.templateFile);\n if (existsSync(tplPath)) {\n tpl.template = safeRead(tplPath);\n }\n }\n\n // Sync flat fields from the updated template\n syncFlatFieldsFromTemplate(tpl);\n activeSession.updatedAt = Date.now();\n}\n\n// ---------------------------------------------------------------------------\n// Theme metadata — populate theme.json + validate template annotations\n// ---------------------------------------------------------------------------\n\n/**\n * Patch base.html to load template_js (the boilerplate only loads template_css).\n * Without this, shared animations JS never runs and scroll-animate elements stay invisible.\n */\nfunction patchBaseTemplate(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const basePath = join(activeSession.themePath, \"templates\", \"layouts\", \"base.html\");\n if (!existsSync(basePath)) return;\n\n try {\n let content = readFileSync(basePath, \"utf-8\");\n // Already patched?\n if (content.includes(\"template_js\")) return;\n\n // Insert {% if template_js %} block right after the main.js require line\n const mainJsLine = '{{ require_js(get_asset_url(\"../../js/main.js\")) }}';\n if (content.includes(mainJsLine)) {\n content = content.replace(\n mainJsLine,\n mainJsLine + '\\n {% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}'\n );\n } else {\n // Fallback: insert before standard_footer_includes\n content = content.replace(\n \"{{ standard_footer_includes }}\",\n '{% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}\\n {{ standard_footer_includes }}'\n );\n }\n\n writeFileSync(basePath, content, \"utf-8\");\n } catch {\n // Non-critical — the generated template has its own require_js fallback\n }\n}\n\n/**\n * Update theme.json with proper name/label and ensure template annotations.\n * Called during writeModulesToDisk.\n */\nfunction updateThemeJson(): void {\n const activeSession = getSession();\n if (!activeSession) return;\n const themeJsonPath = join(activeSession.themePath, \"theme.json\");\n if (!existsSync(themeJsonPath)) return;\n\n try {\n const themeData = JSON.parse(readFileSync(themeJsonPath, \"utf-8\"));\n themeData.label = activeSession.themeName;\n themeData.name = activeSession.themeName;\n writeFileSync(themeJsonPath, JSON.stringify(themeData, null, 2), \"utf-8\");\n } catch {\n // Non-critical\n }\n}\n\n/**\n * Ensure the page template has proper HubSpot annotations.\n */\nfunction ensureTemplateAnnotations(templateContent: string, label: string, pageType: PageType = \"landing_page\"): string {\n // Check if annotations already exist\n if (templateContent.includes(\"templateType\")) return templateContent;\n\n const templateType = pageType === \"blog_post\" ? \"blog_post\" : \"page\";\n const annotations = `<!--\n templateType: ${templateType}\n isAvailableForNewContent: true\n label: \"${label}\"\n-->\\n`;\n return annotations + templateContent;\n}\n\n// ---------------------------------------------------------------------------\n// Template generation — build page templates from module data\n// ---------------------------------------------------------------------------\n\n/**\n * Build a HubSpot page template for a specific TemplateEntry.\n */\nfunction generateTemplateForEntry(tpl: TemplateEntry): string {\n if (tpl.modules.length === 0) return \"\";\n\n const activeSession = getSession()!;\n const name = activeSession.themeName;\n const ordered = getOrderedModulesFrom(tpl);\n\n const sections = ordered.map((mod) => {\n return ` {% dnd_section padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true %}\n {% dnd_module path=\"../modules/${mod.moduleName}.module\" %}\n {% end_dnd_module %}\n {% end_dnd_section %}`;\n }).join(\"\\n\\n\");\n\n const templateType = tpl.pageType === \"blog_post\" ? \"blog_post\" : \"page\";\n\n return `<!--\n templateType: ${templateType}\n isAvailableForNewContent: true\n label: \"${tpl.label}\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% set template_css = \"../../css/${name}-theme.css\" %}\n{% set template_js = \"../../js/${name}-animations.js\" %}\n\n{% block header %}\n{% endblock header %}\n\n{% block body %}\n<div class=\"${name}-page\">\n {% dnd_area \"main_content\" label=\"${tpl.label}\" %}\n\n${sections}\n\n {% end_dnd_area %}\n</div>\n{{ require_js(get_asset_url(\"../../js/${name}-animations.js\")) }}\n{% endblock body %}\n\n{% block footer %}\n{% endblock footer %}\n`;\n}\n\n/**\n * Write a blog listing template alongside a blog post template.\n */\nfunction writeBlogListingTemplate(templatesDir: string, tpl: TemplateEntry): void {\n const listingContent = `<!--\n templateType: blog_listing\n isAvailableForNewContent: true\n label: \"${tpl.label} - Listing\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% block body %}\n<div class=\"blog-listing\">\n <h1>{{ group.public_title }}</h1>\n {% for content in contents %}\n <article class=\"blog-listing__post\">\n <h2><a href=\"{{ content.absolute_url }}\">{{ content.name }}</a></h2>\n {% if content.featured_image %}\n <img src=\"{{ content.featured_image }}\" alt=\"{{ content.featured_image_alt_text }}\">\n {% endif %}\n <p>{{ content.post_summary|truncatewords(30) }}</p>\n <span class=\"blog-listing__date\">{{ content.publish_date|datetimeformat('%B %d, %Y') }}</span>\n </article>\n {% endfor %}\n {% if next_page_num %}\n <a href=\"{{ next_page_url }}\">Next Page</a>\n {% endif %}\n</div>\n{% endblock body %}\n`;\n writeFileSync(\n join(templatesDir, `${tpl.id}-listing.html`),\n listingContent,\n \"utf-8\"\n );\n}\n\n/**\n * Build a HubSpot page template that assembles all modules in display order.\n * Legacy — used when no templates array exists (flat session).\n */\nfunction generateTemplateFromModules(): string {\n const activeSession = getSession();\n if (!activeSession || activeSession.modules.length === 0) return \"\";\n\n const name = activeSession.themeName;\n const ordered = getOrderedModules();\n\n const sections = ordered.map((mod) => {\n return ` {% dnd_section padding={\"top\":\"0\",\"bottom\":\"0\",\"left\":\"0\",\"right\":\"0\"}, full_width=true %}\n {% dnd_module path=\"../modules/${mod.moduleName}.module\" %}\n {% end_dnd_module %}\n {% end_dnd_section %}`;\n }).join(\"\\n\\n\");\n\n return `<!--\n templateType: page\n isAvailableForNewContent: true\n label: \"${name} Landing Page\"\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% set template_css = \"../../css/${name}-theme.css\" %}\n{% set template_js = \"../../js/${name}-animations.js\" %}\n\n{% block header %}\n{% endblock header %}\n\n{% block body %}\n<div class=\"${name}-page\">\n {% dnd_area \"main_content\" label=\"${name} Landing Page\" %}\n\n${sections}\n\n {% end_dnd_area %}\n</div>\n{{ require_js(get_asset_url(\"../../js/${name}-animations.js\")) }}\n{% endblock body %}\n\n{% block footer %}\n{% endblock footer %}\n`;\n}\n","export * from \"./types.js\";\nexport * from \"./store.js\";\nexport * from \"./state.js\";\nexport * from \"./disk.js\";\nexport * from \"./templates.js\";\n","/**\n * Re-export shim — session logic lives in session/ directory.\n * Kept for ESM import compatibility (NodeNext resolves ./session.js to file, not session/index.js).\n */\nexport * from \"./session/index.js\";\n","/**\n * Lightweight HubL subset renderer for local preview.\n *\n * Supports the constructs that AI-generated HubSpot modules actually use:\n * {{ module.field }} — variable access\n * {{ module.group.child }} — nested access\n * {% if module.field %}...{% endif %} — conditionals (+ {% else %})\n * {% for item in module.list %}...{% endfor %} — loops\n * {{ item.field }} — loop variable access\n *\n * get_asset_url(\"assets/...\") is resolved to /theme-assets/ for local preview.\n * Everything else (require_css, dnd_area, etc.) is stripped.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FieldDef {\n name: string;\n type: string;\n default?: unknown;\n children?: FieldDef[];\n occurrence?: { min: number; max: number };\n tab?: string;\n}\n\nexport interface RenderContext {\n module: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a render context from a fields.json array, using each field's default.\n */\nexport function buildContextFromFields(fields: FieldDef[]): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const field of fields) {\n if (field.type === \"group\" && field.occurrence && Array.isArray(field.default)) {\n // Repeater group — default is an array of objects\n result[field.name] = field.default;\n } else if (field.type === \"group\" && field.children) {\n // Nested group (e.g. styles) — recurse into children\n result[field.name] = buildContextFromFields(field.children);\n } else {\n result[field.name] = field.default ?? \"\";\n }\n }\n\n return result;\n}\n\n/**\n * Render a HubL template string with the given context.\n * Returns plain HTML suitable for browser rendering.\n */\nexport function renderHubL(template: string, context: RenderContext): string {\n let output = template;\n\n // 1. Strip HubSpot-only directives that don't apply in preview\n output = stripDirectives(output);\n\n // 2. Process {% for %} loops (must come before if/expressions)\n output = processForLoops(output, context);\n\n // 3. Process {% if %} / {% else %} / {% endif %} conditionals\n output = processConditionals(output, context);\n\n // 4. Resolve {{ expression }} variable references\n output = resolveExpressions(output, context);\n\n // 5. Clean up any remaining unresolved tags\n output = cleanupRemaining(output);\n\n return output;\n}\n\n/**\n * Assemble a full preview HTML page from rendered modules + CSS/JS.\n */\nexport function assemblePreview(opts: {\n renderedModules: string[];\n sharedCss?: string;\n moduleCssArray: string[];\n sharedJs?: string;\n moduleJsArray: string[];\n}): string {\n const styleBlocks = [\n opts.sharedCss || \"\",\n ...opts.moduleCssArray,\n ]\n .filter(Boolean)\n .map((css) => `<style>${css}</style>`)\n .join(\"\\n\");\n\n const scriptBlocks = [\n opts.sharedJs || \"\",\n ...opts.moduleJsArray,\n ]\n .filter(Boolean)\n .map((js) => `<script>${js}</script>`)\n .join(\"\\n\");\n\n const body = opts.renderedModules.join(\"\\n\");\n\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\">\n${styleBlocks}\n<style>\nhtml{scroll-behavior:smooth}\n.vsp-img-wrap{position:relative;display:inline-block}\n.vsp-img-badge{position:absolute;top:8px;right:8px;background:rgba(0,0,0,.75);color:#fff;font:500 11px/1 -apple-system,sans-serif;padding:5px 8px;border-radius:4px;pointer-events:none;opacity:.85;white-space:nowrap;z-index:10}\n</style>\n</head>\n<body>\n${body}\n${scriptBlocks}\n<script>\n// Anchor link handler — smooth scroll to module sections\ndocument.addEventListener('click',function(e){\n var a=e.target.closest('a[href^=\"#\"]');\n if(!a)return;\n var id=a.getAttribute('href').slice(1);\n if(!id)return;\n var el=document.getElementById(id);\n if(el){\n e.preventDefault();\n el.scrollIntoView({behavior:'smooth',block:'start'});\n }\n});\n// Placeholder image badges\ndocument.querySelectorAll('img').forEach(function(img){\n var src=img.src||img.getAttribute('src')||'';\n if(src.indexOf('placehold')!==-1){\n var w=document.createElement('span');\n w.className='vsp-img-wrap';\n img.parentNode.insertBefore(w,img);\n w.appendChild(img);\n var b=document.createElement('span');\n b.className='vsp-img-badge';\n b.textContent='Placeholder — replace in HubSpot';\n w.appendChild(b);\n }\n});\n</script>\n</body>\n</html>`;\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n// Pre-compiled regex patterns for stripDirectives (avoid recompilation per render)\nconst RE_REQUIRE_TAG = /\\{%[-\\s]*require_(css|js)\\b.*?%\\}/gs;\nconst RE_END_REQUIRE_TAG = /\\{%[-\\s]*end_require_(css|js)\\s*%\\}/gs;\nconst RE_REQUIRE_EXPR = /\\{\\{[-\\s]*require_(css|js)\\(.*?\\)\\s*\\}\\}/gs;\nconst RE_GET_ASSET_URL = /\\{\\{[-\\s]*get_asset_url\\([\"'](?:[^\"'\\/]+\\/)?assets\\/(.*?)[\"']\\)\\s*\\}\\}/gs;\nconst RE_GET_ASSET_URL_STRIP = /\\{\\{[-\\s]*get_asset_url\\(.*?\\)\\s*\\}\\}/gs;\nconst RE_DND_TAGS = /\\{%[-\\s]*(end_)?(dnd_area|dnd_section|dnd_column|dnd_row|dnd_module)\\b.*?%\\}/gs;\nconst RE_MODULE_TAG = /\\{%[-\\s]*module\\b.*?%\\}/gs;\nconst RE_TEMPLATE_TAGS = /\\{%[-\\s]*(extends|block|endblock|set)\\b.*?%\\}/gs;\nconst RE_ANNOTATIONS = /\\{#.*?#\\}/gs;\nconst RE_CONTENT_VARS = /\\{\\{[-\\s]*content\\.\\w+.*?\\}\\}/gs;\n// Match only INNERMOST if/endif blocks (body must not contain other {% if %} tags).\n// The while-loop peels layers from inside out, resolving nested conditionals correctly.\nconst RE_IF_PATTERN = /\\{%[-\\s]*if\\s+(.*?)\\s*-?%\\}((?:(?!\\{%[-\\s]*if\\s)[\\s\\S])*?)\\{%[-\\s]*endif\\s*-?%\\}/g;\n\n/**\n * Strip HubSpot-specific directives that have no meaning in local preview.\n */\nfunction stripDirectives(tpl: string): string {\n tpl = tpl.replace(RE_REQUIRE_TAG, \"\");\n tpl = tpl.replace(RE_END_REQUIRE_TAG, \"\");\n tpl = tpl.replace(RE_REQUIRE_EXPR, \"\");\n // Resolve get_asset_url(\"assets/filename\") → /theme-assets/filename for preview\n RE_GET_ASSET_URL.lastIndex = 0;\n tpl = tpl.replace(RE_GET_ASSET_URL, (_match, filename) => `/theme-assets/${filename}`);\n // Strip any remaining get_asset_url() calls with non-standard paths\n RE_GET_ASSET_URL_STRIP.lastIndex = 0;\n tpl = tpl.replace(RE_GET_ASSET_URL_STRIP, \"\");\n tpl = tpl.replace(RE_DND_TAGS, \"\");\n tpl = tpl.replace(RE_MODULE_TAG, \"\");\n tpl = tpl.replace(RE_TEMPLATE_TAGS, \"\");\n tpl = tpl.replace(RE_ANNOTATIONS, \"\");\n tpl = tpl.replace(RE_CONTENT_VARS, \"\");\n return tpl;\n}\n\n/**\n * Process {% for VAR in PATH %}...{% endfor %} loops.\n * Uses balanced tag matching to handle nested for-loops correctly.\n */\nfunction processForLoops(tpl: string, context: RenderContext): string {\n let result = tpl;\n let safety = 0;\n\n while (safety < 30) {\n safety++;\n const match = findOutermostFor(result);\n if (!match) break;\n\n const { varName, iterExpr, body, start, end } = match;\n const items = resolveIterable(iterExpr, context);\n\n let rendered = \"\";\n if (Array.isArray(items)) {\n rendered = items\n .map((item, index) => {\n const loopContext: RenderContext = {\n ...context,\n [varName]: item,\n loop: { index: index + 1, index0: index, first: index === 0, last: index === items.length - 1, length: items.length },\n };\n\n let out = processForLoops(body, loopContext);\n out = processConditionals(out, loopContext);\n out = resolveExpressions(out, loopContext);\n return out;\n })\n .join(\"\");\n }\n\n result = result.slice(0, start) + rendered + result.slice(end);\n }\n\n return result;\n}\n\n/**\n * Find the first outermost {% for %}...{% endfor %} block with balanced nesting.\n */\nfunction findOutermostFor(tpl: string): { varName: string; iterExpr: string; body: string; start: number; end: number } | null {\n const openTag = /\\{%[-\\s]*for\\s+(\\w+)\\s+in\\s+([\\w.]+(?:\\([^)]*\\))?(?:\\|[\\w(),\"' ]+)*)\\s*-?%\\}/g;\n const forOrEndfor = /\\{%[-\\s]*(for\\s|endfor)\\s*.*?-?%\\}/g;\n\n const firstOpen = openTag.exec(tpl);\n if (!firstOpen) return null;\n\n const varName = firstOpen[1];\n const iterExpr = firstOpen[2];\n const bodyStart = firstOpen.index + firstOpen[0].length;\n\n // Find matching endfor by counting nesting depth\n forOrEndfor.lastIndex = bodyStart;\n let depth = 1;\n let m: RegExpExecArray | null;\n\n while ((m = forOrEndfor.exec(tpl)) !== null) {\n if (m[1].startsWith(\"for\")) {\n depth++;\n } else {\n depth--;\n if (depth === 0) {\n const body = tpl.slice(bodyStart, m.index);\n return { varName, iterExpr, body, start: firstOpen.index, end: m.index + m[0].length };\n }\n }\n }\n\n return null; // Unmatched for-loop\n}\n\n/**\n * Process {% if EXPR %}...{% else %}...{% endif %} conditionals.\n * Supports {% elif %} as well.\n */\nfunction processConditionals(tpl: string, context: RenderContext): string {\n // Process from innermost out\n let result = tpl;\n let safety = 0;\n\n while (RE_IF_PATTERN.test(result) && safety < 50) {\n safety++;\n result = result.replace(RE_IF_PATTERN, (_match, condition: string, body: string) => {\n // Split on {% else %} and {% elif %}\n const elseMatch = body.split(/\\{%[-\\s]*else\\s*-?%\\}/);\n const ifBody = elseMatch[0];\n const elseBody = elseMatch[1] || \"\";\n\n // Check for {% elif %} (treat as nested if-else)\n const elifParts = ifBody.split(/\\{%[-\\s]*elif\\s+(.*?)\\s*-?%\\}/);\n\n if (elifParts.length > 1) {\n // Has elif branches\n if (evaluateCondition(condition, context)) {\n return elifParts[0];\n }\n // Check elif branches\n for (let i = 1; i < elifParts.length; i += 2) {\n const elifCondition = elifParts[i];\n const elifBody = elifParts[i + 1] || \"\";\n if (evaluateCondition(elifCondition, context)) {\n return elifBody;\n }\n }\n return elseBody;\n }\n\n if (evaluateCondition(condition, context)) {\n return ifBody;\n }\n return elseBody;\n });\n\n RE_IF_PATTERN.lastIndex = 0;\n }\n\n return result;\n}\n\n/**\n * Resolve all {{ expression }} references in the template.\n */\nfunction resolveExpressions(tpl: string, context: RenderContext): string {\n return tpl.replace(/\\{\\{[-\\s]*(.*?)[-\\s]*\\}\\}/g, (_match, expr: string) => {\n const trimmed = expr.trim();\n\n // Handle filters: {{ value|filter }}\n const filterParts = trimmed.split(\"|\");\n const path = filterParts[0].trim();\n\n let value = resolvePath(context, path);\n\n // Apply basic filters\n for (let i = 1; i < filterParts.length; i++) {\n value = applyFilter(value, filterParts[i].trim());\n }\n\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"object\") return JSON.stringify(value);\n // Strip literal \\n sequences that AI sometimes puts in field defaults\n let str = String(value);\n str = str.replace(/\\\\n/g, \" \").replace(/\\n/g, \" \");\n return str;\n });\n}\n\n/**\n * Clean up any remaining HubL tags that weren't handled.\n */\nfunction cleanupRemaining(tpl: string): string {\n // Remove any remaining {% ... %} tags\n tpl = tpl.replace(/\\{%.*?%\\}/gs, \"\");\n // Remove any remaining {{ ... }} that reference unknown paths\n tpl = tpl.replace(/\\{\\{.*?\\}\\}/gs, \"\");\n return tpl;\n}\n\n/**\n * Resolve an iterable expression for {% for %} loops.\n * Handles dotted paths (module.services) and range(start, end) calls.\n */\nfunction resolveIterable(expr: string, context: RenderContext): unknown {\n // Handle range(start, end) — with possible filter on args\n const rangeMatch = expr.match(/^range\\(\\s*(.+?)\\s*,\\s*(.+?)\\s*\\)$/);\n if (rangeMatch) {\n const start = resolveNumericArg(rangeMatch[1], context);\n const end = resolveNumericArg(rangeMatch[2], context);\n const arr: number[] = [];\n for (let i = start; i < end; i++) arr.push(i);\n return arr;\n }\n\n // Handle split('...') filter: \"value|split('\\n')\"\n const splitMatch = expr.match(/^(.+?)\\|split\\(['\"](.+?)['\"]\\)$/);\n if (splitMatch) {\n const val = resolvePath(context, splitMatch[1].trim());\n if (typeof val === \"string\") return val.split(splitMatch[2]);\n return [];\n }\n\n return resolvePath(context, expr);\n}\n\n/**\n * Resolve a numeric argument that may be a literal, a path, or a path|filter.\n */\nfunction resolveNumericArg(arg: string, context: RenderContext): number {\n const trimmed = arg.trim();\n\n // Apply filters (e.g. \"item.rating|int\")\n const filterParts = trimmed.split(\"|\");\n const path = filterParts[0].trim();\n\n // Literal number\n if (!isNaN(Number(path))) return Number(path);\n\n // Path lookup\n let value = resolvePath(context, path);\n for (let i = 1; i < filterParts.length; i++) {\n value = applyFilter(value, filterParts[i].trim());\n }\n return Number(value) || 0;\n}\n\n/**\n * Resolve a dot-path expression against a context object.\n * E.g. \"module.styles.bg_color.color\" → context.module.styles.bg_color.color\n */\nfunction resolvePath(context: RenderContext, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = context;\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n/**\n * Evaluate a simple condition expression.\n * Supports: path truthiness, \"not path\", \"path == value\", \"path != value\"\n */\nfunction evaluateCondition(expr: string, context: RenderContext): boolean {\n const trimmed = expr.trim();\n\n // Handle \"not\" prefix\n if (trimmed.startsWith(\"not \")) {\n return !evaluateCondition(trimmed.slice(4), context);\n }\n\n // Handle \"and\" / \"or\"\n if (trimmed.includes(\" and \")) {\n return trimmed.split(\" and \").every((part) => evaluateCondition(part, context));\n }\n if (trimmed.includes(\" or \")) {\n return trimmed.split(\" or \").some((part) => evaluateCondition(part, context));\n }\n\n // Handle comparison operators\n const eqMatch = trimmed.match(/^(.+?)\\s*(==|!=|>=|<=|>|<)\\s*(.+)$/);\n if (eqMatch) {\n const left = resolvePath(context, eqMatch[1].trim());\n const operator = eqMatch[2];\n let right: unknown = eqMatch[3].trim();\n\n // Parse right side: string literal, number, or path\n if (\n (typeof right === \"string\" && right.startsWith('\"') && right.endsWith('\"')) ||\n (typeof right === \"string\" && right.startsWith(\"'\") && right.endsWith(\"'\"))\n ) {\n right = (right as string).slice(1, -1);\n } else if (!isNaN(Number(right))) {\n right = Number(right);\n } else {\n right = resolvePath(context, right as string);\n }\n\n switch (operator) {\n case \"==\": return left == right;\n case \"!=\": return left != right;\n case \">\": return Number(left) > Number(right);\n case \"<\": return Number(left) < Number(right);\n case \">=\": return Number(left) >= Number(right);\n case \"<=\": return Number(left) <= Number(right);\n }\n }\n\n // Simple truthiness\n const value = resolvePath(context, trimmed);\n return isTruthy(value);\n}\n\n/**\n * Check HubL-style truthiness (empty strings and 0 are falsy).\n */\nfunction isTruthy(value: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (value === \"\") return false;\n if (value === 0) return false;\n if (value === false) return false;\n if (Array.isArray(value) && value.length === 0) return false;\n return true;\n}\n\n/**\n * Apply a basic HubL filter.\n */\nfunction applyFilter(value: unknown, filter: string): unknown {\n const str = value === null || value === undefined ? \"\" : String(value);\n\n // Handle filters with arguments: truncate(100), default(\"fallback\")\n const argMatch = filter.match(/^(\\w+)\\((.*)\\)$/);\n const filterName = argMatch ? argMatch[1] : filter;\n const filterArg = argMatch ? argMatch[2].replace(/^[\"']|[\"']$/g, \"\") : undefined;\n\n switch (filterName) {\n case \"escape\":\n case \"e\":\n return str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n case \"lower\":\n return str.toLowerCase();\n case \"upper\":\n return str.toUpperCase();\n case \"capitalize\":\n return str.charAt(0).toUpperCase() + str.slice(1);\n case \"trim\":\n return str.trim();\n case \"truncate\":\n if (filterArg) {\n const len = parseInt(filterArg, 10);\n return str.length > len ? str.slice(0, len) + \"...\" : str;\n }\n return str;\n case \"default\":\n return isTruthy(value) ? value : (filterArg ?? \"\");\n case \"length\":\n if (Array.isArray(value)) return value.length;\n return str.length;\n case \"join\":\n if (Array.isArray(value)) return value.join(filterArg ?? \", \");\n return str;\n case \"int\":\n case \"float\":\n return Number(str) || 0;\n case \"abs\":\n return Math.abs(Number(str));\n case \"round\":\n return Math.round(Number(str));\n default:\n // Unknown filter — pass through\n return value;\n }\n}\n","/**\n * Preview builder — assembles rendered HubL modules into a full HTML page.\n */\n\nimport {\n renderHubL,\n buildContextFromFields,\n assemblePreview,\n type FieldDef,\n} from \"../hubl/renderer.js\";\nimport { getSession, getOrderedModules } from \"./session.js\";\n\n/**\n * Extract CSS custom property values from shared CSS.\n * Returns defaults for dark theme if properties aren't found.\n */\nfunction extractThemeColors(sharedCss: string | undefined): {\n bg: string;\n surface: string;\n text: string;\n textMuted: string;\n border: string;\n} {\n if (!sharedCss) {\n return { bg: \"#0f0f14\", surface: \"#1a1a20\", text: \"#ffffff\", textMuted: \"#666\", border: \"#333\" };\n }\n\n const extract = (names: string[], fallback: string): string => {\n for (const name of names) {\n // Match --name: #hex or --name: rgb(...) etc.\n const re = new RegExp(`${name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*:\\\\s*([^;})]+)`, \"i\");\n const m = sharedCss.match(re);\n if (m) return m[1].trim();\n }\n return fallback;\n };\n\n const bg = extract([\"--bg\", \"--background\", \"--color-bg\", \"--bg-primary\", \"--body-bg\"], \"#0f0f14\");\n const surface = extract([\"--surface\", \"--bg-secondary\", \"--card-bg\", \"--color-surface\"], bg);\n const text = extract([\"--text\", \"--color-text\", \"--text-primary\", \"--fg\", \"--foreground\"], \"#ffffff\");\n const textMuted = extract([\"--text-muted\", \"--text-secondary\", \"--muted\", \"--color-text-muted\"], \"#666\");\n const border = extract([\"--border\", \"--border-color\", \"--color-border\"], \"#333\");\n\n return { bg, surface, text, textMuted, border };\n}\n\n/**\n * Build a full preview HTML page from the current session state.\n * Each module's HubL template is rendered with its fields.json defaults,\n * then assembled with shared CSS/JS into a complete page.\n */\nexport function buildPreviewHtml(): string {\n const session = getSession();\n if (!session) {\n return welcomePreview();\n }\n\n const modules = getOrderedModules();\n const moduleOrder = session.moduleOrder || [];\n\n // Nothing to show yet — no modules and no pending order\n if (modules.length === 0 && moduleOrder.length === 0) {\n return welcomePreview();\n }\n\n const renderedModules: string[] = [];\n const moduleCssArray: string[] = [];\n const moduleJsArray: string[] = [];\n const renderedNames = new Set<string>();\n\n for (const mod of modules) {\n // Skip template-like content that was accidentally stored as a module\n if (mod.moduleHtml.includes(\"dnd_area\") || mod.moduleHtml.includes(\"extends \")) {\n continue;\n }\n\n // Build context from fields.json defaults\n let context: { module: Record<string, unknown> };\n try {\n const fields: FieldDef[] = JSON.parse(mod.fieldsJson);\n context = { module: buildContextFromFields(fields) };\n } catch {\n context = { module: {} };\n }\n\n // Render HubL template with context\n const rendered = renderHubL(mod.moduleHtml, context);\n\n // Wrap each module in a container with id + data attribute for anchor links\n const anchorId = mod.moduleName.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n renderedModules.push(\n `<div class=\"vibespot-module\" id=\"${anchorId}\" data-module=\"${mod.moduleName}\">${rendered}</div>`\n );\n renderedNames.add(mod.moduleName);\n\n if (mod.moduleCss) moduleCssArray.push(mod.moduleCss);\n if (mod.moduleJs) moduleJsArray.push(mod.moduleJs);\n }\n\n // Render skeleton placeholders for modules in moduleOrder that aren't generated yet\n const theme = extractThemeColors(session.sharedCss);\n for (const name of moduleOrder) {\n if (!renderedNames.has(name)) {\n const anchorId = name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n renderedModules.push(\n `<div class=\"vibespot-module vibespot-module--pending\" id=\"${anchorId}\" data-module=\"${name}\">\n <div class=\"vibespot-placeholder\">\n <div class=\"vibespot-placeholder__name\">${name}</div>\n </div>\n </div>`\n );\n }\n }\n\n // Add placeholder CSS for pending modules — uses theme colors\n const placeholderCss = `body{background:${theme.bg}}.vibespot-placeholder{display:flex;align-items:center;justify-content:center;min-height:200px;padding:3rem;background:${theme.surface};border:1px dashed ${theme.border};border-radius:12px;margin:1rem 0}.vibespot-placeholder__name{font-size:1.5rem;font-weight:600;font-family:system-ui,sans-serif;color:${theme.textMuted};letter-spacing:.5px;animation:vp-fade 2s ease-in-out infinite}@keyframes vp-fade{0%,100%{opacity:.3}50%{opacity:.8}}`;\n\n return assemblePreview({\n renderedModules,\n sharedCss: session.sharedCss,\n moduleCssArray: [placeholderCss, ...moduleCssArray],\n sharedJs: session.sharedJs,\n moduleJsArray,\n });\n}\n\n/**\n * Static welcome screen — shown before first generation.\n */\nfunction welcomePreview(): 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\">\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n background: #0f0f14;\n color: #888;\n }\n .welcome {\n text-align: center;\n padding: 2rem;\n }\n .welcome__wave {\n font-size: 4rem;\n margin-bottom: 1.2rem;\n opacity: 0.4;\n animation: float 3s ease-in-out infinite;\n }\n @keyframes float {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-6px); }\n }\n .welcome__title {\n font-size: 1.4rem;\n font-weight: 600;\n color: #bbb;\n letter-spacing: 0.5px;\n margin-bottom: 0.4rem;\n }\n .welcome__sub {\n font-size: 1rem;\n color: #666;\n font-weight: 300;\n }\n</style>\n</head>\n<body>\n<div class=\"welcome\">\n <div class=\"welcome__wave\">\\u224B</div>\n <div class=\"welcome__title\">vibeSpot</div>\n <div class=\"welcome__sub\">Build Something Great</div>\n</div>\n</body>\n</html>`;\n}\n\n/**\n * Build a preview HTML page for a single module (used by the dashboard module library).\n */\nexport function buildModulePreviewHtml(moduleName: string): string {\n const session = getSession();\n if (!session) return \"\";\n\n // Find the module across all templates\n let mod: typeof session.modules[0] | undefined;\n for (const tpl of session.templates) {\n mod = tpl.modules.find((m) => m.moduleName === moduleName);\n if (mod) break;\n }\n // Fallback: check flat modules array\n if (!mod) {\n mod = session.modules.find((m) => m.moduleName === moduleName);\n }\n if (!mod) return \"\";\n\n let context: { module: Record<string, unknown> };\n try {\n const fields: FieldDef[] = JSON.parse(mod.fieldsJson);\n context = { module: buildContextFromFields(fields) };\n } catch {\n context = { module: {} };\n }\n\n const rendered = renderHubL(mod.moduleHtml, context);\n\n return assemblePreview({\n renderedModules: [\n `<div class=\"vibespot-module\" data-module=\"${mod.moduleName}\">${rendered}</div>`,\n ],\n sharedCss: session.sharedCss,\n moduleCssArray: mod.moduleCss ? [mod.moduleCss] : [],\n sharedJs: session.sharedJs,\n moduleJsArray: mod.moduleJs ? [mod.moduleJs] : [],\n });\n}\n\n// Note: The generating screen (spinner + rotating messages) is now\n// rendered client-side in preview.js to avoid an extra server round-trip.\n","/**\n * Structured logging helper.\n * Writes to both console and a rotating log file at ~/.vibespot/logs/.\n * Log files are named vibespot-YYYY-MM-DD.log and auto-pruned after 7 days.\n */\n\nimport { appendFileSync, mkdirSync, readdirSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst LOG_DIR = join(homedir(), \".vibespot\", \"logs\");\nconst MAX_AGE_DAYS = 7;\n\nlet logDirReady = false;\nlet pruned = false;\n\nfunction ensureLogDir(): void {\n if (logDirReady) return;\n try {\n mkdirSync(LOG_DIR, { recursive: true });\n logDirReady = true;\n } catch {\n // If we can't create the dir, file logging silently disabled\n }\n}\n\nfunction pruneOldLogs(): void {\n if (pruned) return;\n pruned = true;\n try {\n const cutoff = Date.now() - MAX_AGE_DAYS * 86400_000;\n for (const f of readdirSync(LOG_DIR)) {\n if (!f.startsWith(\"vibespot-\") || !f.endsWith(\".log\")) continue;\n // Extract date from filename: vibespot-YYYY-MM-DD.log\n const dateStr = f.slice(9, 19);\n const ts = new Date(dateStr).getTime();\n if (ts && ts < cutoff) {\n try { unlinkSync(join(LOG_DIR, f)); } catch { /* ignore */ }\n }\n }\n } catch { /* ignore */ }\n}\n\nfunction todayFile(): string {\n const d = new Date();\n const date = d.toISOString().slice(0, 10);\n return join(LOG_DIR, `vibespot-${date}.log`);\n}\n\nfunction timestamp(): string {\n return new Date().toISOString().slice(11, 23); // HH:mm:ss.SSS\n}\n\nfunction writeToFile(level: string, line: string): void {\n ensureLogDir();\n if (!logDirReady) return;\n if (!pruned) pruneOldLogs();\n try {\n appendFileSync(todayFile(), `${timestamp()} ${level} ${line}\\n`);\n } catch { /* ignore — don't break the app over logging */ }\n}\n\nexport const log = {\n info(context: string, message: string, data?: Record<string, unknown>): void {\n const line = data\n ? `[${context}] ${message} ${JSON.stringify(data)}`\n : `[${context}] ${message}`;\n console.log(line);\n writeToFile(\"INFO\", line);\n },\n\n warn(context: string, message: string, data?: Record<string, unknown>): void {\n const line = data\n ? `[${context}] ${message} ${JSON.stringify(data)}`\n : `[${context}] ${message}`;\n console.warn(line);\n writeToFile(\"WARN\", line);\n },\n\n error(context: string, message: string, err?: unknown): void {\n const errMsg = err instanceof Error ? err.message : err ? String(err) : \"\";\n const line = errMsg\n ? `[${context}] ${message}: ${errMsg}`\n : `[${context}] ${message}`;\n console.error(line);\n writeToFile(\"ERROR\", line);\n },\n};\n","/**\n * AI response parser — extracts vibespot-modules JSON from AI responses.\n */\n\nimport { updateModules } from \"./session.js\";\nimport type { ModuleFiles } from \"../ai/engine.js\";\nimport { log } from \"./log.js\";\n\n/**\n * Try JSON.parse, and if it fails, attempt to repair common AI JSON issues\n * (unescaped quotes inside string values) and retry.\n */\nexport function tryParseJSON(raw: string): unknown | null {\n try {\n return JSON.parse(raw);\n } catch {\n // Fall through to repair\n }\n\n let repaired = raw;\n let lastPos = -1;\n for (let attempt = 0; attempt < 20; attempt++) {\n try {\n return JSON.parse(repaired);\n } catch (err) {\n if (!(err instanceof SyntaxError)) return null;\n const posMatch = /position (\\d+)/.exec(err.message);\n if (!posMatch) return null;\n const pos = parseInt(posMatch[1], 10);\n if (pos <= lastPos) return null;\n lastPos = pos;\n const searchStart = Math.max(0, pos - 5);\n const nearSlice = repaired.slice(searchStart, pos + 1);\n const lastQuote = nearSlice.lastIndexOf('\"');\n if (lastQuote === -1) return null;\n const absPos = searchStart + lastQuote;\n if (absPos > 0 && repaired[absPos - 1] === \"\\\\\") return null;\n repaired = repaired.slice(0, absPos) + '\\\\\"' + repaired.slice(absPos + 1);\n }\n }\n return null;\n}\n\n/**\n * Attempt to salvage a truncated JSON response by finding complete module objects.\n */\nexport function tryRepairTruncatedJSON(raw: string): Record<string, unknown> | null {\n const modulesIdx = raw.indexOf('\"modules\"');\n if (modulesIdx === -1) return null;\n\n const arrayStart = raw.indexOf(\"[\", modulesIdx);\n if (arrayStart === -1) return null;\n\n let lastCompleteModule = -1;\n let braceDepth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = arrayStart + 1; i < raw.length; i++) {\n const ch = raw[i];\n if (escaped) { escaped = false; continue; }\n if (ch === \"\\\\\") { escaped = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n\n if (ch === \"{\") braceDepth++;\n if (ch === \"}\") {\n braceDepth--;\n if (braceDepth === 0) {\n lastCompleteModule = i;\n }\n }\n }\n\n if (lastCompleteModule === -1) return null;\n\n const upToLastModule = raw.slice(0, lastCompleteModule + 1);\n const repaired = upToLastModule + \"]}\";\n\n const jsonStr = repaired.trimStart().startsWith(\"{\") ? repaired : \"{\" + repaired;\n return tryParseJSON(jsonStr) as Record<string, unknown> | null;\n}\n\n/**\n * Convert a raw module object from AI JSON into a ModuleFiles entry.\n */\nfunction toModuleFiles(m: Record<string, unknown>): ModuleFiles {\n return {\n moduleName: String(m.moduleName || \"\"),\n fieldsJson: typeof m.fieldsJson === \"string\"\n ? m.fieldsJson\n : JSON.stringify(m.fieldsJson, null, 2),\n metaJson: typeof m.metaJson === \"string\"\n ? m.metaJson\n : JSON.stringify(m.metaJson, null, 2),\n moduleHtml: String(m.moduleHtml || \"\"),\n moduleCss: String(m.moduleCss || \"\"),\n moduleJs: m.moduleJs ? String(m.moduleJs) : undefined,\n };\n}\n\n/**\n * Parse vibespot-modules JSON blocks from an AI response and update the session.\n */\nexport function parseAndApplyModules(\n response: string,\n onWarning?: (warning: string) => void\n): void {\n let modulesApplied = false;\n let match;\n\n // Look for ```vibespot-modules ... ``` blocks\n const blockPattern = /```vibespot-modules\\s*\\n?([\\s\\S]*?)```/g;\n\n while ((match = blockPattern.exec(response)) !== null) {\n try {\n log.info(\"parse\", \"Found vibespot-modules block\", { length: match[1].length });\n const data = tryParseJSON(match[1]);\n if (!data || typeof data !== \"object\") {\n log.warn(\"parse\", \"tryParseJSON returned non-object\", { result: typeof data });\n throw new Error(\"Invalid JSON after repair\");\n }\n\n const obj = data as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules)) {\n updateModules({\n modules: obj.modules.map((m: Record<string, unknown>) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n }\n } catch (err) {\n log.warn(\"parse\", \"Failed to parse vibespot-modules block\", { error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n // Also try to find standalone JSON that looks like module data\n if (!modulesApplied) {\n const jsonPattern = /```(?:json)?\\s*\\n([\\s\\S]*?)```/g;\n while ((match = jsonPattern.exec(response)) !== null) {\n if (!match[1].includes('\"modules\"')) continue;\n try {\n const data = tryParseJSON(match[1]);\n if (!data || typeof data !== \"object\") throw new Error(\"Invalid JSON after repair\");\n const obj = data as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules)) {\n updateModules({\n modules: obj.modules.map((m: Record<string, unknown>) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n }\n } catch (err) {\n log.warn(\"parse\", \"Failed to parse JSON module block\", { error: err instanceof Error ? err.message : String(err) });\n }\n }\n }\n\n // Handle truncated responses (hit max_tokens — unclosed code fence)\n if (!modulesApplied) {\n const fenceCount = (response.match(/```/g) || []).length;\n if (fenceCount % 2 !== 0 && response.includes('\"modules\"')) {\n log.info(\"parse\", \"Detected truncated response (odd fence count), attempting salvage\");\n const lastFenceIdx = response.lastIndexOf(\"```\");\n let truncated = response.slice(lastFenceIdx + 3);\n truncated = truncated.replace(/^[\\w-]*\\s*\\n?/, \"\");\n const salvaged = tryRepairTruncatedJSON(truncated);\n if (salvaged) {\n const obj = salvaged as Record<string, unknown>;\n if (obj.modules && Array.isArray(obj.modules) && obj.modules.length > 0) {\n log.info(\"parse\", \"Salvaged modules from truncated response\", { count: obj.modules.length });\n updateModules({\n modules: (obj.modules as Record<string, unknown>[]).map((m) => toModuleFiles(m)),\n sharedCss: obj.sharedCss !== undefined ? String(obj.sharedCss) : undefined,\n sharedJs: obj.sharedJs !== undefined ? String(obj.sharedJs) : undefined,\n });\n modulesApplied = true;\n if (onWarning) {\n onWarning(\"Response was truncated — some modules may be incomplete. Try sending your request again for the full set.\");\n }\n }\n }\n }\n }\n\n // Warn user if the response looked like it should contain modules but parsing failed\n if (!modulesApplied) {\n log.info(\"parse\", \"No modules applied\", {\n responseLength: response.length,\n hasVibespot: response.includes(\"vibespot-modules\"),\n hasModules: response.includes('\"modules\"'),\n fenceCount: (response.match(/```/g) || []).length,\n });\n const hasModuleRef = response.includes(\"vibespot-modules\") || response.includes('\"modules\"');\n const describesProse = /\\bmodule|modul/i.test(response) &&\n (/\\bcreated?\\b|\\berstellt\\b|\\bgenerat/i.test(response) || /\\|.*\\|.*\\|/m.test(response));\n\n if (hasModuleRef || describesProse) {\n const msg = hasModuleRef\n ? \"Module changes could not be applied — the AI response contained invalid JSON. Try sending your request again.\"\n : \"The AI described modules but did not include the required structured data. Try sending your request again.\";\n log.warn(\"parse\", msg);\n if (onWarning) {\n onWarning(msg);\n }\n }\n }\n}\n","/**\n * AI prompt construction — system prompts, state context, message building.\n */\n\nimport { getConversionGuide, getDesignGuide, getContentGuide, getHubspotRules, getPageTypeGuide, getHumanifyGuide } from \"../ai/prompts.js\";\nimport {\n getSession,\n getActiveTemplate,\n getModuleLibrary,\n} from \"./session.js\";\nimport type { UploadedFileContext } from \"./routes/upload-files.js\";\n\n// Multimodal content block types (compatible with Anthropic/OpenAI APIs)\nexport type ContentBlock =\n | { type: \"text\"; text: string }\n | { type: \"image\"; source: { type: \"base64\"; media_type: string; data: string } };\n\nexport type MultimodalMessage = {\n role: \"user\" | \"assistant\";\n content: string | ContentBlock[];\n};\n\n/**\n * Get the active template's page type and brand assets from the session.\n */\nexport function getPromptContext(): { pageType?: string; brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean } } {\n const session = getSession();\n if (!session) return {};\n const tpl = getActiveTemplate();\n return {\n pageType: tpl?.pageType,\n brandAssets: session.brandAssets,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic prompt caching — system prompt as block array with cache_control\n// ---------------------------------------------------------------------------\n\nexport interface SystemPromptBlock {\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n}\n\n/**\n * Build the system prompt as an array of blocks for Anthropic API engines.\n * Static reference guides are marked with cache_control for prompt caching.\n */\nexport function buildVibeSystemPromptBlocks(\n conversionGuide: string,\n themeName: string,\n editMode: boolean = false,\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): SystemPromptBlock[] {\n // Block 1: Core instructions (semi-static — varies by themeName)\n const core = buildCoreInstructions(themeName, editMode);\n const blocks: SystemPromptBlock[] = [{ type: \"text\", text: core }];\n\n // Block 2: Reference guides — CACHED (identical across all calls)\n if (editMode) {\n const guides = `## HubSpot CMS Rules\\n${getHubspotRules()}\\n\\n## Conversion Guide Reference\\n${conversionGuide}`;\n blocks.push({ type: \"text\", text: guides, cache_control: { type: \"ephemeral\" } });\n } else {\n const guides = `## Design Quality\n- Use modern, clean design with proper spacing and typography\n- Include responsive CSS (mobile breakpoint at 767px)\n- Add scroll animation classes where appropriate\n- Use CSS custom properties for the design system\n- Make content editable through fields.json (headlines, text, colors, images, links)\n\n## Scroll Animation CSS Fallback (IMPORTANT)\nWhen using scroll-animate classes (opacity: 0 → visible), you MUST include a CSS-only fallback animation in sharedCss that auto-reveals elements after a delay. This ensures content is visible even if the JS file fails to load:\n\\`\\`\\`css\n@keyframes scroll-animate-fallback {\n to { opacity: 1; transform: none; }\n}\n.scroll-animate {\n animation: scroll-animate-fallback 0.1s 3s forwards;\n}\n.scroll-animate.visible {\n animation: none;\n}\n\\`\\`\\`\nThis makes elements appear after 3 seconds if JS never adds the .visible class. Once JS runs normally and adds .visible, the animation is cancelled.\n\n## Design Guide\n${getDesignGuide()}\n\n## Content & Copywriting Guide\n${getContentGuide()}\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}`;\n blocks.push({ type: \"text\", text: guides, cache_control: { type: \"ephemeral\" } });\n }\n\n // Block 3: Dynamic content (changes per session/template — NOT cached)\n const dynamic = buildDynamicSection(pageType, brandAssets);\n if (dynamic) {\n blocks.push({ type: \"text\", text: dynamic });\n }\n\n // Block 4: Format reminder\n blocks.push({\n type: \"text\",\n text: `## REMINDER — Output Format (CRITICAL)\\nYour response MUST contain a \\`\\`\\`vibespot-modules code block with the full JSON. Without this block, no modules will be created. Do NOT respond with only text, tables, or descriptions. The JSON block is mandatory.`,\n });\n\n return blocks;\n}\n\n/** Build the dynamic (per-session) prompt sections. */\nfunction buildDynamicSection(\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): string {\n const parts: string[] = [];\n\n if (pageType) {\n const section = getPageTypeGuide(pageType);\n if (section) parts.push(`## Page Type Context\\n${section}`);\n }\n\n if (brandAssets?.styleguide) {\n parts.push(`## Brand Style Guide\\n${brandAssets.styleguide}`);\n }\n if (brandAssets?.brandvoice) {\n parts.push(`## Brand Voice\\n${brandAssets.brandvoice}`);\n }\n if (brandAssets?.humanify !== false) {\n const humanifyGuide = getHumanifyGuide();\n if (humanifyGuide) parts.push(`## Anti-AI Copy Rules (Humanify)\\n${humanifyGuide}`);\n }\n\n return parts.join(\"\\n\\n\");\n}\n\n/** Build the core instructions block (reused by both string and block builders). */\nfunction buildCoreInstructions(themeName: string, editMode: boolean): string {\n return `You are vibeSpot, an AI that builds HubSpot CMS landing pages from natural language descriptions.\n\n## Your Role\nYou generate native HubSpot CMS modules directly from user descriptions. Every module you create is immediately compatible with HubSpot's drag-and-drop page editor.\n\n## Output Format — CRITICAL\nYou MUST include a \\`\\`\\`vibespot-modules code block with ALL module data as JSON. This is the ONLY way modules get created. A text summary, table, or description of modules does NOT work — you must output the actual JSON.\n\n\\`\\`\\`vibespot-modules\n{\n \"modules\": [\n {\n \"moduleName\": \"Hero\",\n \"fieldsJson\": \"...\",\n \"metaJson\": \"...\",\n \"moduleHtml\": \"...\",\n \"moduleCss\": \"...\",\n \"moduleJs\": null\n }\n ],\n \"sharedCss\": \"...\",\n \"sharedJs\": \"...\"\n}\n\\`\\`\\`\n\nNEVER respond with only a text summary. The vibespot-modules JSON block is mandatory.\n\n## Rules\n- fieldsJson, metaJson must be valid JSON strings\n- moduleHtml uses HubL template syntax ({{ module.field_name }})\n- moduleCss is vanilla CSS (no Tailwind, no Sass)\n- moduleJs is optional vanilla JS (wrapped in IIFE)\n- NEVER use CDN imports (@import url(), <link> to external CDNs like Google Fonts, cdnjs, unpkg, jsdelivr)\n- For fonts, use system font stacks with good fallbacks. Define them as CSS custom properties in sharedCss:\n --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-mono: 'SF Mono', SFMono-Regular, Consolas, monospace;\n- All assets must be self-contained — no external HTTP requests in CSS or HTML\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER put literal \\\\n newline sequences in field default values — use plain text without line breaks\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- All CSS classes must use a unique prefix \"${themeName}-\" to avoid theme conflicts\n- Use BEM naming: ${themeName}-module__element--modifier\n- metaJson must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 } and iterate with {% for %}\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder image\", \"width\": 800, \"height\": 600 }\n\n## Images & Media\n- Users can upload images that get placed in the theme's assets/ folder automatically\n- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url(\"${themeName}/assets/filename.ext\") }}\n- IMPORTANT: get_asset_url() paths must include the theme name prefix \"${themeName}/\" because HubSpot resolves from the Design Manager root\n- For background images with uploaded assets: style=\"background-image: url('{{ get_asset_url(\\\\\"${themeName}/assets/filename.ext\\\\\") }}')\"\n- For images without an uploaded asset, use image fields (type \"image\") with placehold.co defaults\n- ALWAYS use image fields (type \"image\") so users can swap images in the page editor\n- Use placehold.co URLs as defaults so the preview looks complete (e.g. https://placehold.co/800x600/1a1a2e/ffffff?text=Hero+Image)\n- In module.html, render images with: <img src=\"{{ module.field_name.src }}\" alt=\"{{ module.field_name.alt }}\" width=\"{{ module.field_name.width }}\" height=\"{{ module.field_name.height }}\">\n- For background images in CSS, use inline styles: style=\"background-image: url('{{ module.field_name.src }}')\"\n- Size placeholders appropriately for their context (hero: 1920x800, cards: 600x400, icons: 200x200, avatars: 150x150)\n- If the user's intent is ambiguous (design reference vs page asset), ask them to clarify\n\n## Navigation & Anchor Links\n- For anchor links, add an id attribute directly on the module's root element in module.html:\n <section id=\"pricing\" class=\"...\"> (NOT on an external wrapper — HubSpot's dnd system strips those)\n- The id should be the moduleName lowercased with spaces replaced by hyphens (e.g. \"Pricing Cards\" → id=\"pricing-cards\")\n- For navigation/menu modules, use anchor links that match these ids: e.g. href=\"#features\"\n- Always include smooth scrolling behavior in navigation link clicks\n- For nav modules, make menu items editable via a repeater group with \"label\" (text) and \"anchor\" (text) fields` +\n (editMode\n ? `\n\n## When modifying existing modules\nThe current template's modules are listed in page order in the user message. This sequence forms the page narrative.\n\n- **Modify**: When the user references an existing module by name or describes changes to existing content, update that module's code. Keep the moduleName unchanged.\n- **Add**: When the user asks for a new section, create a new module and insert it at the narratively correct position. Consider the page flow: navigation → hero → content sections → social proof → CTA → footer.\n- **Rearrange**: When the user asks to reorder sections, include a \"moduleOrder\" array in the vibespot-modules JSON with the new sequence of module names.\n- **Remove**: When the user asks to remove a section, omit it from the output.\n- **Preserve**: Always include ALL modules you want to keep (modified + unchanged) in your output. Modules omitted from the output will be removed from the page.\n- **Design consistency**: Match the existing theme's design language — reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`\n : \"\");\n}\n\n/**\n * Build the system prompt for vibe coding mode.\n */\nexport function buildVibeSystemPrompt(\n conversionGuide: string,\n themeName: string,\n editMode: boolean = false,\n pageType?: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean }\n): string {\n const core = `You are vibeSpot, an AI that builds HubSpot CMS landing pages from natural language descriptions.\n\n## Your Role\nYou generate native HubSpot CMS modules directly from user descriptions. Every module you create is immediately compatible with HubSpot's drag-and-drop page editor.\n\n## Output Format — CRITICAL\nYou MUST include a \\`\\`\\`vibespot-modules code block with ALL module data as JSON. This is the ONLY way modules get created. A text summary, table, or description of modules does NOT work — you must output the actual JSON.\n\n\\`\\`\\`vibespot-modules\n{\n \"modules\": [\n {\n \"moduleName\": \"Hero\",\n \"fieldsJson\": \"...\",\n \"metaJson\": \"...\",\n \"moduleHtml\": \"...\",\n \"moduleCss\": \"...\",\n \"moduleJs\": null\n }\n ],\n \"sharedCss\": \"...\",\n \"sharedJs\": \"...\"\n}\n\\`\\`\\`\n\nNEVER respond with only a text summary. The vibespot-modules JSON block is mandatory.\n\n## Rules\n- fieldsJson, metaJson must be valid JSON strings\n- moduleHtml uses HubL template syntax ({{ module.field_name }})\n- moduleCss is vanilla CSS (no Tailwind, no Sass)\n- moduleJs is optional vanilla JS (wrapped in IIFE)\n- NEVER use CDN imports (@import url(), <link> to external CDNs like Google Fonts, cdnjs, unpkg, jsdelivr)\n- For fonts, use system font stacks with good fallbacks. Define them as CSS custom properties in sharedCss:\n --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n --font-mono: 'SF Mono', SFMono-Regular, Consolas, monospace;\n- All assets must be self-contained — no external HTTP requests in CSS or HTML\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER put literal \\\\n newline sequences in field default values — use plain text without line breaks\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- All CSS classes must use a unique prefix \"${themeName}-\" to avoid theme conflicts\n- Use BEM naming: ${themeName}-module__element--modifier\n- metaJson must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 } and iterate with {% for %}\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder image\", \"width\": 800, \"height\": 600 }\n\n## Images & Media\n- Users can upload images that get placed in the theme's assets/ folder automatically\n- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url(\"${themeName}/assets/filename.ext\") }}\n- IMPORTANT: get_asset_url() paths must include the theme name prefix \"${themeName}/\" because HubSpot resolves from the Design Manager root\n- For background images with uploaded assets: style=\"background-image: url('{{ get_asset_url(\\\\\"${themeName}/assets/filename.ext\\\\\") }}')\"\n- For images without an uploaded asset, use image fields (type \"image\") with placehold.co defaults\n- ALWAYS use image fields (type \"image\") so users can swap images in the page editor\n- Use placehold.co URLs as defaults so the preview looks complete (e.g. https://placehold.co/800x600/1a1a2e/ffffff?text=Hero+Image)\n- In module.html, render images with: <img src=\"{{ module.field_name.src }}\" alt=\"{{ module.field_name.alt }}\" width=\"{{ module.field_name.width }}\" height=\"{{ module.field_name.height }}\">\n- For background images in CSS, use inline styles: style=\"background-image: url('{{ module.field_name.src }}')\"\n- Size placeholders appropriately for their context (hero: 1920x800, cards: 600x400, icons: 200x200, avatars: 150x150)\n- If the user's intent is ambiguous (design reference vs page asset), ask them to clarify\n\n## Navigation & Anchor Links\n- For anchor links, add an id attribute directly on the module's root element in module.html:\n <section id=\"pricing\" class=\"...\"> (NOT on an external wrapper — HubSpot's dnd system strips those)\n- The id should be the moduleName lowercased with spaces replaced by hyphens (e.g. \"Pricing Cards\" → id=\"pricing-cards\")\n- For navigation/menu modules, use anchor links that match these ids: e.g. href=\"#features\"\n- Always include smooth scrolling behavior in navigation link clicks\n- For nav modules, make menu items editable via a repeater group with \"label\" (text) and \"anchor\" (text) fields\n\n## When modifying existing modules\nThe current template's modules are listed in page order in the user message. This sequence forms the page narrative.\n\n- **Modify**: When the user references an existing module by name or describes changes to existing content, update that module's code. Keep the moduleName unchanged.\n- **Add**: When the user asks for a new section, create a new module and insert it at the narratively correct position. Consider the page flow: navigation → hero → content sections → social proof → CTA → footer.\n- **Rearrange**: When the user asks to reorder sections, include a \"moduleOrder\" array in the vibespot-modules JSON with the new sequence of module names.\n- **Remove**: When the user asks to remove a section, omit it from the output.\n- **Preserve**: Always include ALL modules you want to keep (modified + unchanged) in your output. Modules omitted from the output will be removed from the page.\n- **Design consistency**: Match the existing theme's design language — reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`;\n\n const pageTypeSection = pageType ? getPageTypeGuide(pageType) : \"\";\n const pageTypePrompt = pageTypeSection ? `\\n\\n## Page Type Context\\n${pageTypeSection}` : \"\";\n\n // Brand assets are included in both creation and edit modes for design consistency\n let brandPrompt = \"\";\n if (brandAssets?.styleguide) {\n brandPrompt += `\\n\\n## Brand Style Guide\\n${brandAssets.styleguide}`;\n }\n if (brandAssets?.brandvoice) {\n brandPrompt += `\\n\\n## Brand Voice\\n${brandAssets.brandvoice}`;\n }\n if (brandAssets?.humanify !== false) {\n const humanifyGuide = getHumanifyGuide();\n if (humanifyGuide) {\n brandPrompt += `\\n\\n## Anti-AI Copy Rules (Humanify)\\n${humanifyGuide}`;\n }\n }\n\n const formatReminder = `\n\n## REMINDER — Output Format (CRITICAL)\nYour response MUST contain a \\`\\`\\`vibespot-modules code block with the full JSON. Without this block, no modules will be created. Do NOT respond with only text, tables, or descriptions. The JSON block is mandatory.`;\n\n if (editMode) {\n return core + pageTypePrompt + brandPrompt + `\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}` + formatReminder;\n }\n\n return core + pageTypePrompt + brandPrompt + `\n\n## Design Quality\n- Use modern, clean design with proper spacing and typography\n- Include responsive CSS (mobile breakpoint at 767px)\n- Add scroll animation classes where appropriate\n- Use CSS custom properties for the design system\n- Make content editable through fields.json (headlines, text, colors, images, links)\n\n## Scroll Animation CSS Fallback (IMPORTANT)\nWhen using scroll-animate classes (opacity: 0 → visible), you MUST include a CSS-only fallback animation in sharedCss that auto-reveals elements after a delay. This ensures content is visible even if the JS file fails to load:\n\\`\\`\\`css\n@keyframes scroll-animate-fallback {\n to { opacity: 1; transform: none; }\n}\n.scroll-animate {\n animation: scroll-animate-fallback 0.1s 3s forwards;\n}\n.scroll-animate.visible {\n animation: none;\n}\n\\`\\`\\`\nThis makes elements appear after 3 seconds if JS never adds the .visible class. Once JS runs normally and adds .visible, the animation is cancelled.\n\n## Design Guide\n${getDesignGuide()}\n\n## Content & Copywriting Guide\n${getContentGuide()}\n\n## HubSpot CMS Rules\n${getHubspotRules()}\n\n## Conversion Guide Reference\n${conversionGuide}` + formatReminder;\n}\n\n/**\n * Build a summary of the current module state for inclusion in user messages.\n */\nexport function buildStateContext(): string {\n const session = getSession()!;\n const parts: string[] = [];\n const modules = session.modules;\n const total = modules.length;\n\n if (total > 0) {\n // Page narrative summary — helps AI understand the page structure\n parts.push(\"\\n\\n## Page Narrative (module sequence)\\n\");\n parts.push(`This template has ${total} module${total === 1 ? \"\" : \"s\"} in this order:\\n`);\n for (let i = 0; i < total; i++) {\n parts.push(`${i + 1}. ${modules[i].moduleName}\\n`);\n }\n parts.push(`\\nWhen the user asks to modify this page, decide whether to MODIFY existing modules, ADD new ones at the right narrative position, REARRANGE the sequence, or REMOVE sections. Always include ALL modules you want to keep in your output.\\n`);\n\n // Detailed module state\n parts.push(\"\\n## Current Module State\\n\");\n for (let i = 0; i < total; i++) {\n const mod = modules[i];\n parts.push(`\\n### ${i + 1}/${total}: ${mod.moduleName}.module\\n`);\n parts.push(`**fields.json:**\\n\\`\\`\\`json\\n${mod.fieldsJson}\\n\\`\\`\\`\\n`);\n parts.push(`**module.html:**\\n\\`\\`\\`html\\n${mod.moduleHtml}\\n\\`\\`\\`\\n`);\n parts.push(`**module.css:**\\n\\`\\`\\`css\\n${mod.moduleCss}\\n\\`\\`\\`\\n`);\n if (mod.moduleJs) {\n parts.push(`**module.js:**\\n\\`\\`\\`js\\n${mod.moduleJs}\\n\\`\\`\\`\\n`);\n }\n }\n if (session.sharedCss) {\n parts.push(`\\n### Shared CSS\\n\\`\\`\\`css\\n${session.sharedCss}\\n\\`\\`\\`\\n`);\n }\n if (session.sharedJs) {\n parts.push(`\\n### Shared JS\\n\\`\\`\\`js\\n${session.sharedJs}\\n\\`\\`\\`\\n`);\n }\n }\n\n const library = getModuleLibrary();\n const currentModuleNames = new Set(session.modules.map((m) => m.moduleName));\n const otherModules = library.filter((e) => !currentModuleNames.has(e.module.moduleName));\n if (otherModules.length > 0) {\n parts.push(\"\\n\\n## Available modules in this theme (reusable)\\n\");\n for (const entry of otherModules) {\n parts.push(`- ${entry.module.moduleName} (used in: ${entry.usedIn.join(\", \")})\\n`);\n }\n parts.push(\"\\nThe user can ask to reuse any of these modules by name.\\n\");\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build the messages array with state context appended to the latest user message.\n * When fileContexts are provided, the last message uses multimodal content blocks.\n */\nexport function buildMessagesWithContext(\n userMessage: string,\n fileContexts?: UploadedFileContext[]\n): MultimodalMessage[] {\n const session = getSession()!;\n\n // Build history from session messages, but exclude the latest user message\n // if it matches the one we're about to send (it was already added to the\n // session by the WebSocket handler before generation starts).\n let history = session.messages.slice(-20);\n if (\n history.length > 0 &&\n history[history.length - 1].role === \"user\" &&\n history[history.length - 1].content === userMessage\n ) {\n history = history.slice(0, -1);\n }\n\n const messages: MultimodalMessage[] =\n history.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const stateContext = buildStateContext();\n\n // Build asset manifest if there are any uploaded assets in the session\n let assetManifest = \"\";\n if (session.assets?.length) {\n const imageAssets = session.assets.filter((a) => a.type === \"image\" && a.usage === \"asset\");\n if (imageAssets.length > 0) {\n assetManifest = `\\n\\n## Available Theme Assets\\nThese images are in the theme's assets/ folder. Reference them with get_asset_url(\"${session.themeName}/assets/filename\"):\\n${imageAssets.map((a) => `- ${a.filename} (${a.originalName}) → get_asset_url(\"${session.themeName}/assets/${a.filename}\")`).join(\"\\n\")}`;\n }\n }\n\n let textContent = userMessage;\n if (stateContext) textContent += `\\n\\n---\\n${stateContext}`;\n if (assetManifest) textContent += assetManifest;\n\n // Format reminder — placed at the end of user content so the model sees it\n // right before it starts generating. This combats the \"forgot the format\" problem.\n textContent += `\\n\\n---\\nRemember: respond with a \\`\\`\\`vibespot-modules JSON block containing ALL modules. No text-only responses.`;\n\n // Add document text from attachments\n const hasFiles = fileContexts && fileContexts.length > 0;\n if (hasFiles) {\n for (const fc of fileContexts) {\n if (fc.type === \"document\" && fc.extractedText) {\n textContent += `\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`;\n }\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n textContent += `\\n\\n[Uploaded image: ${fc.originalName} → available as get_asset_url(\"${fc.assetPath}\")]`;\n }\n }\n }\n\n // Build multimodal content if there are image attachments\n const imageFiles = hasFiles ? fileContexts.filter((fc) => fc.type === \"image\" && fc.base64) : [];\n\n if (imageFiles.length > 0) {\n const contentBlocks: ContentBlock[] = [];\n // Add image blocks first so the AI \"sees\" them\n for (const img of imageFiles) {\n contentBlocks.push({\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: img.mimeType,\n data: img.base64!,\n },\n });\n }\n // Add the text content\n contentBlocks.push({ type: \"text\", text: textContent });\n messages.push({ role: \"user\", content: contentBlocks });\n } else {\n messages.push({ role: \"user\", content: textContent });\n }\n\n return messages;\n}\n","/**\n * AI engine implementations — streaming handlers for each supported engine.\n */\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport { spawn } from \"node:child_process\";\nimport { getConversionGuide } from \"../ai/prompts.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { getValidAccessToken, OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } from \"../utils/claude-oauth.js\";\nimport { getSession } from \"./session.js\";\nimport { buildVibeSystemPrompt, buildVibeSystemPromptBlocks, buildStateContext, buildMessagesWithContext, getPromptContext, type SystemPromptBlock, type MultimodalMessage } from \"./ai-prompts.js\";\nimport { log } from \"./log.js\";\nimport type { UploadedFileContext } from \"./routes/upload-files.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy-loaded Anthropic SDK\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<typeof import(\"@anthropic-ai/sdk\").default> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\n// ---------------------------------------------------------------------------\n// File context helper (for CLI engines that don't support vision)\n// ---------------------------------------------------------------------------\n\nfunction buildFileContextText(fileContexts?: UploadedFileContext[]): string {\n if (!fileContexts?.length) return \"\";\n const parts: string[] = [];\n for (const fc of fileContexts) {\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n parts.push(`\\n[Uploaded image: ${fc.originalName} → use get_asset_url(\"${fc.assetPath}\")]`);\n }\n if (fc.type === \"document\" && fc.extractedText) {\n parts.push(`\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`);\n }\n }\n return parts.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// CLI status messages (shown while waiting for buffered CLI output)\n// ---------------------------------------------------------------------------\n\nexport const CLI_STATUS_MESSAGES = [\n \"Analyzing your request...\",\n \"Reading the conversion guide...\",\n \"Planning module structure...\",\n \"Generating HTML templates...\",\n \"Writing CSS styles...\",\n \"Creating field definitions...\",\n \"Building module metadata...\",\n \"Assembling theme assets...\",\n \"Polishing the output...\",\n \"Almost there — hang tight...\",\n];\n\n// ---------------------------------------------------------------------------\n// Anthropic Streaming API (shared helper + public wrappers)\n// ---------------------------------------------------------------------------\n\nconst RATE_LIMIT_DELAYS = [10, 20, 40, 60, 120]; // seconds\n\n/**\n * Shared streaming helper for all Anthropic-based engines.\n * Accepts a pre-built SDK client and system prompt (string or blocks).\n */\nasync function _streamAnthropic(\n client: InstanceType<Awaited<ReturnType<typeof getAnthropicSDK>>>,\n system: string | SystemPromptBlock[],\n messages: MultimodalMessage[],\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n): Promise<void> {\n for (let attempt = 0; ; attempt++) {\n try {\n let fullResponse = \"\";\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n try {\n const stream = client.messages.stream({\n model,\n max_tokens: 48000,\n system: system as any,\n messages: messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[],\n });\n\n for await (const event of stream) {\n if (\n event.type === \"content_block_delta\" &&\n event.delta.type === \"text_delta\"\n ) {\n const text = event.delta.text;\n fullResponse += text;\n onChunk(text);\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n return;\n } catch (err: unknown) {\n const status = (err as { status?: number }).status;\n const errType = (err as { error?: { type?: string } }).error?.type;\n const is429 = status === 429\n || errType === \"rate_limit_error\"\n || (err instanceof Error && err.message.includes(\"429\"));\n\n if (!is429 || attempt >= RATE_LIMIT_DELAYS.length) throw err;\n\n const wait = RATE_LIMIT_DELAYS[attempt];\n log.warn(\"ai-engine\", `Rate limited (429), attempt ${attempt + 1}/${RATE_LIMIT_DELAYS.length} — waiting ${wait}s`);\n if (onStatus) onStatus(`Rate limited by Anthropic API — retrying in ${wait}s...`);\n await new Promise((r) => setTimeout(r, wait * 1000));\n if (onStatus) onStatus(\"Retrying...\");\n }\n }\n}\n\n/** Prepare common Anthropic call context (messages, system prompt blocks). */\nfunction prepareAnthropicContext(userMessage: string, themeName: string, fileContexts?: UploadedFileContext[]) {\n const conversionGuide = getConversionGuide();\n const session = getSession()!;\n const editMode = session.modules.length > 0;\n const messages = buildMessagesWithContext(userMessage, fileContexts);\n const ctx = getPromptContext();\n const systemBlocks = buildVibeSystemPromptBlocks(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n return { messages, systemBlocks, conversionGuide, editMode };\n}\n\nexport async function streamWithAnthropicAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ apiKey });\n const { messages, systemBlocks } = prepareAnthropicContext(userMessage, themeName, fileContexts);\n\n log.info(\"anthropic\", \"API call\", {\n model,\n systemBlockCount: systemBlocks.length,\n cachedBlocks: systemBlocks.filter((b) => b.cache_control).length,\n messageCount: messages.length,\n });\n\n await _streamAnthropic(client, systemBlocks, messages, model, onChunk, onStatus, onFinish);\n}\n\n// ---------------------------------------------------------------------------\n// Claude OAuth Streaming API — uses OAuth token + required headers\n// ---------------------------------------------------------------------------\n\nexport async function streamWithClaudeOAuth(\n userMessage: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const accessToken = await getValidAccessToken();\n if (!accessToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n authToken: accessToken,\n defaultHeaders: OAUTH_EXTRA_HEADERS,\n } as any);\n\n const { messages, systemBlocks } = prepareAnthropicContext(userMessage, themeName, fileContexts);\n\n // Prepend required Claude Code system prefix as separate first block\n const oauthBlocks: SystemPromptBlock[] = [\n { type: \"text\", text: OAUTH_SYSTEM_PREFIX },\n ...systemBlocks,\n ];\n\n log.info(\"anthropic-oauth\", \"API call\", {\n model,\n systemBlockCount: oauthBlocks.length,\n cachedBlocks: oauthBlocks.filter((b) => b.cache_control).length,\n messageCount: messages.length,\n });\n\n await _streamAnthropic(client, oauthBlocks, messages, model, onChunk, onStatus, onFinish);\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI Streaming API (uses native fetch — no npm dependency)\n// ---------------------------------------------------------------------------\n\nexport async function streamWithOpenAIAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n model: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const editMode = getSession()!.modules.length > 0;\n const messages = buildMessagesWithContext(userMessage, fileContexts);\n const ctx = getPromptContext();\n\n // Convert multimodal messages to OpenAI format\n const openaiMessages = messages.map((m) => {\n if (typeof m.content === \"string\") return m;\n // Convert Anthropic-style content blocks to OpenAI format\n return {\n role: m.role,\n content: m.content.map((block) => {\n if (block.type === \"text\") return { type: \"text\" as const, text: block.text };\n return {\n type: \"image_url\" as const,\n image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` },\n };\n }),\n };\n });\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n model,\n max_tokens: 48000,\n stream: true,\n messages: [\n { role: \"system\", content: buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets) },\n ...openaiMessages,\n ],\n }),\n });\n\n if (!response.ok) {\n const err = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${err}`);\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n let fullResponse = \"\";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") break;\n\n try {\n const parsed = JSON.parse(data);\n const delta = parsed.choices?.[0]?.delta?.content;\n if (delta) {\n fullResponse += delta;\n onChunk(delta);\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n}\n\n// ---------------------------------------------------------------------------\n// Gemini Streaming API (uses native fetch — no npm dependency)\n// ---------------------------------------------------------------------------\n\nexport async function streamWithGeminiAPI(\n userMessage: string,\n apiKey: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const session = getSession()!;\n const editMode = session.modules.length > 0;\n const stateContext = buildStateContext();\n const ctx = getPromptContext();\n\n const contents: Array<{ role: string; parts: Array<{ text?: string; inlineData?: { mimeType: string; data: string } }> }> = [];\n\n for (const m of session.messages.slice(-20)) {\n contents.push({\n role: m.role === \"assistant\" ? \"model\" : \"user\",\n parts: [{ text: m.content }],\n });\n }\n\n let userContent = stateContext\n ? `${userMessage}\\n\\n---\\n${stateContext}`\n : userMessage;\n\n // Add document text from file contexts\n if (fileContexts?.length) {\n for (const fc of fileContexts) {\n if (fc.type === \"document\" && fc.extractedText) {\n userContent += `\\n\\n---\\n[Attached document: ${fc.originalName}]\\n${fc.extractedText}`;\n }\n if (fc.type === \"image\" && fc.usage === \"asset\" && fc.assetPath) {\n userContent += `\\n\\n[Uploaded image: ${fc.originalName} → available as get_asset_url(\"${fc.assetPath}\")]`;\n }\n }\n }\n\n const userParts: Array<{ text?: string; inlineData?: { mimeType: string; data: string } }> = [];\n\n // Add image parts for Gemini vision\n if (fileContexts?.length) {\n for (const fc of fileContexts) {\n if (fc.type === \"image\" && fc.base64) {\n userParts.push({ inlineData: { mimeType: fc.mimeType, data: fc.base64 } });\n }\n }\n }\n\n userParts.push({ text: userContent });\n contents.push({ role: \"user\", parts: userParts });\n\n const model = \"gemini-2.5-flash\";\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n systemInstruction: { parts: [{ text: buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets) }] },\n contents,\n generationConfig: { maxOutputTokens: 48000 },\n }),\n });\n\n if (!response.ok) {\n const err = await response.text();\n throw new Error(`Gemini API error (${response.status}): ${err}`);\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n const heartbeat = setInterval(() => {\n statusIndex++;\n sendStatus(CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)]);\n }, 6000);\n\n let fullResponse = \"\";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n\n try {\n const parsed = JSON.parse(data);\n const text = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n if (text) {\n fullResponse += text;\n onChunk(text);\n }\n } catch { /* skip malformed SSE lines */ }\n }\n }\n } finally {\n clearInterval(heartbeat);\n }\n\n if (onFinish) onFinish(fullResponse);\n}\n\n// ---------------------------------------------------------------------------\n// CLI subprocess helper — sends prompt via stdin to avoid shell arg limits\n// ---------------------------------------------------------------------------\n\nexport function spawnCLI(\n bin: string,\n args: string[],\n prompt: string,\n onChunk?: (chunk: string) => void\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const env = { ...process.env };\n delete env.CLAUDECODE;\n\n const child = spawn(bin, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let settled = false;\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n fn();\n };\n\n child.stdout.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n stdout += chunk;\n if (onChunk) onChunk(chunk);\n });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) =>\n settle(() => reject(new Error(`${bin} failed to start: ${err.message}`)))\n );\n\n child.on(\"close\", (code) => {\n settle(() => {\n if (code !== 0) {\n reject(new Error(\n `${bin} exited with code ${code}.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n (stdout ? `Output: ${stdout.slice(0, 500)}` : \"No output\")\n ));\n } else {\n resolve(stdout);\n }\n });\n });\n\n // Write prompt to stdin with backpressure handling for large prompts\n child.stdin.on(\"error\", () => {});\n const ok = child.stdin.write(prompt);\n if (!ok) {\n // Buffer is full — wait for drain before ending\n child.stdin.once(\"drain\", () => child.stdin.end());\n } else {\n child.stdin.end();\n }\n\n const timer = setTimeout(() => {\n child.kill();\n settle(() => reject(new Error(\n `${bin} timed out after 5 minutes.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n `Partial output (${stdout.length} chars): ${stdout.slice(0, 500)}`\n )));\n }, 300_000);\n\n // Clear timeout when process exits normally\n child.on(\"close\", () => clearTimeout(timer));\n });\n}\n\n// ---------------------------------------------------------------------------\n// Claude Code subprocess\n// ---------------------------------------------------------------------------\n\nexport async function generateWithClaudeCode(\n userMessage: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const config = loadConfig();\n const editMode = getSession()!.modules.length > 0;\n const ctx = getPromptContext();\n\n let prompt = buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n prompt += \"\\n\\n## User Request\\n\" + userMessage;\n prompt += buildStateContext();\n prompt += buildFileContextText(fileContexts);\n prompt += \"\\n\\n---\\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.\";\n\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n\n const heartbeat = setInterval(() => {\n statusIndex++;\n const msg = CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)];\n sendStatus(msg);\n }, 6000);\n\n try {\n const result = await spawnCLI(\"claude\", args, prompt, (chunk) => {\n onChunk(chunk);\n });\n if (onFinish) onFinish(result);\n } finally {\n clearInterval(heartbeat);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generic CLI subprocess (Gemini CLI, Codex CLI)\n// ---------------------------------------------------------------------------\n\nexport async function generateWithCLI(\n cli: \"gemini\" | \"codex\",\n userMessage: string,\n themeName: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n onFinish?: (fullResponse: string) => void,\n fileContexts?: UploadedFileContext[]\n): Promise<void> {\n const conversionGuide = getConversionGuide();\n const editMode = getSession()!.modules.length > 0;\n const ctx = getPromptContext();\n\n let prompt = buildVibeSystemPrompt(conversionGuide, themeName, editMode, ctx.pageType, ctx.brandAssets);\n prompt += \"\\n\\n## User Request\\n\" + userMessage;\n prompt += buildStateContext();\n prompt += buildFileContextText(fileContexts);\n prompt += \"\\n\\n---\\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.\";\n\n let bin: string;\n let args: string[];\n if (cli === \"gemini\") {\n bin = \"gemini\";\n args = [];\n } else {\n bin = \"codex\";\n args = [\"exec\", \"--full-auto\"];\n }\n\n let statusIndex = 0;\n const sendStatus = onStatus || (() => {});\n sendStatus(CLI_STATUS_MESSAGES[0]);\n\n const heartbeat = setInterval(() => {\n statusIndex++;\n const msg = CLI_STATUS_MESSAGES[Math.min(statusIndex, CLI_STATUS_MESSAGES.length - 1)];\n sendStatus(msg);\n }, 6000);\n\n try {\n const result = await spawnCLI(bin, args, prompt, (chunk) => {\n onChunk(chunk);\n });\n if (onFinish) onFinish(result);\n } finally {\n clearInterval(heartbeat);\n }\n}\n","/**\n * Shared helpers for route handlers.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nexport function jsonResponse(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n}\n\nexport function readBody(req: IncomingMessage, callback: (body: string) => void): void {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk) => chunks.push(chunk));\n req.on(\"end\", () => callback(Buffer.concat(chunks).toString(\"utf-8\")));\n}\n\n/**\n * Read request body and parse as JSON with error handling.\n * Returns 400 with an error message if parsing fails.\n */\nexport function readJsonBody<T = Record<string, unknown>>(\n req: IncomingMessage,\n res: ServerResponse,\n callback: (data: T) => void\n): void {\n readBody(req, (body) => {\n try {\n callback(JSON.parse(body || \"{}\") as T);\n } catch {\n jsonResponse(res, 400, { error: \"Invalid JSON in request body\" });\n }\n });\n}\n","/**\n * File upload route — handles images (page assets) and documents (AI context).\n *\n * Images are copied to theme/assets/ and sent to AI as vision input.\n * Documents (PDF, DOCX, MD, TXT) are text-extracted for AI context only.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { createWriteStream, mkdirSync, existsSync, readFileSync, copyFileSync } from \"node:fs\";\nimport { join, extname } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport Busboy from \"busboy\";\nimport { jsonResponse } from \"../route-helpers.js\";\nimport { getSession, addSessionAsset, type SessionAsset } from \"../session.js\";\nimport { log } from \"../log.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB\n\nconst IMAGE_MIMES = new Set([\n \"image/png\", \"image/jpeg\", \"image/jpg\", \"image/svg+xml\",\n \"image/webp\", \"image/gif\",\n]);\n\nconst DOCUMENT_MIMES = new Set([\n \"application/pdf\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"text/markdown\", \"text/plain\",\n]);\n\nconst SUPPORTED_MIMES = new Set([...IMAGE_MIMES, ...DOCUMENT_MIMES]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Sanitize a filename for safe filesystem use. */\nfunction sanitizeFilename(name: string): string {\n return name\n .replace(/[^a-zA-Z0-9._-]/g, \"_\")\n .replace(/_{2,}/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase();\n}\n\n/** Deduplicate filename if it already exists in the target directory. */\nfunction deduplicateFilename(dir: string, name: string): string {\n if (!existsSync(join(dir, name))) return name;\n const ext = extname(name);\n const base = name.slice(0, -ext.length || undefined);\n let counter = 1;\n while (existsSync(join(dir, `${base}-${counter}${ext}`))) counter++;\n return `${base}-${counter}${ext}`;\n}\n\n/** Extract text from a PDF file using pdf-parse. */\nasync function extractPdfText(filePath: string): Promise<string> {\n const pdfParse = (await import(\"pdf-parse\")).default;\n const buffer = readFileSync(filePath);\n const data = await pdfParse(buffer);\n return data.text;\n}\n\n/** Extract text from a DOCX file using mammoth. */\nasync function extractDocxText(filePath: string): Promise<string> {\n const mammoth = await import(\"mammoth\");\n const result = await mammoth.extractRawText({ path: filePath });\n return result.value;\n}\n\n/** Read plain text or markdown file. */\nfunction extractPlainText(filePath: string): string {\n return readFileSync(filePath, \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Route handler\n// ---------------------------------------------------------------------------\n\nexport function handleFileUploadRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 400, { error: \"No active session\" });\n return;\n }\n\n const contentType = req.headers[\"content-type\"] || \"\";\n if (!contentType.includes(\"multipart/form-data\")) {\n jsonResponse(res, 400, { error: \"Expected multipart/form-data\" });\n return;\n }\n\n const results: SessionAsset[] = [];\n const errors: string[] = [];\n let fileCount = 0;\n const writePromises: Promise<void>[] = [];\n\n const bb = Busboy({ headers: req.headers, limits: { fileSize: MAX_FILE_SIZE, files: 10 } });\n\n bb.on(\"file\", (fieldname, fileStream, info) => {\n const { filename: originalName, mimeType } = info;\n fileCount++;\n\n if (!SUPPORTED_MIMES.has(mimeType)) {\n errors.push(`Unsupported file type: ${originalName} (${mimeType})`);\n fileStream.resume(); // drain the stream\n return;\n }\n\n const isImage = IMAGE_MIMES.has(mimeType);\n const sanitized = sanitizeFilename(originalName);\n const id = randomUUID();\n\n // Determine storage path\n let targetDir: string;\n let finalFilename: string;\n\n if (isImage) {\n // Images go to theme/assets/\n targetDir = join(session.themePath, \"assets\");\n mkdirSync(targetDir, { recursive: true });\n finalFilename = deduplicateFilename(targetDir, sanitized);\n } else {\n // Documents go to .vibespot/uploads/ (context only)\n targetDir = join(session.themePath, \".vibespot\", \"uploads\");\n mkdirSync(targetDir, { recursive: true });\n finalFilename = `${id}-${sanitized}`;\n }\n\n const targetPath = join(targetDir, finalFilename);\n const writeStream = createWriteStream(targetPath);\n let fileSize = 0;\n let truncated = false;\n\n fileStream.on(\"data\", (chunk: Buffer) => {\n fileSize += chunk.length;\n });\n\n fileStream.on(\"limit\", () => {\n truncated = true;\n errors.push(`File too large (>10MB): ${originalName}`);\n });\n\n fileStream.pipe(writeStream);\n\n // Track each file write as a promise so we can await them all\n writePromises.push(new Promise<void>((resolve) => {\n writeStream.on(\"finish\", () => {\n if (!truncated) {\n const asset: SessionAsset = {\n id,\n filename: finalFilename,\n originalName,\n type: isImage ? \"image\" : \"document\",\n usage: isImage ? \"asset\" : \"context\",\n mimeType,\n size: fileSize,\n addedAt: new Date().toISOString(),\n };\n results.push(asset);\n addSessionAsset(asset);\n }\n resolve();\n });\n writeStream.on(\"error\", () => {\n errors.push(`Failed to write: ${originalName}`);\n resolve();\n });\n }));\n });\n\n bb.on(\"finish\", async () => {\n // Wait for all file writes to complete before responding\n await Promise.all(writePromises);\n\n // Extract text from documents\n for (const asset of results) {\n if (asset.type === \"document\") {\n const filePath = join(session.themePath, \".vibespot\", \"uploads\", asset.filename);\n try {\n if (asset.mimeType === \"application/pdf\") {\n asset.extractedText = await extractPdfText(filePath);\n } else if (asset.mimeType === \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\") {\n asset.extractedText = await extractDocxText(filePath);\n } else {\n asset.extractedText = extractPlainText(filePath);\n }\n log.info(\"upload\", `Extracted text from ${asset.originalName} (${asset.extractedText.length} chars)`);\n } catch (err) {\n log.warn(\"upload\", `Failed to extract text from ${asset.originalName}: ${err}`);\n asset.extractedText = `[Could not extract text from ${asset.originalName}]`;\n }\n }\n }\n\n if (fileCount === 0) {\n jsonResponse(res, 400, { error: \"No files uploaded\" });\n return;\n }\n\n jsonResponse(res, 200, {\n files: results.map((a) => ({\n id: a.id,\n filename: a.filename,\n originalName: a.originalName,\n type: a.type,\n usage: a.usage,\n size: a.size,\n })),\n errors: errors.length > 0 ? errors : undefined,\n });\n });\n\n bb.on(\"error\", (err) => {\n log.error(\"upload\", `Busboy error: ${err}`);\n jsonResponse(res, 500, { error: \"Upload failed\" });\n });\n\n req.pipe(bb);\n}\n\n// ---------------------------------------------------------------------------\n// Get uploaded asset data (for AI context)\n// ---------------------------------------------------------------------------\n\nexport interface UploadedFileContext {\n id: string;\n filename: string;\n originalName: string;\n type: \"image\" | \"document\";\n usage: \"asset\" | \"context\";\n /** Base64-encoded image data (for vision API) */\n base64?: string;\n mimeType: string;\n /** Extracted text content (for documents) */\n extractedText?: string;\n /** Asset path for get_asset_url() references */\n assetPath?: string;\n}\n\n/** Load full context for uploaded files by their IDs. */\nexport function getFileContexts(fileIds: string[]): UploadedFileContext[] {\n const session = getSession();\n if (!session?.assets) return [];\n\n return fileIds\n .map((id) => {\n const asset = session.assets!.find((a) => a.id === id);\n if (!asset) return null;\n\n const ctx: UploadedFileContext = {\n id: asset.id,\n filename: asset.filename,\n originalName: asset.originalName,\n type: asset.type,\n usage: asset.usage,\n mimeType: asset.mimeType,\n };\n\n if (asset.type === \"image\") {\n // Load base64 for vision API\n const imgPath = join(session.themePath, \"assets\", asset.filename);\n if (existsSync(imgPath)) {\n ctx.base64 = readFileSync(imgPath).toString(\"base64\");\n }\n ctx.assetPath = `${session.themeName}/assets/${asset.filename}`;\n } else if (asset.type === \"document\") {\n // Load extracted text\n ctx.extractedText = asset.extractedText;\n }\n\n return ctx;\n })\n .filter((c): c is UploadedFileContext => c !== null);\n}\n\n/** Get the asset manifest (list of all image assets) for the AI system prompt. */\nexport function getAssetManifest(): string[] {\n const session = getSession();\n if (!session?.assets) return [];\n return session.assets\n .filter((a) => a.type === \"image\" && a.usage === \"asset\")\n .map((a) => a.filename);\n}\n","/**\n * Engine adapter for agentic pipeline — structured output via each provider's\n * native mechanism (Anthropic tool_use, OpenAI json_schema, Gemini responseSchema)\n * for API engines, or prompt-based JSON extraction for CLI engines.\n *\n * This is a lower-level API than the streaming functions in ai-engines.ts.\n * It accepts custom system prompts and structured output schemas, returning\n * parsed JSON or raw text.\n */\n\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport { spawnCLI } from \"../ai-engines.js\";\nimport { tryParseJSON, tryRepairTruncatedJSON } from \"../ai-parser.js\";\nimport { loadConfig } from \"../../utils/config.js\";\nimport { OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } from \"../../utils/claude-oauth.js\";\nimport { log } from \"../log.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MultimodalContent {\n type: \"text\";\n text: string;\n}\n\nexport interface AgentMessage {\n role: \"user\" | \"assistant\";\n content: string | MultimodalContent[];\n}\n\nexport interface StructuredOutputSpec {\n /** JSON Schema for the expected output */\n schema: Record<string, unknown>;\n /** Name for the schema / tool (used by Anthropic tool_use, OpenAI json_schema) */\n name: string;\n}\n\n/** System prompt block with optional cache control (for Anthropic prompt caching). */\nexport interface SystemPromptBlock {\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n}\n\nexport interface AgentCallOptions {\n systemPrompt: string;\n /** When provided, used instead of systemPrompt for Anthropic engines (enables prompt caching). */\n systemBlocks?: SystemPromptBlock[];\n messages: AgentMessage[];\n structuredOutput?: StructuredOutputSpec;\n maxTokens?: number;\n onChunk?: (chunk: string) => void;\n onStatus?: (status: string) => void;\n}\n\nexport type AgentCallResult =\n | { type: \"structured\"; data: unknown }\n | { type: \"text\"; text: string };\n\nexport type AgentEngine =\n | \"anthropic-api\"\n | \"claude-oauth\"\n | \"openai-api\"\n | \"gemini-api\"\n | \"claude-code\"\n | \"gemini-cli\"\n | \"codex-cli\";\n\n// ---------------------------------------------------------------------------\n// Rate limit retry delays (shared with ai-engines.ts pattern)\n// ---------------------------------------------------------------------------\n\nconst RATE_LIMIT_DELAYS = [10, 20, 40, 60, 120]; // seconds\n\nasync function withRateLimitRetry<T>(\n fn: () => Promise<T>,\n onStatus?: (status: string) => void,\n): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await fn();\n } catch (err: unknown) {\n const status = (err as { status?: number }).status;\n const errType = (err as { error?: { type?: string } }).error?.type;\n const is429 =\n status === 429 ||\n errType === \"rate_limit_error\" ||\n (err instanceof Error && err.message.includes(\"429\"));\n\n if (!is429 || attempt >= RATE_LIMIT_DELAYS.length) throw err;\n\n const wait = RATE_LIMIT_DELAYS[attempt];\n log.warn(\n \"agent-adapter\",\n `Rate limited (429), attempt ${attempt + 1}/${RATE_LIMIT_DELAYS.length} — waiting ${wait}s`,\n );\n if (onStatus) onStatus(`Rate limited — retrying in ${wait}s...`);\n await new Promise((r) => setTimeout(r, wait * 1000));\n if (onStatus) onStatus(\"Retrying...\");\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Post-processing: ensure fieldsJson / metaJson are strings, not objects\n// ---------------------------------------------------------------------------\n\nfunction stringifyJsonFields(data: unknown): unknown {\n if (data && typeof data === \"object\" && !Array.isArray(data)) {\n const obj = data as Record<string, unknown>;\n for (const key of [\"fieldsJson\", \"metaJson\"]) {\n if (obj[key] && typeof obj[key] === \"object\") {\n obj[key] = JSON.stringify(obj[key]);\n }\n }\n }\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic Adapter — structured output via tool_use\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<\n typeof import(\"@anthropic-ai/sdk\").default\n> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n extraHeaders?: Record<string, string>,\n systemPrefix?: string,\n): Promise<AgentCallResult> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n apiKey,\n ...(extraHeaders ? { defaultHeaders: extraHeaders } : {}),\n });\n\n const messages =\n opts.messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[];\n\n // Resolve system prompt: prefer blocks (with cache control), fall back to string\n let system: string | SystemPromptBlock[] = opts.systemPrompt;\n if (opts.systemBlocks) {\n system = systemPrefix\n ? [{ type: \"text\" as const, text: systemPrefix }, ...opts.systemBlocks]\n : opts.systemBlocks;\n } else if (systemPrefix) {\n system = [\n { type: \"text\" as const, text: systemPrefix },\n { type: \"text\" as const, text: opts.systemPrompt },\n ];\n }\n\n if (opts.structuredOutput) {\n // Use tool_use to enforce structured output\n const tool: Anthropic.Tool = {\n name: opts.structuredOutput.name,\n description: `Return the result as structured JSON matching the ${opts.structuredOutput.name} schema.`,\n input_schema:\n opts.structuredOutput.schema as Anthropic.Tool.InputSchema,\n };\n\n return withRateLimitRetry(async () => {\n const response = await client.messages.create({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n tools: [tool],\n tool_choice: { type: \"tool\", name: opts.structuredOutput!.name },\n });\n\n // Extract tool_use input from the response\n for (const block of response.content) {\n if (block.type === \"tool_use\") {\n return {\n type: \"structured\" as const,\n data: stringifyJsonFields(block.input),\n };\n }\n }\n\n // Fallback: no tool_use block found — return text\n const textParts = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text);\n return { type: \"text\" as const, text: textParts.join(\"\") };\n }, opts.onStatus);\n }\n\n // Non-structured: regular text generation\n return withRateLimitRetry(async () => {\n let fullText = \"\";\n const stream = client.messages.stream({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n });\n\n for await (const event of stream) {\n if (\n event.type === \"content_block_delta\" &&\n event.delta.type === \"text_delta\"\n ) {\n fullText += event.delta.text;\n if (opts.onChunk) opts.onChunk(event.delta.text);\n }\n }\n\n return { type: \"text\" as const, text: fullText };\n }, opts.onStatus);\n}\n\n/**\n * OAuth variant — uses authToken (Bearer) instead of apiKey, adds required headers + system prefix.\n */\nasync function callAnthropicOAuth(\n accessToken: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({\n authToken: accessToken,\n defaultHeaders: OAUTH_EXTRA_HEADERS,\n } as any);\n\n const messages =\n opts.messages as unknown as import(\"@anthropic-ai/sdk\").MessageParam[];\n\n // Build system with OAuth prefix + optional cache blocks\n let system: string | SystemPromptBlock[];\n if (opts.systemBlocks) {\n system = [\n { type: \"text\" as const, text: OAUTH_SYSTEM_PREFIX },\n ...opts.systemBlocks,\n ];\n } else {\n system = [\n { type: \"text\" as const, text: OAUTH_SYSTEM_PREFIX },\n { type: \"text\" as const, text: opts.systemPrompt },\n ];\n }\n\n if (opts.structuredOutput) {\n const tool: Anthropic.Tool = {\n name: opts.structuredOutput.name,\n description: `Return the result as structured JSON matching the ${opts.structuredOutput.name} schema.`,\n input_schema: opts.structuredOutput.schema as Anthropic.Tool.InputSchema,\n };\n\n return withRateLimitRetry(async () => {\n const response = await client.messages.create({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n tools: [tool],\n tool_choice: { type: \"tool\", name: opts.structuredOutput!.name },\n });\n\n for (const block of response.content) {\n if (block.type === \"tool_use\") {\n return { type: \"structured\" as const, data: stringifyJsonFields(block.input) };\n }\n }\n\n const textParts = response.content\n .filter((b): b is Anthropic.TextBlock => b.type === \"text\")\n .map((b) => b.text);\n return { type: \"text\" as const, text: textParts.join(\"\") };\n }, opts.onStatus);\n }\n\n return withRateLimitRetry(async () => {\n let fullText = \"\";\n const stream = client.messages.stream({\n model,\n max_tokens: opts.maxTokens || 16000,\n system: system as any,\n messages,\n });\n\n for await (const event of stream) {\n if (event.type === \"content_block_delta\" && event.delta.type === \"text_delta\") {\n fullText += event.delta.text;\n if (opts.onChunk) opts.onChunk(event.delta.text);\n }\n }\n\n return { type: \"text\" as const, text: fullText };\n }, opts.onStatus);\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI Adapter — structured output via response_format json_schema\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively add `additionalProperties: false` to all object-type schemas\n * (required by OpenAI's structured output).\n */\nfunction addAdditionalPropertiesFalse(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...schema };\n if (result.type === \"object\") {\n result.additionalProperties = false;\n if (\n result.properties &&\n typeof result.properties === \"object\"\n ) {\n const props: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(\n result.properties as Record<string, unknown>,\n )) {\n props[k] =\n v && typeof v === \"object\"\n ? addAdditionalPropertiesFalse(v as Record<string, unknown>)\n : v;\n }\n result.properties = props;\n }\n }\n if (result.items && typeof result.items === \"object\") {\n result.items = addAdditionalPropertiesFalse(\n result.items as Record<string, unknown>,\n );\n }\n return result;\n}\n\nasync function callOpenAI(\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const openaiMessages = [\n { role: \"system\", content: opts.systemPrompt },\n ...opts.messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === \"string\"\n ? m.content\n : m.content.map((b) => ({ type: \"text\" as const, text: b.text })),\n })),\n ];\n\n const body: Record<string, unknown> = {\n model,\n max_tokens: opts.maxTokens || 16000,\n messages: openaiMessages,\n };\n\n if (opts.structuredOutput) {\n body.response_format = {\n type: \"json_schema\",\n json_schema: {\n name: opts.structuredOutput.name,\n strict: true,\n schema: addAdditionalPropertiesFalse(opts.structuredOutput.schema),\n },\n };\n }\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const err = await response.text();\n const status = response.status;\n if (status === 429) {\n const error = new Error(`OpenAI rate limit: ${err}`);\n (error as unknown as { status: number }).status = 429;\n throw error;\n }\n throw new Error(`OpenAI API error (${status}): ${err}`);\n }\n\n const json = await response.json();\n const content = json.choices?.[0]?.message?.content || \"\";\n\n if (opts.structuredOutput) {\n try {\n return {\n type: \"structured\",\n data: stringifyJsonFields(JSON.parse(content)),\n };\n } catch {\n log.warn(\"agent-adapter\", \"OpenAI structured output parse failed, returning raw text\");\n return { type: \"text\", text: content };\n }\n }\n\n return { type: \"text\", text: content };\n}\n\n// ---------------------------------------------------------------------------\n// Gemini Adapter — structured output via responseMimeType + responseSchema\n// ---------------------------------------------------------------------------\n\nasync function callGemini(\n apiKey: string,\n _model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const model = _model || \"gemini-2.5-flash\";\n\n // Gemini uses \"user\" / \"model\" roles\n const contents = opts.messages.map((m) => ({\n role: m.role === \"assistant\" ? \"model\" : \"user\",\n parts:\n typeof m.content === \"string\"\n ? [{ text: m.content }]\n : m.content.map((b) => ({ text: b.text })),\n }));\n\n const body: Record<string, unknown> = {\n systemInstruction: { parts: [{ text: opts.systemPrompt }] },\n contents,\n generationConfig: {\n maxOutputTokens: opts.maxTokens || 16000,\n ...(opts.structuredOutput\n ? {\n responseMimeType: \"application/json\",\n responseSchema: opts.structuredOutput.schema,\n }\n : {}),\n },\n };\n\n const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const err = await response.text();\n const status = response.status;\n if (status === 429) {\n const error = new Error(`Gemini rate limit: ${err}`);\n (error as unknown as { status: number }).status = 429;\n throw error;\n }\n throw new Error(`Gemini API error (${status}): ${err}`);\n }\n\n const json = await response.json();\n const text = json.candidates?.[0]?.content?.parts?.[0]?.text || \"\";\n\n if (opts.structuredOutput) {\n try {\n return {\n type: \"structured\",\n data: stringifyJsonFields(JSON.parse(text)),\n };\n } catch {\n log.warn(\"agent-adapter\", \"Gemini structured output parse failed, returning raw text\");\n return { type: \"text\", text };\n }\n }\n\n return { type: \"text\", text };\n}\n\n// ---------------------------------------------------------------------------\n// CLI Adapter — structured output via prompt instructions + post-parse\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve CLI binary and args for a given engine.\n */\nfunction resolveCLIBinary(engine: AgentEngine): { bin: string; args: string[] } {\n switch (engine) {\n case \"claude-code\": {\n const config = loadConfig();\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n return { bin: \"claude\", args };\n }\n case \"gemini-cli\":\n return { bin: \"gemini\", args: [] };\n case \"codex-cli\":\n return { bin: \"codex\", args: [\"exec\", \"--full-auto\"] };\n default:\n throw new Error(`Not a CLI engine: ${engine}`);\n }\n}\n\n/**\n * Build a flat prompt string from AgentCallOptions for CLI engines.\n * CLI engines receive a single string via stdin — no separate system/user messages.\n */\nfunction buildCLIPrompt(opts: AgentCallOptions): string {\n const parts: string[] = [opts.systemPrompt];\n\n for (const msg of opts.messages) {\n const role = msg.role === \"user\" ? \"User\" : \"Assistant\";\n const text =\n typeof msg.content === \"string\"\n ? msg.content\n : msg.content.map((b) => b.text).join(\"\\n\");\n parts.push(`\\n\\n## ${role}\\n${text}`);\n }\n\n if (opts.structuredOutput) {\n const schemaDesc = describeSchema(opts.structuredOutput.schema);\n parts.push(`\\n\\n## Output Format — CRITICAL\nRespond with a JSON code block. Wrap your JSON in \\`\\`\\`json fences. No prose or explanation before or after the code block.\n\nThe JSON must match this structure:\n${schemaDesc}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Generate a human-readable schema description from a JSON Schema object.\n */\nfunction describeSchema(schema: Record<string, unknown>, indent = 0): string {\n const pad = \" \".repeat(indent);\n const props = schema.properties as Record<string, Record<string, unknown>> | undefined;\n const required = (schema.required as string[]) || [];\n\n if (!props) return `${pad}${JSON.stringify(schema)}`;\n\n const lines: string[] = [\"{\"];\n for (const [key, prop] of Object.entries(props)) {\n const req = required.includes(key) ? \" (required)\" : \"\";\n const type = prop.type || \"any\";\n const desc = prop.description ? ` — ${prop.description}` : \"\";\n const enumVals = prop.enum ? ` [${(prop.enum as string[]).join(\", \")}]` : \"\";\n\n if (type === \"array\" && prop.items) {\n const itemType = (prop.items as Record<string, unknown>).type || \"object\";\n lines.push(`${pad} \"${key}\": ${type}<${itemType}>${req}${desc}${enumVals}`);\n } else if (type === \"object\" && prop.properties) {\n lines.push(`${pad} \"${key}\": ${describeSchema(prop as Record<string, unknown>, indent + 1)}${req}${desc}`);\n } else {\n lines.push(`${pad} \"${key}\": ${type}${req}${desc}${enumVals}`);\n }\n }\n lines.push(`${pad}}`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Extract JSON from CLI output that may contain prose, markdown fences, or raw JSON.\n * Tries multiple strategies to find valid JSON anywhere in the output.\n */\nfunction extractJSON(output: string): unknown | null {\n const trimmed = output.trim();\n\n // Strategy 1: Direct parse (output is raw JSON with no surrounding text)\n const direct = tryParseJSON(trimmed);\n if (direct && typeof direct === \"object\") return direct;\n\n // Strategy 2: Extract from markdown fenced code blocks (```json ... ``` or ``` ... ```)\n const fenceMatch = trimmed.match(/```(?:json|vibespot-modules)?\\s*\\n([\\s\\S]*?)```/i);\n if (fenceMatch) {\n const fenced = fenceMatch[1].trim();\n const parsed = tryParseJSON(fenced);\n if (parsed && typeof parsed === \"object\") return parsed;\n const repaired = tryRepairTruncatedJSON(fenced);\n if (repaired && typeof repaired === \"object\") return repaired;\n }\n\n // Strategy 3: Find the outermost { ... } in the output (greedy brace matching)\n const firstBrace = trimmed.indexOf(\"{\");\n const lastBrace = trimmed.lastIndexOf(\"}\");\n if (firstBrace !== -1 && lastBrace > firstBrace) {\n const braceContent = trimmed.slice(firstBrace, lastBrace + 1);\n const parsed = tryParseJSON(braceContent);\n if (parsed && typeof parsed === \"object\") return parsed;\n const repaired = tryRepairTruncatedJSON(braceContent);\n if (repaired && typeof repaired === \"object\") return repaired;\n }\n\n // Strategy 4: Try repair on the full output (may be truncated JSON with leading text stripped)\n const repaired = tryRepairTruncatedJSON(trimmed);\n if (repaired && typeof repaired === \"object\") return repaired;\n\n return null;\n}\n\n/**\n * Call an AI agent via CLI subprocess with prompt-based JSON extraction.\n */\nasync function callAgentCLI(\n engine: AgentEngine,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n const { bin, args } = resolveCLIBinary(engine);\n const prompt = buildCLIPrompt(opts);\n\n const rawOutput = await spawnCLI(bin, args, prompt, opts.onChunk);\n\n if (!opts.structuredOutput) {\n return { type: \"text\", text: rawOutput };\n }\n\n // Extract JSON from CLI output using multiple strategies\n const parsed = extractJSON(rawOutput);\n if (parsed) {\n return {\n type: \"structured\",\n data: stringifyJsonFields(parsed as Record<string, unknown>),\n };\n }\n\n log.warn(\"agent-cli\", `${engine}: failed to parse structured output, returning text`, {\n outputPreview: rawOutput.slice(0, 500),\n outputLength: rawOutput.length,\n });\n return { type: \"text\", text: rawOutput };\n}\n\n// ---------------------------------------------------------------------------\n// Unified entry points\n// ---------------------------------------------------------------------------\n\nconst API_ENGINES = new Set([\"anthropic-api\", \"claude-oauth\", \"openai-api\", \"gemini-api\"]);\n\n/**\n * Call an AI agent via API with optional structured output enforcement.\n */\nexport async function callAgentAPI(\n engine: AgentEngine,\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n log.info(\"agent-adapter\", `${engine} API call`, {\n model,\n structured: !!opts.structuredOutput,\n schemaName: opts.structuredOutput?.name,\n systemPromptLength: opts.systemPrompt.length,\n messageCount: opts.messages.length,\n });\n\n switch (engine) {\n case \"anthropic-api\":\n return callAnthropic(apiKey, model, opts);\n case \"claude-oauth\": {\n // Resolve fresh OAuth token at call time (auto-refreshes if needed)\n const { getValidAccessToken } = await import(\"../../utils/claude-oauth.js\");\n const oauthToken = await getValidAccessToken();\n if (!oauthToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n return callAnthropicOAuth(oauthToken, model, opts);\n }\n case \"openai-api\":\n return callOpenAI(apiKey, model, opts);\n case \"gemini-api\":\n return callGemini(apiKey, model, opts);\n default:\n throw new Error(`Unsupported API engine: ${engine}`);\n }\n}\n\n/**\n * Unified agent call dispatcher — routes to API or CLI adapter based on engine type.\n * Stages should call this instead of callAgentAPI directly.\n */\nexport async function callAgent(\n engine: AgentEngine,\n apiKey: string,\n model: string,\n opts: AgentCallOptions,\n): Promise<AgentCallResult> {\n if (API_ENGINES.has(engine)) {\n return callAgentAPI(engine, apiKey, model, opts);\n }\n\n log.info(\"agent-adapter\", `${engine} CLI call`, {\n structured: !!opts.structuredOutput,\n schemaName: opts.structuredOutput?.name,\n systemPromptLength: opts.systemPrompt.length,\n messageCount: opts.messages.length,\n });\n\n return callAgentCLI(engine, model, opts);\n}\n\n/**\n * Check if an engine type supports the agentic pipeline.\n * All engine types now support agentic mode — CLI engines use\n * prompt-based JSON extraction instead of native structured output.\n */\nexport function isAgenticCapable(\n engine: string,\n): engine is AgentEngine {\n return (\n engine === \"anthropic-api\" ||\n engine === \"claude-oauth\" ||\n engine === \"openai-api\" ||\n engine === \"gemini-api\" ||\n engine === \"claude-code\" ||\n engine === \"gemini-cli\" ||\n engine === \"codex-cli\"\n );\n}\n\n/**\n * Check if an engine is a CLI engine (subprocess-based).\n */\nexport function isCLIEngine(engine: string): boolean {\n return engine === \"claude-code\" || engine === \"gemini-cli\" || engine === \"codex-cli\";\n}\n","/**\n * Prompt builder for Stage 1: Intent Analyzer.\n * ~1.5K tokens — intent classification rules only. No guides.\n */\n\nexport function buildIntentAnalyzerPrompt(\n themeName: string,\n moduleNames: string[],\n libraryModuleNames: { name: string; usedIn: string[] }[],\n themeContext?: string,\n): string {\n const moduleList =\n moduleNames.length > 0\n ? `Current template modules (in page order):\\n${moduleNames.map((n, i) => `${i + 1}. ${n}`).join(\"\\n\")}`\n : \"No modules yet (new page).\";\n\n const libraryList =\n libraryModuleNames.length > 0\n ? `\\n\\nModule library (reusable from other templates):\\n${libraryModuleNames.map((m) => `- ${m.name} (used in: ${m.usedIn.join(\", \")})`).join(\"\\n\")}`\n : \"\";\n\n const contextSection = themeContext\n ? `\\n\\n## Product Context\\n${themeContext}`\n : \"\";\n\n return `You are the Intent Analyzer for vibeSpot, a HubSpot CMS page builder.\n\nYour job: classify the user's request and plan which modules need work. You do NOT generate module code — you only plan.\n\n## Theme: \"${themeName}\"\n\n${moduleList}${libraryList}${contextSection}\n\n## Classification Rules\n\n1. **create** — User wants a new page from scratch (e.g., \"build me a landing page for...\")\n2. **modify** — User wants to change existing modules (e.g., \"make the hero button red\", \"update the pricing\")\n3. **add** — User wants new modules added to the existing page (e.g., \"add a testimonials section\")\n4. **remove** — User wants modules removed (e.g., \"remove the footer\")\n5. **rearrange** — User wants to reorder modules (e.g., \"move pricing above features\")\n6. **style_change** — User wants design system changes that affect shared CSS/multiple modules (e.g., \"change the color scheme to blue\")\n7. **question** — User is asking a question, not requesting changes (e.g., \"what modules do I have?\"). Provide the answer directly.\n\n## Key Rules\n\n- For **modify**: list only the modules that actually need changes in \\`affectedModules\\`. Everything else goes in \\`unchangedModules\\`.\n- For **add**: new modules go in \\`newModules\\` with a descriptive name, brief description, and position index (0-based).\n- For **reuse**: if the user references a module from the library, put it in \\`reuseModules\\` with the source template name. Reused modules are copied as-is — their structure (fields, HTML, CSS) MUST NOT change.\n- For **style_change**: set \\`designSystemChanges: true\\`. All modules become affected since they need the updated design system.\n- For **question**: set \\`intent: \"question\"\\` and provide the answer in the \\`answer\\` field. The pipeline will short-circuit.\n- When the user references \"the rest of the page\", \"match the page style\", \"consistent with other sections\", or similar cross-module language, they want the target module to match the shared design system. Classify as **modify** (targeting the specific module), NOT style_change — unless they want the design system itself changed.\n- \\`guidesNeeded\\` determines which reference guides downstream stages receive. Only include what's actually needed:\n - \"design\" — for new pages, layout changes, design system work\n - \"content\" — for new pages, content-heavy changes\n - \"conversion\" — for any module code generation\n - \"hubspot_rules\" — for any module code generation\n - \"humanify\" — when generating user-facing copy\n\n## Conversation Context\n\nYou receive recent chat history (up to 3 prior exchanges). Use it to resolve:\n- **Back-references**: \"same section\", \"that module\", \"the one above\" → look at which module was modified in the previous turn\n- **Corrections**: \"I meant the hero\", \"no, the stakes section\", \"I was referencing X\" → the user is correcting YOUR previous classification. Re-apply the PREVIOUS request to the correct module. This is NOT a question — it's a \"modify\" intent.\n- **Follow-ups**: \"now make it bigger\", \"also add a CTA\" → applies to the module(s) from the previous turn\n\nCRITICAL: When the user corrects a misclassification (e.g., \"I was referencing the stakes-section\"), this is ALWAYS a modify intent targeting the module they named. NEVER classify corrections as \"question\".\n\n## Compound Requests\n\nIf the user asks for multiple things (e.g., \"make hero taller AND add testimonials\"), capture ALL parts:\n- Affected existing modules in \\`affectedModules\\`\n- New modules in \\`newModules\\`\n- Set the broadest applicable intent (prefer \"modify\" + newModules over splitting)`;\n}\n\n/** JSON Schema for PipelinePlan (used for structured output). */\nexport const INTENT_ANALYZER_SCHEMA = {\n type: \"object\",\n properties: {\n intent: {\n type: \"string\",\n enum: [\n \"create\",\n \"modify\",\n \"add\",\n \"remove\",\n \"rearrange\",\n \"style_change\",\n \"question\",\n ],\n },\n affectedModules: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Names of existing modules that need changes\",\n },\n unchangedModules: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Names of existing modules that stay as-is\",\n },\n newModules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n description: { type: \"string\" },\n position: { type: \"number\" },\n },\n required: [\"name\", \"description\", \"position\"],\n },\n description: \"New modules to create\",\n },\n reuseModules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n sourceTemplate: { type: \"string\" },\n position: { type: \"number\" },\n },\n required: [\"name\", \"sourceTemplate\", \"position\"],\n },\n description: \"Modules to copy from the library (immutable structure)\",\n },\n guidesNeeded: {\n type: \"array\",\n items: {\n type: \"string\",\n enum: [\n \"design\",\n \"content\",\n \"conversion\",\n \"hubspot_rules\",\n \"humanify\",\n ],\n },\n },\n designSystemChanges: {\n type: \"boolean\",\n description: \"True if shared CSS / design system needs regeneration\",\n },\n answer: {\n type: \"string\",\n description:\n 'For \"question\" intent only — the answer to return directly',\n },\n },\n required: [\n \"intent\",\n \"affectedModules\",\n \"unchangedModules\",\n \"newModules\",\n \"guidesNeeded\",\n \"designSystemChanges\",\n ],\n} as const;\n","/**\n * Stage 1: Intent Analyzer\n * Fast classification of user request. ~1.5K token system prompt.\n * Returns a PipelinePlan telling downstream stages what to do.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { PipelinePlan, PipelineEvent } from \"../types.js\";\nimport type { SessionSnapshot } from \"../../session/types.js\";\nimport {\n buildIntentAnalyzerPrompt,\n INTENT_ANALYZER_SCHEMA,\n} from \"../prompts/intent-analyzer.js\";\nimport { log } from \"../../log.js\";\n\nexport async function runIntentAnalyzer(\n userMessage: string,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n onEvent: (event: PipelineEvent) => void,\n libraryModules: { name: string; usedIn: string[] }[],\n): Promise<PipelinePlan> {\n onEvent({\n type: \"agent_step\",\n step: \"analyzing\",\n label: \"Analyzing your request...\",\n });\n\n const moduleNames = snapshot.modules.map((m) => m.moduleName);\n\n const systemPrompt = buildIntentAnalyzerPrompt(\n snapshot.themeName,\n moduleNames,\n libraryModules,\n snapshot.brandAssets?.themeContext,\n );\n\n // Build messages with recent conversation history for context resolution\n // (e.g., \"same section\", \"that module\", corrections like \"I meant the hero\")\n const messages: { role: \"user\" | \"assistant\"; content: string }[] = [];\n const recentMessages = snapshot.messages.slice(-6); // Last 3 exchanges max\n for (const msg of recentMessages) {\n if (msg.role === \"user\" || msg.role === \"assistant\") {\n // Truncate long assistant messages to just the first line (narrative summary)\n const content =\n msg.role === \"assistant\" && msg.content.length > 300\n ? msg.content.slice(0, 300) + \"...\"\n : msg.content;\n messages.push({ role: msg.role, content });\n }\n }\n // Always end with the current user message\n messages.push({ role: \"user\", content: userMessage });\n\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages,\n structuredOutput: {\n schema: INTENT_ANALYZER_SCHEMA as unknown as Record<string, unknown>,\n name: \"pipeline_plan\",\n },\n maxTokens: 2000,\n });\n\n if (result.type !== \"structured\") {\n log.warn(\"intent-analyzer\", \"Did not get structured output, falling back\");\n // Fallback: treat as full page create/modify\n const isNew = snapshot.modules.length === 0;\n return {\n intent: isNew ? \"create\" : \"modify\",\n affectedModules: isNew ? [] : moduleNames,\n unchangedModules: [],\n newModules: [],\n guidesNeeded: [\"design\", \"content\", \"conversion\", \"hubspot_rules\", \"humanify\"],\n designSystemChanges: isNew,\n };\n }\n\n const plan = result.data as PipelinePlan;\n\n // Ensure arrays exist\n plan.affectedModules = plan.affectedModules || [];\n plan.unchangedModules = plan.unchangedModules || [];\n plan.newModules = plan.newModules || [];\n plan.guidesNeeded = plan.guidesNeeded || [];\n\n log.info(\"intent-analyzer\", \"Plan\", {\n intent: plan.intent,\n affected: plan.affectedModules.length,\n unchanged: plan.unchangedModules.length,\n new: plan.newModules.length,\n reuse: plan.reuseModules?.length || 0,\n designSystem: plan.designSystemChanges,\n });\n\n onEvent({\n type: \"agent_decision\",\n step: \"analyzing\",\n decision: formatDecision(plan),\n });\n\n return plan;\n}\n\nfunction formatDecision(plan: PipelinePlan): string {\n const parts: string[] = [`Intent: ${plan.intent}`];\n\n if (plan.affectedModules.length > 0) {\n parts.push(`Modifying: ${plan.affectedModules.join(\", \")}`);\n }\n if (plan.unchangedModules.length > 0) {\n parts.push(`Unchanged: ${plan.unchangedModules.join(\", \")}`);\n }\n if (plan.newModules.length > 0) {\n parts.push(\n `New: ${plan.newModules.map((m) => m.name).join(\", \")}`,\n );\n }\n if (plan.reuseModules?.length) {\n parts.push(\n `Reuse: ${plan.reuseModules.map((m) => `${m.name} from ${m.sourceTemplate}`).join(\", \")}`,\n );\n }\n if (plan.designSystemChanges) {\n parts.push(\"Design system changes: yes\");\n }\n\n return parts.join(\" | \");\n}\n","/**\n * Prompt builders for Stage 2: Design System + Module Planner.\n *\n * Stage 2 is split into two sequential calls:\n * 2a: Design System — creates :root variables, shared CSS, shared JS\n * 2b: Module Planner — receives the CSS, plans modules with content briefs\n *\n * This split ensures the design system is complete and correct before\n * module developers reference it.\n */\n\nimport type { SystemPromptBlock } from \"../engine-adapter.js\";\n\n// ---------------------------------------------------------------------------\n// Stage 2a: Design System\n// ---------------------------------------------------------------------------\n\nexport function buildDesignSystemPrompt(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; themeContext?: string },\n): string {\n const parts: string[] = [];\n\n parts.push(`You are the Design System Architect for vibeSpot, a HubSpot CMS page builder.\n\nYour job: create a complete, production-ready CSS design system for a landing page theme. You produce the :root custom properties, shared utility/component CSS, and optional shared JS (scroll animations). Downstream agents will use YOUR CSS classes and variables to build individual modules.\n\n## Theme: \"${themeName}\"\n\n## Output Requirements\n\n### cssVariables\nA flat object mapping CSS custom property names to values. Every variable your CSS references MUST be defined here. Include ALL of these categories:\n\n**Colors** (at minimum):\n- --${themeName}-color-bg: page background\n- --${themeName}-color-surface: card/section background\n- --${themeName}-color-dark: dark section background\n- --${themeName}-color-dark-surface: card bg inside dark sections\n- --${themeName}-color-text: primary text color\n- --${themeName}-color-text-inverse: text on dark backgrounds\n- --${themeName}-color-text-muted: secondary/muted text\n- --${themeName}-color-primary: primary brand color\n- --${themeName}-color-primary-dark: darker variant for hover states\n- --${themeName}-color-accent: accent/highlight color\n- --${themeName}-color-accent-light: light tint for pill/badge backgrounds\n- --${themeName}-color-border: default border color\n- --${themeName}-color-border-hover: border on hover\n\n**Typography**:\n- --${themeName}-font-display: display/heading font stack (system fonts only)\n- --${themeName}-font-body: body text font stack (system fonts only)\n- --${themeName}-size-h1 through --${themeName}-size-h3: heading sizes using clamp()\n- --${themeName}-size-body, --${themeName}-size-lg, --${themeName}-size-small, --${themeName}-size-label\n- --${themeName}-leading-tight, --${themeName}-leading-snug, --${themeName}-leading-body: line heights\n- --${themeName}-tracking-tight, --${themeName}-tracking-wide: letter spacing\n\n**Spacing**:\n- --${themeName}-space-xs through --${themeName}-space-xl, --${themeName}-space-section\n- --${themeName}-max-width: content max-width (1152-1280px)\n\n**Effects**:\n- --${themeName}-radius-sm, --${themeName}-radius-md, --${themeName}-radius-lg, --${themeName}-radius-full\n- --${themeName}-shadow-card-hover, --${themeName}-shadow-button\n- --${themeName}-transition-fast, --${themeName}-transition-base, --${themeName}-transition-slow\n\n### sharedCss\nComplete CSS file content. MUST include:\n1. A \\`:root {}\\` block with ALL variables from cssVariables\n2. Reset (box-sizing, margin, padding)\n3. Body styles referencing your variables\n4. Typography rules (h1-h6, p)\n5. Layout utilities (.${themeName}-container, .${themeName}-section, .${themeName}-section--dark)\n6. Grid system (.${themeName}-grid, .${themeName}-grid--2/3/4 with responsive breakpoints)\n7. Card component (.${themeName}-card with hover lift)\n8. Button component (.${themeName}-btn, .${themeName}-btn--primary, .${themeName}-btn--secondary)\n CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus — HubSpot overrides link hover styles\n9. Pill/badge (.${themeName}-pill)\n10. Decorative elements (at least one background treatment: grid pattern, noise, gradient orb)\n11. Scroll animation CSS ([data-animate], [data-animate-stagger]) with 3s CSS-only fallback\n12. Section label (.${themeName}-label) — uppercase, letter-spacing, accent color\n13. Stat number styling\n14. Responsive mobile styles (@media max-width: 767px)\n\n### sharedJs (optional)\nIntersectionObserver-based scroll animation JS. Wrap in IIFE.\n\n## CSS Rules — CRITICAL\n- All classes MUST use prefix \"${themeName}-\"\n- Use BEM naming: ${themeName}-module__element--modifier\n- Use system font stacks ONLY (no Google Fonts @import, no external CDN)\n- Every var() reference in CSS must have a matching declaration in :root\n- No Tailwind, no Sass, no PostCSS\n- Use clamp() for fluid typography sizing\n\n## Font Strategy\nUse system font stacks that approximate the desired aesthetic. Pick TWO stacks:\n- Display: for headings (e.g., Georgia, \"Times New Roman\", serif for editorial)\n- Body: for text (e.g., system-ui, -apple-system, \"Segoe UI\", sans-serif)\n\nGood system font stacks by style:\n| Style | Display Stack | Body Stack |\n|-------|--------------|------------|\n| Editorial | Georgia, Cambria, \"Times New Roman\", serif | system-ui, -apple-system, \"Segoe UI\", sans-serif |\n| Modern | system-ui, -apple-system, sans-serif | \"Segoe UI\", Roboto, sans-serif |\n| Warm | Optima, Candara, \"Noto Sans\", sans-serif | \"Trebuchet MS\", system-ui, sans-serif |\n| Monospace/Tech | \"SF Mono\", \"Cascadia Code\", \"Fira Code\", monospace | system-ui, sans-serif |\n| Geometric | Futura, \"Century Gothic\", \"Trebuchet MS\", sans-serif | system-ui, sans-serif |`);\n\n parts.push(`\\n\\n## Design Guide\\n${getArchitectDesignSummary()}`);\n\n if (brandAssets?.styleguide) {\n parts.push(`\\n\\n## Brand Style Guide\\n${brandAssets.styleguide}`);\n }\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build design system prompt as blocks with cache control.\n * The design guide summary is static and cached.\n */\nexport function buildDesignSystemPromptBlocks(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; themeContext?: string },\n): SystemPromptBlock[] {\n // Build core prompt without the design guide (pass empty brandAssets to skip dynamic parts)\n const full = buildDesignSystemPrompt(themeName);\n // Split at the design guide marker\n const marker = \"\\n\\n## Design Guide\\n\";\n const markerIdx = full.indexOf(marker);\n\n if (markerIdx === -1) {\n // Fallback: no split possible, return as single block\n return [{ type: \"text\", text: full }];\n }\n\n const corePart = full.slice(0, markerIdx);\n const designGuide = `## Design Guide\\n${getArchitectDesignSummary()}`;\n\n const blocks: SystemPromptBlock[] = [\n { type: \"text\", text: corePart },\n { type: \"text\", text: designGuide, cache_control: { type: \"ephemeral\" } },\n ];\n\n // Dynamic brand assets\n const dynamicParts: string[] = [];\n if (brandAssets?.styleguide) dynamicParts.push(`## Brand Style Guide\\n${brandAssets.styleguide}`);\n if (brandAssets?.themeContext) dynamicParts.push(`## Product Context\\n${brandAssets.themeContext}`);\n if (dynamicParts.length > 0) {\n blocks.push({ type: \"text\", text: dynamicParts.join(\"\\n\\n\") });\n }\n\n return blocks;\n}\n\n/** JSON Schema for Design System output. */\nexport const DESIGN_SYSTEM_SCHEMA = {\n type: \"object\",\n properties: {\n cssVariables: {\n type: \"object\",\n description: \"CSS custom property name → value map. Every var() used in sharedCss must be defined here.\",\n },\n sharedCss: {\n type: \"string\",\n description: \"Complete shared CSS file. MUST start with :root {} block defining all cssVariables, followed by reset, typography, layout, components, animations, and responsive styles.\",\n },\n sharedJs: {\n type: \"string\",\n description: \"Optional shared JS for scroll animations (IntersectionObserver). Wrap in IIFE. Empty string if not needed.\",\n },\n aesthetic: {\n type: \"string\",\n description: \"Brief description of the chosen aesthetic direction (e.g., 'dark luxury with warm gold accents')\",\n },\n },\n required: [\"cssVariables\", \"sharedCss\", \"aesthetic\"],\n} as const;\n\n// ---------------------------------------------------------------------------\n// Stage 2b: Module Planner\n// ---------------------------------------------------------------------------\n\nexport function buildModulePlannerPrompt(\n themeName: string,\n sharedCss: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n guidesNeeded?: string[],\n): string {\n const parts: string[] = [];\n\n parts.push(`You are the Module Planner for vibeSpot, a HubSpot CMS page builder.\n\nYour job: plan the modules for a landing page. You define what each module contains (content brief) and how it should be laid out. You do NOT write module code — downstream Module Developers handle that.\n\nThe Design System has already been created. Your module plans MUST reference the existing CSS classes and variables.\n\n## Theme: \"${themeName}\"\n\n## Available CSS Classes\nThe following shared CSS classes are available for modules to use. Reference these in your layoutNotes:\n\n\\`\\`\\`css\n${sharedCss}\n\\`\\`\\`\n\n## Output Rules\n- Module names: descriptive, title-case (e.g., \"Hero Banner\", \"Pricing Cards\")\n- Content briefs: describe the actual copy/content each module needs (headlines, body text, CTAs, stats)\n- Layout notes: describe the visual layout using the available CSS classes above\n- Reference specific CSS classes from the shared CSS in your layout notes (e.g., \"Use ${themeName}-grid--3 for card layout, ${themeName}-section--dark for background\")\n- moduleOrder: list module names in the order they should appear on the page`);\n\n if (!guidesNeeded || guidesNeeded.includes(\"content\")) {\n parts.push(`\\n\\n## Content & Copywriting Guide\\n${getArchitectContentSummary()}`);\n }\n\n if (brandAssets?.brandvoice) {\n parts.push(`\\n\\n## Brand Voice\\n${brandAssets.brandvoice}`);\n }\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n parts.push(`\\n\\n## Anti-AI Copy Rules\\n${getArchitectHumanifySummary()}`);\n }\n\n return parts.join(\"\");\n}\n\n/** JSON Schema for Module Planner output. */\nexport const MODULE_PLANNER_SCHEMA = {\n type: \"object\",\n properties: {\n modules: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Module name in title-case\" },\n description: { type: \"string\", description: \"What this module does\" },\n contentBrief: { type: \"string\", description: \"Specific content: headlines, body copy, stats, CTAs\" },\n layoutNotes: { type: \"string\", description: \"Visual layout approach referencing shared CSS classes\" },\n },\n required: [\"name\", \"description\", \"contentBrief\", \"layoutNotes\"],\n },\n },\n moduleOrder: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Module names in page display order\",\n },\n narrative: {\n type: \"string\",\n description: \"Brief description of the page story/flow\",\n },\n },\n required: [\"modules\", \"moduleOrder\", \"narrative\"],\n} as const;\n\n// ---------------------------------------------------------------------------\n// Legacy export — kept for backward compatibility with pipeline imports\n// ---------------------------------------------------------------------------\n\n/** @deprecated Use DESIGN_SYSTEM_SCHEMA + MODULE_PLANNER_SCHEMA instead */\nexport const PAGE_ARCHITECT_SCHEMA = DESIGN_SYSTEM_SCHEMA;\n\n/** @deprecated Use buildDesignSystemPrompt + buildModulePlannerPrompt instead */\nexport function buildPageArchitectPrompt(\n themeName: string,\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n guidesNeeded?: string[],\n): string {\n return buildDesignSystemPrompt(themeName, brandAssets);\n}\n\n// ---------------------------------------------------------------------------\n// Guide summaries (shared by both prompts)\n// ---------------------------------------------------------------------------\n\nfunction getArchitectDesignSummary(): string {\n return `### Design Philosophy\nYou are a senior UI designer. Every page must look professionally designed, not like AI output.\nAvoid \"AI slop\": purple gradients on white, cookie-cutter card grids, no personality.\n\nBefore designing, decide on:\n- **Aesthetic direction**: minimal editorial? bold brutalist? warm organic? luxury dark-mode? Pick one and commit.\n- **One memorable element**: unusual layout, clever animation, striking color, unexpected typography.\n- **Audience**: the audience shapes the vibe. A SaaS dashboard ≠ a restaurant landing page.\n\nWhen a user gives a simple prompt like \"build me a landing page for a coffee shop,\" internally expand it:\n- Pick an aesthetic (warm, editorial, slightly vintage)\n- Pick specific colors (cream bg #faf7f2, espresso #3c1e0e, gold accent #c4956a)\n- Decide hero style (full-bleed image background, overlaid text)\n- Choose layout approach (asymmetric sections, large visual areas)\n- Add texture (subtle paper/grain noise overlay)\n- Set animations (scroll-triggered reveals)\nThe user gives the \"what,\" you decide the \"how it should look and feel.\"\n\n### Typography Scale\nInclude these in the CSS custom properties:\n\\`\\`\\`\nh1: clamp(2.5rem, 5vw, 4.5rem) /* Hero headlines — BIG */\nh2: clamp(1.75rem, 3vw, 3rem) /* Section headings */\nh3: clamp(1.25rem, 2vw, 1.75rem) /* Card titles, subheadings */\nbody: 1rem - 1.125rem /* 16-18px body text */\nsmall: 0.875rem /* Captions, labels */\nline-height: 1.1-1.2 for headings, 1.5-1.7 for body\nletter-spacing: -0.02em to -0.04em for large headings (tighter = more premium)\n\\`\\`\\`\n\n### Color Palettes\nPick a dominant (70%), secondary (25%), accent (5%). Ensure WCAG AA contrast (4.5:1 body, 3:1 large text).\n\n\\`\\`\\`\nDARK LUXURY: --bg: #0a0a0a; --surface: #141414; --text: #e8e8e8; --primary: #c9a84c; --accent: #e8d5a3\nWARM EARTH: --bg: #faf7f2; --surface: #f0ebe3; --text: #2d2418; --primary: #8b5e3c; --accent: #c4956a\nCOOL MINIMAL: --bg: #fafafa; --surface: #f1f1f1; --text: #1a1a1a; --primary: #0055ff; --accent: #00c4ff\nFOREST: --bg: #0f1a0f; --surface: #1a2e1a; --text: #d4e8d0; --primary: #4ade80; --accent: #22c55e\nEDITORIAL CREAM:--bg: #fffdf5; --surface: #f5f0e8; --text: #1c1917; --primary: #dc2626; --accent: #f97316\nNOIR: --bg: #000000; --surface: #111111; --text: #ffffff; --primary: #ffffff; --accent: #666666\n\\`\\`\\`\n\n### Layout Patterns\n1. **Split hero**: Content left, visual right (50/50 or 60/40)\n2. **Full-bleed hero**: Edge-to-edge background with centered content overlay\n3. **Bento grid**: Asymmetric grid with mixed card sizes (span-2, span-1)\n4. **Staggered/offset**: Content blocks not perfectly aligned, adds dynamism\n5. **Overlapping elements**: Cards/images that break grid lines, overlap sections\n6. **Scroll-based reveal**: Content appears as you scroll\n\n### Background Treatments (pick 1-2 per page)\nInclude these in shared CSS:\n- **Subtle grid pattern**: linear-gradient with thin lines at 60px intervals\n- **Noise texture overlay**: SVG feTurbulence filter at 0.03 opacity, fixed position\n- **Gradient orb/blob**: 600px radial-gradient circle, blurred 80px, absolute positioned\n- **Radial gradient on sections**: radial-gradient(ellipse at top, rgba(primary, 0.05), transparent 70%)\n- **Background alternation**: alternate section backgrounds every 2-3 sections to create visual \"chapters\"\n\n### Micro-Interactions (include in shared CSS)\n- **Card hover**: translateY(-4px) + box-shadow 20px 40px rgba(0,0,0,0.1), transition 0.3s ease\n- **Button hover**: translateY(-1px) + box-shadow 4px 12px rgba(primary, 0.3), transition 0.2s\n- **Link underline**: pseudo-element width 0 → 100% on hover, transition 0.3s\n- **Scroll animations**: data-animate elements start opacity:0 translateY(20px), animate to visible via IntersectionObserver. Include CSS fallback: elements become visible after 3s even if JS fails.\n- **Stagger children**: transition-delay: calc(var(--index) * 100ms)\n\n### Component Requirements\n- **Hero**: Visually dominant headline (largest on page), subheading with lower contrast, clear CTA with hover, visual interest (gradient/image/pattern/animation), min 80vh. Every hero needs a \"wow.\"\n- **Navigation**: Sticky with backdrop-blur-md bg-white/80, logo left, CTA right, active state indicator, smooth transition on scroll (shrink, shadow, bg change)\n- **Cards**: Subtle border OR shadow (not both heavy), rounded-xl to rounded-2xl, consistent padding, hover lift. Optional: subtle gradient border with pseudo-element\n- **Buttons**: Primary filled + secondary outlined/ghost, generous padding (px-6 py-3 min). CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus/:active — HubSpot overrides link hover styles\n- **Footer**: Darker than page, multi-column (3-4 cols), stacked on mobile, subtle separator from main content\n\n### Spacing\n- Section padding: 80-128px vertical\n- Content max-width: 1152-1280px centered\n- Card padding: 24-32px, gap: 24-32px\n- Between heading and body: 16-24px\n- Generous whitespace = premium. Cramped = amateur.\n- Mobile: always responsive, use clamp() for fluid sizing\n\n### Quality Checklist\n- [ ] Color palette has personality (not generic blue/purple on white)\n- [ ] Typography scale is consistent (headings use clamp(), body 16-18px)\n- [ ] Spacing is generous (sections have 80px+ padding)\n- [ ] At least one \"wow\" element (animation, unusual layout, bold color)\n- [ ] Backgrounds aren't flat (subtle pattern, gradient, or texture)\n- [ ] Hover states exist (cards lift, buttons shift, links animate)\n- [ ] Scroll animations present with CSS fallback\n- [ ] Mobile responsive (works at 375px)\n- [ ] Contrast ratios pass WCAG AA\n- [ ] Page feels cohesive (one aesthetic direction, not a Frankenstein)\n\n### Anti-Patterns\n\n| Don't | Do Instead |\n|-------|-----------|\n| Purple gradient on white | Choose a palette with personality |\n| Symmetric 3-col grids for everything | Mix layouts: bento, split, offset, overlapping |\n| Flat white/gray backgrounds | Add subtle texture, gradient, or pattern |\n| Tiny padding between sections | Use 80-128px for breathing room |\n| All animations same speed | Stagger with increasing delays |\n| Skip hover/focus states | Every interactive element needs feedback |\n| Use \\`<br>\\` tags for spacing | Use proper margin/padding |\n| Put everything in a shadowed card | Vary: full-bleed, contained, floating |`;\n}\n\nfunction getArchitectContentSummary(): string {\n return `### Mandatory Page Sections (generate all)\n1. **Navigation Bar** — Logo, 4-5 nav links, CTA button, sticky on scroll\n2. **Hero** — Badge/pill, primary headline, subheadline, primary + secondary CTA, trust signals, visual element\n3. **Social Proof Bar** — Logo strip of 4-6 clients OR stats bar (compact, py-8)\n4. **Features/Services** — Section label + headline, 3-6 cards with icon/title/description/metric\n5. **How It Works** — 3-4 numbered steps with titles, descriptions, visuals, connected flow\n6. **Testimonials** — At least 3 with full quotes, names, roles, ratings\n7. **Pricing/Value** — Pricing tiers or key metrics in large text with context + CTA\n8. **FAQ** — 4-6 real questions with specific, helpful answers\n9. **Final CTA** — Strong headline, subtext, primary + secondary buttons, visually distinct\n10. **Footer** — Brand name, 3-4 link columns with 3-5 links each, contact info, social icons, copyright\n\n### Optional Sections (include 1-2 when they fit)\n- Comparison table (\"Us vs. Them\")\n- Case study highlight\n- Team/About strip\n- Blog/Resource teasers\n- Partners/Integrations logo grid\n\n### Headline Rules — The \"Bar Test\"\nEvery headline should pass this test: if you shouted it across a bar, would someone turn their head?\n\n| Don't | Do Instead |\n|-------|-----------|\n| \"Our Services\" | \"What We Actually Do\" |\n| \"How It Works\" | \"Unclogged in 3 Steps\" |\n| \"Pricing\" | \"Cheaper Than Your Uber Eats Habit\" |\n| \"Testimonials\" | \"Don't Take Our Word For It\" |\n| \"Get Started\" | \"Blocked Drain? Text Us a Photo.\" |\n| \"Features\" | \"Everything You Get, Nothing You Don't\" |\n\n### CTA Button Copy\nNever use \"Submit\" or \"Learn More.\" Tie CTAs to specific outcomes:\n\"Book Now — From €49 →\" · \"Start Free Trial · No Card Required\" · \"Get My Custom Quote in 10 Min\" · \"Join 2,000+ Happy Customers\"\n\n### Minimum Content Quantities\n\n| Element | Min | Why |\n|---------|-----|-----|\n| Testimonials | 3 | One looks fake, two looks thin |\n| Feature cards | 4 | Three is a wireframe |\n| FAQ items | 4 | Fewer looks like hiding something |\n| Process steps | 3 | Natural narrative arc |\n| Stats/metrics | 3 | Singles look accidental |\n| Footer columns | 3 | Fewer = side project |\n| Nav links | 4-5 | Establishes depth |\n| CTA repetitions | 3 | Hero, mid-page, closing |\n\n### Business Type Content Templates\n\n**Local Service** (plumber, electrician, cleaner): Hero = pain point + speed promise. Must-have: service area, response time, pricing. CTAs: phone, WhatsApp, booking. Stats: response time, jobs done, satisfaction.\n\n**SaaS/Tech Product**: Hero = outcome-first (\"Save 10hrs/week\"). Must-have: feature grid, integration logos, product visual. CTAs: free trial, demo. Stats: performance, customer count, uptime.\n\n**Restaurant/Food**: Hero = sensory/emotional (\"Farm-to-table since 2019\"). Must-have: menu highlights with prices, hours, location. CTAs: reserve, order, menu. Stats: years open, dishes served.\n\n**E-commerce/DTC**: Hero = benefit + social proof (\"Join 50K+ happy sleepers\"). Must-have: features, comparison, guarantee. CTAs: shop, add to cart, \"Try risk-free.\" Stats: units sold, return rate.\n\n**Agency/Consultancy**: Hero = expertise + outcome (\"Scaled 40+ brands past €1M\"). Must-have: services, case studies, process. CTAs: book call, see cases, get proposal. Stats: clients, revenue, years.\n\n### Content Density — Never Leave Empty Space\nAt every viewport-height (100vh), the user should see:\n- At least one piece of **specific data** (number, price, time, rating)\n- At least one piece of **social proof** (quote, logo, rating, customer count)\n- At least one **visual element** (icon, illustration, decorative shape, gradient block)\n- A clear sense of **what section they're in** (label + headline visible)\n\nEvery card must contain ALL of: icon/visual, title (3-6 words), description (2-3 sentences with specific detail), optional link/metric. Never generate a card that is just a title and one sentence.\n\n### Content Rhythm & Visual Pacing\nAlternate section density — don't make every section the same weight:\n- HERO: Full, rich, attention-grabbing (100vh)\n- TRUST BAR: Compact (py-8 to py-12)\n- FEATURES: Dense, multi-card grid (tall section)\n- HOW IT WORKS: Medium, 3-4 steps with breathing room\n- TESTIMONIALS: Dense, 3+ cards\n- PRICING: Medium, 2-3 focused cards\n- FAQ: Compact (accordion saves space)\n- FINAL CTA: Full width, bold, short (50vh max)\n- FOOTER: Dense with links, compact\n\nAlternate backgrounds every 2-3 sections to create visual \"chapters.\" Sprinkle trust signals throughout (not just one section).\n\n### Body Copy Rules\n- Never write generic filler. Every sentence needs a SPECIFIC detail.\n- Invent plausible specifics: neighborhood names, \"48 hours\" not \"quickly\", \"€49\" not \"affordable\"\n- Keep paragraphs to 2-3 sentences max\n- Aim for 6th-grade reading level\n- Include section labels (UPPERCASE, letter-spacing 0.1em, accent color, 2-3 words) above every headline`;\n}\n\nfunction getArchitectHumanifySummary(): string {\n return `### Banned Punctuation\n- **Em dashes (—)**: NEVER use. Biggest AI tell. Replace with periods, commas, or parentheses.\n- **Semicolons**: Feel academic, not conversational. Use periods instead.\n- **Exclamation marks**: One per page maximum. Zero is ideal for B2B.\n\n### Banned Words\n**HARD BANNED (always rewrite):**\ndelve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone\n\n**SOFT BANNED (rewrite unless truly specific):**\nseamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)\n\n### Banned Openers\n\"In today's\", \"In an era\", \"In the realm\", \"Whether you're\", \"Are you tired\", \"Imagine a world\", \"Picture this\", \"Here's the thing\", \"Let's face it\", \"Look no further\", \"Say goodbye to\", \"Gone are the days\", \"It's no secret\", \"At its core\", \"At the end of the day\", \"When it comes to\"\n\n### Banned Closers\n\"The future of [X] is here\", \"Your journey starts here\", \"Join the revolution\", \"Experience the difference\", \"See what's possible\", \"Ready to take the next step\"\n\n### Banned Structures\n- \"It's not about X, it's about Y\" (second biggest AI tell after em dashes)\n- \"It's not just X, it's Y\"\n- \"[X]. Here's why.\" / \"[X]. And it matters.\"\n- \"Despite the challenges\"\n- Tricolon abuse (\"Fast, reliable, revolutionary\") — once per page max\n\n### Positive Rules\n- Be concrete, not abstract: \"42 minutes\" not \"fast\", \"€29/month\" not \"affordable\"\n- Use plain short words: use > utilize, start > commence, help > facilitate\n- Vary sentence length aggressively: mix 3-word, 12-word, and 25-word sentences\n- Front-load the benefit in the first 5 words\n- Write like you'd explain it in a bar — if you wouldn't say it holding a beer, rewrite it`;\n}\n","/**\n * Stage 2: Design System + Module Planner\n *\n * Split into two sequential agent calls:\n * 2a: Design System — creates :root vars, shared CSS, shared JS\n * 2b: Module Planner — plans modules using the finalized CSS\n *\n * This ensures module developers get a complete, working design system.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { PipelinePlan, PageBlueprint, DesignSystemOutput, PipelineEvent } from \"../types.js\";\nimport type { SessionSnapshot } from \"../../session/types.js\";\nimport {\n buildDesignSystemPrompt,\n buildDesignSystemPromptBlocks,\n buildModulePlannerPrompt,\n DESIGN_SYSTEM_SCHEMA,\n MODULE_PLANNER_SCHEMA,\n} from \"../prompts/page-architect.js\";\nimport { log } from \"../../log.js\";\n\nexport async function runPageArchitect(\n userMessage: string,\n plan: PipelinePlan,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n onEvent: (event: PipelineEvent) => void,\n): Promise<PageBlueprint> {\n // -------------------------------------------------------------------------\n // Stage 2a: Design System\n // -------------------------------------------------------------------------\n\n onEvent({\n type: \"agent_step\",\n step: \"designing\",\n label: \"Creating design system...\",\n });\n\n const isAnthropicEngine = engine === \"anthropic-api\" || engine === \"claude-oauth\";\n const designPrompt = buildDesignSystemPrompt(\n snapshot.themeName,\n snapshot.brandAssets,\n );\n const designBlocks = isAnthropicEngine\n ? buildDesignSystemPromptBlocks(snapshot.themeName, snapshot.brandAssets)\n : undefined;\n\n let designUserContent = `## User Request\\n${userMessage}`;\n if (snapshot.modules.length > 0 && plan.designSystemChanges) {\n designUserContent += `\\n\\n## Current Shared CSS (update this)\\n\\`\\`\\`css\\n${snapshot.sharedCss}\\n\\`\\`\\``;\n }\n\n const designResult = await callAgent(engine, apiKey, model, {\n systemPrompt: designPrompt,\n systemBlocks: designBlocks,\n messages: [{ role: \"user\", content: designUserContent }],\n structuredOutput: {\n schema: DESIGN_SYSTEM_SCHEMA as unknown as Record<string, unknown>,\n name: \"design_system\",\n },\n maxTokens: 16000,\n });\n\n let designSystem: DesignSystemOutput;\n\n if (designResult.type !== \"structured\") {\n log.warn(\"page-architect\", \"Design system: did not get structured output, using fallback\");\n designSystem = {\n cssVariables: {},\n sharedCss: snapshot.sharedCss || \"\",\n sharedJs: snapshot.sharedJs || \"\",\n aesthetic: \"default\",\n };\n } else {\n designSystem = designResult.data as DesignSystemOutput;\n log.info(\"page-architect\", \"Design system created\", {\n aesthetic: designSystem.aesthetic,\n varCount: Object.keys(designSystem.cssVariables || {}).length,\n cssLength: designSystem.sharedCss?.length || 0,\n });\n }\n\n // Ensure :root block exists in sharedCss\n let sharedCss = designSystem.sharedCss || \"\";\n const vars = designSystem.cssVariables;\n if (vars && typeof vars === \"object\" && Object.keys(vars).length > 0) {\n if (!sharedCss.includes(\":root\")) {\n const varLines = Object.entries(vars)\n .map(([k, v]) => ` ${k.startsWith(\"--\") ? k : `--${k}`}: ${v};`)\n .join(\"\\n\");\n sharedCss = `:root {\\n${varLines}\\n}\\n\\n${sharedCss}`;\n }\n }\n\n // Detect if the user requested web fonts that couldn't be used\n const fontNotes: string[] = [];\n const webFontPattern = /\\b(Montserrat|Inter|Poppins|Raleway|Playfair|Lato|Roboto|Open\\s?Sans|Nunito|Merriweather|Oswald|Source\\s?Sans|Fira\\s?Sans|Work\\s?Sans|Manrope|Plus\\s?Jakarta)\\b/gi;\n const requestedFonts = [...new Set((userMessage.match(webFontPattern) || []).map((f) => f.trim()))];\n if (requestedFonts.length > 0) {\n const usedFonts = requestedFonts.filter((f) =>\n sharedCss.toLowerCase().includes(f.toLowerCase()),\n );\n const droppedFonts = requestedFonts.filter((f) => !usedFonts.includes(f));\n if (droppedFonts.length > 0) {\n fontNotes.push(\n `Note: ${droppedFonts.join(\", \")} not available — HubSpot modules use system font stacks (no external font imports allowed)`,\n );\n }\n }\n\n const decisionParts = [\n `Design system: ${designSystem.aesthetic || \"created\"} | ${Object.keys(vars || {}).length} variables, ${sharedCss.length} chars CSS`,\n ...fontNotes,\n ];\n\n onEvent({\n type: \"agent_decision\",\n step: \"designing\",\n decision: decisionParts.join(\"\\n\"),\n });\n\n // Emit design system ready so the preview can start showing themed placeholders\n onEvent({\n type: \"design_system_ready\",\n sharedCss,\n sharedJs: designSystem.sharedJs || \"\",\n aesthetic: designSystem.aesthetic || \"\",\n });\n\n // -------------------------------------------------------------------------\n // Stage 2b: Module Planner\n // -------------------------------------------------------------------------\n\n onEvent({\n type: \"agent_step\",\n step: \"designing\",\n label: \"Planning modules...\",\n });\n\n const plannerPrompt = buildModulePlannerPrompt(\n snapshot.themeName,\n sharedCss,\n snapshot.brandAssets,\n plan.guidesNeeded,\n );\n\n let plannerUserContent = `## User Request\\n${userMessage}`;\n if (plan.newModules.length > 0) {\n plannerUserContent += `\\n\\n## Planned Modules\\n${plan.newModules.map((m, i) => `${i + 1}. **${m.name}** — ${m.description}`).join(\"\\n\")}`;\n }\n if (snapshot.modules.length > 0 && !plan.designSystemChanges) {\n plannerUserContent += `\\n\\n## Existing Modules (keeping)\\n${snapshot.modules.map((m) => `- ${m.moduleName}`).join(\"\\n\")}`;\n }\n\n const plannerResult = await callAgent(engine, apiKey, model, {\n systemPrompt: plannerPrompt,\n messages: [{ role: \"user\", content: plannerUserContent }],\n structuredOutput: {\n schema: MODULE_PLANNER_SCHEMA as unknown as Record<string, unknown>,\n name: \"module_plan\",\n },\n maxTokens: 8000,\n });\n\n let modulePlan: { modules: PageBlueprint[\"modules\"]; moduleOrder: string[]; narrative: string };\n\n if (plannerResult.type !== \"structured\") {\n log.warn(\"page-architect\", \"Module planner: did not get structured output, using fallback\");\n modulePlan = {\n modules: plan.newModules.map((m) => ({\n name: m.name,\n description: m.description,\n contentBrief: \"Generate appropriate content\",\n layoutNotes: \"Use responsive layout\",\n })),\n moduleOrder: plan.newModules.map((m) => m.name),\n narrative: \"Page generated from user request\",\n };\n } else {\n modulePlan = plannerResult.data as typeof modulePlan;\n log.info(\"page-architect\", \"Module plan\", {\n moduleCount: modulePlan.modules.length,\n });\n }\n\n onEvent({\n type: \"agent_decision\",\n step: \"designing\",\n decision: `Page: ${modulePlan.narrative} | ${modulePlan.modules.length} modules planned`,\n });\n\n // -------------------------------------------------------------------------\n // Assemble full blueprint\n // -------------------------------------------------------------------------\n\n return {\n designSystem: {\n cssVariables: designSystem.cssVariables || {},\n sharedCss,\n sharedJs: designSystem.sharedJs,\n },\n modules: modulePlan.modules,\n moduleOrder: modulePlan.moduleOrder,\n narrative: modulePlan.narrative,\n };\n}\n","/**\n * Shared types for the agentic pipeline.\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { SessionSnapshot } from \"../session/types.js\";\n\n// ---------------------------------------------------------------------------\n// Stage 1 output: Intent Analyzer\n// ---------------------------------------------------------------------------\n\nexport interface PipelinePlan {\n intent:\n | \"create\"\n | \"modify\"\n | \"add\"\n | \"remove\"\n | \"rearrange\"\n | \"style_change\"\n | \"question\";\n affectedModules: string[];\n unchangedModules: string[];\n newModules: { name: string; description: string; position: number }[];\n reuseModules?: {\n name: string;\n sourceTemplate: string;\n position: number;\n }[];\n guidesNeeded: (\n | \"design\"\n | \"content\"\n | \"conversion\"\n | \"hubspot_rules\"\n | \"humanify\"\n )[];\n designSystemChanges: boolean;\n answer?: string; // For \"question\" intent — short-circuits the pipeline\n}\n\n// ---------------------------------------------------------------------------\n// Stage 2a output: Design System\n// ---------------------------------------------------------------------------\n\nexport interface DesignSystemOutput {\n cssVariables: Record<string, string>;\n sharedCss: string;\n sharedJs?: string;\n aesthetic: string;\n}\n\n// ---------------------------------------------------------------------------\n// Stage 2 combined output: Page Architect (Design System + Module Plan)\n// ---------------------------------------------------------------------------\n\nexport interface PageBlueprint {\n designSystem: {\n cssVariables: Record<string, string>;\n sharedCss: string;\n sharedJs?: string;\n };\n modules: {\n name: string;\n description: string;\n contentBrief: string;\n layoutNotes: string;\n }[];\n moduleOrder: string[];\n narrative: string;\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline events (emitted to WebSocket)\n// ---------------------------------------------------------------------------\n\nexport type PipelineStep =\n | \"analyzing\"\n | \"designing\"\n | \"developing\"\n | \"quality_check\";\n\nexport type ModuleStatus =\n | \"queued\"\n | \"generating\"\n | \"validating\"\n | \"retrying\"\n | \"complete\"\n | \"failed\";\n\nexport type PipelineEvent =\n | { type: \"agent_step\"; step: PipelineStep; label: string }\n | { type: \"agent_decision\"; step: string; decision: string }\n | {\n type: \"module_progress\";\n module: string;\n status: ModuleStatus;\n current: number;\n total: number;\n moduleFiles?: ModuleFiles;\n }\n | {\n type: \"design_system_ready\";\n sharedCss: string;\n sharedJs: string;\n aesthetic: string;\n }\n | {\n type: \"blueprint_ready\";\n moduleOrder: string[];\n sharedCss: string;\n sharedJs?: string;\n }\n | { type: \"module_stream\"; module: string; content: string }\n | {\n type: \"pipeline_complete\";\n modulesGenerated: number;\n modulesUnchanged: number;\n durationMs: number;\n answer?: string;\n }\n | {\n type: \"pipeline_partial\";\n succeeded: string[];\n failed: string[];\n durationMs: number;\n };\n\n// ---------------------------------------------------------------------------\n// Pipeline result (returned by orchestrator)\n// ---------------------------------------------------------------------------\n\nexport interface PipelineResult {\n modules: ModuleFiles[];\n moduleOrder: string[];\n sharedCss: string;\n sharedJs: string;\n assistantMessage: string;\n stats: {\n modulesGenerated: number;\n modulesUnchanged: number;\n modulesFailed: number;\n durationMs: number;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Module spec (passed to Stage 3 Module Developer)\n// ---------------------------------------------------------------------------\n\nexport interface ModuleSpec {\n name: string;\n description: string;\n contentBrief: string;\n layoutNotes: string;\n existingCode?: ModuleFiles; // Present when modifying an existing module\n}\n\n// ---------------------------------------------------------------------------\n// Concurrency limiter helper\n// ---------------------------------------------------------------------------\n\nexport function createConcurrencyLimiter(maxConcurrent: number) {\n let running = 0;\n const queue: Array<() => void> = [];\n\n return async function limit<T>(fn: () => Promise<T>): Promise<T> {\n if (running >= maxConcurrent) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n running++;\n try {\n return await fn();\n } finally {\n running--;\n if (queue.length > 0) {\n queue.shift()!();\n }\n }\n };\n}\n","/**\n * Prompt builder for Stage 3: Module Developer.\n * Output format rules (~2K) + conversion guide (~20K) + HubSpot rules (~20K) = ~42K tokens.\n * Design/content guides excluded — those decisions were made in Stage 2.\n */\n\nimport {\n getConversionGuide,\n getHubspotRules,\n} from \"../../../ai/prompts.js\";\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { SystemPromptBlock } from \"../engine-adapter.js\";\n\nexport function buildModuleDeveloperPrompt(\n themeName: string,\n sharedCss: string,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n): string {\n const parts: string[] = [];\n\n parts.push(`You are a Module Developer for vibeSpot, a HubSpot CMS page builder.\n\nYour job: generate ONE HubSpot CMS module. You receive a module specification and must produce the complete module code.\n\n## Theme: \"${themeName}\"\n\n## Output Rules — CRITICAL\nYou produce a single module with these fields:\n- **moduleName**: Exact module name (title-case, e.g., \"Hero Banner\")\n- **fieldsJson**: Valid JSON string — the module's fields.json content\n- **metaJson**: Valid JSON string — must include host_template_types: [\"PAGE\"], is_available_for_new_content: true\n- **moduleHtml**: HubL template ({{ module.field_name }} syntax)\n- **moduleCss**: Vanilla CSS (no Tailwind, no Sass, no CDN imports)\n- **moduleJs**: Optional vanilla JS wrapped in IIFE, or null\n\n## CSS Rules\n- All CSS classes must use prefix \"${themeName}-\"\n- Use BEM naming: ${themeName}-moduleName__element--modifier\n- Reference the theme's CSS custom properties (shown below)\n- No CDN imports (@import url(), external <link> tags)\n- Use system font stacks — no Google Fonts\n\n## Field Rules\n- Use \"type\": \"text\" (NEVER \"textarea\" — it's deprecated)\n- NEVER use \"name\": \"name\" (reserved) — use \"item_name\" instead\n- NEVER use \"name\": \"label\" (reserved) — use \"section_label\" instead\n- NEVER put literal \\\\n in field defaults\n- Wrap style fields in a \"styles\" group with \"tab\": \"STYLE\"\n- Color fields: type \"color\", default { \"color\": \"#hex\", \"opacity\": 100 }\n- Link fields: type \"link\", default { \"url\": { \"href\": \"#\", \"type\": \"EXTERNAL\" }, \"open_in_new_tab\": false, \"no_follow\": false }\n- Image fields: type \"image\", default { \"src\": \"https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot\", \"alt\": \"Placeholder\", \"width\": 800, \"height\": 600 }\n- For repeater groups, use \"occurrence\": { \"min\": 0, \"max\": 100 }\n\n## Images & Assets\n- Use get_asset_url(\"${themeName}/assets/filename.ext\") for uploaded assets\n- For placeholder images, use image fields with placehold.co defaults\n- Size placeholders appropriately (hero: 1920x800, cards: 600x400, icons: 200x200)\n\n## Navigation & Anchors\n- Add id attribute on module root element: id=\"module-name-lowercased\"\n- For nav modules, use anchor links (#features, #pricing, etc.)\n- Include smooth scroll behavior in nav click handlers\n\n## metaJson Template\n{ \"host_template_types\": [\"PAGE\"], \"is_available_for_new_content\": true }`);\n\n if (sharedCss) {\n parts.push(`\\n\\n## Theme Shared CSS (use these custom properties)\\n\\`\\`\\`css\\n${sharedCss}\\n\\`\\`\\``);\n }\n\n if (!guidesNeeded || guidesNeeded.includes(\"hubspot_rules\")) {\n parts.push(`\\n\\n## HubSpot CMS Rules\\n${getHubspotRules()}`);\n }\n\n if (!guidesNeeded || guidesNeeded.includes(\"conversion\")) {\n parts.push(`\\n\\n## Conversion Guide\\n${getConversionGuide()}`);\n }\n\n if (brandAssets?.themeContext) {\n parts.push(`\\n\\n## Product Context\\n${brandAssets.themeContext}`);\n }\n\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n parts.push(`\\n\\n## Anti-AI Copy Rules\\n${getModuleDevHumanifySummary()}`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Build the module developer prompt as an array of blocks with cache control.\n * Static reference guides (conversion guide + HubSpot rules) are marked for caching.\n * These are identical across all parallel module generation calls in a batch.\n */\nexport function buildModuleDeveloperPromptBlocks(\n themeName: string,\n sharedCss: string,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean; themeContext?: string },\n): SystemPromptBlock[] {\n const blocks: SystemPromptBlock[] = [];\n\n // Block 1: Core instructions (varies by themeName + sharedCss)\n let core = buildModuleDeveloperPrompt(themeName, \"\", [], brandAssets ? { ...brandAssets, humanify: false } : undefined);\n if (sharedCss) {\n core += `\\n\\n## Theme Shared CSS (use these custom properties)\\n\\`\\`\\`css\\n${sharedCss}\\n\\`\\`\\``;\n }\n blocks.push({ type: \"text\", text: core });\n\n // Block 2: Reference guides — CACHED (identical across all module calls)\n const guideParts: string[] = [];\n if (!guidesNeeded || guidesNeeded.includes(\"hubspot_rules\")) {\n guideParts.push(`## HubSpot CMS Rules\\n${getHubspotRules()}`);\n }\n if (!guidesNeeded || guidesNeeded.includes(\"conversion\")) {\n guideParts.push(`## Conversion Guide\\n${getConversionGuide()}`);\n }\n if (guideParts.length > 0) {\n blocks.push({ type: \"text\", text: guideParts.join(\"\\n\\n\"), cache_control: { type: \"ephemeral\" } });\n }\n\n // Block 3: Dynamic content (brand assets, humanify)\n const dynamicParts: string[] = [];\n if (brandAssets?.themeContext) {\n dynamicParts.push(`## Product Context\\n${brandAssets.themeContext}`);\n }\n if (brandAssets?.humanify !== false && guidesNeeded?.includes(\"humanify\")) {\n dynamicParts.push(`## Anti-AI Copy Rules\\n${getModuleDevHumanifySummary()}`);\n }\n if (dynamicParts.length > 0) {\n blocks.push({ type: \"text\", text: dynamicParts.join(\"\\n\\n\") });\n }\n\n return blocks;\n}\n\n/**\n * Condensed humanify guide for Stage 3 (~2K chars).\n * Keeps: banned punctuation, full banned word list, banned openers/closers/structures,\n * positive rules, testimonial rules. Drops: full examples, explanations, sniff test.\n */\nfunction getModuleDevHumanifySummary(): string {\n return `### Banned Punctuation\n- **Em dashes (—)**: NEVER use. Replace with periods, commas, or parentheses. Hyphens for compounds fine.\n- **Semicolons**: Use periods instead in marketing copy.\n- **Exclamation marks**: One per page max. Zero ideal for B2B.\n\n### Banned Words\n**HARD BANNED:**\ndelve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone\n\n**SOFT BANNED (rewrite unless truly earned):**\nseamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)\n\n### Banned Openers\nNever start a heading or paragraph with: \"In today's\", \"In an era\", \"In the realm\", \"Whether you're\", \"Are you tired\", \"Imagine a world\", \"Picture this\", \"Here's the thing\", \"Let's face it\", \"Look no further\", \"Say goodbye to\", \"Gone are the days\", \"It's no secret\", \"At its core\", \"At the end of the day\", \"When it comes to\"\n\n### Banned Closers\nNever end with: \"The future of [X] is here\", \"Your journey starts here\", \"Join the revolution\", \"Experience the difference\", \"See what's possible\", \"Ready to take the next step\"\n\n### Banned Structures\n- \"It's not about X, it's about Y\" — just state Y\n- \"It's not just X, it's Y\" — just state Y\n- \"[X]. Here's why.\" / \"[X]. And it matters.\"\n- \"Despite the challenges\"\n- Tricolon abuse (\"Fast, reliable, revolutionary\") — max once per page\n\n### Positive Writing Rules\n- Be concrete: \"42 minutes\" not \"fast\", \"€29/month\" not \"affordable\", \"2,847 teams\" not \"thousands\"\n- Use plain words: use > utilize, start > commence, help > facilitate, enough > sufficient\n- Vary sentence length: mix 3-word, 12-word, and 25-word sentences\n- Front-load the benefit in the first 5 words\n- Write like you'd say it in a bar. If you wouldn't say it holding a beer, rewrite it.\n- One adjective per noun max. Zero is often better.\n\n### Testimonial Rules\n- Include a specific problem that was solved\n- Include a concrete detail (time saved, money saved, specific task)\n- Keep slightly imperfect (fragments OK, mild hedging like \"honestly didn't think\")\n- Full names, specific roles (not \"John D., CEO\")\n- Never start with \"This product is...\" — start with the person's situation\n- Vary length and voice across testimonials`;\n}\n\n/**\n * Build the user message for a single module generation call.\n */\nexport function buildModuleUserMessage(\n userMessage: string,\n spec: { name: string; description: string; contentBrief: string; layoutNotes: string },\n existingCode?: ModuleFiles,\n): string {\n const parts: string[] = [];\n\n parts.push(`## User Request\\n${userMessage}`);\n\n parts.push(`\\n\\n## Module Specification\n- **Name**: ${spec.name}\n- **Description**: ${spec.description}\n- **Content Brief**: ${spec.contentBrief}\n- **Layout Notes**: ${spec.layoutNotes}`);\n\n if (existingCode) {\n parts.push(`\\n\\n## Existing Module Code (modify this)\n**fields.json:**\n\\`\\`\\`json\n${existingCode.fieldsJson}\n\\`\\`\\`\n\n**module.html:**\n\\`\\`\\`html\n${existingCode.moduleHtml}\n\\`\\`\\`\n\n**module.css:**\n\\`\\`\\`css\n${existingCode.moduleCss}\n\\`\\`\\``);\n\n if (existingCode.moduleJs) {\n parts.push(`\\n**module.js:**\n\\`\\`\\`js\n${existingCode.moduleJs}\n\\`\\`\\``);\n }\n }\n\n return parts.join(\"\");\n}\n\n/** JSON Schema for a single ModuleFiles (used for structured output). */\nexport const MODULE_DEVELOPER_SCHEMA = {\n type: \"object\",\n properties: {\n moduleName: { type: \"string\" },\n fieldsJson: {\n type: \"string\",\n description: \"Complete fields.json content as a JSON string\",\n },\n metaJson: {\n type: \"string\",\n description: \"Complete meta.json content as a JSON string\",\n },\n moduleHtml: {\n type: \"string\",\n description: \"Complete module.html HubL template content\",\n },\n moduleCss: {\n type: \"string\",\n description: \"Complete module.css vanilla CSS content\",\n },\n moduleJs: {\n type: \"string\",\n description: \"Optional module.js vanilla JS content, or empty string if not needed\",\n },\n },\n required: [\n \"moduleName\",\n \"fieldsJson\",\n \"metaJson\",\n \"moduleHtml\",\n \"moduleCss\",\n ],\n} as const;\n","/**\n * Stage 3: Module Developer (Parallel)\n * One API call per module, run with concurrency limiter.\n * ~42K token system prompt (conversion guide + HubSpot rules).\n */\n\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport type { ModuleSpec, PipelineEvent } from \"../types.js\";\nimport { createConcurrencyLimiter } from \"../types.js\";\nimport {\n buildModuleDeveloperPrompt,\n buildModuleDeveloperPromptBlocks,\n buildModuleUserMessage,\n MODULE_DEVELOPER_SCHEMA,\n} from \"../prompts/module-developer.js\";\nimport { log } from \"../../log.js\";\n\nexport interface ModuleDevResult {\n moduleName: string;\n module?: ModuleFiles;\n error?: string;\n}\n\nexport async function runModuleDeveloper(\n userMessage: string,\n specs: ModuleSpec[],\n sharedCss: string,\n themeName: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n concurrency: number,\n onEvent: (event: PipelineEvent) => void,\n guidesNeeded?: string[],\n brandAssets?: { styleguide?: string; brandvoice?: string; humanify?: boolean },\n): Promise<ModuleDevResult[]> {\n onEvent({\n type: \"agent_step\",\n step: \"developing\",\n label: `Generating ${specs.length} module${specs.length === 1 ? \"\" : \"s\"}...`,\n });\n\n const isAnthropicEngine = engine === \"anthropic-api\" || engine === \"claude-oauth\";\n const systemPrompt = buildModuleDeveloperPrompt(\n themeName,\n sharedCss,\n guidesNeeded,\n brandAssets,\n );\n const systemBlocks = isAnthropicEngine\n ? buildModuleDeveloperPromptBlocks(themeName, sharedCss, guidesNeeded, brandAssets)\n : undefined;\n\n const limit = createConcurrencyLimiter(concurrency);\n const total = specs.length;\n\n const promises = specs.map((spec, index) =>\n limit(async (): Promise<ModuleDevResult> => {\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"generating\",\n current: index + 1,\n total,\n });\n\n let lastError = \"\";\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n if (attempt > 0) {\n log.warn(\"module-developer\", `${spec.name}: retrying after failure (attempt ${attempt + 1})`);\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"retrying\",\n current: index + 1,\n total,\n });\n }\n\n const module = await generateSingleModule(\n userMessage,\n spec,\n systemPrompt,\n engine,\n apiKey,\n model,\n 0,\n systemBlocks,\n );\n\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"complete\",\n current: index + 1,\n total,\n moduleFiles: module,\n });\n\n return { moduleName: spec.name, module };\n } catch (err) {\n lastError =\n err instanceof Error ? err.message\n : typeof err === \"object\" && err !== null ? JSON.stringify(err)\n : String(err);\n log.error(\"module-developer\", `Failed: ${spec.name} (attempt ${attempt + 1})`, {\n error: lastError,\n });\n }\n }\n\n // Both attempts failed\n onEvent({\n type: \"module_progress\",\n module: spec.name,\n status: \"failed\",\n current: index + 1,\n total,\n });\n\n return { moduleName: spec.name, error: lastError };\n }),\n );\n\n const results = await Promise.allSettled(promises);\n\n return results.map((r) => {\n if (r.status === \"fulfilled\") return r.value;\n return {\n moduleName: \"unknown\",\n error: r.reason instanceof Error ? r.reason.message : String(r.reason),\n };\n });\n}\n\nasync function generateSingleModule(\n userMessage: string,\n spec: ModuleSpec,\n systemPrompt: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n retryCount = 0,\n systemBlocks?: import(\"../engine-adapter.js\").SystemPromptBlock[],\n): Promise<ModuleFiles> {\n const userContent = buildModuleUserMessage(\n userMessage,\n spec,\n spec.existingCode,\n );\n\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n systemBlocks,\n messages: [{ role: \"user\", content: userContent }],\n structuredOutput: {\n schema: MODULE_DEVELOPER_SCHEMA as unknown as Record<string, unknown>,\n name: \"module_output\",\n },\n maxTokens: 16000,\n });\n\n if (result.type !== \"structured\") {\n if (retryCount < 2) {\n log.warn(\n \"module-developer\",\n `${spec.name}: no structured output, retry ${retryCount + 1}`,\n );\n return generateSingleModule(\n userMessage,\n spec,\n systemPrompt,\n engine,\n apiKey,\n model,\n retryCount + 1,\n systemBlocks,\n );\n }\n throw new Error(\n `Module \"${spec.name}\" failed to produce structured output after ${retryCount + 1} attempts`,\n );\n }\n\n const data = result.data as Record<string, unknown>;\n\n // Ensure fieldsJson and metaJson are strings\n const fieldsJson =\n typeof data.fieldsJson === \"string\"\n ? data.fieldsJson\n : JSON.stringify(data.fieldsJson, null, 2);\n const metaJson =\n typeof data.metaJson === \"string\"\n ? data.metaJson\n : JSON.stringify(data.metaJson, null, 2);\n\n return {\n moduleName: spec.name, // Always use canonical spec name, never AI-generated casing\n fieldsJson,\n metaJson,\n moduleHtml: String(data.moduleHtml || \"\"),\n moduleCss: String(data.moduleCss || \"\"),\n moduleJs: data.moduleJs ? String(data.moduleJs) : undefined,\n };\n}\n","/**\n * Stage 4: Validator + Assembler\n * Mostly rule-based code — catches errors before they reach the user or HubSpot.\n * Reuses patterns from auto-fix.ts.\n */\n\nimport type { ModuleFiles } from \"../../../ai/engine.js\";\nimport type { PipelineEvent } from \"../types.js\";\nimport { tryParseJSON } from \"../../ai-parser.js\";\nimport { log } from \"../../log.js\";\n\nexport interface ValidationIssue {\n module: string;\n field: string;\n message: string;\n autoFixed: boolean;\n}\n\nexport interface ValidationResult {\n module: ModuleFiles;\n issues: ValidationIssue[];\n valid: boolean;\n}\n\n/**\n * Validate and auto-fix a set of generated modules.\n */\nexport function validateModules(\n modules: ModuleFiles[],\n themeName: string,\n onEvent: (event: PipelineEvent) => void,\n): ValidationResult[] {\n onEvent({\n type: \"agent_step\",\n step: \"quality_check\",\n label: \"Quality check...\",\n });\n\n return modules.map((mod) => {\n const issues: ValidationIssue[] = [];\n let fixedModule = { ...mod };\n\n // --- JSON parsability ---\n fixedModule.fieldsJson = validateAndFixJson(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n \"fieldsJson\",\n issues,\n );\n fixedModule.metaJson = validateAndFixJson(\n fixedModule.metaJson,\n fixedModule.moduleName,\n \"metaJson\",\n issues,\n );\n\n // --- Reserved field names ---\n fixedModule.fieldsJson = fixReservedFieldNames(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n issues,\n );\n\n // --- Deprecated field types ---\n fixedModule.fieldsJson = fixDeprecatedFieldTypes(\n fixedModule.fieldsJson,\n fixedModule.moduleName,\n issues,\n );\n\n // --- CDN import stripping ---\n fixedModule.moduleCss = stripCdnImports(\n fixedModule.moduleCss,\n fixedModule.moduleName,\n \"moduleCss\",\n issues,\n );\n\n // --- CSS prefix auto-fix ---\n fixedModule.moduleCss = fixCssPrefix(\n fixedModule.moduleCss,\n fixedModule.moduleName,\n themeName,\n issues,\n );\n fixedModule.moduleHtml = fixHtmlClassPrefix(\n fixedModule.moduleHtml,\n fixedModule.moduleName,\n themeName,\n issues,\n );\n\n // --- HubL basic checks + auto-fix ---\n fixedModule.moduleHtml = fixHublSyntax(\n fixedModule.moduleHtml,\n fixedModule.moduleName,\n issues,\n );\n\n // --- metaJson required fields ---\n fixedModule.metaJson = ensureMetaFields(\n fixedModule.metaJson,\n fixedModule.moduleName,\n issues,\n );\n\n const valid = issues.every((i) => i.autoFixed);\n\n if (issues.length > 0) {\n log.info(\"validator\", `${fixedModule.moduleName}: ${issues.length} issues`, {\n autoFixed: issues.filter((i) => i.autoFixed).length,\n unfixed: issues.filter((i) => !i.autoFixed).length,\n });\n }\n\n return { module: fixedModule, issues, valid };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Validators\n// ---------------------------------------------------------------------------\n\nfunction validateAndFixJson(\n jsonStr: string,\n moduleName: string,\n field: string,\n issues: ValidationIssue[],\n): string {\n if (!jsonStr || jsonStr.trim() === \"\") {\n issues.push({\n module: moduleName,\n field,\n message: `Empty ${field}`,\n autoFixed: field === \"metaJson\", // metaJson can be auto-generated\n });\n if (field === \"metaJson\") {\n return JSON.stringify({\n host_template_types: [\"PAGE\"],\n is_available_for_new_content: true,\n });\n }\n return jsonStr;\n }\n\n const parsed = tryParseJSON(jsonStr);\n if (parsed === null) {\n issues.push({\n module: moduleName,\n field,\n message: `Invalid JSON in ${field}`,\n autoFixed: false,\n });\n }\n return jsonStr;\n}\n\nfunction fixReservedFieldNames(\n fieldsJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n let fixed = fieldsJson;\n\n // \"name\": \"name\" → \"name\": \"item_name\"\n const namePattern = /\"name\"\\s*:\\s*\"name\"/g;\n if (namePattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"name\" is a reserved field name → renamed to \"item_name\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"name\"\\s*:\\s*\"name\"/g, '\"name\": \"item_name\"');\n }\n\n // \"name\": \"label\" → \"name\": \"section_label\"\n const labelPattern = /\"name\"\\s*:\\s*\"label\"/g;\n if (labelPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"label\" is a reserved field name → renamed to \"section_label\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"name\"\\s*:\\s*\"label\"/g, '\"name\": \"section_label\"');\n }\n\n return fixed;\n}\n\nfunction fixDeprecatedFieldTypes(\n fieldsJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n let fixed = fieldsJson;\n\n const textareaPattern = /\"type\"\\s*:\\s*\"textarea\"/g;\n if (textareaPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field: \"fieldsJson\",\n message: '\"textarea\" is deprecated → changed to \"text\"',\n autoFixed: true,\n });\n fixed = fixed.replace(/\"type\"\\s*:\\s*\"textarea\"/g, '\"type\": \"text\"');\n }\n\n return fixed;\n}\n\nfunction stripCdnImports(\n css: string,\n moduleName: string,\n field: string,\n issues: ValidationIssue[],\n): string {\n if (!css) return css;\n let fixed = css;\n\n // @import url(...) for external fonts\n const importPattern = /@import\\s+url\\([^)]*(?:fonts\\.googleapis|cdnjs|unpkg|jsdelivr)[^)]*\\)\\s*;?/gi;\n if (importPattern.test(fixed)) {\n issues.push({\n module: moduleName,\n field,\n message: \"CDN @import removed (external imports not allowed)\",\n autoFixed: true,\n });\n fixed = fixed.replace(importPattern, \"/* CDN import removed */\");\n }\n\n return fixed;\n}\n\n/** Classes that should NOT be prefixed (framework/HubSpot/utility). */\nconst SKIP_CLASSES = new Set([\n \"visible\", \"active\", \"scroll-animate\", \"hidden\", \"open\", \"closed\",\n \"fade-in\", \"fade-out\", \"is-active\", \"is-open\", \"is-visible\",\n]);\nfunction shouldSkipClass(name: string): boolean {\n return (\n SKIP_CLASSES.has(name) ||\n name.startsWith(\"body-wrapper\") ||\n name.startsWith(\"dnd-\") ||\n name.startsWith(\"row-\") ||\n name.startsWith(\"hs-\") ||\n name.startsWith(\"hs_\")\n );\n}\n\n/**\n * Auto-fix CSS classes that don't use the theme prefix.\n * Adds `themeName-` prefix to unprefixed class selectors in CSS and returns the fixed CSS.\n */\nfunction fixCssPrefix(\n css: string,\n moduleName: string,\n themeName: string,\n issues: ValidationIssue[],\n): string {\n if (!css) return css;\n\n const prefix = themeName + \"-\";\n\n // Collect unprefixed class names (deduplicated, ordered by first occurrence)\n const classPattern = /\\.([a-zA-Z][\\w-]*)/g;\n const unprefixedSet = new Set<string>();\n let match;\n\n while ((match = classPattern.exec(css)) !== null) {\n const className = match[1];\n if (!className.startsWith(prefix) && !shouldSkipClass(className)) {\n unprefixedSet.add(className);\n }\n }\n\n if (unprefixedSet.size <= 3) return css; // minor — don't bother\n\n // Replace each unprefixed class with the prefixed version\n let fixed = css;\n for (const name of unprefixedSet) {\n // Replace in selectors: .className → .prefix-className\n // Use word boundary to avoid partial matches\n const selectorRe = new RegExp(`\\\\.${escapeRegex(name)}(?=[\\\\s,{:+~>\\\\[\\\\]])`, \"g\");\n fixed = fixed.replace(selectorRe, `.${prefix}${name}`);\n }\n\n if (fixed !== css) {\n issues.push({\n module: moduleName,\n field: \"moduleCss\",\n message: `${unprefixedSet.size} CSS classes auto-prefixed with \"${prefix}\"`,\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\n/**\n * Auto-fix class references in HTML to match prefixed CSS classes.\n */\nfunction fixHtmlClassPrefix(\n html: string,\n moduleName: string,\n themeName: string,\n issues: ValidationIssue[],\n): string {\n if (!html) return html;\n\n const prefix = themeName + \"-\";\n\n // Find all class=\"...\" attributes and check for unprefixed classes\n const classAttrRe = /class=\"([^\"]*)\"/g;\n let anyFixed = false;\n\n const fixed = html.replace(classAttrRe, (fullMatch, classValue: string) => {\n const classes = classValue.split(/\\s+/);\n let changed = false;\n const newClasses = classes.map((cls: string) => {\n if (cls && !cls.startsWith(prefix) && !shouldSkipClass(cls) && /^[a-zA-Z][\\w-]*$/.test(cls)) {\n changed = true;\n return prefix + cls;\n }\n return cls;\n });\n if (changed) {\n anyFixed = true;\n return `class=\"${newClasses.join(\" \")}\"`;\n }\n return fullMatch;\n });\n\n if (anyFixed) {\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `HTML class references auto-prefixed with \"${prefix}\"`,\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction fixHublSyntax(\n html: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n if (!html) return html;\n let fixed = html;\n\n // Two-pass approach to handle unbalanced HubL tags:\n // Pass 1: collect all tags in order to find orphans\n // Pass 2: apply fixes\n\n const tagPattern = /\\{%[-~]?\\s*(if|for|block|macro|endif|endfor|endblock|endmacro)\\b[^%]*%\\}/g;\n const tags: { tag: string; isOpen: boolean; baseTag: string; start: number; end: number }[] = [];\n let match;\n\n while ((match = tagPattern.exec(fixed)) !== null) {\n const tag = match[1];\n const isOpen = !tag.startsWith(\"end\");\n const baseTag = isOpen ? tag : tag.replace(\"end\", \"\");\n tags.push({ tag, isOpen, baseTag, start: match.index, end: match.index + match[0].length });\n }\n\n // Match openers to closers using a stack\n const stack: number[] = []; // indices into tags[]\n const orphanClosers: number[] = []; // indices of unmatched closers\n\n for (let i = 0; i < tags.length; i++) {\n if (tags[i].isOpen) {\n stack.push(i);\n } else {\n // Find matching opener on the stack (search from top)\n let found = -1;\n for (let j = stack.length - 1; j >= 0; j--) {\n if (tags[stack[j]].baseTag === tags[i].baseTag) {\n found = j;\n break;\n }\n }\n if (found !== -1) {\n stack.splice(found, 1);\n } else {\n orphanClosers.push(i);\n }\n }\n }\n\n // Remove orphan closing tags (replace in reverse order to keep positions valid)\n if (orphanClosers.length > 0) {\n for (let i = orphanClosers.length - 1; i >= 0; i--) {\n const t = tags[orphanClosers[i]];\n fixed =\n fixed.slice(0, t.start) +\n `<!-- removed orphan {% ${t.tag} %} -->` +\n fixed.slice(t.end);\n }\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `Removed ${orphanClosers.length} orphan closing tag${orphanClosers.length === 1 ? \"\" : \"s\"} with no matching opener`,\n autoFixed: true,\n });\n }\n\n // Append missing closing tags for unclosed openers (stack has unmatched openers)\n if (stack.length > 0) {\n const unclosed = stack.map((i) => tags[i].baseTag);\n const closers = unclosed\n .reverse()\n .map((tag) => `{% end${tag} %}`)\n .join(\"\\n\");\n fixed = `${fixed}\\n${closers}`;\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: `Added ${unclosed.length} missing closing tag${unclosed.length === 1 ? \"\" : \"s\"}: ${unclosed.map((t) => `{% end${t} %}`).join(\", \")}`,\n autoFixed: true,\n });\n }\n\n // Fix now() → local_dt\n if (/\\bnow\\(\\)/.test(fixed)) {\n fixed = fixed.replace(/\\bnow\\(\\)/g, \"local_dt\");\n issues.push({\n module: moduleName,\n field: \"moduleHtml\",\n message: \"Replaced now() with local_dt (now() is not valid HubL)\",\n autoFixed: true,\n });\n }\n\n return fixed;\n}\n\nfunction ensureMetaFields(\n metaJson: string,\n moduleName: string,\n issues: ValidationIssue[],\n): string {\n const parsed = tryParseJSON(metaJson);\n if (!parsed || typeof parsed !== \"object\") return metaJson;\n\n const obj = parsed as Record<string, unknown>;\n let changed = false;\n\n if (!obj.host_template_types) {\n obj.host_template_types = [\"PAGE\"];\n changed = true;\n }\n if (obj.is_available_for_new_content === undefined) {\n obj.is_available_for_new_content = true;\n changed = true;\n }\n\n if (changed) {\n issues.push({\n module: moduleName,\n field: \"metaJson\",\n message: \"Added missing meta.json required fields\",\n autoFixed: true,\n });\n return JSON.stringify(obj, null, 2);\n }\n\n return metaJson;\n}\n","/**\n * Agentic Pipeline Orchestrator\n *\n * Runs the 4-stage pipeline:\n * 1. Intent Analyzer — classify request, plan modules\n * 2. Page Architect — design system + module specs (new pages / design changes only)\n * 3. Module Developer — parallel per-module generation\n * 4. Validator — rule-based checks + auto-fix\n */\n\nimport type { ModuleFiles } from \"../../ai/engine.js\";\nimport type { SessionSnapshot } from \"../session/types.js\";\nimport type { AgentEngine } from \"./engine-adapter.js\";\nimport { isCLIEngine } from \"./engine-adapter.js\";\nimport type {\n PipelineEvent,\n PipelineResult,\n ModuleSpec,\n PageBlueprint,\n} from \"./types.js\";\nimport { runIntentAnalyzer } from \"./stages/intent-analyzer.js\";\nimport { runPageArchitect } from \"./stages/page-architect.js\";\nimport { runModuleDeveloper } from \"./stages/module-developer.js\";\nimport { validateModules } from \"./stages/validator.js\";\nimport { log } from \"../log.js\";\nimport { execSync } from \"node:child_process\";\n\nexport { isAgenticCapable, isCLIEngine } from \"./engine-adapter.js\";\n\n/**\n * Run the full agentic pipeline for a user message.\n *\n * @param userMessage The user's chat message\n * @param snapshot Immutable copy of session state at pipeline start\n * @param engine Which API engine to use\n * @param apiKey API key for the engine\n * @param model Model ID\n * @param concurrency Max parallel module generation calls\n * @param onEvent Callback for pipeline progress events (WebSocket)\n * @param libraryModules Modules available for reuse from other templates\n */\nexport async function runAgentPipeline(\n userMessage: string,\n snapshot: SessionSnapshot,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n concurrency: number,\n onEvent: (event: PipelineEvent) => void,\n libraryModules: { name: string; usedIn: string[] }[],\n): Promise<PipelineResult> {\n const startTime = Date.now();\n\n // All engines use the configured concurrency (default 20, cap in ai-handler)\n const effectiveConcurrency = concurrency;\n\n if (isCLIEngine(engine)) {\n const binMap: Record<string, string> = {\n \"claude-code\": \"claude\",\n \"gemini-cli\": \"gemini\",\n \"codex-cli\": \"codex\",\n };\n const bin = binMap[engine];\n if (bin) {\n try {\n execSync(`command -v ${bin}`, { stdio: \"ignore\" });\n } catch {\n throw new Error(\n `CLI engine \"${engine}\" requires \"${bin}\" to be installed and on your PATH.`,\n );\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 1: Intent Analyzer\n // -----------------------------------------------------------------------\n\n const plan = await runIntentAnalyzer(\n userMessage,\n snapshot,\n engine,\n apiKey,\n model,\n onEvent,\n libraryModules,\n );\n\n // Short-circuit for questions\n if (plan.intent === \"question\" && plan.answer) {\n const durationMs = Date.now() - startTime;\n onEvent({\n type: \"pipeline_complete\",\n modulesGenerated: 0,\n modulesUnchanged: snapshot.modules.length,\n durationMs,\n answer: plan.answer,\n });\n return {\n modules: [...snapshot.modules],\n moduleOrder: snapshot.moduleOrder as string[],\n sharedCss: snapshot.sharedCss,\n sharedJs: snapshot.sharedJs,\n assistantMessage: plan.answer,\n stats: {\n modulesGenerated: 0,\n modulesUnchanged: snapshot.modules.length,\n modulesFailed: 0,\n durationMs,\n },\n };\n }\n\n // -----------------------------------------------------------------------\n // Stage 2: Page Architect (new pages or design system changes)\n // -----------------------------------------------------------------------\n\n let blueprint: PageBlueprint | null = null;\n let sharedCss = snapshot.sharedCss;\n let sharedJs = snapshot.sharedJs;\n\n const needsArchitect =\n plan.intent === \"create\" || plan.designSystemChanges;\n\n if (needsArchitect) {\n // Stage 2 now runs two sequential calls:\n // 2a: Design System (CSS vars + shared CSS/JS) — emits design_system_ready\n // 2b: Module Planner (module specs + order) — uses the finalized CSS\n blueprint = await runPageArchitect(\n userMessage,\n plan,\n snapshot,\n engine,\n apiKey,\n model,\n onEvent,\n );\n // sharedCss already has :root block merged by the stage\n sharedCss = blueprint.designSystem.sharedCss || sharedCss;\n sharedJs = blueprint.designSystem.sharedJs || sharedJs;\n\n // Notify client of module order for incremental preview placeholders\n onEvent({\n type: \"blueprint_ready\",\n moduleOrder: blueprint.moduleOrder,\n sharedCss,\n sharedJs,\n });\n }\n\n // -----------------------------------------------------------------------\n // Build module specs for Stage 3\n // -----------------------------------------------------------------------\n\n const moduleSpecs: ModuleSpec[] = [];\n\n // New modules from the plan\n if (blueprint) {\n for (const bpMod of blueprint.modules) {\n moduleSpecs.push({\n name: bpMod.name,\n description: bpMod.description,\n contentBrief: bpMod.contentBrief,\n layoutNotes: bpMod.layoutNotes,\n });\n }\n } else {\n // No blueprint — build specs from plan\n for (const newMod of plan.newModules) {\n moduleSpecs.push({\n name: newMod.name,\n description: newMod.description,\n contentBrief: \"Generate appropriate content based on the user request\",\n layoutNotes: \"Use responsive layout matching the existing design system\",\n });\n }\n\n // Affected existing modules (modifications)\n for (const modName of plan.affectedModules) {\n const existing = snapshot.modules.find(\n (m) => m.moduleName === modName,\n );\n if (existing) {\n moduleSpecs.push({\n name: modName,\n description: `Modify existing module: ${modName}`,\n contentBrief: \"Apply the user's requested changes\",\n layoutNotes: \"Preserve existing layout unless changes are requested\",\n existingCode: existing,\n });\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 3: Module Developer (parallel)\n // -----------------------------------------------------------------------\n\n let generatedModules: ModuleFiles[] = [];\n let failedModules: string[] = [];\n\n if (moduleSpecs.length > 0) {\n const devResults = await runModuleDeveloper(\n userMessage,\n moduleSpecs,\n sharedCss,\n snapshot.themeName,\n engine,\n apiKey,\n model,\n effectiveConcurrency,\n onEvent,\n plan.guidesNeeded,\n snapshot.brandAssets,\n );\n\n for (const r of devResults) {\n if (r.module) {\n generatedModules.push(r.module);\n } else {\n failedModules.push(r.moduleName);\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 4: Quality Check\n // -----------------------------------------------------------------------\n\n let validationResults: import(\"./stages/validator.js\").ValidationResult[] | null = null;\n\n if (generatedModules.length > 0) {\n validationResults = validateModules(\n generatedModules,\n snapshot.themeName,\n onEvent,\n );\n\n // Replace generated modules with validated/auto-fixed versions\n generatedModules = validationResults.map((r) => r.module);\n\n // Log quality check summary with details\n const totalIssues = validationResults.reduce(\n (sum, r) => sum + r.issues.length,\n 0,\n );\n if (totalIssues > 0) {\n const autoFixed = validationResults.reduce(\n (sum, r) => sum + r.issues.filter((i) => i.autoFixed).length,\n 0,\n );\n log.info(\"pipeline\", `Quality check: ${totalIssues} issues, ${autoFixed} auto-fixed`);\n\n // Build detailed issue list for the user\n const issueDetails = validationResults\n .flatMap((r) => r.issues)\n .map((i) => `${i.autoFixed ? \"✓\" : \"⚠\"} ${i.module}: ${i.message}`)\n .join(\"\\n\");\n\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: `${totalIssues} issues found, ${autoFixed} auto-fixed\\n${issueDetails}`,\n });\n } else {\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: \"All modules passed quality checks\",\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Assemble final module list\n // -----------------------------------------------------------------------\n\n const finalModules = assembleModuleList(\n snapshot,\n plan,\n generatedModules,\n blueprint,\n libraryModules,\n );\n\n // Build module order (reconciles any missing modules automatically)\n const moduleOrder = buildModuleOrder(\n snapshot,\n plan,\n blueprint,\n finalModules,\n );\n\n // Warn if moduleOrder was missing modules (reconciled in buildModuleOrder)\n if (blueprint?.moduleOrder?.length) {\n const blueprintSet = new Set(blueprint.moduleOrder);\n const missing = finalModules\n .filter((m) => !blueprintSet.has(m.moduleName))\n .map((m) => m.moduleName);\n if (missing.length > 0) {\n onEvent({\n type: \"agent_decision\",\n step: \"quality_check\",\n decision: `⚠ ${missing.length} module${missing.length === 1 ? \"\" : \"s\"} missing from page order — auto-inserted: ${missing.join(\", \")}`,\n });\n }\n }\n\n // -----------------------------------------------------------------------\n // Build assistant message\n // -----------------------------------------------------------------------\n\n const durationMs = Date.now() - startTime;\n const modulesGenerated = generatedModules.length;\n const modulesUnchanged = plan.unchangedModules.length;\n\n const validationIssues = validationResults\n ? validationResults.flatMap((r) => r.issues)\n : [];\n\n const assistantMessage = buildAssistantMessage(\n plan,\n modulesGenerated,\n modulesUnchanged,\n failedModules,\n durationMs,\n blueprint,\n validationIssues,\n );\n\n // -----------------------------------------------------------------------\n // Emit completion event\n // -----------------------------------------------------------------------\n\n if (failedModules.length > 0) {\n onEvent({\n type: \"pipeline_partial\",\n succeeded: generatedModules.map((m) => m.moduleName),\n failed: failedModules,\n durationMs,\n });\n } else {\n onEvent({\n type: \"pipeline_complete\",\n modulesGenerated,\n modulesUnchanged,\n durationMs,\n });\n }\n\n return {\n modules: finalModules,\n moduleOrder,\n sharedCss,\n sharedJs,\n assistantMessage,\n stats: {\n modulesGenerated,\n modulesUnchanged,\n modulesFailed: failedModules.length,\n durationMs,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Assemble the final module list by combining generated, unchanged, and reused modules.\n */\nfunction assembleModuleList(\n snapshot: SessionSnapshot,\n plan: { unchangedModules: string[]; reuseModules?: { name: string; sourceTemplate: string; position: number }[] },\n generatedModules: ModuleFiles[],\n blueprint: PageBlueprint | null,\n libraryModules: { name: string; usedIn: string[]; module?: ModuleFiles }[],\n): ModuleFiles[] {\n const result: ModuleFiles[] = [];\n const added = new Set<string>();\n\n // Add generated modules\n for (const mod of generatedModules) {\n result.push(mod);\n added.add(mod.moduleName);\n }\n\n // Add unchanged modules from snapshot\n for (const name of plan.unchangedModules) {\n if (added.has(name)) continue;\n const existing = snapshot.modules.find((m) => m.moduleName === name);\n if (existing) {\n result.push(existing as ModuleFiles);\n added.add(name);\n }\n }\n\n // Add reused modules from library\n if (plan.reuseModules) {\n for (const reuse of plan.reuseModules) {\n if (added.has(reuse.name)) continue;\n const libEntry = libraryModules.find(\n (l) => l.name === reuse.name && (l as { module?: ModuleFiles }).module,\n );\n if (libEntry && (libEntry as { module?: ModuleFiles }).module) {\n result.push((libEntry as { module: ModuleFiles }).module);\n added.add(reuse.name);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Build the final module order.\n */\nfunction buildModuleOrder(\n snapshot: SessionSnapshot,\n plan: { intent: string; newModules: { name: string; position: number }[]; reuseModules?: { name: string; position: number }[] },\n blueprint: PageBlueprint | null,\n finalModules: ModuleFiles[],\n): string[] {\n // If blueprint provides order, use it — but reconcile with actual modules\n if (blueprint?.moduleOrder?.length) {\n const order = [...blueprint.moduleOrder];\n // Append any generated modules missing from the blueprint order\n // (AI sometimes drops modules from moduleOrder while still generating them)\n const orderSet = new Set(order);\n for (const mod of finalModules) {\n if (!orderSet.has(mod.moduleName)) {\n // Insert before footer if present, otherwise append\n const footerIdx = order.findIndex(\n (n) => n.toLowerCase().includes(\"footer\"),\n );\n if (footerIdx !== -1) {\n order.splice(footerIdx, 0, mod.moduleName);\n } else {\n order.push(mod.moduleName);\n }\n orderSet.add(mod.moduleName);\n log.warn(\n \"pipeline\",\n `Module \"${mod.moduleName}\" missing from blueprint order — inserted`,\n );\n }\n }\n return order;\n }\n\n // For create intent, use the order from finalModules\n if (plan.intent === \"create\") {\n return finalModules.map((m) => m.moduleName);\n }\n\n // Start with existing order\n const order = [...(snapshot.moduleOrder as string[])];\n\n // Insert new modules at their specified positions\n const insertions = [\n ...plan.newModules.map((m) => ({ name: m.name, position: m.position })),\n ...(plan.reuseModules || []).map((m) => ({\n name: m.name,\n position: m.position,\n })),\n ].sort((a, b) => a.position - b.position);\n\n for (const ins of insertions) {\n const pos = Math.min(ins.position, order.length);\n order.splice(pos, 0, ins.name);\n }\n\n // Filter to only modules that exist in finalModules\n const moduleNames = new Set(finalModules.map((m) => m.moduleName));\n return order.filter((name) => moduleNames.has(name));\n}\n\nfunction buildAssistantMessage(\n plan: { intent: string; affectedModules: string[]; newModules: { name: string }[] },\n modulesGenerated: number,\n modulesUnchanged: number,\n failedModules: string[],\n durationMs: number,\n blueprint: PageBlueprint | null,\n validationIssues: { module: string; message: string; autoFixed: boolean }[],\n): string {\n const seconds = Math.round(durationMs / 1000);\n const parts: string[] = [];\n\n if (plan.intent === \"create\") {\n parts.push(\n `Created ${modulesGenerated} module${modulesGenerated === 1 ? \"\" : \"s\"} in ${seconds}s.`,\n );\n } else if (plan.intent === \"modify\" || plan.intent === \"style_change\") {\n parts.push(\n `Updated ${modulesGenerated} module${modulesGenerated === 1 ? \"\" : \"s\"} in ${seconds}s.`,\n );\n if (modulesUnchanged > 0) {\n parts.push(`${modulesUnchanged} module${modulesUnchanged === 1 ? \"\" : \"s\"} unchanged.`);\n }\n } else if (plan.intent === \"add\") {\n const newNames = plan.newModules.map((m) => m.name).join(\", \");\n parts.push(`Added ${newNames} in ${seconds}s.`);\n } else if (plan.intent === \"remove\") {\n parts.push(`Removed modules in ${seconds}s.`);\n } else if (plan.intent === \"rearrange\") {\n parts.push(`Rearranged modules in ${seconds}s.`);\n }\n\n // Add narrative summary from blueprint\n if (blueprint?.narrative) {\n parts.push(`\\n\\n${blueprint.narrative}`);\n }\n\n if (failedModules.length > 0) {\n parts.push(\n `\\n\\n**Failed:** ${failedModules.join(\", \")}. You can retry these individually.`,\n );\n }\n\n // Add validation details\n const unfixed = validationIssues.filter((i) => !i.autoFixed);\n const fixed = validationIssues.filter((i) => i.autoFixed);\n if (fixed.length > 0 || unfixed.length > 0) {\n const valParts: string[] = [];\n if (fixed.length > 0) {\n valParts.push(`**Auto-fixed:** ${fixed.map((i) => `${i.module}: ${i.message}`).join(\", \")}`);\n }\n if (unfixed.length > 0) {\n valParts.push(`**Warnings:** ${unfixed.map((i) => `${i.module}: ${i.message}`).join(\", \")}`);\n }\n parts.push(`\\n\\n${valParts.join(\"\\n\")}`);\n }\n\n return parts.join(\"\");\n}\n","/**\n * AI handler coordinator for vibe coding mode.\n * Dispatches to engine implementations, manages generation state,\n * and delegates response parsing. Supports both single-call and\n * agentic pipeline modes.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { loadConfig, getApiKeyForEngine, type AIEngineType } from \"../utils/config.js\";\nimport { getSession, addMessage, saveSession, updateModules, reorderModules, getModuleLibrary, getActiveTemplate } from \"./session.js\";\nimport { parseAndApplyModules } from \"./ai-parser.js\";\nimport { log } from \"./log.js\";\nimport {\n streamWithAnthropicAPI,\n streamWithClaudeOAuth,\n streamWithOpenAIAPI,\n streamWithGeminiAPI,\n generateWithClaudeCode,\n generateWithCLI,\n} from \"./ai-engines.js\";\nimport { hasValidOAuthToken, getValidAccessToken } from \"../utils/claude-oauth.js\";\nimport { getFileContexts } from \"./routes/upload-files.js\";\nimport { runAgentPipeline, isAgenticCapable, isCLIEngine } from \"./agent/pipeline.js\";\nimport type { AgentEngine } from \"./agent/engine-adapter.js\";\nimport type { PipelineEvent, PipelineResult } from \"./agent/types.js\";\nimport type { SessionSnapshot, PipelineMetadata } from \"./session/types.js\";\n\n// ---------------------------------------------------------------------------\n// Parse warning callback — set by the WebSocket handler\n// ---------------------------------------------------------------------------\n\nlet parseWarningCallback: ((warning: string) => void) | null = null;\n\nexport function setParseWarningCallback(cb: ((warning: string) => void) | null): void {\n parseWarningCallback = cb;\n}\n\n// ---------------------------------------------------------------------------\n// Generation lock — prevents session switching while AI is generating\n// ---------------------------------------------------------------------------\n\nlet generatingSessionId: string | null = null;\n\nexport function isGenerating(): boolean {\n return generatingSessionId !== null;\n}\n\n// ---------------------------------------------------------------------------\n// Finish response — save message and parse modules\n// ---------------------------------------------------------------------------\n\nfunction finishResponse(fullResponse: string): void {\n if (generatingSessionId) {\n const current = getSession();\n if (!current || current.id !== generatingSessionId) {\n log.warn(\"ai-handler\", \"Session changed during generation — discarding AI output\");\n return;\n }\n }\n addMessage(\"assistant\", fullResponse);\n parseAndApplyModules(fullResponse, parseWarningCallback || undefined);\n saveSession();\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Stream an AI response for a chat message.\n * Calls onChunk with text fragments as they arrive.\n * After the full response, parses module JSON blocks and updates the session.\n */\nexport async function handleGenerateStream(\n userMessage: string,\n onChunk: (chunk: string) => void,\n onStatus?: (status: string) => void,\n fileIds?: string[]\n): Promise<void> {\n const session = getSession();\n if (!session) throw new Error(\"No active session\");\n\n const capturedSessionId = session.id;\n generatingSessionId = capturedSessionId;\n\n // Load file contexts for any attached files\n const fileContexts = fileIds?.length ? getFileContexts(fileIds) : undefined;\n\n try {\n const config = loadConfig();\n const engine = config.aiEngine || detectDefaultEngine();\n\n switch (engine) {\n case \"anthropic-api\":\n case \"api\": {\n const apiKey = getApiKeyForEngine(\"anthropic-api\", config);\n if (!apiKey) throw new Error(\"Anthropic API key not configured. Open Settings to add one.\");\n await streamWithAnthropicAPI(userMessage, apiKey, session.themeName,\n config.anthropicApiModel || \"claude-sonnet-4-6\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"claude-oauth\": {\n await streamWithClaudeOAuth(userMessage, session.themeName,\n config.anthropicApiModel || \"claude-sonnet-4-6\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"openai-api\": {\n const apiKey = getApiKeyForEngine(\"openai-api\", config);\n if (!apiKey) throw new Error(\"OpenAI API key not configured. Open Settings to add one.\");\n await streamWithOpenAIAPI(userMessage, apiKey, session.themeName,\n config.openaiApiModel || \"gpt-4o\", onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"gemini-api\": {\n const apiKey = getApiKeyForEngine(\"gemini-api\", config);\n if (!apiKey) throw new Error(\"Gemini API key not configured. Open Settings to add one.\");\n await streamWithGeminiAPI(userMessage, apiKey, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n }\n case \"claude-code\":\n await generateWithClaudeCode(userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n case \"gemini-cli\":\n await generateWithCLI(\"gemini\", userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n case \"codex-cli\":\n await generateWithCLI(\"codex\", userMessage, session.themeName, onChunk, onStatus, finishResponse, fileContexts);\n break;\n default:\n throw new Error(`Unknown AI engine: ${engine}. Open Settings to configure one.`);\n }\n } finally {\n generatingSessionId = null;\n parseWarningCallback = null;\n }\n}\n\n/**\n * Detect the best available engine when none is configured.\n */\nfunction detectDefaultEngine(): AIEngineType {\n const config = loadConfig();\n if (hasValidOAuthToken()) return \"claude-oauth\";\n if (config.anthropicApiKey || process.env.ANTHROPIC_API_KEY) return \"anthropic-api\";\n if (config.openaiApiKey || process.env.OPENAI_API_KEY) return \"openai-api\";\n if (config.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY) return \"gemini-api\";\n try { execSync(\"claude --version\", { stdio: \"pipe\" }); return \"claude-code\"; } catch {}\n try { execSync(\"gemini --version\", { stdio: \"pipe\" }); return \"gemini-cli\"; } catch {}\n try { execSync(\"codex --version\", { stdio: \"pipe\" }); return \"codex-cli\"; } catch {}\n throw new Error(\"No AI engine available. Open Settings to configure one.\");\n}\n\n/**\n * Non-streaming generation (used by REST API fallback).\n */\nexport async function handleGenerate(userMessage: string): Promise<string> {\n let fullResponse = \"\";\n await handleGenerateStream(userMessage, (chunk) => {\n fullResponse += chunk;\n });\n return fullResponse;\n}\n\n// ---------------------------------------------------------------------------\n// Agentic pipeline\n// ---------------------------------------------------------------------------\n\n/**\n * Take an immutable snapshot of the current session state for the agentic pipeline.\n */\nfunction takeSnapshot(): SessionSnapshot {\n const session = getSession()!;\n const tpl = getActiveTemplate();\n const modules = tpl ? [...tpl.modules] : [...session.modules];\n const moduleOrder = tpl ? [...tpl.moduleOrder] : [...session.moduleOrder];\n\n return {\n modules,\n moduleOrder,\n sharedCss: tpl?.sharedCss || session.sharedCss,\n sharedJs: tpl?.sharedJs || session.sharedJs,\n messages: [...session.messages],\n themeName: session.themeName,\n themePath: session.themePath,\n brandAssets: session.brandAssets ? { ...session.brandAssets } : undefined,\n };\n}\n\n/**\n * Resolve the API engine type and key/model for agentic pipeline.\n */\nexport function resolveAgenticEngine(config: ReturnType<typeof loadConfig>): {\n engine: AgentEngine;\n apiKey: string;\n model: string;\n} {\n const engineType = config.aiEngine || detectDefaultEngine();\n\n if (!isAgenticCapable(engineType)) {\n throw new Error(\"Agentic pipeline is not available for this engine.\");\n }\n\n // CLI engines don't need an API key\n if (isCLIEngine(engineType)) {\n let model = \"\";\n if (engineType === \"claude-code\") {\n model = config.claudeCodeModel || \"\";\n }\n return { engine: engineType as AgentEngine, apiKey: \"\", model };\n }\n\n // Claude OAuth resolves its token at call time in the engine adapter\n let apiKey: string | undefined;\n if (engineType === \"claude-oauth\") {\n if (!hasValidOAuthToken()) {\n throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n }\n apiKey = \"oauth\"; // Token resolved fresh in engine adapter (auto-refresh)\n } else {\n apiKey = getApiKeyForEngine(engineType, config);\n }\n if (!apiKey) {\n throw new Error(`API key not configured for ${engineType}. Open Settings to add one.`);\n }\n\n let model: string;\n switch (engineType) {\n case \"anthropic-api\":\n case \"claude-oauth\":\n model = config.anthropicApiModel || \"claude-sonnet-4-6\";\n break;\n case \"openai-api\":\n model = config.openaiApiModel || \"gpt-4o\";\n break;\n case \"gemini-api\":\n model = \"gemini-2.5-flash\";\n break;\n default:\n model = \"\";\n }\n\n return { engine: engineType as AgentEngine, apiKey, model };\n}\n\n/**\n * Run the agentic pipeline for a user message.\n * Returns the PipelineResult. The caller (WebSocket handler in server.ts)\n * is responsible for applying the result to the session and committing.\n */\nexport async function handleAgenticGenerate(\n userMessage: string,\n onEvent: (event: PipelineEvent) => void,\n fileIds?: string[],\n): Promise<PipelineResult> {\n const session = getSession();\n if (!session) throw new Error(\"No active session\");\n\n const capturedSessionId = session.id;\n generatingSessionId = capturedSessionId;\n\n try {\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n const concurrency = config.agenticConcurrency || 20;\n\n const snapshot = takeSnapshot();\n\n // Build library module list for intent analyzer\n const library = getModuleLibrary();\n const currentModuleNames = new Set(\n snapshot.modules.map((m) => m.moduleName),\n );\n const libraryModules = library\n .filter((e) => !currentModuleNames.has(e.module.moduleName))\n .map((e) => ({ name: e.module.moduleName, usedIn: e.usedIn }));\n\n const result = await runAgentPipeline(\n userMessage,\n snapshot,\n engine,\n apiKey,\n model,\n concurrency,\n onEvent,\n libraryModules,\n );\n\n // Verify session hasn't changed during generation\n const current = getSession();\n if (!current || current.id !== capturedSessionId) {\n log.warn(\"ai-handler\", \"Session changed during agentic generation — discarding output\");\n throw new Error(\"Session changed during generation\");\n }\n\n return result;\n } finally {\n generatingSessionId = null;\n }\n}\n\n/**\n * Apply a pipeline result to the current session.\n * Called by the WebSocket handler after successful pipeline execution.\n */\nexport function applyPipelineResult(result: PipelineResult, pipelineMeta?: PipelineMetadata): void {\n // Update modules in the session (merges new + updates existing)\n updateModules({\n modules: result.modules,\n sharedCss: result.sharedCss,\n sharedJs: result.sharedJs,\n });\n\n // Set the module order from the pipeline result\n reorderModules(result.moduleOrder);\n\n // Add assistant message to chat history with pipeline metadata\n addMessage(\"assistant\", result.assistantMessage, pipelineMeta);\n saveSession();\n}\n\n/**\n * Check if agentic mode should be used for the current configuration.\n */\nexport function shouldUseAgenticMode(): {\n useAgentic: boolean;\n needsPrompt: boolean;\n reason?: string;\n} {\n const config = loadConfig();\n const engine = config.aiEngine || detectDefaultEngine();\n\n if (!isAgenticCapable(engine)) {\n return {\n useAgentic: false,\n needsPrompt: false,\n reason: \"Agentic pipeline is not available for this engine.\",\n };\n }\n\n if (config.agenticMode === undefined) {\n return { useAgentic: false, needsPrompt: true };\n }\n\n return { useAgentic: config.agenticMode, needsPrompt: false };\n}\n","/**\n * AI-powered design extraction — analyzes a theme's CSS/HTML/fields\n * and generates a structured design system document (styleguide).\n *\n * Supports all configured AI engines: Anthropic API, OpenAI API, Gemini API,\n * Claude Code CLI, Gemini CLI, Codex CLI.\n */\n\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { resolveAsset, readFile } from \"../utils/fs.js\";\nimport { loadConfig, getApiKeyForEngine, type AIEngineType } from \"../utils/config.js\";\n\n// ---------------------------------------------------------------------------\n// Lazy-loaded Anthropic SDK\n// ---------------------------------------------------------------------------\n\nlet _AnthropicCtor: typeof import(\"@anthropic-ai/sdk\").default | null = null;\nasync function getAnthropicSDK(): Promise<typeof import(\"@anthropic-ai/sdk\").default> {\n if (!_AnthropicCtor) {\n const mod = await import(\"@anthropic-ai/sdk\");\n _AnthropicCtor = mod.default;\n }\n return _AnthropicCtor;\n}\n\n// ---------------------------------------------------------------------------\n// Theme file collection\n// ---------------------------------------------------------------------------\n\nconst MAX_CONTENT_CHARS = 80_000;\n\nfunction safeRead(path: string): string {\n try { return readFileSync(path, \"utf-8\"); } catch { return \"\"; }\n}\n\n/**\n * Collect CSS, HTML, and fields.json from a theme directory.\n * Prioritizes CSS (most design-relevant), then HTML, then fields.\n */\nexport function collectThemeFiles(themePath: string): string {\n const parts: string[] = [];\n let totalChars = 0;\n\n function addSection(label: string, content: string): boolean {\n if (!content.trim()) return true;\n const section = `\\n### ${label}\\n\\`\\`\\`\\n${content}\\n\\`\\`\\`\\n`;\n if (totalChars + section.length > MAX_CONTENT_CHARS) return false;\n parts.push(section);\n totalChars += section.length;\n return true;\n }\n\n // Theme metadata\n const themeJson = safeRead(join(themePath, \"theme.json\"));\n if (themeJson) addSection(\"theme.json\", themeJson);\n\n // Shared CSS files (highest priority for design tokens)\n const cssDir = join(themePath, \"css\");\n if (existsSync(cssDir)) {\n for (const f of readdirSync(cssDir).filter((f) => f.endsWith(\".css\"))) {\n if (!addSection(`css/${f}`, safeRead(join(cssDir, f)))) break;\n }\n }\n\n // Module CSS files\n const modulesDir = join(themePath, \"modules\");\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const css = safeRead(join(modPath, \"module.css\"));\n if (css && !addSection(`modules/${dir}/module.css`, css)) break;\n }\n }\n\n // Module HTML templates (for component patterns)\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const html = safeRead(join(modPath, \"module.html\"));\n if (html && !addSection(`modules/${dir}/module.html`, html)) break;\n }\n }\n\n // Module fields.json (for content structure)\n if (existsSync(modulesDir)) {\n for (const dir of readdirSync(modulesDir).filter((d) => d.endsWith(\".module\"))) {\n const modPath = join(modulesDir, dir);\n const fields = safeRead(join(modPath, \"fields.json\"));\n if (fields && !addSection(`modules/${dir}/fields.json`, fields)) break;\n }\n }\n\n return parts.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Extraction prompt\n// ---------------------------------------------------------------------------\n\nlet _extractionPrompt = \"\";\nfunction getExtractionPrompt(): string {\n if (!_extractionPrompt) {\n try { _extractionPrompt = readFile(resolveAsset(\"extraction-prompt.md\")); } catch { _extractionPrompt = \"\"; }\n }\n return _extractionPrompt;\n}\n\n// ---------------------------------------------------------------------------\n// CLI subprocess helper\n// ---------------------------------------------------------------------------\n\nfunction spawnCLIForExtraction(bin: string, args: string[], prompt: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const env = { ...process.env };\n delete env.CLAUDECODE; // prevent recursion if running inside Claude Code\n\n const child = spawn(bin, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) =>\n reject(new Error(`${bin} failed to start: ${err.message}`))\n );\n\n child.on(\"close\", (code) => {\n if (code === 0 || stdout.trim()) resolve(stdout.trim());\n else reject(new Error(`${bin} exited with code ${code}: ${stderr.trim()}`));\n });\n\n child.stdin.write(prompt);\n child.stdin.end();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main extraction function\n// ---------------------------------------------------------------------------\n\nexport interface ExtractionProgress {\n status: string;\n}\n\n/**\n * Extract a design system document from a theme directory using AI.\n * Uses the currently configured AI engine.\n * Returns a markdown string suitable for saving as a styleguide.\n */\nexport async function extractDesignContext(\n themePath: string,\n onProgress?: (p: ExtractionProgress) => void,\n): Promise<string> {\n onProgress?.({ status: \"Collecting theme files...\" });\n const themeContent = collectThemeFiles(themePath);\n if (!themeContent.trim()) {\n throw new Error(\"No CSS, HTML, or fields.json files found in theme.\");\n }\n\n const systemPrompt = getExtractionPrompt();\n if (!systemPrompt) {\n throw new Error(\"Extraction prompt not found (assets/extraction-prompt.md).\");\n }\n\n const userMessage = `Analyze this HubSpot CMS theme and extract the design system:\\n${themeContent}`;\n\n onProgress?.({ status: \"Analyzing design patterns...\" });\n\n const config = loadConfig();\n const engine = (config.aiEngine || \"anthropic-api\") as AIEngineType;\n let text = \"\";\n\n switch (engine) {\n // ----- API engines -----\n case \"anthropic-api\":\n case \"api\": {\n const apiKey = getApiKeyForEngine(\"anthropic-api\");\n if (!apiKey) throw new Error(\"Anthropic API key not configured. Open Settings to add one.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ apiKey });\n const response = await client.messages.create({\n model: config.anthropicApiModel || \"claude-sonnet-4-6\",\n max_tokens: 8000,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userMessage }],\n });\n text = response.content\n .filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n break;\n }\n\n case \"claude-oauth\": {\n const { getValidAccessToken, OAUTH_EXTRA_HEADERS, OAUTH_SYSTEM_PREFIX } = await import(\"../utils/claude-oauth.js\");\n const accessToken = await getValidAccessToken();\n if (!accessToken) throw new Error(\"Claude OAuth session expired. Please re-authenticate in Settings.\");\n\n const AnthropicSDK = await getAnthropicSDK();\n const client = new AnthropicSDK({ authToken: accessToken, defaultHeaders: OAUTH_EXTRA_HEADERS } as any);\n const response = await client.messages.create({\n model: config.anthropicApiModel || \"claude-sonnet-4-6\",\n max_tokens: 8000,\n system: [\n { type: \"text\", text: OAUTH_SYSTEM_PREFIX },\n { type: \"text\", text: systemPrompt },\n ] as any,\n messages: [{ role: \"user\", content: userMessage }],\n });\n text = response.content\n .filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n break;\n }\n\n case \"openai-api\": {\n const apiKey = getApiKeyForEngine(\"openai-api\");\n if (!apiKey) throw new Error(\"OpenAI API key not configured. Open Settings to add one.\");\n\n const resp = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({\n model: config.openaiApiModel || \"gpt-4o\",\n max_tokens: 8000,\n messages: [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userMessage },\n ],\n }),\n });\n if (!resp.ok) throw new Error(`OpenAI API error: ${resp.status} ${await resp.text()}`);\n const data = await resp.json() as { choices: { message: { content: string } }[] };\n text = data.choices?.[0]?.message?.content || \"\";\n break;\n }\n\n case \"gemini-api\": {\n const apiKey = getApiKeyForEngine(\"gemini-api\");\n if (!apiKey) throw new Error(\"Gemini API key not configured. Open Settings to add one.\");\n\n const model = config.geminiApiModel || \"gemini-2.5-flash\";\n const resp = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n system_instruction: { parts: [{ text: systemPrompt }] },\n contents: [{ role: \"user\", parts: [{ text: userMessage }] }],\n generationConfig: { maxOutputTokens: 8000 },\n }),\n },\n );\n if (!resp.ok) throw new Error(`Gemini API error: ${resp.status} ${await resp.text()}`);\n const data = await resp.json() as { candidates: { content: { parts: { text: string }[] } }[] };\n text = data.candidates?.[0]?.content?.parts?.map((p) => p.text).join(\"\") || \"\";\n break;\n }\n\n // ----- CLI engines -----\n case \"claude-code\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n const args = [\"--print\"];\n if (config.claudeCodeModel) args.push(\"--model\", config.claudeCodeModel);\n text = await spawnCLIForExtraction(\"claude\", args, combinedPrompt);\n break;\n }\n\n case \"gemini-cli\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n text = await spawnCLIForExtraction(\"gemini\", [], combinedPrompt);\n break;\n }\n\n case \"codex-cli\": {\n const combinedPrompt = `${systemPrompt}\\n\\n## User Request\\n${userMessage}`;\n text = await spawnCLIForExtraction(\"codex\", [], combinedPrompt);\n break;\n }\n\n default:\n throw new Error(`Unknown AI engine: ${engine}. Open Settings to configure one.`);\n }\n\n if (!text.trim()) {\n throw new Error(\"AI returned empty response.\");\n }\n\n onProgress?.({ status: \"Design extraction complete.\" });\n return text;\n}\n","/**\n * Brand Voice Extractor\n *\n * Lightweight AI call that analyzes rendered preview HTML to extract writing\n * style, tone, and voice guidelines. Runs on-demand from the brand assets panel.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport { log } from \"../../log.js\";\n\n/**\n * Extract a brand voice guide from the rendered preview HTML.\n * The preview has all HubL resolved to actual field default values, so the AI\n * sees real copy instead of `{{ module.field_name }}` placeholders.\n *\n * Returns a markdown string, or null if extraction fails.\n */\nexport async function extractBrandvoice(\n previewHtml: string,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n): Promise<string | null> {\n if (!previewHtml || previewHtml.length < 50) return null;\n\n const systemPrompt = `You are a brand strategist. Analyze the rendered landing page HTML below and extract a concise brand voice guide. The HTML contains the actual text content with all template variables resolved to their default values.\n\nReturn a markdown document with these sections (skip any section where the content provides no signal):\n\n## Tone\nOverall communication style (e.g., confident but approachable, technical but clear, playful, authoritative).\n\n## Voice Characteristics\n3-5 bullet points describing how this brand \"sounds\" (e.g., \"Uses short, punchy sentences\", \"Addresses reader directly with 'you'\").\n\n## Vocabulary\n- **Preferred words**: Terms used repeatedly or intentionally\n- **Avoided patterns**: Any notable absences or anti-patterns\n\n## Sentence Style\nTypical sentence length, structure, use of questions, imperatives, etc.\n\n## Dos and Don'ts\n3-4 practical rules for writing in this voice (e.g., \"Do: Lead with benefits, not features\", \"Don't: Use jargon without context\").\n\nKeep it actionable — this guide will be fed to AI to maintain consistent copy across pages.`;\n\n try {\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages: [{ role: \"user\", content: previewHtml }],\n maxTokens: 1000,\n });\n\n const text = result.type === \"text\" ? result.text : JSON.stringify(result.data);\n if (!text || text.trim().length < 20) return null;\n\n log.info(\"brandvoice-extractor\", `Extracted brand voice (${text.length} chars)`);\n return text.trim();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(\"brandvoice-extractor\", `Brand voice extraction failed: ${msg}`);\n return null;\n }\n}\n","/**\n * Post-pipeline stage: Theme Context Extractor\n *\n * Lightweight AI call that extracts a product/company brief from the rendered\n * preview HTML. Runs in the background after pipeline completion — never blocks\n * the preview or user interaction.\n */\n\nimport type { AgentEngine } from \"../engine-adapter.js\";\nimport { callAgent } from \"../engine-adapter.js\";\nimport { log } from \"../../log.js\";\n\n/**\n * Extract a product/company context brief from the rendered preview HTML.\n * The preview has all HubL resolved to actual field default values, so the AI\n * sees real copy instead of `{{ module.field_name }}` placeholders.\n *\n * Returns a markdown string, or null if extraction fails or produces nothing useful.\n */\nexport async function extractThemeContext(\n previewHtml: string,\n existingContext: string | undefined,\n engine: AgentEngine,\n apiKey: string,\n model: string,\n): Promise<string | null> {\n if (!previewHtml || previewHtml.length < 50) return null;\n\n const existingNote = existingContext\n ? `\\n\\nExisting product context (update if the new content adds info, keep what's still accurate):\\n${existingContext}`\n : \"\";\n\n const systemPrompt = `You are a content analyst. Extract a concise product/company brief from the rendered landing page HTML below. The HTML contains the actual text content (headings, paragraphs, button labels, image alt text, etc.) with all template variables resolved to their default values.\n\nReturn a markdown document with these sections (skip any section where the content provides no information):\n\n## Product / Company\nName, one-line description, and what it does.\n\n## Value Propositions\nKey benefits or features (bulleted list).\n\n## Target Audience\nWho this product/service is for.\n\n## Tone & Voice\nCommunication style (e.g., professional, casual, technical, friendly).\n\n## Key Terminology\nSpecific terms, product names, or branded language used consistently.\n\nKeep it concise — this brief is used as context for AI-generated content on other pages in the same theme.${existingNote}`;\n\n try {\n const result = await callAgent(engine, apiKey, model, {\n systemPrompt,\n messages: [{ role: \"user\", content: previewHtml }],\n maxTokens: 1000,\n });\n\n const text = result.type === \"text\" ? result.text : JSON.stringify(result.data);\n if (!text || text.trim().length < 20) return null;\n\n log.info(\"context-extractor\", `Extracted theme context (${text.length} chars)`);\n return text.trim();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(\"context-extractor\", `Theme context extraction failed: ${msg}`);\n return null;\n }\n}\n","import { buildProgram } from \"./cli/program.js\";\n\nconst program = buildProgram();\nprogram.parseAsync(process.argv).catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import { Command } from \"commander\";\nimport { wizardCommand } from \"../commands/wizard.js\";\nimport { initCommand } from \"../commands/init.js\";\nimport { convertCommand } from \"../commands/convert.js\";\nimport { uploadCommand } from \"../commands/upload.js\";\nimport { doctorCommand } from \"../commands/doctor.js\";\nimport { vibeCommand } from \"../commands/vibe.js\";\nimport { getVersion } from \"../utils/fs.js\";\n\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"vibespot\")\n .description(\n \"AI-powered HubSpot CMS landing page builder\"\n )\n .version(getVersion())\n .action(vibeCommand);\n\n program\n .command(\"wizard\")\n .description(\"Classic CLI wizard — step-by-step conversion flow\")\n .action(wizardCommand);\n\n program\n .command(\"init\")\n .description(\"Check and install required tools\")\n .action(initCommand);\n\n program\n .command(\"convert\")\n .description(\"Convert a React project to HubSpot modules\")\n .action(convertCommand);\n\n program\n .command(\"upload\")\n .description(\"Upload theme to HubSpot\")\n .action(uploadCommand);\n\n program\n .command(\"doctor\")\n .description(\"Diagnose environment issues\")\n .action(doctorCommand);\n\n return program;\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runPreflight } from \"../wizard/preflight.js\";\nimport { setupSource } from \"../wizard/source.js\";\nimport { setupTheme } from \"../wizard/theme-setup.js\";\nimport { runConversion } from \"../wizard/conversion.js\";\nimport { runUpload } from \"../wizard/uploader.js\";\nimport { showNextSteps } from \"../wizard/next-steps.js\";\nimport { saveConfig } from \"../utils/config.js\";\n\nexport async function wizardCommand(): Promise<void> {\n printBanner();\n\n // Step 1: Preflight checks\n const preflight = await runPreflight();\n\n // Step 2: Source setup\n const source = await setupSource();\n saveConfig({ lastSourcePath: source.sourceDir });\n\n // Step 3: Theme setup\n const themeInfo = await setupTheme();\n saveConfig({ lastThemePath: themeInfo.themePath });\n\n // Step 4: AI conversion\n await runConversion({\n aiEngine: preflight.aiEngine,\n model: preflight.model,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n });\n\n // Step 5: Upload\n await runUpload(themeInfo.themePath);\n\n // Step 6: Next steps + optional cleanup\n await showNextSteps({\n portalId: preflight.portalId,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n wasCloned: source.wasCloned,\n });\n}\n","import { theme } from \"./theme.js\";\nimport { getVersion } from \"../utils/fs.js\";\n\nexport function printBanner() {\n const v = theme.vibes;\n const o = theme.accent; // HubSpot orange for \"Spot\"\n const m = theme.muted;\n\n // Block-pixel ASCII art: \"vibe ≋ Spot\"\n const lines = [\n `${v(\"██ ██ ██ █████ ▄▄▄▄▄\")}${v(\" ≋≋≋≋≋≋≋≋ \")}${o(\"▄▄▄▄▄ █████ ▄▄▄▄ ▀▀██▀▀\")}`,\n `${v(\"██ ██ ██ ██ ██ ██ \")}${v(\" ≋≋≋≋≋≋ \")}${o(\"██ ██ ██ ██ ██ ██ \")}`,\n `${v(\"██ ██ ██ █████ ████ \")}${v(\" ≋≋≋≋ \")}${o(\"▀▀▀▄ █████ ██ ██ ██ \")}`,\n `${v(\" █▄▄█▀ ██ ██ ██ ██ \")}${v(\" ≋≋≋≋≋≋ \")}${o(\" ██ ██ ██ ██ ██ \")}`,\n `${v(\" ▀▀▀ ██ █████ ▀▀▀▀▀\")}${v(\" ≋≋≋≋≋≋≋≋ \")}${o(\"▀▀▀▀ ██ ▀▀▀▀ ██ \")}`,\n ];\n\n console.log();\n for (const line of lines) {\n console.log(` ${line}`);\n }\n console.log();\n console.log(` ${m(\"AI-powered HubSpot Landing Pages\")} ${theme.dim(`v${getVersion()}`)}`);\n console.log();\n}\n","import chalk from \"chalk\";\n\nexport const palette = {\n accent: \"#FF7A59\",\n accentBright: \"#FF9A7A\",\n success: \"#00BDA5\",\n info: \"#0066FF\",\n warn: \"#FFB020\",\n error: \"#E23D2D\",\n muted: \"#8B8D91\",\n vibes: \"#00BDD6\",\n};\n\nconst noColor = !!process.env.NO_COLOR;\n\nfunction hex(color: string) {\n return noColor ? chalk : chalk.hex(color);\n}\n\nexport const theme = {\n accent: hex(palette.accent),\n accentBright: hex(palette.accentBright),\n success: hex(palette.success),\n info: hex(palette.info),\n warn: hex(palette.warn),\n error: hex(palette.error),\n muted: hex(palette.muted),\n vibes: hex(palette.vibes),\n heading: noColor ? chalk.bold : chalk.bold.hex(palette.accent),\n command: hex(palette.accentBright),\n dim: chalk.dim,\n bold: chalk.bold,\n};\n","import {\n detectNode,\n detectGit,\n detectHubSpotCLI,\n detectClaudeCode,\n detectGeminiCLI,\n detectCodexCLI,\n detectHubSpotAuth,\n hasAnthropicKey,\n nodeVersionOk,\n} from \"../utils/detect.js\";\nimport { hasValidOAuthToken } from \"../utils/claude-oauth.js\";\nimport { run, runPassthrough } from \"../utils/shell.js\";\nimport { saveConfig, loadConfig, getHubSpotPak, getActiveHubSpotAccount, addHubSpotAccount, type AIEngineType } from \"../utils/config.js\";\nimport { validatePak } from \"../hubspot/api.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport interface PreflightResult {\n aiEngine: AIEngineType;\n model?: string;\n portalId: string;\n portalName: string;\n}\n\nexport async function runPreflight(): Promise<PreflightResult> {\n await ui.intro(\"Checking your environment\");\n\n // Node.js\n const node = detectNode();\n if (!node.found) {\n ui.logError(\"Node.js not found. Install it from https://nodejs.org\");\n process.exit(1);\n }\n if (!nodeVersionOk(node.version)) {\n ui.logError(\n `Node.js ${node.version} is too old. Version 18+ required. Update at https://nodejs.org`\n );\n process.exit(1);\n }\n ui.logSuccess(`Node.js v${node.version}`);\n\n // Git\n const git = detectGit();\n if (!git.found) {\n ui.logError(\"Git not found. Install it from https://git-scm.com\");\n process.exit(1);\n }\n ui.logSuccess(`Git ${git.version}`);\n\n // HubSpot connection\n const config = loadConfig();\n const useApi = config.hubspotUploadMode !== \"cli\";\n let portalId = \"\";\n let portalName = \"\";\n\n if (useApi) {\n // API mode — check for PAK in config\n let pak = getHubSpotPak();\n const acct = getActiveHubSpotAccount();\n\n if (!pak) {\n ui.logWarn(\"No HubSpot account connected\");\n await ui.note(\n \"You need a Personal Access Key to deploy themes.\\n\" +\n \"Create one at: https://app.hubspot.com/l/personal-access-key\\n\" +\n \"Make sure the Content scope is enabled.\",\n \"HubSpot connection required\"\n );\n\n const key = await ui.text({\n message: \"Paste your Personal Access Key:\",\n placeholder: \"pat-na1-...\",\n validate: (v) => v.trim() ? undefined : \"Key is required\",\n });\n\n const s = await ui.spinner();\n s.start(\"Validating key...\");\n try {\n const info = await validatePak(key);\n addHubSpotAccount(key, info.portalId, info.portalName, info.dataCenter);\n pak = key;\n portalId = info.portalId;\n portalName = info.portalName;\n s.stop(`Connected to ${info.portalName} (${info.portalId})`);\n } catch (err) {\n s.stop(\"Validation failed\");\n ui.logError(`Invalid key: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n } else {\n portalId = acct?.portalId || \"\";\n portalName = acct?.portalName || \"\";\n ui.logSuccess(\n `HubSpot${portalName ? `: ${portalName}` : \"\"}${portalId ? ` (${portalId})` : \"\"} — API mode`\n );\n }\n } else {\n // CLI mode — require hs CLI\n let hs = detectHubSpotCLI();\n if (!hs.found) {\n ui.logWarn(\"HubSpot CLI not found\");\n const install = await ui.confirm({ message: \"Install HubSpot CLI globally?\" });\n if (!install) {\n ui.logError(\"HubSpot CLI is required in CLI mode. Install: npm install -g @hubspot/cli\");\n process.exit(1);\n }\n const s = await ui.spinner();\n s.start(\"Installing HubSpot CLI...\");\n const result = run(\"npm install -g @hubspot/cli\");\n if (!result.success) {\n s.stop(\"Failed\");\n ui.logError(\"Try: npm install -g @hubspot/cli\");\n process.exit(1);\n }\n hs = detectHubSpotCLI();\n s.stop(`HubSpot CLI v${hs.version} installed`);\n } else {\n ui.logSuccess(`HubSpot CLI v${hs.version}`);\n }\n\n let auth = detectHubSpotAuth();\n if (!auth.authenticated) {\n ui.logWarn(\"HubSpot not authenticated\");\n const doAuth = await ui.confirm({ message: \"Run `hs init` now?\" });\n if (!doAuth) {\n ui.logError(\"Run `hs init` manually.\");\n process.exit(1);\n }\n const s = await ui.spinner();\n s.start(\"Waiting for HubSpot authentication...\");\n const authOk = runPassthrough(\"hs init\");\n if (!authOk) {\n s.stop(\"Authentication failed\");\n process.exit(1);\n }\n auth = detectHubSpotAuth();\n s.stop(`Connected to portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`);\n } else {\n ui.logSuccess(\n `HubSpot portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`\n );\n }\n portalId = auth.portalId;\n portalName = auth.portalName;\n }\n\n // AI Engine selection\n const claude = detectClaudeCode();\n const gemini = detectGeminiCLI();\n const codex = detectCodexCLI();\n const hasKey = hasAnthropicKey();\n\n const engineLabels: Record<AIEngineType, string> = {\n \"claude-code\": \"Claude Code\",\n \"api\": \"Anthropic API\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-api\": \"Gemini API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"codex-cli\": \"OpenAI Codex\",\n };\n\n const hasOAuth = hasValidOAuthToken();\n\n let aiEngine: AIEngineType;\n const lastUsed = config.aiEngine;\n\n // Always build list of available engines\n const available: { value: AIEngineType; label: string; hint: string }[] = [];\n\n if (claude.found) {\n available.push({\n value: \"claude-code\",\n label: \"Claude Code\",\n hint: lastUsed === \"claude-code\"\n ? \"last used — recommended\"\n : \"uses your existing Claude subscription — recommended\",\n });\n }\n if (hasOAuth) {\n available.push({\n value: \"claude-oauth\",\n label: \"Claude (OAuth)\",\n hint: lastUsed === \"claude-oauth\"\n ? \"last used\"\n : \"uses your Claude Pro/Max subscription via OAuth\",\n });\n }\n if (gemini.found) {\n available.push({\n value: \"gemini-cli\",\n label: \"Gemini CLI\",\n hint: lastUsed === \"gemini-cli\"\n ? \"last used\"\n : \"uses your existing Gemini setup\",\n });\n }\n if (codex.found) {\n available.push({\n value: \"codex-cli\",\n label: \"OpenAI Codex\",\n hint: lastUsed === \"codex-cli\"\n ? \"last used\"\n : \"uses your existing OpenAI setup\",\n });\n }\n if (hasKey) {\n available.push({\n value: \"api\",\n label: \"Anthropic API\",\n hint: lastUsed === \"api\"\n ? \"last used\"\n : \"uses your API key\",\n });\n }\n\n // Sort last-used engine to the top\n if (lastUsed) {\n available.sort((a, b) =>\n a.value === lastUsed ? -1 : b.value === lastUsed ? 1 : 0\n );\n }\n\n if (available.length === 1) {\n // Only one option — use it automatically\n aiEngine = available[0].value;\n ui.logSuccess(`AI engine: ${engineLabels[aiEngine]} (auto-detected)`);\n } else if (available.length > 1) {\n // Multiple available — always ask\n aiEngine = await ui.select({\n message: \"Choose your AI engine:\",\n options: available,\n });\n } else {\n // None available — guide the user\n await ui.note(\n \"You need an AI coding assistant to power the conversion.\\n\\n\" +\n `${theme.bold(\"Option 1:\")} Install Claude Code ${theme.muted(\"(recommended)\")}\\n` +\n \" https://claude.ai/code\\n\\n\" +\n `${theme.bold(\"Option 2:\")} Install Gemini CLI\\n` +\n \" https://github.com/google-gemini/gemini-cli\\n\\n\" +\n `${theme.bold(\"Option 3:\")} Install OpenAI Codex\\n` +\n \" https://github.com/openai/codex\\n\\n\" +\n `${theme.bold(\"Option 4:\")} Set an Anthropic API key\\n` +\n \" export ANTHROPIC_API_KEY=sk-ant-...\\n\" +\n \" (get one at https://console.anthropic.com)\",\n \"AI engine required\"\n );\n\n aiEngine = await ui.select({\n message: \"Which will you set up?\",\n options: [\n {\n value: \"claude-code\" as const,\n label: \"Claude Code\",\n hint: \"I'll install it now\",\n },\n {\n value: \"gemini-cli\" as const,\n label: \"Gemini CLI\",\n hint: \"I'll install it now\",\n },\n {\n value: \"codex-cli\" as const,\n label: \"OpenAI Codex\",\n hint: \"I'll install it now\",\n },\n {\n value: \"api\" as const,\n label: \"Anthropic API\",\n hint: \"I'll enter my key\",\n },\n ],\n });\n\n if (aiEngine === \"api\") {\n const key = await ui.text({\n message: \"Enter your Anthropic API key:\",\n placeholder: \"sk-ant-api03-...\",\n validate: (v) =>\n v.startsWith(\"sk-ant-\") ? undefined : \"Key should start with sk-ant-\",\n });\n process.env.ANTHROPIC_API_KEY = key;\n saveConfig({ anthropicApiKey: key });\n }\n }\n\n // Model selection for Claude Code\n let model: string | undefined;\n if (aiEngine === \"claude-code\") {\n model = await ui.select({\n message: \"Which model?\",\n options: [\n { value: \"sonnet\", label: \"Sonnet\", hint: \"fast, recommended\" },\n { value: \"opus\", label: \"Opus\", hint: \"most capable\" },\n { value: \"haiku\", label: \"Haiku\", hint: \"fastest, cheapest\" },\n ],\n });\n }\n\n saveConfig({ aiEngine });\n\n await ui.outro(\"Environment ready!\");\n\n return {\n aiEngine,\n model,\n portalId,\n portalName,\n };\n}\n","import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { readFileSync, existsSync, readdirSync } from \"node:fs\";\nimport { run } from \"./shell.js\";\nimport { loadConfig, maskApiKey, isCliToolEnabled, getActiveHubSpotAccount, type AIEngineType, type HubSpotAccountConfig } from \"./config.js\";\nimport { detectDataCenterFromPak } from \"../hubspot/api.js\";\nimport { hasValidOAuthToken, getOAuthTokenInfo } from \"./claude-oauth.js\";\n\nconst whichCmd = process.platform === \"win32\" ? \"where\" : \"which\";\n\nexport interface ToolInfo {\n name: string;\n found: boolean;\n version: string;\n path: string;\n}\n\nexport function detectNode(): ToolInfo {\n const result = run(\"node --version\");\n return {\n name: \"Node.js\",\n found: result.success,\n version: result.stdout.replace(/^v/, \"\"),\n path: run(`${whichCmd} node`).stdout,\n };\n}\n\nexport function detectGit(): ToolInfo {\n const result = run(\"git --version\");\n return {\n name: \"Git\",\n found: result.success,\n version: result.stdout.replace(\"git version \", \"\"),\n path: run(`${whichCmd} git`).stdout,\n };\n}\n\nexport function detectHubSpotCLI(): ToolInfo {\n const result = run(\"hs --version\");\n return {\n name: \"HubSpot CLI\",\n found: result.success,\n version: result.stdout,\n path: run(`${whichCmd} hs`).stdout,\n };\n}\n\nexport interface CLIToolInfo extends ToolInfo {\n authenticated: boolean;\n authDetail: string;\n}\n\nexport function detectClaudeCode(): CLIToolInfo {\n const result = run(\"claude --version\");\n if (!result.success) {\n return { name: \"Claude Code\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Check for Claude Code auth by looking for credentials\n // Claude Code stores OAuth tokens in ~/.claude/ — if the dir exists with credentials, it's authenticated\n const claudeDir = join(homedir(), \".claude\");\n let authenticated = false;\n let authDetail = \"Not signed in — run `claude` to authenticate\";\n\n try {\n if (existsSync(claudeDir)) {\n // If .claude dir exists with auth files, consider it authenticated\n const files = readdirSync(claudeDir);\n const hasAuth = files.some(f =>\n f.includes(\"credentials\") || f.includes(\"auth\") || f.includes(\"token\") || f === \".credentials.json\"\n );\n if (hasAuth || files.length > 2) {\n // Has some config — likely authenticated\n authenticated = true;\n authDetail = \"Authenticated\";\n }\n }\n } catch { /* ignore */ }\n\n return {\n name: \"Claude Code\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} claude`).stdout,\n authenticated,\n authDetail,\n };\n}\n\nexport function detectDataCenter(portalId: string): string {\n try {\n const configPath = join(homedir(), \".hscli\", \"config.yml\");\n if (!existsSync(configPath)) return \"na1\";\n\n const config = readFileSync(configPath, \"utf-8\");\n\n // Find the account block matching this portal ID\n const accountIdx = config.indexOf(`accountId: ${portalId}`);\n if (accountIdx === -1) return \"na1\";\n\n // Look for personalAccessKey after this account entry\n const keyIdx = config.indexOf(\"personalAccessKey:\", accountIdx);\n if (keyIdx === -1) return \"na1\";\n\n // Extract the key value (next non-empty trimmed line after the label)\n const keySection = config.slice(keyIdx, keyIdx + 300);\n const keyMatch = keySection.match(/personalAccessKey:[\\s>-]*\\n\\s+(\\S+)/);\n if (!keyMatch) return \"na1\";\n\n // CiRldTE = base64 prefix for \"eu1\" datacenter in HubSpot personal access keys\n if (keyMatch[1].startsWith(\"CiRldTE\")) return \"eu1\";\n } catch {\n // Fall through to default\n }\n return \"na1\";\n}\n\nexport interface HubSpotAccount {\n name: string;\n portalId: string;\n authType: string;\n isDefault: boolean;\n}\n\nexport function detectHubSpotAuth(): {\n authenticated: boolean;\n portalName: string;\n portalId: string;\n accounts: HubSpotAccount[];\n} {\n const result = run(\"hs accounts list\");\n if (!result.success || !result.stdout) {\n return { authenticated: false, portalName: \"\", portalId: \"\", accounts: [] };\n }\n\n // Parse all accounts from the table\n const accounts: HubSpotAccount[] = [];\n let defaultName = \"\";\n let defaultId = \"\";\n\n // Default account line: \"Account: name [standard] (123456)\"\n const defaultMatch = result.stdout.match(/Account:\\s*(.+?)\\s*\\((\\d+)\\)/);\n if (defaultMatch) {\n defaultName = defaultMatch[1].trim();\n defaultId = defaultMatch[2].trim();\n }\n\n // Parse table rows: \"name [standard] 123456 personalaccesskey\"\n const lines = result.stdout.split(\"\\n\");\n for (const line of lines) {\n const tableMatch = line.match(/^\\s*(.+?)\\s+(\\d{5,})\\s+(.*)/);\n if (tableMatch && !/Account ID/i.test(line) && !/^-+$/.test(line.trim()) && !/^Name\\s/i.test(line.trim())) {\n const name = tableMatch[1].trim();\n const portalId = tableMatch[2].trim();\n const authType = tableMatch[3]?.trim() || \"unknown\";\n accounts.push({\n name,\n portalId,\n authType,\n isDefault: portalId === defaultId,\n });\n }\n }\n\n if (defaultMatch) {\n return {\n authenticated: true,\n portalName: defaultName,\n portalId: defaultId,\n accounts,\n };\n }\n\n // Fallback: at least one account in table\n if (accounts.length > 0) {\n return {\n authenticated: true,\n portalName: accounts[0].name,\n portalId: accounts[0].portalId,\n accounts,\n };\n }\n\n return {\n authenticated: result.stdout.length > 0,\n portalName: \"\",\n portalId: \"\",\n accounts: [],\n };\n}\n\nexport function detectGeminiCLI(): CLIToolInfo {\n const result = run(\"gemini --version\");\n if (!result.success) {\n return { name: \"Gemini CLI\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Gemini CLI uses Google Cloud auth — check for application default credentials\n const adcPath = join(homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\");\n const hasAdc = existsSync(adcPath);\n // Also check GOOGLE_API_KEY / GEMINI_API_KEY env vars\n const hasEnvKey = !!(process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GOOGLE_AI_API_KEY);\n const authenticated = hasAdc || hasEnvKey;\n\n return {\n name: \"Gemini CLI\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} gemini`).stdout,\n authenticated,\n authDetail: authenticated ? \"Authenticated\" : \"Run `gemini` to sign in with Google\",\n };\n}\n\nexport function detectCodexCLI(): CLIToolInfo {\n const result = run(\"codex --version\");\n if (!result.success) {\n return { name: \"OpenAI Codex CLI\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Not installed\" };\n }\n\n // Codex CLI supports OAuth (stored in ~/.codex/auth.json) or OPENAI_API_KEY\n const hasKey = !!(process.env.OPENAI_API_KEY);\n let hasOAuth = false;\n try {\n const authFile = join(homedir(), \".codex\", \"auth.json\");\n if (existsSync(authFile)) {\n const content = readFileSync(authFile, \"utf-8\");\n hasOAuth = content.length > 10; // non-empty auth file\n }\n } catch { /* ignore */ }\n\n const authenticated = hasKey || hasOAuth;\n const detail = hasOAuth ? \"Authenticated (OAuth)\" : hasKey ? \"Authenticated (API key)\" : \"Not authenticated\";\n return {\n name: \"OpenAI Codex CLI\",\n found: true,\n version: result.stdout,\n path: run(`${whichCmd} codex`).stdout,\n authenticated,\n authDetail: detail,\n };\n}\n\nexport function detectGitHubCLI(): ToolInfo {\n const result = run(\"gh --version\");\n return {\n name: \"GitHub CLI\",\n found: result.success,\n version: result.stdout.split(\"\\n\")[0]?.replace(\"gh version \", \"\").split(\" \")[0] || \"\",\n path: run(`${whichCmd} gh`).stdout,\n };\n}\n\nexport function detectGitHubAuth(): { authenticated: boolean; username: string } {\n const result = run(\"gh auth status 2>&1\");\n if (!result.success && !result.stdout) {\n return { authenticated: false, username: \"\" };\n }\n // gh auth status outputs to stderr, but our run() captures both\n const output = result.stdout || result.stderr || \"\";\n const match = output.match(/Logged in to github\\.com.*account\\s+(\\S+)/);\n if (match) {\n return { authenticated: true, username: match[1] };\n }\n // Alternate pattern: \"account borismichel\"\n const altMatch = output.match(/account\\s+(\\S+)/);\n if (altMatch && output.includes(\"Logged in\")) {\n return { authenticated: true, username: altMatch[1] };\n }\n return { authenticated: output.includes(\"Logged in\"), username: \"\" };\n}\n\nexport function hasAnthropicKey(): boolean {\n return !!process.env.ANTHROPIC_API_KEY;\n}\n\nexport function nodeVersionOk(version: string): boolean {\n const major = parseInt(version.split(\".\")[0], 10);\n return major >= 18;\n}\n\nexport function hsCliVersionOk(version: string): boolean {\n const major = parseInt(version.split(\".\")[0], 10);\n return !isNaN(major) && major >= 8;\n}\n\n// ---------------------------------------------------------------------------\n// Comprehensive environment status (used by GET /api/settings/status)\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Config-based HubSpot auth (for API mode — no CLI subprocess)\n// ---------------------------------------------------------------------------\n\nexport function detectHubSpotAuthFromConfig(): {\n authenticated: boolean;\n portalName: string;\n portalId: string;\n dataCenter: string;\n accounts: HubSpotAccount[];\n uploadMode: \"api\" | \"cli\";\n} {\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n const configAccounts = config.hubspotAccounts || [];\n\n const accounts: HubSpotAccount[] = configAccounts.map((a) => ({\n name: a.portalName,\n portalId: a.portalId,\n authType: \"personalaccesskey\",\n isDefault: a.portalId === (config.activeHubSpotAccount || configAccounts[0]?.portalId),\n }));\n\n const active = getActiveHubSpotAccount();\n\n return {\n authenticated: !!active,\n portalName: active?.portalName || \"\",\n portalId: active?.portalId || \"\",\n dataCenter: active ? active.dataCenter : \"na1\",\n accounts,\n uploadMode,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Comprehensive environment status (used by GET /api/settings/status)\n// ---------------------------------------------------------------------------\n\nexport interface EnvironmentStatus {\n tools: {\n node: ToolInfo;\n git: ToolInfo;\n hubspot: ToolInfo & { authenticated: boolean; portalName: string; portalId: string; dataCenter: string; accounts: HubSpotAccount[]; uploadMode: \"api\" | \"cli\" };\n github: ToolInfo & { authenticated: boolean; username: string };\n claudeCode: CLIToolInfo;\n claudeOAuth: { authenticated: boolean; expiresAt?: string };\n geminiCli: CLIToolInfo;\n codexCli: CLIToolInfo;\n };\n apiKeys: {\n anthropic: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n openai: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n gemini: { configured: boolean; masked: string; source: \"config\" | \"env\" | null };\n };\n activeEngine: AIEngineType | null;\n availableEngines: AIEngineType[];\n enabledCLITools: string[];\n}\n\nconst DISABLED_CLI: CLIToolInfo = { name: \"\", found: false, version: \"\", path: \"\", authenticated: false, authDetail: \"Disabled\" };\n\nexport function detectEnvironment(): EnvironmentStatus {\n const config = loadConfig();\n\n const node = detectNode();\n const git = detectGit();\n\n // HubSpot: API mode uses config, CLI mode uses hs CLI\n const hsUploadMode = config.hubspotUploadMode || \"api\";\n let hsInfo: ToolInfo & { authenticated: boolean; portalName: string; portalId: string; dataCenter: string; accounts: HubSpotAccount[]; uploadMode: \"api\" | \"cli\" };\n\n if (hsUploadMode === \"cli\") {\n const hs = detectHubSpotCLI();\n const hsAuth = hs.found ? detectHubSpotAuth() : { authenticated: false, portalName: \"\", portalId: \"\", accounts: [] as HubSpotAccount[] };\n const dc = hsAuth.portalId ? detectDataCenter(hsAuth.portalId) : \"na1\";\n hsInfo = { ...hs, ...hsAuth, dataCenter: dc, uploadMode: \"cli\" };\n } else {\n // API mode — read from vibespot config, no subprocess\n const apiAuth = detectHubSpotAuthFromConfig();\n hsInfo = {\n name: \"HubSpot API\",\n found: true, // always available (built-in)\n version: \"v3\",\n path: \"\",\n ...apiAuth,\n };\n }\n\n // GitHub CLI — always checked (lightweight)\n const gh = detectGitHubCLI();\n const ghAuth = gh.found ? detectGitHubAuth() : { authenticated: false, username: \"\" };\n\n // Claude OAuth token check\n const claudeOAuth = {\n authenticated: hasValidOAuthToken(),\n expiresAt: getOAuthTokenInfo()?.expiresAt,\n };\n\n // AI CLI tools — only check if enabled in config (lazy loading)\n const enabledTools = config.enabledCLITools || [];\n const claude = isCliToolEnabled(\"claude-code\") ? detectClaudeCode() : { ...DISABLED_CLI, name: \"Claude Code\" };\n const gemini = isCliToolEnabled(\"gemini-cli\") ? detectGeminiCLI() : { ...DISABLED_CLI, name: \"Gemini CLI\" };\n const codex = isCliToolEnabled(\"codex-cli\") ? detectCodexCLI() : { ...DISABLED_CLI, name: \"OpenAI Codex CLI\" };\n\n // Determine API key status\n function keyStatus(configKey: string | undefined, ...envVars: string[]): { configured: boolean; masked: string; source: \"config\" | \"env\" | null } {\n if (configKey) return { configured: true, masked: maskApiKey(configKey), source: \"config\" };\n for (const v of envVars) {\n if (process.env[v]) return { configured: true, masked: maskApiKey(process.env[v]!), source: \"env\" };\n }\n return { configured: false, masked: \"\", source: null };\n }\n\n const anthropicKey = keyStatus(config.anthropicApiKey, \"ANTHROPIC_API_KEY\");\n const openaiKey = keyStatus(config.openaiApiKey, \"OPENAI_API_KEY\");\n const geminiKey = keyStatus(config.geminiApiKey, \"GEMINI_API_KEY\", \"GOOGLE_AI_API_KEY\");\n\n // Build available engines — CLI tools must be enabled + authenticated\n const available: AIEngineType[] = [];\n if (claude.found && claude.authenticated) available.push(\"claude-code\");\n if (claudeOAuth.authenticated) available.push(\"claude-oauth\");\n if (anthropicKey.configured) available.push(\"anthropic-api\");\n if (openaiKey.configured) available.push(\"openai-api\");\n if (gemini.found && gemini.authenticated) available.push(\"gemini-cli\");\n if (geminiKey.configured) available.push(\"gemini-api\");\n if (codex.found && codex.authenticated) available.push(\"codex-cli\");\n\n return {\n tools: {\n node,\n git,\n hubspot: hsInfo,\n github: { ...gh, ...ghAuth },\n claudeCode: claude,\n claudeOAuth,\n geminiCli: gemini,\n codexCli: codex,\n },\n apiKeys: {\n anthropic: anthropicKey,\n openai: openaiKey,\n gemini: geminiKey,\n },\n activeEngine: config.aiEngine || null,\n availableEngines: available,\n enabledCLITools: enabledTools,\n };\n}\n","import * as p from \"@clack/prompts\";\nimport { theme } from \"../cli/theme.js\";\n\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\nexport function handleCancel(value: unknown): void {\n if (p.isCancel(value)) {\n p.cancel(theme.muted(\"Operation cancelled.\"));\n process.exit(0);\n }\n}\n\nexport async function intro(title: string): Promise<void> {\n p.intro(theme.heading(title));\n}\n\nexport async function outro(message: string): Promise<void> {\n p.outro(theme.success(message));\n}\n\nexport async function note(message: string, title?: string): Promise<void> {\n p.note(message, title ? theme.heading(title) : undefined);\n}\n\nexport async function text(opts: {\n message: string;\n placeholder?: string;\n defaultValue?: string;\n validate?: (value: string) => string | undefined;\n}): Promise<string> {\n const result = await p.text({\n message: theme.accent(opts.message),\n placeholder: opts.placeholder,\n defaultValue: opts.defaultValue,\n validate: opts.validate,\n });\n handleCancel(result);\n return result as string;\n}\n\nexport async function confirm(opts: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean> {\n const result = await p.confirm({\n message: theme.accent(opts.message),\n initialValue: opts.initialValue ?? true,\n });\n handleCancel(result);\n return result as boolean;\n}\n\nexport async function select<T extends string>(opts: {\n message: string;\n options: { value: T; label: string; hint?: string }[];\n}): Promise<T> {\n const result = await p.select({\n message: theme.accent(opts.message),\n options: opts.options,\n });\n handleCancel(result);\n return result as T;\n}\n\nexport async function spinner(): Promise<{\n start: (msg: string) => void;\n stop: (msg: string) => void;\n message: (msg: string) => void;\n}> {\n const s = p.spinner();\n return {\n start: (msg: string) => s.start(theme.muted(msg)),\n stop: (msg: string) => s.stop(theme.success(msg)),\n message: (msg: string) => s.message(theme.muted(msg)),\n };\n}\n\nexport function log(message: string): void {\n p.log.info(message);\n}\n\nexport function logSuccess(message: string): void {\n p.log.success(theme.success(message));\n}\n\nexport function logWarn(message: string): void {\n p.log.warn(theme.warn(message));\n}\n\nexport function logError(message: string): void {\n p.log.error(theme.error(message));\n}\n\nexport function logStep(message: string): void {\n p.log.step(theme.accent(message));\n}\n","import { readdirSync, statSync } from \"node:fs\";\nimport { join, basename, extname } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport { fileExists, readFile } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport interface ComponentInfo {\n name: string;\n path: string;\n description: string;\n}\n\nexport interface SourceAnalysis {\n sourceDir: string;\n wasCloned: boolean;\n components: ComponentInfo[];\n hasTailwind: boolean;\n cssVarCount: number;\n fonts: string[];\n interactions: string[];\n}\n\nfunction findComponents(dir: string): ComponentInfo[] {\n const components: ComponentInfo[] = [];\n\n // Common locations for landing page components\n const searchDirs = [\n join(dir, \"src/components/landing\"),\n join(dir, \"src/components/sections\"),\n join(dir, \"src/components\"),\n join(dir, \"src/pages\"),\n join(dir, \"app/components\"),\n join(dir, \"components\"),\n ];\n\n for (const searchDir of searchDirs) {\n if (!fileExists(searchDir)) continue;\n\n try {\n const files = readdirSync(searchDir);\n for (const file of files) {\n const filePath = join(searchDir, file);\n const stat = statSync(filePath);\n if (!stat.isFile()) continue;\n\n const ext = extname(file);\n if (![\".tsx\", \".jsx\"].includes(ext)) continue;\n\n // Skip utility/UI components\n const name = basename(file, ext);\n if (name.startsWith(\"ui\") || name === \"index\") continue;\n\n // Read the file to extract a description\n const content = readFile(filePath);\n const desc = describeComponent(name, content);\n\n components.push({ name, path: filePath, description: desc });\n }\n } catch {\n // Directory read failed, skip\n }\n }\n\n return components;\n}\n\nfunction describeComponent(name: string, content: string): string {\n const hints: string[] = [];\n\n if (/carousel|slider|swiper|embla/i.test(content)) hints.push(\"carousel\");\n if (/accordion|collapsible|expand/i.test(content)) hints.push(\"accordion\");\n if (/form|submit|input.*email/i.test(content)) hints.push(\"form\");\n if (/nav|navigation|menu/i.test(content)) hints.push(\"navigation\");\n if (/hero|headline|tagline/i.test(content)) hints.push(\"hero\");\n if (/footer|copyright/i.test(content)) hints.push(\"footer\");\n if (/testimonial|quote|review/i.test(content)) hints.push(\"testimonials\");\n if (/pricing|plan|tier/i.test(content)) hints.push(\"pricing\");\n if (/faq|question.*answer/i.test(content)) hints.push(\"FAQ\");\n if (/feature|benefit|advantage/i.test(content)) hints.push(\"features\");\n if (/contact|get.in.touch/i.test(content)) hints.push(\"contact\");\n if (/cta|call.to.action/i.test(content)) hints.push(\"CTA\");\n if (/team|member|bio/i.test(content)) hints.push(\"team\");\n\n if (hints.length === 0) {\n // Fallback: infer from component name\n const readable = name\n .replace(/Section$/, \"\")\n .replace(/([A-Z])/g, \" $1\")\n .trim();\n return readable;\n }\n\n return hints.join(\", \");\n}\n\nfunction analyzeCSS(dir: string): { varCount: number; fonts: string[] } {\n const cssFiles = [\n join(dir, \"src/index.css\"),\n join(dir, \"src/globals.css\"),\n join(dir, \"src/app/globals.css\"),\n join(dir, \"app/globals.css\"),\n ];\n\n let varCount = 0;\n const fonts: string[] = [];\n\n for (const cssFile of cssFiles) {\n if (!fileExists(cssFile)) continue;\n\n const content = readFile(cssFile);\n const varMatches = content.match(/--[\\w-]+:/g);\n if (varMatches) varCount += varMatches.length;\n\n const fontMatches = content.match(\n /font-family:\\s*['\"]([^'\"]+)['\"]/g\n );\n if (fontMatches) {\n for (const m of fontMatches) {\n const font = m.match(/['\"]([^'\"]+)['\"]/)?.[1];\n if (font && !fonts.includes(font)) fonts.push(font);\n }\n }\n\n const importMatches = content.match(\n /@import\\s+url\\([^)]*fonts\\.googleapis\\.com[^)]*family=([^&)]+)/g\n );\n if (importMatches) {\n for (const m of importMatches) {\n const font = m.match(/family=([^&)]+)/)?.[1]?.replace(/\\+/g, \" \");\n if (font && !fonts.includes(font)) fonts.push(font);\n }\n }\n }\n\n return { varCount, fonts };\n}\n\nfunction detectInteractions(dir: string): string[] {\n const interactions: string[] = [];\n\n const hooksDir = join(dir, \"src/hooks\");\n if (fileExists(hooksDir)) {\n try {\n const hooks = readdirSync(hooksDir);\n for (const hook of hooks) {\n if (/scroll/i.test(hook)) interactions.push(\"Scroll animations\");\n if (/intersection/i.test(hook)) interactions.push(\"Scroll animations\");\n }\n } catch {\n // Ignore\n }\n }\n\n // Check landing components for patterns\n const componentDir = join(dir, \"src/components/landing\");\n if (fileExists(componentDir)) {\n try {\n const files = readdirSync(componentDir);\n for (const file of files) {\n if (!file.endsWith(\".tsx\") && !file.endsWith(\".jsx\")) continue;\n const content = readFile(join(componentDir, file));\n if (/carousel|embla|swiper/i.test(content) && !interactions.includes(\"Carousel\"))\n interactions.push(\"Carousel\");\n if (/accordion|collapsible/i.test(content) && !interactions.includes(\"Accordion\"))\n interactions.push(\"Accordion\");\n if (/typing|typewriter/i.test(content) && !interactions.includes(\"Typing animation\"))\n interactions.push(\"Typing animation\");\n if (/parallax|requestAnimationFrame/i.test(content) && !interactions.includes(\"Parallax\"))\n interactions.push(\"Parallax\");\n }\n } catch {\n // Ignore\n }\n }\n\n if (interactions.length === 0) {\n interactions.push(\"Scroll animations\");\n }\n\n return interactions;\n}\n\n/**\n * Headless source analysis — called from the vibe server (no interactive prompts).\n * Clones the repo if it's a URL, then analyzes components.\n */\nexport function analyzeSource(input: string): SourceAnalysis {\n let sourceDir: string;\n let wasCloned = false;\n\n if (input.startsWith(\"http\") || input.startsWith(\"git@\")) {\n wasCloned = true;\n const repoName =\n basename(input.replace(/\\.git$/, \"\")) || \"react-source\";\n sourceDir = join(process.cwd(), \"workspace\", repoName);\n\n if (!fileExists(sourceDir)) {\n const result = run(`git clone --depth 1 \"${input}\" \"${sourceDir}\"`);\n if (!result.success) {\n throw new Error(`Failed to clone ${input}: ${result.stderr}`);\n }\n }\n } else {\n sourceDir = input;\n if (!fileExists(sourceDir)) {\n throw new Error(`Directory not found: ${sourceDir}`);\n }\n }\n\n const components = findComponents(sourceDir);\n const hasTailwind =\n fileExists(join(sourceDir, \"tailwind.config.ts\")) ||\n fileExists(join(sourceDir, \"tailwind.config.js\"));\n const { varCount, fonts } = analyzeCSS(sourceDir);\n const interactions = detectInteractions(sourceDir);\n\n return {\n sourceDir,\n wasCloned,\n components,\n hasTailwind,\n cssVarCount: varCount,\n fonts,\n interactions,\n };\n}\n\nexport async function setupSource(): Promise<SourceAnalysis> {\n await ui.intro(\"Source Project\");\n\n const input = await ui.text({\n message: \"GitHub URL or local path to your React project:\",\n placeholder: \"https://github.com/user/my-lovable-page\",\n validate: (v) => {\n if (!v.trim()) return \"Please enter a URL or path\";\n return undefined;\n },\n });\n\n let sourceDir: string;\n let wasCloned = false;\n\n if (input.startsWith(\"http\") || input.startsWith(\"git@\")) {\n wasCloned = true;\n // Clone from GitHub\n const repoName =\n basename(input.replace(/\\.git$/, \"\")) || \"react-source\";\n sourceDir = join(process.cwd(), \"workspace\", repoName);\n\n if (fileExists(sourceDir)) {\n // Already cloned from a previous run — reuse it\n ui.logSuccess(`Using existing clone: ${theme.dim(sourceDir)}`);\n } else {\n const s = await ui.spinner();\n s.start(\"Cloning repository...\");\n\n const result = run(`git clone --depth 1 \"${input}\" \"${sourceDir}\"`);\n if (!result.success) {\n s.stop(\"Clone failed\");\n ui.logError(\n `Failed to clone ${input}. Check the URL and your access permissions.`\n );\n process.exit(1);\n }\n\n s.stop(`Cloned to ${theme.dim(sourceDir)}`);\n }\n } else {\n sourceDir = input;\n if (!fileExists(sourceDir)) {\n ui.logError(`Directory not found: ${sourceDir}`);\n process.exit(1);\n }\n ui.logSuccess(`Using local source: ${theme.dim(sourceDir)}`);\n }\n\n // Analyze\n const s = await ui.spinner();\n s.start(\"Analyzing project structure...\");\n\n const components = findComponents(sourceDir);\n const hasTailwind =\n fileExists(join(sourceDir, \"tailwind.config.ts\")) ||\n fileExists(join(sourceDir, \"tailwind.config.js\"));\n const { varCount, fonts } = analyzeCSS(sourceDir);\n const interactions = detectInteractions(sourceDir);\n\n s.stop(`Found ${components.length} landing page components`);\n\n if (components.length === 0) {\n ui.logWarn(\n \"No components found. Make sure the React source has .tsx/.jsx files in src/components/\"\n );\n process.exit(1);\n }\n\n // Show summary\n const componentList = components\n .map((c, i) => ` ${theme.dim(`${i + 1}.`)} ${theme.bold(c.name)} ${theme.muted(`— ${c.description}`)}`)\n .join(\"\\n\");\n\n const cssInfo = hasTailwind\n ? `Tailwind + custom CSS (${varCount} variables)`\n : `Custom CSS (${varCount} variables)`;\n const fontInfo = fonts.length > 0 ? fonts.join(\", \") : \"System fonts\";\n const jsInfo = interactions.join(\", \");\n\n await ui.note(\n `${componentList}\\n\\n CSS: ${cssInfo}\\n JS: ${jsInfo}\\n Font: ${fontInfo}`,\n `${components.length} components detected`\n );\n\n const ok = await ui.confirm({ message: \"Does this look right?\" });\n if (!ok) {\n ui.logError(\"Please adjust your source directory and try again.\");\n process.exit(0);\n }\n\n await ui.outro(\"Source analyzed!\");\n\n return {\n sourceDir,\n wasCloned,\n components,\n hasTailwind,\n cssVarCount: varCount,\n fonts,\n interactions,\n };\n}\n","import { join } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport { fileExists, readFile, writeFile, ensureDir } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport { loadConfig, getHubSpotPak } from \"../utils/config.js\";\nimport { createThemeScaffold } from \"../hubspot/theme-scaffold.js\";\nimport { fetchTheme } from \"../hubspot/fetcher.js\";\n\nexport interface ThemeInfo {\n themePath: string;\n themeName: string;\n}\n\nexport async function setupTheme(): Promise<ThemeInfo> {\n await ui.intro(\"HubSpot Theme Setup\");\n\n const choice = await ui.select({\n message: \"Do you have an existing HubSpot theme?\",\n options: [\n {\n value: \"fetch\" as const,\n label: \"Fetch my existing theme from HubSpot\",\n hint: \"downloads your current theme\",\n },\n {\n value: \"create\" as const,\n label: \"Start fresh (HubSpot Boilerplate)\",\n hint: \"creates a new starter theme\",\n },\n ],\n });\n\n let themeName: string;\n let themePath: string;\n\n const workspaceDir = join(process.cwd(), \"workspace\");\n ensureDir(workspaceDir);\n\n if (choice === \"fetch\") {\n themeName = await ui.text({\n message: \"What's your theme name in HubSpot?\",\n placeholder: \"My-Company-Theme\",\n validate: (v) =>\n v.trim() ? undefined : \"Theme name is required\",\n });\n\n themePath = join(workspaceDir, themeName);\n\n const s = await ui.spinner();\n s.start(\"Fetching theme from HubSpot...\");\n\n const config = loadConfig();\n const pak = getHubSpotPak();\n\n if (config.hubspotUploadMode === \"cli\" || !pak) {\n // CLI fallback\n const result = run(`hs cms fetch \"${themeName}\" \"${themePath}\"`);\n if (!result.success) {\n s.stop(\"Fetch failed\");\n ui.logError(\n `Could not fetch theme \"${themeName}\". Check the name in HubSpot Design Manager.`\n );\n process.exit(1);\n }\n } else {\n // API mode\n try {\n await fetchTheme(pak, themeName, themePath);\n } catch (err) {\n s.stop(\"Fetch failed\");\n ui.logError(\n `Could not fetch theme \"${themeName}\": ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n }\n\n s.stop(`Theme fetched: ${theme.dim(themePath)}`);\n } else {\n themeName = await ui.text({\n message: \"Name for your new theme:\",\n placeholder: \"my-theme\",\n defaultValue: \"my-theme\",\n });\n\n themePath = join(workspaceDir, themeName);\n\n const s = await ui.spinner();\n s.start(\"Creating theme...\");\n\n try {\n createThemeScaffold(themePath, themeName);\n } catch (err) {\n s.stop(\"Creation failed\");\n ui.logError(\n `Could not create theme \"${themeName}\": ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n\n s.stop(`Theme created: ${theme.dim(themePath)}`);\n }\n\n // Validate and patch\n await ui.intro(\"Checking theme compatibility\");\n\n const baseHtmlPath = join(themePath, \"templates/layouts/base.html\");\n if (!fileExists(baseHtmlPath)) {\n ui.logError(\n `base.html not found at ${baseHtmlPath}. Your theme may have a different structure.`\n );\n process.exit(1);\n }\n ui.logSuccess(\"base.html found\");\n\n // Check for template_css and template_js support\n let baseHtml = readFile(baseHtmlPath);\n let patched = false;\n\n if (!baseHtml.includes(\"template_css\")) {\n ui.logWarn(\"Missing template_css support in base.html\");\n\n // Find the line with require_css for theme-overrides or the last require_css\n const cssInsertPoint =\n baseHtml.indexOf(\"theme-overrides.css\") !== -1\n ? baseHtml.indexOf(\n \"{{\",\n baseHtml.lastIndexOf(\"\\n\", baseHtml.indexOf(\"theme-overrides.css\"))\n )\n : baseHtml.lastIndexOf(\"require_css\");\n\n if (cssInsertPoint > 0) {\n const insertBefore = baseHtml.lastIndexOf(\"\\n\", cssInsertPoint);\n const block = `\\n {% if template_css %}\\n {{ require_css(get_asset_url(template_css)) }}\\n {% endif %}`;\n baseHtml =\n baseHtml.slice(0, insertBefore) + block + baseHtml.slice(insertBefore);\n patched = true;\n }\n } else {\n ui.logSuccess(\"template_css support\");\n }\n\n if (!baseHtml.includes(\"template_js\")) {\n ui.logWarn(\"Missing template_js support in base.html\");\n\n // Find the line with require_js for main.js or the last require_js\n const jsLine = baseHtml.indexOf(\"require_js\");\n if (jsLine > 0) {\n const lineEnd = baseHtml.indexOf(\"\\n\", jsLine);\n // Find the end of the require_js line (including closing tags)\n const nextLine = baseHtml.indexOf(\"\\n\", lineEnd + 1);\n const block = `\\n {% if template_js %}\\n {{ require_js(get_asset_url(template_js)) }}\\n {% endif %}`;\n const insertAt =\n baseHtml.indexOf(\"}}\", jsLine) + 2 + baseHtml.slice(baseHtml.indexOf(\"}}\", jsLine) + 2).indexOf(\"\\n\") + 1;\n baseHtml =\n baseHtml.slice(0, baseHtml.indexOf(\"\\n\", baseHtml.indexOf(\"}}\", jsLine) + 2)) +\n block +\n baseHtml.slice(baseHtml.indexOf(\"\\n\", baseHtml.indexOf(\"}}\", jsLine) + 2));\n patched = true;\n }\n } else {\n ui.logSuccess(\"template_js support\");\n }\n\n if (patched) {\n const s = await ui.spinner();\n s.start(\"Patching base.html...\");\n writeFile(baseHtmlPath, baseHtml);\n s.stop(\"base.html patched with template_css/template_js support\");\n }\n\n // Check .hsignore\n const hsignorePath = join(themePath, \".hsignore\");\n if (fileExists(hsignorePath)) {\n const hsignore = readFile(hsignorePath);\n if (!hsignore.includes(\"docs/\")) {\n writeFile(hsignorePath, hsignore + \"\\ndocs/\\n\");\n ui.logSuccess(\"Added docs/ to .hsignore\");\n }\n } else {\n writeFile(hsignorePath, \"docs/\\n*.md\\nnode_modules/\\n.git\\n\");\n ui.logSuccess(\"Created .hsignore\");\n }\n\n await ui.outro(\"Theme ready!\");\n\n return { themePath, themeName };\n}\n","/**\n * Theme scaffold generator — replaces `hs cms theme create`.\n * Creates the standard HubSpot theme directory structure locally.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Create a minimal HubSpot theme directory structure.\n * Produces the same layout as `hs cms theme create` but without\n * boilerplate modules/templates (vibespot generates those via AI).\n */\nexport function createThemeScaffold(themePath: string, themeName: string): void {\n // Create directories\n mkdirSync(themePath, { recursive: true });\n mkdirSync(join(themePath, \"templates\"), { recursive: true });\n mkdirSync(join(themePath, \"modules\"), { recursive: true });\n mkdirSync(join(themePath, \"css\"), { recursive: true });\n mkdirSync(join(themePath, \"js\"), { recursive: true });\n mkdirSync(join(themePath, \"images\"), { recursive: true });\n mkdirSync(join(themePath, \"assets\"), { recursive: true });\n\n // theme.json — required by HubSpot\n const themeJson = {\n label: themeName,\n preview_path: \"./templates/home.html\",\n screenshot_path: \"./images/template-previews/home.png\",\n enable_domain_stylesheets: false,\n version: \"1.0.0\",\n author: {\n name: \"vibeSpot\",\n url: \"https://github.com/borismichel/vibespot\",\n },\n };\n writeFileSync(join(themePath, \"theme.json\"), JSON.stringify(themeJson, null, 2) + \"\\n\");\n\n // fields.json — empty theme-level fields\n writeFileSync(join(themePath, \"fields.json\"), \"[]\\n\");\n\n // Placeholder landing page template — gets replaced once AI generates real modules.\n // Marked isAvailableForNewContent: false so it won't appear in HubSpot as a usable template.\n const landingTemplate = `<!--\n templateType: page\n isAvailableForNewContent: false\n label: ${themeName} (placeholder)\n screenshotPath: ../images/template-previews/home.png\n-->\n{% extends \"./layouts/base.html\" %}\n\n{% block body %}\n{% dnd_area \"main_content\"\n label=\"Main Content\",\n class=\"body-container body-container--${themeName}\"\n%}\n{% end_dnd_area %}\n{% endblock body %}\n`;\n writeFileSync(join(themePath, \"templates\", \"home.html\"), landingTemplate);\n\n // Base layout\n const baseLayout = `<!--\n templateType: none\n isAvailableForNewContent: false\n label: Base Layout\n-->\n<!DOCTYPE html>\n<html lang=\"{{ html_lang }}\" {{ html_lang_dir }}>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n {% if template_css %}\n {{ require_css(get_asset_url(template_css)) }}\n {% endif %}\n {{ standard_header_includes }}\n</head>\n<body>\n {% block body %}{% endblock body %}\n {% if template_js %}\n {{ require_js(get_asset_url(template_js)) }}\n {% endif %}\n {{ standard_footer_includes }}\n</body>\n</html>\n`;\n mkdirSync(join(themePath, \"templates\", \"layouts\"), { recursive: true });\n writeFileSync(join(themePath, \"templates\", \"layouts\", \"base.html\"), baseLayout);\n}\n","import { join } from \"node:path\";\nimport { readdirSync, rmSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets } from \"../ai/engine.js\";\nimport type { AIEngineType } from \"../utils/config.js\";\nimport { ClaudeCodeEngine } from \"../ai/claude-code.js\";\nimport { ClaudeAPIEngine } from \"../ai/claude-api.js\";\nimport { GeminiCLIEngine } from \"../ai/gemini-cli.js\";\nimport { CodexCLIEngine } from \"../ai/codex-cli.js\";\nimport { getConversionGuide } from \"../ai/prompts.js\";\nimport { readFile, writeFile, fileExists } from \"../utils/fs.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nfunction createEngine(type: AIEngineType, model?: string): AIEngine {\n switch (type) {\n case \"claude-code\":\n return new ClaudeCodeEngine(model);\n case \"gemini-cli\":\n return new GeminiCLIEngine();\n case \"codex-cli\":\n return new CodexCLIEngine();\n case \"api\":\n return new ClaudeAPIEngine();\n }\n}\n\nexport async function runConversion(opts: {\n aiEngine: AIEngineType;\n model?: string;\n sourceDir: string;\n themePath: string;\n}): Promise<GeneratedAssets> {\n await ui.intro(\"Converting React to HubSpot Modules\");\n\n await ui.note(\n \"AI will now analyze your React code and create\\nHubSpot-native modules. This takes 2-5 minutes.\",\n \"AI Conversion\"\n );\n\n const engine = createEngine(opts.aiEngine, opts.model);\n\n const conversionGuide = getConversionGuide();\n\n const s = await ui.spinner();\n s.start(\"Starting AI conversion...\");\n\n const startTime = Date.now();\n\n const result = await engine.convert({\n sourceDir: opts.sourceDir,\n themePath: opts.themePath,\n conversionGuide,\n onProgress: (step, detail) => {\n if (step === \"created\") {\n ui.logSuccess(detail);\n } else {\n s.message(detail);\n }\n },\n });\n\n const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);\n s.stop(`AI conversion complete (${elapsed}s)`);\n\n // Validate and auto-fix all known HubSpot issues before upload\n const fixes = validateAndFix(opts.themePath);\n for (const fix of fixes) {\n ui.logSuccess(`Auto-fixed: ${fix}`);\n }\n\n // Show conversion checklist\n const checklist = buildChecklist(opts.themePath, result);\n const lines: string[] = [];\n for (const item of checklist) {\n const icon = item.passed ? \"\\u2705\" : \"\\u274c\";\n const severity = !item.passed ? (item.critical ? \" (CRITICAL)\" : \" (cosmetic)\") : \"\";\n lines.push(`${icon} ${item.label}${severity}`);\n }\n const passed = checklist.filter((c) => c.passed).length;\n lines.push(`\\n${passed}/${checklist.length} checks passed`);\n await ui.note(lines.join(\"\\n\"), \"Conversion Checklist\");\n\n const criticalFailures = checklist.filter((c) => !c.passed && c.critical);\n const cosmeticFailures = checklist.filter((c) => !c.passed && !c.critical);\n\n if (criticalFailures.length > 0) {\n ui.logError(\n `${criticalFailures.length} critical issue(s) — upload will likely fail:\\n` +\n criticalFailures.map((c) => ` - ${c.label}`).join(\"\\n\")\n );\n const proceed = await ui.confirm({\n message: \"Continue with upload anyway?\",\n initialValue: false,\n });\n if (!proceed) {\n throw new Error(\"Conversion aborted due to critical checklist failures.\");\n }\n } else if (cosmeticFailures.length > 0) {\n ui.logWarn(\n `${cosmeticFailures.length} non-critical issue(s) — page will work but may look incomplete:\\n` +\n cosmeticFailures.map((c) => ` - ${c.label}`).join(\"\\n\")\n );\n }\n\n // Offer to clean up log file\n const logPath = join(opts.themePath, \"..\", \"vibespot-conversion.log\");\n if (fileExists(logPath)) {\n const keepLog = await ui.confirm({\n message: \"Keep conversion log file for debugging?\",\n initialValue: false,\n });\n if (!keepLog) {\n rmSync(logPath);\n } else {\n ui.logSuccess(`Log saved: ${logPath}`);\n }\n }\n\n await ui.outro(\"Files ready for upload!\");\n\n return result;\n}\n\n/**\n * Run all validation and auto-fix routines before upload.\n * Returns a list of human-readable fix descriptions.\n */\nexport function validateAndFix(themePath: string): string[] {\n const fixes: string[] = [];\n\n // 1. Template annotations\n validateTemplates(themePath);\n\n // 2. Module meta.json\n validateModuleMeta(themePath);\n\n // 3. Fix fields.json issues in all modules\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n\n const moduleName = entry.replace(\".module\", \"\");\n let content = readFile(fieldsPath);\n let changed = false;\n\n // 3a. \"textarea\" → \"text\"\n if (content.includes('\"textarea\"')) {\n content = content.replace(/\"textarea\"/g, '\"text\"');\n changed = true;\n fixes.push(`${moduleName}: \"textarea\" → \"text\"`);\n }\n\n // 3b. \"name\": \"name\" → \"name\": \"item_name\"\n if (/\"name\":\\s*\"name\"/.test(content)) {\n content = content.replace(/\"name\":\\s*\"name\"/g, '\"name\": \"item_name\"');\n changed = true;\n fixes.push(`${moduleName}: reserved field name \"name\" → \"item_name\"`);\n }\n\n // 3c. Fix \"choice\" fields — choices must be [[\"value\",\"Label\"]] not [\"value\"]\n // 3d. Fix \"link\" fields — default must be { url: { href, type }, open_in_new_tab, no_follow }\n try {\n const fields = JSON.parse(content);\n let jsonFixed = false;\n if (fixChoiceFields(fields)) {\n jsonFixed = true;\n fixes.push(`${moduleName}: fixed choice field format`);\n }\n if (fixLinkFields(fields)) {\n jsonFixed = true;\n fixes.push(`${moduleName}: fixed link field default value`);\n }\n if (jsonFixed) {\n content = JSON.stringify(fields, null, 2) + \"\\n\";\n changed = true;\n }\n } catch {\n fixes.push(`${moduleName}: fields.json has invalid JSON — manual fix needed`);\n }\n\n if (changed) writeFile(fieldsPath, content);\n\n // 3d. Fix now() in module.html\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (fileExists(htmlPath)) {\n let html = readFile(htmlPath);\n if (html.includes(\"now()\")) {\n html = html.replace(/now\\(\\)/g, \"local_dt\");\n writeFile(htmlPath, html);\n fixes.push(`${moduleName}: now() → local_dt`);\n }\n }\n }\n }\n\n // 4. Remove HubDB templates (requires CMS Hub Pro/Enterprise)\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\")) continue;\n const filePath = join(templatesDir, file);\n const content = readFile(filePath);\n if (content.includes(\"hubdb_table\") || content.includes(\"hubdb_table_rows\")) {\n rmSync(filePath);\n fixes.push(`Removed ${file} (HubDB requires CMS Hub Pro/Enterprise)`);\n }\n }\n }\n\n return fixes;\n}\n\n/** Recursively fix choice fields: convert string[] choices to [value, Label][] */\nfunction fixChoiceFields(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"choice\" && Array.isArray(f.choices)) {\n const needsFix = f.choices.some((c: unknown) => typeof c === \"string\");\n if (needsFix) {\n f.choices = (f.choices as unknown[]).map((c: unknown) => {\n if (typeof c === \"string\") {\n const label = c.charAt(0).toUpperCase() + c.slice(1);\n return [c, label];\n }\n return c;\n });\n fixed = true;\n }\n }\n\n // Recurse into group children\n if (Array.isArray(f.children)) {\n if (fixChoiceFields(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\n/** Recursively fix link fields: default must be { url: { href, type }, open_in_new_tab, no_follow } */\nfunction fixLinkFields(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"link\") {\n const def = f.default;\n // Fix if default is a string, missing, or doesn't have the required url.href structure\n const needsFix =\n typeof def === \"string\" ||\n def === undefined ||\n def === null ||\n (typeof def === \"object\" && !(def as Record<string, unknown>).url);\n\n if (needsFix) {\n const href = typeof def === \"string\" ? def : \"\";\n f.default = {\n url: { href, type: \"EXTERNAL\" },\n open_in_new_tab: false,\n no_follow: false,\n };\n fixed = true;\n }\n }\n\n // Recurse into group children\n if (Array.isArray(f.children)) {\n if (fixLinkFields(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\ninterface CheckItem {\n label: string;\n passed: boolean;\n /** If true, failure blocks upload. If false, it's cosmetic / nice-to-have. */\n critical: boolean;\n}\n\nfunction buildChecklist(themePath: string, result: GeneratedAssets): CheckItem[] {\n const items: CheckItem[] = [];\n\n // Modules — critical: upload will fail with zero modules\n const moduleCount = result.modules.length;\n items.push({\n label: `Modules created (${moduleCount})`,\n passed: moduleCount > 0,\n critical: true,\n });\n\n // fields.json — critical: invalid fields cause upload deserialization errors\n let fieldsOk = true;\n for (const m of result.modules) {\n if (m.fieldsJson.includes('\"textarea\"') || /\"name\":\\s*\"name\"/.test(m.fieldsJson)) {\n fieldsOk = false;\n break;\n }\n }\n items.push({\n label: \"fields.json valid (no textarea, no reserved names)\",\n passed: moduleCount > 0 && fieldsOk,\n critical: true,\n });\n\n // module.html — critical: modules won't render without it\n const allHaveHtml = result.modules.every((m) => m.moduleHtml.length > 0);\n items.push({\n label: \"module.html created for each module\",\n passed: moduleCount > 0 && allHaveHtml,\n critical: true,\n });\n\n // module.css — not critical (page works but looks unstyled)\n const missingCss = result.modules.filter((m) => !m.moduleCss).map((m) => m.moduleName);\n const allHaveCss = missingCss.length === 0;\n items.push({\n label: allHaveCss\n ? \"module.css created for each module\"\n : `module.css missing for: ${missingCss.join(\", \")}`,\n passed: moduleCount > 0 && allHaveCss,\n critical: false,\n });\n\n // Style tab — not critical (modules work without style tab)\n const hasStyleTab = result.modules.some((m) => m.fieldsJson.includes('\"STYLE\"'));\n items.push({\n label: \"Style tab fields (color pickers)\",\n passed: hasStyleTab,\n critical: false,\n });\n\n // Shared CSS — not critical (modules have their own CSS)\n items.push({\n label: \"Shared CSS with design system variables\",\n passed: result.sharedCss.length > 50,\n critical: false,\n });\n\n // Shared JS — not critical (page works without animations)\n items.push({\n label: \"Shared JS for scroll animations\",\n passed: result.sharedJs.length > 50,\n critical: false,\n });\n\n // Page template — critical: no template means no page in HubSpot\n items.push({\n label: \"Page template with dnd_area\",\n passed: result.template.length > 0 && result.template.includes(\"dnd_area\"),\n critical: true,\n });\n\n // Template annotations — critical: template won't appear in picker\n const templatesDir = join(themePath, \"templates\");\n let templateAnnotated = false;\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\") && /templateType\\s*:\\s*page/i.test(content)) {\n templateAnnotated = true;\n break;\n }\n }\n }\n items.push({\n label: \"Template annotations (templateType: page)\",\n passed: templateAnnotated,\n critical: true,\n });\n\n return items;\n}\n\n/**\n * Ensure all templates in templates/ have the required HubSpot annotations.\n * Without `templateType: page` and `isAvailableForNewContent: true`,\n * the template won't appear in HubSpot's template picker.\n */\nexport function validateTemplates(themePath: string): void {\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return;\n\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n\n const filePath = join(templatesDir, file);\n let content = readFile(filePath);\n\n // Skip files that don't look like page templates\n if (!content.includes(\"dnd_area\") && !content.includes(\"extends\")) continue;\n\n const hasTemplateType = /templateType\\s*:\\s*page/i.test(content);\n const hasAvailable = /isAvailableForNewContent\\s*:\\s*true/i.test(content);\n\n if (hasTemplateType && hasAvailable) continue;\n\n // Build the annotation block\n const label = file.replace(\".html\", \"\").replace(/[-_]/g, \" \").replace(/\\b\\w/g, c => c.toUpperCase());\n\n if (content.includes(\"<!--\") && content.indexOf(\"-->\") < 200) {\n // Has an existing comment block at the top — patch it\n const commentEnd = content.indexOf(\"-->\");\n let annotation = content.slice(0, commentEnd);\n\n if (!hasTemplateType) {\n annotation += \"\\n templateType: page\";\n }\n if (!hasAvailable) {\n annotation += \"\\n isAvailableForNewContent: true\";\n }\n if (!/label\\s*:/i.test(annotation)) {\n annotation += `\\n label: ${label}`;\n }\n\n content = annotation + content.slice(commentEnd);\n } else {\n // No annotation block — prepend one\n const block = `<!--\\n templateType: page\\n isAvailableForNewContent: true\\n label: ${label}\\n-->\\n`;\n content = block + content;\n }\n\n writeFile(filePath, content);\n ui.logSuccess(`Template \"${file}\" — annotations verified`);\n }\n\n}\n\n/**\n * Ensure all module meta.json files have the required fields for\n * landing page compatibility.\n */\nexport function validateModuleMeta(themePath: string): void {\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const metaPath = join(modulesDir, entry, \"meta.json\");\n if (!fileExists(metaPath)) continue;\n\n try {\n const meta = JSON.parse(readFile(metaPath));\n let changed = false;\n\n if (!meta.host_template_types || !meta.host_template_types.includes(\"PAGE\")) {\n meta.host_template_types = [\"PAGE\"];\n changed = true;\n }\n if (!meta.is_available_for_new_content) {\n meta.is_available_for_new_content = true;\n changed = true;\n }\n\n if (changed) {\n writeFile(metaPath, JSON.stringify(meta, null, 2) + \"\\n\");\n }\n } catch {\n // Skip malformed meta.json\n }\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join, basename } from \"node:path\";\nimport { readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide, getHubspotRules } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\n/** Boilerplate modules from `hs cms theme create` — used to distinguish AI-generated modules. */\nconst BOILERPLATE_MODULES = new Set([\n \"button.module\",\n \"card.module\",\n \"menu.module\",\n \"pricing-card.module\",\n \"social-follow.module\",\n]);\n\n/** Boilerplate templates from `hs cms theme create`. */\nconst BOILERPLATE_TEMPLATES = new Set([\n \"about.html\",\n \"blog-index.html\",\n \"blog-post.html\",\n \"contact.html\",\n \"home.html\",\n \"hubdb.html\",\n \"landing-page.html\",\n \"pricing.html\",\n \"qa-test.html\",\n \"base.html\",\n]);\n\nexport class ClaudeCodeEngine implements AIEngine {\n private model?: string;\n private reported = new Set<string>();\n private moduleCount = 0;\n private expectedModules = 0;\n\n constructor(model?: string) {\n this.model = model;\n }\n\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n // Reset progress tracking\n this.reported.clear();\n this.moduleCount = 0;\n this.expectedModules = 0;\n\n // Count source components to estimate expected modules\n const sourceComponents = this.countSourceComponents(sourceDir);\n\n // Snapshot existing files so we can detect what Claude actually created\n const existingModules = this.listModules(themePath);\n const existingCss = this.listDir(join(themePath, \"css\"));\n const existingJs = this.listDir(join(themePath, \"js\"));\n const existingTemplates = this.listDir(join(themePath, \"templates\"));\n\n // Build the prompt for Claude Code\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", `Starting Claude Code (${sourceComponents} source components found)...`);\n\n // Run Claude Code with real-time progress tracking\n let stdout = \"\";\n let stderr = \"\";\n\n // Poll the filesystem every 3s to show progress\n const progressInterval = setInterval(() => {\n this.reportProgress(themePath, existingModules, existingCss, existingJs, existingTemplates, onProgress);\n }, 3000);\n\n try {\n await new Promise<void>((resolve, reject) => {\n // Strip CLAUDECODE env var to allow running from inside a Claude Code session\n const env = { ...process.env };\n delete env.CLAUDECODE;\n\n const args = [\n \"--print\",\n \"--max-turns\", \"50\",\n \"--allowedTools\", \"Read,Glob,Grep,Write,Edit,Bash\",\n ];\n if (this.model) args.push(\"--model\", this.model);\n\n const child = spawn(\"claude\", args, {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env,\n shell: true,\n });\n\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Claude Code failed to start: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(\n `Claude Code exited with code ${code}.\\n` +\n (stderr ? `Stderr: ${stderr.slice(0, 500)}\\n` : \"\") +\n (stdout ? `Output: ${stdout.slice(0, 500)}` : \"No output\")\n ));\n } else {\n resolve();\n }\n });\n\n // Handle stdin errors (EPIPE if claude exits before prompt is fully written)\n child.stdin.on(\"error\", () => {});\n\n // Send prompt via stdin and close\n child.stdin.write(prompt);\n child.stdin.end();\n\n // 30 min timeout\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Claude Code timed out after 30 minutes\"));\n }, 1_800_000);\n });\n } finally {\n clearInterval(progressInterval);\n }\n\n // Write full log to workspace for debugging\n const logPath = join(themePath, \"..\", \"vibespot-conversion.log\");\n try {\n const timestamp = new Date().toISOString();\n const logContent = [\n `=== vibeSpot Conversion Log ===`,\n `Timestamp: ${timestamp}`,\n `Source: ${sourceDir}`,\n `Theme: ${themePath}`,\n `Model: ${this.model || \"default\"}`,\n ``,\n `=== PROMPT SENT ===`,\n prompt.slice(0, 500) + \"\\n... (truncated, full guide follows)\",\n ``,\n `=== CLAUDE CODE STDOUT ===`,\n stdout || \"(empty)\",\n ``,\n `=== CLAUDE CODE STDERR ===`,\n stderr || \"(empty)\",\n ``,\n ].join(\"\\n\");\n writeFileSync(logPath, logContent, \"utf-8\");\n onProgress(\"status\", `Log written to ${basename(logPath)}`);\n } catch {\n // Non-critical — don't fail if log can't be written\n }\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n // Scan the theme directory for what Claude Code created\n const result = this.scanGeneratedFiles(themePath);\n\n // Validate that new files were actually created\n const newModules = result.modules.filter(\n (m) => !existingModules.has(m.moduleName + \".module\")\n );\n\n if (newModules.length === 0) {\n const outputPreview = stdout.slice(0, 1500) || \"(no output)\";\n const stderrPreview = stderr.slice(0, 500);\n throw new Error(\n \"Claude Code did not create any new module files.\\n\\n\" +\n \"This usually means the model described the conversion instead of using Write tool to create files.\\n\\n\" +\n \"Possible causes:\\n\" +\n \" - Model didn't use Write tool (just printed text)\\n\" +\n \" - Claude Code hit a rate limit or API error\\n\" +\n \" - The source directory was not accessible\\n\\n\" +\n `Source: ${opts.sourceDir}\\n` +\n `Theme: ${themePath}\\n` +\n (stderrPreview ? `\\nStderr:\\n${stderrPreview}\\n` : \"\") +\n `\\nClaude output:\\n${outputPreview}`\n );\n }\n\n return result;\n }\n\n /** Poll filesystem and emit \"created\" events for newly detected files. */\n private reportProgress(\n themePath: string,\n existingModules: Set<string>,\n existingCss: Set<string>,\n existingJs: Set<string>,\n existingTemplates: Set<string>,\n onProgress: (step: string, detail: string) => void,\n ): void {\n let newItems = 0;\n\n // Check for new CSS files\n const currentCss = this.listDir(join(themePath, \"css\"));\n for (const f of currentCss) {\n if (existingCss.has(f) || !f.endsWith(\".css\")) continue;\n const key = `css:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Shared CSS (${f})`);\n newItems++;\n }\n }\n\n // Check for new JS files\n const currentJs = this.listDir(join(themePath, \"js\"));\n for (const f of currentJs) {\n if (existingJs.has(f) || !f.endsWith(\".js\")) continue;\n const key = `js:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Shared JS (${f})`);\n newItems++;\n }\n }\n\n // Try to detect expected module count from template file (once it exists)\n if (this.expectedModules === 0) {\n this.expectedModules = this.detectExpectedModules(themePath, existingTemplates);\n }\n\n // Check for new modules\n const currentModules = this.listModules(themePath);\n for (const mod of currentModules) {\n if (existingModules.has(mod)) continue;\n const key = `module:${mod}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n this.moduleCount++;\n const counter = this.expectedModules > 0\n ? `[${this.moduleCount}/${this.expectedModules}]`\n : `[${this.moduleCount}]`;\n onProgress(\"created\", `Module ${counter}: ${mod.replace(\".module\", \"\")}`);\n newItems++;\n }\n }\n\n // Check for new templates\n const currentTemplates = this.listDir(join(themePath, \"templates\"));\n for (const f of currentTemplates) {\n if (existingTemplates.has(f) || !f.endsWith(\".html\")) continue;\n const key = `template:${f}`;\n if (!this.reported.has(key)) {\n this.reported.add(key);\n onProgress(\"created\", `Page template (${f})`);\n newItems++;\n }\n }\n\n // Update spinner status text (only if no new items were just logged)\n if (newItems === 0) {\n if (this.moduleCount > 0) {\n const of = this.expectedModules > 0 ? `/${this.expectedModules}` : \"\";\n onProgress(\"status\", `${this.moduleCount}${of} modules created, conversion continuing...`);\n } else if (this.reported.size > 0) {\n onProgress(\"status\", \"Shared assets created, building modules...\");\n } else {\n onProgress(\"status\", \"Claude Code is analyzing source files...\");\n }\n }\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `You are converting a React landing page to native HubSpot CMS modules.\n\nSOURCE DIRECTORY: ${sourceDir}\nTHEME DIRECTORY: ${themePath}\n\nIMPORTANT — YOU MUST CREATE REAL FILES:\nYou have access to Write, Edit, Read, Glob, Grep, and Bash tools. You MUST use the Write tool to create each file. Do NOT just describe or list what files should be created — actually call the Write tool for every single file. If you do not call Write, no files will be created and the conversion will fail.\n\nSTEP-BY-STEP PROCESS:\n1. Use Glob to find all .tsx/.jsx files in ${sourceDir}/src/\n2. Use Read to read each component file and understand the page structure\n3. Use Write to create a shared CSS file at ${themePath}/css/<name>-theme.css\n - Include CSS custom properties, design system variables, utility classes\n - Add theme-override countermeasures (.body-wrapper:has(), scoped !important overrides)\n4. Use Write to create a shared JS file at ${themePath}/js/<name>-animations.js\n - Convert React hooks to vanilla JS (IntersectionObserver for scroll animations)\n - IIFE wrapper, DOMContentLoaded setup\n5. For EACH visual section of the page, use Write to create ALL FOUR files:\n a. ${themePath}/modules/<name>.module/fields.json\n - Editable fields for the section content\n - NEVER use \"textarea\" type (use \"text\" instead)\n - NEVER use \"name\" as a field name (use \"item_name\" instead)\n - Add a \"styles\" group with \"tab\": \"STYLE\" containing color pickers\n b. ${themePath}/modules/<name>.module/meta.json\n - Must include: host_template_types: [\"PAGE\"], is_available_for_new_content: true\n c. ${themePath}/modules/<name>.module/module.html\n - HubL template that renders the section (convert JSX to HubL)\n d. ${themePath}/modules/<name>.module/module.css\n - REQUIRED — complete vanilla CSS for this section\n - Must include: layout, spacing, colors, typography, backgrounds, gradients, shadows, borders, hover effects, responsive breakpoints\n - Convert ALL Tailwind classes to BEM-style CSS. Do NOT skip this file.\n6. Use Write to create a page template at ${themePath}/templates/lp-<name>.html\n - Annotation: templateType: page, isAvailableForNewContent: true\n - Extends \"./layouts/base.html\"\n - Sets template_css and template_js variables\n - Wraps modules in dnd_area with dnd_section containers\n7. Read ${themePath}/templates/layouts/base.html and ensure it supports template_css and template_js variables\n\nCSS QUALITY: The converted page must visually match the original React page. Every module.css must be self-contained with complete styling for that section.\n\nDo NOT run hs upload — I will handle that separately.\n\nHUBSPOT CMS RULES:\n${getHubspotRules()}\n\nCONVERSION GUIDE:\n${guide}`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n // Find shared CSS (any non-boilerplate CSS in css/)\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n // Find shared JS (any non-boilerplate JS in js/)\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n // Find new template (prefer lp-* or non-boilerplate templates)\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n // First pass: look for lp-* templates (AI-generated naming convention)\n for (const file of readdirSync(templatesDir)) {\n if (file.startsWith(\"lp-\") && file.endsWith(\".html\")) {\n result.template = readFile(join(templatesDir, file));\n break;\n }\n }\n // Second pass: any non-boilerplate template with dnd_area\n if (!result.template) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !BOILERPLATE_TEMPLATES.has(file) &&\n !file.startsWith(\"system\")\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n // Third pass: fall back to any template with dnd_area\n if (!result.template) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n }\n\n // Scan modules/\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n // Only count modules that have at least fields.json and module.html\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n\n /** List module directories in modules/ */\n private listModules(themePath: string): Set<string> {\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return new Set();\n return new Set(\n readdirSync(modulesDir).filter((e) => e.endsWith(\".module\"))\n );\n }\n\n /** List files in a directory */\n private listDir(dir: string): Set<string> {\n if (!fileExists(dir)) return new Set();\n return new Set(readdirSync(dir));\n }\n\n /** Detect expected module count from template file (counts dnd_module references) */\n private detectExpectedModules(themePath: string, existingTemplates: Set<string>): number {\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return 0;\n\n for (const file of readdirSync(templatesDir)) {\n if (existingTemplates.has(file)) continue;\n if (!file.endsWith(\".html\") || file === \"base.html\" || file.startsWith(\"system\")) continue;\n\n try {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n const matches = content.match(/dnd_module/g);\n return matches ? matches.length : 0;\n }\n } catch {\n // Skip unreadable files\n }\n }\n return 0;\n }\n\n /** Count .tsx/.jsx component files in the source directory */\n private countSourceComponents(sourceDir: string): number {\n const srcDir = join(sourceDir, \"src\");\n if (!fileExists(srcDir)) return 0;\n return this.countComponentsRecursive(srcDir);\n }\n\n private countComponentsRecursive(dir: string): number {\n let count = 0;\n for (const entry of readdirSync(dir)) {\n const fullPath = join(dir, entry);\n try {\n const stat = statSync(fullPath);\n if (stat.isDirectory() && entry !== \"node_modules\" && entry !== \".git\") {\n count += this.countComponentsRecursive(fullPath);\n } else if (/\\.(tsx|jsx)$/.test(entry) && !entry.includes(\".test.\") && !entry.includes(\".spec.\")) {\n count++;\n }\n } catch {\n // Skip unreadable entries\n }\n }\n return count;\n }\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport { join, basename } from \"node:path\";\nimport { readdirSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport {\n buildSystemPrompt,\n buildCssPrompt,\n buildJsPrompt,\n buildModulePrompt,\n buildTemplatePrompt,\n} from \"./prompts.js\";\nimport { readFile, fileExists, writeFile, ensureDir } from \"../utils/fs.js\";\n\nexport class ClaudeAPIEngine implements AIEngine {\n private client: Anthropic;\n private model = \"claude-sonnet-4-6\";\n\n constructor(apiKey?: string) {\n this.client = new Anthropic({\n apiKey: apiKey || process.env.ANTHROPIC_API_KEY,\n });\n }\n\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, conversionGuide, onProgress } = opts;\n const systemPrompt = buildSystemPrompt(conversionGuide);\n\n // Determine a page prefix from the source dir name\n const dirName = basename(sourceDir) || \"page\";\n const pagePrefix = dirName\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 15);\n\n // Step 1: Generate shared CSS\n onProgress(\"css\", \"Analyzing design system...\");\n const indexCss = this.findAndReadCSS(sourceDir);\n const tailwindConfig = this.findAndReadTailwind(sourceDir);\n\n const cssContent = await this.complete(\n systemPrompt,\n buildCssPrompt(indexCss, tailwindConfig, pagePrefix)\n );\n const cssPath = join(themePath, \"css\", `${pagePrefix}-theme.css`);\n writeFile(cssPath, cssContent);\n onProgress(\"css-done\", `Created css/${pagePrefix}-theme.css`);\n\n // Step 2: Generate shared JS\n onProgress(\"js\", \"Creating shared JavaScript...\");\n const hooksSource = this.findAndReadHooks(sourceDir);\n const interactiveSource = this.findInteractiveComponents(sourceDir);\n\n const jsContent = await this.complete(\n systemPrompt,\n buildJsPrompt(hooksSource, interactiveSource, pagePrefix)\n );\n const jsPath = join(themePath, \"js\", `${pagePrefix}-animations.js`);\n writeFile(jsPath, jsContent);\n onProgress(\"js-done\", `Created js/${pagePrefix}-animations.js`);\n\n // Step 3: Generate modules\n onProgress(\"modules\", \"Building modules...\");\n const components = this.findComponents(sourceDir);\n const modules: ModuleFiles[] = [];\n\n for (let i = 0; i < components.length; i++) {\n const comp = components[i];\n const moduleName = comp.name\n .replace(/Section$/, \"\")\n .replace(/([A-Z])/g, \" $1\")\n .trim();\n\n onProgress(\n \"module\",\n `Building ${moduleName}.module (${i + 1}/${components.length})...`\n );\n\n const source = readFile(comp.path);\n const response = await this.complete(\n systemPrompt,\n buildModulePrompt(source, moduleName, `See css/${pagePrefix}-theme.css`)\n );\n\n try {\n const parsed = JSON.parse(response);\n const mod: ModuleFiles = {\n moduleName,\n fieldsJson: typeof parsed.fieldsJson === \"string\"\n ? parsed.fieldsJson\n : JSON.stringify(parsed.fieldsJson, null, 2),\n metaJson: typeof parsed.metaJson === \"string\"\n ? parsed.metaJson\n : JSON.stringify(parsed.metaJson, null, 2),\n moduleHtml: parsed.moduleHtml || \"\",\n moduleCss: parsed.moduleCss || \"\",\n moduleJs: parsed.moduleJs || undefined,\n };\n\n // Write module files\n const modDir = join(themePath, \"modules\", `${moduleName}.module`);\n ensureDir(modDir);\n writeFile(join(modDir, \"fields.json\"), mod.fieldsJson);\n writeFile(join(modDir, \"meta.json\"), mod.metaJson);\n writeFile(join(modDir, \"module.html\"), mod.moduleHtml);\n writeFile(join(modDir, \"module.css\"), mod.moduleCss);\n if (mod.moduleJs) writeFile(join(modDir, \"module.js\"), mod.moduleJs);\n\n modules.push(mod);\n onProgress(\"module-done\", `${moduleName}.module (${this.countFiles(mod)} files)`);\n } catch {\n onProgress(\"module-error\", `Failed to parse ${moduleName} — skipping`);\n }\n }\n\n // Step 4: Generate template\n onProgress(\"template\", \"Creating page template...\");\n const moduleNames = modules.map((m) => m.moduleName);\n const templateContent = await this.complete(\n systemPrompt,\n buildTemplatePrompt(moduleNames, dirName, pagePrefix)\n );\n\n const templatePath = join(\n themePath,\n \"templates\",\n `lp-${pagePrefix}.html`\n );\n writeFile(templatePath, templateContent);\n onProgress(\"template-done\", `Created templates/lp-${pagePrefix}.html`);\n\n return {\n sharedCss: cssContent,\n sharedJs: jsContent,\n template: templateContent,\n modules,\n };\n }\n\n private async complete(system: string, user: string): Promise<string> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: 8192,\n system,\n messages: [{ role: \"user\", content: user }],\n });\n\n const textBlock = response.content.find((b) => b.type === \"text\");\n return textBlock?.text || \"\";\n }\n\n private findAndReadCSS(dir: string): string {\n const paths = [\n join(dir, \"src/index.css\"),\n join(dir, \"src/globals.css\"),\n join(dir, \"src/app/globals.css\"),\n join(dir, \"app/globals.css\"),\n ];\n for (const p of paths) {\n if (fileExists(p)) return readFile(p);\n }\n return \"\";\n }\n\n private findAndReadTailwind(dir: string): string {\n const paths = [\n join(dir, \"tailwind.config.ts\"),\n join(dir, \"tailwind.config.js\"),\n join(dir, \"tailwind.config.mjs\"),\n ];\n for (const p of paths) {\n if (fileExists(p)) return readFile(p);\n }\n return \"\";\n }\n\n private findAndReadHooks(dir: string): string {\n const hooksDir = join(dir, \"src/hooks\");\n if (!fileExists(hooksDir)) return \"\";\n try {\n return readdirSync(hooksDir)\n .filter((f) => f.endsWith(\".ts\") || f.endsWith(\".tsx\"))\n .map((f) => `// ${f}\\n${readFile(join(hooksDir, f))}`)\n .join(\"\\n\\n\");\n } catch {\n return \"\";\n }\n }\n\n private findInteractiveComponents(dir: string): string {\n const components = this.findComponents(dir);\n const interactive: string[] = [];\n\n for (const comp of components) {\n const content = readFile(comp.path);\n if (\n /carousel|accordion|typing|parallax|embla|swiper|collapsible/i.test(\n content\n )\n ) {\n interactive.push(`// ${comp.name}\\n${content}`);\n }\n }\n\n return interactive.join(\"\\n\\n\");\n }\n\n private findComponents(dir: string): { name: string; path: string }[] {\n const searchDirs = [\n join(dir, \"src/components/landing\"),\n join(dir, \"src/components/sections\"),\n join(dir, \"src/components\"),\n ];\n\n for (const searchDir of searchDirs) {\n if (!fileExists(searchDir)) continue;\n try {\n return readdirSync(searchDir)\n .filter(\n (f) =>\n (f.endsWith(\".tsx\") || f.endsWith(\".jsx\")) &&\n !f.startsWith(\"ui\") &&\n f !== \"index.tsx\" &&\n f !== \"index.jsx\"\n )\n .map((f) => ({\n name: f.replace(/\\.(tsx|jsx)$/, \"\"),\n path: join(searchDir, f),\n }));\n } catch {\n continue;\n }\n }\n\n return [];\n }\n\n private countFiles(mod: ModuleFiles): number {\n let count = 3; // fields.json, meta.json, module.html always present\n if (mod.moduleCss) count++;\n if (mod.moduleJs) count++;\n return count;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\nexport class GeminiCLIEngine implements AIEngine {\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", \"Running Gemini CLI (this may take a few minutes)...\");\n\n // Use async spawn so the event loop stays free for spinner animation\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"gemini\", [\"-p\", prompt], {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env },\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Gemini CLI failed: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0 && stderr && !stdout) {\n reject(new Error(`Gemini CLI failed: ${stderr}`));\n } else {\n resolve();\n }\n });\n\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Gemini CLI timed out after 10 minutes\"));\n }, 600_000);\n });\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n return this.scanGeneratedFiles(themePath);\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `Read the conversion guide below, then convert the React landing page at ${sourceDir} into native HubSpot CMS modules for the theme at ${themePath}.\n\nINSTRUCTIONS:\n1. Analyze all .tsx/.jsx components in the React source\n2. Create a shared CSS file in css/ with design system variables, utilities, and theme-override countermeasures\n3. Create a shared JS file in js/ for scroll animations and interactive features\n4. For each visual section, create a HubSpot module in modules/ with fields.json, meta.json, module.html, module.css\n5. Add a styles group with \"tab\": \"STYLE\" and color pickers to each module\n6. Create a page template in templates/ that assembles all modules\n7. Make sure base.html supports template_css and template_js variables\n\nCONVERSION GUIDE:\n${guide}\n\nDo NOT run hs upload — I will handle that separately.\nCreate all files directly in the theme directory.`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n (file.includes(\"theme\") || file.includes(\"page\")) &&\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n (file.includes(\"animation\") || file.includes(\"page\")) &&\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { readdirSync, statSync } from \"node:fs\";\nimport type { AIEngine, GeneratedAssets, ModuleFiles } from \"./engine.js\";\nimport { getConversionGuide } from \"./prompts.js\";\nimport { readFile, fileExists } from \"../utils/fs.js\";\n\nexport class CodexCLIEngine implements AIEngine {\n async convert(opts: {\n sourceDir: string;\n themePath: string;\n conversionGuide: string;\n onProgress: (step: string, detail: string) => void;\n }): Promise<GeneratedAssets> {\n const { sourceDir, themePath, onProgress } = opts;\n const guide = opts.conversionGuide || getConversionGuide();\n\n const prompt = this.buildFullPrompt(sourceDir, themePath, guide);\n\n onProgress(\"convert\", \"Running OpenAI Codex (this may take a few minutes)...\");\n\n // Use async spawn so the event loop stays free for spinner animation\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"codex\", [\"exec\", \"--full-auto\", prompt], {\n cwd: themePath,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env },\n shell: true,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString(); });\n child.stderr.on(\"data\", (d: Buffer) => { stderr += d.toString(); });\n\n child.on(\"error\", (err) => reject(new Error(`Codex CLI failed: ${err.message}`)));\n child.on(\"close\", (code) => {\n if (code !== 0 && stderr && !stdout) {\n reject(new Error(`Codex CLI failed: ${stderr}`));\n } else {\n resolve();\n }\n });\n\n setTimeout(() => {\n child.kill();\n reject(new Error(\"Codex CLI timed out after 10 minutes\"));\n }, 600_000);\n });\n\n onProgress(\"scan\", \"Scanning generated files...\");\n\n return this.scanGeneratedFiles(themePath);\n }\n\n private buildFullPrompt(\n sourceDir: string,\n themePath: string,\n guide: string\n ): string {\n return `Read the conversion guide below, then convert the React landing page at ${sourceDir} into native HubSpot CMS modules for the theme at ${themePath}.\n\nINSTRUCTIONS:\n1. Analyze all .tsx/.jsx components in the React source\n2. Create a shared CSS file in css/ with design system variables, utilities, and theme-override countermeasures\n3. Create a shared JS file in js/ for scroll animations and interactive features\n4. For each visual section, create a HubSpot module in modules/ with fields.json, meta.json, module.html, module.css\n5. Add a styles group with \"tab\": \"STYLE\" and color pickers to each module\n6. Create a page template in templates/ that assembles all modules\n7. Make sure base.html supports template_css and template_js variables\n\nCONVERSION GUIDE:\n${guide}\n\nDo NOT run hs upload — I will handle that separately.\nCreate all files directly in the theme directory.`;\n }\n\n private scanGeneratedFiles(themePath: string): GeneratedAssets {\n const result: GeneratedAssets = {\n sharedCss: \"\",\n sharedJs: \"\",\n template: \"\",\n modules: [],\n };\n\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (\n (file.includes(\"theme\") || file.includes(\"page\")) &&\n file.endsWith(\".css\") &&\n file !== \"theme-overrides.css\" &&\n file !== \"main.css\" &&\n file !== \"style.css\"\n ) {\n result.sharedCss = readFile(join(cssDir, file));\n break;\n }\n }\n }\n\n const jsDir = join(themePath, \"js\");\n if (fileExists(jsDir)) {\n for (const file of readdirSync(jsDir)) {\n if (\n (file.includes(\"animation\") || file.includes(\"page\")) &&\n file.endsWith(\".js\") &&\n file !== \"main.js\"\n ) {\n result.sharedJs = readFile(join(jsDir, file));\n break;\n }\n }\n }\n\n const templatesDir = join(themePath, \"templates\");\n if (fileExists(templatesDir)) {\n for (const file of readdirSync(templatesDir)) {\n if (\n file.endsWith(\".html\") &&\n !file.startsWith(\"system\") &&\n file !== \"base.html\"\n ) {\n const content = readFile(join(templatesDir, file));\n if (content.includes(\"dnd_area\")) {\n result.template = content;\n break;\n }\n }\n }\n }\n\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const modDir = join(modulesDir, entry);\n if (!statSync(modDir).isDirectory()) continue;\n\n const moduleFiles: ModuleFiles = {\n moduleName: entry.replace(\".module\", \"\"),\n fieldsJson: \"\",\n metaJson: \"\",\n moduleHtml: \"\",\n moduleCss: \"\",\n };\n\n const fj = join(modDir, \"fields.json\");\n if (fileExists(fj)) moduleFiles.fieldsJson = readFile(fj);\n\n const mj = join(modDir, \"meta.json\");\n if (fileExists(mj)) moduleFiles.metaJson = readFile(mj);\n\n const mh = join(modDir, \"module.html\");\n if (fileExists(mh)) moduleFiles.moduleHtml = readFile(mh);\n\n const mc = join(modDir, \"module.css\");\n if (fileExists(mc)) moduleFiles.moduleCss = readFile(mc);\n\n const mjs = join(modDir, \"module.js\");\n if (fileExists(mjs)) moduleFiles.moduleJs = readFile(mjs);\n\n if (moduleFiles.fieldsJson && moduleFiles.moduleHtml) {\n result.modules.push(moduleFiles);\n }\n }\n }\n\n return result;\n }\n}\n","import { join, basename } from \"node:path\";\nimport { run } from \"../utils/shell.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport {\n parseUploadErrors,\n parseApiErrors,\n autoFixError,\n type UploadError,\n} from \"../server/auto-fix.js\";\nimport { loadConfig, getHubSpotPak } from \"../utils/config.js\";\nimport { uploadTheme, deleteFile } from \"../hubspot/uploader.js\";\n\n/** Count \"Uploaded file\" lines in hs cms upload output */\nfunction countUploadedFiles(output: string): number {\n return (output.match(/^Uploaded file /gm) || []).length;\n}\n\nexport async function runUpload(themePath: string): Promise<boolean> {\n await ui.intro(\"Uploading to HubSpot\");\n\n const themeName = basename(themePath) || themePath;\n const config = loadConfig();\n const pak = getHubSpotPak();\n const useApi = config.hubspotUploadMode !== \"cli\" && !!pak;\n const s = await ui.spinner();\n\n const MAX_RETRIES = 3;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n s.start(\n attempt === 1\n ? \"Uploading theme...\"\n : `Retrying upload (attempt ${attempt}/${MAX_RETRIES})...`\n );\n\n let errors: UploadError[] = [];\n let uploadedCount = 0;\n let success = false;\n\n if (useApi) {\n // API mode — parallel upload\n const result = await uploadTheme(pak!, themePath, themeName, {\n onFileComplete: () => { uploadedCount++; },\n });\n success = result.success;\n if (!success) {\n errors = parseApiErrors(result.errors);\n } else {\n uploadedCount = result.uploaded;\n }\n } else {\n // CLI mode\n const result = run(`hs cms upload \"${themePath}\" \"${themeName}\"`, {\n cwd: join(themePath, \"..\"),\n });\n const fullOutput = [result.stdout, result.stderr].filter(Boolean).join(\"\\n\");\n uploadedCount = countUploadedFiles(fullOutput);\n success = result.success;\n if (!success) {\n errors = parseUploadErrors(fullOutput);\n }\n }\n\n if (success) {\n s.stop(`All files uploaded! (${uploadedCount} files)`);\n await ui.outro(\"Upload complete!\");\n return true;\n }\n\n if (uploadedCount > 0) {\n s.stop(`${uploadedCount} files uploaded, but some errors occurred`);\n } else {\n s.stop(\"Upload failed\");\n }\n\n if (errors.length === 0) {\n ui.logError(\"Upload failed with unknown error.\");\n\n if (uploadedCount > 0) {\n ui.logWarn(\n \"Most files uploaded successfully. The theme may already be usable in HubSpot.\\n\" +\n \"You can check your HubSpot Design Manager to verify.\"\n );\n const proceed = await ui.confirm({\n message: \"Continue anyway (theme is likely uploaded)?\",\n initialValue: true,\n });\n if (proceed) return true;\n }\n\n if (attempt < MAX_RETRIES) {\n const retry = await ui.confirm({ message: \"Try uploading again?\" });\n if (!retry) break;\n continue;\n }\n break;\n }\n\n // Try to auto-fix known errors\n let anyFixed = false;\n for (const error of errors) {\n if (error.fixable) {\n const fixed = autoFixError(themePath, error);\n if (fixed) {\n ui.logSuccess(`Auto-fixed: ${error.message}`);\n anyFixed = true;\n } else {\n ui.logWarn(`Could not auto-fix: ${error.message}`);\n }\n } else {\n ui.logError(error.message);\n }\n }\n\n if (anyFixed && attempt < MAX_RETRIES) continue;\n\n if (uploadedCount > 0) {\n ui.logWarn(\n `${uploadedCount} files uploaded successfully despite errors.\\n` +\n \"The theme may work — check HubSpot Design Manager.\"\n );\n const proceed = await ui.confirm({\n message: \"Continue anyway?\",\n initialValue: true,\n });\n if (proceed) return true;\n }\n\n if (!anyFixed) {\n s.start(\"Cleaning up stuck modules...\");\n if (useApi) {\n try { await deleteFile(pak!, `${themeName}/modules`); } catch { /* ignore */ }\n } else {\n run(`hs cms delete \"${themeName}/modules\"`, { cwd: join(themePath, \"..\") });\n }\n s.stop(\"Cleaned up modules, retrying...\");\n }\n }\n\n ui.logError(\"Upload failed after multiple attempts.\");\n return false;\n}\n","/**\n * Auto-fix utilities for common HubSpot upload errors.\n * Shared between CLI wizard (uploader.ts) and web server (server.ts).\n */\n\nimport { join } from \"node:path\";\nimport { readdirSync, rmSync } from \"node:fs\";\nimport { readFile, writeFile, fileExists } from \"../utils/fs.js\";\n\nexport interface UploadError {\n file: string;\n message: string;\n fixable: boolean;\n}\n\n/** Parse API upload errors into the standard UploadError interface. */\nexport function parseApiErrors(apiErrors: { file: string; status: number; message: string; category?: string; detail?: string }[]): UploadError[] {\n const errors: UploadError[] = [];\n\n for (const err of apiErrors) {\n const msg = `${err.message}${err.detail ? ` — ${err.detail}` : \"\"}`;\n let fixable = false;\n\n // Detect fixable error patterns from API response messages\n if (/textarea|unknown.*field.*type/i.test(msg)) fixable = true;\n if (/reserved.*name|missing field name|field null/i.test(msg)) fixable = true;\n if (/could not resolve.*now/i.test(msg)) fixable = true;\n if (/hubdb|do not have access/i.test(msg)) fixable = true;\n if (/invalid default value|link.*invalid|deserializ/i.test(msg)) fixable = true;\n if (/color.*invalid/i.test(msg)) fixable = true;\n\n errors.push({\n file: err.file || \"unknown\",\n message: msg,\n fixable,\n });\n }\n\n return errors;\n}\n\nexport function parseUploadErrors(output: string): UploadError[] {\n const errors: UploadError[] = [];\n\n if (/textarea.*not.*valid|unknown.*field.*type/i.test(output)) {\n const fileMatch = output.match(/(?:in|file:?)\\s+(\\S+fields\\.json)/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: '\"textarea\" is not a valid field type',\n fixable: true,\n });\n }\n\n if (/missing field name|field null/i.test(output)) {\n const fileMatch = output.match(/(?:in|file:?)\\s+(\\S+fields\\.json)/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: '\"name\" is a reserved field name',\n fixable: true,\n });\n }\n\n if (/could not resolve.*now/i.test(output)) {\n errors.push({\n file: \"module.html\",\n message: \"now() is not a valid HubL function\",\n fixable: true,\n });\n }\n\n if (/hubdb|do not have access to hubdb/i.test(output)) {\n errors.push({\n file: \"templates\",\n message: \"HubDB requires CMS Hub Pro/Enterprise\",\n fixable: true,\n });\n }\n\n if (/invalid default value|link.*field.*invalid/i.test(output)) {\n const fieldMatch = output.match(/field.*?(\\w+)\\s+has an invalid/i);\n errors.push({\n file: fieldMatch?.[1] || \"fields.json\",\n message: `Link field has invalid default value`,\n fixable: true,\n });\n }\n\n if (/failed to deserialize/i.test(output)) {\n const fileMatch = output.match(/file '([^']+)'/i);\n errors.push({\n file: fileMatch?.[1] || \"fields.json\",\n message: \"fields.json deserialization error\",\n fixable: true,\n });\n }\n\n if (/format for the color value is invalid/i.test(output)) {\n errors.push({\n file: \"fields.json\",\n message: \"Color field has invalid format (rgba/rgb/named — must be hex)\",\n fixable: true,\n });\n }\n\n return errors;\n}\n\nexport function applyAutoFixes(themePath: string): string[] {\n const fixes: string[] = [];\n if (fixTextareaFields(themePath)) fixes.push('textarea → text');\n if (fixReservedNames(themePath)) fixes.push('name → item_name');\n if (fixNowFunction(themePath)) fixes.push('now() → local_dt');\n if (fixHubDbTemplates(themePath)) fixes.push('Removed HubDB templates');\n if (fixLinkFieldDefaults(themePath)) fixes.push('Fixed link field defaults');\n if (fixColorFieldDefaults(themePath)) fixes.push('Fixed rgba/invalid color values → hex');\n if (fixCdnImports(themePath)) fixes.push('Stripped CDN @import statements');\n return fixes;\n}\n\nexport function autoFixError(themePath: string, error: UploadError): boolean {\n if (error.message.includes(\"textarea\")) return fixTextareaFields(themePath);\n if (error.message.includes(\"reserved field name\")) return fixReservedNames(themePath);\n if (error.message.includes(\"now()\")) return fixNowFunction(themePath);\n if (error.message.includes(\"HubDB\")) return fixHubDbTemplates(themePath);\n if (error.message.includes(\"invalid default value\") || error.message.includes(\"deserialization\"))\n return fixLinkFieldDefaults(themePath);\n if (error.message.includes(\"invalid format\") && error.message.includes(\"color\"))\n return fixColorFieldDefaults(themePath);\n return false;\n}\n\nexport function fixTextareaFields(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n let content = readFile(fieldsPath);\n if (content.includes('\"textarea\"')) {\n content = content.replace(/\"textarea\"/g, '\"text\"');\n writeFile(fieldsPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixReservedNames(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n let content = readFile(fieldsPath);\n if (/\"name\":\\s*\"name\"/g.test(content)) {\n content = content.replace(/\"name\":\\s*\"name\"/g, '\"name\": \"item_name\"');\n writeFile(fieldsPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixNowFunction(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (!fileExists(htmlPath)) continue;\n let content = readFile(htmlPath);\n if (content.includes(\"now()\")) {\n content = content.replace(/now\\(\\)/g, \"local_dt\");\n writeFile(htmlPath, content);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixHubDbTemplates(themePath: string): boolean {\n let fixed = false;\n const templatesDir = join(themePath, \"templates\");\n if (!fileExists(templatesDir)) return false;\n\n for (const file of readdirSync(templatesDir)) {\n if (!file.endsWith(\".html\")) continue;\n const filePath = join(templatesDir, file);\n const content = readFile(filePath);\n if (content.includes(\"hubdb_table\") || content.includes(\"hubdb_table_rows\")) {\n rmSync(filePath);\n fixed = true;\n }\n }\n return fixed;\n}\n\nexport function fixLinkFieldDefaults(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n try {\n const fields = JSON.parse(readFile(fieldsPath));\n if (fixLinkFieldsRecursive(fields)) {\n writeFile(fieldsPath, JSON.stringify(fields, null, 2) + \"\\n\");\n fixed = true;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n return fixed;\n}\n\n/**\n * Strip external CDN @import statements from all CSS files.\n * Google Fonts and other CDN imports can fail in HubSpot's editor\n * and cause content to appear invisible.\n */\nexport function fixCdnImports(themePath: string): boolean {\n let fixed = false;\n\n // Check shared CSS files\n const cssDir = join(themePath, \"css\");\n if (fileExists(cssDir)) {\n for (const file of readdirSync(cssDir)) {\n if (!file.endsWith(\".css\")) continue;\n const filePath = join(cssDir, file);\n let content = readFile(filePath);\n const cleaned = content.replace(/@import\\s+url\\(['\"]?https?:\\/\\/[^)]+['\"]?\\)\\s*;?/gi, \"\");\n if (cleaned !== content) {\n writeFile(filePath, cleaned);\n fixed = true;\n }\n }\n }\n\n // Check module CSS files\n const modulesDir = join(themePath, \"modules\");\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const cssPath = join(modulesDir, entry, \"module.css\");\n if (!fileExists(cssPath)) continue;\n let content = readFile(cssPath);\n const cleaned = content.replace(/@import\\s+url\\(['\"]?https?:\\/\\/[^)]+['\"]?\\)\\s*;?/gi, \"\");\n if (cleaned !== content) {\n writeFile(cssPath, cleaned);\n fixed = true;\n }\n }\n }\n\n // Check module HTML for <link> tags to external CDNs\n if (fileExists(modulesDir)) {\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const htmlPath = join(modulesDir, entry, \"module.html\");\n if (!fileExists(htmlPath)) continue;\n let content = readFile(htmlPath);\n const cleaned = content.replace(/<link[^>]+href=['\"]https?:\\/\\/[^'\"]+['\"][^>]*>/gi, \"\");\n if (cleaned !== content) {\n writeFile(htmlPath, cleaned);\n fixed = true;\n }\n }\n }\n\n return fixed;\n}\n\n/**\n * Fix color fields that use rgba(), rgb(), named colors, or 3-digit hex.\n * HubSpot requires: { \"color\": \"#rrggbb\", \"opacity\": 100 }\n */\nexport function fixColorFieldDefaults(themePath: string): boolean {\n let fixed = false;\n const modulesDir = join(themePath, \"modules\");\n if (!fileExists(modulesDir)) return false;\n\n for (const entry of readdirSync(modulesDir)) {\n if (!entry.endsWith(\".module\")) continue;\n const fieldsPath = join(modulesDir, entry, \"fields.json\");\n if (!fileExists(fieldsPath)) continue;\n try {\n const fields = JSON.parse(readFile(fieldsPath));\n if (fixColorFieldsRecursive(fields)) {\n writeFile(fieldsPath, JSON.stringify(fields, null, 2) + \"\\n\");\n fixed = true;\n }\n } catch {\n // Skip malformed JSON\n }\n }\n return fixed;\n}\n\nfunction fixColorFieldsRecursive(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"color\" && f.default && typeof f.default === \"object\") {\n const def = f.default as Record<string, unknown>;\n const colorVal = def.color;\n if (typeof colorVal === \"string\" && !isValidHexColor(colorVal)) {\n const converted = convertToHex(colorVal);\n if (converted) {\n def.color = converted.hex;\n // If the rgba had opacity, use that instead of the existing opacity\n if (converted.opacity !== undefined) {\n def.opacity = converted.opacity;\n }\n fixed = true;\n }\n }\n }\n\n if (Array.isArray(f.children)) {\n if (fixColorFieldsRecursive(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n\nfunction isValidHexColor(color: string): boolean {\n return /^#[0-9a-fA-F]{6}$/.test(color);\n}\n\nfunction convertToHex(color: string): { hex: string; opacity?: number } | null {\n // 3-digit hex → 6-digit\n const hex3 = color.match(/^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/);\n if (hex3) {\n return { hex: `#${hex3[1]}${hex3[1]}${hex3[2]}${hex3[2]}${hex3[3]}${hex3[3]}` };\n }\n\n // rgba(r, g, b, a)\n const rgba = color.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*([\\d.]+))?\\s*\\)/i);\n if (rgba) {\n const r = Math.min(255, parseInt(rgba[1]));\n const g = Math.min(255, parseInt(rgba[2]));\n const b = Math.min(255, parseInt(rgba[3]));\n const hex = `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\n const opacity = rgba[4] !== undefined ? Math.round(parseFloat(rgba[4]) * 100) : undefined;\n return { hex, opacity };\n }\n\n // Named colors (common ones)\n const named: Record<string, string> = {\n white: \"#ffffff\", black: \"#000000\", red: \"#ff0000\", green: \"#008000\",\n blue: \"#0000ff\", yellow: \"#ffff00\", orange: \"#ffa500\", purple: \"#800080\",\n gray: \"#808080\", grey: \"#808080\", transparent: \"#000000\",\n };\n const lower = color.toLowerCase().trim();\n if (named[lower]) {\n return { hex: named[lower], opacity: lower === \"transparent\" ? 0 : undefined };\n }\n\n return null;\n}\n\nfunction fixLinkFieldsRecursive(fields: unknown[]): boolean {\n let fixed = false;\n for (const field of fields) {\n if (typeof field !== \"object\" || field === null) continue;\n const f = field as Record<string, unknown>;\n\n if (f.type === \"link\") {\n const def = f.default;\n const needsFix =\n typeof def === \"string\" ||\n def === undefined ||\n def === null ||\n (typeof def === \"object\" && !(def as Record<string, unknown>).url);\n\n if (needsFix) {\n const href = typeof def === \"string\" ? def : \"\";\n f.default = {\n url: { href, type: \"EXTERNAL\" },\n open_in_new_tab: false,\n no_follow: false,\n };\n fixed = true;\n }\n }\n\n if (Array.isArray(f.children)) {\n if (fixLinkFieldsRecursive(f.children as unknown[])) fixed = true;\n }\n }\n return fixed;\n}\n","/**\n * Parallel theme uploader — walks a theme directory and uploads all files\n * to HubSpot via the CMS Source Code API v3.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { uploadFile, type HubSpotApiError, type UploadResult } from \"./api.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UploadFileError {\n file: string;\n status: number;\n message: string;\n category?: string;\n detail?: string;\n}\n\nexport interface UploadThemeResult {\n success: boolean;\n uploaded: number;\n failed: number;\n total: number;\n errors: UploadFileError[];\n}\n\nexport interface UploadThemeOptions {\n concurrency?: number;\n onFileStart?: (path: string) => void;\n onFileComplete?: (path: string) => void;\n onFileError?: (path: string, error: UploadFileError) => void;\n onProgress?: (completed: number, total: number) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Excluded directories\n// ---------------------------------------------------------------------------\n\nconst EXCLUDED_DIRS = new Set([\".git\", \"node_modules\", \".vibespot\", \".DS_Store\"]);\n\n// ---------------------------------------------------------------------------\n// File discovery\n// ---------------------------------------------------------------------------\n\n/** Recursively walk a directory and return all file paths. */\nfunction walkDir(dir: string): string[] {\n const files: string[] = [];\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (EXCLUDED_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith(\".\") && entry.name !== \".gitkeep\") continue;\n\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n files.push(...walkDir(fullPath));\n } else if (entry.isFile()) {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// Parallel upload\n// ---------------------------------------------------------------------------\n\n/** Run async tasks with bounded concurrency. */\nasync function parallelMap<T>(\n items: T[],\n concurrency: number,\n fn: (item: T) => Promise<void>,\n): Promise<void> {\n let index = 0;\n\n async function worker(): Promise<void> {\n while (index < items.length) {\n const i = index++;\n await fn(items[i]);\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());\n await Promise.all(workers);\n}\n\n// ---------------------------------------------------------------------------\n// Main upload function\n// ---------------------------------------------------------------------------\n\n/**\n * Upload an entire theme directory to HubSpot.\n * Files are uploaded in parallel with configurable concurrency.\n */\nexport async function uploadTheme(\n pak: string,\n themePath: string,\n themeName: string,\n opts: UploadThemeOptions = {},\n): Promise<UploadThemeResult> {\n const concurrency = opts.concurrency ?? 5;\n\n // Discover all files\n const localFiles = walkDir(themePath);\n const total = localFiles.length;\n let uploaded = 0;\n let failed = 0;\n const errors: UploadFileError[] = [];\n\n await parallelMap(localFiles, concurrency, async (localPath) => {\n // Map local path to remote path: themeName/relative/path\n const rel = relative(themePath, localPath).replace(/\\\\/g, \"/\");\n const remotePath = `${themeName}/${rel}`;\n\n opts.onFileStart?.(rel);\n\n const result = await uploadFile(pak, remotePath, localPath);\n\n if (result.success) {\n uploaded++;\n opts.onFileComplete?.(rel);\n } else {\n failed++;\n const err: UploadFileError = {\n file: rel,\n status: result.error?.status || 0,\n message: result.error?.message || \"Unknown error\",\n category: result.error?.category,\n detail: result.error?.detail,\n };\n errors.push(err);\n opts.onFileError?.(rel, err);\n }\n\n opts.onProgress?.(uploaded + failed, total);\n });\n\n return {\n success: failed === 0,\n uploaded,\n failed,\n total,\n errors,\n };\n}\n\n/**\n * Delete a remote path (used for cleaning up stuck modules).\n */\nexport { deleteFile } from \"./api.js\";\n","import { execFileSync } from \"node:child_process\";\nimport { rmSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\nimport { detectDataCenter } from \"../utils/detect.js\";\nimport { fileExists } from \"../utils/fs.js\";\n\nexport async function showNextSteps(opts: {\n portalId: string;\n sourceDir: string;\n themePath: string;\n wasCloned: boolean;\n}): Promise<void> {\n const { portalId, sourceDir, themePath, wasCloned } = opts;\n await ui.intro(\"You're all set!\");\n\n const dataCenter = detectDataCenter(portalId);\n const host =\n dataCenter === \"eu1\" ? \"app-eu1.hubspot.com\" : \"app.hubspot.com\";\n\n await ui.note(\n `Your React page has been converted and uploaded to HubSpot.\\n` +\n `The theme and modules are now in your account, but you still\\n` +\n `need to ${theme.bold(\"create a new landing page\")} that uses them.\\n\\n` +\n `Next steps:\\n\\n` +\n ` ${theme.bold(\"1.\")} Go to HubSpot ${theme.muted(\"→\")} Content ${theme.muted(\"→\")} Landing Pages ${theme.muted(\"→\")} Create\\n` +\n ` ${theme.bold(\"2.\")} Choose your uploaded theme from the theme picker\\n` +\n ` ${theme.bold(\"3.\")} Select the landing page template that was just created\\n` +\n ` ${theme.bold(\"4.\")} Your converted modules will appear — drag them onto the page\\n` +\n ` ${theme.bold(\"5.\")} Click each section to edit text, images, and colors\\n` +\n ` ${theme.bold(\"6.\")} Upload images via File Manager ${theme.muted(\"(Settings → Files)\")}\\n` +\n ` ${theme.bold(\"7.\")} Preview and publish!`,\n \"What's next\"\n );\n\n const openBrowser = await ui.confirm({\n message: \"Open HubSpot Landing Pages in your browser?\",\n });\n\n if (openBrowser) {\n const url = portalId\n ? `https://${host}/page-ui/${portalId}/management/pages/landing`\n : `https://${host}`;\n\n try {\n // Cross-platform browser open\n const platform = process.platform;\n if (platform === \"darwin\") {\n execFileSync(\"open\", [url], { stdio: \"ignore\" });\n } else if (platform === \"win32\") {\n execFileSync(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n } else {\n execFileSync(\"xdg-open\", [url], { stdio: \"ignore\" });\n }\n ui.logSuccess(\"Opening HubSpot Landing Pages...\");\n } catch {\n ui.log(`Open this URL in your browser: ${theme.info(url)}`);\n }\n }\n\n // Offer to clean up local directories\n const dirsToClean: { path: string; label: string }[] = [];\n if (wasCloned && fileExists(sourceDir)) {\n dirsToClean.push({ path: sourceDir, label: `Cloned source (${basename(sourceDir)})` });\n }\n if (fileExists(themePath)) {\n dirsToClean.push({ path: themePath, label: `Theme directory (${basename(themePath)})` });\n }\n\n if (dirsToClean.length > 0) {\n const cleanup = await ui.confirm({\n message: \"Clean up local working directories?\",\n });\n\n if (cleanup) {\n for (const dir of dirsToClean) {\n try {\n rmSync(dir.path, { recursive: true, force: true });\n ui.logSuccess(`Removed ${dir.label}`);\n } catch {\n ui.logWarn(`Could not remove ${dir.label} — delete manually if needed.`);\n }\n }\n }\n }\n\n await ui.outro(`Thanks for using hub${theme.vibes(\"Vibes\")}! ${theme.vibes(\"~\")}`);\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runPreflight } from \"../wizard/preflight.js\";\n\nexport async function initCommand(): Promise<void> {\n printBanner();\n await runPreflight();\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { setupSource } from \"../wizard/source.js\";\nimport { setupTheme } from \"../wizard/theme-setup.js\";\nimport { runConversion } from \"../wizard/conversion.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nexport async function convertCommand(): Promise<void> {\n printBanner();\n\n const config = loadConfig();\n\n if (!config.aiEngine) {\n ui.logError(\n \"AI engine not configured. Run `vibespot init` first or use the full wizard with `vibespot`.\"\n );\n process.exit(1);\n }\n\n const source = await setupSource();\n const themeInfo = await setupTheme();\n\n await runConversion({\n aiEngine: config.aiEngine,\n sourceDir: source.sourceDir,\n themePath: themeInfo.themePath,\n });\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport { runUpload } from \"../wizard/uploader.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\n\nexport async function uploadCommand(): Promise<void> {\n printBanner();\n\n const config = loadConfig();\n\n if (!config.lastThemePath) {\n const path = await ui.text({\n message: \"Path to your HubSpot theme directory:\",\n placeholder: \"./my-theme\",\n validate: (v) => (v.trim() ? undefined : \"Path is required\"),\n });\n await runUpload(path);\n } else {\n const useLast = await ui.confirm({\n message: `Upload from ${config.lastThemePath}?`,\n });\n\n if (useLast) {\n await runUpload(config.lastThemePath);\n } else {\n const path = await ui.text({\n message: \"Path to your HubSpot theme directory:\",\n placeholder: \"./my-theme\",\n });\n await runUpload(path);\n }\n }\n}\n","import { printBanner } from \"../cli/banner.js\";\nimport {\n detectNode,\n detectGit,\n detectHubSpotCLI,\n detectClaudeCode,\n detectGeminiCLI,\n detectCodexCLI,\n detectHubSpotAuth,\n nodeVersionOk,\n hsCliVersionOk,\n} from \"../utils/detect.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport * as ui from \"../prompts/prompter.js\";\nimport { theme } from \"../cli/theme.js\";\n\nexport async function doctorCommand(): Promise<void> {\n printBanner();\n await ui.intro(\"Environment Diagnostics\");\n\n let issues = 0;\n\n // Node.js\n const node = detectNode();\n if (!node.found) {\n ui.logError(\"Node.js — not installed\");\n ui.log(\" Install from https://nodejs.org\");\n issues++;\n } else if (!nodeVersionOk(node.version)) {\n ui.logWarn(`Node.js v${node.version} — too old (need 18+)`);\n ui.log(\" Update at https://nodejs.org\");\n issues++;\n } else {\n ui.logSuccess(`Node.js v${node.version}`);\n }\n\n // Git\n const git = detectGit();\n if (!git.found) {\n ui.logError(\"Git — not installed\");\n ui.log(\" Install from https://git-scm.com\");\n issues++;\n } else {\n ui.logSuccess(`Git ${git.version}`);\n }\n\n // HubSpot CLI (only needed for deployment)\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n ui.logWarn(\"HubSpot CLI — not installed (only needed for deployment)\");\n ui.log(\" Install: npm install -g @hubspot/cli\");\n } else if (!hsCliVersionOk(hs.version)) {\n ui.logWarn(`HubSpot CLI v${hs.version} — too old (need v8+)`);\n ui.log(\" Update: npm install -g @hubspot/cli@latest\");\n issues++;\n } else {\n ui.logSuccess(`HubSpot CLI v${hs.version}`);\n\n // HubSpot auth\n const auth = detectHubSpotAuth();\n if (!auth.authenticated) {\n ui.logWarn(\"HubSpot — not authenticated\");\n ui.log(\" Run: hs init\");\n } else {\n ui.logSuccess(\n `HubSpot portal${auth.portalName ? `: ${auth.portalName}` : \"\"} (ID: ${auth.portalId})`\n );\n }\n }\n\n // AI engines\n const claude = detectClaudeCode();\n if (claude.found) {\n ui.logSuccess(`Claude Code ${claude.version} at ${claude.path}`);\n } else {\n ui.log(theme.muted(\"Claude Code — not installed\"));\n }\n\n const gemini = detectGeminiCLI();\n if (gemini.found) {\n ui.logSuccess(`Gemini CLI ${gemini.version} at ${gemini.path}`);\n } else {\n ui.log(theme.muted(\"Gemini CLI — not installed\"));\n }\n\n const codex = detectCodexCLI();\n if (codex.found) {\n ui.logSuccess(`OpenAI Codex ${codex.version} at ${codex.path}`);\n } else {\n ui.log(theme.muted(\"OpenAI Codex — not installed\"));\n }\n\n // API keys\n const config = loadConfig();\n\n const anthropicKey = !!(config.anthropicApiKey || process.env.ANTHROPIC_API_KEY);\n const openaiKey = !!(config.openaiApiKey || process.env.OPENAI_API_KEY);\n const geminiKey = !!(config.geminiApiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_AI_API_KEY);\n\n if (anthropicKey) ui.logSuccess(\"Anthropic API key configured\");\n else ui.log(theme.muted(\"Anthropic API key — not set\"));\n\n if (openaiKey) ui.logSuccess(\"OpenAI API key configured\");\n else ui.log(theme.muted(\"OpenAI API key — not set\"));\n\n if (geminiKey) ui.logSuccess(\"Google AI API key configured\");\n else ui.log(theme.muted(\"Google AI API key — not set\"));\n const engineLabels: Record<string, string> = {\n \"claude-code\": \"Claude Code\",\n \"api\": \"Anthropic API\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-api\": \"Gemini API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"codex-cli\": \"OpenAI Codex\",\n };\n if (config.aiEngine) {\n ui.logSuccess(`AI engine: ${engineLabels[config.aiEngine] || config.aiEngine}`);\n }\n if (config.lastThemePath) {\n ui.log(theme.muted(`Last theme: ${config.lastThemePath}`));\n }\n\n // No AI option available\n if (!claude.found && !gemini.found && !codex.found && !anthropicKey && !openaiKey && !geminiKey) {\n ui.logWarn(\"No AI engine available\");\n ui.log(\" Fastest: Set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY)\");\n ui.log(\" Or install: Claude Code — https://claude.ai/code\");\n ui.log(\" Gemini CLI — https://github.com/google-gemini/gemini-cli\");\n ui.log(\" Codex CLI — https://github.com/openai/codex\");\n issues++;\n }\n\n console.log();\n if (issues === 0) {\n await ui.outro(\"Everything looks good!\");\n } else {\n await ui.outro(\n theme.warn(`${issues} issue${issues > 1 ? \"s\" : \"\"} found — see above`)\n );\n }\n}\n","/**\n * `vibespot vibe` — Vibe coding mode.\n * Immediately starts a local server and opens the browser.\n * All setup happens in the web UI — zero CLI prompts.\n */\n\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { execFileSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { startServer } from \"../server/server.js\";\nimport { saveSession } from \"../server/session.js\";\n\nconst DEFAULT_PORT = 4200;\n\nexport async function vibeCommand(): Promise<void> {\n const accent = chalk.hex(\"#e8613a\");\n const dim = chalk.dim;\n\n console.log(\"\");\n console.log(accent(\" v vibeSpot\"));\n console.log(dim(\" Starting...\\n\"));\n\n const uiDir = resolveUiDir();\n if (!uiDir) {\n console.error(chalk.red(\" Could not find UI assets. Is the package installed correctly?\"));\n process.exit(1);\n }\n\n try {\n const { port, close } = await startServer({ port: DEFAULT_PORT, uiDir });\n const url = `http://localhost:${port}`;\n\n console.log(accent(` v ${url}`));\n console.log(dim(\" Press Ctrl+C to stop\\n\"));\n\n // Auto-open browser\n try {\n if (process.platform === \"darwin\") {\n execFileSync(\"open\", [url], { stdio: \"ignore\" });\n } else if (process.platform === \"win32\") {\n execFileSync(\"cmd\", [\"/c\", \"start\", \"\", url], { stdio: \"ignore\" });\n } else {\n execFileSync(\"xdg-open\", [url], { stdio: \"ignore\" });\n }\n } catch {\n // Browser open failed — user can open manually\n }\n\n // Keep running until Ctrl+C\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", () => {\n console.log(dim(\"\\n Saving session...\"));\n saveSession();\n close();\n console.log(dim(\" Goodbye!\\n\"));\n resolve();\n // Force exit after a short grace period — open connections\n // (WebSocket, keep-alive HTTP) can keep the process alive indefinitely.\n setTimeout(() => process.exit(0), 500);\n });\n });\n } catch (err) {\n console.error(chalk.red(` Failed to start: ${err instanceof Error ? err.message : String(err)}`));\n process.exit(1);\n }\n}\n\nfunction resolveUiDir(): string | null {\n const candidates = [\n join(import.meta.dirname, \"../../ui\"),\n join(import.meta.dirname, \"../ui\"),\n join(process.cwd(), \"ui\"),\n ];\n\n for (const dir of candidates) {\n if (existsSync(join(dir, \"index.html\"))) return dir;\n }\n\n return null;\n}\n","/**\n * Local development server for vibeSpot vibe coding mode.\n * Serves the UI, handles WebSocket connections, and manages AI interactions.\n */\n\nimport { createServer, IncomingMessage, ServerResponse } from \"node:http\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, extname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport {\n getSession,\n addMessage,\n getOrderedModules,\n updateModules,\n reorderModules,\n writeModulesToDisk,\n saveSession,\n getActiveTemplate,\n} from \"./session.js\";\nimport { commitThemeState, commitTemplateState, isGitAvailable } from \"./project-git.js\";\nimport { buildPreviewHtml, buildModulePreviewHtml } from \"./preview.js\";\nimport { handleGenerateStream, handleAgenticGenerate, applyPipelineResult, shouldUseAgenticMode, setParseWarningCallback, resolveAgenticEngine } from \"./ai-handler.js\";\nimport { loadConfig, getHubSpotPak, getActiveHubSpotAccount } from \"../utils/config.js\";\nimport { detectHubSpotAuth, detectDataCenter, detectHubSpotAuthFromConfig } from \"../utils/detect.js\";\nimport { applyAutoFixes, parseUploadErrors, parseApiErrors } from \"./auto-fix.js\";\nimport { startStreamingJob, startJobSafe, getJob, addJobListener, removeJobListener } from \"./process-manager.js\";\nimport { uploadTheme, type UploadFileError } from \"../hubspot/uploader.js\";\nimport { jsonResponse } from \"./route-helpers.js\";\nimport { getChangelog } from \"../utils/fs.js\";\n\n// Route modules\nimport {\n handleSetupInfoRoute,\n handleSetupCreateRoute,\n handleSetupFetchRoute,\n handleSetupOpenRoute,\n handleSetupResumeRoute,\n handleSetupApiKeyRoute,\n handleSetupRemoteThemesRoute,\n} from \"./routes/setup.js\";\nimport {\n handleSettingsStatusRoute,\n handleSettingsEngineRoute,\n handleSettingsApiKeyRoute,\n handleSettingsInstallRoute,\n handleSettingsHsAuthRoute,\n handleSettingsGhAuthRoute,\n handleSettingsHsSwitchRoute,\n handleSettingsGhLogoutRoute,\n handleSettingsCLIAuthRoute,\n handleSettingsHsModeRoute,\n handleSettingsCliToggleRoute,\n handleSettingsGenericRoute,\n handleSettingsJobRoute,\n} from \"./routes/settings.js\";\nimport {\n handleClaudeOAuthSaveRoute,\n handleClaudeOAuthStatusRoute,\n handleClaudeOAuthLogoutRoute,\n} from \"./routes/claude-oauth.js\";\nimport {\n handleThemesRoute,\n handleThemeSwitchRoute,\n handleDeleteLocalThemeRoute,\n handleRenameThemeRoute,\n} from \"./routes/themes.js\";\nimport {\n handleDashboardRoute,\n handleDownloadZipRoute,\n handleTemplatesRoute,\n handleTemplateActivateRoute,\n handleTemplateRenameRoute,\n handleTemplateCloneRoute,\n handleModuleLibraryRoute,\n handleAddModuleToTemplateRoute,\n handleBrandAssetsRoute,\n handleDesignExtractRoute,\n handleReferenceImportRoute,\n} from \"./routes/templates.js\";\nimport {\n handleSessionRoute,\n handleModulesRoute,\n handleReorderRoute,\n handleUploadRoute,\n handleFieldRoute,\n handleImportRoute,\n handleHistoryRoute,\n handleRollbackRoute,\n handleCodeUpdateRoute,\n} from \"./routes/modules.js\";\nimport { handleFileUploadRoute } from \"./routes/upload-files.js\";\n\n// ---------------------------------------------------------------------------\n// MIME types for static serving\n// ---------------------------------------------------------------------------\n\nconst MIME_TYPES: Record<string, string> = {\n \".html\": \"text/html\",\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".webp\": \"image/webp\",\n \".gif\": \"image/gif\",\n \".ico\": \"image/x-icon\",\n \".woff2\": \"font/woff2\",\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface ServerOptions {\n port: number;\n uiDir: string;\n}\n\nexport function startServer(opts: ServerOptions): Promise<{ port: number; close: () => void }> {\n const { port, uiDir } = opts;\n\n const server = createServer((req, res) => handleRequest(req, res, uiDir));\n\n // WebSocket server — upgrade on the same HTTP server\n const wss = new WebSocketServer({ server });\n wss.on(\"connection\", (ws) => handleWsConnection(ws));\n\n return new Promise((resolve, reject) => {\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n // Try next port\n server.listen(port + 1, () => {\n resolve({\n port: port + 1,\n close: () => { server.close(); wss.close(); },\n });\n });\n } else {\n reject(err);\n }\n });\n\n server.listen(port, () => {\n resolve({\n port,\n close: () => { server.close(); wss.close(); },\n });\n });\n });\n}\n\n// ---------------------------------------------------------------------------\n// HTTP request handler\n// ---------------------------------------------------------------------------\n\nfunction handleRequest(req: IncomingMessage, res: ServerResponse, uiDir: string): void {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n const method = req.method || \"GET\";\n\n // Security headers\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n res.setHeader(\"X-Frame-Options\", \"DENY\");\n res.setHeader(\"X-XSS-Protection\", \"1; mode=block\");\n res.setHeader(\"Referrer-Policy\", \"strict-origin-when-cross-origin\");\n\n // API routes\n if (url.pathname.startsWith(\"/api/\")) {\n handleApiRoute(method, url.pathname, req, res);\n return;\n }\n\n // Preview route — returns rendered preview HTML\n if (url.pathname === \"/preview\") {\n const html = buildPreviewHtml();\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n return;\n }\n\n // Single-module preview (for dashboard module library)\n if (url.pathname === \"/module-preview\") {\n const moduleName = url.searchParams.get(\"module\") || \"\";\n const html = buildModulePreviewHtml(moduleName);\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html || \"<!-- module not found -->\");\n return;\n }\n\n // Theme assets — serve uploaded images for preview\n if (url.pathname.startsWith(\"/theme-assets/\")) {\n serveThemeAsset(url.pathname.slice(\"/theme-assets/\".length), res);\n return;\n }\n\n // Documentation — served from ui/docs/ directory\n if (url.pathname === \"/docs\") {\n res.writeHead(301, { Location: \"/docs/\" });\n res.end();\n return;\n }\n if (url.pathname.startsWith(\"/docs/\")) {\n const docPath = url.pathname.slice(5) || \"/index.html\"; // strip \"/docs\"\n serveStatic(docPath, join(uiDir, \"docs\"), req, res);\n return;\n }\n\n // Static files from ui/ directory\n serveStatic(url.pathname, uiDir, req, res);\n}\n\n// ---------------------------------------------------------------------------\n// API routes\n// ---------------------------------------------------------------------------\n\nfunction handleApiRoute(\n method: string,\n path: string,\n req: IncomingMessage,\n res: ServerResponse\n): void {\n // CORS — restrict to localhost origins only\n const origin = req.headers.origin || \"\";\n if (/^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/.test(origin)) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n }\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n switch (path) {\n case \"/api/session\":\n handleSessionRoute(method, res);\n break;\n\n case \"/api/modules\":\n handleModulesRoute(method, req, res);\n break;\n\n case \"/api/modules/reorder\":\n handleReorderRoute(req, res);\n break;\n\n case \"/api/modules/code\":\n handleCodeUpdateRoute(req, res);\n break;\n\n case \"/api/upload\":\n handleUploadRoute(res);\n break;\n\n case \"/api/upload-files\":\n if (method === \"POST\") handleFileUploadRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/field\":\n handleFieldRoute(req, res);\n break;\n\n case \"/api/import\":\n handleImportRoute(req, res);\n break;\n\n case \"/api/setup\":\n handleSetupInfoRoute(res);\n break;\n\n case \"/api/setup/create\":\n handleSetupCreateRoute(req, res);\n break;\n\n case \"/api/setup/fetch\":\n handleSetupFetchRoute(req, res);\n break;\n\n case \"/api/setup/open\":\n handleSetupOpenRoute(req, res);\n break;\n\n case \"/api/setup/resume\":\n handleSetupResumeRoute(req, res);\n break;\n\n case \"/api/setup/apikey\":\n handleSetupApiKeyRoute(req, res);\n break;\n\n case \"/api/setup/remote-themes\":\n if (method === \"GET\") handleSetupRemoteThemesRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n // Settings routes\n case \"/api/settings/status\":\n if (method === \"GET\") handleSettingsStatusRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/engine\":\n if (method === \"POST\") handleSettingsEngineRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/apikey\":\n if (method === \"POST\") handleSettingsApiKeyRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/install\":\n if (method === \"POST\") handleSettingsInstallRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-auth\":\n if (method === \"POST\") handleSettingsHsAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/gh-auth\":\n if (method === \"POST\") handleSettingsGhAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-switch\":\n if (method === \"POST\") handleSettingsHsSwitchRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/gh-logout\":\n if (method === \"POST\") handleSettingsGhLogoutRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/cli-auth\":\n if (method === \"POST\") handleSettingsCLIAuthRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/hs-mode\":\n if (method === \"POST\") handleSettingsHsModeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/cli-toggle\":\n if (method === \"POST\") handleSettingsCliToggleRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/save\":\n if (method === \"POST\") handleClaudeOAuthSaveRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/status\":\n if (method === \"GET\") handleClaudeOAuthStatusRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings/claude-oauth/logout\":\n if (method === \"POST\") handleClaudeOAuthLogoutRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/settings\":\n if (method === \"POST\") handleSettingsGenericRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/changelog\":\n if (method === \"GET\") {\n jsonResponse(res, 200, { changelog: getChangelog() });\n } else {\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n }\n break;\n\n case \"/api/themes\":\n handleThemesRoute(method, req, res);\n break;\n\n case \"/api/themes/switch\":\n if (method === \"POST\") handleThemeSwitchRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/themes/delete-local\":\n if (method === \"POST\") handleDeleteLocalThemeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/themes/rename\":\n if (method === \"POST\") handleRenameThemeRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/history\":\n if (method === \"GET\") handleHistoryRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/rollback\":\n if (method === \"POST\") handleRollbackRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n // Dashboard & template routes\n case \"/api/dashboard\":\n if (method === \"GET\") handleDashboardRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates\":\n handleTemplatesRoute(method, req, res);\n break;\n\n case \"/api/templates/activate\":\n if (method === \"POST\") handleTemplateActivateRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates/rename\":\n if (method === \"POST\") handleTemplateRenameRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/templates/clone\":\n if (method === \"POST\") handleTemplateCloneRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/module-library\":\n if (method === \"GET\") handleModuleLibraryRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/brand-assets\":\n handleBrandAssetsRoute(method, req, res);\n break;\n\n case \"/api/brand-assets/extract\":\n if (method === \"POST\") handleDesignExtractRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/brand-assets/import-reference\":\n if (method === \"POST\") handleReferenceImportRoute(req, res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n case \"/api/download-zip\":\n if (method === \"GET\") handleDownloadZipRoute(res);\n else jsonResponse(res, 405, { error: \"Method not allowed\" });\n break;\n\n default:\n // Prefix match for job polling: /api/settings/job/:id\n if (path.startsWith(\"/api/settings/job/\") && method === \"GET\") {\n handleSettingsJobRoute(path, res);\n }\n // Prefix match for template add-module: /api/templates/:id/add-module\n else if (path.match(/^\\/api\\/templates\\/[^/]+\\/add-module$/) && method === \"POST\") {\n handleAddModuleToTemplateRoute(path, req, res);\n } else {\n jsonResponse(res, 404, { error: \"Not found\" });\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// WebSocket handler\n// ---------------------------------------------------------------------------\n\nfunction handleWsConnection(ws: WebSocket): void {\n ws.on(\"message\", async (data) => {\n let msg: { type: string; [key: string]: unknown };\n try {\n msg = JSON.parse(data.toString());\n } catch {\n ws.send(JSON.stringify({ type: \"error\", message: \"Invalid JSON\" }));\n return;\n }\n\n switch (msg.type) {\n case \"chat\": {\n const userMessage = String(msg.message || \"\");\n if (!userMessage.trim()) return;\n\n addMessage(\"user\", userMessage);\n saveSession();\n\n const fileIds = Array.isArray(msg.fileIds) ? msg.fileIds as string[] : undefined;\n\n // Check if agentic mode should be used\n const agenticCheck = shouldUseAgenticMode();\n\n // Notify frontend if agentic mode needs first-run prompt\n if (agenticCheck.needsPrompt) {\n ws.send(JSON.stringify({ type: \"agentic_prompt\" }));\n // Don't block — fall through to single-call mode for now.\n // User can choose agentic mode from the prompt, which saves to config.\n }\n\n try {\n if (agenticCheck.useAgentic) {\n // --- Agentic pipeline mode ---\n // Collect pipeline events for metadata persistence\n const pipelineSteps: { step: string; label: string; decisions?: string[] }[] = [];\n const pipelineModules: { name: string; status: \"complete\" | \"failed\" }[] = [];\n\n const result = await handleAgenticGenerate(\n userMessage,\n (event) => {\n // Don't send moduleFiles over WebSocket (too large)\n if (event.type === \"module_progress\" && event.moduleFiles) {\n const { moduleFiles, ...wsEvent } = event;\n ws.send(JSON.stringify(wsEvent));\n } else {\n ws.send(JSON.stringify(event));\n }\n\n // Collect events for metadata + incremental preview\n if (event.type === \"agent_step\") {\n pipelineSteps.push({ step: event.step, label: event.label });\n } else if (event.type === \"agent_decision\") {\n const last = pipelineSteps[pipelineSteps.length - 1];\n if (last) {\n if (!last.decisions) last.decisions = [];\n last.decisions.push(event.decision);\n }\n } else if (event.type === \"design_system_ready\") {\n // Design system created — push CSS to session for themed placeholders\n updateModules({ sharedCss: event.sharedCss, sharedJs: event.sharedJs });\n } else if (event.type === \"blueprint_ready\") {\n // Module plan ready — set order for incremental preview placeholders\n updateModules({ sharedCss: event.sharedCss, sharedJs: event.sharedJs });\n reorderModules(event.moduleOrder);\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n } else if (event.type === \"module_progress\" && event.status === \"complete\" && event.moduleFiles) {\n // Push completed module to session immediately for incremental preview\n updateModules({ modules: [{\n moduleName: event.module,\n fieldsJson: event.moduleFiles.fieldsJson,\n metaJson: event.moduleFiles.metaJson,\n moduleHtml: event.moduleFiles.moduleHtml,\n moduleCss: event.moduleFiles.moduleCss,\n moduleJs: event.moduleFiles.moduleJs,\n }] });\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n pipelineModules.push({ name: event.module, status: \"complete\" });\n } else if (event.type === \"module_progress\" && event.status === \"failed\") {\n pipelineModules.push({ name: event.module, status: \"failed\" });\n }\n },\n fileIds,\n );\n\n // Apply result to session with pipeline metadata\n applyPipelineResult(result, {\n steps: pipelineSteps,\n modules: pipelineModules,\n stats: result.stats,\n });\n\n } else {\n // --- Single-call mode (existing behavior) ---\n setParseWarningCallback((warning) => {\n ws.send(JSON.stringify({ type: \"parse_warning\", message: warning }));\n });\n\n await handleGenerateStream(\n userMessage,\n (chunk) => {\n ws.send(JSON.stringify({ type: \"stream\", content: chunk }));\n },\n (status) => {\n ws.send(JSON.stringify({ type: \"stream_status\", content: status }));\n },\n fileIds\n );\n }\n\n // Write modules to disk and commit for version history\n const currentSession = getSession();\n if (currentSession) {\n writeModulesToDisk();\n const activeTpl = getActiveTemplate();\n let commitHash: string | null = null;\n if (activeTpl) {\n const filePaths = activeTpl.moduleOrder.map((n: string) => `modules/${n}.module`);\n if (activeTpl.templateFile) filePaths.push(activeTpl.templateFile);\n if (activeTpl.sharedCss) filePaths.push(`css/${currentSession.themeName}-theme.css`);\n if (activeTpl.sharedJs) filePaths.push(`js/${currentSession.themeName}-animations.js`);\n commitHash = commitTemplateState(currentSession.themePath, activeTpl.id, userMessage, filePaths);\n } else {\n commitHash = commitThemeState(currentSession.themePath, userMessage);\n }\n if (commitHash) {\n ws.send(JSON.stringify({ type: \"version_created\", hash: commitHash }));\n }\n }\n\n // After generation, send updated preview\n ws.send(JSON.stringify({ type: \"generation_complete\" }));\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n\n // Suggest brand asset extraction if none exist yet\n {\n const sess = getSession();\n if (sess && agenticCheck.useAgentic && !sess.brandAssets?.styleguide && !sess.brandAssets?.brandvoice && !sess.brandAssets?.themeContext) {\n ws.send(JSON.stringify({ type: \"suggest_brand_extraction\" }));\n }\n }\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n break;\n }\n\n case \"extract_brand_assets\": {\n const session = getSession();\n if (!session) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No active session\" }));\n break;\n }\n\n // Fire-and-forget — run extraction in background, never block the UI\n (async () => {\n try {\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n\n // Extract theme context from rendered preview HTML\n const { buildPreviewHtml } = await import(\"./preview.js\");\n const previewHtml = buildPreviewHtml();\n if (!previewHtml || previewHtml.length < 50) return;\n\n const { extractThemeContext } = await import(\"./agent/stages/context-extractor.js\");\n const themeContext = await extractThemeContext(\n previewHtml,\n session.brandAssets?.themeContext,\n engine,\n apiKey,\n model,\n );\n\n const { mkdirSync, writeFileSync } = await import(\"node:fs\");\n\n if (themeContext) {\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.themeContext = themeContext;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n if (!existsSync(assetDir)) mkdirSync(assetDir, { recursive: true });\n writeFileSync(join(assetDir, \"theme-context.md\"), themeContext);\n\n saveSession();\n ws.send(JSON.stringify({ type: \"brand_asset_extracted\", assetType: \"themeContext\" }));\n }\n\n // Also extract styleguide if missing\n if (!session.brandAssets?.styleguide) {\n try {\n const { extractDesignContext } = await import(\"../ai/design-extractor.js\");\n const styleguide = await extractDesignContext(session.themePath);\n if (styleguide) {\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.styleguide = styleguide;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n if (!existsSync(assetDir)) mkdirSync(assetDir, { recursive: true });\n writeFileSync(join(assetDir, \"styleguide.md\"), styleguide);\n\n saveSession();\n ws.send(JSON.stringify({ type: \"brand_asset_extracted\", assetType: \"styleguide\" }));\n }\n } catch { /* non-critical */ }\n }\n\n ws.send(JSON.stringify({ type: \"brand_extraction_complete\" }));\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"brand_extraction_error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n })();\n break;\n }\n\n case \"start_upload\": {\n const session = getSession();\n if (!session) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No active session\" }));\n break;\n }\n\n try {\n writeModulesToDisk();\n\n // Apply auto-fixes before uploading\n const fixes = applyAutoFixes(session.themePath);\n if (fixes.length > 0) {\n ws.send(JSON.stringify({ type: \"upload_status\", phase: \"autofix\", fixes }));\n }\n\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (uploadMode === \"api\") {\n // --- API mode: direct HTTP uploads ---\n const pak = getHubSpotPak();\n if (!pak) {\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: \"No HubSpot account configured. Open Settings → HubSpot to add one.\",\n errors: [{ file: \"\", message: \"No HubSpot account configured\", fixable: false }],\n }));\n break;\n }\n\n ws.send(JSON.stringify({ type: \"upload_started\", jobId: \"api-upload\" }));\n\n const result = await uploadTheme(pak, session.themePath, session.themeName, {\n onFileStart: (path) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: `Uploading ${path}\\n` }));\n },\n onFileComplete: (path) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: ` ✓ ${path}\\n` }));\n },\n onFileError: (path, err) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk: ` ✗ ${path}: ${err.message}\\n` }));\n },\n onProgress: (completed, total) => {\n ws.send(JSON.stringify({ type: \"upload_progress\", completed, total }));\n },\n });\n\n if (result.success) {\n const acct = getActiveHubSpotAccount();\n ws.send(JSON.stringify({\n type: \"upload_complete\",\n output: `Uploaded ${result.uploaded} files`,\n portalId: acct?.portalId || \"\",\n dataCenter: acct?.dataCenter || \"na1\",\n themeName: session.themeName,\n }));\n } else {\n const errors = parseApiErrors(result.errors);\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: result.errors.map((e) => `${e.file}: ${e.message}`).join(\"\\n\"),\n errors,\n }));\n }\n } else {\n // --- CLI mode: legacy hs cms upload subprocess ---\n const jobId = startStreamingJob(\n `hs cms upload \"${session.themePath}\" \"${session.themeName}\"`,\n \"Uploading to HubSpot\",\n { cwd: join(session.themePath, \"..\"), timeout: 180_000 }\n );\n\n ws.send(JSON.stringify({ type: \"upload_started\", jobId }));\n\n const chunkListener = (chunk: string) => {\n ws.send(JSON.stringify({ type: \"upload_output\", chunk }));\n };\n addJobListener(jobId, chunkListener);\n\n const pollInterval = setInterval(() => {\n const job = getJob(jobId);\n if (!job || job.status === \"running\") return;\n\n clearInterval(pollInterval);\n removeJobListener(jobId, chunkListener);\n\n if (job.status === \"completed\") {\n const auth = detectHubSpotAuth();\n const dc = auth.portalId ? detectDataCenter(auth.portalId) : \"na1\";\n ws.send(JSON.stringify({\n type: \"upload_complete\",\n output: job.output,\n portalId: auth.portalId || \"\",\n dataCenter: dc,\n themeName: session.themeName,\n }));\n } else {\n const errors = parseUploadErrors(job.output);\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: job.output,\n errors,\n exitCode: job.exitCode,\n }));\n }\n }, 500);\n }\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"error\",\n message: err instanceof Error ? err.message : String(err),\n }));\n }\n break;\n }\n\n case \"upload_fix_with_ai\": {\n const errorContext = String(msg.errorContext || \"\");\n if (!errorContext.trim()) {\n ws.send(JSON.stringify({ type: \"error\", message: \"No error context provided\" }));\n break;\n }\n\n const fixPrompt = `The HubSpot upload (\"hs cms upload\") failed. Below is the upload log output containing the errors.\n\nIMPORTANT: Be verbose in your response. For each error:\n1. State exactly which file has the problem and what the error is\n2. Explain WHY this error occurs (e.g. \"HubSpot doesn't support textarea field type\" or \"field name 'name' is reserved in HubSpot modules\")\n3. Describe the specific fix you're applying (e.g. \"Changing field type from textarea to text\" or \"Renaming field from 'name' to 'item_name'\")\n4. Apply the fix to the module files\n\nCRITICAL: After fixing the reported errors, scan ALL other module files in the theme for the same issues. For example, if you fix \"name\" → \"item_name\" in one module, check every other module's fields.json for the same problem. Fix all occurrences, not just the ones in the error log.\n\nAfter fixing all errors, summarize the changes you made.\n\nUpload log:\n${errorContext}`;\n addMessage(\"user\", fixPrompt);\n saveSession();\n\n ws.send(JSON.stringify({ type: \"upload_fix_started\" }));\n\n try {\n await handleGenerateStream(fixPrompt, (chunk) => {\n // Stream to both the chat panel and the upload panel\n ws.send(JSON.stringify({ type: \"stream\", content: chunk }));\n ws.send(JSON.stringify({ type: \"upload_fix_stream\", content: chunk }));\n });\n\n // Write fixes to disk and commit\n const fixSession = getSession();\n if (fixSession) {\n writeModulesToDisk();\n const fixHash = commitThemeState(fixSession.themePath, \"AI fix: upload errors\");\n if (fixHash) {\n ws.send(JSON.stringify({ type: \"version_created\", hash: fixHash }));\n }\n }\n\n ws.send(JSON.stringify({ type: \"upload_fix_complete\" }));\n ws.send(JSON.stringify({\n type: \"modules_updated\",\n modules: getOrderedModules().map((m) => m.moduleName),\n }));\n } catch (err) {\n ws.send(JSON.stringify({\n type: \"upload_failed\",\n output: err instanceof Error ? err.message : String(err),\n errors: [{ file: \"AI fix\", message: err instanceof Error ? err.message : String(err), fixable: false }],\n }));\n }\n break;\n }\n\n case \"ping\":\n ws.send(JSON.stringify({ type: \"pong\" }));\n break;\n\n default:\n ws.send(JSON.stringify({ type: \"error\", message: `Unknown type: ${msg.type}` }));\n }\n });\n\n // Send initial state\n const session = getSession();\n if (session) {\n const cfg = loadConfig();\n const engineLabels: Record<string, string> = {\n \"claude-code\": \"Claude Code\",\n \"anthropic-api\": \"Anthropic API\",\n \"claude-oauth\": \"Claude (OAuth)\",\n \"openai-api\": \"OpenAI API\",\n \"gemini-cli\": \"Gemini CLI\",\n \"gemini-api\": \"Gemini API\",\n \"codex-cli\": \"Codex CLI\",\n \"api\": \"Anthropic API\",\n };\n const activeTpl = getActiveTemplate();\n ws.send(JSON.stringify({\n type: \"init\",\n sessionId: session.id,\n themeName: session.themeName,\n modules: getOrderedModules().map((m) => m.moduleName),\n messageCount: session.messages.length,\n messages: session.messages,\n gitAvailable: isGitAvailable(),\n engine: cfg.aiEngine ? engineLabels[cfg.aiEngine] || cfg.aiEngine : \"\",\n // Multi-template context\n templateId: activeTpl?.id || null,\n pageType: activeTpl?.pageType || null,\n templates: (session.templates || []).map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n })),\n }));\n } else {\n ws.send(JSON.stringify({ type: \"needs_setup\" }));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Theme asset serving (uploaded images for preview)\n// ---------------------------------------------------------------------------\n\nfunction serveThemeAsset(filename: string, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"No session\");\n return;\n }\n const filePath = join(session.themePath, \"assets\", filename);\n if (!existsSync(filePath)) {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"Asset not found\");\n return;\n }\n const ext = extname(filePath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n const buffer = readFileSync(filePath);\n res.writeHead(200, {\n \"Content-Type\": contentType,\n \"Cache-Control\": \"no-cache\",\n });\n res.end(buffer);\n}\n\n// ---------------------------------------------------------------------------\n// Static file serving\n// ---------------------------------------------------------------------------\n\nconst staticCache = new Map<string, { buffer: Buffer; etag: string; contentType: string }>();\n\nfunction serveStatic(pathname: string, uiDir: string, req: IncomingMessage, res: ServerResponse): void {\n // Default to index.html\n let filePath = pathname === \"/\" ? \"/index.html\" : pathname;\n const fullPath = join(uiDir, filePath);\n\n if (!existsSync(fullPath)) {\n // SPA fallback — serve index.html for unknown routes\n const indexPath = join(uiDir, \"index.html\");\n if (existsSync(indexPath)) {\n const content = readFileSync(indexPath);\n res.writeHead(200, { \"Content-Type\": \"text/html\", \"Cache-Control\": \"no-cache\" });\n res.end(content);\n } else {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"Not found\");\n }\n return;\n }\n\n const ext = extname(fullPath);\n const contentType = MIME_TYPES[ext] || \"application/octet-stream\";\n const isHtml = ext === \".html\";\n\n try {\n // Always re-read from disk to pick up changes during development\n const buffer = readFileSync(fullPath);\n const etag = '\"' + createHash(\"md5\").update(buffer).digest(\"hex\").slice(0, 16) + '\"';\n\n // Check If-None-Match for 304\n const clientEtag = req.headers[\"if-none-match\"];\n if (clientEtag === etag) {\n res.writeHead(304);\n res.end();\n return;\n }\n\n res.writeHead(200, {\n \"Content-Type\": contentType,\n \"Cache-Control\": \"no-cache\",\n \"ETag\": etag,\n });\n res.end(buffer);\n } catch {\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal Server Error\");\n }\n}\n","/**\n * Background process manager for long-running CLI operations\n * (tool installation, OAuth flows, etc.)\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\n\nexport interface ProcessJob {\n id: string;\n command: string;\n description: string;\n status: \"running\" | \"completed\" | \"failed\";\n output: string;\n exitCode: number | null;\n startedAt: number;\n completedAt: number | null;\n}\n\nconst jobs = new Map<string, ProcessJob>();\n\nfunction _attachJobHandlers(child: ChildProcess, job: ProcessJob, timeout?: number): void {\n child.stdout?.on(\"data\", (d: Buffer) => {\n job.output += d.toString();\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n job.output += d.toString();\n });\n\n child.on(\"close\", (code) => {\n job.status = code === 0 ? \"completed\" : \"failed\";\n job.exitCode = code;\n job.completedAt = Date.now();\n });\n\n child.on(\"error\", (err) => {\n job.status = \"failed\";\n job.output += `\\nProcess error: ${err.message}`;\n job.completedAt = Date.now();\n });\n\n const t = timeout || 300_000;\n setTimeout(() => {\n if (job.status === \"running\") {\n child.kill();\n job.status = \"failed\";\n job.output += \"\\nProcess timed out\";\n job.completedAt = Date.now();\n }\n }, t);\n}\n\n/**\n * Start a job safely using an argument array (no shell interpolation).\n * Preferred over the string overload to prevent command injection.\n */\nexport function startJobSafe(\n cmd: string,\n args: string[],\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number; stdin?: string }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: ProcessJob = {\n id,\n command: `${cmd} ${args.join(\" \")}`,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n };\n\n jobs.set(id, job);\n\n const child: ChildProcess = spawn(cmd, args, {\n cwd: opts?.cwd,\n stdio: [opts?.stdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n // Windows needs shell to resolve .cmd/.bat from PATH; args array prevents injection\n shell: process.platform === \"win32\",\n });\n\n if (opts?.stdin && child.stdin) {\n child.stdin.write(opts.stdin);\n child.stdin.end();\n }\n\n _attachJobHandlers(child, job, opts?.timeout);\n return id;\n}\n\nexport function startJob(\n command: string,\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: ProcessJob = {\n id,\n command,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n };\n\n jobs.set(id, job);\n\n const parts = command.split(\" \");\n const child: ChildProcess = spawn(parts[0], parts.slice(1), {\n cwd: opts?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n shell: true,\n });\n\n _attachJobHandlers(child, job, opts?.timeout);\n return id;\n}\n\nexport function getJob(id: string): ProcessJob | undefined {\n return jobs.get(id);\n}\n\nexport function cleanupOldJobs(): void {\n const cutoff = Date.now() - 30 * 60 * 1000;\n for (const [id, job] of jobs) {\n if (job.completedAt && job.completedAt < cutoff) {\n jobs.delete(id);\n }\n }\n}\n\n// Clean up periodically\nsetInterval(cleanupOldJobs, 10 * 60 * 1000);\n\n// ---------------------------------------------------------------------------\n// Streaming jobs — same as regular jobs but also emit output chunks to listeners\n// ---------------------------------------------------------------------------\n\nexport interface StreamingJob extends ProcessJob {\n listeners: Set<(chunk: string) => void>;\n}\n\nexport function startStreamingJob(\n command: string,\n description: string,\n opts?: { cwd?: string; env?: Record<string, string>; timeout?: number }\n): string {\n const id = `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;\n\n const job: StreamingJob = {\n id,\n command,\n description,\n status: \"running\",\n output: \"\",\n exitCode: null,\n startedAt: Date.now(),\n completedAt: null,\n listeners: new Set(),\n };\n\n jobs.set(id, job);\n\n const parts = command.split(\" \");\n const child: ChildProcess = spawn(parts[0], parts.slice(1), {\n cwd: opts?.cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...opts?.env },\n shell: true,\n });\n\n const emitChunk = (chunk: string) => {\n for (const listener of job.listeners) {\n try { listener(chunk); } catch { /* listener error — ignore */ }\n }\n };\n\n child.stdout?.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n job.output += chunk;\n emitChunk(chunk);\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n const chunk = d.toString();\n job.output += chunk;\n emitChunk(chunk);\n });\n\n child.on(\"close\", (code) => {\n job.status = code === 0 ? \"completed\" : \"failed\";\n job.exitCode = code;\n job.completedAt = Date.now();\n job.listeners.clear();\n });\n\n child.on(\"error\", (err) => {\n job.status = \"failed\";\n job.output += `\\nProcess error: ${err.message}`;\n job.completedAt = Date.now();\n job.listeners.clear();\n });\n\n // Timeout safety net\n const timeout = opts?.timeout || 300_000;\n setTimeout(() => {\n if (job.status === \"running\") {\n child.kill();\n job.status = \"failed\";\n job.output += \"\\nProcess timed out\";\n job.completedAt = Date.now();\n job.listeners.clear();\n }\n }, timeout);\n\n return id;\n}\n\nexport function addJobListener(jobId: string, listener: (chunk: string) => void): void {\n const job = jobs.get(jobId);\n if (!job || !(\"listeners\" in job)) return;\n\n const streamingJob = job as StreamingJob;\n\n // Send buffered output first\n if (streamingJob.output) {\n try { listener(streamingJob.output); } catch { /* ignore */ }\n }\n\n streamingJob.listeners.add(listener);\n}\n\nexport function removeJobListener(jobId: string, listener: (chunk: string) => void): void {\n const job = jobs.get(jobId);\n if (!job || !(\"listeners\" in job)) return;\n\n (job as StreamingJob).listeners.delete(listener);\n}\n","/**\n * Setup routes — onboarding flow in the browser.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readdirSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { execFileSync, type ExecFileSyncOptions } from \"node:child_process\";\n\n// Windows needs shell to resolve .cmd/.bat from PATH; args array prevents injection\nconst _shellOpt: ExecFileSyncOptions = process.platform === \"win32\" ? { shell: true } : {};\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { loadConfig, getHubSpotPak } from \"../../utils/config.js\";\nimport { createThemeScaffold } from \"../../hubspot/theme-scaffold.js\";\nimport { fetchTheme } from \"../../hubspot/fetcher.js\";\nimport { getMetadata, listRootFolders } from \"../../hubspot/api.js\";\nimport {\n getSession,\n createSession,\n scanThemeFromDisk,\n saveSession,\n loadSession,\n listSessions,\n} from \"../session.js\";\nimport { isGenerating } from \"../ai-handler.js\";\nimport { detectEnvironment } from \"../../utils/detect.js\";\nimport { saveConfig } from \"../../utils/config.js\";\nimport { ensureDir } from \"../../utils/fs.js\";\n\nexport const WORKSPACE_DIR = join(homedir(), \"vibespot-themes\");\n\nlet _themeListCache: { data: Array<{ name: string; moduleCount: number }>; ts: number } | null = null;\nconst THEME_LIST_TTL = 5000;\n\nexport function getLocalThemes(): Array<{ name: string; moduleCount: number }> {\n if (_themeListCache && Date.now() - _themeListCache.ts < THEME_LIST_TTL) return _themeListCache.data;\n const themes: Array<{ name: string; moduleCount: number }> = [];\n if (existsSync(WORKSPACE_DIR)) {\n try {\n for (const entry of readdirSync(WORKSPACE_DIR, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const themeJson = join(WORKSPACE_DIR, entry.name, \"theme.json\");\n if (existsSync(themeJson)) {\n let moduleCount = 0;\n const modulesDir = join(WORKSPACE_DIR, entry.name, \"modules\");\n if (existsSync(modulesDir)) {\n try {\n moduleCount = readdirSync(modulesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory()).length;\n } catch { /* ignore */ }\n }\n themes.push({ name: entry.name, moduleCount });\n }\n }\n }\n } catch { /* ignore */ }\n }\n _themeListCache = { data: themes, ts: Date.now() };\n return themes;\n}\n\nexport function handleSetupInfoRoute(res: ServerResponse): void {\n const session = getSession();\n const env = detectEnvironment();\n\n let hsInstalled = false;\n try {\n execFileSync(\"hs\", [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", ..._shellOpt });\n hsInstalled = true;\n } catch { /* not installed */ }\n\n const sessions = listSessions()\n .sort((a, b) => b.updatedAt - a.updatedAt)\n .slice(0, 10);\n\n const localThemes = getLocalThemes();\n\n jsonResponse(res, 200, {\n hasActiveSession: !!session,\n activeSession: session ? {\n id: session.id,\n themeName: session.themeName,\n moduleCount: session.modules.length,\n } : null,\n hsInstalled,\n aiAvailable: env.availableEngines.length > 0,\n availableEngines: env.availableEngines,\n activeEngine: env.activeEngine,\n sessions,\n localThemes,\n });\n}\n\nexport function handleSetupCreateRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { name } = JSON.parse(body);\n if (!name || typeof name !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n const themeName = name\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const themePath = join(WORKSPACE_DIR, themeName);\n ensureDir(WORKSPACE_DIR);\n\n if (existsSync(themePath)) {\n rmSync(themePath, { recursive: true, force: true });\n }\n\n // Create theme scaffold locally (no CLI dependency)\n createThemeScaffold(themePath, themeName);\n\n createSession(themePath, themeName);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName,\n themePath,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupFetchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { name: rawName } = JSON.parse(body);\n if (!rawName || typeof rawName !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n // Strip leading/trailing slashes (HubSpot DM \"Copy path\" gives \"/theme-name\")\n const name = rawName.replace(/^\\/+|\\/+$/g, \"\");\n if (!name) {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n\n const pak = getHubSpotPak();\n const config = loadConfig();\n\n // Sanitize for local directory name (marketplace paths like @marketplace/Theme → _marketplace_Theme)\n const safeDirName = name.includes(\"/\") || name.includes(\"@\")\n ? name.replace(/[@/]/g, \"_\").replace(/_+/g, \"_\").replace(/^_|_$/g, \"\")\n : name;\n const themePath = join(WORKSPACE_DIR, safeDirName);\n ensureDir(WORKSPACE_DIR);\n\n if (config.hubspotUploadMode === \"cli\" || !pak) {\n // CLI fallback\n execFileSync(\"hs\", [\"cms\", \"fetch\", name, themePath], {\n encoding: \"utf-8\",\n stdio: \"pipe\",\n ..._shellOpt,\n });\n\n createSession(themePath, safeDirName);\n scanThemeFromDisk(themePath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: safeDirName,\n themePath,\n moduleCount: getSession()?.modules.length || 0,\n });\n } else {\n // API mode (default) — use original name for API, safe name for local\n fetchTheme(pak, name, themePath)\n .then(() => {\n createSession(themePath, safeDirName);\n scanThemeFromDisk(themePath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: safeDirName,\n themePath,\n moduleCount: getSession()?.modules.length || 0,\n });\n })\n .catch((err) => {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n } catch (err) {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n}\n\nexport function handleSetupOpenRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { path: themePath } = JSON.parse(body);\n if (!themePath || typeof themePath !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme path is required\" });\n return;\n }\n\n let fullPath = themePath;\n if (!existsSync(fullPath)) {\n fullPath = join(WORKSPACE_DIR, themePath);\n }\n if (!existsSync(fullPath)) {\n jsonResponse(res, 400, { error: `Theme folder not found: ${themePath}` });\n return;\n }\n\n const themeName = basename(fullPath);\n createSession(fullPath, themeName);\n scanThemeFromDisk(fullPath);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n themeName,\n themePath: fullPath,\n moduleCount: getSession()?.modules.length || 0,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupResumeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n if (isGenerating()) { jsonResponse(res, 409, { error: \"Cannot switch projects while AI is generating.\", generating: true }); return; }\n const { sessionId } = JSON.parse(body);\n if (!sessionId || typeof sessionId !== \"string\") {\n jsonResponse(res, 400, { error: \"Session ID is required\" });\n return;\n }\n\n const session = loadSession(sessionId);\n if (!session) {\n jsonResponse(res, 404, { error: \"Session not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: session.themeName,\n themePath: session.themePath,\n moduleCount: session.modules.length,\n messageCount: session.messages.length,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSetupApiKeyRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { apiKey } = JSON.parse(body);\n if (!apiKey || typeof apiKey !== \"string\") {\n jsonResponse(res, 400, { error: \"API key is required\" });\n return;\n }\n\n saveConfig({ anthropicApiKey: apiKey });\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n/**\n * List themes available on HubSpot Design Manager.\n * Returns folders at the root level that look like themes (contain theme.json).\n */\nexport function handleSetupRemoteThemesRoute(res: ServerResponse): void {\n const pak = getHubSpotPak();\n if (!pak) {\n jsonResponse(res, 200, { themes: [], error: \"No HubSpot account connected\" });\n return;\n }\n\n (async () => {\n const folders = await listRootFolders(pak);\n\n if (folders.length === 0) {\n jsonResponse(res, 200, { themes: [] });\n return;\n }\n\n const themes: Array<{ name: string; path: string }> = [];\n\n // Check which folders have a theme.json (in parallel)\n const checks = folders.map(async (folder) => {\n const folderPath = folder.path || folder.name;\n try {\n const tjMeta = await getMetadata(pak, `${folderPath}/theme.json`);\n if (tjMeta && !tjMeta.folder) {\n themes.push({ name: folder.name, path: folderPath });\n }\n } catch { /* not a theme */ }\n });\n\n await Promise.all(checks);\n themes.sort((a, b) => a.name.localeCompare(b.name));\n\n const localThemes = getLocalThemes();\n const localNames = new Set(localThemes.map((t) => t.name));\n\n jsonResponse(res, 200, {\n themes: themes.map((t) => ({\n ...t,\n existsLocally: localNames.has(t.name),\n })),\n });\n })().catch((err) => {\n jsonResponse(res, 200, {\n themes: [],\n error: err instanceof Error ? err.message : String(err),\n });\n });\n}\n","/**\n * Settings routes — environment management, API keys, tool install, auth.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readFileSync, appendFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { loadConfig, saveConfig, getApiKeyForEngine, addHubSpotAccount, removeHubSpotAccount, setActiveHubSpotAccount, setCliToolEnabled, type AIEngineType, type HubSpotAccountConfig } from \"../../utils/config.js\";\nimport { listSessions } from \"../session.js\";\nimport { getLocalThemes } from \"./setup.js\";\nimport { detectEnvironment, detectHubSpotCLI, detectHubSpotAuth, detectGitHubCLI, detectGitHubAuth } from \"../../utils/detect.js\";\nimport { validatePak } from \"../../hubspot/api.js\";\nimport { getVersion } from \"../../utils/fs.js\";\nimport { startJob, startJobSafe, getJob } from \"../process-manager.js\";\n\n// ---------------------------------------------------------------------------\n// Live model catalog — fetched from provider APIs, cached 10 minutes\n// ---------------------------------------------------------------------------\n\ntype ModelEntry = { id: string; label: string };\nconst modelCache: { data: Record<string, ModelEntry[]>; ts: number } = { data: {}, ts: 0 };\nconst MODEL_CACHE_TTL = 10 * 60 * 1000;\n\nconst STATIC_MODELS: Record<string, ModelEntry[]> = {\n \"claude-code\": [\n { id: \"sonnet\", label: \"Claude Sonnet (default)\" },\n { id: \"opus\", label: \"Claude Opus\" },\n { id: \"haiku\", label: \"Claude Haiku\" },\n ],\n \"codex-cli\": [\n { id: \"o4-mini\", label: \"o4 Mini (default)\" },\n { id: \"o3\", label: \"o3\" },\n { id: \"gpt-4o\", label: \"GPT-4o\" },\n ],\n};\n\nasync function fetchAnthropicModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\"https://api.anthropic.com/v1/models\", {\n headers: { \"x-api-key\": apiKey, \"anthropic-version\": \"2023-06-01\" },\n });\n if (!resp.ok) return [];\n const data = await resp.json() as { data: { id: string; display_name: string }[] };\n return data.data\n .filter((m) => !m.id.startsWith(\"claude-3-\") && !m.id.startsWith(\"claude-2\"))\n .map((m) => ({ id: m.id, label: m.display_name }));\n}\n\nasync function fetchOpenAIModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (!resp.ok) return [];\n const data = await resp.json() as { data: { id: string }[] };\n const keep = /^(gpt-4o|gpt-4o-mini|o[1-4](-mini)?|o[1-4]-pro)$/;\n return data.data\n .filter((m) => keep.test(m.id))\n .sort((a, b) => a.id.localeCompare(b.id))\n .map((m) => ({ id: m.id, label: m.id }));\n}\n\nasync function fetchGeminiModels(apiKey: string): Promise<ModelEntry[]> {\n const resp = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,\n );\n if (!resp.ok) return [];\n const data = await resp.json() as { models: { name: string; displayName: string }[] };\n return data.models\n .filter((m) => m.name.includes(\"gemini-2\"))\n .map((m) => ({ id: m.name.replace(\"models/\", \"\"), label: m.displayName }));\n}\n\nasync function getModelCatalog(): Promise<Record<string, ModelEntry[]>> {\n if (Date.now() - modelCache.ts < MODEL_CACHE_TTL && Object.keys(modelCache.data).length > 0) {\n return modelCache.data;\n }\n\n const config = loadConfig();\n const catalog: Record<string, ModelEntry[]> = { ...STATIC_MODELS };\n\n const jobs: Promise<void>[] = [];\n\n const anthropicKey = getApiKeyForEngine(\"anthropic-api\", config);\n if (anthropicKey) {\n jobs.push(\n fetchAnthropicModels(anthropicKey)\n .then((models) => {\n if (models.length) {\n catalog[\"anthropic-api\"] = models;\n catalog[\"claude-oauth\"] = models; // same model list\n }\n })\n .catch(() => {}),\n );\n }\n\n const openaiKey = getApiKeyForEngine(\"openai-api\", config);\n if (openaiKey) {\n jobs.push(\n fetchOpenAIModels(openaiKey)\n .then((models) => { if (models.length) catalog[\"openai-api\"] = models; })\n .catch(() => {}),\n );\n }\n\n const geminiKey = getApiKeyForEngine(\"gemini-api\", config);\n if (geminiKey) {\n jobs.push(\n fetchGeminiModels(geminiKey)\n .then((models) => {\n if (models.length) {\n catalog[\"gemini-api\"] = models;\n catalog[\"gemini-cli\"] = models;\n }\n })\n .catch(() => {}),\n );\n }\n\n await Promise.all(jobs);\n\n modelCache.data = catalog;\n modelCache.ts = Date.now();\n return catalog;\n}\n\nexport function handleSettingsStatusRoute(res: ServerResponse): void {\n const env = detectEnvironment();\n const config = loadConfig();\n\n const configPayload = {\n aiEngine: config.aiEngine || null,\n claudeCodeModel: config.claudeCodeModel || null,\n anthropicApiModel: config.anthropicApiModel || null,\n openaiApiModel: config.openaiApiModel || null,\n hubspotUploadMode: config.hubspotUploadMode || \"api\",\n hubspotAccounts: (config.hubspotAccounts || []).map((a: HubSpotAccountConfig) => ({\n portalId: a.portalId,\n portalName: a.portalName,\n dataCenter: a.dataCenter,\n })),\n activeHubSpotAccount: config.activeHubSpotAccount || null,\n enabledCLITools: config.enabledCLITools || [],\n agenticMode: config.agenticMode,\n agenticConcurrency: config.agenticConcurrency,\n };\n\n const sessionCount = listSessions().length;\n const localThemeCount = getLocalThemes().length;\n\n const version = getVersion();\n\n getModelCatalog().then((models) => {\n jsonResponse(res, 200, {\n version,\n environment: env,\n config: configPayload,\n models,\n sessionCount,\n localThemeCount,\n });\n }).catch(() => {\n jsonResponse(res, 200, {\n version,\n environment: env,\n config: configPayload,\n models: STATIC_MODELS,\n sessionCount,\n localThemeCount,\n });\n });\n}\n\nexport function handleSettingsEngineRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { engine, model } = JSON.parse(body);\n\n const validEngines: AIEngineType[] = [\n \"claude-code\", \"anthropic-api\", \"claude-oauth\", \"openai-api\", \"gemini-cli\", \"gemini-api\", \"codex-cli\",\n ];\n if (!validEngines.includes(engine)) {\n jsonResponse(res, 400, { error: `Invalid engine: ${engine}` });\n return;\n }\n\n const configUpdate: Record<string, unknown> = { aiEngine: engine };\n if (model) {\n switch (engine) {\n case \"claude-code\":\n configUpdate.claudeCodeModel = model;\n break;\n case \"anthropic-api\":\n case \"claude-oauth\":\n configUpdate.anthropicApiModel = model;\n break;\n case \"openai-api\":\n configUpdate.openaiApiModel = model;\n break;\n }\n }\n\n saveConfig(configUpdate as any);\n jsonResponse(res, 200, { ok: true, engine });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsApiKeyRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { provider, apiKey } = JSON.parse(body);\n\n if (!provider || typeof provider !== \"string\") {\n jsonResponse(res, 400, { error: \"provider is required\" });\n return;\n }\n\n if (!apiKey) {\n const configUpdate: Record<string, unknown> = {};\n switch (provider) {\n case \"anthropic\": configUpdate.anthropicApiKey = \"\"; break;\n case \"openai\": configUpdate.openaiApiKey = \"\"; break;\n case \"gemini\": configUpdate.geminiApiKey = \"\"; break;\n default:\n jsonResponse(res, 400, { error: `Unknown provider: ${provider}` });\n return;\n }\n saveConfig(configUpdate as any);\n jsonResponse(res, 200, { ok: true, provider, deleted: true });\n return;\n }\n\n const configUpdate: Record<string, unknown> = {};\n switch (provider) {\n case \"anthropic\": configUpdate.anthropicApiKey = apiKey; break;\n case \"openai\": configUpdate.openaiApiKey = apiKey; break;\n case \"gemini\": configUpdate.geminiApiKey = apiKey; break;\n default:\n jsonResponse(res, 400, { error: `Unknown provider: ${provider}` });\n return;\n }\n\n saveConfig(configUpdate as any);\n\n let autoSelectedEngine: string | null = null;\n const currentConfig = loadConfig();\n if (!currentConfig.aiEngine) {\n const engineMap: Record<string, string> = {\n anthropic: \"anthropic-api\",\n openai: \"openai-api\",\n gemini: \"gemini-api\",\n };\n const engine = engineMap[provider];\n if (engine) {\n saveConfig({ aiEngine: engine } as any);\n autoSelectedEngine = engine;\n }\n }\n\n jsonResponse(res, 200, { ok: true, provider, autoSelectedEngine });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsInstallRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { tool } = JSON.parse(body);\n\n const installCommands: Record<string, { cmd: string; desc: string }> = {\n hubspot: { cmd: \"npm install -g @hubspot/cli\", desc: \"Installing HubSpot CLI\" },\n claude: { cmd: \"npm install -g @anthropic-ai/claude-code\", desc: \"Installing Claude Code\" },\n gemini: { cmd: \"npm install -g @google/gemini-cli\", desc: \"Installing Gemini CLI\" },\n codex: { cmd: process.platform === \"darwin\" ? \"brew install --cask codex\" : \"npm install -g @openai/codex\", desc: \"Installing OpenAI Codex\" },\n gh: { cmd: process.platform === \"darwin\" ? \"brew install gh\" : \"npm install -g @cli/gh\", desc: \"Installing GitHub CLI\" },\n };\n\n const config = installCommands[tool];\n if (!config) {\n jsonResponse(res, 400, { error: `Unknown tool: ${tool}. Valid: ${Object.keys(installCommands).join(\", \")}` });\n return;\n }\n\n const jobId = startJob(config.cmd, config.desc, { timeout: 120_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsHsAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const parsed = JSON.parse(body || \"{}\");\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (parsed.personalAccessKey) {\n if (uploadMode === \"api\") {\n // API mode: validate PAK directly via HTTP, store in config\n validatePak(parsed.personalAccessKey).then((info) => {\n addHubSpotAccount(parsed.personalAccessKey, info.portalId, info.portalName, info.dataCenter);\n jsonResponse(res, 200, {\n ok: true,\n portalName: info.portalName,\n portalId: info.portalId,\n dataCenter: info.dataCenter,\n });\n }).catch((err) => {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n });\n return;\n } else {\n // CLI mode: use hs auth command\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\", needsInstall: true });\n return;\n }\n const jobId = startJobSafe(\n \"hs\", [\"auth\", `--pak=${parsed.personalAccessKey}`],\n \"Authenticating with HubSpot\",\n { timeout: 30_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n }\n\n // No key provided — check existing auth\n if (uploadMode === \"api\") {\n const accounts = config.hubspotAccounts || [];\n if (accounts.length > 0 && !parsed.force) {\n const active = accounts.find((a) => a.portalId === config.activeHubSpotAccount) || accounts[0];\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n portalName: active.portalName,\n portalId: active.portalId,\n });\n return;\n }\n } else {\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\", needsInstall: true });\n return;\n }\n const auth = detectHubSpotAuth();\n if (auth.authenticated && !parsed.force) {\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n portalName: auth.portalName,\n portalId: auth.portalId,\n });\n return;\n }\n }\n\n jsonResponse(res, 200, {\n needsKey: true,\n instructions: \"Create a personal access key in HubSpot\",\n url: \"https://app.hubspot.com/portal-recommend/l?slug=personal-access-key\",\n steps: [\n \"Click the link above to open HubSpot\",\n \"Select your account\",\n \"Create a Personal Access Key with CMS permissions\",\n \"Copy the key and paste it below\",\n ],\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsGhAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const parsed = JSON.parse(body || \"{}\");\n\n const gh = detectGitHubCLI();\n if (!gh.found) {\n jsonResponse(res, 400, { error: \"GitHub CLI not installed\", needsInstall: true });\n return;\n }\n\n const auth = detectGitHubAuth();\n if (auth.authenticated && !parsed.force) {\n jsonResponse(res, 200, {\n ok: true,\n alreadyAuthenticated: true,\n username: auth.username,\n });\n return;\n }\n\n if (parsed.token) {\n const jobId = startJobSafe(\n \"gh\", [\"auth\", \"login\", \"--with-token\"],\n \"Authenticating with GitHub\",\n { timeout: 30_000, stdin: parsed.token }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n\n const jobId = startJob(\n \"gh auth login --web --git-protocol https\",\n \"GitHub authentication (check your browser)\",\n { timeout: 300_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, browserAuthRequired: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsHsSwitchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { portalId, action } = JSON.parse(body);\n const config = loadConfig();\n const uploadMode = config.hubspotUploadMode || \"api\";\n\n if (uploadMode === \"api\") {\n // API mode: synchronous config updates (no subprocess)\n if (action === \"remove\" && portalId) {\n removeHubSpotAccount(portalId);\n jsonResponse(res, 200, { ok: true });\n return;\n }\n if (portalId) {\n setActiveHubSpotAccount(portalId);\n jsonResponse(res, 200, { ok: true });\n return;\n }\n } else {\n // CLI mode: use hs accounts commands\n const hs = detectHubSpotCLI();\n if (!hs.found) {\n jsonResponse(res, 400, { error: \"HubSpot CLI not installed\" });\n return;\n }\n // Validate portalId is numeric to prevent injection\n const safePortalId = String(portalId).replace(/[^0-9]/g, \"\");\n if (!safePortalId) {\n jsonResponse(res, 400, { error: \"Invalid portalId\" });\n return;\n }\n if (action === \"remove\") {\n const jobId = startJobSafe(\"hs\", [\"accounts\", \"remove\", safePortalId], `Removing HubSpot account ${safePortalId}`, { timeout: 15_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n if (safePortalId) {\n const jobId = startJobSafe(\"hs\", [\"accounts\", \"use\", safePortalId], `Switching to HubSpot account ${safePortalId}`, { timeout: 15_000 });\n jsonResponse(res, 200, { ok: true, jobId });\n return;\n }\n }\n\n jsonResponse(res, 400, { error: \"portalId required\" });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleSettingsGhLogoutRoute(res: ServerResponse): void {\n const jobId = startJob(\n \"gh auth logout --hostname github.com -y\",\n \"Logging out of GitHub\",\n { timeout: 15_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId });\n}\n\nexport function handleSettingsCLIAuthRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { cli, apiKey } = JSON.parse(body || \"{}\");\n\n switch (cli) {\n case \"claude\": {\n const jobId = startJob(\n \"CLAUDECODE= claude --print -p 'reply OK'\",\n \"Authenticating Claude Code (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"If Claude Code opens a browser window, complete the sign-in there.\" });\n break;\n }\n case \"gemini\": {\n const jobId = startJob(\n \"gemini -p 'reply OK'\",\n \"Authenticating Gemini CLI (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"If Gemini opens a browser window, complete the sign-in there.\" });\n break;\n }\n case \"codex\": {\n if (apiKey && apiKey.trim()) {\n const key = apiKey.trim();\n process.env.OPENAI_API_KEY = key;\n saveConfig({ openaiApiKey: key } as any);\n if (process.platform !== \"win32\") {\n // Sanitize key to prevent shell profile injection — only allow\n // alphanumeric chars, dashes, underscores, and dots (valid API key chars)\n const safeKey = /^[A-Za-z0-9_\\-.:]+$/.test(key) ? key : \"\";\n if (safeKey) {\n const profileLine = `export OPENAI_API_KEY=\"${safeKey}\"`;\n const shellProfile = process.env.SHELL?.includes(\"zsh\")\n ? join(homedir(), \".zshrc\")\n : join(homedir(), \".bashrc\");\n try {\n const existing = existsSync(shellProfile)\n ? readFileSync(shellProfile, \"utf-8\")\n : \"\";\n if (!existing.includes(\"OPENAI_API_KEY\")) {\n appendFileSync(shellProfile, `\\n# Added by vibeSpot\\n${profileLine}\\n`);\n }\n } catch { /* ignore profile write errors */ }\n }\n }\n jsonResponse(res, 200, { ok: true, message: \"API key saved\" });\n } else {\n const jobId = startJob(\n \"codex login\",\n \"Authenticating Codex CLI (check your browser if prompted)\",\n { timeout: 120_000 }\n );\n jsonResponse(res, 200, { ok: true, jobId, hint: \"Complete the sign-in in your browser.\" });\n }\n break;\n }\n default:\n jsonResponse(res, 400, { error: `Unknown CLI: ${cli}` });\n }\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// HubSpot upload mode toggle\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsHsModeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { mode } = JSON.parse(body);\n if (mode !== \"api\" && mode !== \"cli\") {\n jsonResponse(res, 400, { error: `Invalid mode: ${mode}. Must be \"api\" or \"cli\".` });\n return;\n }\n saveConfig({ hubspotUploadMode: mode } as any);\n jsonResponse(res, 200, { ok: true, mode });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// CLI tool toggle\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsCliToggleRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { toolId, enabled } = JSON.parse(body);\n if (!toolId || typeof enabled !== \"boolean\") {\n jsonResponse(res, 400, { error: \"toolId (string) and enabled (boolean) required\" });\n return;\n }\n setCliToolEnabled(toolId, enabled);\n jsonResponse(res, 200, { ok: true, toolId, enabled });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Generic settings save (used for agentic mode, etc.)\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsGenericRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const data = JSON.parse(body);\n const allowedKeys = [\"agenticMode\", \"agenticConcurrency\"];\n const update: Record<string, unknown> = {};\n\n for (const key of allowedKeys) {\n if (key in data) update[key] = data[key];\n }\n\n if (Object.keys(update).length === 0) {\n jsonResponse(res, 400, { error: \"No valid settings fields provided\" });\n return;\n }\n\n saveConfig(update as import(\"../../utils/config.js\").VibeSpotConfig);\n jsonResponse(res, 200, { ok: true, updated: Object.keys(update) });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Job polling\n// ---------------------------------------------------------------------------\n\nexport function handleSettingsJobRoute(path: string, res: ServerResponse): void {\n const jobId = path.replace(\"/api/settings/job/\", \"\");\n if (!jobId) {\n jsonResponse(res, 400, { error: \"Job ID required\" });\n return;\n }\n\n const job = getJob(jobId);\n if (!job) {\n jsonResponse(res, 404, { error: \"Job not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n id: job.id,\n status: job.status,\n description: job.description,\n output: job.output,\n exitCode: job.exitCode,\n startedAt: job.startedAt,\n completedAt: job.completedAt,\n });\n}\n","/**\n * Claude OAuth routes — save/manage OAuth tokens for Claude Pro/Max access.\n * Users obtain tokens via `claude setup-token` (Claude Code CLI).\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { saveConfig, loadConfig } from \"../../utils/config.js\";\nimport {\n saveInitialToken,\n hasValidOAuthToken,\n getOAuthTokenInfo,\n clearOAuthTokens,\n} from \"../../utils/claude-oauth.js\";\n\n/**\n * POST /api/settings/claude-oauth/save\n * Save an OAuth token (from `claude setup-token` or manual paste).\n */\nexport function handleClaudeOAuthSaveRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { access_token, refresh_token } = JSON.parse(body);\n if (!access_token || typeof access_token !== \"string\") {\n jsonResponse(res, 400, { error: \"access_token is required\" });\n return;\n }\n\n saveInitialToken(access_token.trim(), (refresh_token || \"\").trim());\n\n // Auto-select claude-oauth as the active engine\n const config = loadConfig();\n if (!config.aiEngine || config.aiEngine !== \"claude-oauth\") {\n saveConfig({ aiEngine: \"claude-oauth\" } as any);\n }\n\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\n/**\n * GET /api/settings/claude-oauth/status\n * Return current OAuth authentication status.\n */\nexport function handleClaudeOAuthStatusRoute(_req: IncomingMessage, res: ServerResponse): void {\n const authenticated = hasValidOAuthToken();\n const info = getOAuthTokenInfo();\n jsonResponse(res, 200, {\n authenticated,\n expiresAt: info?.expiresAt || null,\n });\n}\n\n/**\n * POST /api/settings/claude-oauth/logout\n * Clear stored OAuth tokens and reset engine if needed.\n */\nexport function handleClaudeOAuthLogoutRoute(_req: IncomingMessage, res: ServerResponse): void {\n try {\n clearOAuthTokens();\n\n const config = loadConfig();\n if (config.aiEngine === \"claude-oauth\") {\n saveConfig({ aiEngine: undefined } as any);\n }\n\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n","/**\n * Theme routes — list, switch, delete, rename themes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport {\n getSession,\n listSessions,\n loadSession,\n deleteSession,\n renameSession,\n saveSession,\n} from \"../session.js\";\nimport { WORKSPACE_DIR } from \"./setup.js\";\n\nexport function handleThemesRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n if (method === \"GET\") {\n const session = getSession();\n const sessions = listSessions()\n .sort((a, b) => b.updatedAt - a.updatedAt);\n\n jsonResponse(res, 200, {\n activeTheme: session\n ? { id: session.id, themeName: session.themeName }\n : null,\n sessions,\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { sessionId, deleteFiles } = JSON.parse(body);\n deleteSession(sessionId, deleteFiles);\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleThemeSwitchRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { sessionId } = JSON.parse(body);\n const session = loadSession(sessionId);\n if (!session) {\n jsonResponse(res, 404, { error: \"Session not found\" });\n return;\n }\n\n jsonResponse(res, 200, {\n ok: true,\n themeName: session.themeName,\n themePath: session.themePath,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleDeleteLocalThemeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { themeName } = JSON.parse(body);\n if (!themeName || typeof themeName !== \"string\") {\n jsonResponse(res, 400, { error: \"Theme name is required\" });\n return;\n }\n const themePath = join(WORKSPACE_DIR, themeName);\n if (!existsSync(themePath)) {\n jsonResponse(res, 404, { error: \"Theme not found on disk\" });\n return;\n }\n rmSync(themePath, { recursive: true, force: true });\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleRenameThemeRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { sessionId, newName } = JSON.parse(body);\n if (!sessionId || !newName || typeof newName !== \"string\") {\n jsonResponse(res, 400, { error: \"sessionId and newName are required\" });\n return;\n }\n const sanitized = newName.toLowerCase().replace(/[^a-z0-9-]/g, \"-\").replace(/^-|-$/g, \"\").replace(/-{2,}/g, \"-\");\n if (!sanitized) {\n jsonResponse(res, 400, { error: \"Invalid name\" });\n return;\n }\n const result = renameSession(sessionId, sanitized);\n if (result.ok) {\n jsonResponse(res, 200, { ok: true, newName: sanitized });\n } else {\n jsonResponse(res, 400, { error: result.error });\n }\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n","/**\n * Dashboard & template routes — CRUD, activate, rename, module library, brand assets, download.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync, readFileSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { execFileSync, type ExecFileSyncOptions } from \"node:child_process\";\n\nconst _shellOpt: ExecFileSyncOptions = process.platform === \"win32\" ? { shell: true } : {};\nimport { jsonResponse, readBody } from \"../route-helpers.js\";\nimport { log } from \"../log.js\";\nimport { getHubSpotPak } from \"../../utils/config.js\";\nimport {\n getSession,\n saveSession,\n getOrderedModules,\n getActiveTemplate,\n setActiveTemplate,\n addTemplate,\n removeTemplate,\n cloneTemplate,\n getModuleLibrary,\n renameTemplate,\n type PageType,\n} from \"../session.js\";\nimport { ensureDir, writeFile } from \"../../utils/fs.js\";\n\nexport function handleDashboardRoute(res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const library = getModuleLibrary();\n jsonResponse(res, 200, {\n themeName: session.themeName,\n themePath: session.themePath,\n templates: session.templates.map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n messageCount: t.messages.length,\n })),\n activeTemplateId: session.activeTemplateId,\n moduleLibrary: library.map((entry) => ({\n moduleName: entry.module.moduleName,\n usedIn: entry.usedIn,\n })),\n brandAssets: {\n hasStyleguide: !!session.brandAssets?.styleguide,\n hasBrandvoice: !!session.brandAssets?.brandvoice,\n hasThemeContext: !!session.brandAssets?.themeContext,\n humanify: session.brandAssets?.humanify !== false,\n },\n });\n}\n\nexport function handleDownloadZipRoute(res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const themePath = session.themePath;\n if (!existsSync(themePath)) {\n jsonResponse(res, 404, { error: \"Theme directory not found\" });\n return;\n }\n\n const themeName = session.themeName || \"theme\";\n const parentDir = join(themePath, \"..\");\n const folderName = basename(themePath);\n\n try {\n const zipFileName = `${themeName}.zip`;\n const tmpZip = join(parentDir, zipFileName);\n\n if (existsSync(tmpZip)) rmSync(tmpZip);\n\n execFileSync(\"zip\", [\n \"-r\", zipFileName, folderName,\n \"-x\", `${folderName}/.git/*`, `${folderName}/.vibespot/*`, `${folderName}/node_modules/*`,\n ], { cwd: parentDir, timeout: 30_000, ..._shellOpt });\n\n const zipData = readFileSync(tmpZip);\n rmSync(tmpZip);\n\n res.writeHead(200, {\n \"Content-Type\": \"application/zip\",\n \"Content-Disposition\": `attachment; filename=\"${zipFileName}\"`,\n \"Content-Length\": zipData.length,\n });\n res.end(zipData);\n } catch (err: any) {\n log.error(\"download-zip\", \"Failed to create zip archive\", err);\n jsonResponse(res, 500, { error: \"Failed to create zip archive\" });\n }\n}\n\nexport function handleTemplatesRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n jsonResponse(res, 200, {\n templates: session.templates.map((t) => ({\n id: t.id,\n label: t.label,\n pageType: t.pageType,\n moduleCount: t.modules.length,\n })),\n activeTemplateId: session.activeTemplateId,\n });\n return;\n }\n\n if (method === \"POST\") {\n readBody(req, (body) => {\n try {\n const { pageType, label } = JSON.parse(body);\n if (!pageType || !label) {\n jsonResponse(res, 400, { error: \"pageType and label are required\" });\n return;\n }\n const validTypes: PageType[] = [\"landing_page\", \"blog_post\", \"website_page\", \"module_only\"];\n if (!validTypes.includes(pageType)) {\n jsonResponse(res, 400, { error: `Invalid pageType: ${pageType}` });\n return;\n }\n\n const entry = addTemplate(pageType, label);\n saveSession();\n\n jsonResponse(res, 200, {\n ok: true,\n template: {\n id: entry.id,\n label: entry.label,\n pageType: entry.pageType,\n },\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { templateId, deleteModules } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const removed = removeTemplate(templateId, !!deleteModules);\n if (!removed) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleTemplateActivateRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const success = setActiveTemplate(templateId);\n if (!success) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n const session = getSession();\n jsonResponse(res, 200, {\n ok: true,\n modules: getOrderedModules().map((m) => m.moduleName),\n messageCount: session?.messages.length || 0,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleTemplateRenameRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId, newLabel } = JSON.parse(body);\n if (!templateId || !newLabel || typeof newLabel !== \"string\") {\n jsonResponse(res, 400, { error: \"templateId and newLabel are required\" });\n return;\n }\n const success = renameTemplate(templateId, newLabel.trim());\n if (!success) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, { ok: true, newLabel: newLabel.trim() });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleTemplateCloneRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { templateId, label } = JSON.parse(body);\n if (!templateId) {\n jsonResponse(res, 400, { error: \"templateId is required\" });\n return;\n }\n const entry = cloneTemplate(templateId, label);\n if (!entry) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n saveSession();\n jsonResponse(res, 200, {\n ok: true,\n template: {\n id: entry.id,\n label: entry.label,\n pageType: entry.pageType,\n moduleCount: entry.modules.length,\n },\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleModuleLibraryRoute(res: ServerResponse): void {\n const library = getModuleLibrary();\n jsonResponse(res, 200, {\n modules: library.map((entry) => ({\n moduleName: entry.module.moduleName,\n usedIn: entry.usedIn,\n fieldsJson: entry.module.fieldsJson,\n })),\n });\n}\n\nexport function handleAddModuleToTemplateRoute(path: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n try {\n const { moduleName } = JSON.parse(body);\n if (!moduleName) {\n jsonResponse(res, 400, { error: \"moduleName is required\" });\n return;\n }\n\n const library = getModuleLibrary();\n const entry = library.find((e) => e.module.moduleName === moduleName);\n if (!entry) {\n jsonResponse(res, 404, { error: `Module \"${moduleName}\" not found in library` });\n return;\n }\n\n const modCopy = { ...entry.module };\n const existing = session.modules.find((m) => m.moduleName === modCopy.moduleName);\n if (!existing) {\n session.modules.push(modCopy);\n session.moduleOrder.push(modCopy.moduleName);\n session.updatedAt = Date.now();\n }\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n\nexport function handleBrandAssetsRoute(method: string, req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n jsonResponse(res, 200, {\n styleguide: session.brandAssets?.styleguide || null,\n brandvoice: session.brandAssets?.brandvoice || null,\n themeContext: session.brandAssets?.themeContext || null,\n });\n return;\n }\n\n if (method === \"POST\") {\n readBody(req, (body) => {\n try {\n const { type, content } = JSON.parse(body);\n if (!type) {\n jsonResponse(res, 400, { error: \"type is required\" });\n return;\n }\n\n if (!session.brandAssets) session.brandAssets = {};\n\n if (type === \"humanify\") {\n session.brandAssets.humanify = content === \"on\";\n session.updatedAt = Date.now();\n saveSession();\n jsonResponse(res, 200, { ok: true });\n return;\n }\n\n if (!content) {\n jsonResponse(res, 400, { error: \"content is required\" });\n return;\n }\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}. Must be \"styleguide\", \"brandvoice\", or \"themeContext\"` });\n return;\n }\n\n const filename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n session.brandAssets[type] = content;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, filename), content);\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readBody(req, (body) => {\n try {\n const { type } = JSON.parse(body);\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}` });\n return;\n }\n\n if (session.brandAssets) {\n delete session.brandAssets[type];\n }\n session.updatedAt = Date.now();\n\n const delFilename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n const filePath = join(session.themePath, \".vibespot\", delFilename);\n if (existsSync(filePath)) rmSync(filePath);\n\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\n// ---------------------------------------------------------------------------\n// Brand asset extraction from theme\n// ---------------------------------------------------------------------------\n\n/** Save a single brand asset to session + disk. */\nfunction saveBrandAsset(\n session: ReturnType<typeof getSession>,\n type: \"styleguide\" | \"brandvoice\" | \"themeContext\",\n content: string,\n): void {\n if (!session) return;\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets[type] = content;\n session.updatedAt = Date.now();\n\n const filename = type === \"themeContext\" ? \"theme-context.md\" : `${type}.md`;\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, filename), content);\n}\n\n/** Extract a single brand asset by type. */\nasync function extractSingleAsset(\n session: NonNullable<ReturnType<typeof getSession>>,\n type: \"styleguide\" | \"brandvoice\" | \"themeContext\",\n sourcePath?: string,\n): Promise<string | null> {\n if (type === \"styleguide\") {\n const { extractDesignContext } = await import(\"../../ai/design-extractor.js\");\n return extractDesignContext(sourcePath || session.themePath);\n }\n\n // brandvoice and themeContext need AI + rendered preview HTML\n const { resolveAgenticEngine } = await import(\"../ai-handler.js\");\n const { loadConfig } = await import(\"../../utils/config.js\");\n const config = loadConfig();\n const { engine, apiKey, model } = resolveAgenticEngine(config);\n\n const { buildPreviewHtml } = await import(\"../preview.js\");\n const previewHtml = buildPreviewHtml();\n if (!previewHtml || previewHtml.length < 50) return null;\n\n if (type === \"brandvoice\") {\n const { extractBrandvoice } = await import(\"../agent/stages/brandvoice-extractor.js\");\n return extractBrandvoice(previewHtml, engine, apiKey, model);\n }\n\n // themeContext\n const { extractThemeContext } = await import(\"../agent/stages/context-extractor.js\");\n return extractThemeContext(previewHtml, session.brandAssets?.themeContext, engine, apiKey, model);\n}\n\nexport function handleDesignExtractRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n (async () => {\n try {\n const parsed = body ? JSON.parse(body) : {};\n const type = parsed.type || \"styleguide\";\n const sourcePath = parsed.sourcePath;\n\n if (type === \"all\") {\n // Extract all three in parallel\n const types = [\"styleguide\", \"brandvoice\", \"themeContext\"] as const;\n const results = await Promise.allSettled(\n types.map((t) => extractSingleAsset(session, t, sourcePath)),\n );\n\n const extracted: Record<string, string | null> = {};\n for (let i = 0; i < types.length; i++) {\n const r = results[i];\n const content = r.status === \"fulfilled\" ? r.value : null;\n if (content) {\n saveBrandAsset(session, types[i], content);\n }\n extracted[types[i]] = content;\n }\n\n saveSession();\n jsonResponse(res, 200, { ok: true, type: \"all\", extracted });\n return;\n }\n\n if (type !== \"styleguide\" && type !== \"brandvoice\" && type !== \"themeContext\") {\n jsonResponse(res, 400, { error: `Invalid type: ${type}` });\n return;\n }\n\n const content = await extractSingleAsset(session, type, sourcePath);\n if (!content) {\n jsonResponse(res, 200, { ok: false, type, error: \"No content to extract from\" });\n return;\n }\n\n saveBrandAsset(session, type, content);\n saveSession();\n jsonResponse(res, 200, { ok: true, type, content });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n })();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Reference theme import — download from HubSpot + extract design\n// ---------------------------------------------------------------------------\n\nexport function handleReferenceImportRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n (async () => {\n try {\n const { source, themeName, localPath } = JSON.parse(body);\n\n let sourcePath: string;\n\n if (source === \"hubspot\") {\n // Download theme from HubSpot to temp location\n if (!themeName) {\n jsonResponse(res, 400, { error: \"themeName is required for HubSpot import\" });\n return;\n }\n const pak = getHubSpotPak();\n if (!pak) {\n jsonResponse(res, 400, { error: \"No HubSpot account connected\" });\n return;\n }\n\n // Strip leading/trailing slashes (HubSpot DM \"Copy path\" gives \"/@marketplace/Theme\")\n const cleanName = themeName.replace(/^\\/+|\\/+$/g, \"\");\n if (!cleanName) {\n jsonResponse(res, 400, { error: \"Invalid theme name\" });\n return;\n }\n\n // Sanitize for local directory name (replace @ and / with safe chars)\n const safeDirName = cleanName.replace(/[@/]/g, \"_\").replace(/_+/g, \"_\");\n const { homedir } = await import(\"node:os\");\n const refDir = join(homedir(), \"vibespot-themes\", \".references\", safeDirName);\n ensureDir(refDir);\n\n const { fetchTheme } = await import(\"../../hubspot/fetcher.js\");\n await fetchTheme(pak, cleanName, refDir);\n sourcePath = refDir;\n } else if (source === \"local\") {\n if (!localPath) {\n jsonResponse(res, 400, { error: \"localPath is required for local import\" });\n return;\n }\n if (!existsSync(localPath)) {\n jsonResponse(res, 400, { error: `Path not found: ${localPath}` });\n return;\n }\n sourcePath = localPath;\n } else {\n jsonResponse(res, 400, { error: \"source must be 'hubspot' or 'local'\" });\n return;\n }\n\n // Extract design context and save to current theme\n const { extractDesignContext } = await import(\"../../ai/design-extractor.js\");\n const styleguide = await extractDesignContext(sourcePath);\n\n if (!session.brandAssets) session.brandAssets = {};\n session.brandAssets.styleguide = styleguide;\n session.updatedAt = Date.now();\n\n const assetDir = join(session.themePath, \".vibespot\");\n ensureDir(assetDir);\n writeFile(join(assetDir, \"styleguide.md\"), styleguide);\n\n saveSession();\n jsonResponse(res, 200, { ok: true, styleguide, source: sourcePath });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n })();\n });\n}\n","/**\n * Module, field, import, session, upload, and history routes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { join } from \"node:path\";\nimport { jsonResponse, readBody, readJsonBody } from \"../route-helpers.js\";\nimport {\n getSession,\n getOrderedModules,\n removeModule,\n detachModule,\n reorderModules,\n updateFieldValue,\n saveSession,\n writeModulesToDisk,\n addMessage,\n reloadModulesFromDisk,\n reloadActiveTemplateFromDisk,\n getActiveTemplate,\n} from \"../session.js\";\nimport { isGenerating } from \"../ai-handler.js\";\nimport { applyAutoFixes } from \"../auto-fix.js\";\nimport { startStreamingJob } from \"../process-manager.js\";\nimport { analyzeSource } from \"../../wizard/source.js\";\nimport { commitThemeState, commitTemplateState, getHistory, getTemplateHistory, rollbackToCommit, rollbackTemplateToCommit, isGitAvailable } from \"../project-git.js\";\n\nexport function handleSessionRoute(method: string, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n jsonResponse(res, 200, {\n id: session.id,\n themeName: session.themeName,\n themePath: session.themePath,\n messageCount: session.messages.length,\n moduleCount: session.modules.length,\n moduleOrder: session.moduleOrder,\n });\n}\n\nexport function handleModulesRoute(\n method: string,\n req: IncomingMessage,\n res: ServerResponse\n): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n if (method === \"GET\") {\n const ordered = getOrderedModules();\n jsonResponse(res, 200, {\n modules: ordered.map((m) => ({\n moduleName: m.moduleName,\n fieldsJson: m.fieldsJson,\n moduleHtml: m.moduleHtml,\n moduleCss: m.moduleCss,\n moduleJs: m.moduleJs || null,\n })),\n sharedCss: session.sharedCss,\n sharedJs: session.sharedJs,\n });\n return;\n }\n\n if (method === \"DELETE\") {\n readJsonBody(req, res, (data: { moduleName: string; deleteEntirely?: boolean }) => {\n if (data.deleteEntirely) {\n removeModule(data.moduleName);\n } else {\n detachModule(data.moduleName);\n }\n saveSession();\n jsonResponse(res, 200, { ok: true });\n });\n return;\n }\n\n jsonResponse(res, 405, { error: \"Method not allowed\" });\n}\n\nexport function handleCodeUpdateRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n readBody(req, (body) => {\n try {\n const data = JSON.parse(body);\n\n // Shared CSS/JS\n if (data.shared) {\n if (data.shared === \"css\") {\n session.sharedCss = data.content;\n } else if (data.shared === \"js\") {\n session.sharedJs = data.content;\n } else {\n jsonResponse(res, 400, { error: \"Invalid shared type\" });\n return;\n }\n // Also update the active template's shared fields\n const tpl = getActiveTemplate();\n if (tpl) {\n if (data.shared === \"css\") tpl.sharedCss = data.content;\n else tpl.sharedJs = data.content;\n }\n session.updatedAt = Date.now();\n saveSession();\n writeModulesToDisk();\n jsonResponse(res, 200, { ok: true });\n return;\n }\n\n // Module file\n const { moduleName, fileType, content } = data;\n if (!moduleName || !fileType) {\n jsonResponse(res, 400, { error: \"moduleName and fileType required\" });\n return;\n }\n\n const mod = session.modules.find((m) => m.moduleName === moduleName);\n if (!mod) {\n jsonResponse(res, 404, { error: `Module \"${moduleName}\" not found` });\n return;\n }\n\n switch (fileType) {\n case \"html\": mod.moduleHtml = content; break;\n case \"css\": mod.moduleCss = content; break;\n case \"js\": mod.moduleJs = content || undefined; break;\n case \"fields\":\n // Validate JSON before saving\n try { JSON.parse(content); } catch {\n jsonResponse(res, 400, { error: \"Invalid JSON in fields.json\" });\n return;\n }\n mod.fieldsJson = content;\n break;\n default:\n jsonResponse(res, 400, { error: `Invalid fileType: ${fileType}` });\n return;\n }\n\n session.updatedAt = Date.now();\n saveSession();\n writeModulesToDisk();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: String(err) });\n }\n });\n}\n\nexport function handleReorderRoute(req: IncomingMessage, res: ServerResponse): void {\n readJsonBody(req, res, (data: { order: unknown }) => {\n if (Array.isArray(data.order)) {\n reorderModules(data.order);\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } else {\n jsonResponse(res, 400, { error: \"order must be an array\" });\n }\n });\n}\n\nexport async function handleUploadRoute(res: ServerResponse): Promise<void> {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n try {\n writeModulesToDisk();\n const fixes = applyAutoFixes(session.themePath);\n\n const jobId = startStreamingJob(\n `hs cms upload \"${session.themePath}\" \"${session.themeName}\"`,\n \"Uploading to HubSpot\",\n { cwd: join(session.themePath, \"..\"), timeout: 180_000 }\n );\n\n jsonResponse(res, 200, {\n ok: true,\n jobId,\n fixes,\n });\n } catch (err) {\n jsonResponse(res, 500, { error: String(err) });\n }\n}\n\nexport function handleFieldRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { moduleName, fieldPath, value } = JSON.parse(body);\n updateFieldValue(moduleName, fieldPath, value);\n saveSession();\n jsonResponse(res, 200, { ok: true });\n } catch (err) {\n jsonResponse(res, 400, { error: String(err) });\n }\n });\n}\n\nexport function handleImportRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const { url } = JSON.parse(body);\n if (!url || typeof url !== \"string\") {\n jsonResponse(res, 400, { error: \"url is required\" });\n return;\n }\n\n const analysis = analyzeSource(url);\n\n const componentSummary = analysis.components\n .map((c) => `- ${c.name}: ${c.description}`)\n .join(\"\\n\");\n\n const summary = {\n sourceDir: analysis.sourceDir,\n componentCount: analysis.components.length,\n components: analysis.components.map((c) => ({\n name: c.name,\n description: c.description,\n })),\n hasTailwind: analysis.hasTailwind,\n cssVarCount: analysis.cssVarCount,\n fonts: analysis.fonts,\n interactions: analysis.interactions,\n conversionPrompt: `Import and convert the React landing page from ${url} to native HubSpot modules.\n\nSource analysis found ${analysis.components.length} components:\n${componentSummary}\n\nDesign system: ${analysis.hasTailwind ? \"Tailwind CSS\" : \"Custom CSS\"}, ${analysis.cssVarCount} CSS variables\nFonts: ${analysis.fonts.length > 0 ? analysis.fonts.join(\", \") : \"System fonts\"}\nInteractions: ${analysis.interactions.join(\", \")}\n\nRead the React source files from ${analysis.sourceDir} and convert each component to a HubSpot module. Preserve the design, layout, colors, and content. Generate fields.json so marketers can edit all text, images, colors, and links in the HubSpot page editor.`,\n };\n\n jsonResponse(res, 200, summary);\n } catch (err) {\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Version history routes\n// ---------------------------------------------------------------------------\n\nexport function handleHistoryRoute(req: IncomingMessage, res: ServerResponse): void {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n if (!isGitAvailable()) {\n jsonResponse(res, 200, { available: false, commits: [] });\n return;\n }\n\n const url = new URL(req.url || \"/\", \"http://localhost\");\n const templateId = url.searchParams.get(\"templateId\");\n\n const commits = templateId\n ? getTemplateHistory(session.themePath, templateId, 50)\n : getHistory(session.themePath, 50);\n jsonResponse(res, 200, { available: true, commits, filtered: !!templateId });\n}\n\nexport function handleRollbackRoute(req: IncomingMessage, res: ServerResponse): void {\n readBody(req, (body) => {\n try {\n const session = getSession();\n if (!session) {\n jsonResponse(res, 404, { error: \"No active session\" });\n return;\n }\n\n const { hash, templateId } = JSON.parse(body);\n if (!hash || typeof hash !== \"string\") {\n jsonResponse(res, 400, { error: \"Commit hash is required\" });\n return;\n }\n\n addMessage(\"assistant\", `Rolled back to version ${hash.slice(0, 7)}.`);\n\n if (templateId) {\n const tpl = session.templates.find((t) => t.id === templateId);\n if (!tpl) {\n jsonResponse(res, 404, { error: \"Template not found\" });\n return;\n }\n const filePaths = tpl.moduleOrder.map((n) => `modules/${n}.module`);\n if (tpl.templateFile) filePaths.push(tpl.templateFile);\n\n const result = rollbackTemplateToCommit(session.themePath, templateId, hash, filePaths);\n if (!result.success) {\n jsonResponse(res, 500, { error: result.error || \"Rollback failed\" });\n return;\n }\n reloadActiveTemplateFromDisk();\n } else {\n const result = rollbackToCommit(session.themePath, hash);\n if (!result.success) {\n jsonResponse(res, 500, { error: result.error || \"Rollback failed\" });\n return;\n }\n reloadModulesFromDisk();\n }\n\n saveSession();\n jsonResponse(res, 200, {\n ok: true,\n modules: getOrderedModules().map((m) => m.moduleName),\n });\n } catch (err) {\n jsonResponse(res, 500, { error: err instanceof Error ? err.message : String(err) });\n }\n });\n}\n"],"mappings":"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","names":["path","fileURLToPath","init_esm_shims","__esmMin","readFileSync","writeFileSync","mkdirSync","existsSync","dirname","join","readFile","path","writeFile","content","fileExists","ensureDir","resolveAsset","name","paths","p","getVersion","_version","candidates","pkg","getChangelog","_changelog","init_fs","__esmMin","init_esm_shims","execSync","run","command","options","err","e","stdout","stderr","runPassthrough","init_shell","__esmMin","init_esm_shims","config_exports","__export","addHubSpotAccount","getActiveHubSpotAccount","getApiKeyForEngine","getConfigDir","getHubSpotPak","isCliToolEnabled","loadConfig","maskApiKey","removeHubSpotAccount","saveConfig","setActiveHubSpotAccount","setCliToolEnabled","join","homedir","chmodSync","fileExists","CONFIG_PATH","raw","readFile","engine","config","c","key","merged","writeFile","CONFIG_DIR","activeId","found","a","pak","portalId","portalName","dataCenter","accounts","idx","entry","update","toolId","enabled","tools","init_config","__esmMin","init_esm_shims","init_fs","claude_oauth_exports","__export","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","clearOAuthTokens","getOAuthTokenInfo","getValidAccessToken","hasValidOAuthToken","saveInitialToken","join","homedir","chmodSync","unlinkSync","loadTokens","fileExists","TOKEN_FILE","readFile","saveTokens","tokens","writeFile","refreshAccessToken","refresh_token","resp","TOKEN_ENDPOINT","CLIENT_ID","data","accessToken","refreshToken","REFRESH_BUFFER_MS","refreshPromise","init_claude_oauth","__esmMin","init_esm_shims","init_fs","readFileSync","basename","exchangePakForToken","pak","cached","tokenCache","TOKEN_REFRESH_BUFFER_MS","resp","BASE_URL","body","data","token","authHeaders","accessToken","encodePath","remotePath","detectDataCenterFromPak","sleep","ms","resolve","parseErrorResponse","fallbackPath","message","category","detail","firstError","text","fetchWithRetry","url","options","retries","MAX_RETRIES","attempt","delay","RETRY_DELAY_MS","validatePak","err","portalId","portalName","uploadFile","localFilePath","fileContent","fileName","formData","blob","error","deleteFile","downloadFile","arrayBuffer","getMetadata","listRootFolders","headers","urlVariants","children","c","init_api","__esmMin","init_esm_shims","fetcher_exports","__export","fetchTheme","mkdirSync","writeFileSync","join","dirname","listFilesRecursive","pak","remotePath","meta","getMetadata","files","rawChildren","child","childName","childPath","childMeta","parallelMap","items","concurrency","fn","index","worker","i","workers","themeName","targetPath","opts","remoteFiles","relativePath","localPath","content","downloadFile","init_fetcher","__esmMin","init_esm_shims","init_api","cachedAsset","name","val","guideCache","readFile","resolveAsset","getConversionGuide","getDesignGuide","getContentGuide","getHubspotRules","getHumanifyGuide","getPageTypeGuide","pageType","fullGuide","header","startIdx","afterHeader","buildSystemPrompt","conversionGuide","buildModulePrompt","componentSource","moduleName","cssVars","buildCssPrompt","indexCss","tailwindConfig","pagePrefix","buildJsPrompt","hooksSource","interactiveComponents","buildTemplatePrompt","moduleNames","themeName","n","i","init_prompts","__esmMin","init_esm_shims","init_fs","init_types","__esmMin","init_esm_shims","existsSync","writeFileSync","mkdirSync","join","isValidHash","hash","isGitAvailable","gitAvailableCache","run","ensureGitRepo","themePath","ensureVibeSpotDir","init","writeGitIgnore","dir","gitignorePath","commitThemeState","message","truncated","commitResult","hashResult","commitTemplateState","templateId","filePaths","fp","fullPath","prefix","maxMsg","fullMessage","getHistory","limit","result","commits","line","parts","timestamp","getTemplateHistory","escapedId","rollbackToCommit","commitHash","verify","msgResult","origMessage","checkout","rollbackMsg","rollbackTemplateToCommit","restored","init_project_git","__esmMin","init_esm_shims","init_shell","readFileSync","existsSync","writeFileSync","mkdirSync","rmSync","join","syncFlatFieldsFromTemplate","tpl","activeSession","getSession","syncFlatFieldsToTemplate","getActiveTemplate","addMessage","role","content","pipeline","msg","saveChatToTheme","addSessionAsset","asset","saveSession","updateModules","assets","newMod","newNameLower","idx","m","n","reorderModules","newOrder","removeModule","moduleName","modDir","detachModule","updateFieldValue","fieldPath","value","mod","fields","setFieldDefault","getOrderedModules","ordered","name","chatDir","chatData","loadChatFromTheme","themePath","chatPath","data","path","parts","fieldName","field","f","init_state","__esmMin","init_esm_shims","init_store","init_templates","existsSync","rmSync","join","migrateSession","session","templateId","entry","getActiveTemplate","activeSession","getSession","setActiveTemplate","tpl","t","syncFlatFieldsFromTemplate","addTemplate","pageType","label","slug","id","cloneTemplate","newLabel","source","m","renameTemplate","removeTemplate","deleteModules","idx","removed","templatesDir","filename","filePath","listingPath","usedElsewhere","mod","exclusiveModules","name","modulesDir","modDir","getModuleLibrary","map","existing","init_templates","__esmMin","init_esm_shims","init_store","init_state","readFileSync","readdirSync","existsSync","writeFileSync","mkdirSync","rmSync","renameSync","join","dirname","homedir","readIndex","_indexCache","INDEX_PATH","rebuildIndex","writeIndex","entries","SESSIONS_DIR","f","data","templates","n","t","upsertIndex","session","entry","idx","e","removeFromIndex","sessionId","removeFromIndexByTheme","themeName","getSession","activeSession","generateId","createSession","themePath","ensureGitRepo","saveSession","filePath","loadSession","migrateSession","listSessions","deleteSession","deleteFiles","renameSession","newName","oldName","oldPath","newPath","err","cssOld","cssNew","jsOld","jsNew","themeJsonPath","themeData","init_store","__esmMin","init_esm_shims","init_project_git","init_templates","readFileSync","readdirSync","existsSync","writeFileSync","mkdirSync","rmSync","join","safeRead","filePath","getOrderedModulesFrom","tpl","ordered","name","mod","m","parseTemplateFile","filename","content","id","pageType","label","labelMatch","moduleRe","moduleNames","match","scanTemplateFiles","templatesDir","modulesByName","sharedCss","sharedJs","chatMessages","entries","allFiles","f","parsed","templateModules","templateOrder","scanThemeFromDisk","themePath","activeSession","getSession","chatFromDisk","loadChatFromTheme","ensureGitRepo","modulesDir","entry","modDir","moduleName","cssDir","jsDir","cssFiles","jsFiles","sgPath","bvPath","tcPath","parsedTemplates","firstOrder","loadedNames","validOrder","n","syncFlatFieldsFromTemplate","migrateSession","writeModulesToDisk","allModules","modulesBaseDir","scaffoldHome","activeTemplateFiles","templateContent","generateTemplateForEntry","annotated","ensureTemplateAnnotations","writeBlogListingTemplate","template","generateTemplateFromModules","file","patchBaseTemplate","updateThemeJson","reloadModulesFromDisk","syncFlatFieldsToTemplate","reloadActiveTemplateFromDisk","getActiveTemplate","tplPath","basePath","mainJsLine","themeJsonPath","themeData","sections","listingContent","getOrderedModules","init_disk","__esmMin","init_esm_shims","init_store","init_state","init_templates","init_project_git","init_session","__esmMin","init_esm_shims","init_types","init_store","init_state","init_disk","init_templates","init_session","__esmMin","init_esm_shims","buildContextFromFields","fields","result","field","renderHubL","template","context","output","stripDirectives","processForLoops","processConditionals","resolveExpressions","cleanupRemaining","assemblePreview","opts","styleBlocks","css","scriptBlocks","js","body","tpl","RE_REQUIRE_TAG","RE_END_REQUIRE_TAG","RE_REQUIRE_EXPR","RE_GET_ASSET_URL","_match","filename","RE_GET_ASSET_URL_STRIP","RE_DND_TAGS","RE_MODULE_TAG","RE_TEMPLATE_TAGS","RE_ANNOTATIONS","RE_CONTENT_VARS","safety","match","findOutermostFor","varName","iterExpr","start","end","items","resolveIterable","rendered","item","index","loopContext","out","openTag","forOrEndfor","firstOpen","bodyStart","depth","m","RE_IF_PATTERN","condition","elseMatch","ifBody","elseBody","elifParts","evaluateCondition","i","elifCondition","elifBody","expr","filterParts","path","value","resolvePath","applyFilter","str","rangeMatch","resolveNumericArg","arr","splitMatch","val","arg","parts","current","part","trimmed","eqMatch","left","operator","right","isTruthy","filter","argMatch","filterName","filterArg","len","init_renderer","__esmMin","init_esm_shims","preview_exports","__export","buildModulePreviewHtml","buildPreviewHtml","extractThemeColors","sharedCss","extract","names","fallback","name","re","m","bg","surface","text","textMuted","border","session","getSession","welcomePreview","modules","getOrderedModules","moduleOrder","renderedModules","moduleCssArray","moduleJsArray","renderedNames","mod","context","fields","buildContextFromFields","rendered","renderHubL","anchorId","theme","placeholderCss","assemblePreview","moduleName","tpl","init_preview","__esmMin","init_esm_shims","init_renderer","init_session","appendFileSync","mkdirSync","readdirSync","unlinkSync","join","homedir","ensureLogDir","logDirReady","LOG_DIR","pruneOldLogs","pruned","cutoff","MAX_AGE_DAYS","f","dateStr","ts","todayFile","date","timestamp","writeToFile","level","line","log","init_log","__esmMin","init_esm_shims","context","message","data","err","errMsg","tryParseJSON","raw","repaired","lastPos","attempt","err","posMatch","pos","searchStart","lastQuote","absPos","tryRepairTruncatedJSON","modulesIdx","arrayStart","lastCompleteModule","braceDepth","inString","escaped","i","ch","jsonStr","toModuleFiles","m","parseAndApplyModules","response","onWarning","modulesApplied","match","blockPattern","log","data","obj","updateModules","jsonPattern","lastFenceIdx","truncated","salvaged","hasModuleRef","describesProse","msg","init_ai_parser","__esmMin","init_esm_shims","init_session","init_log","getPromptContext","session","getSession","getActiveTemplate","buildVibeSystemPromptBlocks","conversionGuide","themeName","editMode","pageType","brandAssets","blocks","buildCoreInstructions","guides","getHubspotRules","getDesignGuide","getContentGuide","dynamic","buildDynamicSection","parts","section","getPageTypeGuide","humanifyGuide","getHumanifyGuide","buildVibeSystemPrompt","core","pageTypeSection","pageTypePrompt","brandPrompt","formatReminder","buildStateContext","modules","total","i","mod","library","getModuleLibrary","currentModuleNames","m","otherModules","e","entry","buildMessagesWithContext","userMessage","fileContexts","history","messages","stateContext","assetManifest","imageAssets","a","textContent","hasFiles","fc","imageFiles","contentBlocks","img","init_ai_prompts","__esmMin","init_esm_shims","init_prompts","init_session","spawn","getAnthropicSDK","_AnthropicCtor","buildFileContextText","fileContexts","parts","fc","_streamAnthropic","client","system","messages","model","onChunk","onStatus","onFinish","attempt","fullResponse","statusIndex","sendStatus","CLI_STATUS_MESSAGES","heartbeat","stream","event","text","err","status","errType","RATE_LIMIT_DELAYS","wait","log","r","prepareAnthropicContext","userMessage","themeName","conversionGuide","getConversionGuide","editMode","getSession","buildMessagesWithContext","ctx","getPromptContext","systemBlocks","buildVibeSystemPromptBlocks","streamWithAnthropicAPI","apiKey","AnthropicSDK","b","streamWithClaudeOAuth","accessToken","getValidAccessToken","OAUTH_EXTRA_HEADERS","oauthBlocks","OAUTH_SYSTEM_PREFIX","streamWithOpenAIAPI","openaiMessages","m","block","response","buildVibeSystemPrompt","reader","decoder","buffer","done","value","lines","line","data","delta","streamWithGeminiAPI","session","stateContext","buildStateContext","contents","userContent","userParts","url","spawnCLI","bin","args","prompt","resolve","reject","env","child","stdout","stderr","settled","settle","fn","d","chunk","code","timer","generateWithClaudeCode","config","loadConfig","msg","result","generateWithCLI","cli","init_ai_engines","__esmMin","init_esm_shims","init_prompts","init_config","init_claude_oauth","init_session","init_ai_prompts","init_log","jsonResponse","res","status","data","readBody","req","callback","chunks","chunk","readJsonBody","body","init_route_helpers","__esmMin","init_esm_shims","createWriteStream","mkdirSync","existsSync","readFileSync","join","extname","randomUUID","Busboy","sanitizeFilename","name","deduplicateFilename","dir","ext","base","counter","extractPdfText","filePath","pdfParse","buffer","extractDocxText","extractPlainText","handleFileUploadRoute","req","res","session","getSession","jsonResponse","results","errors","fileCount","writePromises","bb","MAX_FILE_SIZE","fieldname","fileStream","info","originalName","mimeType","SUPPORTED_MIMES","isImage","IMAGE_MIMES","sanitized","id","targetDir","finalFilename","targetPath","writeStream","fileSize","truncated","chunk","resolve","asset","addSessionAsset","log","err","a","getFileContexts","fileIds","ctx","imgPath","c","DOCUMENT_MIMES","init_upload_files","__esmMin","init_esm_shims","init_route_helpers","init_session","init_log","withRateLimitRetry","fn","onStatus","attempt","err","status","errType","RATE_LIMIT_DELAYS","wait","log","r","stringifyJsonFields","data","obj","key","getAnthropicSDK","_AnthropicCtor","callAnthropic","apiKey","model","opts","extraHeaders","systemPrefix","AnthropicSDK","client","messages","system","tool","response","block","b","fullText","stream","event","callAnthropicOAuth","accessToken","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","addAdditionalPropertiesFalse","schema","result","props","k","v","callOpenAI","openaiMessages","m","body","error","content","callGemini","_model","contents","url","text","resolveCLIBinary","engine","config","loadConfig","args","buildCLIPrompt","parts","msg","role","schemaDesc","describeSchema","indent","pad","required","lines","prop","req","type","desc","enumVals","itemType","extractJSON","output","trimmed","direct","tryParseJSON","fenceMatch","fenced","parsed","repaired","tryRepairTruncatedJSON","firstBrace","lastBrace","braceContent","callAgentCLI","bin","prompt","rawOutput","spawnCLI","callAgentAPI","getValidAccessToken","oauthToken","callAgent","API_ENGINES","isAgenticCapable","isCLIEngine","init_engine_adapter","__esmMin","init_esm_shims","init_ai_engines","init_ai_parser","init_config","init_claude_oauth","init_log","buildIntentAnalyzerPrompt","themeName","moduleNames","libraryModuleNames","themeContext","moduleList","n","i","libraryList","m","contextSection","INTENT_ANALYZER_SCHEMA","init_intent_analyzer","__esmMin","init_esm_shims","runIntentAnalyzer","userMessage","snapshot","engine","apiKey","model","onEvent","libraryModules","moduleNames","m","systemPrompt","buildIntentAnalyzerPrompt","messages","recentMessages","msg","content","result","callAgent","INTENT_ANALYZER_SCHEMA","log","isNew","plan","formatDecision","parts","init_intent_analyzer","__esmMin","init_esm_shims","init_engine_adapter","init_log","buildDesignSystemPrompt","themeName","brandAssets","parts","getArchitectDesignSummary","buildDesignSystemPromptBlocks","full","markerIdx","corePart","designGuide","blocks","dynamicParts","buildModulePlannerPrompt","sharedCss","guidesNeeded","getArchitectContentSummary","getArchitectHumanifySummary","DESIGN_SYSTEM_SCHEMA","MODULE_PLANNER_SCHEMA","init_page_architect","__esmMin","init_esm_shims","runPageArchitect","userMessage","plan","snapshot","engine","apiKey","model","onEvent","isAnthropicEngine","designPrompt","buildDesignSystemPrompt","designBlocks","buildDesignSystemPromptBlocks","designUserContent","designResult","callAgent","DESIGN_SYSTEM_SCHEMA","designSystem","log","sharedCss","vars","k","v","fontNotes","webFontPattern","requestedFonts","f","usedFonts","droppedFonts","decisionParts","plannerPrompt","buildModulePlannerPrompt","plannerUserContent","m","i","plannerResult","MODULE_PLANNER_SCHEMA","modulePlan","init_page_architect","__esmMin","init_esm_shims","init_engine_adapter","init_log","createConcurrencyLimiter","maxConcurrent","running","queue","fn","resolve","init_types","__esmMin","init_esm_shims","buildModuleDeveloperPrompt","themeName","sharedCss","guidesNeeded","brandAssets","parts","getHubspotRules","getConversionGuide","getModuleDevHumanifySummary","buildModuleDeveloperPromptBlocks","blocks","core","guideParts","dynamicParts","buildModuleUserMessage","userMessage","spec","existingCode","MODULE_DEVELOPER_SCHEMA","init_module_developer","__esmMin","init_esm_shims","init_prompts","runModuleDeveloper","userMessage","specs","sharedCss","themeName","engine","apiKey","model","concurrency","onEvent","guidesNeeded","brandAssets","isAnthropicEngine","systemPrompt","buildModuleDeveloperPrompt","systemBlocks","buildModuleDeveloperPromptBlocks","limit","createConcurrencyLimiter","total","promises","spec","index","lastError","attempt","log","module","generateSingleModule","err","r","retryCount","userContent","buildModuleUserMessage","result","callAgent","MODULE_DEVELOPER_SCHEMA","data","fieldsJson","metaJson","init_module_developer","__esmMin","init_esm_shims","init_engine_adapter","init_types","init_log","validateModules","modules","themeName","onEvent","mod","issues","fixedModule","validateAndFixJson","fixReservedFieldNames","fixDeprecatedFieldTypes","stripCdnImports","fixCssPrefix","fixHtmlClassPrefix","fixHublSyntax","ensureMetaFields","valid","i","log","jsonStr","moduleName","field","tryParseJSON","fieldsJson","fixed","css","importPattern","shouldSkipClass","name","SKIP_CLASSES","prefix","classPattern","unprefixedSet","match","className","selectorRe","escapeRegex","html","classAttrRe","anyFixed","fullMatch","classValue","classes","changed","newClasses","cls","s","tagPattern","tags","tag","isOpen","baseTag","stack","orphanClosers","found","j","t","unclosed","closers","metaJson","parsed","obj","init_validator","__esmMin","init_esm_shims","init_ai_parser","init_log","execSync","runAgentPipeline","userMessage","snapshot","engine","apiKey","model","concurrency","onEvent","libraryModules","startTime","effectiveConcurrency","isCLIEngine","bin","plan","runIntentAnalyzer","durationMs","blueprint","sharedCss","sharedJs","runPageArchitect","moduleSpecs","bpMod","newMod","modName","existing","m","generatedModules","failedModules","devResults","runModuleDeveloper","r","validationResults","validateModules","totalIssues","sum","autoFixed","i","log","issueDetails","finalModules","assembleModuleList","moduleOrder","buildModuleOrder","blueprintSet","missing","modulesGenerated","modulesUnchanged","validationIssues","assistantMessage","buildAssistantMessage","result","added","mod","name","reuse","libEntry","l","order","orderSet","footerIdx","n","insertions","a","b","ins","pos","moduleNames","seconds","parts","newNames","unfixed","fixed","valParts","init_pipeline","__esmMin","init_esm_shims","init_engine_adapter","init_intent_analyzer","init_page_architect","init_module_developer","init_validator","init_log","ai_handler_exports","__export","applyPipelineResult","handleAgenticGenerate","handleGenerate","handleGenerateStream","isGenerating","resolveAgenticEngine","setParseWarningCallback","shouldUseAgenticMode","execSync","cb","parseWarningCallback","generatingSessionId","finishResponse","fullResponse","current","getSession","log","addMessage","parseAndApplyModules","saveSession","userMessage","onChunk","onStatus","fileIds","session","fileContexts","getFileContexts","config","loadConfig","engine","detectDefaultEngine","apiKey","getApiKeyForEngine","streamWithAnthropicAPI","streamWithClaudeOAuth","streamWithOpenAIAPI","streamWithGeminiAPI","generateWithClaudeCode","generateWithCLI","hasValidOAuthToken","chunk","takeSnapshot","tpl","getActiveTemplate","modules","moduleOrder","engineType","isAgenticCapable","isCLIEngine","model","onEvent","capturedSessionId","concurrency","snapshot","library","getModuleLibrary","currentModuleNames","m","libraryModules","e","result","runAgentPipeline","pipelineMeta","updateModules","reorderModules","init_ai_handler","__esmMin","init_esm_shims","init_config","init_session","init_ai_parser","init_log","init_ai_engines","init_claude_oauth","init_upload_files","init_pipeline","design_extractor_exports","__export","collectThemeFiles","extractDesignContext","existsSync","readdirSync","readFileSync","join","spawn","getAnthropicSDK","_AnthropicCtor","safeRead","path","themePath","parts","totalChars","addSection","label","content","section","MAX_CONTENT_CHARS","themeJson","cssDir","f","modulesDir","dir","d","modPath","css","html","fields","getExtractionPrompt","_extractionPrompt","readFile","resolveAsset","spawnCLIForExtraction","bin","args","prompt","resolve","reject","env","child","stdout","stderr","err","code","onProgress","themeContent","systemPrompt","userMessage","config","loadConfig","engine","text","apiKey","getApiKeyForEngine","AnthropicSDK","block","getValidAccessToken","OAUTH_EXTRA_HEADERS","OAUTH_SYSTEM_PREFIX","accessToken","resp","model","p","combinedPrompt","init_design_extractor","__esmMin","init_esm_shims","init_fs","init_config","brandvoice_extractor_exports","__export","extractBrandvoice","previewHtml","engine","apiKey","model","systemPrompt","result","callAgent","text","log","err","msg","init_brandvoice_extractor","__esmMin","init_esm_shims","init_engine_adapter","init_log","context_extractor_exports","__export","extractThemeContext","previewHtml","existingContext","engine","apiKey","model","systemPrompt","result","callAgent","text","log","err","msg","init_context_extractor","__esmMin","init_esm_shims","init_engine_adapter","init_log","init_esm_shims","init_esm_shims","Command","init_esm_shims","init_esm_shims","init_esm_shims","chalk","palette","noColor","hex","color","theme","init_fs","printBanner","v","theme","o","m","lines","line","getVersion","init_esm_shims","init_esm_shims","init_shell","init_config","init_claude_oauth","join","homedir","readFileSync","existsSync","readdirSync","whichCmd","detectNode","result","run","detectGit","detectHubSpotCLI","detectClaudeCode","claudeDir","authenticated","authDetail","files","f","detectDataCenter","portalId","configPath","config","accountIdx","keyIdx","keyMatch","detectHubSpotAuth","accounts","defaultName","defaultId","defaultMatch","lines","line","tableMatch","name","authType","detectGeminiCLI","adcPath","hasAdc","hasEnvKey","detectCodexCLI","hasKey","hasOAuth","authFile","detail","detectGitHubCLI","detectGitHubAuth","output","match","altMatch","hasAnthropicKey","nodeVersionOk","version","hsCliVersionOk","major","detectHubSpotAuthFromConfig","loadConfig","uploadMode","configAccounts","a","active","getActiveHubSpotAccount","DISABLED_CLI","detectEnvironment","node","git","hsUploadMode","hsInfo","hs","hsAuth","dc","gh","ghAuth","claudeOAuth","hasValidOAuthToken","getOAuthTokenInfo","enabledTools","claude","isCliToolEnabled","gemini","codex","keyStatus","configKey","envVars","maskApiKey","v","anthropicKey","openaiKey","geminiKey","available","init_claude_oauth","init_shell","init_config","init_api","init_esm_shims","p","handleCancel","value","theme","intro","title","outro","message","note","text","opts","result","confirm","select","spinner","s","msg","log","logSuccess","logWarn","logError","runPreflight","intro","node","detectNode","logError","nodeVersionOk","logSuccess","git","detectGit","config","loadConfig","useApi","portalId","portalName","pak","getHubSpotPak","acct","getActiveHubSpotAccount","logWarn","note","key","text","v","s","spinner","info","validatePak","addHubSpotAccount","err","hs","detectHubSpotCLI","confirm","run","auth","detectHubSpotAuth","runPassthrough","claude","detectClaudeCode","gemini","detectGeminiCLI","codex","detectCodexCLI","hasKey","hasAnthropicKey","engineLabels","hasOAuth","hasValidOAuthToken","aiEngine","lastUsed","available","a","b","select","theme","saveConfig","model","outro","init_esm_shims","init_shell","init_fs","readdirSync","statSync","join","basename","extname","findComponents","dir","components","searchDirs","join","searchDir","fileExists","files","readdirSync","file","filePath","statSync","ext","extname","name","basename","content","readFile","desc","describeComponent","hints","analyzeCSS","cssFiles","varCount","fonts","cssFile","varMatches","fontMatches","m","font","importMatches","detectInteractions","interactions","hooksDir","hooks","hook","componentDir","analyzeSource","input","sourceDir","wasCloned","repoName","result","run","hasTailwind","setupSource","intro","text","v","logSuccess","theme","s","spinner","logError","logWarn","componentList","c","i","cssInfo","fontInfo","jsInfo","note","confirm","outro","init_esm_shims","init_shell","init_fs","join","init_config","init_esm_shims","mkdirSync","writeFileSync","join","createThemeScaffold","themePath","themeName","themeJson","landingTemplate","baseLayout","init_fetcher","setupTheme","intro","choice","select","themeName","themePath","workspaceDir","join","ensureDir","text","v","s","spinner","config","loadConfig","pak","getHubSpotPak","run","logError","fetchTheme","err","theme","createThemeScaffold","baseHtmlPath","fileExists","logSuccess","baseHtml","readFile","patched","logWarn","cssInsertPoint","insertBefore","jsLine","lineEnd","nextLine","block","insertAt","writeFile","hsignorePath","hsignore","outro","init_esm_shims","join","readdirSync","rmSync","init_esm_shims","init_prompts","init_fs","spawn","join","basename","readdirSync","statSync","writeFileSync","BOILERPLATE_TEMPLATES","ClaudeCodeEngine","model","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","sourceComponents","existingModules","existingCss","join","existingJs","existingTemplates","prompt","stdout","stderr","progressInterval","resolve","reject","env","args","child","spawn","d","err","code","logPath","logContent","writeFileSync","basename","result","m","outputPreview","stderrPreview","newItems","currentCss","f","key","currentJs","currentModules","mod","counter","currentTemplates","of","getHubspotRules","cssDir","fileExists","file","readdirSync","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","statSync","moduleFiles","fj","mj","mh","mc","mjs","e","dir","matches","srcDir","count","fullPath","init_esm_shims","init_prompts","init_fs","Anthropic","join","basename","readdirSync","ClaudeAPIEngine","apiKey","opts","sourceDir","themePath","conversionGuide","onProgress","systemPrompt","buildSystemPrompt","dirName","pagePrefix","indexCss","tailwindConfig","cssContent","buildCssPrompt","cssPath","writeFile","hooksSource","interactiveSource","jsContent","buildJsPrompt","jsPath","components","modules","i","comp","moduleName","source","readFile","response","buildModulePrompt","parsed","mod","modDir","ensureDir","moduleNames","m","templateContent","buildTemplatePrompt","templatePath","system","user","b","dir","paths","p","fileExists","hooksDir","f","interactive","content","searchDirs","searchDir","count","init_esm_shims","init_prompts","init_fs","spawn","join","readdirSync","statSync","GeminiCLIEngine","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","prompt","resolve","reject","child","stdout","stderr","d","err","code","result","cssDir","fileExists","file","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","moduleFiles","fj","mj","mh","mc","mjs","init_esm_shims","init_prompts","init_fs","spawn","join","readdirSync","statSync","CodexCLIEngine","opts","sourceDir","themePath","onProgress","guide","getConversionGuide","prompt","resolve","reject","child","stdout","stderr","d","err","code","result","cssDir","fileExists","file","readFile","jsDir","templatesDir","content","modulesDir","entry","modDir","moduleFiles","fj","mj","mh","mc","mjs","init_prompts","init_fs","createEngine","type","model","ClaudeCodeEngine","GeminiCLIEngine","CodexCLIEngine","ClaudeAPIEngine","runConversion","opts","intro","note","engine","conversionGuide","getConversionGuide","spinner","startTime","result","step","detail","logSuccess","elapsed","fixes","validateAndFix","fix","checklist","buildChecklist","lines","item","icon","severity","passed","c","criticalFailures","cosmeticFailures","logError","confirm","logWarn","logPath","join","fileExists","rmSync","outro","themePath","validateTemplates","validateModuleMeta","modulesDir","entry","readdirSync","fieldsPath","moduleName","content","readFile","changed","fields","jsonFixed","fixChoiceFields","fixLinkFields","writeFile","htmlPath","html","templatesDir","file","filePath","fixed","field","f","label","def","href","items","moduleCount","fieldsOk","m","allHaveHtml","missingCss","allHaveCss","hasStyleTab","templateAnnotated","hasTemplateType","hasAvailable","commentEnd","annotation","metaPath","meta","init_esm_shims","init_shell","join","basename","init_esm_shims","init_fs","join","readdirSync","rmSync","parseApiErrors","apiErrors","errors","err","msg","fixable","parseUploadErrors","output","fileMatch","fieldMatch","applyAutoFixes","themePath","fixes","fixTextareaFields","fixReservedNames","fixNowFunction","fixHubDbTemplates","fixLinkFieldDefaults","fixColorFieldDefaults","fixCdnImports","autoFixError","error","fixed","modulesDir","fileExists","entry","fieldsPath","content","readFile","writeFile","htmlPath","templatesDir","file","filePath","fields","fixLinkFieldsRecursive","cssDir","cleaned","cssPath","fixColorFieldsRecursive","field","f","def","colorVal","isValidHexColor","converted","convertToHex","color","hex3","rgba","r","g","b","hex","opacity","named","lower","href","init_config","init_esm_shims","init_api","readdirSync","join","relative","EXCLUDED_DIRS","walkDir","dir","files","entry","fullPath","parallelMap","items","concurrency","fn","index","worker","i","workers","uploadTheme","pak","themePath","themeName","opts","localFiles","total","uploaded","failed","errors","localPath","rel","remotePath","result","uploadFile","err","countUploadedFiles","output","runUpload","themePath","intro","themeName","basename","config","loadConfig","pak","getHubSpotPak","useApi","s","spinner","MAX_RETRIES","attempt","errors","uploadedCount","success","result","uploadTheme","parseApiErrors","run","join","fullOutput","parseUploadErrors","outro","logError","logWarn","confirm","anyFixed","error","autoFixError","logSuccess","deleteFile","init_esm_shims","execFileSync","rmSync","basename","init_fs","showNextSteps","opts","portalId","sourceDir","themePath","wasCloned","intro","host","detectDataCenter","note","theme","confirm","url","platform","execFileSync","logSuccess","log","dirsToClean","fileExists","basename","dir","rmSync","logWarn","outro","init_config","wizardCommand","printBanner","preflight","runPreflight","source","setupSource","saveConfig","themeInfo","setupTheme","runConversion","runUpload","showNextSteps","init_esm_shims","initCommand","printBanner","runPreflight","init_esm_shims","init_config","convertCommand","printBanner","config","loadConfig","logError","source","setupSource","themeInfo","setupTheme","runConversion","init_esm_shims","init_config","uploadCommand","printBanner","config","loadConfig","confirm","runUpload","path","text","v","init_esm_shims","init_config","doctorCommand","printBanner","intro","issues","node","detectNode","nodeVersionOk","logSuccess","logWarn","log","logError","git","detectGit","hs","detectHubSpotCLI","hsCliVersionOk","auth","detectHubSpotAuth","claude","detectClaudeCode","theme","gemini","detectGeminiCLI","codex","detectCodexCLI","config","loadConfig","anthropicKey","openaiKey","geminiKey","engineLabels","outro","init_esm_shims","join","existsSync","execFileSync","chalk","init_esm_shims","init_session","init_project_git","init_preview","init_ai_handler","init_config","createServer","readFileSync","existsSync","join","extname","createHash","WebSocketServer","init_esm_shims","spawn","jobs","_attachJobHandlers","child","job","timeout","d","code","err","startJobSafe","cmd","args","description","opts","id","startJob","command","parts","getJob","cleanupOldJobs","cutoff","startStreamingJob","emitChunk","chunk","listener","addJobListener","jobId","streamingJob","removeJobListener","init_route_helpers","init_fs","init_esm_shims","init_route_helpers","init_config","existsSync","readdirSync","rmSync","join","basename","homedir","execFileSync","init_fetcher","init_api","init_session","init_ai_handler","init_config","init_fs","_shellOpt","WORKSPACE_DIR","join","homedir","_themeListCache","THEME_LIST_TTL","getLocalThemes","themes","existsSync","entry","readdirSync","themeJson","moduleCount","modulesDir","e","handleSetupInfoRoute","res","session","getSession","env","detectEnvironment","hsInstalled","execFileSync","sessions","listSessions","b","localThemes","jsonResponse","handleSetupCreateRoute","req","readBody","body","isGenerating","name","themeName","themePath","ensureDir","rmSync","createThemeScaffold","createSession","saveSession","err","handleSetupFetchRoute","rawName","pak","getHubSpotPak","config","loadConfig","safeDirName","scanThemeFromDisk","fetchTheme","handleSetupOpenRoute","fullPath","basename","handleSetupResumeRoute","sessionId","loadSession","handleSetupApiKeyRoute","apiKey","saveConfig","handleSetupRemoteThemesRoute","folders","listRootFolders","checks","folder","folderPath","tjMeta","getMetadata","a","localNames","t","init_esm_shims","init_route_helpers","init_config","init_session","existsSync","readFileSync","appendFileSync","join","homedir","init_api","init_fs","modelCache","MODEL_CACHE_TTL","STATIC_MODELS","fetchAnthropicModels","apiKey","resp","m","fetchOpenAIModels","data","keep","a","b","fetchGeminiModels","getModelCatalog","config","loadConfig","catalog","jobs","anthropicKey","getApiKeyForEngine","models","openaiKey","geminiKey","handleSettingsStatusRoute","res","env","detectEnvironment","configPayload","sessionCount","listSessions","localThemeCount","getLocalThemes","version","getVersion","jsonResponse","handleSettingsEngineRoute","req","readBody","body","engine","model","configUpdate","saveConfig","err","handleSettingsApiKeyRoute","provider","autoSelectedEngine","handleSettingsInstallRoute","tool","installCommands","jobId","startJob","handleSettingsHsAuthRoute","parsed","uploadMode","validatePak","info","addHubSpotAccount","detectHubSpotCLI","startJobSafe","accounts","active","auth","detectHubSpotAuth","handleSettingsGhAuthRoute","detectGitHubCLI","detectGitHubAuth","handleSettingsHsSwitchRoute","portalId","action","removeHubSpotAccount","setActiveHubSpotAccount","safePortalId","handleSettingsGhLogoutRoute","handleSettingsCLIAuthRoute","cli","key","safeKey","profileLine","shellProfile","join","homedir","existsSync","readFileSync","appendFileSync","handleSettingsHsModeRoute","mode","handleSettingsCliToggleRoute","toolId","enabled","setCliToolEnabled","handleSettingsGenericRoute","allowedKeys","update","handleSettingsJobRoute","path","job","getJob","init_esm_shims","init_route_helpers","init_config","init_claude_oauth","handleClaudeOAuthSaveRoute","req","res","readBody","body","access_token","refresh_token","jsonResponse","saveInitialToken","config","loadConfig","saveConfig","err","handleClaudeOAuthStatusRoute","_req","authenticated","hasValidOAuthToken","info","getOAuthTokenInfo","handleClaudeOAuthLogoutRoute","clearOAuthTokens","init_esm_shims","init_route_helpers","init_session","existsSync","rmSync","join","handleThemesRoute","method","req","res","session","getSession","sessions","listSessions","a","b","jsonResponse","readBody","body","sessionId","deleteFiles","deleteSession","err","handleThemeSwitchRoute","loadSession","handleDeleteLocalThemeRoute","themeName","themePath","join","WORKSPACE_DIR","existsSync","rmSync","handleRenameThemeRoute","newName","sanitized","result","renameSession","init_esm_shims","init_route_helpers","init_log","init_config","init_session","init_fs","existsSync","readFileSync","rmSync","join","basename","execFileSync","_shellOpt","handleDashboardRoute","res","session","getSession","jsonResponse","library","getModuleLibrary","t","entry","handleDownloadZipRoute","themePath","themeName","parentDir","folderName","zipFileName","tmpZip","zipData","err","log","handleTemplatesRoute","method","req","readBody","body","pageType","label","addTemplate","saveSession","templateId","deleteModules","removeTemplate","handleTemplateActivateRoute","setActiveTemplate","getOrderedModules","m","handleTemplateRenameRoute","newLabel","renameTemplate","handleTemplateCloneRoute","cloneTemplate","handleModuleLibraryRoute","handleAddModuleToTemplateRoute","path","moduleName","e","modCopy","handleBrandAssetsRoute","type","content","filename","assetDir","ensureDir","writeFile","delFilename","filePath","saveBrandAsset","extractSingleAsset","sourcePath","extractDesignContext","resolveAgenticEngine","loadConfig","config","engine","apiKey","model","buildPreviewHtml","previewHtml","extractBrandvoice","extractThemeContext","handleDesignExtractRoute","parsed","types","results","extracted","i","r","handleReferenceImportRoute","source","localPath","pak","getHubSpotPak","cleanName","safeDirName","homedir","refDir","fetchTheme","styleguide","init_esm_shims","init_route_helpers","init_session","join","init_project_git","handleSessionRoute","method","res","session","getSession","jsonResponse","handleModulesRoute","req","ordered","getOrderedModules","m","readJsonBody","data","removeModule","detachModule","saveSession","handleCodeUpdateRoute","readBody","body","tpl","getActiveTemplate","writeModulesToDisk","moduleName","fileType","content","mod","err","handleReorderRoute","reorderModules","handleUploadRoute","fixes","applyAutoFixes","jobId","startStreamingJob","join","handleFieldRoute","fieldPath","value","updateFieldValue","handleImportRoute","url","analysis","analyzeSource","componentSummary","c","summary","handleHistoryRoute","isGitAvailable","templateId","commits","getTemplateHistory","getHistory","handleRollbackRoute","hash","addMessage","t","filePaths","n","result","rollbackTemplateToCommit","reloadActiveTemplateFromDisk","rollbackToCommit","reloadModulesFromDisk","init_upload_files","MIME_TYPES","startServer","opts","port","uiDir","server","createServer","req","res","handleRequest","wss","WebSocketServer","ws","handleWsConnection","resolve","reject","err","url","method","handleApiRoute","html","buildPreviewHtml","moduleName","buildModulePreviewHtml","serveThemeAsset","docPath","serveStatic","join","path","origin","handleSessionRoute","handleModulesRoute","handleReorderRoute","handleCodeUpdateRoute","handleUploadRoute","handleFileUploadRoute","jsonResponse","handleFieldRoute","handleImportRoute","handleSetupInfoRoute","handleSetupCreateRoute","handleSetupFetchRoute","handleSetupOpenRoute","handleSetupResumeRoute","handleSetupApiKeyRoute","handleSetupRemoteThemesRoute","handleSettingsStatusRoute","handleSettingsEngineRoute","handleSettingsApiKeyRoute","handleSettingsInstallRoute","handleSettingsHsAuthRoute","handleSettingsGhAuthRoute","handleSettingsHsSwitchRoute","handleSettingsGhLogoutRoute","handleSettingsCLIAuthRoute","handleSettingsHsModeRoute","handleSettingsCliToggleRoute","handleClaudeOAuthSaveRoute","handleClaudeOAuthStatusRoute","handleClaudeOAuthLogoutRoute","handleSettingsGenericRoute","getChangelog","handleThemesRoute","handleThemeSwitchRoute","handleDeleteLocalThemeRoute","handleRenameThemeRoute","handleHistoryRoute","handleRollbackRoute","handleDashboardRoute","handleTemplatesRoute","handleTemplateActivateRoute","handleTemplateRenameRoute","handleTemplateCloneRoute","handleModuleLibraryRoute","handleBrandAssetsRoute","handleDesignExtractRoute","handleReferenceImportRoute","handleDownloadZipRoute","handleSettingsJobRoute","handleAddModuleToTemplateRoute","data","msg","userMessage","addMessage","saveSession","fileIds","agenticCheck","shouldUseAgenticMode","pipelineSteps","pipelineModules","result","handleAgenticGenerate","event","moduleFiles","wsEvent","last","updateModules","reorderModules","getOrderedModules","applyPipelineResult","setParseWarningCallback","warning","handleGenerateStream","chunk","status","currentSession","getSession","writeModulesToDisk","activeTpl","getActiveTemplate","commitHash","filePaths","n","commitTemplateState","commitThemeState","m","sess","session","config","loadConfig","engine","apiKey","model","resolveAgenticEngine","previewHtml","extractThemeContext","themeContext","mkdirSync","writeFileSync","assetDir","existsSync","extractDesignContext","styleguide","fixes","applyAutoFixes","pak","getHubSpotPak","uploadTheme","completed","total","acct","getActiveHubSpotAccount","errors","parseApiErrors","e","jobId","startStreamingJob","chunkListener","addJobListener","pollInterval","job","getJob","removeJobListener","auth","detectHubSpotAuth","dc","detectDataCenter","parseUploadErrors","errorContext","fixPrompt","fixSession","fixHash","cfg","engineLabels","isGitAvailable","t","filename","filePath","ext","extname","contentType","buffer","readFileSync","serveStatic","pathname","uiDir","req","res","fullPath","join","existsSync","indexPath","content","readFileSync","ext","extname","contentType","MIME_TYPES","isHtml","buffer","etag","createHash","init_session","DEFAULT_PORT","vibeCommand","accent","chalk","dim","uiDir","resolveUiDir","port","close","startServer","url","execFileSync","resolve","saveSession","err","candidates","join","dir","existsSync","init_fs","buildProgram","program","Command","getVersion","vibeCommand","wizardCommand","initCommand","convertCommand","uploadCommand","doctorCommand","program","buildProgram","err"]}
|