@duffcloudservices/cli 0.1.4 → 0.3.0
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 +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../package.json","../src/commands/auth.ts","../src/auth/device-flow.ts","../src/auth/credentials.ts","../src/commands/sites.ts","../src/api/portal-client.ts","../src/commands/init.ts","../src/templates/site-deploy.yml","../src/commands/validate.ts","../src/commands/plans.ts","../src/commands/capture-snapshots.ts"],"sourcesContent":["/**\n * Main CLI entry point.\n */\n\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { version } from '../package.json'\n\n// Import commands\nimport { loginCommand, logoutCommand, whoamiCommand } from './commands/auth.js'\nimport { listSitesCommand, showSiteCommand } from './commands/sites.js'\nimport { initCommand } from './commands/init.js'\nimport { validateCommand } from './commands/validate.js'\nimport { plansCommand } from './commands/plans.js'\nimport { captureSnapshotsCommand } from './commands/capture-snapshots.js'\n\nconst program = new Command()\n\nprogram\n .name('dcs')\n .description('DCS (Duff Cloud Services) CLI for customer site management')\n .version(version)\n\n// =============================================================================\n// Auth Commands\n// =============================================================================\n\nprogram\n .command('login')\n .description('Authenticate with DCS Portal using Google OAuth')\n .action(async () => {\n await loginCommand()\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .action(async () => {\n await logoutCommand()\n })\n\nprogram\n .command('whoami')\n .description('Display current authenticated user')\n .action(async () => {\n await whoamiCommand()\n })\n\n// =============================================================================\n// Site Commands\n// =============================================================================\n\nconst sitesCmd = program\n .command('sites')\n .description('Manage customer sites')\n\nsitesCmd\n .command('list')\n .description('List sites you have access to')\n .action(async () => {\n await listSitesCommand()\n })\n\nsitesCmd\n .command('show <slug>')\n .description('Show details for a specific site')\n .action(async (slug: string) => {\n await showSiteCommand(slug)\n })\n\n// =============================================================================\n// Init Command\n// =============================================================================\n\nprogram\n .command('init')\n .description('Initialize DCS integration for a customer site')\n .requiredOption('-s, --site-slug <slug>', 'Site slug (lowercase, hyphens)')\n .requiredOption('-n, --site-name <name>', 'Human-readable site name')\n .option('-f, --framework <framework>', 'Site framework (vue, astro)', 'vue')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--dry-run', 'Show what would be created without writing files')\n .option('--force', 'Overwrite existing files')\n .action(async (options) => {\n await initCommand({\n siteSlug: options.siteSlug,\n siteName: options.siteName,\n framework: options.framework,\n target: options.target,\n dryRun: options.dryRun,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Validate Command\n// =============================================================================\n\nprogram\n .command('validate')\n .description('Validate .dcs configuration files')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--fix', 'Attempt to fix common issues')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n await validateCommand({\n target: options.target,\n fix: options.fix,\n verbose: options.verbose,\n })\n })\n\n// =============================================================================\n// Plans Command\n// =============================================================================\n\nprogram\n .command('plans')\n .description('Generate DCS integration plan files for AI-assisted onboarding')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--force', 'Overwrite existing plan files')\n .action(async (options) => {\n await plansCommand({\n target: options.target,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Capture Snapshots Command\n// =============================================================================\n\nprogram\n .command('capture-snapshots')\n .description('Capture visual snapshots of site pages for the portal page editor')\n .option('-t, --target <dir>', 'Target directory containing .dcs/pages.yaml', '.')\n .option('-u, --base-url <url>', 'Base URL of the running site', 'http://localhost:5173')\n .option('-p, --pages <slugs>', 'Comma-separated list of page slugs to capture')\n .option('--dry-run', 'Show what would be captured without launching browser')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n const pages = options.pages ? options.pages.split(',').map((s: string) => s.trim()) : undefined\n await captureSnapshotsCommand({\n target: options.target,\n baseUrl: options.baseUrl,\n dryRun: options.dryRun,\n verbose: options.verbose,\n pages,\n })\n })\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nprogram.exitOverride()\n\ntry {\n await program.parseAsync(process.argv)\n} catch (error) {\n if (error instanceof Error) {\n // Commander errors (help, version) are expected\n if ((error as { code?: string }).code === 'commander.helpDisplayed' ||\n (error as { code?: string }).code === 'commander.version') {\n process.exit(0)\n }\n\n console.error(chalk.red('Error:'), error.message)\n process.exit(1)\n }\n}\n","{\n \"name\": \"@duffcloudservices/cli\",\n \"version\": \"0.1.4\",\n \"description\": \"CLI for DCS site onboarding and configuration\",\n \"type\": \"module\",\n \"bin\": {\n \"dcs\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src --ext .ts\",\n \"prepublishOnly\": \"pnpm run build\"\n },\n \"dependencies\": {\n \"chalk\": \"^5.3.0\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^12.1.0\",\n \"conf\": \"^13.0.1\",\n \"handlebars\": \"^4.7.8\",\n \"inquirer\": \"^9.3.7\",\n \"js-yaml\": \"^4.1.0\",\n \"open\": \"^10.1.0\",\n \"ora\": \"^8.1.1\",\n \"playwright\": \"^1.49.0\"\n },\n \"devDependencies\": {\n \"@types/inquirer\": \"^9.0.7\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^20.11.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.6.3\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"dcs\",\n \"cli\",\n \"cms\",\n \"onboarding\",\n \"duff-cloud-services\"\n ],\n \"author\": \"Duff Cloud Services\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/duffn/dcs\"\n },\n \"homepage\": \"https://portal.duffcloudservices.com\",\n \"bugs\": {\n \"url\": \"https://github.com/duffn/dcs/issues\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","/**\n * Authentication commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport { login as performLogin, type DeviceFlowOptions } from '../auth/device-flow.js'\nimport { clearCredentials, getCurrentUserEmail, getConfigPath } from '../auth/credentials.js'\n\n/**\n * Login command - authenticate with DCS Portal.\n */\nexport async function loginCommand(options: DeviceFlowOptions = {}): Promise<void> {\n try {\n await performLogin(options)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Login failed:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Logout command - clear stored credentials.\n */\nexport async function logoutCommand(): Promise<void> {\n clearCredentials()\n console.log(chalk.green('✓'), 'Logged out successfully')\n}\n\n/**\n * Whoami command - display current user.\n */\nexport async function whoamiCommand(): Promise<void> {\n const email = getCurrentUserEmail()\n\n if (!email) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n return\n }\n\n console.log('Logged in as:', chalk.green(email))\n console.log(chalk.dim('Config file:'), chalk.dim(getConfigPath()))\n}\n","/**\n * Device authorization flow for CLI authentication.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant) style flow\n * for authenticating CLI users via the DCS Portal.\n */\n\nimport open from 'open'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { storeCredentials, type StoredCredentials } from './credentials.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface DeviceAuthResponse {\n deviceCode: string\n userCode: string\n verificationUri: string\n verificationUriComplete: string\n expiresIn: number\n interval: number\n}\n\nexport interface TokenResponse {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresIn: number\n email: string\n userId: string\n}\n\nexport interface DeviceFlowOptions {\n apiUrl?: string\n clientId?: string\n}\n\n/**\n * Error codes returned during device authorization polling.\n */\ntype DeviceAuthError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied'\n\n/**\n * Sleep for a given number of milliseconds.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Start the device authorization flow.\n */\nexport async function startDeviceAuth(options: DeviceFlowOptions = {}): Promise<DeviceAuthResponse> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n const clientId = options.clientId || 'dcs-cli'\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/device`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ client_id: clientId }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to start device authorization: ${error}`)\n }\n\n const data = await response.json()\n\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: data.verification_uri,\n verificationUriComplete: data.verification_uri_complete,\n expiresIn: data.expires_in,\n interval: data.interval,\n }\n}\n\n/**\n * Poll for authorization completion.\n */\nasync function pollForToken(\n deviceCode: string,\n apiUrl: string\n): Promise<TokenResponse | { error: DeviceAuthError }> {\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: deviceCode,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n })\n\n if (!response.ok) {\n const data = await response.json()\n return { error: data.error as DeviceAuthError }\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n tokenType: data.token_type,\n expiresIn: data.expires_in,\n email: data.email,\n userId: data.user_id,\n }\n}\n\n/**\n * Complete the device authorization flow.\n *\n * This function:\n * 1. Starts the device authorization\n * 2. Opens the browser to the verification URL\n * 3. Polls for completion\n * 4. Stores credentials on success\n */\nexport async function login(options: DeviceFlowOptions = {}): Promise<StoredCredentials> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const spinner = ora('Starting authentication...').start()\n\n // Start device auth\n let deviceAuth: DeviceAuthResponse\n try {\n deviceAuth = await startDeviceAuth(options)\n } catch (error) {\n spinner.fail('Failed to start authentication')\n throw error\n }\n\n spinner.stop()\n\n // Display instructions\n console.log()\n console.log(chalk.bold('Please visit:'), chalk.cyan(deviceAuth.verificationUri))\n console.log(chalk.bold('And enter code:'), chalk.yellow.bold(deviceAuth.userCode))\n console.log()\n\n // Try to open browser\n try {\n await open(deviceAuth.verificationUriComplete)\n console.log(chalk.dim('(Browser opened automatically)'))\n } catch {\n console.log(chalk.dim('(Please open the link manually)'))\n }\n\n console.log()\n\n // Poll for completion\n const pollSpinner = ora('Waiting for authentication...').start()\n\n let interval = deviceAuth.interval\n const deadline = Date.now() + deviceAuth.expiresIn * 1000\n\n while (Date.now() < deadline) {\n await sleep(interval * 1000)\n\n try {\n const result = await pollForToken(deviceAuth.deviceCode, apiUrl)\n\n if ('error' in result) {\n if (result.error === 'authorization_pending') {\n continue\n }\n if (result.error === 'slow_down') {\n interval += 5\n continue\n }\n if (result.error === 'expired_token') {\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n }\n if (result.error === 'access_denied') {\n pollSpinner.fail('Authentication denied')\n throw new Error('Authentication was denied.')\n }\n } else {\n // Success!\n const credentials: StoredCredentials = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: Date.now() + result.expiresIn * 1000,\n email: result.email,\n userId: result.userId,\n }\n\n storeCredentials(credentials)\n\n pollSpinner.succeed(`Authenticated as ${chalk.green(result.email)}`)\n return credentials\n }\n } catch (error) {\n // Network error - keep polling\n if (error instanceof Error && error.message.includes('fetch')) {\n continue\n }\n throw error\n }\n }\n\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n}\n\n/**\n * Refresh the access token using the refresh token.\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n options: DeviceFlowOptions = {}\n): Promise<{ accessToken: string; expiresIn: number }> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n\n if (!response.ok) {\n throw new Error('Failed to refresh access token. Please login again.')\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n expiresIn: data.expires_in,\n }\n}\n","/**\n * Credential storage for CLI authentication.\n *\n * Stores access tokens, refresh tokens, and user info in a config file.\n * Uses the `conf` package for encrypted storage on supported platforms.\n */\n\nimport Conf from 'conf'\n\nexport interface StoredCredentials {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n userId: string\n}\n\ninterface ConfigSchema {\n auth?: StoredCredentials\n}\n\nconst config = new Conf<ConfigSchema>({\n projectName: 'dcs-cli',\n schema: {\n auth: {\n type: 'object',\n properties: {\n accessToken: { type: 'string' },\n refreshToken: { type: 'string' },\n expiresAt: { type: 'number' },\n email: { type: 'string' },\n userId: { type: 'string' },\n },\n },\n },\n})\n\n/**\n * Store credentials after successful authentication.\n */\nexport function storeCredentials(credentials: StoredCredentials): void {\n config.set('auth', credentials)\n}\n\n/**\n * Get stored credentials.\n * Returns undefined if not authenticated.\n */\nexport function getCredentials(): StoredCredentials | undefined {\n return config.get('auth')\n}\n\n/**\n * Clear stored credentials (logout).\n */\nexport function clearCredentials(): void {\n config.delete('auth')\n}\n\n/**\n * Check if user is authenticated with valid (non-expired) credentials.\n */\nexport function isAuthenticated(): boolean {\n const creds = getCredentials()\n if (!creds) return false\n\n // Check if access token is expired (with 5 minute buffer)\n const bufferMs = 5 * 60 * 1000\n return creds.expiresAt > Date.now() + bufferMs\n}\n\n/**\n * Get the current user's email if authenticated.\n */\nexport function getCurrentUserEmail(): string | undefined {\n return getCredentials()?.email\n}\n\n/**\n * Get the current user's ID if authenticated.\n */\nexport function getCurrentUserId(): string | undefined {\n return getCredentials()?.userId\n}\n\n/**\n * Get the access token if available and not expired.\n */\nexport function getAccessToken(): string | undefined {\n const creds = getCredentials()\n if (!creds) return undefined\n\n // Return even if expired - the API client will handle refresh\n return creds.accessToken\n}\n\n/**\n * Get the refresh token if available.\n */\nexport function getRefreshToken(): string | undefined {\n return getCredentials()?.refreshToken\n}\n\n/**\n * Update the access token after a refresh.\n */\nexport function updateAccessToken(accessToken: string, expiresIn: number): void {\n const creds = getCredentials()\n if (!creds) return\n\n config.set('auth', {\n ...creds,\n accessToken,\n expiresAt: Date.now() + expiresIn * 1000,\n })\n}\n\n/**\n * Get the path to the config file (for debugging).\n */\nexport function getConfigPath(): string {\n return config.path\n}\n","/**\n * Sites commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport Table from 'cli-table3'\nimport { getPortalClient, type Site } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\n\n/**\n * List sites command - display sites the user has access to.\n */\nexport async function listSitesCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n\n if (sites.length === 0) {\n console.log(chalk.yellow('No sites found.'))\n console.log('Ask your company admin to grant you access to sites.')\n return\n }\n\n console.log(chalk.bold('\\nYour Sites:\\n'))\n\n const table = new Table({\n head: [\n chalk.cyan('Slug'),\n chalk.cyan('Name'),\n chalk.cyan('Role'),\n chalk.cyan('Company'),\n chalk.cyan('URL'),\n ],\n style: {\n head: [],\n border: [],\n },\n })\n\n for (const site of sites) {\n table.push([\n site.slug,\n site.name,\n formatRole(site.role),\n site.companyName,\n site.productionUrl || chalk.dim('(not deployed)'),\n ])\n }\n\n console.log(table.toString())\n console.log()\n console.log(chalk.dim(`Total: ${sites.length} site(s)`))\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Show site details command.\n */\nexport async function showSiteCommand(slug: string): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const site = await client.getSite(slug)\n displaySiteDetails(site)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\nfunction formatRole(role: string): string {\n switch (role) {\n case 'owner':\n return chalk.magenta(role)\n case 'admin':\n return chalk.red(role)\n case 'editor':\n return chalk.yellow(role)\n case 'viewer':\n return chalk.blue(role)\n default:\n return role\n }\n}\n\nfunction displaySiteDetails(site: Site): void {\n console.log()\n console.log(chalk.bold('Site Details'))\n console.log()\n console.log(' Slug: ', chalk.cyan(site.slug))\n console.log(' Name: ', site.name)\n console.log(' Role: ', formatRole(site.role))\n console.log(' Company: ', site.companyName)\n console.log(' Company ID: ', chalk.dim(site.companyId))\n if (site.productionUrl) {\n console.log(' URL: ', chalk.green(site.productionUrl))\n } else {\n console.log(' URL: ', chalk.dim('(not deployed)'))\n }\n console.log()\n}\n","/**\n * Portal API client for CLI operations.\n */\n\nimport { getAccessToken, getRefreshToken, updateAccessToken, isAuthenticated } from '../auth/credentials.js'\nimport { refreshAccessToken } from '../auth/device-flow.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface Site {\n slug: string\n name: string\n role: 'owner' | 'admin' | 'editor' | 'viewer'\n companyId: string\n companyName: string\n productionUrl?: string\n}\n\nexport interface PortalClientOptions {\n apiUrl?: string\n}\n\nexport class PortalClient {\n private apiUrl: string\n\n constructor(options: PortalClientOptions = {}) {\n this.apiUrl = options.apiUrl || process.env.DCS_API_URL || DEFAULT_API_URL\n }\n\n /**\n * Make an authenticated API request.\n */\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n // Get access token\n let accessToken = getAccessToken()\n\n // Check if we need to refresh\n if (!isAuthenticated() && getRefreshToken()) {\n const refreshToken = getRefreshToken()!\n try {\n const { accessToken: newToken, expiresIn } = await refreshAccessToken(refreshToken, {\n apiUrl: this.apiUrl,\n })\n updateAccessToken(newToken, expiresIn)\n accessToken = newToken\n } catch {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n }\n\n if (!accessToken) {\n throw new Error('Not authenticated. Please login with `dcs login`.')\n }\n\n const response = await fetch(`${this.apiUrl}${path}`, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (response.status === 401) {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`API error (${response.status}): ${error}`)\n }\n\n return response.json()\n }\n\n /**\n * List sites accessible to the authenticated user.\n */\n async listSites(): Promise<{ sites: Site[] }> {\n return this.request('/api/v1/cli/sites')\n }\n\n /**\n * Get details for a specific site.\n */\n async getSite(slug: string): Promise<Site> {\n return this.request(`/api/v1/cli/sites/${slug}`)\n }\n\n /**\n * Validate that the user can create/modify a site.\n */\n async validateSiteAccess(\n slug: string,\n operation: 'read' | 'edit' | 'create'\n ): Promise<{ valid: boolean; role?: string; message?: string }> {\n try {\n const { sites } = await this.listSites()\n const site = sites.find((s) => s.slug === slug)\n\n if (!site) {\n if (operation === 'create') {\n // Check if user has any admin access to create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n return canCreate\n ? { valid: true, message: 'New site will be created' }\n : { valid: false, message: 'You need admin access in a company to create new sites' }\n }\n return { valid: false, message: `Site '${slug}' not found or you don't have access` }\n }\n\n if (operation === 'read') {\n return { valid: true, role: site.role }\n }\n\n if (operation === 'edit') {\n const canEdit = ['owner', 'admin', 'editor'].includes(site.role)\n return canEdit\n ? { valid: true, role: site.role }\n : { valid: false, role: site.role, message: 'You need editor access to modify this site' }\n }\n\n if (operation === 'create') {\n return { valid: false, message: 'Site already exists' }\n }\n\n return { valid: false }\n } catch (error) {\n if (error instanceof Error) {\n return { valid: false, message: error.message }\n }\n return { valid: false, message: 'Unknown error' }\n }\n }\n}\n\n// Singleton instance\nlet clientInstance: PortalClient | null = null\n\nexport function getPortalClient(options?: PortalClientOptions): PortalClient {\n if (!clientInstance) {\n clientInstance = new PortalClient(options)\n }\n return clientInstance\n}\n","/**\n * Init command - initialize DCS integration for a customer site.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { getPortalClient } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\nimport siteDeployTemplate from '../templates/site-deploy.yml'\n\nexport interface InitOptions {\n siteSlug: string\n siteName: string\n framework?: 'vue' | 'astro'\n target?: string\n dryRun?: boolean\n force?: boolean\n}\n\n/**\n * Initialize DCS integration for a customer site.\n */\nexport async function initCommand(options: InitOptions): Promise<void> {\n const { siteSlug, siteName, framework = 'vue', target = '.', dryRun = false, force = false } = options\n\n // 1. Check authentication\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n // 2. Validate site access\n const spinner = ora('Validating site access...').start()\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n const existingSite = sites.find((s) => s.slug === siteSlug)\n\n if (existingSite) {\n if (!['owner', 'admin', 'editor'].includes(existingSite.role)) {\n spinner.fail(`You don't have edit access to site '${siteSlug}'`)\n process.exit(1)\n }\n spinner.succeed(`Site '${siteSlug}' found (role: ${existingSite.role})`)\n } else {\n // Check if user can create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n if (!canCreate) {\n spinner.fail('You need admin access in a company to create new sites')\n process.exit(1)\n }\n spinner.info(`Creating new site: ${siteSlug}`)\n }\n } catch (error) {\n spinner.fail('Failed to validate site access')\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 3. Resolve target directory\n const targetDir = path.resolve(target)\n console.log(chalk.dim(`Target directory: ${targetDir}`))\n\n // 4. Generate configuration files\n const generatedFiles: string[] = []\n const timestamp = new Date().toISOString()\n\n try {\n // .dcs/site.yaml\n const siteYaml = generateSiteYaml({ siteSlug, siteName, timestamp, framework })\n generatedFiles.push(await writeFile(targetDir, '.dcs/site.yaml', siteYaml, { dryRun, force }))\n\n // .dcs/pages.yaml\n const pagesYaml = generatePagesYaml({ siteSlug, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/pages.yaml', pagesYaml, { dryRun, force }))\n\n // .dcs/content.yaml\n const contentYaml = generateContentYaml({ timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/content.yaml', contentYaml, { dryRun, force }))\n\n // .dcs/seo.yaml\n const seoYaml = generateSeoYaml({ siteName, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/seo.yaml', seoYaml, { dryRun, force }))\n\n // .dcs/SECTION-CONVENTIONS.md\n const sectionConventions = generateSectionConventions()\n generatedFiles.push(\n await writeFile(targetDir, '.dcs/SECTION-CONVENTIONS.md', sectionConventions, { dryRun, force })\n )\n\n // .github/copilot-instructions.md\n const copilotInstructions = generateCopilotInstructions({ siteName })\n generatedFiles.push(\n await writeFile(targetDir, '.github/copilot-instructions.md', copilotInstructions, { dryRun, force })\n )\n\n // .github/workflows/site-deploy.yml\n const deployWorkflow = generateDeployWorkflow()\n generatedFiles.push(\n await writeFile(targetDir, '.github/workflows/site-deploy.yml', deployWorkflow, { dryRun, force })\n )\n\n // .plans/README.md\n const plansReadme = generatePlansReadme({ siteName, framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/README.md', plansReadme, { dryRun, force }))\n\n // .plans/00-audit-site.md\n const plan00 = generatePlan00AuditSite({ framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/00-audit-site.md', plan00, { dryRun, force }))\n\n // .plans/01-integrate-content.md\n const plan01 = generatePlan01IntegrateContent()\n generatedFiles.push(await writeFile(targetDir, '.plans/01-integrate-content.md', plan01, { dryRun, force }))\n\n // .plans/02-configure-seo.md\n const plan02 = generatePlan02ConfigureSeo()\n generatedFiles.push(await writeFile(targetDir, '.plans/02-configure-seo.md', plan02, { dryRun, force }))\n\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 5. Display results\n console.log()\n console.log(chalk.bold('Generated files:'))\n for (const file of generatedFiles) {\n console.log(` ${chalk.green('✓')} ${file}`)\n }\n\n // 6. Post-init instructions\n console.log()\n console.log(chalk.bold('Next steps:'))\n console.log(' 1. Install CMS package:', chalk.cyan('pnpm add @duffcloudservices/cms'))\n console.log(' 2. Configure Vite plugins in your build config')\n console.log(' 3. Follow the plans in', chalk.cyan('.plans/'), 'directory')\n console.log()\n}\n\n// =============================================================================\n// File Generation Helpers\n// =============================================================================\n\ninterface WriteOptions {\n dryRun: boolean\n force: boolean\n}\n\nasync function writeFile(\n targetDir: string,\n relativePath: string,\n content: string,\n options: WriteOptions\n): Promise<string> {\n const fullPath = path.join(targetDir, relativePath)\n\n if (options.dryRun) {\n console.log(chalk.dim(`Would create: ${relativePath}`))\n return relativePath\n }\n\n // Check if file exists\n try {\n await fs.access(fullPath)\n if (!options.force) {\n console.log(chalk.yellow(`Skipped (exists): ${relativePath}`))\n return relativePath\n }\n } catch {\n // File doesn't exist, continue\n }\n\n // Create directory if needed\n await fs.mkdir(path.dirname(fullPath), { recursive: true })\n\n // Write file\n await fs.writeFile(fullPath, content, 'utf8')\n\n return relativePath\n}\n\n// =============================================================================\n// Template Generators\n// =============================================================================\n\nfunction generateSiteYaml(data: {\n siteSlug: string\n siteName: string\n timestamp: string\n framework: string\n}): string {\n return `# DCS Site Configuration\n# This file defines the site identity and Azure resource bindings.\n# Generated by: @duffcloudservices/cli\n\nsite_name: ${data.siteName}\nsite_slug: ${data.siteSlug}\n\n# Azure Static Web App resource ID\n# Set this after provisioning the SWA resource in Azure\nswa_resource_id: \"\"\n\n# URLs are populated after first deployment\nproduction_url: \"\"\npreview_url: \"\"\n\n# Google Analytics Configuration\ngoogle_analytics_id: \"\"\n\n# Azure Authentication (Managed Identity)\n# These values are used by GitHub Actions for OIDC authentication\nazure:\n client_id: \"\"\n tenant_id: \"\"\n subscription_id: \"\"\n\n# Portal API Configuration\nportal_api_url: \"https://portal.duffcloudservices.com\"\n\n# Site metadata for portal\nmetadata:\n framework: ${data.framework}\n managed_by: dcs\n created_at: \"${data.timestamp}\"\n`\n}\n\nfunction generatePagesYaml(data: { siteSlug: string; timestamp: string }): string {\n return `# .dcs/pages.yaml\n# Page registry for DCS CMS integration and snapshot capture.\n# Generated by: @duffcloudservices/cli\n#\n# This file defines page metadata only. Text content is stored in content.yaml\n# and text keys are auto-discovered during snapshot capture.\n\nversion: 3\nsiteSlug: ${data.siteSlug}\nlastUpdated: \"${data.timestamp}\"\ngeneratedBy: dcs-cli\n\npages:\n # Home page - always protected\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n\n# Excluded paths - never captured\nexcluded:\n - /404\n - /dev-*\n - /_*\n\n# Snapshot configuration\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000\n captureFullPage: true\n`\n}\n\nfunction generateContentYaml(data: { timestamp: string }): string {\n return `# .dcs/content.yaml\n# Managed by DCS Portal - Do not edit manually\n# This file contains all text content for the site.\n#\n# Content is organized by page slug, with each page containing\n# key-value pairs for editable text. The \\`global\\` section contains\n# text shared across all pages (navigation, footer, etc.).\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global text content (shared across all pages)\nglobal: {}\n\n# Page-specific text content\npages: {}\n`\n}\n\nfunction generateSeoYaml(data: { siteName: string; timestamp: string }): string {\n return `# .dcs/seo.yaml\n# Managed by DCS Portal - Do not edit manually\n# SEO configuration for all pages and global defaults.\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global/site-wide SEO defaults\nglobal:\n siteName: \"${data.siteName}\"\n siteUrl: \"\" # Set your production URL\n locale: en_US\n defaultTitle: \"${data.siteName}\"\n defaultDescription: \"\"\n titleTemplate: \"%s | ${data.siteName}\"\n\n # Social media handles\n social:\n twitter: \"\"\n linkedin: \"\"\n\n # Default images for social sharing\n images:\n logo: \"\"\n ogDefault: \"\"\n twitterDefault: \"\"\n\n # Default robots directive\n robots: \"index, follow\"\n\n# Page-specific SEO configurations\npages:\n home:\n title: Home\n description: \"\"\n noTitleTemplate: true\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n`\n}\n\nfunction generateSectionConventions(): string {\n return `# Section Conventions for DCS CMS Integration\n\nThis document describes the HTML attributes used by the DCS visual page editor\nfor section identification and snapshot capture.\n\n## Required Attributes\n\n### \\`data-dcs-section\\`\n\nMarks an element as an editable section. The value should match the section's\nidentifier in content.yaml.\n\n\\`\\`\\`html\n<section data-dcs-section=\"hero\">\n <!-- Section content -->\n</section>\n\\`\\`\\`\n\n### \\`data-dcs-text\\`\n\nMarks an element's text as editable via the portal. The value should be the\ntext key from content.yaml.\n\n\\`\\`\\`html\n<h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n\\`\\`\\`\n\n## Best Practices\n\n1. Use semantic section names that describe the content\n2. Keep text keys hierarchical (section.element)\n3. Ensure all user-editable text has a data-dcs-text attribute\n4. Don't nest sections unnecessarily\n`\n}\n\nfunction generateCopilotInstructions(data: { siteName: string }): string {\n return `# GitHub Copilot Instructions for ${data.siteName}\n\n## Project Overview\n\nThis is a **DCS-managed customer site**. Content and feature changes are managed\nthrough the DCS Portal at https://portal.duffcloudservices.com.\n\n## Technology Stack\n\n- Vue 3 + Composition API\n- VitePress (or Vite)\n- @duffcloudservices/cms for content management\n- Azure Static Web Apps for hosting\n\n## Content Management\n\n### Text Content\n\nUse the \\`useTextContent\\` composable for all editable text:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Default Title',\n 'hero.subtitle': 'Default subtitle'\n }\n})\n</script>\n\n<template>\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n</template>\n\\`\\`\\`\n\n### SEO\n\nUse the \\`useSEO\\` composable for page SEO:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n## Key Conventions\n\n1. All user-editable text must use \\`useTextContent\\`\n2. All pages must have SEO configured via \\`useSEO\\`\n3. Section elements need \\`data-dcs-section\\` attributes\n4. Text elements need \\`data-dcs-text\\` attributes\n5. See \\`.dcs/SECTION-CONVENTIONS.md\\` for details\n`\n}\n\nfunction generatePlansReadme(data: { siteName: string; framework: string }): string {\n return `# DCS Integration Plans for ${data.siteName}\n\nThis directory contains step-by-step plans for integrating this site with the\nDCS content management system.\n\n## Overview\n\nThese plans guide AI assistants (like GitHub Copilot) through the integration\nprocess. Follow them in order for best results.\n\n## Plans\n\n1. **00-audit-site.md** - Analyze current site structure\n2. **01-create-use-text-content.md** - Set up the text content composable\n3. **02-integrate-home-page.md** - Add CMS integration to home page\n4. **03-integrate-remaining-pages.md** - Complete other pages\n5. **04-capture-snapshots.md** - Configure visual editor snapshots\n6. **05-verify-deployment.md** - Test and verify deployment\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Getting Started\n\n1. Open plan 00 and ask Copilot to execute it\n2. Review the output and make any needed adjustments\n3. Continue with subsequent plans\n\n## Configuration Files\n\n- \\`.dcs/site.yaml\\` - Site identity and Azure config\n- \\`.dcs/pages.yaml\\` - Page registry for CMS\n- \\`.dcs/content.yaml\\` - Text content (managed by Portal)\n- \\`.dcs/seo.yaml\\` - SEO configuration (managed by Portal)\n`\n}\n\nfunction generateDeployWorkflow(): string {\n return siteDeployTemplate\n}\n\nfunction generatePlan00AuditSite(data: { framework: string }): string {\n return `# Plan 00: Audit Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand what pages exist, their routes,\nand identify all text content that should be managed via DCS.\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Tasks\n\n### 1. List All Pages\n\nIdentify all page components/routes in the project:\n\n- For Vue/VitePress: Look in \\`src/views/\\`, \\`src/pages/\\`, or \\`.vitepress/\\`\n- For Astro: Look in \\`src/pages/\\`\n\nCreate a list of:\n- Route path (e.g., \\`/\\`, \\`/about\\`, \\`/services\\`)\n- Component file path\n- Page title (from existing meta or content)\n\n### 2. Identify Text Content\n\nFor each page, list all user-facing text that should be editable:\n\n- Headings (h1, h2, etc.)\n- Paragraphs and body text\n- Button labels\n- Navigation links\n- Footer content\n\nUse this naming convention for text keys:\n\\`\\`\\`\n{pageSlug}.{section}.{element}\n\nExamples:\n- home.hero.title\n- home.hero.subtitle\n- home.cta.buttonText\n- about.intro.heading\n\\`\\`\\`\n\n### 3. Update pages.yaml\n\nAdd all discovered pages to \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n \n - slug: about\n path: /about\n type: static\n title: About Us\n deletable: true\n\\`\\`\\`\n\n### 4. Document Findings\n\nCreate a brief summary of:\n- Total number of pages\n- Estimated number of text keys per page\n- Any complex sections (carousels, tabs, etc.)\n- Shared components that contain text (header, footer)\n\n## Output\n\nAfter completing this plan, you should have:\n1. Updated \\`.dcs/pages.yaml\\` with all pages\n2. A mental map of text content to migrate\n3. Understanding of component structure\n`\n}\n\nfunction generatePlan01IntegrateContent(): string {\n return `# Plan 01: Integrate Text Content\n\n## Objective\n\nSet up the \\`useTextContent\\` composable and migrate all static text to the\nDCS content management system.\n\n## Prerequisites\n\n- Completed Plan 00 (site audit)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Install DCS CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### 2. Configure Vite Plugin\n\nAdd the DCS content plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({\n contentPath: '.dcs/content.yaml',\n }),\n ],\n})\n\\`\\`\\`\n\n### 3. Integrate Home Page\n\nStart with the home page as a template:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Your Default Title',\n 'hero.subtitle': 'Your default subtitle text',\n 'hero.cta': 'Get Started',\n }\n})\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### 4. Key Patterns\n\n**Text Keys**: Use hierarchical naming\n- \\`{section}.{element}\\` for page-specific content\n- Prefix with \\`global.\\` for shared content\n\n**Default Values**: Always provide defaults that match current content\n- This ensures the site works before CMS content is loaded\n- Defaults serve as fallback if content fetch fails\n\n**Data Attributes**: Required for visual editor\n- \\`data-dcs-section\\` on section containers\n- \\`data-dcs-text\\` on text elements (value = text key)\n\n### 5. Migrate Remaining Pages\n\nApply the same pattern to all pages identified in Plan 00.\n\nFor each page:\n1. Import \\`useTextContent\\`\n2. Define defaults for all text content\n3. Replace static text with \\`{{ t('key') }}\\`\n4. Add \\`data-dcs-text\\` attributes\n\n### 6. Handle Global Content\n\nCreate a shared composable for navigation/footer:\n\n\\`\\`\\`typescript\n// composables/useGlobalContent.ts\nimport { useTextContent } from '@duffcloudservices/cms'\n\nexport function useGlobalContent() {\n return useTextContent({\n pageSlug: 'global',\n defaults: {\n 'nav.home': 'Home',\n 'nav.about': 'About',\n 'footer.copyright': '© 2025 Company Name',\n }\n })\n}\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. Run \\`pnpm dev\\` - site should display normally with defaults\n2. Check browser console for any missing text key warnings\n3. Verify all text elements have \\`data-dcs-text\\` attributes\n`\n}\n\nfunction generatePlan02ConfigureSeo(): string {\n return `# Plan 02: Configure SEO\n\n## Objective\n\nSet up the \\`useSEO\\` composable to manage page metadata through DCS Portal.\n\n## Prerequisites\n\n- Completed Plan 01 (content integration)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Configure Vite Plugin\n\nAdd the DCS SEO plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ contentPath: '.dcs/content.yaml' }),\n dcsSeoPlugin({ seoPath: '.dcs/seo.yaml' }),\n ],\n})\n\\`\\`\\`\n\n### 2. Add SEO to Pages\n\nFor each page, add the \\`useSEO\\` composable:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\n// Content setup\nconst { t } = useTextContent({ pageSlug: 'home', defaults: { ... } })\n\n// SEO setup - applies head tags automatically\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n### 3. Configure Default SEO\n\nUpdate \\`.dcs/seo.yaml\\` with site-wide defaults:\n\n\\`\\`\\`yaml\nglobal:\n siteName: \"Your Site Name\"\n siteUrl: \"https://yoursite.com\"\n locale: en_US\n defaultTitle: \"Your Site Name\"\n defaultDescription: \"Your site description for search engines\"\n titleTemplate: \"%s | Your Site Name\"\n \n social:\n twitter: \"@yourhandle\"\n linkedin: \"company/yourcompany\"\n \n images:\n logo: \"/images/logo.png\"\n ogDefault: \"/images/og-default.png\"\n\npages:\n home:\n title: Home\n description: \"Welcome to our site\"\n noTitleTemplate: true # Home page often skips \" | Site Name\"\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n \n about:\n title: About Us\n description: \"Learn more about our company\"\n\\`\\`\\`\n\n### 4. Page-Specific Overrides\n\nEach page can have custom SEO settings that override globals:\n\n\\`\\`\\`yaml\npages:\n services:\n title: Our Services\n description: \"Explore our professional services\"\n canonical: \"https://yoursite.com/services\"\n openGraph:\n title: \"Services - Custom OG Title\"\n description: \"Custom description for social sharing\"\n image: \"/images/services-og.png\"\n\\`\\`\\`\n\n### 5. Dynamic Pages (Blogs, etc.)\n\nFor dynamic pages like blog posts, you'll pass SEO data dynamically:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst props = defineProps<{ post: BlogPost }>()\n\nconst { applyHead } = useSEO('blog-post', {\n title: props.post.title,\n description: props.post.excerpt,\n openGraph: {\n type: 'article',\n image: props.post.featuredImage,\n }\n})\napplyHead()\n</script>\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. View page source - check for meta tags\n2. Use browser dev tools to inspect \\`<head>\\`\n3. Test with social sharing debuggers (Facebook, Twitter, LinkedIn)\n4. Validate with Google's Rich Results Test\n`\n}\n","# Site Deployment Workflow\n# Generated by @duffcloudservices/cli\n# This workflow deploys the site to Azure Static Web Apps using OIDC authentication.\n\nname: Site Deployment\n\non:\n push:\n branches:\n - 'release/**'\n - 'master'\n workflow_dispatch:\n inputs:\n trigger_reason:\n description: 'Reason for manual deployment'\n required: false\n type: string\n default: 'Manual deployment'\n\n# Prevent concurrent deployments for the same branch\nconcurrency:\n group: site-deploy-${{ github.ref }}\n cancel-in-progress: false\n\npermissions:\n contents: read\n issues: write\n pull-requests: read\n id-token: write # Required for Azure OIDC authentication\n\njobs:\n deploy:\n name: Deploy to Azure Static Web App\n runs-on: ubuntu-latest\n # Skip deployment on branch creation events\n if: github.event.before != '0000000000000000000000000000000000000000'\n\n steps:\n - name: Determine deployment environment\n id: environment\n run: |\n BRANCH=\"${{ github.ref_name }}\"\n echo \"Branch: $BRANCH\"\n \n if [ \"$BRANCH\" == \"master\" ]; then\n echo \"environment=Production\" >> $GITHUB_OUTPUT\n echo \"environment_name=Production\" >> $GITHUB_OUTPUT\n echo \"swa_environment=\" >> $GITHUB_OUTPUT\n else\n echo \"environment=preview\" >> $GITHUB_OUTPUT\n echo \"environment_name=Preview\" >> $GITHUB_OUTPUT\n echo \"swa_environment=preview\" >> $GITHUB_OUTPUT\n fi\n\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Read site configuration\n id: config\n run: |\n CONFIG_FILE=\".dcs/site.yaml\"\n \n if [ ! -f \"$CONFIG_FILE\" ]; then\n echo \"::error::Missing DCS site configuration file: $CONFIG_FILE\"\n exit 1\n fi\n \n SITE_NAME=$(grep -E '^site_name:' \"$CONFIG_FILE\" | sed 's/site_name:\\s*//' | tr -d '\"' | tr -d \"'\")\n SITE_SLUG=$(grep -E '^site_slug:' \"$CONFIG_FILE\" | sed 's/site_slug:\\s*//' | tr -d '\"' | tr -d \"'\")\n SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' \"$CONFIG_FILE\" | sed 's/swa_resource_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=$(grep -E '^portal_api_url:' \"$CONFIG_FILE\" | sed 's/portal_api_url:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=\"${PORTAL_API_URL:-https://portal.duffcloudservices.com}\"\n GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' \"$CONFIG_FILE\" | sed 's/google_analytics_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PROD_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^prod_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/prod_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n DEV_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^dev_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/dev_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n \n # Azure Authentication\n AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' \"$CONFIG_FILE\" | sed 's/.*client_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' \"$CONFIG_FILE\" | sed 's/.*tenant_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' \"$CONFIG_FILE\" | sed 's/.*subscription_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n # Build Configuration\n APP_LOCATION=$(grep -E '^\\s*app_location:' \"$CONFIG_FILE\" | sed 's/.*app_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' \"$CONFIG_FILE\" | sed 's/.*output_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n APP_LOCATION=\"${APP_LOCATION:-dist}\"\n OUTPUT_LOCATION=\"${OUTPUT_LOCATION:-.}\"\n\n if [ -z \"$SWA_RESOURCE_ID\" ]; then\n echo \"::error::Missing swa_resource_id in $CONFIG_FILE\"\n exit 1\n fi\n \n echo \"site_name=$SITE_NAME\" >> $GITHUB_OUTPUT\n echo \"site_slug=$SITE_SLUG\" >> $GITHUB_OUTPUT\n echo \"swa_resource_id=$SWA_RESOURCE_ID\" >> $GITHUB_OUTPUT\n echo \"app_location=$APP_LOCATION\" >> $GITHUB_OUTPUT\n echo \"output_location=$OUTPUT_LOCATION\" >> $GITHUB_OUTPUT\n echo \"azure_client_id=$AZURE_CLIENT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_tenant_id=$AZURE_TENANT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_subscription_id=$AZURE_SUBSCRIPTION_ID\" >> $GITHUB_OUTPUT\n echo \"portal_api_url=$PORTAL_API_URL\" >> $GITHUB_OUTPUT\n echo \"google_analytics_id=$GOOGLE_ANALYTICS_ID\" >> $GITHUB_OUTPUT\n echo \"prod_appinsights_connection_string=$PROD_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n echo \"dev_appinsights_connection_string=$DEV_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n\n - name: Azure login via OIDC\n uses: azure/login@v2\n with:\n client-id: ${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}\n tenant-id: ${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}\n subscription-id: ${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}\n\n - name: Get deployment tokens\n id: tokens\n run: |\n # Get access token for DCS API\n ACCESS_TOKEN=$(az account get-access-token --resource \"api://304711b9-8532-4659-beb7-c85726de9ae5\" --query accessToken -o tsv)\n \n SITE_NAME=\"${{ steps.config.outputs.site_name }}\"\n SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n PORTAL_API_URL=\"${{ steps.config.outputs.portal_api_url }}\"\n \n RESPONSE=$(curl -s -X POST \"${PORTAL_API_URL}/api/v1/sites/deployment-tokens\" \\\n -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"siteName\\\": \\\"$SITE_NAME\\\", \\\"siteSlug\\\": \\\"$SITE_SLUG\\\"}\")\n \n SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)\n \n if [ -z \"$SWA_TOKEN\" ] || [ \"$SWA_TOKEN\" == \"null\" ]; then\n echo \"::error::Failed to retrieve SWA token from DCS API\"\n echo \"Response: $RESPONSE\"\n exit 1\n fi\n \n echo \"::add-mask::$SWA_TOKEN\"\n echo \"swa_token=$SWA_TOKEN\" >> $GITHUB_OUTPUT\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Install pnpm\n uses: pnpm/action-setup@v4\n\n - name: Setup pnpm cache\n uses: actions/cache@v4\n with:\n path: ~/.pnpm-store\n key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}\n restore-keys: |\n ${{ runner.os }}-pnpm-\n\n - name: Install dependencies\n run: pnpm install --frozen-lockfile\n\n - name: Create environment file\n run: |\n cat > .env << EOF\n VITE_APP_VERSION=\"${{ github.sha }}\"\n VITE_BACKEND_URI=\"${{ steps.config.outputs.portal_api_url }}\"\n VITE_SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n VITE_GOOGLE_ANALYTICS_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n VITE_PROD_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.prod_appinsights_connection_string }}\"\n VITE_DEV_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.dev_appinsights_connection_string }}\"\n EOF\n\n - name: Build site\n run: pnpm run build\n\n - name: Inject Google Analytics\n if: steps.config.outputs.google_analytics_id != ''\n run: |\n GA_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n OUTPUT_DIR=\"${{ steps.config.outputs.app_location }}\"\n echo \"Injecting Google Analytics tag ${GA_ID} into ${OUTPUT_DIR}...\"\n find \"$OUTPUT_DIR\" -name \"*.html\" -exec sed -i \"s|</head>|<script async src=\\\"https://www.googletagmanager.com/gtag/js?id=${GA_ID}\\\"></script><script>window.dataLayer=window.dataLayer\\|\\|[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','${GA_ID}')</script>\\n</head>|\" {} +\n INJECTED=$(grep -rl \"googletagmanager\" \"$OUTPUT_DIR\" --include=\"*.html\" | wc -l)\n echo \"Injected GA tag into ${INJECTED} HTML file(s)\"\n\n - name: Deploy to Azure Static Web Apps\n uses: Azure/static-web-apps-deploy@v1\n with:\n azure_static_web_apps_api_token: ${{ steps.tokens.outputs.swa_token }}\n repo_token: ${{ secrets.GITHUB_TOKEN }}\n action: \"upload\"\n app_location: ${{ steps.config.outputs.app_location }}\n output_location: ${{ steps.config.outputs.output_location }}\n skip_app_build: true\n deployment_environment: ${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}\n","/**\n * Validate command - validate existing .dcs configuration files.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface ValidateOptions {\n target?: string\n fix?: boolean\n verbose?: boolean\n}\n\ninterface ValidationResult {\n file: string\n valid: boolean\n errors: string[]\n warnings: string[]\n}\n\n/**\n * Validate .dcs configuration files for a customer site.\n */\nexport async function validateCommand(options: ValidateOptions): Promise<void> {\n const { target = '.', fix = false, verbose = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n\n console.log(chalk.bold('Validating DCS configuration...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'to create DCS configuration.')\n process.exit(1)\n }\n\n const results: ValidationResult[] = []\n\n // Validate each config file\n results.push(await validateSiteYaml(dcsDir, verbose))\n results.push(await validatePagesYaml(dcsDir, verbose))\n results.push(await validateContentYaml(dcsDir, verbose))\n results.push(await validateSeoYaml(dcsDir, verbose))\n\n // Display results\n console.log(chalk.bold('Validation Results:'))\n console.log()\n\n let hasErrors = false\n let hasWarnings = false\n\n for (const result of results) {\n if (!result.valid || result.errors.length > 0) {\n hasErrors = true\n console.log(`${chalk.red('✗')} ${result.file}`)\n for (const error of result.errors) {\n console.log(` ${chalk.red('error:')} ${error}`)\n }\n } else if (result.warnings.length > 0) {\n hasWarnings = true\n console.log(`${chalk.yellow('!')} ${result.file}`)\n } else {\n console.log(`${chalk.green('✓')} ${result.file}`)\n }\n\n for (const warning of result.warnings) {\n console.log(` ${chalk.yellow('warning:')} ${warning}`)\n }\n }\n\n console.log()\n\n if (hasErrors) {\n console.log(chalk.red('Validation failed with errors.'))\n if (fix) {\n console.log(chalk.yellow('Some errors can be fixed automatically. Run with --fix flag.'))\n }\n process.exit(1)\n } else if (hasWarnings) {\n console.log(chalk.yellow('Validation passed with warnings.'))\n } else {\n console.log(chalk.green('All configuration files are valid.'))\n }\n}\n\n// =============================================================================\n// File Validators\n// =============================================================================\n\nasync function validateSiteYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'site.yaml')\n const result: ValidationResult = {\n file: '.dcs/site.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (!config.site_name) {\n result.errors.push('Missing required field: site_name')\n result.valid = false\n }\n\n if (!config.site_slug) {\n result.errors.push('Missing required field: site_slug')\n result.valid = false\n } else if (!/^[a-z0-9-]+$/.test(config.site_slug as string)) {\n result.errors.push('site_slug must be lowercase alphanumeric with hyphens only')\n result.valid = false\n }\n\n // Warnings for empty optional fields\n if (!config.swa_resource_id) {\n result.warnings.push('swa_resource_id is not set (required for deployment)')\n }\n\n if (!config.production_url) {\n result.warnings.push('production_url is not set')\n }\n\n if (verbose) {\n console.log(chalk.dim(` site_name: ${config.site_name}`))\n console.log(chalk.dim(` site_slug: ${config.site_slug}`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validatePagesYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'pages.yaml')\n const result: ValidationResult = {\n file: '.dcs/pages.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n if (!config.siteSlug) {\n result.errors.push('Missing required field: siteSlug')\n result.valid = false\n }\n\n // Validate pages array\n const pages = config.pages as Array<Record<string, unknown>> | undefined\n if (!pages || !Array.isArray(pages)) {\n result.errors.push('Missing or invalid pages array')\n result.valid = false\n } else {\n // Check for home page\n const hasHome = pages.some((p) => p.slug === 'home')\n if (!hasHome) {\n result.warnings.push('No home page defined (slug: home)')\n }\n\n // Validate each page\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i]\n if (!page.slug) {\n result.errors.push(`Page ${i}: missing slug`)\n result.valid = false\n }\n if (!page.path) {\n result.errors.push(`Page ${i}: missing path`)\n result.valid = false\n }\n if (!page.type) {\n result.warnings.push(`Page '${page.slug}': missing type (defaulting to static)`)\n }\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pages.length} pages defined`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateContentYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'content.yaml')\n const result: ValidationResult = {\n file: '.dcs/content.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate structure\n if (config.global !== undefined && typeof config.global !== 'object') {\n result.errors.push('global must be an object')\n result.valid = false\n }\n\n if (config.pages !== undefined && typeof config.pages !== 'object') {\n result.errors.push('pages must be an object')\n result.valid = false\n }\n\n if (verbose) {\n const globalKeys = config.global ? Object.keys(config.global as object).length : 0\n const pageCount = config.pages ? Object.keys(config.pages as object).length : 0\n console.log(chalk.dim(` ${globalKeys} global keys, ${pageCount} page sections`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateSeoYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'seo.yaml')\n const result: ValidationResult = {\n file: '.dcs/seo.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate global section\n const global = config.global as Record<string, unknown> | undefined\n if (!global) {\n result.errors.push('Missing required section: global')\n result.valid = false\n } else {\n if (!global.siteName) {\n result.warnings.push('global.siteName is not set')\n }\n if (!global.siteUrl) {\n result.warnings.push('global.siteUrl is not set (required for SEO)')\n }\n if (!global.defaultDescription) {\n result.warnings.push('global.defaultDescription is not set')\n }\n }\n\n // Validate pages section\n const pages = config.pages as Record<string, unknown> | undefined\n if (pages && typeof pages === 'object') {\n const pageKeys = Object.keys(pages)\n if (!pageKeys.includes('home')) {\n result.warnings.push('No SEO configuration for home page')\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pageKeys.length} page SEO configs`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n","/**\n * Plans command - generate/regenerate Copilot prompt plans for DCS integration.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface PlansOptions {\n target?: string\n force?: boolean\n}\n\n/**\n * Generate DCS integration plan files for AI-assisted onboarding.\n */\nexport async function plansCommand(options: PlansOptions): Promise<void> {\n const { target = '.', force = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n const plansDir = path.join(targetDir, '.plans')\n\n console.log(chalk.bold('Generating DCS integration plans...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'first to create DCS configuration.')\n process.exit(1)\n }\n\n // Read site config for context\n let siteName = 'Customer Site'\n let framework = 'vue'\n try {\n const siteYaml = await fs.readFile(path.join(dcsDir, 'site.yaml'), 'utf8')\n const siteConfig = yaml.load(siteYaml) as Record<string, unknown>\n siteName = (siteConfig.site_name as string) || siteName\n const metadata = siteConfig.metadata as Record<string, unknown> | undefined\n framework = (metadata?.framework as string) || framework\n } catch {\n console.log(chalk.yellow('Warning: Could not read site.yaml'))\n }\n\n // Generate plan files\n const plans: Array<{ name: string; content: string }> = [\n { name: '00-audit-site.md', content: generateAuditPlan(siteName) },\n { name: '01-create-use-text-content.md', content: generateTextContentPlan(siteName, framework) },\n { name: '02-integrate-home-page.md', content: generateHomePagePlan(siteName) },\n { name: '03-integrate-remaining-pages.md', content: generateRemainingPagesPlan(siteName) },\n { name: '04-capture-snapshots.md', content: generateSnapshotsPlan(siteName) },\n { name: '05-verify-deployment.md', content: generateVerifyPlan(siteName) },\n ]\n\n // Create plans directory\n await fs.mkdir(plansDir, { recursive: true })\n\n // Write plan files\n const generatedFiles: string[] = []\n for (const plan of plans) {\n const filePath = path.join(plansDir, plan.name)\n\n // Check if file exists\n try {\n await fs.access(filePath)\n if (!force) {\n console.log(chalk.yellow(`Skipped (exists): .plans/${plan.name}`))\n continue\n }\n } catch {\n // File doesn't exist, continue\n }\n\n await fs.writeFile(filePath, plan.content, 'utf8')\n generatedFiles.push(plan.name)\n console.log(`${chalk.green('✓')} .plans/${plan.name}`)\n }\n\n console.log()\n if (generatedFiles.length > 0) {\n console.log(chalk.green(`Generated ${generatedFiles.length} plan files.`))\n } else {\n console.log(chalk.yellow('No new files generated. Use --force to overwrite existing files.'))\n }\n}\n\n// =============================================================================\n// Plan Templates\n// =============================================================================\n\nfunction generateAuditPlan(siteName: string): string {\n return `# Plan 00: Audit ${siteName} Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand:\n- What pages exist and their routing structure\n- Current component organization\n- Existing content patterns\n- Build/deploy configuration\n\n## Steps\n\n### Step 1: List All Pages\n\nScan the project for page components:\n\n\\`\\`\\`\n# For VitePress\nls docs/**/*.md\n\n# For Vue/Vite\nls src/pages/**/*.vue\nls src/views/**/*.vue\n\\`\\`\\`\n\n### Step 2: Identify Content Patterns\n\nLook for:\n- Hardcoded text strings in templates\n- Existing i18n or content management\n- Props-based content passing\n- API-driven content\n\n### Step 3: Document Current Structure\n\nCreate a list of:\n1. All page routes\n2. Reusable components with text content\n3. Layout components (header, footer)\n4. Any existing SEO implementation\n\n### Step 4: Update pages.yaml\n\nUpdate \\`.dcs/pages.yaml\\` with all discovered pages:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # Add discovered pages here...\n\\`\\`\\`\n\n## Output\n\n- Updated \\`.dcs/pages.yaml\\` with all pages\n- List of components requiring content integration\n- Notes on any existing content management to migrate\n`\n}\n\nfunction generateTextContentPlan(siteName: string, framework: string): string {\n return `# Plan 01: Create useTextContent Composable for ${siteName}\n\n## Objective\n\nSet up the text content composable for runtime/build-time content management.\n\n## Prerequisites\n\n- \\`@duffcloudservices/cms\\` package installed\n- Vite/VitePress project configured\n\n## Steps\n\n### Step 1: Install CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### Step 2: Configure Vite Plugin\n\n${framework === 'vue' ? `\nIn \\`vite.config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n})\n\\`\\`\\`\n` : `\nIn \\`.vitepress/config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vitepress'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n vite: {\n plugins: [\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n },\n})\n\\`\\`\\`\n`}\n\n### Step 3: Create Site Composable Wrapper (Optional)\n\nCreate a site-specific wrapper for consistent defaults:\n\n\\`\\`\\`typescript\n// src/composables/useContent.ts\nimport { useTextContent as baseuseTextContent } from '@duffcloudservices/cms'\n\nexport function useContent(pageSlug: string, defaults: Record<string, string> = {}) {\n return baseuseTextContent({ pageSlug, defaults })\n}\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` - should start without errors\n2. Import and use composable in a test component\n3. Verify content.yaml values are accessible via \\`t()\\` function\n`\n}\n\nfunction generateHomePagePlan(siteName: string): string {\n return `# Plan 02: Integrate Home Page for ${siteName}\n\n## Objective\n\nAdd DCS CMS integration to the home page, including:\n- Text content management\n- SEO configuration\n- Data attributes for visual editor\n\n## Steps\n\n### Step 1: Identify Home Page Component\n\nFind the main home page component:\n- VitePress: \\`docs/index.md\\` or \\`docs/.vitepress/theme/components/Home.vue\\`\n- Vue: \\`src/pages/index.vue\\` or \\`src/views/Home.vue\\`\n\n### Step 2: Add Text Content Integration\n\nReplace hardcoded text with \\`t()\\` function calls:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Welcome to ${siteName}',\n 'hero.subtitle': 'Your subtitle here',\n 'hero.cta': 'Get Started',\n // Add all text content...\n }\n})\n\n// Apply SEO\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### Step 3: Update content.yaml\n\nAdd all discovered text keys to \\`.dcs/content.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n hero.title: \"Welcome to ${siteName}\"\n hero.subtitle: \"Your subtitle here\"\n hero.cta: \"Get Started\"\n\\`\\`\\`\n\n### Step 4: Update seo.yaml\n\nConfigure SEO for the home page in \\`.dcs/seo.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n title: Home\n description: \"Description for ${siteName}\"\n noTitleTemplate: true\n openGraph:\n type: website\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` and verify home page renders correctly\n2. Check browser console for any errors\n3. Verify SEO meta tags in page source\n4. Test that data-dcs-* attributes are present\n`\n}\n\nfunction generateRemainingPagesPlan(siteName: string): string {\n return `# Plan 03: Integrate Remaining Pages for ${siteName}\n\n## Objective\n\nApply the same integration pattern to all remaining pages identified in the audit.\n\n## Steps\n\n### For Each Page\n\n1. **Open the page component**\n\n2. **Add imports and composable setup**:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'PAGE_SLUG', // Match pages.yaml slug\n defaults: {\n // Add all text content with sensible defaults\n }\n})\n\nconst { applyHead } = useSEO('PAGE_SLUG')\napplyHead()\n</script>\n\\`\\`\\`\n\n3. **Replace all hardcoded text** with \\`t()\\` calls\n\n4. **Add data-dcs-* attributes** to sections and text elements\n\n5. **Update content.yaml** with the page's text content\n\n6. **Update seo.yaml** with SEO configuration\n\n### Global Components\n\nDon't forget shared components like:\n- Header/Navigation\n- Footer\n- Sidebar\n\nThese should use \\`pageSlug: 'global'\\` for shared content.\n\n## Checklist\n\n- [ ] About page\n- [ ] Contact page\n- [ ] Services/Products pages\n- [ ] Blog index (if applicable)\n- [ ] Header component\n- [ ] Footer component\n- [ ] Any other pages...\n\n## Verification\n\nFor each page:\n1. Page renders correctly\n2. No console errors\n3. SEO meta tags present\n4. Data attributes present\n`\n}\n\nfunction generateSnapshotsPlan(siteName: string): string {\n return `# Plan 04: Configure Snapshot Capture for ${siteName}\n\n## Objective\n\nEnsure the visual page editor can capture accurate snapshots of all pages.\n\n## Prerequisites\n\n- All pages integrated with \\`useTextContent\\`\n- All pages have \\`data-dcs-section\\` attributes\n- All editable text has \\`data-dcs-text\\` attributes\n\n## Steps\n\n### Step 1: Verify pages.yaml\n\nEnsure all pages are listed in \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # All other pages...\n\nexcluded:\n - /404\n - /dev-*\n - /_*\n\\`\\`\\`\n\n### Step 2: Configure Snapshot Settings\n\nReview snapshot configuration:\n\n\\`\\`\\`yaml\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000 # Adjust if page has animations\n captureFullPage: true\n\\`\\`\\`\n\n### Step 3: Test Local Snapshots\n\nBuild and preview the site:\n\n\\`\\`\\`bash\npnpm build\npnpm preview\n\\`\\`\\`\n\nManually verify each page:\n1. Opens correctly\n2. All content is visible\n3. No loading spinners or placeholders\n\n### Step 4: Verify Data Attributes\n\nUse browser DevTools to check:\n- Each section has \\`data-dcs-section\\`\n- Each editable text element has \\`data-dcs-text\\`\n- Attribute values match content.yaml keys\n\n## Verification\n\n1. Site builds without errors\n2. All pages accessible at documented paths\n3. Data attributes present in rendered HTML\n4. No dynamic content that would prevent snapshot capture\n`\n}\n\nfunction generateVerifyPlan(siteName: string): string {\n return `# Plan 05: Verify Deployment for ${siteName}\n\n## Objective\n\nTest the complete integration before handing off to the DCS Portal.\n\n## Steps\n\n### Step 1: Run Validation\n\n\\`\\`\\`bash\nnpx @duffcloudservices/cli validate\n\\`\\`\\`\n\nFix any reported errors or warnings.\n\n### Step 2: Build Test\n\n\\`\\`\\`bash\npnpm build\n\\`\\`\\`\n\nEnsure no build errors related to:\n- Missing content keys\n- SEO configuration\n- Vite plugin issues\n\n### Step 3: Local Preview\n\n\\`\\`\\`bash\npnpm preview\n\\`\\`\\`\n\nVerify:\n- [ ] Home page loads correctly\n- [ ] All pages accessible\n- [ ] SEO meta tags correct\n- [ ] No console errors\n- [ ] Content displays correctly\n\n### Step 4: Deploy to Staging\n\nDeploy to Azure Static Web Apps staging environment:\n\n\\`\\`\\`bash\n# If using GitHub Actions, push to a PR branch\ngit push origin feature/dcs-integration\n\\`\\`\\`\n\n### Step 5: Portal Registration\n\n1. Log in to DCS Portal\n2. Navigate to Sites > Add Site\n3. Enter site slug and GitHub repo\n4. Trigger initial snapshot capture\n\n### Step 6: Visual Editor Test\n\n1. Open the site in DCS Portal\n2. Navigate to Pages\n3. Select a page\n4. Verify visual editor loads correctly\n5. Test editing a text field\n6. Save and verify changes deploy\n\n## Success Criteria\n\n- [ ] All validation passes\n- [ ] Site builds and deploys\n- [ ] Portal can capture snapshots\n- [ ] Visual editor works\n- [ ] Content changes deploy correctly\n\n## Handoff\n\nOnce verified:\n1. Merge the integration PR\n2. Notify the site owner\n3. Provide portal access credentials\n`\n}\n","/**\n * Capture visual snapshots of customer site pages for the portal's visual page editor.\n *\n * This command:\n * 1. Reads page configuration from .dcs/pages.yaml\n * 2. Launches a headless browser via Playwright\n * 3. Captures full-page screenshots and section bounds\n * 4. Detects text keys from data-text-key attributes\n * 5. Outputs JSON manifests and PNG screenshots\n *\n * The output is consumed by the DCS portal to render the visual page editor.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport yaml from 'js-yaml'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Browser, ElementHandle, Page } from 'playwright'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface PagesConfig {\n siteSlug: string\n version: number | string\n pages: PageConfig[]\n snapshot: {\n viewport: { width: number; height: number }\n waitAfterLoad: number\n captureFullPage: boolean\n outputDir?: string\n }\n}\n\ninterface PageConfig {\n slug: string\n path: string\n type: string\n title: string\n deletable: boolean\n textKeys?: string[]\n /** For dynamic pages: template pattern like /topics/:topic */\n pathTemplate?: string\n /** For dynamic pages: list of actual slugs to capture */\n instances?: string[]\n}\n\ninterface ContentConfig {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n}\n\ninterface TextKeyInfo {\n key: string\n currentValue: string\n elementType: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n domPath?: string\n}\n\ninterface SectionInfo {\n sectionId: string\n sectionType: 'header' | 'footer' | 'hero' | 'content' | 'gallery' | 'cta' | 'custom'\n displayName: string\n domPath: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n textKeys: TextKeyInfo[]\n isEditable: boolean\n isStructuralOnly: boolean\n}\n\ninterface PageSnapshot {\n pageSlug: string\n path: string\n type: string\n deletable: boolean\n title: string\n capturedAt: string\n previewUrl: string\n viewport: { width: number; height: number }\n pageHeight: number\n sections: SectionInfo[]\n}\n\ninterface SnapshotManifest {\n siteSlug: string\n capturedAt: string\n capturedVersion?: string\n deploymentRef?: string\n pagesConfigVersion: number | string\n pages: Array<{\n pageSlug: string\n pagePath: string\n pageType: string\n deletable: boolean\n title: string\n capturedAt: string\n sectionCount: number\n hasSnapshot: boolean\n }>\n}\n\ninterface TargetedSnapshotsConfig {\n mode: 'skip' | 'targeted' | 'full'\n pages: string[]\n reason: string\n triggeredBy?: string\n}\n\ninterface CaptureSnapshotsOptions {\n target: string\n baseUrl: string\n dryRun?: boolean\n verbose?: boolean\n pages?: string[]\n}\n\nconst CARD_THUMBNAIL_ASPECT_RATIO = 16 / 9\nconst ASSET_WAIT_TIMEOUT_MS = 5000\n\nasync function disableCaptureMotion(page: Page): Promise<void> {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -1ms !important;\n animation-duration: 1ms !important;\n animation-iteration-count: 1 !important;\n scroll-behavior: auto !important;\n transition-delay: 0s !important;\n transition-duration: 0s !important;\n }\n `,\n }).catch(() => {\n // Some pages can navigate before style injection completes; capture can continue without this stabilization.\n })\n}\n\nasync function waitForAboveFoldAssets(page: Page): Promise<void> {\n await page.evaluate(async (timeoutMs) => {\n const waitForFonts = typeof document.fonts?.ready?.then === 'function'\n ? document.fonts.ready.catch(() => undefined)\n : Promise.resolve()\n\n const visibleImages = Array.from(document.images).filter((img) => {\n const rect = img.getBoundingClientRect()\n return rect.bottom >= -100 && rect.top <= window.innerHeight * 1.5\n })\n\n const waitForImages = Promise.all(visibleImages.map((img) => {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve) => {\n const done = () => resolve()\n img.addEventListener('load', done, { once: true })\n img.addEventListener('error', done, { once: true })\n })\n }))\n\n await Promise.race([\n Promise.all([waitForFonts, waitForImages]),\n new Promise((resolve) => window.setTimeout(resolve, timeoutMs)),\n ])\n }, ASSET_WAIT_TIMEOUT_MS).catch((error) => {\n console.warn(` Warning: could not verify above-fold assets: ${error instanceof Error ? error.message : String(error)}`)\n })\n}\n\nasync function lazyLoadPage(page: Page, viewportHeight: number): Promise<number> {\n let pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n const scrollStep = Math.max(viewportHeight, 1)\n\n for (let scrollY = 0; scrollY < pageHeight; scrollY += scrollStep) {\n await page.evaluate((y) => window.scrollTo(0, y), scrollY)\n await page.waitForTimeout(100)\n }\n\n await page.waitForTimeout(200)\n pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n\n return pageHeight\n}\n\nasync function resetScrollForFullPageCapture(page: Page): Promise<void> {\n await page.evaluate(() => {\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.waitForFunction(() => Math.abs(window.scrollY) < 1, undefined, { timeout: 1000 }).catch(() => undefined)\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n await page.waitForTimeout(350)\n await waitForAboveFoldAssets(page)\n}\n\nasync function withFullPageCaptureLayout<T>(page: Page, capture: () => Promise<T>): Promise<T> {\n const styleHandle = await page.addStyleTag({\n content: `\n html[data-dcs-full-page-snapshot=\"true\"] header[data-section=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [data-section-type=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [role=\"banner\"] {\n position: static !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] [data-dcs-pin-full-page-nav=\"true\"] {\n position: absolute !important;\n top: 0 !important;\n right: 0 !important;\n bottom: auto !important;\n left: 0 !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] .VPNavBar {\n transform: none !important;\n }\n `,\n }).catch(() => null)\n\n await page.evaluate(() => {\n const candidates = Array.from(document.querySelectorAll(\n 'header[data-section=\"header\"] > nav, [data-section-type=\"header\"] > nav, [role=\"banner\"] > nav, .VPNav'\n ))\n candidates.forEach((element) => {\n const style = window.getComputedStyle(element)\n const className = element instanceof HTMLElement && typeof element.className === 'string' ? element.className : ''\n if (style.position === 'fixed' || style.position === 'sticky' || /\\b(fixed|sticky)\\b/.test(className) || element.classList.contains('VPNav')) {\n element.setAttribute('data-dcs-pin-full-page-nav', 'true')\n }\n })\n document.documentElement.setAttribute('data-dcs-full-page-snapshot', 'true')\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n\n try {\n return await capture()\n } finally {\n await styleHandle?.evaluate((el) => el.parentNode?.removeChild(el)).catch(() => undefined)\n await page.evaluate(() => {\n document.documentElement.removeAttribute('data-dcs-full-page-snapshot')\n document.querySelectorAll('[data-dcs-pin-full-page-nav]').forEach((element) => {\n element.removeAttribute('data-dcs-pin-full-page-nav')\n })\n }).catch(() => undefined)\n }\n}\n\nasync function captureHeaderThumbnail(\n page: Page,\n outputPath: string,\n viewport: { width: number; height: number }\n): Promise<void> {\n await page.setViewportSize(viewport)\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(200)\n await waitForAboveFoldAssets(page)\n\n const clip = await page.evaluate((aspectRatio) => {\n const width = Math.max(document.documentElement.clientWidth, window.innerWidth, 1)\n const pageHeight = Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0, 1)\n const height = Math.min(Math.round(width / aspectRatio), pageHeight)\n return { x: 0, y: 0, width, height }\n }, CARD_THUMBNAIL_ASPECT_RATIO)\n\n await page.screenshot({\n path: outputPath,\n clip,\n })\n}\n\n// =============================================================================\n// Section Detection Selectors\n// =============================================================================\n\nconst SECTION_SELECTORS = {\n explicit: '[data-section]',\n header: 'header, [role=\"banner\"], .header, #header',\n footer: 'footer, [role=\"contentinfo\"], .footer, #footer',\n hero: '[class*=\"hero\"], [data-section-type=\"hero\"], .hero-section, #hero',\n content: 'main, [role=\"main\"], article, .content, .main-content',\n gallery: '[class*=\"gallery\"], [data-section-type=\"gallery\"]',\n cta: '[class*=\"cta\"], [data-section-type=\"cta\"], .call-to-action',\n} as const\n\n// =============================================================================\n// Configuration Loaders\n// =============================================================================\n\nfunction loadPagesConfig(targetDir: string): PagesConfig {\n const configPath = path.join(targetDir, '.dcs', 'pages.yaml')\n if (!fs.existsSync(configPath)) {\n throw new Error(`Configuration file not found: ${configPath}`)\n }\n const content = fs.readFileSync(configPath, 'utf-8')\n return yaml.load(content) as PagesConfig\n}\n\nfunction loadContentConfig(targetDir: string): ContentConfig | null {\n const contentPath = path.join(targetDir, '.dcs', 'content.yaml')\n if (!fs.existsSync(contentPath)) {\n return null\n }\n const content = fs.readFileSync(contentPath, 'utf-8')\n return yaml.load(content) as ContentConfig\n}\n\nfunction loadTargetedSnapshotsConfig(targetDir: string): TargetedSnapshotsConfig | null {\n const targetedPath = path.join(targetDir, '.dcs', 'targeted-snapshots.yaml')\n if (!fs.existsSync(targetedPath)) {\n return null\n }\n const content = fs.readFileSync(targetedPath, 'utf-8')\n return yaml.load(content) as TargetedSnapshotsConfig\n}\n\nfunction getValidTextKeysForPage(\n contentConfig: ContentConfig | null,\n pageSlug: string\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (!contentConfig) return keys\n\n // Add global keys\n if (contentConfig.global) {\n for (const [key, value] of Object.entries(contentConfig.global)) {\n keys.set(key, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n // Add page-specific keys\n const pageContent = contentConfig.pages?.[pageSlug]\n if (pageContent) {\n for (const [key, value] of Object.entries(pageContent)) {\n const fullKey = `${pageSlug}.${key}`\n keys.set(fullKey, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n return keys\n}\n\n// =============================================================================\n// Page Resolution\n// =============================================================================\n\ninterface ResolvedPage {\n slug: string\n path: string\n config: PageConfig\n}\n\nasync function resolveAllPages(config: PagesConfig): Promise<ResolvedPage[]> {\n const resolved: ResolvedPage[] = []\n\n for (const page of config.pages) {\n if (page.type === 'dynamic' && page.instances) {\n // Expand dynamic pages into individual instances\n for (const instance of page.instances) {\n const instancePath = page.pathTemplate\n ? page.pathTemplate.replace(/:[\\w]+/, instance)\n : `${page.path}/${instance}`\n\n resolved.push({\n slug: `${page.slug}-${instance}`,\n path: instancePath,\n config: { ...page, slug: `${page.slug}-${instance}` },\n })\n }\n } else {\n resolved.push({\n slug: page.slug,\n path: page.path,\n config: page,\n })\n }\n }\n\n return resolved\n}\n\n// =============================================================================\n// DOM Helpers\n// =============================================================================\n\nasync function getDomPath(element: ElementHandle): Promise<string> {\n return element.evaluate((el: Element) => {\n const parts: string[] = []\n let current: Element | null = el\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase()\n\n if (current.id) {\n selector += `#${current.id}`\n } else {\n const classes = Array.from(current.classList)\n .filter((c) => !c.match(/^(v-|nuxt-|vue-|react-|ng-|_)/))\n .slice(0, 2)\n .join('.')\n if (classes) {\n selector += `.${classes}`\n }\n }\n\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n })\n}\n\nasync function generateSectionDisplayName(\n element: ElementHandle,\n sectionType: string,\n index: number\n): Promise<string> {\n // Try to find a heading within the section\n const headingText = await element.evaluate((el: Element) => {\n const heading = el.querySelector('h1, h2, h3, h4, [class*=\"title\"], [class*=\"heading\"]')\n if (heading && heading.textContent) {\n return heading.textContent.trim().slice(0, 50)\n }\n return null\n })\n\n if (headingText) return headingText\n\n // Fall back to section type + index\n const typeLabels: Record<string, string> = {\n header: 'Header',\n footer: 'Footer',\n hero: 'Hero Section',\n content: 'Content Section',\n gallery: 'Gallery',\n cta: 'Call to Action',\n }\n\n return typeLabels[sectionType] || `Section ${index + 1}`\n}\n\n// =============================================================================\n// Text Key Detection\n// =============================================================================\n\nasync function detectTextKeys(\n _page: Page,\n sectionElement: ElementHandle,\n _pageSlug: string,\n _validTextKeys: Map<string, string>\n): Promise<TextKeyInfo[]> {\n const textKeys: TextKeyInfo[] = []\n\n // Find editable text and managed image markers within this section.\n const textKeyElements = await sectionElement.$$('[data-text-key], [data-dcs-text], [data-dcs-image-key]')\n\n for (const element of textKeyElements) {\n const keyInfo = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n const explicitImageUrl = el.getAttribute('data-dcs-image-url')\n const imageUrl = el instanceof HTMLImageElement ? el.currentSrc || el.src : ''\n return {\n key: el.getAttribute('data-text-key') || el.getAttribute('data-dcs-text') || el.getAttribute('data-dcs-image-key') || '',\n elementType: el.tagName.toLowerCase(),\n currentValue: (explicitImageUrl || imageUrl || el.textContent?.trim() || '').slice(0, 500),\n bounds: {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n },\n }\n })\n\n if (keyInfo.key && !textKeys.some((textKey) => textKey.key === keyInfo.key)) {\n const domPath = await getDomPath(element)\n textKeys.push({\n key: keyInfo.key,\n currentValue: keyInfo.currentValue,\n elementType: keyInfo.elementType,\n domPath,\n bounds: {\n x: Math.round(keyInfo.bounds.x),\n y: Math.round(keyInfo.bounds.y),\n width: Math.round(keyInfo.bounds.width),\n height: Math.round(keyInfo.bounds.height),\n },\n })\n }\n }\n\n return textKeys\n}\n\n// =============================================================================\n// Fallback Section Detection\n// =============================================================================\n\nasync function detectFallbackContentSections(\n page: Page,\n existingSections: SectionInfo[]\n): Promise<ElementHandle[]> {\n // Find large content blocks that might be sections\n const candidates = await page.$$('section, article, div[class*=\"section\"], div[class*=\"container\"]')\n\n const results: ElementHandle[] = []\n const existingBounds = existingSections.map((s) => s.bounds)\n\n for (const candidate of candidates) {\n const bounds = await candidate.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n y: rect.top + window.scrollY,\n height: rect.height,\n }\n })\n\n // Skip if too small or overlaps with existing sections\n if (bounds.height < 100) continue\n\n const overlaps = existingBounds.some(\n (eb) =>\n bounds.y < eb.y + eb.height && bounds.y + bounds.height > eb.y\n )\n\n if (!overlaps) {\n results.push(candidate)\n }\n }\n\n return results\n}\n\n// =============================================================================\n// Page Capture\n// =============================================================================\n\nasync function capturePageSnapshot(\n browser: Browser,\n pageInfo: ResolvedPage,\n snapshotConfig: PagesConfig['snapshot'],\n siteSlug: string,\n validTextKeys: Map<string, string>,\n outputDir: string,\n verbose: boolean\n): Promise<PageSnapshot> {\n const context = await browser.newContext({\n viewport: snapshotConfig.viewport,\n ignoreHTTPSErrors: true,\n reducedMotion: 'reduce',\n })\n const page = await context.newPage()\n\n // Build page URL and append dcs-hide-ribbon to suppress preview ribbon in screenshots\n const rawUrl = pageInfo.path.startsWith('http')\n ? pageInfo.path\n : `${process.env.SITE_BASE_URL || 'http://localhost:5173'}${pageInfo.path}`\n const pageUrl = new URL(rawUrl)\n pageUrl.searchParams.set('dcs-hide-ribbon', '')\n const url = pageUrl.toString()\n\n if (verbose) {\n console.log(` Capturing: ${pageInfo.slug} (${url})`)\n }\n\n try {\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })\n await disableCaptureMotion(page)\n await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {\n if (verbose) console.log(` Note: networkidle timeout, continuing anyway`)\n })\n } catch (error) {\n console.error(` Failed to load: ${error}`)\n await context.close()\n throw error\n }\n\n await page.waitForTimeout(snapshotConfig.waitAfterLoad)\n await waitForAboveFoldAssets(page)\n\n // Create output directory for this page\n const pageOutputDir = path.join(outputDir, siteSlug, pageInfo.slug)\n fs.mkdirSync(path.join(pageOutputDir, 'sections'), { recursive: true })\n\n const title = await page.title()\n\n // Scroll through the page once so lazy-loaded content is present before the full-page capture.\n let pageHeight = await lazyLoadPage(page, snapshotConfig.viewport.height)\n await resetScrollForFullPageCapture(page)\n\n // Capture full page screenshot with fixed nav pinned to the document origin.\n const fullPagePath = path.join(pageOutputDir, 'full-page.png')\n await withFullPageCaptureLayout(page, () =>\n page.screenshot({\n path: fullPagePath,\n fullPage: snapshotConfig.captureFullPage,\n })\n )\n\n // Capture the header/hero-focused card image at the configured desktop viewport.\n const thumbnailPath = path.join(pageOutputDir, 'thumbnail.png')\n await captureHeaderThumbnail(page, thumbnailPath, snapshotConfig.viewport)\n\n // Ensure we're at scroll position 0 before capturing bounds.\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(100)\n\n // Detect sections\n const sections: SectionInfo[] = []\n const seenDomPaths = new Set<string>()\n\n // Helper to get accurate bounds for fixed/sticky elements\n // For fixed/sticky elements, we need to calculate their \"document position\"\n // based on their role (header = top, footer = bottom)\n const getAccurateBounds = async (el: ElementHandle, sectionType?: string) => {\n return el.evaluate((element: Element, type: string | undefined) => {\n const rect = element.getBoundingClientRect()\n const style = window.getComputedStyle(element)\n const position = style.position\n const isFixed = position === 'fixed' || position === 'sticky'\n \n let y = rect.top + window.scrollY\n \n // For fixed/sticky headers, they should be at y=0 (top of page)\n if (isFixed && (type === 'header' || element.tagName.toLowerCase() === 'header')) {\n y = 0\n }\n // For fixed/sticky footers, they should be at the bottom of the page\n else if (isFixed && (type === 'footer' || element.tagName.toLowerCase() === 'footer')) {\n y = document.documentElement.scrollHeight - rect.height\n }\n \n return {\n x: rect.left + window.scrollX,\n y,\n width: rect.width,\n height: rect.height,\n isFixed,\n }\n }, sectionType)\n }\n\n // Phase 1: Explicit [data-section] elements\n const explicitElements = await page.$$(SECTION_SELECTORS.explicit)\n\n for (let i = 0; i < explicitElements.length; i++) {\n const element = explicitElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n // First, get basic metadata\n const basicMetadata = await element.evaluate((el: Element) => {\n return {\n sectionId: el.getAttribute('data-section') || '',\n sectionLabel: el.getAttribute('data-section-label') || '',\n sectionType: el.getAttribute('data-section-type') || '',\n isDynamic: el.hasAttribute('data-dynamic'),\n }\n })\n\n // Determine section type early so we can pass it to bounds calculation\n let resolvedType: SectionInfo['sectionType'] = 'content'\n if (basicMetadata.sectionType) {\n resolvedType = basicMetadata.sectionType as SectionInfo['sectionType']\n } else if (['header', 'footer'].includes(basicMetadata.sectionId)) {\n resolvedType = basicMetadata.sectionId as SectionInfo['sectionType']\n } else if (basicMetadata.sectionId.includes('hero')) {\n resolvedType = 'hero'\n } else if (basicMetadata.sectionId.includes('gallery')) {\n resolvedType = 'gallery'\n }\n\n // Get accurate bounds with fixed/sticky handling\n const bounds = await getAccurateBounds(element, resolvedType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = basicMetadata.sectionId || `section-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(resolvedType)\n\n // Detect text keys\n let textKeys: TextKeyInfo[] = []\n if (!isStructuralOnly) {\n textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n\n // Generate display name - prefer data-section-label, then heading, then fallback\n let displayName = basicMetadata.sectionLabel\n if (!displayName) {\n displayName = await generateSectionDisplayName(element, resolvedType, i)\n }\n\n if (verbose) {\n console.log(\n ` Section: ${displayName} (${sectionId}) at y=${Math.round(bounds.y)}${\n basicMetadata.isDynamic ? ' [dynamic]' : ''\n } - ${textKeys.length} text keys`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: resolvedType,\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n\n // Phase 2: Type-based sections (header, footer, hero, etc.)\n for (const [sectionType, selector] of Object.entries(SECTION_SELECTORS)) {\n if (sectionType === 'explicit') continue\n\n const elements = await page.$$(selector)\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n\n // Skip elements that are INSIDE an explicit [data-section]\n // This prevents nested elements from being detected as separate sections\n const isInsideExplicitSection = await element.evaluate((el: Element) => {\n // Walk up the DOM tree to check if any ancestor has data-section\n let current: HTMLElement | null = el.parentElement\n while (current) {\n if (current.dataset?.section) {\n return true\n }\n current = current.parentElement\n }\n return false\n })\n\n if (isInsideExplicitSection) {\n continue // Silently skip - these are intentionally nested inside explicit sections\n }\n\n // Skip elements that are ancestors of already-detected explicit sections\n // This prevents the <main> container from being detected as a \"content\" section\n // when it contains explicit [data-section] children\n const isAncestorOfExplicitSection = await element.evaluate((el: Element) => {\n const explicitSections = el.querySelectorAll('[data-section]')\n return explicitSections.length > 0\n })\n\n if (isAncestorOfExplicitSection) {\n if (verbose) {\n console.log(` Skipping ${sectionType} container (contains explicit sections)`)\n }\n continue\n }\n\n seenDomPaths.add(domPath)\n\n // Use accurate bounds calculation for fixed/sticky elements\n const bounds = await getAccurateBounds(element, sectionType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `${sectionType}-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(sectionType)\n const textKeys = isStructuralOnly\n ? []\n : await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n\n // Skip content sections that have no text keys - they add noise without value\n if (sectionType === 'content' && textKeys.length === 0) {\n continue\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n const displayName = await generateSectionDisplayName(element, sectionType, i)\n\n if (verbose) {\n console.log(` Section: ${displayName} at y=${Math.round(bounds.y)}${bounds.isFixed ? ' [fixed]' : ''}`)\n }\n\n sections.push({\n sectionId,\n sectionType: sectionType as SectionInfo['sectionType'],\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n }\n\n // Fallback: If no content sections, try heuristic detection\n const hasContentSection = sections.some((s) => s.sectionType === 'content')\n if (!hasContentSection) {\n if (verbose) console.log(` No content sections found, trying fallback detection...`)\n\n const fallbackElements = await detectFallbackContentSections(page, sections)\n\n for (let i = 0; i < fallbackElements.length; i++) {\n const element = fallbackElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n const bounds = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n }\n })\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `content-fallback-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped fallback section screenshot: ${sectionId}`)\n }\n\n const textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n const displayName = await generateSectionDisplayName(element, 'content', i)\n\n if (verbose) {\n console.log(\n ` Fallback Section: ${displayName} at y=${Math.round(bounds.y)} (${textKeys.length} text keys)`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: 'content',\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable: textKeys.length > 0,\n isStructuralOnly: false,\n })\n }\n }\n\n await context.close()\n\n // Sort sections by vertical position\n sections.sort((a, b) => a.bounds.y - b.bounds.y)\n\n return {\n pageSlug: pageInfo.slug,\n path: pageInfo.path,\n type: pageInfo.config.type,\n deletable: pageInfo.config.deletable,\n title: title || pageInfo.config.title,\n capturedAt: new Date().toISOString(),\n previewUrl: url,\n viewport: snapshotConfig.viewport,\n pageHeight,\n sections,\n }\n}\n\n// =============================================================================\n// Main Command\n// =============================================================================\n\nexport async function captureSnapshotsCommand(options: CaptureSnapshotsOptions): Promise<void> {\n const { target, baseUrl, dryRun, verbose, pages: targetedPages } = options\n const targetDir = path.resolve(target)\n\n console.log(chalk.blue('📸 Page Snapshot Capture'))\n console.log(chalk.gray('========================'))\n console.log(chalk.gray(`Target: ${targetDir}`))\n console.log(chalk.gray(`Base URL: ${baseUrl}`))\n\n // Set base URL for capture\n process.env.SITE_BASE_URL = baseUrl\n\n // Load configuration\n let config: PagesConfig\n try {\n config = loadPagesConfig(targetDir)\n } catch (error) {\n console.error(chalk.red('Error:'), (error as Error).message)\n console.log(chalk.yellow('\\nMake sure .dcs/pages.yaml exists in the target directory.'))\n process.exit(1)\n }\n\n console.log(chalk.gray(`Site: ${config.siteSlug}`))\n\n const outputDir = path.join(targetDir, config.snapshot.outputDir || '.dcs/snapshots')\n\n // Load content configuration (optional)\n const contentConfig = loadContentConfig(targetDir)\n if (contentConfig) {\n const pageCount = Object.keys(contentConfig.pages || {}).length\n const globalCount = Object.keys(contentConfig.global || {}).length\n console.log(chalk.gray(`Content: Loaded ${pageCount} pages, ${globalCount} global keys`))\n }\n\n // Check for targeted snapshots config\n const targetedConfig = loadTargetedSnapshotsConfig(targetDir)\n const isTargetedCapture = Boolean(\n (targetedPages && targetedPages.length > 0) ||\n (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0)\n )\n\n if (targetedConfig?.mode === 'skip') {\n console.log('')\n console.log(chalk.yellow('⏭️ Skip mode activated!'))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n console.log(chalk.gray(' No snapshots will be captured.'))\n\n if (!dryRun) {\n fs.mkdirSync(outputDir, { recursive: true })\n const skipMarker = {\n skipped: true,\n reason: targetedConfig.reason,\n triggeredBy: targetedConfig.triggeredBy,\n timestamp: new Date().toISOString(),\n }\n fs.writeFileSync(path.join(outputDir, 'skipped.json'), JSON.stringify(skipMarker, null, 2))\n }\n\n console.log(chalk.green('\\n✅ Snapshot capture skipped (as requested)'))\n return\n }\n\n // Resolve pages\n let pagesToCapture = await resolveAllPages(config)\n console.log(chalk.gray(`Found ${pagesToCapture.length} total pages in configuration`))\n\n // Apply filtering from command-line --pages option\n if (targetedPages && targetedPages.length > 0) {\n const targetedSlugs = new Set(targetedPages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n }\n // Apply filtering from targeted-snapshots.yaml\n else if (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0) {\n const targetedSlugs = new Set(targetedConfig.pages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n }\n\n // List pages to capture\n console.log('')\n console.log('Pages to capture:')\n for (const p of pagesToCapture) {\n console.log(chalk.gray(` - ${p.slug} (${p.config.type}) ${p.path}`))\n }\n\n if (pagesToCapture.length === 0) {\n console.log(chalk.yellow('\\n⚠️ No pages to capture!'))\n return\n }\n\n if (dryRun) {\n console.log(chalk.yellow('\\n--dry-run: Stopping before browser launch'))\n return\n }\n\n // Dynamic import of playwright to avoid requiring it when not using this command\n const { chromium } = await import('playwright')\n\n // Clear and create output directory\n if (fs.existsSync(outputDir)) {\n fs.rmSync(outputDir, { recursive: true })\n }\n fs.mkdirSync(outputDir, { recursive: true })\n\n const spinner = ora('Launching browser...').start()\n const browser = await chromium.launch()\n spinner.succeed('Browser launched')\n\n const snapshots: PageSnapshot[] = []\n const failures: string[] = []\n\n for (const pageInfo of pagesToCapture) {\n const pageSpinner = ora(`Capturing ${pageInfo.slug}...`).start()\n\n try {\n const validTextKeys = getValidTextKeysForPage(contentConfig, pageInfo.slug)\n const snapshot = await capturePageSnapshot(\n browser,\n pageInfo,\n config.snapshot,\n config.siteSlug,\n validTextKeys,\n outputDir,\n verbose || false\n )\n snapshots.push(snapshot)\n\n // Save individual page snapshot JSON\n const snapshotPath = path.join(outputDir, config.siteSlug, pageInfo.slug, 'snapshot.json')\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2))\n\n pageSpinner.succeed(`${pageInfo.slug}: ${snapshot.sections.length} sections captured`)\n } catch (error) {\n const message = `${pageInfo.slug}: ${(error as Error).message}`\n failures.push(message)\n pageSpinner.fail(`${pageInfo.slug}: Failed - ${(error as Error).message}`)\n }\n }\n\n await browser.close()\n\n // Create manifest\n const manifest: SnapshotManifest = {\n siteSlug: config.siteSlug,\n capturedAt: new Date().toISOString(),\n capturedVersion: process.env.GITHUB_SHA,\n deploymentRef: process.env.GITHUB_REF || 'local',\n pagesConfigVersion: config.version,\n pages: snapshots.map((s) => ({\n pageSlug: s.pageSlug,\n pagePath: s.path,\n pageType: s.type,\n deletable: s.deletable,\n title: s.title,\n capturedAt: s.capturedAt,\n sectionCount: s.sections.length,\n hasSnapshot: true,\n })),\n }\n\n const manifestPath = path.join(outputDir, config.siteSlug, 'manifest.json')\n if (isTargetedCapture) {\n console.log(chalk.gray(' Targeted capture: leaving the existing remote manifest intact'))\n } else {\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n }\n\n console.log('')\n console.log(chalk.green('✅ Snapshot capture complete!'))\n console.log(chalk.gray(` Captured ${snapshots.length} pages`))\n console.log(chalk.gray(` - Static: ${snapshots.filter((s) => s.type === 'static').length}`))\n console.log(chalk.gray(` - Index: ${snapshots.filter((s) => s.type === 'index').length}`))\n console.log(chalk.gray(` - Dynamic: ${snapshots.filter((s) => s.type === 'dynamic').length}`))\n console.log(chalk.gray(` Output: ${outputDir}`))\n\n if (failures.length > 0) {\n throw new Error(`Snapshot capture failed for ${failures.length} page(s): ${failures.join('; ')}`)\n }\n}\n"],"mappings":"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","names":["chalk","chalk","data","chalk","chalk","DEFAULT_API_URL","path","chalk","chalk","ora","chalk","ora","fs","path","chalk","config","fs","path","chalk","yaml","fs","path","yaml","chalk","ora","config","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../package.json","../src/commands/auth.ts","../src/auth/device-flow.ts","../src/auth/credentials.ts","../src/commands/sites.ts","../src/api/portal-client.ts","../src/commands/init.ts","../src/templates/site-deploy.yml","../src/commands/validate.ts","../src/commands/plans.ts","../src/commands/capture-snapshots.ts"],"sourcesContent":["/**\n * Main CLI entry point.\n */\n\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { version } from '../package.json'\n\n// Import commands\nimport { loginCommand, logoutCommand, whoamiCommand } from './commands/auth.js'\nimport { listSitesCommand, showSiteCommand } from './commands/sites.js'\nimport { initCommand } from './commands/init.js'\nimport { validateCommand } from './commands/validate.js'\nimport { plansCommand } from './commands/plans.js'\nimport { captureSnapshotsCommand } from './commands/capture-snapshots.js'\n\nconst program = new Command()\n\nprogram\n .name('dcs')\n .description('DCS (Duff Cloud Services) CLI for customer site management')\n .version(version)\n\n// =============================================================================\n// Auth Commands\n// =============================================================================\n\nprogram\n .command('login')\n .description('Authenticate with DCS Portal using Google OAuth')\n .action(async () => {\n await loginCommand()\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .action(async () => {\n await logoutCommand()\n })\n\nprogram\n .command('whoami')\n .description('Display current authenticated user')\n .action(async () => {\n await whoamiCommand()\n })\n\n// =============================================================================\n// Site Commands\n// =============================================================================\n\nconst sitesCmd = program\n .command('sites')\n .description('Manage customer sites')\n\nsitesCmd\n .command('list')\n .description('List sites you have access to')\n .action(async () => {\n await listSitesCommand()\n })\n\nsitesCmd\n .command('show <slug>')\n .description('Show details for a specific site')\n .action(async (slug: string) => {\n await showSiteCommand(slug)\n })\n\n// =============================================================================\n// Init Command\n// =============================================================================\n\nprogram\n .command('init')\n .description('Initialize DCS integration for a customer site')\n .requiredOption('-s, --site-slug <slug>', 'Site slug (lowercase, hyphens)')\n .requiredOption('-n, --site-name <name>', 'Human-readable site name')\n .option('-f, --framework <framework>', 'Site framework (vue, astro)', 'vue')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--dry-run', 'Show what would be created without writing files')\n .option('--force', 'Overwrite existing files')\n .action(async (options) => {\n await initCommand({\n siteSlug: options.siteSlug,\n siteName: options.siteName,\n framework: options.framework,\n target: options.target,\n dryRun: options.dryRun,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Validate Command\n// =============================================================================\n\nprogram\n .command('validate')\n .description('Validate .dcs configuration files')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--fix', 'Attempt to fix common issues')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n await validateCommand({\n target: options.target,\n fix: options.fix,\n verbose: options.verbose,\n })\n })\n\n// =============================================================================\n// Plans Command\n// =============================================================================\n\nprogram\n .command('plans')\n .description('Generate DCS integration plan files for AI-assisted onboarding')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--force', 'Overwrite existing plan files')\n .action(async (options) => {\n await plansCommand({\n target: options.target,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Capture Snapshots Command\n// =============================================================================\n\nprogram\n .command('capture-snapshots')\n .description('Capture visual snapshots of site pages for the portal page editor')\n .option('-t, --target <dir>', 'Target directory containing .dcs/pages.yaml', '.')\n .option('-u, --base-url <url>', 'Base URL of the running site', 'http://localhost:5173')\n .option('-p, --pages <slugs>', 'Comma-separated list of page slugs to capture')\n .option('--dry-run', 'Show what would be captured without launching browser')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n const pages = options.pages ? options.pages.split(',').map((s: string) => s.trim()) : undefined\n await captureSnapshotsCommand({\n target: options.target,\n baseUrl: options.baseUrl,\n dryRun: options.dryRun,\n verbose: options.verbose,\n pages,\n })\n })\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nprogram.exitOverride()\n\ntry {\n await program.parseAsync(process.argv)\n} catch (error) {\n if (error instanceof Error) {\n // Commander errors (help, version) are expected\n if ((error as { code?: string }).code === 'commander.helpDisplayed' ||\n (error as { code?: string }).code === 'commander.version') {\n process.exit(0)\n }\n\n console.error(chalk.red('Error:'), error.message)\n process.exit(1)\n }\n}\n","{\n \"name\": \"@duffcloudservices/cli\",\n \"version\": \"0.3.0\",\n \"description\": \"CLI for DCS site onboarding and configuration\",\n \"type\": \"module\",\n \"bin\": {\n \"dcs\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src --ext .ts\",\n \"prepublishOnly\": \"pnpm run build\"\n },\n \"dependencies\": {\n \"chalk\": \"^5.3.0\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^12.1.0\",\n \"conf\": \"^13.0.1\",\n \"handlebars\": \"^4.7.8\",\n \"inquirer\": \"^9.3.7\",\n \"js-yaml\": \"^4.1.0\",\n \"open\": \"^10.1.0\",\n \"ora\": \"^8.1.1\",\n \"playwright\": \"^1.49.0\"\n },\n \"devDependencies\": {\n \"@types/inquirer\": \"^9.0.7\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^20.11.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.6.3\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"dcs\",\n \"cli\",\n \"cms\",\n \"onboarding\",\n \"duff-cloud-services\"\n ],\n \"author\": \"Duff Cloud Services\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/duffn/dcs\"\n },\n \"homepage\": \"https://portal.duffcloudservices.com\",\n \"bugs\": {\n \"url\": \"https://github.com/duffn/dcs/issues\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","/**\n * Authentication commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport { login as performLogin, type DeviceFlowOptions } from '../auth/device-flow.js'\nimport { clearCredentials, getCurrentUserEmail, getConfigPath } from '../auth/credentials.js'\n\n/**\n * Login command - authenticate with DCS Portal.\n */\nexport async function loginCommand(options: DeviceFlowOptions = {}): Promise<void> {\n try {\n await performLogin(options)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Login failed:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Logout command - clear stored credentials.\n */\nexport async function logoutCommand(): Promise<void> {\n clearCredentials()\n console.log(chalk.green('✓'), 'Logged out successfully')\n}\n\n/**\n * Whoami command - display current user.\n */\nexport async function whoamiCommand(): Promise<void> {\n const email = getCurrentUserEmail()\n\n if (!email) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n return\n }\n\n console.log('Logged in as:', chalk.green(email))\n console.log(chalk.dim('Config file:'), chalk.dim(getConfigPath()))\n}\n","/**\n * Device authorization flow for CLI authentication.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant) style flow\n * for authenticating CLI users via the DCS Portal.\n */\n\nimport open from 'open'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { storeCredentials, type StoredCredentials } from './credentials.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface DeviceAuthResponse {\n deviceCode: string\n userCode: string\n verificationUri: string\n verificationUriComplete: string\n expiresIn: number\n interval: number\n}\n\nexport interface TokenResponse {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresIn: number\n email: string\n userId: string\n}\n\nexport interface DeviceFlowOptions {\n apiUrl?: string\n clientId?: string\n}\n\n/**\n * Error codes returned during device authorization polling.\n */\ntype DeviceAuthError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied'\n\n/**\n * Sleep for a given number of milliseconds.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Start the device authorization flow.\n */\nexport async function startDeviceAuth(options: DeviceFlowOptions = {}): Promise<DeviceAuthResponse> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n const clientId = options.clientId || 'dcs-cli'\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/device`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ client_id: clientId }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to start device authorization: ${error}`)\n }\n\n const data = await response.json()\n\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: data.verification_uri,\n verificationUriComplete: data.verification_uri_complete,\n expiresIn: data.expires_in,\n interval: data.interval,\n }\n}\n\n/**\n * Poll for authorization completion.\n */\nasync function pollForToken(\n deviceCode: string,\n apiUrl: string\n): Promise<TokenResponse | { error: DeviceAuthError }> {\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: deviceCode,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n })\n\n if (!response.ok) {\n const data = await response.json()\n return { error: data.error as DeviceAuthError }\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n tokenType: data.token_type,\n expiresIn: data.expires_in,\n email: data.email,\n userId: data.user_id,\n }\n}\n\n/**\n * Complete the device authorization flow.\n *\n * This function:\n * 1. Starts the device authorization\n * 2. Opens the browser to the verification URL\n * 3. Polls for completion\n * 4. Stores credentials on success\n */\nexport async function login(options: DeviceFlowOptions = {}): Promise<StoredCredentials> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const spinner = ora('Starting authentication...').start()\n\n // Start device auth\n let deviceAuth: DeviceAuthResponse\n try {\n deviceAuth = await startDeviceAuth(options)\n } catch (error) {\n spinner.fail('Failed to start authentication')\n throw error\n }\n\n spinner.stop()\n\n // Display instructions\n console.log()\n console.log(chalk.bold('Please visit:'), chalk.cyan(deviceAuth.verificationUri))\n console.log(chalk.bold('And enter code:'), chalk.yellow.bold(deviceAuth.userCode))\n console.log()\n\n // Try to open browser\n try {\n await open(deviceAuth.verificationUriComplete)\n console.log(chalk.dim('(Browser opened automatically)'))\n } catch {\n console.log(chalk.dim('(Please open the link manually)'))\n }\n\n console.log()\n\n // Poll for completion\n const pollSpinner = ora('Waiting for authentication...').start()\n\n let interval = deviceAuth.interval\n const deadline = Date.now() + deviceAuth.expiresIn * 1000\n\n while (Date.now() < deadline) {\n await sleep(interval * 1000)\n\n try {\n const result = await pollForToken(deviceAuth.deviceCode, apiUrl)\n\n if ('error' in result) {\n if (result.error === 'authorization_pending') {\n continue\n }\n if (result.error === 'slow_down') {\n interval += 5\n continue\n }\n if (result.error === 'expired_token') {\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n }\n if (result.error === 'access_denied') {\n pollSpinner.fail('Authentication denied')\n throw new Error('Authentication was denied.')\n }\n } else {\n // Success!\n const credentials: StoredCredentials = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: Date.now() + result.expiresIn * 1000,\n email: result.email,\n userId: result.userId,\n }\n\n storeCredentials(credentials)\n\n pollSpinner.succeed(`Authenticated as ${chalk.green(result.email)}`)\n return credentials\n }\n } catch (error) {\n // Network error - keep polling\n if (error instanceof Error && error.message.includes('fetch')) {\n continue\n }\n throw error\n }\n }\n\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n}\n\n/**\n * Refresh the access token using the refresh token.\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n options: DeviceFlowOptions = {}\n): Promise<{ accessToken: string; expiresIn: number }> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n\n if (!response.ok) {\n throw new Error('Failed to refresh access token. Please login again.')\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n expiresIn: data.expires_in,\n }\n}\n","/**\n * Credential storage for CLI authentication.\n *\n * Stores access tokens, refresh tokens, and user info in a config file.\n * Uses the `conf` package for encrypted storage on supported platforms.\n */\n\nimport Conf from 'conf'\n\nexport interface StoredCredentials {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n userId: string\n}\n\ninterface ConfigSchema {\n auth?: StoredCredentials\n}\n\nconst config = new Conf<ConfigSchema>({\n projectName: 'dcs-cli',\n schema: {\n auth: {\n type: 'object',\n properties: {\n accessToken: { type: 'string' },\n refreshToken: { type: 'string' },\n expiresAt: { type: 'number' },\n email: { type: 'string' },\n userId: { type: 'string' },\n },\n },\n },\n})\n\n/**\n * Store credentials after successful authentication.\n */\nexport function storeCredentials(credentials: StoredCredentials): void {\n config.set('auth', credentials)\n}\n\n/**\n * Get stored credentials.\n * Returns undefined if not authenticated.\n */\nexport function getCredentials(): StoredCredentials | undefined {\n return config.get('auth')\n}\n\n/**\n * Clear stored credentials (logout).\n */\nexport function clearCredentials(): void {\n config.delete('auth')\n}\n\n/**\n * Check if user is authenticated with valid (non-expired) credentials.\n */\nexport function isAuthenticated(): boolean {\n const creds = getCredentials()\n if (!creds) return false\n\n // Check if access token is expired (with 5 minute buffer)\n const bufferMs = 5 * 60 * 1000\n return creds.expiresAt > Date.now() + bufferMs\n}\n\n/**\n * Get the current user's email if authenticated.\n */\nexport function getCurrentUserEmail(): string | undefined {\n return getCredentials()?.email\n}\n\n/**\n * Get the current user's ID if authenticated.\n */\nexport function getCurrentUserId(): string | undefined {\n return getCredentials()?.userId\n}\n\n/**\n * Get the access token if available and not expired.\n */\nexport function getAccessToken(): string | undefined {\n const creds = getCredentials()\n if (!creds) return undefined\n\n // Return even if expired - the API client will handle refresh\n return creds.accessToken\n}\n\n/**\n * Get the refresh token if available.\n */\nexport function getRefreshToken(): string | undefined {\n return getCredentials()?.refreshToken\n}\n\n/**\n * Update the access token after a refresh.\n */\nexport function updateAccessToken(accessToken: string, expiresIn: number): void {\n const creds = getCredentials()\n if (!creds) return\n\n config.set('auth', {\n ...creds,\n accessToken,\n expiresAt: Date.now() + expiresIn * 1000,\n })\n}\n\n/**\n * Get the path to the config file (for debugging).\n */\nexport function getConfigPath(): string {\n return config.path\n}\n","/**\n * Sites commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport Table from 'cli-table3'\nimport { getPortalClient, type Site } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\n\n/**\n * List sites command - display sites the user has access to.\n */\nexport async function listSitesCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n\n if (sites.length === 0) {\n console.log(chalk.yellow('No sites found.'))\n console.log('Ask your company admin to grant you access to sites.')\n return\n }\n\n console.log(chalk.bold('\\nYour Sites:\\n'))\n\n const table = new Table({\n head: [\n chalk.cyan('Slug'),\n chalk.cyan('Name'),\n chalk.cyan('Role'),\n chalk.cyan('Company'),\n chalk.cyan('URL'),\n ],\n style: {\n head: [],\n border: [],\n },\n })\n\n for (const site of sites) {\n table.push([\n site.slug,\n site.name,\n formatRole(site.role),\n site.companyName,\n site.productionUrl || chalk.dim('(not deployed)'),\n ])\n }\n\n console.log(table.toString())\n console.log()\n console.log(chalk.dim(`Total: ${sites.length} site(s)`))\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Show site details command.\n */\nexport async function showSiteCommand(slug: string): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const site = await client.getSite(slug)\n displaySiteDetails(site)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\nfunction formatRole(role: string): string {\n switch (role) {\n case 'owner':\n return chalk.magenta(role)\n case 'admin':\n return chalk.red(role)\n case 'editor':\n return chalk.yellow(role)\n case 'viewer':\n return chalk.blue(role)\n default:\n return role\n }\n}\n\nfunction displaySiteDetails(site: Site): void {\n console.log()\n console.log(chalk.bold('Site Details'))\n console.log()\n console.log(' Slug: ', chalk.cyan(site.slug))\n console.log(' Name: ', site.name)\n console.log(' Role: ', formatRole(site.role))\n console.log(' Company: ', site.companyName)\n console.log(' Company ID: ', chalk.dim(site.companyId))\n if (site.productionUrl) {\n console.log(' URL: ', chalk.green(site.productionUrl))\n } else {\n console.log(' URL: ', chalk.dim('(not deployed)'))\n }\n console.log()\n}\n","/**\n * Portal API client for CLI operations.\n */\n\nimport { getAccessToken, getRefreshToken, updateAccessToken, isAuthenticated } from '../auth/credentials.js'\nimport { refreshAccessToken } from '../auth/device-flow.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface Site {\n slug: string\n name: string\n role: 'owner' | 'admin' | 'editor' | 'viewer'\n companyId: string\n companyName: string\n productionUrl?: string\n}\n\nexport interface PortalClientOptions {\n apiUrl?: string\n}\n\nexport class PortalClient {\n private apiUrl: string\n\n constructor(options: PortalClientOptions = {}) {\n this.apiUrl = options.apiUrl || process.env.DCS_API_URL || DEFAULT_API_URL\n }\n\n /**\n * Make an authenticated API request.\n */\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n // Get access token\n let accessToken = getAccessToken()\n\n // Check if we need to refresh\n if (!isAuthenticated() && getRefreshToken()) {\n const refreshToken = getRefreshToken()!\n try {\n const { accessToken: newToken, expiresIn } = await refreshAccessToken(refreshToken, {\n apiUrl: this.apiUrl,\n })\n updateAccessToken(newToken, expiresIn)\n accessToken = newToken\n } catch {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n }\n\n if (!accessToken) {\n throw new Error('Not authenticated. Please login with `dcs login`.')\n }\n\n const response = await fetch(`${this.apiUrl}${path}`, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (response.status === 401) {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`API error (${response.status}): ${error}`)\n }\n\n return response.json()\n }\n\n /**\n * List sites accessible to the authenticated user.\n */\n async listSites(): Promise<{ sites: Site[] }> {\n return this.request('/api/v1/cli/sites')\n }\n\n /**\n * Get details for a specific site.\n */\n async getSite(slug: string): Promise<Site> {\n return this.request(`/api/v1/cli/sites/${slug}`)\n }\n\n /**\n * Validate that the user can create/modify a site.\n */\n async validateSiteAccess(\n slug: string,\n operation: 'read' | 'edit' | 'create'\n ): Promise<{ valid: boolean; role?: string; message?: string }> {\n try {\n const { sites } = await this.listSites()\n const site = sites.find((s) => s.slug === slug)\n\n if (!site) {\n if (operation === 'create') {\n // Check if user has any admin access to create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n return canCreate\n ? { valid: true, message: 'New site will be created' }\n : { valid: false, message: 'You need admin access in a company to create new sites' }\n }\n return { valid: false, message: `Site '${slug}' not found or you don't have access` }\n }\n\n if (operation === 'read') {\n return { valid: true, role: site.role }\n }\n\n if (operation === 'edit') {\n const canEdit = ['owner', 'admin', 'editor'].includes(site.role)\n return canEdit\n ? { valid: true, role: site.role }\n : { valid: false, role: site.role, message: 'You need editor access to modify this site' }\n }\n\n if (operation === 'create') {\n return { valid: false, message: 'Site already exists' }\n }\n\n return { valid: false }\n } catch (error) {\n if (error instanceof Error) {\n return { valid: false, message: error.message }\n }\n return { valid: false, message: 'Unknown error' }\n }\n }\n}\n\n// Singleton instance\nlet clientInstance: PortalClient | null = null\n\nexport function getPortalClient(options?: PortalClientOptions): PortalClient {\n if (!clientInstance) {\n clientInstance = new PortalClient(options)\n }\n return clientInstance\n}\n","/**\n * Init command - initialize DCS integration for a customer site.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { getPortalClient } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\nimport siteDeployTemplate from '../templates/site-deploy.yml'\n\nexport interface InitOptions {\n siteSlug: string\n siteName: string\n framework?: 'vue' | 'astro'\n target?: string\n dryRun?: boolean\n force?: boolean\n}\n\n/**\n * Initialize DCS integration for a customer site.\n */\nexport async function initCommand(options: InitOptions): Promise<void> {\n const { siteSlug, siteName, framework = 'vue', target = '.', dryRun = false, force = false } = options\n\n // 1. Check authentication\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n // 2. Validate site access\n const spinner = ora('Validating site access...').start()\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n const existingSite = sites.find((s) => s.slug === siteSlug)\n\n if (existingSite) {\n if (!['owner', 'admin', 'editor'].includes(existingSite.role)) {\n spinner.fail(`You don't have edit access to site '${siteSlug}'`)\n process.exit(1)\n }\n spinner.succeed(`Site '${siteSlug}' found (role: ${existingSite.role})`)\n } else {\n // Check if user can create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n if (!canCreate) {\n spinner.fail('You need admin access in a company to create new sites')\n process.exit(1)\n }\n spinner.info(`Creating new site: ${siteSlug}`)\n }\n } catch (error) {\n spinner.fail('Failed to validate site access')\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 3. Resolve target directory\n const targetDir = path.resolve(target)\n console.log(chalk.dim(`Target directory: ${targetDir}`))\n\n // 4. Generate configuration files\n const generatedFiles: string[] = []\n const timestamp = new Date().toISOString()\n\n try {\n // .dcs/site.yaml\n const siteYaml = generateSiteYaml({ siteSlug, siteName, timestamp, framework })\n generatedFiles.push(await writeFile(targetDir, '.dcs/site.yaml', siteYaml, { dryRun, force }))\n\n // .dcs/pages.yaml\n const pagesYaml = generatePagesYaml({ siteSlug, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/pages.yaml', pagesYaml, { dryRun, force }))\n\n // .dcs/content.yaml\n const contentYaml = generateContentYaml({ timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/content.yaml', contentYaml, { dryRun, force }))\n\n // .dcs/seo.yaml\n const seoYaml = generateSeoYaml({ siteName, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/seo.yaml', seoYaml, { dryRun, force }))\n\n // .dcs/SECTION-CONVENTIONS.md\n const sectionConventions = generateSectionConventions()\n generatedFiles.push(\n await writeFile(targetDir, '.dcs/SECTION-CONVENTIONS.md', sectionConventions, { dryRun, force })\n )\n\n // .github/copilot-instructions.md\n const copilotInstructions = generateCopilotInstructions({ siteName })\n generatedFiles.push(\n await writeFile(targetDir, '.github/copilot-instructions.md', copilotInstructions, { dryRun, force })\n )\n\n // .github/workflows/site-deploy.yml\n const deployWorkflow = generateDeployWorkflow()\n generatedFiles.push(\n await writeFile(targetDir, '.github/workflows/site-deploy.yml', deployWorkflow, { dryRun, force })\n )\n\n // .plans/README.md\n const plansReadme = generatePlansReadme({ siteName, framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/README.md', plansReadme, { dryRun, force }))\n\n // .plans/00-audit-site.md\n const plan00 = generatePlan00AuditSite({ framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/00-audit-site.md', plan00, { dryRun, force }))\n\n // .plans/01-integrate-content.md\n const plan01 = generatePlan01IntegrateContent()\n generatedFiles.push(await writeFile(targetDir, '.plans/01-integrate-content.md', plan01, { dryRun, force }))\n\n // .plans/02-configure-seo.md\n const plan02 = generatePlan02ConfigureSeo()\n generatedFiles.push(await writeFile(targetDir, '.plans/02-configure-seo.md', plan02, { dryRun, force }))\n\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 5. Display results\n console.log()\n console.log(chalk.bold('Generated files:'))\n for (const file of generatedFiles) {\n console.log(` ${chalk.green('✓')} ${file}`)\n }\n\n // 6. Post-init instructions\n console.log()\n console.log(chalk.bold('Next steps:'))\n console.log(' 1. Install CMS package:', chalk.cyan('pnpm add @duffcloudservices/cms'))\n console.log(' 2. Configure Vite plugins in your build config')\n console.log(' 3. Follow the plans in', chalk.cyan('.plans/'), 'directory')\n console.log()\n}\n\n// =============================================================================\n// File Generation Helpers\n// =============================================================================\n\ninterface WriteOptions {\n dryRun: boolean\n force: boolean\n}\n\nasync function writeFile(\n targetDir: string,\n relativePath: string,\n content: string,\n options: WriteOptions\n): Promise<string> {\n const fullPath = path.join(targetDir, relativePath)\n\n if (options.dryRun) {\n console.log(chalk.dim(`Would create: ${relativePath}`))\n return relativePath\n }\n\n // Check if file exists\n try {\n await fs.access(fullPath)\n if (!options.force) {\n console.log(chalk.yellow(`Skipped (exists): ${relativePath}`))\n return relativePath\n }\n } catch {\n // File doesn't exist, continue\n }\n\n // Create directory if needed\n await fs.mkdir(path.dirname(fullPath), { recursive: true })\n\n // Write file\n await fs.writeFile(fullPath, content, 'utf8')\n\n return relativePath\n}\n\n// =============================================================================\n// Template Generators\n// =============================================================================\n\nfunction generateSiteYaml(data: {\n siteSlug: string\n siteName: string\n timestamp: string\n framework: string\n}): string {\n return `# DCS Site Configuration\n# This file defines the site identity and Azure resource bindings.\n# Generated by: @duffcloudservices/cli\n\nsite_name: ${data.siteName}\nsite_slug: ${data.siteSlug}\n\n# Azure Static Web App resource ID\n# Set this after provisioning the SWA resource in Azure\nswa_resource_id: \"\"\n\n# URLs are populated after first deployment\nproduction_url: \"\"\npreview_url: \"\"\n\n# Google Analytics Configuration\ngoogle_analytics_id: \"\"\n\n# Azure Authentication (Managed Identity)\n# These values are used by GitHub Actions for OIDC authentication\nazure:\n client_id: \"\"\n tenant_id: \"\"\n subscription_id: \"\"\n\n# Portal API Configuration\nportal_api_url: \"https://portal.duffcloudservices.com\"\n\n# Site metadata for portal\nmetadata:\n framework: ${data.framework}\n managed_by: dcs\n created_at: \"${data.timestamp}\"\n`\n}\n\nfunction generatePagesYaml(data: { siteSlug: string; timestamp: string }): string {\n return `# .dcs/pages.yaml\n# Page registry for DCS CMS integration and snapshot capture.\n# Generated by: @duffcloudservices/cli\n#\n# This file defines page metadata only. Text content is stored in content.yaml\n# and text keys are auto-discovered during snapshot capture.\n\nversion: 3\nsiteSlug: ${data.siteSlug}\nlastUpdated: \"${data.timestamp}\"\ngeneratedBy: dcs-cli\n\npages:\n # Home page - always protected\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n\n# Excluded paths - never captured\nexcluded:\n - /404\n - /dev-*\n - /_*\n\n# Snapshot configuration\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000\n captureFullPage: true\n`\n}\n\nfunction generateContentYaml(data: { timestamp: string }): string {\n return `# .dcs/content.yaml\n# Managed by DCS Portal - Do not edit manually\n# This file contains all text content for the site.\n#\n# Content is organized by page slug, with each page containing\n# key-value pairs for editable text. The \\`global\\` section contains\n# text shared across all pages (navigation, footer, etc.).\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global text content (shared across all pages)\nglobal: {}\n\n# Page-specific text content\npages: {}\n`\n}\n\nfunction generateSeoYaml(data: { siteName: string; timestamp: string }): string {\n return `# .dcs/seo.yaml\n# Managed by DCS Portal - Do not edit manually\n# SEO configuration for all pages and global defaults.\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global/site-wide SEO defaults\nglobal:\n siteName: \"${data.siteName}\"\n siteUrl: \"\" # Set your production URL\n locale: en_US\n defaultTitle: \"${data.siteName}\"\n defaultDescription: \"\"\n titleTemplate: \"%s | ${data.siteName}\"\n\n # Social media handles\n social:\n twitter: \"\"\n linkedin: \"\"\n\n # Default images for social sharing\n images:\n logo: \"\"\n ogDefault: \"\"\n twitterDefault: \"\"\n\n # Default robots directive\n robots: \"index, follow\"\n\n# Page-specific SEO configurations\npages:\n home:\n title: Home\n description: \"\"\n noTitleTemplate: true\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n`\n}\n\nfunction generateSectionConventions(): string {\n return `# Section Conventions for DCS CMS Integration\n\nThis document describes the HTML attributes used by the DCS visual page editor\nfor section identification and snapshot capture.\n\n## Required Attributes\n\n### \\`data-dcs-section\\`\n\nMarks an element as an editable section. The value should match the section's\nidentifier in content.yaml.\n\n\\`\\`\\`html\n<section data-dcs-section=\"hero\">\n <!-- Section content -->\n</section>\n\\`\\`\\`\n\n### \\`data-dcs-text\\`\n\nMarks an element's text as editable via the portal. The value should be the\ntext key from content.yaml.\n\n\\`\\`\\`html\n<h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n\\`\\`\\`\n\n## Best Practices\n\n1. Use semantic section names that describe the content\n2. Keep text keys hierarchical (section.element)\n3. Ensure all user-editable text has a data-dcs-text attribute\n4. Don't nest sections unnecessarily\n`\n}\n\nfunction generateCopilotInstructions(data: { siteName: string }): string {\n return `# GitHub Copilot Instructions for ${data.siteName}\n\n## Project Overview\n\nThis is a **DCS-managed customer site**. Content and feature changes are managed\nthrough the DCS Portal at https://portal.duffcloudservices.com.\n\n## Technology Stack\n\n- Vue 3 + Composition API\n- VitePress (or Vite)\n- @duffcloudservices/cms for content management\n- Azure Static Web Apps for hosting\n\n## Content Management\n\n### Text Content\n\nUse the \\`useTextContent\\` composable for all editable text:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Default Title',\n 'hero.subtitle': 'Default subtitle'\n }\n})\n</script>\n\n<template>\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n</template>\n\\`\\`\\`\n\n### SEO\n\nUse the \\`useSEO\\` composable for page SEO:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n## Key Conventions\n\n1. All user-editable text must use \\`useTextContent\\`\n2. All pages must have SEO configured via \\`useSEO\\`\n3. Section elements need \\`data-dcs-section\\` attributes\n4. Text elements need \\`data-dcs-text\\` attributes\n5. See \\`.dcs/SECTION-CONVENTIONS.md\\` for details\n`\n}\n\nfunction generatePlansReadme(data: { siteName: string; framework: string }): string {\n return `# DCS Integration Plans for ${data.siteName}\n\nThis directory contains step-by-step plans for integrating this site with the\nDCS content management system.\n\n## Overview\n\nThese plans guide AI assistants (like GitHub Copilot) through the integration\nprocess. Follow them in order for best results.\n\n## Plans\n\n1. **00-audit-site.md** - Analyze current site structure\n2. **01-create-use-text-content.md** - Set up the text content composable\n3. **02-integrate-home-page.md** - Add CMS integration to home page\n4. **03-integrate-remaining-pages.md** - Complete other pages\n5. **04-capture-snapshots.md** - Configure visual editor snapshots\n6. **05-verify-deployment.md** - Test and verify deployment\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Getting Started\n\n1. Open plan 00 and ask Copilot to execute it\n2. Review the output and make any needed adjustments\n3. Continue with subsequent plans\n\n## Configuration Files\n\n- \\`.dcs/site.yaml\\` - Site identity and Azure config\n- \\`.dcs/pages.yaml\\` - Page registry for CMS\n- \\`.dcs/content.yaml\\` - Text content (managed by Portal)\n- \\`.dcs/seo.yaml\\` - SEO configuration (managed by Portal)\n`\n}\n\nfunction generateDeployWorkflow(): string {\n return siteDeployTemplate\n}\n\nfunction generatePlan00AuditSite(data: { framework: string }): string {\n return `# Plan 00: Audit Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand what pages exist, their routes,\nand identify all text content that should be managed via DCS.\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Tasks\n\n### 1. List All Pages\n\nIdentify all page components/routes in the project:\n\n- For Vue/VitePress: Look in \\`src/views/\\`, \\`src/pages/\\`, or \\`.vitepress/\\`\n- For Astro: Look in \\`src/pages/\\`\n\nCreate a list of:\n- Route path (e.g., \\`/\\`, \\`/about\\`, \\`/services\\`)\n- Component file path\n- Page title (from existing meta or content)\n\n### 2. Identify Text Content\n\nFor each page, list all user-facing text that should be editable:\n\n- Headings (h1, h2, etc.)\n- Paragraphs and body text\n- Button labels\n- Navigation links\n- Footer content\n\nUse this naming convention for text keys:\n\\`\\`\\`\n{pageSlug}.{section}.{element}\n\nExamples:\n- home.hero.title\n- home.hero.subtitle\n- home.cta.buttonText\n- about.intro.heading\n\\`\\`\\`\n\n### 3. Update pages.yaml\n\nAdd all discovered pages to \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n \n - slug: about\n path: /about\n type: static\n title: About Us\n deletable: true\n\\`\\`\\`\n\n### 4. Document Findings\n\nCreate a brief summary of:\n- Total number of pages\n- Estimated number of text keys per page\n- Any complex sections (carousels, tabs, etc.)\n- Shared components that contain text (header, footer)\n\n## Output\n\nAfter completing this plan, you should have:\n1. Updated \\`.dcs/pages.yaml\\` with all pages\n2. A mental map of text content to migrate\n3. Understanding of component structure\n`\n}\n\nfunction generatePlan01IntegrateContent(): string {\n return `# Plan 01: Integrate Text Content\n\n## Objective\n\nSet up the \\`useTextContent\\` composable and migrate all static text to the\nDCS content management system.\n\n## Prerequisites\n\n- Completed Plan 00 (site audit)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Install DCS CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### 2. Configure Vite Plugin\n\nAdd the DCS content plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({\n contentPath: '.dcs/content.yaml',\n }),\n ],\n})\n\\`\\`\\`\n\n### 3. Integrate Home Page\n\nStart with the home page as a template:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Your Default Title',\n 'hero.subtitle': 'Your default subtitle text',\n 'hero.cta': 'Get Started',\n }\n})\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### 4. Key Patterns\n\n**Text Keys**: Use hierarchical naming\n- \\`{section}.{element}\\` for page-specific content\n- Prefix with \\`global.\\` for shared content\n\n**Default Values**: Always provide defaults that match current content\n- This ensures the site works before CMS content is loaded\n- Defaults serve as fallback if content fetch fails\n\n**Data Attributes**: Required for visual editor\n- \\`data-dcs-section\\` on section containers\n- \\`data-dcs-text\\` on text elements (value = text key)\n\n### 5. Migrate Remaining Pages\n\nApply the same pattern to all pages identified in Plan 00.\n\nFor each page:\n1. Import \\`useTextContent\\`\n2. Define defaults for all text content\n3. Replace static text with \\`{{ t('key') }}\\`\n4. Add \\`data-dcs-text\\` attributes\n\n### 6. Handle Global Content\n\nCreate a shared composable for navigation/footer:\n\n\\`\\`\\`typescript\n// composables/useGlobalContent.ts\nimport { useTextContent } from '@duffcloudservices/cms'\n\nexport function useGlobalContent() {\n return useTextContent({\n pageSlug: 'global',\n defaults: {\n 'nav.home': 'Home',\n 'nav.about': 'About',\n 'footer.copyright': '© 2025 Company Name',\n }\n })\n}\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. Run \\`pnpm dev\\` - site should display normally with defaults\n2. Check browser console for any missing text key warnings\n3. Verify all text elements have \\`data-dcs-text\\` attributes\n`\n}\n\nfunction generatePlan02ConfigureSeo(): string {\n return `# Plan 02: Configure SEO\n\n## Objective\n\nSet up the \\`useSEO\\` composable to manage page metadata through DCS Portal.\n\n## Prerequisites\n\n- Completed Plan 01 (content integration)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Configure Vite Plugin\n\nAdd the DCS SEO plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ contentPath: '.dcs/content.yaml' }),\n dcsSeoPlugin({ seoPath: '.dcs/seo.yaml' }),\n ],\n})\n\\`\\`\\`\n\n### 2. Add SEO to Pages\n\nFor each page, add the \\`useSEO\\` composable:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\n// Content setup\nconst { t } = useTextContent({ pageSlug: 'home', defaults: { ... } })\n\n// SEO setup - applies head tags automatically\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n### 3. Configure Default SEO\n\nUpdate \\`.dcs/seo.yaml\\` with site-wide defaults:\n\n\\`\\`\\`yaml\nglobal:\n siteName: \"Your Site Name\"\n siteUrl: \"https://yoursite.com\"\n locale: en_US\n defaultTitle: \"Your Site Name\"\n defaultDescription: \"Your site description for search engines\"\n titleTemplate: \"%s | Your Site Name\"\n \n social:\n twitter: \"@yourhandle\"\n linkedin: \"company/yourcompany\"\n \n images:\n logo: \"/images/logo.png\"\n ogDefault: \"/images/og-default.png\"\n\npages:\n home:\n title: Home\n description: \"Welcome to our site\"\n noTitleTemplate: true # Home page often skips \" | Site Name\"\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n \n about:\n title: About Us\n description: \"Learn more about our company\"\n\\`\\`\\`\n\n### 4. Page-Specific Overrides\n\nEach page can have custom SEO settings that override globals:\n\n\\`\\`\\`yaml\npages:\n services:\n title: Our Services\n description: \"Explore our professional services\"\n canonical: \"https://yoursite.com/services\"\n openGraph:\n title: \"Services - Custom OG Title\"\n description: \"Custom description for social sharing\"\n image: \"/images/services-og.png\"\n\\`\\`\\`\n\n### 5. Dynamic Pages (Blogs, etc.)\n\nFor dynamic pages like blog posts, you'll pass SEO data dynamically:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst props = defineProps<{ post: BlogPost }>()\n\nconst { applyHead } = useSEO('blog-post', {\n title: props.post.title,\n description: props.post.excerpt,\n openGraph: {\n type: 'article',\n image: props.post.featuredImage,\n }\n})\napplyHead()\n</script>\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. View page source - check for meta tags\n2. Use browser dev tools to inspect \\`<head>\\`\n3. Test with social sharing debuggers (Facebook, Twitter, LinkedIn)\n4. Validate with Google's Rich Results Test\n`\n}\n","# Site Deployment Workflow\n# Generated by @duffcloudservices/cli\n# This workflow deploys the site to Azure Static Web Apps using OIDC authentication.\n\nname: Site Deployment\n\non:\n push:\n branches:\n - 'release/**'\n - 'master'\n workflow_dispatch:\n inputs:\n trigger_reason:\n description: 'Reason for manual deployment'\n required: false\n type: string\n default: 'Manual deployment'\n\n# Prevent concurrent deployments for the same branch\nconcurrency:\n group: site-deploy-${{ github.ref }}\n cancel-in-progress: false\n\npermissions:\n contents: read\n issues: write\n pull-requests: read\n id-token: write # Required for Azure OIDC authentication\n\njobs:\n deploy:\n name: Deploy to Azure Static Web App\n runs-on: ubuntu-latest\n # Skip deployment on branch creation events\n if: github.event.before != '0000000000000000000000000000000000000000'\n\n steps:\n - name: Determine deployment environment\n id: environment\n run: |\n BRANCH=\"${{ github.ref_name }}\"\n echo \"Branch: $BRANCH\"\n \n if [ \"$BRANCH\" == \"master\" ]; then\n echo \"environment=Production\" >> $GITHUB_OUTPUT\n echo \"environment_name=Production\" >> $GITHUB_OUTPUT\n echo \"swa_environment=\" >> $GITHUB_OUTPUT\n else\n echo \"environment=preview\" >> $GITHUB_OUTPUT\n echo \"environment_name=Preview\" >> $GITHUB_OUTPUT\n echo \"swa_environment=preview\" >> $GITHUB_OUTPUT\n fi\n\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Read site configuration\n id: config\n run: |\n CONFIG_FILE=\".dcs/site.yaml\"\n \n if [ ! -f \"$CONFIG_FILE\" ]; then\n echo \"::error::Missing DCS site configuration file: $CONFIG_FILE\"\n exit 1\n fi\n \n SITE_NAME=$(grep -E '^site_name:' \"$CONFIG_FILE\" | sed 's/site_name:\\s*//' | tr -d '\"' | tr -d \"'\")\n SITE_SLUG=$(grep -E '^site_slug:' \"$CONFIG_FILE\" | sed 's/site_slug:\\s*//' | tr -d '\"' | tr -d \"'\")\n SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' \"$CONFIG_FILE\" | sed 's/swa_resource_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=$(grep -E '^portal_api_url:' \"$CONFIG_FILE\" | sed 's/portal_api_url:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=\"${PORTAL_API_URL:-https://portal.duffcloudservices.com}\"\n GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' \"$CONFIG_FILE\" | sed 's/google_analytics_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PROD_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^prod_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/prod_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n DEV_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^dev_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/dev_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n \n # Azure Authentication\n AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' \"$CONFIG_FILE\" | sed 's/.*client_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' \"$CONFIG_FILE\" | sed 's/.*tenant_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' \"$CONFIG_FILE\" | sed 's/.*subscription_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n # Build Configuration\n APP_LOCATION=$(grep -E '^\\s*app_location:' \"$CONFIG_FILE\" | sed 's/.*app_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' \"$CONFIG_FILE\" | sed 's/.*output_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n APP_LOCATION=\"${APP_LOCATION:-dist}\"\n OUTPUT_LOCATION=\"${OUTPUT_LOCATION:-.}\"\n\n if [ -z \"$SWA_RESOURCE_ID\" ]; then\n echo \"::error::Missing swa_resource_id in $CONFIG_FILE\"\n exit 1\n fi\n \n echo \"site_name=$SITE_NAME\" >> $GITHUB_OUTPUT\n echo \"site_slug=$SITE_SLUG\" >> $GITHUB_OUTPUT\n echo \"swa_resource_id=$SWA_RESOURCE_ID\" >> $GITHUB_OUTPUT\n echo \"app_location=$APP_LOCATION\" >> $GITHUB_OUTPUT\n echo \"output_location=$OUTPUT_LOCATION\" >> $GITHUB_OUTPUT\n echo \"azure_client_id=$AZURE_CLIENT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_tenant_id=$AZURE_TENANT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_subscription_id=$AZURE_SUBSCRIPTION_ID\" >> $GITHUB_OUTPUT\n echo \"portal_api_url=$PORTAL_API_URL\" >> $GITHUB_OUTPUT\n echo \"google_analytics_id=$GOOGLE_ANALYTICS_ID\" >> $GITHUB_OUTPUT\n echo \"prod_appinsights_connection_string=$PROD_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n echo \"dev_appinsights_connection_string=$DEV_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n\n - name: Azure login via OIDC\n uses: azure/login@v2\n with:\n client-id: ${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}\n tenant-id: ${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}\n subscription-id: ${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}\n\n - name: Get deployment tokens\n id: tokens\n run: |\n # Get access token for DCS API\n ACCESS_TOKEN=$(az account get-access-token --resource \"api://304711b9-8532-4659-beb7-c85726de9ae5\" --query accessToken -o tsv)\n \n SITE_NAME=\"${{ steps.config.outputs.site_name }}\"\n SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n PORTAL_API_URL=\"${{ steps.config.outputs.portal_api_url }}\"\n \n RESPONSE=$(curl -s -X POST \"${PORTAL_API_URL}/api/v1/sites/deployment-tokens\" \\\n -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"siteName\\\": \\\"$SITE_NAME\\\", \\\"siteSlug\\\": \\\"$SITE_SLUG\\\"}\")\n \n SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)\n \n if [ -z \"$SWA_TOKEN\" ] || [ \"$SWA_TOKEN\" == \"null\" ]; then\n echo \"::error::Failed to retrieve SWA token from DCS API\"\n echo \"Response: $RESPONSE\"\n exit 1\n fi\n \n echo \"::add-mask::$SWA_TOKEN\"\n echo \"swa_token=$SWA_TOKEN\" >> $GITHUB_OUTPUT\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Install pnpm\n uses: pnpm/action-setup@v4\n\n - name: Setup pnpm cache\n uses: actions/cache@v4\n with:\n path: ~/.pnpm-store\n key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}\n restore-keys: |\n ${{ runner.os }}-pnpm-\n\n - name: Install dependencies\n run: pnpm install --frozen-lockfile\n\n - name: Create environment file\n run: |\n cat > .env << EOF\n VITE_APP_VERSION=\"${{ github.sha }}\"\n VITE_BACKEND_URI=\"${{ steps.config.outputs.portal_api_url }}\"\n VITE_SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n VITE_GOOGLE_ANALYTICS_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n VITE_PROD_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.prod_appinsights_connection_string }}\"\n VITE_DEV_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.dev_appinsights_connection_string }}\"\n EOF\n\n - name: Build site\n run: pnpm run build\n\n - name: Inject Google Analytics\n if: steps.config.outputs.google_analytics_id != ''\n run: |\n GA_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n OUTPUT_DIR=\"${{ steps.config.outputs.app_location }}\"\n echo \"Injecting Google Analytics tag ${GA_ID} into ${OUTPUT_DIR}...\"\n find \"$OUTPUT_DIR\" -name \"*.html\" -exec sed -i \"s|</head>|<script async src=\\\"https://www.googletagmanager.com/gtag/js?id=${GA_ID}\\\"></script><script>window.dataLayer=window.dataLayer\\|\\|[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','${GA_ID}')</script>\\n</head>|\" {} +\n INJECTED=$(grep -rl \"googletagmanager\" \"$OUTPUT_DIR\" --include=\"*.html\" | wc -l)\n echo \"Injected GA tag into ${INJECTED} HTML file(s)\"\n\n - name: Deploy to Azure Static Web Apps\n uses: Azure/static-web-apps-deploy@v1\n with:\n azure_static_web_apps_api_token: ${{ steps.tokens.outputs.swa_token }}\n repo_token: ${{ secrets.GITHUB_TOKEN }}\n action: \"upload\"\n app_location: ${{ steps.config.outputs.app_location }}\n output_location: ${{ steps.config.outputs.output_location }}\n skip_app_build: true\n deployment_environment: ${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}\n","/**\n * Validate command - validate existing .dcs configuration files.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface ValidateOptions {\n target?: string\n fix?: boolean\n verbose?: boolean\n}\n\ninterface ValidationResult {\n file: string\n valid: boolean\n errors: string[]\n warnings: string[]\n}\n\n/**\n * Validate .dcs configuration files for a customer site.\n */\nexport async function validateCommand(options: ValidateOptions): Promise<void> {\n const { target = '.', fix = false, verbose = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n\n console.log(chalk.bold('Validating DCS configuration...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'to create DCS configuration.')\n process.exit(1)\n }\n\n const results: ValidationResult[] = []\n\n // Validate each config file\n results.push(await validateSiteYaml(dcsDir, verbose))\n results.push(await validatePagesYaml(dcsDir, verbose))\n results.push(await validateContentYaml(dcsDir, verbose))\n results.push(await validateSeoYaml(dcsDir, verbose))\n\n // Display results\n console.log(chalk.bold('Validation Results:'))\n console.log()\n\n let hasErrors = false\n let hasWarnings = false\n\n for (const result of results) {\n if (!result.valid || result.errors.length > 0) {\n hasErrors = true\n console.log(`${chalk.red('✗')} ${result.file}`)\n for (const error of result.errors) {\n console.log(` ${chalk.red('error:')} ${error}`)\n }\n } else if (result.warnings.length > 0) {\n hasWarnings = true\n console.log(`${chalk.yellow('!')} ${result.file}`)\n } else {\n console.log(`${chalk.green('✓')} ${result.file}`)\n }\n\n for (const warning of result.warnings) {\n console.log(` ${chalk.yellow('warning:')} ${warning}`)\n }\n }\n\n console.log()\n\n if (hasErrors) {\n console.log(chalk.red('Validation failed with errors.'))\n if (fix) {\n console.log(chalk.yellow('Some errors can be fixed automatically. Run with --fix flag.'))\n }\n process.exit(1)\n } else if (hasWarnings) {\n console.log(chalk.yellow('Validation passed with warnings.'))\n } else {\n console.log(chalk.green('All configuration files are valid.'))\n }\n}\n\n// =============================================================================\n// File Validators\n// =============================================================================\n\nasync function validateSiteYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'site.yaml')\n const result: ValidationResult = {\n file: '.dcs/site.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (!config.site_name) {\n result.errors.push('Missing required field: site_name')\n result.valid = false\n }\n\n if (!config.site_slug) {\n result.errors.push('Missing required field: site_slug')\n result.valid = false\n } else if (!/^[a-z0-9-]+$/.test(config.site_slug as string)) {\n result.errors.push('site_slug must be lowercase alphanumeric with hyphens only')\n result.valid = false\n }\n\n // Warnings for empty optional fields\n if (!config.swa_resource_id) {\n result.warnings.push('swa_resource_id is not set (required for deployment)')\n }\n\n if (!config.production_url) {\n result.warnings.push('production_url is not set')\n }\n\n if (verbose) {\n console.log(chalk.dim(` site_name: ${config.site_name}`))\n console.log(chalk.dim(` site_slug: ${config.site_slug}`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validatePagesYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'pages.yaml')\n const result: ValidationResult = {\n file: '.dcs/pages.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n if (!config.siteSlug) {\n result.errors.push('Missing required field: siteSlug')\n result.valid = false\n }\n\n // Validate pages array\n const pages = config.pages as Array<Record<string, unknown>> | undefined\n if (!pages || !Array.isArray(pages)) {\n result.errors.push('Missing or invalid pages array')\n result.valid = false\n } else {\n // Check for home page\n const hasHome = pages.some((p) => p.slug === 'home')\n if (!hasHome) {\n result.warnings.push('No home page defined (slug: home)')\n }\n\n // Validate each page\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i]\n if (!page.slug) {\n result.errors.push(`Page ${i}: missing slug`)\n result.valid = false\n }\n if (!page.path) {\n result.errors.push(`Page ${i}: missing path`)\n result.valid = false\n }\n if (!page.type) {\n result.warnings.push(`Page '${page.slug}': missing type (defaulting to static)`)\n }\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pages.length} pages defined`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateContentYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'content.yaml')\n const result: ValidationResult = {\n file: '.dcs/content.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate structure\n if (config.global !== undefined && typeof config.global !== 'object') {\n result.errors.push('global must be an object')\n result.valid = false\n }\n\n if (config.pages !== undefined && typeof config.pages !== 'object') {\n result.errors.push('pages must be an object')\n result.valid = false\n }\n\n if (verbose) {\n const globalKeys = config.global ? Object.keys(config.global as object).length : 0\n const pageCount = config.pages ? Object.keys(config.pages as object).length : 0\n console.log(chalk.dim(` ${globalKeys} global keys, ${pageCount} page sections`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateSeoYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'seo.yaml')\n const result: ValidationResult = {\n file: '.dcs/seo.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate global section\n const global = config.global as Record<string, unknown> | undefined\n if (!global) {\n result.errors.push('Missing required section: global')\n result.valid = false\n } else {\n if (!global.siteName) {\n result.warnings.push('global.siteName is not set')\n }\n if (!global.siteUrl) {\n result.warnings.push('global.siteUrl is not set (required for SEO)')\n }\n if (!global.defaultDescription) {\n result.warnings.push('global.defaultDescription is not set')\n }\n }\n\n // Validate pages section\n const pages = config.pages as Record<string, unknown> | undefined\n if (pages && typeof pages === 'object') {\n const pageKeys = Object.keys(pages)\n if (!pageKeys.includes('home')) {\n result.warnings.push('No SEO configuration for home page')\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pageKeys.length} page SEO configs`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n","/**\n * Plans command - generate/regenerate Copilot prompt plans for DCS integration.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface PlansOptions {\n target?: string\n force?: boolean\n}\n\n/**\n * Generate DCS integration plan files for AI-assisted onboarding.\n */\nexport async function plansCommand(options: PlansOptions): Promise<void> {\n const { target = '.', force = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n const plansDir = path.join(targetDir, '.plans')\n\n console.log(chalk.bold('Generating DCS integration plans...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'first to create DCS configuration.')\n process.exit(1)\n }\n\n // Read site config for context\n let siteName = 'Customer Site'\n let framework = 'vue'\n try {\n const siteYaml = await fs.readFile(path.join(dcsDir, 'site.yaml'), 'utf8')\n const siteConfig = yaml.load(siteYaml) as Record<string, unknown>\n siteName = (siteConfig.site_name as string) || siteName\n const metadata = siteConfig.metadata as Record<string, unknown> | undefined\n framework = (metadata?.framework as string) || framework\n } catch {\n console.log(chalk.yellow('Warning: Could not read site.yaml'))\n }\n\n // Generate plan files\n const plans: Array<{ name: string; content: string }> = [\n { name: '00-audit-site.md', content: generateAuditPlan(siteName) },\n { name: '01-create-use-text-content.md', content: generateTextContentPlan(siteName, framework) },\n { name: '02-integrate-home-page.md', content: generateHomePagePlan(siteName) },\n { name: '03-integrate-remaining-pages.md', content: generateRemainingPagesPlan(siteName) },\n { name: '04-capture-snapshots.md', content: generateSnapshotsPlan(siteName) },\n { name: '05-verify-deployment.md', content: generateVerifyPlan(siteName) },\n ]\n\n // Create plans directory\n await fs.mkdir(plansDir, { recursive: true })\n\n // Write plan files\n const generatedFiles: string[] = []\n for (const plan of plans) {\n const filePath = path.join(plansDir, plan.name)\n\n // Check if file exists\n try {\n await fs.access(filePath)\n if (!force) {\n console.log(chalk.yellow(`Skipped (exists): .plans/${plan.name}`))\n continue\n }\n } catch {\n // File doesn't exist, continue\n }\n\n await fs.writeFile(filePath, plan.content, 'utf8')\n generatedFiles.push(plan.name)\n console.log(`${chalk.green('✓')} .plans/${plan.name}`)\n }\n\n console.log()\n if (generatedFiles.length > 0) {\n console.log(chalk.green(`Generated ${generatedFiles.length} plan files.`))\n } else {\n console.log(chalk.yellow('No new files generated. Use --force to overwrite existing files.'))\n }\n}\n\n// =============================================================================\n// Plan Templates\n// =============================================================================\n\nfunction generateAuditPlan(siteName: string): string {\n return `# Plan 00: Audit ${siteName} Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand:\n- What pages exist and their routing structure\n- Current component organization\n- Existing content patterns\n- Build/deploy configuration\n\n## Steps\n\n### Step 1: List All Pages\n\nScan the project for page components:\n\n\\`\\`\\`\n# For VitePress\nls docs/**/*.md\n\n# For Vue/Vite\nls src/pages/**/*.vue\nls src/views/**/*.vue\n\\`\\`\\`\n\n### Step 2: Identify Content Patterns\n\nLook for:\n- Hardcoded text strings in templates\n- Existing i18n or content management\n- Props-based content passing\n- API-driven content\n\n### Step 3: Document Current Structure\n\nCreate a list of:\n1. All page routes\n2. Reusable components with text content\n3. Layout components (header, footer)\n4. Any existing SEO implementation\n\n### Step 4: Update pages.yaml\n\nUpdate \\`.dcs/pages.yaml\\` with all discovered pages:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # Add discovered pages here...\n\\`\\`\\`\n\n## Output\n\n- Updated \\`.dcs/pages.yaml\\` with all pages\n- List of components requiring content integration\n- Notes on any existing content management to migrate\n`\n}\n\nfunction generateTextContentPlan(siteName: string, framework: string): string {\n return `# Plan 01: Create useTextContent Composable for ${siteName}\n\n## Objective\n\nSet up the text content composable for runtime/build-time content management.\n\n## Prerequisites\n\n- \\`@duffcloudservices/cms\\` package installed\n- Vite/VitePress project configured\n\n## Steps\n\n### Step 1: Install CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### Step 2: Configure Vite Plugin\n\n${framework === 'vue' ? `\nIn \\`vite.config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n})\n\\`\\`\\`\n` : `\nIn \\`.vitepress/config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vitepress'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n vite: {\n plugins: [\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n },\n})\n\\`\\`\\`\n`}\n\n### Step 3: Create Site Composable Wrapper (Optional)\n\nCreate a site-specific wrapper for consistent defaults:\n\n\\`\\`\\`typescript\n// src/composables/useContent.ts\nimport { useTextContent as baseuseTextContent } from '@duffcloudservices/cms'\n\nexport function useContent(pageSlug: string, defaults: Record<string, string> = {}) {\n return baseuseTextContent({ pageSlug, defaults })\n}\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` - should start without errors\n2. Import and use composable in a test component\n3. Verify content.yaml values are accessible via \\`t()\\` function\n`\n}\n\nfunction generateHomePagePlan(siteName: string): string {\n return `# Plan 02: Integrate Home Page for ${siteName}\n\n## Objective\n\nAdd DCS CMS integration to the home page, including:\n- Text content management\n- SEO configuration\n- Data attributes for visual editor\n\n## Steps\n\n### Step 1: Identify Home Page Component\n\nFind the main home page component:\n- VitePress: \\`docs/index.md\\` or \\`docs/.vitepress/theme/components/Home.vue\\`\n- Vue: \\`src/pages/index.vue\\` or \\`src/views/Home.vue\\`\n\n### Step 2: Add Text Content Integration\n\nReplace hardcoded text with \\`t()\\` function calls:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Welcome to ${siteName}',\n 'hero.subtitle': 'Your subtitle here',\n 'hero.cta': 'Get Started',\n // Add all text content...\n }\n})\n\n// Apply SEO\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### Step 3: Update content.yaml\n\nAdd all discovered text keys to \\`.dcs/content.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n hero.title: \"Welcome to ${siteName}\"\n hero.subtitle: \"Your subtitle here\"\n hero.cta: \"Get Started\"\n\\`\\`\\`\n\n### Step 4: Update seo.yaml\n\nConfigure SEO for the home page in \\`.dcs/seo.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n title: Home\n description: \"Description for ${siteName}\"\n noTitleTemplate: true\n openGraph:\n type: website\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` and verify home page renders correctly\n2. Check browser console for any errors\n3. Verify SEO meta tags in page source\n4. Test that data-dcs-* attributes are present\n`\n}\n\nfunction generateRemainingPagesPlan(siteName: string): string {\n return `# Plan 03: Integrate Remaining Pages for ${siteName}\n\n## Objective\n\nApply the same integration pattern to all remaining pages identified in the audit.\n\n## Steps\n\n### For Each Page\n\n1. **Open the page component**\n\n2. **Add imports and composable setup**:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'PAGE_SLUG', // Match pages.yaml slug\n defaults: {\n // Add all text content with sensible defaults\n }\n})\n\nconst { applyHead } = useSEO('PAGE_SLUG')\napplyHead()\n</script>\n\\`\\`\\`\n\n3. **Replace all hardcoded text** with \\`t()\\` calls\n\n4. **Add data-dcs-* attributes** to sections and text elements\n\n5. **Update content.yaml** with the page's text content\n\n6. **Update seo.yaml** with SEO configuration\n\n### Global Components\n\nDon't forget shared components like:\n- Header/Navigation\n- Footer\n- Sidebar\n\nThese should use \\`pageSlug: 'global'\\` for shared content.\n\n## Checklist\n\n- [ ] About page\n- [ ] Contact page\n- [ ] Services/Products pages\n- [ ] Blog index (if applicable)\n- [ ] Header component\n- [ ] Footer component\n- [ ] Any other pages...\n\n## Verification\n\nFor each page:\n1. Page renders correctly\n2. No console errors\n3. SEO meta tags present\n4. Data attributes present\n`\n}\n\nfunction generateSnapshotsPlan(siteName: string): string {\n return `# Plan 04: Configure Snapshot Capture for ${siteName}\n\n## Objective\n\nEnsure the visual page editor can capture accurate snapshots of all pages.\n\n## Prerequisites\n\n- All pages integrated with \\`useTextContent\\`\n- All pages have \\`data-dcs-section\\` attributes\n- All editable text has \\`data-dcs-text\\` attributes\n\n## Steps\n\n### Step 1: Verify pages.yaml\n\nEnsure all pages are listed in \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # All other pages...\n\nexcluded:\n - /404\n - /dev-*\n - /_*\n\\`\\`\\`\n\n### Step 2: Configure Snapshot Settings\n\nReview snapshot configuration:\n\n\\`\\`\\`yaml\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000 # Adjust if page has animations\n captureFullPage: true\n\\`\\`\\`\n\n### Step 3: Test Local Snapshots\n\nBuild and preview the site:\n\n\\`\\`\\`bash\npnpm build\npnpm preview\n\\`\\`\\`\n\nManually verify each page:\n1. Opens correctly\n2. All content is visible\n3. No loading spinners or placeholders\n\n### Step 4: Verify Data Attributes\n\nUse browser DevTools to check:\n- Each section has \\`data-dcs-section\\`\n- Each editable text element has \\`data-dcs-text\\`\n- Attribute values match content.yaml keys\n\n## Verification\n\n1. Site builds without errors\n2. All pages accessible at documented paths\n3. Data attributes present in rendered HTML\n4. No dynamic content that would prevent snapshot capture\n`\n}\n\nfunction generateVerifyPlan(siteName: string): string {\n return `# Plan 05: Verify Deployment for ${siteName}\n\n## Objective\n\nTest the complete integration before handing off to the DCS Portal.\n\n## Steps\n\n### Step 1: Run Validation\n\n\\`\\`\\`bash\nnpx @duffcloudservices/cli validate\n\\`\\`\\`\n\nFix any reported errors or warnings.\n\n### Step 2: Build Test\n\n\\`\\`\\`bash\npnpm build\n\\`\\`\\`\n\nEnsure no build errors related to:\n- Missing content keys\n- SEO configuration\n- Vite plugin issues\n\n### Step 3: Local Preview\n\n\\`\\`\\`bash\npnpm preview\n\\`\\`\\`\n\nVerify:\n- [ ] Home page loads correctly\n- [ ] All pages accessible\n- [ ] SEO meta tags correct\n- [ ] No console errors\n- [ ] Content displays correctly\n\n### Step 4: Deploy to Staging\n\nDeploy to Azure Static Web Apps staging environment:\n\n\\`\\`\\`bash\n# If using GitHub Actions, push to a PR branch\ngit push origin feature/dcs-integration\n\\`\\`\\`\n\n### Step 5: Portal Registration\n\n1. Log in to DCS Portal\n2. Navigate to Sites > Add Site\n3. Enter site slug and GitHub repo\n4. Trigger initial snapshot capture\n\n### Step 6: Visual Editor Test\n\n1. Open the site in DCS Portal\n2. Navigate to Pages\n3. Select a page\n4. Verify visual editor loads correctly\n5. Test editing a text field\n6. Save and verify changes deploy\n\n## Success Criteria\n\n- [ ] All validation passes\n- [ ] Site builds and deploys\n- [ ] Portal can capture snapshots\n- [ ] Visual editor works\n- [ ] Content changes deploy correctly\n\n## Handoff\n\nOnce verified:\n1. Merge the integration PR\n2. Notify the site owner\n3. Provide portal access credentials\n`\n}\n","/**\n * Capture visual snapshots of customer site pages for the portal's visual page editor.\n *\n * This command:\n * 1. Reads page configuration from .dcs/pages.yaml\n * 2. Launches a headless browser via Playwright\n * 3. Captures full-page screenshots and section bounds\n * 4. Detects text keys from data-text-key attributes\n * 5. Outputs JSON manifests and PNG screenshots\n *\n * The output is consumed by the DCS portal to render the visual page editor.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport yaml from 'js-yaml'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Browser, ElementHandle, Page } from 'playwright'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface PagesConfig {\n siteSlug: string\n version: number | string\n pages: PageConfig[]\n snapshot: {\n viewport: { width: number; height: number }\n waitAfterLoad: number\n captureFullPage: boolean\n outputDir?: string\n }\n}\n\ninterface PageConfig {\n slug: string\n path: string\n type: string\n title: string\n deletable: boolean\n textKeys?: string[]\n /** For dynamic pages: template pattern like /topics/:topic */\n pathTemplate?: string\n /** For dynamic pages: list of actual slugs to capture */\n instances?: string[]\n}\n\ninterface ContentConfig {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n}\n\ninterface TextKeyInfo {\n key: string\n currentValue: string\n elementType: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n domPath?: string\n}\n\ninterface SectionInfo {\n sectionId: string\n sectionType: 'header' | 'footer' | 'hero' | 'content' | 'gallery' | 'cta' | 'custom'\n displayName: string\n domPath: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n textKeys: TextKeyInfo[]\n isEditable: boolean\n isStructuralOnly: boolean\n}\n\ninterface PageSnapshot {\n pageSlug: string\n path: string\n type: string\n deletable: boolean\n title: string\n capturedAt: string\n previewUrl: string\n viewport: { width: number; height: number }\n pageHeight: number\n sections: SectionInfo[]\n}\n\ninterface SnapshotManifest {\n siteSlug: string\n capturedAt: string\n capturedVersion?: string\n deploymentRef?: string\n pagesConfigVersion: number | string\n pages: Array<{\n pageSlug: string\n pagePath: string\n pageType: string\n deletable: boolean\n title: string\n capturedAt: string\n sectionCount: number\n hasSnapshot: boolean\n }>\n}\n\ninterface TargetedSnapshotsConfig {\n mode: 'skip' | 'targeted' | 'full'\n pages: string[]\n reason: string\n triggeredBy?: string\n}\n\ninterface CaptureSnapshotsOptions {\n target: string\n baseUrl: string\n dryRun?: boolean\n verbose?: boolean\n pages?: string[]\n}\n\nconst CARD_THUMBNAIL_ASPECT_RATIO = 16 / 9\nconst ASSET_WAIT_TIMEOUT_MS = 5000\n\nasync function disableCaptureMotion(page: Page): Promise<void> {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -1ms !important;\n animation-duration: 1ms !important;\n animation-iteration-count: 1 !important;\n scroll-behavior: auto !important;\n transition-delay: 0s !important;\n transition-duration: 0s !important;\n }\n `,\n }).catch(() => {\n // Some pages can navigate before style injection completes; capture can continue without this stabilization.\n })\n}\n\nasync function waitForAboveFoldAssets(page: Page): Promise<void> {\n await page.evaluate(async (timeoutMs) => {\n const waitForFonts = typeof document.fonts?.ready?.then === 'function'\n ? document.fonts.ready.catch(() => undefined)\n : Promise.resolve()\n\n const visibleImages = Array.from(document.images).filter((img) => {\n const rect = img.getBoundingClientRect()\n return rect.bottom >= -100 && rect.top <= window.innerHeight * 1.5\n })\n\n const waitForImages = Promise.all(visibleImages.map((img) => {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve) => {\n const done = () => resolve()\n img.addEventListener('load', done, { once: true })\n img.addEventListener('error', done, { once: true })\n })\n }))\n\n await Promise.race([\n Promise.all([waitForFonts, waitForImages]),\n new Promise((resolve) => window.setTimeout(resolve, timeoutMs)),\n ])\n }, ASSET_WAIT_TIMEOUT_MS).catch((error) => {\n console.warn(` Warning: could not verify above-fold assets: ${error instanceof Error ? error.message : String(error)}`)\n })\n}\n\nasync function lazyLoadPage(page: Page, viewportHeight: number): Promise<number> {\n let pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n const scrollStep = Math.max(viewportHeight, 1)\n\n for (let scrollY = 0; scrollY < pageHeight; scrollY += scrollStep) {\n await page.evaluate((y) => window.scrollTo(0, y), scrollY)\n await page.waitForTimeout(100)\n }\n\n await page.waitForTimeout(200)\n pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n\n return pageHeight\n}\n\nasync function resetScrollForFullPageCapture(page: Page): Promise<void> {\n await page.evaluate(() => {\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.waitForFunction(() => Math.abs(window.scrollY) < 1, undefined, { timeout: 1000 }).catch(() => undefined)\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n await page.waitForTimeout(350)\n await waitForAboveFoldAssets(page)\n}\n\nasync function withFullPageCaptureLayout<T>(page: Page, capture: () => Promise<T>): Promise<T> {\n const styleHandle = await page.addStyleTag({\n content: `\n html[data-dcs-full-page-snapshot=\"true\"] header[data-section=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [data-section-type=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [role=\"banner\"] {\n position: static !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] [data-dcs-pin-full-page-nav=\"true\"] {\n position: absolute !important;\n top: 0 !important;\n right: 0 !important;\n bottom: auto !important;\n left: 0 !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] .VPNavBar {\n transform: none !important;\n }\n `,\n }).catch(() => null)\n\n await page.evaluate(() => {\n const candidates = Array.from(document.querySelectorAll(\n 'header[data-section=\"header\"] > nav, [data-section-type=\"header\"] > nav, [role=\"banner\"] > nav, .VPNav'\n ))\n candidates.forEach((element) => {\n const style = window.getComputedStyle(element)\n const className = element instanceof HTMLElement && typeof element.className === 'string' ? element.className : ''\n if (style.position === 'fixed' || style.position === 'sticky' || /\\b(fixed|sticky)\\b/.test(className) || element.classList.contains('VPNav')) {\n element.setAttribute('data-dcs-pin-full-page-nav', 'true')\n }\n })\n document.documentElement.setAttribute('data-dcs-full-page-snapshot', 'true')\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n\n try {\n return await capture()\n } finally {\n await styleHandle?.evaluate((el) => el.parentNode?.removeChild(el)).catch(() => undefined)\n await page.evaluate(() => {\n document.documentElement.removeAttribute('data-dcs-full-page-snapshot')\n document.querySelectorAll('[data-dcs-pin-full-page-nav]').forEach((element) => {\n element.removeAttribute('data-dcs-pin-full-page-nav')\n })\n }).catch(() => undefined)\n }\n}\n\nasync function captureHeaderThumbnail(\n page: Page,\n outputPath: string,\n viewport: { width: number; height: number }\n): Promise<void> {\n await page.setViewportSize(viewport)\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(200)\n await waitForAboveFoldAssets(page)\n\n const clip = await page.evaluate((aspectRatio) => {\n const width = Math.max(document.documentElement.clientWidth, window.innerWidth, 1)\n const pageHeight = Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0, 1)\n const height = Math.min(Math.round(width / aspectRatio), pageHeight)\n return { x: 0, y: 0, width, height }\n }, CARD_THUMBNAIL_ASPECT_RATIO)\n\n await page.screenshot({\n path: outputPath,\n clip,\n })\n}\n\n// =============================================================================\n// Section Detection Selectors\n// =============================================================================\n\nconst SECTION_SELECTORS = {\n explicit: '[data-section]',\n header: 'header, [role=\"banner\"], .header, #header',\n footer: 'footer, [role=\"contentinfo\"], .footer, #footer',\n hero: '[class*=\"hero\"], [data-section-type=\"hero\"], .hero-section, #hero',\n content: 'main, [role=\"main\"], article, .content, .main-content',\n gallery: '[class*=\"gallery\"], [data-section-type=\"gallery\"]',\n cta: '[class*=\"cta\"], [data-section-type=\"cta\"], .call-to-action',\n} as const\n\n// =============================================================================\n// Configuration Loaders\n// =============================================================================\n\nfunction loadPagesConfig(targetDir: string): PagesConfig {\n const configPath = path.join(targetDir, '.dcs', 'pages.yaml')\n if (!fs.existsSync(configPath)) {\n throw new Error(`Configuration file not found: ${configPath}`)\n }\n const content = fs.readFileSync(configPath, 'utf-8')\n return yaml.load(content) as PagesConfig\n}\n\nfunction loadContentConfig(targetDir: string): ContentConfig | null {\n const contentPath = path.join(targetDir, '.dcs', 'content.yaml')\n if (!fs.existsSync(contentPath)) {\n return null\n }\n const content = fs.readFileSync(contentPath, 'utf-8')\n return yaml.load(content) as ContentConfig\n}\n\nfunction loadTargetedSnapshotsConfig(targetDir: string): TargetedSnapshotsConfig | null {\n const targetedPath = path.join(targetDir, '.dcs', 'targeted-snapshots.yaml')\n if (!fs.existsSync(targetedPath)) {\n return null\n }\n const content = fs.readFileSync(targetedPath, 'utf-8')\n return yaml.load(content) as TargetedSnapshotsConfig\n}\n\nfunction getValidTextKeysForPage(\n contentConfig: ContentConfig | null,\n pageSlug: string\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (!contentConfig) return keys\n\n // Add global keys\n if (contentConfig.global) {\n for (const [key, value] of Object.entries(contentConfig.global)) {\n keys.set(key, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n // Add page-specific keys\n const pageContent = contentConfig.pages?.[pageSlug]\n if (pageContent) {\n for (const [key, value] of Object.entries(pageContent)) {\n const fullKey = `${pageSlug}.${key}`\n keys.set(fullKey, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n return keys\n}\n\n// =============================================================================\n// Page Resolution\n// =============================================================================\n\ninterface ResolvedPage {\n slug: string\n path: string\n config: PageConfig\n}\n\nasync function resolveAllPages(config: PagesConfig): Promise<ResolvedPage[]> {\n const resolved: ResolvedPage[] = []\n\n for (const page of config.pages) {\n if (page.type === 'dynamic' && page.instances) {\n // Expand dynamic pages into individual instances\n for (const instance of page.instances) {\n const instancePath = page.pathTemplate\n ? page.pathTemplate.replace(/:[\\w]+/, instance)\n : `${page.path}/${instance}`\n\n resolved.push({\n slug: `${page.slug}-${instance}`,\n path: instancePath,\n config: { ...page, slug: `${page.slug}-${instance}` },\n })\n }\n } else {\n resolved.push({\n slug: page.slug,\n path: page.path,\n config: page,\n })\n }\n }\n\n return resolved\n}\n\n// =============================================================================\n// DOM Helpers\n// =============================================================================\n\nasync function getDomPath(element: ElementHandle): Promise<string> {\n return element.evaluate((el: Element) => {\n const parts: string[] = []\n let current: Element | null = el\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase()\n\n if (current.id) {\n selector += `#${current.id}`\n } else {\n const classes = Array.from(current.classList)\n .filter((c) => !c.match(/^(v-|nuxt-|vue-|react-|ng-|_)/))\n .slice(0, 2)\n .join('.')\n if (classes) {\n selector += `.${classes}`\n }\n }\n\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n })\n}\n\nasync function generateSectionDisplayName(\n element: ElementHandle,\n sectionType: string,\n index: number\n): Promise<string> {\n // Try to find a heading within the section\n const headingText = await element.evaluate((el: Element) => {\n const heading = el.querySelector('h1, h2, h3, h4, [class*=\"title\"], [class*=\"heading\"]')\n if (heading && heading.textContent) {\n return heading.textContent.trim().slice(0, 50)\n }\n return null\n })\n\n if (headingText) return headingText\n\n // Fall back to section type + index\n const typeLabels: Record<string, string> = {\n header: 'Header',\n footer: 'Footer',\n hero: 'Hero Section',\n content: 'Content Section',\n gallery: 'Gallery',\n cta: 'Call to Action',\n }\n\n return typeLabels[sectionType] || `Section ${index + 1}`\n}\n\n// =============================================================================\n// Text Key Detection\n// =============================================================================\n\nasync function detectTextKeys(\n _page: Page,\n sectionElement: ElementHandle,\n _pageSlug: string,\n _validTextKeys: Map<string, string>\n): Promise<TextKeyInfo[]> {\n const textKeys: TextKeyInfo[] = []\n\n // Find editable text and managed image markers within this section.\n const textKeyElements = await sectionElement.$$('[data-text-key], [data-dcs-text], [data-dcs-image-key]')\n\n for (const element of textKeyElements) {\n const keyInfo = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n const explicitImageUrl = el.getAttribute('data-dcs-image-url')\n const imageUrl = el instanceof HTMLImageElement ? el.currentSrc || el.src : ''\n return {\n key: el.getAttribute('data-text-key') || el.getAttribute('data-dcs-text') || el.getAttribute('data-dcs-image-key') || '',\n elementType: el.tagName.toLowerCase(),\n currentValue: (explicitImageUrl || imageUrl || el.textContent?.trim() || '').slice(0, 500),\n bounds: {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n },\n }\n })\n\n if (keyInfo.key && !textKeys.some((textKey) => textKey.key === keyInfo.key)) {\n const domPath = await getDomPath(element)\n textKeys.push({\n key: keyInfo.key,\n currentValue: keyInfo.currentValue,\n elementType: keyInfo.elementType,\n domPath,\n bounds: {\n x: Math.round(keyInfo.bounds.x),\n y: Math.round(keyInfo.bounds.y),\n width: Math.round(keyInfo.bounds.width),\n height: Math.round(keyInfo.bounds.height),\n },\n })\n }\n }\n\n return textKeys\n}\n\n// =============================================================================\n// Fallback Section Detection\n// =============================================================================\n\nasync function detectFallbackContentSections(\n page: Page,\n existingSections: SectionInfo[]\n): Promise<ElementHandle[]> {\n // Find large content blocks that might be sections\n const candidates = await page.$$('section, article, div[class*=\"section\"], div[class*=\"container\"]')\n\n const results: ElementHandle[] = []\n const existingBounds = existingSections.map((s) => s.bounds)\n\n for (const candidate of candidates) {\n const bounds = await candidate.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n y: rect.top + window.scrollY,\n height: rect.height,\n }\n })\n\n // Skip if too small or overlaps with existing sections\n if (bounds.height < 100) continue\n\n const overlaps = existingBounds.some(\n (eb) =>\n bounds.y < eb.y + eb.height && bounds.y + bounds.height > eb.y\n )\n\n if (!overlaps) {\n results.push(candidate)\n }\n }\n\n return results\n}\n\n// =============================================================================\n// Page Capture\n// =============================================================================\n\nasync function capturePageSnapshot(\n browser: Browser,\n pageInfo: ResolvedPage,\n snapshotConfig: PagesConfig['snapshot'],\n siteSlug: string,\n validTextKeys: Map<string, string>,\n outputDir: string,\n verbose: boolean\n): Promise<PageSnapshot> {\n const context = await browser.newContext({\n viewport: snapshotConfig.viewport,\n ignoreHTTPSErrors: true,\n reducedMotion: 'reduce',\n })\n const page = await context.newPage()\n\n // Build page URL and append dcs-hide-ribbon to suppress preview ribbon in screenshots\n const rawUrl = pageInfo.path.startsWith('http')\n ? pageInfo.path\n : `${process.env.SITE_BASE_URL || 'http://localhost:5173'}${pageInfo.path}`\n const pageUrl = new URL(rawUrl)\n pageUrl.searchParams.set('dcs-hide-ribbon', '')\n const url = pageUrl.toString()\n\n if (verbose) {\n console.log(` Capturing: ${pageInfo.slug} (${url})`)\n }\n\n try {\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })\n await disableCaptureMotion(page)\n await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {\n if (verbose) console.log(` Note: networkidle timeout, continuing anyway`)\n })\n } catch (error) {\n console.error(` Failed to load: ${error}`)\n await context.close()\n throw error\n }\n\n await page.waitForTimeout(snapshotConfig.waitAfterLoad)\n await waitForAboveFoldAssets(page)\n\n // Create output directory for this page\n const pageOutputDir = path.join(outputDir, siteSlug, pageInfo.slug)\n fs.mkdirSync(path.join(pageOutputDir, 'sections'), { recursive: true })\n\n const title = await page.title()\n\n // Scroll through the page once so lazy-loaded content is present before the full-page capture.\n let pageHeight = await lazyLoadPage(page, snapshotConfig.viewport.height)\n await resetScrollForFullPageCapture(page)\n\n // Capture full page screenshot with fixed nav pinned to the document origin.\n const fullPagePath = path.join(pageOutputDir, 'full-page.png')\n await withFullPageCaptureLayout(page, () =>\n page.screenshot({\n path: fullPagePath,\n fullPage: snapshotConfig.captureFullPage,\n })\n )\n\n // Capture the header/hero-focused card image at the configured desktop viewport.\n const thumbnailPath = path.join(pageOutputDir, 'thumbnail.png')\n await captureHeaderThumbnail(page, thumbnailPath, snapshotConfig.viewport)\n\n // Ensure we're at scroll position 0 before capturing bounds.\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(100)\n\n // Detect sections\n const sections: SectionInfo[] = []\n const seenDomPaths = new Set<string>()\n\n // Helper to get accurate bounds for fixed/sticky elements\n // For fixed/sticky elements, we need to calculate their \"document position\"\n // based on their role (header = top, footer = bottom)\n const getAccurateBounds = async (el: ElementHandle, sectionType?: string) => {\n return el.evaluate((element: Element, type: string | undefined) => {\n const rect = element.getBoundingClientRect()\n const style = window.getComputedStyle(element)\n const position = style.position\n const isFixed = position === 'fixed' || position === 'sticky'\n \n let y = rect.top + window.scrollY\n \n // For fixed/sticky headers, they should be at y=0 (top of page)\n if (isFixed && (type === 'header' || element.tagName.toLowerCase() === 'header')) {\n y = 0\n }\n // For fixed/sticky footers, they should be at the bottom of the page\n else if (isFixed && (type === 'footer' || element.tagName.toLowerCase() === 'footer')) {\n y = document.documentElement.scrollHeight - rect.height\n }\n \n return {\n x: rect.left + window.scrollX,\n y,\n width: rect.width,\n height: rect.height,\n isFixed,\n }\n }, sectionType)\n }\n\n // Phase 1: Explicit [data-section] elements\n const explicitElements = await page.$$(SECTION_SELECTORS.explicit)\n\n for (let i = 0; i < explicitElements.length; i++) {\n const element = explicitElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n // First, get basic metadata\n const basicMetadata = await element.evaluate((el: Element) => {\n return {\n sectionId: el.getAttribute('data-section') || '',\n sectionLabel: el.getAttribute('data-section-label') || '',\n sectionType: el.getAttribute('data-section-type') || '',\n isDynamic: el.hasAttribute('data-dynamic'),\n }\n })\n\n // Determine section type early so we can pass it to bounds calculation\n let resolvedType: SectionInfo['sectionType'] = 'content'\n if (basicMetadata.sectionType) {\n resolvedType = basicMetadata.sectionType as SectionInfo['sectionType']\n } else if (['header', 'footer'].includes(basicMetadata.sectionId)) {\n resolvedType = basicMetadata.sectionId as SectionInfo['sectionType']\n } else if (basicMetadata.sectionId.includes('hero')) {\n resolvedType = 'hero'\n } else if (basicMetadata.sectionId.includes('gallery')) {\n resolvedType = 'gallery'\n }\n\n // Get accurate bounds with fixed/sticky handling\n const bounds = await getAccurateBounds(element, resolvedType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = basicMetadata.sectionId || `section-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(resolvedType)\n\n // Detect text keys\n let textKeys: TextKeyInfo[] = []\n if (!isStructuralOnly) {\n textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n\n // Generate display name - prefer data-section-label, then heading, then fallback\n let displayName = basicMetadata.sectionLabel\n if (!displayName) {\n displayName = await generateSectionDisplayName(element, resolvedType, i)\n }\n\n if (verbose) {\n console.log(\n ` Section: ${displayName} (${sectionId}) at y=${Math.round(bounds.y)}${\n basicMetadata.isDynamic ? ' [dynamic]' : ''\n } - ${textKeys.length} text keys`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: resolvedType,\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n\n // Phase 2: Type-based sections (header, footer, hero, etc.)\n for (const [sectionType, selector] of Object.entries(SECTION_SELECTORS)) {\n if (sectionType === 'explicit') continue\n\n const elements = await page.$$(selector)\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n\n // Skip elements that are INSIDE an explicit [data-section]\n // This prevents nested elements from being detected as separate sections\n const isInsideExplicitSection = await element.evaluate((el: Element) => {\n // Walk up the DOM tree to check if any ancestor has data-section\n let current: HTMLElement | null = el.parentElement\n while (current) {\n if (current.dataset?.section) {\n return true\n }\n current = current.parentElement\n }\n return false\n })\n\n if (isInsideExplicitSection) {\n continue // Silently skip - these are intentionally nested inside explicit sections\n }\n\n // Skip elements that are ancestors of already-detected explicit sections\n // This prevents the <main> container from being detected as a \"content\" section\n // when it contains explicit [data-section] children\n const isAncestorOfExplicitSection = await element.evaluate((el: Element) => {\n const explicitSections = el.querySelectorAll('[data-section]')\n return explicitSections.length > 0\n })\n\n if (isAncestorOfExplicitSection) {\n if (verbose) {\n console.log(` Skipping ${sectionType} container (contains explicit sections)`)\n }\n continue\n }\n\n seenDomPaths.add(domPath)\n\n // Use accurate bounds calculation for fixed/sticky elements\n const bounds = await getAccurateBounds(element, sectionType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `${sectionType}-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(sectionType)\n const textKeys = isStructuralOnly\n ? []\n : await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n\n // Skip content sections that have no text keys - they add noise without value\n if (sectionType === 'content' && textKeys.length === 0) {\n continue\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n const displayName = await generateSectionDisplayName(element, sectionType, i)\n\n if (verbose) {\n console.log(` Section: ${displayName} at y=${Math.round(bounds.y)}${bounds.isFixed ? ' [fixed]' : ''}`)\n }\n\n sections.push({\n sectionId,\n sectionType: sectionType as SectionInfo['sectionType'],\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n }\n\n // Fallback: If no content sections, try heuristic detection\n const hasContentSection = sections.some((s) => s.sectionType === 'content')\n if (!hasContentSection) {\n if (verbose) console.log(` No content sections found, trying fallback detection...`)\n\n const fallbackElements = await detectFallbackContentSections(page, sections)\n\n for (let i = 0; i < fallbackElements.length; i++) {\n const element = fallbackElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n const bounds = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n }\n })\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `content-fallback-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped fallback section screenshot: ${sectionId}`)\n }\n\n const textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n const displayName = await generateSectionDisplayName(element, 'content', i)\n\n if (verbose) {\n console.log(\n ` Fallback Section: ${displayName} at y=${Math.round(bounds.y)} (${textKeys.length} text keys)`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: 'content',\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable: textKeys.length > 0,\n isStructuralOnly: false,\n })\n }\n }\n\n await context.close()\n\n // Sort sections by vertical position\n sections.sort((a, b) => a.bounds.y - b.bounds.y)\n\n return {\n pageSlug: pageInfo.slug,\n path: pageInfo.path,\n type: pageInfo.config.type,\n deletable: pageInfo.config.deletable,\n title: title || pageInfo.config.title,\n capturedAt: new Date().toISOString(),\n previewUrl: url,\n viewport: snapshotConfig.viewport,\n pageHeight,\n sections,\n }\n}\n\n// =============================================================================\n// Main Command\n// =============================================================================\n\nexport async function captureSnapshotsCommand(options: CaptureSnapshotsOptions): Promise<void> {\n const { target, baseUrl, dryRun, verbose, pages: targetedPages } = options\n const targetDir = path.resolve(target)\n\n console.log(chalk.blue('📸 Page Snapshot Capture'))\n console.log(chalk.gray('========================'))\n console.log(chalk.gray(`Target: ${targetDir}`))\n console.log(chalk.gray(`Base URL: ${baseUrl}`))\n\n // Set base URL for capture\n process.env.SITE_BASE_URL = baseUrl\n\n // Load configuration\n let config: PagesConfig\n try {\n config = loadPagesConfig(targetDir)\n } catch (error) {\n console.error(chalk.red('Error:'), (error as Error).message)\n console.log(chalk.yellow('\\nMake sure .dcs/pages.yaml exists in the target directory.'))\n process.exit(1)\n }\n\n console.log(chalk.gray(`Site: ${config.siteSlug}`))\n\n const outputDir = path.join(targetDir, config.snapshot.outputDir || '.dcs/snapshots')\n\n // Load content configuration (optional)\n const contentConfig = loadContentConfig(targetDir)\n if (contentConfig) {\n const pageCount = Object.keys(contentConfig.pages || {}).length\n const globalCount = Object.keys(contentConfig.global || {}).length\n console.log(chalk.gray(`Content: Loaded ${pageCount} pages, ${globalCount} global keys`))\n }\n\n // Check for targeted snapshots config\n const targetedConfig = loadTargetedSnapshotsConfig(targetDir)\n const isTargetedCapture = Boolean(\n (targetedPages && targetedPages.length > 0) ||\n (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0)\n )\n\n if (targetedConfig?.mode === 'skip') {\n console.log('')\n console.log(chalk.yellow('⏭️ Skip mode activated!'))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n console.log(chalk.gray(' No snapshots will be captured.'))\n\n if (!dryRun) {\n fs.mkdirSync(outputDir, { recursive: true })\n const skipMarker = {\n skipped: true,\n reason: targetedConfig.reason,\n triggeredBy: targetedConfig.triggeredBy,\n timestamp: new Date().toISOString(),\n }\n fs.writeFileSync(path.join(outputDir, 'skipped.json'), JSON.stringify(skipMarker, null, 2))\n }\n\n console.log(chalk.green('\\n✅ Snapshot capture skipped (as requested)'))\n return\n }\n\n // Resolve pages\n let pagesToCapture = await resolveAllPages(config)\n console.log(chalk.gray(`Found ${pagesToCapture.length} total pages in configuration`))\n\n // Apply filtering from command-line --pages option\n if (targetedPages && targetedPages.length > 0) {\n const targetedSlugs = new Set(targetedPages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n }\n // Apply filtering from targeted-snapshots.yaml\n else if (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0) {\n const targetedSlugs = new Set(targetedConfig.pages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n }\n\n // List pages to capture\n console.log('')\n console.log('Pages to capture:')\n for (const p of pagesToCapture) {\n console.log(chalk.gray(` - ${p.slug} (${p.config.type}) ${p.path}`))\n }\n\n if (pagesToCapture.length === 0) {\n console.log(chalk.yellow('\\n⚠️ No pages to capture!'))\n return\n }\n\n if (dryRun) {\n console.log(chalk.yellow('\\n--dry-run: Stopping before browser launch'))\n return\n }\n\n // Dynamic import of playwright to avoid requiring it when not using this command\n const { chromium } = await import('playwright')\n\n // Clear and create output directory\n if (fs.existsSync(outputDir)) {\n fs.rmSync(outputDir, { recursive: true })\n }\n fs.mkdirSync(outputDir, { recursive: true })\n\n const spinner = ora('Launching browser...').start()\n const browser = await chromium.launch()\n spinner.succeed('Browser launched')\n\n const snapshots: PageSnapshot[] = []\n const failures: string[] = []\n\n for (const pageInfo of pagesToCapture) {\n const pageSpinner = ora(`Capturing ${pageInfo.slug}...`).start()\n\n try {\n const validTextKeys = getValidTextKeysForPage(contentConfig, pageInfo.slug)\n const snapshot = await capturePageSnapshot(\n browser,\n pageInfo,\n config.snapshot,\n config.siteSlug,\n validTextKeys,\n outputDir,\n verbose || false\n )\n snapshots.push(snapshot)\n\n // Save individual page snapshot JSON\n const snapshotPath = path.join(outputDir, config.siteSlug, pageInfo.slug, 'snapshot.json')\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2))\n\n pageSpinner.succeed(`${pageInfo.slug}: ${snapshot.sections.length} sections captured`)\n } catch (error) {\n const message = `${pageInfo.slug}: ${(error as Error).message}`\n failures.push(message)\n pageSpinner.fail(`${pageInfo.slug}: Failed - ${(error as Error).message}`)\n }\n }\n\n await browser.close()\n\n // Create manifest\n const manifest: SnapshotManifest = {\n siteSlug: config.siteSlug,\n capturedAt: new Date().toISOString(),\n capturedVersion: process.env.GITHUB_SHA,\n deploymentRef: process.env.GITHUB_REF || 'local',\n pagesConfigVersion: config.version,\n pages: snapshots.map((s) => ({\n pageSlug: s.pageSlug,\n pagePath: s.path,\n pageType: s.type,\n deletable: s.deletable,\n title: s.title,\n capturedAt: s.capturedAt,\n sectionCount: s.sections.length,\n hasSnapshot: true,\n })),\n }\n\n const manifestPath = path.join(outputDir, config.siteSlug, 'manifest.json')\n if (isTargetedCapture) {\n console.log(chalk.gray(' Targeted capture: leaving the existing remote manifest intact'))\n } else {\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n }\n\n console.log('')\n console.log(chalk.green('✅ Snapshot capture complete!'))\n console.log(chalk.gray(` Captured ${snapshots.length} pages`))\n console.log(chalk.gray(` - Static: ${snapshots.filter((s) => s.type === 'static').length}`))\n console.log(chalk.gray(` - Index: ${snapshots.filter((s) => s.type === 'index').length}`))\n console.log(chalk.gray(` - Dynamic: ${snapshots.filter((s) => s.type === 'dynamic').length}`))\n console.log(chalk.gray(` Output: ${outputDir}`))\n\n if (failures.length > 0) {\n throw new Error(`Snapshot capture failed for ${failures.length} page(s): ${failures.join('; ')}`)\n }\n}\n"],"mappings":"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","names":["chalk","chalk","data","chalk","chalk","DEFAULT_API_URL","path","chalk","chalk","ora","chalk","ora","fs","path","chalk","config","fs","path","chalk","yaml","fs","path","yaml","chalk","ora","config","chalk"]}
|