forgecraft 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/adapters/nextjs.ts","../src/core/adapters/react-vite.ts","../src/core/adapters/django.ts","../src/core/adapters/flutter.ts","../src/core/adapters/vue-nuxt.ts","../src/core/adapters/svelte.ts","../src/core/adapters/generic.ts","../src/core/adapters/plugin.ts","../src/core/adapters/index.ts","../src/core/utils/sound.ts","../src/core/orchestrator/index.ts","../src/core/orchestrator/prompts.ts","../src/core/worker/index.ts","../src/core/utils/a11y.ts","../src/core/worker/prompts/index.ts","../src/core/git/index.ts","../src/state/index.ts","../src/core/pipeline/index.ts","../src/core/utils/cost.ts","../src/core/visualizer/index.ts","../src/core/visualizer/scanner.ts","../src/core/visualizer/template.ts","../src/core/pipeline/auto.ts","../src/core/github/index.ts","../src/core/utils/attachments.ts","../src/core/templates/index.ts","../src/core/utils/cicd.ts","../src/core/utils/config.ts"],"sourcesContent":["import type { FrameworkAdapter } from \"./base.js\";\n\nexport const nextjsAdapter: FrameworkAdapter = {\n id: \"nextjs\",\n name: \"Next.js\",\n language: \"typescript\",\n scaffoldCommands: [\n \"npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias '@/*' --use-npm --no-turbopack\",\n ],\n buildCommand: \"npm run build\",\n lintCommand: \"npm run lint\",\n typecheckCommand: \"npx tsc --noEmit\",\n devCommand: \"npm run dev\",\n devPort: 3000,\n designSupport: true,\n packageManager: \"npm\",\n requiredFiles: [\"package.json\", \"tsconfig.json\", \"next.config.ts\", \"tailwind.config.ts\"],\n testCommand: \"npx vitest run\",\n testFramework: \"Vitest\",\n\n buildPromptAdditions: `\nFOR NEXT.JS:\n- Use App Router (not Pages Router)\n- Server Components by default — only use \"use client\" when genuinely needed\n- Server Actions for mutations\n- Proper metadata and SEO in layout.tsx\n- Dynamic imports for heavy components\n- Use next/image for all images with width, height, alt\n- Use next/link for all internal navigation\n- Use next/font for custom fonts (no Google Fonts CDN)\n\nASSETS & SEO (REQUIRED):\n- Create favicon.ico, icon.svg, and apple-touch-icon.png in public/\n- Create og.png (1200x630 placeholder) for Open Graph sharing\n- Add complete metadata in layout.tsx: title, description, Open Graph tags, Twitter cards\n- Create robots.txt, sitemap.xml, and manifest.json in public/\n- Ensure every page has a unique <title> and meta description\n`.trim(),\n\n designPromptAdditions: `\nNEXT.JS DESIGN:\n- Components use shadcn/ui + Tailwind CSS\n- Use CSF3 format for Storybook stories\n- Include viewport decorators for mobile (375px) and desktop (1440px)\n`.trim(),\n\n fileStructure: `\nsrc/\n├── app/\n│ ├── layout.tsx # Root layout with metadata\n│ ├── page.tsx # Home page\n│ ├── globals.css # Global styles + Tailwind\n│ └── [route]/\n│ └── page.tsx # Route pages\n├── components/\n│ ├── ui/ # shadcn/ui components\n│ └── [feature]/ # Feature-specific components\n├── lib/\n│ └── utils.ts # Shared utilities\n└── types/\n └── index.ts # TypeScript types\npublic/\n├── favicon.ico\n├── og.png\n├── robots.txt\n└── sitemap.xml\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nexport const reactViteAdapter: FrameworkAdapter = {\n id: \"react\",\n name: \"React + Vite\",\n language: \"typescript\",\n scaffoldCommands: [\n \"npm create vite@latest . -- --template react-ts\",\n \"npm install\",\n \"npm install -D tailwindcss @tailwindcss/vite\",\n \"npm install react-router-dom\",\n ],\n buildCommand: \"npm run build\",\n lintCommand: \"npm run lint\",\n typecheckCommand: \"npx tsc --noEmit\",\n devCommand: \"npm run dev\",\n devPort: 5173,\n designSupport: true,\n packageManager: \"npm\",\n requiredFiles: [\"package.json\", \"tsconfig.json\", \"vite.config.ts\", \"index.html\"],\n testCommand: \"npx vitest run\",\n testFramework: \"Vitest\",\n\n buildPromptAdditions: `\nFOR REACT + VITE:\n- Use React Router v6 with createBrowserRouter for routing\n- Functional components only — no class components\n- Use TypeScript strict mode throughout\n- Tailwind CSS for styling (configured via @tailwindcss/vite plugin)\n- Use React.lazy() and Suspense for code splitting\n- State management: React context + useReducer for simple state, or Zustand if complex\n- No SSR — this is a client-side SPA\n- Put entry point in src/main.tsx, root component in src/App.tsx\n- Environment variables: import.meta.env.VITE_* prefix\n\nVITE CONFIG:\n- Configure in vite.config.ts\n- Add @tailwindcss/vite plugin\n- Set resolve aliases: \"@/\" -> \"./src/\"\n\nASSETS & SEO:\n- favicon.ico and icon.svg in public/\n- og.png (1200x630) in public/\n- Update index.html with proper <title>, meta description, og tags\n- robots.txt in public/\n`.trim(),\n\n designPromptAdditions: `\nREACT + VITE DESIGN:\n- Components use Tailwind CSS utility classes\n- Use CSF3 format for Storybook stories\n- No Next.js-specific features (no next/image, next/link, etc.)\n- Use standard <img>, <a> tags or react-router <Link>\n`.trim(),\n\n fileStructure: `\nsrc/\n├── main.tsx # Entry point\n├── App.tsx # Root component with router\n├── index.css # Global styles + Tailwind\n├── components/\n│ ├── ui/ # Reusable UI components\n│ └── [feature]/ # Feature-specific components\n├── pages/\n│ └── [PageName].tsx # Route pages\n├── hooks/\n│ └── use[Hook].ts # Custom hooks\n├── lib/\n│ └── utils.ts # Shared utilities\n└── types/\n └── index.ts # TypeScript types\npublic/\n├── favicon.ico\n├── og.png\n└── robots.txt\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nconst isWin = process.platform === \"win32\";\nconst venvBin = isWin ? \"venv\\\\Scripts\" : \"venv/bin\";\nconst python = isWin ? \"python\" : \"python3\";\nconst venvPython = isWin ? `${venvBin}\\\\python` : `${venvBin}/python`;\nconst venvPip = isWin ? `${venvBin}\\\\pip` : `${venvBin}/pip`;\nconst venvDjangoAdmin = isWin ? `${venvBin}\\\\django-admin` : `${venvBin}/django-admin`;\n\nexport const djangoAdapter: FrameworkAdapter = {\n id: \"django\",\n name: \"Django\",\n language: \"python\",\n scaffoldCommands: [\n `${python} -m venv venv`,\n `${venvPip} install django djangorestframework django-cors-headers python-dotenv`,\n `${venvDjangoAdmin} startproject config .`,\n `${venvPython} manage.py startapp core`,\n ],\n buildCommand: `${venvPython} manage.py check --deploy`,\n lintCommand: `${venvPython} -m py_compile manage.py`,\n typecheckCommand: isWin ? \"echo Type checking skipped (Python)\" : \"echo 'Type checking skipped (Python)'\",\n devCommand: `${venvPython} manage.py runserver`,\n devPort: 8000,\n designSupport: false,\n packageManager: \"pip\",\n requiredFiles: [\"manage.py\", \"config/settings.py\", \"config/urls.py\", \"requirements.txt\"],\n testCommand: `${venvPython} manage.py test`,\n testFramework: \"Django TestCase\",\n\n buildPromptAdditions: `\nFOR DJANGO:\n- Use Django 5.x with Django REST Framework for APIs\n- Class-Based Views for CRUD, function-based views for custom logic\n- Use Django models with proper field types, validators, and Meta classes\n- Migrations: always run makemigrations + migrate after model changes\n- URL patterns in config/urls.py, app-level urls in each app's urls.py\n- Use Django templates (Jinja-style) for server-rendered pages\n- Static files in static/, templates in templates/\n- Settings: use python-dotenv for environment variables\n- Always create a superuser: python manage.py createsuperuser --noinput\n (set DJANGO_SUPERUSER_USERNAME, DJANGO_SUPERUSER_PASSWORD, DJANGO_SUPERUSER_EMAIL)\n- Register models in admin.py for Django Admin access\n- Use django-cors-headers for API CORS configuration\n\nPLATFORM NOTE:\n- On Windows, use venv\\\\Scripts\\\\python instead of venv/bin/python\n- On Unix/macOS, use venv/bin/python\n- Detect the platform and use the correct path\n\nSECURITY:\n- CSRF protection enabled by default — don't disable it\n- Use Django's built-in auth system (User model, login/logout views)\n- Set DEBUG=False in production settings\n- Configure ALLOWED_HOSTS properly\n\nAFTER WRITING CODE:\n1. Run: ${venvPython} manage.py makemigrations\n2. Run: ${venvPython} manage.py migrate\n3. Run: ${venvPython} manage.py check --deploy\n4. Fix any warnings or errors before proceeding\n\nALWAYS generate a requirements.txt with: ${venvPip} freeze > requirements.txt\n`.trim(),\n\n designPromptAdditions: `\nDJANGO DESIGN:\n- No Storybook — Django uses server-rendered templates\n- Design phase is skipped for Django projects\n- Focus on clean, functional UI with Django templates + CSS\n`.trim(),\n\n fileStructure: `\nconfig/\n├── __init__.py\n├── settings.py # Django settings\n├── urls.py # Root URL configuration\n├── wsgi.py # WSGI entry point\n└── asgi.py # ASGI entry point\ncore/\n├── __init__.py\n├── admin.py # Admin registrations\n├── apps.py # App config\n├── models.py # Database models\n├── views.py # Views (CBV/FBV)\n├── urls.py # App URL patterns\n├── serializers.py # DRF serializers\n├── forms.py # Django forms\n├── tests.py # Tests\n├── migrations/\n│ └── __init__.py\n└── templates/\n └── core/ # App templates\nstatic/\n├── css/\n├── js/\n└── images/\ntemplates/\n└── base.html # Base template\nmanage.py # Django CLI\nrequirements.txt # Python dependencies\n.env # Environment variables\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nconst isWin = process.platform === \"win32\";\n\nexport const flutterAdapter: FrameworkAdapter = {\n id: \"flutter\",\n name: \"Flutter\",\n language: \"dart\",\n scaffoldCommands: [\n \"flutter create --org com.forge --platforms web,android,ios .\",\n ],\n buildCommand: \"flutter build web\",\n lintCommand: \"dart analyze\",\n typecheckCommand: \"dart analyze\",\n devCommand: \"flutter run -d chrome\",\n devPort: 8080,\n designSupport: false,\n packageManager: \"pub\",\n requiredFiles: [\"pubspec.yaml\", \"lib/main.dart\"],\n testCommand: \"flutter test\",\n testFramework: \"Flutter Test\",\n\n buildPromptAdditions: `\nFOR FLUTTER:\n- Use Dart 3 with null safety throughout\n- Material Design 3 (Material You) as the default design system\n- Use StatelessWidget when possible, StatefulWidget only when needed\n- Prefer Riverpod or Provider for state management\n- Use go_router for navigation/routing\n- Follow the feature-first folder structure (not layer-first)\n- Small, focused widgets (< 100 lines per file)\n- Extract reusable widgets to lib/widgets/\n- Use const constructors wherever possible for performance\n\nDART BEST PRACTICES:\n- Use final for immutable variables, var only when mutation is needed\n- Named parameters for widget constructors: Widget({required this.title})\n- Use records and patterns (Dart 3): switch expressions, if-case, sealed classes\n- Proper async/await with error handling (try/catch, .catchError)\n- Use freezed or json_serializable for data models\n- Extension methods for utility functions\n- Typedef for complex function signatures\n\nUI PATTERNS:\n- Responsive layouts: LayoutBuilder, MediaQuery, or responsive_framework\n- Use Theme.of(context) for consistent theming\n- Custom color schemes with ColorScheme.fromSeed()\n- Adaptive design: different layouts for mobile/tablet/desktop\n- Hero animations for navigation transitions\n- Slivers for complex scrolling layouts (CustomScrollView, SliverAppBar)\n\nNETWORKING:\n- Use dio or http package for API calls\n- Repository pattern for data access\n- Model classes with fromJson/toJson\n\nASSETS:\n- Put images in assets/images/\n- Put fonts in assets/fonts/\n- Register assets in pubspec.yaml\n- Use flutter_svg for SVG support\n- App icon: flutter_launcher_icons package\n\nAFTER WRITING CODE:\n1. Run: dart analyze — fix all issues\n2. Run: flutter test — run all tests\n3. Run: flutter build web — verify build works\n\nPLATFORM NOTE:\n- Primary target is web, but code should be platform-agnostic\n- Use kIsWeb from dart:foundation for web-specific code\n- Avoid dart:html — use universal_html or conditional imports\n`.trim(),\n\n designPromptAdditions: `\nFLUTTER DESIGN:\n- No Storybook — Flutter uses widget previews\n- Design phase is skipped for Flutter projects\n- Focus on Material Design 3 with custom theming\n- Use WidgetBook package if interactive component previews are needed\n`.trim(),\n\n fileStructure: `\nlib/\n├── main.dart # App entry point\n├── app.dart # MaterialApp / root widget\n├── router.dart # go_router configuration\n├── theme/\n│ ├── app_theme.dart # ThemeData + ColorScheme\n│ └── text_styles.dart # Typography\n├── features/\n│ └── [feature]/\n│ ├── screens/ # Full-page widgets\n│ ├── widgets/ # Feature-specific widgets\n│ ├── models/ # Data models\n│ ├── providers/ # Riverpod providers\n│ └── repositories/ # Data access\n├── widgets/\n│ └── [shared_widget].dart # Reusable widgets\n├── models/\n│ └── [model].dart # Shared data models\n├── services/\n│ └── api_client.dart # HTTP client\n└── utils/\n └── extensions.dart # Extension methods\ntest/\n├── widget_test.dart # Widget tests\n└── [feature]_test.dart # Feature tests\nassets/\n├── images/\n└── fonts/\npubspec.yaml # Dependencies\nanalysis_options.yaml # Linter rules\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nexport const vueNuxtAdapter: FrameworkAdapter = {\n id: \"vue\",\n name: \"Vue 3 + Nuxt 3\",\n language: \"typescript\",\n scaffoldCommands: [\n \"npx nuxi@latest init . --force --packageManager npm\",\n \"npm install\",\n \"npm install -D @nuxtjs/tailwindcss\",\n ],\n buildCommand: \"npm run build\",\n lintCommand: \"npm run lint\",\n typecheckCommand: \"npx nuxi typecheck\",\n devCommand: \"npm run dev\",\n devPort: 3000,\n designSupport: true,\n packageManager: \"npm\",\n requiredFiles: [\"package.json\", \"nuxt.config.ts\", \"app.vue\"],\n testCommand: \"npx vitest run\",\n testFramework: \"Vitest\",\n\n buildPromptAdditions: `\nFOR VUE 3 + NUXT 3:\n- Use Composition API with <script setup lang=\"ts\"> — NO Options API\n- Use Nuxt 3 auto-imports (ref, computed, watch, useState, useFetch are available globally)\n- File-based routing in pages/ directory\n- Server API routes in server/api/ directory\n- Layouts in layouts/ directory, components in components/ (auto-imported)\n- Use useFetch() or useAsyncData() for data fetching (SSR-compatible)\n- Use useState() for shared reactive state across components\n- Use definePageMeta() for page-level config (layout, middleware)\n- Use Nuxt modules in nuxt.config.ts (e.g., @nuxtjs/tailwindcss)\n- Use VueUse composables where appropriate (npm install @vueuse/nuxt)\n\nNUXT-SPECIFIC:\n- Server routes: export default defineEventHandler() in server/api/\n- Middleware: defineNuxtRouteMiddleware() in middleware/\n- Plugins: defineNuxtPlugin() in plugins/\n- Use runtimeConfig in nuxt.config.ts for environment variables\n- Use <NuxtLink> for navigation, <NuxtPage> for route rendering\n- Use <NuxtLayout> for layout system\n\nSTYLING:\n- Tailwind CSS via @nuxtjs/tailwindcss module\n- Scoped styles with <style scoped> when needed\n- CSS variables for theming\n\nASSETS & SEO:\n- Use useHead() or useSeoMeta() for page-level meta tags\n- Put static assets in public/\n- favicon.ico, og.png in public/\n- Use Nuxt Image module for optimized images if needed\n`.trim(),\n\n designPromptAdditions: `\nVUE + NUXT DESIGN:\n- Components use Composition API with <script setup>\n- Tailwind CSS for styling\n- Use CSF3 format for Storybook stories\n- Include viewport decorators for mobile (375px) and desktop (1440px)\n- Use Vue-specific Storybook renderer (@storybook/vue3)\n`.trim(),\n\n fileStructure: `\napp.vue # Root app component\nnuxt.config.ts # Nuxt configuration\npages/\n├── index.vue # Home page (/)\n├── about.vue # /about\n└── [slug].vue # Dynamic routes\ncomponents/\n├── ui/ # Reusable UI components\n└── [feature]/ # Feature-specific components\ncomposables/\n└── use[Name].ts # Custom composables\nlayouts/\n└── default.vue # Default layout\nserver/\n├── api/\n│ └── [endpoint].ts # API routes\n└── middleware/\n └── [name].ts # Server middleware\nmiddleware/\n└── auth.ts # Route middleware\nplugins/\n└── [name].ts # Nuxt plugins\npublic/\n├── favicon.ico\n├── og.png\n└── robots.txt\nassets/\n└── css/\n └── main.css # Global styles\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nexport const svelteAdapter: FrameworkAdapter = {\n id: \"svelte\",\n name: \"SvelteKit\",\n language: \"typescript\",\n scaffoldCommands: [\n \"npx sv create . --template minimal --types ts --no-add-ons --no-install\",\n \"npm install\",\n \"npm install -D tailwindcss @tailwindcss/vite\",\n ],\n buildCommand: \"npm run build\",\n lintCommand: \"npm run check\",\n typecheckCommand: \"npx svelte-check --tsconfig ./tsconfig.json\",\n devCommand: \"npm run dev\",\n devPort: 5173,\n designSupport: true,\n packageManager: \"npm\",\n requiredFiles: [\"package.json\", \"svelte.config.js\", \"vite.config.ts\"],\n testCommand: \"npx vitest run\",\n testFramework: \"Vitest\",\n\n buildPromptAdditions: `\nFOR SVELTEKIT:\n- Use Svelte 5 runes syntax: $state(), $derived(), $effect(), $props()\n- Do NOT use legacy Svelte 4 syntax (no export let, no $: reactive, no on:click)\n- File-based routing in src/routes/ directory\n- +page.svelte for pages, +layout.svelte for layouts\n- +page.server.ts for server-side load functions\n- +server.ts for API endpoints\n- Use form actions for mutations (+page.server.ts actions)\n- Components in src/lib/components/\n- Shared utilities in src/lib/\n- Use $lib/ alias for imports from src/lib/\n\nSVELTEKIT-SPECIFIC:\n- Load functions: export const load = async ({ fetch, params }) => { ... }\n- Form actions: export const actions = { default: async ({ request }) => { ... } }\n- Hooks: src/hooks.server.ts for server hooks, src/hooks.client.ts for client\n- Error pages: +error.svelte\n- Use goto() for programmatic navigation\n- Use enhance for progressive form enhancement: <form use:enhance>\n- Environment variables: $env/static/private, $env/static/public\n\nSVELTE 5 RUNES (REQUIRED):\n- State: let count = $state(0)\n- Derived: let doubled = $derived(count * 2)\n- Effects: $effect(() => { console.log(count) })\n- Props: let { name, age = 25 } = $props()\n- Bindable: let { value = $bindable() } = $props()\n- Event handlers: onclick={handler} (NOT on:click)\n- Snippets: {#snippet name()}...{/snippet} and {@render name()}\n\nSTYLING:\n- Tailwind CSS via @tailwindcss/vite plugin in vite.config.ts\n- Scoped styles by default in <style> blocks\n- Global styles in src/app.css\n\nASSETS & SEO:\n- Static assets in static/ directory\n- favicon.ico, og.png in static/\n- Use svelte:head for per-page meta tags\n- robots.txt in static/\n`.trim(),\n\n designPromptAdditions: `\nSVELTEKIT DESIGN:\n- Use Svelte 5 runes syntax in all components\n- Tailwind CSS for styling\n- Use CSF3 format for Storybook stories\n- Use @storybook/svelte renderer\n- Include viewport decorators for mobile (375px) and desktop (1440px)\n`.trim(),\n\n fileStructure: `\nsrc/\n├── app.html # HTML template\n├── app.css # Global styles + Tailwind\n├── routes/\n│ ├── +page.svelte # Home page\n│ ├── +layout.svelte # Root layout\n│ ├── +error.svelte # Error page\n│ └── [route]/\n│ ├── +page.svelte # Route page\n│ └── +page.server.ts # Server load function\n├── lib/\n│ ├── components/\n│ │ ├── ui/ # Reusable UI components\n│ │ └── [feature]/ # Feature-specific components\n│ ├── server/\n│ │ └── db.ts # Server-only utilities\n│ └── utils.ts # Shared utilities\n└── hooks.server.ts # Server hooks\nstatic/\n├── favicon.ico\n├── og.png\n└── robots.txt\nsvelte.config.js # Svelte configuration\nvite.config.ts # Vite configuration\n`.trim(),\n};\n","import type { FrameworkAdapter } from \"./base.js\";\n\nexport const genericAdapter: FrameworkAdapter = {\n id: \"generic\",\n name: \"Custom Stack\",\n language: \"typescript\", // default, but prompts tell the agent to auto-detect\n scaffoldCommands: [],\n buildCommand: \"AUTO_DETECT\",\n lintCommand: \"AUTO_DETECT\",\n typecheckCommand: \"AUTO_DETECT\",\n devCommand: \"npm run dev\",\n devPort: 3000,\n designSupport: false,\n packageManager: \"npm\",\n requiredFiles: [],\n\n buildPromptAdditions: `\nCUSTOM TECH STACK — IMPORTANT:\nThis project uses a tech stack chosen by the user. You MUST auto-detect everything.\n\nSTEP 1 — DETECT THE STACK:\nBefore writing ANY code, read these files (whichever exist):\n- package.json (Node.js/JS/TS projects)\n- requirements.txt / pyproject.toml / Pipfile (Python projects)\n- go.mod (Go projects)\n- Cargo.toml (Rust projects)\n- pom.xml / build.gradle (Java/Kotlin projects)\n- pubspec.yaml (Flutter/Dart projects)\n- Gemfile (Ruby projects)\n- composer.json (PHP projects)\n- Any existing source files to understand patterns\n\nSTEP 2 — FOLLOW EXISTING CONVENTIONS:\n- If the project already has code, match its style exactly (naming, file structure, patterns)\n- If starting from scratch, use the standard project structure for the detected tech stack\n- Use the package manager the project already uses (npm, yarn, pnpm, pip, poetry, cargo, go, etc.)\n\nSTEP 3 — BUILD VERIFICATION:\n- Detect the correct build/lint/test commands from the project config\n- For Node.js: check package.json \"scripts\" for build, lint, typecheck commands\n- For Python: use pytest, flake8/ruff, mypy if configured\n- For Go: use go build, go vet, go test\n- For Rust: use cargo build, cargo clippy, cargo test\n- If no build script exists, skip the build step — do NOT fail\n- If a command does not exist or is not configured, skip it gracefully\n\nSTEP 4 — SCAFFOLDING:\n- If the project directory is empty, scaffold the project using the standard tooling for the stack\n (e.g., npm init, cargo init, go mod init, django-admin startproject, etc.)\n- Install dependencies after scaffolding\n`.trim(),\n\n designPromptAdditions: \"\",\n\n fileStructure: `\n(auto-detected — read the project's existing files to determine structure)\n`.trim(),\n};\n","// ============================================================\n// Custom Adapter Plugin Loader\n// Loads user-defined adapters from .forge/adapters/*.js\n// ============================================================\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport type { FrameworkAdapter } from \"./base.js\";\n\nconst ADAPTERS_DIR = \".forge/adapters\";\n\n/**\n * Load custom adapters from .forge/adapters/ directory.\n * Each .js file should default-export a FrameworkAdapter object.\n *\n * Example .forge/adapters/rails.js:\n * export default {\n * id: \"rails\",\n * name: \"Ruby on Rails\",\n * language: \"javascript\", // closest available\n * scaffoldCommands: [\"rails new . --api\"],\n * buildCommand: \"bundle exec rails assets:precompile\",\n * lintCommand: \"bundle exec rubocop\",\n * typecheckCommand: \"bundle exec srb tc\",\n * devCommand: \"bin/rails server\",\n * devPort: 3000,\n * designSupport: false,\n * packageManager: \"npm\",\n * requiredFiles: [\"Gemfile\", \"config/routes.rb\"],\n * buildPromptAdditions: \"FOR RAILS: Use Rails 7+ conventions...\",\n * designPromptAdditions: \"\",\n * fileStructure: \"app/\\\\n├── controllers/\\\\n├── models/\\\\n...\",\n * };\n */\nexport async function loadCustomAdapters(\n workingDir?: string\n): Promise<Record<string, FrameworkAdapter>> {\n const result: Record<string, FrameworkAdapter> = {};\n const dir = path.join(workingDir || process.cwd(), ADAPTERS_DIR);\n\n try {\n await fs.access(dir);\n } catch {\n return result; // No custom adapters directory — that's fine\n }\n\n let files: string[];\n try {\n files = await fs.readdir(dir);\n } catch {\n return result;\n }\n\n const jsFiles = files.filter(\n (f) => f.endsWith(\".js\") || f.endsWith(\".mjs\")\n );\n\n for (const file of jsFiles) {\n try {\n const filePath = path.join(dir, file);\n const fileUrl = pathToFileURL(filePath).href;\n const mod = await import(fileUrl);\n const adapter = mod.default ?? mod;\n\n if (!validateAdapter(adapter)) {\n console.warn(` Warning: ${file} is not a valid adapter (missing required fields). Skipping.`);\n continue;\n }\n\n result[adapter.id] = adapter;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(` Warning: Failed to load adapter ${file}: ${msg}`);\n }\n }\n\n return result;\n}\n\nfunction validateAdapter(a: any): a is FrameworkAdapter {\n if (typeof a !== \"object\" || a === null) return false;\n\n const requiredStrings = [\n \"id\", \"name\", \"language\",\n \"buildCommand\", \"lintCommand\", \"typecheckCommand\",\n \"devCommand\", \"buildPromptAdditions\", \"designPromptAdditions\", \"fileStructure\",\n ];\n for (const key of requiredStrings) {\n if (typeof a[key] !== \"string\") return false;\n }\n\n const requiredArrays = [\"scaffoldCommands\", \"requiredFiles\"];\n for (const key of requiredArrays) {\n if (!Array.isArray(a[key])) return false;\n }\n\n return true;\n}\n","// ============================================================\n// Adapter Registry — Factory for framework adapters\n// ============================================================\n\nimport type { FrameworkAdapter } from \"./base.js\";\nimport { nextjsAdapter } from \"./nextjs.js\";\nimport { reactViteAdapter } from \"./react-vite.js\";\nimport { djangoAdapter } from \"./django.js\";\nimport { flutterAdapter } from \"./flutter.js\";\nimport { vueNuxtAdapter } from \"./vue-nuxt.js\";\nimport { svelteAdapter } from \"./svelte.js\";\nimport { genericAdapter } from \"./generic.js\";\nimport { loadCustomAdapters } from \"./plugin.js\";\n\nconst builtinAdapters: Record<string, FrameworkAdapter> = {\n nextjs: nextjsAdapter,\n react: reactViteAdapter,\n django: djangoAdapter,\n flutter: flutterAdapter,\n vue: vueNuxtAdapter,\n svelte: svelteAdapter,\n generic: genericAdapter,\n};\n\n// Merge built-in + user-defined adapters (user adapters win on collision)\nlet adapters: Record<string, FrameworkAdapter> = { ...builtinAdapters };\n\n/** Load custom adapters from .forge/adapters/ and merge into registry */\nexport async function refreshAdapters(workingDir?: string): Promise<void> {\n const custom = await loadCustomAdapters(workingDir);\n adapters = { ...builtinAdapters, ...custom };\n}\n\n/** Get the adapter for a framework. Throws if unknown. */\nexport function getAdapter(framework: string): FrameworkAdapter {\n const adapter = adapters[framework];\n if (!adapter) {\n throw new Error(\n `Unknown framework \"${framework}\". Supported: ${Object.keys(adapters).join(\", \")}`\n );\n }\n return adapter;\n}\n\n/** List all supported frameworks */\nexport function listAdapters(): FrameworkAdapter[] {\n return Object.values(adapters);\n}\n\nexport type { FrameworkAdapter } from \"./base.js\";\n","import { exec } from \"child_process\";\nimport { platform } from \"os\";\n\n/**\n * Play a system notification sound (fire-and-forget).\n * Skips silently if not a TTY or if the sound command fails.\n */\nexport function playSound(): void {\n if (!process.stdout.isTTY) return;\n\n try {\n switch (platform()) {\n case \"darwin\":\n exec(\"afplay /System/Library/Sounds/Blow.aiff\", () => {});\n break;\n case \"win32\":\n exec(\n \"powershell -NoProfile -NonInteractive -Command \\\"[System.Media.SystemSounds]::Exclamation.Play()\\\"\",\n { shell: \"cmd.exe\" },\n () => {},\n );\n break;\n case \"linux\":\n exec(\n \"paplay /usr/share/sounds/freedesktop/stereo/complete.oga\",\n (err) => {\n // Fallback to terminal bell if paplay fails\n if (err) process.stdout.write(\"\\x07\");\n },\n );\n break;\n default:\n // Terminal bell as universal fallback\n process.stdout.write(\"\\x07\");\n }\n } catch {\n // Silently ignore — sound is non-critical\n }\n}\n","import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport chalk from \"chalk\";\nimport type {\n Plan,\n Story,\n SprintState,\n ForgeConfig,\n OrchestratorDecision,\n WorkerMode,\n} from \"../../types/plan.js\";\nimport { ORCHESTRATOR_SYSTEM_PROMPT } from \"./prompts.js\";\nimport { getAdapter } from \"../adapters/index.js\";\nimport { playSound } from \"../utils/sound.js\";\n\n// ============================================================\n// Orchestrator Agent\n// The project manager. Never writes code.\n// Plans, routes, reviews, and crafts prompts for the Worker.\n// Framework-aware via adapter system.\n// ============================================================\n\nexport class Orchestrator {\n private config: ForgeConfig;\n private plan: Plan | null = null;\n\n constructor(config: ForgeConfig) {\n this.config = config;\n }\n\n // ── Plan Generation ───────────────────────────────────────\n\n async generatePlan(description: string): Promise<Plan> {\n const adapter = getAdapter(this.config.framework);\n\n const isGeneric = adapter.id === \"generic\";\n const frameworkHint = isGeneric\n ? `Tech stack: Detect from the user's description below. The user may specify their own framework, language, and tools.`\n : `Framework: ${adapter.name} (${this.config.framework})\\n Language: ${adapter.language}`;\n\n // Detect if this is an existing project by scanning for source files\n const existingProjectHint = `\n IMPORTANT — EXISTING PROJECT DETECTION:\n Before planning, check if this is an existing project:\n 1. Read package.json, requirements.txt, pubspec.yaml, or Cargo.toml to understand current dependencies\n 2. List the src/ or app/ directory to understand what already exists\n 3. Read any README.md, CLAUDE.md, or docs/ for context\n\n If source files already exist:\n - DO NOT recreate files that already exist — build on top of them\n - Plan stories that ADD to or MODIFY the existing codebase\n - Reference existing patterns, component styles, and architecture in your story descriptions\n - Include \"Existing files to modify: [list]\" in story descriptions where relevant\n - If the user says \"add X\" or \"upgrade X\", treat it as an enhancement to the existing project\n\n If this is a brand new empty project:\n - Plan from scratch as usual\n `;\n\n const prompt = `\n The user wants to build the following:\n \"${description}\"\n\n ${frameworkHint}\n Design support: ${adapter.designSupport ? \"yes (Storybook)\" : \"no\"}\n\n ${existingProjectHint}\n\n IMPORTANT: If the user references any files (like .md, .txt, .pdf, or any document),\n READ those files first to understand the full requirements before planning.\n Also read any existing project files (package.json, requirements.txt, etc.) to understand the current state.\n\n Break this down into epics and stories. Each story should be:\n - Small enough to build in one agent session\n - Independently testable\n - Ordered by dependency (foundation first)\n\n Story types:\n - \"ui\" = has a visual component ${adapter.designSupport ? \"(needs design phase)\" : \"(no design phase for this framework)\"}\n - \"backend\" = API/database only (skip design phase)\n - \"fullstack\" = both UI and backend\n\n Return ONLY valid JSON with this structure:\n {\n \"project\": \"project name\",\n \"framework\": \"${this.config.framework}\",\n \"description\": \"brief description\",\n \"epics\": [\n {\n \"id\": \"epic-id\",\n \"title\": \"Epic Title\",\n \"stories\": [\n {\n \"id\": \"story-id\",\n \"title\": \"Story Title\",\n \"description\": \"What to build\",\n \"type\": \"ui\" | \"backend\" | \"fullstack\",\n \"priority\": 1,\n \"dependencies\": [\"other-story-id\"]\n }\n ]\n }\n ]\n }\n\n CRITICAL: Return ONLY the raw JSON object. No explanation, no markdown, no text before or after.\n Your entire response must be a single valid JSON object starting with { and ending with }.\n `;\n\n const resultText = await this.runQuery(prompt, { maxTurns: 10 });\n\n let rawPlan: any;\n try {\n rawPlan = JSON.parse(this.cleanJson(resultText));\n } catch {\n // Show a preview of what was returned for debugging\n const preview = resultText.slice(0, 200).replace(/\\n/g, \" \");\n throw new Error(\n `Failed to parse plan — Claude returned text instead of JSON.\\n` +\n ` Preview: \"${preview}${resultText.length > 200 ? \"...\" : \"\"}\"\\n` +\n ` Try a more specific description, e.g.: forge auto \"build a todo app with React and Express\"`\n );\n }\n\n // Validate required fields\n if (!rawPlan.epics || !Array.isArray(rawPlan.epics)) {\n throw new Error(\"Invalid plan: missing epics array. Try regenerating.\");\n }\n\n // Hydrate with default status fields\n const plan: Plan = {\n project: rawPlan.project || \"Untitled\",\n framework: rawPlan.framework || this.config.framework,\n description: rawPlan.description || description,\n created: new Date().toISOString(),\n epics: rawPlan.epics.map((epic: any) => ({\n id: epic.id || `epic-${Date.now()}`,\n title: epic.title || \"Untitled Epic\",\n status: \"planned\" as const,\n stories: (epic.stories || []).map((story: any) => ({\n id: story.id || `story-${Date.now()}`,\n title: story.title || \"Untitled Story\",\n description: story.description || \"\",\n type: story.type || \"fullstack\",\n status: \"planned\" as const,\n branch: null,\n designApproved: false,\n tags: [],\n priority: story.priority || 1,\n dependencies: story.dependencies || [],\n })),\n })),\n };\n\n this.plan = plan;\n return plan;\n }\n\n // ── User Input Routing ────────────────────────────────────\n\n async routeUserInput(\n message: string,\n currentState: SprintState\n ): Promise<OrchestratorDecision> {\n const routingPrompt = `\n Current sprint state:\n - Phase: ${currentState.currentPhase}\n - Current story: ${currentState.currentStory || \"none\"}\n - Worker mode: ${currentState.workerMode || \"idle\"}\n - Framework: ${this.config.framework}\n\n The user said: \"${message}\"\n\n Classify this input and decide what to do.\n Return ONLY valid JSON with this structure:\n {\n \"action\": \"route-to-worker\" | \"add-story\" | \"reprioritize\" | \"answer\" | \"queue-change\",\n \"workerMode\": \"design\" | \"build\" | \"review\" | \"fix\" (if routing to worker),\n \"response\": \"your response to the user\" (if answering directly),\n \"prompt\": \"detailed prompt for the worker\" (if routing to worker),\n \"story\": { \"title\": \"...\", \"description\": \"...\", \"type\": \"...\" } (if adding a new story)\n }\n\n Routing rules:\n - Visual tweak (color, spacing, font) → route-to-worker, mode: fix\n - Layout/UX redesign → route-to-worker, mode: design\n - Bug fix → route-to-worker, mode: fix\n - New feature → add-story (include title, description, type)\n - Content change (text, labels) → route-to-worker, mode: fix\n - Question → answer directly\n - If worker is currently active → queue-change (apply after current task)\n `;\n\n const resultText = await this.runQuery(routingPrompt, { maxTurns: 1 });\n try {\n return JSON.parse(this.cleanJson(resultText));\n } catch {\n // Fallback: treat as a question if JSON parse fails\n return { action: \"answer\", response: resultText };\n }\n }\n\n // ── Prompt Crafting ───────────────────────────────────────\n\n craftWorkerPrompt(\n story: Story,\n mode: WorkerMode,\n context: {\n plan: Plan;\n designMeta?: any;\n existingFiles?: string[];\n }\n ): string {\n const adapter = getAdapter(this.config.framework);\n\n switch (mode) {\n case \"design\":\n return this.craftDesignPrompt(story, context, adapter);\n case \"build\":\n return this.craftBuildPrompt(story, context, adapter);\n case \"test\":\n return this.craftBuildPrompt(story, context, adapter); // Tests use build prompt as base\n case \"review\":\n return this.craftReviewPrompt(story, context, adapter);\n case \"fix\":\n return this.craftFixPrompt(story, context, adapter);\n }\n }\n\n private craftDesignPrompt(\n story: Story,\n context: { plan: Plan },\n adapter: any\n ): string {\n const refsDir = [\".forge\", \"designs\", \"references\"].join(\"/\");\n\n return `\n You are designing the UI for: \"${story.title}\"\n\n Description: ${story.description}\n App: ${context.plan.project} (${adapter.name})\n Full app context: ${context.plan.description}\n\n DESIGN REFERENCES:\n Check if ${refsDir}/ exists and contains reference images.\n If so, read them and match their visual style, color palette, typography, and layout.\n\n Create a Storybook story file that renders this component/page.\n The file should be at: stories/${story.id}.stories.tsx\n\n Requirements:\n - Must be responsive (mobile-first: 375px, then 768px, then 1440px)\n - Use Tailwind CSS for styling\n - Include realistic placeholder data (not lorem ipsum)\n - Show default, loading, empty, and error states as separate stories\n - Use a distinctive, professional design (not generic AI aesthetics)\n - Choose fonts and colors that match the app's purpose\n\n DO NOT write the actual app code. Only the Storybook preview.\n `;\n }\n\n private craftBuildPrompt(\n story: Story,\n context: { plan: Plan; designMeta?: any; existingFiles?: string[] },\n adapter: any\n ): string {\n const isGeneric = adapter.id === \"generic\";\n const designRef = context.designMeta\n ? `\\nApproved design: Follow the design in stories/${story.id}.stories.tsx exactly.`\n : \"\";\n\n const existingRef = context.existingFiles?.length\n ? `\\nExisting files to reference for patterns:\\n${context.existingFiles.map((f) => ` - ${f}`).join(\"\\n\")}`\n : \"\";\n\n const verifySteps = isGeneric\n ? `\n After writing code:\n 1. Detect the project's build/lint/typecheck commands from its config files (package.json scripts, Makefile, pyproject.toml, etc.)\n 2. Run whichever commands exist — skip any that aren't configured\n 3. Fix any errors before finishing\n `\n : `\n After writing code:\n 1. Run: ${adapter.buildCommand} (fix any errors)\n 2. Run: ${adapter.lintCommand} (fix any warnings)\n 3. Run: ${adapter.typecheckCommand} (fix any type errors)\n `;\n\n const structureRef = isGeneric\n ? `\n Project structure:\n Read the existing project files to understand the structure.\n If starting from scratch, use the standard conventions for this tech stack.\n `\n : `\n Expected project structure:\n ${adapter.fileStructure}\n `;\n\n return `\n Implement: \"${story.title}\"\n\n Description: ${story.description}\n App: ${context.plan.project} (${adapter.name})\n ${designRef}\n ${existingRef}\n\n ${structureRef}\n\n CRITICAL — EXISTING CODE RULES:\n - FIRST read the project's existing files (ls src/, ls app/, etc.) before creating anything\n - Do NOT overwrite or recreate files that already exist — modify them instead\n - Match existing code style, naming conventions, and architecture patterns\n - Import from and extend existing components, utilities, and types\n - If the project already has a layout/shell/nav, USE it — don't create a duplicate\n\n Technical requirements:\n - Follow existing code patterns in the project\n - Small, focused components/functions\n - Proper error handling\n - Responsive design (mobile-first)\n\n ${verifySteps}\n\n If any command fails, read the error, fix it, and re-run.\n Do NOT leave broken code.\n `;\n }\n\n private craftReviewPrompt(\n story: Story,\n context: { plan: Plan; designMeta?: any },\n adapter: any\n ): string {\n const isGeneric = adapter.id === \"generic\";\n const runCmds = isGeneric\n ? `Run the project's build/lint/test commands (detect from config files). Skip any that aren't configured.`\n : `Run:\\n - ${adapter.buildCommand}\\n - ${adapter.lintCommand}\\n - ${adapter.typecheckCommand}`;\n\n return `\n Review the code for: \"${story.title}\"\n\n Framework: ${adapter.name}\n\n Check:\n 1. Does the implementation match the story description?\n ${context.designMeta ? \"2. Does it match the approved design?\" : \"\"}\n 3. Code quality — no shortcuts, proper types\n 4. Responsive design — works on mobile and desktop\n 5. Error handling — graceful failures, loading states\n 6. Accessibility — semantic HTML, ARIA labels\n 7. No debug logs, no commented-out code, no TODOs\n\n ${runCmds}\n\n If you find issues:\n - Minor (formatting, missing type): fix them directly\n - Major (broken logic, missing feature): list them and do NOT fix\n\n Return a summary of what you found and what you fixed.\n `;\n }\n\n private craftFixPrompt(\n story: Story,\n context: { plan: Plan },\n adapter: any\n ): string {\n const isGeneric = adapter.id === \"generic\";\n const verifyCmds = isGeneric\n ? \"After fixing, run the project's build/test commands to verify (detect from config files).\"\n : `After fixing, run ${adapter.buildCommand} and ${adapter.typecheckCommand} to verify.`;\n\n return `\n Fix an issue in: \"${story.title}\"\n Framework: ${adapter.name}\n\n Make the smallest possible change. Do not refactor.\n ${verifyCmds}\n `;\n }\n\n // ── SDK Query Helper ──────────────────────────────────────\n\n private async runQuery(\n prompt: string,\n opts: { maxTurns?: number } = {}\n ): Promise<string> {\n const maxRetries = 3;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n return await this.executeQuery(prompt, opts);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n\n if (this.isAuthError(msg) && attempt < maxRetries) {\n playSound();\n console.log(chalk.yellow(\"\\n Authentication expired\"));\n console.log(chalk.dim(\" Run: claude login\"));\n console.log(chalk.dim(` Retrying in 30s (attempt ${attempt}/${maxRetries})...\\n`));\n await new Promise((r) => setTimeout(r, 30_000));\n continue;\n }\n\n throw error;\n }\n }\n\n throw new Error(\"Max retries exceeded\");\n }\n\n private async executeQuery(\n prompt: string,\n opts: { maxTurns?: number } = {}\n ): Promise<string> {\n let resultText = \"\";\n\n try {\n for await (const msg of query({\n prompt,\n options: {\n model: this.config.model,\n systemPrompt: ORCHESTRATOR_SYSTEM_PROMPT,\n maxTurns: opts.maxTurns,\n allowedTools: [\"Read\", \"Glob\", \"Grep\"],\n },\n })) {\n if (msg.type === \"result\") {\n // Check for errors first\n const hasError = \"is_error\" in msg && msg.is_error;\n const errors = \"errors\" in msg && Array.isArray(msg.errors) ? msg.errors : [];\n const subtype = \"subtype\" in msg ? String(msg.subtype) : \"\";\n\n if (hasError || errors.length > 0) {\n // Build a useful error message from whatever info we have\n const errorParts: string[] = [];\n if (subtype) errorParts.push(subtype);\n for (const e of errors) {\n const eStr = typeof e === \"string\" ? e : JSON.stringify(e);\n if (eStr && eStr.length > 0) errorParts.push(eStr);\n }\n const errorMsg = errorParts.length > 0\n ? errorParts.join(\" — \")\n : \"Unknown error from Claude. Check your auth with: claude login\";\n throw new Error(errorMsg);\n }\n\n if (\"result\" in msg && typeof msg.result === \"string\") {\n resultText = msg.result;\n }\n }\n\n if (msg.type === \"assistant\" && msg.message?.content) {\n const textBlocks = msg.message.content\n .filter((b: any) => b.type === \"text\")\n .map((b: any) => b.text);\n if (textBlocks.length > 0) {\n resultText = textBlocks.join(\"\\n\");\n }\n }\n }\n } catch (error) {\n // Re-throw our own errors\n if (error instanceof Error && !error.message.includes(\"EPIPE\") && !error.message.includes(\"ENOENT\")) {\n throw error;\n }\n // Wrap unexpected errors with more context\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to communicate with Claude. ${msg}\\n` +\n \" Possible fixes:\\n\" +\n \" 1. Run: claude login\\n\" +\n \" 2. Check your internet connection\\n\" +\n \" 3. Run: forge doctor\"\n );\n }\n\n if (!resultText) {\n throw new Error(\n \"No response from Claude.\\n\" +\n \" Possible fixes:\\n\" +\n \" 1. Run: claude login\\n\" +\n \" 2. Check your internet connection\\n\" +\n \" 3. Run: forge doctor\"\n );\n }\n\n return resultText;\n }\n\n private isAuthError(msg: string): boolean {\n const lower = msg.toLowerCase();\n return (\n lower.includes(\"401\") ||\n lower.includes(\"unauthorized\") ||\n lower.includes(\"auth\") ||\n lower.includes(\"token expired\") ||\n lower.includes(\"session expired\") ||\n lower.includes(\"not authenticated\")\n );\n }\n\n private cleanJson(text: string): string {\n // Normalize Windows line endings and strip markdown fences\n let cleaned = text\n .replace(/\\r\\n/g, \"\\n\")\n .replace(/\\r/g, \"\\n\")\n .replace(/```json\\s*/g, \"\")\n .replace(/```\\s*/g, \"\")\n .trim();\n\n // Try parsing as-is first\n try {\n JSON.parse(cleaned);\n return cleaned;\n } catch {\n // Not valid yet — try extracting JSON object\n }\n\n // Extract the first { ... } block (handles text before/after JSON)\n const firstBrace = cleaned.indexOf(\"{\");\n const lastBrace = cleaned.lastIndexOf(\"}\");\n if (firstBrace !== -1 && lastBrace > firstBrace) {\n const extracted = cleaned.slice(firstBrace, lastBrace + 1);\n try {\n JSON.parse(extracted);\n return extracted;\n } catch {\n // Still invalid — return extracted anyway, let caller handle the error\n return extracted;\n }\n }\n\n return cleaned;\n }\n}\n","// ============================================================\n// System Prompts for the Orchestrator Agent\n// ============================================================\n\nexport const ORCHESTRATOR_SYSTEM_PROMPT = `\nYou are the Orchestrator — a senior tech lead managing an AI development sprint for the \"Forge\" framework.\n\nYOUR ROLE:\nYou NEVER write code. You plan, delegate, and review. You are the brain of the operation.\n\nRESPONSIBILITIES:\n1. Break user requirements into epics and stories\n2. Craft detailed, specific prompts for the Worker agent\n3. Route user feedback to the correct Worker mode\n4. Review Worker output and decide next steps\n5. Maintain project context and sprint state\n\nWHEN CREATING STORIES:\n- Each story must be independently buildable\n- UI stories need design phase; backend-only stories skip it\n- Order stories by dependency (foundation/setup first, then data layer, then UI, then integration)\n- Each story should result in working, testable code\n- Story IDs should be kebab-case: \"auth-login\", \"dashboard-layout\"\n- First story should always be project setup/scaffolding\n\nWHEN CRAFTING WORKER PROMPTS:\n- Include EXACT file paths to create or modify\n- Reference approved designs when available\n- Specify the framework patterns to follow (App Router, Server Components, etc.)\n- Include acceptance criteria — what \"done\" looks like\n- Never be vague — the Worker should not have to guess anything\n- Include the full project context the Worker needs (but not more)\n\nWHEN ROUTING USER FEEDBACK:\n- Visual tweak (color, spacing, font, border) → Worker fix mode, direct code change\n- Layout/UX redesign (page structure, navigation, flow) → Worker design mode first, then build\n- Bug fix (something broken, error, crash) → Worker fix mode with debug focus\n- New feature (something that doesn't exist yet) → Create new story, full pipeline\n- Content change (text, labels, copy) → Worker fix mode, string replacement\n- Question about the project → Answer directly, don't route\n- If Worker is currently busy → Queue the change for after current task\n\nWHEN REVIEWING WORKER OUTPUT:\n- Does the output match what was requested?\n- Did the Worker run build/lint/typecheck?\n- Are there any obvious issues?\n- Minor issues: send back to Worker fix mode\n- Major issues: send back to Worker build mode with specific instructions\n\nOUTPUT FORMAT:\nAlways respond with valid JSON when asked for structured data.\nWhen answering questions, respond in plain text.\nNever include markdown code fences around JSON output.\n\nDESIGN QUALITY STANDARDS:\nWhen the Worker is in design mode, ensure designs reference real products:\n- Finance/Banking → Stripe, Mercury, Wise\n- Dashboard/Admin → Linear, Vercel, Raycast\n- E-commerce → Shopify Admin, Gumroad\n- Social/Community → Discord, Slack\n- Content/Blog → Medium, Ghost, Substack\n- SaaS/Productivity → Notion, Figma\n- Healthcare/Church/Nonprofit → warm, trustworthy, accessible\n\nNEVER accept generic AI-looking designs (purple gradients, Inter font, rounded white cards on gray).\n`.trim();\n\n// ============================================================\n// Routing Classification Prompt\n// Used when the Orchestrator needs to classify user input\n// ============================================================\n\nexport const ROUTING_CLASSIFICATION_PROMPT = `\nClassify the user's message into one of these categories:\n\n1. VISUAL_TWEAK — Small CSS/styling change\n Examples: \"make it blue\", \"bigger font\", \"add shadow\", \"more padding\"\n \n2. REDESIGN — Significant layout or UX change\n Examples: \"redesign the dashboard\", \"switch to sidebar nav\", \"make it look like Stripe\"\n \n3. BUG_FIX — Something is broken or not working\n Examples: \"button doesn't work\", \"page is blank\", \"getting an error\"\n \n4. NEW_FEATURE — Something that doesn't exist yet\n Examples: \"add dark mode\", \"add recurring donations\", \"add export to PDF\"\n \n5. CONTENT_CHANGE — Text, labels, or copy changes\n Examples: \"change 'Submit' to 'Send'\", \"update the footer text\"\n \n6. QUESTION — Asking for information\n Examples: \"what stack are we using?\", \"how many stories left?\", \"show me the schema\"\n \n7. PRIORITY_CHANGE — Wants to reorder or skip stories\n Examples: \"do reports next\", \"skip the settings page for now\"\n\nRespond with ONLY the category name and a brief routing instruction.\n`.trim();\n","import { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport chalk from \"chalk\";\nimport type { WorkerMode, ForgeConfig } from \"../../types/plan.js\";\nimport {\n getDesignPrompt,\n getBuildPrompt,\n getTestPrompt,\n getReviewPrompt,\n getFixPrompt,\n} from \"./prompts/index.js\";\nimport { playSound } from \"../utils/sound.js\";\n\n// ============================================================\n// Worker Agent\n// Single agent, multiple modes. Does the actual work.\n// Framework-aware prompts via adapter system.\n// ============================================================\n\nconst MODE_TOOLS: Record<WorkerMode, string[]> = {\n design: [\"Read\", \"Write\", \"Glob\", \"LS\"],\n build: [\"Read\", \"Write\", \"Edit\", \"Bash\", \"Glob\", \"LS\", \"Grep\"],\n test: [\"Read\", \"Write\", \"Edit\", \"Bash\", \"Glob\", \"LS\", \"Grep\"],\n review: [\"Read\", \"Bash\", \"Glob\", \"LS\", \"Grep\"],\n fix: [\"Read\", \"Write\", \"Edit\", \"Bash\", \"Glob\", \"LS\", \"Grep\"],\n};\n\nfunction getModePrompt(mode: WorkerMode, framework?: string): string {\n switch (mode) {\n case \"design\": return getDesignPrompt(framework);\n case \"build\": return getBuildPrompt(framework);\n case \"test\": return getTestPrompt(framework);\n case \"review\": return getReviewPrompt(framework);\n case \"fix\": return getFixPrompt(framework);\n }\n}\n\nconst MODE_MAX_TURNS: Record<WorkerMode, number> = {\n design: 30,\n build: 50,\n test: 30,\n review: 20,\n fix: 15,\n};\n\nexport interface WorkerUsage {\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n durationMs: number;\n}\n\nexport interface WorkerResult {\n success: boolean;\n filesCreated: string[];\n filesModified: string[];\n errors: string[];\n summary: string;\n usage: WorkerUsage;\n}\n\nexport interface WorkerProgressEvent {\n type: \"tool_use\" | \"tool_running\" | \"tool_done\" | \"thinking\" | \"status\";\n content: string;\n tool?: string;\n elapsed?: number;\n}\n\nexport interface WorkerProgressCallback {\n (event: WorkerProgressEvent): void;\n}\n\nexport interface WorkerSandboxOptions {\n sandbox?: boolean;\n yes?: boolean;\n workingDir?: string;\n allowedDomains?: string[];\n}\n\nconst MAX_AUTH_RETRIES = 3;\nconst AUTH_RETRY_DELAY_MS = 30_000; // 30s between retries\n\nexport class Worker {\n private config: ForgeConfig;\n private sandboxOpts: WorkerSandboxOptions;\n private mute: boolean;\n\n constructor(config: ForgeConfig, sandboxOpts: WorkerSandboxOptions = {}, mute = false) {\n this.config = config;\n this.sandboxOpts = sandboxOpts;\n this.mute = mute;\n }\n\n async run(\n mode: WorkerMode,\n prompt: string,\n options: {\n workingDir?: string;\n onProgress?: WorkerProgressCallback;\n } = {}\n ): Promise<WorkerResult> {\n // Retry loop for auth/token errors\n for (let attempt = 1; attempt <= MAX_AUTH_RETRIES; attempt++) {\n const result = await this.executeQuery(mode, prompt, options);\n\n // Check if it's a recoverable auth error\n if (!result.success && this.isAuthError(result.errors)) {\n if (attempt < MAX_AUTH_RETRIES) {\n this.notifyAuthError(attempt);\n await this.waitForReauth();\n // Clear auth errors before retrying — keep other errors\n result.errors = result.errors.filter((e) => !this.isAuthErrorMsg(e));\n continue;\n }\n // Final attempt failed — add help text\n result.errors.push(\n \"Authentication failed after retries. Run: claude login\"\n );\n }\n\n return result;\n }\n\n // Should never reach here, but TypeScript needs it\n return {\n success: false,\n filesCreated: [],\n filesModified: [],\n errors: [\"Max retries exceeded\"],\n summary: \"\",\n usage: { inputTokens: 0, outputTokens: 0, costUsd: 0, durationMs: 0 },\n };\n }\n\n private async executeQuery(\n mode: WorkerMode,\n prompt: string,\n options: {\n workingDir?: string;\n onProgress?: WorkerProgressCallback;\n } = {}\n ): Promise<WorkerResult> {\n let workingDir = options.workingDir || this.sandboxOpts.workingDir;\n if (!workingDir) {\n try {\n workingDir = process.cwd();\n } catch {\n // CWD was deleted (EPERM uv_cwd) — use the sandbox working dir or fail gracefully\n return {\n success: false,\n filesCreated: [],\n filesModified: [],\n errors: [\"Working directory no longer exists. Please cd to your project and retry.\"],\n summary: \"\",\n usage: { inputTokens: 0, outputTokens: 0, costUsd: 0, durationMs: 0 },\n };\n }\n }\n const { onProgress } = options;\n\n const result: WorkerResult = {\n success: false,\n filesCreated: [],\n filesModified: [],\n errors: [],\n summary: \"\",\n usage: { inputTokens: 0, outputTokens: 0, costUsd: 0, durationMs: 0 },\n };\n\n const sdkOptions: Record<string, any> = {\n model: this.config.model,\n systemPrompt: getModePrompt(mode, this.config.framework),\n allowedTools: MODE_TOOLS[mode],\n cwd: workingDir,\n maxTurns: MODE_MAX_TURNS[mode],\n };\n\n if (this.sandboxOpts.yes && !this.sandboxOpts.sandbox) {\n sdkOptions.permissionMode = \"bypassPermissions\";\n }\n\n if (this.sandboxOpts.sandbox) {\n sdkOptions.permissionMode = \"bypassPermissions\";\n sdkOptions.sandbox = {\n enabled: true,\n autoAllowBashIfSandboxed: true,\n filesystem: {\n allowWrite: [workingDir],\n },\n network: {\n allowedDomains: this.sandboxOpts.allowedDomains || [\n \"registry.npmjs.org\",\n \"api.anthropic.com\",\n ],\n },\n };\n }\n\n try {\n for await (const msg of query({ prompt, options: sdkOptions })) {\n\n // Assistant message — contains tool_use and text blocks\n if (msg.type === \"assistant\") {\n // Accumulate usage from individual messages as fallback\n if (msg.message?.usage) {\n result.usage.inputTokens += msg.message.usage.input_tokens || 0;\n result.usage.outputTokens += msg.message.usage.output_tokens || 0;\n }\n for (const block of msg.message?.content || []) {\n if (block.type === \"text\") {\n onProgress?.({ type: \"thinking\", content: block.text.slice(0, 120) });\n }\n if (block.type === \"tool_use\") {\n const detail = this.describeToolUse(block);\n onProgress?.({ type: \"tool_use\", content: detail, tool: block.name });\n\n if (block.name === \"Write\" && block.input?.file_path) {\n result.filesCreated.push(block.input.file_path);\n }\n if (block.name === \"Edit\" && block.input?.file_path) {\n result.filesModified.push(block.input.file_path);\n }\n }\n }\n }\n\n // Tool progress — fires while a tool is still running\n if (msg.type === \"tool_progress\") {\n onProgress?.({\n type: \"tool_running\",\n content: msg.tool_name,\n tool: msg.tool_name,\n elapsed: msg.elapsed_time_seconds,\n });\n }\n\n // Tool use summary — human-readable summary after tool completes\n if (msg.type === \"tool_use_summary\") {\n onProgress?.({ type: \"tool_done\", content: msg.summary });\n }\n\n // Result — final message with usage stats\n if (msg.type === \"result\") {\n if (\"result\" in msg && typeof msg.result === \"string\") {\n result.summary = msg.result;\n }\n // Check for errors — handle empty strings and objects\n const hasError = \"is_error\" in msg && msg.is_error;\n const errors = \"errors\" in msg && Array.isArray(msg.errors) ? msg.errors : [];\n const subtype = \"subtype\" in msg ? String(msg.subtype || \"\") : \"\";\n\n if (errors.length > 0) {\n for (const e of errors) {\n const eStr = typeof e === \"string\" ? e : JSON.stringify(e);\n if (eStr && eStr.length > 0) result.errors.push(eStr);\n }\n }\n if (hasError && result.errors.length === 0) {\n result.errors.push(\n subtype || \"Claude encountered an error. Run: claude login\"\n );\n }\n // Extract token usage — SDK result gives cumulative totals, so overwrite\n if (msg.usage) {\n result.usage.inputTokens = msg.usage.input_tokens || 0;\n result.usage.outputTokens = msg.usage.output_tokens || 0;\n }\n if (typeof msg.total_cost_usd === \"number\") {\n result.usage.costUsd = msg.total_cost_usd;\n }\n if (typeof msg.duration_ms === \"number\") {\n result.usage.durationMs = msg.duration_ms;\n }\n // Handle max turns cap gracefully\n if (\"is_error\" in msg && msg.subtype === \"error_max_turns\") {\n result.errors.push(`Hit ${MODE_MAX_TURNS[mode]}-turn safety cap — story may be incomplete`);\n }\n }\n }\n\n result.success = result.errors.length === 0;\n\n if (!result.summary) {\n result.summary = \"Completed without summary.\";\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n result.success = false;\n result.errors.push(msg);\n }\n\n return result;\n }\n\n // ── Auth Error Detection ──────────────────────────────────\n\n private isAuthErrorMsg(msg: string): boolean {\n const lower = msg.toLowerCase();\n return (\n lower.includes(\"401\") ||\n lower.includes(\"unauthorized\") ||\n lower.includes(\"auth\") ||\n lower.includes(\"token expired\") ||\n lower.includes(\"session expired\") ||\n lower.includes(\"not authenticated\") ||\n lower.includes(\"login required\") ||\n lower.includes(\"credential\")\n );\n }\n\n private isAuthError(errors: string[]): boolean {\n return errors.some((e) => this.isAuthErrorMsg(e));\n }\n\n private notifyAuthError(attempt: number): void {\n if (!this.mute) playSound();\n console.log(\"\");\n console.log(chalk.yellow(\" ⚠ Authentication expired\"));\n console.log(chalk.dim(\" Your Claude session token has expired.\"));\n console.log(chalk.dim(\" Please re-authenticate:\"));\n console.log(chalk.white(\" claude login\"));\n console.log(chalk.dim(`\\n Waiting to retry (attempt ${attempt}/${MAX_AUTH_RETRIES})...`));\n console.log(chalk.dim(\" Forge will resume automatically once auth is renewed.\\n\"));\n }\n\n private waitForReauth(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, AUTH_RETRY_DELAY_MS));\n }\n\n /** Turn a tool_use block into a short human-readable string */\n private describeToolUse(block: any): string {\n const name = block.name;\n const input = block.input || {};\n\n switch (name) {\n case \"Write\":\n return `Write ${this.shortPath(input.file_path)}`;\n case \"Edit\":\n return `Edit ${this.shortPath(input.file_path)}`;\n case \"Read\":\n return `Read ${this.shortPath(input.file_path)}`;\n case \"Bash\":\n return `Run ${(input.command || \"\").slice(0, 60)}`;\n case \"Glob\":\n return `Search ${input.pattern || \"\"}`;\n case \"Grep\":\n return `Grep ${input.pattern || \"\"}`;\n default:\n return name;\n }\n }\n\n private shortPath(p?: string): string {\n if (!p) return \"\";\n const parts = p.split(/[/\\\\]/);\n return parts.length > 2 ? \".../\" + parts.slice(-2).join(\"/\") : p;\n }\n}\n","// ============================================================\n// Accessibility Audit Prompt Additions\n// WCAG 2.1 AA compliance checks injected into review phase\n// ============================================================\n\nexport function getA11yPromptAdditions(): string {\n return `\nACCESSIBILITY AUDIT (WCAG 2.1 AA):\nAfter reviewing the code, also check for these accessibility requirements:\n\n1. PERCEIVABLE:\n - All images have meaningful alt text (not \"image\" or \"photo\")\n - Color is not the only means of conveying information\n - Text has sufficient color contrast (4.5:1 for normal text, 3:1 for large text)\n - Content is readable and functional at 200% zoom\n\n2. OPERABLE:\n - All interactive elements are keyboard accessible (Tab, Enter, Space, Escape)\n - Focus order is logical and visible (no outline:none without replacement)\n - Skip navigation link for keyboard users\n - No keyboard traps (user can always Tab out)\n - Touch targets are at least 44x44px on mobile\n\n3. UNDERSTANDABLE:\n - Form inputs have associated <label> elements\n - Error messages are clear and specific\n - Language attribute set on <html> tag\n - Consistent navigation patterns across pages\n\n4. ROBUST:\n - Semantic HTML elements (nav, main, header, footer, article, section)\n - ARIA roles and labels where semantic HTML isn't sufficient\n - ARIA landmarks for page regions\n - Live regions (aria-live) for dynamic content updates\n\nFIX directly if you find:\n- Missing alt text on images\n- Missing form labels\n- Missing lang attribute\n- Non-semantic containers that should be semantic elements\n- Missing skip navigation\n\nREPORT (do not fix) if you find:\n- Color contrast issues (requires visual verification)\n- Keyboard trap issues\n- Complex ARIA patterns that need careful testing\n`.trim();\n}\n","// ============================================================\n// Worker System Prompts — one per mode\n// Framework-specific additions are injected by the adapter.\n// ============================================================\n\nimport { getAdapter } from \"../../adapters/index.js\";\nimport { getA11yPromptAdditions } from \"../../utils/a11y.js\";\n\n/** Build the design system prompt, optionally with framework additions */\nexport function getDesignPrompt(framework?: string): string {\n const adapter = framework ? getAdapter(framework) : null;\n const additions = adapter?.designPromptAdditions || \"\";\n\n return `\nYou are a senior UI/UX designer working within the Forge development framework.\nYour job is to create beautiful, professional component previews as Storybook stories.\n\nDESIGN PRINCIPLES:\n- Mobile-first: design for 375px, then scale up to 768px and 1440px\n- Use the project's design system (shadcn/ui + Tailwind CSS)\n- Choose distinctive typography — NEVER use Inter, Arial, or Roboto\n- Commit to a cohesive color palette with a dominant color and sharp accents\n- Add micro-interactions (hover states, transitions)\n- Use realistic placeholder data (real names, real numbers, not \"Lorem ipsum\")\n- Include all states: default, loading, empty, error, success\n\nSTORYBOOK OUTPUT FORMAT:\n- Create .stories.tsx files in the stories/ directory\n- Each story file should export a default meta and named story variants\n- Use CSF3 format (Component Story Format)\n- Include args/controls for interactive props\n- Add viewport decorators for mobile/desktop\n\nQUALITY BAR:\nYour designs should look like they came from a professional design agency.\nReference real, well-designed products:\n- Finance → Stripe, Mercury, Wise\n- Dashboards → Linear, Vercel\n- E-commerce → Shopify, Gumroad\n- Social → Discord, Slack\n\nNEVER produce:\n- Purple gradient backgrounds\n- Generic rounded white cards on gray\n- Stock-photo placeholder images\n- Inconsistent spacing or alignment\n- Missing hover/focus states\n\n${additions}\n`.trim();\n}\n\n/** Build the build system prompt, optionally with framework additions */\nexport function getBuildPrompt(framework?: string): string {\n const adapter = framework ? getAdapter(framework) : null;\n const additions = adapter?.buildPromptAdditions || \"\";\n const isGeneric = adapter?.id === \"generic\";\n\n const verifyBlock = isGeneric\n ? `\nAFTER WRITING CODE:\n1. Detect the project's build/lint/typecheck/test commands from its config files\n (package.json scripts, Makefile, pyproject.toml, Cargo.toml, etc.)\n2. Run whichever commands exist — skip any that aren't configured\n3. Fix ALL errors before proceeding\n4. If no build system is configured, verify the code is syntactically valid\n`\n : `\nAFTER WRITING CODE:\n1. Run: ${adapter?.buildCommand || \"npm run build\"} — fix ALL errors before proceeding\n2. Run: ${adapter?.lintCommand || \"npm run lint\"} — fix warnings\n3. Run: ${adapter?.typecheckCommand || \"npx tsc --noEmit\"} — fix type errors\n`;\n\n return `\nYou are a senior fullstack developer working within the Forge development framework.\nYour job is to implement features based on approved designs and story requirements.\n\nCODING STANDARDS:\n- Small, focused components and functions (< 100 lines per file)\n- Proper error handling — try/catch, error boundaries, loading states\n- Responsive design — mobile-first with Tailwind breakpoints\n- Semantic HTML with accessibility (ARIA labels, keyboard navigation)\n- Clean imports, no circular dependencies\n\n${additions}\n\n${verifyBlock}\n\nIf any command fails:\n- Read the full error output\n- Identify the root cause\n- Fix it\n- Re-run the command\n- Repeat until all checks pass\n\nNEVER leave code in a broken state. Every commit must build successfully.\n\nGIT:\n- Make small, atomic changes\n- Write descriptive file names and function names\n`.trim();\n}\n\n/** Review system prompt — framework-aware build/lint/typecheck commands */\nexport function getReviewPrompt(framework?: string): string {\n const adapter = framework ? getAdapter(framework) : null;\n const isGeneric = adapter?.id === \"generic\";\n const lang = adapter?.language || \"typescript\";\n\n const langCheck = isGeneric\n ? \"3. Code quality — follows the language's best practices and idioms\"\n : lang === \"typescript\"\n ? \"3. TypeScript strict compliance (no any, no ts-ignore, no ts-expect-error)\"\n : \"3. Python code quality (no bare except, proper type hints where used)\";\n\n const runBlock = isGeneric\n ? `\nRUN THESE COMMANDS:\n- Detect build/lint/test commands from the project's config files (package.json, Makefile, pyproject.toml, Cargo.toml, etc.)\n- Run whichever exist — skip any that aren't configured\n- Run tests if a test runner is configured\n`\n : `\nRUN THESE COMMANDS:\n- ${adapter?.buildCommand || \"npm run build\"}\n- ${adapter?.lintCommand || \"npm run lint\"}\n- ${adapter?.typecheckCommand || \"npx tsc --noEmit\"}\n- npm run test (if tests exist)\n`;\n\n return `\nYou are a QA engineer reviewing code within the Forge development framework.\nYour job is to catch issues before code is merged to main.\n\nREVIEW CHECKLIST:\n1. Implementation matches the story description\n2. If design was approved — implementation matches the design\n${langCheck}\n4. Responsive design works at 375px, 768px, 1440px\n5. Error handling — what happens when things fail?\n6. Loading states — what does the user see while waiting?\n7. Empty states — what if there's no data?\n8. Accessibility — semantic HTML, focus management, screen reader support\n9. No debug logs (console.log / print() for debugging)\n10. No commented-out code\n11. No TODO/FIXME/HACK comments left behind\n12. No hardcoded values that should be configurable\n\n${runBlock}\n\n${getA11yPromptAdditions()}\n\nISSUE CLASSIFICATION:\n- MINOR: formatting, missing type annotation, unused import\n → Fix it directly, note what you fixed\n- MAJOR: broken logic, missing error handling, security issue, missing feature\n → List the issue with file path and line, do NOT attempt to fix\n\nOUTPUT:\nProvide a structured review summary:\n- Files reviewed\n- Issues found (with severity)\n- Issues auto-fixed\n- Commands run and their results\n- PASS or FAIL recommendation\n`.trim();\n}\n\n/** Test generation system prompt — framework-aware test runner */\nexport function getTestPrompt(framework?: string): string {\n const adapter = framework ? getAdapter(framework) : null;\n const isGeneric = adapter?.id === \"generic\";\n const testCmd = adapter?.testCommand || \"npm test\";\n const testFramework = adapter?.testFramework || \"the project's test framework\";\n\n const runBlock = isGeneric\n ? `\nTEST RUNNER:\n- Detect the test framework from config files (package.json, pyproject.toml, pubspec.yaml, Cargo.toml, etc.)\n- Common test commands: npm test, npx vitest run, pytest, flutter test, cargo test, go test ./...\n- Run whichever test command exists — skip if none configured\n- Install the test framework if missing (e.g., npm install -D vitest @testing-library/react)\n`\n : `\nTEST RUNNER:\n- Framework: ${testFramework}\n- Run: ${testCmd}\n- Install test dependencies if missing\n`;\n\n return `\nYou are a senior QA engineer writing automated tests within the Forge development framework.\nYour job is to generate comprehensive, meaningful tests for the code that was just built.\n\nTEST PRINCIPLES:\n- Test behavior, not implementation details\n- Each test should be independent and deterministic\n- Cover the happy path first, then edge cases\n- Use descriptive test names that explain WHAT is being tested and WHY\n- Prefer integration tests over unit tests for UI components\n- Mock external dependencies (APIs, databases), not internal code\n- Keep tests fast — avoid unnecessary setup/teardown\n\nWHAT TO TEST:\n1. Component rendering — does it render without errors?\n2. User interactions — clicks, form inputs, navigation\n3. State changes — does the UI update correctly?\n4. Error states — what happens when things fail?\n5. Edge cases — empty data, long strings, special characters\n6. API integration — correct requests, response handling\n7. Accessibility — ARIA attributes, keyboard navigation\n\n${runBlock}\n\nTEST FILE ORGANIZATION:\n- Co-locate tests with source files (e.g., Button.test.tsx next to Button.tsx)\n- Or use a __tests__/ directory within each feature folder\n- Follow the project's existing test patterns if any exist\n\nAFTER WRITING TESTS:\n1. Run the test suite — ALL tests must pass\n2. If a test fails, fix the test (not the source code, unless there's a genuine bug)\n3. Aim for meaningful coverage, not 100% line coverage\n\nDO NOT:\n- Write snapshot tests (they're fragile and provide little value)\n- Test implementation details (internal state, private methods)\n- Write tests that depend on each other\n- Leave TODO or skip markers\n`.trim();\n}\n\n/** Fix system prompt — framework-aware commands */\nexport function getFixPrompt(framework?: string): string {\n const adapter = framework ? getAdapter(framework) : null;\n const isGeneric = adapter?.id === \"generic\";\n\n const verifyBlock = isGeneric\n ? `\nAFTER EVERY FIX:\n1. Detect the project's build/test commands from its config files\n2. Run whichever exist to verify nothing is broken\n3. Verify the fix works\n`\n : `\nAFTER EVERY FIX:\n1. Run: ${adapter?.buildCommand || \"npm run build\"}\n2. Run: ${adapter?.typecheckCommand || \"npx tsc --noEmit\"}\n3. Verify the fix works\n`;\n\n return `\nYou are a debugger and problem solver within the Forge development framework.\nYour job is to make targeted fixes without breaking anything else.\n\nPRINCIPLES:\n- Make the SMALLEST possible change to fix the issue\n- Do NOT refactor surrounding code — you're here to fix, not improve\n- Read the existing code carefully before changing anything\n- Understand the full context of what you're changing\n\nFOR VISUAL TWEAKS:\n- Change only the specific CSS/Tailwind classes needed\n- Verify the change doesn't break other viewports\n- Check both mobile and desktop after changing\n\nFOR BUG FIXES:\n- Read error messages and stack traces carefully\n- Trace the issue to its ROOT CAUSE\n- Fix the cause, not the symptom\n- Check if the same bug pattern exists elsewhere\n- Verify the fix by running the relevant command\n\n${verifyBlock}\n\nIf your fix introduces new errors, undo it and try a different approach.\n`.trim();\n}\n\n// ── Legacy exports for backward compatibility ────────────────\n// These are used by Worker when no framework is specified.\n\nexport const DESIGN_SYSTEM_PROMPT = getDesignPrompt();\nexport const BUILD_SYSTEM_PROMPT = getBuildPrompt();\nexport const TEST_SYSTEM_PROMPT = getTestPrompt();\nexport const REVIEW_SYSTEM_PROMPT = getReviewPrompt();\nexport const FIX_SYSTEM_PROMPT = getFixPrompt();\n","import simpleGit, { type SimpleGit } from \"simple-git\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\n// ============================================================\n// Git Manager\n// Handles branch strategy, tagging, and snapshots.\n// ============================================================\n\nexport class GitManager {\n private git: SimpleGit;\n private basePath: string;\n private cachedBranch: string | null = null;\n private repoVerified = false;\n\n constructor(basePath: string = process.cwd()) {\n this.basePath = basePath;\n this.git = simpleGit(basePath);\n }\n\n // ── Branch Operations ─────────────────────────────────────\n\n async createBranch(name: string): Promise<void> {\n // Always commit dirty state before switching branches\n await this.stashDirtyState();\n\n const currentBranch = await this.getCurrentBranch();\n if (currentBranch !== \"main\") {\n await this.git.checkout(\"main\");\n }\n await this.git.checkoutLocalBranch(name);\n }\n\n async checkout(branch: string): Promise<void> {\n await this.stashDirtyState();\n await this.git.checkout(branch);\n this.cachedBranch = branch;\n }\n\n async merge(branch: string): Promise<void> {\n await this.git.merge([branch, \"--no-ff\"]);\n }\n\n async deleteBranch(branch: string): Promise<void> {\n await this.git.deleteLocalBranch(branch, true);\n }\n\n async getCurrentBranch(): Promise<string> {\n if (this.cachedBranch) return this.cachedBranch;\n try {\n const status = await this.git.status();\n if (status.current) {\n this.cachedBranch = status.current;\n return this.cachedBranch;\n }\n } catch {\n // Don't cache on error — let next call retry\n }\n return \"main\";\n }\n\n async listBranches(): Promise<string[]> {\n const result = await this.git.branchLocal();\n return result.all;\n }\n\n // ── Commit Operations ─────────────────────────────────────\n\n async commitAll(message: string): Promise<void> {\n // Use git add + commit in one flow; skip status check — let commit fail silently if nothing staged\n await this.git.add(\".\");\n try {\n await this.git.commit(message);\n } catch (err: any) {\n // \"nothing to commit\" is not an error\n if (!err?.message?.includes(\"nothing to commit\")) throw err;\n }\n }\n\n async commitState(message: string): Promise<void> {\n await this.git.add(\".forge/\");\n await this.git.add(\"forge.config.json\");\n\n const status = await this.git.status();\n if (status.staged.length > 0) {\n await this.git.commit(`forge: ${message}`);\n }\n }\n\n // ── Tag Operations ────────────────────────────────────────\n\n async tag(name: string): Promise<void> {\n await this.git.addTag(name);\n }\n\n async listTags(): Promise<string[]> {\n const result = await this.git.tags();\n return result.all.filter((t) => t.startsWith(\"forge/\"));\n }\n\n async checkoutTag(tagName: string): Promise<void> {\n await this.stashDirtyState();\n await this.git.checkout(tagName);\n }\n\n // ── Diff & History ────────────────────────────────────────\n\n async getDiff(branch: string): Promise<string> {\n return this.git.diff([\"main\", branch]);\n }\n\n /** Diff between any two refs (tags, commits, branches) */\n async getDiff2(ref1: string, ref2: string): Promise<string> {\n return this.git.diff([ref1, ref2]);\n }\n\n async getLog(count: number = 10): Promise<any[]> {\n const log = await this.git.log({ maxCount: count });\n return log.all as any[];\n }\n\n /** Get the current HEAD commit hash */\n async getHead(): Promise<string> {\n const log = await this.git.log({ maxCount: 1 });\n return log.latest?.hash || \"\";\n }\n\n /** Revert a specific commit (creates a new revert commit) */\n async revertCommit(hash: string): Promise<void> {\n await this.git.raw([\"revert\", \"--no-edit\", hash]);\n }\n\n /** Get forge-related commits (feat:, fix:, forge:, docs:, ci:) */\n async getForgeLog(count: number = 20): Promise<Array<{ hash: string; date: string; message: string }>> {\n const log = await this.git.log({ maxCount: count });\n return (log.all as any[]).map((entry) => ({\n hash: entry.hash,\n date: entry.date,\n message: entry.message,\n }));\n }\n\n /** Get tags pointing at a specific commit */\n async getTagsAtCommit(hash: string): Promise<string[]> {\n try {\n const result = await this.git.raw([\"tag\", \"--points-at\", hash]);\n return result.trim().split(\"\\n\").filter(Boolean);\n } catch {\n return [];\n }\n }\n\n // ── Push Operations ─────────────────────────────────────\n\n /** Check if a remote named 'origin' exists */\n async hasRemote(): Promise<boolean> {\n try {\n const remotes = await this.git.getRemotes();\n return remotes.some((r) => r.name === \"origin\");\n } catch {\n return false;\n }\n }\n\n /** Push current branch to origin */\n async push(): Promise<void> {\n const branch = await this.getCurrentBranch();\n await this.git.push(\"origin\", branch, [\"--set-upstream\"]);\n }\n\n /** Push tags to origin */\n async pushTags(): Promise<void> {\n await this.git.pushTags(\"origin\");\n }\n\n // ── Status ────────────────────────────────────────────────\n\n async isClean(): Promise<boolean> {\n const status = await this.git.status();\n return status.isClean();\n }\n\n async hasUncommittedChanges(): Promise<boolean> {\n const status = await this.git.status();\n return !status.isClean();\n }\n\n // ── Init (for new projects) ───────────────────────────────\n\n async ensureRepo(): Promise<void> {\n if (this.repoVerified) return;\n\n const isRepo = await this.git.checkIsRepo();\n if (!isRepo) {\n await this.git.init();\n }\n // Ensure .gitignore has common entries\n await this.ensureGitignore();\n\n // Initial commit if repo is empty\n const log = await this.git.log().catch(() => null);\n if (!log || log.total === 0) {\n await this.git.add(\".\");\n await this.git.commit(\"Initial commit\");\n }\n\n this.repoVerified = true;\n }\n\n async ensureMainBranch(): Promise<void> {\n const current = await this.getCurrentBranch();\n if (current !== \"main\") {\n // Only check branches if we're not already on main\n const branches = await this.listBranches();\n if (!branches.includes(\"main\")) {\n await this.git.branch([\"-M\", \"main\"]);\n this.cachedBranch = \"main\";\n }\n }\n }\n\n // ── Internal ──────────────────────────────────────────────\n\n /** Commit any uncommitted changes before branch operations */\n private async stashDirtyState(): Promise<void> {\n const status = await this.git.status();\n if (status.isClean()) return;\n\n await this.git.add(\".\");\n await this.git.commit(\"forge: save state before branch switch\");\n }\n\n /** Ensure .gitignore covers build artifacts and forge internals */\n private async ensureGitignore(): Promise<void> {\n const gitignorePath = path.join(this.basePath, \".gitignore\");\n const requiredEntries = [\n \"node_modules/\",\n \".next/\",\n \".forge/snapshots/\",\n \"dist/\",\n \".env\",\n \".env.local\",\n ];\n\n let content = \"\";\n try {\n content = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {\n // File doesn't exist yet\n }\n\n const missing = requiredEntries.filter((e) => !content.includes(e));\n if (missing.length > 0) {\n const addition = (content ? \"\\n\" : \"\") + missing.join(\"\\n\") + \"\\n\";\n await fs.appendFile(gitignorePath, addition);\n }\n }\n}\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport type {\n Plan,\n SprintState,\n Phase,\n ForgeConfig,\n Snapshot,\n} from \"../types/plan.js\";\n\n// ============================================================\n// State Manager\n// Reads/writes .forge/ JSON files. Source of truth for the sprint.\n// ============================================================\n\nconst FORGE_DIR = \".forge\";\nconst PLAN_FILE = \"plan.json\";\nconst STATE_FILE = \"state.json\";\nconst CONFIG_FILE = \"forge.config.json\";\nconst SNAPSHOTS_DIR = \"snapshots\";\n\nclass StateManager {\n private basePath: string;\n private cache: Map<string, { data: any; mtime: number }> = new Map();\n private dirCreated: Set<string> = new Set();\n\n constructor(basePath: string = process.cwd()) {\n this.basePath = basePath;\n }\n\n /** Clear all caches (call when basePath changes) */\n clearCache(): void {\n this.cache.clear();\n this.dirCreated.clear();\n }\n\n // ── Paths ─────────────────────────────────────────────────\n\n private forgePath(...parts: string[]): string {\n return path.join(this.basePath, FORGE_DIR, ...parts);\n }\n\n private rootPath(...parts: string[]): string {\n return path.join(this.basePath, ...parts);\n }\n\n // ── Plan ──────────────────────────────────────────────────\n\n async savePlan(plan: Plan): Promise<void> {\n await this.writeJson(this.forgePath(PLAN_FILE), plan);\n }\n\n async getPlan(): Promise<Plan | null> {\n return this.readJson<Plan>(this.forgePath(PLAN_FILE));\n }\n\n async hasPlan(): Promise<boolean> {\n return this.fileExists(this.forgePath(PLAN_FILE));\n }\n\n // ── State ─────────────────────────────────────────────────\n\n async getState(): Promise<SprintState> {\n const state = await this.readJson<SprintState>(\n this.forgePath(STATE_FILE)\n );\n return (\n state || {\n currentPhase: \"init\",\n currentStory: null,\n workerMode: null,\n queue: [],\n history: [],\n }\n );\n }\n\n async updatePhase(phase: Phase): Promise<void> {\n const state = await this.getState();\n state.currentPhase = phase;\n await this.writeJson(this.forgePath(STATE_FILE), state);\n }\n\n async updateState(updates: Partial<SprintState>): Promise<void> {\n const state = await this.getState();\n Object.assign(state, updates);\n await this.writeJson(this.forgePath(STATE_FILE), state);\n }\n\n async addHistoryEntry(entry: {\n action: string;\n storyId: string | null;\n details: string;\n snapshotId?: string;\n }): Promise<void> {\n const state = await this.getState();\n state.history.push({\n ...entry,\n timestamp: new Date().toISOString(),\n snapshotId: entry.snapshotId || null,\n });\n await this.writeJson(this.forgePath(STATE_FILE), state);\n }\n\n // ── Config ────────────────────────────────────────────────\n\n async getConfig(): Promise<ForgeConfig | null> {\n return this.readJson<ForgeConfig>(this.rootPath(CONFIG_FILE));\n }\n\n async saveConfig(config: ForgeConfig): Promise<void> {\n await this.writeJson(this.rootPath(CONFIG_FILE), config);\n }\n\n async isInitialized(): Promise<boolean> {\n return this.fileExists(this.rootPath(CONFIG_FILE));\n }\n\n // ── Snapshots ─────────────────────────────────────────────\n\n async saveSnapshot(data: {\n action: string;\n storyId: string | null;\n branch: string;\n commitBefore?: string;\n }): Promise<string> {\n const state = await this.getState();\n const id = String(state.history.length + 1).padStart(3, \"0\");\n\n const snapshot: Snapshot = {\n id,\n action: data.action,\n storyId: data.storyId,\n timestamp: new Date().toISOString(),\n files: [],\n branch: data.branch,\n commitBefore: data.commitBefore || \"\",\n };\n\n await this.writeJson(\n this.forgePath(SNAPSHOTS_DIR, `${id}-pre-${data.action}-${data.storyId || \"global\"}.json`),\n snapshot\n );\n\n await this.addHistoryEntry({\n action: data.action,\n storyId: data.storyId,\n details: `Snapshot taken before ${data.action}`,\n snapshotId: id,\n });\n\n return id;\n }\n\n async getSnapshot(id: string): Promise<Snapshot | null> {\n const dir = this.forgePath(SNAPSHOTS_DIR);\n const files = await fs.readdir(dir).catch(() => []);\n const match = files.find((f) => f.startsWith(id));\n if (!match) return null;\n return this.readJson<Snapshot>(path.join(dir, match));\n }\n\n async listSnapshots(): Promise<Snapshot[]> {\n const dir = this.forgePath(SNAPSHOTS_DIR);\n const files = await fs.readdir(dir).catch(() => []);\n const snapshots: Snapshot[] = [];\n for (const file of files.filter((f) => f.endsWith(\".json\"))) {\n const snapshot = await this.readJson<Snapshot>(path.join(dir, file));\n if (snapshot) snapshots.push(snapshot);\n }\n return snapshots.sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n }\n\n // ── Design References ──────────────────────────────────────\n\n async saveDesignReferences(files: string[]): Promise<void> {\n const refDir = this.forgePath(\"designs\", \"references\");\n await fs.mkdir(refDir, { recursive: true });\n\n const manifest: Array<{ original: string; saved: string }> = [];\n for (const file of files) {\n const basename = path.basename(file);\n const dest = path.join(refDir, basename);\n await fs.copyFile(file, dest);\n manifest.push({ original: file, saved: dest });\n }\n\n await this.writeJson(this.forgePath(\"designs\", \"references.json\"), manifest);\n }\n\n async getDesignReferences(): Promise<string[]> {\n const manifest = await this.readJson<Array<{ saved: string }>>(\n this.forgePath(\"designs\", \"references.json\")\n );\n return manifest?.map((r) => r.saved) || [];\n }\n\n // ── Helpers ───────────────────────────────────────────────\n\n private async readJson<T>(filePath: string): Promise<T | null> {\n try {\n const stat = await fs.stat(filePath);\n const mtime = stat.mtimeMs;\n\n // Return cached if file hasn't changed\n const cached = this.cache.get(filePath);\n if (cached && cached.mtime === mtime) {\n return cached.data as T;\n }\n\n const content = await fs.readFile(filePath, \"utf-8\");\n const data = JSON.parse(content) as T;\n this.cache.set(filePath, { data, mtime });\n return data;\n } catch (err: any) {\n // File not found is expected — return null\n if (err?.code === \"ENOENT\") return null;\n // JSON parse error — file is corrupted, return null\n if (err instanceof SyntaxError) return null;\n // Permission or other system errors — propagate so caller knows\n throw err;\n }\n }\n\n private async writeJson(filePath: string, data: any): Promise<void> {\n const dir = path.dirname(filePath);\n // Only mkdir if we haven't already created this directory\n if (!this.dirCreated.has(dir)) {\n await fs.mkdir(dir, { recursive: true });\n this.dirCreated.add(dir);\n }\n const json = JSON.stringify(data, null, 2);\n await fs.writeFile(filePath, json);\n // Update cache immediately — avoid re-reading what we just wrote\n const stat = await fs.stat(filePath);\n this.cache.set(filePath, { data, mtime: stat.mtimeMs });\n }\n\n private async fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// Singleton\nexport const stateManager = new StateManager();\n","import chalk from \"chalk\";\nimport ora from \"ora\";\nimport inquirer from \"inquirer\";\nimport type {\n Plan,\n Story,\n ForgeConfig,\n QueuedChange,\n} from \"../../types/plan.js\";\nimport { Orchestrator } from \"../orchestrator/index.js\";\nimport { Worker, type WorkerProgressCallback } from \"../worker/index.js\";\nimport { GitManager } from \"../git/index.js\";\nimport { getAdapter } from \"../adapters/index.js\";\nimport { stateManager } from \"../../state/index.js\";\n\n// ============================================================\n// Pipeline Engine\n// Manages the phase flow: plan > design > build > review\n// with human review gates between each phase.\n// Builds on main — no feature branches. Per-story commits + tags.\n// ============================================================\n\nconst MAX_REGEN_ATTEMPTS = 5;\n\nexport class Pipeline {\n private orchestrator: Orchestrator;\n private worker: Worker;\n private git: GitManager;\n private config: ForgeConfig;\n private plan: Plan | null = null;\n private changeQueue: QueuedChange[] = [];\n\n constructor(config: ForgeConfig) {\n this.config = config;\n this.orchestrator = new Orchestrator(config);\n this.worker = new Worker(config, {});\n this.git = new GitManager();\n }\n\n // ── Full Sprint (all phases) ──────────────────────────────\n\n async runSprint(description: string): Promise<void> {\n console.log(chalk.bold(\"\\n forge\") + chalk.dim(\" sprint\\n\"));\n\n await this.git.ensureRepo();\n await this.git.ensureMainBranch();\n\n // Phase 1: Plan\n this.plan = await this.runPlanPhase(description);\n if (!this.plan) return;\n\n // Phase 2: Design (skip if framework doesn't support it)\n const adapter = getAdapter(this.config.framework);\n if (adapter.designSupport) {\n await this.runDesignPhase(this.plan);\n } else {\n console.log(chalk.dim(`\\n Design skipped (${adapter.name} — no Storybook support)\\n`));\n }\n\n // Phase 3: Build\n await this.runBuildPhase(this.plan);\n\n // Phase 4: Review\n await this.runReviewPhase(this.plan);\n\n // Done\n console.log(chalk.dim(\"\\n ─────────────────────────────────\"));\n console.log(chalk.bold(\" Sprint complete\\n\"));\n console.log(chalk.dim(\" Run your app and test it.\"));\n console.log(chalk.dim(' Use ' + chalk.white('forge fix \"description\"') + ' to make changes.'));\n console.log(chalk.dim(\" Use \" + chalk.white(\"forge undo\") + \" to revert.\\n\"));\n }\n\n // ── Phase 1: Plan ────────────────────────────────────────\n\n async runPlanPhase(description: string, attempt = 1): Promise<Plan | null> {\n if (attempt === 1) {\n console.log(chalk.bold(\" Plan\\n\"));\n }\n\n const spinner = ora({ text: \"Analyzing requirements...\", indent: 2 }).start();\n\n let plan: Plan;\n try {\n plan = await this.orchestrator.generatePlan(description);\n } catch (err) {\n spinner.fail(\"Plan generation failed\");\n console.log(chalk.red(` ${err instanceof Error ? err.message : err}`));\n return null;\n }\n spinner.succeed(\"Plan generated\");\n\n this.displayPlan(plan);\n\n // User gate\n const { action } = await inquirer.prompt([\n {\n type: \"list\",\n name: \"action\",\n message: \"Action\",\n choices: [\n { name: \"Approve plan\", value: \"approve\" },\n { name: \"Edit (describe changes)\", value: \"edit\" },\n { name: \"Regenerate\", value: \"regen\" },\n { name: \"Cancel\", value: \"cancel\" },\n ],\n },\n ]);\n\n switch (action) {\n case \"approve\":\n await stateManager.savePlan(plan);\n await stateManager.updatePhase(\"plan\");\n await this.git.commitState(\"Sprint plan approved\");\n await this.git.tag(\"forge/v0.0-plan\");\n console.log(chalk.green(\" Plan saved\\n\"));\n return plan;\n\n case \"edit\": {\n if (attempt >= MAX_REGEN_ATTEMPTS) {\n console.log(chalk.yellow(` Max edit attempts (${MAX_REGEN_ATTEMPTS}) reached. Approving current plan.`));\n await stateManager.savePlan(plan);\n return plan;\n }\n const { changes } = await inquirer.prompt([\n { type: \"input\", name: \"changes\", message: \"Describe changes:\" },\n ]);\n console.log(chalk.dim(\" Re-planning...\"));\n return this.runPlanPhase(`${description}\\n\\nUser edits: ${changes}`, attempt + 1);\n }\n\n case \"regen\": {\n if (attempt >= MAX_REGEN_ATTEMPTS) {\n console.log(chalk.yellow(` Max regeneration attempts (${MAX_REGEN_ATTEMPTS}) reached.`));\n return null;\n }\n return this.runPlanPhase(description, attempt + 1);\n }\n\n case \"cancel\":\n console.log(chalk.dim(\" Cancelled.\"));\n return null;\n\n default:\n return null;\n }\n }\n\n // ── Phase 2: Design ──────────────────────────────────────\n\n async runDesignPhase(plan: Plan): Promise<void> {\n const uiStories = this.getStoriesByType(plan, [\"ui\", \"fullstack\"]);\n\n if (uiStories.length === 0) {\n console.log(chalk.dim(\"\\n No UI stories, skipping design.\\n\"));\n return;\n }\n\n console.log(chalk.bold(`\\n Design`) + chalk.dim(` · ${uiStories.length} stories\\n`));\n\n for (const story of uiStories) {\n const spinner = ora({ text: story.title, indent: 4 }).start();\n\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"design\", { plan });\n\n const result = await this.worker.run(\"design\", prompt, {\n onProgress: (event) => {\n if (event.type === \"tool_use\") {\n spinner.text = event.content;\n }\n },\n });\n\n if (result.success) {\n spinner.succeed(story.title);\n } else {\n spinner.fail(story.title);\n console.log(chalk.red(` ${result.errors.join(\", \")}`));\n continue;\n }\n\n // User gate\n const adapter = getAdapter(this.config.framework);\n console.log(chalk.dim(` Preview: http://localhost:${adapter.devPort}\\n`));\n\n const { approval } = await inquirer.prompt([\n {\n type: \"list\",\n name: \"approval\",\n message: `Approve \"${story.title}\"?`,\n choices: [\n { name: \"Approve\", value: \"approve\" },\n { name: \"Request changes\", value: \"change\" },\n { name: \"Skip\", value: \"skip\" },\n ],\n },\n ]);\n\n if (approval === \"approve\") {\n story.designApproved = true;\n story.status = \"design-approved\";\n console.log(chalk.green(` Approved\\n`));\n } else if (approval === \"change\") {\n const { feedback } = await inquirer.prompt([\n { type: \"input\", name: \"feedback\", message: \"Describe changes:\" },\n ]);\n // Re-run design with feedback\n const fixResult = await this.worker.run(\"fix\", `Revise design for \"${story.title}\": ${feedback}`);\n if (fixResult.success) {\n console.log(chalk.dim(\" Revised.\\n\"));\n }\n }\n }\n\n await stateManager.savePlan(plan);\n await stateManager.updatePhase(\"design\");\n await this.git.commitAll(\"forge: designs reviewed\");\n await this.git.tag(\"forge/v0.1-designs\");\n\n console.log(chalk.dim(\" Design phase complete\\n\"));\n }\n\n // ── Phase 3: Build ───────────────────────────────────────\n\n async runBuildPhase(plan: Plan): Promise<void> {\n const buildableStories = this.getAllStories(plan).filter(\n (s) => s.status === \"design-approved\" || s.status === \"planned\"\n );\n\n if (buildableStories.length === 0) {\n console.log(chalk.dim(\"\\n No stories to build.\\n\"));\n return;\n }\n\n console.log(chalk.bold(`\\n Build`) + chalk.dim(` · ${buildableStories.length} stories\\n`));\n\n for (const story of buildableStories) {\n const spinner = ora({ text: story.title, indent: 4 }).start();\n\n // Snapshot before building\n const headBefore = await this.git.getHead();\n await stateManager.saveSnapshot({\n action: \"build\",\n storyId: story.id,\n branch: \"main\",\n commitBefore: headBefore,\n });\n\n story.status = \"building\";\n await stateManager.savePlan(plan);\n\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"build\", {\n plan,\n designMeta: story.designApproved ? { storyId: story.id } : undefined,\n });\n\n const result = await this.worker.run(\"build\", prompt, {\n onProgress: (event) => {\n if (event.type === \"tool_use\") {\n spinner.text = event.content;\n }\n },\n });\n\n if (result.success) {\n await this.git.commitAll(`feat: ${story.title}`);\n story.status = \"reviewing\";\n spinner.succeed(story.title + chalk.dim(` · ${result.filesCreated.length} files`));\n } else {\n story.status = \"blocked\";\n spinner.fail(story.title);\n for (const error of result.errors) {\n console.log(chalk.red(` ${error}`));\n }\n }\n\n await stateManager.savePlan(plan);\n await this.processQueue(plan);\n }\n\n await stateManager.updatePhase(\"build\");\n console.log(chalk.dim(\"\\n Build phase complete\\n\"));\n }\n\n // ── Phase 4: Review ──────────────────────────────────────\n\n async runReviewPhase(plan: Plan): Promise<void> {\n const reviewableStories = this.getAllStories(plan).filter(\n (s) => s.status === \"reviewing\"\n );\n\n if (reviewableStories.length === 0) return;\n\n console.log(chalk.bold(`\\n Review`) + chalk.dim(` · ${reviewableStories.length} stories\\n`));\n\n for (const story of reviewableStories) {\n const spinner = ora({ text: story.title, indent: 4 }).start();\n\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"review\", {\n plan,\n designMeta: story.designApproved ? { storyId: story.id } : undefined,\n });\n\n let result;\n try {\n result = await this.worker.run(\"review\", prompt);\n } catch (err) {\n spinner.fail(story.title + chalk.dim(\" — review error\"));\n console.log(chalk.red(` ${err instanceof Error ? err.message : err}`));\n continue;\n }\n\n if (result.success) {\n const tagName = `forge/v0.${this.getNextTagNumber()}-${story.id}`;\n await this.git.tag(tagName);\n story.tags.push(tagName);\n story.status = \"done\";\n spinner.succeed(story.title + chalk.dim(` [${tagName}]`));\n } else {\n spinner.fail(story.title);\n console.log(chalk.dim(` ${result.summary}`));\n }\n\n await stateManager.savePlan(plan);\n }\n\n await stateManager.updatePhase(\"review\");\n console.log(chalk.dim(\"\\n Review phase complete\\n\"));\n }\n\n // ── Change Queue ─────────────────────────────────────────\n\n queueChange(change: QueuedChange): void {\n this.changeQueue.push(change);\n console.log(chalk.dim(` Queued: \"${change.message}\"`));\n }\n\n private async processQueue(plan: Plan): Promise<void> {\n if (this.changeQueue.length === 0) return;\n\n console.log(chalk.dim(`\\n Processing ${this.changeQueue.length} queued changes...\\n`));\n\n for (const change of this.changeQueue) {\n const decision = await this.orchestrator.routeUserInput(\n change.message,\n await stateManager.getState()\n );\n\n if (decision.action === \"route-to-worker\" && decision.prompt) {\n const mode = decision.workerMode || \"fix\";\n const spinner = ora({ text: change.message, indent: 4 }).start();\n const result = await this.worker.run(mode, decision.prompt);\n if (result.success) {\n await this.git.commitAll(`fix: ${change.message}`);\n spinner.succeed(change.message);\n } else {\n spinner.fail(change.message);\n }\n }\n }\n\n this.changeQueue = [];\n }\n\n // ── Helpers ──────────────────────────────────────────────\n\n private displayPlan(plan: Plan): void {\n console.log(chalk.bold(`\\n ${plan.project}`) + chalk.dim(` · ${plan.framework}`));\n console.log(chalk.dim(` ${plan.description}\\n`));\n\n for (const epic of plan.epics) {\n console.log(chalk.dim(` ${epic.title}`));\n for (const story of epic.stories) {\n const tag = story.type === \"ui\" ? \"ui\" : story.type === \"backend\" ? \"api\" : \"full\";\n console.log(` ${chalk.dim(`[${tag}]`)} ${story.id} ${chalk.dim(\"—\")} ${story.title}`);\n }\n console.log(\"\");\n }\n }\n\n private getAllStories(plan: Plan): Story[] {\n return plan.epics.flatMap((epic) => epic.stories);\n }\n\n private getStoriesByType(plan: Plan, types: string[]): Story[] {\n return this.getAllStories(plan).filter((s) => types.includes(s.type));\n }\n\n private tagCounter = 2;\n private getNextTagNumber(): number {\n return this.tagCounter++;\n }\n}\n","// ============================================================\n// Cost Estimation\n// Estimates token usage and cost before running a sprint\n// ============================================================\n\nimport type { Plan, Story } from \"../../types/plan.js\";\n\n// Approximate tokens per phase per story type\nconst TOKENS_PER_STORY: Record<string, { input: number; output: number }> = {\n \"ui-design\": { input: 8_000, output: 12_000 },\n \"ui-build\": { input: 15_000, output: 25_000 },\n \"ui-test\": { input: 10_000, output: 15_000 },\n \"ui-review\": { input: 12_000, output: 8_000 },\n \"backend-build\": { input: 12_000, output: 20_000 },\n \"backend-test\": { input: 8_000, output: 12_000 },\n \"backend-review\":{ input: 10_000, output: 6_000 },\n \"fullstack-design\": { input: 8_000, output: 12_000 },\n \"fullstack-build\": { input: 20_000, output: 35_000 },\n \"fullstack-test\": { input: 12_000, output: 18_000 },\n \"fullstack-review\": { input: 15_000, output: 10_000 },\n};\n\n// Claude pricing per 1M tokens (as of 2025)\nconst PRICING: Record<string, { input: number; output: number }> = {\n sonnet: { input: 3.0, output: 15.0 },\n opus: { input: 15.0, output: 75.0 },\n haiku: { input: 0.25, output: 1.25 },\n};\n\nexport interface CostEstimate {\n totalInputTokens: number;\n totalOutputTokens: number;\n estimatedCostUsd: number;\n perStory: {\n storyId: string;\n title: string;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n }[];\n model: string;\n}\n\nexport function estimateCost(plan: Plan, model: string = \"sonnet\"): CostEstimate {\n const pricing = PRICING[model] || PRICING.sonnet;\n const stories = plan.epics.flatMap((e) => e.stories);\n const perStory: CostEstimate[\"perStory\"] = [];\n\n let totalInput = 0;\n let totalOutput = 0;\n\n // Planning phase (orchestrator)\n totalInput += 5_000;\n totalOutput += 8_000;\n\n // README generation\n totalInput += 8_000;\n totalOutput += 5_000;\n\n for (const story of stories) {\n let storyInput = 0;\n let storyOutput = 0;\n\n const phases = getPhases(story);\n for (const phase of phases) {\n const key = `${story.type}-${phase}`;\n const tokens = TOKENS_PER_STORY[key];\n if (tokens) {\n storyInput += tokens.input;\n storyOutput += tokens.output;\n }\n }\n\n totalInput += storyInput;\n totalOutput += storyOutput;\n\n const storyCost =\n (storyInput / 1_000_000) * pricing.input +\n (storyOutput / 1_000_000) * pricing.output;\n\n perStory.push({\n storyId: story.id,\n title: story.title,\n inputTokens: storyInput,\n outputTokens: storyOutput,\n costUsd: storyCost,\n });\n }\n\n const totalCost =\n (totalInput / 1_000_000) * pricing.input +\n (totalOutput / 1_000_000) * pricing.output;\n\n return {\n totalInputTokens: totalInput,\n totalOutputTokens: totalOutput,\n estimatedCostUsd: totalCost,\n perStory,\n model,\n };\n}\n\nfunction getPhases(story: Story): string[] {\n switch (story.type) {\n case \"ui\":\n return [\"design\", \"build\", \"test\", \"review\"];\n case \"backend\":\n return [\"build\", \"test\", \"review\"];\n case \"fullstack\":\n return [\"design\", \"build\", \"test\", \"review\"];\n default:\n return [\"build\", \"test\", \"review\"];\n }\n}\n\nexport function formatCostEstimate(est: CostEstimate): string {\n const lines: string[] = [];\n lines.push(` Model: ${est.model}`);\n lines.push(` Stories: ${est.perStory.length}`);\n lines.push(\"\");\n\n for (const s of est.perStory) {\n const tokens = `${formatTokens(s.inputTokens)} in / ${formatTokens(s.outputTokens)} out`;\n lines.push(` ${s.title}`);\n lines.push(` ${tokens} · ~$${s.costUsd.toFixed(4)}`);\n }\n\n lines.push(\"\");\n lines.push(` Total: ${formatTokens(est.totalInputTokens)} in / ${formatTokens(est.totalOutputTokens)} out`);\n lines.push(` Estimated cost: ~$${est.estimatedCostUsd.toFixed(2)}`);\n\n return lines.join(\"\\n\");\n}\n\nfunction formatTokens(n: number): string {\n if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + \"M\";\n if (n >= 1_000) return (n / 1_000).toFixed(1) + \"k\";\n return String(n);\n}\n","// ============================================================\n// Project Visualizer\n// Scans the project and generates an interactive HTML dashboard\n// ============================================================\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { scanProject, type ProjectGraph } from \"./scanner.js\";\nimport { generateDashboardHTML } from \"./template.js\";\n\nexport { type ProjectGraph, type ProjectFile } from \"./scanner.js\";\n\nexport interface VisualizerOptions {\n workingDir?: string;\n outputPath?: string;\n open?: boolean;\n}\n\nexport async function generateVisualization(\n options: VisualizerOptions = {}\n): Promise<{ outputPath: string; graph: ProjectGraph }> {\n const workingDir = options.workingDir || process.cwd();\n const outputPath = options.outputPath || path.join(workingDir, \".forge\", \"project-map.html\");\n\n // Scan project\n const graph = await scanProject(workingDir);\n\n // Load previous snapshot for diff mode\n let prevGraph: ProjectGraph | null = null;\n const snapshotPath = path.join(workingDir, \".forge\", \"viz-snapshot.json\");\n try {\n const raw = await fs.readFile(snapshotPath, \"utf-8\");\n prevGraph = JSON.parse(raw);\n } catch {\n // No previous snapshot\n }\n\n // Detect project name\n let projectName = path.basename(workingDir);\n try {\n const pkgPath = path.join(workingDir, \"package.json\");\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf-8\"));\n if (pkg.name) projectName = pkg.name;\n } catch {\n try {\n const pubPath = path.join(workingDir, \"pubspec.yaml\");\n const content = await fs.readFile(pubPath, \"utf-8\");\n const nameMatch = content.match(/^name:\\s*(.+)/m);\n if (nameMatch) projectName = nameMatch[1].trim();\n } catch {\n // Use directory name\n }\n }\n\n // Generate HTML\n const resolvedRoot = path.resolve(workingDir);\n const html = generateDashboardHTML(graph, projectName, prevGraph, resolvedRoot);\n\n // Write to .forge/ directory\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html);\n\n // Save current snapshot (strip preview to save space)\n const snapshot = JSON.parse(JSON.stringify(graph));\n for (const f of snapshot.files) delete f.preview;\n await fs.writeFile(snapshotPath, JSON.stringify(snapshot));\n\n // Open in browser\n if (options.open !== false) {\n await openBrowser(outputPath);\n }\n\n return { outputPath, graph };\n}\n\nasync function openBrowser(filePath: string): Promise<void> {\n const { exec } = await import(\"child_process\");\n\n const url = `file://${path.resolve(filePath)}`;\n\n const platform = process.platform;\n let cmd: string;\n\n if (platform === \"darwin\") {\n cmd = `open \"${url}\"`;\n } else if (platform === \"win32\") {\n cmd = `start \"\" \"${url}\"`;\n } else {\n cmd = `xdg-open \"${url}\"`;\n }\n\n exec(cmd, (err) => {\n // Silently ignore — browser open is best-effort\n if (err) {\n // Fallback: just print the path\n }\n });\n}\n","// ============================================================\n// Project Scanner\n// Recursively scans project files, extracts imports, exports,\n// routes, components, and builds a full dependency graph.\n// ============================================================\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\n// ── Types ─────────────────────────────────────────────────\n\nexport interface ProjectFile {\n /** Relative path from project root */\n path: string;\n /** File extension without dot */\n ext: string;\n /** File size in bytes */\n size: number;\n /** Auto-detected role */\n role: FileRole;\n /** Short description of what this file does */\n description: string;\n /** Imports this file makes (relative paths resolved) */\n imports: string[];\n /** Named exports from this file */\n exports: string[];\n /** API routes defined in this file */\n routes: RouteInfo[];\n /** Component names defined (React, Vue, Svelte) */\n components: string[];\n /** Category for grouping in the UI */\n category: FileCategory;\n /** Lines of code */\n lines: number;\n /** First 25 lines of file content */\n preview: string;\n /** Whether this file has a matching test file */\n hasTests: boolean;\n}\n\nexport type FileRole =\n | \"page\"\n | \"component\"\n | \"layout\"\n | \"api-route\"\n | \"middleware\"\n | \"hook\"\n | \"utility\"\n | \"model\"\n | \"service\"\n | \"config\"\n | \"style\"\n | \"test\"\n | \"asset\"\n | \"entry\"\n | \"store\"\n | \"type\"\n | \"unknown\";\n\nexport type FileCategory =\n | \"pages\"\n | \"components\"\n | \"api\"\n | \"data\"\n | \"config\"\n | \"styles\"\n | \"tests\"\n | \"assets\"\n | \"utilities\"\n | \"types\";\n\nexport interface RouteInfo {\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"ALL\";\n path: string;\n handler: string;\n}\n\nexport interface DependencyEdge {\n from: string;\n to: string;\n type: \"import\" | \"dynamic-import\" | \"route\";\n}\n\nexport interface ProjectGraph {\n files: ProjectFile[];\n edges: DependencyEdge[];\n framework: string;\n totalFiles: number;\n totalLines: number;\n languages: Record<string, number>;\n entryPoints: string[];\n apiRoutes: RouteInfo[];\n circularDeps: string[][];\n orphanFiles: string[];\n healthScore: {\n grade: string;\n score: number;\n details: {\n avgFileSize: number;\n testRatio: number;\n circularCount: number;\n orphanCount: number;\n maxComplexity: number;\n };\n };\n apiDocs: {\n method: string;\n path: string;\n handler: string;\n params: string[];\n description: string;\n }[];\n scanTimestamp: number;\n}\n\n// ── Ignore Patterns ───────────────────────────────────────\n\nconst IGNORE_DIRS = new Set([\n \"node_modules\", \".git\", \".next\", \".nuxt\", \".svelte-kit\",\n \"dist\", \"build\", \".output\", \"__pycache__\", \".venv\", \"venv\",\n \".forge\", \".github\", \".vscode\", \"coverage\", \".turbo\",\n \"android\", \"ios\", \".dart_tool\", \".idea\",\n]);\n\nconst IGNORE_FILES = new Set([\n \".DS_Store\", \"Thumbs.db\", \"package-lock.json\", \"yarn.lock\",\n \"pnpm-lock.yaml\", \".gitignore\", \".eslintcache\",\n]);\n\nconst CODE_EXTENSIONS = new Set([\n \"ts\", \"tsx\", \"js\", \"jsx\", \"mjs\", \"cjs\",\n \"vue\", \"svelte\",\n \"py\", \"dart\",\n \"css\", \"scss\", \"less\", \"sass\",\n \"json\", \"yaml\", \"yml\", \"toml\",\n \"html\",\n]);\n\n// ── Scanner ───────────────────────────────────────────────\n\nexport async function scanProject(rootDir: string): Promise<ProjectGraph> {\n const files: ProjectFile[] = [];\n const edges: DependencyEdge[] = [];\n const languages: Record<string, number> = {};\n\n await walkDir(rootDir, rootDir, files);\n\n // Build import edges\n for (const file of files) {\n for (const imp of file.imports) {\n edges.push({ from: file.path, to: imp, type: \"import\" });\n }\n }\n\n // Aggregate\n const totalLines = files.reduce((sum, f) => sum + f.lines, 0);\n for (const file of files) {\n const lang = extToLanguage(file.ext);\n languages[lang] = (languages[lang] || 0) + file.lines;\n }\n\n const entryPoints = files\n .filter((f) => f.role === \"entry\" || f.role === \"page\")\n .map((f) => f.path);\n\n const apiRoutes = files.flatMap((f) => f.routes);\n\n // Detect framework\n const framework = detectFramework(files);\n\n // Analyze project structure\n const circularDeps = detectCircularDeps(files, edges);\n const orphans = findOrphanFiles(files, edges);\n matchTestFiles(files);\n const apiDocs = extractApiDocs(files);\n const healthScore = computeHealthScore(files, edges, circularDeps, orphans);\n\n return {\n files,\n edges,\n framework,\n totalFiles: files.length,\n totalLines,\n languages,\n entryPoints,\n apiRoutes,\n circularDeps,\n orphanFiles: orphans,\n healthScore,\n apiDocs,\n scanTimestamp: Date.now(),\n };\n}\n\n// ── Analysis Functions ────────────────────────────────────\n\nfunction detectCircularDeps(files: ProjectFile[], edges: DependencyEdge[]): string[][] {\n const adj: Record<string, string[]> = {};\n for (const e of edges) {\n if (!adj[e.from]) adj[e.from] = [];\n adj[e.from].push(e.to);\n }\n\n const cycles: string[][] = [];\n const visited = new Set<string>();\n const stack = new Set<string>();\n const pathArr: string[] = [];\n\n function dfs(node: string) {\n if (cycles.length >= 50) return;\n if (stack.has(node)) {\n const idx = pathArr.indexOf(node);\n if (idx !== -1) cycles.push(pathArr.slice(idx));\n return;\n }\n if (visited.has(node)) return;\n visited.add(node);\n stack.add(node);\n pathArr.push(node);\n\n // Normalize adjacency lookups\n const neighbors = adj[node] || [];\n for (const next of neighbors) {\n // Try to resolve to actual file path\n const resolved = files.find(f => {\n const fN = f.path.replace(/\\.(ts|tsx|js|jsx)$/, '');\n const nN = next.replace(/\\.(ts|tsx|js|jsx)$/, '');\n return fN === nN || fN.endsWith('/' + nN) || nN.endsWith('/' + fN);\n });\n dfs(resolved ? resolved.path : next);\n }\n\n pathArr.pop();\n stack.delete(node);\n }\n\n for (const f of files) {\n dfs(f.path);\n }\n\n return cycles;\n}\n\nfunction findOrphanFiles(files: ProjectFile[], edges: DependencyEdge[]): string[] {\n const hasOutgoing = new Set(edges.map(e => e.from));\n const hasIncoming = new Set<string>();\n\n for (const e of edges) {\n // Try to match \"to\" against actual file paths\n for (const f of files) {\n const fN = f.path.replace(/\\.(ts|tsx|js|jsx)$/, '');\n const eN = e.to.replace(/\\.(ts|tsx|js|jsx)$/, '');\n if (fN === eN || fN.endsWith('/' + eN) || eN.endsWith('/' + fN)) {\n hasIncoming.add(f.path);\n break;\n }\n }\n }\n\n return files\n .filter(f => !hasOutgoing.has(f.path) && !hasIncoming.has(f.path))\n .filter(f => f.role !== 'config' && f.role !== 'asset' && f.role !== 'style') // exclude non-code files\n .map(f => f.path);\n}\n\nfunction matchTestFiles(files: ProjectFile[]): void {\n const testFiles = files.filter(f => f.role === 'test');\n const testTargets = new Set<string>();\n\n for (const tf of testFiles) {\n // Extract target name: \"Button.test.tsx\" -> \"Button\"\n const name = path.basename(tf.path)\n .replace(/\\.(test|spec)\\.(ts|tsx|js|jsx|py)$/, '');\n testTargets.add(name);\n }\n\n for (const f of files) {\n if (f.role === 'test') continue;\n const name = path.basename(f.path).replace(/\\.(ts|tsx|js|jsx|py|dart|vue|svelte)$/, '');\n f.hasTests = testTargets.has(name);\n }\n}\n\nfunction extractApiDocs(files: ProjectFile[]): ProjectGraph['apiDocs'] {\n const docs: ProjectGraph['apiDocs'] = [];\n for (const f of files) {\n for (const r of f.routes) {\n const params = (r.path.match(/:(\\w+)/g) || []).map(p => p.slice(1));\n docs.push({\n method: r.method,\n path: r.path,\n handler: r.handler,\n params,\n description: f.description,\n });\n }\n }\n return docs;\n}\n\nfunction computeHealthScore(\n files: ProjectFile[],\n edges: DependencyEdge[],\n circularDeps: string[][],\n orphans: string[]\n): ProjectGraph['healthScore'] {\n const totalFiles = files.length;\n if (totalFiles === 0) {\n return { grade: 'N/A', score: 0, details: { avgFileSize: 0, testRatio: 0, circularCount: 0, orphanCount: 0, maxComplexity: 0 } };\n }\n\n const avgFileSize = files.reduce((s, f) => s + f.lines, 0) / totalFiles;\n const testFileCount = files.filter(f => f.role === 'test').length;\n const nonTestFiles = files.filter(f => f.role !== 'test' && f.role !== 'config' && f.role !== 'style' && f.role !== 'asset').length;\n const testRatio = nonTestFiles > 0 ? testFileCount / nonTestFiles : 0;\n const circularCount = circularDeps.length;\n const orphanCount = orphans.length;\n\n // Max file complexity (lines * imports)\n const maxComplexity = Math.max(...files.map(f => {\n const deps = edges.filter(e => e.from === f.path).length;\n return f.lines + deps * 20;\n }), 0);\n\n // Score calculation (0-100)\n let score = 100;\n\n // Penalize large average file size (>200 lines = penalty)\n if (avgFileSize > 200) score -= Math.min(20, (avgFileSize - 200) / 20);\n\n // Reward test coverage (0 tests = -25, >50% = full)\n score -= Math.max(0, 25 - testRatio * 50);\n\n // Penalize circular deps (-5 each, max -20)\n score -= Math.min(20, circularCount * 5);\n\n // Penalize orphans (-2 each, max -15)\n score -= Math.min(15, orphanCount * 2);\n\n // Penalize very complex files\n if (maxComplexity > 500) score -= Math.min(10, (maxComplexity - 500) / 100);\n\n score = Math.max(0, Math.min(100, Math.round(score)));\n\n let grade: string;\n if (score >= 90) grade = 'A';\n else if (score >= 80) grade = 'B';\n else if (score >= 70) grade = 'C';\n else if (score >= 60) grade = 'D';\n else grade = 'F';\n\n return {\n grade,\n score,\n details: {\n avgFileSize: Math.round(avgFileSize),\n testRatio: Math.round(testRatio * 100) / 100,\n circularCount,\n orphanCount,\n maxComplexity: Math.round(maxComplexity),\n },\n };\n}\n\n// ── Directory Walker ──────────────────────────────────────\n\nasync function walkDir(\n dir: string,\n rootDir: string,\n files: ProjectFile[]\n): Promise<void> {\n let entries;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(rootDir, fullPath).replace(/\\\\/g, \"/\");\n\n if (entry.isDirectory()) {\n if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(\".\")) {\n await walkDir(fullPath, rootDir, files);\n }\n continue;\n }\n\n if (IGNORE_FILES.has(entry.name)) continue;\n\n const ext = entry.name.split(\".\").pop()?.toLowerCase() || \"\";\n if (!CODE_EXTENSIONS.has(ext)) continue;\n\n try {\n const stat = await fs.stat(fullPath);\n // Skip very large files (likely generated)\n if (stat.size > 500_000) continue;\n\n const content = await fs.readFile(fullPath, \"utf-8\");\n const lines = content.split(\"\\n\").length;\n const preview = content.split(\"\\n\").slice(0, 25).join(\"\\n\");\n\n const imports = extractImports(content, relativePath, ext);\n const exports = extractExports(content, ext);\n const routes = extractRoutes(content, relativePath, ext);\n const components = extractComponents(content, ext);\n const role = detectRole(relativePath, content, ext);\n const category = roleToCategory(role);\n const description = generateDescription(relativePath, role, content, ext, components, routes, exports);\n\n files.push({\n path: relativePath,\n ext,\n size: stat.size,\n role,\n description,\n imports,\n exports,\n routes,\n components,\n category,\n lines,\n preview,\n hasTests: false,\n });\n } catch {\n // Skip unreadable files\n }\n }\n}\n\n// ── Import Extraction ─────────────────────────────────────\n\nfunction extractImports(content: string, filePath: string, ext: string): string[] {\n const imports: string[] = [];\n const dir = path.dirname(filePath);\n\n if ([\"ts\", \"tsx\", \"js\", \"jsx\", \"mjs\", \"cjs\", \"svelte\", \"vue\"].includes(ext)) {\n // ES imports: import X from \"...\"\n const esImportRe = /import\\s+(?:[\\s\\S]*?)\\s+from\\s+[\"']([^\"']+)[\"']/g;\n let match;\n while ((match = esImportRe.exec(content)) !== null) {\n const spec = match[1];\n if (spec.startsWith(\".\")) {\n imports.push(resolveImportPath(dir, spec));\n }\n }\n\n // Side-effect imports: import \"...\"\n const sideRe = /import\\s+[\"']([^\"']+)[\"']/g;\n while ((match = sideRe.exec(content)) !== null) {\n const spec = match[1];\n if (spec.startsWith(\".\")) {\n imports.push(resolveImportPath(dir, spec));\n }\n }\n\n // Dynamic imports: import(\"...\")\n const dynRe = /import\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\n while ((match = dynRe.exec(content)) !== null) {\n const spec = match[1];\n if (spec.startsWith(\".\")) {\n imports.push(resolveImportPath(dir, spec));\n }\n }\n\n // require(\"...\")\n const reqRe = /require\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\n while ((match = reqRe.exec(content)) !== null) {\n const spec = match[1];\n if (spec.startsWith(\".\")) {\n imports.push(resolveImportPath(dir, spec));\n }\n }\n }\n\n if (ext === \"py\") {\n // Python: from X import Y, import X\n const pyFromRe = /from\\s+(\\.[\\w.]*)\\s+import/g;\n let match;\n while ((match = pyFromRe.exec(content)) !== null) {\n imports.push(match[1].replace(/\\./g, \"/\"));\n }\n }\n\n if (ext === \"dart\") {\n // Dart: import 'package:...' or import '../...'\n const dartRe = /import\\s+['\"](?:package:[^'\"]+\\/)?([^'\"]+)['\"]/g;\n let match;\n while ((match = dartRe.exec(content)) !== null) {\n if (!match[1].startsWith(\"dart:\")) {\n imports.push(match[1]);\n }\n }\n }\n\n return [...new Set(imports)];\n}\n\nfunction resolveImportPath(fromDir: string, specifier: string): string {\n // Remove extensions for matching\n let resolved = path.posix.join(fromDir, specifier).replace(/\\\\/g, \"/\");\n // Remove leading ./\n if (resolved.startsWith(\"./\")) resolved = resolved.slice(2);\n return resolved;\n}\n\n// ── Export Extraction ─────────────────────────────────────\n\nfunction extractExports(content: string, ext: string): string[] {\n const exports: string[] = [];\n\n if ([\"ts\", \"tsx\", \"js\", \"jsx\", \"mjs\", \"svelte\", \"vue\"].includes(ext)) {\n // export function/class/const/let\n const namedRe = /export\\s+(?:async\\s+)?(?:function|class|const|let|var|type|interface)\\s+(\\w+)/g;\n let match;\n while ((match = namedRe.exec(content)) !== null) {\n exports.push(match[1]);\n }\n\n // export default\n if (/export\\s+default/.test(content)) {\n exports.push(\"default\");\n }\n }\n\n if (ext === \"py\") {\n // Python: class definitions and top-level functions\n const pyRe = /^(?:class|def)\\s+(\\w+)/gm;\n let match;\n while ((match = pyRe.exec(content)) !== null) {\n exports.push(match[1]);\n }\n }\n\n return exports;\n}\n\n// ── Route Extraction ──────────────────────────────────────\n\nfunction extractRoutes(content: string, filePath: string, ext: string): RouteInfo[] {\n const routes: RouteInfo[] = [];\n\n // Next.js App Router API routes (route.ts)\n if (filePath.includes(\"api/\") && (filePath.endsWith(\"route.ts\") || filePath.endsWith(\"route.js\"))) {\n const methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"] as const;\n for (const method of methods) {\n const re = new RegExp(`export\\\\s+(?:async\\\\s+)?function\\\\s+${method}\\\\b`);\n if (re.test(content)) {\n const routePath = \"/\" + filePath\n .replace(/^src\\/app\\//, \"\")\n .replace(/\\/route\\.(ts|js)$/, \"\")\n .replace(/\\[(\\w+)\\]/g, \":$1\");\n routes.push({ method, path: routePath, handler: filePath });\n }\n }\n }\n\n // Nuxt server routes (server/api/*.ts)\n if (filePath.startsWith(\"server/api/\") && [\"ts\", \"js\"].includes(ext)) {\n const routePath = \"/\" + filePath\n .replace(/^server\\//, \"\")\n .replace(/\\.(ts|js)$/, \"\")\n .replace(/\\[(\\w+)\\]/g, \":$1\");\n routes.push({ method: \"ALL\", path: routePath, handler: filePath });\n }\n\n // SvelteKit API routes (+server.ts)\n if (filePath.endsWith(\"+server.ts\") || filePath.endsWith(\"+server.js\")) {\n const methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"] as const;\n for (const method of methods) {\n const re = new RegExp(`export\\\\s+(?:async\\\\s+)?function\\\\s+${method}\\\\b`);\n if (re.test(content)) {\n const routePath = \"/\" + filePath\n .replace(/^src\\/routes\\//, \"\")\n .replace(/\\/\\+server\\.(ts|js)$/, \"\")\n .replace(/\\[(\\w+)\\]/g, \":$1\");\n routes.push({ method, path: routePath, handler: filePath });\n }\n }\n }\n\n // Express-style: app.get(\"/path\", handler)\n const expressRe = /(?:app|router)\\.(get|post|put|delete|patch|all)\\(\\s*[\"']([^\"']+)[\"']/gi;\n let match;\n while ((match = expressRe.exec(content)) !== null) {\n routes.push({\n method: match[1].toUpperCase() as RouteInfo[\"method\"],\n path: match[2],\n handler: filePath,\n });\n }\n\n // Django urls (path(\"...\", view))\n if (ext === \"py\" && filePath.includes(\"urls\")) {\n const djangoRe = /path\\(\\s*[\"']([^\"']+)[\"']\\s*,\\s*(\\w+)/g;\n while ((match = djangoRe.exec(content)) !== null) {\n routes.push({ method: \"ALL\", path: \"/\" + match[1], handler: match[2] });\n }\n }\n\n return routes;\n}\n\n// ── Component Extraction ──────────────────────────────────\n\nfunction extractComponents(content: string, ext: string): string[] {\n const components: string[] = [];\n\n if ([\"tsx\", \"jsx\"].includes(ext)) {\n // React: export function ComponentName or export default function\n const fnRe = /export\\s+(?:default\\s+)?function\\s+([A-Z]\\w+)/g;\n let match;\n while ((match = fnRe.exec(content)) !== null) {\n components.push(match[1]);\n }\n // const ComponentName = () =>\n const arrowRe = /(?:export\\s+)?(?:const|let)\\s+([A-Z]\\w+)\\s*=\\s*(?:\\([^)]*\\)|[^=])\\s*=>/g;\n while ((match = arrowRe.exec(content)) !== null) {\n components.push(match[1]);\n }\n }\n\n if (ext === \"vue\") {\n // Vue SFC — the component name is typically the filename\n components.push(\"VueComponent\");\n }\n\n if (ext === \"svelte\") {\n components.push(\"SvelteComponent\");\n }\n\n if (ext === \"dart\") {\n // Flutter widgets\n const dartRe = /class\\s+(\\w+)\\s+extends\\s+(?:Stateless|Stateful)Widget/g;\n let match;\n while ((match = dartRe.exec(content)) !== null) {\n components.push(match[1]);\n }\n }\n\n return [...new Set(components)];\n}\n\n// ── Role Detection ────────────────────────────────────────\n\nfunction detectRole(filePath: string, content: string, ext: string): FileRole {\n const lowerPath = filePath.toLowerCase();\n const fileName = path.basename(filePath);\n\n // Config files\n if ([\"json\", \"yaml\", \"yml\", \"toml\"].includes(ext) || fileName.startsWith(\".\")) {\n return \"config\";\n }\n if (lowerPath.includes(\"config\") || lowerPath.includes(\"tsconfig\") || lowerPath.endsWith(\".config.ts\") || lowerPath.endsWith(\".config.js\")) {\n return \"config\";\n }\n\n // Tests\n if (lowerPath.includes(\".test.\") || lowerPath.includes(\".spec.\") || lowerPath.includes(\"__tests__\") || lowerPath.startsWith(\"test/\") || lowerPath.startsWith(\"tests/\")) {\n return \"test\";\n }\n\n // Styles\n if ([\"css\", \"scss\", \"less\", \"sass\"].includes(ext)) {\n return \"style\";\n }\n\n // Types\n if (lowerPath.includes(\"types\") || lowerPath.endsWith(\".d.ts\")) {\n return \"type\";\n }\n\n // API routes\n if (lowerPath.includes(\"/api/\") || lowerPath.includes(\"route.ts\") || lowerPath.includes(\"+server.\")) {\n return \"api-route\";\n }\n\n // Pages\n if (lowerPath.includes(\"/pages/\") || lowerPath.includes(\"/routes/\") || lowerPath.includes(\"+page.\")) {\n return \"page\";\n }\n\n // Layouts\n if (lowerPath.includes(\"layout\") || lowerPath.includes(\"+layout.\")) {\n return \"layout\";\n }\n\n // Middleware\n if (lowerPath.includes(\"middleware\") || lowerPath.includes(\"hooks\")) {\n return \"middleware\";\n }\n\n // Entry points\n if (fileName === \"main.ts\" || fileName === \"main.tsx\" || fileName === \"main.dart\" || fileName === \"index.ts\" || fileName === \"app.tsx\" || fileName === \"app.vue\" || fileName === \"manage.py\") {\n return \"entry\";\n }\n\n // Hooks / composables\n if (lowerPath.includes(\"/hooks/\") || lowerPath.includes(\"/composables/\") || fileName.startsWith(\"use\")) {\n return \"hook\";\n }\n\n // Models / schemas\n if (lowerPath.includes(\"/models/\") || lowerPath.includes(\"schema\") || lowerPath.includes(\"prisma\")) {\n return \"model\";\n }\n\n // Services / repositories\n if (lowerPath.includes(\"/services/\") || lowerPath.includes(\"/repositories/\") || lowerPath.includes(\"/lib/\") || lowerPath.includes(\"/server/\")) {\n return \"service\";\n }\n\n // Store / state\n if (lowerPath.includes(\"/store/\") || lowerPath.includes(\"/providers/\") || lowerPath.includes(\"context\")) {\n return \"store\";\n }\n\n // Components\n if (lowerPath.includes(\"/components/\") || lowerPath.includes(\"/widgets/\") || lowerPath.includes(\"/ui/\")) {\n return \"component\";\n }\n\n // TSX/JSX with PascalCase = likely a component\n if ([\"tsx\", \"jsx\"].includes(ext) && /^[A-Z]/.test(fileName)) {\n return \"component\";\n }\n\n // Commands (CLI)\n if (lowerPath.includes(\"/commands/\") || lowerPath.includes(\"/cmd/\")) {\n return \"utility\";\n }\n\n // Utilities\n if (lowerPath.includes(\"/utils/\") || lowerPath.includes(\"/helpers/\") || lowerPath.includes(\"/lib/\")) {\n return \"utility\";\n }\n\n // Any TS/JS/PY/Dart source file defaults to utility rather than unknown\n if ([\"ts\", \"tsx\", \"js\", \"jsx\", \"py\", \"dart\"].includes(ext)) {\n return \"utility\";\n }\n\n return \"unknown\";\n}\n\nfunction roleToCategory(role: FileRole): FileCategory {\n switch (role) {\n case \"page\":\n case \"layout\":\n case \"entry\":\n return \"pages\";\n case \"component\":\n return \"components\";\n case \"api-route\":\n case \"middleware\":\n return \"api\";\n case \"model\":\n case \"service\":\n case \"store\":\n case \"hook\":\n return \"data\";\n case \"config\":\n return \"config\";\n case \"style\":\n return \"styles\";\n case \"test\":\n return \"tests\";\n case \"asset\":\n return \"assets\";\n case \"type\":\n return \"types\";\n case \"utility\":\n return \"utilities\";\n default:\n return \"utilities\";\n }\n}\n\n// ── Description Generation ────────────────────────────────\n\nfunction generateDescription(\n filePath: string,\n role: FileRole,\n content: string,\n ext: string,\n components: string[],\n routes: RouteInfo[],\n exports: string[]\n): string {\n const fileName = path.basename(filePath);\n\n // Config files\n if (role === \"config\") {\n if (fileName === \"package.json\") return \"Project dependencies and scripts\";\n if (fileName === \"tsconfig.json\") return \"TypeScript compiler configuration\";\n if (fileName.includes(\"tailwind\")) return \"Tailwind CSS theme and plugin configuration\";\n if (fileName.includes(\"next.config\")) return \"Next.js framework configuration\";\n if (fileName.includes(\"nuxt.config\")) return \"Nuxt 3 framework configuration\";\n if (fileName.includes(\"svelte.config\")) return \"SvelteKit framework configuration\";\n if (fileName.includes(\"vite.config\")) return \"Vite bundler configuration\";\n if (fileName === \"pubspec.yaml\") return \"Dart/Flutter dependencies\";\n if (fileName === \"requirements.txt\") return \"Python dependencies\";\n return \"Configuration file\";\n }\n\n // Tests\n if (role === \"test\") {\n const testTarget = fileName.replace(/\\.(test|spec)\\.(ts|tsx|js|jsx)$/, \"\");\n return `Tests for ${testTarget}`;\n }\n\n // Styles\n if (role === \"style\") {\n if (fileName.includes(\"global\") || fileName === \"app.css\") return \"Global styles and CSS variables\";\n return `Styles for ${fileName.replace(/\\.(css|scss|less)$/, \"\")}`;\n }\n\n // API routes\n if (routes.length > 0) {\n const methods = routes.map((r) => `${r.method} ${r.path}`).join(\", \");\n return `API endpoint: ${methods}`;\n }\n\n // Components\n if (components.length > 0) {\n return `${components[0]} component${components.length > 1 ? ` (+${components.length - 1} more)` : \"\"}`;\n }\n\n // Pages\n if (role === \"page\") {\n const route = filePath\n .replace(/^src\\/(app|routes|pages)\\//, \"/\")\n .replace(/\\/?(page|index|\\+page)\\.(tsx?|jsx?|vue|svelte)$/, \"\")\n .replace(/\\[(\\w+)\\]/g, \":$1\") || \"/\";\n return `Page: ${route}`;\n }\n\n // Layout\n if (role === \"layout\") return \"Layout wrapper — shared UI shell for child routes\";\n\n // Entry\n if (role === \"entry\") return \"Application entry point\";\n\n // Hooks\n if (role === \"hook\") {\n const hookName = exports.find((e) => e.startsWith(\"use\")) || fileName.replace(/\\.(ts|js)$/, \"\");\n return `Custom hook: ${hookName}`;\n }\n\n // Models\n if (role === \"model\") {\n const modelNames = exports.filter((e) => e !== \"default\").slice(0, 3).join(\", \");\n return modelNames ? `Data model: ${modelNames}` : \"Data model definitions\";\n }\n\n // Services\n if (role === \"service\") {\n return `Service: ${fileName.replace(/\\.(ts|js|py)$/, \"\")}`;\n }\n\n // Store\n if (role === \"store\") return \"State management / data store\";\n\n // Middleware\n if (role === \"middleware\") return \"Request middleware / interceptor\";\n\n // Types\n if (role === \"type\") {\n const typeNames = exports.filter((e) => e !== \"default\").slice(0, 3).join(\", \");\n return typeNames ? `Type definitions: ${typeNames}` : \"Type definitions\";\n }\n\n // Utilities\n if (role === \"utility\") {\n const utilNames = exports.filter((e) => e !== \"default\").slice(0, 3).join(\", \");\n return utilNames ? `Utilities: ${utilNames}` : \"Utility functions\";\n }\n\n // Generic\n return `${fileName.replace(/\\.(ts|tsx|js|jsx|py|dart)$/, \"\")} module`;\n}\n\n// ── Framework Detection ───────────────────────────────────\n\nfunction detectFramework(files: ProjectFile[]): string {\n const paths = new Set(files.map((f) => f.path));\n\n if (paths.has(\"next.config.ts\") || paths.has(\"next.config.js\") || paths.has(\"next.config.mjs\")) return \"Next.js\";\n if (paths.has(\"nuxt.config.ts\") || paths.has(\"nuxt.config.js\")) return \"Nuxt 3\";\n if (paths.has(\"svelte.config.js\") || paths.has(\"svelte.config.ts\")) return \"SvelteKit\";\n if (paths.has(\"pubspec.yaml\")) return \"Flutter\";\n if (paths.has(\"manage.py\")) return \"Django\";\n if (paths.has(\"vite.config.ts\") || paths.has(\"vite.config.js\")) return \"Vite\";\n if (paths.has(\"Cargo.toml\")) return \"Rust\";\n if (paths.has(\"go.mod\")) return \"Go\";\n\n return \"Unknown\";\n}\n\nfunction extToLanguage(ext: string): string {\n switch (ext) {\n case \"ts\":\n case \"tsx\":\n return \"TypeScript\";\n case \"js\":\n case \"jsx\":\n case \"mjs\":\n case \"cjs\":\n return \"JavaScript\";\n case \"py\":\n return \"Python\";\n case \"dart\":\n return \"Dart\";\n case \"vue\":\n return \"Vue\";\n case \"svelte\":\n return \"Svelte\";\n case \"css\":\n case \"scss\":\n case \"less\":\n case \"sass\":\n return \"CSS\";\n case \"html\":\n return \"HTML\";\n case \"json\":\n return \"JSON\";\n case \"yaml\":\n case \"yml\":\n return \"YAML\";\n case \"md\":\n case \"mdx\":\n return \"Markdown\";\n default:\n return ext.toUpperCase();\n }\n}\n","// ============================================================\n// Dashboard HTML Template\n// Self-contained interactive project visualizer\n// No external dependencies — everything is inline\n// ============================================================\n\nimport type { ProjectGraph } from \"./scanner.js\";\n\nexport function generateDashboardHTML(graph: ProjectGraph, projectName: string, prevGraph?: ProjectGraph | null, projectRoot?: string): string {\n const dataJson = JSON.stringify(graph);\n const prevDataJson = prevGraph ? JSON.stringify(prevGraph) : 'null';\n const rootPath = projectRoot ? escapeHtml(projectRoot) : '';\n const hs = graph.healthScore;\n const gradeColor = hs.grade === 'A' ? '#34d399' : hs.grade === 'B' ? '#60a5fa' : hs.grade === 'C' ? '#fbbf24' : hs.grade === 'D' ? '#fb923c' : hs.grade === 'F' ? '#f87171' : '#6b7280';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>${escapeHtml(projectName)} — Project Map</title>\n<style>\n${CSS}\n</style>\n</head>\n<body>\n<div id=\"app\">\n <!-- Header -->\n <header id=\"header\">\n <div class=\"header-left\">\n <h1>${escapeHtml(projectName)}</h1>\n <span class=\"badge\">${graph.framework}</span>\n <span class=\"health-badge\" style=\"background:${gradeColor}20;color:${gradeColor};border:1px solid ${gradeColor}40\" title=\"Health Score: ${hs.score}/100\">${hs.grade}</span>\n <span class=\"divider\"></span>\n <span class=\"stat\">${graph.totalFiles} files</span>\n <span class=\"stat\">${formatNumber(graph.totalLines)} lines</span>\n </div>\n <div class=\"header-right\">\n <div class=\"search-box\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/></svg>\n <input type=\"text\" id=\"search\" placeholder=\"Search files... ( / )\" autocomplete=\"off\">\n </div>\n <div class=\"view-toggles\">\n <button class=\"view-btn active\" data-view=\"overview\">Overview</button>\n <button class=\"view-btn\" data-view=\"architecture\">Architecture</button>\n <button class=\"view-btn\" data-view=\"map\">System Map</button>\n <button class=\"view-btn\" data-view=\"list\">Files</button>${graph.apiDocs.length > 0 ? `\n <button class=\"view-btn\" data-view=\"apidocs\">API Docs</button>` : ''}${prevGraph ? `\n <button class=\"view-btn\" data-view=\"diff\">Diff</button>` : ''}\n </div>\n </div>\n </header>\n\n <!-- Language Bar -->\n <div id=\"lang-bar\">\n ${Object.entries(graph.languages)\n .sort((a, b) => b[1] - a[1])\n .map(([lang, lines]) => {\n const pct = ((lines / graph.totalLines) * 100).toFixed(1);\n return `<div class=\"lang-segment\" style=\"flex:${lines}\" title=\"${lang}: ${formatNumber(lines)} lines (${pct}%)\">\n <span class=\"lang-label\">${lang} ${pct}%</span>\n </div>`;\n })\n .join(\"\")}\n </div>\n\n <!-- Main Content -->\n <div id=\"content\">\n <!-- Sidebar -->\n <aside id=\"sidebar\">\n <div class=\"sidebar-section\" id=\"category-tree\"></div>\n ${graph.apiRoutes.length > 0 ? `\n <div class=\"sidebar-section\">\n <h3>API Routes</h3>\n <div id=\"api-routes\">\n ${graph.apiRoutes.map((r) => `\n <div class=\"route-item\">\n <span class=\"method method-${r.method.toLowerCase()}\">${r.method}</span>\n <span class=\"route-path\">${escapeHtml(r.path)}</span>\n </div>\n `).join(\"\")}\n </div>\n </div>` : \"\"}\n </aside>\n\n <!-- Main Panel -->\n <main id=\"main-panel\">\n <!-- Overview -->\n <div id=\"overview-view\" class=\"view active-view\">\n <div id=\"overview-container\"></div>\n </div>\n\n <!-- Architecture -->\n <div id=\"architecture-view\" class=\"view\">\n <div id=\"arch-container\"></div>\n </div>\n\n <!-- System Map -->\n <div id=\"map-view\" class=\"view\">\n <div id=\"sysmap-container\"></div>\n </div>\n\n <!-- File List -->\n <div id=\"list-view\" class=\"view\">\n <div id=\"file-list\"></div>\n </div>\n\n <!-- API Docs -->\n <div id=\"apidocs-view\" class=\"view\">\n <div id=\"apidocs-container\"></div>\n </div>\n\n <!-- Diff View -->\n <div id=\"diff-view\" class=\"view\">\n <div id=\"diff-container\"></div>\n </div>\n </main>\n\n <!-- Detail Panel -->\n <aside id=\"detail-panel\" class=\"hidden\">\n <button id=\"close-detail\">×</button>\n <div id=\"detail-content\"></div>\n </aside>\n </div>\n</div>\n\n<script>\nconst DATA = ${dataJson};\nconst PREV = ${prevDataJson};\nconst PROJECT_ROOT = '${rootPath}';\n${JS}\n</script>\n</body>\n</html>`;\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\n\nfunction formatNumber(n: number): string {\n if (n >= 1000) return (n / 1000).toFixed(1) + \"k\";\n return String(n);\n}\n\n// ── Inline CSS ────────────────────────────────────────────\n\nconst CSS = `\n:root {\n --bg: #0d1117;\n --surface: #161b22;\n --surface-hover: #1c2128;\n --surface-raised: #1f2937;\n --border: #21262d;\n --border-light: #30363d;\n --text: #e6edf3;\n --text-secondary: #9ca3af;\n --text-muted: #6b7280;\n --accent: #60a5fa;\n --accent-dim: rgba(96,165,250,0.12);\n --green: #34d399;\n --green-dim: rgba(52,211,153,0.12);\n --yellow: #fbbf24;\n --yellow-dim: rgba(251,191,36,0.12);\n --red: #f87171;\n --red-dim: rgba(248,113,113,0.12);\n --purple: #a78bfa;\n --purple-dim: rgba(167,139,250,0.12);\n --orange: #fb923c;\n --cyan: #22d3ee;\n --radius: 6px;\n --radius-lg: 10px;\n}\n\n* { margin: 0; padding: 0; box-sizing: border-box; }\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n -webkit-font-smoothing: antialiased;\n}\n\n#app { display: flex; flex-direction: column; height: 100vh; }\n\n/* Header */\n#header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 24px;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n flex-shrink: 0;\n}\n.header-left { display: flex; align-items: center; gap: 12px; }\n.header-right { display: flex; align-items: center; gap: 16px; }\nh1 { font-size: 15px; font-weight: 600; letter-spacing: -0.2px; }\n.badge {\n background: var(--accent-dim);\n color: var(--accent);\n padding: 3px 10px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.3px;\n text-transform: uppercase;\n}\n.divider { width: 1px; height: 16px; background: var(--border-light); }\n.stat { color: var(--text-muted); font-size: 12px; font-weight: 500; }\n\n.search-box {\n display: flex;\n align-items: center;\n gap: 8px;\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 6px 12px;\n color: var(--text-muted);\n transition: border-color .15s;\n}\n.search-box:focus-within { border-color: var(--accent); }\n.search-box input {\n background: none;\n border: none;\n color: var(--text);\n font-size: 13px;\n outline: none;\n width: 180px;\n font-family: inherit;\n}\n.search-box input::placeholder { color: var(--text-muted); }\n\n.view-toggles {\n display: flex;\n gap: 1px;\n background: var(--border);\n border-radius: var(--radius);\n padding: 1px;\n}\n.view-btn {\n background: var(--bg);\n border: none;\n color: var(--text-muted);\n padding: 6px 14px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 500;\n transition: all .15s;\n font-family: inherit;\n}\n.view-btn:first-child { border-radius: 5px 0 0 5px; }\n.view-btn:last-child { border-radius: 0 5px 5px 0; }\n.view-btn:hover { color: var(--text-secondary); }\n.view-btn.active { background: var(--surface); color: var(--text); }\n\n/* Language Bar */\n#lang-bar {\n display: flex;\n height: 3px;\n flex-shrink: 0;\n}\n.lang-segment {\n position: relative;\n min-width: 4px;\n cursor: pointer;\n transition: opacity .15s;\n}\n.lang-segment:nth-child(1) { background: var(--accent); }\n.lang-segment:nth-child(2) { background: var(--green); }\n.lang-segment:nth-child(3) { background: var(--yellow); }\n.lang-segment:nth-child(4) { background: var(--purple); }\n.lang-segment:nth-child(5) { background: var(--orange); }\n.lang-segment:nth-child(6) { background: var(--red); }\n.lang-segment:nth-child(n+7) { background: var(--text-muted); }\n.lang-label {\n display: none;\n position: absolute;\n top: 8px;\n left: 50%;\n transform: translateX(-50%);\n background: var(--surface-raised);\n border: 1px solid var(--border-light);\n padding: 4px 10px;\n border-radius: 4px;\n font-size: 11px;\n white-space: nowrap;\n color: var(--text);\n pointer-events: none;\n z-index: 10;\n}\n.lang-segment:hover .lang-label { display: block; }\n.lang-segment:hover { opacity: 0.8; }\n\n/* Content Layout */\n#content { display: flex; flex: 1; overflow: hidden; }\n\n/* Sidebar */\n#sidebar {\n width: 260px;\n background: var(--surface);\n border-right: 1px solid var(--border);\n overflow-y: auto;\n flex-shrink: 0;\n padding: 16px 0;\n}\n.sidebar-section { padding: 0 12px; margin-bottom: 20px; }\n.sidebar-section h3 {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n color: var(--text-muted);\n margin-bottom: 8px;\n padding: 0 8px;\n}\n.category-group { margin-bottom: 2px; }\n.category-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: var(--radius);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-secondary);\n user-select: none;\n transition: all .1s;\n}\n.category-header:hover { background: var(--surface-hover); color: var(--text); }\n.cat-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }\n.category-count {\n margin-left: auto;\n font-size: 11px;\n color: var(--text-muted);\n font-weight: 400;\n}\n.category-files { padding-left: 12px; }\n.file-item {\n display: block;\n padding: 4px 8px;\n border-radius: 4px;\n cursor: pointer;\n font-size: 11px;\n font-family: 'SF Mono', 'Fira Code', monospace;\n color: var(--text-muted);\n transition: all .1s;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.file-item:hover { background: var(--surface-hover); color: var(--text-secondary); }\n.file-item.selected { background: var(--accent-dim); color: var(--accent); }\n.file-item .file-dir { color: var(--text-muted); opacity: 0.6; }\n\n/* Route items */\n.route-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 8px;\n font-size: 12px;\n border-radius: 4px;\n}\n.route-item:hover { background: var(--surface-hover); }\n.method {\n font-size: 9px;\n font-weight: 700;\n font-family: 'SF Mono', 'Fira Code', monospace;\n padding: 2px 5px;\n border-radius: 3px;\n min-width: 34px;\n text-align: center;\n letter-spacing: 0.3px;\n}\n.method-get { background: var(--green-dim); color: var(--green); }\n.method-post { background: var(--accent-dim); color: var(--accent); }\n.method-put { background: var(--yellow-dim); color: var(--yellow); }\n.method-delete { background: var(--red-dim); color: var(--red); }\n.method-patch { background: var(--purple-dim); color: var(--purple); }\n.method-all { background: rgba(107,114,128,0.12); color: var(--text-muted); }\n.route-path { color: var(--text-muted); font-family: 'SF Mono', monospace; font-size: 11px; }\n\n/* Main Panel */\n#main-panel { flex: 1; position: relative; overflow: hidden; }\n.view { display: none; width: 100%; height: 100%; }\n.active-view { display: block; }\n\n/* Overview */\n#overview-container { padding: 32px; overflow: auto; height: 100%; }\n.overview-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));\n gap: 20px;\n margin-bottom: 28px;\n}\n.insight-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n padding: 20px 24px;\n}\n.insight-card h3 {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--text-muted);\n margin-bottom: 16px;\n}\n.insight-stat {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-bottom: 12px;\n}\n.insight-number {\n font-size: 28px;\n font-weight: 700;\n letter-spacing: -1px;\n}\n.insight-label { font-size: 13px; color: var(--text-muted); }\n.insight-bar-row {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 8px;\n font-size: 12px;\n}\n.insight-bar-name { width: 100px; color: var(--text-secondary); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.insight-bar-track { flex: 1; height: 6px; background: var(--bg); border-radius: 3px; overflow: hidden; }\n.insight-bar-fill { height: 100%; border-radius: 3px; transition: width .3s; }\n.insight-bar-val { width: 50px; color: var(--text-muted); font-size: 11px; }\n.insight-list-item {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid var(--border);\n font-size: 13px;\n}\n.insight-list-item:last-child { border-bottom: none; }\n.insight-list-item .name { color: var(--text-secondary); font-family: 'SF Mono', monospace; font-size: 12px; }\n.insight-list-item .val { color: var(--text-muted); }\n.overview-section-title {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--text-muted);\n margin-bottom: 14px;\n}\n\n/* Architecture View */\n#arch-container { padding: 32px; overflow: auto; height: 100%; }\n.arch-diagram { max-width: 1000px; margin: 0 auto; }\n.arch-layer {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n padding: 20px 24px;\n margin-bottom: 2px;\n}\n.arch-layer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 14px;\n}\n.arch-layer-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n color: var(--text-muted);\n}\n.arch-layer-count { font-size: 11px; color: var(--text-muted); }\n.arch-layer-files {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.arch-file {\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 8px 14px;\n font-size: 12px;\n cursor: pointer;\n transition: all .15s;\n min-width: 120px;\n max-width: 220px;\n}\n.arch-file:hover { border-color: var(--accent); background: var(--accent-dim); }\n.arch-file .arch-file-name { font-weight: 500; color: var(--text); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.arch-file .arch-file-desc { font-size: 10px; color: var(--text-muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.arch-arrow {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 3px 0;\n color: var(--border-light);\n gap: 12px;\n}\n.arch-arrow svg { width: 18px; height: 18px; }\n.arch-flow-label {\n font-size: 10px;\n color: var(--text-muted);\n letter-spacing: 0.5px;\n}\n.arch-toggle {\n background: none;\n border: 1px solid var(--border);\n color: var(--text-muted);\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n font-family: inherit;\n}\n.arch-toggle:hover { color: var(--text-secondary); border-color: var(--border-light); }\n\n/* System Map View */\n#sysmap-container { padding: 0; overflow: hidden; height: 100%; position: relative; cursor: grab; }\n#sysmap-container.grabbing { cursor: grabbing; }\n#sysmap-svg { display: block; position: absolute; top: 0; left: 0; }\n#sysmap-svg .sd-zone { cursor: default; }\n#sysmap-svg .sd-node { cursor: pointer; }\n#sysmap-svg .sd-node:hover .sd-node-bg { fill: #1c2128; stroke-width: 2; }\n#sysmap-svg text { font-family: 'Inter', -apple-system, sans-serif; pointer-events: none; }\n#sysmap-svg .sd-arrow { stroke: #30363d; fill: none; stroke-width: 1.5; }\n#sysmap-svg .sd-arrow-head { fill: #30363d; }\n#sysmap-svg .sd-arrow-label {\n fill: #9ca3af; font-size: 9px; font-weight: 500;\n paint-order: stroke; stroke: #0d1117; stroke-width: 3px; stroke-linejoin: round;\n}\n#sysmap-svg .sd-zone-border { fill: none; stroke: var(--border-light); stroke-width: 1; stroke-dasharray: 6 4; rx: 14; }\n#sysmap-svg .sd-zone-label { fill: var(--text-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.6px; }\n#sysmap-svg .sd-zone-bg { rx: 14; }\n#sysmap-svg .sd-step-num { fill: var(--accent); font-size: 11px; font-weight: 700; }\n#sysmap-svg .sd-step-bg { fill: var(--accent-dim); stroke: var(--accent); stroke-width: 1; }\n.sysmap-controls {\n position: absolute;\n bottom: 20px;\n right: 20px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n z-index: 10;\n}\n.sysmap-ctrl-btn {\n width: 36px;\n height: 36px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n color: var(--text-secondary);\n font-size: 18px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all .1s;\n font-family: inherit;\n line-height: 1;\n}\n.sysmap-ctrl-btn:hover { background: var(--surface-hover); color: var(--text); }\n.sysmap-zoom-level {\n text-align: center;\n font-size: 10px;\n color: var(--text-muted);\n padding: 4px 0;\n user-select: none;\n}\n\n/* List View */\n#file-list { padding: 20px 28px; overflow: auto; height: 100%; }\n.list-header {\n display: grid;\n grid-template-columns: minmax(200px, 1fr) 2fr 80px 80px;\n gap: 12px;\n padding: 8px 12px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--text-muted);\n border-bottom: 1px solid var(--border);\n margin-bottom: 4px;\n}\n.list-group-header {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--text-muted);\n padding: 16px 12px 6px 12px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.list-group-dot { width: 6px; height: 6px; border-radius: 2px; }\n.list-row {\n display: grid;\n grid-template-columns: minmax(200px, 1fr) 2fr 80px 80px;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border-radius: var(--radius);\n cursor: pointer;\n font-size: 13px;\n}\n.list-row:hover { background: var(--surface-hover); }\n.list-path { color: var(--text); font-family: 'SF Mono', monospace; font-size: 12px; }\n.list-desc { color: var(--text-muted); font-size: 12px; }\n.list-lines { color: var(--text-muted); font-size: 12px; text-align: right; }\n.list-size { color: var(--text-muted); font-size: 12px; text-align: right; }\n\n/* Detail Panel */\n#detail-panel {\n width: 320px;\n background: var(--surface);\n border-left: 1px solid var(--border);\n overflow-y: auto;\n padding: 24px;\n flex-shrink: 0;\n position: relative;\n}\n#detail-panel.hidden { display: none; }\n#close-detail {\n position: absolute;\n top: 16px;\n right: 16px;\n background: none;\n border: none;\n color: var(--text-muted);\n font-size: 18px;\n cursor: pointer;\n padding: 4px 8px;\n border-radius: 4px;\n line-height: 1;\n}\n#close-detail:hover { background: var(--surface-hover); color: var(--text); }\n.detail-path {\n font-family: 'SF Mono', monospace;\n font-size: 11px;\n color: var(--text-muted);\n margin-bottom: 4px;\n word-break: break-all;\n}\n.detail-title { font-size: 16px; font-weight: 600; margin-bottom: 4px; }\n.detail-desc { font-size: 13px; color: var(--text-secondary); margin-bottom: 20px; line-height: 1.5; }\n.detail-tags { display: flex; gap: 6px; margin-bottom: 20px; flex-wrap: wrap; }\n.detail-tag {\n padding: 3px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 500;\n}\n.detail-tag-role { background: var(--accent-dim); color: var(--accent); }\n.detail-tag-cat { background: var(--purple-dim); color: var(--purple); }\n.detail-stats {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n margin-bottom: 20px;\n}\n.detail-stat-box {\n background: var(--bg);\n border-radius: var(--radius);\n padding: 10px 12px;\n}\n.detail-stat-val { font-size: 18px; font-weight: 700; }\n.detail-stat-label { font-size: 11px; color: var(--text-muted); }\n.detail-section { margin-bottom: 20px; }\n.detail-section h4 {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n color: var(--text-muted);\n margin-bottom: 8px;\n}\n.detail-link {\n display: block;\n padding: 4px 8px;\n font-size: 12px;\n font-family: 'SF Mono', monospace;\n color: var(--accent);\n border-radius: 4px;\n cursor: pointer;\n text-decoration: none;\n transition: background .1s;\n}\n.detail-link:hover { background: var(--accent-dim); }\n.detail-export {\n display: inline-block;\n padding: 3px 8px;\n font-size: 11px;\n font-family: 'SF Mono', monospace;\n background: var(--bg);\n border-radius: 4px;\n margin: 2px;\n color: var(--text-secondary);\n}\n\n/* Expand button */\n.expand-btn {\n display: block;\n margin-top: 8px;\n padding: 5px 12px;\n background: none;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n color: var(--text-muted);\n font-size: 11px;\n font-family: inherit;\n cursor: pointer;\n transition: all .1s;\n}\n.expand-btn:hover { color: var(--text-secondary); border-color: var(--border-light); background: var(--surface-hover); }\n\n/* Health Badge */\n.health-badge {\n padding: 2px 10px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.5px;\n}\n\n/* File Preview */\n.detail-preview {\n background: #1a1e26;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 14px 16px;\n font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 12px;\n line-height: 1.7;\n color: #c9d1d9;\n overflow-x: auto;\n white-space: pre;\n max-height: 340px;\n overflow-y: auto;\n margin-bottom: 16px;\n tab-size: 2;\n}\n.detail-preview .line-num {\n display: inline-block;\n width: 28px;\n color: #484f58;\n text-align: right;\n margin-right: 14px;\n user-select: none;\n font-size: 11px;\n}\n.detail-preview-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.vscode-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 4px 10px;\n background: #0078d4;\n color: #fff;\n border: none;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: background .1s;\n}\n.vscode-btn:hover { background: #106ebe; }\n\n/* Minimap */\n.sysmap-minimap {\n position: absolute;\n bottom: 20px;\n left: 20px;\n width: 180px;\n height: 120px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n z-index: 10;\n opacity: 0.85;\n}\n.sysmap-minimap:hover { opacity: 1; }\n.sysmap-minimap svg { width: 100%; height: 100%; }\n.minimap-viewport { fill: var(--accent); opacity: 0.15; stroke: var(--accent); stroke-width: 1.5; }\n\n/* API Docs */\n#apidocs-container { padding: 32px; overflow: auto; height: 100%; }\n.api-doc-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n padding: 16px 20px;\n margin-bottom: 12px;\n}\n.api-doc-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 8px;\n}\n.api-doc-path { font-family: 'SF Mono', monospace; font-size: 14px; font-weight: 600; color: var(--text); }\n.api-doc-handler { font-size: 11px; color: var(--text-muted); cursor: pointer; }\n.api-doc-handler:hover { color: var(--accent); }\n.api-doc-params { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }\n.api-doc-param {\n background: var(--purple-dim);\n color: var(--purple);\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-family: 'SF Mono', monospace;\n}\n.api-doc-desc { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }\n\n/* Diff View */\n#diff-container { padding: 32px; overflow: auto; height: 100%; }\n.diff-summary {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n margin-bottom: 28px;\n}\n.diff-stat-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n padding: 16px 20px;\n text-align: center;\n}\n.diff-stat-val { font-size: 24px; font-weight: 700; }\n.diff-stat-label { font-size: 11px; color: var(--text-muted); margin-top: 4px; }\n.diff-added { color: var(--green); }\n.diff-removed { color: var(--red); }\n.diff-changed { color: var(--yellow); }\n.diff-file-list { margin-top: 16px; }\n.diff-file-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-radius: var(--radius);\n font-size: 13px;\n cursor: pointer;\n}\n.diff-file-item:hover { background: var(--surface-hover); }\n.diff-badge {\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.3px;\n}\n.diff-badge-added { background: var(--green-dim); color: var(--green); }\n.diff-badge-removed { background: var(--red-dim); color: var(--red); }\n.diff-badge-changed { background: var(--yellow-dim); color: var(--yellow); }\n\n/* Test Coverage Indicator */\n.test-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; margin-right: 4px; flex-shrink: 0; }\n.test-dot-covered { background: var(--green); }\n.test-dot-uncovered { background: var(--red); opacity: 0.5; }\n\n/* PNG Export */\n.export-btn {\n background: var(--surface);\n border: 1px solid var(--border);\n color: var(--text-secondary);\n padding: 6px 14px;\n border-radius: var(--radius);\n font-size: 12px;\n cursor: pointer;\n font-family: inherit;\n transition: all .1s;\n}\n.export-btn:hover { background: var(--surface-hover); color: var(--text); }\n\n/* Scrollbar */\n::-webkit-scrollbar { width: 6px; height: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }\n::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }\n`;\n\n// ── Inline JS ─────────────────────────────────────────────\n\nconst JS = `\n(function() {\n const files = DATA.files;\n const edges = DATA.edges;\n\n // ── Category Config ─────────────────────────────────\n const CAT_COLORS = {\n pages: '#60a5fa',\n components: '#34d399',\n api: '#f87171',\n data: '#fbbf24',\n config: '#6b7280',\n styles: '#a78bfa',\n tests: '#fb923c',\n assets: '#4b5563',\n utilities: '#22d3ee',\n types: '#93c5fd',\n };\n\n const CAT_LABELS = {\n pages: 'Pages',\n components: 'Components',\n api: 'API',\n data: 'Data',\n config: 'Config',\n styles: 'Styles',\n tests: 'Tests',\n assets: 'Assets',\n utilities: 'Utilities',\n types: 'Types',\n };\n\n const CAT_ORDER = ['pages', 'components', 'api', 'data', 'utilities', 'styles', 'config', 'types', 'tests', 'assets'];\n\n // ── Build Category Map ──────────────────────────────\n const byCategory = {};\n for (const f of files) {\n if (!byCategory[f.category]) byCategory[f.category] = [];\n byCategory[f.category].push(f);\n }\n\n // ── Helper: short display name with parent dir ──────\n function displayName(filePath) {\n const parts = filePath.split('/');\n if (parts.length >= 2) {\n return parts[parts.length - 2] + '/' + parts[parts.length - 1];\n }\n return parts[parts.length - 1];\n }\n\n // ── Sidebar Category Tree ───────────────────────────\n const treeEl = document.getElementById('category-tree');\n treeEl.innerHTML = '<h3>Explorer</h3>';\n\n for (const cat of CAT_ORDER) {\n const catFiles = byCategory[cat];\n if (!catFiles || catFiles.length === 0) continue;\n\n const group = document.createElement('div');\n group.className = 'category-group';\n\n const header = document.createElement('div');\n header.className = 'category-header';\n header.innerHTML =\n '<span class=\"cat-dot\" style=\"background:' + CAT_COLORS[cat] + '\"></span>' +\n CAT_LABELS[cat] +\n '<span class=\"category-count\">' + catFiles.length + '</span>';\n\n const filesEl = document.createElement('div');\n filesEl.className = 'category-files';\n filesEl.style.display = 'none';\n\n for (const f of catFiles) {\n const item = document.createElement('div');\n item.className = 'file-item';\n item.dataset.path = f.path;\n // Show parent/file to distinguish duplicate names like index.ts\n const dn = displayName(f.path);\n const parts = dn.split('/');\n if (parts.length > 1) {\n item.innerHTML = '<span class=\"file-dir\">' + parts[0] + '/</span>' + parts[1];\n } else {\n item.textContent = dn;\n }\n item.addEventListener('click', () => selectFile(f));\n filesEl.appendChild(item);\n }\n\n header.addEventListener('click', () => {\n filesEl.style.display = filesEl.style.display === 'none' ? 'block' : 'none';\n });\n\n group.appendChild(header);\n group.appendChild(filesEl);\n treeEl.appendChild(group);\n }\n\n // Auto-expand first 3 non-empty categories\n const groups = treeEl.querySelectorAll('.category-files');\n for (let i = 0; i < Math.min(3, groups.length); i++) groups[i].style.display = 'block';\n\n // ── File Selection ──────────────────────────────────\n let selectedFile = null;\n\n function selectFile(f) {\n selectedFile = f;\n\n document.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));\n const el = document.querySelector('.file-item[data-path=\"' + CSS.escape(f.path) + '\"]');\n if (el) el.classList.add('selected');\n\n const panel = document.getElementById('detail-panel');\n panel.classList.remove('hidden');\n\n const imports = f.imports || [];\n const importedBy = [];\n for (const e of edges) {\n const toNorm = e.to.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n const fNorm = f.path.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n if (toNorm.endsWith(fNorm) || fNorm.endsWith(toNorm)) {\n importedBy.push(e.from);\n }\n }\n\n document.getElementById('detail-content').innerHTML =\n '<div class=\"detail-path\">' + f.path + '</div>' +\n '<div class=\"detail-title\">' + f.path.split('/').pop() + '</div>' +\n '<div class=\"detail-desc\">' + f.description + '</div>' +\n '<div class=\"detail-tags\">' +\n '<span class=\"detail-tag detail-tag-role\">' + f.role + '</span>' +\n '<span class=\"detail-tag detail-tag-cat\">' + f.category + '</span>' +\n '</div>' +\n '<div class=\"detail-stats\">' +\n '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + f.lines + '</div><div class=\"detail-stat-label\">Lines</div></div>' +\n '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + formatBytes(f.size) + '</div><div class=\"detail-stat-label\">Size</div></div>' +\n '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + imports.length + '</div><div class=\"detail-stat-label\">Imports</div></div>' +\n '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + importedBy.length + '</div><div class=\"detail-stat-label\">Used by</div></div>' +\n '</div>' +\n (f.exports.length > 0 ? '<div class=\"detail-section\"><h4>Exports</h4><div>' + f.exports.map(e => '<span class=\"detail-export\">' + e + '</span>').join('') + '</div></div>' : '') +\n (f.routes.length > 0 ? '<div class=\"detail-section\"><h4>Routes</h4>' + f.routes.map(r => '<div class=\"route-item\"><span class=\"method method-' + r.method.toLowerCase() + '\">' + r.method + '</span><span class=\"route-path\">' + r.path + '</span></div>').join('') + '</div>' : '') +\n (imports.length > 0 ? '<div class=\"detail-section\"><h4>Dependencies (' + imports.length + ')</h4>' + imports.map(i => '<div class=\"detail-link\" onclick=\"window._selectByPath(\\\\'' + i + '\\\\')\">' + i + '</div>').join('') + '</div>' : '') +\n (importedBy.length > 0 ? '<div class=\"detail-section\"><h4>Dependents (' + importedBy.length + ')</h4>' + importedBy.map(i => '<div class=\"detail-link\" onclick=\"window._selectByPath(\\\\'' + i + '\\\\')\">' + i + '</div>').join('') + '</div>' : '') +\n (f.preview ? '<div class=\"detail-section\"><div class=\"detail-preview-header\"><h4>Preview</h4><div style=\"display:flex;align-items:center;gap:8px\"><span style=\"font-size:10px;color:var(--text-muted)\">' + (f.hasTests ? '<span class=\"test-dot test-dot-covered\"></span>Has tests' : '<span class=\"test-dot test-dot-uncovered\"></span>No tests') + '</span>' + (PROJECT_ROOT ? '<button class=\"vscode-btn\" onclick=\"window._openInVSCode(\\\\'' + f.path + '\\\\')\">Open in VS Code</button>' : '') + '</div></div><div class=\"detail-preview\">' + formatPreview(f.preview) + '</div></div>' : '');\n }\n\n function escapeHtmlInline(s) {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n }\n\n function formatPreview(src) {\n const lines = src.split('\\\\n');\n return lines.map((line, i) => '<span class=\"line-num\">' + (i + 1) + '</span>' + escapeHtmlInline(line)).join('\\\\n');\n }\n\n // Open file in VS Code — reuses existing window for the folder, no duplicates\n window._openInVSCode = function(relativePath) {\n if (!PROJECT_ROOT) return;\n const fullPath = PROJECT_ROOT + '/' + relativePath;\n // vscode://file/ reuses existing window if the folder is already open\n // -r flag reuses window, -g goes to file\n window.location.href = 'vscode://file/' + encodeURI(fullPath) + ':1';\n };\n\n window._selectByPath = function(p) {\n const match = files.find(f => f.path === p || f.path.replace(/\\\\.(ts|tsx|js|jsx)$/, '') === p.replace(/\\\\.(ts|tsx|js|jsx)$/, ''));\n if (match) selectFile(match);\n };\n\n window._expandList = function(btn, group) {\n const items = document.querySelectorAll('[data-expand=\"' + group + '\"]');\n const showing = items[0] && items[0].style.display !== 'none';\n items.forEach(el => el.style.display = showing ? 'none' : '');\n btn.textContent = showing ? 'Show all ' + (items.length + parseInt(btn.textContent.match(/\\\\d+/)?.[0] || items.length)) : 'Show less';\n if (showing) {\n btn.textContent = 'Show all';\n } else {\n btn.textContent = 'Show less';\n }\n };\n\n document.getElementById('close-detail').addEventListener('click', () => {\n document.getElementById('detail-panel').classList.add('hidden');\n selectedFile = null;\n document.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));\n });\n\n // ── View Switching ──────────────────────────────────\n let currentView = 'overview';\n\n document.querySelectorAll('.view-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.view-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n const view = btn.dataset.view;\n currentView = view;\n document.querySelectorAll('.view').forEach(v => v.classList.remove('active-view'));\n document.getElementById(view + '-view').classList.add('active-view');\n if (view === 'overview') buildOverview();\n if (view === 'architecture') buildArchitecture();\n if (view === 'map') buildSystemMap();\n if (view === 'list') buildList();\n if (view === 'apidocs') buildApiDocs();\n if (view === 'diff') buildDiff();\n });\n });\n\n // ── Search ──────────────────────────────────────────\n document.getElementById('search').addEventListener('input', (e) => {\n const q = e.target.value.toLowerCase();\n document.querySelectorAll('.file-item').forEach(el => {\n const path = el.dataset.path || '';\n el.style.display = path.toLowerCase().includes(q) ? '' : 'none';\n });\n if (q) {\n document.querySelectorAll('.category-files').forEach(el => el.style.display = 'block');\n }\n });\n\n // ── Overview ────────────────────────────────────────\n function buildOverview() {\n const container = document.getElementById('overview-container');\n\n const totalImports = edges.length;\n const avgImports = files.length > 0 ? (totalImports / files.length).toFixed(1) : 0;\n const sortedByLines = [...files].sort((a, b) => b.lines - a.lines);\n\n // Most connected (imported by most)\n const importedByCounts = {};\n for (const e of edges) {\n importedByCounts[e.to] = (importedByCounts[e.to] || 0) + 1;\n }\n const mostUsed = Object.entries(importedByCounts).sort((a, b) => b[1] - a[1]);\n\n // Complexity hotspots: files with high lines + high dependencies\n const fileComplexity = files.map(f => {\n const deps = edges.filter(e => e.from === f.path).length;\n const dependents = edges.filter(e => {\n const toNorm = e.to.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n const fNorm = f.path.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n return toNorm.endsWith(fNorm) || fNorm.endsWith(toNorm);\n }).length;\n return { file: f, deps, dependents, score: f.lines * 0.3 + deps * 15 + dependents * 20 };\n }).sort((a, b) => b.score - a.score);\n\n const catEntries = CAT_ORDER.filter(c => byCategory[c] && byCategory[c].length > 0)\n .map(c => ({ cat: c, count: byCategory[c].length }));\n const maxCatCount = Math.max(...catEntries.map(c => c.count), 1);\n\n const langs = Object.entries(DATA.languages).sort((a, b) => b[1] - a[1]);\n const maxLangLines = Math.max(...langs.map(l => l[1]), 1);\n\n // Test coverage stats\n const testedFiles = files.filter(f => f.hasTests).length;\n const testableFiles = files.filter(f => f.role !== 'test' && f.role !== 'config' && f.role !== 'style' && f.role !== 'asset').length;\n const testCovPct = testableFiles > 0 ? Math.round(testedFiles / testableFiles * 100) : 0;\n const hs = DATA.healthScore;\n\n let html = '<div class=\"overview-grid\">';\n\n // Health Score\n html += '<div class=\"insight-card\">';\n html += '<h3>Code Health</h3>';\n const gc = hs.grade === 'A' ? 'var(--green)' : hs.grade === 'B' ? 'var(--accent)' : hs.grade === 'C' ? 'var(--yellow)' : hs.grade === 'D' ? 'var(--orange)' : 'var(--red)';\n html += '<div class=\"insight-stat\"><span class=\"insight-number\" style=\"color:' + gc + '\">' + hs.grade + '</span><span class=\"insight-label\">' + hs.score + '/100</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Avg file size</span><span class=\"val\">' + hs.details.avgFileSize + ' lines</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Test ratio</span><span class=\"val\">' + Math.round(hs.details.testRatio * 100) + '%</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Circular deps</span><span class=\"val\" style=\"color:' + (hs.details.circularCount > 0 ? 'var(--red)' : 'var(--green)') + '\">' + hs.details.circularCount + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Orphan files</span><span class=\"val\">' + hs.details.orphanCount + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Max complexity</span><span class=\"val\">' + hs.details.maxComplexity + '</span></div>';\n html += '</div>';\n\n // Project Summary\n html += '<div class=\"insight-card\">';\n html += '<h3>Project Summary</h3>';\n html += '<div class=\"insight-stat\"><span class=\"insight-number\">' + files.length + '</span><span class=\"insight-label\">source files</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Total lines</span><span class=\"val\">' + DATA.totalLines.toLocaleString() + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Connections</span><span class=\"val\">' + totalImports + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Avg imports/file</span><span class=\"val\">' + avgImports + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">API endpoints</span><span class=\"val\">' + DATA.apiRoutes.length + '</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Test coverage</span><span class=\"val\">' + testCovPct + '% (' + testedFiles + '/' + testableFiles + ')</span></div>';\n html += '<div class=\"insight-list-item\"><span class=\"name\">Framework</span><span class=\"val\">' + DATA.framework + '</span></div>';\n html += '</div>';\n\n // File Distribution\n html += '<div class=\"insight-card\">';\n html += '<h3>File Distribution</h3>';\n for (const c of catEntries) {\n html += '<div class=\"insight-bar-row\">';\n html += '<span class=\"insight-bar-name\">' + CAT_LABELS[c.cat] + '</span>';\n html += '<span class=\"insight-bar-track\"><span class=\"insight-bar-fill\" style=\"width:' + (c.count / maxCatCount * 100) + '%;background:' + CAT_COLORS[c.cat] + '\"></span></span>';\n html += '<span class=\"insight-bar-val\">' + c.count + ' files</span>';\n html += '</div>';\n }\n html += '</div>';\n\n // Languages\n html += '<div class=\"insight-card\">';\n html += '<h3>Languages</h3>';\n for (const [lang, lines] of langs) {\n const pct = ((lines / DATA.totalLines) * 100).toFixed(1);\n html += '<div class=\"insight-bar-row\">';\n html += '<span class=\"insight-bar-name\">' + lang + '</span>';\n html += '<span class=\"insight-bar-track\"><span class=\"insight-bar-fill\" style=\"width:' + (lines / maxLangLines * 100) + '%;background:var(--accent)\"></span></span>';\n html += '<span class=\"insight-bar-val\">' + pct + '%</span>';\n html += '</div>';\n }\n html += '</div>';\n\n // Complexity Hotspots\n html += '<div class=\"insight-card\">';\n html += '<h3>Complexity Hotspots</h3>';\n if (fileComplexity.length === 0) {\n html += '<div style=\"color:var(--text-muted);font-size:13px;padding:8px 0\">No complexity data</div>';\n } else {\n for (let hi = 0; hi < fileComplexity.length; hi++) {\n const h = fileComplexity[hi];\n const hidden = hi >= 6 ? ' data-expand=\"hotspots\" style=\"display:none\"' : '';\n html += '<div class=\"insight-list-item\" style=\"cursor:pointer\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + h.file.path + '\\\\')\">';\n html += '<span class=\"name\" title=\"' + h.file.path + '\">' + displayName(h.file.path) + '</span>';\n html += '<span class=\"val\">' + h.file.lines + ' ln, ' + h.deps + ' deps, ' + h.dependents + ' used</span>';\n html += '</div>';\n }\n if (fileComplexity.length > 6) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'hotspots\\\\')\">Show all ' + fileComplexity.length + '</button>';\n }\n }\n html += '</div>';\n\n html += '</div>'; // end grid\n\n // Most Used Modules\n html += '<div class=\"overview-section-title\">Most Used Modules</div>';\n html += '<div class=\"overview-grid\"><div class=\"insight-card\" style=\"grid-column:1/-1\">';\n if (mostUsed.length === 0) {\n html += '<div style=\"color:var(--text-muted);font-size:13px;padding:8px 0\">No inter-file dependencies detected</div>';\n } else {\n const maxUsed = mostUsed[0][1];\n for (let mi = 0; mi < mostUsed.length; mi++) {\n const [filePath, count] = mostUsed[mi];\n const hidden = mi >= 8 ? ' data-expand=\"mostused\" style=\"display:none\"' : '';\n html += '<div class=\"insight-bar-row\" style=\"cursor:pointer\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + filePath + '\\\\')\">';\n html += '<span class=\"insight-bar-name\" title=\"' + filePath + '\">' + displayName(filePath) + '</span>';\n html += '<span class=\"insight-bar-track\"><span class=\"insight-bar-fill\" style=\"width:' + (count / maxUsed * 100) + '%;background:var(--green)\"></span></span>';\n html += '<span class=\"insight-bar-val\">' + count + ' deps</span>';\n html += '</div>';\n }\n if (mostUsed.length > 8) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'mostused\\\\')\">Show all ' + mostUsed.length + '</button>';\n }\n }\n html += '</div></div>';\n\n // Largest Files\n html += '<div class=\"overview-section-title\">Largest Files</div>';\n html += '<div class=\"overview-grid\"><div class=\"insight-card\" style=\"grid-column:1/-1\">';\n const maxLines = sortedByLines.length > 0 ? sortedByLines[0].lines : 1;\n for (let fi = 0; fi < sortedByLines.length; fi++) {\n const f = sortedByLines[fi];\n const hidden = fi >= 10 ? ' data-expand=\"largest\" style=\"display:none\"' : '';\n html += '<div class=\"insight-bar-row\" style=\"cursor:pointer\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<span class=\"insight-bar-name\" title=\"' + f.path + '\">' + displayName(f.path) + '</span>';\n html += '<span class=\"insight-bar-track\"><span class=\"insight-bar-fill\" style=\"width:' + (f.lines / maxLines * 100) + '%;background:' + CAT_COLORS[f.category] + '\"></span></span>';\n html += '<span class=\"insight-bar-val\">' + f.lines + ' ln</span>';\n html += '</div>';\n }\n if (sortedByLines.length > 10) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'largest\\\\')\">Show all ' + sortedByLines.length + '</button>';\n }\n html += '</div></div>';\n\n // Circular Dependencies\n if (DATA.circularDeps && DATA.circularDeps.length > 0) {\n html += '<div class=\"overview-section-title\" style=\"color:var(--red)\">Circular Dependencies (' + DATA.circularDeps.length + ')</div>';\n html += '<div class=\"overview-grid\"><div class=\"insight-card\" style=\"grid-column:1/-1;border-color:var(--red);border-color:rgba(248,113,113,0.3)\">';\n for (let ci = 0; ci < DATA.circularDeps.length; ci++) {\n const cycle = DATA.circularDeps[ci];\n const hidden = ci >= 5 ? ' data-expand=\"cycles\" style=\"display:none\"' : '';\n html += '<div class=\"insight-list-item\"' + hidden + '>';\n html += '<span class=\"name\" style=\"width:auto;text-align:left\">' + cycle.map(p => displayName(p)).join(' → ') + ' → ' + displayName(cycle[0]) + '</span>';\n html += '</div>';\n }\n if (DATA.circularDeps.length > 5) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'cycles\\\\')\">Show all ' + DATA.circularDeps.length + '</button>';\n }\n html += '</div></div>';\n }\n\n // Orphan Files\n if (DATA.orphanFiles && DATA.orphanFiles.length > 0) {\n html += '<div class=\"overview-section-title\">Orphan Files (' + DATA.orphanFiles.length + ')</div>';\n html += '<div class=\"overview-grid\"><div class=\"insight-card\" style=\"grid-column:1/-1\">';\n html += '<div style=\"color:var(--text-muted);font-size:12px;margin-bottom:12px\">Files with no imports and not imported by anything</div>';\n for (let oi = 0; oi < DATA.orphanFiles.length; oi++) {\n const op = DATA.orphanFiles[oi];\n const hidden = oi >= 8 ? ' data-expand=\"orphans\" style=\"display:none\"' : '';\n html += '<div class=\"insight-list-item\" style=\"cursor:pointer\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + op + '\\\\')\">';\n html += '<span class=\"name\">' + displayName(op) + '</span>';\n html += '</div>';\n }\n if (DATA.orphanFiles.length > 8) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'orphans\\\\')\">Show all ' + DATA.orphanFiles.length + '</button>';\n }\n html += '</div></div>';\n }\n\n // Test Coverage Map\n html += '<div class=\"overview-section-title\">Test Coverage Map</div>';\n html += '<div class=\"overview-grid\"><div class=\"insight-card\" style=\"grid-column:1/-1\">';\n const testable = files.filter(f => f.role !== 'test' && f.role !== 'config' && f.role !== 'style' && f.role !== 'asset');\n if (testable.length === 0) {\n html += '<div style=\"color:var(--text-muted);font-size:13px\">No testable files</div>';\n } else {\n const tested = testable.filter(f => f.hasTests);\n const untested = testable.filter(f => !f.hasTests);\n html += '<div style=\"margin-bottom:12px;font-size:12px;color:var(--text-secondary)\">';\n html += '<span class=\"test-dot test-dot-covered\"></span> ' + tested.length + ' covered';\n html += ' <span class=\"test-dot test-dot-uncovered\"></span> ' + untested.length + ' uncovered';\n html += '</div>';\n // Show untested files (they need attention)\n for (let ui = 0; ui < untested.length; ui++) {\n const f = untested[ui];\n const hidden = ui >= 10 ? ' data-expand=\"untested\" style=\"display:none\"' : '';\n html += '<div class=\"insight-list-item\" style=\"cursor:pointer\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<span class=\"name\"><span class=\"test-dot test-dot-uncovered\"></span>' + displayName(f.path) + '</span>';\n html += '<span class=\"val\">' + f.lines + ' lines</span>';\n html += '</div>';\n }\n if (untested.length > 10) {\n html += '<button class=\"expand-btn\" onclick=\"window._expandList(this,\\\\'untested\\\\')\">Show all ' + untested.length + ' uncovered</button>';\n }\n }\n html += '</div></div>';\n\n container.innerHTML = html;\n }\n\n // ── Architecture View ───────────────────────────────\n function buildArchitecture() {\n const container = document.getElementById('arch-container');\n\n const layers = [\n { label: 'Entry Points', desc: 'CLI entry, main exports', roles: ['entry'], cats: [] },\n { label: 'Presentation Layer', desc: 'Pages, layouts, and route entry points', roles: ['page', 'layout'], cats: ['pages'] },\n { label: 'Component Layer', desc: 'Reusable UI components', roles: ['component'], cats: ['components'] },\n { label: 'State & Data Layer', desc: 'Stores, hooks, services, and data fetching', roles: ['store', 'hook', 'service', 'model'], cats: ['data'] },\n { label: 'API Layer', desc: 'API routes, middleware, and server endpoints', roles: ['api-route', 'middleware'], cats: ['api'] },\n { label: 'Core Logic', desc: 'Business logic, utilities, and shared modules', roles: ['utility'], cats: ['utilities'] },\n { label: 'Types & Config', desc: 'Type definitions and configuration', roles: ['config', 'type'], cats: ['config', 'types'] },\n { label: 'Tests', desc: 'Test files', roles: ['test'], cats: ['tests'] },\n { label: 'Assets & Styles', desc: 'Static assets and stylesheets', roles: ['asset', 'style'], cats: ['assets', 'styles'] },\n ];\n\n const arrowDown = '<svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M10 4v12M6 12l4 4 4-4\"/></svg>';\n\n let html = '<div class=\"arch-diagram\">';\n\n let shownLayers = 0;\n for (const layer of layers) {\n const layerFiles = files.filter(f =>\n layer.roles.includes(f.role) || layer.cats.includes(f.category)\n );\n if (layerFiles.length === 0) continue;\n\n if (shownLayers > 0) {\n html += '<div class=\"arch-arrow\">' + arrowDown + '<span class=\"arch-flow-label\">imports</span>' + arrowDown + '</div>';\n }\n\n const collapsed = layerFiles.length > 12;\n const showCount = collapsed ? 12 : layerFiles.length;\n const layerId = 'arch-layer-' + shownLayers;\n\n html += '<div class=\"arch-layer\">';\n html += '<div class=\"arch-layer-header\">';\n html += '<span class=\"arch-layer-label\">' + layer.label + '</span>';\n html += '<span class=\"arch-layer-count\">' + layerFiles.length + ' files</span>';\n html += '</div>';\n html += '<div class=\"arch-layer-files\" id=\"' + layerId + '\">';\n\n for (let i = 0; i < layerFiles.length; i++) {\n const f = layerFiles[i];\n const dn = displayName(f.path);\n const hidden = collapsed && i >= showCount ? ' style=\"display:none\" data-arch-extra=\"' + layerId + '\"' : '';\n html += '<div class=\"arch-file\"' + hidden + ' onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\" title=\"' + f.path + '\">';\n html += '<div class=\"arch-file-name\">' + dn + '</div>';\n html += '<div class=\"arch-file-desc\">' + f.description + '</div>';\n html += '</div>';\n }\n\n if (collapsed) {\n html += '<button class=\"arch-toggle\" data-target=\"' + layerId + '\" onclick=\"window._toggleArchLayer(this)\">Show all ' + layerFiles.length + '</button>';\n }\n\n html += '</div>'; // files\n html += '</div>'; // layer\n shownLayers++;\n }\n\n html += '</div>';\n container.innerHTML = html;\n }\n\n window._toggleArchLayer = function(btn) {\n const target = btn.dataset.target;\n const extras = document.querySelectorAll('[data-arch-extra=\"' + target + '\"]');\n const showing = extras[0] && extras[0].style.display !== 'none';\n extras.forEach(el => el.style.display = showing ? 'none' : '');\n btn.textContent = showing ? 'Show all' : 'Show less';\n };\n\n // Component detail panel (used by System Map)\n let _sysComps = {};\n window._selectComponent = function(compId) {\n const c = _sysComps[compId];\n if (!c) return;\n\n selectedFile = null;\n document.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));\n\n const panel = document.getElementById('detail-panel');\n panel.classList.remove('hidden');\n\n const totalLines = c.files.reduce((s, f) => s + f.lines, 0);\n const totalSize = c.files.reduce((s, f) => s + f.size, 0);\n // Count incoming/outgoing component-level deps\n const outgoing = new Set();\n const incoming = new Set();\n const compPaths = new Set(c.files.map(f => f.path));\n for (const e of edges) {\n if (compPaths.has(e.from) && !compPaths.has(e.to)) outgoing.add(e.to);\n if (!compPaths.has(e.from) && compPaths.has(e.to)) incoming.add(e.from);\n }\n\n let html = '';\n html += '<div class=\"detail-title\" style=\"margin-bottom:2px\">' + c.label + '</div>';\n html += '<div class=\"detail-desc\">' + c.desc + '</div>';\n html += '<div class=\"detail-stats\">';\n html += '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + c.count + '</div><div class=\"detail-stat-label\">Files</div></div>';\n html += '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + totalLines.toLocaleString() + '</div><div class=\"detail-stat-label\">Lines</div></div>';\n html += '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + formatBytes(totalSize) + '</div><div class=\"detail-stat-label\">Total Size</div></div>';\n html += '<div class=\"detail-stat-box\"><div class=\"detail-stat-val\">' + outgoing.size + ' / ' + incoming.size + '</div><div class=\"detail-stat-label\">Out / In deps</div></div>';\n html += '</div>';\n\n // File list\n html += '<div class=\"detail-section\"><h4>Files (' + c.count + ')</h4>';\n const sorted = [...c.files].sort((a, b) => b.lines - a.lines);\n for (const f of sorted) {\n const dn = displayName(f.path);\n html += '<div class=\"detail-link\" style=\"display:flex;justify-content:space-between;align-items:center\" onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<span>' + dn + '</span>';\n html += '<span style=\"color:var(--text-muted);font-size:10px;flex-shrink:0;margin-left:8px\">' + f.lines + ' ln</span>';\n html += '</div>';\n }\n html += '</div>';\n\n // Exports across all files\n const allExports = [];\n for (const f of c.files) {\n for (const e of f.exports) {\n if (e !== 'default') allExports.push(e);\n }\n }\n if (allExports.length > 0) {\n html += '<div class=\"detail-section\"><h4>Exports (' + allExports.length + ')</h4><div>';\n for (let ei = 0; ei < allExports.length; ei++) {\n const hidden = ei >= 20 ? ' data-expand=\"compexports\" style=\"display:none\"' : '';\n html += '<span class=\"detail-export\"' + hidden + '>' + allExports[ei] + '</span>';\n }\n if (allExports.length > 20) {\n html += '<button class=\"expand-btn\" style=\"margin-top:6px\" onclick=\"window._expandList(this,\\\\'compexports\\\\')\">Show all ' + allExports.length + '</button>';\n }\n html += '</div></div>';\n }\n\n // Routes\n const allRoutes = [];\n for (const f of c.files) {\n for (const r of f.routes) allRoutes.push(r);\n }\n if (allRoutes.length > 0) {\n html += '<div class=\"detail-section\"><h4>Routes (' + allRoutes.length + ')</h4>';\n for (const r of allRoutes) {\n html += '<div class=\"route-item\"><span class=\"method method-' + r.method.toLowerCase() + '\">' + r.method + '</span><span class=\"route-path\">' + r.path + '</span></div>';\n }\n html += '</div>';\n }\n\n document.getElementById('detail-content').innerHTML = html;\n };\n\n // ── System Map View (SVG Architecture Diagram) ──────\n function buildSystemMap() {\n const container = document.getElementById('sysmap-container');\n\n // ── Build Components ──────────────────────────────\n function buildComponents() {\n const comps = [];\n\n const entryFiles = files.filter(f => f.role === 'entry');\n const cliFiles = files.filter(f => f.path.includes('/cli/') && f.role !== 'entry');\n const pageFiles = files.filter(f => f.role === 'page' || f.role === 'layout');\n const componentFiles = files.filter(f => f.role === 'component');\n const apiFiles = files.filter(f => f.role === 'api-route');\n const middlewareFiles = files.filter(f => f.role === 'middleware');\n const storeFiles = files.filter(f => f.role === 'store' || f.role === 'hook');\n const serviceFiles = files.filter(f => f.role === 'service' || f.role === 'model');\n const utilFiles = files.filter(f => f.role === 'utility' && !f.path.includes('/cli/'));\n const configFiles = files.filter(f => f.role === 'config');\n const typeFiles = files.filter(f => f.role === 'type');\n const testFiles = files.filter(f => f.category === 'tests');\n const styleFiles = files.filter(f => f.category === 'styles');\n const assetFiles = files.filter(f => f.category === 'assets');\n\n const icons = {\n entry: '<path d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-4 0h4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n cli: '<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M7 9l3 3-3 3M13 15h4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n page: '<path d=\"M4 4h16v16H4z\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M4 9h16M9 9v11\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n component: '<rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n api: '<circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M12 2v4M12 18v4M2 12h4M18 12h4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n middleware: '<path d=\"M12 3v18M3 12h18\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n store: '<ellipse cx=\"12\" cy=\"5\" rx=\"8\" ry=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M4 5v14c0 1.66 3.58 3 8 3s8-1.34 8-3V5\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n service: '<rect x=\"2\" y=\"6\" width=\"20\" height=\"12\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><circle cx=\"6\" cy=\"12\" r=\"1\" fill=\"currentColor\"/><circle cx=\"10\" cy=\"12\" r=\"1\" fill=\"currentColor\"/><circle cx=\"14\" cy=\"12\" r=\"1\" fill=\"currentColor\"/>',\n util: '<path d=\"M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n config: '<circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n type: '<path d=\"M4 7V4h16v3M9 20h6M12 4v16\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n test: '<path d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M9 14l2 2 4-4\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n style: '<circle cx=\"13.5\" cy=\"6.5\" r=\"2.5\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><path d=\"M19 9s-3 5.5-3 8a3 3 0 006 0c0-2.5-3-8-3-8z\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n asset: '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" fill=\"currentColor\"/><path d=\"M21 15l-5-5L5 21\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"/>',\n };\n\n function add(id, label, color, icon, cf, desc) {\n if (cf.length > 0) comps.push({ id, label, color, icon: icons[icon] || icons.util, files: cf, count: cf.length, desc });\n }\n\n add('entry', 'Entry Points', '#60a5fa', 'entry', entryFiles, 'App bootstrap');\n add('cli', 'CLI Commands', '#60a5fa', 'cli', cliFiles, 'Command interface');\n add('pages', 'Pages & Routes', '#60a5fa', 'page', pageFiles, 'Route entries');\n add('components', 'Components', '#34d399', 'component', componentFiles, 'Reusable UI');\n add('api', 'API Endpoints', '#f87171', 'api', apiFiles, 'Server endpoints');\n add('middleware', 'Middleware', '#f87171', 'middleware', middlewareFiles, 'Request processing');\n add('store', 'State & Hooks', '#fbbf24', 'store', storeFiles, 'State mgmt');\n add('services', 'Services', '#fbbf24', 'service', serviceFiles, 'Business logic');\n add('utils', 'Utilities', '#22d3ee', 'util', utilFiles, 'Shared helpers');\n add('config', 'Configuration', '#6b7280', 'config', configFiles, 'App config');\n add('types', 'Types', '#93c5fd', 'type', typeFiles, 'Type defs');\n add('tests', 'Tests', '#fb923c', 'test', testFiles, 'Test suite');\n add('styles', 'Styles', '#a78bfa', 'style', styleFiles, 'Stylesheets');\n add('assets', 'Assets', '#4b5563', 'asset', assetFiles, 'Static files');\n\n return comps;\n }\n\n const comps = buildComponents();\n // Store for _selectComponent\n _sysComps = {};\n for (const c of comps) _sysComps[c.id] = c;\n\n if (comps.length === 0) {\n container.innerHTML = '<div style=\"padding:60px;text-align:center;color:var(--text-muted)\">No files to visualize</div>';\n return;\n }\n\n // ── Layout ────────────────────────────────────────\n const NW = 190, NH = 70, GAP = 50, ZONE_PAD = 24;\n\n const zoneMap = [\n { id: 'interface', label: 'Interface Layer', comps: ['entry', 'cli', 'pages'], color: '#60a5fa' },\n { id: 'presentation', label: 'Presentation', comps: ['components'], color: '#34d399' },\n { id: 'server', label: 'Server Layer', comps: ['api', 'middleware'], color: '#f87171' },\n { id: 'data', label: 'Data Layer', comps: ['store', 'services'], color: '#fbbf24' },\n { id: 'core', label: 'Core', comps: ['utils'], color: '#22d3ee' },\n { id: 'foundation', label: 'Foundation', comps: ['config', 'types'], color: '#6b7280' },\n { id: 'quality', label: 'Quality & Assets', comps: ['tests', 'styles', 'assets'], color: '#fb923c' },\n ];\n\n const compById = {};\n for (const c of comps) compById[c.id] = c;\n const activeZones = zoneMap.filter(z => z.comps.some(cid => compById[cid]));\n\n const nodePositions = {};\n const zones = [];\n let stepNum = 1;\n\n // Two-column layout — side-by-side zones where possible\n // Pair zones: [0,1], [2,3], [4,5], [6]\n let curY = 60;\n let zi = 0;\n\n while (zi < activeZones.length) {\n const z1 = activeZones[zi];\n const z1Comps = z1.comps.filter(cid => compById[cid]);\n const z2 = activeZones[zi + 1];\n const z2Comps = z2 ? z2.comps.filter(cid => compById[cid]) : [];\n\n // If z1 has 3+ comps or z2 is empty, make z1 full-width\n const fullWidth = z1Comps.length >= 3 || z2Comps.length === 0;\n\n if (fullWidth) {\n // Full-width zone\n const cols = Math.min(z1Comps.length, 4);\n const rows = Math.ceil(z1Comps.length / cols);\n const zw = cols * (NW + GAP) + ZONE_PAD * 2 - GAP;\n const zh = rows * (NH + GAP) + 44 + ZONE_PAD - GAP;\n const zx = 60;\n\n zones.push({ x: zx, y: curY, w: zw, h: zh, label: z1.label, color: z1.color, step: stepNum++ });\n\n for (let ci = 0; ci < z1Comps.length; ci++) {\n const col = ci % cols;\n const row = Math.floor(ci / cols);\n nodePositions[z1Comps[ci]] = {\n x: zx + ZONE_PAD + col * (NW + GAP),\n y: curY + 44 + row * (NH + GAP),\n comp: compById[z1Comps[ci]],\n };\n }\n curY += zh + 40;\n zi++;\n } else {\n // Side-by-side zones\n const z1cols = Math.min(z1Comps.length, 2);\n const z1rows = Math.ceil(z1Comps.length / z1cols);\n const z1w = z1cols * (NW + GAP) + ZONE_PAD * 2 - GAP;\n const z1h = z1rows * (NH + GAP) + 44 + ZONE_PAD - GAP;\n\n const z2cols = Math.min(z2Comps.length, 2);\n const z2rows = Math.ceil(z2Comps.length / z2cols);\n const z2w = z2cols * (NW + GAP) + ZONE_PAD * 2 - GAP;\n const z2h = z2rows * (NH + GAP) + 44 + ZONE_PAD - GAP;\n\n const maxH = Math.max(z1h, z2h);\n const z1x = 60;\n const z2x = z1x + z1w + 50;\n\n zones.push({ x: z1x, y: curY, w: z1w, h: maxH, label: z1.label, color: z1.color, step: stepNum++ });\n zones.push({ x: z2x, y: curY, w: z2w, h: maxH, label: z2.label, color: z2.color, step: stepNum++ });\n\n for (let ci = 0; ci < z1Comps.length; ci++) {\n const col = ci % z1cols;\n const row = Math.floor(ci / z1cols);\n nodePositions[z1Comps[ci]] = {\n x: z1x + ZONE_PAD + col * (NW + GAP),\n y: curY + 44 + row * (NH + GAP),\n comp: compById[z1Comps[ci]],\n };\n }\n for (let ci = 0; ci < z2Comps.length; ci++) {\n const col = ci % z2cols;\n const row = Math.floor(ci / z2cols);\n nodePositions[z2Comps[ci]] = {\n x: z2x + ZONE_PAD + col * (NW + GAP),\n y: curY + 44 + row * (NH + GAP),\n comp: compById[z2Comps[ci]],\n };\n }\n curY += maxH + 40;\n zi += 2;\n }\n }\n\n const totalW = Math.max(800, ...zones.map(z => z.x + z.w + 80));\n const totalH = curY + 60;\n\n // ── Connections ───────────────────────────────────\n const connections = [];\n const compFileMap = {};\n for (const c of comps) {\n for (const f of c.files) compFileMap[f.path] = c.id;\n }\n\n const compEdges = {};\n for (const e of edges) {\n const fromComp = compFileMap[e.from];\n let toComp = compFileMap[e.to];\n if (!toComp) {\n for (const f of files) {\n const toN = e.to.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n const fN = f.path.replace(/\\\\.(ts|tsx|js|jsx)$/, '');\n if (toN.endsWith(fN) || fN.endsWith(toN)) { toComp = compFileMap[f.path]; break; }\n }\n }\n if (fromComp && toComp && fromComp !== toComp) {\n const key = fromComp + '>' + toComp;\n compEdges[key] = (compEdges[key] || 0) + 1;\n }\n }\n\n for (const [key, count] of Object.entries(compEdges)) {\n const [from, to] = key.split('>');\n if (nodePositions[from] && nodePositions[to]) {\n connections.push({ from, to, label: count + (count > 1 ? ' imports' : ' import') });\n }\n }\n\n // ── Build SVG ────────────────────────────────────\n let svg = '<svg id=\"sysmap-svg\" xmlns=\"http://www.w3.org/2000/svg\">';\n svg += '<defs>';\n svg += '<marker id=\"ah\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\"><polygon class=\"sd-arrow-head\" points=\"0 0, 10 3.5, 0 7\"/></marker>';\n svg += '</defs>';\n svg += '<g id=\"sysmap-world\">';\n\n // Zones\n for (const z of zones) {\n svg += '<g class=\"sd-zone\">';\n svg += '<rect class=\"sd-zone-bg\" x=\"' + z.x + '\" y=\"' + z.y + '\" width=\"' + z.w + '\" height=\"' + z.h + '\" fill=\"' + z.color + '06\"/>';\n svg += '<rect class=\"sd-zone-border\" x=\"' + z.x + '\" y=\"' + z.y + '\" width=\"' + z.w + '\" height=\"' + z.h + '\"/>';\n svg += '<text class=\"sd-zone-label\" x=\"' + (z.x + 18) + '\" y=\"' + (z.y + 22) + '\">' + z.label + '</text>';\n svg += '<circle class=\"sd-step-bg\" cx=\"' + (z.x + z.w - 22) + '\" cy=\"' + (z.y + 18) + '\" r=\"12\"/>';\n svg += '<text class=\"sd-step-num\" x=\"' + (z.x + z.w - 22) + '\" y=\"' + (z.y + 22) + '\" text-anchor=\"middle\">' + z.step + '</text>';\n svg += '</g>';\n }\n\n // Arrows — offset duplicates so labels don't overlap\n const drawnPairs = {};\n for (const conn of connections) {\n const f = nodePositions[conn.from];\n const t = nodePositions[conn.to];\n if (!f || !t) continue;\n\n const pairKey = [conn.from, conn.to].sort().join('|');\n const pairIdx = drawnPairs[pairKey] || 0;\n drawnPairs[pairKey] = pairIdx + 1;\n const offset = pairIdx * 14;\n\n const fx = f.x + NW / 2;\n const fy = f.y + NH;\n const tx = t.x + NW / 2;\n const ty = t.y;\n const dx = Math.abs(tx - fx);\n const dy = Math.abs(ty - fy);\n\n if (dy < NH) {\n // Same row — horizontal arrow\n const sx = fx > tx ? f.x : f.x + NW;\n const ex = fx > tx ? t.x + NW : t.x;\n const ay = f.y + NH / 2 + offset;\n svg += '<path class=\"sd-arrow\" d=\"M' + sx + ' ' + ay + ' L' + ex + ' ' + ay + '\" marker-end=\"url(#ah)\"/>';\n svg += '<text class=\"sd-arrow-label\" x=\"' + ((sx + ex) / 2) + '\" y=\"' + (ay - 8) + '\" text-anchor=\"middle\">' + conn.label + '</text>';\n } else if (dx < 30) {\n // Vertical\n const ox = 10 + offset;\n svg += '<path class=\"sd-arrow\" d=\"M' + (fx + ox) + ' ' + fy + ' L' + (tx + ox) + ' ' + ty + '\" marker-end=\"url(#ah)\"/>';\n svg += '<text class=\"sd-arrow-label\" x=\"' + (fx + ox + 10) + '\" y=\"' + ((fy + ty) / 2) + '\">' + conn.label + '</text>';\n } else {\n // Curved\n const midY = (fy + ty) / 2;\n const curveOff = offset * (tx > fx ? 1 : -1);\n svg += '<path class=\"sd-arrow\" d=\"M' + fx + ' ' + fy + ' C' + (fx + curveOff) + ' ' + midY + ' ' + (tx + curveOff) + ' ' + midY + ' ' + tx + ' ' + ty + '\" marker-end=\"url(#ah)\"/>';\n // Place label at curve midpoint with background\n const lx = (fx + tx) / 2 + curveOff / 2;\n const ly = midY - 6;\n svg += '<text class=\"sd-arrow-label\" x=\"' + lx + '\" y=\"' + ly + '\" text-anchor=\"middle\">' + conn.label + '</text>';\n }\n }\n\n // Nodes\n for (const [cid, pos] of Object.entries(nodePositions)) {\n const c = pos.comp;\n const x = pos.x;\n const y = pos.y;\n const totalLines = c.files.reduce((s, f) => s + f.lines, 0);\n\n svg += '<g class=\"sd-node\" onclick=\"window._selectComponent(\\\\'' + cid + '\\\\')\">';\n svg += '<rect class=\"sd-node-bg\" x=\"' + x + '\" y=\"' + y + '\" width=\"' + NW + '\" height=\"' + NH + '\" rx=\"8\" fill=\"#161b22\" stroke=\"' + c.color + '\" stroke-width=\"1.5\"/>';\n svg += '<rect x=\"' + x + '\" y=\"' + y + '\" width=\"' + NW + '\" height=\"4\" rx=\"8\" fill=\"' + c.color + '\" opacity=\"0.5\"/>';\n // Clip label text\n svg += '<clipPath id=\"clip-' + cid + '\"><rect x=\"' + (x + 36) + '\" y=\"' + (y + 10) + '\" width=\"' + (NW - 48) + '\" height=\"20\"/></clipPath>';\n svg += '<g transform=\"translate(' + (x + 14) + ',' + (y + 14) + ') scale(0.7)\" color=\"' + c.color + '\">' + c.icon + '</g>';\n svg += '<text x=\"' + (x + 40) + '\" y=\"' + (y + 28) + '\" fill=\"#e6edf3\" font-size=\"12\" font-weight=\"600\" clip-path=\"url(#clip-' + cid + ')\">' + c.label + '</text>';\n svg += '<text x=\"' + (x + 14) + '\" y=\"' + (y + 46) + '\" fill=\"#6b7280\" font-size=\"10\">' + c.count + ' file' + (c.count !== 1 ? 's' : '') + '</text>';\n svg += '<text x=\"' + (x + NW - 14) + '\" y=\"' + (y + 46) + '\" fill=\"#6b7280\" font-size=\"10\" text-anchor=\"end\">' + totalLines.toLocaleString() + ' lines</text>';\n svg += '<text x=\"' + (x + 14) + '\" y=\"' + (y + NH - 8) + '\" fill=\"#4b5563\" font-size=\"9\">' + c.desc + '</text>';\n svg += '</g>';\n }\n\n svg += '</g></svg>';\n\n // Zoom controls HTML\n let controls = '<div class=\"sysmap-controls\">';\n controls += '<button class=\"sysmap-ctrl-btn\" id=\"sysmap-zin\" title=\"Zoom in\">+</button>';\n controls += '<div class=\"sysmap-zoom-level\" id=\"sysmap-zlevel\">100%</div>';\n controls += '<button class=\"sysmap-ctrl-btn\" id=\"sysmap-zout\" title=\"Zoom out\">−</button>';\n controls += '<button class=\"sysmap-ctrl-btn\" id=\"sysmap-zfit\" title=\"Fit to screen\" style=\"margin-top:8px;font-size:13px\">Fit</button>';\n controls += '<button class=\"export-btn\" id=\"sysmap-export\" style=\"margin-top:8px;font-size:11px;width:36px;padding:6px 0;text-align:center\" title=\"Export as PNG\">PNG</button>';\n controls += '</div>';\n\n // Minimap HTML\n let minimap = '<div class=\"sysmap-minimap\" id=\"sysmap-minimap\">';\n minimap += '<svg viewBox=\"0 0 ' + totalW + ' ' + totalH + '\" preserveAspectRatio=\"xMidYMid meet\">';\n // Draw mini zones\n for (const z of zones) {\n minimap += '<rect x=\"' + z.x + '\" y=\"' + z.y + '\" width=\"' + z.w + '\" height=\"' + z.h + '\" fill=\"' + z.color + '\" opacity=\"0.15\" rx=\"8\"/>';\n }\n // Draw mini nodes\n for (const [cid, pos] of Object.entries(nodePositions)) {\n const c = pos.comp;\n minimap += '<rect x=\"' + pos.x + '\" y=\"' + pos.y + '\" width=\"' + NW + '\" height=\"' + NH + '\" fill=\"' + c.color + '\" opacity=\"0.4\" rx=\"4\"/>';\n }\n minimap += '<rect class=\"minimap-viewport\" id=\"minimap-vp\" x=\"0\" y=\"0\" width=\"200\" height=\"150\"/>';\n minimap += '</svg></div>';\n\n container.innerHTML = svg + controls + minimap;\n\n // ── Zoom & Pan ───────────────────────────────────\n const svgEl = document.getElementById('sysmap-svg');\n const world = document.getElementById('sysmap-world');\n const zLabel = document.getElementById('sysmap-zlevel');\n let zoom = 1;\n let panX = 0, panY = 0;\n let isPanning = false;\n let startX = 0, startY = 0;\n\n function applyTransform() {\n world.setAttribute('transform', 'translate(' + panX + ',' + panY + ') scale(' + zoom + ')');\n svgEl.setAttribute('width', Math.max(totalW * zoom + Math.abs(panX), container.clientWidth));\n svgEl.setAttribute('height', Math.max(totalH * zoom + Math.abs(panY), container.clientHeight));\n zLabel.textContent = Math.round(zoom * 100) + '%';\n }\n\n function setZoom(z, cx, cy) {\n const oldZoom = zoom;\n zoom = Math.max(0.2, Math.min(3, z));\n // Zoom towards center point\n if (cx !== undefined) {\n panX = cx - (cx - panX) * (zoom / oldZoom);\n panY = cy - (cy - panY) * (zoom / oldZoom);\n }\n applyTransform();\n }\n\n function fitToScreen() {\n const cw = container.clientWidth;\n const ch = container.clientHeight;\n const scaleX = (cw - 40) / totalW;\n const scaleY = (ch - 40) / totalH;\n zoom = Math.min(scaleX, scaleY, 1.5);\n panX = (cw - totalW * zoom) / 2;\n panY = 20;\n applyTransform();\n }\n\n // Initial fit\n svgEl.setAttribute('width', container.clientWidth);\n svgEl.setAttribute('height', container.clientHeight);\n fitToScreen();\n\n // Mouse wheel zoom\n container.addEventListener('wheel', (e) => {\n e.preventDefault();\n const rect = container.getBoundingClientRect();\n const cx = e.clientX - rect.left;\n const cy = e.clientY - rect.top;\n const delta = e.deltaY > 0 ? 0.9 : 1.1;\n setZoom(zoom * delta, cx, cy);\n }, { passive: false });\n\n // Pan via drag\n container.addEventListener('mousedown', (e) => {\n if (e.target.closest('.sd-node') || e.target.closest('.sysmap-controls')) return;\n isPanning = true;\n startX = e.clientX - panX;\n startY = e.clientY - panY;\n container.classList.add('grabbing');\n });\n window.addEventListener('mousemove', (e) => {\n if (!isPanning) return;\n panX = e.clientX - startX;\n panY = e.clientY - startY;\n applyTransform();\n });\n window.addEventListener('mouseup', () => {\n isPanning = false;\n container.classList.remove('grabbing');\n });\n\n // Button controls\n document.getElementById('sysmap-zin').addEventListener('click', () => {\n const rect = container.getBoundingClientRect();\n setZoom(zoom * 1.25, rect.width / 2, rect.height / 2);\n });\n document.getElementById('sysmap-zout').addEventListener('click', () => {\n const rect = container.getBoundingClientRect();\n setZoom(zoom * 0.8, rect.width / 2, rect.height / 2);\n });\n document.getElementById('sysmap-zfit').addEventListener('click', fitToScreen);\n\n // Minimap viewport update\n const minimapVp = document.getElementById('minimap-vp');\n function updateMinimap() {\n if (!minimapVp) return;\n const cw = container.clientWidth;\n const ch = container.clientHeight;\n // What part of the world is visible?\n const vx = -panX / zoom;\n const vy = -panY / zoom;\n const vw = cw / zoom;\n const vh = ch / zoom;\n minimapVp.setAttribute('x', String(Math.max(0, vx)));\n minimapVp.setAttribute('y', String(Math.max(0, vy)));\n minimapVp.setAttribute('width', String(Math.min(totalW, vw)));\n minimapVp.setAttribute('height', String(Math.min(totalH, vh)));\n }\n\n // Patch applyTransform to also update minimap\n const _origApply = applyTransform;\n applyTransform = function() {\n _origApply();\n updateMinimap();\n };\n updateMinimap();\n\n // PNG Export\n document.getElementById('sysmap-export').addEventListener('click', function() {\n try {\n const svgClone = svgEl.cloneNode(true);\n // Inline styles for export\n const styleText = document.querySelector('style').textContent;\n const styleEl = document.createElementNS('http://www.w3.org/2000/svg', 'style');\n styleEl.textContent = styleText;\n svgClone.insertBefore(styleEl, svgClone.firstChild);\n // Set proper dimensions\n const w = totalW * zoom + 100;\n const h = totalH * zoom + 100;\n svgClone.setAttribute('width', w);\n svgClone.setAttribute('height', h);\n svgClone.setAttribute('viewBox', '0 0 ' + totalW + ' ' + totalH);\n svgClone.querySelector('#sysmap-world').setAttribute('transform', '');\n\n const svgData = new XMLSerializer().serializeToString(svgClone);\n const blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n\n const img = new Image();\n img.onload = function() {\n const canvas = document.createElement('canvas');\n canvas.width = totalW * 2;\n canvas.height = totalH * 2;\n const ctx = canvas.getContext('2d');\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n URL.revokeObjectURL(url);\n\n canvas.toBlob(function(pngBlob) {\n const a = document.createElement('a');\n a.href = URL.createObjectURL(pngBlob);\n a.download = 'system-map.png';\n a.click();\n URL.revokeObjectURL(a.href);\n }, 'image/png');\n };\n img.src = url;\n } catch(ex) {\n console.error('PNG export failed:', ex);\n }\n });\n\n // Keyboard navigation for System Map\n function handleMapKeys(e) {\n if (currentView !== 'map') return;\n if (e.target.tagName === 'INPUT') return;\n const PAN_STEP = 50;\n const rect = container.getBoundingClientRect();\n switch(e.key) {\n case 'ArrowUp': panY += PAN_STEP; applyTransform(); e.preventDefault(); break;\n case 'ArrowDown': panY -= PAN_STEP; applyTransform(); e.preventDefault(); break;\n case 'ArrowLeft': panX += PAN_STEP; applyTransform(); e.preventDefault(); break;\n case 'ArrowRight': panX -= PAN_STEP; applyTransform(); e.preventDefault(); break;\n case '=': case '+': setZoom(zoom * 1.15, rect.width / 2, rect.height / 2); e.preventDefault(); break;\n case '-': setZoom(zoom * 0.87, rect.width / 2, rect.height / 2); e.preventDefault(); break;\n case '0': fitToScreen(); e.preventDefault(); break;\n }\n }\n document.addEventListener('keydown', handleMapKeys);\n }\n\n // ── List View ───────────────────────────────────────\n function buildList() {\n const container = document.getElementById('file-list');\n let html = '<div class=\"list-header\"><span>File</span><span>Description</span><span style=\"text-align:right\">Lines</span><span style=\"text-align:right\">Size</span></div>';\n\n for (const cat of CAT_ORDER) {\n const catFiles = byCategory[cat];\n if (!catFiles || catFiles.length === 0) continue;\n\n html += '<div class=\"list-group-header\"><span class=\"list-group-dot\" style=\"background:' + CAT_COLORS[cat] + '\"></span>' + CAT_LABELS[cat] + ' (' + catFiles.length + ')</div>';\n for (const f of catFiles) {\n html += '<div class=\"list-row\" onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<div class=\"list-path\">' + (f.role !== 'test' && f.role !== 'config' && f.role !== 'style' && f.role !== 'asset' ? '<span class=\"test-dot ' + (f.hasTests ? 'test-dot-covered' : 'test-dot-uncovered') + '\"></span>' : '') + f.path + '</div>';\n html += '<div class=\"list-desc\">' + f.description + '</div>';\n html += '<div class=\"list-lines\">' + f.lines + '</div>';\n html += '<div class=\"list-size\">' + formatBytes(f.size) + '</div>';\n html += '</div>';\n }\n }\n\n container.innerHTML = html;\n }\n\n // ── Utilities ───────────────────────────────────────\n function formatBytes(b) {\n if (b > 1024 * 1024) return (b / 1024 / 1024).toFixed(1) + ' MB';\n if (b > 1024) return (b / 1024).toFixed(1) + ' KB';\n return b + ' B';\n }\n\n // ── API Docs View ──────────────────────────────────\n function buildApiDocs() {\n const container = document.getElementById('apidocs-container');\n const docs = DATA.apiDocs || [];\n\n if (docs.length === 0) {\n container.innerHTML = '<div style=\"padding:60px;text-align:center;color:var(--text-muted)\">No API routes detected</div>';\n return;\n }\n\n // Group by base path\n const grouped = {};\n for (const d of docs) {\n const base = d.path.split('/').slice(0, 3).join('/') || d.path;\n if (!grouped[base]) grouped[base] = [];\n grouped[base].push(d);\n }\n\n let html = '<h2 style=\"font-size:16px;font-weight:600;margin-bottom:24px\">API Documentation</h2>';\n html += '<div style=\"color:var(--text-muted);font-size:12px;margin-bottom:20px\">' + docs.length + ' endpoints auto-detected</div>';\n\n for (const [base, endpoints] of Object.entries(grouped)) {\n html += '<div style=\"margin-bottom:24px\">';\n html += '<div style=\"font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.6px;color:var(--text-muted);margin-bottom:8px\">' + base + '</div>';\n for (const ep of endpoints) {\n html += '<div class=\"api-doc-card\">';\n html += '<div class=\"api-doc-header\">';\n html += '<span class=\"method method-' + ep.method.toLowerCase() + '\">' + ep.method + '</span>';\n html += '<span class=\"api-doc-path\">' + ep.path + '</span>';\n html += '</div>';\n html += '<div class=\"api-doc-handler\" onclick=\"window._selectByPath(\\\\'' + ep.handler + '\\\\')\">' + ep.handler + '</div>';\n if (ep.params.length > 0) {\n html += '<div class=\"api-doc-params\">';\n for (const p of ep.params) {\n html += '<span class=\"api-doc-param\">:' + p + '</span>';\n }\n html += '</div>';\n }\n html += '<div class=\"api-doc-desc\">' + ep.description + '</div>';\n html += '</div>';\n }\n html += '</div>';\n }\n\n container.innerHTML = html;\n }\n\n // ── Diff View ─────────────────────────────────────\n function buildDiff() {\n const container = document.getElementById('diff-container');\n if (!PREV) {\n container.innerHTML = '<div style=\"padding:60px;text-align:center;color:var(--text-muted)\">No previous scan to compare against.<br><span style=\"font-size:12px\">Run forge viz again to see changes.</span></div>';\n return;\n }\n\n const prevPaths = new Set(PREV.files.map(f => f.path));\n const currPaths = new Set(DATA.files.map(f => f.path));\n const prevByPath = {};\n for (const f of PREV.files) prevByPath[f.path] = f;\n const currByPath = {};\n for (const f of DATA.files) currByPath[f.path] = f;\n\n const added = DATA.files.filter(f => !prevPaths.has(f.path));\n const removed = PREV.files.filter(f => !currPaths.has(f.path));\n const changed = DATA.files.filter(f => {\n if (!prevByPath[f.path]) return false;\n return f.lines !== prevByPath[f.path].lines || f.size !== prevByPath[f.path].size;\n });\n\n const linesAdded = added.reduce((s, f) => s + f.lines, 0) + changed.reduce((s, f) => s + Math.max(0, f.lines - (prevByPath[f.path]?.lines || 0)), 0);\n const linesRemoved = removed.reduce((s, f) => s + f.lines, 0) + changed.reduce((s, f) => s + Math.max(0, (prevByPath[f.path]?.lines || 0) - f.lines), 0);\n\n const prevTs = PREV.scanTimestamp ? new Date(PREV.scanTimestamp).toLocaleString() : 'unknown';\n const currTs = DATA.scanTimestamp ? new Date(DATA.scanTimestamp).toLocaleString() : 'now';\n\n let html = '<h2 style=\"font-size:16px;font-weight:600;margin-bottom:8px\">Changes Since Last Scan</h2>';\n html += '<div style=\"color:var(--text-muted);font-size:12px;margin-bottom:24px\">' + prevTs + ' → ' + currTs + '</div>';\n\n html += '<div class=\"diff-summary\">';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val diff-added\">+' + added.length + '</div><div class=\"diff-stat-label\">Files Added</div></div>';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val diff-removed\">-' + removed.length + '</div><div class=\"diff-stat-label\">Files Removed</div></div>';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val diff-changed\">' + changed.length + '</div><div class=\"diff-stat-label\">Files Changed</div></div>';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val diff-added\">+' + linesAdded + '</div><div class=\"diff-stat-label\">Lines Added</div></div>';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val diff-removed\">-' + linesRemoved + '</div><div class=\"diff-stat-label\">Lines Removed</div></div>';\n html += '<div class=\"diff-stat-card\"><div class=\"diff-stat-val\">' + DATA.totalFiles + '</div><div class=\"diff-stat-label\">Total Files Now</div></div>';\n html += '</div>';\n\n // Health score change\n if (PREV.healthScore) {\n const delta = DATA.healthScore.score - PREV.healthScore.score;\n const arrow = delta > 0 ? '↑' : delta < 0 ? '↓' : '=';\n const dColor = delta > 0 ? 'var(--green)' : delta < 0 ? 'var(--red)' : 'var(--text-muted)';\n html += '<div class=\"insight-card\" style=\"margin-bottom:24px\">';\n html += '<h3>Health Score Change</h3>';\n html += '<div style=\"font-size:20px;font-weight:700;color:' + dColor + '\">' + PREV.healthScore.grade + ' → ' + DATA.healthScore.grade + ' <span style=\"font-size:14px\">(' + (delta > 0 ? '+' : '') + delta + ' ' + arrow + ')</span></div>';\n html += '</div>';\n }\n\n if (added.length > 0) {\n html += '<div class=\"overview-section-title diff-added\">Added Files</div>';\n html += '<div class=\"diff-file-list\">';\n for (const f of added) {\n html += '<div class=\"diff-file-item\" onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<span class=\"diff-badge diff-badge-added\">NEW</span>';\n html += '<span style=\"font-family:\\\\'SF Mono\\\\',monospace;font-size:12px\">' + f.path + '</span>';\n html += '<span style=\"color:var(--text-muted);font-size:11px;margin-left:auto\">' + f.lines + ' lines</span>';\n html += '</div>';\n }\n html += '</div>';\n }\n\n if (removed.length > 0) {\n html += '<div class=\"overview-section-title diff-removed\" style=\"margin-top:20px\">Removed Files</div>';\n html += '<div class=\"diff-file-list\">';\n for (const f of removed) {\n html += '<div class=\"diff-file-item\">';\n html += '<span class=\"diff-badge diff-badge-removed\">DEL</span>';\n html += '<span style=\"font-family:\\\\'SF Mono\\\\',monospace;font-size:12px\">' + f.path + '</span>';\n html += '<span style=\"color:var(--text-muted);font-size:11px;margin-left:auto\">' + f.lines + ' lines</span>';\n html += '</div>';\n }\n html += '</div>';\n }\n\n if (changed.length > 0) {\n html += '<div class=\"overview-section-title diff-changed\" style=\"margin-top:20px\">Changed Files</div>';\n html += '<div class=\"diff-file-list\">';\n for (const f of changed) {\n const prev = prevByPath[f.path];\n const lineDelta = f.lines - (prev ? prev.lines : 0);\n html += '<div class=\"diff-file-item\" onclick=\"window._selectByPath(\\\\'' + f.path + '\\\\')\">';\n html += '<span class=\"diff-badge diff-badge-changed\">MOD</span>';\n html += '<span style=\"font-family:\\\\'SF Mono\\\\',monospace;font-size:12px\">' + f.path + '</span>';\n html += '<span style=\"color:' + (lineDelta >= 0 ? 'var(--green)' : 'var(--red)') + ';font-size:11px;margin-left:auto\">' + (lineDelta >= 0 ? '+' : '') + lineDelta + ' lines</span>';\n html += '</div>';\n }\n html += '</div>';\n }\n\n container.innerHTML = html;\n }\n\n // ── Init ────────────────────────────────────────────\n buildOverview();\n\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n document.getElementById('detail-panel').classList.add('hidden');\n selectedFile = null;\n document.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));\n }\n if (e.key === '/' && e.target.tagName !== 'INPUT') {\n e.preventDefault();\n document.getElementById('search').focus();\n }\n });\n})();\n`;\n","import chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\nimport readline from \"readline\";\nimport inquirer from \"inquirer\";\nimport type {\n Plan,\n Story,\n ForgeConfig,\n QueuedChange,\n} from \"../../types/plan.js\";\nimport { Orchestrator } from \"../orchestrator/index.js\";\nimport { Worker, type WorkerProgressCallback, type WorkerUsage } from \"../worker/index.js\";\nimport { GitManager } from \"../git/index.js\";\nimport { stateManager } from \"../../state/index.js\";\nimport { playSound } from \"../utils/sound.js\";\nimport { GitHubSync } from \"../github/index.js\";\nimport { buildAttachmentPrompt, type Attachment } from \"../utils/attachments.js\";\nimport { getAdapter } from \"../adapters/index.js\";\nimport { estimateCost, formatCostEstimate } from \"../utils/cost.js\";\nimport { generateVisualization } from \"../visualizer/index.js\";\n\n// ============================================================\n// Autonomous Pipeline\n// Runs plan > design > build > review with no human gates\n// (except a review gate after build).\n// Shows live progress with elapsed timer.\n// Type messages anytime — queued and handled between stories.\n// Independent stories are built in parallel.\n// ============================================================\n\nexport interface AutoPipelineOptions {\n workingDir?: string;\n sandbox?: boolean;\n yes?: boolean;\n allowedDomains?: string[];\n quiet?: boolean;\n mute?: boolean;\n deploy?: boolean;\n skipDesign?: boolean;\n skipTests?: boolean;\n attachments?: Attachment[];\n}\n\nexport class AutoPipeline {\n private orchestrator: Orchestrator;\n private worker: Worker;\n private git: GitManager;\n private config: ForgeConfig;\n private plan: Plan | null = null;\n private options: AutoPipelineOptions;\n private chatQueue: QueuedChange[] = [];\n private rl: readline.Interface | null = null;\n private startTime: number = 0;\n private totalUsage: WorkerUsage = { inputTokens: 0, outputTokens: 0, costUsd: 0, durationMs: 0 };\n private shuttingDown = false;\n private activeSpinner: Ora | null = null;\n private tagCounter = 1;\n\n constructor(config: ForgeConfig, options: AutoPipelineOptions = {}) {\n this.config = config;\n this.options = options;\n this.orchestrator = new Orchestrator(config);\n this.worker = new Worker(\n config,\n {\n sandbox: options.sandbox ?? true,\n yes: options.yes ?? false,\n workingDir: options.workingDir,\n allowedDomains: options.allowedDomains,\n },\n options.mute,\n );\n this.git = new GitManager();\n this.setupGracefulShutdown();\n }\n\n // ── Graceful Shutdown ────────────────────────────────────\n\n private setupGracefulShutdown(): void {\n const handler = async () => {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n\n // Stop spinner so shutdown message is visible\n if (this.activeSpinner?.isSpinning) {\n this.activeSpinner.stop();\n }\n\n console.log(chalk.yellow(\"\\n\\n Interrupted — saving progress...\"));\n\n try {\n if (this.plan) {\n await stateManager.savePlan(this.plan);\n await this.git.commitAll(\"forge: save progress (interrupted)\");\n console.log(chalk.dim(\" Progress saved. Resume with: forge resume\\n\"));\n }\n } catch {\n console.log(chalk.dim(\" Could not save progress.\\n\"));\n }\n\n this.stopChatListener();\n process.exit(130);\n };\n\n process.on(\"SIGINT\", handler);\n // SIGTERM is unreliable on Windows — only register on Unix\n if (process.platform !== \"win32\") {\n process.on(\"SIGTERM\", handler);\n }\n }\n\n // ── Resume an interrupted sprint ──────────────────────────\n\n async resume(existingPlan: Plan): Promise<{ success: boolean; plan: Plan; errors: string[] }> {\n const errors: string[] = [];\n this.startTime = Date.now();\n this.plan = existingPlan;\n\n console.log(chalk.bold(\"\\n forge\") + chalk.dim(\" resume\"));\n console.log(chalk.dim(` Continuing from last checkpoint\\n`));\n\n this.startChatListener();\n await this.git.ensureRepo();\n await this.git.ensureMainBranch();\n\n const allStories = this.getAllStories(this.plan);\n const needsDesign = allStories.some(\n (s) => s.status === \"planned\" && (s.type === \"ui\" || s.type === \"fullstack\")\n );\n const needsBuild = allStories.some(\n (s) => s.status === \"planned\" || s.status === \"design-approved\"\n );\n const needsTest = allStories.some((s) => s.status === \"testing\");\n const needsReview = allStories.some((s) => s.status === \"reviewing\");\n\n if (!needsDesign && !needsBuild && !needsTest && !needsReview) {\n console.log(chalk.green(\" Nothing to resume — all stories are complete or blocked.\\n\"));\n this.stopChatListener();\n return { success: true, plan: this.plan, errors };\n }\n\n // Design (only stories not yet designed)\n if (needsDesign && !this.options.skipDesign) {\n try { await this.runDesignPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"design\");\n errors.push(`Design: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // Build (only stories not yet built)\n if (needsBuild) {\n try { await this.runBuildPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"build\");\n errors.push(`Build: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // Test (stories that just finished building)\n if (needsTest || needsBuild) {\n try { await this.runTestPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"test\");\n errors.push(`Test: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // Review (only stories awaiting review)\n if (needsReview || needsBuild) {\n const gateAction = await this.reviewGate();\n if (gateAction === \"abort\") {\n this.stopChatListener();\n this.printSummary(errors);\n return { success: false, plan: this.plan, errors: [...errors, \"Aborted\"] };\n }\n if (gateAction !== \"skip\") {\n try { await this.runReviewPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"review\");\n errors.push(`Review: ${err instanceof Error ? err.message : err}`);\n }\n }\n }\n\n // Auto-push on resume too\n await this.autoPush();\n\n this.stopChatListener();\n this.printSummary(errors);\n await stateManager.updatePhase(\"done\");\n\n const failedCount = allStories.filter((s) => s.status === \"blocked\").length;\n return { success: errors.length === 0 && failedCount === 0, plan: this.plan, errors };\n }\n\n // ── Full Autonomous Sprint ─────────────────────────────────\n\n async run(description: string): Promise<{ success: boolean; plan: Plan | null; errors: string[] }> {\n const errors: string[] = [];\n this.startTime = Date.now();\n\n console.log(chalk.bold(\"\\n forge\") + chalk.dim(\" auto\"));\n const flags = [\n `sandbox ${this.options.sandbox !== false ? \"on\" : \"off\"}`,\n ...(this.options.yes ? [\"auto-approve on\"] : []),\n ].join(\" · \");\n console.log(chalk.dim(` ${flags} · type a message anytime to queue feedback\\n`));\n\n this.startChatListener();\n\n await this.git.ensureRepo();\n await this.git.ensureMainBranch();\n\n // ── Plan ───────────────────────────────────────────────\n const planSpinner = ora({ text: `${this.elapsed()} Planning...`, indent: 2 }).start();\n this.activeSpinner = planSpinner;\n try {\n const attachmentContext = buildAttachmentPrompt(this.options.attachments || []);\n this.plan = await this.orchestrator.generatePlan(description + attachmentContext);\n planSpinner.succeed(`${this.elapsed()} Plan ready`);\n this.activeSpinner = null;\n this.displayPlan(this.plan);\n\n // Show cost estimate\n const costEst = estimateCost(this.plan, this.config.model);\n console.log(chalk.dim(` Estimated cost: ~$${costEst.estimatedCostUsd.toFixed(2)} (${this.config.model})\\n`));\n\n await stateManager.savePlan(this.plan);\n await stateManager.updatePhase(\"plan\");\n await this.git.commitState(\"Sprint plan\");\n await this.git.tag(\"forge/v0.0-plan\");\n\n // Sync to GitHub Issues if enabled\n await this.syncToGitHub(this.plan);\n } catch (err) {\n planSpinner.fail(`${this.elapsed()} Planning failed`);\n this.activeSpinner = null;\n const msg = err instanceof Error ? err.message : String(err);\n console.log(chalk.red(`\\n ${msg}\\n`));\n if (!msg.includes(\"Possible fixes\")) {\n console.log(chalk.dim(\" Possible fixes:\"));\n console.log(chalk.dim(\" 1. Run: claude login\"));\n console.log(chalk.dim(\" 2. Check your internet connection\"));\n console.log(chalk.dim(\" 3. Run: forge doctor\\n\"));\n }\n this.stopChatListener();\n return { success: false, plan: null, errors: [`Plan: ${msg}`] };\n }\n\n this.showInputHint();\n\n // ── Design ─────────────────────────────────────────────\n if (this.options.skipDesign) {\n // Mark all stories as ready for build (skip design = treat as approved)\n for (const story of this.getAllStories(this.plan)) {\n if (story.status === \"planned\") {\n story.designApproved = true;\n }\n }\n console.log(chalk.dim(`\\n Design skipped (--skip-design)\\n`));\n } else {\n try { await this.runDesignPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"design\");\n errors.push(`Design: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // ── Build ──────────────────────────────────────────────\n try { await this.runBuildPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"build\");\n errors.push(`Build: ${err instanceof Error ? err.message : err}`);\n }\n\n // ── Test ────────────────────────────────────────────────\n if (this.options.skipTests) {\n console.log(chalk.dim(`\\n Test skipped (--skip-tests)\\n`));\n } else {\n try { await this.runTestPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"test\");\n errors.push(`Test: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // ── README ─────────────────────────────────────────────\n try { await this.generateReadme(this.plan); }\n catch (err) { errors.push(`README: ${err instanceof Error ? err.message : err}`); }\n\n // ── Review Gate ────────────────────────────────────────\n const gateAction = await this.reviewGate();\n\n if (gateAction === \"abort\") {\n this.stopChatListener();\n this.printSummary(errors);\n await stateManager.updatePhase(\"done\");\n return { success: false, plan: this.plan, errors: [...errors, \"Aborted by user\"] };\n }\n\n // ── Review ─────────────────────────────────────────────\n if (gateAction !== \"skip\") {\n try { await this.runReviewPhase(this.plan); }\n catch (err) {\n await this.saveProgressOnError(\"review\");\n errors.push(`Review: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n // ── Deploy (optional) ──────────────────────────────────\n if (this.options.deploy) {\n try { await this.runDeployPhase(this.plan); }\n catch (err) { errors.push(`Deploy: ${err instanceof Error ? err.message : err}`); }\n }\n\n // ── Project Map ────────────────────────────────────────\n try {\n const vizSpinner = ora({ text: `${this.elapsed()} Generating project map...`, indent: 2 }).start();\n this.activeSpinner = vizSpinner;\n const { graph } = await generateVisualization({ open: false });\n vizSpinner.succeed(`${this.elapsed()} Project map (${graph.totalFiles} files)`);\n this.activeSpinner = null;\n } catch {\n // Best-effort — don't fail the sprint for viz\n }\n\n // ── Auto-push to GitHub ───────────────────────────────\n await this.autoPush();\n\n this.stopChatListener();\n this.printSummary(errors);\n await stateManager.updatePhase(\"done\");\n\n const allStories = this.getAllStories(this.plan);\n const failedCount = allStories.filter((s) => s.status === \"blocked\").length;\n return { success: errors.length === 0 && failedCount === 0, plan: this.plan, errors };\n }\n\n /** Save current progress when a phase fails — prevents data loss */\n private async saveProgressOnError(phase: string): Promise<void> {\n try {\n if (this.plan) {\n await stateManager.savePlan(this.plan);\n await stateManager.updatePhase(phase as any);\n await this.git.commitAll(`forge: save progress (${phase} interrupted)`);\n console.log(chalk.dim(` Progress saved. Resume with: forge resume\\n`));\n }\n } catch {\n // Best-effort — don't let save failure mask the original error\n }\n }\n\n // ── Elapsed Timer ─────────────────────────────────────────\n\n private elapsed(): string {\n const sec = Math.floor((Date.now() - this.startTime) / 1000);\n const minutes = Math.floor(sec / 60);\n const seconds = sec % 60;\n return chalk.dim(`[${minutes}:${seconds.toString().padStart(2, \"0\")}]`);\n }\n\n // ── Progress Helper with Timer ────────────────────────────\n\n private makeProgress(\n spinner: Ora,\n storyTitle: string\n ): { onProgress: WorkerProgressCallback; stop: () => void } {\n if (this.options.quiet) {\n return { onProgress: () => {}, stop: () => {} };\n }\n\n let lastDetail = \"\";\n\n const update = () => {\n if (!spinner.isSpinning) return;\n const prefix = this.elapsed();\n const detail = lastDetail ? \" \" + chalk.dim(lastDetail) : \"\";\n spinner.text = `${prefix} ${storyTitle}${detail}`;\n };\n\n // Tick every 5 seconds — keeps timer fresh without excessive terminal redraws\n const interval = setInterval(update, 5000);\n\n const onProgress: WorkerProgressCallback = (event) => {\n switch (event.type) {\n case \"tool_use\":\n lastDetail = event.content;\n break;\n case \"tool_running\":\n if (event.elapsed && event.elapsed > 3) {\n lastDetail = `${event.tool} (${Math.round(event.elapsed!)}s)`;\n }\n break;\n case \"tool_done\":\n lastDetail = event.content.slice(0, 80);\n break;\n }\n update();\n };\n\n return {\n onProgress,\n stop: () => clearInterval(interval),\n };\n }\n\n // ── Input Hint ────────────────────────────────────────────\n\n private showInputHint(): void {\n if (process.stdout.isTTY && !this.options.quiet) {\n // Stop spinner briefly so the hint is visible\n const wasSpinning = this.activeSpinner?.isSpinning ?? false;\n const spinnerText = this.activeSpinner?.text ?? \"\";\n if (wasSpinning) this.activeSpinner!.stop();\n\n process.stdout.write(chalk.dim(\"\\n ─ type a message and press enter ─\\n > \"));\n\n if (wasSpinning && this.activeSpinner) {\n this.activeSpinner.start(spinnerText);\n }\n }\n }\n\n // ── Dependency Grouping ───────────────────────────────────\n\n private groupByDependency(stories: Story[]): Story[][] {\n const batches: Story[][] = [];\n const completed = new Set<string>();\n const remaining = new Set(stories.map((s) => s.id));\n const storyMap = new Map(stories.map((s) => [s.id, s]));\n\n // Also treat already-done stories as completed deps\n if (this.plan) {\n for (const s of this.getAllStories(this.plan)) {\n if (s.status === \"done\" || s.status === \"reviewing\") {\n completed.add(s.id);\n }\n }\n }\n\n let iterations = 0;\n const maxIterations = stories.length + 1; // Safety: prevent infinite loop on circular deps\n\n while (remaining.size > 0 && iterations < maxIterations) {\n iterations++;\n\n const batch: Story[] = [];\n for (const id of remaining) {\n const story = storyMap.get(id)!;\n const depsReady = story.dependencies.every(\n (dep) => completed.has(dep) || !remaining.has(dep) // dep done or not in buildable set\n );\n if (depsReady) {\n batch.push(story);\n }\n }\n\n if (batch.length === 0) {\n // Circular deps — break the cycle by running remaining stories sequentially\n for (const id of remaining) {\n batches.push([storyMap.get(id)!]);\n }\n break;\n }\n\n batches.push(batch);\n for (const s of batch) {\n completed.add(s.id);\n remaining.delete(s.id);\n }\n }\n\n return batches;\n }\n\n // ── Design Phase (parallel) ───────────────────────────────\n\n private async runDesignPhase(plan: Plan): Promise<void> {\n const uiStories = this.getStoriesByType(plan, [\"ui\", \"fullstack\"]).filter(\n (s) => s.status === \"planned\"\n );\n if (uiStories.length === 0) return;\n\n console.log(chalk.bold(`\\n Design`) + chalk.dim(` · ${uiStories.length} stories\\n`));\n\n // Design stories are independent — run in parallel\n if (uiStories.length > 1) {\n await Promise.all(\n uiStories.map((story, i) =>\n this.designSingleStory(story, plan, i + 1, uiStories.length)\n )\n );\n } else {\n await this.designSingleStory(uiStories[0], plan, 1, 1);\n }\n\n await stateManager.savePlan(plan);\n await stateManager.updatePhase(\"design\");\n await this.git.commitState(\"Designs complete\");\n await this.git.tag(\"forge/v0.1-designs\");\n\n await this.processChatQueue();\n this.showInputHint();\n }\n\n private async designSingleStory(\n story: Story,\n plan: Plan,\n index: number,\n total: number\n ): Promise<void> {\n const label = `[${index}/${total}] ${story.title}`;\n const spinner = ora({ text: `${this.elapsed()} ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n story.status = \"designing\";\n\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"design\", { plan });\n const result = await this.worker.run(\"design\", prompt, {\n onProgress: progress.onProgress,\n });\n\n this.addUsage(result.usage);\n const tokens = chalk.dim(\n `${this.formatTokens(result.usage.inputTokens)} in / ${this.formatTokens(result.usage.outputTokens)} out`\n );\n\n if (result.success) {\n spinner.succeed(`${this.elapsed()} ${label} ${tokens}`);\n story.designApproved = true;\n story.status = \"design-approved\";\n } else {\n spinner.warn(`${this.elapsed()} ${label}` + chalk.dim(\" skipped\") + ` ${tokens}`);\n // Still mark as design-approved so build can proceed\n story.designApproved = false;\n story.status = \"design-approved\";\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n }\n\n // ── Build Phase (parallel by dependency) ──────────────────\n\n private async runBuildPhase(plan: Plan): Promise<void> {\n const buildable = this.getAllStories(plan).filter(\n (s) => s.status === \"design-approved\" || s.status === \"planned\"\n );\n if (buildable.length === 0) return;\n\n console.log(chalk.bold(`\\n Build`) + chalk.dim(` · ${buildable.length} stories\\n`));\n\n const batches = this.groupByDependency(buildable);\n let storyIndex = 0;\n\n for (const batch of batches) {\n if (batch.length === 1) {\n storyIndex++;\n await this.buildSingleStory(batch[0], plan, storyIndex, buildable.length, false);\n } else {\n // Parallel batch — stories run concurrently\n console.log(chalk.dim(` ${this.elapsed()} parallel: ${batch.map((s) => s.title).join(\", \")}`));\n\n // Capture indices before async work\n const indexedBatch = batch.map((story) => {\n storyIndex++;\n return { story, index: storyIndex };\n });\n\n // Run in parallel but do NOT let parallel builds commit individually\n await Promise.all(\n indexedBatch.map(({ story, index }) =>\n this.buildSingleStory(story, plan, index, buildable.length, true)\n )\n );\n\n // Commit all parallel changes together\n const builtTitles = batch\n .filter((s) => s.status === \"reviewing\")\n .map((s) => s.title);\n if (builtTitles.length > 0) {\n await this.git.commitAll(`feat: ${builtTitles.join(\", \")}`);\n }\n }\n\n await stateManager.savePlan(plan);\n await this.processChatQueue();\n this.showInputHint();\n }\n\n await stateManager.updatePhase(\"build\");\n }\n\n private async buildSingleStory(\n story: Story,\n plan: Plan,\n index: number,\n total: number,\n parallel: boolean\n ): Promise<void> {\n const label = `[${index}/${total}] ${story.title}`;\n const spinner = ora({ text: `${this.elapsed()} ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n // Snapshot before building (capture current HEAD for undo)\n const headBefore = await this.git.getHead();\n await stateManager.saveSnapshot({\n action: \"build\",\n storyId: story.id,\n branch: \"main\",\n commitBefore: headBefore,\n });\n\n story.status = \"building\";\n\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"build\", {\n plan,\n designMeta: story.designApproved ? { storyId: story.id } : undefined,\n });\n\n const result = await this.worker.run(\"build\", prompt, {\n onProgress: progress.onProgress,\n });\n\n this.addUsage(result.usage);\n const tokens = chalk.dim(\n `${this.formatTokens(result.usage.inputTokens)} in / ${this.formatTokens(result.usage.outputTokens)} out`\n );\n\n if (result.success) {\n if (!parallel) {\n await this.git.commitAll(`feat: ${story.title}`);\n }\n story.status = \"reviewing\";\n spinner.succeed(\n `${this.elapsed()} ${label}` +\n chalk.dim(` · ${result.filesCreated.length} files`) +\n ` ${tokens}`\n );\n } else {\n story.status = \"blocked\";\n spinner.fail(`${this.elapsed()} ${label} ${tokens}`);\n for (const error of result.errors) {\n console.log(chalk.red(` ${error}`));\n }\n // Show max-turns warning prominently\n if (result.errors.some((e) => e.includes(\"safety cap\"))) {\n console.log(chalk.yellow(` Tip: Story may be too large. Try splitting it into smaller stories.`));\n }\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n }\n\n // ── Test Phase ──────────────────────────────────────────\n\n private async runTestPhase(plan: Plan): Promise<void> {\n // Test stories that just finished building (status \"reviewing\" means built, ready for QA)\n const testable = this.getAllStories(plan).filter(\n (s) => s.status === \"reviewing\" || s.status === \"testing\"\n );\n if (testable.length === 0) return;\n\n let adapter: any = null;\n try { adapter = getAdapter(this.config.framework); } catch { /* ignore */ }\n\n // Skip test phase if adapter has no testCommand and framework isn't generic\n if (adapter && !adapter.testCommand && adapter.id !== \"generic\") return;\n\n console.log(chalk.bold(`\\n Test`) + chalk.dim(` · ${testable.length} stories\\n`));\n\n for (let i = 0; i < testable.length; i++) {\n const story = testable[i];\n const label = `[${i + 1}/${testable.length}] ${story.title}`;\n const spinner = ora({ text: `${this.elapsed()} ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n story.status = \"testing\";\n\n const testPrompt = this.craftTestPrompt(story, plan, adapter);\n const result = await this.worker.run(\"test\", testPrompt, {\n onProgress: progress.onProgress,\n });\n\n this.addUsage(result.usage);\n const tokens = chalk.dim(\n `${this.formatTokens(result.usage.inputTokens)} in / ${this.formatTokens(result.usage.outputTokens)} out`\n );\n\n if (result.success) {\n await this.git.commitAll(`test: ${story.title}`);\n story.status = \"reviewing\"; // Move back to reviewing for QA phase\n spinner.succeed(\n `${this.elapsed()} ${label}` +\n chalk.dim(` · ${result.filesCreated.length} test files`) +\n ` ${tokens}`\n );\n } else {\n // Tests failed but don't block — still move to review\n story.status = \"reviewing\";\n spinner.warn(`${this.elapsed()} ${label}` + chalk.dim(\" tests incomplete\") + ` ${tokens}`);\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n }\n\n await stateManager.savePlan(plan);\n await stateManager.updatePhase(\"test\");\n await this.git.commitState(\"Tests generated\");\n }\n\n private craftTestPrompt(story: Story, plan: Plan, adapter: any): string {\n const isGeneric = !adapter || adapter.id === \"generic\";\n\n const testInfo = isGeneric\n ? `\n Detect the test framework from the project's config files.\n Common setups: Vitest, Jest, pytest, Flutter test, Go test, Cargo test.\n Install test dependencies if needed.\n `\n : `\n Test framework: ${adapter.testFramework || \"Vitest\"}\n Test command: ${adapter.testCommand || \"npm test\"}\n `;\n\n return `\n Generate tests for: \"${story.title}\"\n\n Description: ${story.description}\n App: ${plan.project} (${plan.framework})\n\n ${testInfo}\n\n Steps:\n 1. Read the source files for this story to understand what was built\n 2. Create test files co-located with the source (e.g., Component.test.tsx)\n 3. Write meaningful tests covering:\n - Happy path (main functionality works)\n - Error handling (graceful failures)\n - Edge cases (empty data, boundary values)\n - User interactions (if UI)\n 4. Run the test suite — all tests must pass\n 5. If tests fail, fix them (fix the test, not the source, unless there's a genuine bug)\n\n Do NOT write snapshot tests.\n Focus on behavior, not implementation details.\n `;\n }\n\n // ── README Generation ─────────────────────────────────────\n\n private async generateReadme(plan: Plan): Promise<void> {\n const builtStories = this.getAllStories(plan).filter(\n (s) => s.status === \"reviewing\" || s.status === \"done\"\n );\n if (builtStories.length === 0) return;\n\n const label = \"README.md\";\n const spinner = ora({ text: `${this.elapsed()} Generating ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n const storyList = builtStories\n .map((s) => `- ${s.title}: ${s.description}`)\n .join(\"\\n\");\n\n const prompt = `\n Generate a README.md for this project.\n\n Project: ${plan.project}\n Description: ${plan.description}\n Framework: ${plan.framework}\n\n Features built:\n ${storyList}\n\n Read the actual codebase to understand the project structure.\n\n Include these sections:\n - Project name and description\n - Key features (from the stories above)\n - Tech stack\n - Getting started (install, dev server, build)\n - Project structure (based on actual files you can see)\n\n Write the README.md file in the project root.\n Keep it concise and professional.\n `;\n\n const result = await this.worker.run(\"build\", prompt, {\n onProgress: progress.onProgress,\n });\n this.addUsage(result.usage);\n\n if (result.success) {\n await this.git.commitAll(\"docs: generate README.md\");\n spinner.succeed(`${this.elapsed()} ${label}`);\n } else {\n spinner.warn(`${this.elapsed()} ${label}` + chalk.dim(\" skipped\"));\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n }\n\n // ── Review Gate ───────────────────────────────────────────\n\n private async reviewGate(): Promise<\"continue\" | \"skip\" | \"abort\"> {\n if (this.options.yes) return \"continue\";\n if (!process.stdout.isTTY) return \"continue\";\n\n // Notify user that build is done\n if (!this.options.mute) {\n playSound();\n }\n\n // Stop spinner and chat listener so inquirer can take over stdin cleanly\n if (this.activeSpinner?.isSpinning) {\n this.activeSpinner.stop();\n this.activeSpinner = null;\n }\n this.stopChatListener();\n\n console.log(\"\");\n\n const { action } = await inquirer.prompt([\n {\n type: \"list\",\n name: \"action\",\n message: `${this.elapsed()} Build complete. What next?`,\n choices: [\n { name: \"Continue to review\", value: \"continue\" },\n { name: \"Skip review\", value: \"skip\" },\n { name: \"Abort\", value: \"abort\" },\n ],\n },\n ]);\n\n // Resume readline\n this.startChatListener();\n\n return action;\n }\n\n // ── Review Phase ──────────────────────────────────────────\n\n private async runReviewPhase(plan: Plan): Promise<void> {\n const reviewable = this.getAllStories(plan).filter((s) => s.status === \"reviewing\");\n if (reviewable.length === 0) return;\n\n console.log(chalk.bold(`\\n Review`) + chalk.dim(` · ${reviewable.length} stories\\n`));\n\n for (let i = 0; i < reviewable.length; i++) {\n const story = reviewable[i];\n const label = `[${i + 1}/${reviewable.length}] ${story.title}`;\n const spinner = ora({ text: `${this.elapsed()} ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n const prompt = this.orchestrator.craftWorkerPrompt(story, \"review\", {\n plan,\n designMeta: story.designApproved ? { storyId: story.id } : undefined,\n });\n\n const result = await this.worker.run(\"review\", prompt, {\n onProgress: progress.onProgress,\n });\n\n this.addUsage(result.usage);\n const tokens = chalk.dim(\n `${this.formatTokens(result.usage.inputTokens)} in / ${this.formatTokens(result.usage.outputTokens)} out`\n );\n\n if (result.success) {\n const tagName = `forge/v0.${this.tagCounter++}-${story.id}`;\n await this.git.tag(tagName);\n story.tags.push(tagName);\n story.status = \"done\";\n spinner.succeed(`${this.elapsed()} ${label}` + chalk.dim(` [${tagName}]`) + ` ${tokens}`);\n } else {\n // Auto-fix attempt\n spinner.text = `${this.elapsed()} ${label}` + chalk.dim(\" fixing issues...\");\n const fixed = await this.autoFix(story, plan, result.summary, spinner, label);\n if (fixed) {\n const tagName = `forge/v0.${this.tagCounter++}-${story.id}`;\n await this.git.tag(tagName);\n story.tags.push(tagName);\n story.status = \"done\";\n spinner.succeed(`${this.elapsed()} ${label}` + chalk.dim(` [${tagName}] fixed`));\n } else {\n story.status = \"blocked\";\n spinner.fail(`${this.elapsed()} ${label}` + chalk.dim(\" blocked\"));\n }\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n\n await stateManager.savePlan(plan);\n await this.processChatQueue();\n this.showInputHint();\n }\n\n await stateManager.updatePhase(\"review\");\n }\n\n // ── Auto-Fix ──────────────────────────────────────────────\n\n private async autoFix(\n story: Story,\n _plan: Plan,\n reviewSummary: string,\n spinner: Ora,\n label: string\n ): Promise<boolean> {\n const fixPrompt = `\n The review found these issues:\n ${reviewSummary}\n\n Fix them. Make minimal changes. Then re-run build/lint/typecheck to verify.\n `;\n\n const fixProgress = this.makeProgress(spinner, label + chalk.dim(\" fix\"));\n try {\n const result = await this.worker.run(\"fix\", fixPrompt, {\n onProgress: fixProgress.onProgress,\n });\n\n if (result.success) {\n await this.git.commitAll(`fix: review issues in ${story.title}`);\n return true;\n }\n return false;\n } finally {\n fixProgress.stop();\n }\n }\n\n // ── Deploy Phase (optional) ───────────────────────────────\n\n private async runDeployPhase(plan: Plan): Promise<void> {\n console.log(chalk.bold(`\\n Deploy`) + chalk.dim(` · GitHub Pages\\n`));\n\n const label = \"GitHub Pages\";\n const spinner = ora({ text: `${this.elapsed()} Configuring ${label}`, indent: 2 }).start();\n this.activeSpinner = spinner;\n const progress = this.makeProgress(spinner, label);\n\n try {\n const framework = plan.framework || \"Next.js\";\n const prompt = `\n Set up GitHub Pages deployment for this ${framework} project.\n\n 1. Update next.config.js or next.config.ts to add: output: \"export\"\n (merge with existing config, do not overwrite other settings)\n 2. Create .github/workflows/deploy.yml for GitHub Pages deployment\n 3. The workflow should:\n - Trigger on push to main\n - Install dependencies with npm ci\n - Build with npm run build\n - Use actions/configure-pages, actions/upload-pages-artifact, actions/deploy-pages\n\n Do NOT run any git commands. Just create/update the configuration files.\n `;\n\n const result = await this.worker.run(\"build\", prompt, {\n onProgress: progress.onProgress,\n });\n this.addUsage(result.usage);\n\n if (result.success) {\n await this.git.commitAll(\"ci: add GitHub Pages deployment\");\n spinner.succeed(`${this.elapsed()} ${label} configured`);\n } else {\n spinner.warn(`${this.elapsed()} ${label}` + chalk.dim(\" skipped\"));\n }\n } finally {\n progress.stop();\n this.activeSpinner = null;\n }\n }\n\n // ── Chat Queue ────────────────────────────────────────────\n\n private startChatListener(): void {\n if (!process.stdin.isTTY) return;\n if (this.rl) return; // Already listening\n\n this.rl = readline.createInterface({\n input: process.stdin,\n terminal: false,\n });\n\n this.rl.on(\"line\", (line) => {\n const msg = line.trim();\n if (!msg) return;\n\n this.chatQueue.push({\n type: \"content-change\",\n message: msg,\n queuedAt: new Date().toISOString(),\n });\n\n const count = this.chatQueue.length;\n\n // Pause the active spinner so our output doesn't collide\n const wasSpinning = this.activeSpinner?.isSpinning ?? false;\n const spinnerText = this.activeSpinner?.text ?? \"\";\n if (wasSpinning) this.activeSpinner!.stop();\n\n // Clear the current line and show confirmation\n if (process.stdout.isTTY) {\n process.stdout.write(`\\r\\x1b[K`);\n }\n console.log(\n chalk.green(` [queued${count > 1 ? ` #${count}` : \"\"}]`) +\n chalk.dim(` ${msg}`) +\n chalk.dim(` — will process between stories`)\n );\n\n // Resume spinner if it was running\n if (wasSpinning && this.activeSpinner) {\n this.activeSpinner.start(spinnerText);\n }\n });\n\n // Show initial input hint on stderr so it doesn't interfere with spinner\n if (process.stderr.isTTY) {\n process.stderr.write(chalk.dim(\" Type a message anytime to queue feedback\\n\\n\"));\n }\n }\n\n private stopChatListener(): void {\n if (this.rl) {\n this.rl.close();\n this.rl = null;\n }\n }\n\n private async processChatQueue(): Promise<void> {\n if (this.chatQueue.length === 0) return;\n\n // Stop spinner while processing queue messages\n if (this.activeSpinner?.isSpinning) {\n this.activeSpinner.stop();\n }\n\n console.log(chalk.dim(`\\n Processing ${this.chatQueue.length} queued message${this.chatQueue.length > 1 ? \"s\" : \"\"}...\\n`));\n\n const messages = [...this.chatQueue];\n this.chatQueue = [];\n\n for (const change of messages) {\n try {\n const state = await stateManager.getState();\n const decision = await this.orchestrator.routeUserInput(change.message, state);\n\n if (decision.action === \"answer\" && decision.response) {\n console.log(chalk.white(` > ${change.message}`));\n console.log(chalk.dim(` ${decision.response}\\n`));\n } else if (decision.action === \"route-to-worker\" && decision.prompt) {\n const mode = decision.workerMode || \"fix\";\n const spinner = ora({ text: change.message, indent: 2 }).start();\n const result = await this.worker.run(mode, decision.prompt);\n if (result.success) {\n await this.git.commitAll(`fix: ${change.message}`);\n spinner.succeed(change.message);\n } else {\n spinner.fail(change.message);\n }\n } else if (decision.action === \"add-story\" && this.plan) {\n const newStory: Story = {\n id: `story-${Date.now()}`,\n title: decision.story?.title || change.message,\n description: decision.story?.description || change.message,\n type: (decision.story?.type as any) || \"fullstack\",\n status: \"planned\",\n branch: null,\n designApproved: false,\n tags: [],\n priority: 99,\n dependencies: [],\n };\n // Add to last epic\n if (this.plan.epics.length > 0) {\n this.plan.epics[this.plan.epics.length - 1].stories.push(newStory);\n console.log(chalk.dim(` + Story added: ${newStory.title}`));\n }\n }\n } catch (err) {\n console.log(chalk.dim(` Could not process: ${change.message}`));\n }\n }\n }\n\n // ── Summary ───────────────────────────────────────────────\n\n private printSummary(errors: string[]): void {\n const elapsedSec = (Date.now() - this.startTime) / 1000;\n const elapsed = elapsedSec >= 60\n ? `${Math.floor(elapsedSec / 60)}m ${Math.round(elapsedSec % 60)}s`\n : `${Math.round(elapsedSec)}s`;\n\n const allStories = this.getAllStories(this.plan!);\n const done = allStories.filter((s) => s.status === \"done\").length;\n const blocked = allStories.filter((s) => s.status === \"blocked\").length;\n const total = allStories.length;\n\n if (!this.options.mute) {\n playSound();\n }\n\n console.log(chalk.dim(\"\\n ─────────────────────────────────\"));\n console.log(chalk.bold(\" Done\") + chalk.dim(` in ${elapsed} · ${done}/${total} stories`));\n\n // Token usage\n const u = this.totalUsage;\n if (u.inputTokens > 0 || u.outputTokens > 0) {\n console.log(chalk.dim(\n ` ${this.formatTokens(u.inputTokens)} in / ${this.formatTokens(u.outputTokens)} out` +\n (u.costUsd > 0 ? ` · ${this.formatCost(u.costUsd)}` : \"\")\n ));\n }\n\n if (blocked > 0) {\n console.log(chalk.yellow(` ${blocked} blocked`) + chalk.dim(\" — \" + chalk.white(\"forge status\")));\n }\n\n for (const e of errors) {\n console.log(chalk.dim(` ! ${e}`));\n }\n\n console.log(\"\");\n }\n\n // ── Usage Tracking ────────────────────────────────────────\n\n private addUsage(usage: WorkerUsage): void {\n this.totalUsage.inputTokens += usage.inputTokens;\n this.totalUsage.outputTokens += usage.outputTokens;\n this.totalUsage.costUsd += usage.costUsd;\n this.totalUsage.durationMs += usage.durationMs;\n }\n\n private formatTokens(n: number): string {\n if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + \"M\";\n if (n >= 1_000) return (n / 1_000).toFixed(1) + \"k\";\n return String(n);\n }\n\n private formatCost(usd: number): string {\n return \"$\" + usd.toFixed(4);\n }\n\n // ── GitHub Sync ────────────────────────────────────────────\n\n private async syncToGitHub(plan: Plan): Promise<void> {\n if (!this.config.githubSync || !this.config.githubRepo) return;\n if (!GitHubSync.isAvailable()) return;\n\n try {\n const gh = new GitHubSync(this.config.githubRepo);\n await gh.ensureLabels();\n const { created, updated } = await gh.syncPlan(plan);\n if (created > 0 || updated > 0) {\n console.log(chalk.dim(` GitHub: ${created} issues created, ${updated} updated`));\n }\n } catch {\n // Silently skip — GitHub sync is best-effort\n }\n }\n\n // ── Auto-Push ───────────────────────────────────────────\n\n private async autoPush(): Promise<void> {\n const hasRemote = await this.git.hasRemote();\n if (!hasRemote) return; // No remote configured — skip silently\n\n const spinner = ora({ text: `${this.elapsed()} Pushing to GitHub...`, indent: 2 }).start();\n this.activeSpinner = spinner;\n\n try {\n await this.git.push();\n await this.git.pushTags();\n spinner.succeed(`${this.elapsed()} Pushed to GitHub`);\n } catch {\n spinner.warn(`${this.elapsed()} Push failed` + chalk.dim(\" — run forge push to retry\"));\n } finally {\n this.activeSpinner = null;\n }\n }\n\n // ── Helpers ───────────────────────────────────────────────\n\n private displayPlan(plan: Plan): void {\n console.log(chalk.bold(`\\n ${plan.project}`) + chalk.dim(` · ${plan.framework}`));\n const total = this.getAllStories(plan).length;\n console.log(chalk.dim(` ${total} stories across ${plan.epics.length} epics\\n`));\n\n for (const epic of plan.epics) {\n console.log(chalk.dim(` ${epic.title}`));\n for (const story of epic.stories) {\n const tag = story.type === \"ui\" ? \"ui\" : story.type === \"backend\" ? \"api\" : \"full\";\n console.log(` ${chalk.dim(`[${tag}]`)} ${story.title}`);\n }\n }\n console.log(\"\");\n }\n\n private getAllStories(plan: Plan): Story[] {\n return plan.epics.flatMap((e) => e.stories);\n }\n\n private getStoriesByType(plan: Plan, types: string[]): Story[] {\n return this.getAllStories(plan).filter((s) => types.includes(s.type));\n }\n}\n","import { spawnSync } from \"child_process\";\nimport type { Plan, Story } from \"../../types/plan.js\";\n\n// ============================================================\n// GitHub Sync\n// Creates and updates GitHub Issues for sprint stories.\n// Uses the `gh` CLI — no extra dependencies.\n// ============================================================\n\nexport class GitHubSync {\n private repo: string;\n\n constructor(repo: string) {\n this.repo = repo;\n }\n\n /** Check if `gh` CLI is installed and authenticated */\n static isAvailable(): boolean {\n const result = spawnSync(\"gh\", [\"auth\", \"status\"], { stdio: \"ignore\", shell: true });\n return result.status === 0;\n }\n\n /** Create GitHub labels for forge story tracking */\n async ensureLabels(): Promise<void> {\n const labels = [\n { name: \"forge\", color: \"000000\", description: \"Managed by ForgeAI\" },\n { name: \"type:ui\", color: \"7057ff\", description: \"UI component\" },\n { name: \"type:backend\", color: \"0075ca\", description: \"Backend/API\" },\n { name: \"type:fullstack\", color: \"008672\", description: \"Full stack\" },\n { name: \"status:planned\", color: \"d4c5f9\", description: \"Planned\" },\n { name: \"status:in-progress\", color: \"fbca04\", description: \"Building\" },\n { name: \"status:review\", color: \"f9d0c4\", description: \"In review\" },\n { name: \"status:done\", color: \"0e8a16\", description: \"Complete\" },\n { name: \"status:blocked\", color: \"e11d48\", description: \"Blocked\" },\n ];\n\n for (const label of labels) {\n spawnSync(\"gh\", [\n \"label\", \"create\", label.name,\n \"--repo\", this.repo,\n \"--color\", label.color,\n \"--description\", label.description,\n \"--force\",\n ], { stdio: \"ignore\", shell: true });\n }\n }\n\n /** Sync all stories in a plan to GitHub Issues */\n async syncPlan(plan: Plan): Promise<{ created: number; updated: number }> {\n let created = 0;\n let updated = 0;\n\n for (const epic of plan.epics) {\n for (const story of epic.stories) {\n const result = await this.syncStory(story, epic.title);\n if (result === \"created\") created++;\n else if (result === \"updated\") updated++;\n }\n }\n\n return { created, updated };\n }\n\n /** Sync a single story to a GitHub Issue */\n private async syncStory(\n story: Story,\n epicTitle: string\n ): Promise<\"created\" | \"updated\" | \"skipped\"> {\n const title = `[${epicTitle}] ${story.title}`;\n const labels = this.getLabels(story);\n const body = [\n `**Story ID:** \\`${story.id}\\``,\n `**Type:** ${story.type}`,\n `**Priority:** ${story.priority}`,\n `**Status:** ${story.status}`,\n \"\",\n story.description,\n \"\",\n \"---\",\n \"_Managed by [ForgeAI](https://github.com/joeljohn159/forgeai)_\",\n ].join(\"\\n\");\n\n // Search for existing issue by story ID in body\n const searchResult = spawnSync(\"gh\", [\n \"issue\", \"list\",\n \"--repo\", this.repo,\n \"--search\", story.id,\n \"--label\", \"forge\",\n \"--json\", \"number\",\n \"--jq\", \".[0].number\",\n \"--state\", \"all\",\n ], { encoding: \"utf-8\", shell: true });\n\n const existingNumber = searchResult.stdout?.trim();\n\n if (existingNumber && /^\\d+$/.test(existingNumber)) {\n // Update existing issue\n const args = [\n \"issue\", \"edit\", existingNumber,\n \"--repo\", this.repo,\n \"--title\", title,\n \"--body\", body,\n ];\n\n // Update labels\n for (const label of labels) {\n args.push(\"--add-label\", label);\n }\n\n spawnSync(\"gh\", args, { stdio: \"ignore\" });\n\n // Close if done\n if (story.status === \"done\") {\n spawnSync(\"gh\", [\n \"issue\", \"close\", existingNumber,\n \"--repo\", this.repo,\n ], { stdio: \"ignore\", shell: true });\n }\n\n return \"updated\";\n }\n\n // Create new issue\n const createArgs = [\n \"issue\", \"create\",\n \"--repo\", this.repo,\n \"--title\", title,\n \"--body\", body,\n ];\n\n for (const label of labels) {\n createArgs.push(\"--label\", label);\n }\n\n const createResult = spawnSync(\"gh\", createArgs, { stdio: \"ignore\" });\n\n if (createResult.status === 0) {\n return \"created\";\n }\n\n return \"skipped\";\n }\n\n /** Map story status to GitHub labels */\n private getLabels(story: Story): string[] {\n const labels = [\"forge\", `type:${story.type}`];\n\n switch (story.status) {\n case \"planned\":\n case \"designing\":\n case \"design-approved\":\n labels.push(\"status:planned\");\n break;\n case \"building\":\n labels.push(\"status:in-progress\");\n break;\n case \"reviewing\":\n labels.push(\"status:review\");\n break;\n case \"done\":\n labels.push(\"status:done\");\n break;\n case \"blocked\":\n labels.push(\"status:blocked\");\n break;\n }\n\n return labels;\n }\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\n// ============================================================\n// Attachment Parser\n// Detects file paths in user input (drag-and-drop), classifies\n// them as images or documents, and returns clean labels.\n// ============================================================\n\nexport interface Attachment {\n /** Display label: [Image1], [Document1], etc. */\n label: string;\n /** Original absolute path */\n originalPath: string;\n /** Resolved absolute path (quotes/escapes removed) */\n resolvedPath: string;\n /** File type category */\n type: \"image\" | \"document\";\n /** File extension */\n ext: string;\n /** File name */\n fileName: string;\n}\n\nexport interface ParsedInput {\n /** The description text with file paths replaced by labels */\n description: string;\n /** Extracted attachments */\n attachments: Attachment[];\n}\n\nconst IMAGE_EXTS = new Set([\n \".png\", \".jpg\", \".jpeg\", \".gif\", \".webp\", \".svg\", \".bmp\", \".ico\", \".tiff\", \".avif\",\n]);\n\nconst DOCUMENT_EXTS = new Set([\n \".pdf\", \".doc\", \".docx\", \".txt\", \".md\", \".csv\", \".xls\", \".xlsx\",\n \".ppt\", \".pptx\", \".rtf\", \".html\", \".json\", \".yaml\", \".yml\",\n \".xml\", \".figma\", \".sketch\", \".xd\",\n]);\n\n// Matches file paths: absolute paths, quoted paths, or escaped-space paths\n// Examples: /Users/foo/bar.png, \"/Users/foo/bar.png\", /Users/foo/bar\\ baz.png\nconst PATH_PATTERN = /(?:\"([^\"]+\\.[\\w]+)\")|(?:'([^']+\\.[\\w]+)')|(?:((?:\\/|[A-Z]:\\\\)(?:[^\\s,;]|\\\\[ ])+\\.[\\w]+))/g;\n\n/**\n * Parse user input to extract file paths and replace them with clean labels.\n * Handles drag-and-drop paths from terminal (absolute paths, quoted, escaped spaces).\n */\nexport function parseAttachments(input: string): ParsedInput {\n const attachments: Attachment[] = [];\n let imageCount = 0;\n let docCount = 0;\n\n let cleaned = input.replace(PATH_PATTERN, (match, quoted, singleQuoted, raw) => {\n const filePath = (quoted || singleQuoted || raw || \"\").replace(/\\\\ /g, \" \").trim();\n\n if (!filePath) return match;\n\n // Resolve the path\n const resolved = path.resolve(filePath);\n\n // Check if the file actually exists\n if (!fs.existsSync(resolved)) return match;\n\n const ext = path.extname(resolved).toLowerCase();\n const fileName = path.basename(resolved);\n\n let type: \"image\" | \"document\";\n let label: string;\n\n if (IMAGE_EXTS.has(ext)) {\n imageCount++;\n type = \"image\";\n label = `[Image${imageCount}]`;\n } else if (DOCUMENT_EXTS.has(ext)) {\n docCount++;\n type = \"document\";\n label = `[Document${docCount}]`;\n } else {\n // Treat unknown file types as documents\n docCount++;\n type = \"document\";\n label = `[Document${docCount}]`;\n }\n\n attachments.push({\n label,\n originalPath: match,\n resolvedPath: resolved,\n type,\n ext,\n fileName,\n });\n\n return label;\n });\n\n // Clean up extra whitespace from removed paths\n cleaned = cleaned.replace(/\\s{2,}/g, \" \").trim();\n\n return { description: cleaned, attachments };\n}\n\n/**\n * Copy attachments to .forge/attachments/ so workers can access them.\n * Returns the list of copied paths.\n */\nexport function stageAttachments(attachments: Attachment[]): string[] {\n if (attachments.length === 0) return [];\n\n const attachDir = path.join(process.cwd(), \".forge\", \"attachments\");\n fs.mkdirSync(attachDir, { recursive: true });\n\n const stagedPaths: string[] = [];\n\n for (const att of attachments) {\n // Use label-based filename to keep it clean: image1.png, document1.pdf\n const stagedName = att.label.replace(/[\\[\\]]/g, \"\").toLowerCase() + att.ext;\n const dest = path.join(attachDir, stagedName);\n\n try {\n fs.copyFileSync(att.resolvedPath, dest);\n stagedPaths.push(dest);\n } catch {\n // If copy fails (permissions, etc.), skip silently\n }\n }\n\n return stagedPaths;\n}\n\n/**\n * Format attachments for display in the terminal.\n */\nexport function formatAttachmentList(attachments: Attachment[]): string {\n if (attachments.length === 0) return \"\";\n\n return attachments\n .map((a) => ` ${a.label} ${a.fileName}`)\n .join(\"\\n\");\n}\n\n/**\n * Build an attachment context block for the orchestrator/worker prompt.\n */\nexport function buildAttachmentPrompt(attachments: Attachment[]): string {\n if (attachments.length === 0) return \"\";\n\n const lines = attachments.map((a) => {\n const relPath = path.relative(process.cwd(), a.resolvedPath);\n if (a.type === \"image\") {\n return `- ${a.label}: Image file at ${relPath} — read this file to see the visual reference`;\n }\n return `- ${a.label}: Document at ${relPath} — read this file for requirements/specs`;\n });\n\n return `\nATTACHMENTS (provided by the user):\n${lines.join(\"\\n\")}\n\nUse these attachments as reference material. For images, match the visual style/layout.\nFor documents, extract requirements and specifications from them.\n`;\n}\n","// ============================================================\n// Template Library\n// Starter templates for common app types\n// ============================================================\n\nexport interface AppTemplate {\n id: string;\n name: string;\n description: string;\n category: \"saas\" | \"ecommerce\" | \"dashboard\" | \"portfolio\" | \"social\" | \"utility\";\n suggestedFrameworks: string[];\n planDescription: string;\n}\n\nexport const templates: AppTemplate[] = [\n {\n id: \"saas-landing\",\n name: \"SaaS Landing Page\",\n description: \"Modern landing page with hero, features, pricing, testimonials, and CTA\",\n category: \"saas\",\n suggestedFrameworks: [\"nextjs\", \"react\", \"svelte\"],\n planDescription: `Build a modern SaaS landing page with:\n- Hero section with headline, subheadline, and CTA button\n- Features grid (6 features with icons and descriptions)\n- Pricing table (3 tiers: Free, Pro, Enterprise)\n- Testimonials carousel with real-looking customer quotes\n- FAQ accordion section\n- Footer with newsletter signup, social links, and legal pages\n- Fully responsive (mobile, tablet, desktop)\n- Dark/light mode toggle\n- Smooth scroll navigation\n- Animated sections on scroll`,\n },\n {\n id: \"dashboard\",\n name: \"Admin Dashboard\",\n description: \"Full-featured admin dashboard with charts, tables, and sidebar navigation\",\n category: \"dashboard\",\n suggestedFrameworks: [\"nextjs\", \"react\", \"vue\"],\n planDescription: `Build an admin dashboard with:\n- Sidebar navigation with collapsible sections\n- Top bar with search, notifications, and user avatar\n- Overview page with KPI cards (revenue, users, orders, growth)\n- Charts section (line chart for revenue, bar chart for users, pie chart for categories)\n- Data table with sorting, filtering, pagination, and search\n- User management page (list, create, edit, delete users)\n- Settings page with profile, notifications, and security tabs\n- Responsive layout (sidebar collapses to hamburger on mobile)\n- Dark/light mode\n- Loading skeletons for all data sections`,\n },\n {\n id: \"ecommerce\",\n name: \"E-Commerce Store\",\n description: \"Product catalog with cart, checkout, and order management\",\n category: \"ecommerce\",\n suggestedFrameworks: [\"nextjs\", \"vue\", \"svelte\"],\n planDescription: `Build an e-commerce store with:\n- Product listing page with grid/list view toggle\n- Product filters (category, price range, rating, in stock)\n- Product detail page with image gallery, sizes, add to cart\n- Shopping cart with quantity controls and price calculation\n- Checkout flow (shipping info, payment placeholder, order summary)\n- Order confirmation page\n- Search with autocomplete\n- Category pages\n- Responsive design (mobile-first)\n- Wishlist functionality`,\n },\n {\n id: \"portfolio\",\n name: \"Developer Portfolio\",\n description: \"Personal portfolio with projects, blog, and contact form\",\n category: \"portfolio\",\n suggestedFrameworks: [\"nextjs\", \"svelte\", \"react\"],\n planDescription: `Build a developer portfolio with:\n- Hero section with name, title, and animated intro\n- About section with skills and experience timeline\n- Projects grid with live preview links and GitHub links\n- Blog section with markdown-rendered posts\n- Contact form with validation\n- Resume/CV download button\n- Social links (GitHub, LinkedIn, Twitter)\n- Dark/light mode\n- Smooth page transitions\n- SEO optimized`,\n },\n {\n id: \"social-feed\",\n name: \"Social Feed App\",\n description: \"Social media feed with posts, comments, likes, and user profiles\",\n category: \"social\",\n suggestedFrameworks: [\"nextjs\", \"react\", \"vue\"],\n planDescription: `Build a social feed app with:\n- Feed page with infinite scroll\n- Post creation with text, images (drag & drop)\n- Like, comment, and share functionality on each post\n- Comment threads with nested replies\n- User profile pages with avatar, bio, and post history\n- Follow/unfollow other users\n- Notification dropdown\n- Search users and posts\n- Responsive design\n- Loading states and empty states`,\n },\n {\n id: \"task-manager\",\n name: \"Task Manager (Kanban)\",\n description: \"Kanban board with drag-and-drop, labels, due dates, and team assignment\",\n category: \"utility\",\n suggestedFrameworks: [\"nextjs\", \"react\", \"vue\"],\n planDescription: `Build a Kanban task manager with:\n- Board view with columns (To Do, In Progress, Review, Done)\n- Drag and drop cards between columns\n- Card detail modal with title, description, labels, due date, assignee\n- Create, edit, and delete cards\n- Filter by label, assignee, or due date\n- Multiple boards (switch between projects)\n- Board settings (rename, reorder columns, archive)\n- Responsive (columns stack on mobile)\n- Keyboard shortcuts (n = new card, / = search)\n- Activity log per card`,\n },\n {\n id: \"chat-app\",\n name: \"Real-Time Chat\",\n description: \"Chat application with channels, DMs, and message history\",\n category: \"social\",\n suggestedFrameworks: [\"nextjs\", \"react\", \"svelte\"],\n planDescription: `Build a chat application with:\n- Channel list sidebar with search\n- Message thread with timestamps and avatars\n- Message input with emoji picker and file attachments\n- Create new channels (public and private)\n- Direct messages between users\n- User presence indicators (online, away, offline)\n- Message reactions (emoji)\n- Unread message badges\n- Responsive design (sidebar slides on mobile)\n- Typing indicators`,\n },\n {\n id: \"blog-cms\",\n name: \"Blog with CMS\",\n description: \"Blog platform with rich text editor, categories, and SEO\",\n category: \"utility\",\n suggestedFrameworks: [\"nextjs\", \"vue\", \"django\"],\n planDescription: `Build a blog platform with:\n- Public blog listing with featured post hero\n- Category and tag filtering\n- Individual post page with rich content, table of contents, reading time\n- Admin dashboard for managing posts\n- Rich text editor (WYSIWYG) for creating/editing posts\n- Image upload and management\n- SEO fields per post (title, description, OG image)\n- Comment system on posts\n- RSS feed generation\n- Responsive design\n- Search functionality`,\n },\n];\n\nexport function getTemplate(id: string): AppTemplate | undefined {\n return templates.find((t) => t.id === id);\n}\n\nexport function listTemplates(): AppTemplate[] {\n return templates;\n}\n\nexport function listTemplatesByCategory(category: string): AppTemplate[] {\n return templates.filter((t) => t.category === category);\n}\n","// ============================================================\n// CI/CD Template Generator\n// Generates GitHub Actions or GitLab CI configs\n// ============================================================\n\nimport type { FrameworkAdapter } from \"../adapters/base.js\";\n\nexport type CIProvider = \"github\" | \"gitlab\";\n\n/** Returns the command if valid, or null if it's a placeholder like AUTO_DETECT */\nfunction cmd(value: string | undefined): string | null {\n if (!value || value === \"AUTO_DETECT\") return null;\n return value;\n}\n\n/** Format a YAML run step, or empty string if command is invalid */\nfunction yamlStep(command: string | undefined): string {\n const c = cmd(command);\n if (!c) return \"\";\n return ` - run: ${c}\\n`;\n}\n\nexport function generateGitHubActions(adapter: FrameworkAdapter): string {\n const isNode = adapter.language === \"typescript\" || adapter.language === \"javascript\";\n const isPython = adapter.language === \"python\";\n const isDart = adapter.language === \"dart\";\n\n if (adapter.id === \"generic\") {\n return `name: CI\n# Generic project — customize the commands below for your stack\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n # TODO: Add your setup steps (Node, Python, etc.)\n # TODO: Add your build/test commands\n - run: echo \"Add your build command here\"\n`;\n }\n\n if (isNode) {\n return `name: CI\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build:\n runs-on: ubuntu-latest\n strategy:\n matrix:\n node-version: [18, 20]\n\n steps:\n - uses: actions/checkout@v4\n - name: Use Node.js \\${{ matrix.node-version }}\n uses: actions/setup-node@v4\n with:\n node-version: \\${{ matrix.node-version }}\n cache: 'npm'\n - run: npm ci\n${yamlStep(adapter.buildCommand)}${yamlStep(adapter.lintCommand)}${yamlStep(adapter.typecheckCommand)}${yamlStep(adapter.testCommand)}`;\n }\n\n if (isPython) {\n return `name: CI\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build:\n runs-on: ubuntu-latest\n strategy:\n matrix:\n python-version: [\"3.11\", \"3.12\"]\n\n steps:\n - uses: actions/checkout@v4\n - name: Set up Python \\${{ matrix.python-version }}\n uses: actions/setup-python@v5\n with:\n python-version: \\${{ matrix.python-version }}\n - run: pip install -r requirements.txt\n${yamlStep(adapter.lintCommand)}${yamlStep(adapter.testCommand)}`;\n }\n\n if (isDart) {\n return `name: CI\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: subosito/flutter-action@v2\n with:\n flutter-version: '3.x'\n - run: flutter pub get\n${yamlStep(adapter.lintCommand)}${yamlStep(adapter.buildCommand)}${yamlStep(adapter.testCommand)}`;\n }\n\n // Unknown language fallback\n return `name: CI\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n${yamlStep(adapter.buildCommand)}${yamlStep(adapter.testCommand)}`;\n}\n\nexport function generateGitLabCI(adapter: FrameworkAdapter): string {\n const isNode = adapter.language === \"typescript\" || adapter.language === \"javascript\";\n\n if (adapter.id === \"generic\") {\n return `# Generic project — customize for your stack\nstages:\n - build\n - test\n\nbuild:\n stage: build\n script:\n - echo \"Add your build command here\"\n`;\n }\n\n if (isNode) {\n const parts = [`image: node:20\n\nstages:\n - build\n - test\n\ncache:\n paths:\n - node_modules/\n\ninstall:\n stage: build\n script:\n - npm ci`];\n\n if (cmd(adapter.buildCommand)) {\n parts.push(`\nbuild:\n stage: build\n script:\n - ${adapter.buildCommand}`);\n }\n\n if (cmd(adapter.lintCommand)) {\n parts.push(`\nlint:\n stage: test\n script:\n - ${adapter.lintCommand}`);\n }\n\n if (cmd(adapter.typecheckCommand)) {\n parts.push(`\ntypecheck:\n stage: test\n script:\n - ${adapter.typecheckCommand}`);\n }\n\n if (cmd(adapter.testCommand)) {\n parts.push(`\ntest:\n stage: test\n script:\n - ${adapter.testCommand}`);\n }\n\n return parts.join(\"\\n\") + \"\\n\";\n }\n\n // Non-node fallback\n const parts = [`stages:\n - build\n - test`];\n\n if (cmd(adapter.buildCommand)) {\n parts.push(`\nbuild:\n stage: build\n script:\n - ${adapter.buildCommand}`);\n }\n\n if (cmd(adapter.testCommand)) {\n parts.push(`\ntest:\n stage: test\n script:\n - ${adapter.testCommand}`);\n }\n\n return parts.join(\"\\n\") + \"\\n\";\n}\n","// ============================================================\n// Config Validation\n// Validates forge.config.json before running commands.\n// ============================================================\n\nimport chalk from \"chalk\";\nimport type { ForgeConfig } from \"../../types/plan.js\";\nimport { listAdapters } from \"../adapters/index.js\";\n\nexport interface ConfigValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nexport function validateConfig(config: any): ConfigValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!config || typeof config !== \"object\") {\n return { valid: false, errors: [\"Config is empty or not an object\"], warnings: [] };\n }\n\n // Framework\n const frameworks = listAdapters().map((a) => a.id);\n if (!config.framework) {\n errors.push(\"Missing 'framework' field\");\n } else if (!frameworks.includes(config.framework)) {\n errors.push(\n `Unknown framework \"${config.framework}\". Supported: ${frameworks.join(\", \")}`\n );\n }\n\n // Model\n const validModels = [\"sonnet\", \"opus\", \"haiku\"];\n if (!config.model) {\n errors.push(\"Missing 'model' field\");\n } else if (!validModels.includes(config.model)) {\n warnings.push(\n `Unknown model \"${config.model}\". Expected: ${validModels.join(\", \")}`\n );\n }\n\n // GitHub sync\n if (config.githubSync && !config.githubRepo) {\n warnings.push(\"GitHub sync enabled but no repo configured\");\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/** Validate config and print errors. Returns config if valid, null if not. */\nexport function loadAndValidateConfig(raw: any): ForgeConfig | null {\n const result = validateConfig(raw);\n\n if (result.warnings.length > 0) {\n for (const w of result.warnings) {\n console.log(chalk.yellow(` warning: ${w}`));\n }\n }\n\n if (!result.valid) {\n for (const e of result.errors) {\n console.log(chalk.red(` error: ${e}`));\n }\n console.log(chalk.dim(\"\\n Fix forge.config.json or run: forge init\\n\"));\n return null;\n }\n\n return raw as ForgeConfig;\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAivBZ,IAAM,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["isWin","query","chalk","query","chalk","fs","path","fs","path","chalk","chalk","fs","path","fs","path","path","fs","exec","platform","cmd","chalk","ora","inquirer","fs","path","chalk","ora","inquirer","parts","chalk","chalk"]}
|