@mistflow-ai/mcp 0.1.2 → 0.1.4-staging.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.
Files changed (2) hide show
  1. package/dist/index.js +106 -106
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{a as gt,d as mt,e as Pe}from"./chunk-ANYHR4WA.js";import{b as tr,e as ar}from"./chunk-ZRRSZS7A.js";import{A as Fa,B as He,C as $t,D as Ea,E as Ua,F as Ha,G as Ga,H as Wa,I as Oa,J as _a,K as ja,L as za,M as at,N as $a,O as Va,P as qa,Q as Ka,R as bt,S as Ya,T as Ja,U as Qa,V as Xa,W as Za,Y as er,b as Ta,c as Pa,d as ht,e as Ba,f as jt,g as Da,h as pe,i as ke,j as Ee,k as Ue,l as Aa,m as he,n as Ia,r as Ma,s as Ra,t as ft,v as La,w as Be,x as Na,y as zt}from"./chunk-5IQGENNI.js";import{Server as Ws}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Os}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as _s,ListToolsRequestSchema as js}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as zs}from"zod-to-json-schema";function p(r,t=!1){let e=r;try{let a=Pa();a&&(e=r+a)}catch{}return{content:[{type:"text",text:e}],isError:t}}function be(r){return p(`This is not a Mistflow project (no mistflow.json found at ${r}).
1
+ import{a as gt,d as mt,e as Be}from"./chunk-ANYHR4WA.js";import{b as rr,c as qt,d as Kt,e as nr}from"./chunk-ZRRSZS7A.js";import{A as Ua,B as He,C as Vt,D as Ha,E as Ga,F as Wa,G as Oa,H as _a,I as ja,J as za,K as $a,L as Va,M as at,N as qa,O as Ka,P as Ya,Q as Ja,R as yt,S as Qa,T as Xa,U as Za,V as er,W as tr,Y as ar,b as Da,c as Aa,d as ht,e as Ia,f as zt,g as ft,h as ue,i as ke,j as Ee,k as Ue,l as Ma,m as he,n as Ra,r as La,s as Na,t as bt,v as Fa,w as De,x as Ea,y as $t}from"./chunk-5IQGENNI.js";import{Server as _s}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as js}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as zs,ListToolsRequestSchema as $s}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as Vs}from"zod-to-json-schema";function p(r,t=!1){let e=r;try{let a=Aa();a&&(e=r+a)}catch{}return{content:[{type:"text",text:e}],isError:t}}function be(r){return p(`This is not a Mistflow project (no mistflow.json found at ${r}).
2
2
 
3
3
  Mistflow creates new projects from scratch \u2014 it doesn't work inside existing codebases.
4
4
 
@@ -7,10 +7,10 @@ To get started:
7
7
  2. Run mist_build (action: 'init') to create a new project in a subdirectory
8
8
  3. Run mist_build (action: 'implement') to build each step
9
9
 
10
- If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}async function De(r,t){try{let{getPage:e,takeScreenshot:a}=await import("./browser-manager-K5BT5YXO.js"),o=await e();await o.goto(r,{waitUntil:"domcontentloaded",timeout:15e3}),await o.waitForLoadState("networkidle").catch(()=>{});let i=await a(o,!1);return{content:[{type:"text",text:t},{type:"image",data:i.toString("base64"),mimeType:"image/png"}]}}catch{return p(t)}}import{z as Vt}from"zod";import{platform as Hn}from"os";import{execFile as rr}from"child_process";var Gn=Vt.object({apiKey:Vt.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:Vt.string().optional().describe("Resume polling for a pending device code. Returned by a previous mist_setup call with status 'pending'. Call mist_setup again with this value after ~15 seconds to check if the user approved.")});function Wn(r){return"error"in r}function On(r){return new Promise(t=>setTimeout(t,r))}function _n(r){return new Promise(t=>{let e=Hn();e==="win32"?rr("cmd.exe",["/c","start","",r],a=>{a&&console.error("Could not open browser:",a.message),t(!a)}):rr(e==="darwin"?"open":"xdg-open",[r],o=>{o&&console.error("Could not open browser:",o.message),t(!o)}),setTimeout(()=>t(!1),5e3)})}var jn={fetch:globalThis.fetch,openBrowser:_n};async function nr(r,t,e,a){let o=e;for(let i=0;i<t;i++){await On(o);let n;try{let s=await a.fetch(`${Ee()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:r})});if(!s.ok)continue;n=await s.json()}catch{continue}if(Wn(n))switch(n.error){case"authorization_pending":continue;case"slow_down":o+=5e3;continue;case"expired_token":return p("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return p("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return p("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}return jt({apiKey:n.api_key,apiKeyId:n.api_key_id,apiKeyName:n.api_key_name,orgId:n.org_id,orgSlug:n.org_slug,email:n.email}),p(`Connected to Mistflow as ${n.org_slug}. You are ready to build and deploy.`)}return null}async function zn(r,t=jn){let e=r;if(e?.apiKey)try{let n=await t.fetch(`${Ee()}/api/org`,{headers:{Authorization:`ApiKey ${e.apiKey}`}});if(!n.ok)return p("Invalid API key. Check the key and try again.",!0);let s=await n.json();return jt({apiKey:e.apiKey,orgId:s.id,orgSlug:s.slug}),p(`Connected to Mistflow as ${s.slug} via API key. You are ready to build and deploy.`)}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(e?.deviceCode){let n=await nr(e.deviceCode,6,5e3,t);return n||p(JSON.stringify({status:"pending",deviceCode:e.deviceCode,instruction:"The user hasn't approved yet. Wait ~15 seconds and call mist_setup again with the same deviceCode."}))}let a;try{let n=await t.fetch(`${Ee()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)return p("Cannot reach Mistflow servers. Check your internet connection.",!0);a=await n.json()}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}let o=`${a.verification_uri}?code=${a.user_code}`;console.error(`
10
+ If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}async function Ae(r,t){try{let{getPage:e,takeScreenshot:a}=await import("./browser-manager-K5BT5YXO.js"),o=await e();await o.goto(r,{waitUntil:"domcontentloaded",timeout:15e3}),await o.waitForLoadState("networkidle").catch(()=>{});let i=await a(o,!1);return{content:[{type:"text",text:t},{type:"image",data:i.toString("base64"),mimeType:"image/png"}]}}catch{return p(t)}}import{z as Yt}from"zod";import{platform as Wn}from"os";import{execFile as ir}from"child_process";var On=Yt.object({apiKey:Yt.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:Yt.string().optional().describe("Resume polling for a pending device code. Returned by a previous mist_setup call with status 'pending'. Call mist_setup again with this value after ~15 seconds to check if the user approved.")});function _n(r){return"error"in r}function jn(r){return new Promise(t=>setTimeout(t,r))}function zn(r){return new Promise(t=>{let e=Wn();e==="win32"?ir("cmd.exe",["/c","start","",r],a=>{a&&console.error("Could not open browser:",a.message),t(!a)}):ir(e==="darwin"?"open":"xdg-open",[r],o=>{o&&console.error("Could not open browser:",o.message),t(!o)}),setTimeout(()=>t(!1),5e3)})}var $n={fetch:globalThis.fetch,openBrowser:zn};async function or(r,t,e,a){let o=e;for(let i=0;i<t;i++){await jn(o);let n;try{let s=await a.fetch(`${Ee()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:r})});if(!s.ok)continue;n=await s.json()}catch{continue}if(_n(n))switch(n.error){case"authorization_pending":continue;case"slow_down":o+=5e3;continue;case"expired_token":return p("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return p("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return p("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}return zt({apiKey:n.api_key,apiKeyId:n.api_key_id,apiKeyName:n.api_key_name,orgId:n.org_id,orgSlug:n.org_slug,email:n.email}),p(`Connected to Mistflow as ${n.org_slug}. You are ready to build and deploy.`)}return null}async function Vn(r,t=$n){let e=r;if(e?.apiKey)try{let n=await t.fetch(`${Ee()}/api/org`,{headers:{Authorization:`ApiKey ${e.apiKey}`}});if(!n.ok)return p("Invalid API key. Check the key and try again.",!0);let s=await n.json();return zt({apiKey:e.apiKey,orgId:s.id,orgSlug:s.slug}),p(`Connected to Mistflow as ${s.slug} via API key. You are ready to build and deploy.`)}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(e?.deviceCode){let n=await or(e.deviceCode,6,5e3,t);return n||p(JSON.stringify({status:"pending",deviceCode:e.deviceCode,instruction:"The user hasn't approved yet. Wait ~15 seconds and call mist_setup again with the same deviceCode."}))}let a;try{let n=await t.fetch(`${Ee()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)return p("Cannot reach Mistflow servers. Check your internet connection.",!0);a=await n.json()}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}let o=`${a.verification_uri}?code=${a.user_code}`;console.error(`
11
11
  Sign in at: ${o}
12
12
  Your code: ${a.user_code}
13
- `);try{await t.openBrowser(o)}catch{}let i=await nr(a.device_code,6,5e3,t);return i||p(JSON.stringify({status:"pending",deviceCode:a.device_code,signInUrl:o,userCode:a.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+a.device_code+"' to check if they approved."}))}var ir={name:"mist_setup",description:"Connect the user's Mistflow account. Call this ONLY when: (a) the user has literally never signed in, or (b) a previous tool call returned error code 'auth_missing' or 'auth_revoked'. DO NOT call this tool in response to 500 errors, 404 errors, network errors, or any generic failure \u2014 those are backend issues, not auth issues. Running mist_setup when not needed wastes the user's time and creates login fatigue. Once signed in, the MCP persists a long-lived API key and never asks again. Two-phase device code flow: (1) Call without deviceCode \u2014 opens browser, returns status 'pending' with deviceCode and userCode. Tell the user the verification code and that they need to approve in the browser. (2) Call again with deviceCode after ~15 seconds \u2014 polls for approval. Also accepts an apiKey for headless auth (skips device code entirely).",inputSchema:Gn,handler:r=>zn(r)};import{z as oe}from"zod";import{existsSync as rt,mkdirSync as aa,readFileSync as xr,readdirSync as Yn,statSync as Jn,unlinkSync as Qn,writeFileSync as ra}from"fs";import{dirname as Xn,join as xe}from"path";import{homedir as We}from"os";import{createHash as Zn,createHmac as wr,randomBytes as ei,randomUUID as yr,timingSafeEqual as ti}from"crypto";var _=`
13
+ `);try{await t.openBrowser(o)}catch{}let i=await or(a.device_code,6,5e3,t);return i||p(JSON.stringify({status:"pending",deviceCode:a.device_code,signInUrl:o,userCode:a.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+a.device_code+"' to check if they approved."}))}var sr={name:"mist_setup",description:"Connect the user's Mistflow account. Call this ONLY when: (a) the user has literally never signed in, or (b) a previous tool call returned error code 'auth_missing' or 'auth_revoked'. DO NOT call this tool in response to 500 errors, 404 errors, network errors, or any generic failure \u2014 those are backend issues, not auth issues. Running mist_setup when not needed wastes the user's time and creates login fatigue. Once signed in, the MCP persists a long-lived API key and never asks again. Two-phase device code flow: (1) Call without deviceCode \u2014 opens browser, returns status 'pending' with deviceCode and userCode. Tell the user the verification code and that they need to approve in the browser. (2) Call again with deviceCode after ~15 seconds \u2014 polls for approval. Also accepts an apiKey for headless auth (skips device code entirely).",inputSchema:On,handler:r=>Vn(r)};import{z as se}from"zod";import{existsSync as rt,mkdirSync as ia,readFileSync as vr,readdirSync as Qn,statSync as Xn,unlinkSync as Zn,writeFileSync as oa}from"fs";import{dirname as ei,join as xe}from"path";import{homedir as We}from"os";import{createHash as ti,createHmac as kr,randomBytes as ai,randomUUID as wr,timingSafeEqual as ri}from"crypto";var _=`
14
14
  IMPORTANT \u2014 Visual identity rules:
15
15
  - Use ONLY the project's CSS custom properties for all colors: var(--color-background), var(--color-foreground), var(--color-primary), var(--color-muted), var(--color-muted-foreground), var(--color-border), var(--color-card).
16
16
  - Use the project's configured fonts from layout.tsx (font-sans for body, font-heading if available). NEVER hardcode font names.
@@ -20,7 +20,7 @@ IMPORTANT \u2014 Visual identity rules:
20
20
  - For muted/secondary text, use text-muted-foreground.
21
21
  - For borders, use border-border.
22
22
  - NEVER use hardcoded hex values, named Tailwind colors (blue-500, purple-600, etc.), or raw RGB/HSL.
23
- `,qt={1:{description:"Bold uppercase typography with stacked 3-line headline and custom SVG button shape.",tags:["bold","uppercase","cinematic"],theme:"dark",colors:[],style:"Bold & Cinematic",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"none",effects:["mesh-gradient-background","custom-svg-button"]}},10:{description:"Left-aligned AI hero with serif accent, staggered word animations, and badge pill.",tags:["ai","serif-accent","animations","badge","left-aligned"],theme:"dark",colors:[],style:"Tech & Professional",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"left-aligned",background:"mesh",animationLib:"framer-motion",navStyle:"none",effects:["split-text-animation","blur-in","badge-pill"]}},12:{description:"High-end logistics hero with accent-colored buttons, clipped-corner shapes, and glassmorphism card.",tags:["logistics","accent-color","glassmorphism","compact"],theme:"dark",colors:[],style:"Bold & Industrial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"left-aligned",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["clipped-corner-buttons","glassmorphism-card"]}},14:{description:"Minimal agency hero with floating white nav, serif italic headline, and play CTA.",tags:["agency","minimal","serif-italic","floating-nav"],theme:"dark",colors:[],style:"Minimal & Elegant",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"floating",effects:["floating-white-nav","serif-italic-headline"]}},16:{description:"Clean minimalist SaaS hero with editorial spacing, gradient background, and motion animations.",tags:["saas","minimal","editorial","gradient","clean","motion"],theme:"light",colors:[],style:"Clean & Editorial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"none",effects:["gradient-overlay","editorial-spacing"]}},18:{description:"Animated loading screen with rotating words, counter, and progress bar intro sequence.",tags:["animation","loading","intro","counter"],theme:"dark",colors:[],style:"Cinematic Intro",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"full-width",background:"solid",animationLib:"framer-motion",navStyle:"none",effects:["word-cycle","counter-animation","progress-bar"]}},21:{description:"Full-screen agency hero with transparent nav, badge pill, mixed serif/sans headline, and corner accents.",tags:["agency","bold","transparent-nav"],theme:"dark",colors:[],style:"Bold & Agency",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"transparent",effects:["corner-accents","badge-pill","mixed-serif-sans"]}},3:{description:"Dark SaaS hero with gradient CTA button, glow effects, and social proof row.",tags:["saas","gradient","glow"],theme:"dark",colors:[],style:"SaaS & Glow",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"sticky",effects:["gradient-cta","glow-effect","social-proof"]}},5:{description:"Dark glassmorphism hero with purple/pink gradient accents, announcement pill, and logo cloud.",tags:["glassmorphism","gradient","logo-cloud","agency"],theme:"dark",colors:[],style:"Glass & Gradient",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"framer-motion",navStyle:"none",effects:["glassmorphism","gradient-text","logo-marquee"]}},7:{description:"Dark tech hero with blurred glass navbar, integration badges, and staggered entrance animations.",tags:["tech","glass","gradient","navbar"],theme:"dark",colors:[],style:"Tech & Glass",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["glass-navbar","integration-badges","staggered-entrance"]}},9:{description:"Web3 hero with gradient text heading, waitlist pill button, and layered glow effects.",tags:["web3","crypto","gradient-text"],theme:"dark",colors:[],style:"Web3 & Minimal",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-text","glow-pill-button"]}},"aethera-hero":{description:"Light cinematic hero with serif headline, gray accent words, fade-rise animation sequence.",tags:["cinematic","light","elegant","serif"],theme:"light",colors:[],style:"Cinematic & Light",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["fade-rise-sequence","muted-accent-words"]}},"bloom-ai-hero":{description:"Split-panel layout with liquid glass panels, serif accent text, and social/community cards.",tags:["glassmorphism","split-panel"],theme:"dark",colors:[],style:"Glass & Split",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"split-panel",background:"mesh",animationLib:"css",navStyle:"none",effects:["liquid-glass","split-layout","community-cards"]}},"datacore-booking-hero":{description:"Booking SaaS hero with glassmorphism tag pill, serif headline, and dual CTA buttons.",tags:["saas","booking","glassmorphism"],theme:"dark",colors:[],style:"SaaS & Booking",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["glassmorphism-pill","serif-headline"]}},"designpro-hero":{description:"Education platform hero with shiny gradient text animation and enrollment CTA.",tags:["education","animation","gradient-text"],theme:"dark",colors:[],style:"Education & Animated",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["shiny-text-animation","staggered-entrance"]}},"digitwist-hero":{description:"AI website builder hero with pre-headline serif, large gradient headline, and pill CTA with arrow.",tags:["ai","saas","gradient"],theme:"dark",colors:[],style:"AI & Builder",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"framer-motion",navStyle:"transparent",effects:["gradient-headline","pill-cta-with-arrow","decorative-gradients"]}},"grow-ai-hero":{description:"Dark hero with massive gradient headline, liquid glass nav, fade-loop background, and logo marquee.",tags:["talent","gradient","liquid-glass","marquee"],theme:"dark",colors:[],style:"Gradient & Marquee",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-headline","liquid-glass","logo-marquee","fade-loop"]}},"liquid-glass-agency":{description:"Multi-section landing page with liquid glass effects, blur-text reveal, feature chess layout, and stats.",tags:["agency","liquid-glass","multi-section"],theme:"dark",colors:[],style:"Glass & Premium",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["liquid-glass","blur-text-reveal","feature-chess","stats-grid"]}},"mindloop-landing":{description:"Multi-section monochrome landing with scroll-driven word reveal, email subscription, and content cards.",tags:["newsletter","monochrome","multi-section","scroll-reveal"],theme:"dark",colors:[],style:"Monochrome & Editorial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"transparent",effects:["scroll-word-reveal","email-subscribe","content-cards"]}},"neuralyn-hero":{description:"SaaS landing with coded dashboard preview, parallax scroll, liquid glass pill, and testimonial section.",tags:["saas","dashboard-preview","parallax","liquid-glass"],theme:"dark",colors:[],style:"SaaS & Dashboard",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"transparent",effects:["dashboard-preview","parallax-scroll","liquid-glass-pill","word-reveal-testimonial"]}},"nexora-hero":{description:"Light SaaS hero with coded dashboard preview, badge pill, play button, and frosted glass wrapper.",tags:["saas","light","dashboard-preview"],theme:"light",colors:[],style:"Light & SaaS",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"transparent",effects:["dashboard-preview","frosted-glass","badge-pill"]}},"portfolio-cosmic-hero":{description:"Portfolio with loading screen, bento project grid, parallax gallery, GSAP marquee, and stats.",tags:["portfolio","bento-grid","parallax","loading-screen"],theme:"dark",colors:[],style:"Portfolio & Cinematic",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"gsap",navStyle:"glassmorphic",effects:["loading-screen","bento-grid","parallax-gallery","gsap-marquee"]}},"power-ai-hero":{description:"Dark hero with massive gradient headline, liquid glass elements, and logo marquee.",tags:["ai","gradient","liquid-glass","marquee"],theme:"dark",colors:[],style:"AI & Power",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-headline","liquid-glass","logo-marquee"]}},"price-calculator":{description:"Interactive pricing calculator with slider, radio options, checkboxes, and 3-tier comparison cards.",tags:["pricing","calculator","interactive"],theme:"dark",colors:[],style:"Calculator & Pricing",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"full-width",background:"solid",animationLib:"none",navStyle:"none",effects:["pricing-slider","comparison-cards","radio-checkboxes"]}},"skyelite-hero":{description:"Light premium hero with overlapping headline lines, uppercase label, and dual pill CTAs.",tags:["luxury","light","premium"],theme:"light",colors:[],style:"Premium & Light",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["overlapping-headlines","dual-pill-ctas"]}},"taskly-hero":{description:"Light hero with gradient glow background, liquid glass navbar, glassy orb visual, and social proof.",tags:["task-management","light","liquid-glass","glow"],theme:"light",colors:[],style:"Glass & Glow",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"split-panel",background:"gradient",animationLib:"css",navStyle:"glassmorphic",effects:["gradient-glow-blobs","liquid-glass-navbar","social-proof"]}},"velorah-hero":{description:"Cinematic hero with serif headline, muted accent words, liquid glass buttons, and fade-rise animations.",tags:["agency","cinematic","serif","liquid-glass"],theme:"dark",colors:[],style:"Cinematic & Serif",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"transparent",effects:["liquid-glass","fade-rise-sequence","muted-accent-words"]}}},$e=[{id:"1",title:"Midnight Bold",category:"Creative",prompt:`Create a responsive, full-screen hero section using React and Tailwind CSS.
23
+ `,Jt={1:{description:"Bold uppercase typography with stacked 3-line headline and custom SVG button shape.",tags:["bold","uppercase","cinematic"],theme:"dark",colors:[],style:"Bold & Cinematic",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"none",effects:["mesh-gradient-background","custom-svg-button"]}},10:{description:"Left-aligned AI hero with serif accent, staggered word animations, and badge pill.",tags:["ai","serif-accent","animations","badge","left-aligned"],theme:"dark",colors:[],style:"Tech & Professional",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"left-aligned",background:"mesh",animationLib:"framer-motion",navStyle:"none",effects:["split-text-animation","blur-in","badge-pill"]}},12:{description:"High-end logistics hero with accent-colored buttons, clipped-corner shapes, and glassmorphism card.",tags:["logistics","accent-color","glassmorphism","compact"],theme:"dark",colors:[],style:"Bold & Industrial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"left-aligned",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["clipped-corner-buttons","glassmorphism-card"]}},14:{description:"Minimal agency hero with floating white nav, serif italic headline, and play CTA.",tags:["agency","minimal","serif-italic","floating-nav"],theme:"dark",colors:[],style:"Minimal & Elegant",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"floating",effects:["floating-white-nav","serif-italic-headline"]}},16:{description:"Clean minimalist SaaS hero with editorial spacing, gradient background, and motion animations.",tags:["saas","minimal","editorial","gradient","clean","motion"],theme:"light",colors:[],style:"Clean & Editorial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"none",effects:["gradient-overlay","editorial-spacing"]}},18:{description:"Animated loading screen with rotating words, counter, and progress bar intro sequence.",tags:["animation","loading","intro","counter"],theme:"dark",colors:[],style:"Cinematic Intro",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"full-width",background:"solid",animationLib:"framer-motion",navStyle:"none",effects:["word-cycle","counter-animation","progress-bar"]}},21:{description:"Full-screen agency hero with transparent nav, badge pill, mixed serif/sans headline, and corner accents.",tags:["agency","bold","transparent-nav"],theme:"dark",colors:[],style:"Bold & Agency",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"transparent",effects:["corner-accents","badge-pill","mixed-serif-sans"]}},3:{description:"Dark SaaS hero with gradient CTA button, glow effects, and social proof row.",tags:["saas","gradient","glow"],theme:"dark",colors:[],style:"SaaS & Glow",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"sticky",effects:["gradient-cta","glow-effect","social-proof"]}},5:{description:"Dark glassmorphism hero with purple/pink gradient accents, announcement pill, and logo cloud.",tags:["glassmorphism","gradient","logo-cloud","agency"],theme:"dark",colors:[],style:"Glass & Gradient",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"framer-motion",navStyle:"none",effects:["glassmorphism","gradient-text","logo-marquee"]}},7:{description:"Dark tech hero with blurred glass navbar, integration badges, and staggered entrance animations.",tags:["tech","glass","gradient","navbar"],theme:"dark",colors:[],style:"Tech & Glass",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["glass-navbar","integration-badges","staggered-entrance"]}},9:{description:"Web3 hero with gradient text heading, waitlist pill button, and layered glow effects.",tags:["web3","crypto","gradient-text"],theme:"dark",colors:[],style:"Web3 & Minimal",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-text","glow-pill-button"]}},"aethera-hero":{description:"Light cinematic hero with serif headline, gray accent words, fade-rise animation sequence.",tags:["cinematic","light","elegant","serif"],theme:"light",colors:[],style:"Cinematic & Light",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["fade-rise-sequence","muted-accent-words"]}},"bloom-ai-hero":{description:"Split-panel layout with liquid glass panels, serif accent text, and social/community cards.",tags:["glassmorphism","split-panel"],theme:"dark",colors:[],style:"Glass & Split",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"split-panel",background:"mesh",animationLib:"css",navStyle:"none",effects:["liquid-glass","split-layout","community-cards"]}},"datacore-booking-hero":{description:"Booking SaaS hero with glassmorphism tag pill, serif headline, and dual CTA buttons.",tags:["saas","booking","glassmorphism"],theme:"dark",colors:[],style:"SaaS & Booking",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["glassmorphism-pill","serif-headline"]}},"designpro-hero":{description:"Education platform hero with shiny gradient text animation and enrollment CTA.",tags:["education","animation","gradient-text"],theme:"dark",colors:[],style:"Education & Animated",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["shiny-text-animation","staggered-entrance"]}},"digitwist-hero":{description:"AI website builder hero with pre-headline serif, large gradient headline, and pill CTA with arrow.",tags:["ai","saas","gradient"],theme:"dark",colors:[],style:"AI & Builder",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"framer-motion",navStyle:"transparent",effects:["gradient-headline","pill-cta-with-arrow","decorative-gradients"]}},"grow-ai-hero":{description:"Dark hero with massive gradient headline, liquid glass nav, fade-loop background, and logo marquee.",tags:["talent","gradient","liquid-glass","marquee"],theme:"dark",colors:[],style:"Gradient & Marquee",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-headline","liquid-glass","logo-marquee","fade-loop"]}},"liquid-glass-agency":{description:"Multi-section landing page with liquid glass effects, blur-text reveal, feature chess layout, and stats.",tags:["agency","liquid-glass","multi-section"],theme:"dark",colors:[],style:"Glass & Premium",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"glassmorphic",effects:["liquid-glass","blur-text-reveal","feature-chess","stats-grid"]}},"mindloop-landing":{description:"Multi-section monochrome landing with scroll-driven word reveal, email subscription, and content cards.",tags:["newsletter","monochrome","multi-section","scroll-reveal"],theme:"dark",colors:[],style:"Monochrome & Editorial",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"transparent",effects:["scroll-word-reveal","email-subscribe","content-cards"]}},"neuralyn-hero":{description:"SaaS landing with coded dashboard preview, parallax scroll, liquid glass pill, and testimonial section.",tags:["saas","dashboard-preview","parallax","liquid-glass"],theme:"dark",colors:[],style:"SaaS & Dashboard",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"solid",animationLib:"framer-motion",navStyle:"transparent",effects:["dashboard-preview","parallax-scroll","liquid-glass-pill","word-reveal-testimonial"]}},"nexora-hero":{description:"Light SaaS hero with coded dashboard preview, badge pill, play button, and frosted glass wrapper.",tags:["saas","light","dashboard-preview"],theme:"light",colors:[],style:"Light & SaaS",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"framer-motion",navStyle:"transparent",effects:["dashboard-preview","frosted-glass","badge-pill"]}},"portfolio-cosmic-hero":{description:"Portfolio with loading screen, bento project grid, parallax gallery, GSAP marquee, and stats.",tags:["portfolio","bento-grid","parallax","loading-screen"],theme:"dark",colors:[],style:"Portfolio & Cinematic",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"gsap",navStyle:"glassmorphic",effects:["loading-screen","bento-grid","parallax-gallery","gsap-marquee"]}},"power-ai-hero":{description:"Dark hero with massive gradient headline, liquid glass elements, and logo marquee.",tags:["ai","gradient","liquid-glass","marquee"],theme:"dark",colors:[],style:"AI & Power",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"animated-gradient",animationLib:"css",navStyle:"transparent",effects:["gradient-headline","liquid-glass","logo-marquee"]}},"price-calculator":{description:"Interactive pricing calculator with slider, radio options, checkboxes, and 3-tier comparison cards.",tags:["pricing","calculator","interactive"],theme:"dark",colors:[],style:"Calculator & Pricing",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"full-width",background:"solid",animationLib:"none",navStyle:"none",effects:["pricing-slider","comparison-cards","radio-checkboxes"]}},"skyelite-hero":{description:"Light premium hero with overlapping headline lines, uppercase label, and dual pill CTAs.",tags:["luxury","light","premium"],theme:"light",colors:[],style:"Premium & Light",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"gradient",animationLib:"css",navStyle:"transparent",effects:["overlapping-headlines","dual-pill-ctas"]}},"taskly-hero":{description:"Light hero with gradient glow background, liquid glass navbar, glassy orb visual, and social proof.",tags:["task-management","light","liquid-glass","glow"],theme:"light",colors:[],style:"Glass & Glow",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"split-panel",background:"gradient",animationLib:"css",navStyle:"glassmorphic",effects:["gradient-glow-blobs","liquid-glass-navbar","social-proof"]}},"velorah-hero":{description:"Cinematic hero with serif headline, muted accent words, liquid glass buttons, and fade-rise animations.",tags:["agency","cinematic","serif","liquid-glass"],theme:"dark",colors:[],style:"Cinematic & Serif",designLanguage:{headingFont:"project-configured",bodyFont:"project-configured",layout:"centered",background:"mesh",animationLib:"css",navStyle:"transparent",effects:["liquid-glass","fade-rise-sequence","muted-accent-words"]}}},$e=[{id:"1",title:"Midnight Bold",category:"Creative",prompt:`Create a responsive, full-screen hero section using React and Tailwind CSS.
24
24
 
25
25
  Layout: min-h-screen container. Content aligned to the top (not centered), with generous top padding (pt-32 mobile, pt-48 desktop).
26
26
 
@@ -443,11 +443,11 @@ Animations (CSS keyframes):
443
443
  - Staggered: h1 (0s), subtext (0.2s delay), CTA (0.4s delay).
444
444
 
445
445
  Minimalist: No decorative blobs, no overlays beyond the mesh gradient.
446
- ${_}`}];function Ae(r){let t=$e.find(a=>a.id===r);if(t)return t;let e=r.toLowerCase().replace(/[^a-z0-9]/g,"");return $e.find(a=>{let o=a.title.toLowerCase().replace(/[^a-z0-9]/g,"");return o===e||o.includes(e)||e.includes(o)})}function or(r){return qt[r]}function Kt(r){return r?$e.filter(t=>t.category.toLowerCase()===r.toLowerCase()):$e}function sr(r){let t=r??$e,e={};for(let o of t){e[o.category]||(e[o.category]=[]);let i=qt[o.id],n=i?` \u2014 ${i.description}`:"";e[o.category].push(`${o.id} \u2014 "${o.title}"${n}`)}let a=[];for(let[o,i]of Object.entries(e))a.push(`**${o}**:
446
+ ${_}`}];function Ie(r){let t=$e.find(a=>a.id===r);if(t)return t;let e=r.toLowerCase().replace(/[^a-z0-9]/g,"");return $e.find(a=>{let o=a.title.toLowerCase().replace(/[^a-z0-9]/g,"");return o===e||o.includes(e)||e.includes(o)})}function lr(r){return Jt[r]}function Qt(r){return r?$e.filter(t=>t.category.toLowerCase()===r.toLowerCase()):$e}function dr(r){let t=r??$e,e={};for(let o of t){e[o.category]||(e[o.category]=[]);let i=Jt[o.id],n=i?` \u2014 ${i.description}`:"";e[o.category].push(`${o.id} \u2014 "${o.title}"${n}`)}let a=[];for(let[o,i]of Object.entries(e))a.push(`**${o}**:
447
447
  ${i.map(n=>` \u2022 ${n}`).join(`
448
448
  `)}`);return a.join(`
449
449
 
450
- `)}function $n(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function Yt(r){return(r?Kt(r):$e).map(e=>{let a=qt[e.id];return{id:e.id,slug:$n(e.title),title:e.title,category:e.category,description:a?.description??"",tags:a?.tags??[],theme:a?.theme??"dark",colors:a?.colors??[],style:a?.style??""}})}function lr(r,t){let e=r.toLowerCase(),a=t?.maxResults??3,o=t?.themePreference,i=[[/saas|software|platform|dashboard|analytics/i,"saas",3],[/agency|studio|creative|design\s*agency/i,"agency",3],[/portfolio|personal|resume|cv/i,"portfolio",3],[/web3|crypto|blockchain|nft|defi/i,"web3",3],[/booking|reservation|hotel|schedule/i,"booking",3],[/ai|artificial|machine\s*learning|automation/i,"ai",2],[/education|course|learning|academy/i,"education",3],[/newsletter|blog|content|editorial/i,"newsletter",3],[/pricing|calculator|cost/i,"pricing",3],[/luxury|premium|jet|high.?end/i,"luxury",3],[/talent|hiring|recruit|hr/i,"talent",3],[/logistics|transport|shipping|delivery/i,"logistics",3],[/invoice|billing|payment/i,"invoicing",3],[/task|project\s*manag|todo/i,"task-management",3],[/fintech|finance|banking|money/i,"fintech",3],[/minimal|clean|simple/i,"minimal",2],[/dark|night|moody/i,"dark",1],[/light|bright|white/i,"light",1],[/glass|frosted|blur/i,"liquid-glass",2],[/animated|animation|motion/i,"animation",2],[/gradient|colorful/i,"gradient",1],[/cinematic|epic|dramatic/i,"cinematic",2]],n=Yt().map(d=>{let u=0;for(let[h,g,y]of i)h.test(e)&&d.tags.includes(g)&&(u+=y);let c=d.category.toLowerCase();return e.includes(c)&&(u+=2),o&&d.theme===o&&(u+=1),d.id==="18"&&!/loader|loading|animation/i.test(e)&&(u-=5),d.id==="price-calculator"&&!/pric|calculator|cost|estimat/i.test(e)&&(u-=5),{preset:d,score:u}});n.sort((d,u)=>u.score+Math.random()*.5-(d.score+Math.random()*.5));let s=n.filter(d=>d.score>0);return(s.length>=a?s.slice(0,a):n.slice(0,a)).map(d=>d.preset)}var cr={"SF Pro":"Inter","SF Pro Display":"Inter","SF Pro Text":"Inter","SF Pro Rounded":"Nunito Sans","SF Mono":"JetBrains Mono","system-ui":"Inter","ui-sans-serif":"Inter",Arial:"DM Sans","Inter Variable":"Inter",inter:"Inter","sohne-var":"Inter",sohne:"Inter",S\u00F6hne:"Inter",s\u00F6hne:"Inter",CohereText:"Source Serif 4","Unica77 Cohere Web":"Inter",Waldenburg:"Playfair Display",waldenburgNormal:"Playfair Display","rb-freigeist-neue":"Space Grotesk","basier-square":"Inter",Basier:"Inter","The Future":"Outfit",GeistMono:"JetBrains Mono",universalSans:"Inter",CursorGothic:"DM Serif Display",jjannon:"Crimson Text","Camera Plain Variable":"DM Sans",domaine:"DM Serif Display","Dammit Sans":"Rubik","Super Sans VF":"DM Sans","Matter Regular":"Inter","Degular Display":"DM Sans",abcDiatype:"DM Sans","ABC Diatype":"DM Sans","ABC Diatype Mono":"JetBrains Mono",__hashicorpSans_96f0ca:"Inter","MongoDB Value Serif":"Source Serif 4","Euclid Circular A":"DM Sans",Haas:"Inter","Cal Sans":"DM Sans",figmaSans:"Inter","GT Walsheim Framer Medium":"DM Sans","GT Walsheim":"DM Sans","GT Walsheim Pro":"DM Sans",Saans:"Inter",MediumLL:"Inter",NotionInter:"Inter",Roobert:"DM Sans","Roobert PRO Medium":"DM Sans","Pin Sans":"DM Sans",CoinbaseDisplay:"DM Sans",CoinbaseSans:"Inter","Kraken-Brand":"Space Grotesk","Kraken-Product":"Inter","Aeonik Pro":"DM Sans","Wise Sans":"DM Sans","BMWTypeNextLatin Light":"Inter",BMWTypeNextLatin:"Inter","IBM Plex Sans":"IBM Plex Sans","NVIDIA-EMEA":"Inter",SpotifyMixUI:"DM Sans","Uber Move":"DM Sans","Uber Move Text":"DM Sans",UberMoveText:"DM Sans","Airbnb Cereal VF":"Nunito Sans","Airbnb Cereal":"Nunito Sans",Cereal:"Nunito Sans","Noto Sans":"Noto Sans","D-DIN":"DM Sans","D-DIN-Bold":"DM Sans","Berkeley Mono":"JetBrains Mono","Circular Std":"DM Sans",Circular:"DM Sans",Graphik:"Inter","Neue Montreal":"Inter"},dr={landing:["visual theme","color","typography","component","layout","do","agent prompt"],design:["visual theme","color","typography","component","layout","depth","do","responsive","agent prompt"],dashboard:["color","typography","component","layout","depth","agent prompt"],crud:["component","layout","depth","agent prompt"],auth:["typography","component","agent prompt"],layout:["color","typography","layout","responsive","agent prompt"],admin:["color","typography","component","layout","depth","agent prompt"],general:["color","typography","component","layout","agent prompt"]},yt=[{id:"cohere",name:"Cohere",category:"AI & Machine Learning",sections:[{title:"1. Visual Theme & Atmosphere",content:`Cohere's interface is a polished enterprise command deck \u2014 confident, clean, and designed to make AI feel like serious infrastructure rather than a consumer toy. The experience lives on a bright white canvas where content is organized into generously rounded cards (22px radius) that create an organic, cloud-like containment language. This is a site that speaks to CTOs and enterprise architects: professional without being cold, sophisticated without being intimidating.
450
+ `)}function qn(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function Xt(r){return(r?Qt(r):$e).map(e=>{let a=Jt[e.id];return{id:e.id,slug:qn(e.title),title:e.title,category:e.category,description:a?.description??"",tags:a?.tags??[],theme:a?.theme??"dark",colors:a?.colors??[],style:a?.style??""}})}function cr(r,t){let e=r.toLowerCase(),a=t?.maxResults??3,o=t?.themePreference,i=[[/saas|software|platform|dashboard|analytics/i,"saas",3],[/agency|studio|creative|design\s*agency/i,"agency",3],[/portfolio|personal|resume|cv/i,"portfolio",3],[/web3|crypto|blockchain|nft|defi/i,"web3",3],[/booking|reservation|hotel|schedule/i,"booking",3],[/ai|artificial|machine\s*learning|automation/i,"ai",2],[/education|course|learning|academy/i,"education",3],[/newsletter|blog|content|editorial/i,"newsletter",3],[/pricing|calculator|cost/i,"pricing",3],[/luxury|premium|jet|high.?end/i,"luxury",3],[/talent|hiring|recruit|hr/i,"talent",3],[/logistics|transport|shipping|delivery/i,"logistics",3],[/invoice|billing|payment/i,"invoicing",3],[/task|project\s*manag|todo/i,"task-management",3],[/fintech|finance|banking|money/i,"fintech",3],[/minimal|clean|simple/i,"minimal",2],[/dark|night|moody/i,"dark",1],[/light|bright|white/i,"light",1],[/glass|frosted|blur/i,"liquid-glass",2],[/animated|animation|motion/i,"animation",2],[/gradient|colorful/i,"gradient",1],[/cinematic|epic|dramatic/i,"cinematic",2]],n=Xt().map(d=>{let u=0;for(let[h,g,y]of i)h.test(e)&&d.tags.includes(g)&&(u+=y);let c=d.category.toLowerCase();return e.includes(c)&&(u+=2),o&&d.theme===o&&(u+=1),d.id==="18"&&!/loader|loading|animation/i.test(e)&&(u-=5),d.id==="price-calculator"&&!/pric|calculator|cost|estimat/i.test(e)&&(u-=5),{preset:d,score:u}});n.sort((d,u)=>u.score+Math.random()*.5-(d.score+Math.random()*.5));let s=n.filter(d=>d.score>0);return(s.length>=a?s.slice(0,a):n.slice(0,a)).map(d=>d.preset)}var ur={"SF Pro":"Inter","SF Pro Display":"Inter","SF Pro Text":"Inter","SF Pro Rounded":"Nunito Sans","SF Mono":"JetBrains Mono","system-ui":"Inter","ui-sans-serif":"Inter",Arial:"DM Sans","Inter Variable":"Inter",inter:"Inter","sohne-var":"Inter",sohne:"Inter",S\u00F6hne:"Inter",s\u00F6hne:"Inter",CohereText:"Source Serif 4","Unica77 Cohere Web":"Inter",Waldenburg:"Playfair Display",waldenburgNormal:"Playfair Display","rb-freigeist-neue":"Space Grotesk","basier-square":"Inter",Basier:"Inter","The Future":"Outfit",GeistMono:"JetBrains Mono",universalSans:"Inter",CursorGothic:"DM Serif Display",jjannon:"Crimson Text","Camera Plain Variable":"DM Sans",domaine:"DM Serif Display","Dammit Sans":"Rubik","Super Sans VF":"DM Sans","Matter Regular":"Inter","Degular Display":"DM Sans",abcDiatype:"DM Sans","ABC Diatype":"DM Sans","ABC Diatype Mono":"JetBrains Mono",__hashicorpSans_96f0ca:"Inter","MongoDB Value Serif":"Source Serif 4","Euclid Circular A":"DM Sans",Haas:"Inter","Cal Sans":"DM Sans",figmaSans:"Inter","GT Walsheim Framer Medium":"DM Sans","GT Walsheim":"DM Sans","GT Walsheim Pro":"DM Sans",Saans:"Inter",MediumLL:"Inter",NotionInter:"Inter",Roobert:"DM Sans","Roobert PRO Medium":"DM Sans","Pin Sans":"DM Sans",CoinbaseDisplay:"DM Sans",CoinbaseSans:"Inter","Kraken-Brand":"Space Grotesk","Kraken-Product":"Inter","Aeonik Pro":"DM Sans","Wise Sans":"DM Sans","BMWTypeNextLatin Light":"Inter",BMWTypeNextLatin:"Inter","IBM Plex Sans":"IBM Plex Sans","NVIDIA-EMEA":"Inter",SpotifyMixUI:"DM Sans","Uber Move":"DM Sans","Uber Move Text":"DM Sans",UberMoveText:"DM Sans","Airbnb Cereal VF":"Nunito Sans","Airbnb Cereal":"Nunito Sans",Cereal:"Nunito Sans","Noto Sans":"Noto Sans","D-DIN":"DM Sans","D-DIN-Bold":"DM Sans","Berkeley Mono":"JetBrains Mono","Circular Std":"DM Sans",Circular:"DM Sans",Graphik:"Inter","Neue Montreal":"Inter"},pr={landing:["visual theme","color","typography","component","layout","do","agent prompt"],design:["visual theme","color","typography","component","layout","depth","do","responsive","agent prompt"],dashboard:["color","typography","component","layout","depth","agent prompt"],crud:["component","layout","depth","agent prompt"],auth:["typography","component","agent prompt"],layout:["color","typography","layout","responsive","agent prompt"],admin:["color","typography","component","layout","depth","agent prompt"],general:["color","typography","component","layout","agent prompt"]},xt=[{id:"cohere",name:"Cohere",category:"AI & Machine Learning",sections:[{title:"1. Visual Theme & Atmosphere",content:`Cohere's interface is a polished enterprise command deck \u2014 confident, clean, and designed to make AI feel like serious infrastructure rather than a consumer toy. The experience lives on a bright white canvas where content is organized into generously rounded cards (22px radius) that create an organic, cloud-like containment language. This is a site that speaks to CTOs and enterprise architects: professional without being cold, sophisticated without being intimidating.
451
451
 
452
452
  The design language bridges two worlds with a dual-typeface system: CohereText, a custom display serif with tight tracking, gives headlines the gravitas of a technology manifesto, while Unica77 Cohere Web handles all body and UI text with geometric Swiss precision. This serif/sans pairing creates a "confident authority meets engineering clarity" personality that perfectly reflects an enterprise AI platform.
453
453
 
@@ -9227,14 +9227,14 @@ What makes Uber's design truly distinctive is its use of full-bleed photography
9227
9227
  5. For shadows, use "whisper shadow (rgba(0,0,0,0.12) 0px 4px 16px)" -- never heavy drop shadows
9228
9228
  6. Keep layouts compact and information-dense -- Uber is efficient, not airy
9229
9229
  7. Illustrations should be warm and human -- describe "stylized people in warm tones" not abstract shapes
9230
- 8. Pair black CTAs with white secondaries for balanced dual-action layouts`}]}],Jt={cohere:{description:"Cohere's interface is a polished enterprise command deck \u2014 confident, clean, and designed to make AI feel like serious infrastructure rather than a consumer toy. The experience lives on a bright wh...",category:"AI & Machine Learning",theme:"light",colors:["#000000","#212121","#17171c","#1863dc","#4c6ee6","#9b60aa"],fonts:{heading:"CohereText",body:"Unica77 Cohere Web"},tags:["light","ai-machine-learning","minimal","bold","enterprise","gradient"]},elevenlabs:{description:"ElevenLabs' website is a study in restrained elegance \u2014 a near-white canvas (`#ffffff`, `#f5f5f5`) where typography and subtle shadows do all the heavy lifting. The design feels like a premium audi...",category:"AI & Machine Learning",theme:"light",colors:["#ffffff","#f5f5f5","#f5f2ef","#000000","#4e4e4e","#777169"],fonts:{heading:"Waldenburg",body:"Inter"},tags:["light","ai-machine-learning","minimal","bold"]},minimax:{description:"MiniMax's website is a clean, product-showcase platform for a Chinese AI technology company that bridges consumer-friendly appeal with technical credibility. The design language is predominantly wh...",category:"AI & Machine Learning",theme:"light",colors:["#1456f0","#3daeff","#ea5ec1","#bfdbfe","#60a5fa","#3b82f6"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["light","ai-machine-learning","minimal","bold","playful","gradient"]},mistral:{description:"Mistral AI's interface is a sun-drenched landscape rendered in code \u2014 a warm, bold, unapologetically European design that trades the typical blue-screen AI aesthetic for golden amber, burnt orange,...",category:"AI & Machine Learning",theme:"dark",colors:["#fa520f","#fb6424","#ff8105","#ff8a00","#ffa110","#ffb83e"],fonts:{heading:"Arial",body:"Inter"},tags:["dark","ai-machine-learning","minimal","bold","elegant","gradient"]},ollama:{description:"Ollama's interface is radical minimalism taken to its logical conclusion \u2014 a pure-white void where content floats without decoration, shadow, or color. The design philosophy mirrors the product its...",category:"AI & Machine Learning",theme:"light",colors:["#000000","#262626","#090909","#ffffff","#fafafa","#e5e5e5"],fonts:{heading:"SF Pro Rounded",body:"ui-sans-serif"},tags:["light","ai-machine-learning","minimal","playful"]},opencode:{description:"OpenCode's website embodies a terminal-native, monospace-first aesthetic that reflects its identity as an open source AI coding agent. The entire visual system is built on a stark dark-on-light con...",category:"AI & Machine Learning",theme:"dark",colors:["#201d1d","#fdfcfc","#9a9898","#302c2c","#646262","#f1eeee"],fonts:{heading:"Inter",body:"16px (1.00rem)"},tags:["dark","ai-machine-learning","minimal","bold"]},replicate:{description:"Replicate's interface is a developer playground crackling with creative energy \u2014 a bold, high-contrast design that feels more like a music festival poster than a typical API platform. The hero sect...",category:"AI & Machine Learning",theme:"dark",colors:["#202020","#ea2804","#dd4425","#2b9a66","#24292e","#ffffff"],fonts:{heading:"rb-freigeist-neue",body:"basier-square"},tags:["dark","ai-machine-learning","minimal","bold","playful","gradient"]},runwayml:{description:"Runway's interface is a cinematic reel brought to life as a website \u2014 a dark, editorial, film-production-grade design where full-bleed photography and video ARE the primary UI elements. This is not...",category:"AI & Machine Learning",theme:"dark",colors:["#000000","#030303","#1a1a1a","#ffffff","#fefefe","#e9ecf2"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","ai-machine-learning","minimal","bold"]},together:{description:"Together AI's interface is a pastel-gradient dreamscape built for enterprise AI infrastructure \u2014 a design that somehow makes GPU clusters and model inference feel light, airy, and optimistic. The h...",category:"AI & Machine Learning",theme:"dark",colors:["#ef2cc1","#fc4c02","#010120","#bdbbff","#ffffff","#000000"],fonts:{heading:"The Future",body:"Inter"},tags:["dark","ai-machine-learning","minimal","playful","enterprise","modern","elegant","data-dense","gradient"]},voltagent:{description:"VoltAgent's interface is a deep-space command terminal for the AI age \u2014 a developer-facing darkness built on near-pure-black surfaces (`#050507`) where the only interruption is the electric pulse o...",category:"AI & Machine Learning",theme:"light",colors:["#00d992","#2fd6a1","#10b981","#818cf8","#306cce","#2554a0"],fonts:{heading:"system-ui",body:"Inter"},tags:["light","ai-machine-learning","playful","data-dense"]},x:{description:"xAI's website is a masterclass in dark-first, monospace-driven brutalist minimalism -- a design system that feels like it was built by engineers who understand that restraint is the ultimate form o...",category:"AI & Machine Learning",theme:"dark",colors:["#ffffff","#1f2228","#000000"],fonts:{heading:"GeistMono",body:"universalSans"},tags:["dark","ai-machine-learning","minimal","elegant","data-dense","gradient"]},cursor:{description:"Cursor's website is a study in warm minimalism meets code-editor elegance. The entire experience is built on a warm off-white canvas (`#f2f1ed`) with dark warm-brown text (`#26251e`) -- not pure bl...",category:"Developer Tools",theme:"light",colors:["#26251e","#f2f1ed","#e6e5e0","#ffffff","#000000","#f54e00"],fonts:{heading:"CursorGothic",body:"jjannon"},tags:["light","developer-tools","minimal"]},expo:{description:"Expo's interface is a luminous, confidence-radiating developer platform built on the premise that tools for building apps should feel as polished as the apps themselves. The entire experience lives...",category:"Developer Tools",theme:"dark",colors:["#000000","#1c2024","#0d74ce","#476cff","#47c2ff","#8145b5"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","developer-tools","playful"]},linear:{description:"Linear's website is a masterclass in dark-mode-first product design \u2014 a near-black canvas (`#08090a`) where content emerges from darkness like starlight. The overall impression is one of extreme pr...",category:"Developer Tools",theme:"dark",colors:["#010102","#08090a","#0f1011","#191a1b","#28282c","#f7f8f8"],fonts:{heading:"Inter Variable",body:"Inter Variable"},tags:["dark","developer-tools","minimal","bold"]},lovable:{description:"Lovable's website radiates warmth through restraint. The entire page sits on a creamy, parchment-toned background (`#f7f4ed`) that immediately separates it from the cold-white conventions of most d...",category:"Developer Tools",theme:"dark",colors:["#f7f4ed","#1c1c1c","#fcfbf8","#5f5f5d","#eceae4","#3b82f6"],fonts:{heading:"Camera Plain Variable",body:"Camera Plain Variable"},tags:["dark","developer-tools","minimal"]},mintlify:{description:"Mintlify's website is a study in documentation-as-product design \u2014 a white, airy, information-rich surface that treats clarity as its highest aesthetic value. The page opens with a luminous white (...",category:"Developer Tools",theme:"light",colors:["#0d0d0d","#ffffff","#18e299","#d4fae8","#0fa76e","#c37d0d"],fonts:{heading:"Inter",body:"Inter"},tags:["light","developer-tools","minimal","bold","gradient"]},posthog:{description:"PostHog's website feels like a startup's internal wiki that escaped into the wild \u2014 warm, irreverent, and deliberately anti-corporate. The background isn't the expected crisp white or dark void of ...",category:"Developer Tools",theme:"dark",colors:["#4d4f46","#23251d","#f54e00","#f7a501","#b17816","#3b82f6"],fonts:{heading:"IBM Plex Sans Variable",body:"IBM Plex Sans Variable"},tags:["dark","developer-tools","bold","playful","modern","gradient"]},raycast:{description:"Raycast's marketing site feels like the dark interior of a precision instrument \u2014 a Swiss watch case carved from obsidian. The background isn't just dark, it's an almost-black blue-tint (`#07080a`)...",category:"Developer Tools",theme:"dark",colors:["#07080a","#ffffff","#ff6363","#55b3ff","#5fc992","#ffbc33"],fonts:{heading:"Inter",body:"16px"},tags:["dark","developer-tools","minimal","data-dense","gradient"]},resend:{description:"Resend's website is a dark, cinematic canvas that treats email infrastructure like a luxury product. The entire page is draped in pure black (`#000000`) with text that glows in near-white (`#f0f0f0...",category:"Developer Tools",theme:"light",colors:["#000000","#f0f0f0","#ffffff","#ff5900","#ff801f","#ffa057"],fonts:{heading:"domaine",body:"inter"},tags:["light","developer-tools","minimal","elegant"]},sentry:{description:"Sentry's website is a dark-mode-first developer tool interface that speaks the language of code editors and terminal windows. The entire aesthetic is rooted in deep purple-black backgrounds (`#1f16...",category:"Developer Tools",theme:"dark",colors:["#1f1633","#150f23","#362d59","#6a5fc1","#79628c","#422082"],fonts:{heading:"Dammit Sans",body:"Rubik"},tags:["dark","developer-tools","bold"]},supabase:{description:"Supabase's website is a dark-mode-native developer platform that channels the aesthetic of a premium code editor \u2014 deep black backgrounds (`#0f0f0f`, `#171717`) with emerald green accents (`#3ecf8e...",category:"Developer Tools",theme:"dark",colors:["#3ecf8e","#00c573","#0f0f0f","#171717","#242424","#2e2e2e"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["dark","developer-tools","minimal","data-dense"]},superhuman:{description:"Superhuman's website feels like opening a luxury envelope \u2014 predominantly white, immaculately clean, with a single dramatic gesture of color that commands attention. The hero section is a cinematic...",category:"Developer Tools",theme:"dark",colors:["#1b1938","#cbb7fb","#292827","#714cb6","#ffffff","#e9e5dd"],fonts:{heading:"Super Sans VF",body:"Super Sans VF"},tags:["dark","developer-tools","minimal","bold","elegant","data-dense","gradient"]},vercel:{description:"Vercel's website is the visual thesis of developer infrastructure made invisible \u2014 a design system so restrained it borders on philosophical. The page is overwhelmingly white (`#ffffff`) with near-...",category:"Developer Tools",theme:"dark",colors:["#171717","#ffffff","#000000","#ff5b4f","#de1d8d","#0a72ef"],fonts:{heading:"Geist",body:"Geist"},tags:["dark","developer-tools","minimal"]},warp:{description:"Warp's website feels like sitting at a campfire in a deep forest \u2014 warm, dark, and alive with quiet confidence. Unlike the cold, blue-tinted blacks favored by most developer tools, Warp wraps every...",category:"Developer Tools",theme:"dark",colors:["#faf9f6","#353534","#868584","#afaeac","#666469","#454545"],fonts:{heading:"Matter Regular",body:"Matter Regular"},tags:["dark","developer-tools","minimal","bold"]},zapier:{description:"Zapier's website radiates warm, approachable professionalism. It rejects the cold monochrome minimalism of developer tools in favor of a cream-tinted canvas (`#fffefb`) that feels like unbleached p...",category:"Developer Tools",theme:"dark",colors:["#201515","#fffefb","#fffdf9","#ff4f00","#36342e","#939084"],fonts:{heading:"Degular Display",body:"Inter"},tags:["dark","developer-tools","minimal","bold","enterprise","modern","elegant"]},clickhouse:{description:`ClickHouse's interface is a high-performance cockpit rendered in acid yellow-green on obsidian black \u2014 a design that screams "speed" before you read a single word. The entire experience lives in da...`,category:"Infrastructure",theme:"dark",colors:["#faff69","#166534","#14572f","#f4f692","#4f5100","#161600"],fonts:{heading:"Inter",body:"Basier"},tags:["dark","infrastructure"]},composio:{description:"Composio's interface is a nocturnal command center \u2014 a dense, developer-focused darkness punctuated by electric cyan and deep cobalt signals. The entire experience is built on an almost-pure-black ...",category:"Infrastructure",theme:"dark",colors:["#0007cd","#00ffff","#0089ff","#0096ff","#0f0f0f","#000000"],fonts:{heading:"abcDiatype",body:"Inter"},tags:["dark","infrastructure","minimal","data-dense","gradient"]},hashicorp:{description:"HashiCorp's website is enterprise infrastructure made tangible \u2014 a design system that must communicate the complexity of cloud infrastructure management while remaining approachable. The visual lan...",category:"Infrastructure",theme:"dark",colors:["#000000","#15181e","#0d0e12","#f1f2f3","#d5d7db","#b2b6bd"],fonts:{heading:"__hashicorpSans_96f0ca",body:"system-ui"},tags:["dark","infrastructure","minimal","bold","enterprise","data-dense"]},mongodb:{description:"MongoDB's website is a deep-forest-meets-terminal experience \u2014 a design system rooted in the darkest teal-black (`#001e2b`) that evokes both the density of a database and the depth of a forest cano...",category:"Infrastructure",theme:"dark",colors:["#001e2b","#00ed64","#00684a","#006cfa","#3860be","#1eaedb"],fonts:{heading:"MongoDB Value Serif",body:"Euclid Circular A"},tags:["dark","infrastructure","bold","enterprise"]},sanity:{description:"Sanity's website is a developer-content platform rendered as a nocturnal command center -- dark, precise, and deeply structured. The entire experience sits on a near-black canvas (`#0b0b0b`) that r...",category:"Infrastructure",theme:"dark",colors:["#0b0b0b","#000000","#f36458","#0052ef","#55beff","#afe3ff"],fonts:{heading:"waldenburgNormal",body:"waldenburgNormal"},tags:["dark","infrastructure","playful"]},stripe:{description:"Stripe's website is the gold standard of fintech design -- a system that manages to feel simultaneously technical and luxurious, precise and warm. The page opens on a clean white canvas (`#ffffff`)...",category:"Infrastructure",theme:"light",colors:["#533afd","#061b31","#ffffff","#1c1e54","#0d253d","#ea2261"],fonts:{heading:"Inter",body:"Inter"},tags:["light","infrastructure","minimal","bold","enterprise","modern","data-dense","gradient"]},airtable:{description:'Airtable\'s website is a clean, enterprise-friendly platform that communicates "sophisticated simplicity" through a white canvas with deep navy text (`#181d26`) and Airtable Blue (`#1b61c9`) as the ...',category:"Design & Productivity",theme:"light",colors:["#181d26","#1b61c9","#ffffff","#006400","#333333","#254fad"],fonts:{heading:"Haas",body:"Haas"},tags:["light","design-productivity","minimal","playful","enterprise"]},cal:{description:"Cal.com's website is a masterclass in monochromatic restraint \u2014 a grayscale world where boldness comes not from color but from the sheer confidence of black text on white space. Inspired by Uber's ...",category:"Design & Productivity",theme:"dark",colors:["#242424","#111111","#ffffff","#0099ff","#3b82f6","#0000ee"],fonts:{heading:"Cal Sans",body:"Inter"},tags:["dark","design-productivity","minimal","bold","modern","data-dense"]},clay:{description:"Clay's website is a warm, playful celebration of color that treats B2B data enrichment like a craft rather than an enterprise chore. The design language is built on a foundation of warm cream backg...",category:"Design & Productivity",theme:"light",colors:["#000000","#ffffff","#faf9f7","#84e7a5","#078a52","#02492a"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["light","design-productivity","playful","enterprise"]},figma:{description:"Figma's interface is the design tool that designed itself \u2014 a masterclass in typographic sophistication where a custom variable font (figmaSans) modulates between razor-thin (weight 320) and bold (...",category:"Design & Productivity",theme:"dark",colors:["#000000","#ffffff"],fonts:{heading:"figmaSans",body:"Inter"},tags:["dark","design-productivity","bold","gradient"]},framer:{description:"Framer's website is a cinematic, tool-obsessed dark canvas that radiates the confidence of a design tool built by designers who worship craft. The entire experience is drenched in pure black \u2014 not ...",category:"Design & Productivity",theme:"dark",colors:["#000000","#ffffff","#0099ff","#a6a6a6","#090909","#0000ee"],fonts:{heading:"GT Walsheim Framer Medium",body:"Inter Variable"},tags:["dark","design-productivity"]},intercom:{description:'Intercom\'s website is a warm, confident customer service platform that communicates "AI-first helpdesk" through a clean, editorial design language. The page operates on a warm off-white canvas (`#f...',category:"Design & Productivity",theme:"light",colors:["#111111","#ffffff","#faf9f6","#ff5600","#fe4c02","#65b5ff"],fonts:{heading:"Saans",body:"MediumLL"},tags:["light","design-productivity","minimal"]},miro:{description:`Miro's website is a clean, collaborative-tool-forward platform that communicates "visual thinking" through generous whitespace, pastel accent colors, and a confident geometric font. The design uses...`,category:"Design & Productivity",theme:"light",colors:["#1c1c1e","#ffffff","#5b76fe","#2a41b6","#ffc6c6","#600000"],fonts:{heading:"Roobert PRO Medium",body:"Noto Sans"},tags:["light","design-productivity","minimal","modern"]},notion:{description:"Notion's website embodies the philosophy of the tool itself: a blank canvas that gets out of your way. The design system is built on warm neutrals rather than cold grays, creating a distinctly appr...",category:"Design & Productivity",theme:"dark",colors:["#ffffff","#0075de","#213183","#005bab","#f6f5f4","#31302e"],fonts:{heading:"NotionInter",body:"NotionInter"},tags:["dark","design-productivity","minimal","bold"]},pinterest:{description:"Pinterest's website is a warm, inspiration-driven canvas that treats visual discovery like a lifestyle magazine. The design operates on a soft, slightly warm white background with Pinterest Red (`#...",category:"Design & Productivity",theme:"dark",colors:["#e60023","#103c25","#0b2819","#211922","#000000","#62625b"],fonts:{heading:"Pin Sans",body:"Pin Sans"},tags:["dark","design-productivity","bold"]},webflow:{description:'Webflow\'s website is a visually rich, tool-forward platform that communicates "design without code" through clean white surfaces, the signature Webflow Blue (`#146ef5`), and a rich secondary color ...',category:"Design & Productivity",theme:"dark",colors:["#080808","#146ef5","#3b89ff","#006acc","#0055d4","#7a3dff"],fonts:{heading:"Inter",body:"20px"},tags:["dark","design-productivity","minimal"]},coinbase:{description:"Coinbase's website is a clean, trustworthy crypto platform that communicates financial reliability through a blue-and-white binary palette. The design uses Coinbase Blue (`#0052ff`) \u2014 a deep, satur...",category:"Fintech",theme:"light",colors:["#0052ff","#ffffff","#0a0b0d","#eef0f3","#578bfa","#0667d0"],fonts:{heading:"CoinbaseDisplay",body:"CoinbaseSans"},tags:["light","fintech","minimal","enterprise"]},kraken:{description:"Kraken's website is a clean, trustworthy crypto exchange that uses purple as its commanding brand color. The design operates on white backgrounds with Kraken Purple (`#7132f5`, `#5741d8`, `#5b1ecf`...",category:"Fintech",theme:"light",colors:["#7132f5","#5741d8","#5b1ecf","#101114","#686b82","#9497a9"],fonts:{heading:"Kraken-Brand",body:"Kraken-Product"},tags:["light","fintech","minimal","bold","enterprise"]},revolut:{description:`Revolut's website is fintech confidence distilled into pixels \u2014 a design system that communicates "your money is in capable hands" through massive typography, generous whitespace, and a disciplined...`,category:"Fintech",theme:"dark",colors:["#191c1f","#ffffff","#f4f4f4","#494fdf","#4f55f1","#376cd5"],fonts:{heading:"Aeonik Pro",body:"Inter"},tags:["dark","fintech","modern"]},wise:{description:`Wise's website is a bold, confident fintech platform that communicates "money without borders" through massive typography and a distinctive lime-green accent. The design operates on a warm off-whit...`,category:"Fintech",theme:"dark",colors:["#0e0f0c","#9fe870","#163300","#e2f6d5","#cdffad","#054d28"],fonts:{heading:"Wise Sans",body:"Inter"},tags:["dark","fintech","minimal","bold","data-dense"]},airbnb:{description:"Airbnb's website is a warm, photography-forward marketplace that feels like flipping through a travel magazine where every page invites you to book. The design operates on a foundation of pure whit...",category:"Enterprise & Consumer",theme:"light",colors:["#ff385c","#e00b41","#c13515","#b32505","#460479","#92174d"],fonts:{heading:"Nunito Sans",body:"Inter"},tags:["light","enterprise-consumer","minimal","bold"]},apple:{description:"Apple's website is a masterclass in controlled drama \u2014 vast expanses of pure black and near-white serve as cinematic backdrops for products that are photographed as if they were sculptures in a gal...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#f5f5f7","#1d1d1f","#0071e3","#0066cc","#2997ff"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","enterprise-consumer","minimal","gradient"]},bmw:{description:"BMW's website is automotive engineering made visual \u2014 a design system that communicates precision, performance, and German industrial confidence. The page alternates between deep dark hero sections...",category:"Enterprise & Consumer",theme:"light",colors:["#ffffff","#1c69d4","#0653b6","#262626","#757575","#bbbbbb"],fonts:{heading:"BMWTypeNextLatin Light",body:"BMWTypeNextLatin"},tags:["light","enterprise-consumer","minimal","elegant"]},ibm:{description:"IBM's website is the digital embodiment of enterprise authority built on the Carbon Design System \u2014 a design language so methodically structured it reads like an engineering specification rendered ...",category:"Enterprise & Consumer",theme:"dark",colors:["#0f62fe","#ffffff","#161616","#262626","#393939","#525252"],fonts:{heading:"IBM Plex Sans",body:"Inter"},tags:["dark","enterprise-consumer","minimal","playful","enterprise"]},nvidia:{description:"NVIDIA's website is a high-contrast, technology-forward experience that communicates raw computational power through design restraint. The page is built on a stark black (`#000000`) and white (`#ff...",category:"Enterprise & Consumer",theme:"dark",colors:["#76b900","#000000","#ffffff","#bff230","#df6500","#ef9100"],fonts:{heading:"NVIDIA-EMEA",body:"NVIDIA-EMEA"},tags:["dark","enterprise-consumer","minimal","bold","playful","data-dense"]},spacex:{description:"SpaceX's website is a full-screen cinematic experience that treats aerospace engineering like a film \u2014 every section is a scene, every photograph is a frame, and the interface disappears entirely b...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#f0f0fa"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["dark","enterprise-consumer","minimal","bold"]},spotify:{description:"Spotify's web interface is a dark, immersive music player that wraps listeners in a near-black cocoon (`#121212`, `#181818`, `#1f1f1f`) where album art and content become the primary source of colo...",category:"Enterprise & Consumer",theme:"dark",colors:["#1ed760","#121212","#181818","#1f1f1f","#ffffff","#b3b3b3"],fonts:{heading:"Inter",body:"SpotifyMixUI"},tags:["dark","enterprise-consumer","bold"]},uber:{description:"Uber's design language is a masterclass in confident minimalism -- a black-and-white universe where every pixel serves a purpose and nothing decorates without earning its place. The entire experien...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#ffffff","#e2e2e2","#f3f3f3","#efefef","#4b4b4b"],fonts:{heading:"Inter",body:"UberMoveText"},tags:["dark","enterprise-consumer","minimal","bold","playful","data-dense"]}};function xt(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ye(r){let t=r.toLowerCase().replace(/[^a-z0-9]/g,"");return yt.find(e=>e.id===r||e.id.toLowerCase().replace(/[^a-z0-9]/g,"")===t||e.name.toLowerCase().replace(/[^a-z0-9]/g,"")===t)}function Ge(r){return Jt[r]}function Qt(r){return!r||r==="All"?yt:yt.filter(t=>t.category.toLowerCase()===r.toLowerCase())}function Xt(r){return Qt(r).map(e=>{let a=Jt[e.id];return{id:e.id,slug:xt(e.name),name:e.name,category:e.category,description:a?.description??"",theme:a?.theme??"light",colors:a?.colors??[],fonts:a?.fonts??{heading:"Inter",body:"Inter"},tags:a?.tags??[]}})}function pr(r){let t=r??yt,e={};for(let o of t)(e[o.category]??=[]).push(o);let a=[];for(let[o,i]of Object.entries(e)){a.push(`**${o}**`);for(let n of i){let s=Jt[n.id];a.push(` - ${n.name} (${n.id}) \u2014 ${s?.theme??"light"}, ${s?.description?.slice(0,60)??""}`)}}return a.join(`
9231
- `)}function ur(r,t){let e=ye(r);if(!e)return null;let a=dr[t]??dr.general;if(!a)return null;let o=e.sections.filter(i=>a.some(n=>i.title.toLowerCase().includes(n)));return o.length===0?null:o.map(i=>`## ${i.title}
9230
+ 8. Pair black CTAs with white secondaries for balanced dual-action layouts`}]}],Zt={cohere:{description:"Cohere's interface is a polished enterprise command deck \u2014 confident, clean, and designed to make AI feel like serious infrastructure rather than a consumer toy. The experience lives on a bright wh...",category:"AI & Machine Learning",theme:"light",colors:["#000000","#212121","#17171c","#1863dc","#4c6ee6","#9b60aa"],fonts:{heading:"CohereText",body:"Unica77 Cohere Web"},tags:["light","ai-machine-learning","minimal","bold","enterprise","gradient"]},elevenlabs:{description:"ElevenLabs' website is a study in restrained elegance \u2014 a near-white canvas (`#ffffff`, `#f5f5f5`) where typography and subtle shadows do all the heavy lifting. The design feels like a premium audi...",category:"AI & Machine Learning",theme:"light",colors:["#ffffff","#f5f5f5","#f5f2ef","#000000","#4e4e4e","#777169"],fonts:{heading:"Waldenburg",body:"Inter"},tags:["light","ai-machine-learning","minimal","bold"]},minimax:{description:"MiniMax's website is a clean, product-showcase platform for a Chinese AI technology company that bridges consumer-friendly appeal with technical credibility. The design language is predominantly wh...",category:"AI & Machine Learning",theme:"light",colors:["#1456f0","#3daeff","#ea5ec1","#bfdbfe","#60a5fa","#3b82f6"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["light","ai-machine-learning","minimal","bold","playful","gradient"]},mistral:{description:"Mistral AI's interface is a sun-drenched landscape rendered in code \u2014 a warm, bold, unapologetically European design that trades the typical blue-screen AI aesthetic for golden amber, burnt orange,...",category:"AI & Machine Learning",theme:"dark",colors:["#fa520f","#fb6424","#ff8105","#ff8a00","#ffa110","#ffb83e"],fonts:{heading:"Arial",body:"Inter"},tags:["dark","ai-machine-learning","minimal","bold","elegant","gradient"]},ollama:{description:"Ollama's interface is radical minimalism taken to its logical conclusion \u2014 a pure-white void where content floats without decoration, shadow, or color. The design philosophy mirrors the product its...",category:"AI & Machine Learning",theme:"light",colors:["#000000","#262626","#090909","#ffffff","#fafafa","#e5e5e5"],fonts:{heading:"SF Pro Rounded",body:"ui-sans-serif"},tags:["light","ai-machine-learning","minimal","playful"]},opencode:{description:"OpenCode's website embodies a terminal-native, monospace-first aesthetic that reflects its identity as an open source AI coding agent. The entire visual system is built on a stark dark-on-light con...",category:"AI & Machine Learning",theme:"dark",colors:["#201d1d","#fdfcfc","#9a9898","#302c2c","#646262","#f1eeee"],fonts:{heading:"Inter",body:"16px (1.00rem)"},tags:["dark","ai-machine-learning","minimal","bold"]},replicate:{description:"Replicate's interface is a developer playground crackling with creative energy \u2014 a bold, high-contrast design that feels more like a music festival poster than a typical API platform. The hero sect...",category:"AI & Machine Learning",theme:"dark",colors:["#202020","#ea2804","#dd4425","#2b9a66","#24292e","#ffffff"],fonts:{heading:"rb-freigeist-neue",body:"basier-square"},tags:["dark","ai-machine-learning","minimal","bold","playful","gradient"]},runwayml:{description:"Runway's interface is a cinematic reel brought to life as a website \u2014 a dark, editorial, film-production-grade design where full-bleed photography and video ARE the primary UI elements. This is not...",category:"AI & Machine Learning",theme:"dark",colors:["#000000","#030303","#1a1a1a","#ffffff","#fefefe","#e9ecf2"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","ai-machine-learning","minimal","bold"]},together:{description:"Together AI's interface is a pastel-gradient dreamscape built for enterprise AI infrastructure \u2014 a design that somehow makes GPU clusters and model inference feel light, airy, and optimistic. The h...",category:"AI & Machine Learning",theme:"dark",colors:["#ef2cc1","#fc4c02","#010120","#bdbbff","#ffffff","#000000"],fonts:{heading:"The Future",body:"Inter"},tags:["dark","ai-machine-learning","minimal","playful","enterprise","modern","elegant","data-dense","gradient"]},voltagent:{description:"VoltAgent's interface is a deep-space command terminal for the AI age \u2014 a developer-facing darkness built on near-pure-black surfaces (`#050507`) where the only interruption is the electric pulse o...",category:"AI & Machine Learning",theme:"light",colors:["#00d992","#2fd6a1","#10b981","#818cf8","#306cce","#2554a0"],fonts:{heading:"system-ui",body:"Inter"},tags:["light","ai-machine-learning","playful","data-dense"]},x:{description:"xAI's website is a masterclass in dark-first, monospace-driven brutalist minimalism -- a design system that feels like it was built by engineers who understand that restraint is the ultimate form o...",category:"AI & Machine Learning",theme:"dark",colors:["#ffffff","#1f2228","#000000"],fonts:{heading:"GeistMono",body:"universalSans"},tags:["dark","ai-machine-learning","minimal","elegant","data-dense","gradient"]},cursor:{description:"Cursor's website is a study in warm minimalism meets code-editor elegance. The entire experience is built on a warm off-white canvas (`#f2f1ed`) with dark warm-brown text (`#26251e`) -- not pure bl...",category:"Developer Tools",theme:"light",colors:["#26251e","#f2f1ed","#e6e5e0","#ffffff","#000000","#f54e00"],fonts:{heading:"CursorGothic",body:"jjannon"},tags:["light","developer-tools","minimal"]},expo:{description:"Expo's interface is a luminous, confidence-radiating developer platform built on the premise that tools for building apps should feel as polished as the apps themselves. The entire experience lives...",category:"Developer Tools",theme:"dark",colors:["#000000","#1c2024","#0d74ce","#476cff","#47c2ff","#8145b5"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","developer-tools","playful"]},linear:{description:"Linear's website is a masterclass in dark-mode-first product design \u2014 a near-black canvas (`#08090a`) where content emerges from darkness like starlight. The overall impression is one of extreme pr...",category:"Developer Tools",theme:"dark",colors:["#010102","#08090a","#0f1011","#191a1b","#28282c","#f7f8f8"],fonts:{heading:"Inter Variable",body:"Inter Variable"},tags:["dark","developer-tools","minimal","bold"]},lovable:{description:"Lovable's website radiates warmth through restraint. The entire page sits on a creamy, parchment-toned background (`#f7f4ed`) that immediately separates it from the cold-white conventions of most d...",category:"Developer Tools",theme:"dark",colors:["#f7f4ed","#1c1c1c","#fcfbf8","#5f5f5d","#eceae4","#3b82f6"],fonts:{heading:"Camera Plain Variable",body:"Camera Plain Variable"},tags:["dark","developer-tools","minimal"]},mintlify:{description:"Mintlify's website is a study in documentation-as-product design \u2014 a white, airy, information-rich surface that treats clarity as its highest aesthetic value. The page opens with a luminous white (...",category:"Developer Tools",theme:"light",colors:["#0d0d0d","#ffffff","#18e299","#d4fae8","#0fa76e","#c37d0d"],fonts:{heading:"Inter",body:"Inter"},tags:["light","developer-tools","minimal","bold","gradient"]},posthog:{description:"PostHog's website feels like a startup's internal wiki that escaped into the wild \u2014 warm, irreverent, and deliberately anti-corporate. The background isn't the expected crisp white or dark void of ...",category:"Developer Tools",theme:"dark",colors:["#4d4f46","#23251d","#f54e00","#f7a501","#b17816","#3b82f6"],fonts:{heading:"IBM Plex Sans Variable",body:"IBM Plex Sans Variable"},tags:["dark","developer-tools","bold","playful","modern","gradient"]},raycast:{description:"Raycast's marketing site feels like the dark interior of a precision instrument \u2014 a Swiss watch case carved from obsidian. The background isn't just dark, it's an almost-black blue-tint (`#07080a`)...",category:"Developer Tools",theme:"dark",colors:["#07080a","#ffffff","#ff6363","#55b3ff","#5fc992","#ffbc33"],fonts:{heading:"Inter",body:"16px"},tags:["dark","developer-tools","minimal","data-dense","gradient"]},resend:{description:"Resend's website is a dark, cinematic canvas that treats email infrastructure like a luxury product. The entire page is draped in pure black (`#000000`) with text that glows in near-white (`#f0f0f0...",category:"Developer Tools",theme:"light",colors:["#000000","#f0f0f0","#ffffff","#ff5900","#ff801f","#ffa057"],fonts:{heading:"domaine",body:"inter"},tags:["light","developer-tools","minimal","elegant"]},sentry:{description:"Sentry's website is a dark-mode-first developer tool interface that speaks the language of code editors and terminal windows. The entire aesthetic is rooted in deep purple-black backgrounds (`#1f16...",category:"Developer Tools",theme:"dark",colors:["#1f1633","#150f23","#362d59","#6a5fc1","#79628c","#422082"],fonts:{heading:"Dammit Sans",body:"Rubik"},tags:["dark","developer-tools","bold"]},supabase:{description:"Supabase's website is a dark-mode-native developer platform that channels the aesthetic of a premium code editor \u2014 deep black backgrounds (`#0f0f0f`, `#171717`) with emerald green accents (`#3ecf8e...",category:"Developer Tools",theme:"dark",colors:["#3ecf8e","#00c573","#0f0f0f","#171717","#242424","#2e2e2e"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["dark","developer-tools","minimal","data-dense"]},superhuman:{description:"Superhuman's website feels like opening a luxury envelope \u2014 predominantly white, immaculately clean, with a single dramatic gesture of color that commands attention. The hero section is a cinematic...",category:"Developer Tools",theme:"dark",colors:["#1b1938","#cbb7fb","#292827","#714cb6","#ffffff","#e9e5dd"],fonts:{heading:"Super Sans VF",body:"Super Sans VF"},tags:["dark","developer-tools","minimal","bold","elegant","data-dense","gradient"]},vercel:{description:"Vercel's website is the visual thesis of developer infrastructure made invisible \u2014 a design system so restrained it borders on philosophical. The page is overwhelmingly white (`#ffffff`) with near-...",category:"Developer Tools",theme:"dark",colors:["#171717","#ffffff","#000000","#ff5b4f","#de1d8d","#0a72ef"],fonts:{heading:"Geist",body:"Geist"},tags:["dark","developer-tools","minimal"]},warp:{description:"Warp's website feels like sitting at a campfire in a deep forest \u2014 warm, dark, and alive with quiet confidence. Unlike the cold, blue-tinted blacks favored by most developer tools, Warp wraps every...",category:"Developer Tools",theme:"dark",colors:["#faf9f6","#353534","#868584","#afaeac","#666469","#454545"],fonts:{heading:"Matter Regular",body:"Matter Regular"},tags:["dark","developer-tools","minimal","bold"]},zapier:{description:"Zapier's website radiates warm, approachable professionalism. It rejects the cold monochrome minimalism of developer tools in favor of a cream-tinted canvas (`#fffefb`) that feels like unbleached p...",category:"Developer Tools",theme:"dark",colors:["#201515","#fffefb","#fffdf9","#ff4f00","#36342e","#939084"],fonts:{heading:"Degular Display",body:"Inter"},tags:["dark","developer-tools","minimal","bold","enterprise","modern","elegant"]},clickhouse:{description:`ClickHouse's interface is a high-performance cockpit rendered in acid yellow-green on obsidian black \u2014 a design that screams "speed" before you read a single word. The entire experience lives in da...`,category:"Infrastructure",theme:"dark",colors:["#faff69","#166534","#14572f","#f4f692","#4f5100","#161600"],fonts:{heading:"Inter",body:"Basier"},tags:["dark","infrastructure"]},composio:{description:"Composio's interface is a nocturnal command center \u2014 a dense, developer-focused darkness punctuated by electric cyan and deep cobalt signals. The entire experience is built on an almost-pure-black ...",category:"Infrastructure",theme:"dark",colors:["#0007cd","#00ffff","#0089ff","#0096ff","#0f0f0f","#000000"],fonts:{heading:"abcDiatype",body:"Inter"},tags:["dark","infrastructure","minimal","data-dense","gradient"]},hashicorp:{description:"HashiCorp's website is enterprise infrastructure made tangible \u2014 a design system that must communicate the complexity of cloud infrastructure management while remaining approachable. The visual lan...",category:"Infrastructure",theme:"dark",colors:["#000000","#15181e","#0d0e12","#f1f2f3","#d5d7db","#b2b6bd"],fonts:{heading:"__hashicorpSans_96f0ca",body:"system-ui"},tags:["dark","infrastructure","minimal","bold","enterprise","data-dense"]},mongodb:{description:"MongoDB's website is a deep-forest-meets-terminal experience \u2014 a design system rooted in the darkest teal-black (`#001e2b`) that evokes both the density of a database and the depth of a forest cano...",category:"Infrastructure",theme:"dark",colors:["#001e2b","#00ed64","#00684a","#006cfa","#3860be","#1eaedb"],fonts:{heading:"MongoDB Value Serif",body:"Euclid Circular A"},tags:["dark","infrastructure","bold","enterprise"]},sanity:{description:"Sanity's website is a developer-content platform rendered as a nocturnal command center -- dark, precise, and deeply structured. The entire experience sits on a near-black canvas (`#0b0b0b`) that r...",category:"Infrastructure",theme:"dark",colors:["#0b0b0b","#000000","#f36458","#0052ef","#55beff","#afe3ff"],fonts:{heading:"waldenburgNormal",body:"waldenburgNormal"},tags:["dark","infrastructure","playful"]},stripe:{description:"Stripe's website is the gold standard of fintech design -- a system that manages to feel simultaneously technical and luxurious, precise and warm. The page opens on a clean white canvas (`#ffffff`)...",category:"Infrastructure",theme:"light",colors:["#533afd","#061b31","#ffffff","#1c1e54","#0d253d","#ea2261"],fonts:{heading:"Inter",body:"Inter"},tags:["light","infrastructure","minimal","bold","enterprise","modern","data-dense","gradient"]},airtable:{description:'Airtable\'s website is a clean, enterprise-friendly platform that communicates "sophisticated simplicity" through a white canvas with deep navy text (`#181d26`) and Airtable Blue (`#1b61c9`) as the ...',category:"Design & Productivity",theme:"light",colors:["#181d26","#1b61c9","#ffffff","#006400","#333333","#254fad"],fonts:{heading:"Haas",body:"Haas"},tags:["light","design-productivity","minimal","playful","enterprise"]},cal:{description:"Cal.com's website is a masterclass in monochromatic restraint \u2014 a grayscale world where boldness comes not from color but from the sheer confidence of black text on white space. Inspired by Uber's ...",category:"Design & Productivity",theme:"dark",colors:["#242424","#111111","#ffffff","#0099ff","#3b82f6","#0000ee"],fonts:{heading:"Cal Sans",body:"Inter"},tags:["dark","design-productivity","minimal","bold","modern","data-dense"]},clay:{description:"Clay's website is a warm, playful celebration of color that treats B2B data enrichment like a craft rather than an enterprise chore. The design language is built on a foundation of warm cream backg...",category:"Design & Productivity",theme:"light",colors:["#000000","#ffffff","#faf9f7","#84e7a5","#078a52","#02492a"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["light","design-productivity","playful","enterprise"]},figma:{description:"Figma's interface is the design tool that designed itself \u2014 a masterclass in typographic sophistication where a custom variable font (figmaSans) modulates between razor-thin (weight 320) and bold (...",category:"Design & Productivity",theme:"dark",colors:["#000000","#ffffff"],fonts:{heading:"figmaSans",body:"Inter"},tags:["dark","design-productivity","bold","gradient"]},framer:{description:"Framer's website is a cinematic, tool-obsessed dark canvas that radiates the confidence of a design tool built by designers who worship craft. The entire experience is drenched in pure black \u2014 not ...",category:"Design & Productivity",theme:"dark",colors:["#000000","#ffffff","#0099ff","#a6a6a6","#090909","#0000ee"],fonts:{heading:"GT Walsheim Framer Medium",body:"Inter Variable"},tags:["dark","design-productivity"]},intercom:{description:'Intercom\'s website is a warm, confident customer service platform that communicates "AI-first helpdesk" through a clean, editorial design language. The page operates on a warm off-white canvas (`#f...',category:"Design & Productivity",theme:"light",colors:["#111111","#ffffff","#faf9f6","#ff5600","#fe4c02","#65b5ff"],fonts:{heading:"Saans",body:"MediumLL"},tags:["light","design-productivity","minimal"]},miro:{description:`Miro's website is a clean, collaborative-tool-forward platform that communicates "visual thinking" through generous whitespace, pastel accent colors, and a confident geometric font. The design uses...`,category:"Design & Productivity",theme:"light",colors:["#1c1c1e","#ffffff","#5b76fe","#2a41b6","#ffc6c6","#600000"],fonts:{heading:"Roobert PRO Medium",body:"Noto Sans"},tags:["light","design-productivity","minimal","modern"]},notion:{description:"Notion's website embodies the philosophy of the tool itself: a blank canvas that gets out of your way. The design system is built on warm neutrals rather than cold grays, creating a distinctly appr...",category:"Design & Productivity",theme:"dark",colors:["#ffffff","#0075de","#213183","#005bab","#f6f5f4","#31302e"],fonts:{heading:"NotionInter",body:"NotionInter"},tags:["dark","design-productivity","minimal","bold"]},pinterest:{description:"Pinterest's website is a warm, inspiration-driven canvas that treats visual discovery like a lifestyle magazine. The design operates on a soft, slightly warm white background with Pinterest Red (`#...",category:"Design & Productivity",theme:"dark",colors:["#e60023","#103c25","#0b2819","#211922","#000000","#62625b"],fonts:{heading:"Pin Sans",body:"Pin Sans"},tags:["dark","design-productivity","bold"]},webflow:{description:'Webflow\'s website is a visually rich, tool-forward platform that communicates "design without code" through clean white surfaces, the signature Webflow Blue (`#146ef5`), and a rich secondary color ...',category:"Design & Productivity",theme:"dark",colors:["#080808","#146ef5","#3b89ff","#006acc","#0055d4","#7a3dff"],fonts:{heading:"Inter",body:"20px"},tags:["dark","design-productivity","minimal"]},coinbase:{description:"Coinbase's website is a clean, trustworthy crypto platform that communicates financial reliability through a blue-and-white binary palette. The design uses Coinbase Blue (`#0052ff`) \u2014 a deep, satur...",category:"Fintech",theme:"light",colors:["#0052ff","#ffffff","#0a0b0d","#eef0f3","#578bfa","#0667d0"],fonts:{heading:"CoinbaseDisplay",body:"CoinbaseSans"},tags:["light","fintech","minimal","enterprise"]},kraken:{description:"Kraken's website is a clean, trustworthy crypto exchange that uses purple as its commanding brand color. The design operates on white backgrounds with Kraken Purple (`#7132f5`, `#5741d8`, `#5b1ecf`...",category:"Fintech",theme:"light",colors:["#7132f5","#5741d8","#5b1ecf","#101114","#686b82","#9497a9"],fonts:{heading:"Kraken-Brand",body:"Kraken-Product"},tags:["light","fintech","minimal","bold","enterprise"]},revolut:{description:`Revolut's website is fintech confidence distilled into pixels \u2014 a design system that communicates "your money is in capable hands" through massive typography, generous whitespace, and a disciplined...`,category:"Fintech",theme:"dark",colors:["#191c1f","#ffffff","#f4f4f4","#494fdf","#4f55f1","#376cd5"],fonts:{heading:"Aeonik Pro",body:"Inter"},tags:["dark","fintech","modern"]},wise:{description:`Wise's website is a bold, confident fintech platform that communicates "money without borders" through massive typography and a distinctive lime-green accent. The design operates on a warm off-whit...`,category:"Fintech",theme:"dark",colors:["#0e0f0c","#9fe870","#163300","#e2f6d5","#cdffad","#054d28"],fonts:{heading:"Wise Sans",body:"Inter"},tags:["dark","fintech","minimal","bold","data-dense"]},airbnb:{description:"Airbnb's website is a warm, photography-forward marketplace that feels like flipping through a travel magazine where every page invites you to book. The design operates on a foundation of pure whit...",category:"Enterprise & Consumer",theme:"light",colors:["#ff385c","#e00b41","#c13515","#b32505","#460479","#92174d"],fonts:{heading:"Nunito Sans",body:"Inter"},tags:["light","enterprise-consumer","minimal","bold"]},apple:{description:"Apple's website is a masterclass in controlled drama \u2014 vast expanses of pure black and near-white serve as cinematic backdrops for products that are photographed as if they were sculptures in a gal...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#f5f5f7","#1d1d1f","#0071e3","#0066cc","#2997ff"],fonts:{heading:"Inter",body:"Inter"},tags:["dark","enterprise-consumer","minimal","gradient"]},bmw:{description:"BMW's website is automotive engineering made visual \u2014 a design system that communicates precision, performance, and German industrial confidence. The page alternates between deep dark hero sections...",category:"Enterprise & Consumer",theme:"light",colors:["#ffffff","#1c69d4","#0653b6","#262626","#757575","#bbbbbb"],fonts:{heading:"BMWTypeNextLatin Light",body:"BMWTypeNextLatin"},tags:["light","enterprise-consumer","minimal","elegant"]},ibm:{description:"IBM's website is the digital embodiment of enterprise authority built on the Carbon Design System \u2014 a design language so methodically structured it reads like an engineering specification rendered ...",category:"Enterprise & Consumer",theme:"dark",colors:["#0f62fe","#ffffff","#161616","#262626","#393939","#525252"],fonts:{heading:"IBM Plex Sans",body:"Inter"},tags:["dark","enterprise-consumer","minimal","playful","enterprise"]},nvidia:{description:"NVIDIA's website is a high-contrast, technology-forward experience that communicates raw computational power through design restraint. The page is built on a stark black (`#000000`) and white (`#ff...",category:"Enterprise & Consumer",theme:"dark",colors:["#76b900","#000000","#ffffff","#bff230","#df6500","#ef9100"],fonts:{heading:"NVIDIA-EMEA",body:"NVIDIA-EMEA"},tags:["dark","enterprise-consumer","minimal","bold","playful","data-dense"]},spacex:{description:"SpaceX's website is a full-screen cinematic experience that treats aerospace engineering like a film \u2014 every section is a scene, every photograph is a frame, and the interface disappears entirely b...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#f0f0fa"],fonts:{heading:"DM Sans",body:"DM Sans"},tags:["dark","enterprise-consumer","minimal","bold"]},spotify:{description:"Spotify's web interface is a dark, immersive music player that wraps listeners in a near-black cocoon (`#121212`, `#181818`, `#1f1f1f`) where album art and content become the primary source of colo...",category:"Enterprise & Consumer",theme:"dark",colors:["#1ed760","#121212","#181818","#1f1f1f","#ffffff","#b3b3b3"],fonts:{heading:"Inter",body:"SpotifyMixUI"},tags:["dark","enterprise-consumer","bold"]},uber:{description:"Uber's design language is a masterclass in confident minimalism -- a black-and-white universe where every pixel serves a purpose and nothing decorates without earning its place. The entire experien...",category:"Enterprise & Consumer",theme:"dark",colors:["#000000","#ffffff","#e2e2e2","#f3f3f3","#efefef","#4b4b4b"],fonts:{heading:"Inter",body:"UberMoveText"},tags:["dark","enterprise-consumer","minimal","bold","playful","data-dense"]}};function wt(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ye(r){let t=r.toLowerCase().replace(/[^a-z0-9]/g,"");return xt.find(e=>e.id===r||e.id.toLowerCase().replace(/[^a-z0-9]/g,"")===t||e.name.toLowerCase().replace(/[^a-z0-9]/g,"")===t)}function Ge(r){return Zt[r]}function ea(r){return!r||r==="All"?xt:xt.filter(t=>t.category.toLowerCase()===r.toLowerCase())}function ta(r){return ea(r).map(e=>{let a=Zt[e.id];return{id:e.id,slug:wt(e.name),name:e.name,category:e.category,description:a?.description??"",theme:a?.theme??"light",colors:a?.colors??[],fonts:a?.fonts??{heading:"Inter",body:"Inter"},tags:a?.tags??[]}})}function hr(r){let t=r??xt,e={};for(let o of t)(e[o.category]??=[]).push(o);let a=[];for(let[o,i]of Object.entries(e)){a.push(`**${o}**`);for(let n of i){let s=Zt[n.id];a.push(` - ${n.name} (${n.id}) \u2014 ${s?.theme??"light"}, ${s?.description?.slice(0,60)??""}`)}}return a.join(`
9231
+ `)}function gr(r,t){let e=ye(r);if(!e)return null;let a=pr[t]??pr.general;if(!a)return null;let o=e.sections.filter(i=>a.some(n=>i.title.toLowerCase().includes(n)));return o.length===0?null:o.map(i=>`## ${i.title}
9232
9232
 
9233
9233
  ${i.content}`).join(`
9234
9234
 
9235
9235
  ---
9236
9236
 
9237
- `)}function hr(r,t){let e=t?.maxResults??3,a=r.toLowerCase(),o=[[/\bcohere\b/i,"cohere"],[/\belevenlabs\b/i,"elevenlabs"],[/\bminimax\b/i,"minimax"],[/\bmistral ai\b/i,"mistral"],[/\bollama\b/i,"ollama"],[/\bopencode ai\b/i,"opencode"],[/\breplicate\b/i,"replicate"],[/\brunwayml\b/i,"runwayml"],[/\btogether ai\b/i,"together"],[/\bvoltagent\b/i,"voltagent"],[/\bxai\b/i,"x"],[/\bcursor\b/i,"cursor"],[/\bexpo\b/i,"expo"],[/\blinear\b/i,"linear"],[/\blovable\b/i,"lovable"],[/\bmintlify\b/i,"mintlify"],[/\bposthog\b/i,"posthog"],[/\braycast\b/i,"raycast"],[/\bresend\b/i,"resend"],[/\bsentry\b/i,"sentry"],[/\bsupabase\b/i,"supabase"],[/\bsuperhuman\b/i,"superhuman"],[/\bvercel\b/i,"vercel"],[/\bwarp\b/i,"warp"],[/\bzapier\b/i,"zapier"],[/\bclickhouse\b/i,"clickhouse"],[/\bcomposio\b/i,"composio"],[/\bhashicorp\b/i,"hashicorp"],[/\bmongodb\b/i,"mongodb"],[/\bsanity\b/i,"sanity"],[/\bstripe\b/i,"stripe"],[/\bairtable\b/i,"airtable"],[/\bcal\.com\b/i,"cal"],[/\bclay\b/i,"clay"],[/\bfigma\b/i,"figma"],[/\bframer\b/i,"framer"],[/\bintercom\b/i,"intercom"],[/\bmiro\b/i,"miro"],[/\bnotion\b/i,"notion"],[/\bpinterest\b/i,"pinterest"],[/\bwebflow\b/i,"webflow"],[/\bcoinbase\b/i,"coinbase"],[/\bkraken\b/i,"kraken"],[/\brevolut\b/i,"revolut"],[/\bwise\b/i,"wise"],[/\bairbnb\b/i,"airbnb"],[/\bapple\b/i,"apple"],[/\bbmw\b/i,"bmw"],[/\bibm\b/i,"ibm"],[/\bnvidia\b/i,"nvidia"],[/\bspacex\b/i,"spacex"],[/\bspotify\b/i,"spotify"],[/\buber\b/i,"uber"]],i=[[/\bdashboard|analytics|saas|admin\b/i,["linear","vercel","posthog","supabase","sentry"],3],[/\bai|machine.?learning|llm|chatbot\b/i,["mistral","opencode","cohere","replicate"],3],[/\bfintech|payment|banking|wallet\b/i,["stripe","revolut","wise","coinbase"],3],[/\bminimal|clean|simple\b/i,["notion","linear","cal","resend"],2],[/\bbold|cinematic|dramatic\b/i,["spacex","bmw","nvidia","apple"],2],[/\bcreative|design|portfolio\b/i,["figma","framer","webflow","pinterest"],2],[/\bproductivity|workspace|docs|notes\b/i,["notion","airtable","miro","superhuman"],2],[/\be-?commerce|marketplace|booking\b/i,["airbnb","spotify","uber","stripe"],2],[/\bdev.?tool|developer|api|cli\b/i,["vercel","linear","supabase","raycast","warp"],2],[/\bmusic|media|streaming\b/i,["spotify","elevenlabs","runwayml"],2],[/\bdark.?mode|dark.?theme\b/i,["linear","cursor","warp","raycast","spacex"],1],[/\blight|bright|white\b/i,["notion","airbnb","stripe","cal"],1]],n={};for(let[d,u]of o)d.test(a)&&(n[u]=(n[u]??0)+10);for(let[d,u,c]of i)if(d.test(a))for(let h of u)n[h]=(n[h]??0)+c;return Xt().map(d=>({...d,score:n[d.id]??0})).sort((d,u)=>u.score+Math.random()*.5-(d.score+Math.random()*.5)).slice(0,e)}var Zt={"resend-email":{description:"Transactional email with React Email templates and webhook handling.",tags:["email","transactional","welcome","notification","invite","alert"],envVars:[{key:"RESEND_API_KEY",description:"Resend API key for sending emails",setupUrl:"https://resend.com/api-keys"}],docsUrl:"https://resend.com/docs/send-with-nextjs",packages:["resend","@react-email/components"],difficulty:"easy"},"r2-storage":{description:"File uploads with drag-and-drop UI, stored in Cloudflare R2.",tags:["storage","upload","file","image","media","attachment","avatar"],envVars:[],docsUrl:"https://developers.cloudflare.com/r2/",packages:[],difficulty:"easy"},"openai-ai":{description:"AI-powered features with OpenAI SDK, streaming chat, and content generation.",tags:["ai","openai","chatbot","gpt","llm","assistant","generation"],envVars:[{key:"OPENAI_API_KEY",description:"OpenAI API key for AI features",setupUrl:"https://platform.openai.com/api-keys"}],docsUrl:"https://platform.openai.com/docs/guides/text-generation",packages:["openai","ai"],difficulty:"medium"},"anthropic-ai":{description:"AI features with the Anthropic SDK, streaming Claude chat, and content generation.",tags:["ai","anthropic","claude","llm","assistant","generation"],envVars:[{key:"ANTHROPIC_API_KEY",description:"Anthropic API key for Claude",setupUrl:"https://console.anthropic.com/settings/keys"}],docsUrl:"https://docs.anthropic.com/en/docs/initial-setup",packages:["@anthropic-ai/sdk","ai"],difficulty:"medium"},"openrouter-ai":{description:"AI model router with access to 200+ models (GPT, Claude, Llama, Mistral, etc.) through one API.",tags:["ai","openrouter","llm","multi-model","claude","gpt","llama","mistral"],envVars:[{key:"OPENROUTER_API_KEY",description:"OpenRouter API key",setupUrl:"https://openrouter.ai/keys"}],docsUrl:"https://openrouter.ai/docs/quickstart",packages:["@openrouter/sdk"],difficulty:"easy"},"stripe-payments":{description:"Payment processing with Stripe Checkout, webhooks, and billing portal.",tags:["payments","stripe","billing","subscription","checkout","invoice"],envVars:[{key:"STRIPE_SECRET_KEY",description:"Stripe secret key",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_PUBLISHABLE_KEY",description:"Stripe publishable key (client-side)",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_WEBHOOK_SECRET",description:"Stripe webhook signing secret",setupUrl:"https://dashboard.stripe.com/webhooks"}],docsUrl:"https://docs.stripe.com/checkout/quickstart",packages:["stripe","@stripe/stripe-js"],difficulty:"advanced"},"elevenlabs-voice":{description:"Text-to-speech and voice generation with ElevenLabs API.",tags:["voice","tts","speech","audio","elevenlabs","narration","podcast"],envVars:[{key:"ELEVENLABS_API_KEY",description:"ElevenLabs API key for voice generation",setupUrl:"https://elevenlabs.io/app/settings/api-keys"}],docsUrl:"https://elevenlabs.io/docs/api-reference/text-to-speech",packages:["elevenlabs"],difficulty:"medium"},"google-maps":{description:"Google Maps embed, Places autocomplete, and geolocation features.",tags:["maps","location","google","places","geocoding","directions","nearby"],envVars:[{key:"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY",description:"Google Maps API key (client-side)",setupUrl:"https://console.cloud.google.com/apis/credentials"}],docsUrl:"https://developers.google.com/maps/documentation/javascript",packages:["@googlemaps/js-api-loader"],difficulty:"medium"},"twilio-sms":{description:"SMS notifications, OTP verification, and phone number validation.",tags:["sms","twilio","otp","phone","verification","text-message"],envVars:[{key:"TWILIO_ACCOUNT_SID",description:"Twilio account SID",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_AUTH_TOKEN",description:"Twilio auth token",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_PHONE_NUMBER",description:"Twilio phone number for sending SMS",setupUrl:"https://console.twilio.com/us1/develop/phone-numbers/manage/incoming"}],docsUrl:"https://www.twilio.com/docs/messaging/quickstart/node",packages:["twilio"],difficulty:"medium"},"posthog-analytics":{description:"Product analytics with event tracking, feature flags, and session replay.",tags:["analytics","posthog","tracking","funnel","event","feature-flag"],envVars:[{key:"NEXT_PUBLIC_POSTHOG_KEY",description:"PostHog project API key",setupUrl:"https://app.posthog.com/project/settings"},{key:"NEXT_PUBLIC_POSTHOG_HOST",description:"PostHog instance URL (default: https://us.i.posthog.com)",setupUrl:"https://app.posthog.com/project/settings"}],docsUrl:"https://posthog.com/docs/libraries/next-js",packages:["posthog-js","posthog-node"],difficulty:"easy"},"firecrawl-scraping":{description:"Web scraping and crawling with markdown output, structured extraction, and async crawls.",tags:["scraping","crawl","firecrawl","web","extract","rag","markdown"],envVars:[{key:"FIRECRAWL_API_KEY",description:"Firecrawl API key",setupUrl:"https://firecrawl.dev"}],docsUrl:"https://docs.firecrawl.dev/sdks/node",packages:["@mendable/firecrawl-js"],difficulty:"easy"},"replicate-media":{description:"Image and video generation with 200+ AI models (Flux, Wan Video, Runway, SDXL, etc.) through one API.",tags:["image","video","replicate","flux","sdxl","generation","media","avatar","thumbnail"],envVars:[{key:"REPLICATE_API_TOKEN",description:"Replicate API token",setupUrl:"https://replicate.com/account/api-tokens"}],docsUrl:"https://replicate.com/docs/get-started/nodejs",packages:["replicate"],difficulty:"medium"}},Ve=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
9237
+ `)}function mr(r,t){let e=t?.maxResults??3,a=r.toLowerCase(),o=[[/\bcohere\b/i,"cohere"],[/\belevenlabs\b/i,"elevenlabs"],[/\bminimax\b/i,"minimax"],[/\bmistral ai\b/i,"mistral"],[/\bollama\b/i,"ollama"],[/\bopencode ai\b/i,"opencode"],[/\breplicate\b/i,"replicate"],[/\brunwayml\b/i,"runwayml"],[/\btogether ai\b/i,"together"],[/\bvoltagent\b/i,"voltagent"],[/\bxai\b/i,"x"],[/\bcursor\b/i,"cursor"],[/\bexpo\b/i,"expo"],[/\blinear\b/i,"linear"],[/\blovable\b/i,"lovable"],[/\bmintlify\b/i,"mintlify"],[/\bposthog\b/i,"posthog"],[/\braycast\b/i,"raycast"],[/\bresend\b/i,"resend"],[/\bsentry\b/i,"sentry"],[/\bsupabase\b/i,"supabase"],[/\bsuperhuman\b/i,"superhuman"],[/\bvercel\b/i,"vercel"],[/\bwarp\b/i,"warp"],[/\bzapier\b/i,"zapier"],[/\bclickhouse\b/i,"clickhouse"],[/\bcomposio\b/i,"composio"],[/\bhashicorp\b/i,"hashicorp"],[/\bmongodb\b/i,"mongodb"],[/\bsanity\b/i,"sanity"],[/\bstripe\b/i,"stripe"],[/\bairtable\b/i,"airtable"],[/\bcal\.com\b/i,"cal"],[/\bclay\b/i,"clay"],[/\bfigma\b/i,"figma"],[/\bframer\b/i,"framer"],[/\bintercom\b/i,"intercom"],[/\bmiro\b/i,"miro"],[/\bnotion\b/i,"notion"],[/\bpinterest\b/i,"pinterest"],[/\bwebflow\b/i,"webflow"],[/\bcoinbase\b/i,"coinbase"],[/\bkraken\b/i,"kraken"],[/\brevolut\b/i,"revolut"],[/\bwise\b/i,"wise"],[/\bairbnb\b/i,"airbnb"],[/\bapple\b/i,"apple"],[/\bbmw\b/i,"bmw"],[/\bibm\b/i,"ibm"],[/\bnvidia\b/i,"nvidia"],[/\bspacex\b/i,"spacex"],[/\bspotify\b/i,"spotify"],[/\buber\b/i,"uber"]],i=[[/\bdashboard|analytics|saas|admin\b/i,["linear","vercel","posthog","supabase","sentry"],3],[/\bai|machine.?learning|llm|chatbot\b/i,["mistral","opencode","cohere","replicate"],3],[/\bfintech|payment|banking|wallet\b/i,["stripe","revolut","wise","coinbase"],3],[/\bminimal|clean|simple\b/i,["notion","linear","cal","resend"],2],[/\bbold|cinematic|dramatic\b/i,["spacex","bmw","nvidia","apple"],2],[/\bcreative|design|portfolio\b/i,["figma","framer","webflow","pinterest"],2],[/\bproductivity|workspace|docs|notes\b/i,["notion","airtable","miro","superhuman"],2],[/\be-?commerce|marketplace|booking\b/i,["airbnb","spotify","uber","stripe"],2],[/\bdev.?tool|developer|api|cli\b/i,["vercel","linear","supabase","raycast","warp"],2],[/\bmusic|media|streaming\b/i,["spotify","elevenlabs","runwayml"],2],[/\bdark.?mode|dark.?theme\b/i,["linear","cursor","warp","raycast","spacex"],1],[/\blight|bright|white\b/i,["notion","airbnb","stripe","cal"],1]],n={};for(let[d,u]of o)d.test(a)&&(n[u]=(n[u]??0)+10);for(let[d,u,c]of i)if(d.test(a))for(let h of u)n[h]=(n[h]??0)+c;return ta().map(d=>({...d,score:n[d.id]??0})).sort((d,u)=>u.score+Math.random()*.5-(d.score+Math.random()*.5)).slice(0,e)}var aa={"resend-email":{description:"Transactional email with React Email templates and webhook handling.",tags:["email","transactional","welcome","notification","invite","alert"],envVars:[{key:"RESEND_API_KEY",description:"Resend API key for sending emails",setupUrl:"https://resend.com/api-keys"}],docsUrl:"https://resend.com/docs/send-with-nextjs",packages:["resend","@react-email/components"],difficulty:"easy"},"r2-storage":{description:"File uploads with drag-and-drop UI, stored in Cloudflare R2.",tags:["storage","upload","file","image","media","attachment","avatar"],envVars:[],docsUrl:"https://developers.cloudflare.com/r2/",packages:[],difficulty:"easy"},"openai-ai":{description:"AI-powered features with OpenAI SDK, streaming chat, and content generation.",tags:["ai","openai","chatbot","gpt","llm","assistant","generation"],envVars:[{key:"OPENAI_API_KEY",description:"OpenAI API key for AI features",setupUrl:"https://platform.openai.com/api-keys"}],docsUrl:"https://platform.openai.com/docs/guides/text-generation",packages:["openai","ai"],difficulty:"medium"},"anthropic-ai":{description:"AI features with the Anthropic SDK, streaming Claude chat, and content generation.",tags:["ai","anthropic","claude","llm","assistant","generation"],envVars:[{key:"ANTHROPIC_API_KEY",description:"Anthropic API key for Claude",setupUrl:"https://console.anthropic.com/settings/keys"}],docsUrl:"https://docs.anthropic.com/en/docs/initial-setup",packages:["@anthropic-ai/sdk","ai"],difficulty:"medium"},"openrouter-ai":{description:"AI model router with access to 200+ models (GPT, Claude, Llama, Mistral, etc.) through one API.",tags:["ai","openrouter","llm","multi-model","claude","gpt","llama","mistral"],envVars:[{key:"OPENROUTER_API_KEY",description:"OpenRouter API key",setupUrl:"https://openrouter.ai/keys"}],docsUrl:"https://openrouter.ai/docs/quickstart",packages:["@openrouter/sdk"],difficulty:"easy"},"stripe-payments":{description:"Payment processing with Stripe Checkout, webhooks, and billing portal.",tags:["payments","stripe","billing","subscription","checkout","invoice"],envVars:[{key:"STRIPE_SECRET_KEY",description:"Stripe secret key",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_PUBLISHABLE_KEY",description:"Stripe publishable key (client-side)",setupUrl:"https://dashboard.stripe.com/apikeys"},{key:"STRIPE_WEBHOOK_SECRET",description:"Stripe webhook signing secret",setupUrl:"https://dashboard.stripe.com/webhooks"}],docsUrl:"https://docs.stripe.com/checkout/quickstart",packages:["stripe","@stripe/stripe-js"],difficulty:"advanced"},"elevenlabs-voice":{description:"Text-to-speech and voice generation with ElevenLabs API.",tags:["voice","tts","speech","audio","elevenlabs","narration","podcast"],envVars:[{key:"ELEVENLABS_API_KEY",description:"ElevenLabs API key for voice generation",setupUrl:"https://elevenlabs.io/app/settings/api-keys"}],docsUrl:"https://elevenlabs.io/docs/api-reference/text-to-speech",packages:["elevenlabs"],difficulty:"medium"},"google-maps":{description:"Google Maps embed, Places autocomplete, and geolocation features.",tags:["maps","location","google","places","geocoding","directions","nearby"],envVars:[{key:"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY",description:"Google Maps API key (client-side)",setupUrl:"https://console.cloud.google.com/apis/credentials"}],docsUrl:"https://developers.google.com/maps/documentation/javascript",packages:["@googlemaps/js-api-loader"],difficulty:"medium"},"twilio-sms":{description:"SMS notifications, OTP verification, and phone number validation.",tags:["sms","twilio","otp","phone","verification","text-message"],envVars:[{key:"TWILIO_ACCOUNT_SID",description:"Twilio account SID",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_AUTH_TOKEN",description:"Twilio auth token",setupUrl:"https://console.twilio.com/"},{key:"TWILIO_PHONE_NUMBER",description:"Twilio phone number for sending SMS",setupUrl:"https://console.twilio.com/us1/develop/phone-numbers/manage/incoming"}],docsUrl:"https://www.twilio.com/docs/messaging/quickstart/node",packages:["twilio"],difficulty:"medium"},"posthog-analytics":{description:"Product analytics with event tracking, feature flags, and session replay.",tags:["analytics","posthog","tracking","funnel","event","feature-flag"],envVars:[{key:"NEXT_PUBLIC_POSTHOG_KEY",description:"PostHog project API key",setupUrl:"https://app.posthog.com/project/settings"},{key:"NEXT_PUBLIC_POSTHOG_HOST",description:"PostHog instance URL (default: https://us.i.posthog.com)",setupUrl:"https://app.posthog.com/project/settings"}],docsUrl:"https://posthog.com/docs/libraries/next-js",packages:["posthog-js","posthog-node"],difficulty:"easy"},"firecrawl-scraping":{description:"Web scraping and crawling with markdown output, structured extraction, and async crawls.",tags:["scraping","crawl","firecrawl","web","extract","rag","markdown"],envVars:[{key:"FIRECRAWL_API_KEY",description:"Firecrawl API key",setupUrl:"https://firecrawl.dev"}],docsUrl:"https://docs.firecrawl.dev/sdks/node",packages:["@mendable/firecrawl-js"],difficulty:"easy"},"replicate-media":{description:"Image and video generation with 200+ AI models (Flux, Wan Video, Runway, SDXL, etc.) through one API.",tags:["image","video","replicate","flux","sdxl","generation","media","avatar","thumbnail"],envVars:[{key:"REPLICATE_API_TOKEN",description:"Replicate API token",setupUrl:"https://replicate.com/account/api-tokens"}],docsUrl:"https://replicate.com/docs/get-started/nodejs",packages:["replicate"],difficulty:"medium"}},Ve=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
9238
9238
 
9239
9239
  ### File Structure
9240
9240
  \`\`\`
@@ -10654,30 +10654,30 @@ export function ImageGenerator() {
10654
10654
  5. **Replicate charges per prediction.** Flux Schnell is ~$0.003/image. Flux Pro is ~$0.05/image. Video models are $0.05-$0.50/generation. Show generation cost to users.
10655
10655
  6. **Cache generated images.** Store output URLs in your database. Replicate URLs expire after a few hours. Download and re-host on R2 for permanent storage.
10656
10656
  7. **Network I/O does NOT count as CPU time on Workers.** Image generation wait time is all network I/O.
10657
- 8. **Never ask the user to paste REPLICATE_API_TOKEN in chat.** Direct them to set it in the Mistflow dashboard (Project Settings > Environment Variables).`}];function Vn(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function qe(r){let t=Ve.find(a=>a.id===r);if(t)return t;let e=r.toLowerCase().replace(/[^a-z0-9]/g,"");return Ve.find(a=>{let o=a.name.toLowerCase().replace(/[^a-z0-9]/g,"");return o===e||o.includes(e)||e.includes(o)})}function Ke(r){return Zt[r]}function ea(r){return r?Ve.filter(t=>t.category.toLowerCase()===r.toLowerCase()):Ve}function gr(r){let t=r??Ve,e={};for(let o of t){e[o.category]||(e[o.category]=[]);let i=Zt[o.id],n=i?` \u2014 ${i.description}`:"",s=i?.packages.length?` (${i.packages.join(", ")})`:"";e[o.category].push(`${o.id} \u2014 "${o.name}"${n}${s}`)}let a=[];for(let[o,i]of Object.entries(e))a.push(`**${o}**:
10657
+ 8. **Never ask the user to paste REPLICATE_API_TOKEN in chat.** Direct them to set it in the Mistflow dashboard (Project Settings > Environment Variables).`}];function Kn(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function qe(r){let t=Ve.find(a=>a.id===r);if(t)return t;let e=r.toLowerCase().replace(/[^a-z0-9]/g,"");return Ve.find(a=>{let o=a.name.toLowerCase().replace(/[^a-z0-9]/g,"");return o===e||o.includes(e)||e.includes(o)})}function Ke(r){return aa[r]}function ra(r){return r?Ve.filter(t=>t.category.toLowerCase()===r.toLowerCase()):Ve}function fr(r){let t=r??Ve,e={};for(let o of t){e[o.category]||(e[o.category]=[]);let i=aa[o.id],n=i?` \u2014 ${i.description}`:"",s=i?.packages.length?` (${i.packages.join(", ")})`:"";e[o.category].push(`${o.id} \u2014 "${o.name}"${n}${s}`)}let a=[];for(let[o,i]of Object.entries(e))a.push(`**${o}**:
10658
10658
  ${i.map(n=>` - ${n}`).join(`
10659
10659
  `)}`);return a.join(`
10660
10660
 
10661
- `)}function mr(r){return(r?ea(r):Ve).map(e=>{let a=Zt[e.id];return{id:e.id,slug:Vn(e.name),name:e.name,category:e.category,description:a?.description??"",tags:a?.tags??[],envVars:a?.envVars??[],docsUrl:a?.docsUrl??"",packages:a?.packages??[],difficulty:a?.difficulty??"medium"}})}var qn=[{name:"Dashboard",description:"Overview with key stats and today's activity",condition:r=>r.surfaceType==="internal-tool"||r.surfaceType==="customer-app",keywords:/\b(dashboard|overview|home.?page|stats)\b/i},{name:"Landing Page",description:"Public page explaining what this does",condition:r=>r.publicLanding===!0,keywords:/\b(landing|marketing|hero|homepage)\b/i},{name:"Scheduling / Booking",description:"Calendar, time slots, reservations",condition:r=>r.scheduling===!0,keywords:/\b(schedul|book|reserv|appointment|calendar|slot)\b/i},{name:"Payments / Billing",description:"Charge users, invoices, subscriptions (experimental \u2014 Stripe integration is early-stage)",condition:()=>!1,keywords:/\b(payment|billing|invoice|subscription|checkout|stripe)\b/i},{name:"Admin Panel",description:"Manage users, roles, and content",condition:r=>r.multiRole===!0||r.primaryActor==="both",keywords:/\b(admin|panel|manage.?user|moderat)\b/i},{name:"User Profiles",description:"Account pages, settings, preferences",condition:r=>r.primaryActor==="customers"||r.primaryActor==="both",keywords:/\b(profile|account|settings|preferences)\b/i},{name:"Search / Browse",description:"Find and filter content or listings",condition:r=>r.surfaceType==="marketplace",keywords:/\b(search|browse|filter|discover|explore)\b/i},{name:"Email Notifications",description:"Welcome emails, alerts, reminders",condition:r=>r.integrations?.includes("email")===!0,keywords:/\b(notification|alert|reminder|email.?notif|sms|welcome.?email)\b/i},{name:"Analytics / Reports",description:"Usage stats, trends, data exports",condition:()=>!1,keywords:/\b(analytics|report|chart|trend|insight|metric)\b/i},{name:"File Uploads",description:"Images, documents, attachments",condition:r=>r.integrations?.includes("file-uploads")===!0,keywords:/\b(upload|image|photo|attachment|document|gallery|file)\b/i},{name:"AI Features",description:"Chatbot, content generation, AI assistant",condition:r=>r.integrations?.includes("ai")===!0,keywords:/\b(ai|chatbot|gpt|llm|generat|assistant)\b/i},{name:"Maps / Location",description:"Google Maps, location search, geolocation",condition:r=>r.integrations?.includes("maps")===!0,keywords:/\b(map|location|address|geo|places)\b/i},{name:"Voice / TTS",description:"Text-to-speech, voice notes, audio generation",condition:()=>!1,keywords:/\b(voice|tts|text.?to.?speech|audio|speak|narrat|podcast|elevenlabs)\b/i},{name:"SMS / Text Messages",description:"Send SMS notifications, OTP verification",condition:r=>r.integrations?.includes("sms")===!0,keywords:/\b(sms|text.?message|twilio|otp|verification.?code|phone.?verif)\b/i},{name:"Web Scraping",description:"Scrape URLs, crawl websites, extract structured data",condition:()=>!1,keywords:/\b(scrape|crawl|web.?scrap|extract.?from.?url|read.?url|ingest.?web|firecrawl)\b/i},{name:"Chat / Messaging",description:"Real-time messaging between users",condition:()=>!1,keywords:/\b(chat|messag|inbox|conversation|dm)\b/i},{name:"Events / Tournaments",description:"Create and manage events, registrations",condition:()=>!1,keywords:/\b(event|tournament|competition|league|registration)\b/i},{name:"High Scores",description:"Track and display top scores with a leaderboard",condition:r=>r.surfaceType==="game",keywords:/\b(high.?score|leaderboard|top.?score|ranking|scoreboard)\b/i},{name:"Save Progress",description:"Save and resume game state between sessions",condition:r=>r.surfaceType==="game",keywords:/\b(save|progress|resume|checkpoint|continue)\b/i},{name:"Guest Play",description:"Play immediately without signing up, optionally link account later",condition:r=>r.surfaceType==="game",keywords:/\b(guest|anonymous|no.?login|play.?now|instant.?play)\b/i},{name:"Levels / Stages",description:"Progressive difficulty with unlockable stages",condition:()=>!1,keywords:/\b(level|stage|unlock|difficult|progress|world)\b/i},{name:"Achievements",description:"Badges, trophies, and milestones for player accomplishments",condition:()=>!1,keywords:/\b(achieve|badge|trophy|milestone|reward|unlock)\b/i},{name:"Daily Challenge",description:"New puzzle or challenge every day to keep players coming back",condition:()=>!1,keywords:/\b(daily|challenge|streak|word.?of.?the.?day|puzzle.?of)\b/i}];function fr(r,t,e){let a=e?.suggestedName||Kn(r),o=t.primaryActor==="both"?"Staff + Customers":t.primaryActor==="staff"?"Staff / Admin":t.primaryActor==="customers"?"End Users":"Users",i=t.audienceType??(t.surfaceType==="internal-tool"?"internal":(t.primaryActor==="customers"||t.primaryActor==="both","b2c")),n=t.surfaceType==="internal-tool"?"Internal tool":t.surfaceType==="marketplace"?"Marketplace":t.surfaceType==="content-site"?"Content site":t.surfaceType==="game"?"Game":"App",s;if(e?.suggestedFeatures&&e.suggestedFeatures.length>0)s=e.suggestedFeatures.map(l=>({name:l.name,description:l.description,checked:l.recommended,source:l.recommended?"explicit":"suggested"}));else{let l=`${r} ${t.primaryAction||""}`;s=[];for(let d of qn){let u=d.keywords.test(l),c=d.condition(t);(u||c)&&s.push({name:d.name,description:d.description,checked:u,source:u?"explicit":"suggested"})}}return{name:a,audience:o,audienceType:i,surfaceType:n,primaryAction:t.primaryAction||"manage items",features:s,publicLanding:t.publicLanding??!1,authModel:t.authModel??"email",dbProvider:t.dbProvider??"neon",integrations:t.integrations??[],language:e?.language||"English"}}function br(r){let t=r.features.filter(s=>s.checked),e=r.features.filter(s=>!s.checked),a={email:"Email sign-up",none:"No login (public)",social:"Social login","invite-only":"Invite-only"},o={neon:"Postgres",turso:"SQLite (legacy)"},i={b2c:"Your customers use this app (business-to-customer)",b2b:"Other businesses sign up for this (SaaS platform)",internal:"Internal team tool (staff only)"},n=[`**${r.name}** \u2014 ${r.surfaceType} for ${r.audience}`,`Audience: ${i[r.audienceType]??r.audienceType}`,`Primary action: ${r.primaryAction}`,`Access: ${a[r.authModel]??r.authModel} | Database: ${o[r.dbProvider]??r.dbProvider}${r.publicLanding?" | Landing page: Yes":""}${r.language&&r.language!=="English"?` | Language: ${r.language}`:""}`,""];if(t.length>0){n.push("**Included:**");for(let s of t)n.push(` \u2713 ${s.name} \u2014 ${s.description}`)}if(r.integrations.length>0&&(n.push(""),n.push(`**Integrations:** ${r.integrations.join(", ")}`)),e.length>0){n.push(""),n.push("**Available to add:**");for(let s of e)n.push(` \u25CB ${s.name} \u2014 ${s.description}`)}return n.join(`
10662
- `)}function Kn(r){let t=r.match(/\b(?:called|named)\s+["']?([A-Za-z][A-Za-z0-9 ]{1,30})["']?/i);if(t)return ta(t[1]);let e=new Set(["build","create","make","a","an","the","for","me","my","app","application","website","web","tool","system","platform","using","with","and","that","this","want","need","please","can","you","i","mist","mistflow"]),o=r.toLowerCase().replace(/[^a-z0-9\s]/g,"").split(/\s+/).filter(i=>i.length>2&&!e.has(i)).slice(0,3);return o.length===0?"my-app":o.join("-")}function ta(r){return r.toLowerCase().replace(/[^a-z0-9\s]/g,"").trim().replace(/\s+/g,"-")}var wt="__mistflow_url_choice__",ai=600*1e3;function vr(){let r=xe(We(),".mistflow","confirm-secret");if(rt(r))try{return Buffer.from(xr(r,"utf-8").trim(),"hex")}catch{}let t=ei(32);return aa(xe(We(),".mistflow"),{recursive:!0}),ra(r,t.toString("hex"),{mode:384}),t}function kr(r){return Zn("sha256").update(r.trim().toLowerCase()).digest("hex").slice(0,16)}function ri(r,t){let e={cwd:r,d:kr(t),exp:Date.now()+ai},a=Buffer.from(JSON.stringify(e)).toString("base64url"),o=wr("sha256",vr()).update(a).digest("base64url");return`${a}.${o}`}function ni(r,t,e){let a=r.split(".");if(a.length!==2)return!1;let[o,i]=a,n=wr("sha256",vr()).update(o).digest("base64url"),s=Buffer.from(i),l=Buffer.from(n);if(s.length!==l.length||!ti(s,l))return!1;try{let d=JSON.parse(Buffer.from(o,"base64url").toString("utf-8"));return!(typeof d.exp!="number"||Date.now()>d.exp||d.cwd!==t||d.d!==kr(e))}catch{return!1}}function ii(r){let t=r,e=We(),a=!1;for(let o=0;o<64;o++){if(rt(xe(t,"mistflow.json")))return"mistflow";if(!a&&rt(xe(t,"package.json"))&&(a=!0),t===e)break;let i=Xn(t);if(i===t)break;t=i}return a?"foreign":"none"}var oi=oe.object({description:oe.string().min(1,"App description or modification request"),conversationId:oe.string().optional().describe("Returned by a previous mist_plan call with status 'clarify'. Pass it back to continue the conversation."),answers:oe.record(oe.string()).optional().describe("User's answers to the clarifying questions from the previous round. Keys are the questions, values are the answers."),existingPlan:oe.record(oe.unknown()).optional().describe("If provided, modifies this existing plan instead of creating a new one. Pass the current plan object from mistflow.json."),existingPlanId:oe.string().optional().describe("Alternative to existingPlan \u2014 pass the planId from a previous mist_plan call to modify that plan."),templateToken:oe.string().optional().describe("Fork from a shared template. Pass the share token (from a mistflow.ai/t/... URL) to clone that project's plan into your workspace."),remixDescription:oe.string().optional().describe("Optional remix request when forking a template. Describes how you want the template to be different. E.g. 'Make it for tracking books instead of habits, add a search feature.' Only used with templateToken."),autonomous:oe.boolean().optional().describe("Skip clarifying questions and generate the plan immediately"),landingDesign:oe.string().optional().describe("ID of a curated landing page design for the hero section. When set, the design's detailed blueprint (colors, fonts, layout, animations) is injected during the landing page implementation step. Use mist_project with action='landing-designs' to browse available landing designs."),appStyle:oe.string().optional().describe("ID of a full-app style (e.g. 'stripe', 'linear', 'vercel', 'notion'). When set, the style's color palette, typography, component specs, shadows, and layout rules are injected during ALL implementation steps for consistent brand-quality design across every page. Use mist_project with action='app-styles' to browse available app styles."),language:oe.string().optional().describe("UI language for the app. All user-facing text, labels, buttons, and content will be generated in this language. Use the language name in English (e.g. 'Spanish', 'French', 'Arabic', 'Japanese'). Defaults to English if not specified."),brandMentioned:oe.boolean().optional().describe("Set to true ONLY when the user's original request explicitly invoked Mistflow by name (e.g. 'build me a CRM using mist', 'make a todo app with mistflow'). Skips the existing-project confirmation gate because the user clearly wants Mistflow. Do NOT set this for generic 'build me X' requests. Do NOT infer this \u2014 only set it when the user literally typed 'mist' or 'mistflow'."),confirmToken:oe.string().optional().describe("The token returned in a previous mist_plan response with status 'confirm_new_project'. Only pass this AFTER asking the user via AskUserQuestion and they chose to scaffold a new Mistflow app in an existing-project directory. The token is bound to the cwd and description \u2014 you must pass the SAME description on the retry, and the tool must be invoked from the same directory.")});function si(r){let t=[[/payment/i,"Payments"],[/database/i,"Database"],[/auth|sign.?up|login|access/i,"Access"],[/landing.?page/i,"Landing page"],[/who.*using|user|role/i,"Users"],[/design|theme|style/i,"Design"],[/deploy/i,"Deploy"],[/domain/i,"Domain"],[/notification/i,"Notify"],[/email/i,"Email"],[/mobile|responsive/i,"Mobile"],[/integrat/i,"Integration"]];for(let[a,o]of t)if(a.test(r))return o;return r.replace(/[?.,!]/g,"").split(/\s+/).filter(a=>!["what","how","do","does","is","are","the","a","an","would","should","you","your","for","this","that","to","of","or","and","want","like","prefer"].includes(a.toLowerCase())).slice(0,2).join(" ").slice(0,12)||"Option"}function li(r){let t=xe(We(),".mistflow","plans",`${r}.json`);if(!rt(t))return null;try{return JSON.parse(xr(t,"utf-8")).plan??null}catch{return null}}async function di(r){let{description:t,conversationId:e,answers:a,existingPlan:o,existingPlanId:i,templateToken:n,remixDescription:s,autonomous:l,language:d,landingDesign:u,appStyle:c,brandMentioned:h,confirmToken:g}=r,y=o;if(!y&&i&&(y=li(i)??void 0,!y))return p("Your previous plan is no longer available. Please describe your app again to generate a new plan.",!0);let x=e;if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let m;if(!x&&!y&&!n){let f=process.cwd(),b=ii(f);if(b==="foreign"&&!h){if(!(g?ni(g,f,t):!1)){let A=ri(f,t);return p(JSON.stringify({status:"confirm_new_project",cwd:f,description:t,confirmToken:A,askUserQuestion:{question:"You're inside an existing project directory. Do you want to scaffold a new Mistflow app here, or edit the existing codebase directly?",header:"Scope",options:[{label:"Scaffold a new Mistflow app in a subdirectory",description:"Creates a fresh project in this folder without touching the existing code."},{label:"Edit this existing codebase directly",description:"Cancel Mistflow. Handle the request by editing the current project's files."}],multiSelect:!1},instruction:["The user is inside an existing project (package.json found up the directory tree, no mistflow.json) and did NOT explicitly invoke Mistflow by name.","MANDATORY: Use the AskUserQuestion tool with the provided askUserQuestion to confirm their intent before calling mist_plan again.","If they pick 'Scaffold a new Mistflow app in a subdirectory', call mist_plan again with the SAME description and confirmToken set to the token returned above.","If they pick 'Edit this existing codebase directly', DO NOT call mist_plan again. Fulfill their request by editing files directly in the current project.",g?"The previous confirmToken was invalid, expired, or did not match the current directory/description. Use the fresh token above.":""].filter(Boolean).join(`
10663
- `)}))}m="Note: You're inside an existing project. Mistflow will create the new app in a subdirectory. It won't modify this codebase."}else b==="foreign"&&h&&(m="Note: You're inside an existing project. Mistflow will create the new app in a subdirectory. It won't modify this codebase.")}if(n)try{if(!(await Qa(n)).plan)return p("This template has no plan to fork. Try a different template.",!0);let b=await Xa(n),S=b.plan,A="";if(s&&b.has_source)try{let J=await zt(b.plan,s),k=J.plan??J,C=J.diff,F=k?.steps??[],D=new Set([...(C?.added??[]).map(ae=>ae.number),...(C?.modified??[]).map(ae=>ae.number)]),U=F.map(ae=>{let _t=ae.number;return D.has(_t)?{...ae,status:"pending"}:{...ae,status:"completed",source:"forked"}});k.steps=U,S=k;let fe=U.filter(ae=>ae.status==="pending").length;A=` Remixed: ${U.filter(ae=>ae.status==="completed").length} steps unchanged, ${fe} steps need re-implementation.`}catch(J){console.error("[plan] Remix failed, using original plan:",J),A=" (Remix failed \u2014 using original plan. You can modify it later.)"}let $=yr(),H=xe(We(),".mistflow","plans");aa(H,{recursive:!0}),ra(xe(H,`${$}.json`),JSON.stringify({plan:S,projectId:b.id,sourceDeploymentId:b.source_deployment_id,forkToken:b.fork_token,requiredEnvVars:b.required_env_vars,dbProvider:b.db_provider}));let Y=S?.name??"forked-app",ue=b.has_source,ne=ue?"Source code will be restored during init. Run init promptly \u2014 the download token expires in 1 hour.":"",te=b.deploy_url?` Instant deploy started \u2014 your app will be live at ${b.deploy_url} in under a minute.`:"";return p(JSON.stringify({planId:$,forkedFrom:b.forked_from,projectId:b.id,hasSource:ue,deployUrl:b.deploy_url,message:`Forked "${b.forked_from}" into your workspace.${A}${te} ${ne} NEXT: Call mist_build with action='init', name='${Y}', and planId='${$}' to create the project now.`}))}catch(f){let b=f instanceof Error?f.message:"Failed to fork template";return p(b,!0)}if(y){let f;try{f=await zt(y,t)}catch(H){let Y=H instanceof Error?H.message:"Failed to modify plan";return p(Y,!0)}let b=f.plan,S=f.diff,A=[];if(S?.added?.length){let H=S.added.map(Y=>Y.title);A.push(`Added ${H.length} step(s): ${H.join(", ")}`)}if(S?.removed?.length){let H=S.removed.map(Y=>Y.title);A.push(`Removed ${H.length} step(s): ${H.join(", ")}`)}if(S?.modified?.length){let H=S.modified.map(Y=>Y.title);A.push(`Modified ${H.length} step(s): ${H.join(", ")}`)}let $=A.length>0?A.join(". "):"No changes detected.";return p(JSON.stringify({plan:b,diff:S,message:`Plan modified. ${$}. Update mistflow.json with the new plan, then continue with mist_build (action: 'implement').`}))}let v,P=a;if(a&&wt in a){v=a[wt];let{[wt]:f,...b}=a;P=Object.keys(b).length>0?b:void 0}let T;try{T=await Na(t,{conversationId:x,answers:P,autonomous:l,language:d})}catch(f){let b=f instanceof Error?f.message:"Failed to generate plan";return p(b,!0)}if(T.status==="clarify"){let f=T.reflection||"",b=T.suggestedName||"",S=T.suggestedFeatures??[],A=T.questions??[],$=A.some(C=>Array.isArray(C.options)&&typeof C.options[0]=="object"&&C.options[0]?.label),H=A.map(C=>{let F=C.decisionKey?String(C.decisionKey).slice(0,12):si(C.question),D;return $&&Array.isArray(C.options)?D=C.options.map(U=>({label:U.label,description:U.description??""})):Array.isArray(C.options)?D=C.options.map((U,fe)=>({label:fe===0?`${U} (Recommended)`:String(U),description:C.why??""})):D=[{label:"Yes (Recommended)",description:C.why??""},{label:"No",description:""}],{question:C.question,header:F,options:D,multiSelect:!1}}),ue=T.decisions?.audienceType??null,ne=S.length>0?fr(t,{primaryActor:null,primaryAction:null,surfaceType:null,audienceType:ue,multiRole:null,publicLanding:null,realMoney:null,scheduling:null,authModel:null,dbProvider:null,integrations:null},{suggestedName:b,suggestedFeatures:S,language:d}):null,te=ne?br(ne):"",J=ta(b||"my-app").slice(0,32);try{let C=await Ra(J);!C.available&&C.suggestion&&(J=C.suggestion)}catch{}te&&(te+=`
10661
+ `)}function br(r){return(r?ra(r):Ve).map(e=>{let a=aa[e.id];return{id:e.id,slug:Kn(e.name),name:e.name,category:e.category,description:a?.description??"",tags:a?.tags??[],envVars:a?.envVars??[],docsUrl:a?.docsUrl??"",packages:a?.packages??[],difficulty:a?.difficulty??"medium"}})}var Yn=[{name:"Dashboard",description:"Overview with key stats and today's activity",condition:r=>r.surfaceType==="internal-tool"||r.surfaceType==="customer-app",keywords:/\b(dashboard|overview|home.?page|stats)\b/i},{name:"Landing Page",description:"Public page explaining what this does",condition:r=>r.publicLanding===!0,keywords:/\b(landing|marketing|hero|homepage)\b/i},{name:"Scheduling / Booking",description:"Calendar, time slots, reservations",condition:r=>r.scheduling===!0,keywords:/\b(schedul|book|reserv|appointment|calendar|slot)\b/i},{name:"Payments / Billing",description:"Charge users, invoices, subscriptions (experimental \u2014 Stripe integration is early-stage)",condition:()=>!1,keywords:/\b(payment|billing|invoice|subscription|checkout|stripe)\b/i},{name:"Admin Panel",description:"Manage users, roles, and content",condition:r=>r.multiRole===!0||r.primaryActor==="both",keywords:/\b(admin|panel|manage.?user|moderat)\b/i},{name:"User Profiles",description:"Account pages, settings, preferences",condition:r=>r.primaryActor==="customers"||r.primaryActor==="both",keywords:/\b(profile|account|settings|preferences)\b/i},{name:"Search / Browse",description:"Find and filter content or listings",condition:r=>r.surfaceType==="marketplace",keywords:/\b(search|browse|filter|discover|explore)\b/i},{name:"Email Notifications",description:"Welcome emails, alerts, reminders",condition:r=>r.integrations?.includes("email")===!0,keywords:/\b(notification|alert|reminder|email.?notif|sms|welcome.?email)\b/i},{name:"Analytics / Reports",description:"Usage stats, trends, data exports",condition:()=>!1,keywords:/\b(analytics|report|chart|trend|insight|metric)\b/i},{name:"File Uploads",description:"Images, documents, attachments",condition:r=>r.integrations?.includes("file-uploads")===!0,keywords:/\b(upload|image|photo|attachment|document|gallery|file)\b/i},{name:"AI Features",description:"Chatbot, content generation, AI assistant",condition:r=>r.integrations?.includes("ai")===!0,keywords:/\b(ai|chatbot|gpt|llm|generat|assistant)\b/i},{name:"Maps / Location",description:"Google Maps, location search, geolocation",condition:r=>r.integrations?.includes("maps")===!0,keywords:/\b(map|location|address|geo|places)\b/i},{name:"Voice / TTS",description:"Text-to-speech, voice notes, audio generation",condition:()=>!1,keywords:/\b(voice|tts|text.?to.?speech|audio|speak|narrat|podcast|elevenlabs)\b/i},{name:"SMS / Text Messages",description:"Send SMS notifications, OTP verification",condition:r=>r.integrations?.includes("sms")===!0,keywords:/\b(sms|text.?message|twilio|otp|verification.?code|phone.?verif)\b/i},{name:"Web Scraping",description:"Scrape URLs, crawl websites, extract structured data",condition:()=>!1,keywords:/\b(scrape|crawl|web.?scrap|extract.?from.?url|read.?url|ingest.?web|firecrawl)\b/i},{name:"Chat / Messaging",description:"Real-time messaging between users",condition:()=>!1,keywords:/\b(chat|messag|inbox|conversation|dm)\b/i},{name:"Events / Tournaments",description:"Create and manage events, registrations",condition:()=>!1,keywords:/\b(event|tournament|competition|league|registration)\b/i},{name:"High Scores",description:"Track and display top scores with a leaderboard",condition:r=>r.surfaceType==="game",keywords:/\b(high.?score|leaderboard|top.?score|ranking|scoreboard)\b/i},{name:"Save Progress",description:"Save and resume game state between sessions",condition:r=>r.surfaceType==="game",keywords:/\b(save|progress|resume|checkpoint|continue)\b/i},{name:"Guest Play",description:"Play immediately without signing up, optionally link account later",condition:r=>r.surfaceType==="game",keywords:/\b(guest|anonymous|no.?login|play.?now|instant.?play)\b/i},{name:"Levels / Stages",description:"Progressive difficulty with unlockable stages",condition:()=>!1,keywords:/\b(level|stage|unlock|difficult|progress|world)\b/i},{name:"Achievements",description:"Badges, trophies, and milestones for player accomplishments",condition:()=>!1,keywords:/\b(achieve|badge|trophy|milestone|reward|unlock)\b/i},{name:"Daily Challenge",description:"New puzzle or challenge every day to keep players coming back",condition:()=>!1,keywords:/\b(daily|challenge|streak|word.?of.?the.?day|puzzle.?of)\b/i}];function yr(r,t,e){let a=e?.suggestedName||Jn(r),o=t.primaryActor==="both"?"Staff + Customers":t.primaryActor==="staff"?"Staff / Admin":t.primaryActor==="customers"?"End Users":"Users",i=t.audienceType??(t.surfaceType==="internal-tool"?"internal":(t.primaryActor==="customers"||t.primaryActor==="both","b2c")),n=t.surfaceType==="internal-tool"?"Internal tool":t.surfaceType==="marketplace"?"Marketplace":t.surfaceType==="content-site"?"Content site":t.surfaceType==="game"?"Game":"App",s;if(e?.suggestedFeatures&&e.suggestedFeatures.length>0)s=e.suggestedFeatures.map(l=>({name:l.name,description:l.description,checked:l.recommended,source:l.recommended?"explicit":"suggested"}));else{let l=`${r} ${t.primaryAction||""}`;s=[];for(let d of Yn){let u=d.keywords.test(l),c=d.condition(t);(u||c)&&s.push({name:d.name,description:d.description,checked:u,source:u?"explicit":"suggested"})}}return{name:a,audience:o,audienceType:i,surfaceType:n,primaryAction:t.primaryAction||"manage items",features:s,publicLanding:t.publicLanding??!0,authModel:t.authModel??"email",dbProvider:t.dbProvider??"neon",integrations:t.integrations??[],language:e?.language||"English"}}function xr(r){let t=r.features.filter(s=>s.checked),e=r.features.filter(s=>!s.checked),a={email:"Email sign-up",none:"No login (public)",social:"Social login","invite-only":"Invite-only"},o={neon:"Postgres",turso:"SQLite (legacy)"},i={b2c:"Your customers use this app (business-to-customer)",b2b:"Other businesses sign up for this (SaaS platform)",internal:"Internal team tool (staff only)"},n=[`**${r.name}** \u2014 ${r.surfaceType} for ${r.audience}`,`Audience: ${i[r.audienceType]??r.audienceType}`,`Primary action: ${r.primaryAction}`,`Access: ${a[r.authModel]??r.authModel} | Database: ${o[r.dbProvider]??r.dbProvider}${r.publicLanding?" | Landing page: Yes":""}${r.language&&r.language!=="English"?` | Language: ${r.language}`:""}`,""];if(t.length>0){n.push("**Included:**");for(let s of t)n.push(` \u2713 ${s.name} \u2014 ${s.description}`)}if(r.integrations.length>0&&(n.push(""),n.push(`**Integrations:** ${r.integrations.join(", ")}`)),e.length>0){n.push(""),n.push("**Available to add:**");for(let s of e)n.push(` \u25CB ${s.name} \u2014 ${s.description}`)}return n.join(`
10662
+ `)}function Jn(r){let t=r.match(/\b(?:called|named)\s+["']?([A-Za-z][A-Za-z0-9 ]{1,30})["']?/i);if(t)return na(t[1]);let e=new Set(["build","create","make","a","an","the","for","me","my","app","application","website","web","tool","system","platform","using","with","and","that","this","want","need","please","can","you","i","mist","mistflow"]),o=r.toLowerCase().replace(/[^a-z0-9\s]/g,"").split(/\s+/).filter(i=>i.length>2&&!e.has(i)).slice(0,3);return o.length===0?"my-app":o.join("-")}function na(r){return r.toLowerCase().replace(/[^a-z0-9\s]/g,"").trim().replace(/\s+/g,"-")}var vt="__mistflow_url_choice__",ni=600*1e3;function Sr(){let r=xe(We(),".mistflow","confirm-secret");if(rt(r))try{return Buffer.from(vr(r,"utf-8").trim(),"hex")}catch{}let t=ai(32);return ia(xe(We(),".mistflow"),{recursive:!0}),oa(r,t.toString("hex"),{mode:384}),t}function Cr(r){return ti("sha256").update(r.trim().toLowerCase()).digest("hex").slice(0,16)}function ii(r,t){let e={cwd:r,d:Cr(t),exp:Date.now()+ni},a=Buffer.from(JSON.stringify(e)).toString("base64url"),o=kr("sha256",Sr()).update(a).digest("base64url");return`${a}.${o}`}function oi(r,t,e){let a=r.split(".");if(a.length!==2)return!1;let[o,i]=a,n=kr("sha256",Sr()).update(o).digest("base64url"),s=Buffer.from(i),l=Buffer.from(n);if(s.length!==l.length||!ri(s,l))return!1;try{let d=JSON.parse(Buffer.from(o,"base64url").toString("utf-8"));return!(typeof d.exp!="number"||Date.now()>d.exp||d.cwd!==t||d.d!==Cr(e))}catch{return!1}}function si(r){let t=r,e=We(),a=!1;for(let o=0;o<64;o++){if(rt(xe(t,"mistflow.json")))return"mistflow";if(!a&&rt(xe(t,"package.json"))&&(a=!0),t===e)break;let i=ei(t);if(i===t)break;t=i}return a?"foreign":"none"}var li=se.object({description:se.string().min(1,"App description or modification request"),conversationId:se.string().optional().describe("Returned by a previous mist_plan call with status 'clarify'. Pass it back to continue the conversation."),answers:se.record(se.string()).optional().describe("User's answers to the clarifying questions from the previous round. Keys are the questions, values are the answers."),existingPlan:se.record(se.unknown()).optional().describe("If provided, modifies this existing plan instead of creating a new one. Pass the current plan object from mistflow.json."),existingPlanId:se.string().optional().describe("Alternative to existingPlan \u2014 pass the planId from a previous mist_plan call to modify that plan."),templateToken:se.string().optional().describe("Fork from a shared template. Pass the share token (from a mistflow.ai/t/... URL) to clone that project's plan into your workspace."),remixDescription:se.string().optional().describe("Optional remix request when forking a template. Describes how you want the template to be different. E.g. 'Make it for tracking books instead of habits, add a search feature.' Only used with templateToken."),autonomous:se.boolean().optional().describe("Skip clarifying questions and generate the plan immediately"),landingDesign:se.string().optional().describe("ID of a curated landing page design for the hero section. When set, the design's detailed blueprint (colors, fonts, layout, animations) is injected during the landing page implementation step. Use mist_project with action='landing-designs' to browse available landing designs."),appStyle:se.string().optional().describe("ID of a full-app style (e.g. 'stripe', 'linear', 'vercel', 'notion'). When set, the style's color palette, typography, component specs, shadows, and layout rules are injected during ALL implementation steps for consistent brand-quality design across every page. Use mist_project with action='app-styles' to browse available app styles."),language:se.string().optional().describe("UI language for the app. All user-facing text, labels, buttons, and content will be generated in this language. Use the language name in English (e.g. 'Spanish', 'French', 'Arabic', 'Japanese'). Defaults to English if not specified."),brandMentioned:se.boolean().optional().describe("Set to true ONLY when the user's original request explicitly invoked Mistflow by name (e.g. 'build me a CRM using mist', 'make a todo app with mistflow'). Skips the existing-project confirmation gate because the user clearly wants Mistflow. Do NOT set this for generic 'build me X' requests. Do NOT infer this \u2014 only set it when the user literally typed 'mist' or 'mistflow'."),confirmToken:se.string().optional().describe("The token returned in a previous mist_plan response with status 'confirm_new_project'. Only pass this AFTER asking the user via AskUserQuestion and they chose to scaffold a new Mistflow app in an existing-project directory. The token is bound to the cwd and description \u2014 you must pass the SAME description on the retry, and the tool must be invoked from the same directory.")});function di(r){let t=[[/payment/i,"Payments"],[/database/i,"Database"],[/auth|sign.?up|login|access/i,"Access"],[/landing.?page/i,"Landing page"],[/who.*using|user|role/i,"Users"],[/design|theme|style/i,"Design"],[/deploy/i,"Deploy"],[/domain/i,"Domain"],[/notification/i,"Notify"],[/email/i,"Email"],[/mobile|responsive/i,"Mobile"],[/integrat/i,"Integration"],[/field|info|propert|detail|contain/i,"Item shape"],[/view|layout|board|grid|list|timeline/i,"View"],[/scope|how many|one.*or.*many|multi/i,"Scope"],[/share|read.?only|viewer|stakeholder/i,"Sharing"],[/workflow|status|state|move|stage|pipeline/i,"Workflow"],[/avoid|bloat|simple|complex|minimal/i,"Constraints"],[/time.*period|quarter|month|sprint/i,"Time periods"],[/swimlane|column|group|categor/i,"Structure"]];for(let[a,o]of t)if(a.test(r))return o;return r.replace(/[?.,!]/g,"").split(/\s+/).filter(a=>!["what","how","do","does","is","are","the","a","an","would","should","you","your","for","this","that","to","of","or","and","want","like","prefer"].includes(a.toLowerCase())).slice(0,2).join(" ").slice(0,12)||"Option"}function ci(r){let t=xe(We(),".mistflow","plans",`${r}.json`);if(!rt(t))return null;try{return JSON.parse(vr(t,"utf-8")).plan??null}catch{return null}}async function pi(r){let{description:t,conversationId:e,answers:a,existingPlan:o,existingPlanId:i,templateToken:n,remixDescription:s,autonomous:l,language:d,landingDesign:u,appStyle:c,brandMentioned:h,confirmToken:g}=r,y=o;if(!y&&i&&(y=ci(i)??void 0,!y))return p("Your previous plan is no longer available. Please describe your app again to generate a new plan.",!0);let x=e;if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let m;if(!x&&!y&&!n){let f=process.cwd(),b=si(f);if(b==="foreign"&&!h){if(!(g?oi(g,f,t):!1)){let I=ii(f,t);return p(JSON.stringify({status:"confirm_new_project",cwd:f,description:t,confirmToken:I,askUserQuestion:{question:"You're inside an existing project directory. Do you want to scaffold a new Mistflow app here, or edit the existing codebase directly?",header:"Scope",options:[{label:"Scaffold a new Mistflow app in a subdirectory",description:"Creates a fresh project in this folder without touching the existing code."},{label:"Edit this existing codebase directly",description:"Cancel Mistflow. Handle the request by editing the current project's files."}],multiSelect:!1},instruction:["The user is inside an existing project (package.json found up the directory tree, no mistflow.json) and did NOT explicitly invoke Mistflow by name.","MANDATORY: Use the AskUserQuestion tool with the provided askUserQuestion to confirm their intent before calling mist_plan again.","If they pick 'Scaffold a new Mistflow app in a subdirectory', call mist_plan again with the SAME description and confirmToken set to the token returned above.","If they pick 'Edit this existing codebase directly', DO NOT call mist_plan again. Fulfill their request by editing files directly in the current project.",g?"The previous confirmToken was invalid, expired, or did not match the current directory/description. Use the fresh token above.":""].filter(Boolean).join(`
10663
+ `)}))}m="Note: You're inside an existing project. Mistflow will create the new app in a subdirectory. It won't modify this codebase."}else b==="foreign"&&h&&(m="Note: You're inside an existing project. Mistflow will create the new app in a subdirectory. It won't modify this codebase.")}if(n)try{if(!(await Za(n)).plan)return p("This template has no plan to fork. Try a different template.",!0);let b=await er(n),S=b.plan,I="";if(s&&b.has_source)try{let ee=await $t(b.plan,s),v=ee.plan??ee,D=ee.diff,B=v?.steps??[],A=new Set([...(D?.added??[]).map(te=>te.number),...(D?.modified??[]).map(te=>te.number)]),U=B.map(te=>{let jt=te.number;return A.has(jt)?{...te,status:"pending"}:{...te,status:"completed",source:"forked"}});v.steps=U,S=v;let oe=U.filter(te=>te.status==="pending").length;I=` Remixed: ${U.filter(te=>te.status==="completed").length} steps unchanged, ${oe} steps need re-implementation.`}catch(ee){console.error("[plan] Remix failed, using original plan:",ee),I=" (Remix failed \u2014 using original plan. You can modify it later.)"}let $=wr(),H=xe(We(),".mistflow","plans");ia(H,{recursive:!0}),oa(xe(H,`${$}.json`),JSON.stringify({plan:S,projectId:b.id,sourceDeploymentId:b.source_deployment_id,forkToken:b.fork_token,requiredEnvVars:b.required_env_vars,dbProvider:b.db_provider}));let K=S?.name??"forked-app",fe=b.has_source,ie=fe?"Source code will be restored during init. Run init promptly \u2014 the download token expires in 1 hour.":"",re=b.deploy_url?` Instant deploy started \u2014 your app will be live at ${b.deploy_url} in under a minute.`:"";return p(JSON.stringify({planId:$,forkedFrom:b.forked_from,projectId:b.id,hasSource:fe,deployUrl:b.deploy_url,message:`Forked "${b.forked_from}" into your workspace.${I}${re} ${ie} NEXT: Call mist_build with action='init', name='${K}', and planId='${$}' to create the project now.`}))}catch(f){let b=f instanceof Error?f.message:"Failed to fork template";return p(b,!0)}if(y){let f;try{f=await $t(y,t)}catch(H){let K=H instanceof Error?H.message:"Failed to modify plan";return p(K,!0)}let b=f.plan,S=f.diff,I=[];if(S?.added?.length){let H=S.added.map(K=>K.title);I.push(`Added ${H.length} step(s): ${H.join(", ")}`)}if(S?.removed?.length){let H=S.removed.map(K=>K.title);I.push(`Removed ${H.length} step(s): ${H.join(", ")}`)}if(S?.modified?.length){let H=S.modified.map(K=>K.title);I.push(`Modified ${H.length} step(s): ${H.join(", ")}`)}let $=I.length>0?I.join(". "):"No changes detected.";return p(JSON.stringify({plan:b,diff:S,message:`Plan modified. ${$}. Update mistflow.json with the new plan, then continue with mist_build (action: 'implement').`}))}let k,T=a;if(a&&vt in a){k=a[vt];let{[vt]:f,...b}=a;T=Object.keys(b).length>0?b:void 0}let C;try{C=await Ea(t,{conversationId:x,answers:T,autonomous:l,language:d})}catch(f){let b=f instanceof Error?f.message:"Failed to generate plan";return p(b,!0)}if(C.status==="clarify"){let f=C.reflection||"",b=C.suggestedName||"",S=C.suggestedFeatures??[],I=C.questions??[],$=I.some(B=>Array.isArray(B.options)&&typeof B.options[0]=="object"&&B.options[0]?.label),H={primaryActor:"Users",primaryAction:"Core action",surfaceType:"App type",audienceType:"Audience",multiRole:"Roles",publicLanding:"Landing page",realMoney:"Payments",scheduling:"Scheduling",authModel:"Access",dbProvider:"Database",integrations:"Integration",entityShape:"Item shape",coreView:"View",scope:"Scope",sharing:"Sharing",workflow:"Workflow",constraints:"Constraints",domain:"Product"},K=I.map(B=>{let A=B.decisionKey&&H[B.decisionKey]||di(B.question),U;return $&&Array.isArray(B.options)?U=B.options.map(oe=>({label:oe.label,description:oe.description??""})):Array.isArray(B.options)?U=B.options.map((oe,Se)=>({label:Se===0?`${oe} (Recommended)`:String(oe),description:B.why??""})):U=[{label:"Yes (Recommended)",description:B.why??""},{label:"No",description:""}],{question:B.question,header:A,options:U,multiSelect:!1}}),ie=C.decisions?.audienceType??null,re=S.length>0?yr(t,{primaryActor:null,primaryAction:null,surfaceType:null,audienceType:ie,multiRole:null,publicLanding:null,realMoney:null,scheduling:null,authModel:null,dbProvider:null,integrations:null},{suggestedName:b,suggestedFeatures:S,language:d}):null,ee=re?xr(re):"",v=na(b||"my-app").slice(0,32);try{let B=await Na(v);!B.available&&B.suggestion&&(v=B.suggestion)}catch{}ee&&(ee+=`
10664
10664
 
10665
- **Your app URL:** https://${J}.mistflow.app`);let k={question:`Your app will be at ${J}.mistflow.app \u2014 want to customize the URL?`,header:"URL",options:[{label:`Keep ${J}.mistflow.app (Recommended)`,description:"This URL is available"},{label:"Choose a different URL",description:"Type your preferred subdomain"}],multiSelect:!1};return H.push(k),p(JSON.stringify({status:"clarify",conversation_id:T.conversation_id,questions:A,suggestedFeatures:S,suggestedName:b,suggestedSubdomain:J,reflection:f,briefText:te,askUserQuestions:H,instruction:[...m?[m,""]:[],f?`${f}
10666
- `:"",te?`Here's what I'd build:
10665
+ **Your app URL:** https://${v}.mistflow.app`);let D={question:`Your app will be at ${v}.mistflow.app \u2014 want to customize the URL?`,header:"URL",options:[{label:`Keep ${v}.mistflow.app (Recommended)`,description:"This URL is available"},{label:"Choose a different URL",description:"Type your preferred subdomain"}],multiSelect:!1};return K.push(D),p(JSON.stringify({status:"clarify",conversation_id:C.conversation_id,questions:I,suggestedFeatures:S,suggestedName:b,suggestedSubdomain:v,reflection:f,briefText:ee,askUserQuestions:K,instruction:[...m?[m,""]:[],f?`${f}
10666
+ `:"",ee?`Here's what I'd build:
10667
10667
 
10668
- ${te}
10669
- `:"","MANDATORY: Use the AskUserQuestion tool to present these questions. Do NOT present them as text.","","IF you have access to the AskUserQuestion tool (Claude Code):"," Use it to present questions with selectable options. Pass the 'askUserQuestions' array directly.","","OTHERWISE (Cursor, Codex, or other hosts):"," Present each question conversationally with the options listed.","","Once you have all answers, call mist_plan again with:",` conversationId: "${T.conversation_id}"`,' answers: { "<question text>": "<user answer>", ... }'," description: (same description as before)","",`IMPORTANT: For the URL question, include the answer with the key "${wt}" in the answers dict.`,`If the user keeps the default, set the value to "${J}".`,"If they type a custom URL, set the value to the custom subdomain (just the subdomain part, not the full URL)."].join(`
10670
- `)}))}let w=T.plan,R=w.name??"Untitled App",V=T.methodology,L=w.steps;if(!Array.isArray(L)||L.length===0)return p("Plan generation incomplete \u2014 the plan is missing implementation steps. Please call mist_plan again with the same description to retry.",!0);let O=w.publicPages;if(!O||Array.isArray(O)&&O.length===0){let f=w.pages,b=L.some(A=>typeof A.name=="string"&&A.name.toLowerCase().includes("landing")||typeof A.title=="string"&&A.title.toLowerCase().includes("landing")),S=Array.isArray(f)&&f.some(A=>A.path==="/"||A.route==="/");b||S?O=["/","/pricing"]:O=["/"]}let W=w.primaryAction;if(!W){let f=w.features;if(Array.isArray(f)&&f.length>0){let S=f.find($=>typeof $.priority=="string"&&$.priority.toLowerCase()==="must-have")??f[0];W={entity:S.name??S.title??"item",action:"create",fromPage:"/dashboard"}}}let z=w.nonNegotiables;(!z||Array.isArray(z)&&z.length===0)&&(z=["Landing page renders correctly at / with content (not a redirect)","Core user action works end-to-end (create entity, see it in list)"]);let K=yr(),ee=xe(We(),".mistflow","plans");aa(ee,{recursive:!0});try{let b=Date.now();for(let S of[ee,xe(We(),".mistflow","mockup-state")])if(rt(S))for(let A of Yn(S))try{let $=xe(S,A),H=Jn($).mtimeMs;b-H>6048e5&&Qn($)}catch{}}catch{}let E;if(u){let f=Ae(u);f?E=f.id:console.error(`Landing design '${u}' not found \u2014 ignoring. Use mist_project action='landing-designs' to browse available landing designs.`)}let N;if(c){let f=ye(c);f?N=f.id:console.error(`App style '${c}' not found \u2014 ignoring. Use mist_project action='app-styles' to browse available app styles.`)}let q={name:w.name,summary:w.summary,dataModel:w.dataModel,pages:w.pages,features:w.features,steps:L.map(f=>({...f,name:f.name??f.title})),design:w.design,landingDesign:E,appStyle:N,dbProvider:w.dbProvider??"neon",authModel:w.authModel,audienceType:w.audienceType??"b2c",roles:w.roles,defaultRole:w.defaultRole,publicPages:O,navStyle:w.navStyle,multiTenant:w.multiTenant,primaryAction:W,nonNegotiables:z,requestedSubdomain:v,...d&&d.toLowerCase()!=="english"?{language:d}:{}};ra(xe(ee,`${K}.json`),JSON.stringify({plan:q,methodology:V}));let ce=L.map(f=>`${f.number}. ${f.name??f.title}`),me=L.some(f=>{let b=`${f.name??f.title} ${f.description??""}`.toLowerCase();return b.includes("landing")||b.includes("hero")||b.includes("marketing")||b.includes("homepage")});if(!E&&me){let f=lr(t,{maxResults:1});f.length>0&&(E=f[0].id,console.error(`Auto-assigned landing layout preset: ${f[0].title} (${E})`))}let ie="",re=[],B;N||(re=hr(t,{maxResults:3}).map(S=>({id:S.id,slug:xt(S.name),name:S.name,description:S.description,theme:S.theme,url:`${Ue()}/designs/app-styles/${xt(S.name)}`})),B={question:"What design style should your app have? Choose a design system for consistent, brand-quality UI across all pages.",header:"App Design",options:[...re.map((S,A)=>({label:A===0?`${S.name} (Recommended)`:S.name,description:`${S.theme} theme \u2014 ${S.description} Preview: ${S.url}`})),{label:"See more app styles",description:`Not sure? Browse all 53 app styles at ${Ue()}/designs?tab=app-styles and pick one.`},{label:"Skip \u2014 use default styling",description:"Proceed without a design system"}],multiSelect:!1},ie=` Ask the user which app style they want using the AskUserQuestion tool with the 'appStyleQuestion' object provided. Recommended: ${re.map(S=>`[${S.name}](${S.url})`).join(", ")}. In your message, say these are top picks based on their description and that if none feel right they can browse all 53 app styles at ${Ue()}/designs?tab=app-styles (include this link verbatim). Once the user picks, pass appStyle='<id>' to the mist_build init call. If user says "skip", proceed without one.`);let M="",I=[];for(let f of L){let b=f.name??f.title,S=f.integrationId;if(S){let A=qe(S);if(A){let $=Ke(A.id);I.push({step:b,presetId:A.id,presetName:A.name,envVars:$?.envVars??[]})}}}if(I.length>0){let f=I.flatMap(A=>A.envVars),b=[...new Set(f.map(A=>A.key))];M=` This plan uses integrations (${I.map(A=>A.presetName).join(", ")}). Detailed blueprints will be auto-injected during each integration step.${b.length>0?` The user will need these API keys: ${b.join(", ")}.`:""}`}return p(JSON.stringify({planId:K,name:w.name,summary:w.summary,stepCount:L.length,steps:ce,design:w.design,...E?{landingDesign:E}:{},...N?{appStyle:N}:{},...re.length>0?{recommendedAppStyles:re}:{},...B?{appStyleQuestion:B}:{},...I.length>0?{integrations:I.map(f=>({step:f.step,preset:f.presetId,name:f.presetName,envVars:f.envVars}))}:{},message:`Plan generated for "${R}" (${L.length} steps).${E?` Landing layout "${E}" auto-assigned.`:""}${N?` App style "${N}" will be applied across all pages.`:""}${M}${ie}`,mockupPrompt:`Before building, ask the user: "Would you like to preview a mockup of your app before we start building? You can iterate on the design, or skip straight to building." If the user wants a mockup, call mist_build with action='mockup' and planId='${K}'. If the user says skip or "just build it", call mist_build with action='init', name='${R}', and planId='${K}' immediately.`,...m?{warning:m}:{}}))}var Sr={name:"mist_plan",description:["ENTRY POINT for creating a NEW web app, website, internal tool, dashboard, landing page, marketplace, content site, or browser game. Mistflow scaffolds a complete Next.js project. It does NOT edit existing codebases.","","WHEN TO CALL THIS \u2014 route here automatically on natural 'build me X' intent. The user does NOT need to say 'mist' or 'mistflow'. Examples that MUST route here:","\u2022 'build me a habit tracker'","\u2022 'make a site for my bakery'","\u2022 'I want an app where users log workouts'","\u2022 'create a dashboard that shows sales'","\u2022 'build a Wordle clone'","\u2022 'build me a CRM using mist' / 'make a todo app with mistflow' (explicit brand invocation)","","PASSING THE DESCRIPTION: Pass the user's words EXACTLY. Do NOT expand, elaborate, add features, rewrite, or strip anything (including 'using mist' / 'with mistflow'). 'build me a habit tracker using mist' becomes description: 'build me a habit tracker using mist'. The description is preserved verbatim.","","BRAND MENTIONED FLAG: If the user's original request literally contained the word 'mist' or 'mistflow' as an explicit invocation (e.g. 'build me a CRM using mist', 'use mistflow to make a todo app'), set brandMentioned: true. If the user did NOT mention the brand by name, omit brandMentioned (do not set it). Only set brandMentioned when the user literally typed the brand name \u2014 never infer it. Do NOT set brandMentioned for the common English noun 'mist' used in other contexts (e.g. 'app about morning mist').","","SAFETY GATE \u2014 the handler walks up the directory tree to detect if you're inside an existing non-Mistflow codebase (package.json found anywhere up the tree, no mistflow.json). When that happens AND brandMentioned is not set, the handler returns status 'confirm_new_project' with a signed confirmToken and an askUserQuestion. On that response:","\u2022 MANDATORY: use the AskUserQuestion tool with the provided askUserQuestion to ask the user.","\u2022 If the user picks 'Scaffold a new Mistflow app in a subdirectory', call mist_plan again with the SAME description and confirmToken set to the token from the response.","\u2022 If the user picks 'Edit this existing codebase directly', DO NOT call mist_plan again. Fulfill their request by editing files directly.","\u2022 The confirmToken is bound to the cwd and description. If either changes, you'll get a fresh token and must ask again.","\u2022 You do not need to pre-check the directory yourself. The handler handles detection.","","FOLLOW-UP FLOW (after the plan is being generated):","\u2022 status 'clarify' \u2192 use AskUserQuestion with the provided askUserQuestions, then call mist_plan again with conversationId + answers + the same description.","\u2022 status 'ready' \u2192 IMMEDIATELY call mist_build (action: 'init') with the returned planId. Do not ask permission.","\u2022 NEVER skip the clarifying questions. The discovery process ensures the right thing gets built.","","EXISTING MISTFLOW PROJECTS (mistflow.json present anywhere up the tree): only call this for changes that need a new data model, third-party integration, or multi-step structural change (pass existingPlan or existingPlanId). For cosmetic changes, new pages without new data models, or bug fixes, do NOT call mist_plan. Use mist_project action='get' for context and edit files directly.","","OTHER MODES: Pass templateToken to fork from a mistflow.ai/t/... shared template. Pass appStyle (53 full-app design systems like 'stripe', 'linear') to apply a design system. Browse via mist_project action='app-styles'. Landing layout presets are auto-assigned based on app description."].join(`
10671
- `),inputSchema:oi,handler:di};import{z as le}from"zod";import{existsSync as It,readFileSync as io}from"fs";import{join as Mt,resolve as oo}from"path";import{homedir as so}from"os";import{execFileSync as ga}from"child_process";import{z as vt}from"zod";import{existsSync as Se,mkdirSync as St,writeFileSync as Ie,readFileSync as Ct,readdirSync as ci,copyFileSync as pi}from"fs";import{join as Z,resolve as ia,dirname as nt}from"path";import{spawn as ui}from"child_process";import{randomBytes as hi}from"crypto";import{simpleGit as Dr}from"simple-git";function gi(r){let t=nt(ia(r)),e=10,a=0;for(;a<e&&t!==nt(t);){if(Se(Z(t,"pnpm-workspace.yaml"))||Se(Z(t,"lerna.json")))return t;let o=Z(t,"package.json");if(Se(o))try{if(JSON.parse(Ct(o,"utf-8")).workspaces)return t}catch{}t=nt(t),a++}return null}function Me(r,t,e,a,o){return new Promise(i=>{let n=ui(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:a}),s="",l="";n.stdout?.on("data",d=>{let u=d.toString();if(l+=u,o)for(let c of u.split(`
10668
+ ${ee}
10669
+ `:"","MANDATORY: Use the AskUserQuestion tool to present these questions. Do NOT present them as text.","","IF you have access to the AskUserQuestion tool (Claude Code):"," Use it to present questions with selectable options. Pass the 'askUserQuestions' array directly.","","OTHERWISE (Cursor, Codex, or other hosts):"," Present each question conversationally with the options listed.","","Once you have all answers, call mist_plan again with:",` conversationId: "${C.conversation_id}"`,' answers: { "<question text>": "<user answer>", ... }'," description: (same description as before)","","NOTE: You may receive another 'clarify' response with follow-up questions if the user's answers","revealed new ambiguity. This is normal \u2014 relay those questions too. Keep going until you get 'ready'.","",`IMPORTANT: For the URL question, include the answer with the key "${vt}" in the answers dict.`,`If the user keeps the default, set the value to "${v}".`,"If they type a custom URL, set the value to the custom subdomain (just the subdomain part, not the full URL)."].join(`
10670
+ `)}))}let w=C.plan,L=w.name??"Untitled App",V=C.methodology,N=w.steps;if(!Array.isArray(N)||N.length===0)return p("Plan generation incomplete \u2014 the plan is missing implementation steps. Please call mist_plan again with the same description to retry.",!0);let O=w.publicPages;if(!O||Array.isArray(O)&&O.length===0){let f=w.pages,b=N.some(I=>typeof I.name=="string"&&I.name.toLowerCase().includes("landing")||typeof I.title=="string"&&I.title.toLowerCase().includes("landing")),S=Array.isArray(f)&&f.some(I=>I.path==="/"||I.route==="/");b||S?O=["/","/pricing"]:O=["/"]}let W=w.primaryAction;if(!W){let f=w.features;if(Array.isArray(f)&&f.length>0){let S=f.find($=>typeof $.priority=="string"&&$.priority.toLowerCase()==="must-have")??f[0];W={entity:S.name??S.title??"item",action:"create",fromPage:"/dashboard"}}}let z=w.nonNegotiables;(!z||Array.isArray(z)&&z.length===0)&&(z=["Landing page renders correctly at / with content (not a redirect)","Core user action works end-to-end (create entity, see it in list)"]);let Y=wr(),Z=xe(We(),".mistflow","plans");ia(Z,{recursive:!0});try{let b=Date.now();for(let S of[Z,xe(We(),".mistflow","mockup-state")])if(rt(S))for(let I of Qn(S))try{let $=xe(S,I),H=Xn($).mtimeMs;b-H>6048e5&&Zn($)}catch{}}catch{}let E;if(u){let f=Ie(u);f?E=f.id:console.error(`Landing design '${u}' not found \u2014 ignoring. Use mist_project action='landing-designs' to browse available landing designs.`)}let F;if(c){let f=ye(c);f?F=f.id:console.error(`App style '${c}' not found \u2014 ignoring. Use mist_project action='app-styles' to browse available app styles.`)}let q={name:w.name,summary:w.summary,dataModel:w.dataModel,pages:w.pages,features:w.features,steps:N.map(f=>({...f,name:f.name??f.title})),design:w.design,landingDesign:E,appStyle:F,dbProvider:w.dbProvider??"neon",authModel:w.authModel,audienceType:w.audienceType??"b2c",roles:w.roles,defaultRole:w.defaultRole,publicPages:O,navStyle:w.navStyle,multiTenant:w.multiTenant,primaryAction:W,nonNegotiables:z,requestedSubdomain:k,...d&&d.toLowerCase()!=="english"?{language:d}:{}};oa(xe(Z,`${Y}.json`),JSON.stringify({plan:q,methodology:V}));let pe=N.map(f=>`${f.number}. ${f.name??f.title}`),me=N.some(f=>{let b=`${f.name??f.title} ${f.description??""}`.toLowerCase();return b.includes("landing")||b.includes("hero")||b.includes("marketing")||b.includes("homepage")});if(!E&&me){let f=cr(t,{maxResults:1});f.length>0&&(E=f[0].id,console.error(`Auto-assigned landing layout preset: ${f[0].title} (${E})`))}let ne="",ae=[],P;F||(ae=mr(t,{maxResults:3}).map(S=>({id:S.id,slug:wt(S.name),name:S.name,description:S.description,theme:S.theme,url:`${Ue()}/designs/app-styles/${wt(S.name)}`})),P={question:"What design style should your app have? Choose a design system for consistent, brand-quality UI across all pages.",header:"App Design",options:[...ae.map((S,I)=>({label:I===0?`${S.name} (Recommended)`:S.name,description:`${S.theme} theme \u2014 ${S.description} Preview: ${S.url}`})),{label:"See more app styles",description:`Not sure? Browse all 53 app styles at ${Ue()}/designs?tab=app-styles and pick one.`},{label:"Skip \u2014 use default styling",description:"Proceed without a design system"}],multiSelect:!1},ne=` Ask the user which app style they want using the AskUserQuestion tool with the 'appStyleQuestion' object provided. Recommended: ${ae.map(S=>`[${S.name}](${S.url})`).join(", ")}. In your message, say these are top picks based on their description and that if none feel right they can browse all 53 app styles at ${Ue()}/designs?tab=app-styles (include this link verbatim). Once the user picks, pass appStyle='<id>' to the mist_build init call. If user says "skip", proceed without one.`);let R="",M=[];for(let f of N){let b=f.name??f.title,S=f.integrationId;if(S){let I=qe(S);if(I){let $=Ke(I.id);M.push({step:b,presetId:I.id,presetName:I.name,envVars:$?.envVars??[]})}}}if(M.length>0){let f=M.flatMap(I=>I.envVars),b=[...new Set(f.map(I=>I.key))];R=` This plan uses integrations (${M.map(I=>I.presetName).join(", ")}). Detailed blueprints will be auto-injected during each integration step.${b.length>0?` The user will need these API keys: ${b.join(", ")}.`:""}`}return p(JSON.stringify({planId:Y,name:w.name,summary:w.summary,stepCount:N.length,steps:pe,design:w.design,...E?{landingDesign:E}:{},...F?{appStyle:F}:{},...ae.length>0?{recommendedAppStyles:ae}:{},...P?{appStyleQuestion:P}:{},...M.length>0?{integrations:M.map(f=>({step:f.step,preset:f.presetId,name:f.presetName,envVars:f.envVars}))}:{},message:`Plan generated for "${L}" (${N.length} steps).${E?` Landing layout "${E}" auto-assigned.`:""}${F?` App style "${F}" will be applied across all pages.`:""}${R}${ne}`,mockupPrompt:`Before building, ask the user: "Would you like to preview a mockup of your app before we start building? You can iterate on the design, or skip straight to building." If the user wants a mockup, call mist_build with action='mockup' and planId='${Y}'. If the user says skip or "just build it", call mist_build with action='init', name='${L}', and planId='${Y}' immediately.`,...m?{warning:m}:{}}))}var Tr={name:"mist_plan",description:["ENTRY POINT for creating a NEW web app, website, internal tool, dashboard, landing page, marketplace, content site, or browser game. Mistflow scaffolds a complete Next.js project. It does NOT edit existing codebases.","","WHEN TO CALL THIS \u2014 route here automatically on natural 'build me X' intent. The user does NOT need to say 'mist' or 'mistflow'. Examples that MUST route here:","\u2022 'build me a habit tracker'","\u2022 'make a site for my bakery'","\u2022 'I want an app where users log workouts'","\u2022 'create a dashboard that shows sales'","\u2022 'build a Wordle clone'","\u2022 'build me a CRM using mist' / 'make a todo app with mistflow' (explicit brand invocation)","","PASSING THE DESCRIPTION: Pass the user's words EXACTLY. Do NOT expand, elaborate, add features, rewrite, or strip anything (including 'using mist' / 'with mistflow'). 'build me a habit tracker using mist' becomes description: 'build me a habit tracker using mist'. The description is preserved verbatim.","","BRAND MENTIONED FLAG: If the user's original request literally contained the word 'mist' or 'mistflow' as an explicit invocation (e.g. 'build me a CRM using mist', 'use mistflow to make a todo app'), set brandMentioned: true. If the user did NOT mention the brand by name, omit brandMentioned (do not set it). Only set brandMentioned when the user literally typed the brand name \u2014 never infer it. Do NOT set brandMentioned for the common English noun 'mist' used in other contexts (e.g. 'app about morning mist').","","SAFETY GATE \u2014 the handler walks up the directory tree to detect if you're inside an existing non-Mistflow codebase (package.json found anywhere up the tree, no mistflow.json). When that happens AND brandMentioned is not set, the handler returns status 'confirm_new_project' with a signed confirmToken and an askUserQuestion. On that response:","\u2022 MANDATORY: use the AskUserQuestion tool with the provided askUserQuestion to ask the user.","\u2022 If the user picks 'Scaffold a new Mistflow app in a subdirectory', call mist_plan again with the SAME description and confirmToken set to the token from the response.","\u2022 If the user picks 'Edit this existing codebase directly', DO NOT call mist_plan again. Fulfill their request by editing files directly.","\u2022 The confirmToken is bound to the cwd and description. If either changes, you'll get a fresh token and must ask again.","\u2022 You do not need to pre-check the directory yourself. The handler handles detection.","","FOLLOW-UP FLOW (after the plan is being generated):","\u2022 status 'clarify' \u2192 use AskUserQuestion with the provided askUserQuestions, then call mist_plan again with conversationId + answers + the same description.","\u2022 You may receive MULTIPLE 'clarify' rounds \u2014 if the user's answers reveal new ambiguity, the planner will ask follow-up questions. Keep relaying questions until you get status 'ready'. This is normal and produces better plans.","\u2022 status 'ready' \u2192 IMMEDIATELY call mist_build (action: 'init') with the returned planId. Do not ask permission.","\u2022 NEVER skip the clarifying questions. The discovery process ensures the right thing gets built.","","EXISTING MISTFLOW PROJECTS (mistflow.json present anywhere up the tree): call this for changes that need a new data model, third-party integration, or multi-step structural change (pass existingPlan or existingPlanId). For simpler features (new pages, UI additions), do NOT call mist_plan, but DO ask the user product questions before building (what fields, what layout, what constraints). Get the spec right, build once. For cosmetic changes and bug fixes, skip questions and edit directly. Use mist_project action='get' for context.","","OTHER MODES: Pass templateToken to fork from a mistflow.ai/t/... shared template. Pass appStyle (53 full-app design systems like 'stripe', 'linear') to apply a design system. Browse via mist_project action='app-styles'. Landing layout presets are auto-assigned based on app description."].join(`
10671
+ `),inputSchema:li,handler:pi};import{z as de}from"zod";import{existsSync as Mt,readFileSync as so}from"fs";import{join as Rt,resolve as lo}from"path";import{homedir as co}from"os";import{execFileSync as ba}from"child_process";import{z as kt}from"zod";import{existsSync as Ce,mkdirSync as Ct,writeFileSync as Me,readFileSync as Tt,readdirSync as ui,copyFileSync as hi}from"fs";import{join as X,resolve as la,dirname as nt}from"path";import{spawn as gi}from"child_process";import{randomBytes as mi}from"crypto";import{simpleGit as Ir}from"simple-git";function fi(r){let t=nt(la(r)),e=10,a=0;for(;a<e&&t!==nt(t);){if(Ce(X(t,"pnpm-workspace.yaml"))||Ce(X(t,"lerna.json")))return t;let o=X(t,"package.json");if(Ce(o))try{if(JSON.parse(Tt(o,"utf-8")).workspaces)return t}catch{}t=nt(t),a++}return null}function Re(r,t,e,a,o){return new Promise(i=>{let n=gi(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:a}),s="",l="";n.stdout?.on("data",d=>{let u=d.toString();if(l+=u,o)for(let c of u.split(`
10672
10672
  `).filter(Boolean))o(c)}),n.stderr?.on("data",d=>{let u=d.toString();if(s+=u,o)for(let c of u.split(`
10673
10673
  `).filter(Boolean))o(c)}),n.on("close",d=>{if(d===0)i({success:!0});else{let u=s.split(`
10674
- `).find(c=>c.startsWith("npm error"))??s.slice(0,300);i({success:!1,error:u})}}),n.on("error",d=>{i({success:!1,error:d.message})})})}var Pl=vt.object({name:vt.string().min(1),plan:vt.any(),path:vt.string().optional()});function G(r,t,e){let a=Z(r,t);St(nt(a),{recursive:!0}),Ie(a,e)}var Cr={amber:{primary:"25 95% 53%",primaryForeground:"0 0% 100%"},emerald:{primary:"160 84% 39%",primaryForeground:"0 0% 100%"},indigo:{primary:"239 84% 67%",primaryForeground:"0 0% 100%"},rose:{primary:"347 77% 50%",primaryForeground:"0 0% 100%"},cyan:{primary:"189 94% 43%",primaryForeground:"0 0% 100%"},violet:{primary:"263 70% 50%",primaryForeground:"0 0% 100%"},orange:{primary:"21 90% 48%",primaryForeground:"0 0% 100%"},teal:{primary:"168 76% 42%",primaryForeground:"0 0% 100%"},sky:{primary:"199 89% 48%",primaryForeground:"0 0% 100%"}},mi={sharp:"0.125rem",subtle:"0.375rem",rounded:"0.75rem",pill:"9999px"};function Tt(r){let t=r.replace("#","");if(t.length!==6&&t.length!==3)return null;let e=t.length===3?t[0]+t[0]+t[1]+t[1]+t[2]+t[2]:t,a=parseInt(e,16);return isNaN(a)?null:{r:a>>16&255,g:a>>8&255,b:a&255}}function Pt(r,t,e){r/=255,t/=255,e/=255;let a=Math.max(r,t,e),o=Math.min(r,t,e),i=(a+o)/2;if(a===o)return{h:0,s:0,l:Math.round(i*100)};let n=a-o,s=i>.5?n/(2-a-o):n/(a+o),l=0;return a===r?l=((t-e)/n+(t<e?6:0))/6:a===t?l=((e-r)/n+2)/6:l=((r-t)/n+4)/6,{h:Math.round(l*360),s:Math.round(s*100),l:Math.round(i*100)}}function Ye(r){let t=Tt(r);if(!t)return"0 0% 50%";let{h:e,s:a,l:o}=Pt(t.r,t.g,t.b);return`${e} ${a}% ${o}%`}function Ar(r){let t=Tt(r);return t?Pt(t.r,t.g,t.b).l:50}function Ir(r){let t=Tt(r);return t?Pt(t.r,t.g,t.b).s:0}function fi(r){let t=ye(r);if(!t)return null;let e=t.sections.find(g=>g.title.toLowerCase().includes("color palette"));if(!e)return null;let a=e.content,o={},i=a.split(/^### /m);for(let g of i){if(!g.trim())continue;let y=g.indexOf(`
10675
- `);if(y===-1)continue;let x=g.slice(0,y).trim().toLowerCase();o[x]=g.slice(y)}let n=g=>g.match(/#[0-9a-fA-F]{6}\b/)?.[0],s,l=["interactive","accent","action","cta","brand"];for(let g of l){if(s)break;for(let y of Object.keys(o))if(y.includes(g)&&(s=n(o[y]),s))break}if(!s){for(let g of Object.keys(o))if(g.includes("secondary")&&(s=n(o[g]),s))break}let d;for(let g of Object.keys(o))if((g.includes("surface")||g.includes("background")||g.includes("canvas"))&&(d=n(o[g]),d))break;let u;for(let g of Object.keys(o))if((g==="primary"||g.includes("text")||g.includes("neutral"))&&(u=n(o[g]),u))break;let c;for(let g of Object.keys(o))if(g.includes("neutral")||g.includes("muted")||g.includes("tertiary")){let x=(o[g].match(/#[0-9a-fA-F]{6}\b/g)??[]).map(v=>({hex:v,l:Ar(v)})).sort((v,P)=>v.l-P.l),m=x[Math.floor(x.length/2)];if(m&&(c=m.hex),c)break}let h;for(let g of Object.keys(o))if((g.includes("border")||g.includes("divider")||g.includes("separator"))&&(h=n(o[g]),h))break;if(!h){let g=a.match(/[Bb]order[^`]*`(#[0-9a-fA-F]{6})`/);g&&(h=g[1])}return s&&Ir(s)<10&&(s=void 0),!s&&!d&&!u?null:{background:d,foreground:u,accent:s,muted:c,border:h}}function bi(r,t){let e=fi(t),a=e?.accent??r.colors.find(x=>Ir(x)>20)??r.colors[0]??"#6366f1",o=Ye(a),i=Ar(a),n=e?.background??(r.theme==="light"?"#ffffff":"#0a0a0a"),s=Tt(n),l=s?Pt(s.r,s.g,s.b):{h:0,s:0,l:100},d=l.s>3?l.h:0,u=Math.min(l.s,15),c=e?.foreground??"#0f172a",h=e?.muted,g=e?.border,y=Math.min(u,10);return{light:{background:`${d} ${u}% 99%`,foreground:Ye(c),card:`${d} ${u}% 100%`,cardForeground:Ye(c),muted:h?Ye(h):`${d} ${Math.min(u,10)}% 96%`,mutedForeground:`${d} ${Math.min(u,8)}% 45%`,border:g?Ye(g):`${d} ${Math.min(u,10)}% 90%`,input:g?Ye(g):`${d} ${Math.min(u,10)}% 90%`,ring:o},dark:{background:`${d} ${y}% 4%`,foreground:"0 0% 95%",card:`${d} ${y}% 6%`,cardForeground:"0 0% 95%",muted:`${d} ${Math.min(y,6)}% 16%`,mutedForeground:`${d} ${Math.min(y,6)}% 65%`,border:`${d} ${Math.min(y,6)}% 16%`,input:`${d} ${Math.min(y,6)}% 16%`,ring:o},primary:o,primaryForeground:i>60?"0 0% 5%":"0 0% 100%"}}function Q(r){let t=r.trim().split(/\s+/),e=parseFloat(t[0]),a=parseFloat(t[1])/100,o=parseFloat(t[2])/100,i=a*Math.min(o,1-o),n=W=>{let z=(W+e/30)%12;return o-i*Math.max(-1,Math.min(z-3,9-z,1))},s=n(0),l=n(8),d=n(4),u=W=>W<=.04045?W/12.92:Math.pow((W+.055)/1.055,2.4),c=u(s),h=u(l),g=u(d),y=Math.cbrt(.4122214708*c+.5363325363*h+.0514459929*g),x=Math.cbrt(.2119034982*c+.6806995451*h+.1073969566*g),m=Math.cbrt(.0883024619*c+.2817188376*h+.6299787005*g),v=.2104542553*y+.793617785*x-.0040720468*m,P=1.9779984951*y-2.428592205*x+.4505937099*m,T=.0259040371*y+.7827717662*x-.808675766*m,w=Math.sqrt(P*P+T*T),R=Math.atan2(T,P)*180/Math.PI;R<0&&(R+=360);let V=v.toFixed(4),L=w.toFixed(4),O=w<1e-4?"0":R.toFixed(2);return`${V} ${L} ${O}`}function yi(r,t){let e=r?.borderRadius??"subtle",a=mi[e]??"0.375rem",o,i,n,s,l=t?Ge(t):void 0,d=!1;if(l){let h=bi(l,t);d=l.theme==="dark",o=d?h.dark:h.light,i=d?h.light:h.dark,n=h.primary,s=h.primaryForeground}else{let h=r?.accentColor??"indigo",g=Cr[h]??Cr.indigo;o={background:"0 0% 100%",foreground:"240 10% 4%",card:"0 0% 100%",cardForeground:"240 10% 4%",muted:"240 5% 96%",mutedForeground:"240 4% 46%",border:"240 6% 90%",input:"240 6% 90%",ring:"240 5% 65%"},i={background:"240 10% 4%",foreground:"0 0% 95%",card:"240 10% 6%",cardForeground:"0 0% 95%",muted:"240 4% 16%",mutedForeground:"240 5% 65%",border:"240 4% 16%",input:"240 4% 16%",ring:"240 5% 65%"},n=g.primary,s=g.primaryForeground}let u=[...Object.entries(o).map(([h,g])=>` --color-${Tr(h)}: oklch(${Q(g)});`),` --color-primary: oklch(${Q(n)});`,` --color-primary-foreground: oklch(${Q(s)});`,` --color-secondary: oklch(${Q(o.muted)});`,` --color-secondary-foreground: oklch(${Q(o.foreground)});`,` --color-accent: oklch(${Q(o.muted)});`,` --color-accent-foreground: oklch(${Q(o.foreground)});`,` --color-destructive: oklch(${Q(d?"0 72% 51%":"0 84% 60%")});`,` --color-destructive-foreground: oklch(${Q("0 0% 100%")});`,` --color-popover: oklch(${Q(o.card)});`,` --color-popover-foreground: oklch(${Q(o.cardForeground)});`," --radius-sm: 0.25rem;",` --radius-md: ${a};`," --radius-lg: 0.5rem;"," --radius-xl: 0.75rem;"].join(`
10674
+ `).find(c=>c.startsWith("npm error"))??s.slice(0,300);i({success:!1,error:u})}}),n.on("error",d=>{i({success:!1,error:d.message})})})}var Al=kt.object({name:kt.string().min(1),plan:kt.any(),path:kt.string().optional()});function G(r,t,e){let a=X(r,t);Ct(nt(a),{recursive:!0}),Me(a,e)}var Pr={amber:{primary:"25 95% 53%",primaryForeground:"0 0% 100%"},emerald:{primary:"160 84% 39%",primaryForeground:"0 0% 100%"},indigo:{primary:"239 84% 67%",primaryForeground:"0 0% 100%"},rose:{primary:"347 77% 50%",primaryForeground:"0 0% 100%"},cyan:{primary:"189 94% 43%",primaryForeground:"0 0% 100%"},violet:{primary:"263 70% 50%",primaryForeground:"0 0% 100%"},orange:{primary:"21 90% 48%",primaryForeground:"0 0% 100%"},teal:{primary:"168 76% 42%",primaryForeground:"0 0% 100%"},sky:{primary:"199 89% 48%",primaryForeground:"0 0% 100%"}},bi={sharp:"0.125rem",subtle:"0.375rem",rounded:"0.75rem",pill:"9999px"};function Pt(r){let t=r.replace("#","");if(t.length!==6&&t.length!==3)return null;let e=t.length===3?t[0]+t[0]+t[1]+t[1]+t[2]+t[2]:t,a=parseInt(e,16);return isNaN(a)?null:{r:a>>16&255,g:a>>8&255,b:a&255}}function Bt(r,t,e){r/=255,t/=255,e/=255;let a=Math.max(r,t,e),o=Math.min(r,t,e),i=(a+o)/2;if(a===o)return{h:0,s:0,l:Math.round(i*100)};let n=a-o,s=i>.5?n/(2-a-o):n/(a+o),l=0;return a===r?l=((t-e)/n+(t<e?6:0))/6:a===t?l=((e-r)/n+2)/6:l=((r-t)/n+4)/6,{h:Math.round(l*360),s:Math.round(s*100),l:Math.round(i*100)}}function Ye(r){let t=Pt(r);if(!t)return"0 0% 50%";let{h:e,s:a,l:o}=Bt(t.r,t.g,t.b);return`${e} ${a}% ${o}%`}function Mr(r){let t=Pt(r);return t?Bt(t.r,t.g,t.b).l:50}function Rr(r){let t=Pt(r);return t?Bt(t.r,t.g,t.b).s:0}function yi(r){let t=ye(r);if(!t)return null;let e=t.sections.find(g=>g.title.toLowerCase().includes("color palette"));if(!e)return null;let a=e.content,o={},i=a.split(/^### /m);for(let g of i){if(!g.trim())continue;let y=g.indexOf(`
10675
+ `);if(y===-1)continue;let x=g.slice(0,y).trim().toLowerCase();o[x]=g.slice(y)}let n=g=>g.match(/#[0-9a-fA-F]{6}\b/)?.[0],s,l=["interactive","accent","action","cta","brand"];for(let g of l){if(s)break;for(let y of Object.keys(o))if(y.includes(g)&&(s=n(o[y]),s))break}if(!s){for(let g of Object.keys(o))if(g.includes("secondary")&&(s=n(o[g]),s))break}let d;for(let g of Object.keys(o))if((g.includes("surface")||g.includes("background")||g.includes("canvas"))&&(d=n(o[g]),d))break;let u;for(let g of Object.keys(o))if((g==="primary"||g.includes("text")||g.includes("neutral"))&&(u=n(o[g]),u))break;let c;for(let g of Object.keys(o))if(g.includes("neutral")||g.includes("muted")||g.includes("tertiary")){let x=(o[g].match(/#[0-9a-fA-F]{6}\b/g)??[]).map(k=>({hex:k,l:Mr(k)})).sort((k,T)=>k.l-T.l),m=x[Math.floor(x.length/2)];if(m&&(c=m.hex),c)break}let h;for(let g of Object.keys(o))if((g.includes("border")||g.includes("divider")||g.includes("separator"))&&(h=n(o[g]),h))break;if(!h){let g=a.match(/[Bb]order[^`]*`(#[0-9a-fA-F]{6})`/);g&&(h=g[1])}return s&&Rr(s)<10&&(s=void 0),!s&&!d&&!u?null:{background:d,foreground:u,accent:s,muted:c,border:h}}function xi(r,t){let e=yi(t),a=e?.accent??r.colors.find(x=>Rr(x)>20)??r.colors[0]??"#6366f1",o=Ye(a),i=Mr(a),n=e?.background??(r.theme==="light"?"#ffffff":"#0a0a0a"),s=Pt(n),l=s?Bt(s.r,s.g,s.b):{h:0,s:0,l:100},d=l.s>3?l.h:0,u=Math.min(l.s,15),c=e?.foreground??"#0f172a",h=e?.muted,g=e?.border,y=Math.min(u,10);return{light:{background:`${d} ${u}% 99%`,foreground:Ye(c),card:`${d} ${u}% 100%`,cardForeground:Ye(c),muted:h?Ye(h):`${d} ${Math.min(u,10)}% 96%`,mutedForeground:`${d} ${Math.min(u,8)}% 45%`,border:g?Ye(g):`${d} ${Math.min(u,10)}% 90%`,input:g?Ye(g):`${d} ${Math.min(u,10)}% 90%`,ring:o},dark:{background:`${d} ${y}% 4%`,foreground:"0 0% 95%",card:`${d} ${y}% 6%`,cardForeground:"0 0% 95%",muted:`${d} ${Math.min(y,6)}% 16%`,mutedForeground:`${d} ${Math.min(y,6)}% 65%`,border:`${d} ${Math.min(y,6)}% 16%`,input:`${d} ${Math.min(y,6)}% 16%`,ring:o},primary:o,primaryForeground:i>60?"0 0% 5%":"0 0% 100%"}}function J(r){let t=r.trim().split(/\s+/),e=parseFloat(t[0]),a=parseFloat(t[1])/100,o=parseFloat(t[2])/100,i=a*Math.min(o,1-o),n=W=>{let z=(W+e/30)%12;return o-i*Math.max(-1,Math.min(z-3,9-z,1))},s=n(0),l=n(8),d=n(4),u=W=>W<=.04045?W/12.92:Math.pow((W+.055)/1.055,2.4),c=u(s),h=u(l),g=u(d),y=Math.cbrt(.4122214708*c+.5363325363*h+.0514459929*g),x=Math.cbrt(.2119034982*c+.6806995451*h+.1073969566*g),m=Math.cbrt(.0883024619*c+.2817188376*h+.6299787005*g),k=.2104542553*y+.793617785*x-.0040720468*m,T=1.9779984951*y-2.428592205*x+.4505937099*m,C=.0259040371*y+.7827717662*x-.808675766*m,w=Math.sqrt(T*T+C*C),L=Math.atan2(C,T)*180/Math.PI;L<0&&(L+=360);let V=k.toFixed(4),N=w.toFixed(4),O=w<1e-4?"0":L.toFixed(2);return`${V} ${N} ${O}`}function wi(r,t){let e=r?.borderRadius??"subtle",a=bi[e]??"0.375rem",o,i,n,s,l=t?Ge(t):void 0,d=!1;if(l){let h=xi(l,t);d=l.theme==="dark",o=d?h.dark:h.light,i=d?h.light:h.dark,n=h.primary,s=h.primaryForeground}else{let h=r?.accentColor??"indigo",g=Pr[h]??Pr.indigo;o={background:"0 0% 100%",foreground:"240 10% 4%",card:"0 0% 100%",cardForeground:"240 10% 4%",muted:"240 5% 96%",mutedForeground:"240 4% 46%",border:"240 6% 90%",input:"240 6% 90%",ring:"240 5% 65%"},i={background:"240 10% 4%",foreground:"0 0% 95%",card:"240 10% 6%",cardForeground:"0 0% 95%",muted:"240 4% 16%",mutedForeground:"240 5% 65%",border:"240 4% 16%",input:"240 4% 16%",ring:"240 5% 65%"},n=g.primary,s=g.primaryForeground}let u=[...Object.entries(o).map(([h,g])=>` --color-${Br(h)}: oklch(${J(g)});`),` --color-primary: oklch(${J(n)});`,` --color-primary-foreground: oklch(${J(s)});`,` --color-secondary: oklch(${J(o.muted)});`,` --color-secondary-foreground: oklch(${J(o.foreground)});`,` --color-accent: oklch(${J(o.muted)});`,` --color-accent-foreground: oklch(${J(o.foreground)});`,` --color-destructive: oklch(${J(d?"0 72% 51%":"0 84% 60%")});`,` --color-destructive-foreground: oklch(${J("0 0% 100%")});`,` --color-popover: oklch(${J(o.card)});`,` --color-popover-foreground: oklch(${J(o.cardForeground)});`," --radius-sm: 0.25rem;",` --radius-md: ${a};`," --radius-lg: 0.5rem;"," --radius-xl: 0.75rem;"].join(`
10676
10676
  `),c="";return l||(c=`
10677
10677
 
10678
10678
  @media (prefers-color-scheme: dark) {
10679
10679
  :root {
10680
- ${[...Object.entries(i).map(([g,y])=>` --color-${Tr(g)}: oklch(${Q(y)});`),` --color-primary: oklch(${Q(n)});`,` --color-primary-foreground: oklch(${Q(s)});`,` --color-secondary: oklch(${Q(i.muted)});`,` --color-secondary-foreground: oklch(${Q(i.foreground)});`,` --color-accent: oklch(${Q(i.muted)});`,` --color-accent-foreground: oklch(${Q(i.foreground)});`,` --color-destructive: oklch(${Q("0 72% 51%")});`,` --color-destructive-foreground: oklch(${Q("0 0% 100%")});`,` --color-popover: oklch(${Q(i.card)});`,` --color-popover-foreground: oklch(${Q(i.cardForeground)});`].join(`
10680
+ ${[...Object.entries(i).map(([g,y])=>` --color-${Br(g)}: oklch(${J(y)});`),` --color-primary: oklch(${J(n)});`,` --color-primary-foreground: oklch(${J(s)});`,` --color-secondary: oklch(${J(i.muted)});`,` --color-secondary-foreground: oklch(${J(i.foreground)});`,` --color-accent: oklch(${J(i.muted)});`,` --color-accent-foreground: oklch(${J(i.foreground)});`,` --color-destructive: oklch(${J("0 72% 51%")});`,` --color-destructive-foreground: oklch(${J("0 0% 100%")});`,` --color-popover: oklch(${J(i.card)});`,` --color-popover-foreground: oklch(${J(i.cardForeground)});`].join(`
10681
10681
  `)}
10682
10682
  }
10683
10683
  }`),`@import "tailwindcss";
@@ -10748,7 +10748,7 @@ button, [role="button"] {
10748
10748
  button:active:not(:disabled), [role="button"]:active:not(:disabled) {
10749
10749
  transform: scale(0.97);
10750
10750
  }
10751
- `}function Tr(r){return r.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}var Pr={"Fredoka One":"Fredoka","Source Sans Pro":"Source_Sans_3","Source Serif Pro":"Source_Serif_4","Open Sans Condensed":"Open_Sans","Baloo 2":"Baloo_2","DM Serif Display":"DM_Serif_Display","DM Serif Text":"DM_Serif_Text","IBM Plex Mono":"IBM_Plex_Mono","IBM Plex Sans":"IBM_Plex_Sans","IBM Plex Serif":"IBM_Plex_Serif","Fira Code":"Fira_Code","Fira Sans":"Fira_Sans","Noto Sans JP":"Noto_Sans_JP","PT Sans":"PT_Sans","PT Serif":"PT_Serif","Work Sans":"Work_Sans","Space Mono":"Space_Mono","Space Grotesk":"Space_Grotesk","Plus Jakarta Sans":"Plus_Jakarta_Sans"};function Br(r){let t=r.replace(/[^A-Za-z0-9_ -]/g,"");return Pr[t]?Pr[t]:t.replace(/\s+/g,"_")}function xi(r){return r?{english:"en",spanish:"es",french:"fr",german:"de",italian:"it",portuguese:"pt",dutch:"nl",russian:"ru",japanese:"ja",chinese:"zh",korean:"ko",arabic:"ar",hebrew:"he",hindi:"hi",turkish:"tr",polish:"pl",swedish:"sv",norwegian:"no",danish:"da",finnish:"fi",thai:"th",vietnamese:"vi",indonesian:"id",malay:"ms",farsi:"fa",persian:"fa",czech:"cs",greek:"el",romanian:"ro",hungarian:"hu",ukrainian:"uk",bengali:"bn",tamil:"ta",telugu:"te",urdu:"ur"}[r.toLowerCase()]??"en":"en"}var wi=new Set(["ar","he","fa","ur"]);function vi(r,t,e){let a=r.replace(/[\\"`$]/g,""),o=xi(e),n=wi.has(o)?`lang="${o}" dir="rtl"`:`lang="${o}"`,s=t?.fonts?.heading,l=t?.fonts?.body;if(!s&&!l)return`import type { Metadata } from "next";
10751
+ `}function Br(r){return r.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}var Dr={"Fredoka One":"Fredoka","Source Sans Pro":"Source_Sans_3","Source Serif Pro":"Source_Serif_4","Open Sans Condensed":"Open_Sans","Baloo 2":"Baloo_2","DM Serif Display":"DM_Serif_Display","DM Serif Text":"DM_Serif_Text","IBM Plex Mono":"IBM_Plex_Mono","IBM Plex Sans":"IBM_Plex_Sans","IBM Plex Serif":"IBM_Plex_Serif","Fira Code":"Fira_Code","Fira Sans":"Fira_Sans","Noto Sans JP":"Noto_Sans_JP","PT Sans":"PT_Sans","PT Serif":"PT_Serif","Work Sans":"Work_Sans","Space Mono":"Space_Mono","Space Grotesk":"Space_Grotesk","Plus Jakarta Sans":"Plus_Jakarta_Sans"};function Ar(r){let t=r.replace(/[^A-Za-z0-9_ -]/g,"");return Dr[t]?Dr[t]:t.replace(/\s+/g,"_")}function vi(r){return r?{english:"en",spanish:"es",french:"fr",german:"de",italian:"it",portuguese:"pt",dutch:"nl",russian:"ru",japanese:"ja",chinese:"zh",korean:"ko",arabic:"ar",hebrew:"he",hindi:"hi",turkish:"tr",polish:"pl",swedish:"sv",norwegian:"no",danish:"da",finnish:"fi",thai:"th",vietnamese:"vi",indonesian:"id",malay:"ms",farsi:"fa",persian:"fa",czech:"cs",greek:"el",romanian:"ro",hungarian:"hu",ukrainian:"uk",bengali:"bn",tamil:"ta",telugu:"te",urdu:"ur"}[r.toLowerCase()]??"en":"en"}var ki=new Set(["ar","he","fa","ur"]);function Si(r,t,e){let a=r.replace(/[\\"`$]/g,""),o=vi(e),n=ki.has(o)?`lang="${o}" dir="rtl"`:`lang="${o}"`,s=t?.fonts?.heading,l=t?.fonts?.body;if(!s&&!l)return`import type { Metadata } from "next";
10752
10752
  import { DM_Sans } from "next/font/google";
10753
10753
  import { Toaster } from "sonner";
10754
10754
  import "./globals.css";
@@ -10764,7 +10764,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
10764
10764
  </html>
10765
10765
  );
10766
10766
  }
10767
- `;let d=Br(s??l),u=Br(l??s);return d===u?`import type { Metadata } from "next";
10767
+ `;let d=Ar(s??l),u=Ar(l??s);return d===u?`import type { Metadata } from "next";
10768
10768
  import { ${d} } from "next/font/google";
10769
10769
  import { Toaster } from "sonner";
10770
10770
  import "./globals.css";
@@ -10797,37 +10797,37 @@ export default function RootLayout({ children }: { children: React.ReactNode })
10797
10797
  </html>
10798
10798
  );
10799
10799
  }
10800
- `}function kt(r,...t){let e=JSON.stringify(r).toLowerCase();return t.some(a=>e.includes(a.toLowerCase()))}var ki={dashboard:"Home",home:"Home",overview:"Home",patient:"Users",member:"Users",user:"Users",people:"Users",team:"Users",client:"Users",contact:"Users",customer:"Users",appointment:"Calendar",schedule:"Calendar",booking:"Calendar",event:"Calendar",billing:"CreditCard",invoice:"CreditCard",payment:"CreditCard",pricing:"CreditCard",treatment:"ClipboardList",plan:"ClipboardList",task:"CheckSquare",exercise:"Dumbbell",workout:"Dumbbell",report:"BarChart3",analytics:"BarChart3",stats:"BarChart3",setting:"Settings",config:"Settings",profile:"User",account:"User",message:"MessageSquare",chat:"MessageSquare",inbox:"MessageSquare",product:"Package",item:"Package",catalog:"Package",order:"ShoppingCart",cart:"ShoppingCart",file:"FileText",document:"FileText",upload:"Upload",notification:"Bell",alert:"Bell",project:"FolderKanban",board:"FolderKanban",post:"PenSquare",blog:"PenSquare",article:"PenSquare",course:"GraduationCap",lesson:"GraduationCap",class:"GraduationCap",habit:"Target",goal:"Target",streak:"Flame",progress:"TrendingUp",feature:"Sparkles",subscription:"CreditCard",price:"CreditCard",recipe:"ChefHat",food:"UtensilsCrossed",meal:"UtensilsCrossed",pet:"PawPrint",animal:"PawPrint",music:"Music",playlist:"ListMusic",song:"Music",photo:"Image",image:"Image",gallery:"Images",video:"Video",movie:"Film",map:"MapPin",location:"MapPin",place:"MapPin",search:"Search",explore:"Compass",inventory:"Boxes",stock:"Boxes",warehouse:"Warehouse",review:"Star",rating:"Star",feedback:"Star",log:"ScrollText",history:"Clock",activity:"Activity"};function na(r){let t=r.toLowerCase().replace(/[^a-z]/g,"");for(let[e,a]of Object.entries(ki))if(t.includes(e))return a;return"Circle"}function it(r){return r.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Si(r){if(r.authModel==="none")return null;let t=["/login","/register","/api/auth","/api/health","/api/webhooks"];if(r.publicPages&&Array.isArray(r.publicPages))for(let i of r.publicPages){if(typeof i!="string"||i.length<1)continue;let n=i.replace(/[\u201C\u201D\u201E\u201F\u2018\u2019\u2033\u2036]/g,"").trim();if(!n)continue;let s=n.startsWith("/")?n:"/"+n;t.includes(s)||t.push(s)}let e=t.filter(i=>i==="/"),a=t.filter(i=>i!=="/"),o=[];o.push('import { NextRequest, NextResponse } from "next/server";'),o.push(""),o.push("const PUBLIC_PREFIXES = [");for(let i of a)o.push(' "'+i+'",');return o.push("];"),o.push(""),e.length>0&&(o.push('const PUBLIC_EXACT = ["'+e.join('", "')+'"];'),o.push("")),o.push("export function middleware(req: NextRequest) {"),o.push(" const { pathname } = req.nextUrl;"),o.push(""),e.length>0&&o.push(" if (PUBLIC_EXACT.includes(pathname)) return NextResponse.next();"),o.push(" if (PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))) return NextResponse.next();"),o.push(""),o.push(' const token = req.cookies.get("better-auth.session_token")?.value || req.cookies.get("__Secure-better-auth.session_token")?.value;'),o.push(" if (!token) {"),o.push(' return NextResponse.redirect(new URL("/login", req.url));'),o.push(" }"),o.push(""),o.push(" return NextResponse.next();"),o.push("}"),o.push(""),o.push("export const config = {"),o.push(' matcher: ["/((?!_next|static|favicon\\\\.ico).*)"],'),o.push("};"),o.push(""),o.join(`
10801
- `)}function Ci(r){if(r.navStyle==="none")return null;let t=["/api","/login","/register","/sign-in","/sign-up","/admin"],a=(r.pages??[]).filter(l=>{let d=l.path??l.route??"";return d==="/"||d===""||d.includes("[")||d.replace(/^\//,"").split("/").length>1?!1:!t.some(c=>d.startsWith(c))}).map(l=>{let d=l.path??l.route??"",u=d.startsWith("/")?d:"/"+d,c=l.name??it(d.replace(/^\//,"")),h=na(c);return{label:c,href:u,icon:h}});a.some(l=>l.href==="/dashboard")||a.unshift({label:"Dashboard",href:"/dashboard",icon:"Home"});let o=r.authModel==="none",i=[...new Set(a.map(l=>l.icon))];o||i.push("LogOut");let n=it(r.name);if(r.navStyle==="topbar"){let l=[];l.push('"use client";'),l.push(""),l.push('import Link from "next/link";'),l.push('import { usePathname } from "next/navigation";'),o||l.push('import { authClient } from "@/lib/auth-client";'),l.push('import { Button } from "@/components/ui/button";'),l.push('import { cn } from "@/lib/utils";'),l.push("import { "+i.join(", ")+' } from "lucide-react";'),l.push(""),l.push("interface TopNavProps {"),l.push(" user: { name: string | null; email: string; role?: string | undefined };"),l.push("}"),l.push(""),l.push("const NAV_ITEMS = [");for(let d of a)l.push(' { label: "'+d.label+'", href: "'+d.href+'", icon: '+d.icon+" },");return l.push("];"),l.push(""),l.push("export default function TopNav({ user }: TopNavProps) {"),l.push(" const pathname = usePathname();"),l.push(""),l.push(" return ("),l.push(' <nav className="border-b bg-card">'),l.push(' <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4">'),l.push(' <div className="flex items-center gap-6">'),l.push(' <span className="text-lg font-semibold">'+n+"</span>"),l.push(' <div className="flex items-center gap-1">'),l.push(" {NAV_ITEMS.map((item) => ("),l.push(" <Link"),l.push(" key={item.href}"),l.push(" href={item.href}"),l.push(' aria-current={pathname === item.href ? "page" : undefined}'),l.push(" className={cn("),l.push(' "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors",'),l.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground"'),l.push(" )}"),l.push(" >"),l.push(' <item.icon className="h-4 w-4" />'),l.push(" {item.label}"),l.push(" </Link>"),l.push(" ))}"),l.push(" </div>"),l.push(" </div>"),o?l.push(' <span className="text-sm text-muted-foreground">{user.name}</span>'):(l.push(' <div className="flex items-center gap-2">'),l.push(' <span className="text-sm text-muted-foreground">{user.email}</span>'),l.push(" <Button"),l.push(' variant="ghost"'),l.push(' size="sm"'),l.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),l.push(" >"),l.push(' <LogOut className="h-4 w-4" />'),l.push(" </Button>"),l.push(" </div>")),l.push(" </div>"),l.push(" </nav>"),l.push(" );"),l.push("}"),l.push(""),{path:"components/topnav.tsx",content:l.join(`
10800
+ `}function St(r,...t){let e=JSON.stringify(r).toLowerCase();return t.some(a=>e.includes(a.toLowerCase()))}var Ci={dashboard:"Home",home:"Home",overview:"Home",patient:"Users",member:"Users",user:"Users",people:"Users",team:"Users",client:"Users",contact:"Users",customer:"Users",appointment:"Calendar",schedule:"Calendar",booking:"Calendar",event:"Calendar",billing:"CreditCard",invoice:"CreditCard",payment:"CreditCard",pricing:"CreditCard",treatment:"ClipboardList",plan:"ClipboardList",task:"CheckSquare",exercise:"Dumbbell",workout:"Dumbbell",report:"BarChart3",analytics:"BarChart3",stats:"BarChart3",setting:"Settings",config:"Settings",profile:"User",account:"User",message:"MessageSquare",chat:"MessageSquare",inbox:"MessageSquare",product:"Package",item:"Package",catalog:"Package",order:"ShoppingCart",cart:"ShoppingCart",file:"FileText",document:"FileText",upload:"Upload",notification:"Bell",alert:"Bell",project:"FolderKanban",board:"FolderKanban",post:"PenSquare",blog:"PenSquare",article:"PenSquare",course:"GraduationCap",lesson:"GraduationCap",class:"GraduationCap",habit:"Target",goal:"Target",streak:"Flame",progress:"TrendingUp",feature:"Sparkles",subscription:"CreditCard",price:"CreditCard",recipe:"ChefHat",food:"UtensilsCrossed",meal:"UtensilsCrossed",pet:"PawPrint",animal:"PawPrint",music:"Music",playlist:"ListMusic",song:"Music",photo:"Image",image:"Image",gallery:"Images",video:"Video",movie:"Film",map:"MapPin",location:"MapPin",place:"MapPin",search:"Search",explore:"Compass",inventory:"Boxes",stock:"Boxes",warehouse:"Warehouse",review:"Star",rating:"Star",feedback:"Star",log:"ScrollText",history:"Clock",activity:"Activity"};function sa(r){let t=r.toLowerCase().replace(/[^a-z]/g,"");for(let[e,a]of Object.entries(Ci))if(t.includes(e))return a;return"Circle"}function it(r){return r.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function Ti(r){if(r.authModel==="none")return null;let t=["/login","/register","/api/auth","/api/health","/api/webhooks"];if(r.publicPages&&Array.isArray(r.publicPages))for(let i of r.publicPages){if(typeof i!="string"||i.length<1)continue;let n=i.replace(/[\u201C\u201D\u201E\u201F\u2018\u2019\u2033\u2036]/g,"").trim();if(!n)continue;let s=n.startsWith("/")?n:"/"+n;t.includes(s)||t.push(s)}let e=t.filter(i=>i==="/"),a=t.filter(i=>i!=="/"),o=[];o.push('import { NextRequest, NextResponse } from "next/server";'),o.push(""),o.push("const PUBLIC_PREFIXES = [");for(let i of a)o.push(' "'+i+'",');return o.push("];"),o.push(""),e.length>0&&(o.push('const PUBLIC_EXACT = ["'+e.join('", "')+'"];'),o.push("")),o.push("export function middleware(req: NextRequest) {"),o.push(" const { pathname } = req.nextUrl;"),o.push(""),e.length>0&&o.push(" if (PUBLIC_EXACT.includes(pathname)) return NextResponse.next();"),o.push(" if (PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))) return NextResponse.next();"),o.push(""),o.push(' const token = req.cookies.get("better-auth.session_token")?.value || req.cookies.get("__Secure-better-auth.session_token")?.value;'),o.push(" if (!token) {"),o.push(' return NextResponse.redirect(new URL("/login", req.url));'),o.push(" }"),o.push(""),o.push(" return NextResponse.next();"),o.push("}"),o.push(""),o.push("export const config = {"),o.push(' matcher: ["/((?!_next|static|favicon\\\\.ico).*)"],'),o.push("};"),o.push(""),o.join(`
10801
+ `)}function Pi(r){if(r.navStyle==="none")return null;let t=["/api","/login","/register","/sign-in","/sign-up","/admin"],a=(r.pages??[]).filter(l=>{let d=l.path??l.route??"";return d==="/"||d===""||d.includes("[")||d.replace(/^\//,"").split("/").length>1?!1:!t.some(c=>d.startsWith(c))}).map(l=>{let d=l.path??l.route??"",u=d.startsWith("/")?d:"/"+d,c=l.name??it(d.replace(/^\//,"")),h=sa(c);return{label:c,href:u,icon:h}});a.some(l=>l.href==="/dashboard")||a.unshift({label:"Dashboard",href:"/dashboard",icon:"Home"});let o=r.authModel==="none",i=[...new Set(a.map(l=>l.icon))];o||i.push("LogOut");let n=it(r.name);if(r.navStyle==="topbar"){let l=[];l.push('"use client";'),l.push(""),l.push('import Link from "next/link";'),l.push('import { usePathname } from "next/navigation";'),o||l.push('import { authClient } from "@/lib/auth-client";'),l.push('import { Button } from "@/components/ui/button";'),l.push('import { cn } from "@/lib/utils";'),l.push("import { "+i.join(", ")+' } from "lucide-react";'),l.push(""),l.push("interface TopNavProps {"),l.push(" user: { name: string | null; email: string; role?: string | undefined };"),l.push("}"),l.push(""),l.push("const NAV_ITEMS = [");for(let d of a)l.push(' { label: "'+d.label+'", href: "'+d.href+'", icon: '+d.icon+" },");return l.push("];"),l.push(""),l.push("export default function TopNav({ user }: TopNavProps) {"),l.push(" const pathname = usePathname();"),l.push(""),l.push(" return ("),l.push(' <nav className="border-b bg-card">'),l.push(' <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4">'),l.push(' <div className="flex items-center gap-6">'),l.push(' <span className="text-lg font-semibold">'+n+"</span>"),l.push(' <div className="flex items-center gap-1">'),l.push(" {NAV_ITEMS.map((item) => ("),l.push(" <Link"),l.push(" key={item.href}"),l.push(" href={item.href}"),l.push(' aria-current={pathname === item.href ? "page" : undefined}'),l.push(" className={cn("),l.push(' "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors",'),l.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground"'),l.push(" )}"),l.push(" >"),l.push(' <item.icon className="h-4 w-4" />'),l.push(" {item.label}"),l.push(" </Link>"),l.push(" ))}"),l.push(" </div>"),l.push(" </div>"),o?l.push(' <span className="text-sm text-muted-foreground">{user.name}</span>'):(l.push(' <div className="flex items-center gap-2">'),l.push(' <span className="text-sm text-muted-foreground">{user.email}</span>'),l.push(" <Button"),l.push(' variant="ghost"'),l.push(' size="sm"'),l.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),l.push(" >"),l.push(' <LogOut className="h-4 w-4" />'),l.push(" </Button>"),l.push(" </div>")),l.push(" </div>"),l.push(" </nav>"),l.push(" );"),l.push("}"),l.push(""),{path:"components/topnav.tsx",content:l.join(`
10802
10802
  `)}}let s=[];s.push('"use client";'),s.push(""),s.push('import { useState } from "react";'),s.push('import Link from "next/link";'),s.push('import { usePathname } from "next/navigation";'),o||s.push('import { authClient } from "@/lib/auth-client";'),s.push('import { Button } from "@/components/ui/button";'),s.push('import { Sheet, SheetContent, SheetTrigger, SheetTitle } from "@/components/ui/sheet";'),s.push('import { cn } from "@/lib/utils";'),s.push("import { Menu, "+i.join(", ")+' } from "lucide-react";'),s.push(""),s.push("interface SidebarProps {"),s.push(" user: { name: string | null; email: string; role?: string | undefined };"),s.push("}"),s.push(""),s.push("const NAV_ITEMS = [");for(let l of a)s.push(' { label: "'+l.label+'", href: "'+l.href+'", icon: '+l.icon+" },");return s.push("];"),s.push(""),s.push('function NavContent({ pathname, user, onNavigate }: { pathname: string; user: SidebarProps["user"]; onNavigate?: () => void }) {'),s.push(" return ("),s.push(" <>"),s.push(' <div className="flex h-14 items-center border-b px-4">'),s.push(' <span className="text-lg font-semibold">'+n+"</span>"),s.push(" </div>"),s.push(' <nav className="flex-1 space-y-1 p-2">'),s.push(" {NAV_ITEMS.map((item) => ("),s.push(" <Link"),s.push(" key={item.href}"),s.push(" href={item.href}"),s.push(" onClick={onNavigate}"),s.push(' aria-current={pathname === item.href ? "page" : undefined}'),s.push(" className={cn("),s.push(' "flex items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium transition-colors",'),s.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"'),s.push(" )}"),s.push(" >"),s.push(' <item.icon className="h-4 w-4" />'),s.push(" {item.label}"),s.push(" </Link>"),s.push(" ))}"),s.push(" </nav>"),s.push(' <div className="border-t p-4">'),s.push(' <div className="flex items-center gap-3">'),s.push(' <div className="flex-1 truncate">'),s.push(' <p className="truncate text-sm font-medium">{user.name ?? "User"}</p>'),s.push(' <p className="truncate text-xs text-muted-foreground">{user.email}</p>'),s.push(" </div>"),o||(s.push(" <Button"),s.push(' variant="ghost"'),s.push(' size="icon"'),s.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),s.push(" >"),s.push(' <LogOut className="h-4 w-4" />'),s.push(" </Button>")),s.push(" </div>"),s.push(" </div>"),s.push(" </>"),s.push(" );"),s.push("}"),s.push(""),s.push("export default function Sidebar({ user }: SidebarProps) {"),s.push(" const pathname = usePathname();"),s.push(" const [open, setOpen] = useState(false);"),s.push(""),s.push(" return ("),s.push(" <>"),s.push(' <aside className="hidden md:flex h-screen w-64 flex-col border-r bg-card">'),s.push(" <NavContent pathname={pathname} user={user} />"),s.push(" </aside>"),s.push(' <div className="sticky top-0 z-[var(--z-sticky)] flex h-14 items-center gap-3 border-b bg-card px-4 md:hidden">'),s.push(" <Sheet open={open} onOpenChange={setOpen}>"),s.push(" <SheetTrigger asChild>"),s.push(' <Button variant="ghost" size="icon" className="-ml-2">'),s.push(' <Menu className="h-5 w-5" />'),s.push(" </Button>"),s.push(" </SheetTrigger>"),s.push(' <SheetContent side="left" className="w-64 p-0">'),s.push(' <SheetTitle className="sr-only">Navigation</SheetTitle>'),s.push(' <div className="flex h-full flex-col">'),s.push(" <NavContent pathname={pathname} user={user} onNavigate={() => setOpen(false)} />"),s.push(" </div>"),s.push(" </SheetContent>"),s.push(" </Sheet>"),s.push(' <span className="text-lg font-semibold">'+n+"</span>"),s.push(" </div>"),s.push(" </>"),s.push(" );"),s.push("}"),s.push(""),{path:"components/sidebar.tsx",content:s.join(`
10803
- `)}}function Ti(r){if(!r.roles||r.roles.length===0)return null;let t=r.roles,e=r.defaultRole??t[0],a=[];a.push("export type Role = "+t.map(o=>'"'+o+'"').join(" | ")+";"),a.push(""),a.push("export const ROLES = ["+t.map(o=>'"'+o+'"').join(", ")+"] as const;"),a.push(""),a.push('export const DEFAULT_ROLE: Role = "'+e+'";'),a.push(""),a.push("export const ROLE_LABELS: Record<Role, string> = {");for(let o of t){let i=o.charAt(0).toUpperCase()+o.slice(1);a.push(' "'+o+'": "'+i+'",')}return a.push("};"),a.push(""),a.push("export function getUserRole(user: Record<string, unknown>): Role {"),a.push(" const role = (user.role as string) ?? DEFAULT_ROLE;"),a.push(" if (ROLES.includes(role as Role)) return role as Role;"),a.push(" return DEFAULT_ROLE;"),a.push("}"),a.push(""),a.push("export function hasRole(userRole: string | undefined, required: Role | Role[]): boolean {"),a.push(" if (!userRole) return false;"),a.push(" const allowed = Array.isArray(required) ? required : [required];"),a.push(" return allowed.includes(userRole as Role);"),a.push("}"),a.push(""),a.join(`
10804
- `)}function Pi(r){let t=it(r.name);if(r.authModel==="none"){let i=[];return i.push("export default function HomePage() {"),i.push(" return ("),i.push(' <main className="flex min-h-screen flex-col items-center justify-center p-8">'),i.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),r.summary&&i.push(' <p className="mt-4 text-lg text-muted-foreground">'+r.summary+"</p>"),i.push(" </main>"),i.push(" );"),i.push("}"),i.push(""),i.join(`
10803
+ `)}}function Bi(r){if(!r.roles||r.roles.length===0)return null;let t=r.roles,e=r.defaultRole??t[0],a=[];a.push("export type Role = "+t.map(o=>'"'+o+'"').join(" | ")+";"),a.push(""),a.push("export const ROLES = ["+t.map(o=>'"'+o+'"').join(", ")+"] as const;"),a.push(""),a.push('export const DEFAULT_ROLE: Role = "'+e+'";'),a.push(""),a.push("export const ROLE_LABELS: Record<Role, string> = {");for(let o of t){let i=o.charAt(0).toUpperCase()+o.slice(1);a.push(' "'+o+'": "'+i+'",')}return a.push("};"),a.push(""),a.push("export function getUserRole(user: Record<string, unknown>): Role {"),a.push(" const role = (user.role as string) ?? DEFAULT_ROLE;"),a.push(" if (ROLES.includes(role as Role)) return role as Role;"),a.push(" return DEFAULT_ROLE;"),a.push("}"),a.push(""),a.push("export function hasRole(userRole: string | undefined, required: Role | Role[]): boolean {"),a.push(" if (!userRole) return false;"),a.push(" const allowed = Array.isArray(required) ? required : [required];"),a.push(" return allowed.includes(userRole as Role);"),a.push("}"),a.push(""),a.join(`
10804
+ `)}function Di(r){let t=it(r.name);if(r.authModel==="none"){let i=[];return i.push("export default function HomePage() {"),i.push(" return ("),i.push(' <main className="flex min-h-screen flex-col items-center justify-center p-8">'),i.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),r.summary&&i.push(' <p className="mt-4 text-lg text-muted-foreground">'+r.summary+"</p>"),i.push(" </main>"),i.push(" );"),i.push("}"),i.push(""),i.join(`
10805
10805
  `)}let e=r.publicPages?.includes("/"),a=r.design?.landingTone;if(e&&a){let i=[];return i.push('import Link from "next/link";'),i.push(""),i.push("export default function HomePage() {"),i.push(" return ("),i.push(' <main className="flex min-h-screen flex-col">'),i.push(' <section className="flex flex-1 flex-col items-center justify-center gap-6 px-4 py-24 text-center">'),i.push(' <h1 className="text-5xl font-bold tracking-tight">'+t+"</h1>"),r.summary&&i.push(' <p className="max-w-2xl text-xl text-muted-foreground">'+r.summary+"</p>"),i.push(' <div className="flex gap-4">'),i.push(' <Link href="/register" className="inline-flex h-11 items-center rounded-md bg-primary px-8 text-sm font-medium text-primary-foreground hover:bg-primary/90">'),i.push(" Get Started"),i.push(" </Link>"),i.push(' <Link href="/login" className="inline-flex h-11 items-center rounded-md border px-8 text-sm font-medium hover:bg-muted">'),i.push(" Sign In"),i.push(" </Link>"),i.push(" </div>"),i.push(" </section>"),i.push(" </main>"),i.push(" );"),i.push("}"),i.push(""),i.join(`
10806
10806
  `)}if(e){let i=[];return i.push('import Link from "next/link";'),i.push(""),i.push("export default function HomePage() {"),i.push(" return ("),i.push(' <main className="flex min-h-screen flex-col items-center justify-center gap-6 p-8 text-center">'),i.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),r.summary&&i.push(' <p className="text-lg text-muted-foreground">'+r.summary+"</p>"),i.push(' <Link href="/login" className="inline-flex h-10 items-center rounded-md bg-primary px-6 text-sm font-medium text-primary-foreground hover:bg-primary/90">'),i.push(" Sign In"),i.push(" </Link>"),i.push(" </main>"),i.push(" );"),i.push("}"),i.push(""),i.join(`
10807
10807
  `)}let o=[];return o.push('import { headers } from "next/headers";'),o.push('import { redirect } from "next/navigation";'),o.push('import { auth } from "@/lib/auth";'),o.push(""),o.push("export default async function HomePage() {"),o.push(" const session = await auth.api.getSession({ headers: await headers() });"),o.push(' if (session) redirect("/dashboard");'),o.push(' redirect("/login");'),o.push("}"),o.push(""),o.join(`
10808
- `)}function Bi(r,t){let e=r.authModel==="none",a=[];return e||(a.push('import { headers } from "next/headers";'),a.push('import { redirect } from "next/navigation";'),a.push('import { auth } from "@/lib/auth";')),r.navStyle==="topbar"?a.push('import TopNav from "@/components/topnav";'):r.navStyle!=="none"&&a.push('import Sidebar from "@/components/sidebar";'),!e&&r.roles&&r.roles.length>0&&a.push('import { getUserRole } from "@/lib/roles";'),a.push(""),a.push("export default async function DashboardLayout({ children }: { children: React.ReactNode }) {"),e?a.push(' const user = { name: "Guest", email: "" };'):(a.push(" const session = await auth.api.getSession({ headers: await headers() });"),a.push(' if (!session) redirect("/login");'),a.push(""),r.roles&&r.roles.length>0?(a.push(" const role = getUserRole(session.user as Record<string, unknown>);"),a.push(" const user = { name: session.user.name, email: session.user.email, role };")):(a.push(" const user = {"),a.push(" name: session.user.name,"),a.push(" email: session.user.email,"),a.push(" role: (session.user as Record<string, unknown>).role as string | undefined,"),a.push(" };"))),a.push(""),r.navStyle==="topbar"?(a.push(" return ("),a.push(' <div className="min-h-screen">'),a.push(" <TopNav user={user} />"),a.push(' <main className="mx-auto max-w-7xl p-6">{children}</main>'),a.push(" </div>"),a.push(" );")):r.navStyle==="none"?(a.push(" return ("),a.push(' <div className="min-h-screen">'),a.push(' <main className="mx-auto max-w-5xl p-6">{children}</main>'),a.push(" </div>"),a.push(" );")):(a.push(" return ("),a.push(' <div className="flex flex-col md:flex-row min-h-screen">'),a.push(" <Sidebar user={user} />"),a.push(' <main className="flex-1 overflow-x-hidden p-4 md:p-6">{children}</main>'),a.push(" </div>"),a.push(" );")),a.push("}"),a.push(""),a.join(`
10809
- `)}function Di(r){let t=it(r.name),e=r.dataModel??[],a=[];if(e.length>0){let o=e.map(n=>na(n.entity??n.name??"item")),i=[...new Set(o)];a.push('import { Card, CardContent } from "@/components/ui/card";'),a.push("import { "+i.join(", ")+' } from "lucide-react";'),a.push("")}if(a.push("export default function DashboardPage() {"),a.push(" return ("),a.push(' <div className="space-y-6">'),a.push(" <div>"),a.push(' <h1 className="text-3xl font-bold">'+t+"</h1>"),r.summary&&a.push(' <p className="mt-1 text-muted-foreground">'+r.summary+"</p>"),a.push(" </div>"),e.length>0){a.push(' <div className="rounded-lg border p-8 text-center">'),a.push(' <h2 className="text-lg font-semibold">Get started</h2>'),a.push(` <p className="mt-1 text-sm text-muted-foreground">Here's what you can do</p>`),a.push(' <div className="mt-6 grid gap-3 sm:grid-cols-2 text-left">');for(let o of e){let i=o.entity??o.name??"Item",n=na(i),s=it(i.replace(/_/g,"-"));a.push(' <div className="flex items-center gap-3 rounded-md border p-3">'),a.push(" <"+n+' className="h-5 w-5 text-muted-foreground" />'),a.push(' <span className="text-sm font-medium">Add your first '+s+"</span>"),a.push(" </div>")}a.push(" </div>"),a.push(" </div>")}return a.push(" </div>"),a.push(" );"),a.push("}"),a.push(""),a.join(`
10810
- `)}function Ai(r,t=!1){if(!r.multiTenant)return null;let e=[];return t?(e.push('import { pgTable, text, timestamp, index } from "drizzle-orm/pg-core";'),e.push('import { user } from "./auth";'),e.push(""),e.push('export const organization = pgTable("organization", {'),e.push(' id: text("id").primaryKey(),'),e.push(' name: text("name").notNull(),'),e.push(' slug: text("slug").unique().notNull(),'),e.push(' createdAt: timestamp("created_at").defaultNow().notNull(),'),e.push(' updatedAt: timestamp("updated_at").defaultNow().notNull(),'),e.push("});"),e.push(""),e.push("export const orgMember = pgTable(")):(e.push('import { sqliteTable, text, index } from "drizzle-orm/sqlite-core";'),e.push('import { sql } from "drizzle-orm";'),e.push('import { user } from "./auth";'),e.push(""),e.push('export const organization = sqliteTable("organization", {'),e.push(' id: text("id").primaryKey(),'),e.push(' name: text("name").notNull(),'),e.push(' slug: text("slug").unique().notNull(),'),e.push(' createdAt: text("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push(' updatedAt: text("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push("});"),e.push(""),e.push("export const orgMember = sqliteTable(")),e.push(' "org_member",'),e.push(" {"),e.push(' id: text("id").primaryKey(),'),e.push(' orgId: text("org_id").notNull().references(() => organization.id),'),e.push(' userId: text("user_id").notNull().references(() => user.id),'),e.push(' role: text("role").notNull(),'),t?e.push(' joinedAt: timestamp("joined_at").defaultNow().notNull(),'):e.push(' joinedAt: text("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push(" },"),e.push(" (table) => ({"),e.push(' orgIdx: index("org_member_org_idx").on(table.orgId),'),e.push(' userIdx: index("org_member_user_idx").on(table.userId),'),e.push(" }),"),e.push(");"),e.push(""),e.join(`
10811
- `)}function Ii(r){if(!r.multiTenant)return null;let t=[];return t.push('import { db } from "./db";'),t.push('import { organization, orgMember } from "@/db/schema/organization";'),t.push('import { eq } from "drizzle-orm";'),t.push(""),t.push("export async function getCurrentOrg(userId: string) {"),t.push(" const membership = await db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.userId, userId))"),t.push(" .limit(1);"),t.push(" if (membership.length === 0) return null;"),t.push(" const org = await db"),t.push(" .select()"),t.push(" .from(organization)"),t.push(" .where(eq(organization.id, membership[0].orgId))"),t.push(" .limit(1);"),t.push(" return org[0] ?? null;"),t.push("}"),t.push(""),t.push("export async function getOrgMembers(orgId: string) {"),t.push(" return db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.orgId, orgId));"),t.push("}"),t.push(""),t.push("export async function inviteToOrg(orgId: string, email: string, role: string) {"),t.push(" const id = crypto.randomUUID();"),t.push(" await db.insert(orgMember).values({"),t.push(" id,"),t.push(" orgId,"),t.push(" userId: email,"),t.push(" role,"),t.push(" });"),t.push(" return { id, orgId, email, role };"),t.push("}"),t.push(""),t.join(`
10812
- `)}function Mi(r){if(!r.multiTenant)return null;let t=[];return t.push('"use client";'),t.push(""),t.push("import {"),t.push(" DropdownMenu,"),t.push(" DropdownMenuContent,"),t.push(" DropdownMenuItem,"),t.push(" DropdownMenuTrigger,"),t.push('} from "@/components/ui/dropdown-menu";'),t.push('import { Button } from "@/components/ui/button";'),t.push('import { ChevronsUpDown } from "lucide-react";'),t.push(""),t.push("interface OrgSwitcherProps {"),t.push(" orgs: Array<{ id: string; name: string }>;"),t.push(" currentOrgId: string;"),t.push("}"),t.push(""),t.push("export default function OrgSwitcher({ orgs, currentOrgId }: OrgSwitcherProps) {"),t.push(" const currentOrg = orgs.find((o) => o.id === currentOrgId);"),t.push(""),t.push(" return ("),t.push(" <DropdownMenu>"),t.push(" <DropdownMenuTrigger asChild>"),t.push(' <Button variant="outline" className="w-full justify-between">'),t.push(' <span className="truncate">{currentOrg?.name ?? "Select org"}</span>'),t.push(' <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />'),t.push(" </Button>"),t.push(" </DropdownMenuTrigger>"),t.push(' <DropdownMenuContent className="w-56">'),t.push(" {orgs.map((org) => ("),t.push(" <DropdownMenuItem key={org.id}>"),t.push(" {org.name}"),t.push(" </DropdownMenuItem>"),t.push(" ))}"),t.push(" </DropdownMenuContent>"),t.push(" </DropdownMenu>"),t.push(" );"),t.push("}"),t.push(""),t.join(`
10813
- `)}function Ri(r,t,e){let a=[],o=r.split("-").map(l=>l.charAt(0).toUpperCase()+l.slice(1)).join(" ");a.push(`# ${o}`),a.push(""),t?.summary&&(a.push(t.summary),a.push(""));let i=t?.features??[];if(i.length>0){a.push("## Features"),a.push("");for(let l of i){let d=l.description?` \u2014 ${l.description}`:"";a.push(`- **${l.name}**${d}`)}a.push("")}a.push("## Tech Stack"),a.push(""),a.push("| Layer | Technology |"),a.push("|-------|------------|"),a.push("| Framework | Next.js 15 (App Router) |"),a.push("| Database | Turso (SQLite at edge) + Drizzle ORM |"),a.push("| Auth | Better Auth (email/password, social login) |"),a.push("| Styling | Tailwind CSS + shadcn/ui |"),a.push("| Deployment | Cloudflare Workers (via Mistflow) |"),e.hasStripe&&a.push("| Payments | Stripe |"),e.hasResend&&a.push("| Email | Resend + React Email |"),e.hasStorage&&a.push("| File Storage | Cloudflare R2 (via Mistflow) |"),e.hasAdmin&&a.push("| Admin | Better Auth admin plugin |"),e.hasAI&&a.push("| AI | Vercel AI SDK + OpenAI |"),a.push("");let n=t?.pages??[];if(n.length>0){a.push("## Pages"),a.push(""),a.push("| Route | Description |"),a.push("|-------|-------------|");for(let l of n){let d=l.path??l.route??l.name??"",u=l.description??"";a.push(`| \`${d.startsWith("/")?d:"/"+d}\` | ${u} |`)}a.push("")}let s=t?.dataModel??[];if(s.length>0){a.push("## Data Model"),a.push("");for(let l of s){let d=l.entity??l.name??"Unknown";if(a.push(`### ${d}`),a.push(""),l.fields.length>0){if(typeof l.fields[0]=="string")a.push(`Fields: ${l.fields.join(", ")}`);else{a.push("| Field | Type |"),a.push("|-------|------|");for(let u of l.fields)a.push(`| ${u.name} | ${u.type} |`)}a.push("")}}}return a.push("## Getting Started"),a.push(""),a.push("### Prerequisites"),a.push(""),a.push("- Node.js 20+"),a.push("- npm"),a.push(""),a.push("### Install"),a.push(""),a.push("```bash"),a.push("npm install"),a.push("```"),a.push(""),a.push("### Set up environment"),a.push(""),a.push("Copy `.env.example` to `.env.local` and fill in the values:"),a.push(""),a.push("```bash"),a.push("cp .env.example .env.local"),a.push("```"),a.push(""),a.push("| Variable | Description | Required |"),a.push("|----------|-------------|----------|"),e.isNeon?a.push("| `DATABASE_URL` | Postgres connection URL | Yes |"):(a.push("| `TURSO_URL` | Database connection URL | Yes |"),a.push("| `TURSO_AUTH_TOKEN` | Database auth token | Yes |")),a.push("| `AUTH_SECRET` | Auth encryption secret (auto-generated) | Yes |"),e.hasStripe&&(a.push("| `STRIPE_SECRET_KEY` | Stripe secret key | Yes |"),a.push("| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | Yes |"),a.push("| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | Yes |")),e.hasResend&&(a.push("| `RESEND_API_KEY` | Resend API key | Yes |"),a.push("| `EMAIL_FROM` | Sender email address | Yes (production) |")),e.hasStorage&&(a.push("| `MISTFLOW_API_KEY` | Mistflow API key for file storage | Yes |"),a.push("| `MISTFLOW_PROJECT_ID` | Mistflow project ID | Yes |")),e.hasAI&&a.push("| `OPENAI_API_KEY` | OpenAI API key | Yes |"),a.push(""),a.push("### Local database"),a.push(""),e.isNeon?(a.push("For local development, start a local Postgres server:"),a.push(""),a.push("```bash"),a.push("# Using Docker:"),a.push("docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:17"),a.push("# Or install via Homebrew: brew install postgresql@17 && brew services start postgresql@17"),a.push("```")):(a.push("For local development, start a local Turso server:"),a.push(""),a.push("```bash"),a.push("npx turso dev"),a.push("```")),a.push(""),a.push("Then set up the database:"),a.push(""),a.push("```bash"),a.push("npm run db:push"),a.push("```"),a.push(""),a.push("### Run"),a.push(""),a.push("```bash"),a.push("npm run dev"),a.push("```"),a.push(""),a.push("Open [http://localhost:3000](http://localhost:3000)."),a.push(""),a.push("## Project Structure"),a.push(""),a.push("```"),a.push("app/"),a.push(" (auth)/ Login and registration pages"),a.push(" (dashboard)/ Authenticated app pages"),e.hasAdmin&&a.push(" (admin)/ Admin panel pages"),a.push(" api/ API routes (auth, health, webhooks)"),a.push(" layout.tsx Root layout with fonts and providers"),a.push(" globals.css Design tokens and Tailwind config"),a.push("components/ Reusable UI components"),a.push("db/"),a.push(" schema/ Database table definitions"),a.push(" index.ts Schema exports"),a.push("lib/"),a.push(" auth.ts Better Auth server config"),a.push(" auth-client.ts Better Auth client config"),a.push(` db.ts ${e.isNeon?"Neon Postgres":"Turso"} database connection`),e.hasStripe&&a.push(" stripe.ts Stripe client"),e.hasResend&&(a.push(" resend.ts Resend client"),a.push(" email.ts Email send helpers")),e.hasStorage&&a.push(" storage.ts File upload/download helpers"),e.hasAI&&a.push(" ai.ts AI client (Vercel AI SDK + OpenAI)"),e.hasResend&&a.push("emails/ React Email templates"),a.push("```"),a.push(""),a.push("## Deploy"),a.push(""),a.push("Deploy to production with Mistflow:"),a.push(""),a.push("```"),a.push("# In your AI editor (Claude Code, Cursor, etc.):"),a.push("mist_deploy action='deploy'"),a.push("```"),a.push(""),a.push("Your app will be live at `https://<app-name>.mistflow.app`."),a.push(""),t?.design&&(a.push("## Design"),a.push(""),t.design.tone&&a.push(`- **Tone**: ${t.design.tone}`),t.design.fonts&&(a.push(`- **Heading font**: ${t.design.fonts.heading}`),a.push(`- **Body font**: ${t.design.fonts.body}`)),t.design.accentColor&&a.push(`- **Accent color**: ${t.design.accentColor}`),t.design.borderRadius&&a.push(`- **Border radius**: ${t.design.borderRadius}`),a.push("")),a.push("---"),a.push(""),a.push("Built with [Mistflow](https://mistflow.ai)"),a.push(""),a.join(`
10814
- `)}async function Mr(r){let{name:t,plan:e,path:a,planId:o}=r,i=ia(a??`./${t}`),n=e?.design,s=e?.appStyle;if(s&&n){let k=Ge(s);if(k?.fonts){let C=F=>cr[F]??F;n.fonts={heading:C(k.fonts.heading),body:C(k.fonts.body)}}}let l=e?kt(e,"stripe","payment","billing","subscription","checkout","pricing"):!1,d=!0,u=e?kt(e,"upload","file storage","image upload","profile picture","attachment","gallery","media","blob"):!1,c=e?kt(e,"admin panel","admin dashboard","admin management"):!1,h=e?kt(e,"ai integration","openai","llm","ai chat","chatbot","gpt"):!1,g=e,y=!0;if(Se(i))return p(`A project already exists at this location (${i}). Choose a different name, or delete the existing folder first.`,!0);St(i,{recursive:!0});try{let k=Z(nt(i),".mistflow","mockups");if(Se(k)){let C=ci(k).filter(F=>F.endsWith(".html"));if(C.length>0){let F=Z(i,".mistflow","mockups");St(F,{recursive:!0});for(let D of C)pi(Z(k,D),Z(F,D));console.error(`Copied ${C.length} mockup file(s) into project`)}}}catch(k){console.error("Could not copy mockup files:",k instanceof Error?k.message:k)}let x=null;try{x=await bt("nextjs")}catch(k){console.error("Could not fetch scaffold from API, using minimal scaffold:",k instanceof Error?k.message:k)}if(x){let k=t.toLowerCase().replace(/[^a-z0-9-]/g,"-");for(let D of x.files){if(D.path==="package.json"||D.path==="middleware.ts"||D.path==="components/sidebar.tsx"||D.path==="components/topnav.tsx"||D.path==="app/(dashboard)/layout.tsx"||D.path==="app/(dashboard)/page.tsx"||D.path==="app/(dashboard)/dashboard/page.tsx"||!l&&(D.path.includes("stripe")||D.path.includes("webhook/stripe"))||!d&&(D.path.includes("resend")||D.path.includes("emails/"))||!c&&(D.path.includes("(admin)")||D.path.includes("admin-sidebar"))||y&&(D.path==="lib/db.ts"||D.path==="lib/auth.ts"||D.path==="drizzle.config.ts"||D.path==="db/schema/auth.ts"))continue;let U=D.content.replace(/\{\{APP_NAME\}\}/g,t).replace(/\{\{WORKER_NAME\}\}/g,k);if(y&&D.path==="next.config.ts"&&(U=U.replace(/serverExternalPackages:\s*\[[^\]]*\],?/g,'serverExternalPackages: ["@electric-sql/pglite"],')),D.path==="next.config.ts"){let fe=gi(i);fe&&(console.error(`[init] Project is inside monorepo at ${fe} \u2014 adding outputFileTracingRoot`),U.includes("outputFileTracingRoot")||(U=U.replace('import type { NextConfig } from "next";',`import type { NextConfig } from "next";
10808
+ `)}function Ai(r,t){let e=r.authModel==="none",a=[];return e||(a.push('import { headers } from "next/headers";'),a.push('import { redirect } from "next/navigation";'),a.push('import { auth } from "@/lib/auth";')),r.navStyle==="topbar"?a.push('import TopNav from "@/components/topnav";'):r.navStyle!=="none"&&a.push('import Sidebar from "@/components/sidebar";'),!e&&r.roles&&r.roles.length>0&&a.push('import { getUserRole } from "@/lib/roles";'),a.push(""),a.push("export default async function DashboardLayout({ children }: { children: React.ReactNode }) {"),e?a.push(' const user = { name: "Guest", email: "" };'):(a.push(" const session = await auth.api.getSession({ headers: await headers() });"),a.push(' if (!session) redirect("/login");'),a.push(""),r.roles&&r.roles.length>0?(a.push(" const role = getUserRole(session.user as Record<string, unknown>);"),a.push(" const user = { name: session.user.name, email: session.user.email, role };")):(a.push(" const user = {"),a.push(" name: session.user.name,"),a.push(" email: session.user.email,"),a.push(" role: (session.user as Record<string, unknown>).role as string | undefined,"),a.push(" };"))),a.push(""),r.navStyle==="topbar"?(a.push(" return ("),a.push(' <div className="min-h-screen">'),a.push(" <TopNav user={user} />"),a.push(' <main className="mx-auto max-w-7xl p-6">{children}</main>'),a.push(" </div>"),a.push(" );")):r.navStyle==="none"?(a.push(" return ("),a.push(' <div className="min-h-screen">'),a.push(' <main className="mx-auto max-w-5xl p-6">{children}</main>'),a.push(" </div>"),a.push(" );")):(a.push(" return ("),a.push(' <div className="flex flex-col md:flex-row min-h-screen">'),a.push(" <Sidebar user={user} />"),a.push(' <main className="flex-1 overflow-x-hidden p-4 md:p-6">{children}</main>'),a.push(" </div>"),a.push(" );")),a.push("}"),a.push(""),a.join(`
10809
+ `)}function Ii(r){let t=it(r.name),e=r.dataModel??[],a=[];if(e.length>0){let o=e.map(n=>sa(n.entity??n.name??"item")),i=[...new Set(o)];a.push('import { Card, CardContent } from "@/components/ui/card";'),a.push("import { "+i.join(", ")+' } from "lucide-react";'),a.push("")}if(a.push("export default function DashboardPage() {"),a.push(" return ("),a.push(' <div className="space-y-6">'),a.push(" <div>"),a.push(' <h1 className="text-3xl font-bold">'+t+"</h1>"),r.summary&&a.push(' <p className="mt-1 text-muted-foreground">'+r.summary+"</p>"),a.push(" </div>"),e.length>0){a.push(' <div className="rounded-lg border p-8 text-center">'),a.push(' <h2 className="text-lg font-semibold">Get started</h2>'),a.push(` <p className="mt-1 text-sm text-muted-foreground">Here's what you can do</p>`),a.push(' <div className="mt-6 grid gap-3 sm:grid-cols-2 text-left">');for(let o of e){let i=o.entity??o.name??"Item",n=sa(i),s=it(i.replace(/_/g,"-"));a.push(' <div className="flex items-center gap-3 rounded-md border p-3">'),a.push(" <"+n+' className="h-5 w-5 text-muted-foreground" />'),a.push(' <span className="text-sm font-medium">Add your first '+s+"</span>"),a.push(" </div>")}a.push(" </div>"),a.push(" </div>")}return a.push(" </div>"),a.push(" );"),a.push("}"),a.push(""),a.join(`
10810
+ `)}function Mi(r,t=!1){if(!r.multiTenant)return null;let e=[];return t?(e.push('import { pgTable, text, timestamp, index } from "drizzle-orm/pg-core";'),e.push('import { user } from "./auth";'),e.push(""),e.push('export const organization = pgTable("organization", {'),e.push(' id: text("id").primaryKey(),'),e.push(' name: text("name").notNull(),'),e.push(' slug: text("slug").unique().notNull(),'),e.push(' createdAt: timestamp("created_at").defaultNow().notNull(),'),e.push(' updatedAt: timestamp("updated_at").defaultNow().notNull(),'),e.push("});"),e.push(""),e.push("export const orgMember = pgTable(")):(e.push('import { sqliteTable, text, index } from "drizzle-orm/sqlite-core";'),e.push('import { sql } from "drizzle-orm";'),e.push('import { user } from "./auth";'),e.push(""),e.push('export const organization = sqliteTable("organization", {'),e.push(' id: text("id").primaryKey(),'),e.push(' name: text("name").notNull(),'),e.push(' slug: text("slug").unique().notNull(),'),e.push(' createdAt: text("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push(' updatedAt: text("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push("});"),e.push(""),e.push("export const orgMember = sqliteTable(")),e.push(' "org_member",'),e.push(" {"),e.push(' id: text("id").primaryKey(),'),e.push(' orgId: text("org_id").notNull().references(() => organization.id),'),e.push(' userId: text("user_id").notNull().references(() => user.id),'),e.push(' role: text("role").notNull(),'),t?e.push(' joinedAt: timestamp("joined_at").defaultNow().notNull(),'):e.push(' joinedAt: text("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),e.push(" },"),e.push(" (table) => ({"),e.push(' orgIdx: index("org_member_org_idx").on(table.orgId),'),e.push(' userIdx: index("org_member_user_idx").on(table.userId),'),e.push(" }),"),e.push(");"),e.push(""),e.join(`
10811
+ `)}function Ri(r){if(!r.multiTenant)return null;let t=[];return t.push('import { db } from "./db";'),t.push('import { organization, orgMember } from "@/db/schema/organization";'),t.push('import { eq } from "drizzle-orm";'),t.push(""),t.push("export async function getCurrentOrg(userId: string) {"),t.push(" const membership = await db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.userId, userId))"),t.push(" .limit(1);"),t.push(" if (membership.length === 0) return null;"),t.push(" const org = await db"),t.push(" .select()"),t.push(" .from(organization)"),t.push(" .where(eq(organization.id, membership[0].orgId))"),t.push(" .limit(1);"),t.push(" return org[0] ?? null;"),t.push("}"),t.push(""),t.push("export async function getOrgMembers(orgId: string) {"),t.push(" return db"),t.push(" .select()"),t.push(" .from(orgMember)"),t.push(" .where(eq(orgMember.orgId, orgId));"),t.push("}"),t.push(""),t.push("export async function inviteToOrg(orgId: string, email: string, role: string) {"),t.push(" const id = crypto.randomUUID();"),t.push(" await db.insert(orgMember).values({"),t.push(" id,"),t.push(" orgId,"),t.push(" userId: email,"),t.push(" role,"),t.push(" });"),t.push(" return { id, orgId, email, role };"),t.push("}"),t.push(""),t.join(`
10812
+ `)}function Li(r){if(!r.multiTenant)return null;let t=[];return t.push('"use client";'),t.push(""),t.push("import {"),t.push(" DropdownMenu,"),t.push(" DropdownMenuContent,"),t.push(" DropdownMenuItem,"),t.push(" DropdownMenuTrigger,"),t.push('} from "@/components/ui/dropdown-menu";'),t.push('import { Button } from "@/components/ui/button";'),t.push('import { ChevronsUpDown } from "lucide-react";'),t.push(""),t.push("interface OrgSwitcherProps {"),t.push(" orgs: Array<{ id: string; name: string }>;"),t.push(" currentOrgId: string;"),t.push("}"),t.push(""),t.push("export default function OrgSwitcher({ orgs, currentOrgId }: OrgSwitcherProps) {"),t.push(" const currentOrg = orgs.find((o) => o.id === currentOrgId);"),t.push(""),t.push(" return ("),t.push(" <DropdownMenu>"),t.push(" <DropdownMenuTrigger asChild>"),t.push(' <Button variant="outline" className="w-full justify-between">'),t.push(' <span className="truncate">{currentOrg?.name ?? "Select org"}</span>'),t.push(' <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />'),t.push(" </Button>"),t.push(" </DropdownMenuTrigger>"),t.push(' <DropdownMenuContent className="w-56">'),t.push(" {orgs.map((org) => ("),t.push(" <DropdownMenuItem key={org.id}>"),t.push(" {org.name}"),t.push(" </DropdownMenuItem>"),t.push(" ))}"),t.push(" </DropdownMenuContent>"),t.push(" </DropdownMenu>"),t.push(" );"),t.push("}"),t.push(""),t.join(`
10813
+ `)}function Ni(r,t,e){let a=[],o=r.split("-").map(l=>l.charAt(0).toUpperCase()+l.slice(1)).join(" ");a.push(`# ${o}`),a.push(""),t?.summary&&(a.push(t.summary),a.push(""));let i=t?.features??[];if(i.length>0){a.push("## Features"),a.push("");for(let l of i){let d=l.description?` \u2014 ${l.description}`:"";a.push(`- **${l.name}**${d}`)}a.push("")}a.push("## Tech Stack"),a.push(""),a.push("| Layer | Technology |"),a.push("|-------|------------|"),a.push("| Framework | Next.js 15 (App Router) |"),a.push("| Database | Turso (SQLite at edge) + Drizzle ORM |"),a.push("| Auth | Better Auth (email/password, social login) |"),a.push("| Styling | Tailwind CSS + shadcn/ui |"),a.push("| Deployment | Cloudflare Workers (via Mistflow) |"),e.hasStripe&&a.push("| Payments | Stripe |"),e.hasResend&&a.push("| Email | Resend + React Email |"),e.hasStorage&&a.push("| File Storage | Cloudflare R2 (via Mistflow) |"),e.hasAdmin&&a.push("| Admin | Better Auth admin plugin |"),e.hasAI&&a.push("| AI | Vercel AI SDK + OpenAI |"),a.push("");let n=t?.pages??[];if(n.length>0){a.push("## Pages"),a.push(""),a.push("| Route | Description |"),a.push("|-------|-------------|");for(let l of n){let d=l.path??l.route??l.name??"",u=l.description??"";a.push(`| \`${d.startsWith("/")?d:"/"+d}\` | ${u} |`)}a.push("")}let s=t?.dataModel??[];if(s.length>0){a.push("## Data Model"),a.push("");for(let l of s){let d=l.entity??l.name??"Unknown";if(a.push(`### ${d}`),a.push(""),l.fields.length>0){if(typeof l.fields[0]=="string")a.push(`Fields: ${l.fields.join(", ")}`);else{a.push("| Field | Type |"),a.push("|-------|------|");for(let u of l.fields)a.push(`| ${u.name} | ${u.type} |`)}a.push("")}}}return a.push("## Getting Started"),a.push(""),a.push("### Prerequisites"),a.push(""),a.push("- Node.js 20+"),a.push("- npm"),a.push(""),a.push("### Install"),a.push(""),a.push("```bash"),a.push("npm install"),a.push("```"),a.push(""),a.push("### Set up environment"),a.push(""),a.push("Copy `.env.example` to `.env.local` and fill in the values:"),a.push(""),a.push("```bash"),a.push("cp .env.example .env.local"),a.push("```"),a.push(""),a.push("| Variable | Description | Required |"),a.push("|----------|-------------|----------|"),e.isNeon?a.push("| `DATABASE_URL` | Postgres connection URL | Yes |"):(a.push("| `TURSO_URL` | Database connection URL | Yes |"),a.push("| `TURSO_AUTH_TOKEN` | Database auth token | Yes |")),a.push("| `AUTH_SECRET` | Auth encryption secret (auto-generated) | Yes |"),e.hasStripe&&(a.push("| `STRIPE_SECRET_KEY` | Stripe secret key | Yes |"),a.push("| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | Yes |"),a.push("| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | Yes |")),e.hasResend&&(a.push("| `RESEND_API_KEY` | Resend API key | Yes |"),a.push("| `EMAIL_FROM` | Sender email address | Yes (production) |")),e.hasStorage&&(a.push("| `MISTFLOW_API_KEY` | Mistflow API key for file storage | Yes |"),a.push("| `MISTFLOW_PROJECT_ID` | Mistflow project ID | Yes |")),e.hasAI&&a.push("| `OPENAI_API_KEY` | OpenAI API key | Yes |"),a.push(""),a.push("### Local database"),a.push(""),e.isNeon?(a.push("For local development, start a local Postgres server:"),a.push(""),a.push("```bash"),a.push("# Using Docker:"),a.push("docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:17"),a.push("# Or install via Homebrew: brew install postgresql@17 && brew services start postgresql@17"),a.push("```")):(a.push("For local development, start a local Turso server:"),a.push(""),a.push("```bash"),a.push("npx turso dev"),a.push("```")),a.push(""),a.push("Then set up the database:"),a.push(""),a.push("```bash"),a.push("npm run db:push"),a.push("```"),a.push(""),a.push("### Run"),a.push(""),a.push("```bash"),a.push("npm run dev"),a.push("```"),a.push(""),a.push("Open [http://localhost:3000](http://localhost:3000)."),a.push(""),a.push("## Project Structure"),a.push(""),a.push("```"),a.push("app/"),a.push(" (auth)/ Login and registration pages"),a.push(" (dashboard)/ Authenticated app pages"),e.hasAdmin&&a.push(" (admin)/ Admin panel pages"),a.push(" api/ API routes (auth, health, webhooks)"),a.push(" layout.tsx Root layout with fonts and providers"),a.push(" globals.css Design tokens and Tailwind config"),a.push("components/ Reusable UI components"),a.push("db/"),a.push(" schema/ Database table definitions"),a.push(" index.ts Schema exports"),a.push("lib/"),a.push(" auth.ts Better Auth server config"),a.push(" auth-client.ts Better Auth client config"),a.push(` db.ts ${e.isNeon?"Neon Postgres":"Turso"} database connection`),e.hasStripe&&a.push(" stripe.ts Stripe client"),e.hasResend&&(a.push(" resend.ts Resend client"),a.push(" email.ts Email send helpers")),e.hasStorage&&a.push(" storage.ts File upload/download helpers"),e.hasAI&&a.push(" ai.ts AI client (Vercel AI SDK + OpenAI)"),e.hasResend&&a.push("emails/ React Email templates"),a.push("```"),a.push(""),a.push("## Deploy"),a.push(""),a.push("Deploy to production with Mistflow:"),a.push(""),a.push("```"),a.push("# In your AI editor (Claude Code, Cursor, etc.):"),a.push("mist_deploy action='deploy'"),a.push("```"),a.push(""),a.push("Your app will be live at `https://<app-name>.mistflow.app`."),a.push(""),t?.design&&(a.push("## Design"),a.push(""),t.design.tone&&a.push(`- **Tone**: ${t.design.tone}`),t.design.fonts&&(a.push(`- **Heading font**: ${t.design.fonts.heading}`),a.push(`- **Body font**: ${t.design.fonts.body}`)),t.design.accentColor&&a.push(`- **Accent color**: ${t.design.accentColor}`),t.design.borderRadius&&a.push(`- **Border radius**: ${t.design.borderRadius}`),a.push("")),a.push("---"),a.push(""),a.push("Built with [Mistflow](https://mistflow.ai)"),a.push(""),a.join(`
10814
+ `)}async function Lr(r){let{name:t,plan:e,path:a,planId:o}=r,i=la(a??`./${t}`),n=e?.design,s=e?.appStyle;if(s&&n){let v=Ge(s);if(v?.fonts){let D=B=>ur[B]??B;n.fonts={heading:D(v.fonts.heading),body:D(v.fonts.body)}}}let l=e?St(e,"stripe","payment","billing","subscription","checkout","pricing"):!1,d=!0,u=e?St(e,"upload","file storage","image upload","profile picture","attachment","gallery","media","blob"):!1,c=e?St(e,"admin panel","admin dashboard","admin management"):!1,h=e?St(e,"ai integration","openai","llm","ai chat","chatbot","gpt"):!1,g=e,y=!0;if(Ce(i))return p(`A project already exists at this location (${i}). Choose a different name, or delete the existing folder first.`,!0);Ct(i,{recursive:!0});try{let v=X(nt(i),".mistflow","mockups");if(Ce(v)){let D=ui(v).filter(B=>B.endsWith(".html"));if(D.length>0){let B=X(i,".mistflow","mockups");Ct(B,{recursive:!0});for(let A of D)hi(X(v,A),X(B,A));console.error(`Copied ${D.length} mockup file(s) into project`)}}}catch(v){console.error("Could not copy mockup files:",v instanceof Error?v.message:v)}let x=null;try{x=await yt("nextjs")}catch(v){console.error("Could not fetch scaffold from API, using minimal scaffold:",v instanceof Error?v.message:v)}if(x){let v=t.toLowerCase().replace(/[^a-z0-9-]/g,"-");for(let A of x.files){if(A.path==="package.json"||A.path==="middleware.ts"||A.path==="components/sidebar.tsx"||A.path==="components/topnav.tsx"||A.path==="app/(dashboard)/layout.tsx"||A.path==="app/(dashboard)/page.tsx"||A.path==="app/(dashboard)/dashboard/page.tsx"||!l&&(A.path.includes("stripe")||A.path.includes("webhook/stripe"))||!d&&(A.path.includes("resend")||A.path.includes("emails/"))||!c&&(A.path.includes("(admin)")||A.path.includes("admin-sidebar"))||y&&(A.path==="lib/db.ts"||A.path==="lib/auth.ts"||A.path==="drizzle.config.ts"||A.path==="db/schema/auth.ts"))continue;let U=A.content.replace(/\{\{APP_NAME\}\}/g,t).replace(/\{\{WORKER_NAME\}\}/g,v);if(y&&A.path==="next.config.ts"&&(U=U.replace(/serverExternalPackages:\s*\[[^\]]*\],?/g,'serverExternalPackages: ["@electric-sql/pglite"],')),A.path==="next.config.ts"){let oe=fi(i);oe&&(console.error(`[init] Project is inside monorepo at ${oe} \u2014 adding outputFileTracingRoot`),U.includes("outputFileTracingRoot")||(U=U.replace('import type { NextConfig } from "next";',`import type { NextConfig } from "next";
10815
10815
  import { dirname } from "path";
10816
10816
  import { fileURLToPath } from "url";
10817
10817
 
10818
10818
  const __dirname = dirname(fileURLToPath(import.meta.url));`),U=U.replace("images: {",`outputFileTracingRoot: __dirname,
10819
- images: {`)))}!c&&D.path.includes("sidebar")&&(U=U.replace(/\{user\.role === "admin"[\s\S]*?<\/Link>\s*\)\}/m,""),U=U.replace(/, Shield/g,"")),G(i,D.path,U)}let C={...x.dependencies};y&&(delete C["@libsql/client"],C["@neondatabase/serverless"]="^0.10.0",C["@electric-sql/pglite"]="^0.2.0"),l&&(C.stripe="^17.0.0"),d&&(C.resend="^4.0.0",C["@react-email/components"]="^0.0.31"),h&&(C.ai="^4.0.0",C["@ai-sdk/openai"]="^1.0.0",C.openai="^4.0.0");let F={"@noble/ciphers":"^1.3.0"};if(G(i,"package.json",JSON.stringify({name:t,version:"0.1.0",private:!0,scripts:{dev:"next dev",build:"next build",start:"next start",lint:"next lint","db:push":"drizzle-kit push","db:studio":"drizzle-kit studio"},dependencies:C,devDependencies:x.devDependencies,optionalDependencies:F},null,2)),x.methodology){let D=x.methodology;y&&(D=D.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres/Neon")),G(i,"AGENTS.md",D),G(i,"CLAUDE.md",D)}y&&(G(i,"lib/db.ts",['import { neon } from "@neondatabase/serverless";','import { drizzle as drizzleNeon } from "drizzle-orm/neon-http";','import { drizzle as drizzlePglite } from "drizzle-orm/pglite";',"","// eslint-disable-next-line @typescript-eslint/no-explicit-any","let _db: any = null;","","function getDb() {"," if (!_db) {",' if (process.env.DATABASE_URL && process.env.DATABASE_URL !== "pglite") {'," // Production / remote Postgres"," const sql = neon(process.env.DATABASE_URL);"," _db = drizzleNeon(sql);"," } else {"," // Local dev \u2014 PGlite (zero-install embedded Postgres)"," // eslint-disable-next-line @typescript-eslint/no-require-imports",' const { PGlite } = require("@electric-sql/pglite");',' const client = new PGlite("./local.pg");'," _db = drizzlePglite(client);"," }"," }"," return _db;","}","","// Lazy proxy \u2014 DB isn't initialized at import/build time","// eslint-disable-next-line @typescript-eslint/no-explicit-any","export const db: any = new Proxy({} as any, {"," get(_target, prop, receiver) {"," const realDb = getDb();"," const value = Reflect.get(realDb, prop, receiver);",' if (typeof value === "function") {'," return value.bind(realDb);"," }"," return value;"," },","});",""].join(`
10819
+ images: {`)))}!c&&A.path.includes("sidebar")&&(U=U.replace(/\{user\.role === "admin"[\s\S]*?<\/Link>\s*\)\}/m,""),U=U.replace(/, Shield/g,"")),G(i,A.path,U)}let D={...x.dependencies};y&&(delete D["@libsql/client"],D["@neondatabase/serverless"]="^0.10.0",D["@electric-sql/pglite"]="^0.2.0"),l&&(D.stripe="^17.0.0"),d&&(D.resend="^4.0.0",D["@react-email/components"]="^0.0.31"),h&&(D.ai="^4.0.0",D["@ai-sdk/openai"]="^1.0.0",D.openai="^4.0.0");let B={"@noble/ciphers":"^1.3.0"};if(G(i,"package.json",JSON.stringify({name:t,version:"0.1.0",private:!0,scripts:{dev:"next dev",build:"next build",start:"next start",lint:"next lint","db:push":"drizzle-kit push","db:studio":"drizzle-kit studio"},dependencies:D,devDependencies:x.devDependencies,optionalDependencies:B},null,2)),x.methodology){let A=x.methodology;y&&(A=A.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres/Neon")),G(i,"AGENTS.md",A),G(i,"CLAUDE.md",A)}y&&(G(i,"lib/db.ts",['import { neon } from "@neondatabase/serverless";','import { drizzle as drizzleNeon } from "drizzle-orm/neon-http";','import { drizzle as drizzlePglite } from "drizzle-orm/pglite";',"","// eslint-disable-next-line @typescript-eslint/no-explicit-any","let _db: any = null;","","function getDb() {"," if (!_db) {",' if (process.env.DATABASE_URL && process.env.DATABASE_URL !== "pglite") {'," // Production / remote Postgres"," const sql = neon(process.env.DATABASE_URL);"," _db = drizzleNeon(sql);"," } else {"," // Local dev \u2014 PGlite (zero-install embedded Postgres)"," // eslint-disable-next-line @typescript-eslint/no-require-imports",' const { PGlite } = require("@electric-sql/pglite");',' const client = new PGlite("./local.pg");'," _db = drizzlePglite(client);"," }"," }"," return _db;","}","","// Lazy proxy \u2014 DB isn't initialized at import/build time","// eslint-disable-next-line @typescript-eslint/no-explicit-any","export const db: any = new Proxy({} as any, {"," get(_target, prop, receiver) {"," const realDb = getDb();"," const value = Reflect.get(realDb, prop, receiver);",' if (typeof value === "function") {'," return value.bind(realDb);"," }"," return value;"," },","});",""].join(`
10820
10820
  `)),G(i,"drizzle.config.ts",['import { defineConfig } from "drizzle-kit";',"","// PGlite for local dev (no Postgres install needed), Neon for production",'const isPglite = !process.env.DATABASE_URL || process.env.DATABASE_URL === "pglite";',"","export default defineConfig({",' schema: "./db/schema",',' out: "./db/migrations",',' dialect: "postgresql",',' ...(isPglite ? { driver: "pglite", dbCredentials: { url: "./local.pg" } } : { dbCredentials: { url: process.env.DATABASE_URL! } }),',"});",""].join(`
10821
10821
  `)),G(i,"db/schema/auth.ts",['import { pgTable, text, boolean, timestamp } from "drizzle-orm/pg-core";',"",'export const user = pgTable("user", {',' id: text("id").primaryKey(),',' name: text("name").notNull(),',' email: text("email").notNull().unique(),',' emailVerified: boolean("email_verified").notNull().default(false),',' image: text("image"),',' role: text("role").default("user"),',' banned: boolean("banned").default(false),',' banReason: text("ban_reason"),',' banExpires: timestamp("ban_expires"),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',"});","",'export const session = pgTable("session", {',' id: text("id").primaryKey(),',' expiresAt: timestamp("expires_at").notNull(),',' token: text("token").notNull().unique(),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',' ipAddress: text("ip_address"),',' userAgent: text("user_agent"),',' userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),',' impersonatedBy: text("impersonated_by"),',"});","",'export const account = pgTable("account", {',' id: text("id").primaryKey(),',' accountId: text("account_id").notNull(),',' providerId: text("provider_id").notNull(),',' userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),',' accessToken: text("access_token"),',' refreshToken: text("refresh_token"),',' idToken: text("id_token"),',' accessTokenExpiresAt: timestamp("access_token_expires_at"),',' refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),',' scope: text("scope"),',' password: text("password"),',' createdAt: timestamp("created_at").notNull(),',' updatedAt: timestamp("updated_at").notNull(),',"});","",'export const verification = pgTable("verification", {',' id: text("id").primaryKey(),',' identifier: text("identifier").notNull(),',' value: text("value").notNull(),',' expiresAt: timestamp("expires_at").notNull(),',' createdAt: timestamp("created_at"),',' updatedAt: timestamp("updated_at"),',"});",""].join(`
10822
10822
  `)),G(i,"lib/auth.ts",['import { betterAuth } from "better-auth";','import { drizzleAdapter } from "better-auth/adapters/drizzle";','import { admin } from "better-auth/plugins/admin";','import { db } from "./db";','import * as schema from "@/db";',"",'const baseURL = process.env.BETTER_AUTH_URL || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";',"","async function sendEmail({ to, subject, html }: { to: string; subject: string; html: string }) {"," const apiKey = process.env.RESEND_API_KEY;"," if (!apiKey) {",' console.log(`\\n\u{1F4E7} [Dev] Email to ${to}:\\n Subject: ${subject}\\n ${html.match(/href="([^"]+)"/)?.[1] ?? "(no link found)"}\\n`);'," return;"," }",' const from = process.env.EMAIL_FROM || "noreply@mail.mistflow.app";',' const res = await fetch("https://api.resend.com/emails", {',' method: "POST",',' headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },'," body: JSON.stringify({ from, to, subject, html }),"," });"," if (!res.ok) {",' console.error(`Email send failed (${res.status}): ${await res.text().catch(() => "unknown")}`);'," }","}","","export const auth = betterAuth({"," baseURL,"," trustedOrigins: [baseURL],",' database: drizzleAdapter(db, { provider: "pg", schema }),'," emailAndPassword: {"," enabled: true,",' requireEmailVerification: process.env.NODE_ENV === "production",'," sendResetPassword: async ({ user, url }: { user: { email: string; name: string }; url: string }) => {"," await sendEmail({"," to: user.email,",' subject: "Reset your password",',' html: `<p>Hi ${user.name},</p><p>Click the link below to reset your password:</p><p><a href="${url}">${url}</a></p>`,'," });"," },"," },"," emailVerification: {"," sendOnSignUp: true,"," sendVerificationEmail: async ({ user, url }: { user: { email: string; name: string }; url: string }) => {"," await sendEmail({"," to: user.email,",' subject: "Verify your email address",',' html: `<p>Hi ${user.name},</p><p>Click the link below to verify your email:</p><p><a href="${url}">${url}</a></p>`,'," });"," },"," },"," secret: process.env.AUTH_SECRET,",' plugins: [admin({ defaultRole: "user" })],'," socialProviders: {"," ...(process.env.GOOGLE_CLIENT_ID ? {"," google: {"," clientId: process.env.GOOGLE_CLIENT_ID,"," clientSecret: process.env.GOOGLE_CLIENT_SECRET!,"," },"," } : {}),"," ...(process.env.GITHUB_CLIENT_ID ? {"," github: {"," clientId: process.env.GITHUB_CLIENT_ID,"," clientSecret: process.env.GITHUB_CLIENT_SECRET!,"," },"," } : {}),"," },","});",""].join(`
10823
- `)))}else G(i,"package.json",JSON.stringify({name:t,version:"0.1.0",private:!0},null,2));G(i,"app/globals.css",yi(n,s)),G(i,"app/layout.tsx",vi(t,n,g?.language)),G(i,"README.md",Ri(t,e,{hasStripe:l,hasResend:d,hasStorage:u,hasAdmin:c,hasAI:h,isNeon:y}));let m=[],v=e?.publicPages;if(Array.isArray(v))m=v;else if(typeof v=="string"){try{m=JSON.parse(v)}catch{m=[]}Array.isArray(m)||(m=[])}if(!m.includes("/")){let k=e?.steps?.some(F=>{let D=((F.name??"")+" "+(F.description??"")).toLowerCase();return D.includes("landing")||D.includes("marketing")||D.includes("homepage")}),C=e?.pages?.some(F=>F.path==="/");(k||C)&&(m=["/",...m])}let P={name:t,summary:e?.summary,authModel:e?.authModel,roles:e?.roles,defaultRole:e?.defaultRole,publicPages:m,navStyle:e?.navStyle,multiTenant:e?.multiTenant,pages:e?.pages,dataModel:e?.dataModel,design:e?.design},T=Si(P);T&&G(i,"middleware.ts",T);let w=Ci(P);w&&G(i,w.path,w.content);let R=Ti(P);if(R&&G(i,"lib/roles.ts",R),G(i,"app/page.tsx",Pi(P)),G(i,"app/(dashboard)/layout.tsx",Bi(P,c)),G(i,"app/(dashboard)/dashboard/page.tsx",Di(P)),P.multiTenant){let k=Ai(P,y);k&&G(i,"db/schema/organization.ts",k);let C=Ii(P);C&&G(i,"lib/org.ts",C);let F=Mi(P);F&&G(i,"components/org-switcher.tsx",F)}G(i,"tsconfig.json",JSON.stringify({compilerOptions:{target:"ES2017",lib:["dom","dom.iterable","esnext"],allowJs:!0,skipLibCheck:!0,strict:!0,noEmit:!0,esModuleInterop:!0,module:"esnext",moduleResolution:"bundler",resolveJsonModule:!0,isolatedModules:!0,jsx:"preserve",incremental:!0,plugins:[{name:"next"}],paths:{"@/*":["./*"]}},include:["next-env.d.ts","**/*.ts","**/*.tsx",".next/types/**/*.ts"],exclude:["node_modules"]},null,2)),l&&G(i,"lib/stripe.ts",['import Stripe from "stripe";',"","let _stripe: Stripe | null = null;","","function getStripe(): Stripe {"," if (!_stripe) {"," if (!process.env.STRIPE_SECRET_KEY) {",' throw new Error("STRIPE_SECRET_KEY is not set");'," }"," _stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {"," typescript: true,"," });"," }"," return _stripe;","}","","// Lazy proxy \u2014 Stripe isn't initialized at import/build time","export const stripe = new Proxy({} as Stripe, {"," get(_target, prop) {"," return (getStripe() as unknown as Record<string | symbol, unknown>)[prop];"," },","});",""].join(`
10823
+ `)))}else G(i,"package.json",JSON.stringify({name:t,version:"0.1.0",private:!0},null,2));G(i,"app/globals.css",wi(n,s)),G(i,"app/layout.tsx",Si(t,n,g?.language)),G(i,"README.md",Ni(t,e,{hasStripe:l,hasResend:d,hasStorage:u,hasAdmin:c,hasAI:h,isNeon:y}));let m=[],k=e?.publicPages;if(Array.isArray(k))m=k;else if(typeof k=="string"){try{m=JSON.parse(k)}catch{m=[]}Array.isArray(m)||(m=[])}if(!m.includes("/")){let v=e?.steps?.some(B=>{let A=((B.name??"")+" "+(B.description??"")).toLowerCase();return A.includes("landing")||A.includes("marketing")||A.includes("homepage")}),D=e?.pages?.some(B=>B.path==="/");(v||D)&&(m=["/",...m])}let T={name:t,summary:e?.summary,authModel:e?.authModel,roles:e?.roles,defaultRole:e?.defaultRole,publicPages:m,navStyle:e?.navStyle,multiTenant:e?.multiTenant,pages:e?.pages,dataModel:e?.dataModel,design:e?.design},C=Ti(T);C&&G(i,"middleware.ts",C);let w=Pi(T);w&&G(i,w.path,w.content);let L=Bi(T);if(L&&G(i,"lib/roles.ts",L),G(i,"app/page.tsx",Di(T)),G(i,"app/(dashboard)/layout.tsx",Ai(T,c)),G(i,"app/(dashboard)/dashboard/page.tsx",Ii(T)),T.multiTenant){let v=Mi(T,y);v&&G(i,"db/schema/organization.ts",v);let D=Ri(T);D&&G(i,"lib/org.ts",D);let B=Li(T);B&&G(i,"components/org-switcher.tsx",B)}G(i,"tsconfig.json",JSON.stringify({compilerOptions:{target:"ES2017",lib:["dom","dom.iterable","esnext"],allowJs:!0,skipLibCheck:!0,strict:!0,noEmit:!0,esModuleInterop:!0,module:"esnext",moduleResolution:"bundler",resolveJsonModule:!0,isolatedModules:!0,jsx:"preserve",incremental:!0,plugins:[{name:"next"}],paths:{"@/*":["./*"]}},include:["next-env.d.ts","**/*.ts","**/*.tsx",".next/types/**/*.ts"],exclude:["node_modules"]},null,2)),l&&G(i,"lib/stripe.ts",['import Stripe from "stripe";',"","let _stripe: Stripe | null = null;","","function getStripe(): Stripe {"," if (!_stripe) {"," if (!process.env.STRIPE_SECRET_KEY) {",' throw new Error("STRIPE_SECRET_KEY is not set");'," }"," _stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {"," typescript: true,"," });"," }"," return _stripe;","}","","// Lazy proxy \u2014 Stripe isn't initialized at import/build time","export const stripe = new Proxy({} as Stripe, {"," get(_target, prop) {"," return (getStripe() as unknown as Record<string | symbol, unknown>)[prop];"," },","});",""].join(`
10824
10824
  `)),d&&(G(i,"lib/resend.ts",['import { Resend } from "resend";',"","let _resend: Resend | null = null;","","function getResend(): Resend {"," if (!_resend) {"," if (!process.env.RESEND_API_KEY) {",' throw new Error("RESEND_API_KEY is not set");'," }"," _resend = new Resend(process.env.RESEND_API_KEY);"," }"," return _resend;","}","","// Lazy proxy \u2014 Resend isn't initialized at import/build time","export const resend = new Proxy({} as Resend, {"," get(_target, prop) {"," return (getResend() as unknown as Record<string | symbol, unknown>)[prop];"," },","});",""].join(`
10825
10825
  `)),G(i,"lib/email.ts",['import { resend } from "./resend";',"",'const FROM = process.env.EMAIL_FROM ?? "onboarding@resend.dev";',"","export async function sendEmail({"," to,"," subject,"," react,","}: {"," to: string;"," subject: string;"," react: React.ReactElement;","}) {"," return resend.emails.send({ from: FROM, to, subject, react });","}",""].join(`
10826
10826
  `))),u&&(G(i,"lib/storage.ts",['const MISTFLOW_API = process.env.MISTFLOW_API_URL ?? "https://api.mistflow.ai";',"const MISTFLOW_API_KEY = process.env.MISTFLOW_API_KEY;","const PROJECT_ID = process.env.MISTFLOW_PROJECT_ID;","","interface UploadResult {"," upload_url: string;"," download_url: string;"," key: string;","}","","function authHeaders(): Record<string, string> {"," return {",' "Content-Type": "application/json",',' "Authorization": `ApiKey ${MISTFLOW_API_KEY}`,'," };","}","",'export async function getUploadUrl(filename: string, contentType: string = "application/octet-stream"): Promise<UploadResult> {'," const res = await fetch(`${MISTFLOW_API}/api/storage/upload-url`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename, content_type: contentType }),"," });"," if (!res.ok) throw new Error(`Storage error: ${res.status}`);"," return res.json();","}","","export async function getDownloadUrl(filename: string): Promise<string> {"," const res = await fetch(`${MISTFLOW_API}/api/storage/download-url`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename }),"," });"," if (!res.ok) throw new Error(`Storage error: ${res.status}`);"," const data = await res.json();"," return data.download_url;","}","","export async function deleteFile(filename: string): Promise<void> {"," await fetch(`${MISTFLOW_API}/api/storage/delete`, {",' method: "POST",'," headers: authHeaders(),"," body: JSON.stringify({ project_id: PROJECT_ID, filename }),"," });","}","","export async function uploadFile(file: File): Promise<string> {"," const { upload_url, download_url } = await getUploadUrl(file.name, file.type);",' await fetch(upload_url, { method: "PUT", body: file, headers: { "Content-Type": file.type } });'," return download_url;","}",""].join(`
10827
10827
  `)),G(i,"app/api/upload/route.ts",['import { NextRequest, NextResponse } from "next/server";','import { getUploadUrl } from "@/lib/storage";',"","export async function POST(req: NextRequest) {"," const { filename, contentType } = await req.json();"," if (!filename) {",' return NextResponse.json({ error: "filename is required" }, { status: 400 });'," }"," try {",' const result = await getUploadUrl(filename, contentType ?? "application/octet-stream");'," return NextResponse.json(result);"," } catch {",' return NextResponse.json({ error: "Failed to get upload URL" }, { status: 500 });'," }","}",""].join(`
10828
10828
  `))),h&&(G(i,"lib/ai.ts",['import { createOpenAI } from "@ai-sdk/openai";',"","export const openai = createOpenAI({"," apiKey: process.env.OPENAI_API_KEY,","});",""].join(`
10829
10829
  `)),G(i,"app/api/chat/route.ts",['import { openai } from "@/lib/ai";','import { streamText } from "ai";',"","export async function POST(req: Request) {"," const { messages } = await req.json();",""," const result = streamText({",' model: openai("gpt-4o"),'," messages,"," });",""," return result.toDataStreamResponse();","}",""].join(`
10830
- `)));let V={name:t,methodologyVersion:x?.version??"1.0",createdAt:new Date().toISOString(),...o?{planId:o}:{},plan:Array.isArray(e?.steps)?{...e,steps:e.steps.map(k=>({number:k.number,name:k.name??k.title,description:k.description,entities:k.entities,pages:k.pages,features:k.features,status:"pending"}))}:e,dbProvider:"neon",env:{managed:{DATABASE_URL:{description:"Postgres connection URL",scope:"production"},AUTH_SECRET:{description:"Auth encryption secret",scope:"production"},...l?{STRIPE_SECRET_KEY:{description:"Stripe secret key",scope:"production"},STRIPE_WEBHOOK_SECRET:{description:"Stripe webhook signing secret",scope:"production"},NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:{description:"Stripe publishable key",scope:"production"}}:{},...d?{RESEND_API_KEY:{description:"Resend API key \u2014 managed by Mistflow by default, override with your own key from resend.com",scope:"production"},EMAIL_FROM:{description:"Sender email address \u2014 managed by Mistflow by default",scope:"production"}}:{},...u?{MISTFLOW_API_KEY:{description:"Mistflow API key for file storage",scope:"production"},MISTFLOW_PROJECT_ID:{description:"Mistflow project ID",scope:"production"}}:{}},...h?{required:{OPENAI_API_KEY:{description:"OpenAI API key",setupUrl:"https://platform.openai.com/api-keys"}}}:{}},authModel:e?.authModel??"email",roles:e?.roles??null,navStyle:e?.navStyle??"sidebar",multiTenant:e?.multiTenant??!1,hasAdmin:c,hasResend:d,hasStorage:u,hasAI:h,deploy:null};G(i,"mistflow.json",JSON.stringify(V,null,2));let L=hi(32).toString("hex"),O=l?`
10830
+ `)));let V={name:t,methodologyVersion:x?.version??"1.0",createdAt:new Date().toISOString(),...o?{planId:o}:{},plan:Array.isArray(e?.steps)?{...e,steps:e.steps.map(v=>({number:v.number,name:v.name??v.title,description:v.description,entities:v.entities,pages:v.pages,features:v.features,status:"pending"}))}:e,dbProvider:"neon",env:{managed:{DATABASE_URL:{description:"Postgres connection URL",scope:"production"},AUTH_SECRET:{description:"Auth encryption secret",scope:"production"},...l?{STRIPE_SECRET_KEY:{description:"Stripe secret key",scope:"production"},STRIPE_WEBHOOK_SECRET:{description:"Stripe webhook signing secret",scope:"production"},NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:{description:"Stripe publishable key",scope:"production"}}:{},...d?{RESEND_API_KEY:{description:"Resend API key \u2014 managed by Mistflow by default, override with your own key from resend.com",scope:"production"},EMAIL_FROM:{description:"Sender email address \u2014 managed by Mistflow by default",scope:"production"}}:{},...u?{MISTFLOW_API_KEY:{description:"Mistflow API key for file storage",scope:"production"},MISTFLOW_PROJECT_ID:{description:"Mistflow project ID",scope:"production"}}:{}},...h?{required:{OPENAI_API_KEY:{description:"OpenAI API key",setupUrl:"https://platform.openai.com/api-keys"}}}:{}},authModel:e?.authModel??"email",roles:e?.roles??null,navStyle:e?.navStyle??"sidebar",multiTenant:e?.multiTenant??!1,hasAdmin:c,hasResend:d,hasStorage:u,hasAI:h,deploy:null};G(i,"mistflow.json",JSON.stringify(V,null,2));let N=mi(32).toString("hex"),O=l?`
10831
10831
  # Stripe
10832
10832
  STRIPE_SECRET_KEY=
10833
10833
  STRIPE_WEBHOOK_SECRET=
@@ -10841,25 +10841,25 @@ EMAIL_FROM=onboarding@resend.dev
10841
10841
  # File Storage (Mistflow managed)
10842
10842
  MISTFLOW_API_KEY=
10843
10843
  MISTFLOW_PROJECT_ID=
10844
- `:"",K=h?`
10844
+ `:"",Y=h?`
10845
10845
  # AI (get your key at https://platform.openai.com/api-keys)
10846
10846
  OPENAI_API_KEY=
10847
- `:"",ee=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
10847
+ `:"",Z=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
10848
10848
  # Set DATABASE_URL only for production or to use a remote Postgres
10849
10849
  # DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`,E=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
10850
10850
  # Set DATABASE_URL only for production or to use a remote Postgres
10851
- # DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`;G(i,".env.local",`${ee}
10852
- AUTH_SECRET=${L}
10853
- ${O}${W}${z}${K}`),G(i,".env.example",`${E}
10851
+ # DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`;G(i,".env.local",`${Z}
10852
+ AUTH_SECRET=${N}
10853
+ ${O}${W}${z}${Y}`),G(i,".env.example",`${E}
10854
10854
  AUTH_SECRET=your-secret-here
10855
- ${O}${W}${z}${K}`);let N=[],q=(k,C)=>{N.push({phase:k,message:C})},ce=(k,C)=>{let F=N.find(D=>D.phase===k&&!D.durationMs);F&&(F.durationMs=C)};q("install","Installing packages...");let me=Date.now(),ie=0,re=await Me("npm",["install"],i,12e4,k=>{let C=k.match(/added (\d+) packages/);C&&(ie=parseInt(C[1],10))});if(re.success||(console.error("[init] npm install failed, retrying..."),re=await Me("npm",["install"],i,12e4,k=>{let C=k.match(/added (\d+) packages/);C&&(ie=parseInt(C[1],10))})),!re.success)return p(`Project files were created at ${i} but package installation failed after 2 attempts: ${re.error}. Run "npm install" manually to retry.`,!0);ce("install",Date.now()-me),N[N.length-1].message=`Installed ${ie||"all"} packages`;let B=x?.shadcnComponents??["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"];q("ui",`Adding ${B.length} UI components...`);let M=Date.now(),I=await Me("npx",["--yes","shadcn@latest","add","-y","-o",...B],i,12e4);if(I.success||(console.error("[init] shadcn install failed, retrying..."),I=await Me("npx",["--yes","shadcn@latest","add","-y","-o",...B],i,12e4)),I.success)N[N.length-1].message=`Added ${B.length} UI components (Button, Card, Dialog, Table, ...)`;else return p(`Project created at ${i} but UI component installation failed after 2 attempts: ${I.error}. Run "npx shadcn@latest add -y -o ${B.join(" ")}" manually to retry.`,!0);ce("ui",Date.now()-M),q("db","Setting up database...");let f=Date.now(),b,S=await Me("npx",["drizzle-kit","push"],i,3e4);S.success?N[N.length-1].message="Database ready":(console.error("DB push failed:",S.error),b="Database setup failed \u2014 login won't work yet. Try deploying again. If it keeps failing, contact support.",N[N.length-1].message=y?"Database setup skipped (configure DATABASE_URL to enable)":"Database setup skipped (configure TURSO_URL to enable)"),ce("db",Date.now()-f),q("git","Initializing git repository...");let A=Date.now();try{let k=Dr(i);await k.init(),await k.add("."),await k.commit("Initial Mistflow project setup"),N[N.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),N[N.length-1].message="Git init skipped"}ce("git",Date.now()-A);let $=g?.requestedSubdomain||void 0,H,Y;try{let k=await ft(t,void 0,"neon",$);H=k.id;let C=Z(i,"mistflow.json"),F=JSON.parse(Ct(C,"utf-8"));if(F.projectId=H,Ie(C,JSON.stringify(F,null,2)),k.managed_env&&Object.keys(k.managed_env).length>0){let D=Z(i,".env.local"),U=Se(D)?Ct(D,"utf-8"):"";for(let[fe,Fe]of Object.entries(k.managed_env)){let ae=new RegExp(`^${fe}=.*$`,"m");ae.test(U)?U=U.replace(ae,`${fe}=${Fe}`):U+=`
10856
- ${fe}=${Fe}`}Ie(D,U)}if(H)try{let{getBaseUrl:D,getAuthHeaders:U}=await import("./api-client-WMD2JG5B.js"),fe=U(),Fe=e?.features,ae={};Array.isArray(Fe)&&Fe.length>0&&(ae.features=Fe.map(_t=>_t.name)),e&&(ae.plan=e),Object.keys(ae).length>0&&await fetch(`${D()}/api/projects/${encodeURIComponent(H)}/state`,{method:"PUT",headers:{...fe,"Content-Type":"application/json"},body:JSON.stringify(ae)})}catch{}}catch(k){let C=k instanceof Error?k.message:String(k);console.error("Could not register project on backend:",C),Y=`Project created locally but NOT registered on Mistflow servers (${C}). Deploy will auto-register it.`}let ue=N.reduce((k,C)=>k+(C.durationMs??0),0),ne={projectPath:i,projectId:H},te=N.map(k=>{let C=k.durationMs?` (${(k.durationMs/1e3).toFixed(1)}s)`:"";return`${k.message}${C}`});ne.progress=te,ne.totalSetupTime=`${(ue/1e3).toFixed(1)}s`;let J=[];return b&&J.push(b),H||J.push("Project was not registered with Mistflow (not signed in). Run mist_setup to sign in BEFORE deploying \u2014 deploy will fail without it."),Y&&(ne.registrationWarning=Y),J.length>0&&(ne.warnings=J),H?ne.nextAction="NEXT: Call mist_build with action='implement' to start building the first plan step. Do this immediately \u2014 do NOT start a dev server or suggest localhost.":ne.nextAction="NEXT: Call mist_build with action='implement' to start building the first plan step. IMPORTANT: You MUST run mist_setup to sign in before deploying \u2014 the project could not be registered because auth is missing.",p(JSON.stringify(ne))}async function Rr(r){let{name:t,plan:e,projectId:a,sourceDeploymentId:o,forkToken:i,requiredEnvVars:n,dbProvider:s,planId:l}=r,d=ia(r.path??`./${t}`);if(Se(d))return p(`A project already exists at this location (${d}). Choose a different name, or delete the existing folder first.`,!0);let{mkdtempSync:u,renameSync:c,rmSync:h,cpSync:g}=await import("fs"),{tmpdir:y}=await import("os"),x=u(Z(y(),"mistflow-fork-")),m=Z(x,"project");St(m,{recursive:!0});let v=[],P=(w,R)=>v.push({phase:w,message:R}),T=(w,R)=>{let V=v.find(L=>L.phase===w);V&&(V.durationMs=R)};try{P("download","Downloading source code from template...");let w=Date.now(),R=Z(x,"source.tar.gz");try{await Za(o,i,R)}catch(b){h(x,{recursive:!0,force:!0});let S=b instanceof Error?b.message:"Source download failed";return p(`Source code download failed: ${S}. You can still build from the plan \u2014 run mist_build init without the source (omit planId and pass the plan directly).`,!0)}T("download",Date.now()-w),v[v.length-1].message="Source code downloaded",P("extract","Extracting source code...");let V=Date.now(),L=await Me("tar",["-xzf",R,"-C",m,"--exclude","node_modules","--exclude",".git"],m,6e4);if(!L.success)return h(x,{recursive:!0,force:!0}),p(`Failed to extract source archive: ${L.error}`,!0);if(T("extract",Date.now()-V),!Se(Z(m,"package.json")))return h(x,{recursive:!0,force:!0}),p("Source archive does not contain a package.json. The template may be corrupted.",!0);let O=[".mistflow",".env.local",".env.example","local.db","local.pg"];for(let b of O){let S=Z(m,b);Se(S)&&h(S,{recursive:!0,force:!0})}let W={name:t,projectId:a,template:"nextjs",createdAt:new Date().toISOString(),planId:l??void 0,plan:e,dbProvider:s};Ie(Z(m,"mistflow.json"),JSON.stringify(W,null,2));let z=n.map(b=>{let S=b.description?`# ${b.description}`:`# ${b.key}`,A=b.setup_url?` (${b.setup_url})`:"";return`${S}${A}
10857
- ${b.key}=`});Ie(Z(m,".env.local"),z.join(`
10855
+ ${O}${W}${z}${Y}`);let F=[],q=(v,D)=>{F.push({phase:v,message:D})},pe=(v,D)=>{let B=F.find(A=>A.phase===v&&!A.durationMs);B&&(B.durationMs=D)};q("install","Installing packages...");let me=Date.now(),ne=0,ae=await Re("npm",["install"],i,12e4,v=>{let D=v.match(/added (\d+) packages/);D&&(ne=parseInt(D[1],10))});if(ae.success||(console.error("[init] npm install failed, retrying..."),ae=await Re("npm",["install"],i,12e4,v=>{let D=v.match(/added (\d+) packages/);D&&(ne=parseInt(D[1],10))})),!ae.success)return p(`Project files were created at ${i} but package installation failed after 2 attempts: ${ae.error}. Run "npm install" manually to retry.`,!0);pe("install",Date.now()-me),F[F.length-1].message=`Installed ${ne||"all"} packages`;let P=x?.shadcnComponents??["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"];q("ui",`Adding ${P.length} UI components...`);let R=Date.now(),M=await Re("npx",["--yes","shadcn@latest","add","-y","-o",...P],i,12e4);if(M.success||(console.error("[init] shadcn install failed, retrying..."),M=await Re("npx",["--yes","shadcn@latest","add","-y","-o",...P],i,12e4)),M.success)F[F.length-1].message=`Added ${P.length} UI components (Button, Card, Dialog, Table, ...)`;else return p(`Project created at ${i} but UI component installation failed after 2 attempts: ${M.error}. Run "npx shadcn@latest add -y -o ${P.join(" ")}" manually to retry.`,!0);pe("ui",Date.now()-R),q("db","Setting up database...");let f=Date.now(),b,S=await Re("npx",["drizzle-kit","push"],i,3e4);S.success?F[F.length-1].message="Database ready":(console.error("DB push failed:",S.error),b="Database setup failed \u2014 login won't work yet. Try deploying again. If it keeps failing, contact support.",F[F.length-1].message=y?"Database setup skipped (configure DATABASE_URL to enable)":"Database setup skipped (configure TURSO_URL to enable)"),pe("db",Date.now()-f),q("git","Initializing git repository...");let I=Date.now();try{let v=Ir(i);await v.init(),await v.add("."),await v.commit("Initial Mistflow project setup"),F[F.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),F[F.length-1].message="Git init skipped"}pe("git",Date.now()-I);let $=g?.requestedSubdomain||void 0,H,K;try{let v=await bt(t,void 0,"neon",$);H=v.id;let D=X(i,"mistflow.json"),B=JSON.parse(Tt(D,"utf-8"));if(B.projectId=H,Me(D,JSON.stringify(B,null,2)),qt(i,Kt(H,t)),v.managed_env&&Object.keys(v.managed_env).length>0){let A=X(i,".env.local"),U=Ce(A)?Tt(A,"utf-8"):"";for(let[oe,Se]of Object.entries(v.managed_env)){let te=new RegExp(`^${oe}=.*$`,"m");te.test(U)?U=U.replace(te,`${oe}=${Se}`):U+=`
10856
+ ${oe}=${Se}`}Me(A,U)}if(H)try{let{getBaseUrl:A,getAuthHeaders:U}=await import("./api-client-WMD2JG5B.js"),oe=U(),Se=e?.features,te={};Array.isArray(Se)&&Se.length>0&&(te.features=Se.map(jt=>jt.name)),e&&(te.plan=e),Object.keys(te).length>0&&await fetch(`${A()}/api/projects/${encodeURIComponent(H)}/state`,{method:"PUT",headers:{...oe,"Content-Type":"application/json"},body:JSON.stringify(te)})}catch{}}catch(v){let D=v instanceof Error?v.message:String(v);console.error("Could not register project on backend:",D),K=`Project created locally but NOT registered on Mistflow servers (${D}). Deploy will auto-register it.`}let fe=F.reduce((v,D)=>v+(D.durationMs??0),0),ie={projectPath:i,projectId:H},re=F.map(v=>{let D=v.durationMs?` (${(v.durationMs/1e3).toFixed(1)}s)`:"";return`${v.message}${D}`});ie.progress=re,ie.totalSetupTime=`${(fe/1e3).toFixed(1)}s`;let ee=[];return b&&ee.push(b),H||ee.push("Project was not registered with Mistflow (not signed in). Run mist_setup to sign in BEFORE deploying \u2014 deploy will fail without it."),K&&(ie.registrationWarning=K),ee.length>0&&(ie.warnings=ee),H?ie.nextAction="NEXT: Call mist_build with action='implement' to start building the first plan step. Do this immediately \u2014 do NOT start a dev server or suggest localhost.":ie.nextAction="NEXT: Call mist_build with action='implement' to start building the first plan step. IMPORTANT: You MUST run mist_setup to sign in before deploying \u2014 the project could not be registered because auth is missing.",p(JSON.stringify(ie))}async function Nr(r){let{name:t,plan:e,projectId:a,sourceDeploymentId:o,forkToken:i,requiredEnvVars:n,dbProvider:s,planId:l}=r,d=la(r.path??`./${t}`);if(Ce(d))return p(`A project already exists at this location (${d}). Choose a different name, or delete the existing folder first.`,!0);let{mkdtempSync:u,renameSync:c,rmSync:h,cpSync:g}=await import("fs"),{tmpdir:y}=await import("os"),x=u(X(y(),"mistflow-fork-")),m=X(x,"project");Ct(m,{recursive:!0});let k=[],T=(w,L)=>k.push({phase:w,message:L}),C=(w,L)=>{let V=k.find(N=>N.phase===w);V&&(V.durationMs=L)};try{T("download","Downloading source code from template...");let w=Date.now(),L=X(x,"source.tar.gz");try{await tr(o,i,L)}catch(b){h(x,{recursive:!0,force:!0});let S=b instanceof Error?b.message:"Source download failed";return p(`Source code download failed: ${S}. You can still build from the plan \u2014 run mist_build init without the source (omit planId and pass the plan directly).`,!0)}C("download",Date.now()-w),k[k.length-1].message="Source code downloaded",T("extract","Extracting source code...");let V=Date.now(),N=await Re("tar",["-xzf",L,"-C",m,"--exclude","node_modules","--exclude",".git"],m,6e4);if(!N.success)return h(x,{recursive:!0,force:!0}),p(`Failed to extract source archive: ${N.error}`,!0);if(C("extract",Date.now()-V),!Ce(X(m,"package.json")))return h(x,{recursive:!0,force:!0}),p("Source archive does not contain a package.json. The template may be corrupted.",!0);let O=[".mistflow",".env.local",".env.example","local.db","local.pg"];for(let b of O){let S=X(m,b);Ce(S)&&h(S,{recursive:!0,force:!0})}let W={name:t,projectId:a,template:"nextjs",createdAt:new Date().toISOString(),planId:l??void 0,plan:e,dbProvider:s};Me(X(m,"mistflow.json"),JSON.stringify(W,null,2));let z=n.map(b=>{let S=b.description?`# ${b.description}`:`# ${b.key}`,I=b.setup_url?` (${b.setup_url})`:"";return`${S}${I}
10857
+ ${b.key}=`});Me(X(m,".env.local"),z.join(`
10858
10858
 
10859
10859
  `)+`
10860
- `);let K=n.map(b=>`${b.key}=`);Ie(Z(m,".env.example"),K.join(`
10860
+ `);let Y=n.map(b=>`${b.key}=`);Me(X(m,".env.example"),Y.join(`
10861
10861
  `)+`
10862
- `),Ie(Z(m,"README.md"),`# ${t}
10862
+ `),Me(X(m,"README.md"),`# ${t}
10863
10863
 
10864
10864
  Forked from a Mistflow template. Built with Next.js, Drizzle ORM, and Better Auth.
10865
10865
 
@@ -10868,21 +10868,21 @@ Forked from a Mistflow template. Built with Next.js, Drizzle ORM, and Better Aut
10868
10868
  1. Copy \`.env.example\` to \`.env.local\` and fill in your values
10869
10869
  2. Run \`npm run dev\`
10870
10870
  3. Deploy with \`mist_deploy\`
10871
- `),P("setup","Setting up project directory...");let ee=Date.now();try{c(m,d)}catch(b){if(b.code==="EXDEV")g(m,d,{recursive:!0}),h(m,{recursive:!0,force:!0});else throw b}T("setup",Date.now()-ee),P("install","Installing packages...");let E=Date.now(),N=0,q=await Me("npm",["install"],d,12e4,b=>{let S=b.match(/added (\d+) packages/);S&&(N=parseInt(S[1],10))});if(q.success||(console.error("[initFromSource] npm install failed, retrying..."),q=await Me("npm",["install"],d,12e4,b=>{let S=b.match(/added (\d+) packages/);S&&(N=parseInt(S[1],10))})),!q.success)return p(`Source code was restored at ${d} but package installation failed: ${q.error}. Run "npm install" manually.`,!0);T("install",Date.now()-E),v[v.length-1].message=`Installed ${N||"all"} packages`,P("git","Initializing git repository...");let ce=Date.now();try{let b=Dr(d);await b.init(),await b.add("."),await b.commit("Forked from Mistflow template"),v[v.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),v[v.length-1].message="Git init skipped"}T("git",Date.now()-ce);try{let{markLocalSetupDone:b}=await import("./api-client-WMD2JG5B.js");await b(a)}catch{console.error("[initFromSource] Could not mark local_setup_done on backend, continuing.")}let me=JSON.parse(Ct(Z(d,"mistflow.json"),"utf-8"));me.projectId=a,Ie(Z(d,"mistflow.json"),JSON.stringify(me,null,2));let ie=e.steps,re=ie?.filter(b=>b.status==="completed").length??0,B=ie?.filter(b=>b.status==="pending").length??0,M=ie?.length??0,I=n.length>0?`
10871
+ `),T("setup","Setting up project directory...");let Z=Date.now();try{c(m,d)}catch(b){if(b.code==="EXDEV")g(m,d,{recursive:!0}),h(m,{recursive:!0,force:!0});else throw b}C("setup",Date.now()-Z),T("install","Installing packages...");let E=Date.now(),F=0,q=await Re("npm",["install"],d,12e4,b=>{let S=b.match(/added (\d+) packages/);S&&(F=parseInt(S[1],10))});if(q.success||(console.error("[initFromSource] npm install failed, retrying..."),q=await Re("npm",["install"],d,12e4,b=>{let S=b.match(/added (\d+) packages/);S&&(F=parseInt(S[1],10))})),!q.success)return p(`Source code was restored at ${d} but package installation failed: ${q.error}. Run "npm install" manually.`,!0);C("install",Date.now()-E),k[k.length-1].message=`Installed ${F||"all"} packages`,T("git","Initializing git repository...");let pe=Date.now();try{let b=Ir(d);await b.init(),await b.add("."),await b.commit("Forked from Mistflow template"),k[k.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),k[k.length-1].message="Git init skipped"}C("git",Date.now()-pe);try{let{markLocalSetupDone:b}=await import("./api-client-WMD2JG5B.js");await b(a)}catch{console.error("[initFromSource] Could not mark local_setup_done on backend, continuing.")}let me=JSON.parse(Tt(X(d,"mistflow.json"),"utf-8"));me.projectId=a,Me(X(d,"mistflow.json"),JSON.stringify(me,null,2)),qt(d,Kt(a,t));let ne=e.steps,ae=ne?.filter(b=>b.status==="completed").length??0,P=ne?.filter(b=>b.status==="pending").length??0,R=ne?.length??0,M=n.length>0?`
10872
10872
 
10873
10873
  Environment variables needed:
10874
10874
  `+n.map(b=>` \u2022 ${b.key}${b.description?` \u2014 ${b.description}`:""}`).join(`
10875
- `):"",f=B>0?`Source code restored with ${re}/${M} steps complete. ${B} steps need implementation \u2014 call mist_build with action='implement' to apply your changes.`:`Source code fully restored (${M} steps complete). Configure your .env.local, then deploy with mist_deploy.`;return p(JSON.stringify({status:"success",projectPath:d,projectId:a,planStepsCompleted:re,planStepsTotal:M,pendingSteps:B,progress:v,nextAction:f+I}))}finally{try{h(x,{recursive:!0,force:!0})}catch{}}}import{z as da}from"zod";import{existsSync as ca,readFileSync as Or,writeFileSync as pa,mkdirSync as Gi}from"fs";import{join as Dt,resolve as Wi,dirname as Oi}from"path";import{createConnection as _i}from"net";import{existsSync as Bt,mkdirSync as Nr,readFileSync as Fr,readdirSync as Er,writeFileSync as Li}from"fs";import{join as Qe,resolve as Ni}from"path";import{homedir as la}from"os";function Je(r){return r.entity??r.name??"Unknown"}function Oe(r){return typeof r=="string"?r:r.name}function sa(r){return typeof r=="string"?"text":r.type}function Lr(r){let t=r.fields||[],e=[];for(let a=0;a<3;a++){let o={};for(let i of t)o[Oe(i)]=Fi(Oe(i),sa(i),Je(r),a);e.push(o)}return e}function Fi(r,t,e,a){let o=r.toLowerCase(),i=(t||"text").toLowerCase();return o==="name"||o==="title"?[`${e} Alpha`,`${e} Beta`,`${e} Gamma`][a]:o==="email"?["alice@example.com","bob@example.com","carol@example.com"][a]:o==="status"?["Active","Pending","Completed"][a]:o==="priority"?["High","Medium","Low"][a]:o.includes("date")||i==="date"?["Jan 15, 2024","Feb 20, 2024","Mar 10, 2024"][a]:o.includes("price")||o.includes("amount")||o.includes("cost")?["$29","$49","$99"][a]:o.includes("count")||o.includes("quantity")||i==="number"||i==="integer"?["12","34","56"][a]:i==="boolean"||i==="bool"?["Yes","No","Yes"][a]:o.includes("description")||i==="textarea"?["Brief description here","Another example entry","Third sample item"][a]:[`Sample ${a+1}`][0]}function Ei(r){let t=r.dataModel??[],e=r.pages??[],a=r.design??{},o=e.map(c=>({label:c.name??c.path??"Page",route:c.path??c.route??"/"})),i=[],n=t.slice(0,3).map(c=>({name:Je(c),fields:(c.fields||[]).map(h=>({name:Oe(h),type:sa(h)})),sampleData:Lr(c)})),s=[];r.primaryAction&&(s.push(`PRIMARY: ${r.primaryAction.action} \u2014 this is the first thing the user sees and does`),s.push(`SURFACE: ${r.primaryAction.dashboardSurface}`)),s.push(`METRICS: Key counts for ${t.map(c=>Je(c)).join(", ")}`),s.push("RECENT: Latest activity or items"),i.push({name:"Dashboard",type:"dashboard",route:"/dashboard",purpose:r.primaryAction?`Action surface \u2014 user comes here to ${r.primaryAction.action.toLowerCase()}. Not a stats display.`:`Overview of ${t.map(c=>Je(c)).join(", ")} with key metrics.`,informationHierarchy:s,interactionStates:["Empty state: new user, no data yet \u2014 show onboarding prompt","Loading state: skeleton placeholders for metrics and table","Populated state: real data with metrics, recent items, quick actions"],entities:n});let l=t[0];if(l){let c=Je(l),h=c.toLowerCase().endsWith("s")?c:`${c}s`;i.push({name:`${c} List`,type:"detail",route:`/${h.toLowerCase()}`,purpose:`Browse, search, and manage ${h.toLowerCase()}. Create new ${c.toLowerCase()}s.`,informationHierarchy:[`HEADER: "${h}" title + "Add ${c}" button`,"SEARCH: Filter/search bar \u2014 users will have many items",`TABLE: ${(l.fields||[]).slice(0,5).map(g=>Oe(g)).join(", ")} columns`,"ROW ACTIONS: Edit, delete on each row"],interactionStates:[`Empty state: "No ${h.toLowerCase()} yet" with create CTA`,"Loading state: skeleton table rows",`Search with no results: "No ${h.toLowerCase()} matching..." with clear filter`],entities:[{name:c,fields:(l.fields||[]).map(g=>({name:Oe(g),type:sa(g)})),sampleData:Lr(l)}]})}r.steps.some(c=>{let h=`${c.name??c.title??""} ${c.description??""}`.toLowerCase();return h.includes("landing")||h.includes("hero")||h.includes("marketing")||h.includes("homepage")})&&i.push({name:"Landing Page",type:"landing",route:"/",purpose:`Convince visitors to sign up for ${r.name}. Answer: what is this, who is it for, why should I care.`,informationHierarchy:[`HERO: One sentence about what ${r.name} does \u2014 not "Transform your X", be specific`,"CTA: Sign up / Get started \u2014 one clear action","PROOF: What makes this valuable (features, not buzzwords)","SECONDARY CTA: Repeat the sign up prompt"],interactionStates:["Mobile: hero stacks vertically, nav collapses to hamburger","Desktop: hero side-by-side or centered, full nav"],entities:[]});let u=[];for(let c of t.slice(0,3)){let h=Je(c);(c.fields||[]).find(y=>Oe(y).toLowerCase()==="name"||Oe(y).toLowerCase()==="title")&&u.push(`What if a ${h.toLowerCase()}'s name is 47 characters? Does the layout break?`),u.push(`What if there are 0 ${h.toLowerCase()}s? 1? 500?`)}return r.authModel&&r.authModel!=="none"&&u.push("What does a brand-new user see? (no data, no setup)"),u.push("What if the network is slow? What loads first?"),{appName:r.name,summary:r.summary??"",screens:i,navigation:{style:r.navStyle??"sidebar",items:o},primaryAction:r.primaryAction??null,designDirection:{tone:a.tone??"professional",accentColor:a.accentColor??"blue",navStyle:r.navStyle??"sidebar",fonts:a.fonts??{heading:"Inter",body:"Inter"}},edgeCases:u}}function Ui(r,t){let e=[];e.push(`# Wireframe sketch for ${r.appName}`),e.push(""),e.push(`**${r.appName}** \u2014 ${r.summary}`),e.push(""),t&&(e.push("## Feedback to apply"),e.push(t),e.push("")),e.push("## Design principles"),e.push(""),e.push("Apply these when deciding layout and hierarchy:"),e.push("1. **Information hierarchy** \u2014 What does the user see first, second, third? The primary action is first. Metrics are second. Everything else is supporting."),e.push("2. **Interaction states** \u2014 Every screen has at least: empty, loading, populated. Show the populated state but add HTML comments noting the others."),e.push("3. **Edge case paranoia** \u2014 What if there are 0 items? 500 items? A 47-character name? Think about these and comment where they matter."),e.push(`4. **Subtraction** \u2014 "As little design as possible" (Dieter Rams). Every element earns its pixels. If removing something doesn't hurt, remove it.`),e.push("5. **Design for trust** \u2014 Clear labels, predictable layout, obvious actions. No mystery meat navigation."),e.push(""),e.push("## Wireframe rules (strict)"),e.push(""),e.push("Write a **single self-contained HTML file** saved to `.mistflow/mockups/mockup-{planId}.html` (replace `{planId}` with the actual plan ID)."),e.push(""),e.push("The wireframe must:"),e.push("- Use **system fonts only** (`-apple-system, system-ui, sans-serif`) \u2014 no Google Fonts, no CDN"),e.push("- Use **inline CSS only** \u2014 no external stylesheets, no Tailwind CDN"),e.push("- Look **intentionally rough** \u2014 thin gray borders (#ddd), light backgrounds (#f8f8f8), no color, no shadows"),e.push("- Use **realistic placeholder content** that matches this specific app (sample data provided below) \u2014 NOT lorem ipsum"),e.push("- Include **HTML comments** explaining design decisions (e.g., `<!-- Primary action is prominent because users come here to check in members -->`)"),e.push("- Show **all screens in a single page** using tabs/sections that the user can click through"),e.push("- Be **responsive** \u2014 test that it looks reasonable at both 1200px and 375px widths"),e.push("- Include a small header bar showing: screen name tabs + the design direction summary"),e.push(""),e.push("The wireframe must NOT:"),e.push("- Use any color except grayscale (#333, #666, #999, #ddd, #f8f8f8, white)"),e.push("- Use any external dependencies \u2014 no CDN, no imports, no build step"),e.push("- Look polished \u2014 it should feel like a sketch on a whiteboard, not a finished product"),e.push("- Include decorative elements \u2014 no icons (use text labels), no illustrations, no gradients"),e.push(""),e.push("## Screens to wireframe"),e.push("");for(let a of r.screens){e.push(`### ${a.name} (\`${a.route}\`)`),e.push(`**Purpose**: ${a.purpose}`),e.push(""),e.push("**Information hierarchy** (render in this order, top to bottom):");for(let o of a.informationHierarchy)e.push(`- ${o}`);e.push(""),e.push("**Interaction states** (add HTML comments for non-visible states):");for(let o of a.interactionStates)e.push(`- ${o}`);if(e.push(""),a.entities.length>0){e.push("**Data model and sample content** (use this real data, not lorem ipsum):");for(let o of a.entities)e.push(`
10875
+ `):"",f=P>0?`Source code restored with ${ae}/${R} steps complete. ${P} steps need implementation \u2014 call mist_build with action='implement' to apply your changes.`:`Source code fully restored (${R} steps complete). Configure your .env.local, then deploy with mist_deploy.`;return p(JSON.stringify({status:"success",projectPath:d,projectId:a,planStepsCompleted:ae,planStepsTotal:R,pendingSteps:P,progress:k,nextAction:f+M}))}finally{try{h(x,{recursive:!0,force:!0})}catch{}}}import{z as ua}from"zod";import{existsSync as ha,readFileSync as jr,writeFileSync as ga,mkdirSync as Oi}from"fs";import{join as At,resolve as _i,dirname as ji}from"path";import{createConnection as zi}from"net";import{existsSync as Dt,mkdirSync as Er,readFileSync as Ur,readdirSync as Hr,writeFileSync as Fi}from"fs";import{join as Qe,resolve as Ei}from"path";import{homedir as pa}from"os";function Je(r){return r.entity??r.name??"Unknown"}function Oe(r){return typeof r=="string"?r:r.name}function ca(r){return typeof r=="string"?"text":r.type}function Fr(r){let t=r.fields||[],e=[];for(let a=0;a<3;a++){let o={};for(let i of t)o[Oe(i)]=Ui(Oe(i),ca(i),Je(r),a);e.push(o)}return e}function Ui(r,t,e,a){let o=r.toLowerCase(),i=(t||"text").toLowerCase();return o==="name"||o==="title"?[`${e} Alpha`,`${e} Beta`,`${e} Gamma`][a]:o==="email"?["alice@example.com","bob@example.com","carol@example.com"][a]:o==="status"?["Active","Pending","Completed"][a]:o==="priority"?["High","Medium","Low"][a]:o.includes("date")||i==="date"?["Jan 15, 2024","Feb 20, 2024","Mar 10, 2024"][a]:o.includes("price")||o.includes("amount")||o.includes("cost")?["$29","$49","$99"][a]:o.includes("count")||o.includes("quantity")||i==="number"||i==="integer"?["12","34","56"][a]:i==="boolean"||i==="bool"?["Yes","No","Yes"][a]:o.includes("description")||i==="textarea"?["Brief description here","Another example entry","Third sample item"][a]:[`Sample ${a+1}`][0]}function Hi(r){let t=r.dataModel??[],e=r.pages??[],a=r.design??{},o=e.map(c=>({label:c.name??c.path??"Page",route:c.path??c.route??"/"})),i=[],n=t.slice(0,3).map(c=>({name:Je(c),fields:(c.fields||[]).map(h=>({name:Oe(h),type:ca(h)})),sampleData:Fr(c)})),s=[];r.primaryAction&&(s.push(`PRIMARY: ${r.primaryAction.action} \u2014 this is the first thing the user sees and does`),s.push(`SURFACE: ${r.primaryAction.dashboardSurface}`)),s.push(`METRICS: Key counts for ${t.map(c=>Je(c)).join(", ")}`),s.push("RECENT: Latest activity or items"),i.push({name:"Dashboard",type:"dashboard",route:"/dashboard",purpose:r.primaryAction?`Action surface \u2014 user comes here to ${r.primaryAction.action.toLowerCase()}. Not a stats display.`:`Overview of ${t.map(c=>Je(c)).join(", ")} with key metrics.`,informationHierarchy:s,interactionStates:["Empty state: new user, no data yet \u2014 show onboarding prompt","Loading state: skeleton placeholders for metrics and table","Populated state: real data with metrics, recent items, quick actions"],entities:n});let l=t[0];if(l){let c=Je(l),h=c.toLowerCase().endsWith("s")?c:`${c}s`;i.push({name:`${c} List`,type:"detail",route:`/${h.toLowerCase()}`,purpose:`Browse, search, and manage ${h.toLowerCase()}. Create new ${c.toLowerCase()}s.`,informationHierarchy:[`HEADER: "${h}" title + "Add ${c}" button`,"SEARCH: Filter/search bar \u2014 users will have many items",`TABLE: ${(l.fields||[]).slice(0,5).map(g=>Oe(g)).join(", ")} columns`,"ROW ACTIONS: Edit, delete on each row"],interactionStates:[`Empty state: "No ${h.toLowerCase()} yet" with create CTA`,"Loading state: skeleton table rows",`Search with no results: "No ${h.toLowerCase()} matching..." with clear filter`],entities:[{name:c,fields:(l.fields||[]).map(g=>({name:Oe(g),type:ca(g)})),sampleData:Fr(l)}]})}r.steps.some(c=>{let h=`${c.name??c.title??""} ${c.description??""}`.toLowerCase();return h.includes("landing")||h.includes("hero")||h.includes("marketing")||h.includes("homepage")})&&i.push({name:"Landing Page",type:"landing",route:"/",purpose:`Convince visitors to sign up for ${r.name}. Answer: what is this, who is it for, why should I care.`,informationHierarchy:[`HERO: One sentence about what ${r.name} does \u2014 not "Transform your X", be specific`,"CTA: Sign up / Get started \u2014 one clear action","PROOF: What makes this valuable (features, not buzzwords)","SECONDARY CTA: Repeat the sign up prompt"],interactionStates:["Mobile: hero stacks vertically, nav collapses to hamburger","Desktop: hero side-by-side or centered, full nav"],entities:[]});let u=[];for(let c of t.slice(0,3)){let h=Je(c);(c.fields||[]).find(y=>Oe(y).toLowerCase()==="name"||Oe(y).toLowerCase()==="title")&&u.push(`What if a ${h.toLowerCase()}'s name is 47 characters? Does the layout break?`),u.push(`What if there are 0 ${h.toLowerCase()}s? 1? 500?`)}return r.authModel&&r.authModel!=="none"&&u.push("What does a brand-new user see? (no data, no setup)"),u.push("What if the network is slow? What loads first?"),{appName:r.name,summary:r.summary??"",screens:i,navigation:{style:r.navStyle??"sidebar",items:o},primaryAction:r.primaryAction??null,designDirection:{tone:a.tone??"professional",accentColor:a.accentColor??"blue",navStyle:r.navStyle??"sidebar",fonts:a.fonts??{heading:"Inter",body:"Inter"}},edgeCases:u}}function Gi(r,t){let e=[];e.push(`# Wireframe sketch for ${r.appName}`),e.push(""),e.push(`**${r.appName}** \u2014 ${r.summary}`),e.push(""),t&&(e.push("## Feedback to apply"),e.push(t),e.push("")),e.push("## Design principles"),e.push(""),e.push("Apply these when deciding layout and hierarchy:"),e.push("1. **Information hierarchy** \u2014 What does the user see first, second, third? The primary action is first. Metrics are second. Everything else is supporting."),e.push("2. **Interaction states** \u2014 Every screen has at least: empty, loading, populated. Show the populated state but add HTML comments noting the others."),e.push("3. **Edge case paranoia** \u2014 What if there are 0 items? 500 items? A 47-character name? Think about these and comment where they matter."),e.push(`4. **Subtraction** \u2014 "As little design as possible" (Dieter Rams). Every element earns its pixels. If removing something doesn't hurt, remove it.`),e.push("5. **Design for trust** \u2014 Clear labels, predictable layout, obvious actions. No mystery meat navigation."),e.push(""),e.push("## Wireframe rules (strict)"),e.push(""),e.push("Write a **single self-contained HTML file** saved to `.mistflow/mockups/mockup-{planId}.html` (replace `{planId}` with the actual plan ID)."),e.push(""),e.push("The wireframe must:"),e.push("- Use **system fonts only** (`-apple-system, system-ui, sans-serif`) \u2014 no Google Fonts, no CDN"),e.push("- Use **inline CSS only** \u2014 no external stylesheets, no Tailwind CDN"),e.push("- Look **intentionally rough** \u2014 thin gray borders (#ddd), light backgrounds (#f8f8f8), no color, no shadows"),e.push("- Use **realistic placeholder content** that matches this specific app (sample data provided below) \u2014 NOT lorem ipsum"),e.push("- Include **HTML comments** explaining design decisions (e.g., `<!-- Primary action is prominent because users come here to check in members -->`)"),e.push("- Show **all screens in a single page** using tabs/sections that the user can click through"),e.push("- Be **responsive** \u2014 test that it looks reasonable at both 1200px and 375px widths"),e.push("- Include a small header bar showing: screen name tabs + the design direction summary"),e.push(""),e.push("The wireframe must NOT:"),e.push("- Use any color except grayscale (#333, #666, #999, #ddd, #f8f8f8, white)"),e.push("- Use any external dependencies \u2014 no CDN, no imports, no build step"),e.push("- Look polished \u2014 it should feel like a sketch on a whiteboard, not a finished product"),e.push("- Include decorative elements \u2014 no icons (use text labels), no illustrations, no gradients"),e.push(""),e.push("## Screens to wireframe"),e.push("");for(let a of r.screens){e.push(`### ${a.name} (\`${a.route}\`)`),e.push(`**Purpose**: ${a.purpose}`),e.push(""),e.push("**Information hierarchy** (render in this order, top to bottom):");for(let o of a.informationHierarchy)e.push(`- ${o}`);e.push(""),e.push("**Interaction states** (add HTML comments for non-visible states):");for(let o of a.interactionStates)e.push(`- ${o}`);if(e.push(""),a.entities.length>0){e.push("**Data model and sample content** (use this real data, not lorem ipsum):");for(let o of a.entities)e.push(`
10876
10876
  **${o.name}** \u2014 fields: ${o.fields.map(i=>`${i.name} (${i.type})`).join(", ")}`),e.push("```json"),e.push(JSON.stringify(o.sampleData,null,2)),e.push("```");e.push("")}}e.push("## Navigation"),e.push(`**Style**: ${r.navigation.style} (use this layout)`),e.push("**Items**:");for(let a of r.navigation.items)e.push(`- ${a.label} \u2192 \`${a.route}\``);if(e.push(""),r.primaryAction&&(e.push("## Primary action (this drives the layout)"),e.push(`- **Action**: ${r.primaryAction.action}`),e.push(`- **Flow**: ${r.primaryAction.flow}`),e.push(`- **Dashboard must show**: ${r.primaryAction.dashboardSurface}`),e.push(""),e.push("The dashboard is an ACTION surface. The primary action should be the most prominent thing on the page \u2014 above the metrics, above the recent items. Users came here to DO something, not to look at numbers."),e.push("")),r.edgeCases.length>0){e.push("## Edge cases to consider"),e.push("Add HTML comments in the wireframe where these matter:");for(let a of r.edgeCases)e.push(`- ${a}`);e.push("")}return e.push("## Design direction (DO NOT apply to wireframe \u2014 this is for reference only)"),e.push(`The final app will use: ${r.designDirection.tone} tone, ${r.designDirection.accentColor} accent, ${r.designDirection.navStyle} nav, ${r.designDirection.fonts.heading} / ${r.designDirection.fonts.body} fonts.`),e.push("The wireframe is grayscale and rough. These tokens will be applied during the actual build."),e.push(""),e.push("## After writing the wireframe"),e.push(""),e.push("1. Write the file to `.mistflow/mockups/mockup-{planId}.html` (replace `{planId}` with the actual plan ID)"),e.push("2. Open it: `open .mistflow/mockups/mockup-{planId}.html`"),e.push(`3. Tell the user: "Here's a rough wireframe of your app. This shows the layout and information hierarchy \u2014 not the final visual design (colors, fonts, polish come during the build). Does the layout feel right? Want to move things around, or shall we start building?"`),e.join(`
10877
- `)}function Ur(r){return Qe(la(),".mistflow","mockup-state",`${r}.json`)}function Hi(r){let t=Ur(r);if(!Bt(t))return null;try{return JSON.parse(Fr(t,"utf-8"))}catch{return null}}function oa(r,t){let e=Qe(la(),".mistflow","mockup-state");Nr(e,{recursive:!0}),Li(Ur(r),JSON.stringify(t,null,2))}async function Hr(r){let{planId:t,feedback:e,approved:a}=r,o=Ni(r.projectPath??process.cwd()),i=Qe(la(),".mistflow","plans",`${t}.json`);if(!Bt(i))return p(`Plan not found for planId '${t}'. Call mist_plan to generate a plan first.`,!0);let n;try{n=JSON.parse(Fr(i,"utf-8"))}catch{return p("Failed to read plan file. Call mist_plan again.",!0)}let s=n.plan;if(!s)return p("Plan data is empty. Call mist_plan again.",!0);let l=Hi(t);l||(l={planId:t,iterationCount:0,approved:!1,screens:[],feedback:[]});let d=Qe(o,".mistflow","mockups");if(Nr(d,{recursive:!0}),a){l.approved=!0,oa(t,l);let g=Bt(d)?Er(d).filter(y=>y.endsWith(".html")):[];return p(JSON.stringify({status:"approved",message:`Wireframe approved after ${l.iterationCount} iteration(s). Layout direction is locked in.`,mockupFiles:g.map(y=>`.mistflow/mockups/${y}`),nextAction:`NEXT: Call mist_build with action='init', name='${s.name}', and planId='${t}' to create the project. The wireframe in .mistflow/mockups/ will be used as layout reference during implementation.`}))}e?(l.iterationCount++,l.feedback.push(e)):l.iterationCount++,oa(t,l);let u=Ei(s);l.screens=u.screens.map(g=>g.name),oa(t,l);let c=Ui(u,e??void 0),h;return l.iterationCount>=3&&(h="The wireframe is shaping up \u2014 want to keep refining the layout, or start building?"),p(JSON.stringify({status:"wireframe",iterationCount:l.iterationCount,screens:u.screens.map(g=>({name:g.name,type:g.type,route:g.route})),wireframePrompt:c,designDirection:u.designDirection,...h?{nudge:h}:{},mockupFile:`mockup-${t}.html`,nextAction:e?`Apply the user's feedback to the wireframe. Rewrite .mistflow/mockups/mockup-${t}.html with the changes. Open it in the browser for review. Ask if they want more changes or are ready to build.`:`Generate the wireframe HTML following the wireframePrompt instructions above. Write it to .mistflow/mockups/mockup-${t}.html, then open it in the browser. Ask the user if the layout feels right.`}))}function Gr(r){let t=Qe(r,".mistflow","mockups");return Bt(t)?Er(t).filter(e=>e.endsWith(".html")).map(e=>Qe(t,e)):[]}function ji(r){return new Promise(t=>{let e=_i({port:r,host:"127.0.0.1"});e.on("connect",()=>{e.destroy(),t(!0)}),e.on("error",()=>{t(!1)})})}var $l=da.object({projectPath:da.string().optional().describe("Path to the project directory (default: cwd)"),step:da.number().optional().describe("Specific step number to implement (default: next incomplete step)")});function zi(r){let t=Dt(r,"mistflow.json");if(!ca(t))return null;try{return JSON.parse(Or(t,"utf-8"))}catch{return null}}function Wr(r,t){let e=Dt(r,"mistflow.json");pa(e,JSON.stringify(t,null,2)+`
10878
- `)}function At(r){return r.entity??r.name??"Unknown"}function $i(r){return r.length===0?"":typeof r[0]=="string"?r.join(", "):r.map(t=>`${t.name} (${t.type})`).join(", ")}function _r(r){return r.path??r.route??r.name??""}function Vi(r){let t=(r||"text").toLowerCase();return t==="string"||t==="varchar"||t==="char"?"text":t==="integer"||t==="int"||t==="number"||t==="float"||t==="decimal"||t==="double"?"number":t==="boolean"||t==="bool"?"boolean":t==="date"||t==="datetime"||t==="timestamp"?"date":t==="email"?"email":t==="url"||t==="uri"?"url":t==="enum"||t==="select"?"select":t==="text"||t==="longtext"||t==="textarea"?"textarea":"text"}function jr(r,t){if(!r.entities||r.entities.length===0)return t;let e=r.entities.map(a=>a.toLowerCase());return t.filter(a=>{let o=At(a).toLowerCase();return e.some(i=>o.includes(i)||i.includes(o))})}function qi(r,t){if(!r.pages||r.pages.length===0)return[];let e=r.pages.map(a=>a.toLowerCase());return t.filter(a=>{let o=(a.name??"").toLowerCase(),i=_r(a).toLowerCase();return e.some(n=>o.includes(n)||n.includes(o)||i.includes(n))})}function Ki(r){if(r.integrationId)return"integration";let t=`${r.name} ${r.description}`.toLowerCase();return t.includes("crud")||t.includes("list")&&t.includes("create")?"crud":t.includes("auth")||t.includes("login")||t.includes("register")?"auth":t.includes("admin")&&(t.includes("panel")||t.includes("dashboard")||t.includes("manage")||t.includes("users"))?"admin":t.includes("dashboard")||t.includes("overview")||t.includes("analytics")?"dashboard":t.includes("schema")||t.includes("database")||t.includes("model")?"schema":t.includes("layout")||t.includes("sidebar")||t.includes("navigation")?"layout":t.includes("deploy")||t.includes("cloudflare")?"deploy":t.includes("organization")||t.includes("team")||t.includes("workspace")||t.includes("multi-tenant")||t.includes("invite member")?"multi-tenant":t.includes("landing")||t.includes("hero")||t.includes("marketing")||t.includes("homepage")?"landing":t.includes("design")||t.includes("theme")||t.includes("styling")||t.includes("ui polish")||t.includes("visual")?"design":"general"}function Yi(r,t){let e=[];if(e.push("### Design choices (decided at plan time \u2014 follow these exactly):"),r.tone&&e.push(`- **App tone**: ${r.tone}`),r.fonts&&(e.push(`- **Heading font**: ${r.fonts.heading} (load from Google Fonts)`),e.push(`- **Body font**: ${r.fonts.body} (load from Google Fonts)`)),r.accentColor&&e.push(`- **Accent color**: ${r.accentColor} (use for primary buttons, active states, highlights only)`),r.borderRadius){let a={sharp:"2px",subtle:"6px",rounded:"12px",pill:"9999px"};e.push(`- **Border radius**: ${r.borderRadius} (${a[r.borderRadius]??r.borderRadius}) \u2014 set as --radius in globals.css`)}if(r.shadowStyle){let a={flat:"No shadows \u2014 use borders for separation",subtle:"shadow-sm on cards, shadow on hover",elevated:"shadow-md on cards, shadow-lg on modals, layered depth",dramatic:"shadow-lg with colored tinting, bold depth"};e.push(`- **Shadow style**: ${r.shadowStyle} \u2014 ${a[r.shadowStyle]??r.shadowStyle}`)}if(r.cardStyle){let a={filled:"bg-card with subtle background fill, no visible border",bordered:"border border-border on white/transparent background",elevated:"bg-card shadow-md, no border, lifted appearance",glass:"bg-white/60 backdrop-blur-sm border border-white/20, translucent"};e.push(`- **Card style**: ${r.cardStyle} \u2014 ${a[r.cardStyle]??r.cardStyle}`)}if(r.landingTone&&e.push(`- **Landing page tone**: ${r.landingTone}`),r.visualStrategy){let a=r.visualStrategy,o=t?.hasDesignPreset===!0,i=a.type==="photo"?"hybrid":a.type;if(e.push(""),e.push("### Visual strategy:"),o&&e.push("**Design preset active** \u2014 the hero section visuals come from the design preset blueprint below. Do NOT use Unsplash photos in the hero. Use CSS gradients, animated backgrounds, or the preset's specified visual approach instead."),i==="hybrid"){if(a.heroImages?.length&&!o){e.push("**Hero image available** \u2014 use this Unsplash photo as the landing page hero BACKGROUND:");let n=a.heroImages[0];e.push(`- URL: ${n.url}`),e.push(`- Alt text for img tag: "${n.alt||"Hero image"} \u2014 Photo by ${n.photographer} on Unsplash"`),e.push("- Use as full-bleed background behind the entire hero section with a dark overlay (bg-black/60). The photo is atmosphere, NOT the main visual.")}if(a.sectionImages?.length){e.push("**Section images available** \u2014 use these for feature sections, about sections, or testimonial backgrounds (NOT the hero):");for(let n of a.sectionImages)e.push(`- ${n.url} \u2014 alt: "${n.alt||"section image"} \u2014 Photo by ${n.photographer} on Unsplash"`)}(a.heroImages?.length||a.sectionImages?.length)&&e.push("For image attribution: put photographer credit in the img alt text (already provided above) and add an HTML comment <!-- Images from Unsplash --> near the images. Do NOT add visible attribution text on the page.")}!o&&(i==="product-ui"||i==="hybrid")&&(e.push(""),e.push("**Hero layout \u2014 split hero with product UI mockup (follow this exactly):**"),e.push("The hero must use a split layout with text on the left and a product preview on the right. This is non-negotiable for SaaS/tool apps."),e.push(""),e.push("```"),e.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),e.push("\u2502 [Logo] [Sign In] [CTA \u2192] \u2502 \u2190 transparent nav, NOT sticky"),e.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),e.push("\u2502 \u2502"),e.push("\u2502 \u25CF Built for [audience] \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502"),e.push("\u2502 \u2502 Glassmorphic \u2502 \u2502"),e.push("\u2502 Big bold headline, \u2502 product mockup \u2502 \u2502"),e.push("\u2502 accent color on key word \u2502 card showing \u2502 \u2502"),e.push("\u2502 \u2502 real app data \u2502 \u2502"),e.push("\u2502 Description paragraph \u2502 (stats, table, \u2502 \u2502"),e.push("\u2502 \u2502 chart, etc.) \u2502 \u2502"),e.push("\u2502 [Primary CTA \u2192] [Secondary] \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502"),e.push("\u2502 \u2502"),e.push("\u2502 500+ 25K+ 99% \u2502"),e.push("\u2502 Label Label Label \u2502"),e.push("\u2502 \u2502"),e.push("\u2502 \u2190 full-bleed photo bg + dark overlay behind all \u2192 \u2502"),e.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),e.push("```"),e.push(""),e.push("**Left side (~55%)**: badge pill \u2192 bold headline (use accent color on ONE key word or phrase) \u2192 description \u2192 two CTA buttons (primary filled + secondary outline/ghost with white text) \u2192 stats row with 3 proof points"),e.push("**Right side (~45%)**: A glassmorphic floating card that previews what the app looks like inside:"),e.push("- Style: `bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl shadow-2xl p-6`"),e.push("- Content: Build realistic fake app data using styled divs \u2014 stat cards, mini table rows, schedule blocks, or charts that match what this specific app's dashboard shows"),e.push("- Must be DOMAIN-SPECIFIC: a library app shows book catalog rows, a gym app shows check-in stats, a CRM shows pipeline cards"),e.push("- Add subtle inner elements with `bg-white/5` or `bg-white/10` for depth"),e.push("**Stats row**: 3 proof-point numbers at the bottom of the left side (e.g., '500+ Gyms', '25K+ Members', '99% Uptime'). Use white text, large font for the number, small muted text for the label.")),!o&&i==="product-ui"&&(e.push("**No stock photos** \u2014 this is a product/SaaS app. Use SVG decorative elements instead:"),e.push("- Abstract dot grids, wave dividers between sections, gradient mesh backgrounds"),e.push("- Subtle background patterns using CSS (radial-gradient dots, grid lines)"))}return e.push(""),e.push("Apply these choices to every file you create. Customize the shadcn CSS variables in globals.css to match. Do NOT use default shadcn blue/zinc theme."),e.push(""),e.push("**CRITICAL: shadcn/ui components are already installed. NEVER write components/ui/*.tsx files from scratch.** If you need a shadcn component, run `npx shadcn@latest add <component>` to pull it. The project already includes: button, card, input, label, form, dialog, table, dropdown-menu, badge, separator, skeleton, sheet, tabs, avatar, select, textarea, checkbox, switch, tooltip, popover, sonner."),e.push(""),e.push("**shadcn MCP server is available.** You have the `shadcn` MCP server installed \u2014 use it to browse and search for components, blocks, and landing page sections from the shadcn registry. Before building a landing page section from scratch, check the registry for existing blocks (hero sections, feature grids, pricing tables, testimonials, footers) and customize them instead of reinventing."),e.push(""),e.push("**Project routes (use these exact paths):** Login is at `/login` (NOT /sign-in). Register is at `/register` (NOT /sign-up). All landing page and nav links MUST use `/login` and `/register`."),e.push(""),e.push("### Design quality rules (non-negotiable):"),e.push(""),e.push("**Typography**: Use the plan fonts everywhere. font-heading for headings, font-body for body text."),e.push("- **Modular scale**: Use a 1.25-1.5 ratio between sizes. A 5-level system covers most needs: xs (0.75rem captions), sm (0.875rem metadata), base (1rem body), lg (1.25-1.5rem subheadings), xl+ (2-4rem headlines). Sizes too close together (14/15/16px) create muddy hierarchy."),e.push("- **Vertical rhythm**: Your line-height IS your spacing unit. If body is 16px with line-height 1.5 (= 24px), vertical spacing should be multiples of 24px."),e.push("- **Weight contrast**: Use font-medium/font-semibold contrast, not just size. Bold headings + regular body creates natural hierarchy without extra sizes."),e.push("- **Measure**: Set `max-width: 65ch` on text containers. Long lines kill readability."),e.push("- **OpenType polish**: Use `font-variant-numeric: tabular-nums` on data tables for aligned columns."),e.push("- Never use more than 2 font families. One well-chosen family in multiple weights is cleaner than two competing typefaces."),e.push("- Never fall back to system fonts. Never use decorative fonts for body text. Minimum 16px (1rem) for body."),e.push(""),e.push("**Color**: Build a functional palette, not a rainbow."),e.push("- **Tinted neutrals**: Pure gray is dead. Add a tiny tint toward the accent color to all your grays (borders, backgrounds, muted text). The tint should be subtle enough not to read as 'colored' but creates subconscious cohesion."),e.push("- **60/30/10 rule**: 60% neutral backgrounds/whitespace, 30% secondary colors (text, borders, inactive states), 10% accent (CTAs, highlights, focus). Accent colors work BECAUSE they're rare. Overuse kills their power."),e.push("- **Dangerous combos to avoid**: Light gray text on white (#1 accessibility fail). Gray text on any colored background (looks washed out and dead, use a darker shade of the background color instead). Yellow text on white. Thin light text on images."),e.push("- **Dark mode is not inverted light mode**: Use lighter surfaces for depth (no shadows). Desaturate accents slightly. Never pure black backgrounds, use dark gray (12-18% lightness). Reduce body text weight slightly (350 vs 400) because light-on-dark reads heavier."),e.push("- Never use pure #000 or #fff. Customize the shadcn CSS variables in globals.css to match the plan palette."),e.push(""),e.push("**Layout & space**: Space is a design material, not leftover."),e.push("- **4px base unit**: All spacing should be multiples of 4: 4, 8, 12, 16, 24, 32, 48, 64, 96px. No arbitrary values. Name tokens semantically (`space-sm`, `space-lg`), not by value."),e.push("- **Use `gap` not margins**: Use `gap` for sibling spacing. It eliminates margin collapse hacks and is more predictable. Reserve margin for positioning, not spacing between siblings."),e.push("- **Rhythm through contrast**: Tight groupings within related items (8-12px), generous separation between sections (48-96px). Vary spacing WITHIN sections too. Not every row needs the same gap. The ratio between inner and outer spacing creates hierarchy."),e.push("- **Density matches content**: Data-dense UIs (dashboards, tables, admin panels) need tighter spacing with more information visible. Marketing pages need generous whitespace and breathing room. Match density to what the user is doing."),e.push("- **The squint test**: Blur your eyes. Can you still identify primary, secondary, and clear groupings? If everything looks the same weight, hierarchy is broken. The most important content should be obvious within 2 seconds."),e.push("- **Asymmetry > centering**: Left-aligned with asymmetric layouts feels more designed. Center-alignment is fine for hero sections and CTAs, not for body content."),e.push("- **Flex vs Grid**: Use Flexbox for 1D layouts (rows of items, navbars, button groups, card contents, most component internals). Use Grid for 2D layouts (page-level structure, dashboards, data-dense interfaces where rows AND columns need coordinated control). Don't default to Grid when Flexbox with `flex-wrap` would be simpler. Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Use named `grid-template-areas` for complex page layouts and redefine at breakpoints."),e.push("- **Cards earn their existence**: Only use cards when content needs clear separation, grid comparison, or interaction boundaries. Not everything needs a container. Never nest cards inside cards. Vary card sizes or mix cards with non-card content to break repetition."),e.push("- **Section variety**: Don't make every section the same structure. Alternate layouts (text-left/image-right, then image-left/text-right), vary section heights, mix full-width with contained content. Monotonous section rhythm signals no designer touched this."),e.push("- **Z-index scale**: Use a semantic scale, not arbitrary numbers. dropdown(10) -> sticky(20) -> modal-backdrop(30) -> modal(40) -> toast(50) -> tooltip(60). Never use 999 or 9999."),e.push("- **Touch targets**: Minimum 44x44px for all interactive elements, even if the visual element is smaller (use padding)."),e.push(""),e.push("**Cards & surfaces**: Match the plan's cardStyle. Stat cards need background fills with the accent color as a subtle icon background or tinted left border. Use bg-muted/30 for section differentiation between page areas. Build a consistent shadow scale (sm -> md -> lg) and use elevation to reinforce hierarchy, not as decoration."),e.push(""),e.push("**Sidebar**: App name + icon at top. Nav items with hover:bg-muted rounded-md transition. Active item: bg-primary/10 text-primary font-medium. User email + sign out at bottom. The sidebar should feel like a distinct surface (bg-card or bg-muted/50)."),e.push("**Mobile navigation**: The desktop sidebar must collapse to a hamburger menu on mobile. Use the shadcn `Sheet` component: a hamburger icon button (visible at `md:hidden`) opens a Sheet from the left containing the same nav items. The desktop sidebar is `hidden md:flex`. For simple apps with 3-5 nav items, a bottom tab bar (`fixed bottom-0`) is an alternative. Always respect `env(safe-area-inset-bottom)` for notched phones."),e.push(""),e.push("**Interaction design**: Every interactive element needs 8 states, not just default and hover."),e.push("- **Required states**: default, hover, focus, active/pressed, disabled, loading, error, success. Design all of them intentionally."),e.push("- **Focus rings**: Use `:focus-visible` (not `:focus`) so rings show for keyboard users but not mouse clicks. Never remove focus indicators without replacement."),e.push("- **Button hierarchy**: Don't make every button primary. Use ghost, outline, secondary, and text-link variants. One primary CTA per view."),e.push("- **Progressive disclosure**: Simple first, advanced behind expandable sections. Don't overwhelm with options."),e.push("- **Undo over confirmation**: For destructive actions, remove immediately + show an undo toast, then permanently delete after timeout. Better than confirmation dialogs that users click through blindly."),e.push("- **Form patterns**: Visible `<label>` elements always (placeholders aren't labels, they disappear). Validate on blur, not on keystroke. Show format expectations with examples."),e.push(""),e.push("**Empty states**: Every empty list/table needs a rich empty state. Not just 'Nothing here'. Include: a simple inline SVG illustration (48x48, relevant to the feature), a specific message that teaches ('Create your first habit to start tracking your daily routine'), and a primary action button. Empty states are the first thing new users see. Make them inviting."),e.push(""),e.push('**Loading states**: Use the shadcn `Skeleton` component. Show skeleton versions of cards, lists, and stats during data fetches. Example: `<Skeleton className="h-8 w-32" />` for a stat number, `<Skeleton className="h-20 w-full" />` for a card. Add a `loading.tsx` file in dashboard route groups. Never show a blank page or a lone spinner.'),e.push(""),e.push('**Images**: For Unsplash images on landing pages, use `next/image` with `placeholder="blur"` and a blurDataURL (a tiny 10x6 base64 image \u2014 generate a solid color blur like `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAAP0lEQVQYV2N89+7dfwYGBgZGRkYGBgYmBjIBE7kaPHv27D8DA8N/BgYGRkZGRgYGJgYyARO5GkjWQHEoxRoAAPyTGAGBMpEAAAAASUVORK5CYII=`). This creates a smooth shimmer-in effect instead of images popping in.'),e.push(""),e.push("**Motion** (`motion` package is installed):"),e.push(""),e.push("**Hero moment strategy**: Pick ONE signature animation per page. Not scattered animations on everything. On a landing page, the hero entrance is your hero moment. On a dashboard, a satisfying success animation after the primary action. One great animation > ten mediocre ones. Everything else gets subtle transitions."),e.push(""),e.push("**Animation opportunity audit** -- check these 5 categories:"),e.push("1. **Missing feedback**: Button has no press response? Add `active:scale-[0.97]` (CSS) or `whileTap={{ scale: 0.97 }}` (motion). Toggle has no slide? Animate it."),e.push("2. **Jarring transitions**: Menu appears instantly? Add 200ms fade + slide. Content swaps without transition? Crossfade between states."),e.push("3. **Unclear relationships**: Parent-child not visually connected? Stagger children 50ms after parent. Deleted item just disappears? Animate it out (opacity + height collapse)."),e.push("4. **Lack of delight**: Success state is flat? Draw a checkmark SVG with stroke-dashoffset animation. Empty state is static? Add a gentle float on the illustration."),e.push("5. **Missed guidance**: First-time user gets no hint? Pulse the primary CTA once on page load. New feature unnoticed? Brief highlight animation."),e.push(""),e.push("**Timing hierarchy**: 100-150ms for instant feedback (buttons, toggles). 200-300ms for state changes (menus, tooltips). 300-500ms for layout shifts (accordions, modals). 500-800ms for entrances (page loads). Exit animations run at 75% of entrance duration."),e.push(""),e.push("**Easing**: Use exponential curves (Quart out, Expo out) for sophisticated physics. Never `ease` (generic), never bounce/elastic (dated). Real objects decelerate smoothly. CSS: `cubic-bezier(0.25, 1, 0.5, 1)` (Quart out) or `cubic-bezier(0.16, 1, 0.3, 1)` (Expo out)."),e.push(""),e.push("**Micro-interaction recipes**:"),e.push("- **Button press**: `active:scale-[0.97] transition-transform duration-100` (CSS only, no motion needed)"),e.push("- **Success checkmark**: SVG `<path>` with `stroke-dasharray` + `stroke-dashoffset` animation from full to 0. 400ms Expo out."),e.push("- **Height expand/collapse**: `grid-template-rows: 0fr -> 1fr` with `transition: grid-template-rows 300ms`. No JS layout measurement needed."),e.push("- **Skeleton to content**: Skeleton pulses (CSS `animate-pulse`), then crossfades to real content with `opacity` transition. Use the pre-scaffolded `<ContentLoader>` component."),e.push("- **Staggered list entrance**: CSS `animation-delay` on children: `.animate-fade-up:nth-child(1) { animation-delay: 0ms } :nth-child(2) { animation-delay: 50ms }` etc. Or use `<StaggerList>` component."),e.push("- **Number count-up**: Use `@number-flow/react` for smooth digit morphing on stats/metrics."),e.push(""),e.push("**Performance**: Only animate `transform` and `opacity` (GPU-accelerated). Never animate width, height, top, left, margin, or padding. For scroll-tied effects on landing pages, use CSS `animation-timeline: scroll()` where supported (progressive enhancement)."),e.push(""),e.push("**Accessibility**: Always respect `prefers-reduced-motion`. Wrap animations in `@media (prefers-reduced-motion: no-preference) { ... }`. Provide static alternatives that convey the same information."),e.push(""),e.push("**SSR-safe (CRITICAL)**: This app deploys to Cloudflare Workers where IntersectionObserver may not fire. NEVER use `initial={{ opacity: 0 }}` (content stays invisible forever). Use CSS @keyframes for entrance effects:"),e.push("```tsx"),e.push("// SAFE: CSS @keyframes in globals.css:"),e.push("// @keyframes fade-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }"),e.push("// .animate-fade-up { animation: fade-up 0.5s ease-out both; }"),e.push("// Use motion ONLY for hover/tap/gesture interactions:"),e.push("import { motion } from 'motion/react';"),e.push("<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }}>"),e.push("// NEVER: <motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }}>"),e.push("```"),e.push("For dashboard pages, keep animations minimal. Button press feedback + skeleton loading + success states. No entrance animations on dashboard pages."),e.push(""),e.push("**UX writing**: Every word earns its place."),e.push("- **Buttons**: Use outcome-focused verb + object ('Save changes', 'Create account'), never generic ('Submit', 'OK', 'Click here')."),e.push("- **Destructive actions**: State exact consequences ('Delete 5 items permanently', not 'Delete selected'). Distinguish 'Delete' (permanent) from 'Remove' (recoverable)."),e.push("- **Error messages**: Answer three questions: what happened, why, and how to fix it. 'Email address is not valid. Include an @ symbol.' not 'Invalid input'. Never blame the user."),e.push("- **Empty states**: Explain what will appear here, why it matters, and give a clear next action."),e.push("- **Loading/progress**: Set time expectations. 'Deploying your app (usually takes 30 seconds)' not just a spinner."),e.push("- **Consistency**: Pick one term and stick with it. 'Delete' everywhere, not Delete/Remove/Trash interchangeably."),e.push("- **Link text**: Make it standalone for accessibility. 'View pricing plans' not 'Click here'."),e.push(""),e.push("**Responsive design**: Mobile-first, then enhance."),e.push("- **Mobile-first CSS**: Write base styles for mobile, then use `min-width` media queries to enhance for larger screens. Never start with desktop and try to cram it into mobile."),e.push("- **Input method matters more than screen size**: Use `@media (pointer: fine)` for mouse/trackpad (smaller targets OK, hover effects work), `@media (pointer: coarse)` for touch (44px targets required, no hover-dependent UI). Use `@media (hover: none)` to detect devices that can't hover. Never require hover to reveal functionality."),e.push("- **Safe areas**: Add `<meta name='viewport' content='width=device-width, initial-scale=1, viewport-fit=cover'>` and use `env(safe-area-inset-bottom)` for bottom navigation on notched phones. Fixed bottom bars must respect safe areas."),e.push("- **Self-adjusting grids**: Use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` for card grids. Columns reflow naturally without breakpoint management."),e.push("- **Container queries over media queries**: For reusable components, use `container-type: inline-size` on the parent and `@container (min-width: 400px)` on children. Components adapt to their container, not the viewport."),e.push("- **No horizontal scroll**: Nothing should overflow the viewport width. Test at 320px width (smallest common phone). Use `overflow-x: hidden` on the body as a safety net but fix the root cause."),e.push("- **Touch-friendly spacing**: On mobile, increase padding on list items and nav links. Thumb zone for primary actions is bottom-center of the screen."),e.push("- **Responsive images**: Use `next/image` with `sizes` prop for responsive sizing. For art direction changes, use `<picture>` with `<source media='...'>`. Always set `width` and `height` to prevent layout shift."),e.push(""),e.push("**Cognitive load**: Simpler apps feel better, even with the same features."),e.push("- **Max 4 items per group**: Humans hold 4 items in working memory. Navigation menus: max 5 top-level items. Forms: max 4 fields per section. Dashboards: max 4 metrics visible without scrolling. Pricing: max 3 tiers."),e.push("- **Single focus per view**: Every page has ONE primary task. If there are two equal CTAs, neither is primary. One primary action, everything else is secondary or tertiary."),e.push("- **Sequential decisions**: Don't present all options at once. Multi-step forms beat one long form. Each step should have one decision."),e.push("- **Progressive disclosure**: Show the essential first, reveal complexity on demand. Use expandable sections, tabs, or drill-down navigation. The default view should cover 80% of use cases."),e.push("- **Visual grouping**: Related items must be visually grouped (proximity + shared background or border). Unrelated items must have clear separation. The Gestalt principle of proximity is the easiest way to reduce cognitive load."),e.push("- **Reduce choice anxiety**: For important decisions, provide a recommended/default option. Highlight it visually. 'Most popular' badges on pricing plans reduce decision time."),e.push("- **Keep context visible**: In multi-step flows, show a progress indicator and allow going back. Never make users remember what they entered on a previous screen."),e.push(""),e.push("**Production hardening**: Designs that only work with perfect data aren't production-ready."),e.push("- **Text overflow**: Use `text-overflow: ellipsis` + `overflow: hidden` for single-line text in constrained spaces. Use `-webkit-line-clamp` for multi-line. Add `min-width: 0` on flex items to prevent overflow."),e.push("- **Extreme inputs**: Test with 100+ character names, emoji in text fields, and empty strings. The layout should not break."),e.push("- **Error resilience**: Every API call needs error UI. Network failures, 404s, 500s all need clear messages with recovery options. Never show raw stack traces."),e.push(""),e.push("**Landing page component patterns** (use these for professional, non-generic landing pages):"),e.push("Build these components inline (shadcn + motion + Tailwind is enough):"),e.push("- **Animated number counters**: Use `@number-flow/react` (installed) for stat sections. `<NumberFlow value={count} />` with smooth digit transitions."),e.push("- **Marquee / logo ticker**: CSS-only infinite scroll (`@keyframes marquee { from { transform: translateX(0) } to { transform: translateX(-50%) } }`) with duplicated content. Pause on hover."),e.push("- **Bento grid**: CSS grid with varied `col-span` and `row-span`. Each cell gets a unique visual. Never make all cells the same size."),e.push("- **Animated beam / connection lines**: SVG paths with `motion` stroke-dashoffset animation for 'how it works' sections."),e.push("- **Shimmer borders**: `background: conic-gradient(...)` wrapper with animation for hero CTAs."),e.push("- **Comparison tables**: Sticky-header table with check/x icons and row highlighting on the recommended plan."),e.push("- **Testimonial carousel**: CSS scroll-snap for horizontal cards with avatar, quote, name, role. Auto-scroll paused on hover."),e.push("- **Gradient mesh backgrounds**: Multiple layered `radial-gradient` backgrounds for hero sections."),e.push("Pick 3-5 per landing page based on the app's content. The goal: every landing page should look like it was designed by a human, not generated by AI."),e.push(""),e.push("**Landing page navbar (non-negotiable):** Fully transparent (no background, no border, no backdrop-blur) and NOT sticky/fixed. It sits inside the hero section and scrolls naturally. White text on dark hero backgrounds. Primary CTA button in the nav uses the accent color (solid fill, rounded-md). Do NOT use `sticky`, `fixed`, `top-0`, or `backdrop-blur` on landing page navbars. Dashboard sidebar/nav IS fixed (different pattern)."),e.push(""),e.push("**Design for THIS app, not any app** (the #1 way to avoid generic AI output):"),e.push("- Every layout decision should be informed by what THIS app does. A gym check-in app needs a prominent search bar. A habit tracker needs a daily view. A CRM needs a pipeline. Don't default to the same dashboard-with-stats layout for everything."),e.push("- Write copy that is SPECIFIC to the app's domain. Not 'Manage your data efficiently' but 'Check in members in under 3 seconds'. Every headline, empty state, and CTA should reference what the user actually does in this app."),e.push("- Choose visual emphasis based on the primary action. If the user comes to log a workout, the workout form should be the biggest thing on the dashboard."),e.push("- Use domain-appropriate metaphors. A fitness app uses progress rings. A project tracker uses kanban columns. A recipe app uses cards with photos. Don't use the same card grid for everything."),e.push(""),e.push("**NEVER do these (AI slop anti-patterns)**:"),e.push("The following are telltale signs of AI-generated design. Avoid all of them."),e.push("*Visual patterns*:"),e.push("- Cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds (the AI color palette)"),e.push("- Gradient text on headings or metrics (decorative noise, not meaningful)"),e.push("- Glassmorphism / blur effects used decoratively (only use blur for overlays/modals that need it)"),e.push("- Rounded rectangles with thick colored border on one side (lazy accent, never looks intentional)"),e.push("- Dark mode with glowing colored box-shadows on accent elements"),e.push("- Small rounded-square icon tiles stacked above headings (the icon-tile pattern)"),e.push("- Pure black (#000) backgrounds in dark mode (use tinted dark gray instead)"),e.push("- 3-column icon + title + paragraph feature grid (use asymmetric layouts, bento grids, or varied card sizes)"),e.push("- Nested cards inside cards (visual noise, flatten the hierarchy)"),e.push("- Everything center-aligned (use left-alignment for body content, reserve center for heroes/CTAs)"),e.push("- Monotonous spacing (same gap everywhere signals no designer touched this)"),e.push("*Copy patterns*:"),e.push("- Hero text like 'Transform your X' / 'One Day at a Time' / 'Join thousands' / 'Unlock the power of' (write SPECIFIC copy)"),e.push("- Generic feature descriptions ('Powerful analytics', 'Seamless integration', 'Lightning fast')"),e.push("- The hero metric template (big number, small label, supporting stats, gradient accent)"),e.push("- Repeating information users can already see. Every word must earn its place."),e.push("*Functional patterns*:"),e.push("- Bounce/elastic easing on animations (feels dated, real objects decelerate smoothly, use Quart/Expo out)"),e.push("- Modals for things that could be inline (modals are a last resort, not a default)"),e.push("- Nav links to pages that don't exist. EVERY link in the sidebar/topnav MUST have a corresponding page."),e.push("- FAKE FORMS. NEVER use `setTimeout` or `await new Promise` to simulate API calls. Every form MUST have a real server action (actions.ts with 'use server') that writes to the database. If a form doesn't persist data, the feature is BROKEN."),e.push(""),e.push("**Favicon**: A default SVG favicon exists at app/icon.svg. On the landing page step, update it to match the app. Keep it simple (a single icon/letter on a rounded square, using the accent color)."),e.join(`
10879
- `)}async function Ji(r){try{let t=await Ya("nextjs",r);return{reminders:t.reminders,skill:t.skill}}catch{return{reminders:`### ${r} step
10877
+ `)}function Gr(r){return Qe(pa(),".mistflow","mockup-state",`${r}.json`)}function Wi(r){let t=Gr(r);if(!Dt(t))return null;try{return JSON.parse(Ur(t,"utf-8"))}catch{return null}}function da(r,t){let e=Qe(pa(),".mistflow","mockup-state");Er(e,{recursive:!0}),Fi(Gr(r),JSON.stringify(t,null,2))}async function Wr(r){let{planId:t,feedback:e,approved:a}=r,o=Ei(r.projectPath??process.cwd()),i=Qe(pa(),".mistflow","plans",`${t}.json`);if(!Dt(i))return p(`Plan not found for planId '${t}'. Call mist_plan to generate a plan first.`,!0);let n;try{n=JSON.parse(Ur(i,"utf-8"))}catch{return p("Failed to read plan file. Call mist_plan again.",!0)}let s=n.plan;if(!s)return p("Plan data is empty. Call mist_plan again.",!0);let l=Wi(t);l||(l={planId:t,iterationCount:0,approved:!1,screens:[],feedback:[]});let d=Qe(o,".mistflow","mockups");if(Er(d,{recursive:!0}),a){l.approved=!0,da(t,l);let g=Dt(d)?Hr(d).filter(y=>y.endsWith(".html")):[];return p(JSON.stringify({status:"approved",message:`Wireframe approved after ${l.iterationCount} iteration(s). Layout direction is locked in.`,mockupFiles:g.map(y=>`.mistflow/mockups/${y}`),nextAction:`NEXT: Call mist_build with action='init', name='${s.name}', and planId='${t}' to create the project. The wireframe in .mistflow/mockups/ will be used as layout reference during implementation.`}))}e?(l.iterationCount++,l.feedback.push(e)):l.iterationCount++,da(t,l);let u=Hi(s);l.screens=u.screens.map(g=>g.name),da(t,l);let c=Gi(u,e??void 0),h;return l.iterationCount>=3&&(h="The wireframe is shaping up \u2014 want to keep refining the layout, or start building?"),p(JSON.stringify({status:"wireframe",iterationCount:l.iterationCount,screens:u.screens.map(g=>({name:g.name,type:g.type,route:g.route})),wireframePrompt:c,designDirection:u.designDirection,...h?{nudge:h}:{},mockupFile:`mockup-${t}.html`,nextAction:e?`Apply the user's feedback to the wireframe. Rewrite .mistflow/mockups/mockup-${t}.html with the changes. Open it in the browser for review. Ask if they want more changes or are ready to build.`:`Generate the wireframe HTML following the wireframePrompt instructions above. Write it to .mistflow/mockups/mockup-${t}.html, then open it in the browser. Ask the user if the layout feels right.`}))}function Or(r){let t=Qe(r,".mistflow","mockups");return Dt(t)?Hr(t).filter(e=>e.endsWith(".html")).map(e=>Qe(t,e)):[]}function $i(r){return new Promise(t=>{let e=zi({port:r,host:"127.0.0.1"});e.on("connect",()=>{e.destroy(),t(!0)}),e.on("error",()=>{t(!1)})})}var Kl=ua.object({projectPath:ua.string().optional().describe("Path to the project directory (default: cwd)"),step:ua.number().optional().describe("Specific step number to implement (default: next incomplete step)")});function Vi(r){let t=At(r,"mistflow.json");if(!ha(t))return null;try{return JSON.parse(jr(t,"utf-8"))}catch{return null}}function _r(r,t){let e=At(r,"mistflow.json");ga(e,JSON.stringify(t,null,2)+`
10878
+ `)}function It(r){return r.entity??r.name??"Unknown"}function qi(r){return r.length===0?"":typeof r[0]=="string"?r.join(", "):r.map(t=>`${t.name} (${t.type})`).join(", ")}function zr(r){return r.path??r.route??r.name??""}function Ki(r){let t=(r||"text").toLowerCase();return t==="string"||t==="varchar"||t==="char"?"text":t==="integer"||t==="int"||t==="number"||t==="float"||t==="decimal"||t==="double"?"number":t==="boolean"||t==="bool"?"boolean":t==="date"||t==="datetime"||t==="timestamp"?"date":t==="email"?"email":t==="url"||t==="uri"?"url":t==="enum"||t==="select"?"select":t==="text"||t==="longtext"||t==="textarea"?"textarea":"text"}function $r(r,t){if(!r.entities||r.entities.length===0)return t;let e=r.entities.map(a=>a.toLowerCase());return t.filter(a=>{let o=It(a).toLowerCase();return e.some(i=>o.includes(i)||i.includes(o))})}function Yi(r,t){if(!r.pages||r.pages.length===0)return[];let e=r.pages.map(a=>a.toLowerCase());return t.filter(a=>{let o=(a.name??"").toLowerCase(),i=zr(a).toLowerCase();return e.some(n=>o.includes(n)||n.includes(o)||i.includes(n))})}function Ji(r){if(r.integrationId)return"integration";let t=`${r.name} ${r.description}`.toLowerCase();return t.includes("crud")||t.includes("list")&&t.includes("create")?"crud":t.includes("auth")||t.includes("login")||t.includes("register")?"auth":t.includes("admin")&&(t.includes("panel")||t.includes("dashboard")||t.includes("manage")||t.includes("users"))?"admin":t.includes("dashboard")||t.includes("overview")||t.includes("analytics")?"dashboard":t.includes("schema")||t.includes("database")||t.includes("model")?"schema":t.includes("layout")||t.includes("sidebar")||t.includes("navigation")?"layout":t.includes("deploy")||t.includes("cloudflare")?"deploy":t.includes("organization")||t.includes("team")||t.includes("workspace")||t.includes("multi-tenant")||t.includes("invite member")?"multi-tenant":t.includes("landing")||t.includes("hero")||t.includes("marketing")||t.includes("homepage")?"landing":t.includes("design")||t.includes("theme")||t.includes("styling")||t.includes("ui polish")||t.includes("visual")?"design":"general"}function Qi(r,t){let e=[];if(e.push("### Design choices (decided at plan time \u2014 follow these exactly):"),r.tone&&e.push(`- **App tone**: ${r.tone}`),r.fonts&&(e.push(`- **Heading font**: ${r.fonts.heading} (load from Google Fonts)`),e.push(`- **Body font**: ${r.fonts.body} (load from Google Fonts)`)),r.accentColor&&e.push(`- **Accent color**: ${r.accentColor} (use for primary buttons, active states, highlights only)`),r.borderRadius){let a={sharp:"2px",subtle:"6px",rounded:"12px",pill:"9999px"};e.push(`- **Border radius**: ${r.borderRadius} (${a[r.borderRadius]??r.borderRadius}) \u2014 set as --radius in globals.css`)}if(r.shadowStyle){let a={flat:"No shadows \u2014 use borders for separation",subtle:"shadow-sm on cards, shadow on hover",elevated:"shadow-md on cards, shadow-lg on modals, layered depth",dramatic:"shadow-lg with colored tinting, bold depth"};e.push(`- **Shadow style**: ${r.shadowStyle} \u2014 ${a[r.shadowStyle]??r.shadowStyle}`)}if(r.cardStyle){let a={filled:"bg-card with subtle background fill, no visible border",bordered:"border border-border on white/transparent background",elevated:"bg-card shadow-md, no border, lifted appearance",glass:"bg-white/60 backdrop-blur-sm border border-white/20, translucent"};e.push(`- **Card style**: ${r.cardStyle} \u2014 ${a[r.cardStyle]??r.cardStyle}`)}if(r.landingTone&&e.push(`- **Landing page tone**: ${r.landingTone}`),r.visualStrategy){let a=r.visualStrategy,o=t?.hasDesignPreset===!0,i=a.type==="photo"?"hybrid":a.type;if(e.push(""),e.push("### Visual strategy:"),o&&e.push("**Design preset active** \u2014 the hero section visuals come from the design preset blueprint below. Do NOT use Unsplash photos in the hero. Use CSS gradients, animated backgrounds, or the preset's specified visual approach instead."),i==="hybrid"){if(a.heroImages?.length&&!o){e.push("**Hero image available** \u2014 use this Unsplash photo as the landing page hero BACKGROUND:");let n=a.heroImages[0];e.push(`- URL: ${n.url}`),e.push(`- Alt text for img tag: "${n.alt||"Hero image"} \u2014 Photo by ${n.photographer} on Unsplash"`),e.push("- Use as full-bleed background behind the entire hero section with a dark overlay (bg-black/60). The photo is atmosphere, NOT the main visual.")}if(a.sectionImages?.length){e.push("**Section images available** \u2014 use these for feature sections, about sections, or testimonial backgrounds (NOT the hero):");for(let n of a.sectionImages)e.push(`- ${n.url} \u2014 alt: "${n.alt||"section image"} \u2014 Photo by ${n.photographer} on Unsplash"`)}(a.heroImages?.length||a.sectionImages?.length)&&e.push("For image attribution: put photographer credit in the img alt text (already provided above) and add an HTML comment <!-- Images from Unsplash --> near the images. Do NOT add visible attribution text on the page.")}!o&&(i==="product-ui"||i==="hybrid")&&(e.push(""),e.push("**Hero layout \u2014 split hero with product UI mockup (follow this exactly):**"),e.push("The hero must use a split layout with text on the left and a product preview on the right. This is non-negotiable for SaaS/tool apps."),e.push(""),e.push("```"),e.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"),e.push("\u2502 [Logo] [Sign In] [CTA \u2192] \u2502 \u2190 transparent nav, NOT sticky"),e.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524"),e.push("\u2502 \u2502"),e.push("\u2502 \u25CF Built for [audience] \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502"),e.push("\u2502 \u2502 Glassmorphic \u2502 \u2502"),e.push("\u2502 Big bold headline, \u2502 product mockup \u2502 \u2502"),e.push("\u2502 accent color on key word \u2502 card showing \u2502 \u2502"),e.push("\u2502 \u2502 real app data \u2502 \u2502"),e.push("\u2502 Description paragraph \u2502 (stats, table, \u2502 \u2502"),e.push("\u2502 \u2502 chart, etc.) \u2502 \u2502"),e.push("\u2502 [Primary CTA \u2192] [Secondary] \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502"),e.push("\u2502 \u2502"),e.push("\u2502 500+ 25K+ 99% \u2502"),e.push("\u2502 Label Label Label \u2502"),e.push("\u2502 \u2502"),e.push("\u2502 \u2190 full-bleed photo bg + dark overlay behind all \u2192 \u2502"),e.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"),e.push("```"),e.push(""),e.push("**Left side (~55%)**: badge pill \u2192 bold headline (use accent color on ONE key word or phrase) \u2192 description \u2192 two CTA buttons (primary filled + secondary outline/ghost with white text) \u2192 stats row with 3 proof points"),e.push("**Right side (~45%)**: A glassmorphic floating card that previews what the app looks like inside:"),e.push("- Style: `bg-white/10 backdrop-blur-lg border border-white/20 rounded-2xl shadow-2xl p-6`"),e.push("- Content: Build realistic fake app data using styled divs \u2014 stat cards, mini table rows, schedule blocks, or charts that match what this specific app's dashboard shows"),e.push("- Must be DOMAIN-SPECIFIC: a library app shows book catalog rows, a gym app shows check-in stats, a CRM shows pipeline cards"),e.push("- Add subtle inner elements with `bg-white/5` or `bg-white/10` for depth"),e.push("**Stats row**: 3 proof-point numbers at the bottom of the left side (e.g., '500+ Gyms', '25K+ Members', '99% Uptime'). Use white text, large font for the number, small muted text for the label.")),!o&&i==="product-ui"&&(e.push("**No stock photos** \u2014 this is a product/SaaS app. Use SVG decorative elements instead:"),e.push("- Abstract dot grids, wave dividers between sections, gradient mesh backgrounds"),e.push("- Subtle background patterns using CSS (radial-gradient dots, grid lines)"))}return e.push(""),e.push("Apply these choices to every file you create. Customize the shadcn CSS variables in globals.css to match. Do NOT use default shadcn blue/zinc theme."),e.push(""),e.push("**CRITICAL: shadcn/ui components are already installed. NEVER write components/ui/*.tsx files from scratch.** If you need a shadcn component, run `npx shadcn@latest add <component>` to pull it. The project already includes: button, card, input, label, form, dialog, table, dropdown-menu, badge, separator, skeleton, sheet, tabs, avatar, select, textarea, checkbox, switch, tooltip, popover, sonner."),e.push(""),e.push("**shadcn MCP server is available.** You have the `shadcn` MCP server installed \u2014 use it to browse and search for components, blocks, and landing page sections from the shadcn registry. Before building a landing page section from scratch, check the registry for existing blocks (hero sections, feature grids, pricing tables, testimonials, footers) and customize them instead of reinventing."),e.push(""),e.push("**Project routes (use these exact paths):** Login is at `/login` (NOT /sign-in). Register is at `/register` (NOT /sign-up). All landing page and nav links MUST use `/login` and `/register`."),e.push(""),e.push("### Design quality rules (non-negotiable):"),e.push(""),e.push("**Typography**: Use the plan fonts everywhere. font-heading for headings, font-body for body text."),e.push("- **Modular scale**: Use a 1.25-1.5 ratio between sizes. A 5-level system covers most needs: xs (0.75rem captions), sm (0.875rem metadata), base (1rem body), lg (1.25-1.5rem subheadings), xl+ (2-4rem headlines). Sizes too close together (14/15/16px) create muddy hierarchy."),e.push("- **Vertical rhythm**: Your line-height IS your spacing unit. If body is 16px with line-height 1.5 (= 24px), vertical spacing should be multiples of 24px."),e.push("- **Weight contrast**: Use font-medium/font-semibold contrast, not just size. Bold headings + regular body creates natural hierarchy without extra sizes."),e.push("- **Measure**: Set `max-width: 65ch` on text containers. Long lines kill readability."),e.push("- **OpenType polish**: Use `font-variant-numeric: tabular-nums` on data tables for aligned columns."),e.push("- Never use more than 2 font families. One well-chosen family in multiple weights is cleaner than two competing typefaces."),e.push("- Never fall back to system fonts. Never use decorative fonts for body text. Minimum 16px (1rem) for body."),e.push(""),e.push("**Color**: Build a functional palette, not a rainbow."),e.push("- **Tinted neutrals**: Pure gray is dead. Add a tiny tint toward the accent color to all your grays (borders, backgrounds, muted text). The tint should be subtle enough not to read as 'colored' but creates subconscious cohesion."),e.push("- **60/30/10 rule**: 60% neutral backgrounds/whitespace, 30% secondary colors (text, borders, inactive states), 10% accent (CTAs, highlights, focus). Accent colors work BECAUSE they're rare. Overuse kills their power."),e.push("- **Dangerous combos to avoid**: Light gray text on white (#1 accessibility fail). Gray text on any colored background (looks washed out and dead, use a darker shade of the background color instead). Yellow text on white. Thin light text on images."),e.push("- **Dark mode is not inverted light mode**: Use lighter surfaces for depth (no shadows). Desaturate accents slightly. Never pure black backgrounds, use dark gray (12-18% lightness). Reduce body text weight slightly (350 vs 400) because light-on-dark reads heavier."),e.push("- Never use pure #000 or #fff. Customize the shadcn CSS variables in globals.css to match the plan palette."),e.push(""),e.push("**Layout & space**: Space is a design material, not leftover."),e.push("- **4px base unit**: All spacing should be multiples of 4: 4, 8, 12, 16, 24, 32, 48, 64, 96px. No arbitrary values. Name tokens semantically (`space-sm`, `space-lg`), not by value."),e.push("- **Use `gap` not margins**: Use `gap` for sibling spacing. It eliminates margin collapse hacks and is more predictable. Reserve margin for positioning, not spacing between siblings."),e.push("- **Rhythm through contrast**: Tight groupings within related items (8-12px), generous separation between sections (48-96px). Vary spacing WITHIN sections too. Not every row needs the same gap. The ratio between inner and outer spacing creates hierarchy."),e.push("- **Density matches content**: Data-dense UIs (dashboards, tables, admin panels) need tighter spacing with more information visible. Marketing pages need generous whitespace and breathing room. Match density to what the user is doing."),e.push("- **The squint test**: Blur your eyes. Can you still identify primary, secondary, and clear groupings? If everything looks the same weight, hierarchy is broken. The most important content should be obvious within 2 seconds."),e.push("- **Asymmetry > centering**: Left-aligned with asymmetric layouts feels more designed. Center-alignment is fine for hero sections and CTAs, not for body content."),e.push("- **Flex vs Grid**: Use Flexbox for 1D layouts (rows of items, navbars, button groups, card contents, most component internals). Use Grid for 2D layouts (page-level structure, dashboards, data-dense interfaces where rows AND columns need coordinated control). Don't default to Grid when Flexbox with `flex-wrap` would be simpler. Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints. Use named `grid-template-areas` for complex page layouts and redefine at breakpoints."),e.push("- **Cards earn their existence**: Only use cards when content needs clear separation, grid comparison, or interaction boundaries. Not everything needs a container. Never nest cards inside cards. Vary card sizes or mix cards with non-card content to break repetition."),e.push("- **Section variety**: Don't make every section the same structure. Alternate layouts (text-left/image-right, then image-left/text-right), vary section heights, mix full-width with contained content. Monotonous section rhythm signals no designer touched this."),e.push("- **Z-index scale**: Use a semantic scale, not arbitrary numbers. dropdown(10) -> sticky(20) -> modal-backdrop(30) -> modal(40) -> toast(50) -> tooltip(60). Never use 999 or 9999."),e.push("- **Touch targets**: Minimum 44x44px for all interactive elements, even if the visual element is smaller (use padding)."),e.push(""),e.push("**Cards & surfaces**: Match the plan's cardStyle. Stat cards need background fills with the accent color as a subtle icon background or tinted left border. Use bg-muted/30 for section differentiation between page areas. Build a consistent shadow scale (sm -> md -> lg) and use elevation to reinforce hierarchy, not as decoration."),e.push(""),e.push("**Sidebar**: App name + icon at top. Nav items with hover:bg-muted rounded-md transition. Active item: bg-primary/10 text-primary font-medium. User email + sign out at bottom. The sidebar should feel like a distinct surface (bg-card or bg-muted/50)."),e.push("**Mobile navigation**: The desktop sidebar must collapse to a hamburger menu on mobile. Use the shadcn `Sheet` component: a hamburger icon button (visible at `md:hidden`) opens a Sheet from the left containing the same nav items. The desktop sidebar is `hidden md:flex`. For simple apps with 3-5 nav items, a bottom tab bar (`fixed bottom-0`) is an alternative. Always respect `env(safe-area-inset-bottom)` for notched phones."),e.push(""),e.push("**Interaction design**: Every interactive element needs 8 states, not just default and hover."),e.push("- **Required states**: default, hover, focus, active/pressed, disabled, loading, error, success. Design all of them intentionally."),e.push("- **Focus rings**: Use `:focus-visible` (not `:focus`) so rings show for keyboard users but not mouse clicks. Never remove focus indicators without replacement."),e.push("- **Button hierarchy**: Don't make every button primary. Use ghost, outline, secondary, and text-link variants. One primary CTA per view."),e.push("- **Progressive disclosure**: Simple first, advanced behind expandable sections. Don't overwhelm with options."),e.push("- **Undo over confirmation**: For destructive actions, remove immediately + show an undo toast, then permanently delete after timeout. Better than confirmation dialogs that users click through blindly."),e.push("- **Form patterns**: Visible `<label>` elements always (placeholders aren't labels, they disappear). Validate on blur, not on keystroke. Show format expectations with examples."),e.push(""),e.push("**Empty states**: Every empty list/table needs a rich empty state. Not just 'Nothing here'. Include: a simple inline SVG illustration (48x48, relevant to the feature), a specific message that teaches ('Create your first habit to start tracking your daily routine'), and a primary action button. Empty states are the first thing new users see. Make them inviting."),e.push(""),e.push('**Loading states**: Use the shadcn `Skeleton` component. Show skeleton versions of cards, lists, and stats during data fetches. Example: `<Skeleton className="h-8 w-32" />` for a stat number, `<Skeleton className="h-20 w-full" />` for a card. Add a `loading.tsx` file in dashboard route groups. Never show a blank page or a lone spinner.'),e.push(""),e.push('**Images**: For Unsplash images on landing pages, use `next/image` with `placeholder="blur"` and a blurDataURL (a tiny 10x6 base64 image \u2014 generate a solid color blur like `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAAP0lEQVQYV2N89+7dfwYGBgZGRkYGBgYmBjIBE7kaPHv27D8DA8N/BgYGRkZGRgYGJgYyARO5GkjWQHEoxRoAAPyTGAGBMpEAAAAASUVORK5CYII=`). This creates a smooth shimmer-in effect instead of images popping in.'),e.push(""),e.push("**Motion** (`motion` package is installed):"),e.push(""),e.push("**Hero moment strategy**: Pick ONE signature animation per page. Not scattered animations on everything. On a landing page, the hero entrance is your hero moment. On a dashboard, a satisfying success animation after the primary action. One great animation > ten mediocre ones. Everything else gets subtle transitions."),e.push(""),e.push("**Animation opportunity audit** -- check these 5 categories:"),e.push("1. **Missing feedback**: Button has no press response? Add `active:scale-[0.97]` (CSS) or `whileTap={{ scale: 0.97 }}` (motion). Toggle has no slide? Animate it."),e.push("2. **Jarring transitions**: Menu appears instantly? Add 200ms fade + slide. Content swaps without transition? Crossfade between states."),e.push("3. **Unclear relationships**: Parent-child not visually connected? Stagger children 50ms after parent. Deleted item just disappears? Animate it out (opacity + height collapse)."),e.push("4. **Lack of delight**: Success state is flat? Draw a checkmark SVG with stroke-dashoffset animation. Empty state is static? Add a gentle float on the illustration."),e.push("5. **Missed guidance**: First-time user gets no hint? Pulse the primary CTA once on page load. New feature unnoticed? Brief highlight animation."),e.push(""),e.push("**Timing hierarchy**: 100-150ms for instant feedback (buttons, toggles). 200-300ms for state changes (menus, tooltips). 300-500ms for layout shifts (accordions, modals). 500-800ms for entrances (page loads). Exit animations run at 75% of entrance duration."),e.push(""),e.push("**Easing**: Use exponential curves (Quart out, Expo out) for sophisticated physics. Never `ease` (generic), never bounce/elastic (dated). Real objects decelerate smoothly. CSS: `cubic-bezier(0.25, 1, 0.5, 1)` (Quart out) or `cubic-bezier(0.16, 1, 0.3, 1)` (Expo out)."),e.push(""),e.push("**Micro-interaction recipes**:"),e.push("- **Button press**: `active:scale-[0.97] transition-transform duration-100` (CSS only, no motion needed)"),e.push("- **Success checkmark**: SVG `<path>` with `stroke-dasharray` + `stroke-dashoffset` animation from full to 0. 400ms Expo out."),e.push("- **Height expand/collapse**: `grid-template-rows: 0fr -> 1fr` with `transition: grid-template-rows 300ms`. No JS layout measurement needed."),e.push("- **Skeleton to content**: Skeleton pulses (CSS `animate-pulse`), then crossfades to real content with `opacity` transition. Use the pre-scaffolded `<ContentLoader>` component."),e.push("- **Staggered list entrance**: CSS `animation-delay` on children: `.animate-fade-up:nth-child(1) { animation-delay: 0ms } :nth-child(2) { animation-delay: 50ms }` etc. Or use `<StaggerList>` component."),e.push("- **Number count-up**: Use `@number-flow/react` for smooth digit morphing on stats/metrics."),e.push(""),e.push("**Performance**: Only animate `transform` and `opacity` (GPU-accelerated). Never animate width, height, top, left, margin, or padding. For scroll-tied effects on landing pages, use CSS `animation-timeline: scroll()` where supported (progressive enhancement)."),e.push(""),e.push("**Accessibility**: Always respect `prefers-reduced-motion`. Wrap animations in `@media (prefers-reduced-motion: no-preference) { ... }`. Provide static alternatives that convey the same information."),e.push(""),e.push("**SSR-safe (CRITICAL)**: This app deploys to Cloudflare Workers where IntersectionObserver may not fire. NEVER use `initial={{ opacity: 0 }}` (content stays invisible forever). Use CSS @keyframes for entrance effects:"),e.push("```tsx"),e.push("// SAFE: CSS @keyframes in globals.css:"),e.push("// @keyframes fade-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }"),e.push("// .animate-fade-up { animation: fade-up 0.5s ease-out both; }"),e.push("// Use motion ONLY for hover/tap/gesture interactions:"),e.push("import { motion } from 'motion/react';"),e.push("<motion.div whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.97 }} transition={{ duration: 0.15 }}>"),e.push("// NEVER: <motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }}>"),e.push("```"),e.push("For dashboard pages, keep animations minimal. Button press feedback + skeleton loading + success states. No entrance animations on dashboard pages."),e.push(""),e.push("**UX writing**: Every word earns its place."),e.push("- **Buttons**: Use outcome-focused verb + object ('Save changes', 'Create account'), never generic ('Submit', 'OK', 'Click here')."),e.push("- **Destructive actions**: State exact consequences ('Delete 5 items permanently', not 'Delete selected'). Distinguish 'Delete' (permanent) from 'Remove' (recoverable)."),e.push("- **Error messages**: Answer three questions: what happened, why, and how to fix it. 'Email address is not valid. Include an @ symbol.' not 'Invalid input'. Never blame the user."),e.push("- **Empty states**: Explain what will appear here, why it matters, and give a clear next action."),e.push("- **Loading/progress**: Set time expectations. 'Deploying your app (usually takes 30 seconds)' not just a spinner."),e.push("- **Consistency**: Pick one term and stick with it. 'Delete' everywhere, not Delete/Remove/Trash interchangeably."),e.push("- **Link text**: Make it standalone for accessibility. 'View pricing plans' not 'Click here'."),e.push(""),e.push("**Responsive design**: Mobile-first, then enhance."),e.push("- **Mobile-first CSS**: Write base styles for mobile, then use `min-width` media queries to enhance for larger screens. Never start with desktop and try to cram it into mobile."),e.push("- **Input method matters more than screen size**: Use `@media (pointer: fine)` for mouse/trackpad (smaller targets OK, hover effects work), `@media (pointer: coarse)` for touch (44px targets required, no hover-dependent UI). Use `@media (hover: none)` to detect devices that can't hover. Never require hover to reveal functionality."),e.push("- **Safe areas**: Add `<meta name='viewport' content='width=device-width, initial-scale=1, viewport-fit=cover'>` and use `env(safe-area-inset-bottom)` for bottom navigation on notched phones. Fixed bottom bars must respect safe areas."),e.push("- **Self-adjusting grids**: Use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` for card grids. Columns reflow naturally without breakpoint management."),e.push("- **Container queries over media queries**: For reusable components, use `container-type: inline-size` on the parent and `@container (min-width: 400px)` on children. Components adapt to their container, not the viewport."),e.push("- **No horizontal scroll**: Nothing should overflow the viewport width. Test at 320px width (smallest common phone). Use `overflow-x: hidden` on the body as a safety net but fix the root cause."),e.push("- **Touch-friendly spacing**: On mobile, increase padding on list items and nav links. Thumb zone for primary actions is bottom-center of the screen."),e.push("- **Responsive images**: Use `next/image` with `sizes` prop for responsive sizing. For art direction changes, use `<picture>` with `<source media='...'>`. Always set `width` and `height` to prevent layout shift."),e.push(""),e.push("**Cognitive load**: Simpler apps feel better, even with the same features."),e.push("- **Max 4 items per group**: Humans hold 4 items in working memory. Navigation menus: max 5 top-level items. Forms: max 4 fields per section. Dashboards: max 4 metrics visible without scrolling. Pricing: max 3 tiers."),e.push("- **Single focus per view**: Every page has ONE primary task. If there are two equal CTAs, neither is primary. One primary action, everything else is secondary or tertiary."),e.push("- **Sequential decisions**: Don't present all options at once. Multi-step forms beat one long form. Each step should have one decision."),e.push("- **Progressive disclosure**: Show the essential first, reveal complexity on demand. Use expandable sections, tabs, or drill-down navigation. The default view should cover 80% of use cases."),e.push("- **Visual grouping**: Related items must be visually grouped (proximity + shared background or border). Unrelated items must have clear separation. The Gestalt principle of proximity is the easiest way to reduce cognitive load."),e.push("- **Reduce choice anxiety**: For important decisions, provide a recommended/default option. Highlight it visually. 'Most popular' badges on pricing plans reduce decision time."),e.push("- **Keep context visible**: In multi-step flows, show a progress indicator and allow going back. Never make users remember what they entered on a previous screen."),e.push(""),e.push("**Production hardening**: Designs that only work with perfect data aren't production-ready."),e.push("- **Text overflow**: Use `text-overflow: ellipsis` + `overflow: hidden` for single-line text in constrained spaces. Use `-webkit-line-clamp` for multi-line. Add `min-width: 0` on flex items to prevent overflow."),e.push("- **Extreme inputs**: Test with 100+ character names, emoji in text fields, and empty strings. The layout should not break."),e.push("- **Error resilience**: Every API call needs error UI. Network failures, 404s, 500s all need clear messages with recovery options. Never show raw stack traces."),e.push(""),e.push("**Landing page component patterns** (use these for professional, non-generic landing pages):"),e.push("Build these components inline (shadcn + motion + Tailwind is enough):"),e.push("- **Animated number counters**: Use `@number-flow/react` (installed) for stat sections. `<NumberFlow value={count} />` with smooth digit transitions."),e.push("- **Marquee / logo ticker**: CSS-only infinite scroll (`@keyframes marquee { from { transform: translateX(0) } to { transform: translateX(-50%) } }`) with duplicated content. Pause on hover."),e.push("- **Bento grid**: CSS grid with varied `col-span` and `row-span`. Each cell gets a unique visual. Never make all cells the same size."),e.push("- **Animated beam / connection lines**: SVG paths with `motion` stroke-dashoffset animation for 'how it works' sections."),e.push("- **Shimmer borders**: `background: conic-gradient(...)` wrapper with animation for hero CTAs."),e.push("- **Comparison tables**: Sticky-header table with check/x icons and row highlighting on the recommended plan."),e.push("- **Testimonial carousel**: CSS scroll-snap for horizontal cards with avatar, quote, name, role. Auto-scroll paused on hover."),e.push("- **Gradient mesh backgrounds**: Multiple layered `radial-gradient` backgrounds for hero sections."),e.push("Pick 3-5 per landing page based on the app's content. The goal: every landing page should look like it was designed by a human, not generated by AI."),e.push(""),e.push("**Landing page navbar (non-negotiable):** Fully transparent (no background, no border, no backdrop-blur) and NOT sticky/fixed. It sits inside the hero section and scrolls naturally. White text on dark hero backgrounds. Primary CTA button in the nav uses the accent color (solid fill, rounded-md). Do NOT use `sticky`, `fixed`, `top-0`, or `backdrop-blur` on landing page navbars. Dashboard sidebar/nav IS fixed (different pattern)."),e.push(""),e.push("**Design for THIS app, not any app** (the #1 way to avoid generic AI output):"),e.push("- Every layout decision should be informed by what THIS app does. A gym check-in app needs a prominent search bar. A habit tracker needs a daily view. A CRM needs a pipeline. Don't default to the same dashboard-with-stats layout for everything."),e.push("- Write copy that is SPECIFIC to the app's domain. Not 'Manage your data efficiently' but 'Check in members in under 3 seconds'. Every headline, empty state, and CTA should reference what the user actually does in this app."),e.push("- Choose visual emphasis based on the primary action. If the user comes to log a workout, the workout form should be the biggest thing on the dashboard."),e.push("- Use domain-appropriate metaphors. A fitness app uses progress rings. A project tracker uses kanban columns. A recipe app uses cards with photos. Don't use the same card grid for everything."),e.push(""),e.push("**NEVER do these (AI slop anti-patterns)**:"),e.push("The following are telltale signs of AI-generated design. Avoid all of them."),e.push("*Visual patterns*:"),e.push("- Cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds (the AI color palette)"),e.push("- Gradient text on headings or metrics (decorative noise, not meaningful)"),e.push("- Glassmorphism / blur effects used decoratively (only use blur for overlays/modals that need it)"),e.push("- Rounded rectangles with thick colored border on one side (lazy accent, never looks intentional)"),e.push("- Dark mode with glowing colored box-shadows on accent elements"),e.push("- Small rounded-square icon tiles stacked above headings (the icon-tile pattern)"),e.push("- Pure black (#000) backgrounds in dark mode (use tinted dark gray instead)"),e.push("- 3-column icon + title + paragraph feature grid (use asymmetric layouts, bento grids, or varied card sizes)"),e.push("- Nested cards inside cards (visual noise, flatten the hierarchy)"),e.push("- Everything center-aligned (use left-alignment for body content, reserve center for heroes/CTAs)"),e.push("- Monotonous spacing (same gap everywhere signals no designer touched this)"),e.push("*Copy patterns*:"),e.push("- Hero text like 'Transform your X' / 'One Day at a Time' / 'Join thousands' / 'Unlock the power of' (write SPECIFIC copy)"),e.push("- Generic feature descriptions ('Powerful analytics', 'Seamless integration', 'Lightning fast')"),e.push("- The hero metric template (big number, small label, supporting stats, gradient accent)"),e.push("- Repeating information users can already see. Every word must earn its place."),e.push("*Functional patterns*:"),e.push("- Bounce/elastic easing on animations (feels dated, real objects decelerate smoothly, use Quart/Expo out)"),e.push("- Modals for things that could be inline (modals are a last resort, not a default)"),e.push("- Nav links to pages that don't exist. EVERY link in the sidebar/topnav MUST have a corresponding page."),e.push("- FAKE FORMS. NEVER use `setTimeout` or `await new Promise` to simulate API calls. Every form MUST have a real server action (actions.ts with 'use server') that writes to the database. If a form doesn't persist data, the feature is BROKEN."),e.push(""),e.push("**Favicon**: A default SVG favicon exists at app/icon.svg. On the landing page step, update it to match the app. Keep it simple (a single icon/letter on a rounded square, using the accent color)."),e.join(`
10879
+ `)}async function Xi(r){try{let t=await Qa("nextjs",r);return{reminders:t.reminders,skill:t.skill}}catch{return{reminders:`### ${r} step
10880
10880
  - Follow existing patterns in the codebase
10881
- - Server Components by default, "use client" only when interactivity is needed`,skill:""}}}async function Qi(r,t,e,a,o,i){let n=[];n.push(`## Step ${r.number}: ${r.name}`),n.push(""),n.push("### What to build:"),n.push(r.description),n.push(""),t.primaryAction&&(n.push("### Primary user action (non-negotiable):"),n.push(`- **Core action**: ${t.primaryAction.action}`),n.push(`- **User flow**: ${t.primaryAction.flow}`),n.push(`- **Dashboard must show**: ${t.primaryAction.dashboardSurface}`),n.push(""),n.push("The dashboard is an ACTION surface, not a stats display. Users must be able to complete the core action directly from the dashboard without navigating to a separate page. If this step builds the dashboard, make sure the primary action is front and center with inline forms/buttons \u2014 not behind a link to another page."),n.push(""));let l=["landing","design","dashboard","crud","layout","admin","general","auth"].includes(o);if(t.design&&l?(n.push(Yi(t.design,{hasDesignPreset:!!t.landingDesign&&(o==="landing"||o==="design")})),n.push("")):t.design&&!l&&(n.push("### Design tokens (for reference only \u2014 this step is not UI-focused):"),t.design.accentColor&&n.push(`- Accent color: ${t.design.accentColor}`),t.design.fonts&&n.push(`- Fonts: ${t.design.fonts.heading} / ${t.design.fonts.body}`),n.push("")),i){let m=Gr(i);if(m.length>0){n.push("### Approved wireframe (MUST READ before writing any files):"),n.push("The user approved a wireframe sketch before building. **Read these files NOW before writing any code for this step:**");for(let v of m){let P=v.replace(i,"").replace(/^\//,"");n.push(`- \`${P}\``)}n.push(""),n.push("The wireframe defines the LAYOUT and INFORMATION HIERARCHY \u2014 what goes where, what's prominent, what's secondary. It includes HTML comments explaining WHY things are placed where they are."),n.push(""),n.push("The wireframe is intentionally rough (grayscale, system fonts). Your job is to:"),n.push("1. **Keep the same layout structure** \u2014 same information hierarchy, same element placement, same sections in the same order"),n.push("2. **Apply the design tokens** \u2014 colors, fonts, shadows, radius from the plan design choices above"),n.push("3. **Elevate the visual quality** \u2014 make it feel designed for THIS specific app, not generic"),n.push("4. **Respect the HTML comments** \u2014 they explain WHY things are placed where they are"),n.push("5. **If the wireframe shows a search bar at the top of the dashboard, your dashboard MUST have a search bar at the top** \u2014 do not rearrange the layout"),n.push("")}}t.roles&&Array.isArray(t.roles)&&t.roles.length>0&&(n.push("### Role system (from plan):"),n.push(`- Roles: ${t.roles.join(", ")}`),n.push(`- Default role for new signups: ${t.defaultRole??t.roles[0]}`),n.push("- Role helpers are in `lib/roles.ts` \u2014 use `getUserRole()` and `hasRole()` for access checks"),n.push("")),t.multiTenant&&(n.push("### Multi-tenant (from plan):"),n.push("- Organization tables are in `db/schema/organization.ts`"),n.push("- Org helpers are in `lib/org.ts` \u2014 use `getCurrentOrg()` to scope queries"),n.push("- All data queries MUST be scoped to the current org (filter by orgId)"),n.push("- Org switcher component is at `components/org-switcher.tsx`"),n.push("- CRITICAL: `getCurrentOrg()` returns null for new users who haven't created an org yet. The dashboard MUST handle this \u2014 if currentOrg is null, redirect to an onboarding page or show an inline 'Create your first team/workspace' form. NEVER call .id on a null org."),n.push("- WARNING: cookies().set() in server actions does NOT work on Cloudflare Workers. Do NOT use setCurrentOrgId() or any cookies().set() call inside server actions. Instead, pass the orgId as a form field or query param, or store the active org in the user's database record."),n.push("")),t.language&&(n.push(`### Language: ${t.language}`),n.push(`ALL user-facing text must be written in ${t.language}:`),n.push("- Page titles, headings, labels, button text, placeholder text"),n.push("- Navigation items, menu labels, footer text"),n.push("- Error messages, success messages, empty states"),n.push("- Landing page copy, marketing text, CTAs"),n.push("- Form labels and validation messages"),n.push("Code (variable names, comments, file names) stays in English."),n.push(`Set the HTML lang attribute to the appropriate locale code for ${t.language}.`),n.push(""));let d=["landing","design","auth","general","crud","dashboard"];t.audienceType&&d.includes(o)&&(t.audienceType==="b2c"?(n.push("### Audience: this app belongs to ONE business. The landing page talks TO their customers."),n.push("- Hero: what the customer gets ('Exceptional catering for your next event'), NOT what the tool does"),n.push("- CTAs: customer action ('Order Catering', 'Book Now'), NOT business action ('Get Started Free')"),n.push("- Testimonials: from customers ('They catered our wedding'), NOT from business owners"),n.push("- Features: customer benefits ('Specify your dietary needs'), NOT business benefits ('Track preferences')"),n.push("- Stats: social proof for customers ('2,400+ events served'), NOT internal metrics ('$48k revenue')"),n.push("- The business name IS the brand. Say it like a business homepage, not a SaaS onboarding."),n.push("")):t.audienceType==="b2b"?(n.push("### Audience: this is a SaaS platform. The landing page pitches TO business owners."),n.push("- Hero: the business pain + solution ('Catering orders managed in one place')"),n.push("- CTAs: business owner action ('Start Free Trial', 'Get Started')"),n.push("- Testimonials: from business owners who use the platform"),n.push("- Features: business benefits ('Track dietary preferences across all orders')"),n.push("- Stats: platform metrics ('500+ businesses', '50K+ orders processed')"),n.push("")):t.audienceType==="internal"&&(n.push("### Audience: internal staff tool. No marketing copy needed."),n.push("- No landing page. Auth page copy is functional: 'Sign in to continue'."),n.push("- Dashboard focuses on operational efficiency, not onboarding or sales."),n.push(""))),e.length>0&&(n.push("### Already completed:"),e.forEach(m=>n.push(`- ${m}`)),n.push(""));let u=t.dataModel?jr(r,t.dataModel):[];u.length>0&&(n.push("### Data model (from plan):"),u.forEach(m=>{let v=At(m),P=$i(m.fields);n.push(`- **${v}**: ${P}`),n.push(` Schema file: \`db/schema/${v.toLowerCase().replace(/\s+/g,"-")}.ts\``)}),n.push(""));let c=t.pages?qi(r,t.pages):[];c.length>0&&(n.push("### Pages to create/update:"),c.forEach(m=>{let v=m.description?` \u2014 ${m.description}`:"";n.push(`- \`${_r(m)}\`${v}`)}),n.push("")),o==="crud"&&u.length>0&&u.forEach(m=>{let v=At(m),P=v.toLowerCase().replace(/\s+/g,"-"),T=P.endsWith("s")?P:`${P}s`;n.push(`### Files for ${v} CRUD:`),n.push(`- List page: \`app/(dashboard)/${T}/page.tsx\` (Server Component)`),n.push(`- Detail page: \`app/(dashboard)/${T}/[id]/page.tsx\``),n.push(`- Create page: \`app/(dashboard)/${T}/new/page.tsx\``),n.push(`- Server Actions: \`app/(dashboard)/${T}/actions.ts\``),n.push(`- DataTable columns: \`components/${P}-table-columns.tsx\``),n.push(`- Form: \`components/${P}-form.tsx\``),n.push("")});let h=t.appStyle;if(h&&l){let m=ur(h,o);if(m){let v=ye(h),P=Ge(h);n.push(`### App style: ${v?.name??h}`),n.push(""),n.push(`Applying the **${v?.name}** app style across this app. Follow these design specifications for consistent, brand-quality UI. The app style defines the visual identity (colors, fonts, spacing). All UI elements must use the project's CSS custom properties (--color-primary, --color-background, etc.) and the fonts configured in layout.tsx.`),P?.fonts&&n.push(`
10882
- **Fonts**: Use \`${P.fonts.heading}\` for headings and \`${P.fonts.body}\` for body text (Google Fonts equivalents if the originals are proprietary).`),n.push(""),n.push(m),n.push("")}}if((o==="landing"||o==="design")&&t.landingDesign){let m=Ae(t.landingDesign);m&&(n.push("### Layout preset: "+m.title),n.push(""),n.push(`Using layout preset: **${m.title}** (${m.category})`),n.push(""),n.push("The following describes the STRUCTURAL layout, animation patterns, and section arrangement for the landing page. Use this as the skeleton. All colors, fonts, and visual styling come from the project's design system and CSS custom properties (defined in globals.css and layout.tsx). Do NOT hardcode any colors or font names."),n.push(""),n.push("---"),n.push(m.prompt),n.push("---"),n.push(""))}let g=r.integrationId?qe(r.integrationId):void 0;if(g){let m=Ke(g.id);if(n.push("### Integration blueprint (follow this closely):"),n.push(""),n.push(`Using integration: **${g.name}** (${g.category})`),m?.docsUrl&&n.push(`Official docs: ${m.docsUrl}`),m?.envVars?.length){n.push(""),n.push("**Required environment variables:**");for(let v of m.envVars)n.push(`- \`${v.key}\`: ${v.description} \u2014 Get it at ${v.setupUrl}`);n.push(""),n.push("**IMPORTANT: Never ask the user to paste API keys in chat.** Direct them to set keys in the Mistflow dashboard (Project Settings > Environment Variables) or at app.mistflow.ai. Use mist_config resource='env' action='list' to check if keys are set (has_value: true/false). Only proceed with deploy once all required keys are set.")}m?.packages?.length&&(n.push(""),n.push(`**Packages to install:** \`npm install ${m.packages.join(" ")}\``)),n.push(""),n.push("---"),n.push(g.prompt),n.push("---"),n.push(""),n.push("**Adaptation rules**: Follow the file structure and code patterns above. Replace placeholder values (app names, URLs, copy) with this app's specific content. Use the app's existing data models and route structure. Never ask the user to paste API keys or secrets in the chat. Direct them to set keys in the Mistflow dashboard."),n.push("")}let{reminders:y,skill:x}=await Ji(o);return n.push(y),n.push(""),x&&(n.push(`### ${o} reference:`),n.push(x),n.push("")),n.join(`
10883
- `)}async function zr(r){let{projectPath:t,step:e}=r,a=Wi(t??process.cwd()),o=zi(a);if(!o)return be(a);let i=o.plan;if(!i||!i.steps||i.steps.length===0)return p("No project plan found. Start by describing your app idea first \u2014 the AI will create a plan for you.",!0);let n,s=i.steps.find(w=>w.status==="in_progress");if(s){let w=i.steps.findIndex(R=>R.number===s.number);w!==-1&&(i.steps[w].status="completed",n=`Auto-completed step ${s.number} (${s.name})`,Wr(a,o))}let l;if(e!==void 0){if(l=i.steps.find(w=>w.number===e),!l)return p(`Step ${e} not found. The plan has ${i.steps.length} steps (numbered ${i.steps[0].number} to ${i.steps[i.steps.length-1].number}).`,!0)}else if(l=i.steps.find(w=>w.status!=="completed"),!l)return p(JSON.stringify({message:"All plan steps are completed!",completedSteps:i.steps.map(w=>w.name),nextAction:"NEXT: Deploy the app now. Call mist_deploy with action='deploy'. Do NOT suggest localhost or ask the user \u2014 just deploy."}));let d=i.steps.filter(w=>w.status==="completed").map(w=>`Step ${w.number}: ${w.name}`),{readLocalState:u}=await import("./state-manager-73ZUDKOP.js"),c=u(a),h=Ki(l),g=[];if((h==="crud"||h==="schema")&&i.dataModel&&l.entities&&l.entities.length>0){let w=jr(l,i.dataModel);for(let R of w){let V=At(R);try{let L=(R.fields||[]).map(E=>typeof E=="string"?{name:E,type:"text"}:{name:E.name,type:Vi(E.type),required:E.required!==!1});if(L.length===0)continue;let O=o.dbProvider==="neon"?"nextjs-neon":"nextjs",W=await Ja(O,V,L),z=0,K=0;for(let E of W.files){let N=Dt(a,E.path);if(ca(N)){K++;continue}Gi(Oi(N),{recursive:!0}),pa(N,E.content),z++}let ee=Dt(a,"db","index.ts");if(ca(ee)){let E=Or(ee,"utf-8");E.includes(W.dbExport)||pa(ee,E.trimEnd()+`
10881
+ - Server Components by default, "use client" only when interactivity is needed`,skill:""}}}async function Zi(r,t,e,a,o,i){let n=[];n.push(`## Step ${r.number}: ${r.name}`),n.push(""),n.push("### What to build:"),n.push(r.description),n.push(""),t.primaryAction&&(n.push("### Primary user action (non-negotiable):"),n.push(`- **Core action**: ${t.primaryAction.action}`),n.push(`- **User flow**: ${t.primaryAction.flow}`),n.push(`- **Dashboard must show**: ${t.primaryAction.dashboardSurface}`),n.push(""),n.push("The dashboard is an ACTION surface, not a stats display. Users must be able to complete the core action directly from the dashboard without navigating to a separate page. If this step builds the dashboard, make sure the primary action is front and center with inline forms/buttons \u2014 not behind a link to another page."),n.push(""));let l=["landing","design","dashboard","crud","layout","admin","general","auth"].includes(o);if(t.design&&l?(n.push(Qi(t.design,{hasDesignPreset:!!t.landingDesign&&(o==="landing"||o==="design")})),n.push("")):t.design&&!l&&(n.push("### Design tokens (for reference only \u2014 this step is not UI-focused):"),t.design.accentColor&&n.push(`- Accent color: ${t.design.accentColor}`),t.design.fonts&&n.push(`- Fonts: ${t.design.fonts.heading} / ${t.design.fonts.body}`),n.push("")),i){let m=Or(i);if(m.length>0){n.push("### Approved wireframe (MUST READ before writing any files):"),n.push("The user approved a wireframe sketch before building. **Read these files NOW before writing any code for this step:**");for(let k of m){let T=k.replace(i,"").replace(/^\//,"");n.push(`- \`${T}\``)}n.push(""),n.push("The wireframe defines the LAYOUT and INFORMATION HIERARCHY \u2014 what goes where, what's prominent, what's secondary. It includes HTML comments explaining WHY things are placed where they are."),n.push(""),n.push("The wireframe is intentionally rough (grayscale, system fonts). Your job is to:"),n.push("1. **Keep the same layout structure** \u2014 same information hierarchy, same element placement, same sections in the same order"),n.push("2. **Apply the design tokens** \u2014 colors, fonts, shadows, radius from the plan design choices above"),n.push("3. **Elevate the visual quality** \u2014 make it feel designed for THIS specific app, not generic"),n.push("4. **Respect the HTML comments** \u2014 they explain WHY things are placed where they are"),n.push("5. **If the wireframe shows a search bar at the top of the dashboard, your dashboard MUST have a search bar at the top** \u2014 do not rearrange the layout"),n.push("")}}t.roles&&Array.isArray(t.roles)&&t.roles.length>0&&(n.push("### Role system (from plan):"),n.push(`- Roles: ${t.roles.join(", ")}`),n.push(`- Default role for new signups: ${t.defaultRole??t.roles[0]}`),n.push("- Role helpers are in `lib/roles.ts` \u2014 use `getUserRole()` and `hasRole()` for access checks"),n.push("")),t.multiTenant&&(n.push("### Multi-tenant (from plan):"),n.push("- Organization tables are in `db/schema/organization.ts`"),n.push("- Org helpers are in `lib/org.ts` \u2014 use `getCurrentOrg()` to scope queries"),n.push("- All data queries MUST be scoped to the current org (filter by orgId)"),n.push("- Org switcher component is at `components/org-switcher.tsx`"),n.push("- CRITICAL: `getCurrentOrg()` returns null for new users who haven't created an org yet. The dashboard MUST handle this \u2014 if currentOrg is null, redirect to an onboarding page or show an inline 'Create your first team/workspace' form. NEVER call .id on a null org."),n.push("- WARNING: cookies().set() in server actions does NOT work on Cloudflare Workers. Do NOT use setCurrentOrgId() or any cookies().set() call inside server actions. Instead, pass the orgId as a form field or query param, or store the active org in the user's database record."),n.push("")),t.language&&(n.push(`### Language: ${t.language}`),n.push(`ALL user-facing text must be written in ${t.language}:`),n.push("- Page titles, headings, labels, button text, placeholder text"),n.push("- Navigation items, menu labels, footer text"),n.push("- Error messages, success messages, empty states"),n.push("- Landing page copy, marketing text, CTAs"),n.push("- Form labels and validation messages"),n.push("Code (variable names, comments, file names) stays in English."),n.push(`Set the HTML lang attribute to the appropriate locale code for ${t.language}.`),n.push(""));let d=["landing","design","auth","general","crud","dashboard"];t.audienceType&&d.includes(o)&&(t.audienceType==="b2c"?(n.push("### Audience: this app belongs to ONE business. The landing page talks TO their customers."),n.push("- Hero: what the customer gets ('Exceptional catering for your next event'), NOT what the tool does"),n.push("- CTAs: customer action ('Order Catering', 'Book Now'), NOT business action ('Get Started Free')"),n.push("- Testimonials: from customers ('They catered our wedding'), NOT from business owners"),n.push("- Features: customer benefits ('Specify your dietary needs'), NOT business benefits ('Track preferences')"),n.push("- Stats: social proof for customers ('2,400+ events served'), NOT internal metrics ('$48k revenue')"),n.push("- The business name IS the brand. Say it like a business homepage, not a SaaS onboarding."),n.push("")):t.audienceType==="b2b"?(n.push("### Audience: this is a SaaS platform. The landing page pitches TO business owners."),n.push("- Hero: the business pain + solution ('Catering orders managed in one place')"),n.push("- CTAs: business owner action ('Start Free Trial', 'Get Started')"),n.push("- Testimonials: from business owners who use the platform"),n.push("- Features: business benefits ('Track dietary preferences across all orders')"),n.push("- Stats: platform metrics ('500+ businesses', '50K+ orders processed')"),n.push("")):t.audienceType==="internal"&&(n.push("### Audience: internal staff tool. No marketing copy needed."),n.push("- No landing page. Auth page copy is functional: 'Sign in to continue'."),n.push("- Dashboard focuses on operational efficiency, not onboarding or sales."),n.push(""))),e.length>0&&(n.push("### Already completed:"),e.forEach(m=>n.push(`- ${m}`)),n.push(""));let u=t.dataModel?$r(r,t.dataModel):[];u.length>0&&(n.push("### Data model (from plan):"),u.forEach(m=>{let k=It(m),T=qi(m.fields);n.push(`- **${k}**: ${T}`),n.push(` Schema file: \`db/schema/${k.toLowerCase().replace(/\s+/g,"-")}.ts\``)}),n.push(""));let c=t.pages?Yi(r,t.pages):[];c.length>0&&(n.push("### Pages to create/update:"),c.forEach(m=>{let k=m.description?` \u2014 ${m.description}`:"";n.push(`- \`${zr(m)}\`${k}`)}),n.push("")),o==="crud"&&u.length>0&&u.forEach(m=>{let k=It(m),T=k.toLowerCase().replace(/\s+/g,"-"),C=T.endsWith("s")?T:`${T}s`;n.push(`### Files for ${k} CRUD:`),n.push(`- List page: \`app/(dashboard)/${C}/page.tsx\` (Server Component)`),n.push(`- Detail page: \`app/(dashboard)/${C}/[id]/page.tsx\``),n.push(`- Create page: \`app/(dashboard)/${C}/new/page.tsx\``),n.push(`- Server Actions: \`app/(dashboard)/${C}/actions.ts\``),n.push(`- DataTable columns: \`components/${T}-table-columns.tsx\``),n.push(`- Form: \`components/${T}-form.tsx\``),n.push("")});let h=t.appStyle;if(h&&l){let m=gr(h,o);if(m){let k=ye(h),T=Ge(h);n.push(`### App style: ${k?.name??h}`),n.push(""),n.push(`Applying the **${k?.name}** app style across this app. Follow these design specifications for consistent, brand-quality UI. The app style defines the visual identity (colors, fonts, spacing). All UI elements must use the project's CSS custom properties (--color-primary, --color-background, etc.) and the fonts configured in layout.tsx.`),T?.fonts&&n.push(`
10882
+ **Fonts**: Use \`${T.fonts.heading}\` for headings and \`${T.fonts.body}\` for body text (Google Fonts equivalents if the originals are proprietary).`),n.push(""),n.push(m),n.push("")}}if((o==="landing"||o==="design")&&t.landingDesign){let m=Ie(t.landingDesign);m&&(n.push("### Layout preset: "+m.title),n.push(""),n.push(`Using layout preset: **${m.title}** (${m.category})`),n.push(""),n.push("The following describes the STRUCTURAL layout, animation patterns, and section arrangement for the landing page. Use this as the skeleton. All colors, fonts, and visual styling come from the project's design system and CSS custom properties (defined in globals.css and layout.tsx). Do NOT hardcode any colors or font names."),n.push(""),n.push("---"),n.push(m.prompt),n.push("---"),n.push(""))}let g=r.integrationId?qe(r.integrationId):void 0;if(g){let m=Ke(g.id);if(n.push("### Integration blueprint (follow this closely):"),n.push(""),n.push(`Using integration: **${g.name}** (${g.category})`),m?.docsUrl&&n.push(`Official docs: ${m.docsUrl}`),m?.envVars?.length){n.push(""),n.push("**Required environment variables:**");for(let k of m.envVars)n.push(`- \`${k.key}\`: ${k.description} \u2014 Get it at ${k.setupUrl}`);n.push(""),n.push("**IMPORTANT: Never ask the user to paste API keys in chat.** Direct them to set keys in the Mistflow dashboard (Project Settings > Environment Variables) or at app.mistflow.ai. Use mist_config resource='env' action='list' to check if keys are set (has_value: true/false). Only proceed with deploy once all required keys are set.")}m?.packages?.length&&(n.push(""),n.push(`**Packages to install:** \`npm install ${m.packages.join(" ")}\``)),n.push(""),n.push("---"),n.push(g.prompt),n.push("---"),n.push(""),n.push("**Adaptation rules**: Follow the file structure and code patterns above. Replace placeholder values (app names, URLs, copy) with this app's specific content. Use the app's existing data models and route structure. Never ask the user to paste API keys or secrets in the chat. Direct them to set keys in the Mistflow dashboard."),n.push("")}let{reminders:y,skill:x}=await Xi(o);return n.push(y),n.push(""),x&&(n.push(`### ${o} reference:`),n.push(x),n.push("")),n.join(`
10883
+ `)}async function Vr(r){let{projectPath:t,step:e}=r,a=_i(t??process.cwd()),o=Vi(a);if(!o)return be(a);let i=o.plan;if(!i||!i.steps||i.steps.length===0)return p("No project plan found. Start by describing your app idea first \u2014 the AI will create a plan for you.",!0);let n,s=i.steps.find(w=>w.status==="in_progress");if(s){let w=i.steps.findIndex(L=>L.number===s.number);w!==-1&&(i.steps[w].status="completed",n=`Auto-completed step ${s.number} (${s.name})`,_r(a,o))}let l;if(e!==void 0){if(l=i.steps.find(w=>w.number===e),!l)return p(`Step ${e} not found. The plan has ${i.steps.length} steps (numbered ${i.steps[0].number} to ${i.steps[i.steps.length-1].number}).`,!0)}else if(l=i.steps.find(w=>w.status!=="completed"),!l)return p(JSON.stringify({message:"All plan steps are completed!",completedSteps:i.steps.map(w=>w.name),nextAction:"NEXT: Deploy the app now. Call mist_deploy with action='deploy'. Do NOT suggest localhost or ask the user \u2014 just deploy."}));let d=i.steps.filter(w=>w.status==="completed").map(w=>`Step ${w.number}: ${w.name}`),{readLocalState:u}=await import("./state-manager-73ZUDKOP.js"),c=u(a),h=Ji(l),g=[];if((h==="crud"||h==="schema")&&i.dataModel&&l.entities&&l.entities.length>0){let w=$r(l,i.dataModel);for(let L of w){let V=It(L);try{let N=(L.fields||[]).map(E=>typeof E=="string"?{name:E,type:"text"}:{name:E.name,type:Ki(E.type),required:E.required!==!1});if(N.length===0)continue;let O=o.dbProvider==="neon"?"nextjs-neon":"nextjs",W=await Xa(O,V,N),z=0,Y=0;for(let E of W.files){let F=At(a,E.path);if(ha(F)){Y++;continue}Oi(ji(F),{recursive:!0}),ga(F,E.content),z++}let Z=At(a,"db","index.ts");if(ha(Z)){let E=jr(Z,"utf-8");E.includes(W.dbExport)||ga(Z,E.trimEnd()+`
10884
10884
  `+W.dbExport+`
10885
- `)}z>0?g.push(`${W.entityPascal} CRUD (${z} new files${K>0?`, ${K} existing skipped`:""})`):K>0&&g.push(`${W.entityPascal} CRUD (all ${K} files already exist \u2014 skipped)`)}catch(L){console.error(`Module generation failed for ${V} (non-fatal):`,L instanceof Error?L.message:L)}}}let y=await Qi(l,i,d,null,h,a),x=i.steps.findIndex(w=>w.number===l.number);if(x!==-1&&(o.plan.steps[x].status="in_progress",Wr(a,o)),c&&o.projectId){let{syncRemoteState:w}=await import("./state-manager-73ZUDKOP.js");w(o.projectId,c).catch(()=>{})}let m=i.steps.every(w=>w.status==="completed"||w.number===l.number),v;if(m){let w=i.primaryAction?.action??"the core action",R=i.primaryAction?.flow??"sign up \u2192 reach dashboard \u2192 complete primary action";v=`THIS IS THE LAST STEP. Rules for speed:
10885
+ `)}z>0?g.push(`${W.entityPascal} CRUD (${z} new files${Y>0?`, ${Y} existing skipped`:""})`):Y>0&&g.push(`${W.entityPascal} CRUD (all ${Y} files already exist \u2014 skipped)`)}catch(N){console.error(`Module generation failed for ${V} (non-fatal):`,N instanceof Error?N.message:N)}}}let y=await Zi(l,i,d,null,h,a),x=i.steps.findIndex(w=>w.number===l.number);if(x!==-1&&(o.plan.steps[x].status="in_progress",_r(a,o)),c&&o.projectId){let{syncRemoteState:w}=await import("./state-manager-73ZUDKOP.js");w(o.projectId,c).catch(()=>{})}let m=i.steps.every(w=>w.status==="completed"||w.number===l.number),k;if(m){let w=i.primaryAction?.action??"the core action",L=i.primaryAction?.flow??"sign up \u2192 reach dashboard \u2192 complete primary action";k=`THIS IS THE LAST STEP. Rules for speed:
10886
10886
 
10887
10887
  1. Write ALL files using PARALLEL tool calls \u2014 batch multiple Write/Edit calls in a single message.
10888
10888
  2. Do NOT read files you already know (AGENTS.md, CLAUDE.md, mistflow.json, middleware.ts, lib/auth.ts, lib/db.ts).
@@ -10891,100 +10891,100 @@ Environment variables needed:
10891
10891
  - app/page.tsx must be a real landing page, NOT a redirect to /login
10892
10892
  - middleware.ts must have "/" in PUBLIC_EXACT or PUBLIC_PREFIXES
10893
10893
  - Forms must use server actions (actions.ts with 'use server'), NOT setTimeout/simulate
10894
- 5. Call mist_build with action='build' to verify a clean production build, then call mist_deploy with action='deploy' to ship it.`}else v=`IMPLEMENT THIS STEP NOW. Rules for speed:
10894
+ 5. Call mist_build with action='build' to verify a clean production build, then call mist_deploy with action='deploy' to ship it.`}else k=`IMPLEMENT THIS STEP NOW. Rules for speed:
10895
10895
 
10896
10896
  1. Write ALL files for this step using PARALLEL tool calls \u2014 batch multiple Write/Edit calls in a single message. Do NOT write one file at a time.
10897
10897
  2. Do NOT read files you already know: AGENTS.md, CLAUDE.md, mistflow.json, middleware.ts, lib/auth.ts, lib/db.ts, drizzle.config.ts \u2014 these haven't changed.
10898
10898
  3. Only read a file if you need to MODIFY it (e.g. sidebar.tsx to add a nav link, db/index.ts to add an export).
10899
- 4. After writing ALL files, call mist_build with action='implement' to move to the next step. The previous step is auto-marked complete \u2014 do NOT call implement twice.`;let P=JSON.stringify({instruction:y,step:{number:l.number,name:l.name,description:l.description,status:"in_progress"},...n?{autoCompleted:n}:{},...g.length>0?{generatedModules:g,generatedNote:"These CRUD modules were pre-generated. Review and customize them instead of writing from scratch. Focus on: business logic in actions.ts, UI polish, and wiring navigation."}:{},progress:`${d.length}/${i.steps.length} steps done`,nextAction:v});return await ji(3e3)?De("http://localhost:3000",P):p(P)}import{z as ua}from"zod";import{resolve as Xi}from"path";import{execFileSync as Zi}from"child_process";var Ql=ua.object({projectPath:ua.string().optional().describe("Path to the project directory (default: current working directory)"),buildOutput:ua.string().optional().describe("Build output to parse (if not provided, runs npm run build)")});function ha(r){let t=[],e=/([^\s(]+)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)/g,a;for(;(a=e.exec(r))!==null;){let[,l,d,u,c,h]=a;t.push({file:l,line:parseInt(d,10),column:parseInt(u,10),message:`${c}: ${h}`,humanMessage:`There is a type error in ${l} on line ${d}: ${h}`,suggestion:`Check line ${d} in ${l}. ${c==="TS2345"?"The types of the arguments do not match.":`Fix the ${c} error.`}`})}let o=/(?:Error:\s*)?\.\/([^\s:]+):(\d+):(\d+)\s*\n\s*(.+)/g;for(;(a=o.exec(r))!==null;){let[,l,d,u,c]=a;t.some(h=>h.file===l&&h.line===parseInt(d,10))||t.push({file:l,line:parseInt(d,10),column:parseInt(u,10),message:c,humanMessage:`There is an error in ${l} on line ${d}: ${c.trim()}`,suggestion:`Check line ${d} in ${l} and fix the issue.`})}let i=/Module not found:\s*(?:Error:\s*)?Can't resolve ['"]([^'"]+)['"]\s*(?:in\s*['"]?([^'"]+)['"]?)?/g;for(;(a=i.exec(r))!==null;){let[,l,d]=a;t.push({file:d,message:`Module not found: ${l}`,humanMessage:`The file ${d??"your project"} is trying to import '${l}' which is not installed.`,suggestion:`Run npm install ${l}`})}let n=/Package subpath ['"]([^'"]+)['"] is not defined by "exports" in .*?node_modules\/([^/]+(?:\/[^/]+)?)\//g;for(;(a=n.exec(r))!==null;){let[,l,d]=a;t.push({message:`ERR_PACKAGE_PATH_NOT_EXPORTED: ${d}${l}`,humanMessage:`The package '${d}' does not export the subpath '${l}'. This is usually caused by a version conflict between packages that depend on different major versions of '${d}'.`,suggestion:`Add '${d}' at the version that exports '${l}' as an optionalDependency in package.json (this pins it at root level and lets the other version nest). Then delete node_modules and package-lock.json, and run npm install.`})}let s=/SyntaxError:\s*([^\s:]+):\s*(.+?)\s*\((\d+):(\d+)\)/g;for(;(a=s.exec(r))!==null;){let[,l,d,u,c]=a;t.some(h=>h.file===l&&h.line===parseInt(u,10))||t.push({file:l,line:parseInt(u,10),column:parseInt(c,10),message:`SyntaxError: ${d}`,humanMessage:`There is a syntax error in ${l} on line ${u}.`,suggestion:`Check line ${u} in ${l} for a missing closing bracket or unexpected token.`})}return t}async function $r(r){let{projectPath:t,buildOutput:e}=r,a=Xi(t??process.cwd()),o=e??"";if(!e)try{return Zi("npm",["run","build"],{cwd:a,stdio:["pipe","pipe","pipe"],timeout:12e4}),p(JSON.stringify({errors:[],rawOutput:"",message:"Build succeeded with no errors."}))}catch(n){let s=n instanceof Error&&"stderr"in n?String(n.stderr):"",l=n instanceof Error&&"stdout"in n?String(n.stdout):"";o=s+`
10900
- `+l}let i=ha(o);return i.length===0?p(JSON.stringify({errors:[],rawOutput:o.slice(0,2e3),message:"Build failed but I could not extract specific errors. Here is the raw output."})):p(JSON.stringify({errors:i,rawOutput:o.slice(0,2e3),message:`Found ${i.length} error${i.length===1?"":"s"} in the build output.`}))}import{existsSync as eo,readFileSync as to}from"fs";import{join as ao}from"path";function ro(r){let t=ao(r,"mistflow.json");if(!eo(t))return null;try{return JSON.parse(to(t,"utf-8"))}catch{return null}}async function Vr(r){try{let t=await fetch(r,{redirect:"follow",signal:AbortSignal.timeout(15e3)}),e=await t.text();return{status:t.status,body:e}}catch(t){return{status:0,body:String(t)}}}async function no(r,t,e){try{let a=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",...e??{}},body:JSON.stringify(t),redirect:"follow",signal:AbortSignal.timeout(15e3)}),o=await a.text(),i;try{i=JSON.parse(o)}catch{}return{status:a.status,json:i}}catch{return{status:0}}}async function qr(r){try{let t=await r.screenshot({type:"png"});return Buffer.from(t).toString("base64")}catch{return}}async function Xe(r,t,e){let a=[],o=i=>{i.type()==="error"&&a.push(i.text())};r.on("console",o);try{let i=await e(),n=await qr(r);return{name:t,status:i.pass?"pass":"fail",detail:i.detail,fix:i.fix,screenshot:n,consoleErrors:a.length>0?a:void 0}}catch(i){let n=await qr(r);return{name:t,status:"fail",detail:`Unexpected error: ${i instanceof Error?i.message:String(i)}`,screenshot:n,consoleErrors:a.length>0?a:void 0}}finally{r.removeListener("console",o)}}async function Kr(r){let t=r.projectPath??process.cwd(),e=ro(t),a=r.url;if(a||(a=e?.deploy?.url),!a)return p("No deploy URL found. Deploy the app first with mist_deploy, then call mist_build action='qa'.",!0);a.startsWith("http")||(a=`https://${a}`);let o=e?.projectId,i=[],n=await Vr(`${a}/api/health`);if(n.status!==200)return i.push({name:"Health endpoint",status:"fail",detail:`Returns ${n.status}`,fix:"The worker is not running or crashed on startup. Check app/api/health/route.ts exists and the build succeeded."}),ot(a,i);i.push({name:"Health endpoint",status:"pass",detail:"Returns 200"});let s=await Vr(`${a}/api/auth/ok`);if(s.status!==200)return i.push({name:"Auth system",status:"fail",detail:`Auth endpoint returns ${s.status}`,fix:"Better Auth is not working. Check lib/auth.ts, lib/db.ts, and that your database env vars are set."}),ot(a,i);i.push({name:"Auth system",status:"pass",detail:"Better Auth running"});let l,d;if(o){let h=await Ha(o);h&&(l=h.email,d=h.password,console.error("[qa] Using admin credentials from backend"))}if(!l||!d){console.error("[qa] No stored credentials, creating throwaway user"),l=`qa-${Date.now()}@mistflow.dev`,d="TestPass123!";let h={Origin:a,Referer:`${a}/register`},g=await no(`${a}/api/auth/sign-up/email`,{name:"QA Test",email:l,password:d},h);if(g.status!==200||!g.json?.user&&!g.json?.token){let y=g.json?.message??g.json?.error??"Unknown error";return i.push({name:"Signup",status:"fail",detail:`HTTP ${g.status}: ${y}`,fix:"Signup failed. Check: 1) Database tables exist (auth schema pushed?). 2) lib/auth.ts config. 3) db/schema/auth.ts has all Better Auth columns including admin plugin fields (role, banned, ban_reason, ban_expires)."}),ot(a,i)}}let u,c;try{let{getIsolatedContext:h,takeScreenshot:g}=await import("./browser-manager-K5BT5YXO.js"),y=await h();u=y.context,c=y.page}catch{return i.push({name:"Browser",status:"fail",detail:"Playwright not installed. Browser-based QA skipped.",fix:"Run: npx playwright install chromium"}),ot(a,i)}try{let h=await Xe(c,"Landing page",async()=>{await c.goto(a,{waitUntil:"domcontentloaded",timeout:3e4}),await c.waitForLoadState("networkidle").catch(()=>{});let y=await c.evaluate(()=>{let m=document.body;if(!m)return"";let v=document.createTreeWalker(m,NodeFilter.SHOW_TEXT),P="",T;for(;T=v.nextNode();){let w=T.parentElement;if(w){let R=window.getComputedStyle(w);R.display!=="none"&&R.visibility!=="hidden"&&parseFloat(R.opacity)>0&&(P+=T.textContent?.trim()+" ")}}return P.trim()});if(y.length<50)return{pass:!1,detail:`Landing page appears blank (${y.length} chars visible). Likely a CSS/JS rendering issue.`,fix:"Common cause: motion/react animations with opacity:0 and whileInView that never trigger on Cloudflare Workers (no Intersection Observer). Replace with CSS animations or set initial={{ opacity: 1 }}."};let x=c.url();return x.includes("/login")||x.includes("/sign-in")?{pass:!1,detail:"Root URL redirects to login instead of showing a landing page",fix:"Check middleware.ts: '/' must be in PUBLIC_EXACT. Check app/page.tsx: must be a landing page, not a redirect."}:{pass:!0,detail:`Renders visible content (${y.length} chars)`}});i.push(h);let g=await Xe(c,"Login",async()=>{await c.goto(`${a}/login`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let y=c.locator('input[type="email"], input[name="email"], input[placeholder*="email" i]'),x=c.locator('input[type="password"], input[name="password"]');try{await y.first().waitFor({state:"visible",timeout:1e4})}catch{return{pass:!1,detail:"Login page has no visible email input field",fix:"Check app/(auth)/login/page.tsx renders a login form with email and password inputs."}}await y.first().fill(l),await x.first().fill(d),await c.locator('button[type="submit"], button:has-text("Sign in"), button:has-text("Log in"), button:has-text("Login")').first().click();try{await c.waitForURL(v=>!v.pathname.includes("/login"),{timeout:1e4})}catch{let v=await c.locator('[role="alert"], .text-red-500, .text-destructive, [data-error]').first().textContent().catch(()=>null);return{pass:!1,detail:v?`Login failed: ${v}`:"Login did not redirect. Page stayed on /login.",fix:"Check auth configuration. If email verification is required, the seeded admin account may not be verified. Check lib/auth.ts emailVerification settings."}}return{pass:!0,detail:`Logged in, redirected to ${c.url()}`}});if(i.push(g),g.status==="pass"){let y=await Xe(c,"Dashboard",async()=>{c.url().includes("/dashboard")||(await c.goto(`${a}/dashboard`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{}));let P=await c.content();return P.length<1e3?{pass:!1,detail:`Dashboard page is very small (${P.length} bytes)`,fix:"Check app/(dashboard)/dashboard/page.tsx exists and the dashboard layout doesn't crash."}:await c.locator('text="Something went wrong"').isVisible().catch(()=>!1)?{pass:!1,detail:"Dashboard shows error boundary",fix:"A server component crashed. Check the page.tsx for unhandled null/undefined or missing database tables."}:{pass:!0,detail:`Loads (${P.length} bytes)`}});i.push(y);let x=await c.evaluate(()=>{let v=[];return document.querySelectorAll("nav a[href], aside a[href]").forEach(T=>{let w=T.getAttribute("href");w&&w.startsWith("/")&&!w.startsWith("/api")&&!w.includes("[")&&w!=="/dashboard"&&w!=="/"&&!w.includes("/login")&&!w.includes("/sign")&&v.push(w)}),[...new Set(v)]});if(x.length>0){let v=0,P=[];for(let T of x.slice(0,8)){let w=await Xe(c,`Page: ${T}`,async()=>{await c.goto(`${a}${T}`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let R=await c.title(),V=await c.content();return R.toLowerCase().includes("500")||R.toLowerCase().includes("server error")?{pass:!1,detail:"Page returns 500 server error",fix:"Server component crashed. Common causes: 1) Database tables missing. 2) Wrong ORM dialect (pgTable vs sqliteTable). 3) Unhandled null/undefined in server component."}:R.toLowerCase().includes("404")||R.toLowerCase().includes("not found")?{pass:!1,detail:"Page returns 404",fix:`Page ${T} not found. Create the page or remove the nav link.`}:await c.locator('text="Something went wrong"').isVisible().catch(()=>!1)?{pass:!1,detail:"Page shows error boundary",fix:"A server component crashed. Check the page.tsx for unhandled errors."}:V.length<500?{pass:!1,detail:`Page is very small (${V.length} bytes)`,fix:"Page may not have rendered. Check the page component."}:{pass:!0,detail:"Loads without errors"}});w.status==="fail"&&(v++,P.push(T)),i.push(w)}}let m=await Xe(c,"Design quality",async()=>{let v=c.url().includes("/dashboard")?c.url():`${a}/dashboard`;c.url().includes("/dashboard")||(await c.goto(v,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{}));let P=await c.evaluate(()=>{let T=[],w=document.querySelectorAll("h1, h2, h3, h4, h5, h6"),R=new Set;w.forEach(f=>{R.add(window.getComputedStyle(f).fontSize)}),w.length>=3&&R.size<2&&T.push("TYPOGRAPHY: All headings appear the same size. Create clear hierarchy with 3+ distinct sizes using a modular scale (1.25-1.5x ratio).");let V=Array.from(w).map(f=>parseInt(f.tagName.charAt(1),10));for(let f=1;f<V.length;f++)if(V[f]-V[f-1]>1){T.push(`TYPOGRAPHY: Heading level skipped (h${V[f-1]} -> h${V[f]}). Use sequential heading levels for accessibility.`);break}let L=document.querySelectorAll("*"),O=!1,W=!1;L.forEach(f=>{let b=window.getComputedStyle(f);b.backgroundColor==="rgb(0, 0, 0)"&&f.clientHeight>100&&f.clientWidth>200&&(O=!0);let S=b.backgroundColor,A=b.color;if(S&&!S.includes("0, 0, 0")&&!S.includes("255, 255, 255")&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"&&A.match(/rgb\((\d+), (\d+), (\d+)\)/)){let $=A.match(/rgb\((\d+), (\d+), (\d+)\)/);if($){let[H,Y,ue]=[parseInt($[1]),parseInt($[2]),parseInt($[3])],ne=Math.abs(H-Y)<10&&Math.abs(Y-ue)<10&&H>80&&H<180,te=S.match(/rgb\((\d+), (\d+), (\d+)\)/);if(ne&&te){let[J,k,C]=[parseInt(te[1]),parseInt(te[2]),parseInt(te[3])];!(Math.abs(J-k)<15&&Math.abs(k-C)<15)&&f.textContent&&f.textContent.trim().length>0&&(W=!0)}}}}),O&&T.push("COLOR: Pure black (#000) background detected on a large element. Use a tinted dark color instead (e.g. oklch(15% 0.01 hue) or a deep navy/charcoal)."),W&&T.push("COLOR: Gray text on a colored background detected. Gray looks washed out on color. Use a darker shade of the background color or white instead.");let z=document.querySelectorAll('[class*="card"], [class*="Card"], [role="group"]'),K=!1;z.forEach(f=>{f.querySelectorAll('[class*="card"], [class*="Card"]').length>0&&(K=!0)}),K&&T.push("LAYOUT: Nested cards detected (card inside card). Flatten the hierarchy. Use spacing and background color to create separation instead.");let ee=document.querySelectorAll("p, li, span, div"),E=0,N=0;ee.forEach(f=>{f.textContent&&f.textContent.trim().length>20&&f.clientHeight>0&&(N++,window.getComputedStyle(f).textAlign==="center"&&E++)}),N>5&&E/N>.7&&T.push("LAYOUT: Most text is center-aligned. Use left-alignment for body content and lists. Reserve center-alignment for heroes and CTAs.");let q=new Set;L.forEach(f=>{let b=window.getComputedStyle(f);b.gap&&b.gap!=="normal"&&b.gap!=="0px"&&q.add(b.gap)}),q.size===1&&L.length>20&&T.push("LAYOUT: Same gap value used everywhere. Vary spacing to create hierarchy: tight within groups (8-12px), generous between sections (32-64px).");let ce=document.querySelectorAll("button, a, input, select, textarea"),me=!1;ce.forEach(f=>{let b=window.getComputedStyle(f);if(b.outline==="none"||b.outline==="0px none rgb(0, 0, 0)"){let S=f;(S.style.outline==="none"||S.style.outline==="0")&&(me=!0)}}),document.querySelectorAll("table, [role='table']").forEach(f=>{f.querySelectorAll("tbody tr").length===0&&(f.parentElement?.querySelector('[class*="empty"], [class*="Empty"], [class*="no-data"], [class*="placeholder"]')||T.push("UX: Empty table with no empty state. Add a helpful message explaining what will appear here and a CTA to create the first item."))});let re=document.querySelectorAll("style"),B=!1;re.forEach(f=>{let b=f.textContent||"";(b.includes("bounce")||b.includes("elastic")||b.match(/cubic-bezier\([^)]*[2-9]\.[0-9]/))&&(B=!0)}),B&&T.push("MOTION: Bounce or elastic easing detected. These feel dated. Use smooth deceleration curves (Quart out, Expo out) instead.");let M=document.querySelectorAll("button, a, input, select, [role='button']"),I=0;return M.forEach(f=>{let b=f.getBoundingClientRect();b.width>0&&b.height>0&&(b.width<32||b.height<32)&&I++}),I>3&&T.push(`ACCESSIBILITY: ${I} interactive elements are smaller than 32x32px. Minimum recommended touch target is 44x44px. Add padding to increase tap area.`),T});return P.length===0?{pass:!0,detail:"No design quality issues detected. Typography hierarchy, color usage, layout patterns, and accessibility basics look good."}:{pass:!1,detail:`${P.length} design quality issue(s) found:
10901
- ${P.map((T,w)=>`${w+1}. ${T}`).join(`
10902
- `)}`,fix:"Fix these design issues in the source code. These are common AI-generated design anti-patterns that make apps look generic. Address each issue, then redeploy and re-run QA."}});if(i.push(m),i.find(v=>v.name==="Landing page"&&v.status==="pass")){let v=await Xe(c,"Landing design quality",async()=>{await c.goto(a,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let P=await c.evaluate(()=>{let T=[];document.querySelectorAll("*").forEach(L=>{let O=window.getComputedStyle(L);(O.getPropertyValue("-webkit-background-clip")||O.getPropertyValue("background-clip"))==="text"&&L.textContent&&L.textContent.trim().length>0&&T.push("SLOP: Gradient text detected. This is a common AI design pattern. Use solid colors for text.")});let R=document.querySelector("section, [class*='hero'], [class*='Hero'], header + div, main > div:first-child");if(R){let L=(R.textContent||"").toLowerCase(),O=["transform your","unlock the power","revolutionize your","take your .* to the next level","the future of","welcome to","get started today","join thousands","powerful analytics","seamless integration","lightning fast"];for(let W of O)if(L.match(new RegExp(W))){T.push(`COPY: Generic hero text detected ('${W}'). Write specific copy about what THIS app does for its users.`);break}}return document.querySelectorAll('[class*="grid"]').forEach(L=>{let W=window.getComputedStyle(L).gridTemplateColumns;if(W){let z=W.split(" ").filter(ee=>ee!=="").length,K=L.children;if(z===3&&K.length===3){let ee=Array.from(K).map(q=>q.offsetHeight),E=ee.every(q=>Math.abs(q-ee[0])<5),N=Array.from(K).every(q=>{let ce=q.querySelectorAll("svg"),me=q.querySelectorAll("h2, h3, h4"),ie=q.querySelectorAll("p");return ce.length>=1&&me.length>=1&&ie.length>=1});E&&N&&T.push("SLOP: 3-column icon + title + paragraph feature grid detected. This is the most common AI layout pattern. Use asymmetric layouts, bento grids, or varied card sizes instead.")}}}),T});return P.length===0?{pass:!0,detail:"Landing page design looks intentional. No generic AI patterns detected."}:{pass:!1,detail:`${P.length} landing design issue(s):
10903
- ${P.map((T,w)=>`${w+1}. ${T}`).join(`
10904
- `)}`,fix:"These patterns make the landing page look AI-generated. Fix them to create a more distinctive, professional design."}});i.push(v)}}}finally{u&&await u.close().catch(()=>{})}if(r.deploymentId){let h=i.filter(x=>x.status==="fail"),g=i.filter(x=>x.status==="pass"),y=Date.now();await Ga(r.deploymentId,{checks:i.map(({screenshot:x,...m})=>m),overall:h.length===0?"pass":"fail",passed:g.length,failed:h.length,duration_ms:Date.now()-y}).catch(()=>{})}return ot(a,i)}function ot(r,t){let e=t.filter(i=>i.status==="fail"),a=t.filter(i=>i.status==="pass"),o=[];if(e.length===0)o.push({type:"text",text:JSON.stringify({status:"pass",message:`QA passed. All ${t.length} checks OK. The app is working correctly.`,url:r,checks:t.map(({screenshot:i,...n})=>n)})});else{let i=e.map((n,s)=>`${s+1}. **${n.name}**: ${n.detail}
10899
+ 4. After writing ALL files, call mist_build with action='implement' to move to the next step. The previous step is auto-marked complete \u2014 do NOT call implement twice.`;let T=JSON.stringify({instruction:y,step:{number:l.number,name:l.name,description:l.description,status:"in_progress"},...n?{autoCompleted:n}:{},...g.length>0?{generatedModules:g,generatedNote:"These CRUD modules were pre-generated. Review and customize them instead of writing from scratch. Focus on: business logic in actions.ts, UI polish, and wiring navigation."}:{},progress:`${d.length}/${i.steps.length} steps done`,nextAction:k});return await $i(3e3)?Ae("http://localhost:3000",T):p(T)}import{z as ma}from"zod";import{resolve as eo}from"path";import{execFileSync as to}from"child_process";var ed=ma.object({projectPath:ma.string().optional().describe("Path to the project directory (default: current working directory)"),buildOutput:ma.string().optional().describe("Build output to parse (if not provided, runs npm run build)")});function fa(r){let t=[],e=/([^\s(]+)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)/g,a;for(;(a=e.exec(r))!==null;){let[,l,d,u,c,h]=a;t.push({file:l,line:parseInt(d,10),column:parseInt(u,10),message:`${c}: ${h}`,humanMessage:`There is a type error in ${l} on line ${d}: ${h}`,suggestion:`Check line ${d} in ${l}. ${c==="TS2345"?"The types of the arguments do not match.":`Fix the ${c} error.`}`})}let o=/(?:Error:\s*)?\.\/([^\s:]+):(\d+):(\d+)\s*\n\s*(.+)/g;for(;(a=o.exec(r))!==null;){let[,l,d,u,c]=a;t.some(h=>h.file===l&&h.line===parseInt(d,10))||t.push({file:l,line:parseInt(d,10),column:parseInt(u,10),message:c,humanMessage:`There is an error in ${l} on line ${d}: ${c.trim()}`,suggestion:`Check line ${d} in ${l} and fix the issue.`})}let i=/Module not found:\s*(?:Error:\s*)?Can't resolve ['"]([^'"]+)['"]\s*(?:in\s*['"]?([^'"]+)['"]?)?/g;for(;(a=i.exec(r))!==null;){let[,l,d]=a;t.push({file:d,message:`Module not found: ${l}`,humanMessage:`The file ${d??"your project"} is trying to import '${l}' which is not installed.`,suggestion:`Run npm install ${l}`})}let n=/Package subpath ['"]([^'"]+)['"] is not defined by "exports" in .*?node_modules\/([^/]+(?:\/[^/]+)?)\//g;for(;(a=n.exec(r))!==null;){let[,l,d]=a;t.push({message:`ERR_PACKAGE_PATH_NOT_EXPORTED: ${d}${l}`,humanMessage:`The package '${d}' does not export the subpath '${l}'. This is usually caused by a version conflict between packages that depend on different major versions of '${d}'.`,suggestion:`Add '${d}' at the version that exports '${l}' as an optionalDependency in package.json (this pins it at root level and lets the other version nest). Then delete node_modules and package-lock.json, and run npm install.`})}let s=/SyntaxError:\s*([^\s:]+):\s*(.+?)\s*\((\d+):(\d+)\)/g;for(;(a=s.exec(r))!==null;){let[,l,d,u,c]=a;t.some(h=>h.file===l&&h.line===parseInt(u,10))||t.push({file:l,line:parseInt(u,10),column:parseInt(c,10),message:`SyntaxError: ${d}`,humanMessage:`There is a syntax error in ${l} on line ${u}.`,suggestion:`Check line ${u} in ${l} for a missing closing bracket or unexpected token.`})}return t}async function qr(r){let{projectPath:t,buildOutput:e}=r,a=eo(t??process.cwd()),o=e??"";if(!e)try{return to("npm",["run","build"],{cwd:a,stdio:["pipe","pipe","pipe"],timeout:12e4}),p(JSON.stringify({errors:[],rawOutput:"",message:"Build succeeded with no errors."}))}catch(n){let s=n instanceof Error&&"stderr"in n?String(n.stderr):"",l=n instanceof Error&&"stdout"in n?String(n.stdout):"";o=s+`
10900
+ `+l}let i=fa(o);return i.length===0?p(JSON.stringify({errors:[],rawOutput:o.slice(0,2e3),message:"Build failed but I could not extract specific errors. Here is the raw output."})):p(JSON.stringify({errors:i,rawOutput:o.slice(0,2e3),message:`Found ${i.length} error${i.length===1?"":"s"} in the build output.`}))}import{existsSync as ao,readFileSync as ro}from"fs";import{join as no}from"path";function io(r){let t=no(r,"mistflow.json");if(!ao(t))return null;try{return JSON.parse(ro(t,"utf-8"))}catch{return null}}async function Kr(r){try{let t=await fetch(r,{redirect:"follow",signal:AbortSignal.timeout(15e3)}),e=await t.text();return{status:t.status,body:e}}catch(t){return{status:0,body:String(t)}}}async function oo(r,t,e){try{let a=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",...e??{}},body:JSON.stringify(t),redirect:"follow",signal:AbortSignal.timeout(15e3)}),o=await a.text(),i;try{i=JSON.parse(o)}catch{}return{status:a.status,json:i}}catch{return{status:0}}}async function Yr(r){try{let t=await r.screenshot({type:"png"});return Buffer.from(t).toString("base64")}catch{return}}async function Xe(r,t,e){let a=[],o=i=>{i.type()==="error"&&a.push(i.text())};r.on("console",o);try{let i=await e(),n=await Yr(r);return{name:t,status:i.pass?"pass":"fail",detail:i.detail,fix:i.fix,screenshot:n,consoleErrors:a.length>0?a:void 0}}catch(i){let n=await Yr(r);return{name:t,status:"fail",detail:`Unexpected error: ${i instanceof Error?i.message:String(i)}`,screenshot:n,consoleErrors:a.length>0?a:void 0}}finally{r.removeListener("console",o)}}async function Jr(r){let t=r.projectPath??process.cwd(),e=io(t),a=r.url;if(a||(a=e?.deploy?.url),!a)return p("No deploy URL found. Deploy the app first with mist_deploy, then call mist_build action='qa'.",!0);a.startsWith("http")||(a=`https://${a}`);let o=e?.projectId,i=[],n=await Kr(`${a}/api/health`);if(n.status!==200)return i.push({name:"Health endpoint",status:"fail",detail:`Returns ${n.status}`,fix:"The worker is not running or crashed on startup. Check app/api/health/route.ts exists and the build succeeded."}),ot(a,i);i.push({name:"Health endpoint",status:"pass",detail:"Returns 200"});let s=await Kr(`${a}/api/auth/ok`);if(s.status!==200)return i.push({name:"Auth system",status:"fail",detail:`Auth endpoint returns ${s.status}`,fix:"Better Auth is not working. Check lib/auth.ts, lib/db.ts, and that your database env vars are set."}),ot(a,i);i.push({name:"Auth system",status:"pass",detail:"Better Auth running"});let l,d;if(o){let h=await Wa(o);h&&(l=h.email,d=h.password,console.error("[qa] Using admin credentials from backend"))}if(!l||!d){console.error("[qa] No stored credentials, creating throwaway user"),l=`qa-${Date.now()}@mistflow.dev`,d="TestPass123!";let h={Origin:a,Referer:`${a}/register`},g=await oo(`${a}/api/auth/sign-up/email`,{name:"QA Test",email:l,password:d},h);if(g.status!==200||!g.json?.user&&!g.json?.token){let y=g.json?.message??g.json?.error??"Unknown error";return i.push({name:"Signup",status:"fail",detail:`HTTP ${g.status}: ${y}`,fix:"Signup failed. Check: 1) Database tables exist (auth schema pushed?). 2) lib/auth.ts config. 3) db/schema/auth.ts has all Better Auth columns including admin plugin fields (role, banned, ban_reason, ban_expires)."}),ot(a,i)}}let u,c;try{let{getIsolatedContext:h,takeScreenshot:g}=await import("./browser-manager-K5BT5YXO.js"),y=await h();u=y.context,c=y.page}catch{return i.push({name:"Browser",status:"fail",detail:"Playwright not installed. Browser-based QA skipped.",fix:"Run: npx playwright install chromium"}),ot(a,i)}try{let h=await Xe(c,"Landing page",async()=>{await c.goto(a,{waitUntil:"domcontentloaded",timeout:3e4}),await c.waitForLoadState("networkidle").catch(()=>{});let y=await c.evaluate(()=>{let m=document.body;if(!m)return"";let k=document.createTreeWalker(m,NodeFilter.SHOW_TEXT),T="",C;for(;C=k.nextNode();){let w=C.parentElement;if(w){let L=window.getComputedStyle(w);L.display!=="none"&&L.visibility!=="hidden"&&parseFloat(L.opacity)>0&&(T+=C.textContent?.trim()+" ")}}return T.trim()});if(y.length<50)return{pass:!1,detail:`Landing page appears blank (${y.length} chars visible). Likely a CSS/JS rendering issue.`,fix:"Common cause: motion/react animations with opacity:0 and whileInView that never trigger on Cloudflare Workers (no Intersection Observer). Replace with CSS animations or set initial={{ opacity: 1 }}."};let x=c.url();return x.includes("/login")||x.includes("/sign-in")?{pass:!1,detail:"Root URL redirects to login instead of showing a landing page",fix:"Check middleware.ts: '/' must be in PUBLIC_EXACT. Check app/page.tsx: must be a landing page, not a redirect."}:{pass:!0,detail:`Renders visible content (${y.length} chars)`}});i.push(h);let g=await Xe(c,"Login",async()=>{await c.goto(`${a}/login`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let y=c.locator('input[type="email"], input[name="email"], input[placeholder*="email" i]'),x=c.locator('input[type="password"], input[name="password"]');try{await y.first().waitFor({state:"visible",timeout:1e4})}catch{return{pass:!1,detail:"Login page has no visible email input field",fix:"Check app/(auth)/login/page.tsx renders a login form with email and password inputs."}}await y.first().fill(l),await x.first().fill(d),await c.locator('button[type="submit"], button:has-text("Sign in"), button:has-text("Log in"), button:has-text("Login")').first().click();try{await c.waitForURL(k=>!k.pathname.includes("/login"),{timeout:1e4})}catch{let k=await c.locator('[role="alert"], .text-red-500, .text-destructive, [data-error]').first().textContent().catch(()=>null);return{pass:!1,detail:k?`Login failed: ${k}`:"Login did not redirect. Page stayed on /login.",fix:"Check auth configuration. If email verification is required, the seeded admin account may not be verified. Check lib/auth.ts emailVerification settings."}}return{pass:!0,detail:`Logged in, redirected to ${c.url()}`}});if(i.push(g),g.status==="pass"){let y=await Xe(c,"Dashboard",async()=>{c.url().includes("/dashboard")||(await c.goto(`${a}/dashboard`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{}));let T=await c.content();return T.length<1e3?{pass:!1,detail:`Dashboard page is very small (${T.length} bytes)`,fix:"Check app/(dashboard)/dashboard/page.tsx exists and the dashboard layout doesn't crash."}:await c.locator('text="Something went wrong"').isVisible().catch(()=>!1)?{pass:!1,detail:"Dashboard shows error boundary",fix:"A server component crashed. Check the page.tsx for unhandled null/undefined or missing database tables."}:{pass:!0,detail:`Loads (${T.length} bytes)`}});i.push(y);let x=await c.evaluate(()=>{let k=[];return document.querySelectorAll("nav a[href], aside a[href]").forEach(C=>{let w=C.getAttribute("href");w&&w.startsWith("/")&&!w.startsWith("/api")&&!w.includes("[")&&w!=="/dashboard"&&w!=="/"&&!w.includes("/login")&&!w.includes("/sign")&&k.push(w)}),[...new Set(k)]});if(x.length>0){let k=0,T=[];for(let C of x.slice(0,8)){let w=await Xe(c,`Page: ${C}`,async()=>{await c.goto(`${a}${C}`,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let L=await c.title(),V=await c.content();return L.toLowerCase().includes("500")||L.toLowerCase().includes("server error")?{pass:!1,detail:"Page returns 500 server error",fix:"Server component crashed. Common causes: 1) Database tables missing. 2) Wrong ORM dialect (pgTable vs sqliteTable). 3) Unhandled null/undefined in server component."}:L.toLowerCase().includes("404")||L.toLowerCase().includes("not found")?{pass:!1,detail:"Page returns 404",fix:`Page ${C} not found. Create the page or remove the nav link.`}:await c.locator('text="Something went wrong"').isVisible().catch(()=>!1)?{pass:!1,detail:"Page shows error boundary",fix:"A server component crashed. Check the page.tsx for unhandled errors."}:V.length<500?{pass:!1,detail:`Page is very small (${V.length} bytes)`,fix:"Page may not have rendered. Check the page component."}:{pass:!0,detail:"Loads without errors"}});w.status==="fail"&&(k++,T.push(C)),i.push(w)}}let m=await Xe(c,"Design quality",async()=>{let k=c.url().includes("/dashboard")?c.url():`${a}/dashboard`;c.url().includes("/dashboard")||(await c.goto(k,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{}));let T=await c.evaluate(()=>{let C=[],w=document.querySelectorAll("h1, h2, h3, h4, h5, h6"),L=new Set;w.forEach(f=>{L.add(window.getComputedStyle(f).fontSize)}),w.length>=3&&L.size<2&&C.push("TYPOGRAPHY: All headings appear the same size. Create clear hierarchy with 3+ distinct sizes using a modular scale (1.25-1.5x ratio).");let V=Array.from(w).map(f=>parseInt(f.tagName.charAt(1),10));for(let f=1;f<V.length;f++)if(V[f]-V[f-1]>1){C.push(`TYPOGRAPHY: Heading level skipped (h${V[f-1]} -> h${V[f]}). Use sequential heading levels for accessibility.`);break}let N=document.querySelectorAll("*"),O=!1,W=!1;N.forEach(f=>{let b=window.getComputedStyle(f);b.backgroundColor==="rgb(0, 0, 0)"&&f.clientHeight>100&&f.clientWidth>200&&(O=!0);let S=b.backgroundColor,I=b.color;if(S&&!S.includes("0, 0, 0")&&!S.includes("255, 255, 255")&&S!=="rgba(0, 0, 0, 0)"&&S!=="transparent"&&I.match(/rgb\((\d+), (\d+), (\d+)\)/)){let $=I.match(/rgb\((\d+), (\d+), (\d+)\)/);if($){let[H,K,fe]=[parseInt($[1]),parseInt($[2]),parseInt($[3])],ie=Math.abs(H-K)<10&&Math.abs(K-fe)<10&&H>80&&H<180,re=S.match(/rgb\((\d+), (\d+), (\d+)\)/);if(ie&&re){let[ee,v,D]=[parseInt(re[1]),parseInt(re[2]),parseInt(re[3])];!(Math.abs(ee-v)<15&&Math.abs(v-D)<15)&&f.textContent&&f.textContent.trim().length>0&&(W=!0)}}}}),O&&C.push("COLOR: Pure black (#000) background detected on a large element. Use a tinted dark color instead (e.g. oklch(15% 0.01 hue) or a deep navy/charcoal)."),W&&C.push("COLOR: Gray text on a colored background detected. Gray looks washed out on color. Use a darker shade of the background color or white instead.");let z=document.querySelectorAll('[class*="card"], [class*="Card"], [role="group"]'),Y=!1;z.forEach(f=>{f.querySelectorAll('[class*="card"], [class*="Card"]').length>0&&(Y=!0)}),Y&&C.push("LAYOUT: Nested cards detected (card inside card). Flatten the hierarchy. Use spacing and background color to create separation instead.");let Z=document.querySelectorAll("p, li, span, div"),E=0,F=0;Z.forEach(f=>{f.textContent&&f.textContent.trim().length>20&&f.clientHeight>0&&(F++,window.getComputedStyle(f).textAlign==="center"&&E++)}),F>5&&E/F>.7&&C.push("LAYOUT: Most text is center-aligned. Use left-alignment for body content and lists. Reserve center-alignment for heroes and CTAs.");let q=new Set;N.forEach(f=>{let b=window.getComputedStyle(f);b.gap&&b.gap!=="normal"&&b.gap!=="0px"&&q.add(b.gap)}),q.size===1&&N.length>20&&C.push("LAYOUT: Same gap value used everywhere. Vary spacing to create hierarchy: tight within groups (8-12px), generous between sections (32-64px).");let pe=document.querySelectorAll("button, a, input, select, textarea"),me=!1;pe.forEach(f=>{let b=window.getComputedStyle(f);if(b.outline==="none"||b.outline==="0px none rgb(0, 0, 0)"){let S=f;(S.style.outline==="none"||S.style.outline==="0")&&(me=!0)}}),document.querySelectorAll("table, [role='table']").forEach(f=>{f.querySelectorAll("tbody tr").length===0&&(f.parentElement?.querySelector('[class*="empty"], [class*="Empty"], [class*="no-data"], [class*="placeholder"]')||C.push("UX: Empty table with no empty state. Add a helpful message explaining what will appear here and a CTA to create the first item."))});let ae=document.querySelectorAll("style"),P=!1;ae.forEach(f=>{let b=f.textContent||"";(b.includes("bounce")||b.includes("elastic")||b.match(/cubic-bezier\([^)]*[2-9]\.[0-9]/))&&(P=!0)}),P&&C.push("MOTION: Bounce or elastic easing detected. These feel dated. Use smooth deceleration curves (Quart out, Expo out) instead.");let R=document.querySelectorAll("button, a, input, select, [role='button']"),M=0;return R.forEach(f=>{let b=f.getBoundingClientRect();b.width>0&&b.height>0&&(b.width<32||b.height<32)&&M++}),M>3&&C.push(`ACCESSIBILITY: ${M} interactive elements are smaller than 32x32px. Minimum recommended touch target is 44x44px. Add padding to increase tap area.`),C});return T.length===0?{pass:!0,detail:"No design quality issues detected. Typography hierarchy, color usage, layout patterns, and accessibility basics look good."}:{pass:!1,detail:`${T.length} design quality issue(s) found:
10901
+ ${T.map((C,w)=>`${w+1}. ${C}`).join(`
10902
+ `)}`,fix:"Fix these design issues in the source code. These are common AI-generated design anti-patterns that make apps look generic. Address each issue, then redeploy and re-run QA."}});if(i.push(m),i.find(k=>k.name==="Landing page"&&k.status==="pass")){let k=await Xe(c,"Landing design quality",async()=>{await c.goto(a,{waitUntil:"domcontentloaded",timeout:15e3}),await c.waitForLoadState("networkidle").catch(()=>{});let T=await c.evaluate(()=>{let C=[];document.querySelectorAll("*").forEach(N=>{let O=window.getComputedStyle(N);(O.getPropertyValue("-webkit-background-clip")||O.getPropertyValue("background-clip"))==="text"&&N.textContent&&N.textContent.trim().length>0&&C.push("SLOP: Gradient text detected. This is a common AI design pattern. Use solid colors for text.")});let L=document.querySelector("section, [class*='hero'], [class*='Hero'], header + div, main > div:first-child");if(L){let N=(L.textContent||"").toLowerCase(),O=["transform your","unlock the power","revolutionize your","take your .* to the next level","the future of","welcome to","get started today","join thousands","powerful analytics","seamless integration","lightning fast"];for(let W of O)if(N.match(new RegExp(W))){C.push(`COPY: Generic hero text detected ('${W}'). Write specific copy about what THIS app does for its users.`);break}}return document.querySelectorAll('[class*="grid"]').forEach(N=>{let W=window.getComputedStyle(N).gridTemplateColumns;if(W){let z=W.split(" ").filter(Z=>Z!=="").length,Y=N.children;if(z===3&&Y.length===3){let Z=Array.from(Y).map(q=>q.offsetHeight),E=Z.every(q=>Math.abs(q-Z[0])<5),F=Array.from(Y).every(q=>{let pe=q.querySelectorAll("svg"),me=q.querySelectorAll("h2, h3, h4"),ne=q.querySelectorAll("p");return pe.length>=1&&me.length>=1&&ne.length>=1});E&&F&&C.push("SLOP: 3-column icon + title + paragraph feature grid detected. This is the most common AI layout pattern. Use asymmetric layouts, bento grids, or varied card sizes instead.")}}}),C});return T.length===0?{pass:!0,detail:"Landing page design looks intentional. No generic AI patterns detected."}:{pass:!1,detail:`${T.length} landing design issue(s):
10903
+ ${T.map((C,w)=>`${w+1}. ${C}`).join(`
10904
+ `)}`,fix:"These patterns make the landing page look AI-generated. Fix them to create a more distinctive, professional design."}});i.push(k)}}}finally{u&&await u.close().catch(()=>{})}if(r.deploymentId){let h=i.filter(x=>x.status==="fail"),g=i.filter(x=>x.status==="pass"),y=Date.now();await Oa(r.deploymentId,{checks:i.map(({screenshot:x,...m})=>m),overall:h.length===0?"pass":"fail",passed:g.length,failed:h.length,duration_ms:Date.now()-y}).catch(()=>{})}return ot(a,i)}function ot(r,t){let e=t.filter(i=>i.status==="fail"),a=t.filter(i=>i.status==="pass"),o=[];if(e.length===0)o.push({type:"text",text:JSON.stringify({status:"pass",message:`QA passed. All ${t.length} checks OK. The app is working correctly.`,url:r,checks:t.map(({screenshot:i,...n})=>n)})});else{let i=e.map((n,s)=>`${s+1}. **${n.name}**: ${n.detail}
10905
10905
  Fix: ${n.fix}`).join(`
10906
10906
 
10907
10907
  `);o.push({type:"text",text:JSON.stringify({status:"fail",message:`QA found ${e.length} issue(s) on the live app. Fix them and redeploy.`,url:r,passed:a.length,failed:e.length,checks:t.map(({screenshot:n,...s})=>s),fixInstructions:`The deployed app at ${r} has ${e.length} issue(s):
10908
10908
 
10909
10909
  ${i}
10910
10910
 
10911
- Fix these issues in the source code, then call mist_deploy with action='deploy'${r.includes("-pv-")?" environment='preview'":""} to redeploy. After redeploying, call mist_build action='qa' again to verify the fixes.`})})}for(let i of t)i.screenshot&&o.push({type:"image",data:i.screenshot,mimeType:"image/png"});return{content:o}}function lo(r){let t=Mt(so(),".mistflow","plans",`${r}.json`);if(!It(t))return null;try{let e=JSON.parse(io(t,"utf-8"));return e.plan?e:null}catch{return null}}var co=le.object({action:le.enum(["init","implement","debug","build","qa","mockup"]).describe("'mockup' generates visual mockup HTML files from a plan for user preview and approval. 'init' creates and sets up a new project from your plan. 'implement' executes the next (or specific) plan step. 'debug' analyzes build errors. 'build' runs the full production build locally (OpenNext for Cloudflare) without deploying. 'qa' tests the live deployed app \u2014 checks landing page, signup, login, dashboard, and nav links. Call after mist_deploy. If issues found, fix and redeploy, then call qa again."),name:le.string().optional().describe("(init) Project name"),planId:le.string().optional().describe("(init/mockup) Plan ID from mist_plan"),plan:le.any().optional().describe("(init) Full plan object \u2014 use planId instead when available"),path:le.string().optional().describe("(init) Target directory path"),landingDesign:le.string().optional().describe("(init) Landing design ID to apply to the landing page. Can be set here if not set during mist_plan. Use mist_project action='landing-designs' to browse."),appStyle:le.string().optional().describe("(init) App style ID to apply across all pages (e.g. 'stripe', 'linear'). Can be set here if not set during mist_plan. Use mist_project action='app-styles' to browse."),projectPath:le.string().optional().describe("Path to the project directory (default: cwd)"),step:le.number().optional().describe("(implement) Specific step number to implement"),buildOutput:le.string().optional().describe("(debug) Build output to parse instead of running a build"),feedback:le.string().optional().describe("(mockup) User feedback on the current mockup \u2014 describe what to change."),approved:le.boolean().optional().describe("(mockup) Set to true when the user approves the mockup. Locks in the design direction."),url:le.string().optional().describe("(qa) URL to test. Defaults to deploy URL from mistflow.json"),deploymentId:le.string().optional().describe("(qa) Deployment ID to associate QA results with. Passed from mist_deploy output.")}),Yr={name:"mist_build",description:"STEP 2-3 of the Mistflow workflow. Build and develop a Mistflow project. Actions: 'mockup' generates visual HTML mockups from a plan for user preview \u2014 call with planId after plan approval if user wants to preview. Pass feedback to iterate, approved=true to lock in the design. 'init' creates and sets up a new project from a plan. Pass the planId returned by mist_plan \u2014 do NOT pass the full plan object. 'implement' executes the next plan step \u2014 call repeatedly until all steps are done. 'build' runs the full production build locally (OpenNext for Cloudflare) to verify before deploying. 'debug' analyzes build errors. 'qa' tests the LIVE deployed app \u2014 call AFTER mist_deploy to verify everything works. If qa finds issues, fix them and redeploy, then call qa again. The full workflow is: mist_plan \u2192 (optional) mist_build mockup \u2192 mist_build init \u2192 mist_build implement (repeat) \u2192 mist_deploy \u2192 mist_build qa (loop until pass).",inputSchema:co,handler:async r=>{let t=r;switch(t.action){case"init":{if(!t.name)return p("Project name is required for init.",!0);let e=t.plan,a=null;if(t.planId){if(a=lo(t.planId),!a)return p(`Plan not found for planId '${t.planId}'. The plan may have expired. Call mist_plan again to generate a new plan.`,!0);e=a.plan}if(!e)return p("No plan provided. Pass the planId returned by mist_plan, or call mist_plan first to generate a plan.",!0);if(!Array.isArray(e?.steps)||e.steps.length===0)return p("The plan is missing a 'steps' array. This usually means the plan generation was incomplete. Call mist_plan again with the same description to get a complete plan with implementation steps.",!0);if(a?.sourceDeploymentId&&a?.forkToken&&a?.projectId)return Rr({name:t.name,plan:e,path:t.path,projectId:a.projectId,sourceDeploymentId:a.sourceDeploymentId,forkToken:a.forkToken,requiredEnvVars:a.requiredEnvVars??[],dbProvider:a.dbProvider??"neon",planId:t.planId});if(t.landingDesign){let o=Ae(t.landingDesign);o?e.landingDesign=o.id:console.error(`Landing design '${t.landingDesign}' not found \u2014 ignoring.`)}if(t.appStyle){let o=ye(t.appStyle);o?e.appStyle=o.id:console.error(`App style '${t.appStyle}' not found \u2014 ignoring.`)}return Mr({name:t.name,plan:e,path:t.path,planId:t.planId})}case"implement":return zr({projectPath:t.projectPath,step:t.step});case"debug":return $r({projectPath:t.projectPath,buildOutput:t.buildOutput});case"build":{let e=oo(t.projectPath??process.cwd());if(!It(Mt(e,"mistflow.json")))return p("Not a Mistflow project \u2014 mistflow.json not found. Run mist_build init first.",!0);if(!It(Mt(e,"node_modules")))try{ga("npm",["install"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:12e4})}catch{return p("npm install failed. Check package.json for issues.",!0)}let o=process.platform==="win32"?"npx.cmd":"npx",i=0,n=2;for(;;){i++;try{ga(o,["@opennextjs/cloudflare","build"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:18e4});let s=It(Mt(e,".open-next"));return p(JSON.stringify({success:!0,buildDir:".open-next",message:s?"Production build succeeded. Ready to deploy with mist_deploy.":"Build completed but .open-next/ directory not found. Check your OpenNext config."}))}catch(s){let l=s instanceof Error&&"stderr"in s?String(s.stderr):"",d=s instanceof Error&&"stdout"in s?String(s.stdout):"",u=l+`
10912
- `+d;if(i<n){let h=[],g=/Module not found:\s*(?:Error:\s*)?Can't resolve ['"]([^'"]+)['"]/g,y;for(;(y=g.exec(u))!==null;){let x=y[1];if(!x.startsWith(".")&&!x.startsWith("@/")&&!x.startsWith("~/")){let m=x.startsWith("@")?x.split("/").slice(0,2).join("/"):x.split("/")[0];h.includes(m)||h.push(m)}}if(h.length>0){console.error(`[build] Auto-installing missing packages: ${h.join(", ")}`);try{ga("npm",["install",...h],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:6e4});continue}catch{console.error("[build] Auto-install failed, reporting build errors")}}}let c=ha(u);return p(JSON.stringify({success:!1,errors:c,rawOutput:u.slice(0,3e3),message:c.length>0?`Build failed with ${c.length} error${c.length===1?"":"s"}:
10911
+ Fix these issues in the source code, then call mist_deploy with action='deploy'${r.includes("-pv-")?" environment='preview'":""} to redeploy. After redeploying, call mist_build action='qa' again to verify the fixes.`})})}for(let i of t)i.screenshot&&o.push({type:"image",data:i.screenshot,mimeType:"image/png"});return{content:o}}function po(r){let t=Rt(co(),".mistflow","plans",`${r}.json`);if(!Mt(t))return null;try{let e=JSON.parse(so(t,"utf-8"));return e.plan?e:null}catch{return null}}var uo=de.object({action:de.enum(["init","implement","debug","build","qa","mockup"]).describe("'mockup' generates visual mockup HTML files from a plan for user preview and approval. 'init' creates and sets up a new project from your plan. 'implement' executes the next (or specific) plan step. 'debug' analyzes build or runtime errors \u2014 for runtime errors, call mist_project errors first then pass the output as buildOutput. 'build' runs the full production build locally (OpenNext for Cloudflare) without deploying. 'qa' tests the live deployed app \u2014 checks landing page, signup, login, dashboard, and nav links. Call after mist_deploy. If issues found, fix and redeploy, then call qa again."),name:de.string().optional().describe("(init) Project name"),planId:de.string().optional().describe("(init/mockup) Plan ID from mist_plan"),plan:de.any().optional().describe("(init) Full plan object \u2014 use planId instead when available"),path:de.string().optional().describe("(init) Target directory path"),landingDesign:de.string().optional().describe("(init) Landing design ID to apply to the landing page. Can be set here if not set during mist_plan. Use mist_project action='landing-designs' to browse."),appStyle:de.string().optional().describe("(init) App style ID to apply across all pages (e.g. 'stripe', 'linear'). Can be set here if not set during mist_plan. Use mist_project action='app-styles' to browse."),projectPath:de.string().optional().describe("Path to the project directory (default: cwd)"),step:de.number().optional().describe("(implement) Specific step number to implement"),buildOutput:de.string().optional().describe("(debug) Build output to parse instead of running a build"),feedback:de.string().optional().describe("(mockup) User feedback on the current mockup \u2014 describe what to change."),approved:de.boolean().optional().describe("(mockup) Set to true when the user approves the mockup. Locks in the design direction."),url:de.string().optional().describe("(qa) URL to test. Defaults to deploy URL from mistflow.json"),deploymentId:de.string().optional().describe("(qa) Deployment ID to associate QA results with. Passed from mist_deploy output.")}),Qr={name:"mist_build",description:"STEP 2-3 of the Mistflow workflow. Build and develop a Mistflow project. Actions: 'mockup' generates visual HTML mockups from a plan for user preview \u2014 call with planId after plan approval if user wants to preview. Pass feedback to iterate, approved=true to lock in the design. 'init' creates and sets up a new project from a plan. Pass the planId returned by mist_plan \u2014 do NOT pass the full plan object. 'implement' executes the next plan step \u2014 call repeatedly until all steps are done. 'build' runs the full production build locally (OpenNext for Cloudflare) to verify before deploying. 'debug' analyzes build or runtime errors \u2014 for runtime errors from production, call mist_project errors first to fetch them, then pass the output as buildOutput. 'qa' tests the LIVE deployed app \u2014 call AFTER mist_deploy to verify everything works. If qa finds issues, fix them and redeploy, then call qa again. The full workflow is: mist_plan \u2192 (optional) mist_build mockup \u2192 mist_build init \u2192 mist_build implement (repeat) \u2192 mist_deploy \u2192 mist_build qa (loop until pass).",inputSchema:uo,handler:async r=>{let t=r;switch(t.action){case"init":{if(!t.name)return p("Project name is required for init.",!0);let e=t.plan,a=null;if(t.planId){if(a=po(t.planId),!a)return p(`Plan not found for planId '${t.planId}'. The plan may have expired. Call mist_plan again to generate a new plan.`,!0);e=a.plan}if(!e)return p("No plan provided. Pass the planId returned by mist_plan, or call mist_plan first to generate a plan.",!0);if(!Array.isArray(e?.steps)||e.steps.length===0)return p("The plan is missing a 'steps' array. This usually means the plan generation was incomplete. Call mist_plan again with the same description to get a complete plan with implementation steps.",!0);if(a?.sourceDeploymentId&&a?.forkToken&&a?.projectId)return Nr({name:t.name,plan:e,path:t.path,projectId:a.projectId,sourceDeploymentId:a.sourceDeploymentId,forkToken:a.forkToken,requiredEnvVars:a.requiredEnvVars??[],dbProvider:a.dbProvider??"neon",planId:t.planId});if(t.landingDesign){let o=Ie(t.landingDesign);o?e.landingDesign=o.id:console.error(`Landing design '${t.landingDesign}' not found \u2014 ignoring.`)}if(t.appStyle){let o=ye(t.appStyle);o?e.appStyle=o.id:console.error(`App style '${t.appStyle}' not found \u2014 ignoring.`)}return Lr({name:t.name,plan:e,path:t.path,planId:t.planId})}case"implement":return Vr({projectPath:t.projectPath,step:t.step});case"debug":return qr({projectPath:t.projectPath,buildOutput:t.buildOutput});case"build":{let e=lo(t.projectPath??process.cwd());if(!Mt(Rt(e,"mistflow.json")))return p("Not a Mistflow project \u2014 mistflow.json not found. Run mist_build init first.",!0);if(!Mt(Rt(e,"node_modules")))try{ba("npm",["install"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:12e4})}catch{return p("npm install failed. Check package.json for issues.",!0)}let o=process.platform==="win32"?"npx.cmd":"npx",i=0,n=2;for(;;){i++;try{ba(o,["@opennextjs/cloudflare","build"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:18e4});let s=Mt(Rt(e,".open-next"));return p(JSON.stringify({success:!0,buildDir:".open-next",message:s?"Production build succeeded. Ready to deploy with mist_deploy.":"Build completed but .open-next/ directory not found. Check your OpenNext config."}))}catch(s){let l=s instanceof Error&&"stderr"in s?String(s.stderr):"",d=s instanceof Error&&"stdout"in s?String(s.stdout):"",u=l+`
10912
+ `+d;if(i<n){let h=[],g=/Module not found:\s*(?:Error:\s*)?Can't resolve ['"]([^'"]+)['"]/g,y;for(;(y=g.exec(u))!==null;){let x=y[1];if(!x.startsWith(".")&&!x.startsWith("@/")&&!x.startsWith("~/")){let m=x.startsWith("@")?x.split("/").slice(0,2).join("/"):x.split("/")[0];h.includes(m)||h.push(m)}}if(h.length>0){console.error(`[build] Auto-installing missing packages: ${h.join(", ")}`);try{ba("npm",["install",...h],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:6e4});continue}catch{console.error("[build] Auto-install failed, reporting build errors")}}}let c=fa(u);return p(JSON.stringify({success:!1,errors:c,rawOutput:u.slice(0,3e3),message:c.length>0?`Build failed with ${c.length} error${c.length===1?"":"s"}:
10913
10913
 
10914
10914
  `+c.map((h,g)=>{let y=h.file?`${h.file}${h.line?`:${h.line}`:""}`:"unknown";return`${g+1}. [${y}] ${h.humanMessage}
10915
10915
  Fix: ${h.suggestion}`}).join(`
10916
10916
 
10917
10917
  `)+`
10918
10918
 
10919
- Fix these and run mist_build build again.`:"Build failed. Run mist_build with action='debug' for detailed analysis."}),!0)}}}case"qa":return Kr({projectPath:t.projectPath,url:t.url,deploymentId:t.deploymentId});case"mockup":return t.planId?Hr({planId:t.planId,projectPath:t.projectPath,feedback:t.feedback,approved:t.approved}):p("planId is required for mockup. Pass the planId from mist_plan.",!0);default:return p(`Unknown action: ${t.action}. Use mockup, init, implement, build, debug, or qa.`,!0)}}};import{z as je}from"zod";import{z as Lt}from"zod";import{resolve as on,join as X,dirname as Nt,basename as Po}from"path";import{existsSync as se,readFileSync as wa,writeFileSync as sn,unlinkSync as Zr,mkdirSync as Bo,cpSync as ba,rmSync as ya,readdirSync as Do}from"fs";import{execFileSync as Ao}from"child_process";import{spawn as Io}from"child_process";import{tmpdir as Mo}from"os";function Jr(r,t){if(r instanceof he)switch(r.code){case"auth_missing":return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);case"auth_revoked":return p("Your Mistflow credentials were revoked. Run mist_setup to reconnect.",!0);case"auth_expired":case"auth_invalid":case"auth_org_not_found":return p("Your Mistflow session needs to be refreshed. Run mist_setup to reconnect.",!0);case"permission_denied":return p(r.message||"You don't have permission for that action.",!0);case"rate_limited":return p("You're hitting rate limits. Wait 30 seconds and try again.",!0);case"quota_exceeded":return p(`${r.message} Upgrade at https://app.mistflow.ai/pricing to lift limits.`,!0);case"validation_error":case"not_found":case"conflict":return p(r.message,!0);case"server_error":case"upstream_error":return p(`${t} failed: the Mistflow backend returned an error. Try again in 30 seconds.`,!0);case"network_error":return p(`${t} failed: cannot reach Mistflow servers. Check your network and try again.`,!0);default:return p(`${t} failed: ${r.message}`,!0)}let e=r instanceof Error?r.message:String(r);return p(`${t} failed: ${e}`,!0)}import{existsSync as st,readFileSync as ma}from"fs";import{join as lt}from"path";import{execFileSync as Td}from"child_process";function po(r){let t=lt(r,".env.local"),e=[];return st(t)?(ma(t,"utf-8").match(/^AUTH_SECRET=(.*)$/m)?.[1]?.trim()||e.push({check:"env",message:"AUTH_SECRET is missing from .env.local. Add a random secret for authentication.",file:".env.local"}),e):(e.push({check:"env",message:"Missing .env.local file. Create one with your local development environment variables (AUTH_SECRET, etc.).",file:".env.local"}),e)}function uo(r){let t=lt(r,"app","api","auth","[...all]","route.ts");return st(t)?[]:[{check:"auth-routes",message:"Missing Better Auth catch-all route at app/api/auth/[...all]/route.ts. Create this file to handle authentication.",file:"app/api/auth/[...all]/route.ts"}]}function ho(r){let t=lt(r,"app","api","health","route.ts");return st(t)?[]:[{check:"health-endpoint",message:"Missing health check endpoint at app/api/health/route.ts. Create this file so Mistflow can verify your deployment.",file:"app/api/health/route.ts"}]}function go(r){let t=lt(r,"mistflow.json");if(!st(t))return[];let e;try{e=JSON.parse(ma(t,"utf-8"))}catch{return[]}let a=e.env;if(!a?.required||typeof a.required!="object")return[];let o=[],i=lt(r,".env"),n="";try{st(i)&&(n=ma(i,"utf-8"))}catch{}for(let[s,l]of Object.entries(a.required)){let d=!!process.env[s],u=n.includes(`${s}=`);if(d||u)continue;let c=l?.description?` for '${l.description}'`:"";o.push({check:"required-env",message:`${s} is required${c}. Run mist_config to set it if you haven't already.`})}return o}function Qr(r){let t=[...po(r),...uo(r),...ho(r)],e=[...go(r)];return{passed:t.length===0,errors:t,warnings:e}}import{existsSync as ge,readFileSync as _e,readdirSync as Rt,statSync as dt}from"fs";import{join as j}from"path";async function Xr(r,t){let e=[];e.push(mo(r)),e.push(fo(r)),e.push(bo(r)),e.push(yo(r)),e.push(xo(r)),e.push(So(r)),t?.plan&&e.push(...wo(r,t)),e.push(vo(r)),e.push(ko(r)),e.push(...To(r)),e.push(Co(r));let a=e.filter(d=>d.status==="fail"),o=e.filter(d=>d.status==="warn"),i=e.filter(d=>d.status==="pass"),n=a.length>0,s=!n,l;return s&&o.length===0?l=`All ${e.length} checks passed`:s?l=`${i.length}/${e.length} passed, ${o.length} warning(s)`:l=`${a.length} issue(s) found:
10919
+ Fix these and run mist_build build again.`:"Build failed. Run mist_build with action='debug' for detailed analysis."}),!0)}}}case"qa":return Jr({projectPath:t.projectPath,url:t.url,deploymentId:t.deploymentId});case"mockup":return t.planId?Wr({planId:t.planId,projectPath:t.projectPath,feedback:t.feedback,approved:t.approved}):p("planId is required for mockup. Pass the planId from mist_plan.",!0);default:return p(`Unknown action: ${t.action}. Use mockup, init, implement, build, debug, or qa.`,!0)}}};import{z as je}from"zod";import{z as Nt}from"zod";import{resolve as ln,join as Q,dirname as Ft,basename as Do}from"path";import{existsSync as le,readFileSync as Sa,writeFileSync as dn,unlinkSync as tn,mkdirSync as Ao,cpSync as wa,rmSync as va,readdirSync as Io}from"fs";import{execFileSync as Mo}from"child_process";import{spawn as Ro}from"child_process";import{tmpdir as Lo}from"os";function Xr(r,t){if(r instanceof he)switch(r.code){case"auth_missing":return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);case"auth_revoked":return p("Your Mistflow credentials were revoked. Run mist_setup to reconnect.",!0);case"auth_expired":case"auth_invalid":case"auth_org_not_found":return p("Your Mistflow session needs to be refreshed. Run mist_setup to reconnect.",!0);case"permission_denied":return p(r.message||"You don't have permission for that action.",!0);case"rate_limited":return p("You're hitting rate limits. Wait 30 seconds and try again.",!0);case"quota_exceeded":return p(`${r.message} Upgrade at https://app.mistflow.ai/pricing to lift limits.`,!0);case"validation_error":case"conflict":return p(r.message,!0);case"not_found":{let a=ft(),o=a?.email||a?.orgSlug;return o&&(r.message.toLowerCase().includes("project")||t.toLowerCase().includes("project"))?p(`Project not found. You are signed in as ${o}. This project may belong to a different account. Run mist_setup to sign in with the account that owns this project.`,!0):p(r.message,!0)}case"server_error":case"upstream_error":return p(`${t} failed: the Mistflow backend returned an error. Try again in 30 seconds.`,!0);case"network_error":return p(`${t} failed: cannot reach Mistflow servers. Check your network and try again.`,!0);default:return p(`${t} failed: ${r.message}`,!0)}let e=r instanceof Error?r.message:String(r);return p(`${t} failed: ${e}`,!0)}import{existsSync as st,readFileSync as ya}from"fs";import{join as lt}from"path";import{execFileSync as Ad}from"child_process";function ho(r){let t=lt(r,".env.local"),e=[];return st(t)?(ya(t,"utf-8").match(/^AUTH_SECRET=(.*)$/m)?.[1]?.trim()||e.push({check:"env",message:"AUTH_SECRET is missing from .env.local. Add a random secret for authentication.",file:".env.local"}),e):(e.push({check:"env",message:"Missing .env.local file. Create one with your local development environment variables (AUTH_SECRET, etc.).",file:".env.local"}),e)}function go(r){let t=lt(r,"app","api","auth","[...all]","route.ts");return st(t)?[]:[{check:"auth-routes",message:"Missing Better Auth catch-all route at app/api/auth/[...all]/route.ts. Create this file to handle authentication.",file:"app/api/auth/[...all]/route.ts"}]}function mo(r){let t=lt(r,"app","api","health","route.ts");return st(t)?[]:[{check:"health-endpoint",message:"Missing health check endpoint at app/api/health/route.ts. Create this file so Mistflow can verify your deployment.",file:"app/api/health/route.ts"}]}function fo(r){let t=lt(r,"mistflow.json");if(!st(t))return[];let e;try{e=JSON.parse(ya(t,"utf-8"))}catch{return[]}let a=e.env;if(!a?.required||typeof a.required!="object")return[];let o=[],i=lt(r,".env"),n="";try{st(i)&&(n=ya(i,"utf-8"))}catch{}for(let[s,l]of Object.entries(a.required)){let d=!!process.env[s],u=n.includes(`${s}=`);if(d||u)continue;let c=l?.description?` for '${l.description}'`:"";o.push({check:"required-env",message:`${s} is required${c}. Run mist_config to set it if you haven't already.`})}return o}function Zr(r){let t=[...ho(r),...go(r),...mo(r)],e=[...fo(r)];return{passed:t.length===0,errors:t,warnings:e}}import{existsSync as ge,readFileSync as _e,readdirSync as Lt,statSync as dt}from"fs";import{join as j}from"path";async function en(r,t){let e=[];e.push(bo(r)),e.push(yo(r)),e.push(xo(r)),e.push(wo(r)),e.push(vo(r)),e.push(To(r)),t?.plan&&e.push(...ko(r,t)),e.push(So(r)),e.push(Co(r)),e.push(...Bo(r)),e.push(Po(r));let a=e.filter(d=>d.status==="fail"),o=e.filter(d=>d.status==="warn"),i=e.filter(d=>d.status==="pass"),n=a.length>0,s=!n,l;return s&&o.length===0?l=`All ${e.length} checks passed`:s?l=`${i.length}/${e.length} passed, ${o.length} warning(s)`:l=`${a.length} issue(s) found:
10920
10920
  ${a.map(d=>` - ${d.message}`).join(`
10921
- `)}`,{passed:s,blocking:n,checksRun:e.length,checks:e,summary:l}}function mo(r){let t=j(r,".open-next","worker.js");if(!ge(t))return{name:"worker-exists",status:"fail",message:"worker.js not found in build output. The build may have failed silently."};let e=_e(t,"utf-8");return e.length<1e3?{name:"worker-exists",status:"warn",message:`worker.js is unusually small (${e.length} bytes). The build output may be incomplete.`}:{name:"worker-exists",status:"pass",message:"Worker bundle exists"}}function fo(r){let t=j(r,".open-next","assets");if(!ge(t))return{name:"assets-exist",status:"warn",message:"No static assets directory in build output. CSS and images may not load."};let e=0;function a(o){try{for(let i of Rt(o)){let n=j(o,i);try{dt(n).isDirectory()?a(n):e++}catch{}}}catch{}}return a(t),e===0?{name:"assets-exist",status:"warn",message:"Assets directory is empty. Your app may not have styles or images."}:{name:"assets-exist",status:"pass",message:`${e} static assets ready`}}function bo(r){let t=j(r,"app","api","health","route.ts");return ge(t)?{name:"health-route",status:"pass",message:"Health endpoint found"}:{name:"health-route",status:"warn",message:"No health endpoint found. Deployment verification may fail."}}function yo(r){let t=j(r,"app","api","auth","[...all]","route.ts");return ge(t)?{name:"auth-route",status:"pass",message:"Auth routes found"}:{name:"auth-route",status:"warn",message:"Auth catch-all route not found. Login/register may not work."}}function xo(r){let t=j(r,".open-next","worker.js");if(!ge(t))return{name:"worker-size",status:"pass",message:"Worker size check skipped"};let a=dt(t).size/(1024*1024);return a>60?{name:"worker-size",status:"fail",message:`Worker bundle is ${a.toFixed(1)}MB \u2014 exceeds Cloudflare's 64MB uncompressed limit.`}:a>30?{name:"worker-size",status:"warn",message:`Worker bundle is ${a.toFixed(1)}MB \u2014 large but within Cloudflare limits. May be slow to upload.`}:{name:"worker-size",status:"pass",message:`Worker bundle: ${a.toFixed(1)}MB`}}function wo(r,t){let e=[],a=new Set;if(t.plan?.pages)for(let n of t.plan.pages){let s=n.path??n.route??n.name;s&&a.add(s.replace(/^\//,""))}if(a.size===0)return[];let o=0,i=[];for(let n of a){if(n.startsWith("api/")||n==="login"||n==="register"||n==="sign-in"||n==="sign-up"){o++;continue}[j(r,"app","(dashboard)",n,"page.tsx"),j(r,"app","(dashboard)",n,"page.ts"),j(r,"app",n,"page.tsx"),j(r,"app",n,"page.ts"),j(r,"app","(admin)",n,"page.tsx")].some(l=>ge(l))?o++:i.push(`/${n}`)}return i.length===0?e.push({name:"plan-routes",status:"pass",message:`All ${a.size} planned pages found`}):i.length<=2?e.push({name:"plan-routes",status:"warn",message:`${o}/${a.size} planned pages found. Missing: ${i.join(", ")}`}):e.push({name:"plan-routes",status:"warn",message:`Only ${o}/${a.size} planned pages found. ${i.length} pages missing.`}),e}function vo(r){let t=j(r,"app");if(!ge(t))return{name:"empty-pages",status:"pass",message:"App directory check skipped"};let e=[];function a(o){try{for(let i of Rt(o)){let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i==="page.tsx"||i==="page.ts"){let l=_e(n,"utf-8").trim();if(l.length<50||l.includes("export default function")&&l.includes("TODO")){let d=n.replace(r+"/","");e.push(d)}}}catch{}}}catch{}}return a(t),e.length>0?{name:"empty-pages",status:"warn",message:`${e.length} page(s) appear to be empty/placeholder: ${e.slice(0,3).join(", ")}`}:{name:"empty-pages",status:"pass",message:"No empty pages detected"}}function ko(r){let t=j(r,"app","page.tsx");if(!ge(t))return{name:"landing-page",status:"warn",message:"No root page.tsx found"};let e=_e(t,"utf-8");if(e.includes('redirect("/login")')||e.includes('redirect("/register")')){let a=j(r,"middleware.ts"),o=ge(a)?_e(a,"utf-8"):"";if(!(o.includes('"/"')||o.includes("'/'")))return{name:"landing-page",status:"fail",message:"Landing page (app/page.tsx) is just a redirect to /login, and middleware does not allow '/' as public. Users will never see a landing page."}}return e.length<200?{name:"landing-page",status:"warn",message:`Landing page is very small (${e.length} chars) \u2014 may be a placeholder.`}:{name:"landing-page",status:"pass",message:"Landing page has content"}}function So(r){let t=j(r,"app");if(!ge(t))return{name:"cookies-in-actions",status:"pass",message:"No app directory"};let e=[];function a(o){try{for(let i of Rt(o)){if(i==="node_modules"||i===".next"||i===".open-next")continue;let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i==="actions.ts"||i==="actions.tsx"){let l=_e(n,"utf-8");l.includes("use server")&&l.includes("cookies()")&&l.includes(".set(")&&e.push(n.replace(r+"/",""))}}catch{}}}catch{}}return a(t),e.length>0?{name:"cookies-in-actions",status:"fail",message:`${e.length} server action(s) use cookies().set() which crashes on Cloudflare Workers: ${e.join(", ")}. Use a database field or form parameter instead.`}:{name:"cookies-in-actions",status:"pass",message:"No cookies().set() in server actions"}}function Co(r){let t=j(r,"app");if(!ge(t))return{name:"fake-forms",status:"pass",message:"No app directory"};let e=[];function a(o){try{for(let i of Rt(o)){if(i==="node_modules"||i===".next"||i===".open-next")continue;let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i.endsWith(".tsx")||i.endsWith(".ts")){let l=_e(n,"utf-8");if((l.includes("setTimeout")||l.includes("new Promise"))&&l.includes("Simulate")||l.includes("simulate")||l.includes("// TODO")&&l.includes("API")){let d=n.replace(r+"/","");e.push(d)}}}catch{}}}catch{}}return a(t),e.length>0?{name:"fake-forms",status:"fail",message:`${e.length} file(s) have fake/simulated API calls instead of real server actions: ${e.slice(0,3).join(", ")}. Forms must use 'use server' actions that write to the database.`}:{name:"fake-forms",status:"pass",message:"No fake form handlers detected"}}function To(r){let e=[j(r,"components","sidebar.tsx"),j(r,"components","topnav.tsx"),j(r,"components","nav.tsx")].find(s=>ge(s));if(!e)return[];let i=(_e(e,"utf-8").match(/href:\s*"([^"]+)"/g)??[]).map(s=>s.replace(/href:\s*"/,"").replace(/"$/,"")).filter(s=>!s.startsWith("/api")&&!s.includes("["));if(i.length===0)return[];let n=[];for(let s of i){let l=s.replace(/^\//,"");if(!l||l==="")continue;[j(r,"app","(dashboard)",l,"page.tsx"),j(r,"app","(dashboard)",l,"page.ts"),j(r,"app","(app)",l,"page.tsx"),j(r,"app",l,"page.tsx"),j(r,"app",l,"page.ts")].some(u=>ge(u))||n.push(s)}return n.length>0?[{name:"nav-links",status:"fail",message:`Sidebar/nav links to pages that don't exist: ${n.join(", ")}. These will 404.`}]:[{name:"nav-links",status:"pass",message:`All ${i.length} nav links have matching pages`}]}function Ce(r,t){return Ao("git",r,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function fa(r){try{return Ce(["rev-parse","--is-inside-work-tree"],r),!0}catch{return!1}}function en(r){try{return Ce(["status","--porcelain"],r).length>0}catch{return!1}}function tn(r,t){return Ce(["add","-A"],r),Ce(["commit","-m",t,"--allow-empty-message"],r),Ce(["rev-parse","HEAD"],r)}function Ro(r){return Ce(["rev-parse","HEAD"],r)}function an(r){try{return Ce(["remote","get-url","origin"],r),!0}catch{return!1}}function Lo(r){try{let t=Ce(["rev-parse","--abbrev-ref","HEAD"],r);return Ce(["push","origin",t],r),{success:!0}}catch(t){return{success:!1,error:t instanceof Error?t.message:"push failed"}}}function No(r){let t=Nt(on(r)),e=Nt(t)===t?t:"/";for(;t!==e&&t!==Nt(t);){if(se(X(t,"pnpm-workspace.yaml"))||se(X(t,"lerna.json")))return t;let a=X(t,"package.json");if(se(a))try{if(JSON.parse(wa(a,"utf-8")).workspaces)return t}catch{}t=Nt(t)}return null}function Fo(r){let t=X(Mo(),"mistflow-build");Bo(t,{recursive:!0});let e=X(t,`${Po(r)}-${Date.now()}`);return ba(r,e,{recursive:!0,filter:a=>{let o=a.slice(r.length);return!(o.startsWith("/.git")||o.startsWith("\\.git")||o==="/node_modules"||o.startsWith("/node_modules/")||o==="/.open-next"||o.startsWith("/.open-next/")||o==="/.next"||o.startsWith("/.next/"))}}),e}function Eo(r,t){let e=X(r,".open-next"),a=X(r,".next");if(se(e)){let o=X(t,".open-next");se(o)&&ya(o,{recursive:!0}),ba(e,o,{recursive:!0})}if(se(a)){let o=X(t,".next");se(o)&&ya(o,{recursive:!0}),ba(a,o,{recursive:!0})}}function Uo(r){try{ya(r,{recursive:!0,force:!0})}catch{console.error(`[deploy] Failed to clean up isolated build dir: ${r}`)}}function Ze(r,t,e,a,o,i){return new Promise(n=>{let s=Io(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:a,...i?{env:i}:{}}),l="",d="";s.stdout?.on("data",u=>{let c=u.toString();if(d+=c,o)for(let h of c.split(`
10921
+ `)}`,{passed:s,blocking:n,checksRun:e.length,checks:e,summary:l}}function bo(r){let t=j(r,".open-next","worker.js");if(!ge(t))return{name:"worker-exists",status:"fail",message:"worker.js not found in build output. The build may have failed silently."};let e=_e(t,"utf-8");return e.length<1e3?{name:"worker-exists",status:"warn",message:`worker.js is unusually small (${e.length} bytes). The build output may be incomplete.`}:{name:"worker-exists",status:"pass",message:"Worker bundle exists"}}function yo(r){let t=j(r,".open-next","assets");if(!ge(t))return{name:"assets-exist",status:"warn",message:"No static assets directory in build output. CSS and images may not load."};let e=0;function a(o){try{for(let i of Lt(o)){let n=j(o,i);try{dt(n).isDirectory()?a(n):e++}catch{}}}catch{}}return a(t),e===0?{name:"assets-exist",status:"warn",message:"Assets directory is empty. Your app may not have styles or images."}:{name:"assets-exist",status:"pass",message:`${e} static assets ready`}}function xo(r){let t=j(r,"app","api","health","route.ts");return ge(t)?{name:"health-route",status:"pass",message:"Health endpoint found"}:{name:"health-route",status:"warn",message:"No health endpoint found. Deployment verification may fail."}}function wo(r){let t=j(r,"app","api","auth","[...all]","route.ts");return ge(t)?{name:"auth-route",status:"pass",message:"Auth routes found"}:{name:"auth-route",status:"warn",message:"Auth catch-all route not found. Login/register may not work."}}function vo(r){let t=j(r,".open-next","worker.js");if(!ge(t))return{name:"worker-size",status:"pass",message:"Worker size check skipped"};let a=dt(t).size/(1024*1024);return a>60?{name:"worker-size",status:"fail",message:`Worker bundle is ${a.toFixed(1)}MB \u2014 exceeds Cloudflare's 64MB uncompressed limit.`}:a>30?{name:"worker-size",status:"warn",message:`Worker bundle is ${a.toFixed(1)}MB \u2014 large but within Cloudflare limits. May be slow to upload.`}:{name:"worker-size",status:"pass",message:`Worker bundle: ${a.toFixed(1)}MB`}}function ko(r,t){let e=[],a=new Set;if(t.plan?.pages)for(let n of t.plan.pages){let s=n.path??n.route??n.name;s&&a.add(s.replace(/^\//,""))}if(a.size===0)return[];let o=0,i=[];for(let n of a){if(n.startsWith("api/")||n==="login"||n==="register"||n==="sign-in"||n==="sign-up"){o++;continue}[j(r,"app","(dashboard)",n,"page.tsx"),j(r,"app","(dashboard)",n,"page.ts"),j(r,"app",n,"page.tsx"),j(r,"app",n,"page.ts"),j(r,"app","(admin)",n,"page.tsx")].some(l=>ge(l))?o++:i.push(`/${n}`)}return i.length===0?e.push({name:"plan-routes",status:"pass",message:`All ${a.size} planned pages found`}):i.length<=2?e.push({name:"plan-routes",status:"warn",message:`${o}/${a.size} planned pages found. Missing: ${i.join(", ")}`}):e.push({name:"plan-routes",status:"warn",message:`Only ${o}/${a.size} planned pages found. ${i.length} pages missing.`}),e}function So(r){let t=j(r,"app");if(!ge(t))return{name:"empty-pages",status:"pass",message:"App directory check skipped"};let e=[];function a(o){try{for(let i of Lt(o)){let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i==="page.tsx"||i==="page.ts"){let l=_e(n,"utf-8").trim();if(l.length<50||l.includes("export default function")&&l.includes("TODO")){let d=n.replace(r+"/","");e.push(d)}}}catch{}}}catch{}}return a(t),e.length>0?{name:"empty-pages",status:"warn",message:`${e.length} page(s) appear to be empty/placeholder: ${e.slice(0,3).join(", ")}`}:{name:"empty-pages",status:"pass",message:"No empty pages detected"}}function Co(r){let t=j(r,"app","page.tsx");if(!ge(t))return{name:"landing-page",status:"warn",message:"No root page.tsx found"};let e=_e(t,"utf-8");if(e.includes('redirect("/login")')||e.includes('redirect("/register")')){let a=j(r,"middleware.ts"),o=ge(a)?_e(a,"utf-8"):"";if(!(o.includes('"/"')||o.includes("'/'")))return{name:"landing-page",status:"fail",message:"Landing page (app/page.tsx) is just a redirect to /login, and middleware does not allow '/' as public. Users will never see a landing page."}}return e.length<200?{name:"landing-page",status:"warn",message:`Landing page is very small (${e.length} chars) \u2014 may be a placeholder.`}:{name:"landing-page",status:"pass",message:"Landing page has content"}}function To(r){let t=j(r,"app");if(!ge(t))return{name:"cookies-in-actions",status:"pass",message:"No app directory"};let e=[];function a(o){try{for(let i of Lt(o)){if(i==="node_modules"||i===".next"||i===".open-next")continue;let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i==="actions.ts"||i==="actions.tsx"){let l=_e(n,"utf-8");l.includes("use server")&&l.includes("cookies()")&&l.includes(".set(")&&e.push(n.replace(r+"/",""))}}catch{}}}catch{}}return a(t),e.length>0?{name:"cookies-in-actions",status:"fail",message:`${e.length} server action(s) use cookies().set() which crashes on Cloudflare Workers: ${e.join(", ")}. Use a database field or form parameter instead.`}:{name:"cookies-in-actions",status:"pass",message:"No cookies().set() in server actions"}}function Po(r){let t=j(r,"app");if(!ge(t))return{name:"fake-forms",status:"pass",message:"No app directory"};let e=[];function a(o){try{for(let i of Lt(o)){if(i==="node_modules"||i===".next"||i===".open-next")continue;let n=j(o,i);try{if(dt(n).isDirectory())a(n);else if(i.endsWith(".tsx")||i.endsWith(".ts")){let l=_e(n,"utf-8");if((l.includes("setTimeout")||l.includes("new Promise"))&&l.includes("Simulate")||l.includes("simulate")||l.includes("// TODO")&&l.includes("API")){let d=n.replace(r+"/","");e.push(d)}}}catch{}}}catch{}}return a(t),e.length>0?{name:"fake-forms",status:"fail",message:`${e.length} file(s) have fake/simulated API calls instead of real server actions: ${e.slice(0,3).join(", ")}. Forms must use 'use server' actions that write to the database.`}:{name:"fake-forms",status:"pass",message:"No fake form handlers detected"}}function Bo(r){let e=[j(r,"components","sidebar.tsx"),j(r,"components","topnav.tsx"),j(r,"components","nav.tsx")].find(s=>ge(s));if(!e)return[];let i=(_e(e,"utf-8").match(/href:\s*"([^"]+)"/g)??[]).map(s=>s.replace(/href:\s*"/,"").replace(/"$/,"")).filter(s=>!s.startsWith("/api")&&!s.includes("["));if(i.length===0)return[];let n=[];for(let s of i){let l=s.replace(/^\//,"");if(!l||l==="")continue;[j(r,"app","(dashboard)",l,"page.tsx"),j(r,"app","(dashboard)",l,"page.ts"),j(r,"app","(app)",l,"page.tsx"),j(r,"app",l,"page.tsx"),j(r,"app",l,"page.ts")].some(u=>ge(u))||n.push(s)}return n.length>0?[{name:"nav-links",status:"fail",message:`Sidebar/nav links to pages that don't exist: ${n.join(", ")}. These will 404.`}]:[{name:"nav-links",status:"pass",message:`All ${i.length} nav links have matching pages`}]}function Te(r,t){return Mo("git",r,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function xa(r){try{return Te(["rev-parse","--is-inside-work-tree"],r),!0}catch{return!1}}function an(r){try{return Te(["status","--porcelain"],r).length>0}catch{return!1}}function rn(r,t){return Te(["add","-A"],r),Te(["commit","-m",t,"--allow-empty-message"],r),Te(["rev-parse","HEAD"],r)}function No(r){return Te(["rev-parse","HEAD"],r)}function nn(r){try{return Te(["remote","get-url","origin"],r),!0}catch{return!1}}function Fo(r){try{let t=Te(["rev-parse","--abbrev-ref","HEAD"],r);return Te(["push","origin",t],r),{success:!0}}catch(t){return{success:!1,error:t instanceof Error?t.message:"push failed"}}}function Eo(r){let t=Ft(ln(r)),e=Ft(t)===t?t:"/";for(;t!==e&&t!==Ft(t);){if(le(Q(t,"pnpm-workspace.yaml"))||le(Q(t,"lerna.json")))return t;let a=Q(t,"package.json");if(le(a))try{if(JSON.parse(Sa(a,"utf-8")).workspaces)return t}catch{}t=Ft(t)}return null}function Uo(r){let t=Q(Lo(),"mistflow-build");Ao(t,{recursive:!0});let e=Q(t,`${Do(r)}-${Date.now()}`);return wa(r,e,{recursive:!0,filter:a=>{let o=a.slice(r.length);return!(o.startsWith("/.git")||o.startsWith("\\.git")||o==="/node_modules"||o.startsWith("/node_modules/")||o==="/.open-next"||o.startsWith("/.open-next/")||o==="/.next"||o.startsWith("/.next/"))}}),e}function Ho(r,t){let e=Q(r,".open-next"),a=Q(r,".next");if(le(e)){let o=Q(t,".open-next");le(o)&&va(o,{recursive:!0}),wa(e,o,{recursive:!0})}if(le(a)){let o=Q(t,".next");le(o)&&va(o,{recursive:!0}),wa(a,o,{recursive:!0})}}function Go(r){try{va(r,{recursive:!0,force:!0})}catch{console.error(`[deploy] Failed to clean up isolated build dir: ${r}`)}}function Ze(r,t,e,a,o,i){return new Promise(n=>{let s=Ro(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:a,...i?{env:i}:{}}),l="",d="";s.stdout?.on("data",u=>{let c=u.toString();if(d+=c,o)for(let h of c.split(`
10922
10922
  `).filter(Boolean))o(h)}),s.stderr?.on("data",u=>{let c=u.toString();if(l+=c,o)for(let h of c.split(`
10923
- `).filter(Boolean))o(h)}),s.on("close",(u,c)=>{n({success:u===0,stdout:d,stderr:l,signal:c})}),s.on("error",u=>{n({success:!1,stdout:d,stderr:l+u.message})})})}var _d=Lt.object({projectPath:Lt.string().optional().describe("Path to the project directory (default: current working directory)"),message:Lt.string().optional().describe("Deploy message"),environment:Lt.enum(["production","preview"]).optional().default("production").describe("Target environment: 'production' (default) or 'preview' for a shareable URL")});function Ho(r){return new Promise(t=>setTimeout(t,r))}function rn(r){switch(r){case"pending":return"Provisioning database...";case"building":return"Building your app...";case"deploying":return"Deploying to Cloudflare...";case"verifying":return"Verifying deployment...";default:return`Status: ${r}`}}function xa(r){let t=X(r,"mistflow.json");if(se(t))try{return JSON.parse(wa(t,"utf-8"))}catch{}return{}}function Go(r,t){let e=X(r,"mistflow.json"),a=xa(r),o=a.deploy?.count??a.deployCount??0;a.deploy={url:t,count:o+1,lastDeployedAt:new Date().toISOString()};let i=a.plan?.steps;if(Array.isArray(i))for(let n of i)n.status==="in_progress"&&(n.status="completed");delete a.deployUrl,delete a.deployCount,sn(e,JSON.stringify(a,null,2)+`
10924
- `)}async function ln(r,t){let a=process.platform==="win32"?"npx.cmd":"npx",o=0,i=X(r,"node_modules",".bin","opennextjs-cloudflare"),n=se(i),s={...process.env,NODE_ENV:"production"},l=await Ze(n?i:a,n?["build"]:["@opennextjs/cloudflare","build"],r,3e5,h=>{h.includes("Compiling")?t?.("Compiling your app..."):h.includes("Collecting page data")?t?.("Collecting page data..."):h.includes("Generating static pages")?t?.("Generating static pages..."):h.match(/^[○●◐λƒ]\s/)?o++:h.includes("Creating Cloudflare worker")?t?.("Creating Cloudflare worker..."):h.includes("Build completed")&&t?.("Build completed!")},s);if(l.success)return{success:!0,buildStats:o>0?`${o} routes compiled`:"build complete"};let d=l.stderr+`
10923
+ `).filter(Boolean))o(h)}),s.on("close",(u,c)=>{n({success:u===0,stdout:d,stderr:l,signal:c})}),s.on("error",u=>{n({success:!1,stdout:d,stderr:l+u.message})})})}var Vd=Nt.object({projectPath:Nt.string().optional().describe("Path to the project directory (default: current working directory)"),message:Nt.string().optional().describe("Deploy message"),environment:Nt.enum(["production","preview"]).optional().default("production").describe("Target environment: 'production' (default) or 'preview' for a shareable URL")});function Wo(r){return new Promise(t=>setTimeout(t,r))}function on(r){switch(r){case"pending":return"Provisioning database...";case"building":return"Building your app...";case"deploying":return"Deploying to Cloudflare...";case"verifying":return"Verifying deployment...";default:return`Status: ${r}`}}function ka(r){let t=Q(r,"mistflow.json");if(le(t))try{return JSON.parse(Sa(t,"utf-8"))}catch{}return{}}function Oo(r,t){let e=Q(r,"mistflow.json"),a=ka(r),o=a.deploy?.count??a.deployCount??0;a.deploy={url:t,count:o+1,lastDeployedAt:new Date().toISOString()};let i=a.plan?.steps;if(Array.isArray(i))for(let n of i)n.status==="in_progress"&&(n.status="completed");delete a.deployUrl,delete a.deployCount,dn(e,JSON.stringify(a,null,2)+`
10924
+ `)}async function cn(r,t){let a=process.platform==="win32"?"npx.cmd":"npx",o=0,i=Q(r,"node_modules",".bin","opennextjs-cloudflare"),n=le(i),s={...process.env,NODE_ENV:"production"},l=await Ze(n?i:a,n?["build"]:["@opennextjs/cloudflare","build"],r,3e5,h=>{h.includes("Compiling")?t?.("Compiling your app..."):h.includes("Collecting page data")?t?.("Collecting page data..."):h.includes("Generating static pages")?t?.("Generating static pages..."):h.match(/^[○●◐λƒ]\s/)?o++:h.includes("Creating Cloudflare worker")?t?.("Creating Cloudflare worker..."):h.includes("Build completed")&&t?.("Build completed!")},s);if(l.success)return{success:!0,buildStats:o>0?`${o} routes compiled`:"build complete"};let d=l.stderr+`
10925
10925
  `+l.stdout;if(l.signal==="SIGKILL"||d.includes("SIGKILL")||d.includes("exit code: 137"))return{success:!1,signal:"SIGKILL",error:"Your app ran out of memory while building. This sometimes happens with complex projects. Try deploying again \u2014 if it keeps failing, we'll help you figure it out."};let u=d.match(/Package subpath ['"]([^'"]+)['"] is not defined by "exports" in .*?node_modules\/([^/]+(?:\/[^/]+)?)\//);if(u){let[,h,g]=u;return{success:!1,error:`Version conflict: '${g}' does not export '${h}'. This happens when multiple dependencies need different major versions of '${g}'. Fix: add "${g}" at the version that exports '${h}' as an optionalDependency in package.json (this pins it at root level and lets the other version nest). Then delete node_modules and package-lock.json, and run npm install.`}}let c=d.split(`
10926
10926
  `).filter(h=>h.includes("error")||h.includes("Error")).slice(0,10);return{success:!1,error:c.length>0?`OpenNext build failed:
10927
10927
  ${c.join(`
10928
10928
  `)}`:`OpenNext build failed:
10929
- ${d.slice(-500)}`}}async function Wo(r,t){let e=No(r);if(e)return console.error(`[deploy] Project is inside monorepo at ${e} \u2014 building in isolated temp directory`),t?.("Detected parent project folder \u2014 building in isolated directory..."),nn(r,t);let a=await ln(r,t);return!a.success&&a.signal==="SIGKILL"?(console.error("[deploy] Build was OOM-killed \u2014 retrying in isolated temp directory"),t?.("Build ran out of memory \u2014 retrying with a fresh setup..."),nn(r,t)):a}async function nn(r,t){let e;try{t?.("Copying project to isolated build directory..."),e=Fo(r),t?.("Installing dependencies...");let o=process.platform==="win32"?"npm.cmd":"npm",i=await Ze(o,["install","--prefer-offline"],e,12e4);if(!i.success)return{success:!1,error:`Failed to install dependencies in isolated build:
10930
- ${i.stderr.slice(-300)}`};await Ze(o,["dedupe"],e,6e4);let n=await ln(e,t);return n.success&&(t?.("Copying build artifacts..."),Eo(e,r)),{...n,builtInIsolation:e}}finally{e&&Uo(e)}}async function Oo(r,t,e){if(!se(X(r,"db","schema")))return{success:!0,skipped:!0,skipReason:"No database structure found"};let a=X(r,"db","schema");try{if(Do(a).filter(g=>g.endsWith(".ts")).length===0)return{success:!0,skipped:!0,skipReason:"No database files found in db/schema/"}}catch{return{success:!0,skipped:!0,skipReason:"Could not read db/schema/ directory"}}if(!se(X(r,"drizzle.config.ts")))return{success:!0,skipped:!0,skipReason:"No drizzle.config.ts found"};let o;try{o=await Ua(t)}catch{return console.error("[deploy] Could not fetch DB credentials \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"Could not fetch database credentials \u2014 backend will handle the database update"}}let{db_provider:i,credentials:n}=o;if(i==="neon"){if(!n.DATABASE_URL)return console.error("[deploy] No DATABASE_URL found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No DATABASE_URL configured \u2014 set it with mist_config"}}else if(!n.TURSO_URL||!n.TURSO_AUTH_TOKEN)return console.error("[deploy] No Turso credentials found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No Turso credentials configured"};let s={PATH:process.env.PATH??"",HOME:process.env.HOME??"",NODE_PATH:process.env.NODE_PATH??"",TMPDIR:process.env.TMPDIR??"",...n},d=process.platform==="win32"?"npx.cmd":"npx",c=await Ze(d,e?["drizzle-kit","push","--force"]:["drizzle-kit","push"],r,6e4,h=>{let g=h.replace(/(?:TURSO_AUTH_TOKEN|DATABASE_URL|AUTH_SECRET)=[^\s&]*/gi,y=>y.split("=")[0]+"=REDACTED").replace(/postgresql:\/\/[^@]*@/g,"postgresql://REDACTED@").replace(/libsql:\/\/[^\s]*/g,"libsql://REDACTED");console.error(`[drizzle-kit] ${g}`)},s);if(!c.success){let h=(c.stderr+`
10929
+ ${d.slice(-500)}`}}async function _o(r,t){let e=Eo(r);if(e)return console.error(`[deploy] Project is inside monorepo at ${e} \u2014 building in isolated temp directory`),t?.("Detected parent project folder \u2014 building in isolated directory..."),sn(r,t);let a=await cn(r,t);return!a.success&&a.signal==="SIGKILL"?(console.error("[deploy] Build was OOM-killed \u2014 retrying in isolated temp directory"),t?.("Build ran out of memory \u2014 retrying with a fresh setup..."),sn(r,t)):a}async function sn(r,t){let e;try{t?.("Copying project to isolated build directory..."),e=Uo(r),t?.("Installing dependencies...");let o=process.platform==="win32"?"npm.cmd":"npm",i=await Ze(o,["install","--prefer-offline"],e,12e4);if(!i.success)return{success:!1,error:`Failed to install dependencies in isolated build:
10930
+ ${i.stderr.slice(-300)}`};await Ze(o,["dedupe"],e,6e4);let n=await cn(e,t);return n.success&&(t?.("Copying build artifacts..."),Ho(e,r)),{...n,builtInIsolation:e}}finally{e&&Go(e)}}async function jo(r,t,e){if(!le(Q(r,"db","schema")))return{success:!0,skipped:!0,skipReason:"No database structure found"};let a=Q(r,"db","schema");try{if(Io(a).filter(g=>g.endsWith(".ts")).length===0)return{success:!0,skipped:!0,skipReason:"No database files found in db/schema/"}}catch{return{success:!0,skipped:!0,skipReason:"Could not read db/schema/ directory"}}if(!le(Q(r,"drizzle.config.ts")))return{success:!0,skipped:!0,skipReason:"No drizzle.config.ts found"};let o;try{o=await Ga(t)}catch{return console.error("[deploy] Could not fetch DB credentials \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"Could not fetch database credentials \u2014 backend will handle the database update"}}let{db_provider:i,credentials:n}=o;if(i==="neon"){if(!n.DATABASE_URL)return console.error("[deploy] No DATABASE_URL found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No DATABASE_URL configured \u2014 set it with mist_config"}}else if(!n.TURSO_URL||!n.TURSO_AUTH_TOKEN)return console.error("[deploy] No Turso credentials found \u2014 skipping local schema push"),{success:!0,skipped:!0,skipReason:"No Turso credentials configured"};let s={PATH:process.env.PATH??"",HOME:process.env.HOME??"",NODE_PATH:process.env.NODE_PATH??"",TMPDIR:process.env.TMPDIR??"",...n},d=process.platform==="win32"?"npx.cmd":"npx",c=await Ze(d,e?["drizzle-kit","push","--force"]:["drizzle-kit","push"],r,6e4,h=>{let g=h.replace(/(?:TURSO_AUTH_TOKEN|DATABASE_URL|AUTH_SECRET)=[^\s&]*/gi,y=>y.split("=")[0]+"=REDACTED").replace(/postgresql:\/\/[^@]*@/g,"postgresql://REDACTED@").replace(/libsql:\/\/[^\s]*/g,"libsql://REDACTED");console.error(`[drizzle-kit] ${g}`)},s);if(!c.success){let h=(c.stderr+`
10931
10931
  `+c.stdout).trim(),g=h.toLowerCase();if(g.includes("data-loss")||g.includes("cannot be reverted"))return{success:!1,error:"Database update blocked: your database has existing data that would be deleted. The update includes changes that would remove existing data. If you're OK with losing the data, deploy again with forceSchema: true."};let y=h.split(`
10932
10932
  `).filter(m=>m.includes("error")||m.includes("Error")||m.includes("ERR")).slice(0,5);return{success:!1,error:`Database update failed:
10933
10933
  ${y.length>0?y.join(`
10934
- `):h.slice(-500)}`}}return{success:!0}}async function dn(r){let{projectPath:t,message:e,environment:a="production",forceSchema:o}=r,i=a,n=on(t??process.cwd());if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let s=Qr(n);if(!s.passed){let B=s.errors.map(M=>{let I=`[${M.check}] ${M.message}`;return M.file&&(I+=` (${M.file}${M.line?`:${M.line}`:""})`),I}).join(`
10934
+ `):h.slice(-500)}`}}return{success:!0}}async function pn(r){let{projectPath:t,message:e,environment:a="production",forceSchema:o}=r,i=a,n=ln(t??process.cwd());if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let s=Zr(n);if(!s.passed){let P=s.errors.map(R=>{let M=`[${R.check}] ${R.message}`;return R.file&&(M+=` (${R.file}${R.line?`:${R.line}`:""})`),M}).join(`
10935
10935
  `);return p(`Deploy blocked \u2014 fix these issues first:
10936
10936
 
10937
- ${B}`,!0)}let l=s.warnings.map(B=>`[${B.check}] ${B.message}`),d;if(fa(n))if(en(n))try{let B=xa(n).deploy?.count??0,M=e?`Deploy v${B+1}: ${e}`:`Deploy v${B+1}`;d=tn(n,M)}catch{console.error("[deploy] Pre-deploy git commit failed, continuing")}else try{d=Ro(n)}catch{}let u=ke(n),c=u?.projectId;if(!c){let B=X(n,"mistflow.json");if(!se(B))return p("No mistflow.json found. Run mist_build (action: 'init') first to set up your project.",!0);try{let M=JSON.parse(wa(B,"utf-8")),I=M.name;if(!I)return p("mistflow.json is missing a project name. Run mist_build (action: 'init') to set up your project.",!0);c=(await ft(I)).id,M.projectId=c,sn(B,JSON.stringify(M,null,2)+`
10938
- `)}catch(M){let I=M instanceof he?M.message:"Check your internet connection.";return p(`Could not register project with Mistflow: ${I}
10937
+ ${P}`,!0)}let l=s.warnings.map(P=>`[${P.check}] ${P.message}`),d;if(xa(n))if(an(n))try{let P=ka(n).deploy?.count??0,R=e?`Deploy v${P+1}: ${e}`:`Deploy v${P+1}`;d=rn(n,R)}catch{console.error("[deploy] Pre-deploy git commit failed, continuing")}else try{d=No(n)}catch{}let u=ke(n),c=u?.projectId;if(!c){let P=Q(n,"mistflow.json");if(!le(P))return p("No mistflow.json found. Run mist_build (action: 'init') first to set up your project.",!0);try{let R=JSON.parse(Sa(P,"utf-8")),M=R.name;if(!M)return p("mistflow.json is missing a project name. Run mist_build (action: 'init') to set up your project.",!0);c=(await bt(M)).id,R.projectId=c,dn(P,JSON.stringify(R,null,2)+`
10938
+ `)}catch(R){let M=R instanceof he?R.message:"Check your internet connection.";return p(`Could not register project with Mistflow: ${M}
10939
10939
 
10940
- Try deploying again in a moment.`,!0)}}if(c&&i==="production")try{let B=await Ma(c),M=B,I=M.deploy_count??B.deployCount??0;(M.deploy_strategy??B.deploy_strategy)==="staging"&&I>0&&(i="preview",console.error("[deploy] Staging mode enabled \u2014 auto-redirecting to preview"))}catch{}let h=[],g=Date.now(),y=(B,M)=>{let I={phase:B,message:M};return h.push(I),I},x=(B,M)=>{B.durationMs=Date.now()-g,M&&(B.message=M)},m=!1;if(c&&i!=="preview"){let B=y("schema","Updating database structure..."),M=await Oo(n,c,o);if(M.skipped)x(B,M.skipReason??"Database update skipped");else if(M.success)m=!0,x(B,"Database updated successfully");else return x(B,"Database update failed"),p(`Deploy blocked \u2014 database update failed:
10940
+ Try deploying again in a moment.`,!0)}}if(c&&i==="production")try{let P=await La(c),R=P,M=R.deploy_count??P.deployCount??0;(R.deploy_strategy??P.deploy_strategy)==="staging"&&M>0&&(i="preview",console.error("[deploy] Staging mode enabled \u2014 auto-redirecting to preview"))}catch{}let h=[],g=Date.now(),y=(P,R)=>{let M={phase:P,message:R};return h.push(M),M},x=(P,R)=>{P.durationMs=Date.now()-g,R&&(P.message=R)},m=!1;if(c&&i!=="preview"){let P=y("schema","Updating database structure..."),R=await jo(n,c,o);if(R.skipped)x(P,R.skipReason??"Database update skipped");else if(R.success)m=!0,x(P,"Database updated successfully");else return x(P,"Database update failed"),p(`Deploy blocked \u2014 database update failed:
10941
10941
 
10942
- ${M.error}
10942
+ ${R.error}
10943
10943
 
10944
- Try deploying again in a moment. If it keeps failing, check your database files in db/schema/.`,!0)}let v=y("build","Compiling your app for Cloudflare..."),P=Date.now(),T=await Wo(n,B=>{v.message=B});if(!T.success)return p(`Deploy blocked \u2014 OpenNext build failed:
10944
+ Try deploying again in a moment. If it keeps failing, check your database files in db/schema/.`,!0)}let k=y("build","Compiling your app for Cloudflare..."),T=Date.now(),C=await _o(n,P=>{k.message=P});if(!C.success)return p(`Deploy blocked \u2014 OpenNext build failed:
10945
10945
 
10946
- ${T.error}`,!0);x(v,`Build complete \u2014 ${T.buildStats??"ready"} (${((Date.now()-P)/1e3).toFixed(0)}s)`);let w=y("qa","Running smoke tests..."),R=await Xr(n,u);if(R.passed)x(w,`Smoke test passed \u2014 ${R.checksRun} checks OK`);else if(x(w,`Smoke test: ${R.summary}`),R.blocking)return p(`Deploy blocked \u2014 smoke test failed:
10946
+ ${C.error}`,!0);x(k,`Build complete \u2014 ${C.buildStats??"ready"} (${((Date.now()-T)/1e3).toFixed(0)}s)`);let w=y("qa","Running smoke tests..."),L=await en(n,u);if(L.passed)x(w,`Smoke test passed \u2014 ${L.checksRun} checks OK`);else if(x(w,`Smoke test: ${L.summary}`),L.blocking)return p(`Deploy blocked \u2014 smoke test failed:
10947
10947
 
10948
- ${R.summary}
10948
+ ${L.summary}
10949
10949
 
10950
- Fix these issues and try again.`,!0);let V=X(n,".open-next");if(!se(V))return p("Build succeeded but .open-next/ directory not found. Check your OpenNext configuration.",!0);y("package","Packaging build artifacts...");let L=X(n,".open-next-build.tar.gz"),O=[".open-next"];if(se(X(n,"db"))&&O.push("db"),se(X(n,"drizzle.config.ts"))&&O.push("drizzle.config.ts"),se(X(n,"package.json"))&&O.push("package.json"),!(await Ze("tar",["-czf",L,"-C",n,...O],n,6e4)).success)return p("Failed to create build archive. Check disk space and permissions.",!0);let z;{let B=X(n,".mistflow-source.tar.gz");(await Ze("tar",["-czf",B,"-C",n,"--exclude",".open-next","--exclude","node_modules","--exclude",".git","--exclude",".next","--exclude",".open-next-build.tar.gz","--exclude",".mistflow-source.tar.gz","."],n,6e4)).success?z=B:console.error("[deploy] Source archive creation failed, continuing without it")}y("upload","Uploading to Mistflow...");let ee=!!u.hasAdmin&&i==="production"?Da()?.email:void 0,E,N;try{let B=await La(c,L,i,ee,m,z,d);if(E=B.deployment_id??B.id,!E)return p("Upload succeeded but no deployment ID was returned. Check the Mistflow dashboard.",!0);N=B.status}catch(B){return Jr(B,"Deploy")}finally{try{Zr(L)}catch{}if(z)try{Zr(z)}catch{}}y("deploying","Deploying to the edge...");let q=Date.now(),ce=12e4,me=3e3,ie=[rn(N)],re=N;for(;Date.now()-q<ce;){await Ho(me);let B;try{B=await Be(E)}catch{continue}if(B.status!==re&&(ie.push(rn(B.status)),re=B.status),B.status==="live"){let M=((Date.now()-q)/1e3).toFixed(0),I=B.url??"";if(!I)return p("Deployment marked as live but no URL was returned. Check the Mistflow dashboard.",!0);let f=i==="preview";if(!f){Go(n,I);try{let{readLocalState:F,syncRemoteState:D}=await import("./state-manager-73ZUDKOP.js"),U=F(n);U&&c&&(U.deployCount=(U.deployCount??0)+1,D(c,U).catch(()=>{}))}catch{}if(fa(n)&&an(n))try{en(n)&&tn(n,"Update deploy metadata"),Lo(n)}catch{}}let b=f?"Preview":"App",S=xa(n),A=S.name,$=typeof A=="string"?A:"my app",H=S.features,Y=Array.isArray(H)?H.length:0,ue=S.plan,ne=ue&&typeof ue=="object"&&"steps"in ue?ue.steps:void 0,te=Array.isArray(ne)?ne.length:0,J=h.map(F=>F.message),k={url:I,time:`${M}s`,deploymentId:E,environment:i,statusLog:ie,deployTimeline:J,message:f?`${b} is live at ${I} \u2014 preview deployed in ${M}s. This URL expires in 72 hours.`:`Your app is live at ${I} \u2014 deployed in ${M}s.`};if(f)k.instruction=["STAGING FLOW: The preview is deployed with an isolated database. Verify it before going live.","","1. Call mist_build with action='qa' and url='"+I+"' to test the preview.","2. If QA passes \u2014 tell the user the preview looks good and ask: 'Ready to go live?'","3. If QA finds issues \u2014 fix them, then call mist_deploy action='deploy' environment='preview' to redeploy the preview.","4. When user confirms \u2014 call mist_deploy action='promote' to push to production (~10s).","","Show the preview URL to the user so they can check it themselves while QA runs."].join(`
10951
- `),k.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${I}' to verify the preview. After QA passes, ask user to confirm, then call mist_deploy action='promote'.`,k.qaRequired=!0,k.qaUrl=I,k.previewDeploymentId=E,k.promoteAction=`mist_deploy action='promote' deploymentId='${E}'`;else{let F=encodeURIComponent(`Just built "${$}" with AI and deployed it live in ${M}s.
10950
+ Fix these issues and try again.`,!0);let V=Q(n,".open-next");if(!le(V))return p("Build succeeded but .open-next/ directory not found. Check your OpenNext configuration.",!0);y("package","Packaging build artifacts...");let N=Q(n,".open-next-build.tar.gz"),O=[".open-next"];if(le(Q(n,"db"))&&O.push("db"),le(Q(n,"drizzle.config.ts"))&&O.push("drizzle.config.ts"),le(Q(n,"package.json"))&&O.push("package.json"),!(await Ze("tar",["-czf",N,"-C",n,...O],n,6e4)).success)return p("Failed to create build archive. Check disk space and permissions.",!0);let z;{let P=Q(n,".mistflow-source.tar.gz");(await Ze("tar",["-czf",P,"-C",n,"--exclude",".open-next","--exclude","node_modules","--exclude",".git","--exclude",".next","--exclude",".open-next-build.tar.gz","--exclude",".mistflow-source.tar.gz","."],n,6e4)).success?z=P:console.error("[deploy] Source archive creation failed, continuing without it")}y("upload","Uploading to Mistflow...");let Z=!!u.hasAdmin&&i==="production"?ft()?.email:void 0,E,F;try{let P=await Fa(c,N,i,Z,m,z,d);if(E=P.deployment_id??P.id,!E)return p("Upload succeeded but no deployment ID was returned. Check the Mistflow dashboard.",!0);F=P.status}catch(P){return Xr(P,"Deploy")}finally{try{tn(N)}catch{}if(z)try{tn(z)}catch{}}y("deploying","Deploying to the edge...");let q=Date.now(),pe=24e4,me=3e3,ne=[on(F)],ae=F;for(;Date.now()-q<pe;){await Wo(me);let P;try{P=await De(E)}catch{continue}if(P.status!==ae&&(ne.push(on(P.status)),ae=P.status),P.status==="live"){let R=((Date.now()-q)/1e3).toFixed(0),M=P.url??"";if(!M)return p("Deployment marked as live but no URL was returned. Check the Mistflow dashboard.",!0);let f=i==="preview";if(!f){Oo(n,M);try{let{readLocalState:B,syncRemoteState:A}=await import("./state-manager-73ZUDKOP.js"),U=B(n);U&&c&&(U.deployCount=(U.deployCount??0)+1,A(c,U).catch(()=>{}))}catch{}if(xa(n)&&nn(n))try{an(n)&&rn(n,"Update deploy metadata"),Fo(n)}catch{}}let b=f?"Preview":"App",S=ka(n),I=S.name,$=typeof I=="string"?I:"my app",H=S.features,K=Array.isArray(H)?H.length:0,fe=S.plan,ie=fe&&typeof fe=="object"&&"steps"in fe?fe.steps:void 0,re=Array.isArray(ie)?ie.length:0,ee=h.map(B=>B.message),v={url:M,time:`${R}s`,deploymentId:E,environment:i,statusLog:ne,deployTimeline:ee,message:f?`${b} is live at ${M} \u2014 preview deployed in ${R}s. This URL expires in 72 hours.`:`Your app is live at ${M} \u2014 deployed in ${R}s.`};if(f)v.instruction=["STAGING FLOW: The preview is deployed with an isolated database. Verify it before going live.","","1. Call mist_build with action='qa' and url='"+M+"' to test the preview.","2. If QA passes \u2014 tell the user the preview looks good and ask: 'Ready to go live?'","3. If QA finds issues \u2014 fix them, then call mist_deploy action='deploy' environment='preview' to redeploy the preview.","4. When user confirms \u2014 call mist_deploy action='promote' to push to production (~10s).","","Show the preview URL to the user so they can check it themselves while QA runs."].join(`
10951
+ `),v.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${M}' to verify the preview. After QA passes, ask user to confirm, then call mist_deploy action='promote'.`,v.qaRequired=!0,v.qaUrl=M,v.previewDeploymentId=E,v.promoteAction=`mist_deploy action='promote' deploymentId='${E}'`;else{let B=encodeURIComponent(`Just built "${$}" with AI and deployed it live in ${R}s.
10952
10952
 
10953
- ${te} steps, ${Y} features, zero config.
10953
+ ${re} steps, ${K} features, zero config.
10954
10954
 
10955
- ${I}
10955
+ ${M}
10956
10956
 
10957
- Built with @mistflow`),D=encodeURIComponent(`I just described an app idea and got a live, working URL in ${M} seconds.
10957
+ Built with @mistflow`),A=encodeURIComponent(`I just described an app idea and got a live, working URL in ${R} seconds.
10958
10958
 
10959
- "${$}" \u2014 ${te} build steps, ${Y} features, fully deployed.
10959
+ "${$}" \u2014 ${re} build steps, ${K} features, fully deployed.
10960
10960
 
10961
- ${I}
10961
+ ${M}
10962
10962
 
10963
- Built with Mistflow (mistflow.ai)`);k.share={tweetUrl:`https://twitter.com/intent/tweet?text=${F}`,linkedInUrl:`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(I)}`,celebration:["","================================================",` ${$} is LIVE!`,"================================================","",` ${I}`,"",` Deployed in ${M}s`,` ${Y>0?`${Y} features`:""}${Y>0&&te>0?" \xB7 ":""}${te>0?`${te} build steps`:""}`,""," Share it:",` Twitter \u2192 https://twitter.com/intent/tweet?text=${F}`,` LinkedIn \u2192 https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(I)}`,"","================================================"].join(`
10964
- `)},k.instruction=["IMPORTANT: The app is deployed but NOT verified yet. Before celebrating:","","1. Call mist_build with action='qa' to test the live app.","2. If QA passes \u2014 present the URL prominently and show the celebration block above.","3. If QA finds issues \u2014 fix them, redeploy (mist_deploy action='deploy'), and run qa again.","4. Keep looping until QA passes. Only celebrate when all checks are green.","","Do NOT show the URL to the user until QA passes. A broken app is worse than no app."].join(`
10965
- `),k.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${I}' to verify the app works. Do NOT tell the user the app is ready until QA passes.`,k.qaRequired=!0,k.qaUrl=I}B.admin_credentials&&(k.adminCredentials={message:`Admin account created. Save these credentials \u2014 they won't be shown again.
10963
+ Built with Mistflow (mistflow.ai)`);v.share={tweetUrl:`https://twitter.com/intent/tweet?text=${B}`,linkedInUrl:`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(M)}`,celebration:["","================================================",` ${$} is LIVE!`,"================================================","",` ${M}`,"",` Deployed in ${R}s`,` ${K>0?`${K} features`:""}${K>0&&re>0?" \xB7 ":""}${re>0?`${re} build steps`:""}`,""," Share it:",` Twitter \u2192 https://twitter.com/intent/tweet?text=${B}`,` LinkedIn \u2192 https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(M)}`,"","================================================"].join(`
10964
+ `)},v.instruction=["IMPORTANT: The app is deployed but NOT verified yet. Before celebrating:","","1. Call mist_build with action='qa' to test the live app.","2. If QA passes \u2014 present the URL prominently and show the celebration block above.","3. If QA finds issues \u2014 fix them, redeploy (mist_deploy action='deploy'), and run qa again.","4. Keep looping until QA passes. Only celebrate when all checks are green.","","Do NOT show the URL to the user until QA passes. A broken app is worse than no app."].join(`
10965
+ `),v.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${M}' to verify the app works. Do NOT tell the user the app is ready until QA passes.`,v.qaRequired=!0,v.qaUrl=M}P.admin_credentials&&(v.adminCredentials={message:`Admin account created. Save these credentials \u2014 they won't be shown again.
10966
10966
 
10967
- Email: ${B.admin_credentials.email}
10968
- Password: ${B.admin_credentials.password}
10969
- Login: ${I}/login
10970
- Admin panel: ${I}/admin`}),l.length>0&&(k.warnings=l);let C=[];if(!f){let F=JSON.stringify(S).toLowerCase();C.push({action:"Add a custom domain",command:"mist_config resource='domain' action='add' domain='yourdomain.com'",reason:"Give your app a professional URL instead of the default .mistflow.app domain.",priority:"medium"}),fa(n)&&!an(n)&&C.push({action:"Back up to GitHub",command:"Create a repo at github.com/new, then tell me the URL and I'll push your code there",reason:"Your source code is saved with every deploy, but GitHub gives you an extra backup and lets you collaborate.",priority:"medium"});let D=F.includes("user")||F.includes("account")||F.includes("member"),U=F.includes("stripe")||F.includes("payment")||F.includes("billing");D&&!U&&C.push({action:"Add payments",command:"Tell the AI: 'Add Stripe payments to my app'",reason:"Your app has user accounts \u2014 you could monetize with subscriptions or one-time payments.",priority:"low"}),C.push({action:"Share your app",command:"mist_project action='share'",reason:"Generate a shareable link so others can fork and customize your app.",priority:"low"}),C.push({action:"Make changes",command:"Describe what you want to change, then run mist_deploy action='deploy'",reason:"Edit your code and redeploy in seconds. Your app is yours to evolve.",priority:"low"}),C.push({action:"See analytics",command:`Visit ${Aa()} to see pageviews and visitors`,reason:"Track how people are using your app.",priority:"low"})}return C.length>0&&(k.nextSteps=C),De(I,JSON.stringify(k))}if(B.status==="failed")return p(B.error??"Deployment failed. Check the Mistflow dashboard for details.",!0)}return p(`Deploy is taking longer than expected. Your deploy ID is ${E} \u2014 check status later.`,!0)}import{z as Ft}from"zod";import{resolve as _o,join as jo}from"path";import{execFileSync as zo}from"child_process";var Jd=Ft.object({projectPath:Ft.string().optional().describe("Path to the project directory (default: cwd)"),action:Ft.enum(["redeploy","rollback"]).describe("Action to perform: 'redeploy' re-deploys the latest build, 'rollback' reverts to a specific previous deployment"),deploymentId:Ft.string().optional().describe("Deployment ID to rollback to (required for 'rollback')")});function Re(r,t){return zo("git",r,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function cn(r){try{return Re(["rev-parse","--is-inside-work-tree"],r),!0}catch{return!1}}async function $o(r,t,e,a){if(t&&cn(r))try{Re(["cat-file","-t",t],r);let o=!1;try{Re(["status","--porcelain"],r).length>0&&(Re(["stash","push","-m","mistflow-rollback-auto-stash"],r),o=!0)}catch{}return Re(["reset","--hard",t],r),Re(["commit","--allow-empty","-m","Rollback to previous deploy"],r),{method:"git",success:!0,...o?{warning:"Your uncommitted changes were saved to git stash. Run `git stash pop` to restore them."}:{}}}catch{}if(e&&a)try{let{existsSync:o,mkdirSync:i,rmSync:n}=await import("fs"),s=jo(r,".mistflow-rollback-source.tar.gz");await Ka(a,s);let{spawn:l}=await import("child_process");await new Promise((d,u)=>{let c=l("tar",["-xzf",s,"-C",r,"--exclude","node_modules","--exclude",".git"],{cwd:r,stdio:"pipe"});c.on("close",h=>h===0?d():u(new Error(`tar exit ${h}`))),c.on("error",u)});try{n(s)}catch{}if(cn(r))try{Re(["add","-A"],r),Re(["commit","-m","Rollback to previous deploy (from source snapshot)"],r)}catch{}return{method:"r2",success:!0}}catch(o){return{method:"r2",success:!1,error:o instanceof Error?o.message:"Source download failed"}}return{method:"none",success:!1,error:"No source snapshot available for this deployment"}}function Vo(r){return new Promise(t=>setTimeout(t,r))}function qo(r){switch(r){case"pending":return"Provisioning...";case"building":return"Building...";case"deploying":return"Deploying to Cloudflare...";case"verifying":return"Verifying deployment...";default:return`Status: ${r}`}}async function pn(r,t,e){let a=Date.now(),o=12e4,i=3e3,n=[],s="";for(;Date.now()-a<o;){await Vo(i);let l;try{l=await Be(r)}catch{continue}if(l.status!==s&&(n.push(qo(l.status)),s=l.status),l.status==="live"){let d=((Date.now()-a)/1e3).toFixed(0),u=l.url??"";if(e)try{await e()}catch{}return p(JSON.stringify({url:u,time:`${d}s`,deploymentId:r,statusLog:n,message:u?`${t} complete \u2014 live at ${u} in ${d}s.`:`${t} complete in ${d}s.`}))}if(l.status==="failed")return p(l.error??`${t} failed. Check the Mistflow dashboard for details.`,!0)}return p(`${t} is taking longer than expected. Deployment ID: ${r} \u2014 check status later.`,!0)}async function va(r){let{projectPath:t,action:e,deploymentId:a}=r,o=_o(t??process.cwd());if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let n=ke(o)?.projectId;if(!n)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"redeploy":{let s=await $a(n);return pn(s.deployment_id,"Redeploy")}case"rollback":{if(!a)return p("Deployment ID is required for rollback. Use mist_project (action: 'deployments') to see previous deployment IDs.",!0);let s=await qa(a);return pn(s.deployment_id,"Rollback",async()=>{let l=await $o(o,s.git_commit_sha,s.source_artifact_key,a);l.success||console.error(`[rollback] Local source restore failed: ${l.error}`),l.warning&&console.error(`[rollback] ${l.warning}`)})}default:return p(`Unknown action: ${e}. Use redeploy or rollback.`,!0)}}catch(s){if(s instanceof he)return p(s.message,!0);let l=s instanceof Error?s.message:"An unexpected error occurred";return p(l,!0)}}import{resolve as Ko}from"path";function Yo(r){return new Promise(t=>setTimeout(t,r))}async function un(r){let t=Ko(r.projectPath??process.cwd());if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let a=ke(t)?.projectId;if(!a)return p("No project ID found. Deploy the project first.",!0);let o=r.deploymentId;if(!o)try{let d=(await at(a)).find(u=>u.environment==="preview"&&u.status==="live");if(!d)return p("No live preview found to promote. Deploy with staging mode first, then call promote after QA passes.",!0);o=d.id}catch{return p("Could not list deployments. Check your connection.",!0)}console.error(`[promote] Promoting preview ${o} to production...`);let i;try{i=(await Va(a,o)).deployment_id}catch(l){let d=l instanceof Error?l.message:"Promote failed";return p(`Promote failed: ${d}`,!0)}let n=Date.now(),s=12e4;for(;Date.now()-n<s;){await Yo(3e3);try{let l=await Be(i);if(l.status==="live"){let d=((Date.now()-n)/1e3).toFixed(0),u=l.url??"",c={url:u,time:`${d}s`,deploymentId:i,environment:"production",promoted:!0,message:`Promoted to production at ${u} in ${d}s. Same build that passed QA on preview.`};return c.instruction=["IMPORTANT: The app has been promoted to production. Run a quick QA check to verify.","","1. Call mist_build with action='qa' to test the live production app.","2. If QA passes \u2014 celebrate! Present the URL to the user.","3. If QA finds issues \u2014 these are likely environment differences (auth, DNS). Report them."].join(`
10971
- `),c.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${u}' to verify production.`,c.qaRequired=!0,c.qaUrl=u,u?De(u,JSON.stringify(c)):p(JSON.stringify(c))}if(l.status==="failed")return p(`Promote failed: ${l.error??"Check the Mistflow dashboard for details."}`,!0)}catch{}}return p(`Promote is taking longer than expected. Deployment ID: ${i}`,!0)}import{z as hn}from"zod";import{spawn as Xo,execFileSync as gn}from"child_process";import{existsSync as Ut,readFileSync as mn,writeFileSync as Zo,mkdirSync as es}from"fs";import{join as et,resolve as ts}from"path";import{homedir as fn}from"os";import{createConnection as as}from"net";import{existsSync as Jo,readFileSync as Qo}from"fs";function Et(r){let t=new Set;if(!Jo(r))return t;let e=Qo(r,"utf-8");for(let a of e.split(`
10972
- `)){let o=a.trim();if(!o||o.startsWith("#"))continue;let i=o.indexOf("=");if(i>0){let n=o.slice(0,i).trim(),s=o.slice(i+1).trim();s&&s!=='""'&&s!=="''"&&t.add(n)}}return t}var rs=hn.object({projectPath:hn.string().optional().describe("Path to the project directory (default: current working directory)")});function bn(){return et(fn(),".mistflow","processes.json")}function yn(){let r=bn();if(!Ut(r))return[];try{return JSON.parse(mn(r,"utf-8"))}catch{return[]}}function xn(r){let t=et(fn(),".mistflow");Ut(t)||es(t,{recursive:!0}),Zo(bn(),JSON.stringify(r,null,2))}function ns(){let r=yn(),t=[];for(let e of r)try{process.kill(e.pid,0),t.push(e)}catch{}xn(t)}function wn(r){return new Promise(t=>{let e=as({port:r,host:"127.0.0.1"});e.on("connect",()=>{e.destroy(),t(!0)}),e.on("error",()=>{t(!1)})})}async function is(r,t,e){let a=Date.now();for(;Date.now()-a<t;){if(await wn(r))return!0;await new Promise(o=>setTimeout(o,e))}return!1}function os(r){return Ut(et(r,"mistflow.json"))}function ss(r){let t=et(r,"mistflow.json");if(!Ut(t))return[];let e;try{e=JSON.parse(mn(t,"utf-8"))}catch{return[]}let a=e.env?.required;if(!a||typeof a!="object")return[];let o=Object.keys(a);if(o.length===0)return[];let i=Et(et(r,".env.local"));return o.filter(n=>!i.has(n))}var vn={name:"mist_preview",description:"Build the app and start a local production server on localhost:3000 for previewing. Use when the user says 'mist preview'.",inputSchema:rs,handler:async r=>{let e=ts(r.projectPath??process.cwd()),a=3e3;if(!os(e))return be(e);let o=ss(e);if(o.length>0)return p(`Missing required environment variables in .env.local: ${o.join(", ")}. Add them to ${et(e,".env.local")} before previewing.`,!0);ns();let i=await wn(a),n=yn(),s=n.some(c=>c.type==="dev-server"&&c.port===a);if(i&&!s)return p(`Port ${a} is in use. Stop the other process or I will use a different port.`,!0);let l=[...n];if(!i){try{gn("npx",["drizzle-kit","push"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:3e4})}catch{console.error("[preview] drizzle-kit push failed, continuing...")}try{gn("npm",["run","build"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:18e4})}catch(y){let x=y instanceof Error&&"stderr"in y?String(y.stderr).slice(-1500):"",m=y instanceof Error&&"stdout"in y?String(y.stdout).slice(-1500):"",v=(x+`
10967
+ Email: ${P.admin_credentials.email}
10968
+ Password: ${P.admin_credentials.password}
10969
+ Login: ${M}/login
10970
+ Admin panel: ${M}/admin`}),l.length>0&&(v.warnings=l);let D=[];if(!f){let B=JSON.stringify(S).toLowerCase();D.push({action:"Add a custom domain",command:"mist_config resource='domain' action='add' domain='yourdomain.com'",reason:"Give your app a professional URL instead of the default .mistflow.app domain.",priority:"medium"}),xa(n)&&!nn(n)&&D.push({action:"Back up to GitHub",command:"Create a repo at github.com/new, then tell me the URL and I'll push your code there",reason:"Your source code is saved with every deploy, but GitHub gives you an extra backup and lets you collaborate.",priority:"medium"});let A=B.includes("user")||B.includes("account")||B.includes("member"),U=B.includes("stripe")||B.includes("payment")||B.includes("billing");A&&!U&&D.push({action:"Add payments",command:"Tell the AI: 'Add Stripe payments to my app'",reason:"Your app has user accounts \u2014 you could monetize with subscriptions or one-time payments.",priority:"low"}),D.push({action:"Share your app",command:"mist_project action='share'",reason:"Generate a shareable link so others can fork and customize your app.",priority:"low"}),D.push({action:"Make changes",command:"Describe what you want to change, then run mist_deploy action='deploy'",reason:"Edit your code and redeploy in seconds. Your app is yours to evolve.",priority:"low"}),D.push({action:"See analytics",command:`Visit ${Ma()} to see pageviews and visitors`,reason:"Track how people are using your app.",priority:"low"})}return D.length>0&&(v.nextSteps=D),Ae(M,JSON.stringify(v))}if(P.status==="failed")return p(P.error??"Deployment failed. Check the Mistflow dashboard for details.",!0)}return p(`Deploy is taking longer than expected. Your deploy ID is ${E} \u2014 check status later.`,!0)}import{z as Et}from"zod";import{resolve as zo,join as $o}from"path";import{execFileSync as Vo}from"child_process";var ec=Et.object({projectPath:Et.string().optional().describe("Path to the project directory (default: cwd)"),action:Et.enum(["redeploy","rollback"]).describe("Action to perform: 'redeploy' re-deploys the latest build, 'rollback' reverts to a specific previous deployment"),deploymentId:Et.string().optional().describe("Deployment ID to rollback to (required for 'rollback')")});function Le(r,t){return Vo("git",r,{cwd:t,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]}).trim()}function un(r){try{return Le(["rev-parse","--is-inside-work-tree"],r),!0}catch{return!1}}async function qo(r,t,e,a){if(t&&un(r))try{Le(["cat-file","-t",t],r);let o=!1;try{Le(["status","--porcelain"],r).length>0&&(Le(["stash","push","-m","mistflow-rollback-auto-stash"],r),o=!0)}catch{}return Le(["reset","--hard",t],r),Le(["commit","--allow-empty","-m","Rollback to previous deploy"],r),{method:"git",success:!0,...o?{warning:"Your uncommitted changes were saved to git stash. Run `git stash pop` to restore them."}:{}}}catch{}if(e&&a)try{let{existsSync:o,mkdirSync:i,rmSync:n}=await import("fs"),s=$o(r,".mistflow-rollback-source.tar.gz");await Ja(a,s);let{spawn:l}=await import("child_process");await new Promise((d,u)=>{let c=l("tar",["-xzf",s,"-C",r,"--exclude","node_modules","--exclude",".git"],{cwd:r,stdio:"pipe"});c.on("close",h=>h===0?d():u(new Error(`tar exit ${h}`))),c.on("error",u)});try{n(s)}catch{}if(un(r))try{Le(["add","-A"],r),Le(["commit","-m","Rollback to previous deploy (from source snapshot)"],r)}catch{}return{method:"r2",success:!0}}catch(o){return{method:"r2",success:!1,error:o instanceof Error?o.message:"Source download failed"}}return{method:"none",success:!1,error:"No source snapshot available for this deployment"}}function Ko(r){return new Promise(t=>setTimeout(t,r))}function Yo(r){switch(r){case"pending":return"Provisioning...";case"building":return"Building...";case"deploying":return"Deploying to Cloudflare...";case"verifying":return"Verifying deployment...";default:return`Status: ${r}`}}async function hn(r,t,e){let a=Date.now(),o=12e4,i=3e3,n=[],s="";for(;Date.now()-a<o;){await Ko(i);let l;try{l=await De(r)}catch{continue}if(l.status!==s&&(n.push(Yo(l.status)),s=l.status),l.status==="live"){let d=((Date.now()-a)/1e3).toFixed(0),u=l.url??"";if(e)try{await e()}catch{}return p(JSON.stringify({url:u,time:`${d}s`,deploymentId:r,statusLog:n,message:u?`${t} complete \u2014 live at ${u} in ${d}s.`:`${t} complete in ${d}s.`}))}if(l.status==="failed")return p(l.error??`${t} failed. Check the Mistflow dashboard for details.`,!0)}return p(`${t} is taking longer than expected. Deployment ID: ${r} \u2014 check status later.`,!0)}async function Ca(r){let{projectPath:t,action:e,deploymentId:a}=r,o=zo(t??process.cwd());if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let n=ke(o)?.projectId;if(!n)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"redeploy":{let s=await qa(n);return hn(s.deployment_id,"Redeploy")}case"rollback":{if(!a)return p("Deployment ID is required for rollback. Use mist_project (action: 'deployments') to see previous deployment IDs.",!0);let s=await Ya(a);return hn(s.deployment_id,"Rollback",async()=>{let l=await qo(o,s.git_commit_sha,s.source_artifact_key,a);l.success||console.error(`[rollback] Local source restore failed: ${l.error}`),l.warning&&console.error(`[rollback] ${l.warning}`)})}default:return p(`Unknown action: ${e}. Use redeploy or rollback.`,!0)}}catch(s){if(s instanceof he)return p(s.message,!0);let l=s instanceof Error?s.message:"An unexpected error occurred";return p(l,!0)}}import{resolve as Jo}from"path";function Qo(r){return new Promise(t=>setTimeout(t,r))}async function gn(r){let t=Jo(r.projectPath??process.cwd());if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let a=ke(t)?.projectId;if(!a)return p("No project ID found. Deploy the project first.",!0);let o=r.deploymentId;if(!o)try{let d=(await at(a)).find(u=>u.environment==="preview"&&u.status==="live");if(!d)return p("No live preview found to promote. Deploy with staging mode first, then call promote after QA passes.",!0);o=d.id}catch{return p("Could not list deployments. Check your connection.",!0)}console.error(`[promote] Promoting preview ${o} to production...`);let i;try{i=(await Ka(a,o)).deployment_id}catch(l){let d=l instanceof Error?l.message:"Promote failed";return p(`Promote failed: ${d}`,!0)}let n=Date.now(),s=12e4;for(;Date.now()-n<s;){await Qo(3e3);try{let l=await De(i);if(l.status==="live"){let d=((Date.now()-n)/1e3).toFixed(0),u=l.url??"",c={url:u,time:`${d}s`,deploymentId:i,environment:"production",promoted:!0,message:`Promoted to production at ${u} in ${d}s. Same build that passed QA on preview.`};return c.instruction=["IMPORTANT: The app has been promoted to production. Run a quick QA check to verify.","","1. Call mist_build with action='qa' to test the live production app.","2. If QA passes \u2014 celebrate! Present the URL to the user.","3. If QA finds issues \u2014 these are likely environment differences (auth, DNS). Report them."].join(`
10971
+ `),c.nextAction=`MANDATORY: Call mist_build with action='qa' and url='${u}' to verify production.`,c.qaRequired=!0,c.qaUrl=u,u?Ae(u,JSON.stringify(c)):p(JSON.stringify(c))}if(l.status==="failed")return p(`Promote failed: ${l.error??"Check the Mistflow dashboard for details."}`,!0)}catch{}}return p(`Promote is taking longer than expected. Deployment ID: ${i}`,!0)}import{z as mn}from"zod";import{spawn as es,execFileSync as fn}from"child_process";import{existsSync as Ht,readFileSync as bn,writeFileSync as ts,mkdirSync as as}from"fs";import{join as et,resolve as rs}from"path";import{homedir as yn}from"os";import{createConnection as ns}from"net";import{existsSync as Xo,readFileSync as Zo}from"fs";function Ut(r){let t=new Set;if(!Xo(r))return t;let e=Zo(r,"utf-8");for(let a of e.split(`
10972
+ `)){let o=a.trim();if(!o||o.startsWith("#"))continue;let i=o.indexOf("=");if(i>0){let n=o.slice(0,i).trim(),s=o.slice(i+1).trim();s&&s!=='""'&&s!=="''"&&t.add(n)}}return t}var is=mn.object({projectPath:mn.string().optional().describe("Path to the project directory (default: current working directory)")});function xn(){return et(yn(),".mistflow","processes.json")}function wn(){let r=xn();if(!Ht(r))return[];try{return JSON.parse(bn(r,"utf-8"))}catch{return[]}}function vn(r){let t=et(yn(),".mistflow");Ht(t)||as(t,{recursive:!0}),ts(xn(),JSON.stringify(r,null,2))}function os(){let r=wn(),t=[];for(let e of r)try{process.kill(e.pid,0),t.push(e)}catch{}vn(t)}function kn(r){return new Promise(t=>{let e=ns({port:r,host:"127.0.0.1"});e.on("connect",()=>{e.destroy(),t(!0)}),e.on("error",()=>{t(!1)})})}async function ss(r,t,e){let a=Date.now();for(;Date.now()-a<t;){if(await kn(r))return!0;await new Promise(o=>setTimeout(o,e))}return!1}function ls(r){return Ht(et(r,"mistflow.json"))}function ds(r){let t=et(r,"mistflow.json");if(!Ht(t))return[];let e;try{e=JSON.parse(bn(t,"utf-8"))}catch{return[]}let a=e.env?.required;if(!a||typeof a!="object")return[];let o=Object.keys(a);if(o.length===0)return[];let i=Ut(et(r,".env.local"));return o.filter(n=>!i.has(n))}var Sn={name:"mist_preview",description:"Build the app and start a local production server on localhost:3000 for previewing. Use when the user says 'mist preview'.",inputSchema:is,handler:async r=>{let e=rs(r.projectPath??process.cwd()),a=3e3;if(!ls(e))return be(e);let o=ds(e);if(o.length>0)return p(`Missing required environment variables in .env.local: ${o.join(", ")}. Add them to ${et(e,".env.local")} before previewing.`,!0);os();let i=await kn(a),n=wn(),s=n.some(c=>c.type==="dev-server"&&c.port===a);if(i&&!s)return p(`Port ${a} is in use. Stop the other process or I will use a different port.`,!0);let l=[...n];if(!i){try{fn("npx",["drizzle-kit","push"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:3e4})}catch{console.error("[preview] drizzle-kit push failed, continuing...")}try{fn("npm",["run","build"],{cwd:e,stdio:["pipe","pipe","pipe"],timeout:18e4})}catch(y){let x=y instanceof Error&&"stderr"in y?String(y.stderr).slice(-1500):"",m=y instanceof Error&&"stdout"in y?String(y.stdout).slice(-1500):"",k=(x+`
10973
10973
  `+m).trim();return p(`Build failed. Fix the errors before previewing:
10974
10974
 
10975
- ${v}`,!0)}let c=Xo("npx",["next","start","-p",String(a)],{cwd:e,detached:!0,stdio:["ignore","pipe","pipe"]}),h="";if(c.stderr?.on("data",y=>{h+=y.toString()}),c.pid&&(c.unref(),l.push({pid:c.pid,type:"dev-server",port:a,startedAt:new Date().toISOString()}),xn(l)),!await is(a,15e3,500)){let y=h?`Server failed to start:
10975
+ ${k}`,!0)}let c=es("npx",["next","start","-p",String(a)],{cwd:e,detached:!0,stdio:["ignore","pipe","pipe"]}),h="";if(c.stderr?.on("data",y=>{h+=y.toString()}),c.pid&&(c.unref(),l.push({pid:c.pid,type:"dev-server",port:a,startedAt:new Date().toISOString()}),vn(l)),!await ss(a,15e3,500)){let y=h?`Server failed to start:
10976
10976
 
10977
- ${h.slice(-1e3)}`:"Server failed to start within 15s. Check for runtime errors.";return p(y,!0)}}let d=`http://localhost:${a}`,u=JSON.stringify({localUrl:d,message:`Preview is live at ${d}. Run mist_deploy to get your permanent URL on mistflow.app.`});return De(d,u)}};import{resolve as ls,join as ds}from"path";import{existsSync as cs,readFileSync as ps}from"fs";var ka="test@mistflow.dev",kn="MistflowTest123!",us="Test User";function Sn(r){let t=ds(r,"mistflow.json");if(!cs(t))return null;try{return JSON.parse(ps(t,"utf-8"))}catch{return null}}function hs(r){let t=JSON.stringify(r).toLowerCase();return["auth","login","sign-in","sign-up","signin","signup"].some(e=>t.includes(e))}function gs(r){let t=r.plan;if(!t?.steps)return[];let e=new Set;for(let o of t.steps)if(o.pages)for(let i of o.pages)e.add(i);let a=new Set(["login","signup","sign-in","sign-up","signin","signout","sign-out","logout"]);return[...e].filter(o=>!a.has(o.toLowerCase())).slice(0,3)}function ms(r){let t=r.deploy;return t?.url?t.url:typeof r.deployUrl=="string"?r.deployUrl:null}async function Cn(r){let t=ls(r.projectPath??process.cwd()),e=r.verifyUrl,a=null;if(e)a=Sn(t);else{if(a=Sn(t),!a)return p("No mistflow.json found. Run mist_deploy first, or pass verifyUrl explicitly.",!0);if(e=ms(a)??void 0,!e)return p("No deploy URL found in mistflow.json. Deploy your app first with mist_deploy.",!0)}e.startsWith("http")||(e=`https://${e}`);let o=a?hs(a):!1,i=a?gs(a):[],n;try{n=await gt()}catch{return p("Browser not available. Install Playwright to use verify: npx playwright install chromium",!0)}let s={verified:!0,url:e,authTested:!1,testUser:null,pages:[],message:""},l=[];try{await n.goto(e,{waitUntil:"domcontentloaded",timeout:3e4}),await n.waitForLoadState("networkidle").catch(()=>{})}catch{return s.verified=!1,s.message=`Could not load ${e}. The app may not be deployed yet or the URL may be incorrect.`,p(JSON.stringify(s),!0)}let d=await n.title(),u=d.toLowerCase().includes("error")||d.toLowerCase().includes("404")?"error":"ok";s.pages.push({path:"/",status:u,title:d});try{let x=await Pe(n,!1);l.push({data:x.toString("base64"),mimeType:"image/png"})}catch{}if(u==="error"&&(s.verified=!1),o)try{(await n.evaluate(async m=>{let v=await fetch("/api/auth/sign-up/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)});return{status:v.status,ok:v.ok}},{email:ka,password:kn,name:us})).ok||await n.evaluate(async m=>{let v=await fetch("/api/auth/sign-in/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)});return{status:v.status,ok:v.ok}},{email:ka,password:kn}),await n.reload({waitUntil:"domcontentloaded",timeout:15e3}),await n.waitForLoadState("networkidle").catch(()=>{}),s.authTested=!0,s.testUser={email:ka};try{let m=await Pe(n,!1);l.push({data:m.toString("base64"),mimeType:"image/png"})}catch{}}catch{console.error("[verify] Auth test failed, continuing with public pages")}for(let x of i){let m=x.startsWith("/")?x:`/${x}`;try{await n.goto(`${e}${m}`,{waitUntil:"domcontentloaded",timeout:15e3}),await n.waitForLoadState("networkidle").catch(()=>{});let v=await n.title(),P=v.toLowerCase().includes("404")||v.toLowerCase().includes("not found")?"error":"ok";s.pages.push({path:m,status:P,title:v}),P==="error"&&(s.verified=!1);try{let T=await Pe(n,!1);l.push({data:T.toString("base64"),mimeType:"image/png"})}catch{}}catch{s.pages.push({path:m,status:"error",title:"Timeout"}),s.verified=!1}}let c=s.pages.filter(x=>x.status==="ok").length,h=s.pages.length,g=s.authTested?" Auth login successful.":"";s.message=s.verified?`Verified ${c}/${h} pages.${g} App looks good.`:`Verified ${c}/${h} pages \u2014 some issues found.${g} Check screenshots.`;let y=[{type:"text",text:JSON.stringify(s)}];for(let x of l)y.push({type:"image",data:x.data,mimeType:x.mimeType});return{content:y}}var fs=je.object({action:je.enum(["deploy","promote","preview","redeploy","rollback","verify"]).describe("'deploy' builds and deploys to Cloudflare (auto-redirects to preview if staging mode is enabled). 'promote' promotes a verified preview to production (~10s, reuses the preview build). 'preview' builds the app and starts a local production server on localhost:3000. 'redeploy' re-deploys the latest build without rebuilding. 'rollback' reverts to a specific previous deployment. 'verify' navigates the deployed app, tests auth, and screenshots pages to confirm it works."),projectPath:je.string().optional().describe("Path to the project directory (default: cwd)"),environment:je.enum(["production","preview"]).optional().describe("(deploy) Target environment. Defaults to 'production'. Projects with staging mode auto-redirect to preview."),forceSchema:je.boolean().optional().describe("(deploy) Reset the database structure. Warning: this deletes existing data. Only use when the user confirms this is OK."),deploymentId:je.string().optional().describe("(rollback/promote) Deployment ID to rollback to, or preview deployment ID to promote"),verifyUrl:je.string().optional().describe("(verify) URL to verify. Defaults to deploy URL from mistflow.json")}),Tn={name:"mist_deploy",description:"STEP 4 (final) of the Mistflow workflow. Deploy a Mistflow project. Call this AFTER all mist_build implement steps are done. If the project has staging mode enabled (configurable in dashboard), the tool automatically deploys to preview first with an isolated database. After QA passes, call action='promote' to go live (~10s, reuses the same build artifact). Actions: 'deploy' (default), 'promote' (promote preview to production), 'preview' (local dev server), 'redeploy', 'rollback', 'verify'. The workflow is: mist_plan \u2192 mist_build init \u2192 implement (repeat) \u2192 mist_build build \u2192 mist_deploy \u2192 mist_build qa.",inputSchema:fs,handler:async r=>{let t=r;switch(t.action){case"deploy":return dn({projectPath:t.projectPath,environment:t.environment??"production",forceSchema:t.forceSchema});case"promote":return un({projectPath:t.projectPath,deploymentId:t.deploymentId});case"preview":return vn.handler({projectPath:t.projectPath});case"redeploy":return va({projectPath:t.projectPath,action:"redeploy"});case"rollback":return va({projectPath:t.projectPath,action:"rollback",deploymentId:t.deploymentId});case"verify":return Cn({projectPath:t.projectPath,verifyUrl:t.verifyUrl});default:return p(`Unknown action: ${t.action}. Use deploy, promote, preview, redeploy, rollback, or verify.`,!0)}}};import{z as de}from"zod";import{resolve as Ht}from"path";import{existsSync as Gt,readFileSync as Wt}from"fs";import{join as Ot}from"path";import{z as Le}from"zod";import{resolve as bs,join as Pn}from"path";import{existsSync as ys,readFileSync as xs,writeFileSync as ws}from"fs";var vs=Le.object({action:Le.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:Le.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:Le.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:Le.object({key:Le.string(),description:Le.string().optional(),setupUrl:Le.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),Bn={name:"mist_state",description:"Read or update project state in mistflow.json. Use action='get' to load plan progress, env var status, and deploy info. Use action='update' to mark plan steps complete or add required env vars. Use when the user says 'mist status', 'mist state', or 'mist update state'.",inputSchema:vs,handler:async r=>{let t=r,e=bs(t.projectPath??process.cwd()),a=Pn(e,"mistflow.json");if(!ys(a))return be(e);let o;try{o=JSON.parse(xs(a,"utf-8"))}catch{return p("Failed to parse mistflow.json.",!0)}if(t.action==="get"){let s=o.plan,l=s?.steps?.filter(m=>m.status==="completed").length??0,d=s?.steps?.length??0,u=Et(Pn(e,".env.local")),c=o.env?.required?Object.entries(o.env.required).map(([m,v])=>({name:m,description:v?.description,configured:u.has(m)})):[];o.projectId&&import("./state-manager-73ZUDKOP.js").then(({fetchRemoteState:m})=>m(o.projectId)).catch(()=>{});let h=[`Project: ${o.name}`];if(s){h.push(`Plan: ${s.summary??s.name??"unnamed"} \u2014 ${l}/${d} steps complete`);for(let m of s.steps){let v=m.status==="completed"?"\u2713":m.status==="in_progress"?"\u2192":" ";h.push(` [${v}] ${m.number}. ${m.name}`)}}let g=c.filter(m=>!m.configured);g.length>0&&h.push(`Missing env vars: ${g.map(m=>m.name).join(", ")}`),o.deploy?.url?h.push(`Deployed: ${o.deploy.url} (${o.deploy.count??0} deploys)`):h.push("Not deployed yet");let y=[],x=s?.steps?.find(m=>m.status!=="completed");return x?y.push(`NEXT: Call mist_build with action='implement' to work on step ${x.number} (${x.name}).`):s&&l===d&&(o.deploy?.url||y.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now.")),g.length>0&&y.push(`Missing env vars in .env.local: ${g.map(m=>m.name).join(", ")}`),p(JSON.stringify({name:o.name,projectId:o.projectId,planProgress:s?{name:s.name,summary:s.summary,totalSteps:d,completedSteps:l,steps:s.steps}:null,envStatus:c,deploy:o.deploy??null,contextMessage:h.join(`
10978
- `),nextSteps:y}))}let i=[];if(t.completedStep!==void 0){let s=o.plan;if(s?.steps){let l=s.steps.findIndex(d=>d.number===t.completedStep);if(l===-1)return p(`Step ${t.completedStep} not found in the plan.`,!0);s.steps[l].status="completed",i.push(`Step ${t.completedStep} marked as completed`)}}t.addEnvVar&&(o.env||(o.env={required:{}}),o.env.required||(o.env.required={}),o.env.required[t.addEnvVar.key]={description:t.addEnvVar.description,setupUrl:t.addEnvVar.setupUrl},i.push(`Added required env var: ${t.addEnvVar.key}`)),ws(a,JSON.stringify(o,null,2)+`
10979
- `),o.projectId&&import("./state-manager-73ZUDKOP.js").then(async({readLocalState:s,syncRemoteState:l})=>{let d=s(e);d&&await l(o.projectId,d)}).catch(()=>{});let n=[];if(t.completedStep!==void 0){let l=o.plan?.steps?.find(d=>d.status!=="completed");l?n.push(`NEXT: Call mist_build with action='implement' to work on step ${l.number} (${l.name}). Do this now.`):n.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT suggest localhost.")}return t.addEnvVar&&(n.push(`Add ${t.addEnvVar.key} to your .env.local file`),t.addEnvVar.setupUrl&&n.push(`Get the value from: ${t.addEnvVar.setupUrl}`)),p(JSON.stringify({updated:!0,changes:i,message:i.length>0?`Project state saved. ${i.join(". ")}.`:"No changes made.",nextSteps:n.length>0?n:void 0}))}};var ks=de.object({action:de.enum(["get","update","share","landing-designs","app-styles","integrations","errors","logs","deployments","version"]).default("get").describe("'get' reads current project state. 'update' marks steps complete or adds env vars. 'share' makes the project a shareable template. 'landing-designs' lists curated landing page hero designs. 'app-styles' lists 54 full-app styles (Stripe, Linear, Vercel, etc.) for consistent brand-quality UI across all pages. 'integrations' lists third-party service integration blueprints (Stripe, Resend, ElevenLabs, etc.) with setup guides. 'errors' fetches runtime errors from the deployed app. 'logs' fetches deploy logs for a specific deployment. 'deployments' lists deployment history. 'version' reports the installed @mistflow-ai/mcp version and whether an upgrade is available."),projectPath:de.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:de.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:de.object({key:de.string(),description:de.string().optional(),setupUrl:de.string().optional()}).optional().describe("(update) Add a required env var to the project manifest"),templateDescription:de.string().optional().describe("(share) Short description of what this template builds"),category:de.string().optional().describe("(landing-designs / app-styles) Filter by category"),presetId:de.string().optional().describe("(landing-designs) Get full details for a specific landing design by ID"),appStyleId:de.string().optional().describe("(app-styles) Get full details for a specific app style by ID (e.g. 'stripe', 'linear')"),integrationId:de.string().optional().describe("(integrations) Get full details for a specific integration preset by ID (e.g. 'stripe-payments', 'resend-email', 'elevenlabs-voice')"),period:de.string().optional().describe("(errors) Time period for errors: '1h', '24h', '7d' (default: '7d')"),deploymentId:de.string().optional().describe("(logs) Deployment ID to fetch logs for. If omitted, fetches logs for the latest deployment.")}),Dn={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info. 'update' marks plan steps complete or adds env vars (note: mist_build implement auto-marks the previous step, so manual updates are rarely needed). 'share' makes the project a forkable template with a shareable URL. 'landing-designs' lists curated landing page hero designs \u2014 pass an ID to mist_plan's landingDesign field to apply it. 'app-styles' lists 54 full-app styles from top companies (Stripe, Linear, Vercel, Notion, etc.) \u2014 pass an ID to mist_plan's appStyle field for consistent brand-quality design across ALL pages. 'integrations' lists third-party service integration blueprints (Stripe, Resend, ElevenLabs, OpenAI, Twilio, etc.) \u2014 these are auto-injected during implementation when the plan includes matching integration steps. 'errors' fetches runtime errors from the live deployed app (same data shown on the dashboard). 'logs' fetches deploy logs for a deployment (phase-by-phase progress with error details). 'deployments' lists deployment history with status and error messages.",inputSchema:ks,handler:async r=>{let t=r;if(["share","errors","logs","deployments"].includes(t.action)&&!pe())return p("You need to sign in first. Run mist_setup to connect your account.",!0);switch(t.action){case"get":case"update":return Bn.handler({action:t.action,projectPath:t.projectPath,completedStep:t.completedStep,addEnvVar:t.addEnvVar});case"share":{let a=Ht(t.projectPath??process.cwd()),o=Ot(a,"mistflow.json");if(!Gt(o))return be(a);let i;try{i=JSON.parse(Wt(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first to register it.",!0);try{let s=await er(n,{isTemplate:!0,description:t.templateDescription});return p(JSON.stringify({shareUrl:s.share_url,shareToken:s.share_token,message:`Your project is now a shareable template!
10977
+ ${h.slice(-1e3)}`:"Server failed to start within 15s. Check for runtime errors.";return p(y,!0)}}let d=`http://localhost:${a}`,u=JSON.stringify({localUrl:d,message:`Preview is live at ${d}. Run mist_deploy to get your permanent URL on mistflow.app.`});return Ae(d,u)}};import{resolve as cs,join as ps}from"path";import{existsSync as us,readFileSync as hs}from"fs";var Ta="test@mistflow.dev",Cn="MistflowTest123!",gs="Test User";function Tn(r){let t=ps(r,"mistflow.json");if(!us(t))return null;try{return JSON.parse(hs(t,"utf-8"))}catch{return null}}function ms(r){let t=JSON.stringify(r).toLowerCase();return["auth","login","sign-in","sign-up","signin","signup"].some(e=>t.includes(e))}function fs(r){let t=r.plan;if(!t?.steps)return[];let e=new Set;for(let o of t.steps)if(o.pages)for(let i of o.pages)e.add(i);let a=new Set(["login","signup","sign-in","sign-up","signin","signout","sign-out","logout"]);return[...e].filter(o=>!a.has(o.toLowerCase())).slice(0,3)}function bs(r){let t=r.deploy;return t?.url?t.url:typeof r.deployUrl=="string"?r.deployUrl:null}async function Pn(r){let t=cs(r.projectPath??process.cwd()),e=r.verifyUrl,a=null;if(e)a=Tn(t);else{if(a=Tn(t),!a)return p("No mistflow.json found. Run mist_deploy first, or pass verifyUrl explicitly.",!0);if(e=bs(a)??void 0,!e)return p("No deploy URL found in mistflow.json. Deploy your app first with mist_deploy.",!0)}e.startsWith("http")||(e=`https://${e}`);let o=a?ms(a):!1,i=a?fs(a):[],n;try{n=await gt()}catch{return p("Browser not available. Install Playwright to use verify: npx playwright install chromium",!0)}let s={verified:!0,url:e,authTested:!1,testUser:null,pages:[],message:""},l=[];try{await n.goto(e,{waitUntil:"domcontentloaded",timeout:3e4}),await n.waitForLoadState("networkidle").catch(()=>{})}catch{return s.verified=!1,s.message=`Could not load ${e}. The app may not be deployed yet or the URL may be incorrect.`,p(JSON.stringify(s),!0)}let d=await n.title(),u=d.toLowerCase().includes("error")||d.toLowerCase().includes("404")?"error":"ok";s.pages.push({path:"/",status:u,title:d});try{let x=await Be(n,!1);l.push({data:x.toString("base64"),mimeType:"image/png"})}catch{}if(u==="error"&&(s.verified=!1),o)try{(await n.evaluate(async m=>{let k=await fetch("/api/auth/sign-up/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)});return{status:k.status,ok:k.ok}},{email:Ta,password:Cn,name:gs})).ok||await n.evaluate(async m=>{let k=await fetch("/api/auth/sign-in/email",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)});return{status:k.status,ok:k.ok}},{email:Ta,password:Cn}),await n.reload({waitUntil:"domcontentloaded",timeout:15e3}),await n.waitForLoadState("networkidle").catch(()=>{}),s.authTested=!0,s.testUser={email:Ta};try{let m=await Be(n,!1);l.push({data:m.toString("base64"),mimeType:"image/png"})}catch{}}catch{console.error("[verify] Auth test failed, continuing with public pages")}for(let x of i){let m=x.startsWith("/")?x:`/${x}`;try{await n.goto(`${e}${m}`,{waitUntil:"domcontentloaded",timeout:15e3}),await n.waitForLoadState("networkidle").catch(()=>{});let k=await n.title(),T=k.toLowerCase().includes("404")||k.toLowerCase().includes("not found")?"error":"ok";s.pages.push({path:m,status:T,title:k}),T==="error"&&(s.verified=!1);try{let C=await Be(n,!1);l.push({data:C.toString("base64"),mimeType:"image/png"})}catch{}}catch{s.pages.push({path:m,status:"error",title:"Timeout"}),s.verified=!1}}let c=s.pages.filter(x=>x.status==="ok").length,h=s.pages.length,g=s.authTested?" Auth login successful.":"";s.message=s.verified?`Verified ${c}/${h} pages.${g} App looks good.`:`Verified ${c}/${h} pages \u2014 some issues found.${g} Check screenshots.`;let y=[{type:"text",text:JSON.stringify(s)}];for(let x of l)y.push({type:"image",data:x.data,mimeType:x.mimeType});return{content:y}}var ys=je.object({action:je.enum(["deploy","promote","preview","redeploy","rollback","verify"]).describe("'deploy' builds and deploys to Cloudflare (auto-redirects to preview if staging mode is enabled). 'promote' promotes a verified preview to production (~10s, reuses the preview build). 'preview' builds the app and starts a local production server on localhost:3000. 'redeploy' re-deploys the latest build without rebuilding. 'rollback' reverts to a specific previous deployment. 'verify' navigates the deployed app, tests auth, and screenshots pages to confirm it works."),projectPath:je.string().optional().describe("Path to the project directory (default: cwd)"),environment:je.enum(["production","preview"]).optional().describe("(deploy) Target environment. Defaults to 'production'. Projects with staging mode auto-redirect to preview."),forceSchema:je.boolean().optional().describe("(deploy) Reset the database structure. Warning: this deletes existing data. Only use when the user confirms this is OK."),deploymentId:je.string().optional().describe("(rollback/promote) Deployment ID to rollback to, or preview deployment ID to promote"),verifyUrl:je.string().optional().describe("(verify) URL to verify. Defaults to deploy URL from mistflow.json")}),Bn={name:"mist_deploy",description:"STEP 4 (final) of the Mistflow workflow. Deploy a Mistflow project. Call this AFTER all mist_build implement steps are done. If the project has staging mode enabled (configurable in dashboard), the tool automatically deploys to preview first with an isolated database. After QA passes, call action='promote' to go live (~10s, reuses the same build artifact). Actions: 'deploy' (default), 'promote' (promote preview to production), 'preview' (local dev server), 'redeploy', 'rollback', 'verify'. The workflow is: mist_plan \u2192 mist_build init \u2192 implement (repeat) \u2192 mist_build build \u2192 mist_deploy \u2192 mist_build qa.",inputSchema:ys,handler:async r=>{let t=r;switch(t.action){case"deploy":return pn({projectPath:t.projectPath,environment:t.environment??"production",forceSchema:t.forceSchema});case"promote":return gn({projectPath:t.projectPath,deploymentId:t.deploymentId});case"preview":return Sn.handler({projectPath:t.projectPath});case"redeploy":return Ca({projectPath:t.projectPath,action:"redeploy"});case"rollback":return Ca({projectPath:t.projectPath,action:"rollback",deploymentId:t.deploymentId});case"verify":return Pn({projectPath:t.projectPath,verifyUrl:t.verifyUrl});default:return p(`Unknown action: ${t.action}. Use deploy, promote, preview, redeploy, rollback, or verify.`,!0)}}};import{z as ce}from"zod";import{resolve as Gt}from"path";import{existsSync as Wt,readFileSync as Ot}from"fs";import{join as _t}from"path";import{z as Ne}from"zod";import{resolve as xs,join as Dn}from"path";import{existsSync as ws,readFileSync as vs,writeFileSync as ks}from"fs";var Ss=Ne.object({action:Ne.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:Ne.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:Ne.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:Ne.object({key:Ne.string(),description:Ne.string().optional(),setupUrl:Ne.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),An={name:"mist_state",description:"Read or update project state in mistflow.json. Use action='get' to load plan progress, env var status, and deploy info. Use action='update' to mark plan steps complete or add required env vars. Use when the user says 'mist status', 'mist state', or 'mist update state'.",inputSchema:Ss,handler:async r=>{let t=r,e=xs(t.projectPath??process.cwd()),a=Dn(e,"mistflow.json");if(!ws(a))return be(e);let o;try{o=JSON.parse(vs(a,"utf-8"))}catch{return p("Failed to parse mistflow.json.",!0)}if(t.action==="get"){let s=o.plan,l=s?.steps?.filter(m=>m.status==="completed").length??0,d=s?.steps?.length??0,u=Ut(Dn(e,".env.local")),c=o.env?.required?Object.entries(o.env.required).map(([m,k])=>({name:m,description:k?.description,configured:u.has(m)})):[];o.projectId&&import("./state-manager-73ZUDKOP.js").then(({fetchRemoteState:m})=>m(o.projectId)).catch(()=>{});let h=[`Project: ${o.name}`];if(s){h.push(`Plan: ${s.summary??s.name??"unnamed"} \u2014 ${l}/${d} steps complete`);for(let m of s.steps){let k=m.status==="completed"?"\u2713":m.status==="in_progress"?"\u2192":" ";h.push(` [${k}] ${m.number}. ${m.name}`)}}let g=c.filter(m=>!m.configured);g.length>0&&h.push(`Missing env vars: ${g.map(m=>m.name).join(", ")}`),o.deploy?.url?h.push(`Deployed: ${o.deploy.url} (${o.deploy.count??0} deploys)`):h.push("Not deployed yet");let y=[],x=s?.steps?.find(m=>m.status!=="completed");return x?y.push(`NEXT: Call mist_build with action='implement' to work on step ${x.number} (${x.name}).`):s&&l===d&&(o.deploy?.url||y.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now.")),g.length>0&&y.push(`Missing env vars in .env.local: ${g.map(m=>m.name).join(", ")}`),p(JSON.stringify({name:o.name,projectId:o.projectId,planProgress:s?{name:s.name,summary:s.summary,totalSteps:d,completedSteps:l,steps:s.steps}:null,envStatus:c,deploy:o.deploy??null,contextMessage:h.join(`
10978
+ `),nextSteps:y}))}let i=[];if(t.completedStep!==void 0){let s=o.plan;if(s?.steps){let l=s.steps.findIndex(d=>d.number===t.completedStep);if(l===-1)return p(`Step ${t.completedStep} not found in the plan.`,!0);s.steps[l].status="completed",i.push(`Step ${t.completedStep} marked as completed`)}}t.addEnvVar&&(o.env||(o.env={required:{}}),o.env.required||(o.env.required={}),o.env.required[t.addEnvVar.key]={description:t.addEnvVar.description,setupUrl:t.addEnvVar.setupUrl},i.push(`Added required env var: ${t.addEnvVar.key}`)),ks(a,JSON.stringify(o,null,2)+`
10979
+ `),o.projectId&&import("./state-manager-73ZUDKOP.js").then(async({readLocalState:s,syncRemoteState:l})=>{let d=s(e);d&&await l(o.projectId,d)}).catch(()=>{});let n=[];if(t.completedStep!==void 0){let l=o.plan?.steps?.find(d=>d.status!=="completed");l?n.push(`NEXT: Call mist_build with action='implement' to work on step ${l.number} (${l.name}). Do this now.`):n.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT suggest localhost.")}return t.addEnvVar&&(n.push(`Add ${t.addEnvVar.key} to your .env.local file`),t.addEnvVar.setupUrl&&n.push(`Get the value from: ${t.addEnvVar.setupUrl}`)),p(JSON.stringify({updated:!0,changes:i,message:i.length>0?`Project state saved. ${i.join(". ")}.`:"No changes made.",nextSteps:n.length>0?n:void 0}))}};var Cs=ce.object({action:ce.enum(["get","update","share","landing-designs","app-styles","integrations","errors","logs","deployments","version"]).default("get").describe("'get' reads current project state. 'update' marks steps complete or adds env vars. 'share' makes the project a shareable template. 'landing-designs' lists curated landing page hero designs. 'app-styles' lists 54 full-app styles (Stripe, Linear, Vercel, etc.) for consistent brand-quality UI across all pages. 'integrations' lists third-party service integration blueprints (Stripe, Resend, ElevenLabs, etc.) with setup guides. 'errors' fetches runtime errors from the deployed app. 'logs' fetches deploy logs for a specific deployment. 'deployments' lists deployment history. 'version' reports the installed @mistflow-ai/mcp version and whether an upgrade is available."),projectPath:ce.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:ce.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:ce.object({key:ce.string(),description:ce.string().optional(),setupUrl:ce.string().optional()}).optional().describe("(update) Add a required env var to the project manifest"),templateDescription:ce.string().optional().describe("(share) Short description of what this template builds"),category:ce.string().optional().describe("(landing-designs / app-styles) Filter by category"),presetId:ce.string().optional().describe("(landing-designs) Get full details for a specific landing design by ID"),appStyleId:ce.string().optional().describe("(app-styles) Get full details for a specific app style by ID (e.g. 'stripe', 'linear')"),integrationId:ce.string().optional().describe("(integrations) Get full details for a specific integration preset by ID (e.g. 'stripe-payments', 'resend-email', 'elevenlabs-voice')"),period:ce.string().optional().describe("(errors) Time period for errors: '1h', '24h', '7d' (default: '7d')"),deploymentId:ce.string().optional().describe("(logs) Deployment ID to fetch logs for. If omitted, fetches logs for the latest deployment.")}),In={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info. 'update' marks plan steps complete or adds env vars (note: mist_build implement auto-marks the previous step, so manual updates are rarely needed). 'share' makes the project a forkable template with a shareable URL. 'landing-designs' lists curated landing page hero designs \u2014 pass an ID to mist_plan's landingDesign field to apply it. 'app-styles' lists 54 full-app styles from top companies (Stripe, Linear, Vercel, Notion, etc.) \u2014 pass an ID to mist_plan's appStyle field for consistent brand-quality design across ALL pages. 'integrations' lists third-party service integration blueprints (Stripe, Resend, ElevenLabs, OpenAI, Twilio, etc.) \u2014 these are auto-injected during implementation when the plan includes matching integration steps. 'errors' fetches runtime errors from the live deployed app (same data shown on the dashboard). 'logs' fetches deploy logs for a deployment (phase-by-phase progress with error details). 'deployments' lists deployment history with status and error messages.",inputSchema:Cs,handler:async r=>{let t=r;if(["share","errors","logs","deployments"].includes(t.action)&&!ue())return p("You need to sign in first. Run mist_setup to connect your account.",!0);switch(t.action){case"get":case"update":return An.handler({action:t.action,projectPath:t.projectPath,completedStep:t.completedStep,addEnvVar:t.addEnvVar});case"share":{let a=Gt(t.projectPath??process.cwd()),o=_t(a,"mistflow.json");if(!Wt(o))return be(a);let i;try{i=JSON.parse(Ot(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first to register it.",!0);try{let s=await ar(n,{isTemplate:!0,description:t.templateDescription});return p(JSON.stringify({shareUrl:s.share_url,shareToken:s.share_token,message:`Your project is now a shareable template!
10980
10980
 
10981
10981
  Anyone can fork it: ${s.share_url}
10982
10982
 
10983
10983
  Others can use it in their AI editor:
10984
- "build me something like ${s.share_url}"`}))}catch(s){let l=s instanceof Error?s.message:"Failed to share project";return p(l,!0)}}case"landing-designs":{if(t.presetId){let n=Ae(t.presetId);if(!n)return p(`Preset '${t.presetId}' not found. Use mist_project action='presets' without presetId to list all available presets.`,!0);let s=or(t.presetId);return p(JSON.stringify({preset:{id:n.id,title:n.title,category:n.category,description:s?.description??"",style:s?.style??"",theme:s?.theme??"dark",colors:s?.colors??[],tags:s?.tags??[],promptLength:n.prompt.length},message:`Landing design "${n.title}" (${n.category}) \u2014 ${s?.description??""}. To use it, pass landingDesign="${n.id}" when calling mist_plan.`}))}let a=Yt(t.category??void 0),o=Kt(t.category),i=sr(o);return p(JSON.stringify({count:a.length,presets:a.map(n=>({id:n.id,title:n.title,category:n.category,description:n.description,style:n.style,theme:n.theme,colors:n.colors})),formatted:i,message:`${a.length} landing designs available.${t.category?` Filtered by: ${t.category}.`:""} To use one, pass landingDesign="<id>" when calling mist_plan. The design blueprint will be injected during the landing page implementation step. Browse them at ${Ue()}/designs?tab=landing-designs.`}))}case"app-styles":{if(t.appStyleId){let n=ye(t.appStyleId);if(!n)return p(`App style '${t.appStyleId}' not found. Use mist_project action='app-styles' without appStyleId to list all available app styles.`,!0);let s=Ge(n.id);return p(JSON.stringify({appStyle:{id:n.id,name:n.name,category:s?.category??n.category,description:s?.description??"",theme:s?.theme??"light",colors:s?.colors??[],fonts:s?.fonts??{heading:"Inter",body:"Inter"},tags:s?.tags??[],sectionCount:n.sections.length,sections:n.sections.map(l=>l.title)},message:`App style "${n.name}" (${n.category}) \u2014 ${s?.description??""}. To use it, pass appStyle="${n.id}" when calling mist_plan. The style's component specs, typography, color palette, and layout rules will be injected during ALL implementation steps for consistent brand-quality design.`}))}let a=Xt(t.category??void 0),o=Qt(t.category??void 0),i=pr(o);return p(JSON.stringify({count:a.length,appStyles:a.map(n=>({id:n.id,name:n.name,category:n.category,description:n.description,theme:n.theme,colors:n.colors,fonts:n.fonts})),formatted:i,message:`${a.length} app styles available.${t.category?` Filtered by: ${t.category}.`:""} To use one, pass appStyle="<id>" when calling mist_plan. The style will be applied across ALL pages for consistent design. Browse them at ${Ue()}/designs?tab=app-styles.`}))}case"integrations":{if(t.integrationId){let n=qe(t.integrationId);if(!n)return p(`Integration '${t.integrationId}' not found. Use mist_project action='integrations' without integrationId to list all available integrations.`,!0);let s=Ke(n.id);return p(JSON.stringify({integration:{id:n.id,name:n.name,category:n.category,description:s?.description??"",packages:s?.packages??[],envVars:s?.envVars??[],docsUrl:s?.docsUrl??"",difficulty:s?.difficulty??"medium"},message:`Integration "${n.name}" (${n.category}) \u2014 ${s?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${s?.envVars?.map(l=>l.key).join(", ")||"none"}. Docs: ${s?.docsUrl??"n/a"}.`}))}let a=mr(t.category??void 0),o=ea(t.category??void 0),i=gr(o);return p(JSON.stringify({count:a.length,integrations:a.map(n=>({id:n.id,name:n.name,category:n.category,description:n.description,packages:n.packages,difficulty:n.difficulty,envVars:n.envVars.map(s=>s.key)})),formatted:i,message:`${a.length} integration blueprints available.${t.category?` Filtered by: ${t.category}.`:""} Integration blueprints are auto-injected during implementation when your plan includes a matching integration step. Use integrationId to see full details including env vars and setup URLs.`}))}case"errors":{let a=Ht(t.projectPath??process.cwd()),o=Ot(a,"mistflow.json");if(!Gt(o))return be(a);let i;try{i=JSON.parse(Wt(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let s=await za(n,t.period??"7d");return s.total===0?p(JSON.stringify({total:0,period:s.period,message:`No runtime errors in the last ${s.period}. The app is running clean.`})):p(JSON.stringify({total:s.total,period:s.period,errors:s.errors,message:`${s.total} runtime error(s) in the last ${s.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(s){let l=s instanceof Error?s.message:"Failed to fetch errors";return p(l,!0)}}case"logs":{let a=Ht(t.projectPath??process.cwd()),o=Ot(a,"mistflow.json");if(!Gt(o))return be(a);let i;try{i=JSON.parse(Wt(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);let s=t.deploymentId;if(!s)try{let l=await at(n);if(l.length===0)return p("No deployments found for this project.",!0);s=l[0].id}catch(l){let d=l instanceof Error?l.message:"Failed to fetch deployments";return p(d,!0)}try{let[l,d]=await Promise.all([ja(s),Be(s)]),u=l.filter(h=>h.level==="error"),c=l.filter(h=>h.level==="warn");return p(JSON.stringify({deploymentId:s,status:d.status,errorMessage:d.error??null,totalLogs:l.length,errorCount:u.length,warnCount:c.length,logs:l.map(h=>({time:h.timestamp,level:h.level,phase:h.phase,message:h.message})),message:d.status==="failed"?`Deployment failed. ${u.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${d.status}. ${l.length} log entries (${u.length} errors, ${c.length} warnings).`}))}catch(l){let d=l instanceof Error?l.message:"Failed to fetch deploy logs";return p(d,!0)}}case"deployments":{let a=Ht(t.projectPath??process.cwd()),o=Ot(a,"mistflow.json");if(!Gt(o))return be(a);let i;try{i=JSON.parse(Wt(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let s=await at(n);return p(JSON.stringify({total:s.length,deployments:s.map(l=>({id:l.id,status:l.status,errorMessage:l.error_message,durationSeconds:l.duration_seconds,isRollback:!!l.rollback_from_id,createdAt:l.created_at})),message:`${s.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(s){let l=s instanceof Error?s.message:"Failed to fetch deployments";return p(l,!0)}}case"version":{let a=ht(),o=a.severity==="none",i={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"UNSUPPORTED \u2014 upgrade required"};return p(JSON.stringify({current:a.current,latest:a.latest||"unknown",minSupported:a.minSupported||"unknown",severity:a.severity,upToDate:o,upgradeCmd:a.upgradeCmd,changelogUrl:a.changelogUrl,backendSignalReceived:a.backendSignalReceived,message:a.backendSignalReceived?`Mistflow MCP ${a.current} (${i[a.severity]??a.severity}). Latest: ${a.latest}.${o?"":` Run \`${a.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${a.current}. The backend hasn't replied yet \u2014 make one other API call (e.g. mist_project action='get') then retry to see the latest version.`}))}default:return p(`Unknown action: ${t.action}. Use get, update, share, landing-designs, app-styles, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as we}from"zod";import{z as Ne}from"zod";import{resolve as Ss}from"path";var Qc=Ne.object({projectPath:Ne.string().optional().describe("Path to the project directory (default: cwd)"),action:Ne.enum(["set","list","delete"]).describe("Action to perform"),key:Ne.string().optional().describe("Environment variable name (required for 'set' and 'delete')"),value:Ne.string().optional().describe("Environment variable value (required for 'set')"),category:Ne.string().optional().describe("Category for the env var (default: 'custom')"),description:Ne.string().optional().describe("Description of what this env var is for"),setupUrl:Ne.string().optional().describe("URL where the user can obtain this value (e.g. Stripe dashboard)")});async function An(r){let{projectPath:t,action:e,key:a,value:o,category:i,description:n,setupUrl:s}=r,l=Ss(t??process.cwd());if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let u=ke(l)?.projectId;if(!u)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"set":return a?o?(await Oa(u,a,o,{category:i,description:n,setupUrl:s}),p(JSON.stringify({set:!0,key:a,message:`Environment variable '${a}' has been set. It will be available on your next deployment.`}))):p("Value is required. Provide the env var value.",!0):p("Key is required. Provide the env var name like 'STRIPE_SECRET_KEY'.",!0);case"list":{let c=await Wa(u);return c.length===0?p(JSON.stringify({envVars:[],message:"No environment variables configured. Use action 'set' to add one."})):p(JSON.stringify({envVars:c.map(h=>({key:h.key,category:h.category,description:h.description,hasValue:h.has_value})),message:`${c.length} environment variable(s) configured.`}))}case"delete":return a?(await _a(u,a),p(JSON.stringify({deleted:!0,key:a,message:`Environment variable '${a}' has been removed.`}))):p("Key is required. Provide the env var name to delete.",!0);default:return p(`Unknown action: ${e}. Use set, list, or delete.`,!0)}}catch(c){if(c instanceof he)return p(c.message,!0);let h=c instanceof Error?c.message:"An unexpected error occurred";return p(h,!0)}}import{z as ct}from"zod";import{resolve as Cs,join as Ts}from"path";import{existsSync as Ps,readFileSync as Bs,writeFileSync as Ds}from"fs";var ip=ct.object({projectPath:ct.string().optional().describe("Path to the project directory (default: cwd)"),action:ct.enum(["add","list","verify","remove"]).describe("Action to perform"),domain:ct.string().optional().describe("Domain name (required for 'add' and 'remove')"),domainId:ct.string().optional().describe("Domain ID (required for 'verify' and 'remove')")});function Sa(r,t){let e=Ts(r,"mistflow.json");if(!Ps(e))return;let a;try{a=JSON.parse(Bs(e,"utf-8"))}catch{return}a.domains=t,Ds(e,JSON.stringify(a,null,2)+`
10985
- `)}async function In(r){let{projectPath:t,action:e,domain:a,domainId:o}=r,i=Cs(t??process.cwd());if(!pe())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let s=ke(i)?.projectId;if(!s)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"add":{if(!a)return p("Domain name is required. Provide the domain like 'myapp.com' or 'app.mycompany.com'.",!0);let l=await Fa(s,a),d=await He(s);return Sa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({added:!0,domain:l.domain,status:l.status,instructions:l.instructions,message:`Domain '${l.domain}' added. Set up DNS records as described, then use action 'verify' to check status.`}))}case"list":{let l=await He(s);return l.length===0?p(JSON.stringify({domains:[],message:"No custom domains configured. Use action 'add' to add one."})):p(JSON.stringify({domains:l.map(d=>({id:d.id,domain:d.domain,status:d.status,ssl:d.ssl_status,error:d.error_message}))}))}case"verify":{if(!o){if(a){let c=(await He(s)).find(h=>h.domain===a);if(c){let h=await $t(s,c.id);return p(JSON.stringify({domain:h.domain,status:h.status,ssl:h.ssl_status,error:h.error_message,message:h.status==="active"?`Domain '${h.domain}' is active and serving traffic.`:`Domain '${h.domain}' is ${h.status}. Make sure DNS records are configured correctly.`}))}return p(`Domain '${a}' not found. Use action 'list' to see configured domains.`,!0)}return p("Provide either domainId or domain name to verify.",!0)}let l=await $t(s,o),d=await He(s);return Sa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({domain:l.domain,status:l.status,ssl:l.ssl_status,error:l.error_message,message:l.status==="active"?`Domain '${l.domain}' is active and serving traffic.`:`Domain '${l.domain}' is ${l.status}. Make sure DNS records are configured correctly.`}))}case"remove":{if(!o&&!a)return p("Provide either domainId or domain name to remove.",!0);let l=o;if(!l&&a){let c=(await He(s)).find(h=>h.domain===a);if(!c)return p(`Domain '${a}' not found.`,!0);l=c.id}await Ea(s,l);let d=await He(s);return Sa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({removed:!0,message:"Domain removed. It may take a few minutes for DNS changes to propagate."}))}default:return p(`Unknown action: ${e}. Use add, list, verify, or remove.`,!0)}}catch(l){if(l instanceof he)return p(l.message,!0);let d=l instanceof Error?l.message:"An unexpected error occurred";return p(d,!0)}}var As=we.object({resource:we.enum(["env","domain"]).describe("'env' manages app secrets and configuration values. 'domain' manages custom domains."),action:we.string().describe("Action to perform. env: 'set', 'list', 'delete'. domain: 'add', 'list', 'verify', 'remove'."),projectPath:we.string().optional().describe("Path to the project directory (default: cwd)"),key:we.string().optional().describe("(env) Variable name"),value:we.string().optional().describe("(env set) Variable value"),category:we.string().optional().describe("(env set) Category"),description:we.string().optional().describe("(env set) Description"),setupUrl:we.string().optional().describe("(env set) URL to obtain the value"),domain:we.string().optional().describe("(domain) Domain name"),domainId:we.string().optional().describe("(domain) Domain ID")}),Mn={name:"mist_config",description:"Manage project configuration: app secrets and custom domains. Set resource='env' to manage encrypted app secrets (set, list, delete). Set resource='domain' to manage custom domains (add, list, verify, remove). Use when the user says 'mist env' or 'mist domain'.",inputSchema:As,handler:async r=>{let t=r;switch(t.resource){case"env":return An({projectPath:t.projectPath,action:t.action,key:t.key,value:t.value,category:t.category,description:t.description,setupUrl:t.setupUrl});case"domain":return In({projectPath:t.projectPath,action:t.action,domain:t.domain,domainId:t.domainId});default:return p(`Unknown resource: ${t.resource}. Use env or domain.`,!0)}}};import{z as ze}from"zod";var Is=ze.object({action:ze.enum(["navigate","go_back","go_forward","click","type","fill","select_option","press_key","hover","screenshot","snapshot"]).describe("Action to perform. Navigation: navigate|go_back|go_forward. Interaction: click|type|fill|select_option|press_key|hover. Visual: screenshot (returns image) | snapshot (returns accessibility tree)."),url:ze.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:ze.string().optional().describe("CSS selector of the target element. Required for: click, type, fill, select_option, hover. Optional for screenshot (captures just that element)."),value:ze.string().optional().describe("Text to type/fill, option to select, or key to press (e.g. 'Enter', 'Tab'). Required for: type, fill, select_option, press_key."),fullPage:ze.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:ze.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),Rn={name:"mist_browser",description:"Unified browser tool for navigating, interacting with, and capturing the app. Use 'navigate' to open a URL, interaction actions (click/type/fill/etc.) to test flows, 'snapshot' to inspect the accessibility tree, and 'screenshot' for a visual capture. Use after mist_preview to verify UI, test flows, and iterate on design.",inputSchema:Is,handler:async r=>{let t=r,e=await gt();if(t.action==="navigate"){if(!t.url)return p("URL is required for 'navigate'.",!0);let i=[],n=d=>{d.type()==="error"&&i.push(d.text())};e.on("console",n),await e.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await e.waitForLoadState("networkidle").catch(()=>{});let s=[],l=d=>s.push(d.message);if(e.on("pageerror",l),await e.waitForTimeout(500),e.removeListener("console",n),e.removeListener("pageerror",l),i.length>0||s.length>0){let d=await mt(e),u=[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:d,consoleErrors:i,pageErrors:s,hasErrors:!0})}];if(t.includeScreenshot){let c=await Pe(e);u.push({type:"image",data:c.toString("base64"),mimeType:"image/png"})}return{content:u}}}else if(t.action==="go_back")await e.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="go_forward")await e.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="click"){if(!t.selector)return p("Selector is required for 'click'.",!0);await e.click(t.selector,{timeout:1e4}),await e.waitForLoadState("domcontentloaded").catch(()=>{}),await e.waitForTimeout(500)}else if(t.action==="type"){if(!t.selector)return p("Selector is required for 'type'.",!0);if(!t.value)return p("Value is required for 'type'.",!0);await e.type(t.selector,t.value,{delay:50})}else if(t.action==="fill"){if(!t.selector)return p("Selector is required for 'fill'.",!0);if(!t.value)return p("Value is required for 'fill'.",!0);await e.fill(t.selector,t.value)}else if(t.action==="select_option"){if(!t.selector)return p("Selector is required for 'select_option'.",!0);if(!t.value)return p("Value is required for 'select_option'.",!0);await e.selectOption(t.selector,t.value)}else if(t.action==="hover"){if(!t.selector)return p("Selector is required for 'hover'.",!0);await e.hover(t.selector,{timeout:1e4})}else if(t.action==="press_key"){if(!t.value)return p("Value is required for 'press_key' (e.g. 'Enter').",!0);await e.keyboard.press(t.value),await e.waitForLoadState("domcontentloaded").catch(()=>{}),await e.waitForTimeout(500)}else if(t.action==="screenshot"){t.url&&(await e.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await e.waitForLoadState("networkidle").catch(()=>{}));let i;if(t.selector){let n=await e.$(t.selector);if(!n)return p(`Element not found: ${t.selector}`,!0);i=await n.screenshot({type:"png"})}else i=await Pe(e,t.fullPage);return{content:[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),message:`Screenshot captured (${t.fullPage?"full page":"viewport"})`})},{type:"image",data:i.toString("base64"),mimeType:"image/png"}]}}else if(t.action==="snapshot"){let i=await mt(e);return{content:[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:i})}]}}let a=await mt(e),o=[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:a})}];if(t.includeScreenshot){let i=await Pe(e);o.push({type:"image",data:i.toString("base64"),mimeType:"image/png"})}return{content:o}}};import{existsSync as Te,readFileSync as ut,writeFileSync as tt}from"fs";import{join as ve}from"path";import{z as pt}from"zod";function Ln(r){let t=ve(r,"mistflow.json");if(!Te(t))return{result:{name:"Project",status:"fail",message:"No mistflow.json found. This is not a Mistflow project.",fix:"Run mist_plan to design your app, then mist_build init to scaffold it."},config:null};let e;try{e=JSON.parse(ut(t,"utf-8"))}catch{return{result:{name:"Project",status:"fail",message:"mistflow.json exists but contains invalid JSON.",fix:"Check mistflow.json for syntax errors. If corrupted beyond repair, delete it and re-run mist_build init."},config:null}}if(!e.name||typeof e.name!="string")return{result:{name:"Project",status:"warn",message:"mistflow.json is missing the 'name' field."},config:e};let a=e.projectId,o=a?`, id: ${a}`:", no projectId (ephemeral)";return{result:{name:"Project",status:"pass",message:`${e.name}${o}`},config:e}}async function Ms(){let r=Ba();if(!r.ok)return{result:{name:"Auth",status:"fail",message:r.reason==="missing"?"No credentials found.":"Credentials file is malformed.",fix:"Run mist_setup to log in."},creds:null,authValid:!1};try{let t=Ia(),e=await fetch(`${Ee()}/api/org`,{headers:t,signal:AbortSignal.timeout(1e4)});try{Ta(e.headers)}catch{}if(!e.ok)return e.status===401?{result:{name:"Auth",status:"fail",message:"API key is invalid or revoked.",fix:"Run mist_setup to re-authenticate."},creds:r.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:`Server returned ${e.status}. Credentials may still be valid.`},creds:r.creds,authValid:!1};let a=await e.json(),o=a.slug??r.creds.orgSlug??"unknown",i=a.plan?`, plan: ${a.plan}`:"";return{result:{name:"Auth",status:"pass",message:`org: ${o}${i}`},creds:r.creds,authValid:!0}}catch(t){return t instanceof he?{result:{name:"Auth",status:"fail",message:`Auth check failed: ${t.message}`,fix:"Run mist_setup to re-authenticate."},creds:r.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:"Could not reach API to validate credentials. Network issue?"},creds:r.creds,authValid:!1}}}async function Rs(){let r=Date.now();try{let t=await bt("nextjs"),e=Date.now()-r;return{result:{name:"API",status:"pass",message:`${Ee()} reachable (${e}ms)`},scaffold:t}}catch(t){let e=Date.now()-r;return{result:{name:"API",status:"fail",message:t instanceof he?t.message:`Timeout or network error after ${e}ms`,fix:"Check your network connection. If using --api-url, verify the backend is running."},scaffold:null}}}function Ls(){let r=ht();if(!r.backendSignalReceived)return{name:"MCP version",status:"warn",message:`v${r.current} installed, but no backend signal received to compare against.`};let t={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"no longer supported"};return r.severity==="none"?{name:"MCP version",status:"pass",message:`v${r.current} (latest)`}:r.severity==="unsupported"?{name:"MCP version",status:"fail",message:`v${r.current} is ${t[r.severity]}. Minimum: v${r.minSupported}.`,fix:`Run \`${r.upgradeCmd}\` then restart your editor.`}:{name:"MCP version",status:"warn",message:`v${r.current} installed, v${r.latest} available (${t[r.severity]}).`,fix:`Run \`${r.upgradeCmd}\` then restart your editor.`}}function Ns(r,t,e,a){let o=ve(r,"AGENTS.md"),i=ve(r,"CLAUDE.md"),n=Te(o),s=t.methodologyVersion??"",l=e?.version??"";if(!n){if(!a||!e?.methodology)return{name:"AGENTS.md",status:"fail",message:"Missing. The host AI has no methodology context for this project.",fix:e?"Run mist_doctor without reportOnly to auto-restore it.":"Cannot restore: API unreachable. Fix connectivity first."};let d=Nn(e.methodology,t);return tt(o,d),tt(i,d),{name:"AGENTS.md",status:"fix",message:`Restored from methodology v${l}.`}}if(a&&!Te(i)){let d=ut(o,"utf-8");tt(i,d)}if(l&&s&&l!==s){if(!a||!e?.methodology)return{name:"AGENTS.md",status:"warn",message:`Methodology v${s} installed, v${l} available.`,fix:"Run mist_doctor to update it."};let d=Nn(e.methodology,t);tt(o,d),tt(i,d);try{let u=ve(r,"mistflow.json"),c=JSON.parse(ut(u,"utf-8"));c.methodologyVersion=l,tt(u,JSON.stringify(c,null,2)+`
10986
- `)}catch{}return{name:"AGENTS.md",status:"fix",message:`Updated methodology v${s} -> v${l}.`}}return{name:"AGENTS.md",status:"pass",message:s?`v${s}`:"present"}}async function Fs(r,t,e,a){let o=t.projectId;if(!o)return{name:"State sync",status:"skip",message:"No projectId. State sync not applicable for ephemeral projects."};if(!e)return{name:"State sync",status:"skip",message:"Skipped (not authenticated)."};let i=tr(r),n=await ar(o);if(!i&&!n)return{name:"State sync",status:"warn",message:"No local or remote state found."};if(!i&&n){if(a){let{writeLocalState:s}=await import("./state-manager-73ZUDKOP.js");return s(r,n),{name:"State sync",status:"fix",message:"Local state was missing. Restored from remote."}}return{name:"State sync",status:"warn",message:"Local .mistflow/state.json is missing but remote state exists.",fix:"Run mist_doctor to restore it from the server."}}return i&&!n?{name:"State sync",status:"warn",message:"Local state exists but no remote state found. Remote sync may have failed."}:i&&i.projectId!==o?{name:"State sync",status:"warn",message:`state.json projectId (${i.projectId}) doesn't match mistflow.json projectId (${o}).`}:{name:"State sync",status:"pass",message:`deployCount: ${i.deployCount}, features: ${i.features.length}`}}function Es(r){let t=[],e=[];if(Te(ve(r,"package.json"))?Te(ve(r,"node_modules"))||t.push("node_modules not installed (run npm install)"):t.push("missing package.json"),!Te(ve(r,".env.local")))t.push("missing .env.local");else try{ut(ve(r,".env.local"),"utf-8").match(/^AUTH_SECRET=(.+)$/m)||t.push("AUTH_SECRET not set in .env.local")}catch{t.push(".env.local exists but is not readable")}let a=ve(r,"app","api","auth","[...all]","route.ts");Te(a)||e.push("missing app/api/auth/[...all]/route.ts");let o=ve(r,"app","api","health","route.ts");return Te(o)||e.push("missing app/api/health/route.ts (needed for deploy verification)"),t.length>0?{name:"Structure",status:"fail",message:t.join("; "),fix:t.map(i=>i.includes("node_modules")?"Run `npm install` in the project directory.":i.includes(".env.local")?"Create .env.local with AUTH_SECRET=<random-secret>.":i.includes("AUTH_SECRET")?"Add AUTH_SECRET=<random-secret> to .env.local.":i.includes("package.json")?"This project is missing package.json. Was it scaffolded correctly?":"").filter(Boolean).join(" ")}:e.length>0?{name:"Structure",status:"warn",message:e.join("; ")}:{name:"Structure",status:"pass",message:"all required files present"}}function Us(r,t){let e=t.env;if(!e?.required||typeof e.required!="object"||Object.keys(e.required).length===0)return{name:"Env vars",status:"pass",message:"no required env vars declared"};let a=[],o="";for(let i of[".env.local",".env"]){let n=ve(r,i);try{Te(n)&&(o+=`
10987
- `+ut(n,"utf-8"))}catch{}}for(let[i,n]of Object.entries(e.required)){let s=o.includes(`${i}=`),l=!!process.env[i];if(!s&&!l){let d=n?.description?` (${n.description})`:"";a.push(`${i}${d}`)}}return a.length>0?{name:"Env vars",status:"warn",message:`${a.length} required but not set: ${a.join(", ")}`,fix:"Set them via mist_config or add to .env.local."}:{name:"Env vars",status:"pass",message:`${Object.keys(e.required).length} required, all set`}}function Hs(r){let t=r.plan;if(!t?.steps||t.steps.length===0)return{name:"Plan",status:"skip",message:"No plan found in mistflow.json."};let e=t.steps.length,a=t.steps.filter(n=>n.status==="completed").length,o=t.steps.filter(n=>n.status==="in_progress").length,i=e-a-o;if(a===e)return{name:"Plan",status:"pass",message:`${a}/${e} steps completed`};if(o>0){let n=t.steps.find(s=>s.status==="in_progress");return{name:"Plan",status:"warn",message:`${a}/${e} completed, step ${n?.number} "${n?.name}" in progress`,fix:"Run mist_build implement to continue."}}return a===0?{name:"Plan",status:"warn",message:`${e} steps planned, none started.`,fix:"Run mist_build implement to start building."}:{name:"Plan",status:"warn",message:`${a}/${e} completed, ${i} remaining.`,fix:"Run mist_build implement to continue."}}function Nn(r,t){let e=t.dbProvider;return e==="neon"||!e?r.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres/Neon"):r}function Gs(r){let t={pass:"PASS",warn:"WARN",fail:"FAIL",fix:"FIXED",skip:"SKIP"},e=Math.max(...r.map(u=>u.name.length)),a=r.map(u=>{let c=".".repeat(e-u.name.length+4),h=t[u.status].padEnd(5);return` ${u.name} ${c} ${h} ${u.message}`}),o=r.filter(u=>u.status==="fix").length,i=r.filter(u=>u.status==="fail").length,n=r.filter(u=>u.status==="warn").length,s=[];o>0&&s.push(`${o} fixed`),i>0&&s.push(`${i} failure${i>1?"s":""}`),n>0&&s.push(`${n} warning${n>1?"s":""}`);let l=`mist_doctor results:
10984
+ "build me something like ${s.share_url}"`}))}catch(s){let l=s instanceof Error?s.message:"Failed to share project";return p(l,!0)}}case"landing-designs":{if(t.presetId){let n=Ie(t.presetId);if(!n)return p(`Preset '${t.presetId}' not found. Use mist_project action='presets' without presetId to list all available presets.`,!0);let s=lr(t.presetId);return p(JSON.stringify({preset:{id:n.id,title:n.title,category:n.category,description:s?.description??"",style:s?.style??"",theme:s?.theme??"dark",colors:s?.colors??[],tags:s?.tags??[],promptLength:n.prompt.length},message:`Landing design "${n.title}" (${n.category}) \u2014 ${s?.description??""}. To use it, pass landingDesign="${n.id}" when calling mist_plan.`}))}let a=Xt(t.category??void 0),o=Qt(t.category),i=dr(o);return p(JSON.stringify({count:a.length,presets:a.map(n=>({id:n.id,title:n.title,category:n.category,description:n.description,style:n.style,theme:n.theme,colors:n.colors})),formatted:i,message:`${a.length} landing designs available.${t.category?` Filtered by: ${t.category}.`:""} To use one, pass landingDesign="<id>" when calling mist_plan. The design blueprint will be injected during the landing page implementation step. Browse them at ${Ue()}/designs?tab=landing-designs.`}))}case"app-styles":{if(t.appStyleId){let n=ye(t.appStyleId);if(!n)return p(`App style '${t.appStyleId}' not found. Use mist_project action='app-styles' without appStyleId to list all available app styles.`,!0);let s=Ge(n.id);return p(JSON.stringify({appStyle:{id:n.id,name:n.name,category:s?.category??n.category,description:s?.description??"",theme:s?.theme??"light",colors:s?.colors??[],fonts:s?.fonts??{heading:"Inter",body:"Inter"},tags:s?.tags??[],sectionCount:n.sections.length,sections:n.sections.map(l=>l.title)},message:`App style "${n.name}" (${n.category}) \u2014 ${s?.description??""}. To use it, pass appStyle="${n.id}" when calling mist_plan. The style's component specs, typography, color palette, and layout rules will be injected during ALL implementation steps for consistent brand-quality design.`}))}let a=ta(t.category??void 0),o=ea(t.category??void 0),i=hr(o);return p(JSON.stringify({count:a.length,appStyles:a.map(n=>({id:n.id,name:n.name,category:n.category,description:n.description,theme:n.theme,colors:n.colors,fonts:n.fonts})),formatted:i,message:`${a.length} app styles available.${t.category?` Filtered by: ${t.category}.`:""} To use one, pass appStyle="<id>" when calling mist_plan. The style will be applied across ALL pages for consistent design. Browse them at ${Ue()}/designs?tab=app-styles.`}))}case"integrations":{if(t.integrationId){let n=qe(t.integrationId);if(!n)return p(`Integration '${t.integrationId}' not found. Use mist_project action='integrations' without integrationId to list all available integrations.`,!0);let s=Ke(n.id);return p(JSON.stringify({integration:{id:n.id,name:n.name,category:n.category,description:s?.description??"",packages:s?.packages??[],envVars:s?.envVars??[],docsUrl:s?.docsUrl??"",difficulty:s?.difficulty??"medium"},message:`Integration "${n.name}" (${n.category}) \u2014 ${s?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${s?.envVars?.map(l=>l.key).join(", ")||"none"}. Docs: ${s?.docsUrl??"n/a"}.`}))}let a=br(t.category??void 0),o=ra(t.category??void 0),i=fr(o);return p(JSON.stringify({count:a.length,integrations:a.map(n=>({id:n.id,name:n.name,category:n.category,description:n.description,packages:n.packages,difficulty:n.difficulty,envVars:n.envVars.map(s=>s.key)})),formatted:i,message:`${a.length} integration blueprints available.${t.category?` Filtered by: ${t.category}.`:""} Integration blueprints are auto-injected during implementation when your plan includes a matching integration step. Use integrationId to see full details including env vars and setup URLs.`}))}case"errors":{let a=Gt(t.projectPath??process.cwd()),o=_t(a,"mistflow.json");if(!Wt(o))return be(a);let i;try{i=JSON.parse(Ot(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let s=await Va(n,t.period??"7d");return s.total===0?p(JSON.stringify({total:0,period:s.period,message:`No runtime errors in the last ${s.period}. The app is running clean.`})):p(JSON.stringify({total:s.total,period:s.period,errors:s.errors,message:`${s.total} runtime error(s) in the last ${s.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(s){let l=s instanceof Error?s.message:"Failed to fetch errors";return p(l,!0)}}case"logs":{let a=Gt(t.projectPath??process.cwd()),o=_t(a,"mistflow.json");if(!Wt(o))return be(a);let i;try{i=JSON.parse(Ot(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);let s=t.deploymentId;if(!s)try{let l=await at(n);if(l.length===0)return p("No deployments found for this project.",!0);s=l[0].id}catch(l){let d=l instanceof Error?l.message:"Failed to fetch deployments";return p(d,!0)}try{let[l,d]=await Promise.all([$a(s),De(s)]),u=l.filter(h=>h.level==="error"),c=l.filter(h=>h.level==="warn");return p(JSON.stringify({deploymentId:s,status:d.status,errorMessage:d.error??null,totalLogs:l.length,errorCount:u.length,warnCount:c.length,logs:l.map(h=>({time:h.timestamp,level:h.level,phase:h.phase,message:h.message})),message:d.status==="failed"?`Deployment failed. ${u.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${d.status}. ${l.length} log entries (${u.length} errors, ${c.length} warnings).`}))}catch(l){let d=l instanceof Error?l.message:"Failed to fetch deploy logs";return p(d,!0)}}case"deployments":{let a=Gt(t.projectPath??process.cwd()),o=_t(a,"mistflow.json");if(!Wt(o))return be(a);let i;try{i=JSON.parse(Ot(o,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=i.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let s=await at(n);return p(JSON.stringify({total:s.length,deployments:s.map(l=>({id:l.id,status:l.status,errorMessage:l.error_message,durationSeconds:l.duration_seconds,isRollback:!!l.rollback_from_id,createdAt:l.created_at})),message:`${s.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(s){let l=s instanceof Error?s.message:"Failed to fetch deployments";return p(l,!0)}}case"version":{let a=ht(),o=a.severity==="none",i={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"UNSUPPORTED \u2014 upgrade required"};return p(JSON.stringify({current:a.current,latest:a.latest||"unknown",minSupported:a.minSupported||"unknown",severity:a.severity,upToDate:o,upgradeCmd:a.upgradeCmd,changelogUrl:a.changelogUrl,backendSignalReceived:a.backendSignalReceived,message:a.backendSignalReceived?`Mistflow MCP ${a.current} (${i[a.severity]??a.severity}). Latest: ${a.latest}.${o?"":` Run \`${a.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${a.current}. The backend hasn't replied yet \u2014 make one other API call (e.g. mist_project action='get') then retry to see the latest version.`}))}default:return p(`Unknown action: ${t.action}. Use get, update, share, landing-designs, app-styles, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as we}from"zod";import{z as Fe}from"zod";import{resolve as Ts}from"path";var tp=Fe.object({projectPath:Fe.string().optional().describe("Path to the project directory (default: cwd)"),action:Fe.enum(["set","list","delete"]).describe("Action to perform"),key:Fe.string().optional().describe("Environment variable name (required for 'set' and 'delete')"),value:Fe.string().optional().describe("Environment variable value (required for 'set')"),category:Fe.string().optional().describe("Category for the env var (default: 'custom')"),description:Fe.string().optional().describe("Description of what this env var is for"),setupUrl:Fe.string().optional().describe("URL where the user can obtain this value (e.g. Stripe dashboard)")});async function Mn(r){let{projectPath:t,action:e,key:a,value:o,category:i,description:n,setupUrl:s}=r,l=Ts(t??process.cwd());if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let u=ke(l)?.projectId;if(!u)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"set":return a?o?(await ja(u,a,o,{category:i,description:n,setupUrl:s}),p(JSON.stringify({set:!0,key:a,message:`Environment variable '${a}' has been set. It will be available on your next deployment.`}))):p("Value is required. Provide the env var value.",!0):p("Key is required. Provide the env var name like 'STRIPE_SECRET_KEY'.",!0);case"list":{let c=await _a(u);return c.length===0?p(JSON.stringify({envVars:[],message:"No environment variables configured. Use action 'set' to add one."})):p(JSON.stringify({envVars:c.map(h=>({key:h.key,category:h.category,description:h.description,hasValue:h.has_value})),message:`${c.length} environment variable(s) configured.`}))}case"delete":return a?(await za(u,a),p(JSON.stringify({deleted:!0,key:a,message:`Environment variable '${a}' has been removed.`}))):p("Key is required. Provide the env var name to delete.",!0);default:return p(`Unknown action: ${e}. Use set, list, or delete.`,!0)}}catch(c){if(c instanceof he)return p(c.message,!0);let h=c instanceof Error?c.message:"An unexpected error occurred";return p(h,!0)}}import{z as ct}from"zod";import{resolve as Ps,join as Bs}from"path";import{existsSync as Ds,readFileSync as As,writeFileSync as Is}from"fs";var dp=ct.object({projectPath:ct.string().optional().describe("Path to the project directory (default: cwd)"),action:ct.enum(["add","list","verify","remove"]).describe("Action to perform"),domain:ct.string().optional().describe("Domain name (required for 'add' and 'remove')"),domainId:ct.string().optional().describe("Domain ID (required for 'verify' and 'remove')")});function Pa(r,t){let e=Bs(r,"mistflow.json");if(!Ds(e))return;let a;try{a=JSON.parse(As(e,"utf-8"))}catch{return}a.domains=t,Is(e,JSON.stringify(a,null,2)+`
10985
+ `)}async function Rn(r){let{projectPath:t,action:e,domain:a,domainId:o}=r,i=Ps(t??process.cwd());if(!ue())return p("No Mistflow credentials found. Run mist_setup to connect your account.",!0);let s=ke(i)?.projectId;if(!s)return p("No project ID found. Deploy your project first with mist_deploy, or initialize with mist_plan + mist_build.",!0);try{switch(e){case"add":{if(!a)return p("Domain name is required. Provide the domain like 'myapp.com' or 'app.mycompany.com'.",!0);let l=await Ua(s,a),d=await He(s);return Pa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({added:!0,domain:l.domain,status:l.status,instructions:l.instructions,message:`Domain '${l.domain}' added. Set up DNS records as described, then use action 'verify' to check status.`}))}case"list":{let l=await He(s);return l.length===0?p(JSON.stringify({domains:[],message:"No custom domains configured. Use action 'add' to add one."})):p(JSON.stringify({domains:l.map(d=>({id:d.id,domain:d.domain,status:d.status,ssl:d.ssl_status,error:d.error_message}))}))}case"verify":{if(!o){if(a){let c=(await He(s)).find(h=>h.domain===a);if(c){let h=await Vt(s,c.id);return p(JSON.stringify({domain:h.domain,status:h.status,ssl:h.ssl_status,error:h.error_message,message:h.status==="active"?`Domain '${h.domain}' is active and serving traffic.`:`Domain '${h.domain}' is ${h.status}. Make sure DNS records are configured correctly.`}))}return p(`Domain '${a}' not found. Use action 'list' to see configured domains.`,!0)}return p("Provide either domainId or domain name to verify.",!0)}let l=await Vt(s,o),d=await He(s);return Pa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({domain:l.domain,status:l.status,ssl:l.ssl_status,error:l.error_message,message:l.status==="active"?`Domain '${l.domain}' is active and serving traffic.`:`Domain '${l.domain}' is ${l.status}. Make sure DNS records are configured correctly.`}))}case"remove":{if(!o&&!a)return p("Provide either domainId or domain name to remove.",!0);let l=o;if(!l&&a){let c=(await He(s)).find(h=>h.domain===a);if(!c)return p(`Domain '${a}' not found.`,!0);l=c.id}await Ha(s,l);let d=await He(s);return Pa(i,d.map(u=>({domain:u.domain,status:u.status}))),p(JSON.stringify({removed:!0,message:"Domain removed. It may take a few minutes for DNS changes to propagate."}))}default:return p(`Unknown action: ${e}. Use add, list, verify, or remove.`,!0)}}catch(l){if(l instanceof he)return p(l.message,!0);let d=l instanceof Error?l.message:"An unexpected error occurred";return p(d,!0)}}var Ms=we.object({resource:we.enum(["env","domain"]).describe("'env' manages app secrets and configuration values. 'domain' manages custom domains."),action:we.string().describe("Action to perform. env: 'set', 'list', 'delete'. domain: 'add', 'list', 'verify', 'remove'."),projectPath:we.string().optional().describe("Path to the project directory (default: cwd)"),key:we.string().optional().describe("(env) Variable name"),value:we.string().optional().describe("(env set) Variable value"),category:we.string().optional().describe("(env set) Category"),description:we.string().optional().describe("(env set) Description"),setupUrl:we.string().optional().describe("(env set) URL to obtain the value"),domain:we.string().optional().describe("(domain) Domain name"),domainId:we.string().optional().describe("(domain) Domain ID")}),Ln={name:"mist_config",description:"Manage project configuration: app secrets and custom domains. Set resource='env' to manage encrypted app secrets (set, list, delete). Set resource='domain' to manage custom domains (add, list, verify, remove). Use when the user says 'mist env' or 'mist domain'.",inputSchema:Ms,handler:async r=>{let t=r;switch(t.resource){case"env":return Mn({projectPath:t.projectPath,action:t.action,key:t.key,value:t.value,category:t.category,description:t.description,setupUrl:t.setupUrl});case"domain":return Rn({projectPath:t.projectPath,action:t.action,domain:t.domain,domainId:t.domainId});default:return p(`Unknown resource: ${t.resource}. Use env or domain.`,!0)}}};import{z as ze}from"zod";var Rs=ze.object({action:ze.enum(["navigate","go_back","go_forward","click","type","fill","select_option","press_key","hover","screenshot","snapshot"]).describe("Action to perform. Navigation: navigate|go_back|go_forward. Interaction: click|type|fill|select_option|press_key|hover. Visual: screenshot (returns image) | snapshot (returns accessibility tree)."),url:ze.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:ze.string().optional().describe("CSS selector of the target element. Required for: click, type, fill, select_option, hover. Optional for screenshot (captures just that element)."),value:ze.string().optional().describe("Text to type/fill, option to select, or key to press (e.g. 'Enter', 'Tab'). Required for: type, fill, select_option, press_key."),fullPage:ze.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:ze.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),Nn={name:"mist_browser",description:"Unified browser tool for navigating, interacting with, and capturing the app. Use 'navigate' to open a URL, interaction actions (click/type/fill/etc.) to test flows, 'snapshot' to inspect the accessibility tree, and 'screenshot' for a visual capture. Use after mist_preview to verify UI, test flows, and iterate on design.",inputSchema:Rs,handler:async r=>{let t=r,e=await gt();if(t.action==="navigate"){if(!t.url)return p("URL is required for 'navigate'.",!0);let i=[],n=d=>{d.type()==="error"&&i.push(d.text())};e.on("console",n),await e.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await e.waitForLoadState("networkidle").catch(()=>{});let s=[],l=d=>s.push(d.message);if(e.on("pageerror",l),await e.waitForTimeout(500),e.removeListener("console",n),e.removeListener("pageerror",l),i.length>0||s.length>0){let d=await mt(e),u=[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:d,consoleErrors:i,pageErrors:s,hasErrors:!0})}];if(t.includeScreenshot){let c=await Be(e);u.push({type:"image",data:c.toString("base64"),mimeType:"image/png"})}return{content:u}}}else if(t.action==="go_back")await e.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="go_forward")await e.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="click"){if(!t.selector)return p("Selector is required for 'click'.",!0);await e.click(t.selector,{timeout:1e4}),await e.waitForLoadState("domcontentloaded").catch(()=>{}),await e.waitForTimeout(500)}else if(t.action==="type"){if(!t.selector)return p("Selector is required for 'type'.",!0);if(!t.value)return p("Value is required for 'type'.",!0);await e.type(t.selector,t.value,{delay:50})}else if(t.action==="fill"){if(!t.selector)return p("Selector is required for 'fill'.",!0);if(!t.value)return p("Value is required for 'fill'.",!0);await e.fill(t.selector,t.value)}else if(t.action==="select_option"){if(!t.selector)return p("Selector is required for 'select_option'.",!0);if(!t.value)return p("Value is required for 'select_option'.",!0);await e.selectOption(t.selector,t.value)}else if(t.action==="hover"){if(!t.selector)return p("Selector is required for 'hover'.",!0);await e.hover(t.selector,{timeout:1e4})}else if(t.action==="press_key"){if(!t.value)return p("Value is required for 'press_key' (e.g. 'Enter').",!0);await e.keyboard.press(t.value),await e.waitForLoadState("domcontentloaded").catch(()=>{}),await e.waitForTimeout(500)}else if(t.action==="screenshot"){t.url&&(await e.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await e.waitForLoadState("networkidle").catch(()=>{}));let i;if(t.selector){let n=await e.$(t.selector);if(!n)return p(`Element not found: ${t.selector}`,!0);i=await n.screenshot({type:"png"})}else i=await Be(e,t.fullPage);return{content:[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),message:`Screenshot captured (${t.fullPage?"full page":"viewport"})`})},{type:"image",data:i.toString("base64"),mimeType:"image/png"}]}}else if(t.action==="snapshot"){let i=await mt(e);return{content:[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:i})}]}}let a=await mt(e),o=[{type:"text",text:JSON.stringify({url:e.url(),title:await e.title(),snapshot:a})}];if(t.includeScreenshot){let i=await Be(e);o.push({type:"image",data:i.toString("base64"),mimeType:"image/png"})}return{content:o}}};import{existsSync as Pe,readFileSync as ut,writeFileSync as tt}from"fs";import{join as ve}from"path";import{z as pt}from"zod";function Fn(r){let t=ve(r,"mistflow.json");if(!Pe(t))return{result:{name:"Project",status:"fail",message:"No mistflow.json found. This is not a Mistflow project.",fix:"Run mist_plan to design your app, then mist_build init to scaffold it."},config:null};let e;try{e=JSON.parse(ut(t,"utf-8"))}catch{return{result:{name:"Project",status:"fail",message:"mistflow.json exists but contains invalid JSON.",fix:"Check mistflow.json for syntax errors. If corrupted beyond repair, delete it and re-run mist_build init."},config:null}}if(!e.name||typeof e.name!="string")return{result:{name:"Project",status:"warn",message:"mistflow.json is missing the 'name' field."},config:e};let a=e.projectId,o=a?`, id: ${a}`:", no projectId (ephemeral)";return{result:{name:"Project",status:"pass",message:`${e.name}${o}`},config:e}}async function Ls(){let r=Ia();if(!r.ok)return{result:{name:"Auth",status:"fail",message:r.reason==="missing"?"No credentials found.":"Credentials file is malformed.",fix:"Run mist_setup to log in."},creds:null,authValid:!1};try{let t=Ra(),e=await fetch(`${Ee()}/api/org`,{headers:t,signal:AbortSignal.timeout(1e4)});try{Da(e.headers)}catch{}if(!e.ok)return e.status===401?{result:{name:"Auth",status:"fail",message:"API key is invalid or revoked.",fix:"Run mist_setup to re-authenticate."},creds:r.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:`Server returned ${e.status}. Credentials may still be valid.`},creds:r.creds,authValid:!1};let a=await e.json(),o=a.slug??r.creds.orgSlug??"unknown",i=a.plan?`, plan: ${a.plan}`:"";return{result:{name:"Auth",status:"pass",message:`org: ${o}${i}`},creds:r.creds,authValid:!0}}catch(t){return t instanceof he?{result:{name:"Auth",status:"fail",message:`Auth check failed: ${t.message}`,fix:"Run mist_setup to re-authenticate."},creds:r.creds,authValid:!1}:{result:{name:"Auth",status:"warn",message:"Could not reach API to validate credentials. Network issue?"},creds:r.creds,authValid:!1}}}async function Ns(){let r=Date.now();try{let t=await yt("nextjs"),e=Date.now()-r;return{result:{name:"API",status:"pass",message:`${Ee()} reachable (${e}ms)`},scaffold:t}}catch(t){let e=Date.now()-r;return{result:{name:"API",status:"fail",message:t instanceof he?t.message:`Timeout or network error after ${e}ms`,fix:"Check your network connection. If using --api-url, verify the backend is running."},scaffold:null}}}function Fs(){let r=ht();if(!r.backendSignalReceived)return{name:"MCP version",status:"warn",message:`v${r.current} installed, but no backend signal received to compare against.`};let t={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"no longer supported"};return r.severity==="none"?{name:"MCP version",status:"pass",message:`v${r.current} (latest)`}:r.severity==="unsupported"?{name:"MCP version",status:"fail",message:`v${r.current} is ${t[r.severity]}. Minimum: v${r.minSupported}.`,fix:`Run \`${r.upgradeCmd}\` then restart your editor.`}:{name:"MCP version",status:"warn",message:`v${r.current} installed, v${r.latest} available (${t[r.severity]}).`,fix:`Run \`${r.upgradeCmd}\` then restart your editor.`}}function Es(r,t,e,a){let o=ve(r,"AGENTS.md"),i=ve(r,"CLAUDE.md"),n=Pe(o),s=t.methodologyVersion??"",l=e?.version??"";if(!n){if(!a||!e?.methodology)return{name:"AGENTS.md",status:"fail",message:"Missing. The host AI has no methodology context for this project.",fix:e?"Run mist_doctor without reportOnly to auto-restore it.":"Cannot restore: API unreachable. Fix connectivity first."};let d=En(e.methodology,t);return tt(o,d),tt(i,d),{name:"AGENTS.md",status:"fix",message:`Restored from methodology v${l}.`}}if(a&&!Pe(i)){let d=ut(o,"utf-8");tt(i,d)}if(l&&s&&l!==s){if(!a||!e?.methodology)return{name:"AGENTS.md",status:"warn",message:`Methodology v${s} installed, v${l} available.`,fix:"Run mist_doctor to update it."};let d=En(e.methodology,t);tt(o,d),tt(i,d);try{let u=ve(r,"mistflow.json"),c=JSON.parse(ut(u,"utf-8"));c.methodologyVersion=l,tt(u,JSON.stringify(c,null,2)+`
10986
+ `)}catch{}return{name:"AGENTS.md",status:"fix",message:`Updated methodology v${s} -> v${l}.`}}return{name:"AGENTS.md",status:"pass",message:s?`v${s}`:"present"}}async function Us(r,t,e,a){let o=t.projectId;if(!o)return{name:"State sync",status:"skip",message:"No projectId. State sync not applicable for ephemeral projects."};if(!e)return{name:"State sync",status:"skip",message:"Skipped (not authenticated)."};let i=rr(r),n=await nr(o);if(!i&&!n)return{name:"State sync",status:"warn",message:"No local or remote state found."};if(!i&&n){if(a){let{writeLocalState:s}=await import("./state-manager-73ZUDKOP.js");return s(r,n),{name:"State sync",status:"fix",message:"Local state was missing. Restored from remote."}}return{name:"State sync",status:"warn",message:"Local .mistflow/state.json is missing but remote state exists.",fix:"Run mist_doctor to restore it from the server."}}return i&&!n?{name:"State sync",status:"warn",message:"Local state exists but no remote state found. Remote sync may have failed."}:i&&i.projectId!==o?{name:"State sync",status:"warn",message:`state.json projectId (${i.projectId}) doesn't match mistflow.json projectId (${o}).`}:{name:"State sync",status:"pass",message:`deployCount: ${i.deployCount}, features: ${i.features.length}`}}function Hs(r){let t=[],e=[];if(Pe(ve(r,"package.json"))?Pe(ve(r,"node_modules"))||t.push("node_modules not installed (run npm install)"):t.push("missing package.json"),!Pe(ve(r,".env.local")))t.push("missing .env.local");else try{ut(ve(r,".env.local"),"utf-8").match(/^AUTH_SECRET=(.+)$/m)||t.push("AUTH_SECRET not set in .env.local")}catch{t.push(".env.local exists but is not readable")}let a=ve(r,"app","api","auth","[...all]","route.ts");Pe(a)||e.push("missing app/api/auth/[...all]/route.ts");let o=ve(r,"app","api","health","route.ts");return Pe(o)||e.push("missing app/api/health/route.ts (needed for deploy verification)"),t.length>0?{name:"Structure",status:"fail",message:t.join("; "),fix:t.map(i=>i.includes("node_modules")?"Run `npm install` in the project directory.":i.includes(".env.local")?"Create .env.local with AUTH_SECRET=<random-secret>.":i.includes("AUTH_SECRET")?"Add AUTH_SECRET=<random-secret> to .env.local.":i.includes("package.json")?"This project is missing package.json. Was it scaffolded correctly?":"").filter(Boolean).join(" ")}:e.length>0?{name:"Structure",status:"warn",message:e.join("; ")}:{name:"Structure",status:"pass",message:"all required files present"}}function Gs(r,t){let e=t.env;if(!e?.required||typeof e.required!="object"||Object.keys(e.required).length===0)return{name:"Env vars",status:"pass",message:"no required env vars declared"};let a=[],o="";for(let i of[".env.local",".env"]){let n=ve(r,i);try{Pe(n)&&(o+=`
10987
+ `+ut(n,"utf-8"))}catch{}}for(let[i,n]of Object.entries(e.required)){let s=o.includes(`${i}=`),l=!!process.env[i];if(!s&&!l){let d=n?.description?` (${n.description})`:"";a.push(`${i}${d}`)}}return a.length>0?{name:"Env vars",status:"warn",message:`${a.length} required but not set: ${a.join(", ")}`,fix:"Set them via mist_config or add to .env.local."}:{name:"Env vars",status:"pass",message:`${Object.keys(e.required).length} required, all set`}}function Ws(r){let t=r.plan;if(!t?.steps||t.steps.length===0)return{name:"Plan",status:"skip",message:"No plan found in mistflow.json."};let e=t.steps.length,a=t.steps.filter(n=>n.status==="completed").length,o=t.steps.filter(n=>n.status==="in_progress").length,i=e-a-o;if(a===e)return{name:"Plan",status:"pass",message:`${a}/${e} steps completed`};if(o>0){let n=t.steps.find(s=>s.status==="in_progress");return{name:"Plan",status:"warn",message:`${a}/${e} completed, step ${n?.number} "${n?.name}" in progress`,fix:"Run mist_build implement to continue."}}return a===0?{name:"Plan",status:"warn",message:`${e} steps planned, none started.`,fix:"Run mist_build implement to start building."}:{name:"Plan",status:"warn",message:`${a}/${e} completed, ${i} remaining.`,fix:"Run mist_build implement to continue."}}function En(r,t){let e=t.dbProvider;return e==="neon"||!e?r.replace(/sqliteTable/g,"pgTable").replace(/drizzle-orm\/sqlite-core/g,"drizzle-orm/pg-core").replace(/Use `text` for dates \(SQLite stores dates as text\)/g,"Use `timestamp` for dates and `boolean` for booleans (native Postgres types)").replace(/text\("created_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("created_at").notNull().defaultNow()').replace(/text\("updated_at"\)\.notNull\(\)\.default\(sql`\(CURRENT_TIMESTAMP\)`\)/g,'timestamp("updated_at").notNull().defaultNow()').replace(/import { sqliteTable, text, integer } from "drizzle-orm\/sqlite-core";\nimport { sql } from "drizzle-orm";/g,'import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";').replace(/`drizzle-kit push` for SQLite\/Turso/g,"`drizzle-kit push` for Postgres/Neon"):r}function Os(r){let t={pass:"PASS",warn:"WARN",fail:"FAIL",fix:"FIXED",skip:"SKIP"},e=Math.max(...r.map(u=>u.name.length)),a=r.map(u=>{let c=".".repeat(e-u.name.length+4),h=t[u.status].padEnd(5);return` ${u.name} ${c} ${h} ${u.message}`}),o=r.filter(u=>u.status==="fix").length,i=r.filter(u=>u.status==="fail").length,n=r.filter(u=>u.status==="warn").length,s=[];o>0&&s.push(`${o} fixed`),i>0&&s.push(`${i} failure${i>1?"s":""}`),n>0&&s.push(`${n} warning${n>1?"s":""}`);let l=`mist_doctor results:
10988
10988
 
10989
10989
  `+a.join(`
10990
10990
  `);s.length>0?l+=`
@@ -10994,7 +10994,7 @@ Others can use it in their AI editor:
10994
10994
  All checks passed.`;let d=r.filter(u=>(u.status==="fail"||u.status==="warn")&&u.fix);if(d.length>0){l+=`
10995
10995
 
10996
10996
  To fix:`;for(let u of d)l+=`
10997
- - ${u.fix}`}return l}var Fn=pt.object({projectPath:pt.string().optional().describe("Path to the project directory. Defaults to the current working directory."),checks:pt.array(pt.enum(["project","auth","api","version","methodology","state","structure","env","plan"])).optional().describe("Run only specific checks. Omit to run all."),reportOnly:pt.boolean().optional().describe("If true, report issues without auto-fixing them.")}),En={name:"mist_doctor",description:"Diagnose project health: checks auth, API connectivity, AGENTS.md methodology, project state, version alignment, and structure. Auto-fixes safe issues (missing AGENTS.md, stale state). Run when something feels off.",inputSchema:Fn,handler:async r=>{let t=Fn.parse(r),e=t.projectPath||process.cwd(),a=!t.reportOnly,o=!t.checks,i=t.checks??[],n=x=>o||i.includes(x),s=[],l=null;if(n("project")){let x=Ln(e);s.push(x.result),l=x.config}else l=Ln(e).config;let d=!1;if(n("auth")||n("state")){let x=await Ms();n("auth")&&s.push(x.result),d=x.authValid}let c=null;if(n("api")||n("methodology")){let x=await Rs();n("api")&&s.push(x.result),c=x.scaffold}n("version")&&s.push(Ls()),l?(n("methodology")&&s.push(Ns(e,l,c,a)),n("state")&&s.push(await Fs(e,l,d,a)),n("structure")&&s.push(Es(e)),n("env")&&s.push(Us(e,l)),n("plan")&&s.push(Hs(l))):o&&(s.push({name:"AGENTS.md",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"State sync",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Structure",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Env vars",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Plan",status:"skip",message:"Skipped (not a Mistflow project)."}));let g=Gs(s),y=s.some(x=>x.status==="fail");return p(g,y)}};var Ca=new Ws({name:"mistflow",version:"0.2.0"},{capabilities:{tools:{}},instructions:`Mistflow is a full-stack app builder that creates and deploys web apps from natural language descriptions. When a user asks to build, create, or make a web app, website, landing page, dashboard, internal tool, marketplace, content site, or browser game, use Mistflow tools. Mistflow creates NEW apps from scratch. It does NOT modify existing non-Mistflow codebases.
10997
+ - ${u.fix}`}return l}var Un=pt.object({projectPath:pt.string().optional().describe("Path to the project directory. Defaults to the current working directory."),checks:pt.array(pt.enum(["project","auth","api","version","methodology","state","structure","env","plan"])).optional().describe("Run only specific checks. Omit to run all."),reportOnly:pt.boolean().optional().describe("If true, report issues without auto-fixing them.")}),Hn={name:"mist_doctor",description:"Diagnose project health: checks auth, API connectivity, AGENTS.md methodology, project state, version alignment, and structure. Auto-fixes safe issues (missing AGENTS.md, stale state). Run when something feels off.",inputSchema:Un,handler:async r=>{let t=Un.parse(r),e=t.projectPath||process.cwd(),a=!t.reportOnly,o=!t.checks,i=t.checks??[],n=x=>o||i.includes(x),s=[],l=null;if(n("project")){let x=Fn(e);s.push(x.result),l=x.config}else l=Fn(e).config;let d=!1;if(n("auth")||n("state")){let x=await Ls();n("auth")&&s.push(x.result),d=x.authValid}let c=null;if(n("api")||n("methodology")){let x=await Ns();n("api")&&s.push(x.result),c=x.scaffold}n("version")&&s.push(Fs()),l?(n("methodology")&&s.push(Es(e,l,c,a)),n("state")&&s.push(await Us(e,l,d,a)),n("structure")&&s.push(Hs(e)),n("env")&&s.push(Gs(e,l)),n("plan")&&s.push(Ws(l))):o&&(s.push({name:"AGENTS.md",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"State sync",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Structure",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Env vars",status:"skip",message:"Skipped (not a Mistflow project)."}),s.push({name:"Plan",status:"skip",message:"Skipped (not a Mistflow project)."}));let g=Os(s),y=s.some(x=>x.status==="fail");return p(g,y)}};var Ba=new _s({name:"mistflow",version:"0.2.0"},{capabilities:{tools:{}},instructions:`Mistflow is a full-stack app builder that creates and deploys web apps from natural language descriptions. When a user asks to build, create, or make a web app, website, landing page, dashboard, internal tool, marketplace, content site, or browser game, use Mistflow tools. Mistflow creates NEW apps from scratch. It does NOT modify existing non-Mistflow codebases.
10998
10998
 
10999
10999
  New app workflow:
11000
11000
  1. mist_plan - pass the user's description EXACTLY as written. Do NOT expand, rephrase, or add features. If the tool returns status "confirm_new_project" (safety gate when inside an existing codebase), you MUST ask the user whether to scaffold a new Mistflow app or edit the existing code. If it returns status "clarify", present the discovery questions to the user, collect answers, and call mist_plan again with the conversationId and answers.
@@ -11025,4 +11025,4 @@ Other tools:
11025
11025
  - mist_project: read/update project state, browse landing designs and app styles, view runtime errors, deploy logs, deployment history, and check MCP version.
11026
11026
  - mist_config: manage encrypted app secrets (env vars) and custom domains.
11027
11027
  - mist_browser: navigate, interact with, and screenshot the app during preview or after deploy.
11028
- - mist_doctor: diagnose project health. Checks auth, API connectivity, AGENTS.md methodology, MCP version, project state, structure, and env vars. Auto-fixes safe issues (missing/outdated AGENTS.md, missing local state). Run when something feels off or tools behave unexpectedly.`}),Un=[ir,Sr,Yr,Tn,Dn,Mn,Rn,En];Ca.setRequestHandler(js,async()=>({tools:Un.map(r=>({name:r.name,description:r.description,inputSchema:zs(r.inputSchema)}))}));Ca.setRequestHandler(_s,async r=>{let t=Un.find(e=>e.name===r.params.name);if(!t)return p(`Unknown tool: ${r.params.name}`,!0);try{let e=t.inputSchema.safeParse(r.params.arguments);if(!e.success){let a=e.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join(", ");return p(`Invalid input: ${a}`,!0)}return await t.handler(e.data)}catch(e){let a=e instanceof Error?e.message:"An unexpected error occurred";return console.error("Tool error:",e),p(a,!0)}});async function $s(){let r=process.argv.indexOf("--api-url");r!==-1&&process.argv[r+1]&&(process.env.MISTFLOW_API_URL=process.argv[r+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let t=new Os;await Ca.connect(t),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}$s().catch(r=>{console.error("Fatal error:",r),process.exit(1)});
11028
+ - mist_doctor: diagnose project health. Checks auth, API connectivity, AGENTS.md methodology, MCP version, project state, structure, and env vars. Auto-fixes safe issues (missing/outdated AGENTS.md, missing local state). Run when something feels off or tools behave unexpectedly.`}),Gn=[sr,Tr,Qr,Bn,In,Ln,Nn,Hn];Ba.setRequestHandler($s,async()=>({tools:Gn.map(r=>({name:r.name,description:r.description,inputSchema:Vs(r.inputSchema)}))}));Ba.setRequestHandler(zs,async r=>{let t=Gn.find(e=>e.name===r.params.name);if(!t)return p(`Unknown tool: ${r.params.name}`,!0);try{let e=t.inputSchema.safeParse(r.params.arguments);if(!e.success){let a=e.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join(", ");return p(`Invalid input: ${a}`,!0)}return await t.handler(e.data)}catch(e){let a=e instanceof Error?e.message:"An unexpected error occurred";return console.error("Tool error:",e),p(a,!0)}});async function qs(){let r=process.argv.indexOf("--api-url");r!==-1&&process.argv[r+1]&&(process.env.MISTFLOW_API_URL=process.argv[r+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let t=new js;await Ba.connect(t),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}qs().catch(r=>{console.error("Fatal error:",r),process.exit(1)});