@mistflow-ai/mcp 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import{a as o,b as s,g as i,k as a}from"./chunk-QZYSCQQ4.js";import{existsSync as u,readFileSync as f,writeFileSync as d,mkdirSync as m}from"fs";import{join as l}from"path";function p(t){return l(t,".mistflow","state.json")}function P(t){let e=p(t);if(!u(e))return null;try{return JSON.parse(f(e,"utf-8"))}catch{return null}}function w(t,e){let r=l(t,".mistflow");u(r)||m(r,{recursive:!0}),d(p(t),JSON.stringify(e,null,2)+`
1
+ import{a as o,b as s,g as i,i as a}from"./chunk-VQPRSDSC.js";import{existsSync as u,readFileSync as f,writeFileSync as d,mkdirSync as m}from"fs";import{join as l}from"path";function p(t){return l(t,".mistflow","state.json")}function P(t){let e=p(t);if(!u(e))return null;try{return JSON.parse(f(e,"utf-8"))}catch{return null}}function w(t,e){let r=l(t,".mistflow");u(r)||m(r,{recursive:!0}),d(p(t),JSON.stringify(e,null,2)+`
2
2
  `)}function C(t,e){return{projectId:t,name:e,template:"",features:[],dbSchema:[],deployCount:0,decisions:[],provenance:[]}}async function x(t){let e;try{e=a()}catch{return null}try{let r=await fetch(`${i()}/api/projects/${encodeURIComponent(t)}/state`,{headers:{...e,"Content-Type":"application/json","X-Mistflow-MCP-Version":o()}});try{s(r.headers)}catch{}return r.ok?await r.json():null}catch{return null}}async function R(t,e){let r;try{r=a()}catch{return!1}try{let n=await fetch(`${i()}/api/projects/${encodeURIComponent(t)}/state`,{method:"PUT",headers:{...r,"Content-Type":"application/json","X-Mistflow-MCP-Version":o()},body:JSON.stringify(e)});try{s(n.headers)}catch{}return n.ok}catch{return!1}}function L(t,e){let r=t.toLowerCase(),n=e.toLowerCase();return n.includes(r)||r.includes(n)?!0:r.split(/\s+/).some(c=>c.length>=3&&n.includes(c))}export{p as a,P as b,w as c,C as d,x as e,R as f,L as g};
@@ -0,0 +1,23 @@
1
+ import{existsSync as w,readFileSync as C,writeFileSync as E,mkdirSync as A}from"fs";import{join as y,dirname as h}from"path";import{homedir as O}from"os";import{fileURLToPath as F}from"url";var d=null;function f(){if(d)return d;try{let e=F(import.meta.url),t=h(e);for(let n=0;n<6;n++){let o=y(t,"package.json");if(w(o)){let s=JSON.parse(C(o,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return d=s.version,s.version}let r=h(t);if(r===t)break;t=r}}catch{}return d="0.0.0","0.0.0"}function g(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?[parseInt(t[1],10),parseInt(t[2],10),parseInt(t[3],10)]:null}function x(e,t){let n=g(e),o=g(t);if(!n||!o)return 0;for(let r=0;r<3;r++){if(n[r]<o[r])return-1;if(n[r]>o[r])return 1}return 0}function b(e,t,n){if(n&&x(e,n)<0)return"unsupported";if(!t||x(e,t)>=0)return"none";let r=g(e),s=g(t);return!r||!s?"none":r[0]<s[0]?"major":r[1]<s[1]?"minor":"patch"}var a=null;function P(e){let t=e.get("x-mistflow-mcp-latest")??"",n=e.get("x-mistflow-mcp-min-supported")??"",o=e.get("x-mistflow-mcp-changelog-url")??"";!t&&!n||(a={latest:t,minSupported:n,changelogUrl:o})}function v(){let e=process.env.MISTFLOW_STATE_DIR||y(O(),".mistflow");return y(e,"upgrade-state.json")}function V(){try{let e=v();return w(e)?JSON.parse(C(e,"utf-8")):{}}catch{return{}}}function L(e){try{let t=v(),n=h(t);w(n)||A(n,{recursive:!0}),E(t,JSON.stringify(e,null,2)+`
2
+ `,{mode:384})}catch{}}var S=!1;function N(e){return e==="patch"?7*864e5:e==="minor"?1*864e5:0}function ie(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!a)return null;let e=f();if(e==="0.0.0")return null;let t=b(e,a.latest,a.minSupported);if(t==="none")return null;if(t==="unsupported")return k(t,e,a);if(S)return null;let n=V(),o=Date.now(),r=N(t);return n.dismissedForVersion===a.latest&&t!=="major"||n.lastLatestSeen===a.latest&&n.lastShownMs&&o-n.lastShownMs<r?null:(S=!0,L({...n,lastShownMs:o,lastLatestSeen:a.latest}),k(t,e,a))}function k(e,t,n){let o="npx -y mistflow-ai install",r=n.changelogUrl?`
3
+ What's new: ${n.changelogUrl}`:"";return e==="unsupported"?`
4
+
5
+ ---
6
+ Mistflow ${t} is no longer supported.
7
+ You must upgrade to ${n.latest} or newer to continue:
8
+
9
+ ${o}
10
+
11
+ After upgrading, restart your AI editor.${r}
12
+ ---`:e==="major"?`
13
+
14
+ ---
15
+ Mistflow ${n.latest} is available (you have ${t}).
16
+ This is a major update. Run \`${o}\` and restart your editor.${r}
17
+ ---`:e==="minor"?`
18
+
19
+ --- Mistflow update available: ${t} -> ${n.latest} ---
20
+ Run \`${o}\` to upgrade, then restart your editor.${r}`:`
21
+
22
+ (Mistflow ${n.latest} is out, you have ${t}. Run \`${o}\` when convenient.)`}function ae(){let e=f(),t=a??{latest:"",minSupported:"",changelogUrl:""},n=b(e,t.latest,t.minSupported);return{current:e,latest:t.latest,minSupported:t.minSupported,severity:n,upgradeCmd:"npx -y mistflow-ai install",changelogUrl:t.changelogUrl,backendSignalReceived:a!==null}}import{readFileSync as B,existsSync as D,writeFileSync as q,mkdirSync as K,renameSync as J,unlinkSync as H}from"fs";import{join as U,dirname as W}from"path";import{homedir as Q}from"os";import{randomBytes as z}from"crypto";function I(){return U(Q(),".mistflow","credentials.json")}function m(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}var G="https://api.mistflow.ai";function T(){let e=I();if(!D(e))return null;try{let t=JSON.parse(B(e,"utf-8"));return typeof t.apiKey=="string"?{[G]:t}:t}catch{return null}}function _(){let e=process.env.MISTFLOW_API_KEY;if(e)return{ok:!0,creds:{apiKey:e,orgId:"",orgSlug:"env"}};let t=T();if(!t)return{ok:!1,reason:"missing"};let n=m(),o=t[n];return o&&typeof o.apiKey=="string"&&o.apiKey&&typeof o.orgId=="string"?{ok:!0,creds:o}:{ok:!1,reason:"missing"}}function ge(e){let t=I(),n=W(t);D(n)||K(n,{recursive:!0});let o=T()??{},r=m();o[r]=e;let s=U(n,`.credentials.tmp.${z(8).toString("hex")}`);try{q(s,JSON.stringify(o,null,2)+`
23
+ `,{mode:384}),J(s,t)}catch(c){try{H(s)}catch{}throw c}}function X(){return _().ok}function $(){return m()}function he(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9102")}return"https://mistflow.ai"}var i=class extends Error{constructor(n,o,r,s){super(o);this.code=n;this.statusCode=r;this.details=s;this.name="MistflowApiError"}get isAuth(){return this.code.startsWith("auth_")}get isTransient(){return this.code==="server_error"||this.code==="upstream_error"||this.code==="network_error"||this.code==="rate_limited"}};function Y(){let e=_();if(!e.ok)throw new i("auth_missing","No Mistflow credentials found.",401);return Z(e.creds)}function Z(e){return{Authorization:`ApiKey ${e.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":f()}}function M(e){try{P(e.headers)}catch{}}async function we(){try{let e=await fetch(`${$()}/health`,{method:"GET",signal:AbortSignal.timeout(5e3)});M(e)}catch{}}async function ee(e,t,n,o){for(let r=0;r<2;r++)try{return await fetch(e,{...t,signal:AbortSignal.timeout(n)})}catch(s){let c=s instanceof Error&&s.name==="TimeoutError",p=s instanceof TypeError;if(o&&r===0&&(c||p)){console.error(`[api] Retrying ${e} after ${c?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function te(e){let t=null;try{t=await e.json()}catch{t=null}let n=t&&typeof t.code=="string"?t.code:void 0,o=t&&typeof t.message=="string"?t.message:t&&typeof t.detail=="string"?t.detail:e.statusText||"Request failed";if(n)return new i(n,o,e.status,t?.details);let r=e.status;return r===401?new i("auth_invalid",o,r):r===403?new i("permission_denied",o,r):r===404?new i("not_found",o,r):r===409?new i("conflict",o,r):r===422?new i("validation_error",o,r):r===429?new i("rate_limited",o,r):r>=500?new i("server_error",e.statusText||"Internal server error",r):new i("client_error",o,r)}async function l(e,t={}){let n=Y(),{timeoutMs:o,idempotent:r,...s}=t,c=o??3e4,p=(s.method??"GET").toUpperCase(),j=r??(p==="GET"||p==="HEAD"),u;try{u=await ee(`${$()}${e}`,{...s,headers:{...n,...s.headers}},c,j)}catch(R){throw R instanceof Error&&R.name==="TimeoutError"?new i("network_error","Request timed out. Try again in a moment."):new i("network_error","Cannot reach Mistflow servers. Check your network.")}if(M(u),!u.ok)throw await te(u);return u.json()}var _e=300*1e3;async function Re(e,t,n="neon",o){return l("/api/projects",{method:"POST",body:JSON.stringify({name:e,template:t,db_provider:n,requested_subdomain:o})})}async function xe(e){return l(`/api/deploy/${encodeURIComponent(e)}/status`)}async function Se(e){return l(`/api/deploy/${encodeURIComponent(e)}/logs`)}async function ke(e,t="7d"){return l(`/api/projects/${encodeURIComponent(e)}/errors?period=${encodeURIComponent(t)}`)}async function Ce(e){return l(`/api/projects/${encodeURIComponent(e)}/deployments`)}async function be(e,t){return l(`/api/projects/${encodeURIComponent(e)}/share`,{method:"POST",body:JSON.stringify({is_template:t?.isTemplate??!1,template_description:t?.description})})}export{f as a,P as b,ie as c,ae as d,ge as e,X as f,$ as g,he as h,Y as i,we as j,Re as k,xe as l,Se as m,ke as n,Ce as o,be as p};
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{a as Ye,d as pe,e as ue}from"./chunk-UNFTM4B3.js";import{c as Ae,d as Ne}from"./chunk-H3J2YAUK.js";import{J as et,K as tt,L as Ce,Q as st,V as rt,X as ot,c as We,d as Ee,e as Te,f as Ge,g as de,h as Je,l as Xe,r as Ze,u as Qe}from"./chunk-QZYSCQQ4.js";import{Server as zs}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Fs}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Bs,ListToolsRequestSchema as Hs}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as Ks}from"zod-to-json-schema";function p(i,t=!1){let s=i;try{let e=We();e&&(s=i+e)}catch{}return{content:[{type:"text",text:s}],isError:t}}function V(i){return p(`This is not a Mistflow project (no mistflow.json found at ${i}).
1
+ import{a as V,d as x,e as I}from"./chunk-UNFTM4B3.js";import{c as F,d as A,e as U,f as H,g as R,h as W,j as B,l as K,m as G,n as Y,o as O,p as z}from"./chunk-VQPRSDSC.js";import{Server as Ue}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Oe}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Le,ListToolsRequestSchema as Me}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as De}from"zod-to-json-schema";function i(a,e=!1){let t=a;try{let r=F();r&&(t=a+r)}catch{}return{content:[{type:"text",text:t}],isError:e}}function y(a){return i(`This is not a Mistflow project (no mistflow.json found at ${a}).
2
2
 
3
3
  Mistflow creates new projects from scratch \u2014 it doesn't work inside existing codebases.
4
4
 
@@ -7,262 +7,17 @@ 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)}import{z as je}from"zod";import{platform as Bt}from"os";import{execFile as nt}from"child_process";var Ht=je.object({apiKey:je.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:je.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 Kt(i){return"error"in i}function Vt(i){return new Promise(t=>setTimeout(t,i))}function Wt(i){return new Promise(t=>{let s=Bt();s==="win32"?nt("cmd.exe",["/c","start","",i],e=>{e&&console.error("Could not open browser:",e.message),t(!e)}):nt(s==="darwin"?"open":"xdg-open",[i],r=>{r&&console.error("Could not open browser:",r.message),t(!r)}),setTimeout(()=>t(!1),5e3)})}var Yt={fetch:globalThis.fetch,openBrowser:Wt};async function it(i,t,s,e){let r=s;for(let a=0;a<t;a++){await Vt(r);let n;try{let l=await e.fetch(`${de()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i})});if(!l.ok)continue;n=await l.json()}catch{continue}if(Kt(n))switch(n.error){case"authorization_pending":continue;case"slow_down":r+=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)}let o=n.email||n.org_name||n.org_slug;return Te({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 ${o}. You are ready to build and deploy.`)}return null}async function Gt(i,t=Yt){let s=i;if(s?.apiKey)try{let n=await t.fetch(`${de()}/api/org`,{headers:{Authorization:`ApiKey ${s.apiKey}`}});if(!n.ok)return p("Invalid API key. Check the key and try again.",!0);let o=await n.json();return Te({apiKey:s.apiKey,orgId:o.id,orgSlug:o.slug}),p(`Connected to Mistflow as ${o.slug} via API key. You are ready to build and deploy.`)}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(s?.deviceCode){let n=await it(s.deviceCode,6,5e3,t);return n||p(JSON.stringify({status:"pending",deviceCode:s.deviceCode,instruction:"The user hasn't approved yet. Wait ~15 seconds and call mist_setup again with the same deviceCode."}))}let e;try{let n=await t.fetch(`${de()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)return p("Cannot reach Mistflow servers. Check your internet connection.",!0);e=await n.json()}catch{return p("Cannot reach Mistflow servers. Check your internet connection.",!0)}let r=`${e.verification_uri}?code=${e.user_code}`;console.error(`
11
- Sign in at: ${r}
12
- Your code: ${e.user_code}
13
- `);try{await t.openBrowser(r)}catch{}let a=await it(e.device_code,6,5e3,t);return a||p(JSON.stringify({status:"pending",deviceCode:e.device_code,signInUrl:r,userCode:e.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+e.device_code+"' to check if they approved."}))}var at={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:Ht,handler:i=>Gt(i)};import{z as j}from"zod";import{existsSync as Is,readFileSync as Rs}from"fs";import{join as Es}from"path";import{homedir as Ts}from"os";import{z as me}from"zod";import{existsSync as F,mkdirSync as ge,writeFileSync as z,readFileSync as fe,readdirSync as ft,copyFileSync as ss}from"fs";import{join as x,resolve as ze,dirname as re,isAbsolute as yt}from"path";import{spawn as rs}from"child_process";import{randomBytes as os}from"crypto";import{simpleGit as bt}from"simple-git";function lt(i,t,s,e=3e3){if(t===void 0)return{stop:()=>{}};let r=0,a=!1,o=setInterval(()=>{a||(r++,i.notification({method:"notifications/progress",params:{progressToken:t,progress:r,message:s()}}).catch(()=>{}))},e);return{stop:()=>{a||(a=!0,clearInterval(o))}}}import{existsSync as Jt,mkdirSync as Xt,readFileSync as or,writeFileSync as Zt}from"fs";import{join as ct}from"path";function Qt(i){return ct(i,".mistflow","manifest.json")}function pt(i,t){let s=ct(i,".mistflow");Jt(s)||Xt(s,{recursive:!0}),Zt(Qt(i),JSON.stringify(t,null,2)+`
14
- `)}function ut(i){if(!i)return"Item";let t=i.replace(/([a-z0-9])([A-Z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);return t.length===0?"Item":t.map(s=>s.charAt(0).toUpperCase()+s.slice(1).toLowerCase()).join("")}function es(i){return i&&i.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[^A-Za-z0-9]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"")||"item"}function ts(i){let t=ut(i);return t.charAt(0).toLowerCase()+t.slice(1)}function Oe(){return["# Integration contracts","","This directory holds the single source of truth for every API shape in","this app. Frontend code and backend routes both import from here, so","drift becomes a compile error instead of a silent runtime bug.","","## The convention","","Every entity defined in `db/schema.ts` MUST have a matching contract","file in this directory. Each contract file exports three things:","","- `<Entity>Schema` \u2014 the Zod schema for reading the entity (derived from"," the Drizzle table via `createSelectSchema`).","- `Create<Entity>Input` \u2014 the Zod schema for creating a new row"," (derived via `createInsertSchema`, typically with `id` and"," `createdAt` omitted).","- `<Entity>` \u2014 the inferred TypeScript type from `<Entity>Schema`.","","File name is the kebab-case singular of the entity, e.g. `habit.ts`,","`blog-post.ts`. One entity per file.","","## Example","","```ts","// contracts/habit.ts",'import { createSelectSchema, createInsertSchema } from "drizzle-zod";','import { z } from "zod";','import { habit } from "@/db/schema";',"","export const HabitSchema = createSelectSchema(habit);","export type Habit = z.infer<typeof HabitSchema>;","","export const CreateHabitInput = createInsertSchema(habit).omit({"," id: true,"," createdAt: true,","});","export type CreateHabitInput = z.infer<typeof CreateHabitInput>;","```","","## Usage","","Frontend fetch and backend route should BOTH import from `contracts/`:","","```ts","// app/api/habits/route.ts",'import { CreateHabitInput, HabitSchema } from "@/contracts/habit";',"","export async function POST(request: Request) {"," const body = CreateHabitInput.parse(await request.json());"," const row = await db.insert(habit).values(body).returning();"," return Response.json(HabitSchema.parse(row[0]));","}","```","","```ts","// app/habits/page.tsx",'import type { Habit } from "@/contracts/habit";',"",'const res = await fetch("/api/habits");',"const habits: Habit[] = await res.json();","```","","## Adding a new entity","","1. Add the Drizzle table to `db/schema.ts`.","2. Create the matching `contracts/<entity>.ts` file using the template above.","3. Import from `contracts/` in every route, server action, and client"," component that touches the entity. Never inline the type.","","Run `mist_doctor` to check for entities that are missing a contract.",""].join(`
15
- `)}function dt(){return["<!-- mist:contracts:start -->","## Integration contracts","","Every API shape in this app lives in `contracts/`. The directory holds","Zod schemas derived from `db/schema.ts` via `drizzle-zod`. Frontend and","backend both import from `contracts/`, so type drift becomes a compile","error instead of a runtime bug.","","### Rules for AI agents","","1. Every API route (`app/api/**/route.ts`) and server action MUST"," import its request + response types from `contracts/<entity>.ts`."," Never inline a Zod schema or a TypeScript type for an entity that"," already has a contract. Never `z.object({ ... })` inside a route"," handler for a known entity.","2. When you add a new entity to `db/schema.ts`, you MUST create the"," matching contract file BEFORE writing any route that uses it. Order"," matters: schema -> contract -> route.","3. Validate every request body with the contract's `.parse()` method."," If validation fails, return a 400 with the Zod error message \u2014 the"," contract is the boundary, not a decoration.","4. Validate every response body before returning it. Use the select"," schema's `.parse()` on the row(s) you read from the DB. This catches"," the case where the DB shape has drifted from the contract.","5. Client components that fetch an entity MUST import the inferred",' TypeScript type (`import type { Habit } from "@/contracts/habit"`)'," \u2014 never hand-write a duplicate type.","","### Contract file shape","","```ts","// contracts/<entity>.ts",'import { createSelectSchema, createInsertSchema } from "drizzle-zod";','import { z } from "zod";','import { entityTable } from "@/db/schema";',"","export const EntitySchema = createSelectSchema(entityTable);","export type Entity = z.infer<typeof EntitySchema>;","","export const CreateEntityInput = createInsertSchema(entityTable).omit({"," id: true,"," createdAt: true,","});","export type CreateEntityInput = z.infer<typeof CreateEntityInput>;","```","","### Example route using a contract","","```ts","// app/api/entities/route.ts",'import { db } from "@/lib/db";','import { entityTable } from "@/db/schema";','import { CreateEntityInput, EntitySchema } from "@/contracts/entity";',"","export async function POST(request: Request) {"," const body = CreateEntityInput.parse(await request.json());"," const [row] = await db.insert(entityTable).values(body).returning();"," return Response.json(EntitySchema.parse(row));","}","```","","Run `mist_doctor` to check every entity in `db/schema.ts` has a","contract file. Missing contracts are reported as warnings.","<!-- mist:contracts:end -->",""].join(`
16
- `)}var Me="<!-- mist:contracts:start -->",mt="<!-- mist:contracts:end -->";function Le(i){let t=dt();if(i.includes(Me)){let e=i.indexOf(Me),r=mt,a=i.indexOf(r,e);if(a===-1)return i.slice(0,e)+t;let n=i.slice(a+r.length);return i.slice(0,e)+t+n.replace(/^\n+/,"")}return i.replace(/\s+$/,"")+`
17
-
18
- `+t}function Ue(i,t){let s=ut(i),e=t??ts(i);return['import { createSelectSchema, createInsertSchema } from "drizzle-zod";','import { z } from "zod";',`import { ${e} } from "@/db/schema";`,"","// Select schema \u2014 shape of a row read from the DB. Use this on both","// sides of the wire: backend validates responses, frontend gets types.",`export const ${s}Schema = createSelectSchema(${e});`,`export type ${s} = z.infer<typeof ${s}Schema>;`,"","// Insert schema \u2014 shape accepted when creating a new row. id +","// createdAt are generated server-side, so we strip them from the","// input contract. Adjust if your schema uses different column names.",`export const Create${s}Input = createInsertSchema(${e}).omit({`," id: true,"," createdAt: true,","});",`export type Create${s}Input = z.infer<typeof Create${s}Input>;`,""].join(`
19
- `)}function De(i){return`contracts/${es(i)}.ts`}function ns(i){let t=re(ze(i)),s=10,e=0;for(;e<s&&t!==re(t);){if(F(x(t,"pnpm-workspace.yaml"))||F(x(t,"lerna.json")))return t;let r=x(t,"package.json");if(F(r))try{if(JSON.parse(fe(r,"utf-8")).workspaces)return t}catch{}t=re(t),e++}return null}function $e(i,t,s,e,r,a){return new Promise(n=>{let o=rs(i,t,{cwd:s,stdio:["pipe","pipe","pipe"],timeout:e,env:a?{...process.env,...a}:process.env}),l="",c="";o.stdout?.on("data",h=>{let w=h.toString();if(c+=w,r)for(let d of w.split(`
20
- `).filter(Boolean))r(d)}),o.stderr?.on("data",h=>{let w=h.toString();if(l+=w,r)for(let d of w.split(`
21
- `).filter(Boolean))r(d)}),o.on("close",h=>{if(h===0)n({success:!0});else{let w=l.split(`
22
- `).find(d=>d.startsWith("npm error"))??l.slice(0,300);n({success:!1,error:w})}}),o.on("error",h=>{n({success:!1,error:h.message})})})}var vr=me.object({name:me.string().min(1),plan:me.any(),path:me.string().optional()});function y(i,t,s){let e=x(i,t);ge(re(e),{recursive:!0}),z(e,s)}function wt(i,t){if(!t||typeof t!="object")return;let s=t.acceptanceCriteria;if(!Array.isArray(s)||s.length===0)return;let e=s.map(r=>{let a=r;return{id:String(a.id??""),feature:String(a.feature??""),description:String(a.description??""),priority:a.priority??"p1",status:a.status??"planned",...a.implementedAt?{implementedAt:a.implementedAt}:{},...a.verifiedAt?{verifiedAt:a.verifiedAt}:{},...a.failedAt?{failedAt:a.failedAt}:{},...a.lastError?{lastError:a.lastError}:{}}});pt(i,{version:1,criteria:e})}function vt(i){if(!F(i))return!0;let t;try{t=ft(i)}catch{return!1}return t.filter(e=>e!==".mistflow").length===0}var is={sharp:"0.125rem",subtle:"0.375rem",rounded:"0.75rem",pill:"9999px"},as=[["--color-background","#ffffff"],["--color-foreground","#0a0a0a"],["--color-card","#ffffff"],["--color-card-foreground","#0a0a0a"],["--color-popover","#ffffff"],["--color-popover-foreground","#0a0a0a"],["--color-muted","#f5f5f5"],["--color-muted-foreground","#525252"],["--color-border","#e5e5e5"],["--color-input","#e5e5e5"],["--color-primary","#0a0a0a"],["--color-primary-foreground","#ffffff"],["--color-ring","#0a0a0a"],["--color-secondary","#f5f5f5"],["--color-secondary-foreground","#0a0a0a"],["--color-accent","#f5f5f5"],["--color-accent-foreground","#0a0a0a"],["--color-destructive","#b91c1c"],["--color-destructive-foreground","#ffffff"],["--color-success","#15803d"],["--color-success-foreground","#ffffff"],["--color-warning","#a16207"],["--color-warning-foreground","#ffffff"],["--color-info","#0369a1"],["--color-info-foreground","#ffffff"]];function ls(i){let t=i.match(/^---\n([\s\S]*?)\n---/);if(!t)return null;let s=t[1],e={theme:"light",colors:{},typography:{},rounded:{},spacing:{}},r=s.split(`
23
- `),a=null,n=null,o=l=>l.replace(/^["']|["']$/g,"").trim();for(let l of r){let c=l.replace(/\r$/,"");if(!c.trim()||c.trim().startsWith("#"))continue;if(c.startsWith("theme:")){let d=o(c.slice(6).trim());(d==="dark"||d==="light")&&(e.theme=d);continue}let h=c.match(/^(colors|typography|rounded|spacing):\s*$/);if(h){a=h[1],n=null;continue}if(a==="typography"){let d=c.match(/^ {2}([a-zA-Z0-9_-]+):\s*$/);if(d){n=d[1].replace(/-/g,"_"),e.typography[n]={};continue}let S=c.match(/^ {4}([a-zA-Z0-9_-]+):\s*(.+)$/);if(S&&n){e.typography[n][S[1]]=o(S[2]);continue}}let w=c.match(/^ {2}([a-zA-Z0-9_-]+):\s*(.+)$/);if(w&&a){let d=w[1].replace(/-/g,"_"),S=o(w[2]);a==="colors"?e.colors[d]=S:a==="rounded"?e.rounded[d]=S:a==="spacing"&&(e.spacing[d]=S)}}return e}function cs(i,t){let s=i.colors,r=[["--color-background",s.background],["--color-foreground",s.on_background??s.on_surface],["--color-card",s.surface??s.background],["--color-card-foreground",s.on_surface??s.on_background],["--color-popover",s.surface??s.background],["--color-popover-foreground",s.on_surface??s.on_background],["--color-muted",s.surface_variant??s.outline_variant??s.outline],["--color-muted-foreground",s.on_surface_variant??s.outline],["--color-border",s.outline_variant??s.outline],["--color-input",s.outline_variant??s.outline],["--color-primary",s.primary],["--color-primary-foreground",s.on_primary],["--color-ring",s.primary],["--color-secondary",s.secondary],["--color-secondary-foreground",s.on_secondary],["--color-accent",s.secondary],["--color-accent-foreground",s.on_secondary],["--color-destructive",s.error],["--color-destructive-foreground",s.on_error],["--color-success",s.success],["--color-success-foreground",s.on_success],["--color-warning",s.warning],["--color-warning-foreground",s.on_warning],["--color-info",s.info??s.primary],["--color-info-foreground",s.on_info??s.on_primary]].filter(o=>typeof o[1]=="string").map(([o,l])=>` ${o}: ${l};`).join(`
24
- `),a=[` --radius-sm: ${i.rounded.sm??"0.25rem"};`,` --radius-md: ${i.rounded.md??t};`,` --radius-lg: ${i.rounded.lg??"0.5rem"};`,` --radius-xl: ${i.rounded.xl??"0.75rem"};`].join(`
25
- `);return`@import "tailwindcss";
26
- @import "tw-animate-css";
27
-
28
- @theme {
29
- ${r}
30
- ${a}
31
- }
32
-
33
-
34
- @layer base {
35
- * { border-color: var(--color-border); }
36
- body { background-color: var(--color-background); color: var(--color-foreground); }
37
- }
38
-
39
- :root {
40
- --ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);
41
- --ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
42
- --ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
43
- --duration-micro: 80ms;
44
- --duration-short: 150ms;
45
- --duration-medium: 250ms;
46
- --duration-long: 400ms;
47
- --z-dropdown: 10;
48
- --z-sticky: 20;
49
- --z-modal-backdrop: 30;
50
- --z-modal: 40;
51
- --z-toast: 50;
52
- --z-tooltip: 60;
53
- }
54
-
55
- @keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
56
- @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
57
- .animate-fade-up { animation: fade-up 0.5s var(--ease-expo-out) both; }
58
- .animate-fade-in { animation: fade-in 0.3s var(--ease-quart-out) both; }
59
-
60
- .stagger > :nth-child(1) { animation-delay: 0ms; }
61
- .stagger > :nth-child(2) { animation-delay: 50ms; }
62
- .stagger > :nth-child(3) { animation-delay: 100ms; }
63
- .stagger > :nth-child(4) { animation-delay: 150ms; }
64
- .stagger > :nth-child(5) { animation-delay: 200ms; }
65
- .stagger > :nth-child(6) { animation-delay: 250ms; }
66
-
67
- @media (prefers-reduced-motion: reduce) {
68
- *, *::before, *::after {
69
- animation-duration: 0.01ms !important;
70
- animation-iteration-count: 1 !important;
71
- transition-duration: 0.01ms !important;
72
- }
73
- }
74
-
75
- :focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
76
- :focus:not(:focus-visible) { outline: none; }
77
-
78
- button, [role="button"] { transition: transform 100ms var(--ease-quart-out); }
79
- button:active:not(:disabled), [role="button"]:active:not(:disabled) { transform: scale(0.97); }
80
- `}function ps(i,t,s){let e=i?.borderRadius??"subtle",r=is[e]??"0.375rem";if(s){let n=ls(s);if(n)return cs(n,r)}return`@import "tailwindcss";
81
- @import "tw-animate-css";
82
-
83
- @theme {
84
- ${[...as.map(([n,o])=>` ${n}: ${o};`)," --radius-sm: 0.25rem;",` --radius-md: ${r};`," --radius-lg: 0.5rem;"," --radius-xl: 0.75rem;"].join(`
85
- `)}
86
- }
87
-
88
- @layer base {
89
- * { border-color: var(--color-border); }
90
- body { background-color: var(--color-background); color: var(--color-foreground); }
91
- }
92
-
93
- :root {
94
- --ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);
95
- --ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
96
- --ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);
97
- --duration-micro: 80ms;
98
- --duration-short: 150ms;
99
- --duration-medium: 250ms;
100
- --duration-long: 400ms;
101
- --z-dropdown: 10;
102
- --z-sticky: 20;
103
- --z-modal-backdrop: 30;
104
- --z-modal: 40;
105
- --z-toast: 50;
106
- --z-tooltip: 60;
107
- }
108
-
109
- @keyframes fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
110
- @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
111
- .animate-fade-up { animation: fade-up 0.5s var(--ease-expo-out) both; }
112
- .animate-fade-in { animation: fade-in 0.3s var(--ease-quart-out) both; }
113
-
114
- .stagger > :nth-child(1) { animation-delay: 0ms; }
115
- .stagger > :nth-child(2) { animation-delay: 50ms; }
116
- .stagger > :nth-child(3) { animation-delay: 100ms; }
117
- .stagger > :nth-child(4) { animation-delay: 150ms; }
118
- .stagger > :nth-child(5) { animation-delay: 200ms; }
119
- .stagger > :nth-child(6) { animation-delay: 250ms; }
120
-
121
- @media (prefers-reduced-motion: reduce) {
122
- *, *::before, *::after {
123
- animation-duration: 0.01ms !important;
124
- animation-iteration-count: 1 !important;
125
- transition-duration: 0.01ms !important;
126
- }
127
- }
10
+ If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}import{z as L}from"zod";import{platform as ye}from"os";import{execFile as J}from"child_process";var we=L.object({apiKey:L.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:L.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 ve(a){return"error"in a}function be(a){return new Promise(e=>setTimeout(e,a))}function ke(a){return new Promise(e=>{let t=ye();t==="win32"?J("cmd.exe",["/c","start","",a],r=>{r&&console.error("Could not open browser:",r.message),e(!r)}):J(t==="darwin"?"open":"xdg-open",[a],s=>{s&&console.error("Could not open browser:",s.message),e(!s)}),setTimeout(()=>e(!1),5e3)})}var Se={fetch:globalThis.fetch,openBrowser:ke};async function X(a,e,t,r){let s=t;for(let l=0;l<e;l++){await be(s);let o;try{let c=await r.fetch(`${R()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a})});if(!c.ok)continue;o=await c.json()}catch{continue}if(ve(o))switch(o.error){case"authorization_pending":continue;case"slow_down":s+=5e3;continue;case"expired_token":return i("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return i("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return i("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}let n=o.email||o.org_name||o.org_slug;return U({apiKey:o.api_key,apiKeyId:o.api_key_id,apiKeyName:o.api_key_name,orgId:o.org_id,orgSlug:o.org_slug,email:o.email}),i(`Connected to Mistflow as ${n}. You are ready to build and deploy.`)}return null}async function Pe(a,e=Se){let t=a;if(t?.apiKey)try{let o=await e.fetch(`${R()}/api/org`,{headers:{Authorization:`ApiKey ${t.apiKey}`}});if(!o.ok)return i("Invalid API key. Check the key and try again.",!0);let n=await o.json();return U({apiKey:t.apiKey,orgId:n.id,orgSlug:n.slug}),i(`Connected to Mistflow as ${n.slug} via API key. You are ready to build and deploy.`)}catch{return i("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(t?.deviceCode){let o=await X(t.deviceCode,6,5e3,e);return o||i(JSON.stringify({status:"pending",deviceCode:t.deviceCode,instruction:"The user hasn't approved yet. Wait ~15 seconds and call mist_setup again with the same deviceCode."}))}let r;try{let o=await e.fetch(`${R()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!o.ok)return i("Cannot reach Mistflow servers. Check your internet connection.",!0);r=await o.json()}catch{return i("Cannot reach Mistflow servers. Check your internet connection.",!0)}let s=`${r.verification_uri}?code=${r.user_code}`;console.error(`
11
+ Sign in at: ${s}
12
+ Your code: ${r.user_code}
13
+ `);try{await e.openBrowser(s)}catch{}let l=await X(r.device_code,6,5e3,e);return l||i(JSON.stringify({status:"pending",deviceCode:r.device_code,signInUrl:s,userCode:r.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+r.device_code+"' to check if they approved."}))}var Q={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:we,handler:a=>Pe(a)};import{z as m}from"zod";import{resolve as _}from"path";import{existsSync as C,readFileSync as T}from"fs";import{join as E}from"path";import{z as f}from"zod";import{resolve as Re,join as ee}from"path";import{existsSync as _e,readFileSync as te,writeFileSync as Ce}from"fs";import{existsSync as xe,readFileSync as Ie}from"fs";function Z(a){let e=new Set;if(!xe(a))return e;let t=Ie(a,"utf-8");for(let r of t.split(`
14
+ `)){let s=r.trim();if(!s||s.startsWith("#"))continue;let l=s.indexOf("=");if(l>0){let o=s.slice(0,l).trim(),n=s.slice(l+1).trim();n&&n!=='""'&&n!=="''"&&e.add(o)}}return e}var Te=f.object({action:f.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:f.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:f.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:f.object({key:f.string(),description:f.string().optional(),setupUrl:f.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),oe={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:Te,handler:async a=>{let e=a,t=Re(e.projectPath??process.cwd()),r=ee(t,"mistflow.json");if(!_e(r))return y(t);let s;try{s=JSON.parse(te(r,"utf-8"))}catch{return i("Failed to parse mistflow.json.",!0)}if(e.action==="get"){if(!s.projectId)try{let{ensureBackendRegistered:d}=await import("./self-heal-QQ53MP7L.js");await d(t)&&(s=JSON.parse(te(r,"utf-8")))}catch{}let n=s.plan,c=n?.steps?.filter(d=>d.status==="completed").length??0,p=n?.steps?.length??0,g=Z(ee(t,".env.local")),h=s.env?.required?Object.entries(s.env.required).map(([d,P])=>({name:d,description:P?.description,configured:g.has(d)})):[];s.projectId&&import("./state-manager-26IE6Y5D.js").then(({fetchRemoteState:d})=>d(s.projectId)).catch(()=>{});let u=[`Project: ${s.name}`];if(n){u.push(`Plan: ${n.summary??n.name??"unnamed"} \u2014 ${c}/${p} steps complete`);for(let d of n.steps){let P=d.status==="completed"?"\u2713":d.status==="in_progress"?"\u2192":" ";u.push(` [${P}] ${d.number}. ${d.name}`)}}let k=h.filter(d=>!d.configured);k.length>0&&u.push(`Missing env vars: ${k.map(d=>d.name).join(", ")}`),s.deploy?.url?u.push(`Deployed: ${s.deploy.url} (${s.deploy.count??0} deploys)`):u.push("Not deployed yet");let S=[],N=n?.steps?.find(d=>d.status!=="completed");return N?S.push(`NEXT: Call mist_build with action='implement' to work on step ${N.number} (${N.name}).`):n&&c===p&&(s.deploy?.url||S.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT ask the user \u2014 just deploy.")),k.length>0&&S.push(`Missing env vars in .env.local: ${k.map(d=>d.name).join(", ")}`),i(JSON.stringify({name:s.name,projectId:s.projectId,planProgress:n?{name:n.name,summary:n.summary,totalSteps:p,completedSteps:c,steps:n.steps}:null,envStatus:h,deploy:s.deploy??null,contextMessage:u.join(`
15
+ `),nextSteps:S}))}let l=[];if(e.completedStep!==void 0){let n=s.plan;if(n?.steps){let c=n.steps.findIndex(p=>p.number===e.completedStep);if(c===-1)return i(`Step ${e.completedStep} not found in the plan.`,!0);n.steps[c].status="completed",l.push(`Step ${e.completedStep} marked as completed`)}}e.addEnvVar&&(s.env||(s.env={required:{}}),s.env.required||(s.env.required={}),s.env.required[e.addEnvVar.key]={description:e.addEnvVar.description,setupUrl:e.addEnvVar.setupUrl},l.push(`Added required env var: ${e.addEnvVar.key}`)),Ce(r,JSON.stringify(s,null,2)+`
16
+ `),s.projectId&&import("./state-manager-26IE6Y5D.js").then(async({readLocalState:n,syncRemoteState:c})=>{let p=n(t);p&&await c(s.projectId,p)}).catch(()=>{});let o=[];if(e.completedStep!==void 0){let c=s.plan?.steps?.find(p=>p.status!=="completed");c?o.push(`NEXT: Call mist_build with action='implement' to work on step ${c.number} (${c.name}). Do this now.`):o.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT suggest localhost.")}return e.addEnvVar&&(o.push(`Add ${e.addEnvVar.key} to your .env.local file`),e.addEnvVar.setupUrl&&o.push(`Get the value from: ${e.addEnvVar.setupUrl}`)),i(JSON.stringify({updated:!0,changes:l,message:l.length>0?`Project state saved. ${l.join(". ")}.`:"No changes made.",nextSteps:o.length>0?o:void 0}))}};var M={},v=[];function se(a){let e=v.find(r=>r.id===a);if(e)return e;let t=a.toLowerCase().replace(/[^a-z0-9]/g,"");return v.find(r=>{let s=r.title.toLowerCase().replace(/[^a-z0-9]/g,"");return s===t||s.includes(t)||t.includes(s)})}function re(a){return M[a]}function D(a){return a?v.filter(e=>e.category.toLowerCase()===a.toLowerCase()):v}function ne(a){let e=a??v;if(e.length===0)return"Landing page presets have been replaced by the tone-based system. The landing page tone is now auto-selected based on your app's description during planning.";let t={};for(let s of e){t[s.category]||(t[s.category]=[]);let l=M[s.id],o=l?` \u2014 ${l.description}`:"";t[s.category].push(`${s.id} \u2014 "${s.title}"${o}`)}let r=[];for(let[s,l]of Object.entries(t))r.push(`**${s}**:
17
+ ${l.map(o=>` \u2022 ${o}`).join(`
18
+ `)}`);return r.join(`
128
19
 
129
- :focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
130
- :focus:not(:focus-visible) { outline: none; }
131
-
132
- button, [role="button"] { transition: transform 100ms var(--ease-quart-out); }
133
- button:active:not(:disabled), [role="button"]:active:not(:disabled) { transform: scale(0.97); }
134
- `}var ht={"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 gt(i){let t=i.replace(/[^A-Za-z0-9_ -]/g,"");return ht[t]?ht[t]:t.replace(/\s+/g,"_")}function us(i){return i?{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"}[i.toLowerCase()]??"en":"en"}var ds=new Set(["ar","he","fa","ur"]);function ms(i,t,s){let e=i.replace(/[\\"`$]/g,""),r=us(s),n=ds.has(r)?`lang="${r}" dir="rtl"`:`lang="${r}"`,o=t?.fonts?.heading,l=t?.fonts?.body;if(!o&&!l)return`import type { Metadata } from "next";
135
- import { DM_Sans } from "next/font/google";
136
- import { Toaster } from "sonner";
137
- import "./globals.css";
138
-
139
- const body = DM_Sans({ subsets: ["latin"], variable: "--font-body" });
140
-
141
- export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
142
-
143
- export default function RootLayout({ children }: { children: React.ReactNode }) {
144
- return (
145
- <html ${n}>
146
- <body className={body.variable}>{children}<Toaster richColors /></body>
147
- </html>
148
- );
149
- }
150
- `;let c=gt(o??l),h=gt(l??o);return c===h?`import type { Metadata } from "next";
151
- import { ${c} } from "next/font/google";
152
- import { Toaster } from "sonner";
153
- import "./globals.css";
154
-
155
- const font = ${c}({ subsets: ["latin"], variable: "--font-body" });
156
-
157
- export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
158
-
159
- export default function RootLayout({ children }: { children: React.ReactNode }) {
160
- return (
161
- <html ${n}>
162
- <body className={font.variable}>{children}<Toaster richColors /></body>
163
- </html>
164
- );
165
- }
166
- `:`import type { Metadata } from "next";
167
- import { ${c}, ${h} } from "next/font/google";
168
- import { Toaster } from "sonner";
169
- import "./globals.css";
170
-
171
- const heading = ${c}({ subsets: ["latin"], variable: "--font-heading" });
172
- const body = ${h}({ subsets: ["latin"], variable: "--font-body" });
173
-
174
- export const metadata: Metadata = { title: "${e}", description: "Built with Mistflow" };
175
-
176
- export default function RootLayout({ children }: { children: React.ReactNode }) {
177
- return (
178
- <html ${n}>
179
- <body className={\`\${heading.variable} \${body.variable}\`}>{children}<Toaster richColors /></body>
180
- </html>
181
- );
182
- }
183
- `}function he(i,...t){let s=JSON.stringify(i).toLowerCase();return t.some(e=>s.includes(e.toLowerCase()))}var hs={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 qe(i){let t=i.toLowerCase().replace(/[^a-z]/g,"");for(let[s,e]of Object.entries(hs))if(t.includes(s))return e;return"Circle"}function oe(i){return i.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}function gs(i){if(i.authModel==="none")return null;let t=["/login","/register","/forgot-password","/reset-password","/api/auth","/api/health","/api/webhooks","/api/admin/seed"];if(i.publicPages&&Array.isArray(i.publicPages))for(let a of i.publicPages){if(typeof a!="string"||a.length<1)continue;let n=a.replace(/[\u201C\u201D\u201E\u201F\u2018\u2019\u2033\u2036]/g,"").trim();if(!n)continue;let o=n.startsWith("/")?n:"/"+n;t.includes(o)||t.push(o)}let s=t.filter(a=>a==="/"),e=t.filter(a=>a!=="/"),r=[];r.push('import { NextRequest, NextResponse } from "next/server";'),r.push(""),r.push("const PUBLIC_PREFIXES = [");for(let a of e)r.push(' "'+a+'",');return r.push("];"),r.push(""),s.length>0&&(r.push('const PUBLIC_EXACT = ["'+s.join('", "')+'"];'),r.push("")),r.push("export function middleware(req: NextRequest) {"),r.push(" const { pathname, search } = req.nextUrl;"),r.push(""),s.length>0&&r.push(" if (PUBLIC_EXACT.includes(pathname)) return NextResponse.next();"),r.push(" if (PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))) return NextResponse.next();"),r.push(""),r.push(' const token = req.cookies.get("better-auth.session_token")?.value || req.cookies.get("__Secure-better-auth.session_token")?.value;'),r.push(" if (!token) {"),r.push(' const loginUrl = new URL("/login", req.url);'),r.push(" const params = new URLSearchParams(search);"),r.push(' for (const key of ["verified", "error"]) {'),r.push(" const v = params.get(key);"),r.push(" if (v) loginUrl.searchParams.set(key, v);"),r.push(" }"),r.push(" return NextResponse.redirect(loginUrl);"),r.push(" }"),r.push(""),r.push(" return NextResponse.next();"),r.push("}"),r.push(""),r.push("export const config = {"),r.push(' matcher: ["/((?!_next|static|favicon\\\\.ico).*)"],'),r.push("};"),r.push(""),r.join(`
184
- `)}function fs(i){if(i.navStyle==="none")return null;let t=["/api","/login","/register","/sign-in","/sign-up","/admin","/pricing","/about","/contact","/terms","/privacy","/onboarding","/join","/forgot-password","/reset-password"],e=(i.pages??[]).filter(l=>{let c=l.path??l.route??"";return c==="/"||c===""||c.includes("[")||c.replace(/^\//,"").split("/").length>1?!1:!t.some(w=>c.startsWith(w))}).map(l=>{let c=l.path??l.route??"",h=c.startsWith("/")?c:"/"+c,w=l.name??oe(c.replace(/^\//,"")),d=qe(w);return{label:w,href:h,icon:d}});e.some(l=>l.href==="/dashboard")||e.unshift({label:"Dashboard",href:"/dashboard",icon:"Home"});let r=i.authModel==="none",a=[...new Set(e.map(l=>l.icon))];r||a.push("LogOut");let n=oe(i.name);if(i.navStyle==="topbar"){let l=[];l.push('"use client";'),l.push(""),l.push('import Link from "next/link";'),l.push('import { usePathname } from "next/navigation";'),r||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 { "+a.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 c of e)l.push(' { label: "'+c.label+'", href: "'+c.href+'", icon: '+c.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>"),r?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(`
185
- `)}}let o=[];o.push('"use client";'),o.push(""),o.push('import { useState } from "react";'),o.push('import Link from "next/link";'),o.push('import { usePathname } from "next/navigation";'),r||o.push('import { authClient } from "@/lib/auth-client";'),o.push('import { Button } from "@/components/ui/button";'),o.push('import { Sheet, SheetContent, SheetTrigger, SheetTitle } from "@/components/ui/sheet";'),o.push('import { cn } from "@/lib/utils";'),o.push("import { Menu, "+a.join(", ")+' } from "lucide-react";'),o.push(""),o.push("interface SidebarProps {"),o.push(" user: { name: string | null; email: string; role?: string | undefined };"),o.push("}"),o.push(""),o.push("const NAV_ITEMS = [");for(let l of e)o.push(' { label: "'+l.label+'", href: "'+l.href+'", icon: '+l.icon+" },");return o.push("];"),o.push(""),o.push('function NavContent({ pathname, user, onNavigate }: { pathname: string; user: SidebarProps["user"]; onNavigate?: () => void }) {'),o.push(" return ("),o.push(" <>"),o.push(' <div className="flex h-14 items-center border-b px-4">'),o.push(' <span className="text-lg font-semibold">'+n+"</span>"),o.push(" </div>"),o.push(' <nav className="flex-1 space-y-1 p-2">'),o.push(" {NAV_ITEMS.map((item) => ("),o.push(" <Link"),o.push(" key={item.href}"),o.push(" href={item.href}"),o.push(" onClick={onNavigate}"),o.push(' aria-current={pathname === item.href ? "page" : undefined}'),o.push(" className={cn("),o.push(' "flex items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium transition-colors",'),o.push(' pathname === item.href ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"'),o.push(" )}"),o.push(" >"),o.push(' <item.icon className="h-4 w-4" />'),o.push(" {item.label}"),o.push(" </Link>"),o.push(" ))}"),o.push(" </nav>"),o.push(' <div className="border-t p-4">'),o.push(' <div className="flex items-center gap-3">'),o.push(' <div className="flex-1 truncate">'),o.push(' <p className="truncate text-sm font-medium">{user.name ?? "User"}</p>'),o.push(' <p className="truncate text-xs text-muted-foreground">{user.email}</p>'),o.push(" </div>"),r||(o.push(" <Button"),o.push(' variant="ghost"'),o.push(' size="icon"'),o.push(' onClick={() => authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = "/login"; } } })}'),o.push(" >"),o.push(' <LogOut className="h-4 w-4" />'),o.push(" </Button>")),o.push(" </div>"),o.push(" </div>"),o.push(" </>"),o.push(" );"),o.push("}"),o.push(""),o.push("export default function Sidebar({ user }: SidebarProps) {"),o.push(" const pathname = usePathname();"),o.push(" const [open, setOpen] = useState(false);"),o.push(""),o.push(" return ("),o.push(" <>"),o.push(' <aside className="hidden md:flex h-screen w-64 flex-col border-r bg-card">'),o.push(" <NavContent pathname={pathname} user={user} />"),o.push(" </aside>"),o.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">'),o.push(" <Sheet open={open} onOpenChange={setOpen}>"),o.push(" <SheetTrigger asChild>"),o.push(' <Button variant="ghost" size="icon" className="-ml-2">'),o.push(' <Menu className="h-5 w-5" />'),o.push(" </Button>"),o.push(" </SheetTrigger>"),o.push(' <SheetContent side="left" className="w-64 p-0">'),o.push(' <SheetTitle className="sr-only">Navigation</SheetTitle>'),o.push(' <div className="flex h-full flex-col">'),o.push(" <NavContent pathname={pathname} user={user} onNavigate={() => setOpen(false)} />"),o.push(" </div>"),o.push(" </SheetContent>"),o.push(" </Sheet>"),o.push(' <span className="text-lg font-semibold">'+n+"</span>"),o.push(" </div>"),o.push(" </>"),o.push(" );"),o.push("}"),o.push(""),{path:"components/sidebar.tsx",content:o.join(`
186
- `)}}function ys(i){if(!i.roles||i.roles.length===0)return null;let t=i.roles,s=i.defaultRole??t[0],e=[];e.push("export type Role = "+t.map(r=>'"'+r+'"').join(" | ")+";"),e.push(""),e.push("export const ROLES = ["+t.map(r=>'"'+r+'"').join(", ")+"] as const;"),e.push(""),e.push('export const DEFAULT_ROLE: Role = "'+s+'";'),e.push(""),e.push("export const ROLE_LABELS: Record<Role, string> = {");for(let r of t){let a=r.charAt(0).toUpperCase()+r.slice(1);e.push(' "'+r+'": "'+a+'",')}return e.push("};"),e.push(""),e.push("export function getUserRole(user: Record<string, unknown>): Role {"),e.push(" const role = (user.role as string) ?? DEFAULT_ROLE;"),e.push(" if (ROLES.includes(role as Role)) return role as Role;"),e.push(" return DEFAULT_ROLE;"),e.push("}"),e.push(""),e.push("export function hasRole(userRole: string | undefined, required: Role | Role[]): boolean {"),e.push(" if (!userRole) return false;"),e.push(" const allowed = Array.isArray(required) ? required : [required];"),e.push(" return allowed.includes(userRole as Role);"),e.push("}"),e.push(""),e.join(`
187
- `)}function bs(i){let t=oe(i.name);if(i.authModel==="none"){let a=[];return a.push("export default function HomePage() {"),a.push(" return ("),a.push(' <main className="flex min-h-screen flex-col items-center justify-center p-8">'),a.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),i.summary&&a.push(' <p className="mt-4 text-lg text-muted-foreground">'+i.summary+"</p>"),a.push(" </main>"),a.push(" );"),a.push("}"),a.push(""),a.join(`
188
- `)}let s=i.publicPages?.includes("/"),e=i.design?.landingTone;if(s&&e){let a=[];return a.push('import Link from "next/link";'),a.push(""),a.push("export default function HomePage() {"),a.push(" return ("),a.push(' <main className="flex min-h-screen flex-col">'),a.push(' <section className="flex flex-1 flex-col items-center justify-center gap-6 px-4 py-24 text-center">'),a.push(' <h1 className="text-5xl font-bold tracking-tight">'+t+"</h1>"),i.summary&&a.push(' <p className="max-w-2xl text-xl text-muted-foreground">'+i.summary+"</p>"),a.push(' <div className="flex gap-4">'),a.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">'),a.push(" Get Started"),a.push(" </Link>"),a.push(' <Link href="/login" className="inline-flex h-11 items-center rounded-md border px-8 text-sm font-medium hover:bg-muted">'),a.push(" Sign In"),a.push(" </Link>"),a.push(" </div>"),a.push(" </section>"),a.push(" </main>"),a.push(" );"),a.push("}"),a.push(""),a.join(`
189
- `)}if(s){let a=[];return a.push('import Link from "next/link";'),a.push(""),a.push("export default function HomePage() {"),a.push(" return ("),a.push(' <main className="flex min-h-screen flex-col items-center justify-center gap-6 p-8 text-center">'),a.push(' <h1 className="text-4xl font-bold">'+t+"</h1>"),i.summary&&a.push(' <p className="text-lg text-muted-foreground">'+i.summary+"</p>"),a.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">'),a.push(" Sign In"),a.push(" </Link>"),a.push(" </main>"),a.push(" );"),a.push("}"),a.push(""),a.join(`
190
- `)}let r=[];return r.push('import { headers } from "next/headers";'),r.push('import { redirect } from "next/navigation";'),r.push('import { auth } from "@/lib/auth";'),r.push(""),r.push("export default async function HomePage() {"),r.push(" const session = await auth.api.getSession({ headers: await headers() });"),r.push(' if (session) redirect("/dashboard");'),r.push(' redirect("/login");'),r.push("}"),r.push(""),r.join(`
191
- `)}function ws(i,t){let s=i.authModel==="none",e=[];s||(e.push('import { Suspense } from "react";'),e.push('import { headers } from "next/headers";'),e.push('import { redirect } from "next/navigation";'),e.push('import { auth } from "@/lib/auth";'),e.push('import { VerifiedBanner } from "@/components/auth/verified-banner";')),i.navStyle==="topbar"?e.push('import TopNav from "@/components/topnav";'):i.navStyle!=="none"&&e.push('import Sidebar from "@/components/sidebar";'),!s&&i.roles&&i.roles.length>0&&e.push('import { getUserRole } from "@/lib/roles";'),e.push(""),e.push("export default async function DashboardLayout({ children }: { children: React.ReactNode }) {"),s?e.push(' const user = { name: "Guest", email: "" };'):(e.push(" const session = await auth.api.getSession({ headers: await headers() });"),e.push(' if (!session) redirect("/login");'),e.push(""),i.roles&&i.roles.length>0?(e.push(" const role = getUserRole(session.user as Record<string, unknown>);"),e.push(" const user = { name: session.user.name, email: session.user.email, role };")):(e.push(" const user = {"),e.push(" name: session.user.name,"),e.push(" email: session.user.email,"),e.push(" role: (session.user as Record<string, unknown>).role as string | undefined,"),e.push(" };"))),e.push("");let r=s?"":"<Suspense fallback={null}><VerifiedBanner /></Suspense>";return i.navStyle==="topbar"?(e.push(" return ("),e.push(' <div className="min-h-screen">'),e.push(" <TopNav user={user} />"),e.push(' <main className="mx-auto max-w-7xl p-6">'+r+"{children}</main>"),e.push(" </div>"),e.push(" );")):i.navStyle==="none"?(e.push(" return ("),e.push(' <div className="min-h-screen">'),e.push(' <main className="mx-auto max-w-5xl p-6">'+r+"{children}</main>"),e.push(" </div>"),e.push(" );")):(e.push(" return ("),e.push(' <div className="flex flex-col md:flex-row min-h-screen">'),e.push(" <Sidebar user={user} />"),e.push(' <main className="flex-1 overflow-x-hidden p-4 md:p-6">'+r+"{children}</main>"),e.push(" </div>"),e.push(" );")),e.push("}"),e.push(""),e.join(`
192
- `)}function vs(i){let t=oe(i.name),s=i.dataModel??[],e=[];if(s.length>0){let r=s.map(n=>qe(n.entity??n.name??"item")),a=[...new Set(r)];e.push('import { Card, CardContent } from "@/components/ui/card";'),e.push("import { "+a.join(", ")+' } from "lucide-react";'),e.push("")}if(e.push("export default function DashboardPage() {"),e.push(" return ("),e.push(' <div className="space-y-6">'),e.push(" <div>"),e.push(' <h1 className="text-3xl font-bold">'+t+"</h1>"),i.summary&&e.push(' <p className="mt-1 text-muted-foreground">'+i.summary+"</p>"),e.push(" </div>"),s.length>0){e.push(' <div className="rounded-lg border p-8 text-center">'),e.push(' <h2 className="text-lg font-semibold">Get started</h2>'),e.push(` <p className="mt-1 text-sm text-muted-foreground">Here's what you can do</p>`),e.push(' <div className="mt-6 grid gap-3 sm:grid-cols-2 text-left">');for(let r of s){let a=r.entity??r.name??"Item",n=qe(a),o=oe(a.replace(/_/g,"-"));e.push(' <div className="flex items-center gap-3 rounded-md border p-3">'),e.push(" <"+n+' className="h-5 w-5 text-muted-foreground" />'),e.push(' <span className="text-sm font-medium">Add your first '+o+"</span>"),e.push(" </div>")}e.push(" </div>"),e.push(" </div>")}return e.push(" </div>"),e.push(" );"),e.push("}"),e.push(""),e.join(`
193
- `)}function Ss(i,t=!1){if(!i.multiTenant)return null;let s=[];return t?(s.push('import { pgTable, text, timestamp, index } from "drizzle-orm/pg-core";'),s.push('import { user } from "./auth";'),s.push(""),s.push('export const organization = pgTable("organization", {'),s.push(' id: text("id").primaryKey(),'),s.push(' name: text("name").notNull(),'),s.push(' slug: text("slug").unique().notNull(),'),s.push(' createdAt: timestamp("created_at").defaultNow().notNull(),'),s.push(' updatedAt: timestamp("updated_at").defaultNow().notNull(),'),s.push("});"),s.push(""),s.push("export const orgMember = pgTable(")):(s.push('import { sqliteTable, text, index } from "drizzle-orm/sqlite-core";'),s.push('import { sql } from "drizzle-orm";'),s.push('import { user } from "./auth";'),s.push(""),s.push('export const organization = sqliteTable("organization", {'),s.push(' id: text("id").primaryKey(),'),s.push(' name: text("name").notNull(),'),s.push(' slug: text("slug").unique().notNull(),'),s.push(' createdAt: text("created_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push(' updatedAt: text("updated_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push("});"),s.push(""),s.push("export const orgMember = sqliteTable(")),s.push(' "org_member",'),s.push(" {"),s.push(' id: text("id").primaryKey(),'),s.push(' orgId: text("org_id").notNull().references(() => organization.id),'),s.push(' userId: text("user_id").notNull().references(() => user.id),'),s.push(' role: text("role").notNull(),'),t?s.push(' joinedAt: timestamp("joined_at").defaultNow().notNull(),'):s.push(' joinedAt: text("joined_at").default(sql`CURRENT_TIMESTAMP`).notNull(),'),s.push(" },"),s.push(" (table) => ({"),s.push(' orgIdx: index("org_member_org_idx").on(table.orgId),'),s.push(' userIdx: index("org_member_user_idx").on(table.userId),'),s.push(" }),"),s.push(");"),s.push(""),s.join(`
194
- `)}function xs(i){if(!i.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(`
195
- `)}function ks(i){if(!i.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(`
196
- `)}function Ps(i,t,s){let e=[],r=i.split("-").map(l=>l.charAt(0).toUpperCase()+l.slice(1)).join(" ");e.push(`# ${r}`),e.push(""),t?.summary&&(e.push(t.summary),e.push(""));let a=t?.features??[];if(a.length>0){e.push("## Features"),e.push("");for(let l of a){let c=l.description?` \u2014 ${l.description}`:"";e.push(`- **${l.name}**${c}`)}e.push("")}e.push("## Tech Stack"),e.push(""),e.push("| Layer | Technology |"),e.push("|-------|------------|"),e.push("| Framework | Next.js 15 (App Router) |"),e.push("| Database | Mistflow Cloud (Postgres) + Drizzle ORM |"),e.push("| Auth | Better Auth (email/password, social login) |"),e.push("| Styling | Tailwind CSS + shadcn/ui |"),e.push("| Deployment | Mistflow Cloud |"),s.hasStripe&&e.push("| Payments | Stripe |"),s.hasResend&&e.push("| Email | Resend + React Email |"),s.hasStorage&&e.push("| File Storage | Mistflow Cloud (managed blob storage) |"),s.hasAdmin&&e.push("| Admin | Better Auth admin plugin |"),s.hasAI&&e.push("| AI | Vercel AI SDK + OpenAI |"),e.push("");let n=t?.pages??[];if(n.length>0){e.push("## Pages"),e.push(""),e.push("| Route | Description |"),e.push("|-------|-------------|");for(let l of n){let c=l.path??l.route??l.name??"",h=l.description??"";e.push(`| \`${c.startsWith("/")?c:"/"+c}\` | ${h} |`)}e.push("")}let o=t?.dataModel??[];if(o.length>0){e.push("## Data Model"),e.push("");for(let l of o){let c=l.entity??l.name??"Unknown";if(e.push(`### ${c}`),e.push(""),l.fields.length>0){if(typeof l.fields[0]=="string")e.push(`Fields: ${l.fields.join(", ")}`);else{e.push("| Field | Type |"),e.push("|-------|------|");for(let h of l.fields)e.push(`| ${h.name} | ${h.type} |`)}e.push("")}}}return e.push("## Getting Started"),e.push(""),e.push("### Prerequisites"),e.push(""),e.push("- Node.js 20+"),e.push("- npm"),e.push(""),e.push("### Install"),e.push(""),e.push("Use the Mistflow CLI (recommended \u2014 streams output live and has no 60s tool-call timeout):"),e.push(""),e.push("```bash"),e.push("npx -y @mistflow-ai/cli install"),e.push("```"),e.push(""),e.push("Or plain npm if you prefer:"),e.push(""),e.push("```bash"),e.push("npm install"),e.push("```"),e.push(""),e.push("### Set up environment"),e.push(""),e.push("Copy `.env.example` to `.env.local` and fill in the values:"),e.push(""),e.push("```bash"),e.push("cp .env.example .env.local"),e.push("```"),e.push(""),e.push("| Variable | Description | Required |"),e.push("|----------|-------------|----------|"),s.isNeon?e.push("| `DATABASE_URL` | Postgres connection URL | Yes |"):(e.push("| `TURSO_URL` | Database connection URL | Yes |"),e.push("| `TURSO_AUTH_TOKEN` | Database auth token | Yes |")),e.push("| `AUTH_SECRET` | Auth encryption secret (auto-generated) | Yes |"),s.hasStripe&&(e.push("| `STRIPE_SECRET_KEY` | Stripe secret key | Yes |"),e.push("| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | Yes |"),e.push("| `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe publishable key | Yes |")),s.hasResend&&(e.push("| `RESEND_API_KEY` | Resend API key | Yes |"),e.push("| `EMAIL_FROM` | Sender email address | Yes (production) |")),s.hasStorage&&(e.push("| `MISTFLOW_API_KEY` | Mistflow API key for file storage | Yes |"),e.push("| `MISTFLOW_PROJECT_ID` | Mistflow project ID | Yes |")),s.hasAI&&e.push("| `OPENAI_API_KEY` | OpenAI API key | Yes |"),e.push(""),e.push("### Local database"),e.push(""),s.isNeon?(e.push("For local development, start a local Postgres server:"),e.push(""),e.push("```bash"),e.push("# Using Docker:"),e.push("docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:17"),e.push("# Or install via Homebrew: brew install postgresql@17 && brew services start postgresql@17"),e.push("```")):(e.push("For local development, start a local Turso server:"),e.push(""),e.push("```bash"),e.push("npx turso dev"),e.push("```")),e.push(""),e.push("Then set up the database:"),e.push(""),e.push("```bash"),e.push("npm run db:push"),e.push("```"),e.push(""),e.push("### Run"),e.push(""),e.push("```bash"),e.push("npm run dev"),e.push("```"),e.push(""),e.push("Open [http://localhost:3000](http://localhost:3000)."),e.push(""),e.push("## Project Structure"),e.push(""),e.push("```"),e.push("app/"),e.push(" (auth)/ Login and registration pages"),e.push(" (dashboard)/ Authenticated app pages"),s.hasAdmin&&e.push(" (admin)/ Admin panel pages"),e.push(" api/ API routes (auth, health, webhooks)"),e.push(" layout.tsx Root layout with fonts and providers"),e.push(" globals.css Design tokens and Tailwind config"),e.push("components/ Reusable UI components"),e.push("db/"),e.push(" schema/ Database table definitions"),e.push(" index.ts Schema exports"),e.push("lib/"),e.push(" auth.ts Better Auth server config"),e.push(" auth-client.ts Better Auth client config"),e.push(` db.ts ${s.isNeon?"Postgres":"SQLite"} database connection`),s.hasStripe&&e.push(" stripe.ts Stripe client"),s.hasResend&&(e.push(" resend.ts Resend client"),e.push(" email.ts Email send helpers")),s.hasStorage&&e.push(" storage.ts File upload/download helpers"),s.hasAI&&e.push(" ai.ts AI client (Vercel AI SDK + OpenAI)"),s.hasResend&&e.push("emails/ React Email templates"),e.push("```"),e.push(""),e.push("## Deploy"),e.push(""),e.push("Deploy to production with Mistflow:"),e.push(""),e.push("```"),e.push("# In your AI editor (Claude Code, Cursor, etc.):"),e.push("mist_deploy action='deploy'"),e.push("```"),e.push(""),e.push("Your app will be live at `https://<app-name>.mistflow.app`."),e.push(""),t?.design&&(e.push("## Design"),e.push(""),t.design.tone&&e.push(`- **Tone**: ${t.design.tone}`),t.design.fonts&&(e.push(`- **Heading font**: ${t.design.fonts.heading}`),e.push(`- **Body font**: ${t.design.fonts.body}`)),t.design.accentColor&&e.push(`- **Accent color**: ${t.design.accentColor}`),t.design.borderRadius&&e.push(`- **Border radius**: ${t.design.borderRadius}`),e.push("")),e.push("---"),e.push(""),e.push("Built with [Mistflow](https://mistflow.ai)"),e.push(""),e.join(`
197
- `)}async function St(i,t){let{name:s,plan:e,path:r,planId:a}=i;if(!r)return p("mist_build init requires an explicit 'path' \u2014 the absolute directory where the project should be scaffolded. Pass the user's project directory (e.g. /Users/alice/projects/my-app). Do not rely on a default.",!0);if(!yt(r))return p(`mist_build init 'path' must be an absolute path \u2014 received '${r}'. Pass the full absolute path to the target directory.`,!0);let n=ze(r),o=e?.design,l=e?.appStyle,c=e?he(e,"stripe","payment","billing","subscription","checkout","pricing"):!1,h=!0,w=e?he(e,"upload","file storage","image upload","profile picture","attachment","gallery","media","blob"):!1,d=e?he(e,"admin panel","admin dashboard","admin management"):!1,S=e?he(e,"ai integration","openai","llm","ai chat","chatbot","gpt"):!1,M=e,P=!0;if(!vt(n))return p(`A project already exists at this location (${n}). Choose a different name, or delete the existing folder first. (A .mistflow/ folder left over from planning is fine \u2014 init will preserve it.)`,!0);ge(n,{recursive:!0});try{let u=x(re(n),".mistflow","mockups");if(F(u)){let g=ft(u).filter(v=>v.endsWith(".html"));if(g.length>0){let v=x(n,".mistflow","mockups");ge(v,{recursive:!0});for(let E of g)ss(x(u,E),x(v,E));console.error(`Copied ${g.length} mockup file(s) into project`)}}}catch(u){console.error("Could not copy mockup files:",u instanceof Error?u.message:u)}let m=null;try{m=await st("nextjs")}catch(u){console.error("Could not fetch scaffold from API, using minimal scaffold:",u instanceof Error?u.message:u)}if(m){let u=s.toLowerCase().replace(/[^a-z0-9-]/g,"-");for(let b of m.files){if(b.path==="package.json"||b.path==="middleware.ts"||b.path==="components/sidebar.tsx"||b.path==="components/topnav.tsx"||b.path==="app/(dashboard)/layout.tsx"||b.path==="app/(dashboard)/page.tsx"||b.path==="app/(dashboard)/dashboard/page.tsx"||!c&&(b.path.includes("stripe")||b.path.includes("webhook/stripe"))||!h&&(b.path.includes("resend")||b.path.includes("emails/"))||!d&&(b.path.includes("(admin)")||b.path.includes("admin-sidebar"))||P&&(b.path==="lib/db.ts"||b.path==="lib/auth.ts"||b.path==="drizzle.config.ts"||b.path==="db/schema/auth.ts"))continue;let k=b.content.replace(/\{\{APP_NAME\}\}/g,s).replace(/\{\{WORKER_NAME\}\}/g,u);if(P&&b.path==="next.config.ts"&&(k=k.replace(/serverExternalPackages:\s*\[[^\]]*\],?/g,'serverExternalPackages: ["@electric-sql/pglite"],')),b.path==="next.config.ts"){let q=ns(n);q&&(console.error(`[init] Project is inside monorepo at ${q} \u2014 adding outputFileTracingRoot`),k.includes("outputFileTracingRoot")||(k=k.replace('import type { NextConfig } from "next";',`import type { NextConfig } from "next";
198
- import { dirname } from "path";
199
- import { fileURLToPath } from "url";
200
-
201
- const __dirname = dirname(fileURLToPath(import.meta.url));`),k=k.replace("images: {",`outputFileTracingRoot: __dirname,
202
- images: {`)))}!d&&b.path.includes("sidebar")&&(k=k.replace(/\{user\.role === "admin"[\s\S]*?<\/Link>\s*\)\}/m,""),k=k.replace(/, Shield/g,"")),y(n,b.path,k)}let g={...m.dependencies},v={...m.devDependencies};if(g["drizzle-zod"]||(g["drizzle-zod"]="^0.5.1"),P&&(delete g["@libsql/client"],g["@neondatabase/serverless"]="^0.10.0",v["@electric-sql/pglite"]="^0.2.0"),c&&(g.stripe="^17.0.0"),h&&(g.resend="^4.0.0",g["@react-email/components"]="^0.0.31"),S&&(g.ai="^4.0.0",g["@ai-sdk/openai"]="^1.0.0",g.openai="^4.0.0"),y(n,"package.json",JSON.stringify({name:s,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:g,devDependencies:v,optionalDependencies:{"@noble/ciphers":"^1.3.0"},overrides:{react:"19.1.0","react-dom":"19.1.0",punycode:"^2.3.1"}},null,2)),m.methodology){let b=m.methodology;P&&(b=b.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")),b=Le(b),y(n,"AGENTS.md",b),y(n,"CLAUDE.md",b)}let $=e?.designMd;$&&y(n,"DESIGN.md",$),P&&(y(n,"lib/db.ts",['import { neon } from "@neondatabase/serverless";','import { drizzle as drizzleNeon } from "drizzle-orm/neon-http";',"","// 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). Lives in"," // lib/db-local.ts and is loaded through a runtime-only require"," // whose path is built from a variable, so esbuild's static"," // analysis can't follow it. Keeps pglite + its 30MB WASM out of"," // the Worker bundle.",' const localPath: string = "./" + "db-local";'," // eslint-disable-next-line @typescript-eslint/no-require-imports"," const { createLocalDb } = require(localPath);"," _db = createLocalDb();"," }"," }"," 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(`
203
- `)),y(n,"lib/db-local.ts",["// Local-dev-only DB factory. Isolated from lib/db.ts so the production","// Cloudflare Worker bundle never loads drizzle-orm/pglite or its","// transitive 30MB WASM binary. Loaded via runtime-only require() from","// db.ts, where the path is built from a variable to defeat static analysis.","",'const pglitePkg: string = ["@electric-sql", "pglite"].join("/");',"// eslint-disable-next-line @typescript-eslint/no-require-imports","const { PGlite } = require(pglitePkg);","",'const drizzlePath: string = "drizzle-orm/" + "pglite";',"// eslint-disable-next-line @typescript-eslint/no-require-imports","const { drizzle } = require(drizzlePath);","","// eslint-disable-next-line @typescript-eslint/no-explicit-any","export function createLocalDb(): any {",' const client = new PGlite("./local.pg");'," return drizzle(client);","}",""].join(`
204
- `)),y(n,"drizzle.config.ts",['import { defineConfig } from "drizzle-kit";',"","// PGlite for local dev (no Postgres install needed), Mistflow Cloud 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(`
205
- `)),y(n,"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(`
206
- `)),y(n,"lib/auth.ts",['import { betterAuth } from "better-auth";','import { drizzleAdapter } from "better-auth/adapters/drizzle";','import { admin } from "better-auth/plugins/admin";','import { nextCookies } from "better-auth/next-js";','import { db } from "./db";','import * as schema from "@/db";',"","async function sendEmail({ to, subject, html, fallbackUrl }: { to: string; subject: string; html: string; fallbackUrl?: string }) {"," const apiKey = process.env.RESEND_API_KEY;"," if (!apiKey) {"," if (fallbackUrl) {"," console.error(`\\n[auth] No RESEND_API_KEY set. Email to ${to} was not sent.`);"," console.error(`[auth] Dev fallback \u2014 use this link directly:\\n ${fallbackUrl}\\n`);"," } else {"," console.error(`[auth] RESEND_API_KEY not set \u2014 skipping email send to ${to}`);"," }"," 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) {",' const body = await res.text().catch(() => "unknown");'," console.error(`[auth] Email send failed (${res.status}): ${body}`);"," throw new Error(`Email send failed: ${res.status}`);"," }","}","","function createAuth() {",' const baseURL = process.env.BETTER_AUTH_URL || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";',' const isLocal = baseURL.includes("localhost") || baseURL.includes("127.0.0.1");'," const canSendEmail = Boolean(process.env.RESEND_API_KEY);"," // Refuse to boot a production app with email auth but no mail sender. Mistflow's"," // deploy pipeline injects a managed Resend key automatically; if it's missing,"," // that's a real misconfig and silent fallbacks let unverified users sign up."," if (!isLocal && !canSendEmail) {",` throw new Error("[auth] RESEND_API_KEY is required in production. The Mistflow deploy pipeline injects one automatically \u2014 if you're seeing this, check the project's env vars in the dashboard, or set your own RESEND_API_KEY.");`," }"," return betterAuth({"," baseURL,"," trustedOrigins: [baseURL],",' database: drizzleAdapter(db, { provider: "pg", schema }),'," emailAndPassword: {"," enabled: true,"," requireEmailVerification: !isLocal && canSendEmail,"," sendResetPassword: async ({ user, token }: { user: { email: string; name: string }; url: string; token: string }) => {"," // Better Auth's default reset URL points at /reset-password/:token (path),"," // which our frontend doesn't route. We build our own pointing at our"," // /reset-password?token=xxx page which reads the token from the query."," const resetUrl = `${baseURL}/reset-password?token=${token}`;"," 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="${resetUrl}">${resetUrl}</a></p>`,'," fallbackUrl: isLocal ? resetUrl : undefined,"," });"," },"," },"," emailVerification: {"," sendOnSignUp: canSendEmail,"," autoSignInAfterVerification: 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>`,'," fallbackUrl: isLocal ? url : undefined,"," });"," },"," },"," secret: process.env.AUTH_SECRET,",' plugins: [admin({ defaultRole: "user" }), nextCookies()],'," databaseHooks: {"," user: {"," create: {"," // Auto-promote the app owner to admin on first signup. ADMIN_EMAIL"," // is injected by the Mistflow deploy pipeline (the email of the"," // account that ran mist_deploy). Email verification still gates"," // login when Resend is configured, so a collision attempt can't"," // actually sign in without clicking a link delivered to the"," // owner's inbox."," before: async (user: { email?: string; [k: string]: unknown }) => {"," const adminEmail = process.env.ADMIN_EMAIL;"," if (adminEmail && user.email?.toLowerCase() === adminEmail.toLowerCase()) {",' return { data: { ...user, role: "admin" } };'," }"," return { data: 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!,"," },"," } : {}),"," },"," });","}","","// Lazy init \u2014 process.env isn't populated at module scope on Cloudflare Workers.","// The `has` trap is required: better-auth's toNextJsHandler does",'// `"handler" in auth ? auth.handler(request) : auth(request)` \u2014 without a `has`',"// trap the default forwards to the empty target object, returns false, and the","// handler tries to call the Proxy as a function, which throws TypeError and","// returns 500 on every /api/auth/* request.","let _auth: ReturnType<typeof createAuth> | null = null;","export const auth = new Proxy({} as ReturnType<typeof createAuth>, {"," get(_target, prop, receiver) {"," if (!_auth) _auth = createAuth();"," const value = Reflect.get(_auth, prop, receiver);",' if (typeof value === "function") return value.bind(_auth);'," return value;"," },"," has(_target, prop) {"," if (!_auth) _auth = createAuth();"," return prop in _auth;"," },","});",""].join(`
207
- `)))}else y(n,"package.json",JSON.stringify({name:s,version:"0.1.0",private:!0},null,2));let _=e?.designMd;y(n,"app/globals.css",ps(o,l,_)),y(n,"app/layout.tsx",ms(s,o,M?.language)),y(n,"README.md",Ps(s,e,{hasStripe:c,hasResend:h,hasStorage:w,hasAdmin:d,hasAI:S,isNeon:P})),y(n,"contracts/README.md",Oe());let H=e?.dataModel??[],L=new Set,U=0;for(let u of H){let g=u.entity??u.name;if(!g||typeof g!="string")continue;let v=De(g);L.has(v)||(L.add(v),y(n,v,Ue(g)),U++)}U===0&&y(n,"contracts/.gitkeep","");let R=[],O=e?.publicPages;if(Array.isArray(O))R=O;else if(typeof O=="string"){try{R=JSON.parse(O)}catch{R=[]}Array.isArray(R)||(R=[])}if(!R.includes("/")){let u=e?.steps?.some(v=>{let E=((v.name??"")+" "+(v.description??"")).toLowerCase();return E.includes("landing")||E.includes("marketing")||E.includes("homepage")}),g=e?.pages?.some(v=>v.path==="/");(u||g)&&(R=["/",...R])}let T={name:s,summary:e?.summary,authModel:e?.authModel,roles:e?.roles,defaultRole:e?.defaultRole,publicPages:R,navStyle:e?.navStyle,multiTenant:e?.multiTenant,pages:e?.pages,dataModel:e?.dataModel,design:e?.design},ne=gs(T);ne&&y(n,"middleware.ts",ne);let te=fs(T);te&&y(n,te.path,te.content);let ie=ys(T);if(ie&&y(n,"lib/roles.ts",ie),y(n,"app/page.tsx",bs(T)),y(n,"app/(dashboard)/layout.tsx",ws(T,d)),y(n,"app/(dashboard)/dashboard/page.tsx",vs(T)),T.multiTenant){let u=Ss(T,P);u&&y(n,"db/schema/organization.ts",u);let g=xs(T);g&&y(n,"lib/org.ts",g);let v=ks(T);v&&y(n,"components/org-switcher.tsx",v)}y(n,"tsconfig.json",JSON.stringify({compilerOptions:{target:"ES2017",lib:["dom","dom.iterable","esnext"],allowJs:!0,skipLibCheck:!0,strict:!1,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)),c&&y(n,"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(`
208
- `)),h&&(y(n,"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(`
209
- `)),y(n,"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(`
210
- `))),w&&(y(n,"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(`
211
- `)),y(n,"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(`
212
- `))),S&&(y(n,"lib/ai.ts",['import { createOpenAI } from "@ai-sdk/openai";',"","export const openai = createOpenAI({"," apiKey: process.env.OPENAI_API_KEY,","});",""].join(`
213
- `)),y(n,"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(`
214
- `)));let ke={name:s,methodologyVersion:m?.version??"1.0",createdAt:new Date().toISOString(),...a?{planId:a}:{},plan:Array.isArray(e?.steps)?{...e,steps:e.steps.map(u=>({number:u.number,name:u.name??u.title,description:u.description,entities:u.entities,pages:u.pages,features:u.features,status:"pending"}))}:e,dbProvider:"neon",env:{managed:{DATABASE_URL:{description:"Postgres connection URL",scope:"production"},AUTH_SECRET:{description:"Auth encryption secret",scope:"production"},...c?{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"}}:{},...h?{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"}}:{},...w?{MISTFLOW_API_KEY:{description:"Mistflow API key for file storage",scope:"production"},MISTFLOW_PROJECT_ID:{description:"Mistflow project ID",scope:"production"}}:{}},...S?{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:d,hasResend:h,hasStorage:w,hasAI:S,deploy:null};y(n,"mistflow.json",JSON.stringify(ke,null,2)),wt(n,e);let Pe=os(32).toString("hex"),ae=c?`
215
- # Stripe
216
- STRIPE_SECRET_KEY=
217
- STRIPE_WEBHOOK_SECRET=
218
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
219
- `:"",Y=h?`
220
- # Email (Resend)
221
- RESEND_API_KEY=
222
- EMAIL_FROM=onboarding@resend.dev
223
- `:"",K=w?`
224
- # File Storage (Mistflow managed)
225
- MISTFLOW_API_KEY=
226
- MISTFLOW_PROJECT_ID=
227
- `:"",le=S?`
228
- # AI (get your key at https://platform.openai.com/api-keys)
229
- OPENAI_API_KEY=
230
- `:"",ce=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
231
- # Set DATABASE_URL only for production or to use a remote Postgres
232
- # DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`,se=`# Local dev: PGlite is used automatically (zero-install embedded Postgres)
233
- # Set DATABASE_URL only for production or to use a remote Postgres
234
- # DATABASE_URL=postgresql://postgres:postgres@localhost:5432/devdb`;y(n,".env.local",`${ce}
235
- AUTH_SECRET=${Pe}
236
- ${ae}${Y}${K}${le}`),y(n,".env.example",`${se}
237
- AUTH_SECRET=your-secret-here
238
- ${ae}${Y}${K}${le}`);let I=[],G=(u,g)=>{I.push({phase:u,message:g})},J=(u,g)=>{let v=I.find(E=>E.phase===u&&!E.durationMs);v&&(v.durationMs=g)};if(t){let u=lt(t.server,t.progressToken,()=>I[I.length-1]?.message??"Setting up project...");t.cleanup=()=>u.stop()}let _e=M?.requestedSubdomain||void 0,N,f;G("register","Registering project on Mistflow...");let C=Date.now();try{let u=await Ze(s,void 0,"neon",_e);N=u.id;let g=x(n,"mistflow.json"),v=JSON.parse(fe(g,"utf-8"));if(v.projectId=N,z(g,JSON.stringify(v,null,2)),Ae(n,Ne(N,s)),u.managed_env&&Object.keys(u.managed_env).length>0){let E=x(n,".env.local"),D=F(E)?fe(E,"utf-8"):"";for(let[$,b]of Object.entries(u.managed_env)){let k=new RegExp(`^${$}=.*$`,"m");k.test(D)?D=D.replace(k,`${$}=${b}`):D+=`
239
- ${$}=${b}`}z(E,D)}try{let{getBaseUrl:E,getAuthHeaders:D}=await import("./api-client-ORACYR2I.js"),$=D(),b=e?.features,k=e?.steps,q={};Array.isArray(b)&&b.length>0&&(q.features=b.map(Z=>Z.name)),e&&(q.plan=e),Array.isArray(k)&&k.length>0&&(q.provenance=k.map(Z=>({feature:Z.name??Z.title??`Step ${Z.number??"?"}`,user_intent:(Z.description??"").slice(0,500),decisions:"Seeded from plan at init",tradeoffs:"",files_affected:[]}))),Object.keys(q).length>0&&await fetch(`${E()}/api/projects/${encodeURIComponent(N)}/state`,{method:"PUT",headers:{...$,"Content-Type":"application/json"},body:JSON.stringify(q)})}catch{}I[I.length-1].message=`Registered as ${N.slice(0,8)}`}catch(u){let g=u instanceof Error?u.message:String(u);console.error("Could not register project on backend:",g),f=`Project created locally but NOT registered on Mistflow servers (${g}). Deploy will auto-register it.`,I[I.length-1].message="Registration skipped (offline \u2014 deploy will retry)"}J("register",Date.now()-C),G("git","Initializing git repository...");let Ie=Date.now();try{let u=bt(n);await u.init(),await u.add("."),await u.commit("Initial Mistflow project setup"),I[I.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),I[I.length-1].message="Git init skipped"}J("git",Date.now()-Ie);let zt=I.reduce((u,g)=>u+(g.durationMs??0),0),X={projectPath:n,projectId:N,status:"awaiting_install"},Ft=I.map(u=>{let g=u.durationMs?` (${(u.durationMs/1e3).toFixed(1)}s)`:"";return`${u.message}${g}`});X.progress=Ft,X.totalSetupTime=`${(zt/1e3).toFixed(1)}s`;let Re=[];N||Re.push("Project was not registered with Mistflow (not signed in). Run mist_setup to sign in BEFORE deploying \u2014 deploy will fail without it."),f&&(X.registrationWarning=f),Re.length>0&&(X.warnings=Re);let Ve=`NEXT: Run \`npx -y @mistflow-ai/cli install ${n}\` via your shell/bash tool \u2014 NOT via the mist_build MCP tool. The CLI streams npm output live and has no MCP 60s timeout; the MCP install action can hit -32001 timeout on slow installs. Do NOT ask the user for permission \u2014 install is a required follow-up to init, not a decision point. If your environment can't run the CLI, fall back to mist_build action='install' projectPath='${n}'. After install finishes, call mist_build action='implement' to build the first plan step \u2014 also without asking.`;return X.nextAction=N?Ve:`${Ve} IMPORTANT: You MUST also run mist_setup to sign in before deploying \u2014 the project could not be registered because auth is missing.`,p(JSON.stringify(X))}async function xt(i){let{name:t,plan:s,projectId:e,sourceDeploymentId:r,forkToken:a,requiredEnvVars:n,dbProvider:o,planId:l}=i;if(!i.path)return p("mist_build init requires an explicit 'path' \u2014 the absolute directory where the forked project should be scaffolded. Pass the user's project directory (e.g. /Users/alice/projects/my-app). Do not rely on a default.",!0);if(!yt(i.path))return p(`mist_build init 'path' must be an absolute path \u2014 received '${i.path}'. Pass the full absolute path to the target directory.`,!0);let c=ze(i.path);if(!vt(c))return p(`A project already exists at this location (${c}). Choose a different name, or delete the existing folder first. (A .mistflow/ folder left over from planning is fine \u2014 init will preserve it.)`,!0);let{mkdtempSync:h,renameSync:w,rmSync:d,cpSync:S}=await import("fs"),{tmpdir:M}=await import("os"),P=h(x(M(),"mistflow-fork-")),m=x(P,"project");ge(m,{recursive:!0});let _=[],H=(U,R)=>_.push({phase:U,message:R}),L=(U,R)=>{let O=_.find(T=>T.phase===U);O&&(O.durationMs=R)};try{H("download","Downloading source code from template...");let U=Date.now(),R=x(P,"source.tar.gz");try{await rt(r,a,R)}catch(f){d(P,{recursive:!0,force:!0});let C=f instanceof Error?f.message:"Source download failed";return p(`Source code download failed: ${C}. You can still build from the plan \u2014 run mist_build init without the source (omit planId and pass the plan directly).`,!0)}L("download",Date.now()-U),_[_.length-1].message="Source code downloaded",H("extract","Extracting source code...");let O=Date.now(),T=await $e("tar",["-xzf",R,"-C",m,"--exclude","node_modules","--exclude",".git"],m,6e4);if(!T.success)return d(P,{recursive:!0,force:!0}),p(`Failed to extract source archive: ${T.error}`,!0);if(L("extract",Date.now()-O),!F(x(m,"package.json")))return d(P,{recursive:!0,force:!0}),p("Source archive does not contain a package.json. The template may be corrupted.",!0);let ne=[".mistflow",".env.local",".env.example","local.db","local.pg"];for(let f of ne){let C=x(m,f);F(C)&&d(C,{recursive:!0,force:!0})}let te={name:t,projectId:e,template:"nextjs",createdAt:new Date().toISOString(),planId:l??void 0,plan:s,dbProvider:o};z(x(m,"mistflow.json"),JSON.stringify(te,null,2)),wt(m,s);let ie=n.map(f=>{let C=f.description?`# ${f.description}`:`# ${f.key}`,Ie=f.setup_url?` (${f.setup_url})`:"";return`${C}${Ie}
240
- ${f.key}=`});z(x(m,".env.local"),ie.join(`
241
-
242
- `)+`
243
- `);let ke=n.map(f=>`${f.key}=`);z(x(m,".env.example"),ke.join(`
244
- `)+`
245
- `),z(x(m,"README.md"),`# ${t}
246
-
247
- Forked from a Mistflow template. Built with Next.js, Drizzle ORM, and Better Auth.
248
-
249
- ## Getting Started
250
-
251
- 1. Copy \`.env.example\` to \`.env.local\` and fill in your values
252
- 2. Run \`npm run dev\`
253
- 3. Deploy with \`mist_deploy\`
254
- `),H("setup","Setting up project directory...");let Pe=Date.now();try{w(m,c)}catch(f){if(f.code==="EXDEV")S(m,c,{recursive:!0}),d(m,{recursive:!0,force:!0});else throw f}L("setup",Date.now()-Pe),H("install","Installing packages...");let ae=Date.now(),Y=0,K=await $e("npm",["install"],c,12e4,f=>{let C=f.match(/added (\d+) packages/);C&&(Y=parseInt(C[1],10))});if(K.success||(console.error("[initFromSource] npm install failed, retrying..."),K=await $e("npm",["install"],c,12e4,f=>{let C=f.match(/added (\d+) packages/);C&&(Y=parseInt(C[1],10))})),!K.success)return p(`Source code was restored at ${c} but package installation failed: ${K.error}. Run "npm install" manually.`,!0);L("install",Date.now()-ae),_[_.length-1].message=`Installed ${Y||"all"} packages`,H("git","Initializing git repository...");let le=Date.now();try{let f=bt(c);await f.init(),await f.add("."),await f.commit("Forked from Mistflow template"),_[_.length-1].message="Git repository initialized"}catch{console.error("Git initialization failed, continuing without git."),_[_.length-1].message="Git init skipped"}L("git",Date.now()-le);try{let{markLocalSetupDone:f}=await import("./api-client-ORACYR2I.js");await f(e)}catch{console.error("[initFromSource] Could not mark local_setup_done on backend, continuing.")}let ce=JSON.parse(fe(x(c,"mistflow.json"),"utf-8"));ce.projectId=e,z(x(c,"mistflow.json"),JSON.stringify(ce,null,2)),Ae(c,Ne(e,t));let se=s.steps,I=se?.filter(f=>f.status==="completed").length??0,G=se?.filter(f=>f.status==="pending").length??0,J=se?.length??0,_e=n.length>0?`
255
-
256
- Environment variables needed:
257
- `+n.map(f=>` \u2022 ${f.key}${f.description?` \u2014 ${f.description}`:""}`).join(`
258
- `):"",N=G>0?`Source code restored with ${I}/${J} steps complete. ${G} steps need implementation \u2014 call mist_build with action='implement' to apply your changes.`:`Source code fully restored (${J} steps complete). Configure your .env.local, then deploy with mist_deploy.`;return p(JSON.stringify({status:"success",projectPath:c,projectId:e,planStepsCompleted:I,planStepsTotal:J,pendingSteps:G,progress:_,nextAction:N+_e}))}finally{try{d(P,{recursive:!0,force:!0})}catch{}}}var Fe={},Q=[];function ye(i){let t=Q.find(e=>e.id===i);if(t)return t;let s=i.toLowerCase().replace(/[^a-z0-9]/g,"");return Q.find(e=>{let r=e.title.toLowerCase().replace(/[^a-z0-9]/g,"");return r===s||r.includes(s)||s.includes(r)})}function kt(i){return Fe[i]}function Be(i){return i?Q.filter(t=>t.category.toLowerCase()===i.toLowerCase()):Q}function Pt(i){let t=i??Q;if(t.length===0)return"Landing page presets have been replaced by the tone-based system. The landing page tone is now auto-selected based on your app's description during planning.";let s={};for(let r of t){s[r.category]||(s[r.category]=[]);let a=Fe[r.id],n=a?` \u2014 ${a.description}`:"";s[r.category].push(`${r.id} \u2014 "${r.title}"${n}`)}let e=[];for(let[r,a]of Object.entries(s))e.push(`**${r}**:
259
- ${a.map(n=>` \u2022 ${n}`).join(`
260
- `)}`);return e.join(`
261
-
262
- `)}function _s(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function _t(i){return(i?Be(i):Q).map(s=>{let e=Fe[s.id];return{id:s.id,slug:_s(s.title),title:s.title,category:s.category,description:e?.description??"",tags:e?.tags??[],theme:e?.theme??"dark",colors:e?.colors??[],style:e?.style??""}})}function Cs(i){let t=Es(Ts(),".mistflow","plans",`${i}.json`);if(!Is(t))return null;try{let s=JSON.parse(Rs(t,"utf-8"));return s.plan?s:null}catch{return null}}var As=j.object({action:j.enum(["init"]).describe("'init' creates and sets up a new project from your plan \u2014 writes files, registers the project, initializes git. Returns fast; does NOT run npm install."),name:j.string().optional().describe("(init) Project name"),planId:j.string().optional().describe("(init) Plan ID from mist_plan"),plan:j.any().optional().describe("(init) Full plan object \u2014 use planId instead when available"),path:j.string().optional().describe("(init) REQUIRED for action='init'. Absolute path to the target directory where the project should be scaffolded (e.g. /Users/alice/projects/my-app). Do not omit \u2014 there is no default. If unsure, ask the user for their working directory."),landingDesign:j.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:j.string().optional().describe("(init) Optional aesthetic-direction hint passed to the backend LLM. DESIGN.md is generated per-product; this string is a hint, not a catalog ID."),confirmDarkTheme:j.boolean().optional().describe("(init) Set to true only after confirming with the user that a dark-themed app style is intentional for a consumer (b2c) app. Skips the dark-on-consumer-app warning."),heroPhoto:j.boolean().optional().describe("(init) Whether the landing page hero uses a lifestyle photo background. true = Unsplash photo + overlaid glass card (HabitFlow, Airbnb). false = pure CSS gradients + glassmorphism (Stripe, Linear). Pass the user's answer to the heroPhotoQuestion from mist_plan. If omitted, defaults based on the plan's audienceType (b2c \u2192 photo, else \u2192 CSS).")}),It={name:"mist_build",description:"STEP 2 of the Mistflow workflow \u2014 scaffolds a new Mistflow project from a plan. 'init' writes files, registers the project, and initializes git. Returns fast (~10s); does NOT run npm install. Pass the planId returned by mist_plan \u2014 do NOT pass the full plan object. All other build steps (install, implement, build, debug, qa, mockup) moved to the `mist` CLI \u2014 invoke via your shell/bash tool: `npx -y @mistflow-ai/cli <command>`. Call `mist_help` for the full CLI reference.",inputSchema:As,handler:async(i,t)=>{let s=i;switch(s.action){case"init":{if(!s.name)return p("Project name is required for init.",!0);let e=s.plan,r=null;if(s.planId){if(r=Cs(s.planId),!r)return p(`Plan not found for planId '${s.planId}'. The plan may have expired. Call mist_plan again to generate a new plan.`,!0);e=r.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(r?.sourceDeploymentId&&r?.forkToken&&r?.projectId)return xt({name:s.name,plan:e,path:s.path,projectId:r.projectId,sourceDeploymentId:r.sourceDeploymentId,forkToken:r.forkToken,requiredEnvVars:r.requiredEnvVars??[],dbProvider:r.dbProvider??"neon",planId:s.planId});if(s.landingDesign){let c=ye(s.landingDesign);c?e.landingDesign=c.id:console.error(`Landing design '${s.landingDesign}' not found \u2014 ignoring.`)}s.appStyle&&(e.appStyle=s.appStyle);let a=e.design,o=e.audienceType==="b2c",l=s.heroPhoto??o;return e.design={...a??{},heroPhoto:l},St({name:s.name,plan:e,path:s.path,planId:s.planId},t)}default:return p(`Unknown action: ${s.action}. Only 'init' is supported on mist_build. All other build steps (install, implement, build, debug, qa, mockup) moved to the \`mist\` CLI \u2014 invoke via your shell/bash tool: \`npx -y @mistflow-ai/cli <command>\`. Call \`mist_help\` for the full CLI reference.`,!0)}}};import{z as A}from"zod";import{resolve as be}from"path";import{existsSync as we,readFileSync as ve}from"fs";import{join as Se}from"path";import{z as B}from"zod";import{resolve as Ms,join as Et}from"path";import{existsSync as Os,readFileSync as Tt,writeFileSync as Ls}from"fs";import{existsSync as Ns,readFileSync as js}from"fs";function Rt(i){let t=new Set;if(!Ns(i))return t;let s=js(i,"utf-8");for(let e of s.split(`
263
- `)){let r=e.trim();if(!r||r.startsWith("#"))continue;let a=r.indexOf("=");if(a>0){let n=r.slice(0,a).trim(),o=r.slice(a+1).trim();o&&o!=='""'&&o!=="''"&&t.add(n)}}return t}var Us=B.object({action:B.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:B.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:B.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:B.object({key:B.string(),description:B.string().optional(),setupUrl:B.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),Ct={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:Us,handler:async i=>{let t=i,s=Ms(t.projectPath??process.cwd()),e=Et(s,"mistflow.json");if(!Os(e))return V(s);let r;try{r=JSON.parse(Tt(e,"utf-8"))}catch{return p("Failed to parse mistflow.json.",!0)}if(t.action==="get"){if(!r.projectId)try{let{ensureBackendRegistered:m}=await import("./self-heal-X2KBLZZU.js");await m(s)&&(r=JSON.parse(Tt(e,"utf-8")))}catch{}let o=r.plan,l=o?.steps?.filter(m=>m.status==="completed").length??0,c=o?.steps?.length??0,h=Rt(Et(s,".env.local")),w=r.env?.required?Object.entries(r.env.required).map(([m,_])=>({name:m,description:_?.description,configured:h.has(m)})):[];r.projectId&&import("./state-manager-W7N7IHSQ.js").then(({fetchRemoteState:m})=>m(r.projectId)).catch(()=>{});let d=[`Project: ${r.name}`];if(o){d.push(`Plan: ${o.summary??o.name??"unnamed"} \u2014 ${l}/${c} steps complete`);for(let m of o.steps){let _=m.status==="completed"?"\u2713":m.status==="in_progress"?"\u2192":" ";d.push(` [${_}] ${m.number}. ${m.name}`)}}let S=w.filter(m=>!m.configured);S.length>0&&d.push(`Missing env vars: ${S.map(m=>m.name).join(", ")}`),r.deploy?.url?d.push(`Deployed: ${r.deploy.url} (${r.deploy.count??0} deploys)`):d.push("Not deployed yet");let M=[],P=o?.steps?.find(m=>m.status!=="completed");return P?M.push(`NEXT: Call mist_build with action='implement' to work on step ${P.number} (${P.name}).`):o&&l===c&&(r.deploy?.url||M.push("NEXT: All steps complete! Call mist_deploy with action='deploy' to deploy the app now. Do NOT ask the user \u2014 just deploy.")),S.length>0&&M.push(`Missing env vars in .env.local: ${S.map(m=>m.name).join(", ")}`),p(JSON.stringify({name:r.name,projectId:r.projectId,planProgress:o?{name:o.name,summary:o.summary,totalSteps:c,completedSteps:l,steps:o.steps}:null,envStatus:w,deploy:r.deploy??null,contextMessage:d.join(`
264
- `),nextSteps:M}))}let a=[];if(t.completedStep!==void 0){let o=r.plan;if(o?.steps){let l=o.steps.findIndex(c=>c.number===t.completedStep);if(l===-1)return p(`Step ${t.completedStep} not found in the plan.`,!0);o.steps[l].status="completed",a.push(`Step ${t.completedStep} marked as completed`)}}t.addEnvVar&&(r.env||(r.env={required:{}}),r.env.required||(r.env.required={}),r.env.required[t.addEnvVar.key]={description:t.addEnvVar.description,setupUrl:t.addEnvVar.setupUrl},a.push(`Added required env var: ${t.addEnvVar.key}`)),Ls(e,JSON.stringify(r,null,2)+`
265
- `),r.projectId&&import("./state-manager-W7N7IHSQ.js").then(async({readLocalState:o,syncRemoteState:l})=>{let c=o(s);c&&await l(r.projectId,c)}).catch(()=>{});let n=[];if(t.completedStep!==void 0){let l=r.plan?.steps?.find(c=>c.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:a,message:a.length>0?`Project state saved. ${a.join(". ")}.`:"No changes made.",nextSteps:n.length>0?n:void 0}))}};var He={"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 Mistflow Cloud.",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"}},ee=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
20
+ `)}function Ee(a){return a.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ae(a){return(a?D(a):v).map(t=>{let r=M[t.id];return{id:t.id,slug:Ee(t.title),title:t.title,category:t.category,description:r?.description??"",tags:r?.tags??[],theme:r?.theme??"dark",colors:r?.colors??[],style:r?.style??""}})}var q={"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 Mistflow Cloud.",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"}},b=[{id:"resend-email",name:"Resend Email",category:"communication",prompt:`## Resend Email Integration
266
21
 
267
22
  ### File Structure
268
23
  \`\`\`
@@ -1682,16 +1437,16 @@ export function ImageGenerator() {
1682
1437
  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.
1683
1438
  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.
1684
1439
  7. **Network I/O does NOT count as CPU time on Workers.** Image generation wait time is all network I/O.
1685
- 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 Ds(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function At(i){let t=ee.find(e=>e.id===i);if(t)return t;let s=i.toLowerCase().replace(/[^a-z0-9]/g,"");return ee.find(e=>{let r=e.name.toLowerCase().replace(/[^a-z0-9]/g,"");return r===s||r.includes(s)||s.includes(r)})}function Nt(i){return He[i]}function Ke(i){return i?ee.filter(t=>t.category.toLowerCase()===i.toLowerCase()):ee}function jt(i){let t=i??ee,s={};for(let r of t){s[r.category]||(s[r.category]=[]);let a=He[r.id],n=a?` \u2014 ${a.description}`:"",o=a?.packages.length?` (${a.packages.join(", ")})`:"";s[r.category].push(`${r.id} \u2014 "${r.name}"${n}${o}`)}let e=[];for(let[r,a]of Object.entries(s))e.push(`**${r}**:
1686
- ${a.map(n=>` - ${n}`).join(`
1687
- `)}`);return e.join(`
1440
+ 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 je(a){return a.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ie(a){let e=b.find(r=>r.id===a);if(e)return e;let t=a.toLowerCase().replace(/[^a-z0-9]/g,"");return b.find(r=>{let s=r.name.toLowerCase().replace(/[^a-z0-9]/g,"");return s===t||s.includes(t)||t.includes(s)})}function le(a){return q[a]}function $(a){return a?b.filter(e=>e.category.toLowerCase()===a.toLowerCase()):b}function ce(a){let e=a??b,t={};for(let s of e){t[s.category]||(t[s.category]=[]);let l=q[s.id],o=l?` \u2014 ${l.description}`:"",n=l?.packages.length?` (${l.packages.join(", ")})`:"";t[s.category].push(`${s.id} \u2014 "${s.name}"${o}${n}`)}let r=[];for(let[s,l]of Object.entries(t))r.push(`**${s}**:
1441
+ ${l.map(o=>` - ${o}`).join(`
1442
+ `)}`);return r.join(`
1688
1443
 
1689
- `)}function Mt(i){return(i?Ke(i):ee).map(s=>{let e=He[s.id];return{id:s.id,slug:Ds(s.name),name:s.name,category:s.category,description:e?.description??"",tags:e?.tags??[],envVars:e?.envVars??[],docsUrl:e?.docsUrl??"",packages:e?.packages??[],difficulty:e?.difficulty??"medium"}})}var $s=A.object({action:A.enum(["get","update"]).default("get").describe("'get' reads current project state (context oracle \u2014 call before making decisions in an existing project). 'update' marks steps complete or adds env vars. All other project queries moved to the CLI in MCP 0.6.0: `mist projects share`, `mist projects errors`, `mist projects logs`, `mist projects deployments`, `mist projects version`, `mist projects designs`, `mist projects app-styles`, `mist projects integrations`."),projectPath:A.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:A.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:A.object({key:A.string(),description:A.string().optional(),setupUrl:A.string().optional()}).optional().describe("(update) Add a required env var to the project manifest"),templateDescription:A.string().optional().describe("(share) Short description of what this template builds"),category:A.string().optional().describe("(landing-designs) Filter by category"),presetId:A.string().optional().describe("(landing-designs) Get full details for a specific landing design by ID"),integrationId:A.string().optional().describe("(integrations) Get full details for a specific integration preset by ID (e.g. 'stripe-payments', 'resend-email', 'elevenlabs-voice')"),period:A.string().optional().describe("(errors) Time period for errors: '1h', '24h', '7d' (default: '7d')"),deploymentId:A.string().optional().describe("(logs) Deployment ID to fetch logs for. If omitted, fetches logs for the latest deployment.")}),Ot={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. '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:$s,handler:async i=>{let t=i;if(["share","errors","logs","deployments"].includes(t.action)&&!Ge())return p("You need to sign in first. Run mist_setup to connect your account.",!0);switch(t.action){case"get":case"update":return Ct.handler({action:t.action,projectPath:t.projectPath,completedStep:t.completedStep,addEnvVar:t.addEnvVar});case"share":{let e=be(t.projectPath??process.cwd()),r=Se(e,"mistflow.json");if(!we(r))return V(e);let a;try{a=JSON.parse(ve(r,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=a.projectId;if(!n)return p("No project ID found. Deploy the project first to register it.",!0);try{let o=await ot(n,{isTemplate:!0,description:t.templateDescription});return p(JSON.stringify({shareUrl:o.share_url,shareToken:o.share_token,message:`Your project is now a shareable template!
1444
+ `)}function pe(a){return(a?$(a):b).map(t=>{let r=q[t.id];return{id:t.id,slug:je(t.name),name:t.name,category:t.category,description:r?.description??"",tags:r?.tags??[],envVars:r?.envVars??[],docsUrl:r?.docsUrl??"",packages:r?.packages??[],difficulty:r?.difficulty??"medium"}})}var Ne=m.object({action:m.enum(["get","update"]).default("get").describe("'get' reads current project state (context oracle \u2014 call before making decisions in an existing project). 'update' marks steps complete or adds env vars. All other project queries moved to the CLI in MCP 0.6.0: `mist projects share`, `mist projects errors`, `mist projects logs`, `mist projects deployments`, `mist projects version`, `mist projects designs`, `mist projects app-styles`, `mist projects integrations`."),projectPath:m.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:m.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:m.object({key:m.string(),description:m.string().optional(),setupUrl:m.string().optional()}).optional().describe("(update) Add a required env var to the project manifest"),templateDescription:m.string().optional().describe("(share) Short description of what this template builds"),category:m.string().optional().describe("(landing-designs) Filter by category"),presetId:m.string().optional().describe("(landing-designs) Get full details for a specific landing design by ID"),integrationId:m.string().optional().describe("(integrations) Get full details for a specific integration preset by ID (e.g. 'stripe-payments', 'resend-email', 'elevenlabs-voice')"),period:m.string().optional().describe("(errors) Time period for errors: '1h', '24h', '7d' (default: '7d')"),deploymentId:m.string().optional().describe("(logs) Deployment ID to fetch logs for. If omitted, fetches logs for the latest deployment.")}),de={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. '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:Ne,handler:async a=>{let e=a;if(["share","errors","logs","deployments"].includes(e.action)&&!H())return i("You need to sign in first. Run mist_setup to connect your account.",!0);switch(e.action){case"get":case"update":return oe.handler({action:e.action,projectPath:e.projectPath,completedStep:e.completedStep,addEnvVar:e.addEnvVar});case"share":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first to register it.",!0);try{let n=await z(o,{isTemplate:!0,description:e.templateDescription});return i(JSON.stringify({shareUrl:n.share_url,shareToken:n.share_token,message:`Your project is now a shareable template!
1690
1445
 
1691
- Anyone can fork it: ${o.share_url}
1446
+ Anyone can fork it: ${n.share_url}
1692
1447
 
1693
1448
  Others can use it in their AI editor:
1694
- "build me something like ${o.share_url}"`}))}catch(o){let l=o instanceof Error?o.message:"Failed to share project";return p(l,!0)}}case"landing-designs":{if(t.presetId){let n=ye(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 o=kt(t.presetId);return p(JSON.stringify({preset:{id:n.id,title:n.title,category:n.category,description:o?.description??"",style:o?.style??"",theme:o?.theme??"dark",colors:o?.colors??[],tags:o?.tags??[],promptLength:n.prompt.length},message:`Landing design "${n.title}" (${n.category}) \u2014 ${o?.description??""}. To use it, pass landingDesign="${n.id}" when calling mist_plan.`}))}let e=_t(t.category??void 0),r=Be(t.category),a=Pt(r);return p(JSON.stringify({count:e.length,presets:e.map(n=>({id:n.id,title:n.title,category:n.category,description:n.description,style:n.style,theme:n.theme,colors:n.colors})),formatted:a,message:`${e.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 ${Je()}/designs?tab=landing-designs.`}))}case"integrations":{if(t.integrationId){let n=At(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 o=Nt(n.id);return p(JSON.stringify({integration:{id:n.id,name:n.name,category:n.category,description:o?.description??"",packages:o?.packages??[],envVars:o?.envVars??[],docsUrl:o?.docsUrl??"",difficulty:o?.difficulty??"medium"},message:`Integration "${n.name}" (${n.category}) \u2014 ${o?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${o?.envVars?.map(l=>l.key).join(", ")||"none"}. Docs: ${o?.docsUrl??"n/a"}.`}))}let e=Mt(t.category??void 0),r=Ke(t.category??void 0),a=jt(r);return p(JSON.stringify({count:e.length,integrations:e.map(n=>({id:n.id,name:n.name,category:n.category,description:n.description,packages:n.packages,difficulty:n.difficulty,envVars:n.envVars.map(o=>o.key)})),formatted:a,message:`${e.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 e=be(t.projectPath??process.cwd()),r=Se(e,"mistflow.json");if(!we(r))return V(e);let a;try{a=JSON.parse(ve(r,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=a.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let o=await tt(n,t.period??"7d");return o.total===0?p(JSON.stringify({total:0,period:o.period,message:`No runtime errors in the last ${o.period}. The app is running clean.`})):p(JSON.stringify({total:o.total,period:o.period,errors:o.errors,message:`${o.total} runtime error(s) in the last ${o.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(o){let l=o instanceof Error?o.message:"Failed to fetch errors";return p(l,!0)}}case"logs":{let e=be(t.projectPath??process.cwd()),r=Se(e,"mistflow.json");if(!we(r))return V(e);let a;try{a=JSON.parse(ve(r,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=a.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);let o=t.deploymentId;if(!o)try{let l=await Ce(n);if(l.length===0)return p("No deployments found for this project.",!0);o=l[0].id}catch(l){let c=l instanceof Error?l.message:"Failed to fetch deployments";return p(c,!0)}try{let[l,c]=await Promise.all([et(o),Qe(o)]),h=l.filter(d=>d.level==="error"),w=l.filter(d=>d.level==="warn");return p(JSON.stringify({deploymentId:o,status:c.status,errorMessage:c.error??null,totalLogs:l.length,errorCount:h.length,warnCount:w.length,logs:l.map(d=>({time:d.timestamp,level:d.level,phase:d.phase,message:d.message})),message:c.status==="failed"?`Deployment failed. ${h.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${c.status}. ${l.length} log entries (${h.length} errors, ${w.length} warnings).`}))}catch(l){let c=l instanceof Error?l.message:"Failed to fetch deploy logs";return p(c,!0)}}case"deployments":{let e=be(t.projectPath??process.cwd()),r=Se(e,"mistflow.json");if(!we(r))return V(e);let a;try{a=JSON.parse(ve(r,"utf-8"))}catch{return p("Could not read mistflow.json.",!0)}let n=a.projectId;if(!n)return p("No project ID found. Deploy the project first.",!0);try{let o=await Ce(n);return p(JSON.stringify({total:o.length,deployments:o.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:`${o.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(o){let l=o instanceof Error?o.message:"Failed to fetch deployments";return p(l,!0)}}case"version":{Ee().backendSignalReceived||await Xe();let e=Ee(),r=e.severity==="none",a={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:e.current,latest:e.latest||"unknown",minSupported:e.minSupported||"unknown",severity:e.severity,upToDate:r,upgradeCmd:e.upgradeCmd,changelogUrl:e.changelogUrl,backendSignalReceived:e.backendSignalReceived,message:e.backendSignalReceived?`Mistflow MCP ${e.current} (${a[e.severity]??e.severity}). Latest: ${e.latest}.${r?"":` Run \`${e.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${e.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, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as W}from"zod";var qs=W.object({action:W.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:W.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:W.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:W.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:W.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:W.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),Lt={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:qs,handler:async i=>{let t=i,s=await Ye();if(t.action==="navigate"){if(!t.url)return p("URL is required for 'navigate'.",!0);let a=[],n=c=>{c.type()==="error"&&a.push(c.text())};s.on("console",n),await s.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await s.waitForLoadState("networkidle").catch(()=>{});let o=[],l=c=>o.push(c.message);if(s.on("pageerror",l),await s.waitForTimeout(500),s.removeListener("console",n),s.removeListener("pageerror",l),a.length>0||o.length>0){let c=await pe(s),h=[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:c,consoleErrors:a,pageErrors:o,hasErrors:!0})}];if(t.includeScreenshot){let w=await ue(s);h.push({type:"image",data:w.toString("base64"),mimeType:"image/png"})}return{content:h}}}else if(t.action==="go_back")await s.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="go_forward")await s.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(t.action==="click"){if(!t.selector)return p("Selector is required for 'click'.",!0);await s.click(t.selector,{timeout:1e4}),await s.waitForLoadState("domcontentloaded").catch(()=>{}),await s.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 s.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 s.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 s.selectOption(t.selector,t.value)}else if(t.action==="hover"){if(!t.selector)return p("Selector is required for 'hover'.",!0);await s.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 s.keyboard.press(t.value),await s.waitForLoadState("domcontentloaded").catch(()=>{}),await s.waitForTimeout(500)}else if(t.action==="screenshot"){t.url&&(await s.goto(t.url,{waitUntil:"domcontentloaded",timeout:3e4}),await s.waitForLoadState("networkidle").catch(()=>{}));let a;if(t.selector){let n=await s.$(t.selector);if(!n)return p(`Element not found: ${t.selector}`,!0);a=await n.screenshot({type:"png"})}else a=await ue(s,t.fullPage);return{content:[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),message:`Screenshot captured (${t.fullPage?"full page":"viewport"})`})},{type:"image",data:a.toString("base64"),mimeType:"image/png"}]}}else if(t.action==="snapshot"){let a=await pe(s);return{content:[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:a})}]}}let e=await pe(s),r=[{type:"text",text:JSON.stringify({url:s.url(),title:await s.title(),snapshot:e})}];if(t.includeScreenshot){let a=await ue(s);r.push({type:"image",data:a.toString("base64"),mimeType:"image/png"})}return{content:r}}};import{z as Ut}from"zod";var Dt=`# Mistflow CLI reference
1449
+ "build me something like ${n.share_url}"`}))}catch(n){let c=n instanceof Error?n.message:"Failed to share project";return i(c,!0)}}case"landing-designs":{if(e.presetId){let o=se(e.presetId);if(!o)return i(`Preset '${e.presetId}' not found. Use mist_project action='presets' without presetId to list all available presets.`,!0);let n=re(e.presetId);return i(JSON.stringify({preset:{id:o.id,title:o.title,category:o.category,description:n?.description??"",style:n?.style??"",theme:n?.theme??"dark",colors:n?.colors??[],tags:n?.tags??[],promptLength:o.prompt.length},message:`Landing design "${o.title}" (${o.category}) \u2014 ${n?.description??""}. To use it, pass landingDesign="${o.id}" when calling mist_plan.`}))}let r=ae(e.category??void 0),s=D(e.category),l=ne(s);return i(JSON.stringify({count:r.length,presets:r.map(o=>({id:o.id,title:o.title,category:o.category,description:o.description,style:o.style,theme:o.theme,colors:o.colors})),formatted:l,message:`${r.length} landing designs available.${e.category?` Filtered by: ${e.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 ${W()}/designs?tab=landing-designs.`}))}case"integrations":{if(e.integrationId){let o=ie(e.integrationId);if(!o)return i(`Integration '${e.integrationId}' not found. Use mist_project action='integrations' without integrationId to list all available integrations.`,!0);let n=le(o.id);return i(JSON.stringify({integration:{id:o.id,name:o.name,category:o.category,description:n?.description??"",packages:n?.packages??[],envVars:n?.envVars??[],docsUrl:n?.docsUrl??"",difficulty:n?.difficulty??"medium"},message:`Integration "${o.name}" (${o.category}) \u2014 ${n?.description??""}. This blueprint is auto-injected during implementation when your plan has a matching integration step. Required env vars: ${n?.envVars?.map(c=>c.key).join(", ")||"none"}. Docs: ${n?.docsUrl??"n/a"}.`}))}let r=pe(e.category??void 0),s=$(e.category??void 0),l=ce(s);return i(JSON.stringify({count:r.length,integrations:r.map(o=>({id:o.id,name:o.name,category:o.category,description:o.description,packages:o.packages,difficulty:o.difficulty,envVars:o.envVars.map(n=>n.key)})),formatted:l,message:`${r.length} integration blueprints available.${e.category?` Filtered by: ${e.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 r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);try{let n=await Y(o,e.period??"7d");return n.total===0?i(JSON.stringify({total:0,period:n.period,message:`No runtime errors in the last ${n.period}. The app is running clean.`})):i(JSON.stringify({total:n.total,period:n.period,errors:n.errors,message:`${n.total} runtime error(s) in the last ${n.period}. Review the errors above and use mist_build debug to investigate.`}))}catch(n){let c=n instanceof Error?n.message:"Failed to fetch errors";return i(c,!0)}}case"logs":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);let n=e.deploymentId;if(!n)try{let c=await O(o);if(c.length===0)return i("No deployments found for this project.",!0);n=c[0].id}catch(c){let p=c instanceof Error?c.message:"Failed to fetch deployments";return i(p,!0)}try{let[c,p]=await Promise.all([G(n),K(n)]),g=c.filter(u=>u.level==="error"),h=c.filter(u=>u.level==="warn");return i(JSON.stringify({deploymentId:n,status:p.status,errorMessage:p.error??null,totalLogs:c.length,errorCount:g.length,warnCount:h.length,logs:c.map(u=>({time:u.timestamp,level:u.level,phase:u.phase,message:u.message})),message:p.status==="failed"?`Deployment failed. ${g.length} error(s) found in logs. Review the logs above to diagnose the issue.`:`Deployment status: ${p.status}. ${c.length} log entries (${g.length} errors, ${h.length} warnings).`}))}catch(c){let p=c instanceof Error?c.message:"Failed to fetch deploy logs";return i(p,!0)}}case"deployments":{let r=_(e.projectPath??process.cwd()),s=E(r,"mistflow.json");if(!C(s))return y(r);let l;try{l=JSON.parse(T(s,"utf-8"))}catch{return i("Could not read mistflow.json.",!0)}let o=l.projectId;if(!o)return i("No project ID found. Deploy the project first.",!0);try{let n=await O(o);return i(JSON.stringify({total:n.length,deployments:n.map(c=>({id:c.id,status:c.status,errorMessage:c.error_message,durationSeconds:c.duration_seconds,isRollback:!!c.rollback_from_id,createdAt:c.created_at})),message:`${n.length} deployment(s) found. Use mist_project action='logs' deploymentId='<id>' to see detailed logs for any deployment.`}))}catch(n){let c=n instanceof Error?n.message:"Failed to fetch deployments";return i(c,!0)}}case"version":{A().backendSignalReceived||await B();let r=A(),s=r.severity==="none",l={none:"up to date",patch:"patch update available",minor:"minor update available",major:"major update available",unsupported:"UNSUPPORTED \u2014 upgrade required"};return i(JSON.stringify({current:r.current,latest:r.latest||"unknown",minSupported:r.minSupported||"unknown",severity:r.severity,upToDate:s,upgradeCmd:r.upgradeCmd,changelogUrl:r.changelogUrl,backendSignalReceived:r.backendSignalReceived,message:r.backendSignalReceived?`Mistflow MCP ${r.current} (${l[r.severity]??r.severity}). Latest: ${r.latest}.${s?"":` Run \`${r.upgradeCmd}\` and restart your editor to upgrade.`}`:`Mistflow MCP ${r.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 i(`Unknown action: ${e.action}. Use get, update, share, landing-designs, integrations, errors, logs, deployments, or version.`,!0)}}};import{z as w}from"zod";var Ae=w.object({action:w.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:w.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:w.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:w.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:w.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:w.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),ue={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:Ae,handler:async a=>{let e=a,t=await V();if(e.action==="navigate"){if(!e.url)return i("URL is required for 'navigate'.",!0);let l=[],o=p=>{p.type()==="error"&&l.push(p.text())};t.on("console",o),await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{});let n=[],c=p=>n.push(p.message);if(t.on("pageerror",c),await t.waitForTimeout(500),t.removeListener("console",o),t.removeListener("pageerror",c),l.length>0||n.length>0){let p=await x(t),g=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:p,consoleErrors:l,pageErrors:n,hasErrors:!0})}];if(e.includeScreenshot){let h=await I(t);g.push({type:"image",data:h.toString("base64"),mimeType:"image/png"})}return{content:g}}}else if(e.action==="go_back")await t.goBack({waitUntil:"domcontentloaded",timeout:1e4});else if(e.action==="go_forward")await t.goForward({waitUntil:"domcontentloaded",timeout:1e4});else if(e.action==="click"){if(!e.selector)return i("Selector is required for 'click'.",!0);await t.click(e.selector,{timeout:1e4}),await t.waitForLoadState("domcontentloaded").catch(()=>{}),await t.waitForTimeout(500)}else if(e.action==="type"){if(!e.selector)return i("Selector is required for 'type'.",!0);if(!e.value)return i("Value is required for 'type'.",!0);await t.type(e.selector,e.value,{delay:50})}else if(e.action==="fill"){if(!e.selector)return i("Selector is required for 'fill'.",!0);if(!e.value)return i("Value is required for 'fill'.",!0);await t.fill(e.selector,e.value)}else if(e.action==="select_option"){if(!e.selector)return i("Selector is required for 'select_option'.",!0);if(!e.value)return i("Value is required for 'select_option'.",!0);await t.selectOption(e.selector,e.value)}else if(e.action==="hover"){if(!e.selector)return i("Selector is required for 'hover'.",!0);await t.hover(e.selector,{timeout:1e4})}else if(e.action==="press_key"){if(!e.value)return i("Value is required for 'press_key' (e.g. 'Enter').",!0);await t.keyboard.press(e.value),await t.waitForLoadState("domcontentloaded").catch(()=>{}),await t.waitForTimeout(500)}else if(e.action==="screenshot"){e.url&&(await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{}));let l;if(e.selector){let o=await t.$(e.selector);if(!o)return i(`Element not found: ${e.selector}`,!0);l=await o.screenshot({type:"png"})}else l=await I(t,e.fullPage);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),message:`Screenshot captured (${e.fullPage?"full page":"viewport"})`})},{type:"image",data:l.toString("base64"),mimeType:"image/png"}]}}else if(e.action==="snapshot"){let l=await x(t);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:l})}]}}let r=await x(t),s=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:r})}];if(e.includeScreenshot){let l=await I(t);s.push({type:"image",data:l.toString("base64"),mimeType:"image/png"})}return{content:s}}};import{z as me}from"zod";var ge=`# Mistflow CLI reference
1695
1450
 
1696
1451
  The Mistflow CLI handles local execution and long-running operations that
1697
1452
  would hit the MCP 60s tool-call ceiling. Every command below is invokable
@@ -1773,6 +1528,6 @@ and decides the next command. Example end-to-end chain:
1773
1528
  echo '{...}' | mist plan --cid ... --pick-stdin --json
1774
1529
  # \u2192 {"status":"ready","plan":{...}}
1775
1530
  # Then: mist_build init (MCP), mist install (CLI), mist_build implement (MCP), etc.
1776
- `,$t={name:"mist_help",description:"Returns the Mistflow CLI command reference. Call this ONCE at session start (or whenever you're unsure which tool to use) to learn every `mist` CLI command, when to prefer it over an MCP tool, and how to chain calls. Results are static \u2014 no backend round-trip, safe to call frequently.",inputSchema:Ut.object({command:Ut.string().optional().describe("Optional: name of a specific command to get focused reference for. Omit to get the full catalog.")}),handler:async i=>{let{command:t}=i;if(!t)return p(Dt);let s=Dt.split(`
1777
- `),e=new RegExp(`^### \`mist ${t}`),r=-1,a=s.length;for(let n=0;n<s.length;n++)if(e.test(s[n]))r=n;else if(r>=0&&s[n].startsWith("### ")){a=n;break}else if(r>=0&&s[n].startsWith("## ")&&n>r){a=n;break}return r<0?p(`No command named '${t}' found. Call mist_help with no args to see the full catalog.`,!0):p(s.slice(r,a).join(`
1778
- `).trim())}};var xe=new zs({name:"mistflow",version:"0.3.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.\n\nCall `mist_help` at any point for the full CLI command reference. The 5 MCP tools handle short, structured, AI-native flows (auth, scaffold, project state, browser automation, CLI discovery). Everything else is the `mist` CLI \u2014 invoke via your shell/bash tool.\n\nNew app workflow:\n1. Plan: run `mist plan --describe \"<user's description>\" --json` via your shell/bash tool. Pass the user's description EXACTLY as written \u2014 do NOT expand, rephrase, or add features. Relay any returned questions to the user, then submit answers via `echo '<answers-json>' | mist plan --token <id> --answers-stdin --json`. When status becomes \"design_clarify_pending\", poll with `mist plan-directions --cid <id> --wait --json`. Present the directions picker, then finalize with `echo '<pick-json>' | mist plan --cid <id> --pick-stdin --json`. If the CLI returns status \"confirm_new_project\" (safety gate when inside an existing codebase), ask the user whether to scaffold a new Mistflow app or edit the existing code.\n2. Mockup (optional): run `mist mockup --plan-id <id>` via your shell/bash tool \u2014 generates a visual HTML wireframe for user preview. Iterative: pass --feedback to refine, --approved to lock in the design.\n3. mist_build init \u2014 scaffolds the project from the plan and initializes git. Pass the planId from the plan response. Returns in ~10s; does NOT run npm install. (MCP stays primary here \u2014 init is fast and structured.)\n4. Install dependencies: run `mist install <projectPath>` via your shell/bash tool (streams output).\n5. Implement: run `mist implement --project-path <path>` via your shell/bash tool \u2014 executes plan steps one at a time. Call repeatedly until all steps are done; it auto-marks the previous step as completed on each call.\n6. Deploy: run `mist deploy [path]` via your shell/bash tool \u2014 tars, uploads, polls status with live streaming. Subcommands: `mist deploy promote` (staging\u2192prod), `mist deploy preview` (local tunnel), `mist deploy rollback <id>`, `mist deploy verify <url>`, `mist deploy redeploy`.\n7. QA: run `mist qa --project-path <path>` via your shell/bash tool. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n\nCompanion CLI (`@mistflow-ai/cli`, invoke as `mist` or via `npx -y @mistflow-ai/cli`) is the primary path for EVERYTHING except the 5 MCP tools below:\n- `mist plan` / `mist plan-directions` \u2014 plan an app.\n- `mist install` / `mist build` / `mist mockup` / `mist implement` / `mist debug` / `mist qa` \u2014 local project lifecycle.\n- `mist deploy` (+ subcommands above) \u2014 deploy orchestration.\n- `mist status` / `mist fix` \u2014 feature manifest viewer + iteration loop.\n- `mist contracts` \u2014 integration-contract layer management.\n- `mist doctor` \u2014 project health diagnostics.\n- `mist login` / `mist projects` / `mist logs` / `mist env` / `mist domains` / `mist rollback` \u2014 cloud-coordination commands.\n- Call mist_help for the full reference.\n\nIMPORTANT \u2014 chaining discipline: once the user approves the plan, the init \u2192 install \u2192 implement (repeat) \u2192 build \u2192 deploy \u2192 qa chain is expected. Do NOT pause between these calls to ask the user \"should I continue?\" or offer options like \"full build vs step-by-step.\" The tool itself will return a status requiring user input when it actually needs one (e.g. confirm_new_project, confirm_dark_theme, awaiting promotion). Otherwise, chain calls continuously. Brief one-line status updates are fine and encouraged; permission requests are not.\n\nDesign presets (optional, between steps 1 and 3): run `mist projects designs`, `mist projects app-styles`, `mist projects integrations` via your shell/bash tool to browse catalogs. After `mist plan` generates a plan, it may recommend designs and styles \u2014 present these to the user before calling mist_build init.\n\nUpdating an existing Mistflow app:\n- Cosmetic or single-file changes: edit files directly, then `mist deploy` to publish.\n- New page or feature (no new data model): mist_project action=get for context, build it, then `mist deploy`.\n- Feature needing new data model or integration: run `mist plan --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Bug fix: run `mist debug --project-path <path>` to analyze, fix the code, then `mist deploy`.\n\nTemplate forking:\n- Users can share apps as templates via `mist projects share <id>`, which returns a shareable URL.\n- To fork a template, run `mist plan --template-token <token>` via your shell/bash tool. Optionally pass --remix-description to customize it.\n\nThe 5 MCP tools:\n- mist_setup: authentication. Only call when user has never signed in or a tool returned 'auth_missing'/'auth_revoked'. Do NOT call for 500s, 404s, or network errors. Also accepts an apiKey parameter for headless auth.\n- mist_build: only supports action='init' \u2014 scaffolds a new project from a plan. All other build actions moved to the CLI.\n- mist_project: read/write project state (actions: 'get' / 'update'). Other project queries (errors, logs, deployments, share, version, catalog browsing) moved to `mist projects <subcommand>` in the CLI.\n- mist_browser: navigate, interact with, and screenshot the app during preview or after deploy. Returns screenshots inline in tool results \u2014 only tool that genuinely needs the MCP transport.\n- mist_help: returns the full CLI command reference. Call once per session to learn the available `mist` commands."}),qt=[at,It,Ot,Lt,$t];xe.setRequestHandler(Hs,async()=>({tools:qt.map(i=>({name:i.name,description:i.description,inputSchema:Ks(i.inputSchema)}))}));xe.setRequestHandler(Bs,async i=>{let t=qt.find(s=>s.name===i.params.name);if(!t)return p(`Unknown tool: ${i.params.name}`,!0);try{let s=t.inputSchema.safeParse(i.params.arguments);if(!s.success){let a=s.error.issues.map(n=>`${n.path.join(".")}: ${n.message}`).join(", ");return p(`Invalid input: ${a}`,!0)}let e=i.params._meta?.progressToken,r={server:xe,progressToken:e};try{return await t.handler(s.data,r)}finally{r.cleanup?.()}}catch(s){let e=s instanceof Error?s.message:"An unexpected error occurred";return console.error("Tool error:",s),p(e,!0)}});async function Vs(){let i=process.argv.indexOf("--api-url");i!==-1&&process.argv[i+1]&&(process.env.MISTFLOW_API_URL=process.argv[i+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let t=new Fs;await xe.connect(t),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}Vs().catch(i=>{console.error("Fatal error:",i),process.exit(1)});
1531
+ `,fe={name:"mist_help",description:"Returns the Mistflow CLI command reference. Call this ONCE at session start (or whenever you're unsure which tool to use) to learn every `mist` CLI command, when to prefer it over an MCP tool, and how to chain calls. Results are static \u2014 no backend round-trip, safe to call frequently.",inputSchema:me.object({command:me.string().optional().describe("Optional: name of a specific command to get focused reference for. Omit to get the full catalog.")}),handler:async a=>{let{command:e}=a;if(!e)return i(ge);let t=ge.split(`
1532
+ `),r=new RegExp(`^### \`mist ${e}`),s=-1,l=t.length;for(let o=0;o<t.length;o++)if(r.test(t[o]))s=o;else if(s>=0&&t[o].startsWith("### ")){l=o;break}else if(s>=0&&t[o].startsWith("## ")&&o>s){l=o;break}return s<0?i(`No command named '${e}' found. Call mist_help with no args to see the full catalog.`,!0):i(t.slice(s,l).join(`
1533
+ `).trim())}};var j=new Ue({name:"mistflow",version:"0.3.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.\n\nCall `mist_help` at any point for the full CLI command reference. The 4 MCP tools handle short, structured, AI-native flows (auth, project state, browser automation, CLI discovery). Everything else is the `mist` CLI \u2014 invoke via your shell/bash tool.\n\nNew app workflow (entirely CLI-driven):\n1. Plan: run `mist plan --describe \"<user's description>\" --json` via your shell/bash tool. Pass the user's description EXACTLY as written \u2014 do NOT expand, rephrase, or add features. Relay any returned questions to the user, then submit answers via `echo '<answers-json>' | mist plan --token <id> --answers-stdin --json`. When status becomes \"design_clarify_pending\", poll with `mist plan-directions --cid <id> --wait --json`. Present the directions picker, then finalize with `echo '<pick-json>' | mist plan --cid <id> --pick-stdin --json`. If the CLI returns status \"confirm_new_project\" (safety gate when inside an existing codebase), ask the user whether to scaffold a new Mistflow app or edit the existing code.\n2. Mockup (optional): run `mist mockup --plan-id <id>` via your shell/bash tool \u2014 generates a visual HTML wireframe for user preview. Iterative: pass --feedback to refine, --approved to lock in the design.\n3. Scaffold: run `mist init --plan-id <id> --path <absolute-path>` via your shell/bash tool. Writes the Next.js scaffold, contracts/, AGENTS.md, and registers the project with Mistflow. Returns fast; does NOT run npm install.\n4. Install dependencies: run `mist install <projectPath>` via your shell/bash tool (streams output).\n5. Implement: run `mist implement --project-path <path>` via your shell/bash tool \u2014 executes plan steps one at a time. Call repeatedly until all steps are done; it auto-marks the previous step as completed on each call.\n6. Deploy: run `mist deploy [path]` via your shell/bash tool \u2014 tars, uploads, polls status with live streaming. Subcommands: `mist deploy promote` (staging\u2192prod), `mist deploy preview` (local tunnel), `mist deploy rollback <id>`, `mist deploy verify <url>`, `mist deploy redeploy`.\n7. QA: run `mist qa --project-path <path>` via your shell/bash tool. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n\nCompanion CLI (`@mistflow-ai/cli`, invoke as `mist` or via `npx -y @mistflow-ai/cli`) is the primary path for EVERYTHING except the 4 MCP tools below:\n- `mist plan` / `mist plan-directions` \u2014 plan an app.\n- `mist init` \u2014 scaffold a new project from a plan (fast, ~10s).\n- `mist install` / `mist build` / `mist mockup` / `mist implement` / `mist debug` / `mist qa` \u2014 local project lifecycle.\n- `mist deploy` (+ promote/preview/rollback/verify/redeploy subcommands) \u2014 deploy orchestration.\n- `mist status` / `mist fix` \u2014 feature manifest viewer + iteration loop.\n- `mist contracts` \u2014 integration-contract layer management.\n- `mist doctor` \u2014 project health diagnostics.\n- `mist login` / `mist projects` / `mist logs` / `mist env` / `mist domains` / `mist rollback` \u2014 cloud-coordination commands.\n- Call mist_help for the full reference.\n\nIMPORTANT \u2014 chaining discipline: once the user approves the plan, the init \u2192 install \u2192 implement (repeat) \u2192 build \u2192 deploy \u2192 qa chain is expected. Do NOT pause between these calls to ask the user \"should I continue?\" or offer options like \"full build vs step-by-step.\" The tool itself will return a status requiring user input when it actually needs one (e.g. confirm_new_project, confirm_dark_theme, awaiting promotion). Otherwise, chain calls continuously. Brief one-line status updates are fine and encouraged; permission requests are not.\n\nDesign presets (optional, between steps 2 and 3): run `mist projects designs`, `mist projects app-styles`, `mist projects integrations` via your shell/bash tool to browse catalogs. After `mist plan` generates a plan, it may recommend designs and styles \u2014 present these to the user before calling `mist init`.\n\nUpdating an existing Mistflow app:\n- Cosmetic or single-file changes: edit files directly, then `mist deploy` to publish.\n- New page or feature (no new data model): mist_project action=get for context, build it, then `mist deploy`.\n- Feature needing new data model or integration: run `mist plan --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Bug fix: run `mist debug --project-path <path>` to analyze, fix the code, then `mist deploy`.\n\nTemplate forking: use the Mistflow dashboard UI (app.mistflow.ai) to fork templates for now. CLI-side template-fork plumbing is in place but the API-client bridge lands in a follow-up release.\n\nThe 4 MCP tools:\n- mist_setup: authentication. Only call when user has never signed in or a tool returned 'auth_missing'/'auth_revoked'. Do NOT call for 500s, 404s, or network errors. Also accepts an apiKey parameter for headless auth.\n- mist_project: read/write project state (actions: 'get' / 'update'). Other project queries (errors, logs, deployments, share, version, catalog browsing) moved to `mist projects <subcommand>` in the CLI.\n- mist_browser: navigate, interact with, and screenshot the app during preview or after deploy. Returns screenshots inline in tool results \u2014 the one tool that genuinely needs the MCP transport.\n- mist_help: returns the full CLI command reference. Call once per session to learn the available `mist` commands."}),he=[Q,de,ue,fe];j.setRequestHandler(Me,async()=>({tools:he.map(a=>({name:a.name,description:a.description,inputSchema:De(a.inputSchema)}))}));j.setRequestHandler(Le,async a=>{let e=he.find(t=>t.name===a.params.name);if(!e)return i(`Unknown tool: ${a.params.name}`,!0);try{let t=e.inputSchema.safeParse(a.params.arguments);if(!t.success){let l=t.error.issues.map(o=>`${o.path.join(".")}: ${o.message}`).join(", ");return i(`Invalid input: ${l}`,!0)}let r=a.params._meta?.progressToken,s={server:j,progressToken:r};try{return await e.handler(t.data,s)}finally{s.cleanup?.()}}catch(t){let r=t instanceof Error?t.message:"An unexpected error occurred";return console.error("Tool error:",t),i(r,!0)}});async function qe(){let a=process.argv.indexOf("--api-url");a!==-1&&process.argv[a+1]&&(process.env.MISTFLOW_API_URL=process.argv[a+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let e=new Oe;await j.connect(e),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}qe().catch(a=>{console.error("Fatal error:",a),process.exit(1)});
@@ -1 +1 @@
1
- import{c as m,d as y}from"./chunk-H3J2YAUK.js";import{f,g as u,k as p,r as g}from"./chunk-QZYSCQQ4.js";import{existsSync as h,readFileSync as w,writeFileSync as b}from"fs";import{join as l}from"path";import{spawn as j}from"child_process";function C(r,t,e,s,o){return new Promise(n=>{let a=j(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:s,...o?{env:{...process.env,...o}}:{}}),c="",d="";a.stdout?.on("data",i=>{c+=i.toString()}),a.stderr?.on("data",i=>{d+=i.toString()}),a.on("close",i=>n({success:i===0,stdout:c,stderr:d})),a.on("error",i=>n({success:!1,stdout:c,stderr:d+i.message}))})}function P(r){let t=l(r,"mistflow.json");if(!h(t))return null;try{return JSON.parse(w(t,"utf-8"))}catch{return null}}function k(r,t){b(l(r,"mistflow.json"),JSON.stringify(t,null,2))}async function S(r,t){try{let e=t.features,s=t.steps,o={plan:t};Array.isArray(e)&&e.length>0&&(o.features=e.map(n=>n.name)),Array.isArray(s)&&s.length>0&&(o.provenance=s.map(n=>({feature:n.name??n.title??`Step ${n.number??"?"}`,user_intent:(n.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${u()}/api/projects/${encodeURIComponent(r)}/state`,{method:"PUT",headers:{...p(),"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(e){console.error("[self-heal] state sync failed:",e instanceof Error?e.message:String(e))}}async function x(r,t={}){let e=P(r);if(e){if(!f())return e.projectId;if(!e.projectId)try{let o=e.plan?.requestedSubdomain,n=await g(e.name,void 0,e.dbProvider??"neon",o);return e.projectId=n.id,k(r,e),m(r,y(n.id,e.name)),e.plan&&await S(n.id,e.plan),console.error(`[self-heal] registered project ${n.id.slice(0,8)} with backend`),n.id}catch(s){console.error("[self-heal] createProject failed:",s instanceof Error?s.message:String(s));return}return t.forceSync&&e.plan&&e.projectId&&await S(e.projectId,e.plan),e.projectId}}async function N(r,t){let e=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],s=t&&t.length>0?t:e,o=l(r,"components","ui"),n=[],a=[];for(let i of s)h(l(o,`${i}.tsx`))?n.push(i):a.push(i);if(a.length===0)return{installed:[],alreadyPresent:n};let c={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},d=await C("npx",["--yes","shadcn@latest","add","-y","-o",...a],r,18e4,c);return d.success?{installed:a,alreadyPresent:n}:{installed:[],alreadyPresent:n,failed:`shadcn add failed for: ${a.join(", ")}. ${d.stderr.slice(-300)}`.trim()}}export{x as ensureBackendRegistered,N as ensureShadcnComponents};
1
+ import{c as m,d as y}from"./chunk-SXNAPWCC.js";import{f,g as u,i as p,k as g}from"./chunk-VQPRSDSC.js";import{existsSync as h,readFileSync as w,writeFileSync as b}from"fs";import{join as l}from"path";import{spawn as j}from"child_process";function C(r,t,e,s,o){return new Promise(n=>{let a=j(r,t,{cwd:e,stdio:["pipe","pipe","pipe"],timeout:s,...o?{env:{...process.env,...o}}:{}}),c="",d="";a.stdout?.on("data",i=>{c+=i.toString()}),a.stderr?.on("data",i=>{d+=i.toString()}),a.on("close",i=>n({success:i===0,stdout:c,stderr:d})),a.on("error",i=>n({success:!1,stdout:c,stderr:d+i.message}))})}function P(r){let t=l(r,"mistflow.json");if(!h(t))return null;try{return JSON.parse(w(t,"utf-8"))}catch{return null}}function k(r,t){b(l(r,"mistflow.json"),JSON.stringify(t,null,2))}async function S(r,t){try{let e=t.features,s=t.steps,o={plan:t};Array.isArray(e)&&e.length>0&&(o.features=e.map(n=>n.name)),Array.isArray(s)&&s.length>0&&(o.provenance=s.map(n=>({feature:n.name??n.title??`Step ${n.number??"?"}`,user_intent:(n.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${u()}/api/projects/${encodeURIComponent(r)}/state`,{method:"PUT",headers:{...p(),"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(e){console.error("[self-heal] state sync failed:",e instanceof Error?e.message:String(e))}}async function x(r,t={}){let e=P(r);if(e){if(!f())return e.projectId;if(!e.projectId)try{let o=e.plan?.requestedSubdomain,n=await g(e.name,void 0,e.dbProvider??"neon",o);return e.projectId=n.id,k(r,e),m(r,y(n.id,e.name)),e.plan&&await S(n.id,e.plan),console.error(`[self-heal] registered project ${n.id.slice(0,8)} with backend`),n.id}catch(s){console.error("[self-heal] createProject failed:",s instanceof Error?s.message:String(s));return}return t.forceSync&&e.plan&&e.projectId&&await S(e.projectId,e.plan),e.projectId}}async function N(r,t){let e=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],s=t&&t.length>0?t:e,o=l(r,"components","ui"),n=[],a=[];for(let i of s)h(l(o,`${i}.tsx`))?n.push(i):a.push(i);if(a.length===0)return{installed:[],alreadyPresent:n};let c={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},d=await C("npx",["--yes","shadcn@latest","add","-y","-o",...a],r,18e4,c);return d.success?{installed:a,alreadyPresent:n}:{installed:[],alreadyPresent:n,failed:`shadcn add failed for: ${a.join(", ")}. ${d.stderr.slice(-300)}`.trim()}}export{x as ensureBackendRegistered,N as ensureShadcnComponents};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e,f,g}from"./chunk-SXNAPWCC.js";import"./chunk-VQPRSDSC.js";export{d as emptyState,e as fetchRemoteState,g as fuzzyMatch,a as getLocalStatePath,b as readLocalState,f as syncRemoteState,c as writeLocalState};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mistflow-ai/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Mistflow MCP server for AI coding editors. Installed into Claude Code, Cursor, Codex CLI, and VS Code Copilot by the mistflow-ai installer.",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- import{A as v,B as w,C as x,D as y,E as z,F as A,G as B,H as C,I as D,J as E,K as F,L as G,M as H,N as I,O as J,P as K,Q as L,R as M,S as N,T as O,U as P,V as Q,W as R,X as S,f as a,g as b,h as c,i as d,j as e,k as f,l as g,m as h,n as i,o as j,p as k,q as l,r as m,s as n,t as o,u as p,v as q,w as r,x as s,y as t,z as u}from"./chunk-QZYSCQQ4.js";export{e as MistflowApiError,u as addDomain,i as checkAuth,j as checkAuthDetailed,l as checkSubdomain,n as createDeployment,m as createProject,D as deleteEnvVar,t as discoverDecisions,K as downloadSource,Q as downloadSourceWithToken,q as fetchDesignDirections,N as fetchModule,L as fetchScaffold,M as fetchStepContext,P as forkTemplate,r as generatePlan,f as getAuthHeaders,b as getBaseUrl,d as getDashboardUrl,y as getDbCredentials,E as getDeployLogs,p as getDeploymentStatus,k as getProject,F as getProjectErrors,z as getSeedInfo,c as getSiteUrl,O as getTemplate,a as hasCredentialsOnDisk,h as hasLocalCredentials,G as listDeployments,v as listDomains,B as listEnvVars,R as markLocalSetupDone,s as modifyPlan,g as pingBackend,I as promotePreview,H as redeployProject,x as removeDomain,J as rollbackDeployment,S as shareProject,o as uploadAndDeploy,A as uploadQAResults,C as upsertEnvVar,w as verifyDomain};
@@ -1,23 +0,0 @@
1
- import{readFileSync as Q,existsSync as I,writeFileSync as z,mkdirSync as G,renameSync as X,unlinkSync as Y}from"fs";import{join as T,dirname as Z}from"path";import{homedir as ee}from"os";import{randomBytes as te}from"crypto";function $(){return T(ee(),".mistflow","credentials.json")}function x(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}var ne="https://api.mistflow.ai";function M(){let e=$();if(!I(e))return null;try{let t=JSON.parse(Q(e,"utf-8"));return typeof t.apiKey=="string"?{[ne]:t}:t}catch{return null}}function m(){let e=process.env.MISTFLOW_API_KEY;if(e)return{ok:!0,creds:{apiKey:e,orgId:"",orgSlug:"env"}};let t=M();if(!t)return{ok:!1,reason:"missing"};let n=x(),r=t[n];return r&&typeof r.apiKey=="string"&&r.apiKey&&typeof r.orgId=="string"?{ok:!0,creds:r}:{ok:!1,reason:"missing"}}function Re(e){let t=$(),n=Z(t);I(n)||G(n,{recursive:!0});let r=M()??{},o=x();r[o]=e;let s=T(n,`.credentials.tmp.${te(8).toString("hex")}`);try{z(s,JSON.stringify(r,null,2)+`
2
- `,{mode:384}),X(s,t)}catch(c){try{Y(s)}catch{}throw c}}function re(){return m().ok}import{existsSync as D,readFileSync as O,writeFileSync as oe,mkdirSync as se}from"fs";import{join as P,dirname as v}from"path";import{homedir as ie}from"os";import{fileURLToPath as ae}from"url";var S=null;function f(){if(S)return S;try{let e=ae(import.meta.url),t=v(e);for(let n=0;n<6;n++){let r=P(t,"package.json");if(D(r)){let s=JSON.parse(O(r,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return S=s.version,s.version}let o=v(t);if(o===t)break;t=o}}catch{}return S="0.0.0","0.0.0"}function k(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?[parseInt(t[1],10),parseInt(t[2],10),parseInt(t[3],10)]:null}function j(e,t){let n=k(e),r=k(t);if(!n||!r)return 0;for(let o=0;o<3;o++){if(n[o]<r[o])return-1;if(n[o]>r[o])return 1}return 0}function F(e,t,n){if(n&&j(e,n)<0)return"unsupported";if(!t||j(e,t)>=0)return"none";let o=k(e),s=k(t);return!o||!s?"none":o[0]<s[0]?"major":o[1]<s[1]?"minor":"patch"}var u=null;function V(e){let t=e.get("x-mistflow-mcp-latest")??"",n=e.get("x-mistflow-mcp-min-supported")??"",r=e.get("x-mistflow-mcp-changelog-url")??"";!t&&!n||(u={latest:t,minSupported:n,changelogUrl:r})}function L(){let e=process.env.MISTFLOW_STATE_DIR||P(ie(),".mistflow");return P(e,"upgrade-state.json")}function ce(){try{let e=L();return D(e)?JSON.parse(O(e,"utf-8")):{}}catch{return{}}}function le(e){try{let t=L(),n=v(t);D(n)||se(n,{recursive:!0}),oe(t,JSON.stringify(e,null,2)+`
3
- `,{mode:384})}catch{}}var E=!1;function ue(e){return e==="patch"?7*864e5:e==="minor"?1*864e5:0}function Pe(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!u)return null;let e=f();if(e==="0.0.0")return null;let t=F(e,u.latest,u.minSupported);if(t==="none")return null;if(t==="unsupported")return A(t,e,u);if(E)return null;let n=ce(),r=Date.now(),o=ue(t);return n.dismissedForVersion===u.latest&&t!=="major"||n.lastLatestSeen===u.latest&&n.lastShownMs&&r-n.lastShownMs<o?null:(E=!0,le({...n,lastShownMs:r,lastLatestSeen:u.latest}),A(t,e,u))}function A(e,t,n){let r="npx -y mistflow-ai install",o=n.changelogUrl?`
4
- What's new: ${n.changelogUrl}`:"";return e==="unsupported"?`
5
-
6
- ---
7
- Mistflow ${t} is no longer supported.
8
- You must upgrade to ${n.latest} or newer to continue:
9
-
10
- ${r}
11
-
12
- After upgrading, restart your AI editor.${o}
13
- ---`:e==="major"?`
14
-
15
- ---
16
- Mistflow ${n.latest} is available (you have ${t}).
17
- This is a major update. Run \`${r}\` and restart your editor.${o}
18
- ---`:e==="minor"?`
19
-
20
- --- Mistflow update available: ${t} -> ${n.latest} ---
21
- Run \`${r}\` to upgrade, then restart your editor.${o}`:`
22
-
23
- (Mistflow ${n.latest} is out, you have ${t}. Run \`${r}\` when convenient.)`}function ve(){let e=f(),t=u??{latest:"",minSupported:"",changelogUrl:""},n=F(e,t.latest,t.minSupported);return{current:e,latest:t.latest,minSupported:t.minSupported,severity:n,upgradeCmd:"npx -y mistflow-ai install",changelogUrl:t.changelogUrl,backendSignalReceived:u!==null}}function y(){return x()}function Te(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9102")}return"https://mistflow.ai"}function $e(){let e=process.env.MISTFLOW_API_URL;if(e){if(e.includes("mistflow.localhost"))return e.replace("api.mistflow","app.mistflow");if(e.includes("localhost:9100"))return e.replace(":9100",":9101")}return"https://app.mistflow.ai"}var a=class extends Error{constructor(n,r,o,s){super(r);this.code=n;this.statusCode=o;this.details=s;this.name="MistflowApiError"}get isAuth(){return this.code.startsWith("auth_")}get isTransient(){return this.code==="server_error"||this.code==="upstream_error"||this.code==="network_error"||this.code==="rate_limited"}};function pe(){let e=m();if(!e.ok)throw new a("auth_missing","No Mistflow credentials found.",401);return de(e.creds)}function de(e){return{Authorization:`ApiKey ${e.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":f()}}function h(e){try{V(e.headers)}catch{}}async function Me(){try{let e=await fetch(`${y()}/health`,{method:"GET",signal:AbortSignal.timeout(5e3)});h(e)}catch{}}async function B(e,t,n,r){for(let o=0;o<2;o++)try{return await fetch(e,{...t,signal:AbortSignal.timeout(n)})}catch(s){let c=s instanceof Error&&s.name==="TimeoutError",p=s instanceof TypeError;if(r&&o===0&&(c||p)){console.error(`[api] Retrying ${e} after ${c?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function _(e){let t=null;try{t=await e.json()}catch{t=null}let n=t&&typeof t.code=="string"?t.code:void 0,r=t&&typeof t.message=="string"?t.message:t&&typeof t.detail=="string"?t.detail:e.statusText||"Request failed";if(n)return new a(n,r,e.status,t?.details);let o=e.status;return o===401?new a("auth_invalid",r,o):o===403?new a("permission_denied",r,o):o===404?new a("not_found",r,o):o===409?new a("conflict",r,o):o===422?new a("validation_error",r,o):o===429?new a("rate_limited",r,o):o>=500?new a("server_error",e.statusText||"Internal server error",o):new a("client_error",r,o)}async function i(e,t={}){let n=pe(),{timeoutMs:r,idempotent:o,...s}=t,c=r??3e4,p=(s.method??"GET").toUpperCase(),d=o??(p==="GET"||p==="HEAD"),l;try{l=await B(`${y()}${e}`,{...s,headers:{...n,...s.headers}},c,d)}catch(R){throw R instanceof Error&&R.name==="TimeoutError"?new a("network_error","Request timed out. Try again in a moment."):new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(l),!l.ok)throw await _(l);return l.json()}function ge(){return m().ok}var C=null,N=0,fe=300*1e3;async function je(){return(await me()).ok}async function me(){if(C!==null&&Date.now()<N)return C?{ok:!0}:{ok:!1,reason:"no_credentials"};if(!ge())return{ok:!1,reason:"no_credentials"};try{return await i("/api/org"),C=!0,N=Date.now()+fe,{ok:!0}}catch(e){if(C=null,!(e instanceof a))return{ok:!1,reason:"network_error"};switch(e.code){case"auth_missing":case"auth_revoked":return{ok:!1,reason:"no_credentials"};case"auth_expired":case"auth_invalid":case"auth_org_not_found":return{ok:!1,reason:"expired"};case"network_error":return{ok:!1,reason:"network_error"};case"rate_limited":case"server_error":case"upstream_error":return{ok:!1,reason:"server_error"};default:return{ok:!1,reason:"server_error"}}}}async function Ee(e){return i(`/api/projects/${encodeURIComponent(e)}`)}async function Ae(e){return i(`/api/projects/check-subdomain?name=${encodeURIComponent(e)}`)}async function Oe(e,t,n="neon",r){return i("/api/projects",{method:"POST",body:JSON.stringify({name:e,template:t,db_provider:n,requested_subdomain:r})})}async function Fe(e,t){return i("/api/deploy",{method:"POST",body:JSON.stringify({project_id:e,build_output_url:t})})}async function Ve(e,t,n="production",r,o,s,c){let{readFileSync:p}=await import("fs"),{basename:d}=await import("path"),l=m();if(!l.ok)throw new a("auth_missing","No Mistflow credentials found.",401);let R=l.creds,q=p(t),K=new Blob([q],{type:"application/gzip"}),g=new FormData;if(g.append("project_id",e),g.append("build",K,d(t)),n!=="production"&&g.append("environment",n),r&&g.append("admin_email",r),o&&g.append("schema_pushed","true"),s){let{existsSync:J}=await import("fs");if(J(s)){let H=p(s),W=new Blob([H],{type:"application/gzip"});g.append("source",W,"source.tar.gz")}}c&&g.append("git_commit_sha",c);let w;try{w=await fetch(`${y()}/api/deploy/upload`,{method:"POST",headers:{Authorization:`ApiKey ${R.apiKey}`,"X-Mistflow-MCP-Version":f()},body:g,signal:AbortSignal.timeout(3e5)})}catch{throw new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(w),!w.ok)throw await _(w);let U=await w.json();return{...U,id:U.deployment_id}}async function Le(e){return i(`/api/deploy/${encodeURIComponent(e)}/status`)}async function Ne(e){return i(`/api/plan/design-directions/${encodeURIComponent(e)}`,{timeoutMs:15e3})}async function Be(e,t){let n={description:e,conversation_id:t?.conversationId,answers:t?.answers};t?.autonomous&&(n.autonomous=!0),t?.language&&t.language.toLowerCase()!=="english"&&(n.language=t.language),t?.designConversationId&&(n.design_conversation_id=t.designConversationId),t?.designDirection&&(n.design_direction=t.designDirection);let r=t?.conversationId||t?.answers||t?.designConversationId?18e4:6e4;return i("/api/plan",{method:"POST",body:JSON.stringify(n),timeoutMs:r,idempotent:!1})}async function qe(e,t){return i("/api/plan/modify",{method:"POST",body:JSON.stringify({existing_plan:e,modification:t})})}async function Ke(e){return i("/api/plan/discover",{method:"POST",body:JSON.stringify({description:e})})}async function Je(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains`,{method:"POST",body:JSON.stringify({domain:t})})}async function He(e){return i(`/api/projects/${encodeURIComponent(e)}/domains`)}async function We(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains/${encodeURIComponent(t)}/verify`)}async function Qe(e,t){return i(`/api/projects/${encodeURIComponent(e)}/domains/${encodeURIComponent(t)}`,{method:"DELETE"})}async function ze(e){return i(`/api/projects/${encodeURIComponent(e)}/db-credentials`)}async function Ge(e){try{return await i(`/api/projects/${encodeURIComponent(e)}/test-accounts`)}catch(t){return t instanceof a&&(t.statusCode===404||t.code==="not_found")||console.error("[api] Failed to fetch seed info:",t instanceof Error?t.message:t),null}}async function Xe(e,t){try{return await i(`/api/deploy/${encodeURIComponent(e)}/qa-results`,{method:"POST",body:JSON.stringify(t)})}catch(n){return console.error("[api] Failed to upload QA results:",n instanceof Error?n.message:n),null}}async function Ye(e){return i(`/api/projects/${encodeURIComponent(e)}/env`)}async function Ze(e,t,n,r){return i(`/api/projects/${encodeURIComponent(e)}/env`,{method:"PUT",body:JSON.stringify({key:t,value:n,category:r?.category??"custom",description:r?.description,setup_url:r?.setupUrl})})}async function et(e,t){return i(`/api/projects/${encodeURIComponent(e)}/env/${encodeURIComponent(t)}`,{method:"DELETE"})}async function tt(e){return i(`/api/deploy/${encodeURIComponent(e)}/logs`)}async function nt(e,t="7d"){return i(`/api/projects/${encodeURIComponent(e)}/errors?period=${encodeURIComponent(t)}`)}async function rt(e){return i(`/api/projects/${encodeURIComponent(e)}/deployments`)}async function ot(e){return i(`/api/deploy/${encodeURIComponent(e)}/redeploy`,{method:"POST"})}async function st(e,t){let n=new URLSearchParams({preview_deployment_id:t});return i(`/api/deploy/${encodeURIComponent(e)}/promote`,{method:"POST",body:n.toString(),headers:{"Content-Type":"application/x-www-form-urlencoded"}})}async function it(e){return i(`/api/deploy/${encodeURIComponent(e)}/rollback`,{method:"POST"})}async function at(e,t){let n=m();if(!n.ok)throw new a("auth_missing","Not authenticated.",401);let r=n.creds,o=await fetch(`${y()}/api/deploy/${encodeURIComponent(e)}/source`,{headers:{Authorization:`ApiKey ${r.apiKey}`,"X-Mistflow-MCP-Version":f()},signal:AbortSignal.timeout(12e4)});if(h(o),!o.ok)throw await _(o);let{writeFileSync:s}=await import("fs"),c=Buffer.from(await o.arrayBuffer());s(t,c)}async function b(e,t){let{timeoutMs:n,idempotent:r,...o}=t??{},s=n??3e4,c=(o.method??"GET").toUpperCase(),p=r??(c==="GET"||c==="HEAD"),d;try{d=await B(`${y()}${e}`,{headers:{"Content-Type":"application/json","X-Mistflow-MCP-Version":f()},...o},s,p)}catch(l){throw l instanceof Error&&l.name==="TimeoutError"?new a("network_error","Request timed out. Try again in a moment."):new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(d),!d.ok)throw await _(d);return d.json()}async function ct(e="nextjs"){return b(`/api/scaffold/${encodeURIComponent(e)}`)}async function lt(e,t){return b(`/api/scaffold/${encodeURIComponent(e)}/context?step_type=${encodeURIComponent(t)}`)}async function ut(e,t,n,r){return b(`/api/scaffold/${encodeURIComponent(e)}/module`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"crud",entity:t,fields:n,entity_plural:r})})}async function pt(e){return b(`/api/templates/${encodeURIComponent(e)}`)}async function dt(e){return i(`/api/templates/${encodeURIComponent(e)}/fork`,{method:"POST"})}async function gt(e,t,n){let r;try{r=await fetch(`${y()}/api/deploy/${encodeURIComponent(e)}/fork-source?fork_token=${encodeURIComponent(t)}`,{headers:{"X-Mistflow-MCP-Version":f()},signal:AbortSignal.timeout(12e4)})}catch{throw new a("network_error","Cannot reach Mistflow servers. Check your network.")}if(h(r),!r.ok)throw r.status===401?new a("validation_error","Fork token expired or invalid. Re-open the project in the dashboard to get a fresh token.",r.status):await _(r);let{writeFileSync:o}=await import("fs"),s=Buffer.from(await r.arrayBuffer());o(n,s)}async function ft(e){await i(`/api/projects/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({local_setup_done:!0})})}async function mt(e,t){return i(`/api/projects/${encodeURIComponent(e)}/share`,{method:"POST",body:JSON.stringify({is_template:t?.isTemplate??!1,template_description:t?.description})})}export{f as a,V as b,Pe as c,ve as d,Re as e,re as f,y as g,Te as h,$e as i,a as j,pe as k,Me as l,ge as m,je as n,me as o,Ee as p,Ae as q,Oe as r,Fe as s,Ve as t,Le as u,Ne as v,Be as w,qe as x,Ke as y,Je as z,He as A,We as B,Qe as C,ze as D,Ge as E,Xe as F,Ye as G,Ze as H,et as I,tt as J,nt as K,rt as L,ot as M,st as N,it as O,at as P,ct as Q,lt as R,ut as S,pt as T,dt as U,gt as V,ft as W,mt as X};
@@ -1 +0,0 @@
1
- import{a,b,c,d,e,f,g}from"./chunk-H3J2YAUK.js";import"./chunk-QZYSCQQ4.js";export{d as emptyState,e as fetchRemoteState,g as fuzzyMatch,a as getLocalStatePath,b as readLocalState,f as syncRemoteState,c as writeLocalState};