@mistflow-ai/mcp 0.7.2-staging.0 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,182 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as s}from"fs";import{dirname as t,join as r}from"path";import{fileURLToPath as l}from"url";var n=process.argv[2];if(n==="--version"||n==="-v"){try{let o=t(l(import.meta.url)),e=r(o,"..","package.json"),i=JSON.parse(s(e,"utf-8"));console.log(i.version)}catch{console.log("unknown")}process.exit(0)}(n==="--help"||n==="-h")&&(console.log(`@mistflow-ai/mcp \u2014 Mistflow MCP server
2
+ var We=Object.defineProperty;var u=(n,e)=>()=>(n&&(e=n(n=0)),e);var re=(n,e)=>{for(var t in e)We(n,t,{get:e[t],enumerable:!0})};import{existsSync as H,readFileSync as ae,writeFileSync as He,mkdirSync as ze}from"fs";import{join as K,dirname as W}from"path";import{homedir as Ge}from"os";import{fileURLToPath as Qe}from"url";function C(){if(M)return M;try{let n=Qe(import.meta.url),e=W(n);for(let t=0;t<6;t++){let o=K(e,"package.json");if(H(o)){let s=JSON.parse(ae(o,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return M=s.version,s.version}let r=W(e);if(r===e)break;e=r}}catch{}return M="0.0.0","0.0.0"}function $(n){let e=/^(\d+)\.(\d+)\.(\d+)/.exec(n.trim());return e?[parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10)]:null}function oe(n,e){let t=$(n),o=$(e);if(!t||!o)return 0;for(let r=0;r<3;r++){if(t[r]<o[r])return-1;if(t[r]>o[r])return 1}return 0}function Xe(n,e,t){if(t&&oe(n,t)<0)return"unsupported";if(!e||oe(n,e)>=0)return"none";let r=$(n),s=$(e);return!r||!s?"none":r[0]<s[0]?"major":r[1]<s[1]?"minor":"patch"}function j(n){let e=n.get("x-mistflow-mcp-latest")??"",t=n.get("x-mistflow-mcp-min-supported")??"",o=n.get("x-mistflow-mcp-changelog-url")??"";!e&&!t||(f={latest:e,minSupported:t,changelogUrl:o})}function le(){let n=process.env.MISTFLOW_STATE_DIR||K(Ge(),".mistflow");return K(n,"upgrade-state.json")}function Ye(){try{let n=le();return H(n)?JSON.parse(ae(n,"utf-8")):{}}catch{return{}}}function Ze(n){try{let e=le(),t=W(e);H(t)||ze(t,{recursive:!0}),He(e,JSON.stringify(n,null,2)+`
3
+ `,{mode:384})}catch{}}function et(n){return n==="patch"?7*864e5:n==="minor"?1*864e5:0}function ce(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!f)return null;let n=C();if(n==="0.0.0")return null;let e=Xe(n,f.latest,f.minSupported);if(e==="none")return null;if(e==="unsupported")return ie(e,n,f);if(se)return null;let t=Ye(),o=Date.now(),r=et(e);return t.dismissedForVersion===f.latest&&e!=="major"||t.lastLatestSeen===f.latest&&t.lastShownMs&&o-t.lastShownMs<r?null:(se=!0,Ze({...t,lastShownMs:o,lastLatestSeen:f.latest}),ie(e,n,f))}function ie(n,e,t){let o="npx -y mistflow-ai install",r=t.changelogUrl?`
4
+ What's new: ${t.changelogUrl}`:"";return n==="unsupported"?`
5
+
6
+ ---
7
+ Mistflow ${e} is no longer supported.
8
+ You must upgrade to ${t.latest} or newer to continue:
9
+
10
+ ${o}
11
+
12
+ After upgrading, restart your AI editor.${r}
13
+ ---`:n==="major"?`
14
+
15
+ ---
16
+ Mistflow ${t.latest} is available (you have ${e}).
17
+ This is a major update. Run \`${o}\` and restart your editor.${r}
18
+ ---`:n==="minor"?`
19
+
20
+ --- Mistflow update available: ${e} -> ${t.latest} ---
21
+ Run \`${o}\` to upgrade, then restart your editor.${r}`:`
22
+
23
+ (Mistflow ${t.latest} is out, you have ${e}. Run \`${o}\` when convenient.)`}var M,f,se,E=u(()=>{"use strict";M=null;f=null;se=!1});function c(n,e=!1){let t=n;try{let o=ce();o&&(t=n+o)}catch{}return{content:[{type:"text",text:t}],isError:e}}function pe(n){return c(`This is not a Mistflow project (no mistflow.json found at ${n}).
24
+
25
+ Mistflow creates new projects from scratch \u2014 it doesn't work inside existing codebases.
26
+
27
+ To get started:
28
+ 1. Run mist plan --describe "<your app idea>"
29
+ 2. Run mist init --plan-id <planId> --path <absolute-path>
30
+ 3. Run mist install <absolute-path>, then mist implement <absolute-path>
31
+
32
+ If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}var R=u(()=>{"use strict";E()});import{readFileSync as tt,existsSync as de,writeFileSync as nt,mkdirSync as rt,renameSync as ot,unlinkSync as st}from"fs";import{join as ue,dirname as it}from"path";import{homedir as at}from"os";import{randomBytes as lt}from"crypto";function me(){return ue(at(),".mistflow","credentials.json")}function A(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}function ge(){let n=me();if(!de(n))return null;try{let e=JSON.parse(tt(n,"utf-8"));return typeof e.apiKey=="string"?{[ct]:e}:e}catch{return null}}function z(){let n=process.env.MISTFLOW_API_KEY;if(n)return{ok:!0,creds:{apiKey:n,orgId:"",orgSlug:"env"}};let e=ge();if(!e)return{ok:!1,reason:"missing"};let t=A(),o=e[t];return o&&typeof o.apiKey=="string"&&o.apiKey&&typeof o.orgId=="string"?{ok:!0,creds:o}:{ok:!1,reason:"missing"}}function G(n){let e=me(),t=it(e);de(t)||rt(t,{recursive:!0});let o=ge()??{},r=A();o[r]=n;let s=ue(t,`.credentials.tmp.${lt(8).toString("hex")}`);try{nt(s,JSON.stringify(o,null,2)+`
33
+ `,{mode:384}),ot(s,e)}catch(i){try{st(s)}catch{}throw i}}function Q(){return z().ok}var ct,X=u(()=>{"use strict";ct="https://api.mistflow.ai"});function g(){return A()}function P(){let n=z();if(!n.ok)throw new m("auth_missing","No Mistflow credentials found.",401);return pt(n.creds)}function pt(n){return{Authorization:`ApiKey ${n.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":C()}}function dt(n){try{j(n.headers)}catch{}}async function ut(n,e,t,o){for(let r=0;r<2;r++)try{return await fetch(n,{...e,signal:AbortSignal.timeout(t)})}catch(s){let i=s instanceof Error&&s.name==="TimeoutError",a=s instanceof TypeError;if(o&&r===0&&(i||a)){console.error(`[api] Retrying ${n} after ${i?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function mt(n){let e=null;try{e=await n.json()}catch{e=null}let t=e&&typeof e.code=="string"?e.code:void 0,o=e&&typeof e.message=="string"?e.message:e&&typeof e.detail=="string"?e.detail:n.statusText||"Request failed";if(t)return new m(t,o,n.status,e?.details);let r=n.status;return r===401?new m("auth_invalid",o,r):r===403?new m("permission_denied",o,r):r===404?new m("not_found",o,r):r===409?new m("conflict",o,r):r===422?new m("validation_error",o,r):r===429?new m("rate_limited",o,r):r>=500?new m("server_error",n.statusText||"Internal server error",r):new m("client_error",o,r)}async function gt(n,e={}){let t=P(),{timeoutMs:o,idempotent:r,...s}=e,i=o??3e4,a=(s.method??"GET").toUpperCase(),p=r??(a==="GET"||a==="HEAD"),l;try{l=await ut(`${g()}${n}`,{...s,headers:{...t,...s.headers}},i,p)}catch(_){throw _ instanceof Error&&_.name==="TimeoutError"?new m("network_error","Request timed out. Try again in a moment."):new m("network_error","Cannot reach Mistflow servers. Check your network.")}if(dt(l),!l.ok)throw await mt(l);return l.json()}async function fe(n,e,t="neon",o){return gt("/api/projects",{method:"POST",body:JSON.stringify({name:n,template:e,db_provider:t,requested_subdomain:o})})}var m,bn,O=u(()=>{"use strict";X();E();m=class extends Error{constructor(t,o,r,s){super(o);this.code=t;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"}};bn=300*1e3});import{z as Y}from"zod";import{platform as ft}from"os";import{execFile as he}from"child_process";function yt(n){return"error"in n}function we(n){return new Promise(e=>setTimeout(e,n))}function wt(n){return new Promise(e=>{let t=ft();t==="win32"?he("cmd.exe",["/c","start","",n],o=>{o&&console.error("Could not open browser:",o.message),e(!o)}):he(t==="darwin"?"open":"xdg-open",[n],r=>{r&&console.error("Could not open browser:",r.message),e(!r)}),setTimeout(()=>e(!1),5e3)})}async function ye(n,e,t,o){let r=t,s=o.sleep??we;for(let i=0;i<e;i++){await s(r);let a;try{let l=await o.fetch(`${g()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:n})});if(!l.ok)continue;a=await l.json()}catch{continue}if(yt(a))switch(a.error){case"authorization_pending":continue;case"slow_down":r+=5e3;continue;case"expired_token":return c("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return c("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return c("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}let p=a.email||a.org_name||a.org_slug;return G({apiKey:a.api_key,apiKeyId:a.api_key_id,apiKeyName:a.api_key_name,orgId:a.org_id,orgSlug:a.org_slug,email:a.email}),c(`Connected to Mistflow as ${p}. You are ready to build and deploy.`)}return null}async function vt(n,e=bt){let t=n;if(t?.apiKey)try{let i=await e.fetch(`${g()}/api/org`,{headers:{Authorization:`ApiKey ${t.apiKey}`}});if(!i.ok)return c("Invalid API key. Check the key and try again.",!0);let a=await i.json();return G({apiKey:t.apiKey,orgId:a.id,orgSlug:a.slug}),c(`Connected to Mistflow as ${a.slug} via API key. You are ready to build and deploy.`)}catch{return c("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(t?.deviceCode){let i=await ye(t.deviceCode,6,5e3,e);return i||c(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 o;try{let i=await e.fetch(`${g()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!i.ok)return c("Cannot reach Mistflow servers. Check your internet connection.",!0);o=await i.json()}catch{return c("Cannot reach Mistflow servers. Check your internet connection.",!0)}let r=`${o.verification_uri}?code=${o.user_code}`;console.error(`
34
+ Sign in at: ${r}
35
+ Your code: ${o.user_code}
36
+ `);try{await e.openBrowser(r)}catch{}let s=await ye(o.device_code,6,5e3,e);return s||c(JSON.stringify({status:"pending",deviceCode:o.device_code,signInUrl:r,userCode:o.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+o.device_code+"' to check if they approved."}))}var ht,bt,be,ve=u(()=>{"use strict";R();O();X();ht=Y.object({apiKey:Y.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:Y.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.")});bt={fetch:globalThis.fetch,openBrowser:wt,sleep:we};be={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:n=>vt(n)}});import{existsSync as _t,readFileSync as kt}from"fs";function _e(n){let e=new Set;if(!_t(n))return e;let t=kt(n,"utf-8");for(let o of t.split(`
37
+ `)){let r=o.trim();if(!r||r.startsWith("#"))continue;let s=r.indexOf("=");if(s>0){let i=r.slice(0,s).trim(),a=r.slice(s+1).trim();a&&a!=='""'&&a!=="''"&&e.add(i)}}return e}var ke=u(()=>{"use strict"});var ne={};re(ne,{emptyState:()=>te,fetchRemoteState:()=>Pt,fuzzyMatch:()=>Tt,getLocalStatePath:()=>Z,readLocalState:()=>Rt,syncRemoteState:()=>jt,writeLocalState:()=>ee});import{existsSync as Se,readFileSync as St,writeFileSync as xt,mkdirSync as Ct}from"fs";import{join as xe}from"path";function Z(n){return xe(n,".mistflow","state.json")}function Rt(n){let e=Z(n);if(!Se(e))return null;try{return JSON.parse(St(e,"utf-8"))}catch{return null}}function ee(n,e){let t=xe(n,".mistflow");Se(t)||Ct(t,{recursive:!0}),xt(Z(n),JSON.stringify(e,null,2)+`
38
+ `)}function te(n,e){return{projectId:n,name:e,template:"",features:[],dbSchema:[],deployCount:0,decisions:[],provenance:[]}}async function Pt(n){let e;try{e=P()}catch{return null}try{let t=await fetch(`${g()}/api/projects/${encodeURIComponent(n)}/state`,{headers:{...e,"Content-Type":"application/json","X-Mistflow-MCP-Version":C()}});try{j(t.headers)}catch{}return t.ok?await t.json():null}catch{return null}}async function jt(n,e){let t;try{t=P()}catch{return!1}try{let o=await fetch(`${g()}/api/projects/${encodeURIComponent(n)}/state`,{method:"PUT",headers:{...t,"Content-Type":"application/json","X-Mistflow-MCP-Version":C()},body:JSON.stringify(e)});try{j(o.headers)}catch{}return o.ok}catch{return!1}}function Tt(n,e){let t=n.toLowerCase(),o=e.toLowerCase();return o.includes(t)||t.includes(o)?!0:t.split(/\s+/).some(s=>s.length>=3&&o.includes(s))}var L=u(()=>{"use strict";O();E()});var Pe={};re(Pe,{ensureBackendRegistered:()=>At,ensureShadcnComponents:()=>Ot});import{existsSync as Re,readFileSync as It,writeFileSync as Ut}from"fs";import{join as N}from"path";import{spawn as Dt}from"child_process";function Mt(n,e,t,o,r){return new Promise(s=>{let i=Dt(n,e,{cwd:t,stdio:["pipe","pipe","pipe"],timeout:o,...r?{env:{...process.env,...r}}:{}}),a="",p="";i.stdout?.on("data",l=>{a+=l.toString()}),i.stderr?.on("data",l=>{p+=l.toString()}),i.on("close",l=>s({success:l===0,stdout:a,stderr:p})),i.on("error",l=>s({success:!1,stdout:a,stderr:p+l.message}))})}function $t(n){let e=N(n,"mistflow.json");if(!Re(e))return null;try{return JSON.parse(It(e,"utf-8"))}catch{return null}}function Et(n,e){Ut(N(n,"mistflow.json"),JSON.stringify(e,null,2))}async function Ce(n,e){try{let t=e.features,o=e.steps,r={plan:e};Array.isArray(t)&&t.length>0&&(r.features=t.map(s=>s.name)),Array.isArray(o)&&o.length>0&&(r.provenance=o.map(s=>({feature:s.name??s.title??`Step ${s.number??"?"}`,user_intent:(s.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${g()}/api/projects/${encodeURIComponent(n)}/state`,{method:"PUT",headers:{...P(),"Content-Type":"application/json"},body:JSON.stringify(r)})}catch(t){console.error("[self-heal] state sync failed:",t instanceof Error?t.message:String(t))}}async function At(n,e={}){let t=$t(n);if(t){if(!Q())return t.projectId;if(!t.projectId)try{let r=t.plan?.requestedSubdomain,s=await fe(t.name,void 0,t.dbProvider??"neon",r);return t.projectId=s.id,Et(n,t),ee(n,te(s.id,t.name)),t.plan&&await Ce(s.id,t.plan),console.error(`[self-heal] registered project ${s.id.slice(0,8)} with backend`),s.id}catch(o){console.error("[self-heal] createProject failed:",o instanceof Error?o.message:String(o));return}return e.forceSync&&t.plan&&t.projectId&&await Ce(t.projectId,t.plan),t.projectId}}async function Ot(n,e){let t=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],o=e&&e.length>0?e:t,r=N(n,"components","ui"),s=[],i=[];for(let l of o)Re(N(r,`${l}.tsx`))?s.push(l):i.push(l);if(i.length===0)return{installed:[],alreadyPresent:s};let a={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},p=await Mt("npx",["--yes","shadcn@latest","add","-y","-o",...i],n,18e4,a);return p.success?{installed:i,alreadyPresent:s}:{installed:[],alreadyPresent:s,failed:`shadcn add failed for: ${i.join(", ")}. ${p.stderr.slice(-300)}`.trim()}}var je=u(()=>{"use strict";O();L()});import{z as y}from"zod";import{resolve as Lt,join as Te}from"path";import{existsSync as Nt,readFileSync as Ie,writeFileSync as qt}from"fs";var Ft,Ue,De=u(()=>{"use strict";R();ke();Ft=y.object({action:y.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:y.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:y.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:y.object({key:y.string(),description:y.string().optional(),setupUrl:y.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),Ue={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:Ft,handler:async n=>{let e=n,t=Lt(e.projectPath??process.cwd()),o=Te(t,"mistflow.json");if(!Nt(o))return pe(t);let r;try{r=JSON.parse(Ie(o,"utf-8"))}catch{return c("Failed to parse mistflow.json.",!0)}if(e.action==="get"){if(!r.projectId)try{let{ensureBackendRegistered:d}=await Promise.resolve().then(()=>(je(),Pe));await d(t)&&(r=JSON.parse(Ie(o,"utf-8")))}catch{}let a=r.plan,p=a?.steps?.filter(d=>d.status==="completed").length??0,l=a?.steps?.length??0,_=_e(Te(t,".env.local")),T=r.env?.required?Object.entries(r.env.required).map(([d,D])=>({name:d,description:D?.description,configured:_.has(d)})):[];r.projectId&&Promise.resolve().then(()=>(L(),ne)).then(({fetchRemoteState:d})=>d(r.projectId)).catch(()=>{});let x=[`Project: ${r.name}`];if(a){x.push(`Plan: ${a.summary??a.name??"unnamed"} \u2014 ${p}/${l} steps complete`);for(let d of a.steps){let D=d.status==="completed"?"\u2713":d.status==="in_progress"?"\u2192":" ";x.push(` [${D}] ${d.number}. ${d.name}`)}}let I=T.filter(d=>!d.configured);I.length>0&&x.push(`Missing env vars: ${I.map(d=>d.name).join(", ")}`),r.deploy?.url?x.push(`Deployed: ${r.deploy.url} (${r.deploy.count??0} deploys)`):x.push("Not deployed yet");let U=[],J=a?.steps?.find(d=>d.status!=="completed");return J?U.push(`NEXT: Run mist implement <absolute-path> to work on step ${J.number} (${J.name}).`):a&&p===l&&(r.deploy?.url||U.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT ask the user \u2014 just deploy.")),I.length>0&&U.push(`Missing env vars in .env.local: ${I.map(d=>d.name).join(", ")}`),c(JSON.stringify({name:r.name,projectId:r.projectId,planProgress:a?{name:a.name,summary:a.summary,totalSteps:l,completedSteps:p,steps:a.steps}:null,envStatus:T,deploy:r.deploy??null,contextMessage:x.join(`
39
+ `),nextSteps:U}))}let s=[];if(e.completedStep!==void 0){let a=r.plan;if(a?.steps){let p=a.steps.findIndex(l=>l.number===e.completedStep);if(p===-1)return c(`Step ${e.completedStep} not found in the plan.`,!0);a.steps[p].status="completed",s.push(`Step ${e.completedStep} marked as completed`)}}e.addEnvVar&&(r.env||(r.env={required:{}}),r.env.required||(r.env.required={}),r.env.required[e.addEnvVar.key]={description:e.addEnvVar.description,setupUrl:e.addEnvVar.setupUrl},s.push(`Added required env var: ${e.addEnvVar.key}`)),qt(o,JSON.stringify(r,null,2)+`
40
+ `),r.projectId&&Promise.resolve().then(()=>(L(),ne)).then(async({readLocalState:a,syncRemoteState:p})=>{let l=a(t);l&&await p(r.projectId,l)}).catch(()=>{});let i=[];if(e.completedStep!==void 0){let p=r.plan?.steps?.find(l=>l.status!=="completed");p?i.push(`NEXT: Run mist implement <absolute-path> to work on step ${p.number} (${p.name}). Do this now.`):i.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT suggest localhost.")}return e.addEnvVar&&(i.push(`Add ${e.addEnvVar.key} to your .env.local file`),e.addEnvVar.setupUrl&&i.push(`Get the value from: ${e.addEnvVar.setupUrl}`)),c(JSON.stringify({updated:!0,changes:s,message:s.length>0?`Project state saved. ${s.join(". ")}.`:"No changes made.",nextSteps:i.length>0?i:void 0}))}}});import{z as w}from"zod";var Vt,Me,$e=u(()=>{"use strict";De();Vt=w.object({action:w.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:w.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:w.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:w.object({key:w.string(),description:w.string().optional(),setupUrl:w.string().optional()}).optional().describe("(update) Add a required env var to the project manifest")}),Me={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info \u2014 call this at the start of an incremental change so you understand the current app before editing. 'update' marks plan steps complete or adds env vars (note: `mist implement` in the CLI auto-marks the previous step, so manual updates are rarely needed).",inputSchema:Vt,handler:async n=>{let e=n;return Ue.handler({action:e.action,projectPath:e.projectPath,completedStep:e.completedStep,addEnvVar:e.addEnvVar})}}});async function Bt(){try{return await import("playwright")}catch{throw new Error("Playwright is not installed. Run: cd packages/mcp-server && pnpm add playwright && npx playwright install chromium")}}async function Jt(){let n=await Bt();return(!k||!k.isConnected())&&(k=await n.chromium.launch({headless:!0})),b&&(await b.close().catch(()=>{}),b=null),b=await k.newContext({viewport:{width:1280,height:720},deviceScaleFactor:1}),h=await b.newPage(),h}async function Ee(){return h&&!h.isClosed()?h:(q||(q=Jt().finally(()=>{q=null})),q)}async function Ae(){h&&!h.isClosed()&&await h.close().catch(()=>{}),b&&await b.close().catch(()=>{}),k?.isConnected()&&await k.close().catch(()=>{}),h=null,b=null,k=null}async function F(n){return await n.locator("body").ariaSnapshot()}async function V(n,e=!1){return await n.screenshot({fullPage:e,type:"png"})}var k,b,h,q,Oe=u(()=>{"use strict";k=null,b=null,h=null,q=null;process.once("SIGTERM",()=>{Ae().finally(()=>process.exit(0))});process.once("SIGINT",()=>{Ae().finally(()=>process.exit(0))})});import{z as S}from"zod";var Kt,Le,Ne=u(()=>{"use strict";R();Oe();Kt=S.object({action:S.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:S.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:S.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:S.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:S.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:S.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),Le={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:Kt,handler:async n=>{let e=n,t=await Ee();if(e.action==="navigate"){if(!e.url)return c("URL is required for 'navigate'.",!0);let s=[],i=l=>{l.type()==="error"&&s.push(l.text())};t.on("console",i),await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{});let a=[],p=l=>a.push(l.message);if(t.on("pageerror",p),await t.waitForTimeout(500),t.removeListener("console",i),t.removeListener("pageerror",p),s.length>0||a.length>0){let l=await F(t),_=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:l,consoleErrors:s,pageErrors:a,hasErrors:!0})}];if(e.includeScreenshot){let T=await V(t);_.push({type:"image",data:T.toString("base64"),mimeType:"image/png"})}return{content:_}}}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 c("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 c("Selector is required for 'type'.",!0);if(!e.value)return c("Value is required for 'type'.",!0);await t.type(e.selector,e.value,{delay:50})}else if(e.action==="fill"){if(!e.selector)return c("Selector is required for 'fill'.",!0);if(!e.value)return c("Value is required for 'fill'.",!0);await t.fill(e.selector,e.value)}else if(e.action==="select_option"){if(!e.selector)return c("Selector is required for 'select_option'.",!0);if(!e.value)return c("Value is required for 'select_option'.",!0);await t.selectOption(e.selector,e.value)}else if(e.action==="hover"){if(!e.selector)return c("Selector is required for 'hover'.",!0);await t.hover(e.selector,{timeout:1e4})}else if(e.action==="press_key"){if(!e.value)return c("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 s;if(e.selector){let i=await t.$(e.selector);if(!i)return c(`Element not found: ${e.selector}`,!0);s=await i.screenshot({type:"png"})}else s=await V(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:s.toString("base64"),mimeType:"image/png"}]}}else if(e.action==="snapshot"){let s=await F(t);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:s})}]}}let o=await F(t),r=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:o})}];if(e.includeScreenshot){let s=await V(t);r.push({type:"image",data:s.toString("base64"),mimeType:"image/png"})}return{content:r}}}});import{z as qe}from"zod";var Fe,Ve,Be=u(()=>{"use strict";R();Fe=`# Mistflow CLI reference
41
+
42
+ The Mistflow CLI handles local execution and long-running operations that
43
+ would hit the MCP 60s tool-call ceiling. Every command below is invokable
44
+ via your shell/bash tool:
45
+
46
+ npx -y @mistflow-ai/cli <command> [...args]
47
+
48
+ Or, if the user has installed the CLI globally:
49
+
50
+ mist <command> [...args]
51
+
52
+ Pass \`--json\` to any command to get a structured JSON response on stdout
53
+ (progress / info goes to stderr). Errors exit non-zero with a message on
54
+ stderr.
55
+
56
+ ## Host AI execution rules
57
+
58
+ When invoking \`mist plan\` through your shell/bash tool, budget at least
59
+ 2 minutes before assuming the command is stuck. Planning calls routinely take
60
+ 30-150s. Heavier commands like install/build/qa/deploy may need a longer
61
+ budget (up to about 5 minutes).
62
+
63
+ Treat the returned JSON as authoritative. If \`mist plan --describe ...\`
64
+ returns \`status: "clarify"\`, the first planning pass is complete and the
65
+ CLI is waiting for user answers. Do NOT re-run the same command or narrate
66
+ that planning is still running. Only poll again when the status is
67
+ \`"design_clarify_pending"\`, and then use \`mist plan-directions\`. Let the
68
+ user answer the questions, then submit those answers exactly once.
69
+
70
+ ## Commands
71
+
72
+ ### \`mist plan --describe "<description>" [--json]\`
73
+ Start a new plan. Returns \`status: "clarify"\` with questions to relay to
74
+ the user, OR \`status: "ready"\` / \`status: "design_clarify_pending"\` if
75
+ the description was specific enough for the backend to skip discovery.
76
+
77
+ The CLI caches ready plans locally and returns a \`planId\` for init /
78
+ mockup / future modifications.
79
+
80
+ ### \`mist plan --token <conversationId> --answers-stdin [--json]\`
81
+ Submit the user's answers to a previous \`clarify\` round. Pipe the
82
+ answers payload on stdin. Prefer an array of answer entries so duplicate
83
+ \`decisionKey\` values cannot overwrite each other:
84
+
85
+ echo '{"answers":[{"question":"Who will use this app?","decisionKey":"primaryActor","answer":"Individuals"}]}' | \\
86
+ mist plan --token <token> --answers-stdin --json
87
+
88
+ Legacy \`{"question text":"answer"}\` objects still work, but hosts must NOT
89
+ collapse answers into a \`{decisionKey: answer}\` map because multiple
90
+ questions can share one decision key.
91
+
92
+ Returns \`status: "ready"\` or \`status: "design_clarify_pending"\`.
93
+
94
+ ### \`mist plan --describe "<change request>" --existing-plan-id <planId> [--json]\`
95
+ Modify an existing saved plan for a feature addition or integration
96
+ addition. Reads the current plan from \`~/.mistflow/plans/<planId>.json\`
97
+ or, when run inside the matching project, from \`mistflow.json\`. Returns
98
+ \`status: "ready"\` with the modified plan and a diff summary.
99
+
100
+ ### \`mist plan --cid <designConversationId> --pick-stdin [--json]\`
101
+ Finalize the plan by submitting the user's picked design direction.
102
+
103
+ echo '{"id":"modern-editorial"}' | \\
104
+ mist plan --cid <cid> --pick-stdin --json
105
+
106
+ ### \`mist plan-directions --cid <designConversationId> [--wait] [--json]\`
107
+ Poll for asynchronously generated design directions. Without \`--wait\`,
108
+ returns the current status once. With \`--wait\`, polls every 2s (up to 2
109
+ min) until ready or failed.
110
+
111
+ ### \`mist init --plan-id <planId> --path <absolute-path> [--json]\`
112
+ Scaffold a fresh Mistflow app from a cached plan. Fast and fully CLI-driven.
113
+
114
+ ### \`mist mockup --plan-id <planId> [--json]\`
115
+ Generate or iterate on a grayscale HTML wireframe before scaffolding. Use
116
+ \`--feedback\` to revise and \`--approved\` to lock it in.
117
+
118
+ ### \`mist install <projectPath> [--json]\`
119
+ Run npm install in a Mistflow project. Streams output live, no timeout.
120
+
121
+ ### \`mist implement <absolute-path> [--json]\`
122
+ Return the next plan step for the host AI to implement. Call repeatedly
123
+ until all steps are complete.
124
+
125
+ ### \`mist build <absolute-path> [--json]\`
126
+ Run the local production build.
127
+
128
+ ### \`mist seed <absolute-path> [--reset] [--allow-remote] [--json]\`
129
+ Seed realistic sample rows from \`plan.dataModel[].sampleRows\` into the
130
+ project database. Defaults to local-only seeding against PGlite; pass
131
+ \`--allow-remote\` only when you intentionally want to seed a remote
132
+ environment. Use before QA when acceptance criteria depend on populated
133
+ tables or lists. This is for app/domain data, not admin/test account
134
+ bootstrap.
135
+
136
+ ### \`mist qa [live-url] [--json]\`
137
+ Run browser QA against the live deployed app. If no URL is passed, Mistflow
138
+ reads \`deploy.url\` from the current project's \`mistflow.json\`.
139
+
140
+ ### \`mist login\`, \`mist deploy\`, \`mist status <id>\`, \`mist logs <id>\`,
141
+ ### \`mist projects [id]\`, \`mist env <subcommand>\`, \`mist domains <subcommand>\`,
142
+ ### \`mist rollback <id>\`
143
+ Shell-native cloud/project coordination. Use \`mist env\` to set required
144
+ integration keys before deploying a feature that depends on them.
145
+
146
+ ## MCP-vs-CLI rule
147
+
148
+ Stays MCP: \`mist_setup\`, \`mist_browser\`, \`mist_project\`, \`mist_help\`
149
+ (this tool). These return structured data the AI routes on and aren't
150
+ long-running.
151
+
152
+ Prefers CLI: planning, install, build, seed, qa, mockup \u2014 anything that can
153
+ exceed 30s or streams useful progress.
154
+
155
+ ## Chaining pattern
156
+
157
+ The AI reads each CLI response's \`status\` field on stdout (with \`--json\`)
158
+ and decides the next command. Example end-to-end chain:
159
+
160
+ mist plan --describe "habit tracker" --json
161
+ # \u2192 {"status":"clarify","conversation_id":"...","questions":[...]}
162
+ # AI asks user, gets answers, then:
163
+ echo '{...}' | mist plan --token ... --answers-stdin --json
164
+ # \u2192 {"status":"design_clarify_pending","design_conversation_id":"..."}
165
+ mist plan-directions --cid ... --wait --json
166
+ # \u2192 {"status":"ready","directions":[...]}
167
+ # AI shows picker, user picks, then:
168
+ echo '{...}' | mist plan --cid ... --pick-stdin --json
169
+ # \u2192 {"status":"ready","plan":{...}}
170
+ mist init --plan-id ... --path /Users/you/projects/habit-tracker --json
171
+ mist install /Users/you/projects/habit-tracker --json
172
+ mist implement /Users/you/projects/habit-tracker --json
173
+ mist build /Users/you/projects/habit-tracker --json
174
+ mist deploy /Users/you/projects/habit-tracker --json
175
+ mist seed /Users/you/projects/habit-tracker --json
176
+ mist qa https://your-app.mistflow.app --json
177
+ `,Ve={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:qe.object({command:qe.string().optional().describe("Optional: name of a specific command to get focused reference for. Omit to get the full catalog.")}),handler:async n=>{let{command:e}=n;if(!e)return c(Fe);let t=Fe.split(`
178
+ `),o=new RegExp(`^### \`mist ${e}`),r=-1,s=t.length;for(let i=0;i<t.length;i++)if(o.test(t[i]))r=i;else if(r>=0&&t[i].startsWith("### ")){s=i;break}else if(r>=0&&t[i].startsWith("## ")&&i>r){s=i;break}return r<0?c(`No command named '${e}' found. Call mist_help with no args to see the full catalog.`,!0):c(t.slice(r,s).join(`
179
+ `).trim())}}});var Yt={};import{Server as Wt}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Ht}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as zt,ListToolsRequestSchema as Gt}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as Qt}from"zod-to-json-schema";async function Xt(){let n=process.argv.indexOf("--api-url");n!==-1&&process.argv[n+1]&&(process.env.MISTFLOW_API_URL=process.argv[n+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let e=new Ht;await B.connect(e),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}var B,Je,Ke=u(()=>{"use strict";R();ve();$e();Ne();Be();B=new Wt({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\nWhen invoking `mist plan` through your shell/bash tool, give it at least a 2 minute budget before assuming it stalled. `mist plan` commonly takes 30-150s. Heavier commands like install/build/qa/deploy can need a longer budget (up to about 5 minutes).\n\nTreat the CLI\'s returned JSON as authoritative. If `mist plan --describe ...` returns `status: "clarify"`, the planning call completed and is waiting on user answers. Do NOT rerun the same command or describe it as "still generating". Only poll again when status is `"design_clarify_pending"`, and then use `mist plan-directions --cid <id> --wait --json`.\n\nWhen submitting discovery answers back to `mist plan --token ... --answers-stdin`, prefer an array payload like `{"answers":[{"question":"...","decisionKey":"...","answer":"..."}]}`. Do NOT collapse answers into a `{decisionKey: answer}` map because multiple discovery questions can share the same decision key.\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 <absolute-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 <live-url>` via your shell/bash tool, or just `mist qa` from inside the project if mistflow.json already has deploy.url. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n8. Seed sample data (optional but recommended before QA when acceptance criteria depend on populated tables/lists): run `mist seed <absolute-path>` via your shell/bash tool. This defaults to local-only seeding against PGlite; use `--allow-remote` only when you intentionally want to seed a remote environment. Use `--reset` to clear the matched tables first.\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 seed` / `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 seed (when needed) \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: ask product questions, call `mist_project action=get` for context, then run `mist plan --describe "<change request>" --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Integration addition: ask product questions, call `mist_project action=get`, optionally browse `mist projects integrations`, run `mist plan --describe "<change request>" --existing-plan-id <id>`, then `mist implement`, then `mist env` to set required keys before deploy, then `mist deploy`, then `mist qa`.\n- Bug fix: run `mist debug <absolute-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.'}),Je=[be,Me,Le,Ve];B.setRequestHandler(Gt,async()=>({tools:Je.map(n=>({name:n.name,description:n.description,inputSchema:Qt(n.inputSchema)}))}));B.setRequestHandler(zt,async n=>{let e=Je.find(t=>t.name===n.params.name);if(!e)return c(`Unknown tool: ${n.params.name}`,!0);try{let t=e.inputSchema.safeParse(n.params.arguments);if(!t.success){let s=t.error.issues.map(i=>`${i.path.join(".")}: ${i.message}`).join(", ");return c(`Invalid input: ${s}`,!0)}let o=n.params._meta?.progressToken,r={server:B,progressToken:o};try{return await e.handler(t.data,r)}finally{r.cleanup?.()}}catch(t){let o=t instanceof Error?t.message:"An unexpected error occurred";return console.error("Tool error:",t),c(o,!0)}});Xt().catch(n=>{console.error("Fatal error:",n),process.exit(1)})});import{readFileSync as Zt}from"fs";import{dirname as en,join as tn}from"path";import{fileURLToPath as nn}from"url";var v=process.argv[2];if(v==="--version"||v==="-v"){try{let n=en(nn(import.meta.url)),e=tn(n,"..","package.json"),t=JSON.parse(Zt(e,"utf-8"));console.log(t.version)}catch{console.log("unknown")}process.exit(0)}(v==="--help"||v==="-h")&&(console.log(`@mistflow-ai/mcp \u2014 Mistflow MCP server
3
180
 
4
181
  Usage:
5
182
  npx @mistflow-ai/mcp Start the MCP server on stdio (default; invoked by editors)
@@ -7,8 +184,8 @@ Usage:
7
184
 
8
185
  To install the server into your editor config, use the installer:
9
186
  npx -y mistflow-ai install
10
- `),process.exit(0));(n==="install"||n==="uninstall")&&(console.error(`'npx @mistflow-ai/mcp ${n}' is no longer supported.
187
+ `),process.exit(0));(v==="install"||v==="uninstall")&&(console.error(`'npx @mistflow-ai/mcp ${v}' is no longer supported.
11
188
  Use the installer package instead:
12
189
 
13
- npx -y mistflow-ai ${n}
14
- `),process.exit(1));await import("./index.js");
190
+ npx -y mistflow-ai ${v}
191
+ `),process.exit(1));await Promise.resolve().then(()=>(Ke(),Yt));
package/dist/index.js CHANGED
@@ -1,4 +1,27 @@
1
- import{a as I,d as j,e as k}from"./chunk-UNFTM4B3.js";import{c as R,d as C,f as _}from"./chunk-RTKBAE4U.js";import{Server as ie}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as re}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ae,ListToolsRequestSchema as le}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as ce}from"zod-to-json-schema";function r(n,e=!1){let t=n;try{let a=R();a&&(t=n+a)}catch{}return{content:[{type:"text",text:t}],isError:e}}function P(n){return r(`This is not a Mistflow project (no mistflow.json found at ${n}).
1
+ var Oe=Object.defineProperty;var S=(n,e)=>()=>(n&&(e=n(n=0)),e);var te=(n,e)=>{for(var t in e)Oe(n,t,{get:e[t],enumerable:!0})};import{existsSync as K,readFileSync as se,writeFileSync as Le,mkdirSync as Ne}from"fs";import{join as B,dirname as J}from"path";import{homedir as qe}from"os";import{fileURLToPath as Fe}from"url";function x(){if(U)return U;try{let n=Fe(import.meta.url),e=J(n);for(let t=0;t<6;t++){let o=B(e,"package.json");if(K(o)){let s=JSON.parse(se(o,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return U=s.version,s.version}let r=J(e);if(r===e)break;e=r}}catch{}return U="0.0.0","0.0.0"}function D(n){let e=/^(\d+)\.(\d+)\.(\d+)/.exec(n.trim());return e?[parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10)]:null}function ne(n,e){let t=D(n),o=D(e);if(!t||!o)return 0;for(let r=0;r<3;r++){if(t[r]<o[r])return-1;if(t[r]>o[r])return 1}return 0}function Ve(n,e,t){if(t&&ne(n,t)<0)return"unsupported";if(!e||ne(n,e)>=0)return"none";let r=D(n),s=D(e);return!r||!s?"none":r[0]<s[0]?"major":r[1]<s[1]?"minor":"patch"}function R(n){let e=n.get("x-mistflow-mcp-latest")??"",t=n.get("x-mistflow-mcp-min-supported")??"",o=n.get("x-mistflow-mcp-changelog-url")??"";!e&&!t||(g={latest:e,minSupported:t,changelogUrl:o})}function ie(){let n=process.env.MISTFLOW_STATE_DIR||B(qe(),".mistflow");return B(n,"upgrade-state.json")}function Be(){try{let n=ie();return K(n)?JSON.parse(se(n,"utf-8")):{}}catch{return{}}}function Je(n){try{let e=ie(),t=J(e);K(t)||Ne(t,{recursive:!0}),Le(e,JSON.stringify(n,null,2)+`
2
+ `,{mode:384})}catch{}}function Ke(n){return n==="patch"?7*864e5:n==="minor"?1*864e5:0}function ae(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!g)return null;let n=x();if(n==="0.0.0")return null;let e=Ve(n,g.latest,g.minSupported);if(e==="none")return null;if(e==="unsupported")return oe(e,n,g);if(re)return null;let t=Be(),o=Date.now(),r=Ke(e);return t.dismissedForVersion===g.latest&&e!=="major"||t.lastLatestSeen===g.latest&&t.lastShownMs&&o-t.lastShownMs<r?null:(re=!0,Je({...t,lastShownMs:o,lastLatestSeen:g.latest}),oe(e,n,g))}function oe(n,e,t){let o="npx -y mistflow-ai install",r=t.changelogUrl?`
3
+ What's new: ${t.changelogUrl}`:"";return n==="unsupported"?`
4
+
5
+ ---
6
+ Mistflow ${e} is no longer supported.
7
+ You must upgrade to ${t.latest} or newer to continue:
8
+
9
+ ${o}
10
+
11
+ After upgrading, restart your AI editor.${r}
12
+ ---`:n==="major"?`
13
+
14
+ ---
15
+ Mistflow ${t.latest} is available (you have ${e}).
16
+ This is a major update. Run \`${o}\` and restart your editor.${r}
17
+ ---`:n==="minor"?`
18
+
19
+ --- Mistflow update available: ${e} -> ${t.latest} ---
20
+ Run \`${o}\` to upgrade, then restart your editor.${r}`:`
21
+
22
+ (Mistflow ${t.latest} is out, you have ${e}. Run \`${o}\` when convenient.)`}var U,g,re,M=S(()=>{"use strict";U=null;g=null;re=!1});import{readFileSync as We,existsSync as ce,writeFileSync as He,mkdirSync as ze,renameSync as Ge,unlinkSync as Qe}from"fs";import{join as pe,dirname as Xe}from"path";import{homedir as Ye}from"os";import{randomBytes as Ze}from"crypto";function de(){return pe(Ye(),".mistflow","credentials.json")}function $(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}function ue(){let n=de();if(!ce(n))return null;try{let e=JSON.parse(We(n,"utf-8"));return typeof e.apiKey=="string"?{[et]:e}:e}catch{return null}}function W(){let n=process.env.MISTFLOW_API_KEY;if(n)return{ok:!0,creds:{apiKey:n,orgId:"",orgSlug:"env"}};let e=ue();if(!e)return{ok:!1,reason:"missing"};let t=$(),o=e[t];return o&&typeof o.apiKey=="string"&&o.apiKey&&typeof o.orgId=="string"?{ok:!0,creds:o}:{ok:!1,reason:"missing"}}function H(n){let e=de(),t=Xe(e);ce(t)||ze(t,{recursive:!0});let o=ue()??{},r=$();o[r]=n;let s=pe(t,`.credentials.tmp.${Ze(8).toString("hex")}`);try{He(s,JSON.stringify(o,null,2)+`
23
+ `,{mode:384}),Ge(s,e)}catch(i){try{Qe(s)}catch{}throw i}}function z(){return W().ok}var et,G=S(()=>{"use strict";et="https://api.mistflow.ai"});function m(){return $()}function C(){let n=W();if(!n.ok)throw new u("auth_missing","No Mistflow credentials found.",401);return tt(n.creds)}function tt(n){return{Authorization:`ApiKey ${n.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":x()}}function nt(n){try{R(n.headers)}catch{}}async function rt(n,e,t,o){for(let r=0;r<2;r++)try{return await fetch(n,{...e,signal:AbortSignal.timeout(t)})}catch(s){let i=s instanceof Error&&s.name==="TimeoutError",a=s instanceof TypeError;if(o&&r===0&&(i||a)){console.error(`[api] Retrying ${n} after ${i?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function ot(n){let e=null;try{e=await n.json()}catch{e=null}let t=e&&typeof e.code=="string"?e.code:void 0,o=e&&typeof e.message=="string"?e.message:e&&typeof e.detail=="string"?e.detail:n.statusText||"Request failed";if(t)return new u(t,o,n.status,e?.details);let r=n.status;return r===401?new u("auth_invalid",o,r):r===403?new u("permission_denied",o,r):r===404?new u("not_found",o,r):r===409?new u("conflict",o,r):r===422?new u("validation_error",o,r):r===429?new u("rate_limited",o,r):r>=500?new u("server_error",n.statusText||"Internal server error",r):new u("client_error",o,r)}async function st(n,e={}){let t=C(),{timeoutMs:o,idempotent:r,...s}=e,i=o??3e4,a=(s.method??"GET").toUpperCase(),p=r??(a==="GET"||a==="HEAD"),l;try{l=await rt(`${m()}${n}`,{...s,headers:{...t,...s.headers}},i,p)}catch(b){throw b instanceof Error&&b.name==="TimeoutError"?new u("network_error","Request timed out. Try again in a moment."):new u("network_error","Cannot reach Mistflow servers. Check your network.")}if(nt(l),!l.ok)throw await ot(l);return l.json()}async function me(n,e,t="neon",o){return st("/api/projects",{method:"POST",body:JSON.stringify({name:n,template:e,db_provider:t,requested_subdomain:o})})}var u,on,E=S(()=>{"use strict";G();M();u=class extends Error{constructor(t,o,r,s){super(o);this.code=t;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"}};on=300*1e3});var ee={};te(ee,{emptyState:()=>Z,fetchRemoteState:()=>wt,fuzzyMatch:()=>vt,getLocalStatePath:()=>X,readLocalState:()=>yt,syncRemoteState:()=>bt,writeLocalState:()=>Y});import{existsSync as be,readFileSync as gt,writeFileSync as ft,mkdirSync as ht}from"fs";import{join as ve}from"path";function X(n){return ve(n,".mistflow","state.json")}function yt(n){let e=X(n);if(!be(e))return null;try{return JSON.parse(gt(e,"utf-8"))}catch{return null}}function Y(n,e){let t=ve(n,".mistflow");be(t)||ht(t,{recursive:!0}),ft(X(n),JSON.stringify(e,null,2)+`
24
+ `)}function Z(n,e){return{projectId:n,name:e,template:"",features:[],dbSchema:[],deployCount:0,decisions:[],provenance:[]}}async function wt(n){let e;try{e=C()}catch{return null}try{let t=await fetch(`${m()}/api/projects/${encodeURIComponent(n)}/state`,{headers:{...e,"Content-Type":"application/json","X-Mistflow-MCP-Version":x()}});try{R(t.headers)}catch{}return t.ok?await t.json():null}catch{return null}}async function bt(n,e){let t;try{t=C()}catch{return!1}try{let o=await fetch(`${m()}/api/projects/${encodeURIComponent(n)}/state`,{method:"PUT",headers:{...t,"Content-Type":"application/json","X-Mistflow-MCP-Version":x()},body:JSON.stringify(e)});try{R(o.headers)}catch{}return o.ok}catch{return!1}}function vt(n,e){let t=n.toLowerCase(),o=e.toLowerCase();return o.includes(t)||t.includes(o)?!0:t.split(/\s+/).some(s=>s.length>=3&&o.includes(s))}var A=S(()=>{"use strict";E();M()});var Se={};te(Se,{ensureBackendRegistered:()=>Pt,ensureShadcnComponents:()=>jt});import{existsSync as ke,readFileSync as _t,writeFileSync as kt}from"fs";import{join as O}from"path";import{spawn as St}from"child_process";function xt(n,e,t,o,r){return new Promise(s=>{let i=St(n,e,{cwd:t,stdio:["pipe","pipe","pipe"],timeout:o,...r?{env:{...process.env,...r}}:{}}),a="",p="";i.stdout?.on("data",l=>{a+=l.toString()}),i.stderr?.on("data",l=>{p+=l.toString()}),i.on("close",l=>s({success:l===0,stdout:a,stderr:p})),i.on("error",l=>s({success:!1,stdout:a,stderr:p+l.message}))})}function Ct(n){let e=O(n,"mistflow.json");if(!ke(e))return null;try{return JSON.parse(_t(e,"utf-8"))}catch{return null}}function Rt(n,e){kt(O(n,"mistflow.json"),JSON.stringify(e,null,2))}async function _e(n,e){try{let t=e.features,o=e.steps,r={plan:e};Array.isArray(t)&&t.length>0&&(r.features=t.map(s=>s.name)),Array.isArray(o)&&o.length>0&&(r.provenance=o.map(s=>({feature:s.name??s.title??`Step ${s.number??"?"}`,user_intent:(s.description??"").slice(0,500),decisions:"Seeded from plan",tradeoffs:"",files_affected:[]}))),await fetch(`${m()}/api/projects/${encodeURIComponent(n)}/state`,{method:"PUT",headers:{...C(),"Content-Type":"application/json"},body:JSON.stringify(r)})}catch(t){console.error("[self-heal] state sync failed:",t instanceof Error?t.message:String(t))}}async function Pt(n,e={}){let t=Ct(n);if(t){if(!z())return t.projectId;if(!t.projectId)try{let r=t.plan?.requestedSubdomain,s=await me(t.name,void 0,t.dbProvider??"neon",r);return t.projectId=s.id,Rt(n,t),Y(n,Z(s.id,t.name)),t.plan&&await _e(s.id,t.plan),console.error(`[self-heal] registered project ${s.id.slice(0,8)} with backend`),s.id}catch(o){console.error("[self-heal] createProject failed:",o instanceof Error?o.message:String(o));return}return e.forceSync&&t.plan&&t.projectId&&await _e(t.projectId,t.plan),t.projectId}}async function jt(n,e){let t=["button","card","input","label","form","dialog","table","dropdown-menu","badge","separator","skeleton","sheet","tabs","avatar","select","textarea","checkbox","switch","tooltip","popover","sonner"],o=e&&e.length>0?e:t,r=O(n,"components","ui"),s=[],i=[];for(let l of o)ke(O(r,`${l}.tsx`))?s.push(l):i.push(l);if(i.length===0)return{installed:[],alreadyPresent:s};let a={NPM_CONFIG_LEGACY_PEER_DEPS:"true"},p=await xt("npx",["--yes","shadcn@latest","add","-y","-o",...i],n,18e4,a);return p.success?{installed:i,alreadyPresent:s}:{installed:[],alreadyPresent:s,failed:`shadcn add failed for: ${i.join(", ")}. ${p.stderr.slice(-300)}`.trim()}}var xe=S(()=>{"use strict";E();A()});async function $t(){try{return await import("playwright")}catch{throw new Error("Playwright is not installed. Run: cd packages/mcp-server && pnpm add playwright && npx playwright install chromium")}}async function Et(){let n=await $t();return(!v||!v.isConnected())&&(v=await n.chromium.launch({headless:!0})),w&&(await w.close().catch(()=>{}),w=null),w=await v.newContext({viewport:{width:1280,height:720},deviceScaleFactor:1}),f=await w.newPage(),f}async function Te(){return f&&!f.isClosed()?f:(L||(L=Et().finally(()=>{L=null})),L)}async function Ie(){f&&!f.isClosed()&&await f.close().catch(()=>{}),w&&await w.close().catch(()=>{}),v?.isConnected()&&await v.close().catch(()=>{}),f=null,w=null,v=null}async function N(n){return await n.locator("body").ariaSnapshot()}async function q(n,e=!1){return await n.screenshot({fullPage:e,type:"png"})}var v,w,f,L,Ue=S(()=>{"use strict";v=null,w=null,f=null,L=null;process.once("SIGTERM",()=>{Ie().finally(()=>process.exit(0))});process.once("SIGINT",()=>{Ie().finally(()=>process.exit(0))})});import{Server as Ot}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as Lt}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Nt,ListToolsRequestSchema as qt}from"@modelcontextprotocol/sdk/types.js";import{zodToJsonSchema as Ft}from"zod-to-json-schema";M();function c(n,e=!1){let t=n;try{let o=ae();o&&(t=n+o)}catch{}return{content:[{type:"text",text:t}],isError:e}}function le(n){return c(`This is not a Mistflow project (no mistflow.json found at ${n}).
2
25
 
3
26
  Mistflow creates new projects from scratch \u2014 it doesn't work inside existing codebases.
4
27
 
@@ -7,13 +30,13 @@ To get started:
7
30
  2. Run mist init --plan-id <planId> --path <absolute-path>
8
31
  3. Run mist install <absolute-path>, then mist implement <absolute-path>
9
32
 
10
- If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}import{z as x}from"zod";import{platform as W}from"os";import{execFile as M}from"child_process";var K=x.object({apiKey:x.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:x.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 B(n){return"error"in n}function L(n){return new Promise(e=>setTimeout(e,n))}function H(n){return new Promise(e=>{let t=W();t==="win32"?M("cmd.exe",["/c","start","",n],a=>{a&&console.error("Could not open browser:",a.message),e(!a)}):M(t==="darwin"?"open":"xdg-open",[n],o=>{o&&console.error("Could not open browser:",o.message),e(!o)}),setTimeout(()=>e(!1),5e3)})}var G={fetch:globalThis.fetch,openBrowser:H,sleep:L};async function q(n,e,t,a){let o=t,l=a.sleep??L;for(let i=0;i<e;i++){await l(o);let s;try{let p=await a.fetch(`${_()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:n})});if(!p.ok)continue;s=await p.json()}catch{continue}if(B(s))switch(s.error){case"authorization_pending":continue;case"slow_down":o+=5e3;continue;case"expired_token":return r("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return r("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return r("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}let d=s.email||s.org_name||s.org_slug;return C({apiKey:s.api_key,apiKeyId:s.api_key_id,apiKeyName:s.api_key_name,orgId:s.org_id,orgSlug:s.org_slug,email:s.email}),r(`Connected to Mistflow as ${d}. You are ready to build and deploy.`)}return null}async function Y(n,e=G){let t=n;if(t?.apiKey)try{let i=await e.fetch(`${_()}/api/org`,{headers:{Authorization:`ApiKey ${t.apiKey}`}});if(!i.ok)return r("Invalid API key. Check the key and try again.",!0);let s=await i.json();return C({apiKey:t.apiKey,orgId:s.id,orgSlug:s.slug}),r(`Connected to Mistflow as ${s.slug} via API key. You are ready to build and deploy.`)}catch{return r("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(t?.deviceCode){let i=await q(t.deviceCode,6,5e3,e);return i||r(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 a;try{let i=await e.fetch(`${_()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!i.ok)return r("Cannot reach Mistflow servers. Check your internet connection.",!0);a=await i.json()}catch{return r("Cannot reach Mistflow servers. Check your internet connection.",!0)}let o=`${a.verification_uri}?code=${a.user_code}`;console.error(`
11
- Sign in at: ${o}
12
- Your code: ${a.user_code}
13
- `);try{await e.openBrowser(o)}catch{}let l=await q(a.device_code,6,5e3,e);return l||r(JSON.stringify({status:"pending",deviceCode:a.device_code,signInUrl:o,userCode:a.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+a.device_code+"' to check if they approved."}))}var E={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:K,handler:n=>Y(n)};import{z as m}from"zod";import{z as u}from"zod";import{resolve as Z,join as U}from"path";import{existsSync as ee,readFileSync as A,writeFileSync as te}from"fs";import{existsSync as Q,readFileSync as X}from"fs";function O(n){let e=new Set;if(!Q(n))return e;let t=X(n,"utf-8");for(let a of t.split(`
14
- `)){let o=a.trim();if(!o||o.startsWith("#"))continue;let l=o.indexOf("=");if(l>0){let i=o.slice(0,l).trim(),s=o.slice(l+1).trim();s&&s!=='""'&&s!=="''"&&e.add(i)}}return e}var oe=u.object({action:u.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:u.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:u.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:u.object({key:u.string(),description:u.string().optional(),setupUrl:u.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),N={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:oe,handler:async n=>{let e=n,t=Z(e.projectPath??process.cwd()),a=U(t,"mistflow.json");if(!ee(a))return P(t);let o;try{o=JSON.parse(A(a,"utf-8"))}catch{return r("Failed to parse mistflow.json.",!0)}if(e.action==="get"){if(!o.projectId)try{let{ensureBackendRegistered:c}=await import("./self-heal-KDCW562K.js");await c(t)&&(o=JSON.parse(A(a,"utf-8")))}catch{}let s=o.plan,d=s?.steps?.filter(c=>c.status==="completed").length??0,p=s?.steps?.length??0,g=O(U(t,".env.local")),y=o.env?.required?Object.entries(o.env.required).map(([c,b])=>({name:c,description:b?.description,configured:g.has(c)})):[];o.projectId&&import("./state-manager-NJPMKZCE.js").then(({fetchRemoteState:c})=>c(o.projectId)).catch(()=>{});let f=[`Project: ${o.name}`];if(s){f.push(`Plan: ${s.summary??s.name??"unnamed"} \u2014 ${d}/${p} steps complete`);for(let c of s.steps){let b=c.status==="completed"?"\u2713":c.status==="in_progress"?"\u2192":" ";f.push(` [${b}] ${c.number}. ${c.name}`)}}let w=y.filter(c=>!c.configured);w.length>0&&f.push(`Missing env vars: ${w.map(c=>c.name).join(", ")}`),o.deploy?.url?f.push(`Deployed: ${o.deploy.url} (${o.deploy.count??0} deploys)`):f.push("Not deployed yet");let v=[],T=s?.steps?.find(c=>c.status!=="completed");return T?v.push(`NEXT: Run mist implement <absolute-path> to work on step ${T.number} (${T.name}).`):s&&d===p&&(o.deploy?.url||v.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT ask the user \u2014 just deploy.")),w.length>0&&v.push(`Missing env vars in .env.local: ${w.map(c=>c.name).join(", ")}`),r(JSON.stringify({name:o.name,projectId:o.projectId,planProgress:s?{name:s.name,summary:s.summary,totalSteps:p,completedSteps:d,steps:s.steps}:null,envStatus:y,deploy:o.deploy??null,contextMessage:f.join(`
15
- `),nextSteps:v}))}let l=[];if(e.completedStep!==void 0){let s=o.plan;if(s?.steps){let d=s.steps.findIndex(p=>p.number===e.completedStep);if(d===-1)return r(`Step ${e.completedStep} not found in the plan.`,!0);s.steps[d].status="completed",l.push(`Step ${e.completedStep} marked as completed`)}}e.addEnvVar&&(o.env||(o.env={required:{}}),o.env.required||(o.env.required={}),o.env.required[e.addEnvVar.key]={description:e.addEnvVar.description,setupUrl:e.addEnvVar.setupUrl},l.push(`Added required env var: ${e.addEnvVar.key}`)),te(a,JSON.stringify(o,null,2)+`
16
- `),o.projectId&&import("./state-manager-NJPMKZCE.js").then(async({readLocalState:s,syncRemoteState:d})=>{let p=s(t);p&&await d(o.projectId,p)}).catch(()=>{});let i=[];if(e.completedStep!==void 0){let d=o.plan?.steps?.find(p=>p.status!=="completed");d?i.push(`NEXT: Run mist implement <absolute-path> to work on step ${d.number} (${d.name}). Do this now.`):i.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT suggest localhost.")}return e.addEnvVar&&(i.push(`Add ${e.addEnvVar.key} to your .env.local file`),e.addEnvVar.setupUrl&&i.push(`Get the value from: ${e.addEnvVar.setupUrl}`)),r(JSON.stringify({updated:!0,changes:l,message:l.length>0?`Project state saved. ${l.join(". ")}.`:"No changes made.",nextSteps:i.length>0?i:void 0}))}};var se=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")}),$={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info \u2014 call this at the start of an incremental change so you understand the current app before editing. 'update' marks plan steps complete or adds env vars (note: `mist implement` in the CLI auto-marks the previous step, so manual updates are rarely needed).",inputSchema:se,handler:async n=>{let e=n;return N.handler({action:e.action,projectPath:e.projectPath,completedStep:e.completedStep,addEnvVar:e.addEnvVar})}};import{z as h}from"zod";var ne=h.object({action:h.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:h.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:h.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:h.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:h.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:h.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),D={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:ne,handler:async n=>{let e=n,t=await I();if(e.action==="navigate"){if(!e.url)return r("URL is required for 'navigate'.",!0);let l=[],i=p=>{p.type()==="error"&&l.push(p.text())};t.on("console",i),await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{});let s=[],d=p=>s.push(p.message);if(t.on("pageerror",d),await t.waitForTimeout(500),t.removeListener("console",i),t.removeListener("pageerror",d),l.length>0||s.length>0){let p=await j(t),g=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:p,consoleErrors:l,pageErrors:s,hasErrors:!0})}];if(e.includeScreenshot){let y=await k(t);g.push({type:"image",data:y.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 r("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 r("Selector is required for 'type'.",!0);if(!e.value)return r("Value is required for 'type'.",!0);await t.type(e.selector,e.value,{delay:50})}else if(e.action==="fill"){if(!e.selector)return r("Selector is required for 'fill'.",!0);if(!e.value)return r("Value is required for 'fill'.",!0);await t.fill(e.selector,e.value)}else if(e.action==="select_option"){if(!e.selector)return r("Selector is required for 'select_option'.",!0);if(!e.value)return r("Value is required for 'select_option'.",!0);await t.selectOption(e.selector,e.value)}else if(e.action==="hover"){if(!e.selector)return r("Selector is required for 'hover'.",!0);await t.hover(e.selector,{timeout:1e4})}else if(e.action==="press_key"){if(!e.value)return r("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 i=await t.$(e.selector);if(!i)return r(`Element not found: ${e.selector}`,!0);l=await i.screenshot({type:"png"})}else l=await k(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 j(t);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:l})}]}}let a=await j(t),o=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:a})}];if(e.includeScreenshot){let l=await k(t);o.push({type:"image",data:l.toString("base64"),mimeType:"image/png"})}return{content:o}}};import{z as F}from"zod";var V=`# Mistflow CLI reference
33
+ If you want to deploy an existing project, use your framework's deploy tools directly.`,!0)}import{z as Q}from"zod";import{platform as it}from"os";import{execFile as ge}from"child_process";E();G();var at=Q.object({apiKey:Q.string().optional().describe("API key (mist_...) for headless auth. Skips the device code flow entirely. Generate one at app.mistflow.ai/mcp-keys."),deviceCode:Q.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 lt(n){return"error"in n}function he(n){return new Promise(e=>setTimeout(e,n))}function ct(n){return new Promise(e=>{let t=it();t==="win32"?ge("cmd.exe",["/c","start","",n],o=>{o&&console.error("Could not open browser:",o.message),e(!o)}):ge(t==="darwin"?"open":"xdg-open",[n],r=>{r&&console.error("Could not open browser:",r.message),e(!r)}),setTimeout(()=>e(!1),5e3)})}var pt={fetch:globalThis.fetch,openBrowser:ct,sleep:he};async function fe(n,e,t,o){let r=t,s=o.sleep??he;for(let i=0;i<e;i++){await s(r);let a;try{let l=await o.fetch(`${m()}/auth/poll`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:n})});if(!l.ok)continue;a=await l.json()}catch{continue}if(lt(a))switch(a.error){case"authorization_pending":continue;case"slow_down":r+=5e3;continue;case"expired_token":return c("The sign-in link expired. Run mist_setup again to get a new code.",!0);case"access_denied":return c("Sign-in was cancelled. Run mist_setup again to try again.",!0);case"already_exchanged":return c("This sign-in link was already used. Run mist_setup again to get a new code.",!0)}let p=a.email||a.org_name||a.org_slug;return H({apiKey:a.api_key,apiKeyId:a.api_key_id,apiKeyName:a.api_key_name,orgId:a.org_id,orgSlug:a.org_slug,email:a.email}),c(`Connected to Mistflow as ${p}. You are ready to build and deploy.`)}return null}async function dt(n,e=pt){let t=n;if(t?.apiKey)try{let i=await e.fetch(`${m()}/api/org`,{headers:{Authorization:`ApiKey ${t.apiKey}`}});if(!i.ok)return c("Invalid API key. Check the key and try again.",!0);let a=await i.json();return H({apiKey:t.apiKey,orgId:a.id,orgSlug:a.slug}),c(`Connected to Mistflow as ${a.slug} via API key. You are ready to build and deploy.`)}catch{return c("Cannot reach Mistflow servers. Check your internet connection.",!0)}if(t?.deviceCode){let i=await fe(t.deviceCode,6,5e3,e);return i||c(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 o;try{let i=await e.fetch(`${m()}/auth/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!i.ok)return c("Cannot reach Mistflow servers. Check your internet connection.",!0);o=await i.json()}catch{return c("Cannot reach Mistflow servers. Check your internet connection.",!0)}let r=`${o.verification_uri}?code=${o.user_code}`;console.error(`
34
+ Sign in at: ${r}
35
+ Your code: ${o.user_code}
36
+ `);try{await e.openBrowser(r)}catch{}let s=await fe(o.device_code,6,5e3,e);return s||c(JSON.stringify({status:"pending",deviceCode:o.device_code,signInUrl:r,userCode:o.user_code,instruction:"The user hasn't approved yet. Wait ~15 seconds, then call mist_setup again with deviceCode='"+o.device_code+"' to check if they approved."}))}var ye={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:at,handler:n=>dt(n)};import{z as y}from"zod";import{z as h}from"zod";import{resolve as Tt,join as Ce}from"path";import{existsSync as It,readFileSync as Re,writeFileSync as Ut}from"fs";import{existsSync as ut,readFileSync as mt}from"fs";function we(n){let e=new Set;if(!ut(n))return e;let t=mt(n,"utf-8");for(let o of t.split(`
37
+ `)){let r=o.trim();if(!r||r.startsWith("#"))continue;let s=r.indexOf("=");if(s>0){let i=r.slice(0,s).trim(),a=r.slice(s+1).trim();a&&a!=='""'&&a!=="''"&&e.add(i)}}return e}var Dt=h.object({action:h.enum(["get","update"]).default("get").describe("'get' reads current project state. 'update' modifies it."),projectPath:h.string().optional().describe("Path to the project directory (default: current working directory)"),completedStep:h.number().optional().describe("(update only) Mark a plan step as completed by step number"),addEnvVar:h.object({key:h.string(),description:h.string().optional(),setupUrl:h.string().optional()}).optional().describe("(update only) Add a required env var to the project manifest")}),Pe={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:Dt,handler:async n=>{let e=n,t=Tt(e.projectPath??process.cwd()),o=Ce(t,"mistflow.json");if(!It(o))return le(t);let r;try{r=JSON.parse(Re(o,"utf-8"))}catch{return c("Failed to parse mistflow.json.",!0)}if(e.action==="get"){if(!r.projectId)try{let{ensureBackendRegistered:d}=await Promise.resolve().then(()=>(xe(),Se));await d(t)&&(r=JSON.parse(Re(o,"utf-8")))}catch{}let a=r.plan,p=a?.steps?.filter(d=>d.status==="completed").length??0,l=a?.steps?.length??0,b=we(Ce(t,".env.local")),P=r.env?.required?Object.entries(r.env.required).map(([d,I])=>({name:d,description:I?.description,configured:b.has(d)})):[];r.projectId&&Promise.resolve().then(()=>(A(),ee)).then(({fetchRemoteState:d})=>d(r.projectId)).catch(()=>{});let k=[`Project: ${r.name}`];if(a){k.push(`Plan: ${a.summary??a.name??"unnamed"} \u2014 ${p}/${l} steps complete`);for(let d of a.steps){let I=d.status==="completed"?"\u2713":d.status==="in_progress"?"\u2192":" ";k.push(` [${I}] ${d.number}. ${d.name}`)}}let j=P.filter(d=>!d.configured);j.length>0&&k.push(`Missing env vars: ${j.map(d=>d.name).join(", ")}`),r.deploy?.url?k.push(`Deployed: ${r.deploy.url} (${r.deploy.count??0} deploys)`):k.push("Not deployed yet");let T=[],V=a?.steps?.find(d=>d.status!=="completed");return V?T.push(`NEXT: Run mist implement <absolute-path> to work on step ${V.number} (${V.name}).`):a&&p===l&&(r.deploy?.url||T.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT ask the user \u2014 just deploy.")),j.length>0&&T.push(`Missing env vars in .env.local: ${j.map(d=>d.name).join(", ")}`),c(JSON.stringify({name:r.name,projectId:r.projectId,planProgress:a?{name:a.name,summary:a.summary,totalSteps:l,completedSteps:p,steps:a.steps}:null,envStatus:P,deploy:r.deploy??null,contextMessage:k.join(`
38
+ `),nextSteps:T}))}let s=[];if(e.completedStep!==void 0){let a=r.plan;if(a?.steps){let p=a.steps.findIndex(l=>l.number===e.completedStep);if(p===-1)return c(`Step ${e.completedStep} not found in the plan.`,!0);a.steps[p].status="completed",s.push(`Step ${e.completedStep} marked as completed`)}}e.addEnvVar&&(r.env||(r.env={required:{}}),r.env.required||(r.env.required={}),r.env.required[e.addEnvVar.key]={description:e.addEnvVar.description,setupUrl:e.addEnvVar.setupUrl},s.push(`Added required env var: ${e.addEnvVar.key}`)),Ut(o,JSON.stringify(r,null,2)+`
39
+ `),r.projectId&&Promise.resolve().then(()=>(A(),ee)).then(async({readLocalState:a,syncRemoteState:p})=>{let l=a(t);l&&await p(r.projectId,l)}).catch(()=>{});let i=[];if(e.completedStep!==void 0){let p=r.plan?.steps?.find(l=>l.status!=="completed");p?i.push(`NEXT: Run mist implement <absolute-path> to work on step ${p.number} (${p.name}). Do this now.`):i.push("NEXT: All steps complete! Run mist deploy <absolute-path> to deploy the app now. Do NOT suggest localhost.")}return e.addEnvVar&&(i.push(`Add ${e.addEnvVar.key} to your .env.local file`),e.addEnvVar.setupUrl&&i.push(`Get the value from: ${e.addEnvVar.setupUrl}`)),c(JSON.stringify({updated:!0,changes:s,message:s.length>0?`Project state saved. ${s.join(". ")}.`:"No changes made.",nextSteps:i.length>0?i:void 0}))}};var Mt=y.object({action:y.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:y.string().optional().describe("Path to the project directory (default: cwd)"),completedStep:y.number().optional().describe("(update) Mark a plan step as completed by step number"),addEnvVar:y.object({key:y.string(),description:y.string().optional(),setupUrl:y.string().optional()}).optional().describe("(update) Add a required env var to the project manifest")}),je={name:"mist_project",description:"Read or update Mistflow project state. 'get' loads plan progress, env vars, and deploy info \u2014 call this at the start of an incremental change so you understand the current app before editing. 'update' marks plan steps complete or adds env vars (note: `mist implement` in the CLI auto-marks the previous step, so manual updates are rarely needed).",inputSchema:Mt,handler:async n=>{let e=n;return Pe.handler({action:e.action,projectPath:e.projectPath,completedStep:e.completedStep,addEnvVar:e.addEnvVar})}};import{z as _}from"zod";Ue();var At=_.object({action:_.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:_.string().optional().describe("URL to navigate to. Required for 'navigate'; optional for 'screenshot' (navigates before capturing)."),selector:_.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:_.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:_.boolean().default(!1).describe("For 'screenshot': capture the full scrollable page instead of just the viewport."),includeScreenshot:_.boolean().default(!1).describe("For navigate/interact actions: also return a screenshot alongside the accessibility snapshot.")}),De={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:At,handler:async n=>{let e=n,t=await Te();if(e.action==="navigate"){if(!e.url)return c("URL is required for 'navigate'.",!0);let s=[],i=l=>{l.type()==="error"&&s.push(l.text())};t.on("console",i),await t.goto(e.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("networkidle").catch(()=>{});let a=[],p=l=>a.push(l.message);if(t.on("pageerror",p),await t.waitForTimeout(500),t.removeListener("console",i),t.removeListener("pageerror",p),s.length>0||a.length>0){let l=await N(t),b=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:l,consoleErrors:s,pageErrors:a,hasErrors:!0})}];if(e.includeScreenshot){let P=await q(t);b.push({type:"image",data:P.toString("base64"),mimeType:"image/png"})}return{content:b}}}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 c("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 c("Selector is required for 'type'.",!0);if(!e.value)return c("Value is required for 'type'.",!0);await t.type(e.selector,e.value,{delay:50})}else if(e.action==="fill"){if(!e.selector)return c("Selector is required for 'fill'.",!0);if(!e.value)return c("Value is required for 'fill'.",!0);await t.fill(e.selector,e.value)}else if(e.action==="select_option"){if(!e.selector)return c("Selector is required for 'select_option'.",!0);if(!e.value)return c("Value is required for 'select_option'.",!0);await t.selectOption(e.selector,e.value)}else if(e.action==="hover"){if(!e.selector)return c("Selector is required for 'hover'.",!0);await t.hover(e.selector,{timeout:1e4})}else if(e.action==="press_key"){if(!e.value)return c("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 s;if(e.selector){let i=await t.$(e.selector);if(!i)return c(`Element not found: ${e.selector}`,!0);s=await i.screenshot({type:"png"})}else s=await q(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:s.toString("base64"),mimeType:"image/png"}]}}else if(e.action==="snapshot"){let s=await N(t);return{content:[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:s})}]}}let o=await N(t),r=[{type:"text",text:JSON.stringify({url:t.url(),title:await t.title(),snapshot:o})}];if(e.includeScreenshot){let s=await q(t);r.push({type:"image",data:s.toString("base64"),mimeType:"image/png"})}return{content:r}}};import{z as Me}from"zod";var $e=`# Mistflow CLI reference
17
40
 
18
41
  The Mistflow CLI handles local execution and long-running operations that
19
42
  would hit the MCP 60s tool-call ceiling. Every command below is invokable
@@ -29,6 +52,20 @@ Pass \`--json\` to any command to get a structured JSON response on stdout
29
52
  (progress / info goes to stderr). Errors exit non-zero with a message on
30
53
  stderr.
31
54
 
55
+ ## Host AI execution rules
56
+
57
+ When invoking \`mist plan\` through your shell/bash tool, budget at least
58
+ 2 minutes before assuming the command is stuck. Planning calls routinely take
59
+ 30-150s. Heavier commands like install/build/qa/deploy may need a longer
60
+ budget (up to about 5 minutes).
61
+
62
+ Treat the returned JSON as authoritative. If \`mist plan --describe ...\`
63
+ returns \`status: "clarify"\`, the first planning pass is complete and the
64
+ CLI is waiting for user answers. Do NOT re-run the same command or narrate
65
+ that planning is still running. Only poll again when the status is
66
+ \`"design_clarify_pending"\`, and then use \`mist plan-directions\`. Let the
67
+ user answer the questions, then submit those answers exactly once.
68
+
32
69
  ## Commands
33
70
 
34
71
  ### \`mist plan --describe "<description>" [--json]\`
@@ -41,11 +78,16 @@ mockup / future modifications.
41
78
 
42
79
  ### \`mist plan --token <conversationId> --answers-stdin [--json]\`
43
80
  Submit the user's answers to a previous \`clarify\` round. Pipe the
44
- answers object on stdin:
81
+ answers payload on stdin. Prefer an array of answer entries so duplicate
82
+ \`decisionKey\` values cannot overwrite each other:
45
83
 
46
- echo '{"primaryActor":"individuals","dbProvider":"neon"}' | \\
84
+ echo '{"answers":[{"question":"Who will use this app?","decisionKey":"primaryActor","answer":"Individuals"}]}' | \\
47
85
  mist plan --token <token> --answers-stdin --json
48
86
 
87
+ Legacy \`{"question text":"answer"}\` objects still work, but hosts must NOT
88
+ collapse answers into a \`{decisionKey: answer}\` map because multiple
89
+ questions can share one decision key.
90
+
49
91
  Returns \`status: "ready"\` or \`status: "design_clarify_pending"\`.
50
92
 
51
93
  ### \`mist plan --describe "<change request>" --existing-plan-id <planId> [--json]\`
@@ -131,6 +173,6 @@ and decides the next command. Example end-to-end chain:
131
173
  mist deploy /Users/you/projects/habit-tracker --json
132
174
  mist seed /Users/you/projects/habit-tracker --json
133
175
  mist qa https://your-app.mistflow.app --json
134
- `,J={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:F.object({command:F.string().optional().describe("Optional: name of a specific command to get focused reference for. Omit to get the full catalog.")}),handler:async n=>{let{command:e}=n;if(!e)return r(V);let t=V.split(`
135
- `),a=new RegExp(`^### \`mist ${e}`),o=-1,l=t.length;for(let i=0;i<t.length;i++)if(a.test(t[i]))o=i;else if(o>=0&&t[i].startsWith("### ")){l=i;break}else if(o>=0&&t[i].startsWith("## ")&&i>o){l=i;break}return o<0?r(`No command named '${e}' found. Call mist_help with no args to see the full catalog.`,!0):r(t.slice(o,l).join(`
136
- `).trim())}};var S=new ie({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 <absolute-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 <live-url>` via your shell/bash tool, or just `mist qa` from inside the project if mistflow.json already has deploy.url. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n8. Seed sample data (optional but recommended before QA when acceptance criteria depend on populated tables/lists): run `mist seed <absolute-path>` via your shell/bash tool. This defaults to local-only seeding against PGlite; use `--allow-remote` only when you intentionally want to seed a remote environment. Use `--reset` to clear the matched tables first.\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 seed` / `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 seed (when needed) \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: ask product questions, call `mist_project action=get` for context, then run `mist plan --describe \"<change request>\" --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Integration addition: ask product questions, call `mist_project action=get`, optionally browse `mist projects integrations`, run `mist plan --describe \"<change request>\" --existing-plan-id <id>`, then `mist implement`, then `mist env` to set required keys before deploy, then `mist deploy`, then `mist qa`.\n- Bug fix: run `mist debug <absolute-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."}),z=[E,$,D,J];S.setRequestHandler(le,async()=>({tools:z.map(n=>({name:n.name,description:n.description,inputSchema:ce(n.inputSchema)}))}));S.setRequestHandler(ae,async n=>{let e=z.find(t=>t.name===n.params.name);if(!e)return r(`Unknown tool: ${n.params.name}`,!0);try{let t=e.inputSchema.safeParse(n.params.arguments);if(!t.success){let l=t.error.issues.map(i=>`${i.path.join(".")}: ${i.message}`).join(", ");return r(`Invalid input: ${l}`,!0)}let a=n.params._meta?.progressToken,o={server:S,progressToken:a};try{return await e.handler(t.data,o)}finally{o.cleanup?.()}}catch(t){let a=t instanceof Error?t.message:"An unexpected error occurred";return console.error("Tool error:",t),r(a,!0)}});async function pe(){let n=process.argv.indexOf("--api-url");n!==-1&&process.argv[n+1]&&(process.env.MISTFLOW_API_URL=process.argv[n+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let e=new re;await S.connect(e),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}pe().catch(n=>{console.error("Fatal error:",n),process.exit(1)});
176
+ `,Ee={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 n=>{let{command:e}=n;if(!e)return c($e);let t=$e.split(`
177
+ `),o=new RegExp(`^### \`mist ${e}`),r=-1,s=t.length;for(let i=0;i<t.length;i++)if(o.test(t[i]))r=i;else if(r>=0&&t[i].startsWith("### ")){s=i;break}else if(r>=0&&t[i].startsWith("## ")&&i>r){s=i;break}return r<0?c(`No command named '${e}' found. Call mist_help with no args to see the full catalog.`,!0):c(t.slice(r,s).join(`
178
+ `).trim())}};var F=new Ot({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\nWhen invoking `mist plan` through your shell/bash tool, give it at least a 2 minute budget before assuming it stalled. `mist plan` commonly takes 30-150s. Heavier commands like install/build/qa/deploy can need a longer budget (up to about 5 minutes).\n\nTreat the CLI\'s returned JSON as authoritative. If `mist plan --describe ...` returns `status: "clarify"`, the planning call completed and is waiting on user answers. Do NOT rerun the same command or describe it as "still generating". Only poll again when status is `"design_clarify_pending"`, and then use `mist plan-directions --cid <id> --wait --json`.\n\nWhen submitting discovery answers back to `mist plan --token ... --answers-stdin`, prefer an array payload like `{"answers":[{"question":"...","decisionKey":"...","answer":"..."}]}`. Do NOT collapse answers into a `{decisionKey: answer}` map because multiple discovery questions can share the same decision key.\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 <absolute-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 <live-url>` via your shell/bash tool, or just `mist qa` from inside the project if mistflow.json already has deploy.url. Call AFTER deploy. Do NOT show the URL to the user until QA passes.\n8. Seed sample data (optional but recommended before QA when acceptance criteria depend on populated tables/lists): run `mist seed <absolute-path>` via your shell/bash tool. This defaults to local-only seeding against PGlite; use `--allow-remote` only when you intentionally want to seed a remote environment. Use `--reset` to clear the matched tables first.\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 seed` / `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 seed (when needed) \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: ask product questions, call `mist_project action=get` for context, then run `mist plan --describe "<change request>" --existing-plan-id <id>`, then `mist implement`, then `mist deploy`.\n- Integration addition: ask product questions, call `mist_project action=get`, optionally browse `mist projects integrations`, run `mist plan --describe "<change request>" --existing-plan-id <id>`, then `mist implement`, then `mist env` to set required keys before deploy, then `mist deploy`, then `mist qa`.\n- Bug fix: run `mist debug <absolute-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.'}),Ae=[ye,je,De,Ee];F.setRequestHandler(qt,async()=>({tools:Ae.map(n=>({name:n.name,description:n.description,inputSchema:Ft(n.inputSchema)}))}));F.setRequestHandler(Nt,async n=>{let e=Ae.find(t=>t.name===n.params.name);if(!e)return c(`Unknown tool: ${n.params.name}`,!0);try{let t=e.inputSchema.safeParse(n.params.arguments);if(!t.success){let s=t.error.issues.map(i=>`${i.path.join(".")}: ${i.message}`).join(", ");return c(`Invalid input: ${s}`,!0)}let o=n.params._meta?.progressToken,r={server:F,progressToken:o};try{return await e.handler(t.data,r)}finally{r.cleanup?.()}}catch(t){let o=t instanceof Error?t.message:"An unexpected error occurred";return console.error("Tool error:",t),c(o,!0)}});async function Vt(){let n=process.argv.indexOf("--api-url");n!==-1&&process.argv[n+1]&&(process.env.MISTFLOW_API_URL=process.argv[n+1]),process.argv.includes("--local")&&!process.env.MISTFLOW_API_URL&&(process.env.MISTFLOW_API_URL="http://localhost:9100");let e=new Lt;await F.connect(e),console.error(`Mistflow MCP server running on stdio (API: ${process.env.MISTFLOW_API_URL||"https://api.mistflow.ai"})`)}Vt().catch(n=>{console.error("Fatal error:",n),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mistflow-ai/mcp",
3
- "version": "0.7.2-staging.0",
3
+ "version": "0.7.3",
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,b,c,d,e}from"./chunk-UNFTM4B3.js";export{b as closeBrowser,c as getIsolatedContext,a as getPage,d as getSnapshot,e as takeScreenshot};
@@ -1,2 +0,0 @@
1
- import{a as o,b as s,f as i,g as a}from"./chunk-RTKBAE4U.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
- `)}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};
@@ -1,23 +0,0 @@
1
- import{existsSync as y,readFileSync as k,writeFileSync as T,mkdirSync as $}from"fs";import{join as f,dirname as m}from"path";import{homedir as M}from"os";import{fileURLToPath as j}from"url";var p=null;function h(){if(p)return p;try{let t=j(import.meta.url),e=m(t);for(let n=0;n<6;n++){let o=f(e,"package.json");if(y(o)){let s=JSON.parse(k(o,"utf-8"));if(s.name==="@mistflow-ai/mcp"&&typeof s.version=="string")return p=s.version,s.version}let r=m(e);if(r===e)break;e=r}}catch{}return p="0.0.0","0.0.0"}function d(t){let e=/^(\d+)\.(\d+)\.(\d+)/.exec(t.trim());return e?[parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10)]:null}function R(t,e){let n=d(t),o=d(e);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 E(t,e,n){if(n&&R(t,n)<0)return"unsupported";if(!e||R(t,e)>=0)return"none";let r=d(t),s=d(e);return!r||!s?"none":r[0]<s[0]?"major":r[1]<s[1]?"minor":"patch"}var a=null;function C(t){let e=t.get("x-mistflow-mcp-latest")??"",n=t.get("x-mistflow-mcp-min-supported")??"",o=t.get("x-mistflow-mcp-changelog-url")??"";!e&&!n||(a={latest:e,minSupported:n,changelogUrl:o})}function b(){let t=process.env.MISTFLOW_STATE_DIR||f(M(),".mistflow");return f(t,"upgrade-state.json")}function A(){try{let t=b();return y(t)?JSON.parse(k(t,"utf-8")):{}}catch{return{}}}function O(t){try{let e=b(),n=m(e);y(n)||$(n,{recursive:!0}),T(e,JSON.stringify(t,null,2)+`
2
- `,{mode:384})}catch{}}var x=!1;function F(t){return t==="patch"?7*864e5:t==="minor"?1*864e5:0}function ie(){if(process.env.MISTFLOW_NO_UPGRADE_CHECK==="1"||!a)return null;let t=h();if(t==="0.0.0")return null;let e=E(t,a.latest,a.minSupported);if(e==="none")return null;if(e==="unsupported")return S(e,t,a);if(x)return null;let n=A(),o=Date.now(),r=F(e);return n.dismissedForVersion===a.latest&&e!=="major"||n.lastLatestSeen===a.latest&&n.lastShownMs&&o-n.lastShownMs<r?null:(x=!0,O({...n,lastShownMs:o,lastLatestSeen:a.latest}),S(e,t,a))}function S(t,e,n){let o="npx -y mistflow-ai install",r=n.changelogUrl?`
3
- What's new: ${n.changelogUrl}`:"";return t==="unsupported"?`
4
-
5
- ---
6
- Mistflow ${e} 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
- ---`:t==="major"?`
13
-
14
- ---
15
- Mistflow ${n.latest} is available (you have ${e}).
16
- This is a major update. Run \`${o}\` and restart your editor.${r}
17
- ---`:t==="minor"?`
18
-
19
- --- Mistflow update available: ${e} -> ${n.latest} ---
20
- Run \`${o}\` to upgrade, then restart your editor.${r}`:`
21
-
22
- (Mistflow ${n.latest} is out, you have ${e}. Run \`${o}\` when convenient.)`}import{readFileSync as V,existsSync as P,writeFileSync as L,mkdirSync as N,renameSync as B,unlinkSync as q}from"fs";import{join as v,dirname as K}from"path";import{homedir as J}from"os";import{randomBytes as H}from"crypto";function D(){return v(J(),".mistflow","credentials.json")}function g(){return(process.env.MISTFLOW_API_URL||"https://api.mistflow.ai").replace(/\/+$/,"")}var W="https://api.mistflow.ai";function U(){let t=D();if(!P(t))return null;try{let e=JSON.parse(V(t,"utf-8"));return typeof e.apiKey=="string"?{[W]:e}:e}catch{return null}}function w(){let t=process.env.MISTFLOW_API_KEY;if(t)return{ok:!0,creds:{apiKey:t,orgId:"",orgSlug:"env"}};let e=U();if(!e)return{ok:!1,reason:"missing"};let n=g(),o=e[n];return o&&typeof o.apiKey=="string"&&o.apiKey&&typeof o.orgId=="string"?{ok:!0,creds:o}:{ok:!1,reason:"missing"}}function de(t){let e=D(),n=K(e);P(n)||N(n,{recursive:!0});let o=U()??{},r=g();o[r]=t;let s=v(n,`.credentials.tmp.${H(8).toString("hex")}`);try{L(s,JSON.stringify(o,null,2)+`
23
- `,{mode:384}),B(s,e)}catch(c){try{q(s)}catch{}throw c}}function Q(){return w().ok}function z(){return g()}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 G(){let t=w();if(!t.ok)throw new i("auth_missing","No Mistflow credentials found.",401);return X(t.creds)}function X(t){return{Authorization:`ApiKey ${t.apiKey}`,"Content-Type":"application/json","X-Mistflow-MCP-Version":h()}}function Y(t){try{C(t.headers)}catch{}}async function Z(t,e,n,o){for(let r=0;r<2;r++)try{return await fetch(t,{...e,signal:AbortSignal.timeout(n)})}catch(s){let c=s instanceof Error&&s.name==="TimeoutError",u=s instanceof TypeError;if(o&&r===0&&(c||u)){console.error(`[api] Retrying ${t} after ${c?"timeout":"network error"}`);continue}throw s}throw new Error("fetchWithRetry: exhausted retries")}async function ee(t){let e=null;try{e=await t.json()}catch{e=null}let n=e&&typeof e.code=="string"?e.code:void 0,o=e&&typeof e.message=="string"?e.message:e&&typeof e.detail=="string"?e.detail:t.statusText||"Request failed";if(n)return new i(n,o,t.status,e?.details);let r=t.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",t.statusText||"Internal server error",r):new i("client_error",o,r)}async function te(t,e={}){let n=G(),{timeoutMs:o,idempotent:r,...s}=e,c=o??3e4,u=(s.method??"GET").toUpperCase(),I=r??(u==="GET"||u==="HEAD"),l;try{l=await Z(`${z()}${t}`,{...s,headers:{...n,...s.headers}},c,I)}catch(_){throw _ instanceof Error&&_.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(Y(l),!l.ok)throw await ee(l);return l.json()}var ye=300*1e3;async function he(t,e,n="neon",o){return te("/api/projects",{method:"POST",body:JSON.stringify({name:t,template:e,db_provider:n,requested_subdomain:o})})}export{h as a,C as b,ie as c,de as d,Q as e,z as f,G as g,he as h};
@@ -1 +0,0 @@
1
- var e=null,a=null,t=null,i=null;async function r(){try{return await import("playwright")}catch{throw new Error("Playwright is not installed. Run: cd packages/mcp-server && pnpm add playwright && npx playwright install chromium")}}async function l(){let n=await r();return(!e||!e.isConnected())&&(e=await n.chromium.launch({headless:!0})),a&&(await a.close().catch(()=>{}),a=null),a=await e.newContext({viewport:{width:1280,height:720},deviceScaleFactor:1}),t=await a.newPage(),t}async function w(){return t&&!t.isClosed()?t:(i||(i=l().finally(()=>{i=null})),i)}async function s(){t&&!t.isClosed()&&await t.close().catch(()=>{}),a&&await a.close().catch(()=>{}),e?.isConnected()&&await e.close().catch(()=>{}),t=null,a=null,e=null}process.once("SIGTERM",()=>{s().finally(()=>process.exit(0))});process.once("SIGINT",()=>{s().finally(()=>process.exit(0))});async function u(){let n=await r();(!e||!e.isConnected())&&(e=await n.chromium.launch({headless:!0}));let o=await e.newContext({viewport:{width:1280,height:720},deviceScaleFactor:1}),c=await o.newPage();return{context:o,page:c}}async function p(n){return await n.locator("body").ariaSnapshot()}async function g(n,o=!1){return await n.screenshot({fullPage:o,type:"png"})}export{w as a,s as b,u as c,p as d,g as e};
@@ -1 +0,0 @@
1
- import{c as m,d as y}from"./chunk-EED3GL6G.js";import{e as f,f as u,g as p,h as g}from"./chunk-RTKBAE4U.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 +0,0 @@
1
- import{a,b,c,d,e,f,g}from"./chunk-EED3GL6G.js";import"./chunk-RTKBAE4U.js";export{d as emptyState,e as fetchRemoteState,g as fuzzyMatch,a as getLocalStatePath,b as readLocalState,f as syncRemoteState,c as writeLocalState};