vibespot 0.9.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -10
- package/dist/index.js +772 -216
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/ui/chat.js +349 -9
- package/ui/dashboard.js +28 -24
- package/ui/dialog.js +3 -1
- package/ui/index.html +22 -10
- package/ui/settings.js +66 -0
- package/ui/styles.css +143 -11
package/dist/index.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ya=Object.defineProperty;var lt=(t,e)=>()=>(t&&(e=t(t=0)),e);var Vs=(t,e)=>{for(var n in e)ya(t,n,{get:e[n],enumerable:!0})};import _d from"path";import{fileURLToPath as Rd}from"url";var f=lt(()=>{"use strict"});import{readFileSync as zn,writeFileSync as ba,mkdirSync as zs,existsSync as zt}from"fs";import{dirname as Sa,join as He}from"path";function x(t){return zn(t,"utf-8")}function F(t,e){zs(Sa(t),{recursive:!0}),ba(t,e,"utf-8")}function S(t){return zt(t)}function we(t){zs(t,{recursive:!0})}function qt(t){let e=[He(import.meta.dirname,"../../assets",t),He(import.meta.dirname,"../assets",t),He(process.cwd(),"assets",t)];for(let n of e)if(zt(n))return n;throw new Error(`Asset not found: ${t}`)}function ut(){if(dt)return dt;let t=[He(import.meta.dirname,"../../package.json"),He(import.meta.dirname,"../package.json"),He(process.cwd(),"package.json")];for(let e of t)if(zt(e))try{let n=JSON.parse(zn(e,"utf-8"));if(n.name==="vibespot"&&n.version)return dt=n.version,dt}catch{}return dt="dev",dt}function qs(){if(Yt)return Yt;let t=[He(import.meta.dirname,"../../CHANGELOG.md"),He(import.meta.dirname,"../CHANGELOG.md"),He(process.cwd(),"CHANGELOG.md")];for(let e of t)if(zt(e))try{return Yt=zn(e,"utf-8"),Yt}catch{}return""}var dt,Yt,X=lt(()=>{"use strict";f();dt="";Yt=""});import{join as Zs}from"path";import{homedir as va}from"os";import{chmodSync as wa}from"fs";function E(){if(!S(Xt))return{};try{let t=JSON.parse(x(Xt));return t.aiEngine==="api"&&(t.aiEngine="anthropic-api"),t}catch{return{}}}function ge(t,e){let n=e||E();switch(t){case"anthropic-api":case"api":return n.anthropicApiKey||process.env.ANTHROPIC_API_KEY;case"openai-api":return n.openaiApiKey||process.env.OPENAI_API_KEY;case"gemini-api":return n.geminiApiKey||process.env.GEMINI_API_KEY||process.env.GOOGLE_AI_API_KEY;default:return}}function qn(t){return t.length<=12?"***":t.slice(0,7)+"..."+t.slice(-4)}function Y(t){let n={...E(),...t};if(F(Xt,JSON.stringify(n,null,2)),process.platform!=="win32")try{wa(Xt,384)}catch{}}function Ze(){let t=E();if(!t.hubspotAccounts?.length)return null;let e=t.activeHubSpotAccount;if(e){let n=t.hubspotAccounts.find(s=>s.portalId===e);if(n)return n}return t.hubspotAccounts[0]||null}function Qt(t,e,n,s){let i=E().hubspotAccounts||[],a=i.findIndex(l=>l.portalId===e),r={portalId:e,portalName:n,personalAccessKey:t,dataCenter:s,addedAt:new Date().toISOString()};a>=0?i[a]=r:i.push(r),Y({hubspotAccounts:i,activeHubSpotAccount:e})}function eo(t){let e=E(),n=(e.hubspotAccounts||[]).filter(o=>o.portalId!==t),s={hubspotAccounts:n};e.activeHubSpotAccount===t&&(s.activeHubSpotAccount=n[0]?.portalId||void 0),Y(s)}function to(t){Y({activeHubSpotAccount:t})}function he(){return Ze()?.personalAccessKey||null}function Zt(t){return E().enabledCLITools?.includes(t)??!1}function no(t,e){let n=E(),s=new Set(n.enabledCLITools||[]);e?s.add(t):s.delete(t),Y({enabledCLITools:[...s]})}var xa,Xt,ee=lt(()=>{"use strict";f();X();xa=Zs(va(),".vibespot"),Xt=Zs(xa,"config.json")});import{readFileSync as Ia}from"fs";import{basename as ka}from"path";async function ao(t){let e=ro.get(t);if(e&&e.expiresAt-Date.now()>Ea)return e;let n=await fetch(`${Le}/localdevauth/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({encodedOAuthRefreshToken:t})});if(!n.ok){let i=await n.text().catch(()=>"");throw new Error(n.status===401||n.status===403?"Invalid or expired Personal Access Key":`Token exchange failed (${n.status}): ${i.slice(0,200)}`)}let s=await n.json(),o={accessToken:s.oauthAccessToken,expiresAt:s.expiresAtMillis,hubId:s.hubId,hubName:s.hubName||""};return ro.set(t,o),o}async function mt(t){let{accessToken:e}=await ao(t);return{Authorization:`Bearer ${e}`}}function rn(t){return t.replace(/^\/+/,"").split("/").filter(Boolean).map(encodeURIComponent).join("/")}function Ma(t){return t.startsWith("pat-eu1-")?"eu1":t.startsWith("pat-na1-")?"na1":t.startsWith("CiRldTE")?"eu1":"na1"}function Na(t){return new Promise(e=>setTimeout(e,t))}async function _t(t,e){let n=`HTTP ${t.status}`,s,o;try{let i=await t.json();if(i.message&&typeof i.message=="string"&&(n=i.message),i.category&&typeof i.category=="string"&&(s=i.category),i.errors&&Array.isArray(i.errors)&&i.errors.length>0){let a=i.errors[0];o=a.message||JSON.stringify(a)}}catch{try{let i=await t.text();i&&(n=i.slice(0,500))}catch{}}return{status:t.status,message:e?`${n} (${e})`:n,category:s,detail:o}}async function Pt(t,e,n=$a){for(let s=0;s<=n;s++){let o=await fetch(t,e);if(o.status===429||o.status>=500&&s<n){let i=Ta*Math.pow(2,s);await Na(i);continue}return o}return fetch(t,e)}async function an(t){let e=await ao(t),n=`${Le}/account-info/v3/details`,s=await Pt(n,{headers:await mt(t)});if(!s.ok){let r=await _t(s);throw new Error(`Failed to get account info: ${r.message}`)}let o=await s.json(),i=String(o.portalId||e.hubId||""),a=e.hubName||o.uiDomain||i;return{portalId:i,portalName:a,dataCenter:Ma(t)}}async function lo(t,e,n){let s=Ia(n),o=ka(n),i=new FormData,a=new Blob([s]);i.append("file",a,o);let r=`${Le}/cms/v3/source-code/published/content/${rn(e)}`,l=await Pt(r,{method:"PUT",headers:await mt(t),body:i});if(!l.ok){let c=await _t(l,e);return{success:!1,path:e,error:c}}return{success:!0,path:e}}async function es(t,e){let n=`${Le}/cms/v3/source-code/published/content/${rn(e)}`,s=await Pt(n,{method:"DELETE",headers:await mt(t)});if(!s.ok&&s.status!==404){let o=await _t(s,e);throw new Error(`Failed to delete ${e}: ${o.message}`)}}async function co(t,e){let n=`${Le}/cms/v3/source-code/published/content/${rn(e)}`,s=await Pt(n,{method:"GET",headers:await mt(t)});if(!s.ok){let i=await _t(s,e);throw new Error(`Failed to download ${e}: ${i.message}`)}let o=await s.arrayBuffer();return Buffer.from(o)}async function ln(t,e){let n=`${Le}/cms/v3/source-code/published/metadata/${rn(e)}`,s=await Pt(n,{method:"GET",headers:await mt(t)});if(s.status===404)return null;if(!s.ok){let o=await _t(s,e);throw new Error(`Failed to get metadata for ${e}: ${o.message}`)}return await s.json()}async function uo(t){let e=await mt(t),n=[`${Le}/cms/v3/source-code/published/metadata`,`${Le}/cms/v3/source-code/published/metadata/`,`${Le}/designmanager/v1/portals/content/listing`];for(let s of n)try{let o=await fetch(s,{method:"GET",headers:e});if(o.ok){let i=await o.json(),a=i.children||i.objects||(Array.isArray(i)?i:null);if(a&&a.length>0)return a.filter(r=>r.folder)}}catch{}return[]}var Le,$a,Ta,Ea,ro,tt=lt(()=>{"use strict";f();Le="https://api.hubapi.com",$a=3,Ta=1e3,Ea=300*1e3,ro=new Map});var yo={};Vs(yo,{fetchTheme:()=>Rt});import{mkdirSync as ho,writeFileSync as Oa}from"fs";import{join as ja,dirname as Fa}from"path";async function os(t,e){let n=await ln(t,e);if(!n)return[];if(!n.folder)return[n.path||e];let s=[],o=n.children||[];for(let i of o){let a=typeof i=="string"?i:i.name;if(!a)continue;let r=`${e}/${a}`;if(typeof i=="string")s.push(...await os(t,r));else{let l=i;l.folder?s.push(...await os(t,l.path||r)):s.push(l.path||r)}}return s}async function Ja(t,e,n){let s=0;async function o(){for(;s<t.length;){let a=s++;await n(t[a])}}let i=Array.from({length:Math.min(e,t.length)},()=>o());await Promise.all(i)}async function Rt(t,e,n,s={}){let o=s.concurrency??5,i=await os(t,e);if(i.length===0)throw new Error(`Theme "${e}" not found on HubSpot or is empty`);ho(n,{recursive:!0}),await Ja(i,o,async a=>{let r=a.startsWith(e+"/")?a.slice(e.length+1):a,l=ja(n,r);ho(Fa(l),{recursive:!0});let c=await co(t,a);Oa(l,c),s.onFile?.(r)})}var pn=lt(()=>{"use strict";f();tt()});var Hs={};Vs(Hs,{collectThemeFiles:()=>Wr,extractDesignContext:()=>ud});import{existsSync as Gn,readdirSync as Un,readFileSync as rd}from"fs";import{join as Fe}from"path";import{spawn as ad}from"child_process";async function ld(){return Js||(Js=(await import("@anthropic-ai/sdk")).default),Js}function Vt(t){try{return rd(t,"utf-8")}catch{return""}}function Wr(t){let e=[],n=0;function s(r,l){if(!l.trim())return!0;let c=`
|
|
2
2
|
### ${r}
|
|
3
3
|
\`\`\`
|
|
4
4
|
${l}
|
|
5
5
|
\`\`\`
|
|
6
|
-
`;return
|
|
7
|
-
${
|
|
6
|
+
`;return n+c.length>cd?!1:(e.push(c),n+=c.length,!0)}let o=Vt(Fe(t,"theme.json"));o&&s("theme.json",o);let i=Fe(t,"css");if(Gn(i)){for(let r of Un(i).filter(l=>l.endsWith(".css")))if(!s(`css/${r}`,Vt(Fe(i,r))))break}let a=Fe(t,"modules");if(Gn(a))for(let r of Un(a).filter(l=>l.endsWith(".module"))){let l=Fe(a,r),c=Vt(Fe(l,"module.css"));if(c&&!s(`modules/${r}/module.css`,c))break}if(Gn(a))for(let r of Un(a).filter(l=>l.endsWith(".module"))){let l=Fe(a,r),c=Vt(Fe(l,"module.html"));if(c&&!s(`modules/${r}/module.html`,c))break}if(Gn(a))for(let r of Un(a).filter(l=>l.endsWith(".module"))){let l=Fe(a,r),c=Vt(Fe(l,"fields.json"));if(c&&!s(`modules/${r}/fields.json`,c))break}return e.join("")}function dd(){if(!Wn)try{Wn=x(qt("extraction-prompt.md"))}catch{Wn=""}return Wn}function Ds(t,e,n){return new Promise((s,o)=>{let i={...process.env};delete i.CLAUDECODE;let a=ad(t,e,{stdio:["pipe","pipe","pipe"],env:i,shell:!0}),r="",l="";a.stdout.on("data",c=>{r+=c.toString()}),a.stderr.on("data",c=>{l+=c.toString()}),a.on("error",c=>o(new Error(`${t} failed to start: ${c.message}`))),a.on("close",c=>{c===0||r.trim()?s(r.trim()):o(new Error(`${t} exited with code ${c}: ${l.trim()}`))}),a.stdin.write(n),a.stdin.end()})}async function ud(t,e){e?.({status:"Collecting theme files..."});let n=Wr(t);if(!n.trim())throw new Error("No CSS, HTML, or fields.json files found in theme.");let s=dd();if(!s)throw new Error("Extraction prompt not found (assets/extraction-prompt.md).");let o=`Analyze this HubSpot CMS theme and extract the design system:
|
|
7
|
+
${n}`;e?.({status:"Analyzing design patterns..."});let i=E(),a=i.aiEngine||"anthropic-api",r="";switch(a){case"anthropic-api":case"api":{let l=ge("anthropic-api");if(!l)throw new Error("Anthropic API key not configured. Open Settings to add one.");let c=await ld();r=(await new c({apiKey:l}).messages.create({model:i.anthropicApiModel||"claude-sonnet-4-6",max_tokens:8e3,system:s,messages:[{role:"user",content:o}]})).content.filter(p=>p.type==="text").map(p=>p.text).join("");break}case"openai-api":{let l=ge("openai-api");if(!l)throw new Error("OpenAI API key not configured. Open Settings to add one.");let c=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${l}`},body:JSON.stringify({model:i.openaiApiModel||"gpt-4o",max_tokens:8e3,messages:[{role:"system",content:s},{role:"user",content:o}]})});if(!c.ok)throw new Error(`OpenAI API error: ${c.status} ${await c.text()}`);r=(await c.json()).choices?.[0]?.message?.content||"";break}case"gemini-api":{let l=ge("gemini-api");if(!l)throw new Error("Gemini API key not configured. Open Settings to add one.");let c=i.geminiApiModel||"gemini-2.5-flash",d=await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${c}:generateContent?key=${l}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({system_instruction:{parts:[{text:s}]},contents:[{role:"user",parts:[{text:o}]}],generationConfig:{maxOutputTokens:8e3}})});if(!d.ok)throw new Error(`Gemini API error: ${d.status} ${await d.text()}`);r=(await d.json()).candidates?.[0]?.content?.parts?.map(p=>p.text).join("")||"";break}case"claude-code":{let l=`${s}
|
|
8
8
|
|
|
9
9
|
## User Request
|
|
10
|
-
${
|
|
10
|
+
${o}`,c=["--print"];i.claudeCodeModel&&c.push("--model",i.claudeCodeModel),r=await Ds("claude",c,l);break}case"gemini-cli":{let l=`${s}
|
|
11
11
|
|
|
12
12
|
## User Request
|
|
13
|
-
${
|
|
13
|
+
${o}`;r=await Ds("gemini",[],l);break}case"codex-cli":{let l=`${s}
|
|
14
14
|
|
|
15
15
|
## User Request
|
|
16
|
-
${
|
|
17
|
-
`);for(let a of i){let r=a.match(/^\s*(.+?)\s+(\d{5,})\s+(.*)/);if(r&&!/Account ID/i.test(a)&&!/^-+$/.test(a.trim())&&!/^Name\s/i.test(a.trim())){let l=r[1].trim(),c=r[2].trim(),
|
|
18
|
-
`)[0]?.replace("gh version ","").split(" ")[0]||"",path
|
|
16
|
+
${o}`;r=await Ds("codex",[],l);break}default:throw new Error(`Unknown AI engine: ${a}. Open Settings to configure one.`)}if(!r.trim())throw new Error("AI returned empty response.");return e?.({status:"Design extraction complete."}),r}var Js,cd,Wn,Ls=lt(()=>{"use strict";f();X();ee();Js=null;cd=8e4;Wn=""});f();f();import{Command as Td}from"commander";f();f();f();import ct from"chalk";var Te={accent:"#FF7A59",accentBright:"#FF9A7A",success:"#00BDA5",info:"#0066FF",warn:"#FFB020",error:"#E23D2D",muted:"#8B8D91",vibes:"#00BDD6"},Ys=!!process.env.NO_COLOR;function De(t){return Ys?ct:ct.hex(t)}var k={accent:De(Te.accent),accentBright:De(Te.accentBright),success:De(Te.success),info:De(Te.info),warn:De(Te.warn),error:De(Te.error),muted:De(Te.muted),vibes:De(Te.vibes),heading:Ys?ct.bold:ct.bold.hex(Te.accent),command:De(Te.accentBright),dim:ct.dim,bold:ct.bold};X();function Ee(){let t=k.vibes,e=k.accent,n=k.muted,s=[`${t("\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2584\u2584\u2584\u2584\u2584")}${t(" \u224B\u224B\u224B\u224B\u224B\u224B\u224B\u224B ")}${e("\u2584\u2584\u2584\u2584\u2584 \u2588\u2588\u2588\u2588\u2588 \u2584\u2584\u2584\u2584 \u2580\u2580\u2588\u2588\u2580\u2580")}`,`${t("\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ")}${t(" \u224B\u224B\u224B\u224B\u224B\u224B ")}${e("\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ")}`,`${t("\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 ")}${t(" \u224B\u224B\u224B\u224B ")}${e("\u2580\u2580\u2580\u2584 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ")}`,`${t(" \u2588\u2584\u2584\u2588\u2580 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ")}${t(" \u224B\u224B\u224B\u224B\u224B\u224B ")}${e(" \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ")}`,`${t(" \u2580\u2580\u2580 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2580\u2580\u2580\u2580\u2580")}${t(" \u224B\u224B\u224B\u224B\u224B\u224B\u224B\u224B ")}${e("\u2580\u2580\u2580\u2580 \u2588\u2588 \u2580\u2580\u2580\u2580 \u2588\u2588 ")}`];console.log();for(let o of s)console.log(` ${o}`);console.log(),console.log(` ${n("AI-powered HubSpot Landing Pages")} ${k.dim(`v${ut()}`)}`),console.log()}f();f();import{join as en}from"path";import{homedir as tn}from"os";import{readFileSync as so,existsSync as nn,readdirSync as Ca}from"fs";f();import{execSync as Xs}from"child_process";function $(t,e={}){try{return{stdout:Xs(t,{encoding:"utf-8",stdio:["pipe","pipe","pipe"],timeout:12e4,...e}).trim(),stderr:"",success:!0}}catch(n){let s=n,o=(s.stdout??"").toString().trim(),i=(s.stderr??"").toString().trim();return{stdout:o,stderr:i,success:!1}}}function Qs(t,e={}){try{return Xs(t,{stdio:"inherit",timeout:3e5,...e}),!0}catch{return!1}}ee();var et=process.platform==="win32"?"where":"which";function kt(){let t=$("node --version");return{name:"Node.js",found:t.success,version:t.stdout.replace(/^v/,""),path:$(`${et} node`).stdout}}function $t(){let t=$("git --version");return{name:"Git",found:t.success,version:t.stdout.replace("git version ",""),path:$(`${et} git`).stdout}}function Me(){let t=$("hs --version");return{name:"HubSpot CLI",found:t.success,version:t.stdout,path:$(`${et} hs`).stdout}}function Tt(){let t=$("claude --version");if(!t.success)return{name:"Claude Code",found:!1,version:"",path:"",authenticated:!1,authDetail:"Not installed"};let e=en(tn(),".claude"),n=!1,s="Not signed in \u2014 run `claude` to authenticate";try{if(nn(e)){let o=Ca(e);(o.some(a=>a.includes("credentials")||a.includes("auth")||a.includes("token")||a===".credentials.json")||o.length>2)&&(n=!0,s="Authenticated")}}catch{}return{name:"Claude Code",found:!0,version:t.stdout,path:$(`${et} claude`).stdout,authenticated:n,authDetail:s}}function Et(t){try{let e=en(tn(),".hscli","config.yml");if(!nn(e))return"na1";let n=so(e,"utf-8"),s=n.indexOf(`accountId: ${t}`);if(s===-1)return"na1";let o=n.indexOf("personalAccessKey:",s);if(o===-1)return"na1";let a=n.slice(o,o+300).match(/personalAccessKey:[\s>-]*\n\s+(\S+)/);if(!a)return"na1";if(a[1].startsWith("CiRldTE"))return"eu1"}catch{}return"na1"}function Ne(){let t=$("hs accounts list");if(!t.success||!t.stdout)return{authenticated:!1,portalName:"",portalId:"",accounts:[]};let e=[],n="",s="",o=t.stdout.match(/Account:\s*(.+?)\s*\((\d+)\)/);o&&(n=o[1].trim(),s=o[2].trim());let i=t.stdout.split(`
|
|
17
|
+
`);for(let a of i){let r=a.match(/^\s*(.+?)\s+(\d{5,})\s+(.*)/);if(r&&!/Account ID/i.test(a)&&!/^-+$/.test(a.trim())&&!/^Name\s/i.test(a.trim())){let l=r[1].trim(),c=r[2].trim(),d=r[3]?.trim()||"unknown";e.push({name:l,portalId:c,authType:d,isDefault:c===s})}}return o?{authenticated:!0,portalName:n,portalId:s,accounts:e}:e.length>0?{authenticated:!0,portalName:e[0].name,portalId:e[0].portalId,accounts:e}:{authenticated:t.stdout.length>0,portalName:"",portalId:"",accounts:[]}}function Mt(){let t=$("gemini --version");if(!t.success)return{name:"Gemini CLI",found:!1,version:"",path:"",authenticated:!1,authDetail:"Not installed"};let e=en(tn(),".config","gcloud","application_default_credentials.json"),n=nn(e),s=!!(process.env.GEMINI_API_KEY||process.env.GOOGLE_API_KEY||process.env.GOOGLE_AI_API_KEY),o=n||s;return{name:"Gemini CLI",found:!0,version:t.stdout,path:$(`${et} gemini`).stdout,authenticated:o,authDetail:o?"Authenticated":"Run `gemini` to sign in with Google"}}function Nt(){let t=$("codex --version");if(!t.success)return{name:"OpenAI Codex CLI",found:!1,version:"",path:"",authenticated:!1,authDetail:"Not installed"};let e=!!process.env.OPENAI_API_KEY,n=!1;try{let i=en(tn(),".codex","auth.json");nn(i)&&(n=so(i,"utf-8").length>10)}catch{}let s=e||n,o=n?"Authenticated (OAuth)":e?"Authenticated (API key)":"Not authenticated";return{name:"OpenAI Codex CLI",found:!0,version:t.stdout,path:$(`${et} codex`).stdout,authenticated:s,authDetail:o}}function Qn(){let t=$("gh --version");return{name:"GitHub CLI",found:t.success,version:t.stdout.split(`
|
|
18
|
+
`)[0]?.replace("gh version ","").split(" ")[0]||"",path:$(`${et} gh`).stdout}}function Zn(){let t=$("gh auth status 2>&1");if(!t.success&&!t.stdout)return{authenticated:!1,username:""};let e=t.stdout||t.stderr||"",n=e.match(/Logged in to github\.com.*account\s+(\S+)/);if(n)return{authenticated:!0,username:n[1]};let s=e.match(/account\s+(\S+)/);return s&&e.includes("Logged in")?{authenticated:!0,username:s[1]}:{authenticated:e.includes("Logged in"),username:""}}function oo(){return!!process.env.ANTHROPIC_API_KEY}function sn(t){return parseInt(t.split(".")[0],10)>=18}function io(t){let e=parseInt(t.split(".")[0],10);return!isNaN(e)&&e>=8}function Aa(){let t=E(),e=t.hubspotUploadMode||"api",n=t.hubspotAccounts||[],s=n.map(i=>({name:i.portalName,portalId:i.portalId,authType:"personalaccesskey",isDefault:i.portalId===(t.activeHubSpotAccount||n[0]?.portalId)})),o=Ze();return{authenticated:!!o,portalName:o?.portalName||"",portalId:o?.portalId||"",dataCenter:o?o.dataCenter:"na1",accounts:s,uploadMode:e}}var Xn={name:"",found:!1,version:"",path:"",authenticated:!1,authDetail:"Disabled"};function on(){let t=E(),e=kt(),n=$t(),s=t.hubspotUploadMode||"api",o;if(s==="cli"){let y=Me(),w=y.found?Ne():{authenticated:!1,portalName:"",portalId:"",accounts:[]},I=w.portalId?Et(w.portalId):"na1";o={...y,...w,dataCenter:I,uploadMode:"cli"}}else o={name:"HubSpot API",found:!0,version:"v3",path:"",...Aa()};let i=Qn(),a=i.found?Zn():{authenticated:!1,username:""},r=t.enabledCLITools||[],l=Zt("claude-code")?Tt():{...Xn,name:"Claude Code"},c=Zt("gemini-cli")?Mt():{...Xn,name:"Gemini CLI"},d=Zt("codex-cli")?Nt():{...Xn,name:"OpenAI Codex CLI"};function u(y,...w){if(y)return{configured:!0,masked:qn(y),source:"config"};for(let I of w)if(process.env[I])return{configured:!0,masked:qn(process.env[I]),source:"env"};return{configured:!1,masked:"",source:null}}let p=u(t.anthropicApiKey,"ANTHROPIC_API_KEY"),g=u(t.openaiApiKey,"OPENAI_API_KEY"),h=u(t.geminiApiKey,"GEMINI_API_KEY","GOOGLE_AI_API_KEY"),b=[];return l.found&&l.authenticated&&b.push("claude-code"),p.configured&&b.push("anthropic-api"),g.configured&&b.push("openai-api"),c.found&&c.authenticated&&b.push("gemini-cli"),h.configured&&b.push("gemini-api"),d.found&&d.authenticated&&b.push("codex-cli"),{tools:{node:e,git:n,hubspot:o,github:{...i,...a},claudeCode:l,geminiCli:c,codexCli:d},apiKeys:{anthropic:p,openai:g,gemini:h},activeEngine:t.aiEngine||null,availableEngines:b,enabledCLITools:r}}ee();tt();f();import*as B from"@clack/prompts";function ts(t){B.isCancel(t)&&(B.cancel(k.muted("Operation cancelled.")),process.exit(0))}async function le(t){B.intro(k.heading(t))}async function ce(t){B.outro(k.success(t))}async function _e(t,e){B.note(t,e?k.heading(e):void 0)}async function Ae(t){let e=await B.text({message:k.accent(t.message),placeholder:t.placeholder,defaultValue:t.defaultValue,validate:t.validate});return ts(e),e}async function ne(t){let e=await B.confirm({message:k.accent(t.message),initialValue:t.initialValue??!0});return ts(e),e}async function pt(t){let e=await B.select({message:k.accent(t.message),options:t.options});return ts(e),e}async function de(){let t=B.spinner();return{start:e=>t.start(k.muted(e)),stop:e=>t.stop(k.success(e)),message:e=>t.message(k.muted(e))}}function z(t){B.log.info(t)}function P(t){B.log.success(k.success(t))}function K(t){B.log.warn(k.warn(t))}function U(t){B.log.error(k.error(t))}async function cn(){await le("Checking your environment");let t=kt();t.found||(U("Node.js not found. Install it from https://nodejs.org"),process.exit(1)),sn(t.version)||(U(`Node.js ${t.version} is too old. Version 18+ required. Update at https://nodejs.org`),process.exit(1)),P(`Node.js v${t.version}`);let e=$t();e.found||(U("Git not found. Install it from https://git-scm.com"),process.exit(1)),P(`Git ${e.version}`);let n=E(),s=n.hubspotUploadMode!=="cli",o="",i="";if(s){let b=he(),y=Ze();if(b)o=y?.portalId||"",i=y?.portalName||"",P(`HubSpot${i?`: ${i}`:""}${o?` (${o})`:""} \u2014 API mode`);else{K("No HubSpot account connected"),await _e(`You need a Personal Access Key to deploy themes.
|
|
19
19
|
Create one at: https://app.hubspot.com/l/personal-access-key
|
|
20
|
-
Make sure the Content scope is enabled.`,"HubSpot connection required");let
|
|
20
|
+
Make sure the Content scope is enabled.`,"HubSpot connection required");let w=await Ae({message:"Paste your Personal Access Key:",placeholder:"pat-na1-...",validate:T=>T.trim()?void 0:"Key is required"}),I=await de();I.start("Validating key...");try{let T=await an(w);Qt(w,T.portalId,T.portalName,T.dataCenter),b=w,o=T.portalId,i=T.portalName,I.stop(`Connected to ${T.portalName} (${T.portalId})`)}catch(T){I.stop("Validation failed"),U(`Invalid key: ${T instanceof Error?T.message:String(T)}`),process.exit(1)}}}else{let b=Me();if(b.found)P(`HubSpot CLI v${b.version}`);else{K("HubSpot CLI not found"),await ne({message:"Install HubSpot CLI globally?"})||(U("HubSpot CLI is required in CLI mode. Install: npm install -g @hubspot/cli"),process.exit(1));let I=await de();I.start("Installing HubSpot CLI..."),$("npm install -g @hubspot/cli").success||(I.stop("Failed"),U("Try: npm install -g @hubspot/cli"),process.exit(1)),b=Me(),I.stop(`HubSpot CLI v${b.version} installed`)}let y=Ne();if(y.authenticated)P(`HubSpot portal${y.portalName?`: ${y.portalName}`:""} (ID: ${y.portalId})`);else{K("HubSpot not authenticated"),await ne({message:"Run `hs init` now?"})||(U("Run `hs init` manually."),process.exit(1));let I=await de();I.start("Waiting for HubSpot authentication..."),Qs("hs init")||(I.stop("Authentication failed"),process.exit(1)),y=Ne(),I.stop(`Connected to portal${y.portalName?`: ${y.portalName}`:""} (ID: ${y.portalId})`)}o=y.portalId,i=y.portalName}let a=Tt(),r=Mt(),l=Nt(),c=oo(),d={"claude-code":"Claude Code",api:"Anthropic API","anthropic-api":"Anthropic API","openai-api":"OpenAI API","gemini-api":"Gemini API","gemini-cli":"Gemini CLI","codex-cli":"OpenAI Codex"},u,p=n.aiEngine,g=[];if(a.found&&g.push({value:"claude-code",label:"Claude Code",hint:p==="claude-code"?"last used \u2014 recommended":"uses your existing Claude subscription \u2014 recommended"}),r.found&&g.push({value:"gemini-cli",label:"Gemini CLI",hint:p==="gemini-cli"?"last used":"uses your existing Gemini setup"}),l.found&&g.push({value:"codex-cli",label:"OpenAI Codex",hint:p==="codex-cli"?"last used":"uses your existing OpenAI setup"}),c&&g.push({value:"api",label:"Anthropic API",hint:p==="api"?"last used":"uses your API key"}),p&&g.sort((b,y)=>b.value===p?-1:y.value===p?1:0),g.length===1)u=g[0].value,P(`AI engine: ${d[u]} (auto-detected)`);else if(g.length>1)u=await pt({message:"Choose your AI engine:",options:g});else if(await _e(`You need an AI coding assistant to power the conversion.
|
|
21
21
|
|
|
22
|
-
${
|
|
22
|
+
${k.bold("Option 1:")} Install Claude Code ${k.muted("(recommended)")}
|
|
23
23
|
https://claude.ai/code
|
|
24
24
|
|
|
25
|
-
${
|
|
25
|
+
${k.bold("Option 2:")} Install Gemini CLI
|
|
26
26
|
https://github.com/google-gemini/gemini-cli
|
|
27
27
|
|
|
28
|
-
${
|
|
28
|
+
${k.bold("Option 3:")} Install OpenAI Codex
|
|
29
29
|
https://github.com/openai/codex
|
|
30
30
|
|
|
31
|
-
${
|
|
31
|
+
${k.bold("Option 4:")} Set an Anthropic API key
|
|
32
32
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
33
|
-
(get one at https://console.anthropic.com)`,"AI engine required"),
|
|
34
|
-
`),
|
|
35
|
-
|
|
36
|
-
CSS: ${
|
|
37
|
-
JS: ${
|
|
38
|
-
Font: ${
|
|
39
|
-
`),
|
|
40
|
-
`);let
|
|
33
|
+
(get one at https://console.anthropic.com)`,"AI engine required"),u=await pt({message:"Which will you set up?",options:[{value:"claude-code",label:"Claude Code",hint:"I'll install it now"},{value:"gemini-cli",label:"Gemini CLI",hint:"I'll install it now"},{value:"codex-cli",label:"OpenAI Codex",hint:"I'll install it now"},{value:"api",label:"Anthropic API",hint:"I'll enter my key"}]}),u==="api"){let b=await Ae({message:"Enter your Anthropic API key:",placeholder:"sk-ant-api03-...",validate:y=>y.startsWith("sk-ant-")?void 0:"Key should start with sk-ant-"});process.env.ANTHROPIC_API_KEY=b,Y({anthropicApiKey:b})}let h;return u==="claude-code"&&(h=await pt({message:"Which model?",options:[{value:"sonnet",label:"Sonnet",hint:"fast, recommended"},{value:"opus",label:"Opus",hint:"most capable"},{value:"haiku",label:"Haiku",hint:"fastest, cheapest"}]})),Y({aiEngine:u}),await ce("Environment ready!"),{aiEngine:u,model:h,portalId:o,portalName:i}}f();import{readdirSync as ns,statSync as _a}from"fs";import{join as q,basename as ss,extname as Pa}from"path";X();function mo(t){let e=[],n=[q(t,"src/components/landing"),q(t,"src/components/sections"),q(t,"src/components"),q(t,"src/pages"),q(t,"app/components"),q(t,"components")];for(let s of n)if(S(s))try{let o=ns(s);for(let i of o){let a=q(s,i);if(!_a(a).isFile())continue;let l=Pa(i);if(![".tsx",".jsx"].includes(l))continue;let c=ss(i,l);if(c.startsWith("ui")||c==="index")continue;let d=x(a),u=Ra(c,d);e.push({name:c,path:a,description:u})}}catch{}return e}function Ra(t,e){let n=[];return/carousel|slider|swiper|embla/i.test(e)&&n.push("carousel"),/accordion|collapsible|expand/i.test(e)&&n.push("accordion"),/form|submit|input.*email/i.test(e)&&n.push("form"),/nav|navigation|menu/i.test(e)&&n.push("navigation"),/hero|headline|tagline/i.test(e)&&n.push("hero"),/footer|copyright/i.test(e)&&n.push("footer"),/testimonial|quote|review/i.test(e)&&n.push("testimonials"),/pricing|plan|tier/i.test(e)&&n.push("pricing"),/faq|question.*answer/i.test(e)&&n.push("FAQ"),/feature|benefit|advantage/i.test(e)&&n.push("features"),/contact|get.in.touch/i.test(e)&&n.push("contact"),/cta|call.to.action/i.test(e)&&n.push("CTA"),/team|member|bio/i.test(e)&&n.push("team"),n.length===0?t.replace(/Section$/,"").replace(/([A-Z])/g," $1").trim():n.join(", ")}function po(t){let e=[q(t,"src/index.css"),q(t,"src/globals.css"),q(t,"src/app/globals.css"),q(t,"app/globals.css")],n=0,s=[];for(let o of e){if(!S(o))continue;let i=x(o),a=i.match(/--[\w-]+:/g);a&&(n+=a.length);let r=i.match(/font-family:\s*['"]([^'"]+)['"]/g);if(r)for(let c of r){let d=c.match(/['"]([^'"]+)['"]/)?.[1];d&&!s.includes(d)&&s.push(d)}let l=i.match(/@import\s+url\([^)]*fonts\.googleapis\.com[^)]*family=([^&)]+)/g);if(l)for(let c of l){let d=c.match(/family=([^&)]+)/)?.[1]?.replace(/\+/g," ");d&&!s.includes(d)&&s.push(d)}}return{varCount:n,fonts:s}}function fo(t){let e=[],n=q(t,"src/hooks");if(S(n))try{let o=ns(n);for(let i of o)/scroll/i.test(i)&&e.push("Scroll animations"),/intersection/i.test(i)&&e.push("Scroll animations")}catch{}let s=q(t,"src/components/landing");if(S(s))try{let o=ns(s);for(let i of o){if(!i.endsWith(".tsx")&&!i.endsWith(".jsx"))continue;let a=x(q(s,i));/carousel|embla|swiper/i.test(a)&&!e.includes("Carousel")&&e.push("Carousel"),/accordion|collapsible/i.test(a)&&!e.includes("Accordion")&&e.push("Accordion"),/typing|typewriter/i.test(a)&&!e.includes("Typing animation")&&e.push("Typing animation"),/parallax|requestAnimationFrame/i.test(a)&&!e.includes("Parallax")&&e.push("Parallax")}}catch{}return e.length===0&&e.push("Scroll animations"),e}function go(t){let e,n=!1;if(t.startsWith("http")||t.startsWith("git@")){n=!0;let l=ss(t.replace(/\.git$/,""))||"react-source";if(e=q(process.cwd(),"workspace",l),!S(e)){let c=$(`git clone --depth 1 "${t}" "${e}"`);if(!c.success)throw new Error(`Failed to clone ${t}: ${c.stderr}`)}}else if(e=t,!S(e))throw new Error(`Directory not found: ${e}`);let s=mo(e),o=S(q(e,"tailwind.config.ts"))||S(q(e,"tailwind.config.js")),{varCount:i,fonts:a}=po(e),r=fo(e);return{sourceDir:e,wasCloned:n,components:s,hasTailwind:o,cssVarCount:i,fonts:a,interactions:r}}async function dn(){await le("Source Project");let t=await Ae({message:"GitHub URL or local path to your React project:",placeholder:"https://github.com/user/my-lovable-page",validate:h=>{if(!h.trim())return"Please enter a URL or path"}}),e,n=!1;if(t.startsWith("http")||t.startsWith("git@")){n=!0;let h=ss(t.replace(/\.git$/,""))||"react-source";if(e=q(process.cwd(),"workspace",h),S(e))P(`Using existing clone: ${k.dim(e)}`);else{let b=await de();b.start("Cloning repository..."),$(`git clone --depth 1 "${t}" "${e}"`).success||(b.stop("Clone failed"),U(`Failed to clone ${t}. Check the URL and your access permissions.`),process.exit(1)),b.stop(`Cloned to ${k.dim(e)}`)}}else e=t,S(e)||(U(`Directory not found: ${e}`),process.exit(1)),P(`Using local source: ${k.dim(e)}`);let s=await de();s.start("Analyzing project structure...");let o=mo(e),i=S(q(e,"tailwind.config.ts"))||S(q(e,"tailwind.config.js")),{varCount:a,fonts:r}=po(e),l=fo(e);s.stop(`Found ${o.length} landing page components`),o.length===0&&(K("No components found. Make sure the React source has .tsx/.jsx files in src/components/"),process.exit(1));let c=o.map((h,b)=>` ${k.dim(`${b+1}.`)} ${k.bold(h.name)} ${k.muted(`\u2014 ${h.description}`)}`).join(`
|
|
34
|
+
`),d=i?`Tailwind + custom CSS (${a} variables)`:`Custom CSS (${a} variables)`,u=r.length>0?r.join(", "):"System fonts",p=l.join(", ");return await _e(`${c}
|
|
35
|
+
|
|
36
|
+
CSS: ${d}
|
|
37
|
+
JS: ${p}
|
|
38
|
+
Font: ${u}`,`${o.length} components detected`),await ne({message:"Does this look right?"})||(U("Please adjust your source directory and try again."),process.exit(0)),await ce("Source analyzed!"),{sourceDir:e,wasCloned:n,components:o,hasTailwind:i,cssVarCount:a,fonts:r,interactions:l}}f();import{join as Ot}from"path";X();ee();f();import{mkdirSync as Ke,writeFileSync as un}from"fs";import{join as Ie}from"path";function mn(t,e){Ke(t,{recursive:!0}),Ke(Ie(t,"templates"),{recursive:!0}),Ke(Ie(t,"modules"),{recursive:!0}),Ke(Ie(t,"css"),{recursive:!0}),Ke(Ie(t,"js"),{recursive:!0}),Ke(Ie(t,"images"),{recursive:!0}),Ke(Ie(t,"assets"),{recursive:!0});let n={label:e,preview_path:"./templates/home.html",screenshot_path:"./images/template-previews/home.png",enable_domain_stylesheets:!1,version:"1.0.0",author:{name:"vibeSpot",url:"https://github.com/borismichel/vibespot"}};un(Ie(t,"theme.json"),JSON.stringify(n,null,2)+`
|
|
39
|
+
`),un(Ie(t,"fields.json"),`[]
|
|
40
|
+
`);let s=`<!--
|
|
41
41
|
templateType: page
|
|
42
42
|
isAvailableForNewContent: false
|
|
43
|
-
label: ${
|
|
43
|
+
label: ${e} (placeholder)
|
|
44
44
|
screenshotPath: ../images/template-previews/home.png
|
|
45
45
|
-->
|
|
46
46
|
{% extends "./layouts/base.html" %}
|
|
@@ -48,11 +48,11 @@ ${C.bold("Option 4:")} Set an Anthropic API key
|
|
|
48
48
|
{% block body %}
|
|
49
49
|
{% dnd_area "main_content"
|
|
50
50
|
label="Main Content",
|
|
51
|
-
class="body-container body-container--${
|
|
51
|
+
class="body-container body-container--${e}"
|
|
52
52
|
%}
|
|
53
53
|
{% end_dnd_area %}
|
|
54
54
|
{% endblock body %}
|
|
55
|
-
`;
|
|
55
|
+
`;un(Ie(t,"templates","home.html"),s);let o=`<!--
|
|
56
56
|
templateType: none
|
|
57
57
|
isAvailableForNewContent: false
|
|
58
58
|
label: Base Layout
|
|
@@ -75,27 +75,27 @@ ${C.bold("Option 4:")} Set an Anthropic API key
|
|
|
75
75
|
{{ standard_footer_includes }}
|
|
76
76
|
</body>
|
|
77
77
|
</html>
|
|
78
|
-
`;
|
|
78
|
+
`;Ke(Ie(t,"templates","layouts"),{recursive:!0}),un(Ie(t,"templates","layouts","base.html"),o)}pn();async function fn(){await le("HubSpot Theme Setup");let t=await pt({message:"Do you have an existing HubSpot theme?",options:[{value:"fetch",label:"Fetch my existing theme from HubSpot",hint:"downloads your current theme"},{value:"create",label:"Start fresh (HubSpot Boilerplate)",hint:"creates a new starter theme"}]}),e,n,s=Ot(process.cwd(),"workspace");if(we(s),t==="fetch"){e=await Ae({message:"What's your theme name in HubSpot?",placeholder:"My-Company-Theme",validate:u=>u.trim()?void 0:"Theme name is required"}),n=Ot(s,e);let l=await de();l.start("Fetching theme from HubSpot...");let c=E(),d=he();if(c.hubspotUploadMode==="cli"||!d)$(`hs cms fetch "${e}" "${n}"`).success||(l.stop("Fetch failed"),U(`Could not fetch theme "${e}". Check the name in HubSpot Design Manager.`),process.exit(1));else try{await Rt(d,e,n)}catch(u){l.stop("Fetch failed"),U(`Could not fetch theme "${e}": ${u instanceof Error?u.message:String(u)}`),process.exit(1)}l.stop(`Theme fetched: ${k.dim(n)}`)}else{e=await Ae({message:"Name for your new theme:",placeholder:"my-theme",defaultValue:"my-theme"}),n=Ot(s,e);let l=await de();l.start("Creating theme...");try{mn(n,e)}catch(c){l.stop("Creation failed"),U(`Could not create theme "${e}": ${c instanceof Error?c.message:String(c)}`),process.exit(1)}l.stop(`Theme created: ${k.dim(n)}`)}await le("Checking theme compatibility");let o=Ot(n,"templates/layouts/base.html");S(o)||(U(`base.html not found at ${o}. Your theme may have a different structure.`),process.exit(1)),P("base.html found");let i=x(o),a=!1;if(i.includes("template_css"))P("template_css support");else{K("Missing template_css support in base.html");let l=i.indexOf("theme-overrides.css")!==-1?i.indexOf("{{",i.lastIndexOf(`
|
|
79
79
|
`,i.indexOf("theme-overrides.css"))):i.lastIndexOf("require_css");if(l>0){let c=i.lastIndexOf(`
|
|
80
80
|
`,l);i=i.slice(0,c)+`
|
|
81
81
|
{% if template_css %}
|
|
82
82
|
{{ require_css(get_asset_url(template_css)) }}
|
|
83
|
-
{% endif %}`+i.slice(c),a=!0}}if(i.includes("template_js"))P("template_js support");else{
|
|
84
|
-
`,l),
|
|
85
|
-
`,c+1),
|
|
83
|
+
{% endif %}`+i.slice(c),a=!0}}if(i.includes("template_js"))P("template_js support");else{K("Missing template_js support in base.html");let l=i.indexOf("require_js");if(l>0){let c=i.indexOf(`
|
|
84
|
+
`,l),d=i.indexOf(`
|
|
85
|
+
`,c+1),u=`
|
|
86
86
|
{% if template_js %}
|
|
87
87
|
{{ require_js(get_asset_url(template_js)) }}
|
|
88
|
-
{% endif %}`,
|
|
88
|
+
{% endif %}`,p=i.indexOf("}}",l)+2+i.slice(i.indexOf("}}",l)+2).indexOf(`
|
|
89
89
|
`)+1;i=i.slice(0,i.indexOf(`
|
|
90
|
-
`,i.indexOf("}}",l)+2))+
|
|
91
|
-
`,i.indexOf("}}",l)+2)),a=!0}}if(a){let l=await
|
|
90
|
+
`,i.indexOf("}}",l)+2))+u+i.slice(i.indexOf(`
|
|
91
|
+
`,i.indexOf("}}",l)+2)),a=!0}}if(a){let l=await de();l.start("Patching base.html..."),F(o,i),l.stop("base.html patched with template_css/template_js support")}let r=Ot(n,".hsignore");if(S(r)){let l=x(r);l.includes("docs/")||(F(r,l+`
|
|
92
92
|
docs/
|
|
93
|
-
`),P("Added docs/ to .hsignore"))}else
|
|
93
|
+
`),P("Added docs/ to .hsignore"))}else F(r,`docs/
|
|
94
94
|
*.md
|
|
95
95
|
node_modules/
|
|
96
96
|
.git
|
|
97
|
-
`),P("Created .hsignore");return await
|
|
98
|
-
## `,s
|
|
97
|
+
`),P("Created .hsignore");return await ce("Theme ready!"),{themePath:n,themeName:e}}f();import{join as xe}from"path";import{readdirSync as jt,rmSync as Mo}from"fs";f();import{spawn as Da}from"child_process";import{join as W,basename as Ha}from"path";import{readdirSync as Pe,statSync as To,writeFileSync as La}from"fs";f();X();var bo=new Map;function ft(t){let e=bo.get(t);if(e!==void 0)return e;try{e=x(qt(t))}catch{e=""}return bo.set(t,e),e}function re(){return ft("conversion-guide.md")||"Conversion guide not found. Using built-in rules."}function So(){return ft("design-guide.md")}function vo(){return ft("content-guide.md")}function Ve(){return ft("hubspot-rules.md")}function wo(){return ft("humanify-guide.md")}function xo(t){let e=ft("page-types.md");if(!e)return"";let s={landing_page:"## Landing Page",blog_post:"## Blog Post",website_page:"## Website Page",module_only:"## Module Only"}[t];if(!s)return"";let o=e.indexOf(s);if(o<0)return"";let i=e.indexOf(`
|
|
98
|
+
## `,o+s.length);return i>=0?e.slice(o,i).trim():e.slice(o).trim()}function Co(t){return`You are a HubSpot CMS expert converting React/Tailwind pages to native HubSpot modules.
|
|
99
99
|
|
|
100
100
|
## Rules
|
|
101
101
|
Follow the conversion guide below EXACTLY. Key rules:
|
|
@@ -112,10 +112,10 @@ Follow the conversion guide below EXACTLY. Key rules:
|
|
|
112
112
|
- Convert React hooks to vanilla JS (no React, no npm packages)
|
|
113
113
|
|
|
114
114
|
## HubSpot CMS Rules
|
|
115
|
-
${
|
|
115
|
+
${Ve()}
|
|
116
116
|
|
|
117
117
|
## Conversion Guide
|
|
118
|
-
${
|
|
118
|
+
${t}`}function Ao(t,e,n){return`Convert this React component to a HubSpot module named "${e}".
|
|
119
119
|
|
|
120
120
|
Return a JSON object with these keys:
|
|
121
121
|
- fieldsJson: complete fields.json content (as JSON string)
|
|
@@ -125,73 +125,73 @@ Return a JSON object with these keys:
|
|
|
125
125
|
- moduleJs: module.js content if interactive behavior needed, or null
|
|
126
126
|
|
|
127
127
|
Design system CSS variables available:
|
|
128
|
-
${
|
|
128
|
+
${n}
|
|
129
129
|
|
|
130
130
|
React component source:
|
|
131
|
-
${
|
|
131
|
+
${t}
|
|
132
132
|
|
|
133
|
-
Return ONLY valid JSON, no markdown fences.`}function
|
|
133
|
+
Return ONLY valid JSON, no markdown fences.`}function Io(t,e,n){return`Create a shared CSS file for a HubSpot CMS landing page.
|
|
134
134
|
|
|
135
135
|
Extract the design system from the source CSS and Tailwind config below.
|
|
136
|
-
Use the class prefix ".${
|
|
136
|
+
Use the class prefix ".${n}-" for all custom classes.
|
|
137
137
|
|
|
138
138
|
Requirements:
|
|
139
139
|
- CSS custom properties for all colors, spacing
|
|
140
|
-
- Page wrapper styles (.${
|
|
141
|
-
- .body-wrapper:has(.${
|
|
142
|
-
- .body-wrapper.${
|
|
143
|
-
- Scoped overrides: .${
|
|
140
|
+
- Page wrapper styles (.${n}-page) with !important font/color
|
|
141
|
+
- .body-wrapper:has(.${n}-page) background fix
|
|
142
|
+
- .body-wrapper.${n}-page-active JS fallback
|
|
143
|
+
- Scoped overrides: .${n}-page h1-h6, p, a with !important
|
|
144
144
|
- .dnd-section padding: 0 !important and .row-fluid max-width: 100%
|
|
145
145
|
- Form overrides for dark themes
|
|
146
146
|
- Utility classes (container, section, grid, glass)
|
|
147
|
-
- Scroll animation classes (.${
|
|
147
|
+
- Scroll animation classes (.${n}-scroll-animate / .visible)
|
|
148
148
|
- Mobile breakpoint at 767px
|
|
149
149
|
- Responsive grid fallbacks
|
|
150
150
|
|
|
151
151
|
Source CSS:
|
|
152
|
-
${
|
|
152
|
+
${t}
|
|
153
153
|
|
|
154
154
|
Tailwind config:
|
|
155
|
-
${
|
|
155
|
+
${e}
|
|
156
156
|
|
|
157
|
-
Return ONLY the CSS content, no markdown fences.`}function
|
|
157
|
+
Return ONLY the CSS content, no markdown fences.`}function ko(t,e,n){return`Create a shared vanilla JS file for a HubSpot CMS landing page.
|
|
158
158
|
|
|
159
159
|
Convert the React hooks and interactive components below to plain JavaScript.
|
|
160
|
-
Use the class prefix "${
|
|
160
|
+
Use the class prefix "${n}-" to match the CSS.
|
|
161
161
|
|
|
162
162
|
Requirements:
|
|
163
163
|
- IIFE wrapper with "use strict"
|
|
164
|
-
- initBodyWrapper: add "${
|
|
165
|
-
- initScrollAnimations: IntersectionObserver for .${
|
|
164
|
+
- initBodyWrapper: add "${n}-page-active" class to .body-wrapper
|
|
165
|
+
- initScrollAnimations: IntersectionObserver for .${n}-scroll-animate \u2192 .visible
|
|
166
166
|
- Convert any carousels, accordions, typing animations to vanilla JS
|
|
167
167
|
- DOMContentLoaded / readyState check
|
|
168
168
|
|
|
169
169
|
React hooks source:
|
|
170
|
-
${
|
|
170
|
+
${t}
|
|
171
171
|
|
|
172
172
|
Interactive component sources:
|
|
173
|
-
${
|
|
173
|
+
${e}
|
|
174
174
|
|
|
175
|
-
Return ONLY the JavaScript content, no markdown fences.`}function
|
|
175
|
+
Return ONLY the JavaScript content, no markdown fences.`}function $o(t,e,n){return`Create a HubSpot page template that assembles these modules:
|
|
176
176
|
|
|
177
|
-
${
|
|
177
|
+
${t.map((s,o)=>`${o+1}. ${s}.module`).join(`
|
|
178
178
|
`)}
|
|
179
179
|
|
|
180
180
|
Template requirements:
|
|
181
181
|
- templateType: page, isAvailableForNewContent: true
|
|
182
182
|
- extends "./layouts/base.html"
|
|
183
|
-
- set template_css = "../../css/${
|
|
184
|
-
- set template_js = "../../js/${
|
|
183
|
+
- set template_css = "../../css/${n}-theme.css"
|
|
184
|
+
- set template_js = "../../js/${n}-animations.js"
|
|
185
185
|
- Empty header and footer blocks (modules handle them)
|
|
186
|
-
- Wrap content in <div class="${
|
|
186
|
+
- Wrap content in <div class="${n}-page">
|
|
187
187
|
- Each module in its own dnd_section with padding zeroed and full_width=true
|
|
188
|
-
- dnd_area label: "${
|
|
188
|
+
- dnd_area label: "${e} Landing Page"
|
|
189
189
|
|
|
190
|
-
Return ONLY the template HTML content, no markdown fences.`}
|
|
190
|
+
Return ONLY the template HTML content, no markdown fences.`}X();var Ga=new Set(["about.html","blog-index.html","blog-post.html","contact.html","home.html","hubdb.html","landing-page.html","pricing.html","qa-test.html","base.html"]),gn=class{model;reported=new Set;moduleCount=0;expectedModules=0;constructor(e){this.model=e}async convert(e){let{sourceDir:n,themePath:s,onProgress:o}=e,i=e.conversionGuide||re();this.reported.clear(),this.moduleCount=0,this.expectedModules=0;let a=this.countSourceComponents(n),r=this.listModules(s),l=this.listDir(W(s,"css")),c=this.listDir(W(s,"js")),d=this.listDir(W(s,"templates")),u=this.buildFullPrompt(n,s,i);o("convert",`Starting Claude Code (${a} source components found)...`);let p="",g="",h=setInterval(()=>{this.reportProgress(s,r,l,c,d,o)},3e3);try{await new Promise((I,T)=>{let J={...process.env};delete J.CLAUDECODE;let D=["--print","--max-turns","50","--allowedTools","Read,Glob,Grep,Write,Edit,Bash"];this.model&&D.push("--model",this.model);let R=Da("claude",D,{cwd:s,stdio:["pipe","pipe","pipe"],env:J,shell:!0});R.stdout.on("data",L=>{p+=L.toString()}),R.stderr.on("data",L=>{g+=L.toString()}),R.on("error",L=>T(new Error(`Claude Code failed to start: ${L.message}`))),R.on("close",L=>{L!==0?T(new Error(`Claude Code exited with code ${L}.
|
|
191
191
|
`+(g?`Stderr: ${g.slice(0,500)}
|
|
192
|
-
`:"")+(
|
|
193
|
-
... (truncated, full guide follows)`,"","=== CLAUDE CODE STDOUT ===",
|
|
194
|
-
`);
|
|
192
|
+
`:"")+(p?`Output: ${p.slice(0,500)}`:"No output"))):I()}),R.stdin.on("error",()=>{}),R.stdin.write(u),R.stdin.end(),setTimeout(()=>{R.kill(),T(new Error("Claude Code timed out after 30 minutes"))},18e5)})}finally{clearInterval(h)}let b=W(s,"..","vibespot-conversion.log");try{let T=["=== vibeSpot Conversion Log ===",`Timestamp: ${new Date().toISOString()}`,`Source: ${n}`,`Theme: ${s}`,`Model: ${this.model||"default"}`,"","=== PROMPT SENT ===",u.slice(0,500)+`
|
|
193
|
+
... (truncated, full guide follows)`,"","=== CLAUDE CODE STDOUT ===",p||"(empty)","","=== CLAUDE CODE STDERR ===",g||"(empty)",""].join(`
|
|
194
|
+
`);La(b,T,"utf-8"),o("status",`Log written to ${Ha(b)}`)}catch{}o("scan","Scanning generated files...");let y=this.scanGeneratedFiles(s);if(y.modules.filter(I=>!r.has(I.moduleName+".module")).length===0){let I=p.slice(0,1500)||"(no output)",T=g.slice(0,500);throw new Error(`Claude Code did not create any new module files.
|
|
195
195
|
|
|
196
196
|
This usually means the model described the conversion instead of using Write tool to create files.
|
|
197
197
|
|
|
@@ -200,66 +200,66 @@ Possible causes:
|
|
|
200
200
|
- Claude Code hit a rate limit or API error
|
|
201
201
|
- The source directory was not accessible
|
|
202
202
|
|
|
203
|
-
Source: ${
|
|
204
|
-
Theme: ${
|
|
205
|
-
`+(
|
|
203
|
+
Source: ${e.sourceDir}
|
|
204
|
+
Theme: ${s}
|
|
205
|
+
`+(T?`
|
|
206
206
|
Stderr:
|
|
207
|
-
${
|
|
207
|
+
${T}
|
|
208
208
|
`:"")+`
|
|
209
209
|
Claude output:
|
|
210
|
-
${
|
|
210
|
+
${I}`)}return y}reportProgress(e,n,s,o,i,a){let r=0,l=this.listDir(W(e,"css"));for(let p of l){if(s.has(p)||!p.endsWith(".css"))continue;let g=`css:${p}`;this.reported.has(g)||(this.reported.add(g),a("created",`Shared CSS (${p})`),r++)}let c=this.listDir(W(e,"js"));for(let p of c){if(o.has(p)||!p.endsWith(".js"))continue;let g=`js:${p}`;this.reported.has(g)||(this.reported.add(g),a("created",`Shared JS (${p})`),r++)}this.expectedModules===0&&(this.expectedModules=this.detectExpectedModules(e,i));let d=this.listModules(e);for(let p of d){if(n.has(p))continue;let g=`module:${p}`;if(!this.reported.has(g)){this.reported.add(g),this.moduleCount++;let h=this.expectedModules>0?`[${this.moduleCount}/${this.expectedModules}]`:`[${this.moduleCount}]`;a("created",`Module ${h}: ${p.replace(".module","")}`),r++}}let u=this.listDir(W(e,"templates"));for(let p of u){if(i.has(p)||!p.endsWith(".html"))continue;let g=`template:${p}`;this.reported.has(g)||(this.reported.add(g),a("created",`Page template (${p})`),r++)}if(r===0)if(this.moduleCount>0){let p=this.expectedModules>0?`/${this.expectedModules}`:"";a("status",`${this.moduleCount}${p} modules created, conversion continuing...`)}else this.reported.size>0?a("status","Shared assets created, building modules..."):a("status","Claude Code is analyzing source files...")}buildFullPrompt(e,n,s){return`You are converting a React landing page to native HubSpot CMS modules.
|
|
211
211
|
|
|
212
|
-
SOURCE DIRECTORY: ${
|
|
213
|
-
THEME DIRECTORY: ${
|
|
212
|
+
SOURCE DIRECTORY: ${e}
|
|
213
|
+
THEME DIRECTORY: ${n}
|
|
214
214
|
|
|
215
215
|
IMPORTANT \u2014 YOU MUST CREATE REAL FILES:
|
|
216
216
|
You have access to Write, Edit, Read, Glob, Grep, and Bash tools. You MUST use the Write tool to create each file. Do NOT just describe or list what files should be created \u2014 actually call the Write tool for every single file. If you do not call Write, no files will be created and the conversion will fail.
|
|
217
217
|
|
|
218
218
|
STEP-BY-STEP PROCESS:
|
|
219
|
-
1. Use Glob to find all .tsx/.jsx files in ${
|
|
219
|
+
1. Use Glob to find all .tsx/.jsx files in ${e}/src/
|
|
220
220
|
2. Use Read to read each component file and understand the page structure
|
|
221
|
-
3. Use Write to create a shared CSS file at ${
|
|
221
|
+
3. Use Write to create a shared CSS file at ${n}/css/<name>-theme.css
|
|
222
222
|
- Include CSS custom properties, design system variables, utility classes
|
|
223
223
|
- Add theme-override countermeasures (.body-wrapper:has(), scoped !important overrides)
|
|
224
|
-
4. Use Write to create a shared JS file at ${
|
|
224
|
+
4. Use Write to create a shared JS file at ${n}/js/<name>-animations.js
|
|
225
225
|
- Convert React hooks to vanilla JS (IntersectionObserver for scroll animations)
|
|
226
226
|
- IIFE wrapper, DOMContentLoaded setup
|
|
227
227
|
5. For EACH visual section of the page, use Write to create ALL FOUR files:
|
|
228
|
-
a. ${
|
|
228
|
+
a. ${n}/modules/<name>.module/fields.json
|
|
229
229
|
- Editable fields for the section content
|
|
230
230
|
- NEVER use "textarea" type (use "text" instead)
|
|
231
231
|
- NEVER use "name" as a field name (use "item_name" instead)
|
|
232
232
|
- Add a "styles" group with "tab": "STYLE" containing color pickers
|
|
233
|
-
b. ${
|
|
233
|
+
b. ${n}/modules/<name>.module/meta.json
|
|
234
234
|
- Must include: host_template_types: ["PAGE"], is_available_for_new_content: true
|
|
235
|
-
c. ${
|
|
235
|
+
c. ${n}/modules/<name>.module/module.html
|
|
236
236
|
- HubL template that renders the section (convert JSX to HubL)
|
|
237
|
-
d. ${
|
|
237
|
+
d. ${n}/modules/<name>.module/module.css
|
|
238
238
|
- REQUIRED \u2014 complete vanilla CSS for this section
|
|
239
239
|
- Must include: layout, spacing, colors, typography, backgrounds, gradients, shadows, borders, hover effects, responsive breakpoints
|
|
240
240
|
- Convert ALL Tailwind classes to BEM-style CSS. Do NOT skip this file.
|
|
241
|
-
6. Use Write to create a page template at ${
|
|
241
|
+
6. Use Write to create a page template at ${n}/templates/lp-<name>.html
|
|
242
242
|
- Annotation: templateType: page, isAvailableForNewContent: true
|
|
243
243
|
- Extends "./layouts/base.html"
|
|
244
244
|
- Sets template_css and template_js variables
|
|
245
245
|
- Wraps modules in dnd_area with dnd_section containers
|
|
246
|
-
7. Read ${
|
|
246
|
+
7. Read ${n}/templates/layouts/base.html and ensure it supports template_css and template_js variables
|
|
247
247
|
|
|
248
248
|
CSS QUALITY: The converted page must visually match the original React page. Every module.css must be self-contained with complete styling for that section.
|
|
249
249
|
|
|
250
250
|
Do NOT run hs upload \u2014 I will handle that separately.
|
|
251
251
|
|
|
252
252
|
HUBSPOT CMS RULES:
|
|
253
|
-
${
|
|
253
|
+
${Ve()}
|
|
254
254
|
|
|
255
255
|
CONVERSION GUIDE:
|
|
256
|
-
${
|
|
257
|
-
${
|
|
256
|
+
${s}`}scanGeneratedFiles(e){let n={sharedCss:"",sharedJs:"",template:"",modules:[]},s=W(e,"css");if(S(s)){for(let r of Pe(s))if(r.endsWith(".css")&&r!=="theme-overrides.css"&&r!=="main.css"&&r!=="style.css"){n.sharedCss=x(W(s,r));break}}let o=W(e,"js");if(S(o)){for(let r of Pe(o))if(r.endsWith(".js")&&r!=="main.js"){n.sharedJs=x(W(o,r));break}}let i=W(e,"templates");if(S(i)){for(let r of Pe(i))if(r.startsWith("lp-")&&r.endsWith(".html")){n.template=x(W(i,r));break}if(!n.template){for(let r of Pe(i))if(r.endsWith(".html")&&!Ga.has(r)&&!r.startsWith("system")){let l=x(W(i,r));if(l.includes("dnd_area")){n.template=l;break}}}if(!n.template){for(let r of Pe(i))if(r.endsWith(".html")&&!r.startsWith("system")&&r!=="base.html"){let l=x(W(i,r));if(l.includes("dnd_area")){n.template=l;break}}}}let a=W(e,"modules");if(S(a))for(let r of Pe(a)){if(!r.endsWith(".module"))continue;let l=W(a,r);if(!To(l).isDirectory())continue;let c={moduleName:r.replace(".module",""),fieldsJson:"",metaJson:"",moduleHtml:"",moduleCss:""},d=W(l,"fields.json");S(d)&&(c.fieldsJson=x(d));let u=W(l,"meta.json");S(u)&&(c.metaJson=x(u));let p=W(l,"module.html");S(p)&&(c.moduleHtml=x(p));let g=W(l,"module.css");S(g)&&(c.moduleCss=x(g));let h=W(l,"module.js");S(h)&&(c.moduleJs=x(h)),c.fieldsJson&&c.moduleHtml&&n.modules.push(c)}return n}listModules(e){let n=W(e,"modules");return S(n)?new Set(Pe(n).filter(s=>s.endsWith(".module"))):new Set}listDir(e){return S(e)?new Set(Pe(e)):new Set}detectExpectedModules(e,n){let s=W(e,"templates");if(!S(s))return 0;for(let o of Pe(s))if(!n.has(o)&&!(!o.endsWith(".html")||o==="base.html"||o.startsWith("system")))try{let i=x(W(s,o));if(i.includes("dnd_area")){let a=i.match(/dnd_module/g);return a?a.length:0}}catch{}return 0}countSourceComponents(e){let n=W(e,"src");return S(n)?this.countComponentsRecursive(n):0}countComponentsRecursive(e){let n=0;for(let s of Pe(e)){let o=W(e,s);try{To(o).isDirectory()&&s!=="node_modules"&&s!==".git"?n+=this.countComponentsRecursive(o):/\.(tsx|jsx)$/.test(s)&&!s.includes(".test.")&&!s.includes(".spec.")&&n++}catch{}}return n}};f();import Ua from"@anthropic-ai/sdk";import{join as V,basename as Wa}from"path";import{readdirSync as Eo}from"fs";X();var hn=class{client;model="claude-sonnet-4-6";constructor(e){this.client=new Ua({apiKey:e||process.env.ANTHROPIC_API_KEY})}async convert(e){let{sourceDir:n,themePath:s,conversionGuide:o,onProgress:i}=e,a=Co(o),r=Wa(n)||"page",l=r.toLowerCase().replace(/[^a-z0-9]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").slice(0,15);i("css","Analyzing design system...");let c=this.findAndReadCSS(n),d=this.findAndReadTailwind(n),u=await this.complete(a,Io(c,d,l)),p=V(s,"css",`${l}-theme.css`);F(p,u),i("css-done",`Created css/${l}-theme.css`),i("js","Creating shared JavaScript...");let g=this.findAndReadHooks(n),h=this.findInteractiveComponents(n),b=await this.complete(a,ko(g,h,l)),y=V(s,"js",`${l}-animations.js`);F(y,b),i("js-done",`Created js/${l}-animations.js`),i("modules","Building modules...");let w=this.findComponents(n),I=[];for(let R=0;R<w.length;R++){let L=w[R],Z=L.name.replace(/Section$/,"").replace(/([A-Z])/g," $1").trim();i("module",`Building ${Z}.module (${R+1}/${w.length})...`);let G=x(L.path),H=await this.complete(a,Ao(G,Z,`See css/${l}-theme.css`));try{let O=JSON.parse(H),te={moduleName:Z,fieldsJson:typeof O.fieldsJson=="string"?O.fieldsJson:JSON.stringify(O.fieldsJson,null,2),metaJson:typeof O.metaJson=="string"?O.metaJson:JSON.stringify(O.metaJson,null,2),moduleHtml:O.moduleHtml||"",moduleCss:O.moduleCss||"",moduleJs:O.moduleJs||void 0},ie=V(s,"modules",`${Z}.module`);we(ie),F(V(ie,"fields.json"),te.fieldsJson),F(V(ie,"meta.json"),te.metaJson),F(V(ie,"module.html"),te.moduleHtml),F(V(ie,"module.css"),te.moduleCss),te.moduleJs&&F(V(ie,"module.js"),te.moduleJs),I.push(te),i("module-done",`${Z}.module (${this.countFiles(te)} files)`)}catch{i("module-error",`Failed to parse ${Z} \u2014 skipping`)}}i("template","Creating page template...");let T=I.map(R=>R.moduleName),J=await this.complete(a,$o(T,r,l)),D=V(s,"templates",`lp-${l}.html`);return F(D,J),i("template-done",`Created templates/lp-${l}.html`),{sharedCss:u,sharedJs:b,template:J,modules:I}}async complete(e,n){return(await this.client.messages.create({model:this.model,max_tokens:8192,system:e,messages:[{role:"user",content:n}]})).content.find(i=>i.type==="text")?.text||""}findAndReadCSS(e){let n=[V(e,"src/index.css"),V(e,"src/globals.css"),V(e,"src/app/globals.css"),V(e,"app/globals.css")];for(let s of n)if(S(s))return x(s);return""}findAndReadTailwind(e){let n=[V(e,"tailwind.config.ts"),V(e,"tailwind.config.js"),V(e,"tailwind.config.mjs")];for(let s of n)if(S(s))return x(s);return""}findAndReadHooks(e){let n=V(e,"src/hooks");if(!S(n))return"";try{return Eo(n).filter(s=>s.endsWith(".ts")||s.endsWith(".tsx")).map(s=>`// ${s}
|
|
257
|
+
${x(V(n,s))}`).join(`
|
|
258
258
|
|
|
259
|
-
`)}catch{return""}}findInteractiveComponents(
|
|
260
|
-
${i}`)}return
|
|
259
|
+
`)}catch{return""}}findInteractiveComponents(e){let n=this.findComponents(e),s=[];for(let o of n){let i=x(o.path);/carousel|accordion|typing|parallax|embla|swiper|collapsible/i.test(i)&&s.push(`// ${o.name}
|
|
260
|
+
${i}`)}return s.join(`
|
|
261
261
|
|
|
262
|
-
`)}findComponents(
|
|
262
|
+
`)}findComponents(e){let n=[V(e,"src/components/landing"),V(e,"src/components/sections"),V(e,"src/components")];for(let s of n)if(S(s))try{return Eo(s).filter(o=>(o.endsWith(".tsx")||o.endsWith(".jsx"))&&!o.startsWith("ui")&&o!=="index.tsx"&&o!=="index.jsx").map(o=>({name:o.replace(/\.(tsx|jsx)$/,""),path:V(s,o)}))}catch{continue}return[]}countFiles(e){let n=3;return e.moduleCss&&n++,e.moduleJs&&n++,n}};f();import{spawn as Ba}from"child_process";import{join as ye}from"path";import{readdirSync as yn,statSync as Ka}from"fs";X();var bn=class{async convert(e){let{sourceDir:n,themePath:s,onProgress:o}=e,i=e.conversionGuide||re(),a=this.buildFullPrompt(n,s,i);return o("convert","Running Gemini CLI (this may take a few minutes)..."),await new Promise((r,l)=>{let c=Ba("gemini",["-p",a],{cwd:s,stdio:["pipe","pipe","pipe"],env:{...process.env},shell:!0}),d="",u="";c.stdout.on("data",p=>{d+=p.toString()}),c.stderr.on("data",p=>{u+=p.toString()}),c.on("error",p=>l(new Error(`Gemini CLI failed: ${p.message}`))),c.on("close",p=>{p!==0&&u&&!d?l(new Error(`Gemini CLI failed: ${u}`)):r()}),setTimeout(()=>{c.kill(),l(new Error("Gemini CLI timed out after 10 minutes"))},6e5)}),o("scan","Scanning generated files..."),this.scanGeneratedFiles(s)}buildFullPrompt(e,n,s){return`Read the conversion guide below, then convert the React landing page at ${e} into native HubSpot CMS modules for the theme at ${n}.
|
|
263
263
|
|
|
264
264
|
INSTRUCTIONS:
|
|
265
265
|
1. Analyze all .tsx/.jsx components in the React source
|
|
@@ -271,10 +271,10 @@ INSTRUCTIONS:
|
|
|
271
271
|
7. Make sure base.html supports template_css and template_js variables
|
|
272
272
|
|
|
273
273
|
CONVERSION GUIDE:
|
|
274
|
-
${
|
|
274
|
+
${s}
|
|
275
275
|
|
|
276
276
|
Do NOT run hs upload \u2014 I will handle that separately.
|
|
277
|
-
Create all files directly in the theme directory.`}scanGeneratedFiles(
|
|
277
|
+
Create all files directly in the theme directory.`}scanGeneratedFiles(e){let n={sharedCss:"",sharedJs:"",template:"",modules:[]},s=ye(e,"css");if(S(s)){for(let r of yn(s))if((r.includes("theme")||r.includes("page"))&&r.endsWith(".css")&&r!=="theme-overrides.css"&&r!=="main.css"&&r!=="style.css"){n.sharedCss=x(ye(s,r));break}}let o=ye(e,"js");if(S(o)){for(let r of yn(o))if((r.includes("animation")||r.includes("page"))&&r.endsWith(".js")&&r!=="main.js"){n.sharedJs=x(ye(o,r));break}}let i=ye(e,"templates");if(S(i)){for(let r of yn(i))if(r.endsWith(".html")&&!r.startsWith("system")&&r!=="base.html"){let l=x(ye(i,r));if(l.includes("dnd_area")){n.template=l;break}}}let a=ye(e,"modules");if(S(a))for(let r of yn(a)){if(!r.endsWith(".module"))continue;let l=ye(a,r);if(!Ka(l).isDirectory())continue;let c={moduleName:r.replace(".module",""),fieldsJson:"",metaJson:"",moduleHtml:"",moduleCss:""},d=ye(l,"fields.json");S(d)&&(c.fieldsJson=x(d));let u=ye(l,"meta.json");S(u)&&(c.metaJson=x(u));let p=ye(l,"module.html");S(p)&&(c.moduleHtml=x(p));let g=ye(l,"module.css");S(g)&&(c.moduleCss=x(g));let h=ye(l,"module.js");S(h)&&(c.moduleJs=x(h)),c.fieldsJson&&c.moduleHtml&&n.modules.push(c)}return n}};f();import{spawn as Va}from"child_process";import{join as be}from"path";import{readdirSync as Sn,statSync as Ya}from"fs";X();var vn=class{async convert(e){let{sourceDir:n,themePath:s,onProgress:o}=e,i=e.conversionGuide||re(),a=this.buildFullPrompt(n,s,i);return o("convert","Running OpenAI Codex (this may take a few minutes)..."),await new Promise((r,l)=>{let c=Va("codex",["exec","--full-auto",a],{cwd:s,stdio:["pipe","pipe","pipe"],env:{...process.env},shell:!0}),d="",u="";c.stdout.on("data",p=>{d+=p.toString()}),c.stderr.on("data",p=>{u+=p.toString()}),c.on("error",p=>l(new Error(`Codex CLI failed: ${p.message}`))),c.on("close",p=>{p!==0&&u&&!d?l(new Error(`Codex CLI failed: ${u}`)):r()}),setTimeout(()=>{c.kill(),l(new Error("Codex CLI timed out after 10 minutes"))},6e5)}),o("scan","Scanning generated files..."),this.scanGeneratedFiles(s)}buildFullPrompt(e,n,s){return`Read the conversion guide below, then convert the React landing page at ${e} into native HubSpot CMS modules for the theme at ${n}.
|
|
278
278
|
|
|
279
279
|
INSTRUCTIONS:
|
|
280
280
|
1. Analyze all .tsx/.jsx components in the React source
|
|
@@ -286,92 +286,92 @@ INSTRUCTIONS:
|
|
|
286
286
|
7. Make sure base.html supports template_css and template_js variables
|
|
287
287
|
|
|
288
288
|
CONVERSION GUIDE:
|
|
289
|
-
${
|
|
289
|
+
${s}
|
|
290
290
|
|
|
291
291
|
Do NOT run hs upload \u2014 I will handle that separately.
|
|
292
|
-
Create all files directly in the theme directory.`}scanGeneratedFiles(
|
|
293
|
-
HubSpot-native modules. This takes 2-5 minutes.`,"AI Conversion");let
|
|
294
|
-
${
|
|
295
|
-
`),"Conversion Checklist");let
|
|
296
|
-
`+
|
|
297
|
-
`)),!await
|
|
298
|
-
`+
|
|
299
|
-
`));let g=
|
|
300
|
-
`,l=!0)}catch{
|
|
292
|
+
Create all files directly in the theme directory.`}scanGeneratedFiles(e){let n={sharedCss:"",sharedJs:"",template:"",modules:[]},s=be(e,"css");if(S(s)){for(let r of Sn(s))if((r.includes("theme")||r.includes("page"))&&r.endsWith(".css")&&r!=="theme-overrides.css"&&r!=="main.css"&&r!=="style.css"){n.sharedCss=x(be(s,r));break}}let o=be(e,"js");if(S(o)){for(let r of Sn(o))if((r.includes("animation")||r.includes("page"))&&r.endsWith(".js")&&r!=="main.js"){n.sharedJs=x(be(o,r));break}}let i=be(e,"templates");if(S(i)){for(let r of Sn(i))if(r.endsWith(".html")&&!r.startsWith("system")&&r!=="base.html"){let l=x(be(i,r));if(l.includes("dnd_area")){n.template=l;break}}}let a=be(e,"modules");if(S(a))for(let r of Sn(a)){if(!r.endsWith(".module"))continue;let l=be(a,r);if(!Ya(l).isDirectory())continue;let c={moduleName:r.replace(".module",""),fieldsJson:"",metaJson:"",moduleHtml:"",moduleCss:""},d=be(l,"fields.json");S(d)&&(c.fieldsJson=x(d));let u=be(l,"meta.json");S(u)&&(c.metaJson=x(u));let p=be(l,"module.html");S(p)&&(c.moduleHtml=x(p));let g=be(l,"module.css");S(g)&&(c.moduleCss=x(g));let h=be(l,"module.js");S(h)&&(c.moduleJs=x(h)),c.fieldsJson&&c.moduleHtml&&n.modules.push(c)}return n}};X();function za(t,e){switch(t){case"claude-code":return new gn(e);case"gemini-cli":return new bn;case"codex-cli":return new vn;case"api":return new hn}}async function wn(t){await le("Converting React to HubSpot Modules"),await _e(`AI will now analyze your React code and create
|
|
293
|
+
HubSpot-native modules. This takes 2-5 minutes.`,"AI Conversion");let e=za(t.aiEngine,t.model),n=re(),s=await de();s.start("Starting AI conversion...");let o=Date.now(),i=await e.convert({sourceDir:t.sourceDir,themePath:t.themePath,conversionGuide:n,onProgress:(h,b)=>{h==="created"?P(b):s.message(b)}}),a=((Date.now()-o)/1e3).toFixed(0);s.stop(`AI conversion complete (${a}s)`);let r=qa(t.themePath);for(let h of r)P(`Auto-fixed: ${h}`);let l=Xa(t.themePath,i),c=[];for(let h of l){let b=h.passed?"\u2705":"\u274C",y=h.passed?"":h.critical?" (CRITICAL)":" (cosmetic)";c.push(`${b} ${h.label}${y}`)}let d=l.filter(h=>h.passed).length;c.push(`
|
|
294
|
+
${d}/${l.length} checks passed`),await _e(c.join(`
|
|
295
|
+
`),"Conversion Checklist");let u=l.filter(h=>!h.passed&&h.critical),p=l.filter(h=>!h.passed&&!h.critical);if(u.length>0){if(U(`${u.length} critical issue(s) \u2014 upload will likely fail:
|
|
296
|
+
`+u.map(b=>` - ${b.label}`).join(`
|
|
297
|
+
`)),!await ne({message:"Continue with upload anyway?",initialValue:!1}))throw new Error("Conversion aborted due to critical checklist failures.")}else p.length>0&&K(`${p.length} non-critical issue(s) \u2014 page will work but may look incomplete:
|
|
298
|
+
`+p.map(h=>` - ${h.label}`).join(`
|
|
299
|
+
`));let g=xe(t.themePath,"..","vibespot-conversion.log");return S(g)&&(await ne({message:"Keep conversion log file for debugging?",initialValue:!1})?P(`Log saved: ${g}`):Mo(g)),await ce("Files ready for upload!"),i}function qa(t){let e=[];Qa(t),Za(t);let n=xe(t,"modules");if(S(n))for(let o of jt(n)){if(!o.endsWith(".module"))continue;let i=xe(n,o,"fields.json");if(!S(i))continue;let a=o.replace(".module",""),r=x(i),l=!1;r.includes('"textarea"')&&(r=r.replace(/"textarea"/g,'"text"'),l=!0,e.push(`${a}: "textarea" \u2192 "text"`)),/"name":\s*"name"/.test(r)&&(r=r.replace(/"name":\s*"name"/g,'"name": "item_name"'),l=!0,e.push(`${a}: reserved field name "name" \u2192 "item_name"`));try{let d=JSON.parse(r),u=!1;No(d)&&(u=!0,e.push(`${a}: fixed choice field format`)),_o(d)&&(u=!0,e.push(`${a}: fixed link field default value`)),u&&(r=JSON.stringify(d,null,2)+`
|
|
300
|
+
`,l=!0)}catch{e.push(`${a}: fields.json has invalid JSON \u2014 manual fix needed`)}l&&F(i,r);let c=xe(n,o,"module.html");if(S(c)){let d=x(c);d.includes("now()")&&(d=d.replace(/now\(\)/g,"local_dt"),F(c,d),e.push(`${a}: now() \u2192 local_dt`))}}let s=xe(t,"templates");if(S(s))for(let o of jt(s)){if(!o.endsWith(".html"))continue;let i=xe(s,o),a=x(i);(a.includes("hubdb_table")||a.includes("hubdb_table_rows"))&&(Mo(i),e.push(`Removed ${o} (HubDB requires CMS Hub Pro/Enterprise)`))}return e}function No(t){let e=!1;for(let n of t){if(typeof n!="object"||n===null)continue;let s=n;s.type==="choice"&&Array.isArray(s.choices)&&s.choices.some(i=>typeof i=="string")&&(s.choices=s.choices.map(i=>{if(typeof i=="string"){let a=i.charAt(0).toUpperCase()+i.slice(1);return[i,a]}return i}),e=!0),Array.isArray(s.children)&&No(s.children)&&(e=!0)}return e}function _o(t){let e=!1;for(let n of t){if(typeof n!="object"||n===null)continue;let s=n;if(s.type==="link"){let o=s.default;if(typeof o=="string"||o===void 0||o===null||typeof o=="object"&&!o.url){let a=typeof o=="string"?o:"";s.default={url:{href:a,type:"EXTERNAL"},open_in_new_tab:!1,no_follow:!1},e=!0}}Array.isArray(s.children)&&_o(s.children)&&(e=!0)}return e}function Xa(t,e){let n=[],s=e.modules.length;n.push({label:`Modules created (${s})`,passed:s>0,critical:!0});let o=!0;for(let u of e.modules)if(u.fieldsJson.includes('"textarea"')||/"name":\s*"name"/.test(u.fieldsJson)){o=!1;break}n.push({label:"fields.json valid (no textarea, no reserved names)",passed:s>0&&o,critical:!0});let i=e.modules.every(u=>u.moduleHtml.length>0);n.push({label:"module.html created for each module",passed:s>0&&i,critical:!0});let a=e.modules.filter(u=>!u.moduleCss).map(u=>u.moduleName),r=a.length===0;n.push({label:r?"module.css created for each module":`module.css missing for: ${a.join(", ")}`,passed:s>0&&r,critical:!1});let l=e.modules.some(u=>u.fieldsJson.includes('"STYLE"'));n.push({label:"Style tab fields (color pickers)",passed:l,critical:!1}),n.push({label:"Shared CSS with design system variables",passed:e.sharedCss.length>50,critical:!1}),n.push({label:"Shared JS for scroll animations",passed:e.sharedJs.length>50,critical:!1}),n.push({label:"Page template with dnd_area",passed:e.template.length>0&&e.template.includes("dnd_area"),critical:!0});let c=xe(t,"templates"),d=!1;if(S(c))for(let u of jt(c)){if(!u.endsWith(".html")||u==="base.html"||u.startsWith("system"))continue;let p=x(xe(c,u));if(p.includes("dnd_area")&&/templateType\s*:\s*page/i.test(p)){d=!0;break}}return n.push({label:"Template annotations (templateType: page)",passed:d,critical:!0}),n}function Qa(t){let e=xe(t,"templates");if(S(e))for(let n of jt(e)){if(!n.endsWith(".html")||n==="base.html"||n.startsWith("system"))continue;let s=xe(e,n),o=x(s);if(!o.includes("dnd_area")&&!o.includes("extends"))continue;let i=/templateType\s*:\s*page/i.test(o),a=/isAvailableForNewContent\s*:\s*true/i.test(o);if(i&&a)continue;let r=n.replace(".html","").replace(/[-_]/g," ").replace(/\b\w/g,l=>l.toUpperCase());if(o.includes("<!--")&&o.indexOf("-->")<200){let l=o.indexOf("-->"),c=o.slice(0,l);i||(c+=`
|
|
301
301
|
templateType: page`),a||(c+=`
|
|
302
302
|
isAvailableForNewContent: true`),/label\s*:/i.test(c)||(c+=`
|
|
303
|
-
label: ${r}`),
|
|
303
|
+
label: ${r}`),o=c+o.slice(l)}else o=`<!--
|
|
304
304
|
templateType: page
|
|
305
305
|
isAvailableForNewContent: true
|
|
306
306
|
label: ${r}
|
|
307
307
|
-->
|
|
308
|
-
`+
|
|
309
|
-
`)}catch{}}}
|
|
310
|
-
`),
|
|
311
|
-
`),
|
|
312
|
-
`);c=
|
|
313
|
-
You can check your HubSpot Design Manager to verify.`),await
|
|
314
|
-
The theme may work \u2014 check HubSpot Design Manager.`),await
|
|
308
|
+
`+o;F(s,o),P(`Template "${n}" \u2014 annotations verified`)}}function Za(t){let e=xe(t,"modules");if(S(e))for(let n of jt(e)){if(!n.endsWith(".module"))continue;let s=xe(e,n,"meta.json");if(S(s))try{let o=JSON.parse(x(s)),i=!1;(!o.host_template_types||!o.host_template_types.includes("PAGE"))&&(o.host_template_types=["PAGE"],i=!0),o.is_available_for_new_content||(o.is_available_for_new_content=!0,i=!0),i&&F(s,JSON.stringify(o,null,2)+`
|
|
309
|
+
`)}catch{}}}f();import{join as Uo,basename as cl}from"path";f();X();import{join as se}from"path";import{readdirSync as Ue,rmSync as el}from"fs";function xn(t){let e=[];for(let n of t){let s=`${n.message}${n.detail?` \u2014 ${n.detail}`:""}`,o=!1;/textarea|unknown.*field.*type/i.test(s)&&(o=!0),/reserved.*name|missing field name|field null/i.test(s)&&(o=!0),/could not resolve.*now/i.test(s)&&(o=!0),/hubdb|do not have access/i.test(s)&&(o=!0),/invalid default value|link.*invalid|deserializ/i.test(s)&&(o=!0),/color.*invalid/i.test(s)&&(o=!0),e.push({file:n.file||"unknown",message:s,fixable:o})}return e}function Cn(t){let e=[];if(/textarea.*not.*valid|unknown.*field.*type/i.test(t)){let n=t.match(/(?:in|file:?)\s+(\S+fields\.json)/i);e.push({file:n?.[1]||"fields.json",message:'"textarea" is not a valid field type',fixable:!0})}if(/missing field name|field null/i.test(t)){let n=t.match(/(?:in|file:?)\s+(\S+fields\.json)/i);e.push({file:n?.[1]||"fields.json",message:'"name" is a reserved field name',fixable:!0})}if(/could not resolve.*now/i.test(t)&&e.push({file:"module.html",message:"now() is not a valid HubL function",fixable:!0}),/hubdb|do not have access to hubdb/i.test(t)&&e.push({file:"templates",message:"HubDB requires CMS Hub Pro/Enterprise",fixable:!0}),/invalid default value|link.*field.*invalid/i.test(t)){let n=t.match(/field.*?(\w+)\s+has an invalid/i);e.push({file:n?.[1]||"fields.json",message:"Link field has invalid default value",fixable:!0})}if(/failed to deserialize/i.test(t)){let n=t.match(/file '([^']+)'/i);e.push({file:n?.[1]||"fields.json",message:"fields.json deserialization error",fixable:!0})}return/format for the color value is invalid/i.test(t)&&e.push({file:"fields.json",message:"Color field has invalid format (rgba/rgb/named \u2014 must be hex)",fixable:!0}),e}function An(t){let e=[];return Ro(t)&&e.push("textarea \u2192 text"),Oo(t)&&e.push("name \u2192 item_name"),jo(t)&&e.push("now() \u2192 local_dt"),Fo(t)&&e.push("Removed HubDB templates"),Jo(t)&&e.push("Fixed link field defaults"),Do(t)&&e.push("Fixed rgba/invalid color values \u2192 hex"),tl(t)&&e.push("Stripped CDN @import statements"),e}function Po(t,e){return e.message.includes("textarea")?Ro(t):e.message.includes("reserved field name")?Oo(t):e.message.includes("now()")?jo(t):e.message.includes("HubDB")?Fo(t):e.message.includes("invalid default value")||e.message.includes("deserialization")?Jo(t):e.message.includes("invalid format")&&e.message.includes("color")?Do(t):!1}function Ro(t){let e=!1,n=se(t,"modules");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".module"))continue;let o=se(n,s,"fields.json");if(!S(o))continue;let i=x(o);i.includes('"textarea"')&&(i=i.replace(/"textarea"/g,'"text"'),F(o,i),e=!0)}return e}function Oo(t){let e=!1,n=se(t,"modules");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".module"))continue;let o=se(n,s,"fields.json");if(!S(o))continue;let i=x(o);/"name":\s*"name"/g.test(i)&&(i=i.replace(/"name":\s*"name"/g,'"name": "item_name"'),F(o,i),e=!0)}return e}function jo(t){let e=!1,n=se(t,"modules");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".module"))continue;let o=se(n,s,"module.html");if(!S(o))continue;let i=x(o);i.includes("now()")&&(i=i.replace(/now\(\)/g,"local_dt"),F(o,i),e=!0)}return e}function Fo(t){let e=!1,n=se(t,"templates");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".html"))continue;let o=se(n,s),i=x(o);(i.includes("hubdb_table")||i.includes("hubdb_table_rows"))&&(el(o),e=!0)}return e}function Jo(t){let e=!1,n=se(t,"modules");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".module"))continue;let o=se(n,s,"fields.json");if(S(o))try{let i=JSON.parse(x(o));Lo(i)&&(F(o,JSON.stringify(i,null,2)+`
|
|
310
|
+
`),e=!0)}catch{}}return e}function tl(t){let e=!1,n=se(t,"css");if(S(n))for(let o of Ue(n)){if(!o.endsWith(".css"))continue;let i=se(n,o),a=x(i),r=a.replace(/@import\s+url\(['"]?https?:\/\/[^)]+['"]?\)\s*;?/gi,"");r!==a&&(F(i,r),e=!0)}let s=se(t,"modules");if(S(s))for(let o of Ue(s)){if(!o.endsWith(".module"))continue;let i=se(s,o,"module.css");if(!S(i))continue;let a=x(i),r=a.replace(/@import\s+url\(['"]?https?:\/\/[^)]+['"]?\)\s*;?/gi,"");r!==a&&(F(i,r),e=!0)}if(S(s))for(let o of Ue(s)){if(!o.endsWith(".module"))continue;let i=se(s,o,"module.html");if(!S(i))continue;let a=x(i),r=a.replace(/<link[^>]+href=['"]https?:\/\/[^'"]+['"][^>]*>/gi,"");r!==a&&(F(i,r),e=!0)}return e}function Do(t){let e=!1,n=se(t,"modules");if(!S(n))return!1;for(let s of Ue(n)){if(!s.endsWith(".module"))continue;let o=se(n,s,"fields.json");if(S(o))try{let i=JSON.parse(x(o));Ho(i)&&(F(o,JSON.stringify(i,null,2)+`
|
|
311
|
+
`),e=!0)}catch{}}return e}function Ho(t){let e=!1;for(let n of t){if(typeof n!="object"||n===null)continue;let s=n;if(s.type==="color"&&s.default&&typeof s.default=="object"){let o=s.default,i=o.color;if(typeof i=="string"&&!nl(i)){let a=sl(i);a&&(o.color=a.hex,a.opacity!==void 0&&(o.opacity=a.opacity),e=!0)}}Array.isArray(s.children)&&Ho(s.children)&&(e=!0)}return e}function nl(t){return/^#[0-9a-fA-F]{6}$/.test(t)}function sl(t){let e=t.match(/^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/);if(e)return{hex:`#${e[1]}${e[1]}${e[2]}${e[2]}${e[3]}${e[3]}`};let n=t.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/i);if(n){let i=Math.min(255,parseInt(n[1])),a=Math.min(255,parseInt(n[2])),r=Math.min(255,parseInt(n[3])),l=`#${i.toString(16).padStart(2,"0")}${a.toString(16).padStart(2,"0")}${r.toString(16).padStart(2,"0")}`,c=n[4]!==void 0?Math.round(parseFloat(n[4])*100):void 0;return{hex:l,opacity:c}}let s={white:"#ffffff",black:"#000000",red:"#ff0000",green:"#008000",blue:"#0000ff",yellow:"#ffff00",orange:"#ffa500",purple:"#800080",gray:"#808080",grey:"#808080",transparent:"#000000"},o=t.toLowerCase().trim();return s[o]?{hex:s[o],opacity:o==="transparent"?0:void 0}:null}function Lo(t){let e=!1;for(let n of t){if(typeof n!="object"||n===null)continue;let s=n;if(s.type==="link"){let o=s.default;if(typeof o=="string"||o===void 0||o===null||typeof o=="object"&&!o.url){let a=typeof o=="string"?o:"";s.default={url:{href:a,type:"EXTERNAL"},open_in_new_tab:!1,no_follow:!1},e=!0}}Array.isArray(s.children)&&Lo(s.children)&&(e=!0)}return e}ee();f();tt();tt();import{readdirSync as ol}from"fs";import{join as il,relative as rl}from"path";var al=new Set([".git","node_modules",".vibespot",".DS_Store"]);function Go(t){let e=[];for(let n of ol(t,{withFileTypes:!0})){if(al.has(n.name)||n.name.startsWith(".")&&n.name!==".gitkeep")continue;let s=il(t,n.name);n.isDirectory()?e.push(...Go(s)):n.isFile()&&e.push(s)}return e}async function ll(t,e,n){let s=0;async function o(){for(;s<t.length;){let a=s++;await n(t[a])}}let i=Array.from({length:Math.min(e,t.length)},()=>o());await Promise.all(i)}async function In(t,e,n,s={}){let o=s.concurrency??5,i=Go(e),a=i.length,r=0,l=0,c=[];return await ll(i,o,async d=>{let u=rl(e,d).replace(/\\/g,"/"),p=`${n}/${u}`;s.onFileStart?.(u);let g=await lo(t,p,d);if(g.success)r++,s.onFileComplete?.(u);else{l++;let h={file:u,status:g.error?.status||0,message:g.error?.message||"Unknown error",category:g.error?.category,detail:g.error?.detail};c.push(h),s.onFileError?.(u,h)}s.onProgress?.(r+l,a)}),{success:l===0,uploaded:r,failed:l,total:a,errors:c}}function dl(t){return(t.match(/^Uploaded file /gm)||[]).length}async function gt(t){await le("Uploading to HubSpot");let e=cl(t)||t,n=E(),s=he(),o=n.hubspotUploadMode!=="cli"&&!!s,i=await de(),a=3;for(let r=1;r<=a;r++){i.start(r===1?"Uploading theme...":`Retrying upload (attempt ${r}/${a})...`);let l=[],c=0,d=!1;if(o){let p=await In(s,t,e,{onFileComplete:()=>{c++}});d=p.success,d?c=p.uploaded:l=xn(p.errors)}else{let p=$(`hs cms upload "${t}" "${e}"`,{cwd:Uo(t,"..")}),g=[p.stdout,p.stderr].filter(Boolean).join(`
|
|
312
|
+
`);c=dl(g),d=p.success,d||(l=Cn(g))}if(d)return i.stop(`All files uploaded! (${c} files)`),await ce("Upload complete!"),!0;if(c>0?i.stop(`${c} files uploaded, but some errors occurred`):i.stop("Upload failed"),l.length===0){if(U("Upload failed with unknown error."),c>0&&(K(`Most files uploaded successfully. The theme may already be usable in HubSpot.
|
|
313
|
+
You can check your HubSpot Design Manager to verify.`),await ne({message:"Continue anyway (theme is likely uploaded)?",initialValue:!0})))return!0;if(r<a){if(!await ne({message:"Try uploading again?"}))break;continue}break}let u=!1;for(let p of l)p.fixable?Po(t,p)?(P(`Auto-fixed: ${p.message}`),u=!0):K(`Could not auto-fix: ${p.message}`):U(p.message);if(!(u&&r<a)){if(c>0&&(K(`${c} files uploaded successfully despite errors.
|
|
314
|
+
The theme may work \u2014 check HubSpot Design Manager.`),await ne({message:"Continue anyway?",initialValue:!0})))return!0;if(!u){if(i.start("Cleaning up stuck modules..."),o)try{await es(s,`${e}/modules`)}catch{}else $(`hs cms delete "${e}/modules"`,{cwd:Uo(t,"..")});i.stop("Cleaned up modules, retrying...")}}}return U("Upload failed after multiple attempts."),!1}f();import{execFileSync as is}from"child_process";import{rmSync as ul}from"fs";import{basename as Wo}from"path";X();async function Bo(t){let{portalId:e,sourceDir:n,themePath:s,wasCloned:o}=t;await le("You're all set!");let a=Et(e)==="eu1"?"app-eu1.hubspot.com":"app.hubspot.com";if(await _e(`Your React page has been converted and uploaded to HubSpot.
|
|
315
315
|
The theme and modules are now in your account, but you still
|
|
316
|
-
need to ${
|
|
316
|
+
need to ${k.bold("create a new landing page")} that uses them.
|
|
317
317
|
|
|
318
318
|
Next steps:
|
|
319
319
|
|
|
320
|
-
${
|
|
321
|
-
${
|
|
322
|
-
${
|
|
323
|
-
${
|
|
324
|
-
${
|
|
325
|
-
${
|
|
326
|
-
${
|
|
327
|
-
`),"utf-8")}function
|
|
328
|
-
`)){let i=
|
|
329
|
-
`)){let r=a.split("|");if(r.length<4)continue;let l=parseInt(r[3],10)*1e3;i.push({hash:r[0],fullHash:r[1],message:r[2],timestamp:l,date:new Date(l).toISOString()})}return i}function Ts(e,t){if(!xe())return{success:!1,error:"Git not available"};if(!Je(_e(e,".git")))return{success:!1,error:"Not a git repo"};let o=A(`git cat-file -t ${t}`,{cwd:e});if(!o.success||o.stdout.trim()!=="commit")return{success:!1,error:`Commit ${t} not found`};let n=A(`git log --format="%s" -1 ${t}`,{cwd:e}),s=n.success?n.stdout:t,i=A(`git checkout ${t} -- .`,{cwd:e});if(!i.success)return{success:!1,error:`Checkout failed: ${i.stderr}`};let a=`Rollback to: ${s}`.slice(0,72);return A(`git commit -m "${a.replace(/"/g,'\\"')}"`,{cwd:e}),{success:!0}}function $s(e,t,o,n){if(!xe())return{success:!1,error:"Git not available"};if(!Je(_e(e,".git")))return{success:!1,error:"Not a git repo"};let s=A(`git cat-file -t ${o}`,{cwd:e});if(!s.success||s.stdout.trim()!=="commit")return{success:!1,error:`Commit ${o} not found`};let i=A(`git log --format="%s" -1 ${o}`,{cwd:e}),a=i.success?i.stdout:o,r=0;for(let u of n)A(`git checkout ${o} -- "${u}"`,{cwd:e}).success&&r++;if(r===0)return{success:!1,error:"No files could be restored from that commit"};A("git add -A",{cwd:e});let c=`${`[${t}] `}Rollback to: ${a}`.slice(0,72);return A(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:e}),{success:!0}}var q=x(ga(),".vibespot","sessions"),Yn=x(q,"_index.json"),ct=null;function gn(){if(ct)return ct;try{return J(Yn)?(ct=JSON.parse(fe(Yn,"utf-8")),ct):Vn()}catch{return Vn()}}function hn(e){ct=e;try{Ge(q,{recursive:!0}),ne(Yn,JSON.stringify(e),"utf-8")}catch{}}function Vn(){if(!J(q))return[];let e=[];for(let t of Ue(q).filter(o=>o.endsWith(".json")&&o!=="_index.json"))try{let o=JSON.parse(fe(x(q,t),"utf-8")),n=o.templates||[];e.push({id:o.id,themeName:o.themeName,updatedAt:o.updatedAt,moduleCount:n.reduce((s,i)=>s+(i.modules?.length||0),0),templateCount:n.length})}catch{}return ct=e,hn(e),e}function ha(e){let t=gn(),o=e.templates||[],n={id:e.id,themeName:e.themeName,updatedAt:e.updatedAt,moduleCount:o.reduce((i,a)=>i+(a.modules?.length||0),0),templateCount:o.length},s=t.findIndex(i=>i.id===e.id);s>=0?t[s]=n:t.push(n),hn(t)}function ya(e){let t=gn().filter(o=>o.id!==e);hn(t)}function ba(e){let t=gn().filter(o=>o.themeName!==e);hn(t)}var m=null;function Nt(e,t){let o={id:Ea(),themePath:e,themeName:t,templates:[],activeTemplateId:"",messages:[],modules:[],sharedCss:"",sharedJs:"",template:"",moduleOrder:[],createdAt:Date.now(),updatedAt:Date.now()};return m=o,Wn(e),o}function Es(e){if(e.templates&&e.templates.length>0)return;if(!e.modules||e.modules.length===0){e.templates=[],e.activeTemplateId="";return}let t=`lp-${e.themeName}`,o={id:t,label:`${e.themeName} Landing Page`,pageType:"landing_page",templateFile:`templates/lp-${e.themeName}.html`,modules:[...e.modules],moduleOrder:[...e.moduleOrder],sharedCss:e.sharedCss||"",sharedJs:e.sharedJs||"",template:e.template||"",messages:[...e.messages]};e.templates=[o],e.activeTemplateId=t}function Fe(){return!m||!m.activeTemplateId||!m.templates?.length?null:m.templates.find(e=>e.id===m.activeTemplateId)||null}function zn(e){if(!m)return!1;let t=m.templates.find(o=>o.id===e);return t?(m.activeTemplateId=e,_t(t),m.updatedAt=Date.now(),!0):!1}function Ns(e,t){if(!m)throw new Error("No active session");let o=t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""),s=`${e==="blog_post"?"bp":e==="website_page"?"wp":e==="module_only"?"mo":"lp"}-${o}`,i={id:s,label:t,pageType:e,templateFile:e==="module_only"?"":`templates/${s}.html`,modules:[],moduleOrder:[],sharedCss:"",sharedJs:"",template:"",messages:[]};return m.templates.push(i),m.activeTemplateId=s,_t(i),m.updatedAt=Date.now(),i}function _s(e,t){if(!m)return null;let o=m.templates.find(l=>l.id===e);if(!o)return null;let n=t||`${o.label} (Copy)`,s=n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""),a=`${o.pageType==="blog_post"?"bp":o.pageType==="website_page"?"wp":o.pageType==="module_only"?"mo":"lp"}-${s}`,r={id:a,label:n,pageType:o.pageType,templateFile:o.pageType==="module_only"?"":`templates/${a}.html`,modules:o.modules.map(l=>({...l})),moduleOrder:[...o.moduleOrder],sharedCss:o.sharedCss,sharedJs:o.sharedJs,template:o.template,messages:[]};return m.templates.push(r),m.activeTemplateId=a,_t(r),m.updatedAt=Date.now(),r}function Rs(e,t){if(!m)return!1;let o=m.templates.find(n=>n.id===e);return o?(o.label=t,m.updatedAt=Date.now(),!0):!1}function Ps(e){if(!m)return!1;let t=m.templates.findIndex(o=>o.id===e);return t<0?!1:(m.templates.splice(t,1),m.activeTemplateId===e&&(m.templates.length>0?zn(m.templates[0].id):(m.activeTemplateId="",m.modules=[],m.moduleOrder=[],m.sharedCss="",m.sharedJs="",m.template="",m.messages=[])),m.updatedAt=Date.now(),!0)}function dt(){if(!m)return[];let e=new Map;for(let t of m.templates)for(let o of t.modules){let n=e.get(o.moduleName);n?n.usedIn.push(t.label):e.set(o.moduleName,{module:o,usedIn:[t.label]})}return Array.from(e.values())}function _t(e){m&&(m.modules=e.modules,m.moduleOrder=e.moduleOrder,m.sharedCss=e.sharedCss,m.sharedJs=e.sharedJs,m.template=e.template,m.messages=e.messages)}function Ve(){if(!m)return;let e=Fe();e&&(e.modules=m.modules,e.moduleOrder=m.moduleOrder,e.sharedCss=m.sharedCss,e.sharedJs=m.sharedJs,e.template=m.template,e.messages=m.messages)}function $(){return m}function ze(e,t){m&&(m.messages.push({role:e,content:t,timestamp:Date.now()}),m.updatedAt=Date.now(),Ve(),wa())}function Os(e){m&&(m.assets||(m.assets=[]),m.assets.push(e),m.updatedAt=Date.now(),j())}function yn(e){if(m){if(e.sharedCss!==void 0&&(m.sharedCss=e.sharedCss),e.sharedJs!==void 0&&(m.sharedJs=e.sharedJs),e.template!==void 0&&(m.template=e.template),e.modules)for(let t of e.modules){let o=m.modules.findIndex(n=>n.moduleName===t.moduleName);o>=0?m.modules[o]=t:(m.modules.push(t),m.moduleOrder.push(t.moduleName))}m.updatedAt=Date.now(),Ve()}}function Ms(e){m&&(m.moduleOrder=e,m.updatedAt=Date.now(),Ve())}function js(e){m&&(m.modules=m.modules.filter(t=>t.moduleName!==e),m.moduleOrder=m.moduleOrder.filter(t=>t!==e),m.updatedAt=Date.now(),Ve())}function Ds(e){m&&(m.moduleOrder=m.moduleOrder.filter(t=>t!==e),m.updatedAt=Date.now(),Ve())}function Js(e,t,o){if(!m)return;let n=m.modules.find(s=>s.moduleName===e);if(n)try{let s=JSON.parse(n.fieldsJson);Us(s,t,o),n.fieldsJson=JSON.stringify(s,null,2),m.updatedAt=Date.now(),Ve()}catch{}}function be(){if(!m)return[];let e=[];for(let t of m.moduleOrder){let o=m.modules.find(n=>n.moduleName===t);o&&e.push(o)}for(let t of m.modules)m.moduleOrder.includes(t.moduleName)||e.push(t);return e}function Sa(e,t){let o=ie(e);if(!o||t==="home.html"||t.endsWith("-listing.html"))return null;let n=t.replace(/\.html$/,""),s="landing_page";n.startsWith("bp-")?s="blog_post":n.startsWith("wp-")?s="website_page":n.startsWith("mo-")&&(s="module_only");let i=n,a=o.match(/<!--[\s\S]*?label:\s*"?([^"\n]+)"?\s*[\s\S]*?-->/);a&&(i=a[1].trim());let r=/dnd_module\s+path=["']\.\.\/modules\/(.+?)\.module["']/g,l=[],c;for(;(c=r.exec(o))!==null;)l.push(c[1]);return{id:n,label:i,pageType:s,moduleNames:l,templateContent:o,filename:t}}function va(e,t,o,n,s){if(!J(e))return[];let i=[],a=Ue(e).filter(r=>r.endsWith(".html")&&r!=="home.html");for(let r of a){let l=x(e,r),c=Sa(l,r);if(!c||c.moduleNames.length===0&&i.length>0)continue;let u=[],p=[];for(let f of c.moduleNames){let g=t.get(f);g&&(u.push(g),p.push(f))}i.push({id:c.id,label:c.label,pageType:c.pageType,templateFile:`templates/${c.filename}`,modules:u,moduleOrder:p,sharedCss:o,sharedJs:n,template:c.templateContent,messages:i.length===0?[...s]:[]})}return i}function Rt(e){if(!m)return;let t=xa(e);t.length>0&&m.messages.length===0&&(m.messages=t),Wn(e);let o=x(e,"modules");if(!J(o))return;let n=Ue(o,{withFileTypes:!0});for(let g of n){if(!g.isDirectory()||!g.name.endsWith(".module"))continue;let y=x(o,g.name),S=g.name.replace(/\.module$/,""),w={moduleName:S,fieldsJson:ie(x(y,"fields.json")),metaJson:ie(x(y,"meta.json")),moduleHtml:ie(x(y,"module.html")),moduleCss:ie(x(y,"module.css")),moduleJs:ie(x(y,"module.js"))||void 0};w.fieldsJson&&w.moduleHtml&&(m.modules.push(w),m.moduleOrder.push(S))}let s=x(e,"css"),i=x(e,"js"),a="",r="";if(J(s)){let g=Ue(s).filter(y=>y.endsWith("-theme.css"));g.length>0&&(a=ie(x(s,g[0])),m.sharedCss=a)}if(J(i)){let g=Ue(i).filter(y=>y.endsWith("-animations.js"));g.length>0&&(r=ie(x(i,g[0])),m.sharedJs=r)}let l=x(e,".vibespot","styleguide.md"),c=x(e,".vibespot","brandvoice.md");(J(l)||J(c))&&(m.brandAssets||(m.brandAssets={}),J(l)&&(m.brandAssets.styleguide=ie(l)),J(c)&&(m.brandAssets.brandvoice=ie(c)));let u=x(e,"templates"),p=new Map(m.modules.map(g=>[g.moduleName,g])),f=va(u,p,a,r,m.messages);if(f.length>0){m.templates=f,m.activeTemplateId=f[0].id;let g=f[0].moduleOrder;if(g.length>0){let y=new Set(m.moduleOrder),S=g.filter(w=>y.has(w));for(let w of m.moduleOrder)S.includes(w)||S.push(w);m.moduleOrder=S}_t(f[0])}else m.templates||(m.templates=[]),m.activeTemplateId||(m.activeTemplateId=""),Es(m)}function j(){if(!m)return;Ge(q,{recursive:!0});let e=x(q,`${m.id}.json`);ne(e,JSON.stringify(m,null,2),"utf-8"),ha(m)}function bn(e){let t=x(q,e+".json");if(!J(t))return null;try{let o=JSON.parse(fe(t,"utf-8"));return o.templates||(o.templates=[]),o.activeTemplateId||(o.activeTemplateId=""),Es(o),m=o,o}catch{return null}}function ut(){return J(q)?gn():[]}function Fs(e,t=!1){let o=x(q,e+".json"),n="";if(t)try{let s=JSON.parse(fe(o,"utf-8"));n=s.themeName||"",s.themePath&&J(s.themePath)&&Et(s.themePath,{recursive:!0,force:!0})}catch{}else try{n=JSON.parse(fe(o,"utf-8")).themeName||""}catch{}try{J(o)&&Et(o)}catch{}if(n&&J(q)){for(let s of Ue(q).filter(i=>i.endsWith(".json")&&i!=="_index.json"))try{JSON.parse(fe(x(q,s),"utf-8")).themeName===n&&Et(x(q,s))}catch{}ba(n)}else ya(e);m?.id===e&&(m=null)}function Hs(e,t){let o=x(q,e+".json");if(!J(o))return{ok:!1,error:"Session not found"};let n;try{n=JSON.parse(fe(o,"utf-8"))}catch{return{ok:!1,error:"Failed to read session"}}let s=n.themeName;if(s===t)return{ok:!0};let i=n.themePath,a=x(fa(i),t);if(J(i)){if(J(a))return{ok:!1,error:"A project with that name already exists"};try{Bn(i,a)}catch(f){return{ok:!1,error:`Failed to rename folder: ${f instanceof Error?f.message:String(f)}`}}let r=x(a,"css",`${s}-theme.css`),l=x(a,"css",`${t}-theme.css`);if(J(r))try{Bn(r,l)}catch{}let c=x(a,"js",`${s}-animations.js`),u=x(a,"js",`${t}-animations.js`);if(J(c))try{Bn(c,u)}catch{}let p=x(a,"theme.json");if(J(p))try{let f=JSON.parse(fe(p,"utf-8"));f.label=t,f.name=t,ne(p,JSON.stringify(f,null,2),"utf-8")}catch{}}if(J(q))for(let r of Ue(q).filter(l=>l.endsWith(".json")&&l!=="_index.json"))try{let l=JSON.parse(fe(x(q,r),"utf-8"));l.themeName===s&&(l.themeName=t,l.themePath=a,l.updatedAt=Date.now(),ne(x(q,r),JSON.stringify(l,null,2),"utf-8"))}catch{}return m&&m.themeName===s&&(m.themeName=t,m.themePath=a,m.updatedAt=Date.now()),Vn(),{ok:!0}}function We(){if(!m)return;let e=m.themePath,t=new Map;if(m.templates.length>0)for(let r of m.templates)for(let l of r.modules)t.set(l.moduleName,l);for(let r of m.modules)t.set(r.moduleName,r);let o=x(e,"modules");Ge(o,{recursive:!0});for(let r of t.values())Ge(x(o,`${r.moduleName}.module`),{recursive:!0});for(let r of t.values()){let l=x(o,`${r.moduleName}.module`);ne(x(l,"fields.json"),r.fieldsJson,"utf-8"),ne(x(l,"meta.json"),r.metaJson,"utf-8"),ne(x(l,"module.html"),r.moduleHtml,"utf-8"),ne(x(l,"module.css"),r.moduleCss,"utf-8"),r.moduleJs&&ne(x(l,"module.js"),r.moduleJs,"utf-8")}if(m.sharedCss){let r=x(e,"css");Ge(r,{recursive:!0}),ne(x(r,`${m.themeName}-theme.css`),m.sharedCss,"utf-8")}if(m.sharedJs){let r=x(e,"js");Ge(r,{recursive:!0}),ne(x(r,`${m.themeName}-animations.js`),m.sharedJs,"utf-8")}let n=x(e,"templates");Ge(n,{recursive:!0});let s=x(n,"home.html");(m.templates.length>0||m.modules.length>0)&&J(s)&&Et(s,{force:!0});let a=new Set;if(m.templates.length>0)for(let r of m.templates){if(r.pageType==="module_only"||r.modules.length===0)continue;let l=r.template||Ia(r),c=ks(l,r.label,r.pageType),u=`${r.id}.html`;ne(x(n,u),c,"utf-8"),a.add(u),r.pageType==="blog_post"&&(Ta(n,r),a.add(`${r.id}-listing.html`))}else if(m.modules.length>0){let r=m.template||ka(),l=ks(r,`${m.themeName} Landing Page`),c=`lp-${m.themeName}.html`;ne(x(n,c),l,"utf-8"),a.add(c)}try{for(let r of Ue(n))r.startsWith("lp-")&&r.endsWith(".html")&&!a.has(r)&&Et(x(n,r),{force:!0})}catch{}Ca(),Aa()}function wa(){if(m)try{let e=x(m.themePath,".vibespot");Ge(e,{recursive:!0});let t={sessionId:m.id,themeName:m.themeName,messages:m.messages,updatedAt:Date.now()};ne(x(e,"chat.json"),JSON.stringify(t,null,2),"utf-8")}catch{}}function xa(e){let t=x(e,".vibespot","chat.json");if(!J(t))return[];try{let o=JSON.parse(fe(t,"utf-8"));return Array.isArray(o.messages)?o.messages:[]}catch{return[]}}function Ls(){m&&(m.modules=[],m.moduleOrder=[],m.sharedCss="",m.sharedJs="",m.template="",Rt(m.themePath),m.updatedAt=Date.now(),Ve())}function Gs(){if(!m)return;let e=Fe();if(!e)return;let t=m.themePath,o=x(t,"modules");e.modules=[];for(let n of e.moduleOrder){let s=x(o,`${n}.module`);if(!J(s))continue;let i={moduleName:n,fieldsJson:ie(x(s,"fields.json")),metaJson:ie(x(s,"meta.json")),moduleHtml:ie(x(s,"module.html")),moduleCss:ie(x(s,"module.css")),moduleJs:ie(x(s,"module.js"))||void 0};i.fieldsJson&&i.moduleHtml&&e.modules.push(i)}if(e.templateFile){let n=x(t,e.templateFile);J(n)&&(e.template=ie(n))}_t(e),m.updatedAt=Date.now()}function Ca(){if(!m)return;let e=x(m.themePath,"templates","layouts","base.html");if(J(e))try{let t=fe(e,"utf-8");if(t.includes("template_js"))return;let o='{{ require_js(get_asset_url("../../js/main.js")) }}';t.includes(o)?t=t.replace(o,o+`
|
|
320
|
+
${k.bold("1.")} Go to HubSpot ${k.muted("\u2192")} Content ${k.muted("\u2192")} Landing Pages ${k.muted("\u2192")} Create
|
|
321
|
+
${k.bold("2.")} Choose your uploaded theme from the theme picker
|
|
322
|
+
${k.bold("3.")} Select the landing page template that was just created
|
|
323
|
+
${k.bold("4.")} Your converted modules will appear \u2014 drag them onto the page
|
|
324
|
+
${k.bold("5.")} Click each section to edit text, images, and colors
|
|
325
|
+
${k.bold("6.")} Upload images via File Manager ${k.muted("(Settings \u2192 Files)")}
|
|
326
|
+
${k.bold("7.")} Preview and publish!`,"What's next"),await ne({message:"Open HubSpot Landing Pages in your browser?"})){let c=e?`https://${a}/page-ui/${e}/management/pages/landing`:`https://${a}`;try{let d=process.platform;d==="darwin"?is("open",[c],{stdio:"ignore"}):d==="win32"?is("cmd",["/c","start","",c],{stdio:"ignore"}):is("xdg-open",[c],{stdio:"ignore"}),P("Opening HubSpot Landing Pages...")}catch{z(`Open this URL in your browser: ${k.info(c)}`)}}let l=[];if(o&&S(n)&&l.push({path:n,label:`Cloned source (${Wo(n)})`}),S(s)&&l.push({path:s,label:`Theme directory (${Wo(s)})`}),l.length>0&&await ne({message:"Clean up local working directories?"}))for(let d of l)try{ul(d.path,{recursive:!0,force:!0}),P(`Removed ${d.label}`)}catch{K(`Could not remove ${d.label} \u2014 delete manually if needed.`)}await ce(`Thanks for using hub${k.vibes("Vibes")}! ${k.vibes("~")}`)}ee();async function Ko(){Ee();let t=await cn(),e=await dn();Y({lastSourcePath:e.sourceDir});let n=await fn();Y({lastThemePath:n.themePath}),await wn({aiEngine:t.aiEngine,model:t.model,sourceDir:e.sourceDir,themePath:n.themePath}),await gt(n.themePath),await Bo({portalId:t.portalId,sourceDir:e.sourceDir,themePath:n.themePath,wasCloned:e.wasCloned})}f();async function Vo(){Ee(),await cn()}f();ee();async function Yo(){Ee();let t=E();t.aiEngine||(U("AI engine not configured. Run `vibespot init` first or use the full wizard with `vibespot`."),process.exit(1));let e=await dn(),n=await fn();await wn({aiEngine:t.aiEngine,sourceDir:e.sourceDir,themePath:n.themePath})}f();ee();async function zo(){Ee();let t=E();if(t.lastThemePath)if(await ne({message:`Upload from ${t.lastThemePath}?`}))await gt(t.lastThemePath);else{let n=await Ae({message:"Path to your HubSpot theme directory:",placeholder:"./my-theme"});await gt(n)}else{let e=await Ae({message:"Path to your HubSpot theme directory:",placeholder:"./my-theme",validate:n=>n.trim()?void 0:"Path is required"});await gt(e)}}f();ee();async function qo(){Ee(),await le("Environment Diagnostics");let t=0,e=kt();e.found?sn(e.version)?P(`Node.js v${e.version}`):(K(`Node.js v${e.version} \u2014 too old (need 18+)`),z(" Update at https://nodejs.org"),t++):(U("Node.js \u2014 not installed"),z(" Install from https://nodejs.org"),t++);let n=$t();n.found?P(`Git ${n.version}`):(U("Git \u2014 not installed"),z(" Install from https://git-scm.com"),t++);let s=Me();if(!s.found)K("HubSpot CLI \u2014 not installed (only needed for deployment)"),z(" Install: npm install -g @hubspot/cli");else if(!io(s.version))K(`HubSpot CLI v${s.version} \u2014 too old (need v8+)`),z(" Update: npm install -g @hubspot/cli@latest"),t++;else{P(`HubSpot CLI v${s.version}`);let p=Ne();p.authenticated?P(`HubSpot portal${p.portalName?`: ${p.portalName}`:""} (ID: ${p.portalId})`):(K("HubSpot \u2014 not authenticated"),z(" Run: hs init"))}let o=Tt();o.found?P(`Claude Code ${o.version} at ${o.path}`):z(k.muted("Claude Code \u2014 not installed"));let i=Mt();i.found?P(`Gemini CLI ${i.version} at ${i.path}`):z(k.muted("Gemini CLI \u2014 not installed"));let a=Nt();a.found?P(`OpenAI Codex ${a.version} at ${a.path}`):z(k.muted("OpenAI Codex \u2014 not installed"));let r=E(),l=!!(r.anthropicApiKey||process.env.ANTHROPIC_API_KEY),c=!!(r.openaiApiKey||process.env.OPENAI_API_KEY),d=!!(r.geminiApiKey||process.env.GEMINI_API_KEY||process.env.GOOGLE_AI_API_KEY);l?P("Anthropic API key configured"):z(k.muted("Anthropic API key \u2014 not set")),c?P("OpenAI API key configured"):z(k.muted("OpenAI API key \u2014 not set")),d?P("Google AI API key configured"):z(k.muted("Google AI API key \u2014 not set"));let u={"claude-code":"Claude Code",api:"Anthropic API","anthropic-api":"Anthropic API","openai-api":"OpenAI API","gemini-api":"Gemini API","gemini-cli":"Gemini CLI","codex-cli":"OpenAI Codex"};r.aiEngine&&P(`AI engine: ${u[r.aiEngine]||r.aiEngine}`),r.lastThemePath&&z(k.muted(`Last theme: ${r.lastThemePath}`)),!o.found&&!i.found&&!a.found&&!l&&!c&&!d&&(K("No AI engine available"),z(" Fastest: Set an API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY)"),z(" Or install: Claude Code \u2014 https://claude.ai/code"),z(" Gemini CLI \u2014 https://github.com/google-gemini/gemini-cli"),z(" Codex CLI \u2014 https://github.com/openai/codex"),t++),console.log(),t===0?await ce("Everything looks good!"):await ce(k.warn(`${t} issue${t>1?"s":""} found \u2014 see above`))}f();import{join as Vn}from"path";import{existsSync as Id}from"fs";import{execFileSync as Bs}from"child_process";import Yn from"chalk";f();import{createServer as yd}from"http";import{readFileSync as Us,existsSync as Ws}from"fs";import{join as Kn,extname as ma}from"path";import{createHash as bd}from"crypto";import{WebSocketServer as Sd}from"ws";f();f();f();f();import{readFileSync as Be,readdirSync as ps,existsSync as pe,writeFileSync as En,mkdirSync as fi,rmSync as cs,renameSync as ds}from"fs";import{join as oe,dirname as vl}from"path";import{homedir as wl}from"os";f();import{existsSync as We,writeFileSync as ml,mkdirSync as pl}from"fs";import{join as Re}from"path";function Qo(t){return/^[0-9a-f]{4,40}$/i.test(t)}var kn=null;function ke(){return kn!==null||(kn=$("git --version").success),kn}function $n(t){if(!ke())return!1;if(We(Re(t,".git")))return Xo(t),!0;let e=$("git init",{cwd:t});return e.success?(fl(t),Xo(t),$("git add -A",{cwd:t}),$('git commit -m "Initial theme"',{cwd:t}),!0):(console.warn(`[project-git] git init failed in ${t}: ${e.stderr}`),!1)}function Xo(t){let e=Re(t,".vibespot");We(e)||pl(e,{recursive:!0})}function fl(t){let e=Re(t,".gitignore");ml(e,[".vibespot/","node_modules/",""].join(`
|
|
327
|
+
`),"utf-8")}function rs(t,e){if(!ke()||!We(Re(t,".git"))||($("git add -A",{cwd:t}),$("git diff --cached --quiet",{cwd:t}).success))return null;let s=e.length>72?e.slice(0,69)+"...":e,o=$(`git commit -m "${s.replace(/"/g,'\\"')}"`,{cwd:t});if(!o.success)return console.warn(`[project-git] commit failed: ${o.stderr}`),null;let i=$("git rev-parse --short HEAD",{cwd:t});return i.success?i.stdout:null}function Zo(t,e,n,s){if(!ke()||!We(Re(t,".git")))return null;for(let u of s){let p=Re(t,u);We(p)&&$(`git add "${u}"`,{cwd:t})}if($("git diff --cached --quiet",{cwd:t}).success)return null;let i=`[${e}] `,a=72-i.length,r=n.length>a?n.slice(0,a-3)+"...":n,l=i+r,c=$(`git commit -m "${l.replace(/"/g,'\\"')}"`,{cwd:t});if(!c.success)return console.warn(`[project-git] template commit failed: ${c.stderr}`),null;let d=$("git rev-parse --short HEAD",{cwd:t});return d.success?d.stdout:null}function ei(t,e=50){if(!ke())return[];if(!We(Re(t,".git")))return[];let n=$(`git log --pretty=format:"%h|%H|%s|%at" -n ${e}`,{cwd:t});if(!n.success||!n.stdout.trim())return[];let s=[];for(let o of n.stdout.split(`
|
|
328
|
+
`)){let i=o.split("|");if(i.length<4)continue;let a=parseInt(i[3],10)*1e3;s.push({hash:i[0],fullHash:i[1],message:i[2],timestamp:a,date:new Date(a).toISOString()})}return s}function ti(t,e,n=50){if(!ke())return[];if(!We(Re(t,".git")))return[];let s=e.replace(/[[\]\\]/g,"\\$&"),o=$(`git log --grep="\\[${s}\\]" --pretty=format:"%h|%H|%s|%at" -n ${n}`,{cwd:t});if(!o.success||!o.stdout.trim())return[];let i=[];for(let a of o.stdout.split(`
|
|
329
|
+
`)){let r=a.split("|");if(r.length<4)continue;let l=parseInt(r[3],10)*1e3;i.push({hash:r[0],fullHash:r[1],message:r[2],timestamp:l,date:new Date(l).toISOString()})}return i}function ni(t,e){if(!ke())return{success:!1,error:"Git not available"};if(!We(Re(t,".git")))return{success:!1,error:"Not a git repo"};if(!Qo(e))return{success:!1,error:"Invalid commit hash"};let n=$(`git cat-file -t ${e}`,{cwd:t});if(!n.success||n.stdout.trim()!=="commit")return{success:!1,error:`Commit ${e} not found`};let s=$(`git log --format="%s" -1 ${e}`,{cwd:t}),o=s.success?s.stdout:e,i=$(`git checkout ${e} -- .`,{cwd:t});if(!i.success)return{success:!1,error:`Checkout failed: ${i.stderr}`};let a=`Rollback to: ${o}`.slice(0,72);return $(`git commit -m "${a.replace(/"/g,'\\"')}"`,{cwd:t}),{success:!0}}function si(t,e,n,s){if(!ke())return{success:!1,error:"Git not available"};if(!We(Re(t,".git")))return{success:!1,error:"Not a git repo"};if(!Qo(n))return{success:!1,error:"Invalid commit hash"};let o=$(`git cat-file -t ${n}`,{cwd:t});if(!o.success||o.stdout.trim()!=="commit")return{success:!1,error:`Commit ${n} not found`};let i=$(`git log --format="%s" -1 ${n}`,{cwd:t}),a=i.success?i.stdout:n,r=0;for(let d of s)$(`git checkout ${n} -- "${d}"`,{cwd:t}).success&&r++;if(r===0)return{success:!1,error:"No files could be restored from that commit"};$("git add -A",{cwd:t});let c=`${`[${e}] `}Rollback to: ${a}`.slice(0,72);return $(`git commit -m "${c.replace(/"/g,'\\"')}"`,{cwd:t}),{success:!0}}f();f();import{readFileSync as gl,existsSync as hl,writeFileSync as yl,mkdirSync as bl}from"fs";import{join as as}from"path";function nt(t){let e=v();e&&(e.modules=t.modules,e.moduleOrder=t.moduleOrder,e.sharedCss=t.sharedCss,e.sharedJs=t.sharedJs,e.template=t.template,e.messages=t.messages)}function Ye(){let t=v();if(!t)return;let e=Se();e&&(e.modules=t.modules,e.moduleOrder=t.moduleOrder,e.sharedCss=t.sharedCss,e.sharedJs=t.sharedJs,e.template=t.template,e.messages=t.messages)}function ze(t,e,n){let s=v();if(!s)return;let o={role:t,content:e,timestamp:Date.now()};n&&(o.pipeline=n),s.messages.push(o),s.updatedAt=Date.now(),Ye(),Sl()}function oi(t){let e=v();e&&(e.assets||(e.assets=[]),e.assets.push(t),e.updatedAt=Date.now(),j())}function Oe(t){let e=v();if(e){if(t.sharedCss!==void 0&&(e.sharedCss=t.sharedCss),t.sharedJs!==void 0&&(e.sharedJs=t.sharedJs),t.template!==void 0&&(e.template=t.template),t.modules)for(let n of t.modules){let s=n.moduleName.toLowerCase(),o=e.modules.findIndex(i=>i.moduleName.toLowerCase()===s);o>=0?e.modules[o]=n:(e.modules.push(n),e.moduleOrder.some(i=>i.toLowerCase()===s)||e.moduleOrder.push(n.moduleName))}e.updatedAt=Date.now(),Ye()}}function ht(t){let e=v();e&&(e.moduleOrder=t,e.updatedAt=Date.now(),Ye())}function ii(t){let e=v();e&&(e.modules=e.modules.filter(n=>n.moduleName!==t),e.moduleOrder=e.moduleOrder.filter(n=>n!==t),e.updatedAt=Date.now(),Ye())}function ri(t){let e=v();e&&(e.moduleOrder=e.moduleOrder.filter(n=>n!==t),e.updatedAt=Date.now(),Ye())}function ai(t,e,n){let s=v();if(!s)return;let o=s.modules.find(i=>i.moduleName===t);if(o)try{let i=JSON.parse(o.fieldsJson);ci(i,e,n),o.fieldsJson=JSON.stringify(i,null,2),s.updatedAt=Date.now(),Ye()}catch{}}function ue(){let t=v();if(!t)return[];let e=[];for(let n of t.moduleOrder){let s=t.modules.find(o=>o.moduleName===n);s&&e.push(s)}for(let n of t.modules)t.moduleOrder.includes(n.moduleName)||e.push(n);return e}function Sl(){let t=v();if(t)try{let e=as(t.themePath,".vibespot");bl(e,{recursive:!0});let n={sessionId:t.id,themeName:t.themeName,messages:t.messages,updatedAt:Date.now()};yl(as(e,"chat.json"),JSON.stringify(n,null,2),"utf-8")}catch{}}function li(t){let e=as(t,".vibespot","chat.json");if(!hl(e))return[];try{let n=JSON.parse(gl(e,"utf-8"));return Array.isArray(n.messages)?n.messages:[]}catch{return[]}}function ci(t,e,n){let s=e.split("."),o=s[0],i=t.find(a=>a.name===o);i&&(s.length===1?i.default=n:i.children&&ci(i.children,s.slice(1).join("."),n))}function Tn(t){if(t.templates&&t.templates.length>0)return;if(!t.modules||t.modules.length===0){t.templates=[],t.activeTemplateId="";return}let e=`lp-${t.themeName}`,n={id:e,label:`${t.themeName} Landing Page`,pageType:"landing_page",templateFile:`templates/lp-${t.themeName}.html`,modules:[...t.modules],moduleOrder:[...t.moduleOrder],sharedCss:t.sharedCss||"",sharedJs:t.sharedJs||"",template:t.template||"",messages:[...t.messages]};t.templates=[n],t.activeTemplateId=e}function Se(){let t=v();return!t||!t.activeTemplateId||!t.templates?.length?null:t.templates.find(e=>e.id===t.activeTemplateId)||null}function ls(t){let e=v();if(!e)return!1;let n=e.templates.find(s=>s.id===t);return n?(e.activeTemplateId=t,nt(n),e.updatedAt=Date.now(),!0):!1}function di(t,e){let n=v();if(!n)throw new Error("No active session");let s=e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""),i=`${t==="blog_post"?"bp":t==="website_page"?"wp":t==="module_only"?"mo":"lp"}-${s}`,a={id:i,label:e,pageType:t,templateFile:t==="module_only"?"":`templates/${i}.html`,modules:[],moduleOrder:[],sharedCss:"",sharedJs:"",template:"",messages:[]};return n.templates.push(a),n.activeTemplateId=i,nt(a),n.updatedAt=Date.now(),a}function ui(t,e){let n=v();if(!n)return null;let s=n.templates.find(c=>c.id===t);if(!s)return null;let o=e||`${s.label} (Copy)`,i=o.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""),r=`${s.pageType==="blog_post"?"bp":s.pageType==="website_page"?"wp":s.pageType==="module_only"?"mo":"lp"}-${i}`,l={id:r,label:o,pageType:s.pageType,templateFile:s.pageType==="module_only"?"":`templates/${r}.html`,modules:s.modules.map(c=>({...c})),moduleOrder:[...s.moduleOrder],sharedCss:s.sharedCss,sharedJs:s.sharedJs,template:s.template,messages:[]};return n.templates.push(l),n.activeTemplateId=r,nt(l),n.updatedAt=Date.now(),l}function mi(t,e){let n=v();if(!n)return!1;let s=n.templates.find(o=>o.id===t);return s?(s.label=e,n.updatedAt=Date.now(),!0):!1}function pi(t){let e=v();if(!e)return!1;let n=e.templates.findIndex(s=>s.id===t);return n<0?!1:(e.templates.splice(n,1),e.activeTemplateId===t&&(e.templates.length>0?ls(e.templates[0].id):(e.activeTemplateId="",e.modules=[],e.moduleOrder=[],e.sharedCss="",e.sharedJs="",e.template="",e.messages=[])),e.updatedAt=Date.now(),!0)}function qe(){let t=v();if(!t)return[];let e=new Map;for(let n of t.templates)for(let s of n.modules){let o=e.get(s.moduleName);o?o.usedIn.push(n.label):e.set(s.moduleName,{module:s,usedIn:[n.label]})}return Array.from(e.values())}var Q=oe(wl(),".vibespot","sessions"),us=oe(Q,"_index.json"),yt=null;function Mn(){if(yt)return yt;try{return pe(us)?(yt=JSON.parse(Be(us,"utf-8")),yt):ms()}catch{return ms()}}function Nn(t){yt=t;try{fi(Q,{recursive:!0}),En(us,JSON.stringify(t),"utf-8")}catch{}}function ms(){if(!pe(Q))return[];let t=[];for(let e of ps(Q).filter(n=>n.endsWith(".json")&&n!=="_index.json"))try{let n=JSON.parse(Be(oe(Q,e),"utf-8")),s=n.templates||[];t.push({id:n.id,themeName:n.themeName,updatedAt:n.updatedAt,moduleCount:s.reduce((o,i)=>o+(i.modules?.length||0),0),templateCount:s.length})}catch{}return yt=t,Nn(t),t}function xl(t){let e=Mn(),n=t.templates||[],s={id:t.id,themeName:t.themeName,updatedAt:t.updatedAt,moduleCount:n.reduce((i,a)=>i+(a.modules?.length||0),0),templateCount:n.length},o=e.findIndex(i=>i.id===t.id);o>=0?e[o]=s:e.push(s),Nn(e)}function Cl(t){let e=Mn().filter(n=>n.id!==t);Nn(e)}function Al(t){let e=Mn().filter(n=>n.themeName!==t);Nn(e)}var fe=null;function v(){return fe}function Il(){return`vibe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function Ft(t,e){let n={id:Il(),themePath:t,themeName:e,templates:[],activeTemplateId:"",messages:[],modules:[],sharedCss:"",sharedJs:"",template:"",moduleOrder:[],createdAt:Date.now(),updatedAt:Date.now()};return fe=n,$n(t),n}function j(){if(!fe)return;fi(Q,{recursive:!0});let t=oe(Q,`${fe.id}.json`);En(t,JSON.stringify(fe,null,2),"utf-8"),xl(fe)}function _n(t){let e=oe(Q,t+".json");if(!pe(e))return null;try{let n=JSON.parse(Be(e,"utf-8"));return n.templates||(n.templates=[]),n.activeTemplateId||(n.activeTemplateId=""),Tn(n),fe=n,n}catch{return null}}function bt(){return pe(Q)?Mn():[]}function gi(t,e=!1){let n=oe(Q,t+".json"),s="";if(e)try{let o=JSON.parse(Be(n,"utf-8"));s=o.themeName||"",o.themePath&&pe(o.themePath)&&cs(o.themePath,{recursive:!0,force:!0})}catch{}else try{s=JSON.parse(Be(n,"utf-8")).themeName||""}catch{}try{pe(n)&&cs(n)}catch{}if(s&&pe(Q)){for(let o of ps(Q).filter(i=>i.endsWith(".json")&&i!=="_index.json"))try{JSON.parse(Be(oe(Q,o),"utf-8")).themeName===s&&cs(oe(Q,o))}catch{}Al(s)}else Cl(t);fe?.id===t&&(fe=null)}function hi(t,e){let n=oe(Q,t+".json");if(!pe(n))return{ok:!1,error:"Session not found"};let s;try{s=JSON.parse(Be(n,"utf-8"))}catch{return{ok:!1,error:"Failed to read session"}}let o=s.themeName;if(o===e)return{ok:!0};let i=s.themePath,a=oe(vl(i),e);if(pe(i)){if(pe(a))return{ok:!1,error:"A project with that name already exists"};try{ds(i,a)}catch(p){return{ok:!1,error:`Failed to rename folder: ${p instanceof Error?p.message:String(p)}`}}let r=oe(a,"css",`${o}-theme.css`),l=oe(a,"css",`${e}-theme.css`);if(pe(r))try{ds(r,l)}catch{}let c=oe(a,"js",`${o}-animations.js`),d=oe(a,"js",`${e}-animations.js`);if(pe(c))try{ds(c,d)}catch{}let u=oe(a,"theme.json");if(pe(u))try{let p=JSON.parse(Be(u,"utf-8"));p.label=e,p.name=e,En(u,JSON.stringify(p,null,2),"utf-8")}catch{}}if(pe(Q))for(let r of ps(Q).filter(l=>l.endsWith(".json")&&l!=="_index.json"))try{let l=JSON.parse(Be(oe(Q,r),"utf-8"));l.themeName===o&&(l.themeName=e,l.themePath=a,l.updatedAt=Date.now(),En(oe(Q,r),JSON.stringify(l,null,2),"utf-8"))}catch{}return fe&&fe.themeName===o&&(fe.themeName=e,fe.themePath=a,fe.updatedAt=Date.now()),ms(),{ok:!0}}f();import{readFileSync as fs,readdirSync as Dt,existsSync as ve,writeFileSync as Ce,mkdirSync as Jt,rmSync as yi}from"fs";import{join as N}from"path";function ae(t){try{return fs(t,"utf-8")}catch{return""}}function kl(t){let e=[];for(let n of t.moduleOrder){let s=t.modules.find(o=>o.moduleName===n);s&&e.push(s)}for(let n of t.modules)t.moduleOrder.includes(n.moduleName)||e.push(n);return e}function $l(t,e){let n=ae(t);if(!n||e==="home.html"||e.endsWith("-listing.html"))return null;let s=e.replace(/\.html$/,""),o="landing_page";s.startsWith("bp-")?o="blog_post":s.startsWith("wp-")?o="website_page":s.startsWith("mo-")&&(o="module_only");let i=s,a=n.match(/<!--[\s\S]*?label:\s*"?([^"\n]+)"?\s*[\s\S]*?-->/);a&&(i=a[1].trim());let r=/dnd_module\s+path=["']\.\.\/modules\/(.+?)\.module["']/g,l=[],c;for(;(c=r.exec(n))!==null;)l.push(c[1]);return{id:s,label:i,pageType:o,moduleNames:l,templateContent:n,filename:e}}function Tl(t,e,n,s,o){if(!ve(t))return[];let i=[],a=Dt(t).filter(r=>r.endsWith(".html")&&r!=="home.html");for(let r of a){let l=N(t,r),c=$l(l,r);if(!c||c.moduleNames.length===0&&i.length>0)continue;let d=[],u=[];for(let p of c.moduleNames){let g=e.get(p);g&&(d.push(g),u.push(p))}i.push({id:c.id,label:c.label,pageType:c.pageType,templateFile:`templates/${c.filename}`,modules:d,moduleOrder:u,sharedCss:n,sharedJs:s,template:c.templateContent,messages:i.length===0?[...o]:[]})}return i}function Ht(t){let e=v();if(!e)return;let n=li(t);n.length>0&&e.messages.length===0&&(e.messages=n),$n(t);let s=N(t,"modules");if(!ve(s))return;let o=Dt(s,{withFileTypes:!0});for(let h of o){if(!h.isDirectory()||!h.name.endsWith(".module"))continue;let b=N(s,h.name),y=h.name.replace(/\.module$/,""),w={moduleName:y,fieldsJson:ae(N(b,"fields.json")),metaJson:ae(N(b,"meta.json")),moduleHtml:ae(N(b,"module.html")),moduleCss:ae(N(b,"module.css")),moduleJs:ae(N(b,"module.js"))||void 0};w.fieldsJson&&w.moduleHtml&&(e.modules.push(w),e.moduleOrder.push(y))}let i=N(t,"css"),a=N(t,"js"),r="",l="";if(ve(i)){let h=Dt(i).filter(b=>b.endsWith("-theme.css"));h.length>0&&(r=ae(N(i,h[0])),e.sharedCss=r)}if(ve(a)){let h=Dt(a).filter(b=>b.endsWith("-animations.js"));h.length>0&&(l=ae(N(a,h[0])),e.sharedJs=l)}let c=N(t,".vibespot","styleguide.md"),d=N(t,".vibespot","brandvoice.md");(ve(c)||ve(d))&&(e.brandAssets||(e.brandAssets={}),ve(c)&&(e.brandAssets.styleguide=ae(c)),ve(d)&&(e.brandAssets.brandvoice=ae(d)));let u=N(t,"templates"),p=new Map(e.modules.map(h=>[h.moduleName,h])),g=Tl(u,p,r,l,e.messages);if(g.length>0){e.templates=g,e.activeTemplateId=g[0].id;let h=g[0].moduleOrder;if(h.length>0){let b=new Set(e.moduleOrder),y=h.filter(w=>b.has(w));for(let w of e.moduleOrder)y.includes(w)||y.push(w);e.moduleOrder=y}nt(g[0])}else e.templates||(e.templates=[]),e.activeTemplateId||(e.activeTemplateId=""),Tn(e)}function Xe(){let t=v();if(!t)return;let e=t.themePath,n=new Map;if(t.templates.length>0)for(let l of t.templates)for(let c of l.modules)n.set(c.moduleName,c);for(let l of t.modules)n.set(l.moduleName,l);let s=N(e,"modules");Jt(s,{recursive:!0});for(let l of n.values())Jt(N(s,`${l.moduleName}.module`),{recursive:!0});for(let l of n.values()){let c=N(s,`${l.moduleName}.module`);Ce(N(c,"fields.json"),l.fieldsJson,"utf-8"),Ce(N(c,"meta.json"),l.metaJson,"utf-8"),Ce(N(c,"module.html"),l.moduleHtml,"utf-8"),Ce(N(c,"module.css"),l.moduleCss,"utf-8"),l.moduleJs&&Ce(N(c,"module.js"),l.moduleJs,"utf-8")}if(t.sharedCss){let l=N(e,"css");Jt(l,{recursive:!0}),Ce(N(l,`${t.themeName}-theme.css`),t.sharedCss,"utf-8")}if(t.sharedJs){let l=N(e,"js");Jt(l,{recursive:!0}),Ce(N(l,`${t.themeName}-animations.js`),t.sharedJs,"utf-8")}let o=N(e,"templates");Jt(o,{recursive:!0});let i=N(o,"home.html");(t.templates.length>0||t.modules.length>0)&&ve(i)&&yi(i,{force:!0});let r=new Set;if(t.templates.length>0)for(let l of t.templates){if(l.pageType==="module_only"||l.modules.length===0)continue;let c=l.template||Nl(l),d=bi(c,l.label,l.pageType),u=`${l.id}.html`;Ce(N(o,u),d,"utf-8"),r.add(u),l.pageType==="blog_post"&&(_l(o,l),r.add(`${l.id}-listing.html`))}else if(t.modules.length>0){let l=t.template||Pl(),c=bi(l,`${t.themeName} Landing Page`),d=`lp-${t.themeName}.html`;Ce(N(o,d),c,"utf-8"),r.add(d)}try{for(let l of Dt(o))l.startsWith("lp-")&&l.endsWith(".html")&&!r.has(l)&&yi(N(o,l),{force:!0})}catch{}El(),Ml()}function Si(){let t=v();t&&(t.modules=[],t.moduleOrder=[],t.sharedCss="",t.sharedJs="",t.template="",Ht(t.themePath),t.updatedAt=Date.now(),Ye())}function vi(){let t=v();if(!t)return;let e=Se();if(!e)return;let n=t.themePath,s=N(n,"modules");e.modules=[];for(let o of e.moduleOrder){let i=N(s,`${o}.module`);if(!ve(i))continue;let a={moduleName:o,fieldsJson:ae(N(i,"fields.json")),metaJson:ae(N(i,"meta.json")),moduleHtml:ae(N(i,"module.html")),moduleCss:ae(N(i,"module.css")),moduleJs:ae(N(i,"module.js"))||void 0};a.fieldsJson&&a.moduleHtml&&e.modules.push(a)}if(e.templateFile){let o=N(n,e.templateFile);ve(o)&&(e.template=ae(o))}nt(e),t.updatedAt=Date.now()}function El(){let t=v();if(!t)return;let e=N(t.themePath,"templates","layouts","base.html");if(ve(e))try{let n=fs(e,"utf-8");if(n.includes("template_js"))return;let s='{{ require_js(get_asset_url("../../js/main.js")) }}';n.includes(s)?n=n.replace(s,s+`
|
|
330
330
|
{% if template_js %}
|
|
331
331
|
{{ require_js(get_asset_url(template_js)) }}
|
|
332
|
-
{% endif %}`):
|
|
332
|
+
{% endif %}`):n=n.replace("{{ standard_footer_includes }}",`{% if template_js %}
|
|
333
333
|
{{ require_js(get_asset_url(template_js)) }}
|
|
334
334
|
{% endif %}
|
|
335
|
-
{{ standard_footer_includes }}`),
|
|
336
|
-
templateType: ${
|
|
335
|
+
{{ standard_footer_includes }}`),Ce(e,n,"utf-8")}catch{}}function Ml(){let t=v();if(!t)return;let e=N(t.themePath,"theme.json");if(ve(e))try{let n=JSON.parse(fs(e,"utf-8"));n.label=t.themeName,n.name=t.themeName,Ce(e,JSON.stringify(n,null,2),"utf-8")}catch{}}function bi(t,e,n="landing_page"){return t.includes("templateType")?t:`<!--
|
|
336
|
+
templateType: ${n==="blog_post"?"blog_post":"page"}
|
|
337
337
|
isAvailableForNewContent: true
|
|
338
|
-
label: "${
|
|
338
|
+
label: "${e}"
|
|
339
339
|
-->
|
|
340
|
-
`+
|
|
341
|
-
{% dnd_module path="../modules/${
|
|
340
|
+
`+t}function Nl(t){if(t.modules.length===0)return"";let n=v().themeName,o=kl(t).map(a=>` {% dnd_section padding={"top":"0","bottom":"0","left":"0","right":"0"}, full_width=true %}
|
|
341
|
+
{% dnd_module path="../modules/${a.moduleName}.module" %}
|
|
342
342
|
{% end_dnd_module %}
|
|
343
343
|
{% end_dnd_section %}`).join(`
|
|
344
344
|
|
|
345
345
|
`);return`<!--
|
|
346
|
-
templateType: ${
|
|
346
|
+
templateType: ${t.pageType==="blog_post"?"blog_post":"page"}
|
|
347
347
|
isAvailableForNewContent: true
|
|
348
|
-
label: "${
|
|
348
|
+
label: "${t.label}"
|
|
349
349
|
-->
|
|
350
350
|
{% extends "./layouts/base.html" %}
|
|
351
351
|
|
|
352
|
-
{% set template_css = "../../css/${
|
|
353
|
-
{% set template_js = "../../js/${
|
|
352
|
+
{% set template_css = "../../css/${n}-theme.css" %}
|
|
353
|
+
{% set template_js = "../../js/${n}-animations.js" %}
|
|
354
354
|
|
|
355
355
|
{% block header %}
|
|
356
356
|
{% endblock header %}
|
|
357
357
|
|
|
358
358
|
{% block body %}
|
|
359
|
-
<div class="${
|
|
360
|
-
{% dnd_area "main_content" label="${
|
|
359
|
+
<div class="${n}-page">
|
|
360
|
+
{% dnd_area "main_content" label="${t.label}" %}
|
|
361
361
|
|
|
362
|
-
${
|
|
362
|
+
${o}
|
|
363
363
|
|
|
364
364
|
{% end_dnd_area %}
|
|
365
365
|
</div>
|
|
366
|
-
{{ require_js(get_asset_url("../../js/${
|
|
366
|
+
{{ require_js(get_asset_url("../../js/${n}-animations.js")) }}
|
|
367
367
|
{% endblock body %}
|
|
368
368
|
|
|
369
369
|
{% block footer %}
|
|
370
370
|
{% endblock footer %}
|
|
371
|
-
`}function
|
|
371
|
+
`}function _l(t,e){let n=`<!--
|
|
372
372
|
templateType: blog_listing
|
|
373
373
|
isAvailableForNewContent: true
|
|
374
|
-
label: "${
|
|
374
|
+
label: "${e.label} - Listing"
|
|
375
375
|
-->
|
|
376
376
|
{% extends "./layouts/base.html" %}
|
|
377
377
|
|
|
@@ -393,8 +393,8 @@ ${n}
|
|
|
393
393
|
{% endif %}
|
|
394
394
|
</div>
|
|
395
395
|
{% endblock body %}
|
|
396
|
-
`;
|
|
397
|
-
{% dnd_module path="../modules/${
|
|
396
|
+
`;Ce(N(t,`${e.id}-listing.html`),n,"utf-8")}function Pl(){let t=v();if(!t||t.modules.length===0)return"";let e=t.themeName,s=ue().map(o=>` {% dnd_section padding={"top":"0","bottom":"0","left":"0","right":"0"}, full_width=true %}
|
|
397
|
+
{% dnd_module path="../modules/${o.moduleName}.module" %}
|
|
398
398
|
{% end_dnd_module %}
|
|
399
399
|
{% end_dnd_section %}`).join(`
|
|
400
400
|
|
|
@@ -415,7 +415,7 @@ ${n}
|
|
|
415
415
|
<div class="${e}-page">
|
|
416
416
|
{% dnd_area "main_content" label="${e} Landing Page" %}
|
|
417
417
|
|
|
418
|
-
${
|
|
418
|
+
${s}
|
|
419
419
|
|
|
420
420
|
{% end_dnd_area %}
|
|
421
421
|
</div>
|
|
@@ -424,15 +424,15 @@ ${o}
|
|
|
424
424
|
|
|
425
425
|
{% block footer %}
|
|
426
426
|
{% endblock footer %}
|
|
427
|
-
`}
|
|
428
|
-
`),
|
|
429
|
-
`),
|
|
427
|
+
`}f();f();function Pn(t){let e={};for(let n of t)n.type==="group"&&n.occurrence&&Array.isArray(n.default)?e[n.name]=n.default:n.type==="group"&&n.children?e[n.name]=Pn(n.children):e[n.name]=n.default??"";return e}function hs(t,e){let n=t;return n=Gl(n),n=Ai(n,e),n=Ii(n,e),n=ki(n,e),n=Wl(n),n}function ys(t){let e=[t.sharedCss||"",...t.moduleCssArray].filter(Boolean).map(o=>`<style>${o}</style>`).join(`
|
|
428
|
+
`),n=[t.sharedJs||"",...t.moduleJsArray].filter(Boolean).map(o=>`<script>${o}</script>`).join(`
|
|
429
|
+
`),s=t.renderedModules.join(`
|
|
430
430
|
`);return`<!DOCTYPE html>
|
|
431
431
|
<html lang="en">
|
|
432
432
|
<head>
|
|
433
433
|
<meta charset="utf-8">
|
|
434
434
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
435
|
-
${
|
|
435
|
+
${e}
|
|
436
436
|
<style>
|
|
437
437
|
html{scroll-behavior:smooth}
|
|
438
438
|
.vsp-img-wrap{position:relative;display:inline-block}
|
|
@@ -440,8 +440,8 @@ html{scroll-behavior:smooth}
|
|
|
440
440
|
</style>
|
|
441
441
|
</head>
|
|
442
442
|
<body>
|
|
443
|
+
${s}
|
|
443
444
|
${n}
|
|
444
|
-
${o}
|
|
445
445
|
<script>
|
|
446
446
|
// Anchor link handler \u2014 smooth scroll to module sections
|
|
447
447
|
document.addEventListener('click',function(e){
|
|
@@ -471,7 +471,11 @@ document.querySelectorAll('img').forEach(function(img){
|
|
|
471
471
|
});
|
|
472
472
|
</script>
|
|
473
473
|
</body>
|
|
474
|
-
</html>`}var
|
|
474
|
+
</html>`}var Rl=/\{%[-\s]*require_(css|js)\b.*?%\}/gs,Ol=/\{%[-\s]*end_require_(css|js)\s*%\}/gs,jl=/\{\{[-\s]*require_(css|js)\(.*?\)\s*\}\}/gs,wi=/\{\{[-\s]*get_asset_url\(["'](?:[^"'\/]+\/)?assets\/(.*?)["']\)\s*\}\}/gs,xi=/\{\{[-\s]*get_asset_url\(.*?\)\s*\}\}/gs,Fl=/\{%[-\s]*(end_)?(dnd_area|dnd_section|dnd_column|dnd_row|dnd_module)\b.*?%\}/gs,Jl=/\{%[-\s]*module\b.*?%\}/gs,Dl=/\{%[-\s]*(extends|block|endblock|set)\b.*?%\}/gs,Hl=/\{#.*?#\}/gs,Ll=/\{\{[-\s]*content\.\w+.*?\}\}/gs,gs=/\{%[-\s]*if\s+(.*?)\s*-?%\}([\s\S]*?)\{%[-\s]*endif\s*-?%\}/g;function Gl(t){return t=t.replace(Rl,""),t=t.replace(Ol,""),t=t.replace(jl,""),wi.lastIndex=0,t=t.replace(wi,(e,n)=>`/theme-assets/${n}`),xi.lastIndex=0,t=t.replace(xi,""),t=t.replace(Fl,""),t=t.replace(Jl,""),t=t.replace(Dl,""),t=t.replace(Hl,""),t=t.replace(Ll,""),t}function Ai(t,e){let n=t,s=0;for(;s<30;){s++;let o=Ul(n);if(!o)break;let{varName:i,iterExpr:a,body:r,start:l,end:c}=o,d=Bl(a,e),u="";Array.isArray(d)&&(u=d.map((p,g)=>{let h={...e,[i]:p,loop:{index:g+1,index0:g,first:g===0,last:g===d.length-1,length:d.length}},b=Ai(r,h);return b=Ii(b,h),b=ki(b,h),b}).join("")),n=n.slice(0,l)+u+n.slice(c)}return n}function Ul(t){let e=/\{%[-\s]*for\s+(\w+)\s+in\s+([\w.]+(?:\([^)]*\))?(?:\|[\w(),"' ]+)*)\s*-?%\}/g,n=/\{%[-\s]*(for\s|endfor)\s*.*?-?%\}/g,s=e.exec(t);if(!s)return null;let o=s[1],i=s[2],a=s.index+s[0].length;n.lastIndex=a;let r=1,l;for(;(l=n.exec(t))!==null;)if(l[1].startsWith("for"))r++;else if(r--,r===0){let c=t.slice(a,l.index);return{varName:o,iterExpr:i,body:c,start:s.index,end:l.index+l[0].length}}return null}function Ii(t,e){let n=t,s=0;for(;gs.test(n)&&s<50;)s++,n=n.replace(gs,(o,i,a)=>{let r=a.split(/\{%[-\s]*else\s*-?%\}/),l=r[0],c=r[1]||"",d=l.split(/\{%[-\s]*elif\s+(.*?)\s*-?%\}/);if(d.length>1){if(St(i,e))return d[0];for(let u=1;u<d.length;u+=2){let p=d[u],g=d[u+1]||"";if(St(p,e))return g}return c}return St(i,e)?l:c}),gs.lastIndex=0;return n}function ki(t,e){return t.replace(/\{\{[-\s]*(.*?)[-\s]*\}\}/g,(n,s)=>{let i=s.trim().split("|"),a=i[0].trim(),r=st(e,a);for(let c=1;c<i.length;c++)r=Ti(r,i[c].trim());if(r==null)return"";if(typeof r=="object")return JSON.stringify(r);let l=String(r);return l=l.replace(/\\n/g," ").replace(/\n/g," "),l})}function Wl(t){return t=t.replace(/\{%.*?%\}/gs,""),t=t.replace(/\{\{.*?\}\}/gs,""),t}function Bl(t,e){let n=t.match(/^range\(\s*(.+?)\s*,\s*(.+?)\s*\)$/);if(n){let o=Ci(n[1],e),i=Ci(n[2],e),a=[];for(let r=o;r<i;r++)a.push(r);return a}let s=t.match(/^(.+?)\|split\(['"](.+?)['"]\)$/);if(s){let o=st(e,s[1].trim());return typeof o=="string"?o.split(s[2]):[]}return st(e,t)}function Ci(t,e){let s=t.trim().split("|"),o=s[0].trim();if(!isNaN(Number(o)))return Number(o);let i=st(e,o);for(let a=1;a<s.length;a++)i=Ti(i,s[a].trim());return Number(i)||0}function st(t,e){let n=e.split("."),s=t;for(let o of n){if(s==null||typeof s!="object")return;s=s[o]}return s}function St(t,e){let n=t.trim();if(n.startsWith("not "))return!St(n.slice(4),e);if(n.includes(" and "))return n.split(" and ").every(i=>St(i,e));if(n.includes(" or "))return n.split(" or ").some(i=>St(i,e));let s=n.match(/^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);if(s){let i=st(e,s[1].trim()),a=s[2],r=s[3].trim();switch(typeof r=="string"&&r.startsWith('"')&&r.endsWith('"')||typeof r=="string"&&r.startsWith("'")&&r.endsWith("'")?r=r.slice(1,-1):isNaN(Number(r))?r=st(e,r):r=Number(r),a){case"==":return i==r;case"!=":return i!=r;case">":return Number(i)>Number(r);case"<":return Number(i)<Number(r);case">=":return Number(i)>=Number(r);case"<=":return Number(i)<=Number(r)}}let o=st(e,n);return $i(o)}function $i(t){return!(t==null||t===""||t===0||t===!1||Array.isArray(t)&&t.length===0)}function Ti(t,e){let n=t==null?"":String(t),s=e.match(/^(\w+)\((.*)\)$/),o=s?s[1]:e,i=s?s[2].replace(/^["']|["']$/g,""):void 0;switch(o){case"escape":case"e":return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");case"lower":return n.toLowerCase();case"upper":return n.toUpperCase();case"capitalize":return n.charAt(0).toUpperCase()+n.slice(1);case"trim":return n.trim();case"truncate":if(i){let a=parseInt(i,10);return n.length>a?n.slice(0,a)+"...":n}return n;case"default":return $i(t)?t:i??"";case"length":return Array.isArray(t)?t.length:n.length;case"join":return Array.isArray(t)?t.join(i??", "):n;case"int":case"float":return Number(n)||0;case"abs":return Math.abs(Number(n));case"round":return Math.round(Number(n));default:return t}}function Kl(t){if(!t)return{bg:"#0f0f14",surface:"#1a1a20",text:"#ffffff",textMuted:"#666",border:"#333"};let e=(r,l)=>{for(let c of r){let d=new RegExp(`${c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\s*:\\s*([^;})]+)`,"i"),u=t.match(d);if(u)return u[1].trim()}return l},n=e(["--bg","--background","--color-bg","--bg-primary","--body-bg"],"#0f0f14"),s=e(["--surface","--bg-secondary","--card-bg","--color-surface"],n),o=e(["--text","--color-text","--text-primary","--fg","--foreground"],"#ffffff"),i=e(["--text-muted","--text-secondary","--muted","--color-text-muted"],"#666"),a=e(["--border","--border-color","--color-border"],"#333");return{bg:n,surface:s,text:o,textMuted:i,border:a}}function Mi(){let t=v();if(!t)return Ei();let e=ue(),n=t.moduleOrder||[];if(e.length===0&&n.length===0)return Ei();let s=[],o=[],i=[],a=new Set;for(let c of e){if(c.moduleHtml.includes("dnd_area")||c.moduleHtml.includes("extends "))continue;let d;try{let g=JSON.parse(c.fieldsJson);d={module:Pn(g)}}catch{d={module:{}}}let u=hs(c.moduleHtml,d),p=c.moduleName.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");s.push(`<div class="vibespot-module" id="${p}" data-module="${c.moduleName}">${u}</div>`),a.add(c.moduleName),c.moduleCss&&o.push(c.moduleCss),c.moduleJs&&i.push(c.moduleJs)}let r=Kl(t.sharedCss);for(let c of n)if(!a.has(c)){let d=c.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");s.push(`<div class="vibespot-module vibespot-module--pending" id="${d}" data-module="${c}">
|
|
475
|
+
<div class="vibespot-placeholder">
|
|
476
|
+
<div class="vibespot-placeholder__name">${c}</div>
|
|
477
|
+
</div>
|
|
478
|
+
</div>`)}let l=`body{background:${r.bg}}.vibespot-placeholder{display:flex;align-items:center;justify-content:center;min-height:200px;padding:3rem;background:${r.surface};border:1px dashed ${r.border};border-radius:12px;margin:1rem 0}.vibespot-placeholder__name{font-size:1.5rem;font-weight:600;font-family:system-ui,sans-serif;color:${r.textMuted};letter-spacing:.5px;animation:vp-fade 2s ease-in-out infinite}@keyframes vp-fade{0%,100%{opacity:.3}50%{opacity:.8}}`;return ys({renderedModules:s,sharedCss:t.sharedCss,moduleCssArray:[l,...o],sharedJs:t.sharedJs,moduleJsArray:i})}function Ei(){return`<!DOCTYPE html>
|
|
475
479
|
<html lang="en">
|
|
476
480
|
<head>
|
|
477
481
|
<meta charset="utf-8">
|
|
@@ -522,7 +526,8 @@ document.querySelectorAll('img').forEach(function(img){
|
|
|
522
526
|
<div class="welcome__sub">Build Something Great</div>
|
|
523
527
|
</div>
|
|
524
528
|
</body>
|
|
525
|
-
</html>`}function
|
|
529
|
+
</html>`}function Ni(t){let e=v();if(!e)return"";let n;for(let i of e.templates)if(n=i.modules.find(a=>a.moduleName===t),n)break;if(n||(n=e.modules.find(i=>i.moduleName===t)),!n)return"";let s;try{let i=JSON.parse(n.fieldsJson);s={module:Pn(i)}}catch{s={module:{}}}let o=hs(n.moduleHtml,s);return ys({renderedModules:[`<div class="vibespot-module" data-module="${n.moduleName}">${o}</div>`],sharedCss:e.sharedCss,moduleCssArray:n.moduleCss?[n.moduleCss]:[],sharedJs:e.sharedJs,moduleJsArray:n.moduleJs?[n.moduleJs]:[]})}f();ee();import{execSync as Ps}from"child_process";f();f();import{appendFileSync as Vl,mkdirSync as Yl,readdirSync as zl,unlinkSync as ql}from"fs";import{join as ws}from"path";import{homedir as Xl}from"os";var Rn=ws(Xl(),".vibespot","logs"),Ql=7,Ss=!1,vs=!1;function Zl(){if(!Ss)try{Yl(Rn,{recursive:!0}),Ss=!0}catch{}}function ec(){if(!vs){vs=!0;try{let t=Date.now()-Ql*864e5;for(let e of zl(Rn)){if(!e.startsWith("vibespot-")||!e.endsWith(".log"))continue;let n=e.slice(9,19),s=new Date(n).getTime();if(s&&s<t)try{ql(ws(Rn,e))}catch{}}}catch{}}}function tc(){let e=new Date().toISOString().slice(0,10);return ws(Rn,`vibespot-${e}.log`)}function nc(){return new Date().toISOString().slice(11,23)}function bs(t,e){if(Zl(),!!Ss){vs||ec();try{Vl(tc(),`${nc()} ${t} ${e}
|
|
530
|
+
`)}catch{}}}var M={info(t,e,n){let s=n?`[${t}] ${e} ${JSON.stringify(n)}`:`[${t}] ${e}`;console.log(s),bs("INFO",s)},warn(t,e,n){let s=n?`[${t}] ${e} ${JSON.stringify(n)}`:`[${t}] ${e}`;console.warn(s),bs("WARN",s)},error(t,e,n){let s=n instanceof Error?n.message:n?String(n):"",o=s?`[${t}] ${e}: ${s}`:`[${t}] ${e}`;console.error(o),bs("ERROR",o)}};function je(t){try{return JSON.parse(t)}catch{}let e=t,n=-1;for(let s=0;s<20;s++)try{return JSON.parse(e)}catch(o){if(!(o instanceof SyntaxError))return null;let i=/position (\d+)/.exec(o.message);if(!i)return null;let a=parseInt(i[1],10);if(a<=n)return null;n=a;let r=Math.max(0,a-5),c=e.slice(r,a+1).lastIndexOf('"');if(c===-1)return null;let d=r+c;if(d>0&&e[d-1]==="\\")return null;e=e.slice(0,d)+'\\"'+e.slice(d+1)}return null}function Lt(t){let e=t.indexOf('"modules"');if(e===-1)return null;let n=t.indexOf("[",e);if(n===-1)return null;let s=-1,o=0,i=!1,a=!1;for(let d=n+1;d<t.length;d++){let u=t[d];if(a){a=!1;continue}if(u==="\\"){a=!0;continue}if(u==='"'){i=!i;continue}i||(u==="{"&&o++,u==="}"&&(o--,o===0&&(s=d)))}if(s===-1)return null;let l=t.slice(0,s+1)+"]}",c=l.trimStart().startsWith("{")?l:"{"+l;return je(c)}function xs(t){return{moduleName:String(t.moduleName||""),fieldsJson:typeof t.fieldsJson=="string"?t.fieldsJson:JSON.stringify(t.fieldsJson,null,2),metaJson:typeof t.metaJson=="string"?t.metaJson:JSON.stringify(t.metaJson,null,2),moduleHtml:String(t.moduleHtml||""),moduleCss:String(t.moduleCss||""),moduleJs:t.moduleJs?String(t.moduleJs):void 0}}function _i(t,e){let n=!1,s,o=/```vibespot-modules\s*\n?([\s\S]*?)```/g;for(;(s=o.exec(t))!==null;)try{M.info("parse","Found vibespot-modules block",{length:s[1].length});let i=je(s[1]);if(!i||typeof i!="object")throw M.warn("parse","tryParseJSON returned non-object",{result:typeof i}),new Error("Invalid JSON after repair");let a=i;a.modules&&Array.isArray(a.modules)&&(Oe({modules:a.modules.map(r=>xs(r)),sharedCss:a.sharedCss!==void 0?String(a.sharedCss):void 0,sharedJs:a.sharedJs!==void 0?String(a.sharedJs):void 0}),n=!0)}catch(i){M.warn("parse","Failed to parse vibespot-modules block",{error:i instanceof Error?i.message:String(i)})}if(!n){let i=/```(?:json)?\s*\n([\s\S]*?)```/g;for(;(s=i.exec(t))!==null;)if(s[1].includes('"modules"'))try{let a=je(s[1]);if(!a||typeof a!="object")throw new Error("Invalid JSON after repair");let r=a;r.modules&&Array.isArray(r.modules)&&(Oe({modules:r.modules.map(l=>xs(l)),sharedCss:r.sharedCss!==void 0?String(r.sharedCss):void 0,sharedJs:r.sharedJs!==void 0?String(r.sharedJs):void 0}),n=!0)}catch(a){M.warn("parse","Failed to parse JSON module block",{error:a instanceof Error?a.message:String(a)})}}if(!n&&(t.match(/```/g)||[]).length%2!==0&&t.includes('"modules"')){M.info("parse","Detected truncated response (odd fence count), attempting salvage");let a=t.lastIndexOf("```"),r=t.slice(a+3);r=r.replace(/^[\w-]*\s*\n?/,"");let l=Lt(r);if(l){let c=l;c.modules&&Array.isArray(c.modules)&&c.modules.length>0&&(M.info("parse","Salvaged modules from truncated response",{count:c.modules.length}),Oe({modules:c.modules.map(d=>xs(d)),sharedCss:c.sharedCss!==void 0?String(c.sharedCss):void 0,sharedJs:c.sharedJs!==void 0?String(c.sharedJs):void 0}),n=!0,e&&e("Response was truncated \u2014 some modules may be incomplete. Try sending your request again for the full set."))}}if(!n){M.info("parse","No modules applied",{responseLength:t.length,hasVibespot:t.includes("vibespot-modules"),hasModules:t.includes('"modules"'),fenceCount:(t.match(/```/g)||[]).length});let i=t.includes("vibespot-modules")||t.includes('"modules"'),a=/\bmodule|modul/i.test(t)&&(/\bcreated?\b|\berstellt\b|\bgenerat/i.test(t)||/\|.*\|.*\|/m.test(t));if(i||a){let r=i?"Module changes could not be applied \u2014 the AI response contained invalid JSON. Try sending your request again.":"The AI described modules but did not include the required structured data. Try sending your request again.";M.warn("parse",r),e&&e(r)}}}f();import{spawn as sc}from"child_process";ee();f();function vt(){let t=v();return t?{pageType:Se()?.pageType,brandAssets:t.brandAssets}:{}}function wt(t,e,n=!1,s,o){let i=`You are vibeSpot, an AI that builds HubSpot CMS landing pages from natural language descriptions.
|
|
526
531
|
|
|
527
532
|
## Your Role
|
|
528
533
|
You generate native HubSpot CMS modules directly from user descriptions. Every module you create is immediately compatible with HubSpot's drag-and-drop page editor.
|
|
@@ -564,8 +569,8 @@ NEVER respond with only a text summary. The vibespot-modules JSON block is manda
|
|
|
564
569
|
- NEVER use "name": "name" (reserved) \u2014 use "item_name" instead
|
|
565
570
|
- NEVER put literal \\n newline sequences in field default values \u2014 use plain text without line breaks
|
|
566
571
|
- Wrap style fields in a "styles" group with "tab": "STYLE"
|
|
567
|
-
- All CSS classes must use a unique prefix "${
|
|
568
|
-
- Use BEM naming: ${
|
|
572
|
+
- All CSS classes must use a unique prefix "${e}-" to avoid theme conflicts
|
|
573
|
+
- Use BEM naming: ${e}-module__element--modifier
|
|
569
574
|
- metaJson must include: host_template_types: ["PAGE"], is_available_for_new_content: true
|
|
570
575
|
- For repeater groups, use "occurrence": { "min": 0, "max": 100 } and iterate with {% for %}
|
|
571
576
|
- Color fields: type "color", default { "color": "#hex", "opacity": 100 }
|
|
@@ -574,9 +579,9 @@ NEVER respond with only a text summary. The vibespot-modules JSON block is manda
|
|
|
574
579
|
|
|
575
580
|
## Images & Media
|
|
576
581
|
- Users can upload images that get placed in the theme's assets/ folder automatically
|
|
577
|
-
- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url("${
|
|
578
|
-
- IMPORTANT: get_asset_url() paths must include the theme name prefix "${
|
|
579
|
-
- For background images with uploaded assets: style="background-image: url('{{ get_asset_url(\\"${
|
|
582
|
+
- When the user uploads an image and wants it on the page, reference it with: {{ get_asset_url("${e}/assets/filename.ext") }}
|
|
583
|
+
- IMPORTANT: get_asset_url() paths must include the theme name prefix "${e}/" because HubSpot resolves from the Design Manager root
|
|
584
|
+
- For background images with uploaded assets: style="background-image: url('{{ get_asset_url(\\"${e}/assets/filename.ext\\") }}')"
|
|
580
585
|
- For images without an uploaded asset, use image fields (type "image") with placehold.co defaults
|
|
581
586
|
- ALWAYS use image fields (type "image") so users can swap images in the page editor
|
|
582
587
|
- Use placehold.co URLs as defaults so the preview looks complete (e.g. https://placehold.co/800x600/1a1a2e/ffffff?text=Hero+Image)
|
|
@@ -601,25 +606,25 @@ The current template's modules are listed in page order in the user message. Thi
|
|
|
601
606
|
- **Rearrange**: When the user asks to reorder sections, include a "moduleOrder" array in the vibespot-modules JSON with the new sequence of module names.
|
|
602
607
|
- **Remove**: When the user asks to remove a section, omit it from the output.
|
|
603
608
|
- **Preserve**: Always include ALL modules you want to keep (modified + unchanged) in your output. Modules omitted from the output will be removed from the page.
|
|
604
|
-
- **Design consistency**: Match the existing theme's design language \u2014 reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`,a=
|
|
609
|
+
- **Design consistency**: Match the existing theme's design language \u2014 reuse the same CSS custom properties, class naming prefix, spacing scale, and typography.`,a=s?xo(s):"",r=a?`
|
|
605
610
|
|
|
606
611
|
## Page Type Context
|
|
607
|
-
${a}`:"",l="";if(
|
|
612
|
+
${a}`:"",l="";if(o?.styleguide&&(l+=`
|
|
608
613
|
|
|
609
614
|
## Brand Style Guide
|
|
610
|
-
${
|
|
615
|
+
${o.styleguide}`),o?.brandvoice&&(l+=`
|
|
611
616
|
|
|
612
617
|
## Brand Voice
|
|
613
|
-
${
|
|
618
|
+
${o.brandvoice}`),o?.humanify!==!1){let d=wo();d&&(l+=`
|
|
614
619
|
|
|
615
620
|
## Anti-AI Copy Rules (Humanify)
|
|
616
|
-
${
|
|
621
|
+
${d}`)}let c="\n\n## REMINDER \u2014 Output Format (CRITICAL)\nYour response MUST contain a ```vibespot-modules code block with the full JSON. Without this block, no modules will be created. Do NOT respond with only text, tables, or descriptions. The JSON block is mandatory.";return n?i+r+l+`
|
|
617
622
|
|
|
618
623
|
## HubSpot CMS Rules
|
|
619
|
-
${
|
|
624
|
+
${Ve()}
|
|
620
625
|
|
|
621
626
|
## Conversion Guide Reference
|
|
622
|
-
${
|
|
627
|
+
${t}`+c:i+r+l+`
|
|
623
628
|
|
|
624
629
|
## Design Quality
|
|
625
630
|
- Use modern, clean design with proper spacing and typography
|
|
@@ -644,118 +649,669 @@ When using scroll-animate classes (opacity: 0 \u2192 visible), you MUST include
|
|
|
644
649
|
This makes elements appear after 3 seconds if JS never adds the .visible class. Once JS runs normally and adds .visible, the animation is cancelled.
|
|
645
650
|
|
|
646
651
|
## Design Guide
|
|
647
|
-
${
|
|
652
|
+
${So()}
|
|
648
653
|
|
|
649
654
|
## Content & Copywriting Guide
|
|
650
|
-
${
|
|
655
|
+
${vo()}
|
|
651
656
|
|
|
652
657
|
## HubSpot CMS Rules
|
|
653
|
-
${
|
|
658
|
+
${Ve()}
|
|
654
659
|
|
|
655
660
|
## Conversion Guide Reference
|
|
656
|
-
${
|
|
661
|
+
${t}`+c}function Gt(){let t=v(),e=[],n=t.modules,s=n.length;if(s>0){e.push(`
|
|
657
662
|
|
|
658
663
|
## Page Narrative (module sequence)
|
|
659
|
-
`),
|
|
660
|
-
`);for(let r=0;r<
|
|
661
|
-
`);
|
|
664
|
+
`),e.push(`This template has ${s} module${s===1?"":"s"} in this order:
|
|
665
|
+
`);for(let r=0;r<s;r++)e.push(`${r+1}. ${n[r].moduleName}
|
|
666
|
+
`);e.push(`
|
|
662
667
|
When the user asks to modify this page, decide whether to MODIFY existing modules, ADD new ones at the right narrative position, REARRANGE the sequence, or REMOVE sections. Always include ALL modules you want to keep in your output.
|
|
663
|
-
`),
|
|
668
|
+
`),e.push(`
|
|
664
669
|
## Current Module State
|
|
665
|
-
`);for(let r=0;r<
|
|
666
|
-
### ${r+1}/${
|
|
667
|
-
`),
|
|
670
|
+
`);for(let r=0;r<s;r++){let l=n[r];e.push(`
|
|
671
|
+
### ${r+1}/${s}: ${l.moduleName}.module
|
|
672
|
+
`),e.push(`**fields.json:**
|
|
668
673
|
\`\`\`json
|
|
669
674
|
${l.fieldsJson}
|
|
670
675
|
\`\`\`
|
|
671
|
-
`),
|
|
676
|
+
`),e.push(`**module.html:**
|
|
672
677
|
\`\`\`html
|
|
673
678
|
${l.moduleHtml}
|
|
674
679
|
\`\`\`
|
|
675
|
-
`),
|
|
680
|
+
`),e.push(`**module.css:**
|
|
676
681
|
\`\`\`css
|
|
677
682
|
${l.moduleCss}
|
|
678
683
|
\`\`\`
|
|
679
|
-
`),l.moduleJs&&
|
|
684
|
+
`),l.moduleJs&&e.push(`**module.js:**
|
|
680
685
|
\`\`\`js
|
|
681
686
|
${l.moduleJs}
|
|
682
687
|
\`\`\`
|
|
683
|
-
`)}
|
|
688
|
+
`)}t.sharedCss&&e.push(`
|
|
684
689
|
### Shared CSS
|
|
685
690
|
\`\`\`css
|
|
686
|
-
${
|
|
691
|
+
${t.sharedCss}
|
|
687
692
|
\`\`\`
|
|
688
|
-
`),
|
|
693
|
+
`),t.sharedJs&&e.push(`
|
|
689
694
|
### Shared JS
|
|
690
695
|
\`\`\`js
|
|
691
|
-
${
|
|
696
|
+
${t.sharedJs}
|
|
692
697
|
\`\`\`
|
|
693
|
-
`)}let
|
|
698
|
+
`)}let o=qe(),i=new Set(t.modules.map(r=>r.moduleName)),a=o.filter(r=>!i.has(r.module.moduleName));if(a.length>0){e.push(`
|
|
694
699
|
|
|
695
700
|
## Available modules in this theme (reusable)
|
|
696
|
-
`);for(let r of a)
|
|
697
|
-
`);
|
|
701
|
+
`);for(let r of a)e.push(`- ${r.module.moduleName} (used in: ${r.usedIn.join(", ")})
|
|
702
|
+
`);e.push(`
|
|
698
703
|
The user can ask to reuse any of these modules by name.
|
|
699
|
-
`)}return
|
|
704
|
+
`)}return e.join("")}function Cs(t,e){let n=v(),s=n.messages.slice(-20);s.length>0&&s[s.length-1].role==="user"&&s[s.length-1].content===t&&(s=s.slice(0,-1));let o=s.map(d=>({role:d.role,content:d.content})),i=Gt(),a="";if(n.assets?.length){let d=n.assets.filter(u=>u.type==="image"&&u.usage==="asset");d.length>0&&(a=`
|
|
700
705
|
|
|
701
706
|
## Available Theme Assets
|
|
702
|
-
These images are in the theme's assets/ folder. Reference them with get_asset_url("${
|
|
703
|
-
${
|
|
704
|
-
`)}`)}let r=
|
|
707
|
+
These images are in the theme's assets/ folder. Reference them with get_asset_url("${n.themeName}/assets/filename"):
|
|
708
|
+
${d.map(u=>`- ${u.filename} (${u.originalName}) \u2192 get_asset_url("${n.themeName}/assets/${u.filename}")`).join(`
|
|
709
|
+
`)}`)}let r=t;i&&(r+=`
|
|
705
710
|
|
|
706
711
|
---
|
|
707
|
-
${i}`),a&&(r+=a);let l=
|
|
712
|
+
${i}`),a&&(r+=a),r+="\n\n---\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.";let l=e&&e.length>0;if(l)for(let d of e)d.type==="document"&&d.extractedText&&(r+=`
|
|
708
713
|
|
|
709
714
|
---
|
|
710
|
-
[Attached document: ${
|
|
711
|
-
${
|
|
715
|
+
[Attached document: ${d.originalName}]
|
|
716
|
+
${d.extractedText}`),d.type==="image"&&d.usage==="asset"&&d.assetPath&&(r+=`
|
|
712
717
|
|
|
713
|
-
[Uploaded image: ${
|
|
714
|
-
[Uploaded image: ${
|
|
718
|
+
[Uploaded image: ${d.originalName} \u2192 available as get_asset_url("${d.assetPath}")]`);let c=l?e.filter(d=>d.type==="image"&&d.base64):[];if(c.length>0){let d=[];for(let u of c)d.push({type:"image",source:{type:"base64",media_type:u.mimeType,data:u.base64}});d.push({type:"text",text:r}),o.push({role:"user",content:d})}else o.push({role:"user",content:r});return o}var As=null;async function oc(){return As||(As=(await import("@anthropic-ai/sdk")).default),As}function Pi(t){if(!t?.length)return"";let e=[];for(let n of t)n.type==="image"&&n.usage==="asset"&&n.assetPath&&e.push(`
|
|
719
|
+
[Uploaded image: ${n.originalName} \u2192 use get_asset_url("${n.assetPath}")]`),n.type==="document"&&n.extractedText&&e.push(`
|
|
715
720
|
|
|
716
721
|
---
|
|
717
|
-
[Attached document: ${
|
|
718
|
-
${
|
|
719
|
-
`);
|
|
722
|
+
[Attached document: ${n.originalName}]
|
|
723
|
+
${n.extractedText}`);return e.join("")}var me=["Analyzing your request...","Reading the conversion guide...","Planning module structure...","Generating HTML templates...","Writing CSS styles...","Creating field definitions...","Building module metadata...","Assembling theme assets...","Polishing the output...","Almost there \u2014 hang tight..."],Is=[10,20,40,60,120];async function Ri(t,e,n,s,o,i,a,r){let l=await oc(),c=new l({apiKey:e}),d=re(),p=v().modules.length>0,g=Cs(t,r),h=vt(),b=wt(d,n,p,h.pageType,h.brandAssets);M.info("anthropic","API call",{model:s,systemPromptLength:b.length,messageCount:g.length,messageRoles:g.map(y=>y.role),lastMessageLength:typeof g[g.length-1]?.content=="string"?g[g.length-1].content.length:"multimodal",conversionGuideLength:d.length});for(let y=0;;y++)try{let w="",I=0,T=i||(()=>{});T(me[0]);let J=setInterval(()=>{I++,T(me[Math.min(I,me.length-1)])},6e3);try{let D=c.messages.stream({model:s,max_tokens:48e3,system:b,messages:g});for await(let R of D)if(R.type==="content_block_delta"&&R.delta.type==="text_delta"){let L=R.delta.text;w+=L,o(L)}}finally{clearInterval(J)}a&&a(w);return}catch(w){let I=w.status,T=w.error?.type;if(!(I===429||T==="rate_limit_error"||w instanceof Error&&w.message.includes("429"))||y>=Is.length)throw w;let D=Is[y];M.warn("ai-engine",`Rate limited (429), attempt ${y+1}/${Is.length} \u2014 waiting ${D}s`),i&&i(`Rate limited by Anthropic API \u2014 retrying in ${D}s...`),await new Promise(R=>setTimeout(R,D*1e3)),i&&i("Retrying...")}}async function Oi(t,e,n,s,o,i,a,r){let l=re(),c=v().modules.length>0,d=Cs(t,r),u=vt(),p=d.map(D=>typeof D.content=="string"?D:{role:D.role,content:D.content.map(R=>R.type==="text"?{type:"text",text:R.text}:{type:"image_url",image_url:{url:`data:${R.source.media_type};base64,${R.source.data}`}})}),g=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({model:s,max_tokens:48e3,stream:!0,messages:[{role:"system",content:wt(l,n,c,u.pageType,u.brandAssets)},...p]})});if(!g.ok){let D=await g.text();throw new Error(`OpenAI API error (${g.status}): ${D}`)}let h=0,b=i||(()=>{});b(me[0]);let y=setInterval(()=>{h++,b(me[Math.min(h,me.length-1)])},6e3),w="",I=g.body.getReader(),T=new TextDecoder,J="";try{for(;;){let{done:D,value:R}=await I.read();if(D)break;J+=T.decode(R,{stream:!0});let L=J.split(`
|
|
724
|
+
`);J=L.pop()||"";for(let Z of L){if(!Z.startsWith("data: "))continue;let G=Z.slice(6).trim();if(G==="[DONE]")break;try{let O=JSON.parse(G).choices?.[0]?.delta?.content;O&&(w+=O,o(O))}catch{}}}}finally{clearInterval(y)}a&&a(w)}async function ji(t,e,n,s,o,i,a){let r=re(),l=v(),c=l.modules.length>0,d=Gt(),u=vt(),p=[];for(let G of l.messages.slice(-20))p.push({role:G.role==="assistant"?"model":"user",parts:[{text:G.content}]});let g=d?`${t}
|
|
720
725
|
|
|
721
726
|
---
|
|
722
|
-
${
|
|
727
|
+
${d}`:t;if(a?.length)for(let G of a)G.type==="document"&&G.extractedText&&(g+=`
|
|
723
728
|
|
|
724
729
|
---
|
|
725
|
-
[Attached document: ${
|
|
726
|
-
${
|
|
730
|
+
[Attached document: ${G.originalName}]
|
|
731
|
+
${G.extractedText}`),G.type==="image"&&G.usage==="asset"&&G.assetPath&&(g+=`
|
|
727
732
|
|
|
728
|
-
[Uploaded image: ${
|
|
729
|
-
`);
|
|
733
|
+
[Uploaded image: ${G.originalName} \u2192 available as get_asset_url("${G.assetPath}")]`);let h=[];if(a?.length)for(let G of a)G.type==="image"&&G.base64&&h.push({inlineData:{mimeType:G.mimeType,data:G.base64}});h.push({text:g}),p.push({role:"user",parts:h});let y=`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse&key=${e}`,w=await fetch(y,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({systemInstruction:{parts:[{text:wt(r,n,c,u.pageType,u.brandAssets)}]},contents:p,generationConfig:{maxOutputTokens:48e3}})});if(!w.ok){let G=await w.text();throw new Error(`Gemini API error (${w.status}): ${G}`)}let I=0,T=o||(()=>{});T(me[0]);let J=setInterval(()=>{I++,T(me[Math.min(I,me.length-1)])},6e3),D="",R=w.body.getReader(),L=new TextDecoder,Z="";try{for(;;){let{done:G,value:H}=await R.read();if(G)break;Z+=L.decode(H,{stream:!0});let O=Z.split(`
|
|
734
|
+
`);Z=O.pop()||"";for(let te of O){if(!te.startsWith("data: "))continue;let ie=te.slice(6).trim();try{let It=JSON.parse(ie).candidates?.[0]?.content?.parts?.[0]?.text;It&&(D+=It,s(It))}catch{}}}}finally{clearInterval(J)}i&&i(D)}function On(t,e,n,s){return new Promise((o,i)=>{let a={...process.env};delete a.CLAUDECODE;let r=sc(t,e,{stdio:["pipe","pipe","pipe"],env:a}),l="",c="",d=!1,u=h=>{d||(d=!0,h())};r.stdout.on("data",h=>{let b=h.toString();l+=b,s&&s(b)}),r.stderr.on("data",h=>{c+=h.toString()}),r.on("error",h=>u(()=>i(new Error(`${t} failed to start: ${h.message}`)))),r.on("close",h=>{u(()=>{h!==0?i(new Error(`${t} exited with code ${h}.
|
|
730
735
|
`+(c?`Stderr: ${c.slice(0,500)}
|
|
731
|
-
`:"")+(l?`Output: ${l.slice(0,500)}`:"No output"))):
|
|
736
|
+
`:"")+(l?`Output: ${l.slice(0,500)}`:"No output"))):o(l)})}),r.stdin.on("error",()=>{}),r.stdin.write(n)?r.stdin.end():r.stdin.once("drain",()=>r.stdin.end());let g=setTimeout(()=>{r.kill(),u(()=>i(new Error(`${t} timed out after 5 minutes.
|
|
737
|
+
`+(c?`Stderr: ${c.slice(0,500)}
|
|
738
|
+
`:"")+`Partial output (${l.length} chars): ${l.slice(0,500)}`)))},3e5);r.on("close",()=>clearTimeout(g))})}async function Fi(t,e,n,s,o,i){let a=re(),r=E(),l=v().modules.length>0,c=vt(),d=wt(a,e,l,c.pageType,c.brandAssets);d+=`
|
|
732
739
|
|
|
733
740
|
## User Request
|
|
734
|
-
`+
|
|
741
|
+
`+t,d+=Gt(),d+=Pi(i),d+="\n\n---\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.";let u=["--print"];r.claudeCodeModel&&u.push("--model",r.claudeCodeModel);let p=0,g=s||(()=>{});g(me[0]);let h=setInterval(()=>{p++;let b=me[Math.min(p,me.length-1)];g(b)},6e3);try{let b=await On("claude",u,d,y=>{n(y)});o&&o(b)}finally{clearInterval(h)}}async function ks(t,e,n,s,o,i,a){let r=re(),l=v().modules.length>0,c=vt(),d=wt(r,n,l,c.pageType,c.brandAssets);d+=`
|
|
735
742
|
|
|
736
743
|
## User Request
|
|
737
|
-
`+
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
Process timed out`,s.completedAt=Date.now())},l),n}function fi(e,t){let o=Qe.get(e);if(!o||!("listeners"in o))return;let n=o;if(n.output)try{t(n.output)}catch{}n.listeners.add(t)}function gi(e,t){let o=Qe.get(e);!o||!("listeners"in o)||o.listeners.delete(t)}h();import{existsSync as ht,readdirSync as hi,rmSync as il}from"fs";import{join as yt,basename as rl}from"path";import{homedir as al}from"os";import{execSync as yi}from"child_process";Z();en();Ye();Z();Q();var Ae=yt(al(),"vibespot-themes"),xn=null,ll=5e3;function Cn(){if(xn&&Date.now()-xn.ts<ll)return xn.data;let e=[];if(ht(Ae))try{for(let t of hi(Ae,{withFileTypes:!0}))if(t.isDirectory()){let o=yt(Ae,t.name,"theme.json");if(ht(o)){let n=0,s=yt(Ae,t.name,"modules");if(ht(s))try{n=hi(s,{withFileTypes:!0}).filter(i=>i.isDirectory()).length}catch{}e.push({name:t.name,moduleCount:n})}}}catch{}return xn={data:e,ts:Date.now()},e}function bi(e){let t=$(),o=Bt(),n=!1;try{yi("hs --version",{encoding:"utf-8",stdio:"pipe"}),n=!0}catch{}let s=ut().sort((a,r)=>r.updatedAt-a.updatedAt).slice(0,10),i=Cn();d(e,200,{hasActiveSession:!!t,activeSession:t?{id:t.id,themeName:t.themeName,moduleCount:t.modules.length}:null,hsInstalled:n,aiAvailable:o.availableEngines.length>0,availableEngines:o.availableEngines,activeEngine:o.activeEngine,sessions:s,localThemes:i})}function Si(e,t){_(e,o=>{try{if(Mt()){d(t,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{name:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"Theme name is required"});return}let s=n.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,""),i=yt(Ae,s);ge(Ae),ht(i)&&il(i,{recursive:!0,force:!0}),Zt(i,s),Nt(i,s),j(),d(t,200,{ok:!0,themeName:s,themePath:i})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function vi(e,t){_(e,o=>{try{if(Mt()){d(t,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{name:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"Theme name is required"});return}let s=n.replace(/^\/+|\/+$/g,"");if(!s){d(t,400,{error:"Theme name is required"});return}let i=ue(),a=N(),r=s.includes("/")||s.includes("@")?s.replace(/[@/]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""):s,l=yt(Ae,r);ge(Ae),a.hubspotUploadMode==="cli"||!i?(yi(`hs cms fetch "${s}" "${l}"`,{encoding:"utf-8",stdio:"pipe"}),Nt(l,r),Rt(l),j(),d(t,200,{ok:!0,themeName:r,themePath:l,moduleCount:$()?.modules.length||0})):Tt(i,s,l).then(()=>{Nt(l,r),Rt(l),j(),d(t,200,{ok:!0,themeName:r,themePath:l,moduleCount:$()?.modules.length||0})}).catch(c=>{d(t,500,{error:c instanceof Error?c.message:String(c)})})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function wi(e,t){_(e,o=>{try{if(Mt()){d(t,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{path:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"Theme path is required"});return}let s=n;if(ht(s)||(s=yt(Ae,n)),!ht(s)){d(t,400,{error:`Theme folder not found: ${n}`});return}let i=rl(s);Nt(s,i),Rt(s),j(),d(t,200,{ok:!0,themeName:i,themePath:s,moduleCount:$()?.modules.length||0})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function xi(e,t){_(e,o=>{try{if(Mt()){d(t,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{sessionId:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"Session ID is required"});return}let s=bn(n);if(!s){d(t,404,{error:"Session not found"});return}d(t,200,{ok:!0,themeName:s.themeName,themePath:s.themePath,moduleCount:s.modules.length,messageCount:s.messages.length})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Ci(e,t){_(e,o=>{try{let{apiKey:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"API key is required"});return}z({anthropicApiKey:n}),d(t,200,{ok:!0})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Ai(e){let t=ue();if(!t){d(e,200,{themes:[],error:"No HubSpot account connected"});return}(async()=>{let o=await jo(t);if(o.length===0){d(e,200,{themes:[]});return}let n=[],s=o.map(async r=>{let l=r.path||r.name;try{let c=await zt(t,`${l}/theme.json`);c&&!c.folder&&n.push({name:r.name,path:l})}catch{}});await Promise.all(s),n.sort((r,l)=>r.name.localeCompare(l.name));let i=Cn(),a=new Set(i.map(r=>r.name));d(e,200,{themes:n.map(r=>({...r,existsLocally:a.has(r.name)}))})})().catch(o=>{d(e,200,{themes:[],error:o instanceof Error?o.message:String(o)})})}h();import{existsSync as cl,readFileSync as dl,appendFileSync as ul}from"fs";import{join as Ii}from"path";import{homedir as Ti}from"os";Z();Ye();Q();var jt={data:{},ts:0},ml=600*1e3,$i={"claude-code":[{id:"sonnet",label:"Claude Sonnet (default)"},{id:"opus",label:"Claude Opus"},{id:"haiku",label:"Claude Haiku"}],"codex-cli":[{id:"o4-mini",label:"o4 Mini (default)"},{id:"o3",label:"o3"},{id:"gpt-4o",label:"GPT-4o"}]};async function pl(e){let t=await fetch("https://api.anthropic.com/v1/models",{headers:{"x-api-key":e,"anthropic-version":"2023-06-01"}});return t.ok?(await t.json()).data.filter(n=>!n.id.startsWith("claude-3-")&&!n.id.startsWith("claude-2")).map(n=>({id:n.id,label:n.display_name})):[]}async function fl(e){let t=await fetch("https://api.openai.com/v1/models",{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)return[];let o=await t.json(),n=/^(gpt-4o|gpt-4o-mini|o[1-4](-mini)?|o[1-4]-pro)$/;return o.data.filter(s=>n.test(s.id)).sort((s,i)=>s.id.localeCompare(i.id)).map(s=>({id:s.id,label:s.id}))}async function gl(e){let t=await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${e}`);return t.ok?(await t.json()).models.filter(n=>n.name.includes("gemini-2")).map(n=>({id:n.name.replace("models/",""),label:n.displayName})):[]}async function hl(){if(Date.now()-jt.ts<ml&&Object.keys(jt.data).length>0)return jt.data;let e=N(),t={...$i},o=[],n=he("anthropic-api",e);n&&o.push(pl(n).then(a=>{a.length&&(t["anthropic-api"]=a)}).catch(()=>{}));let s=he("openai-api",e);s&&o.push(fl(s).then(a=>{a.length&&(t["openai-api"]=a)}).catch(()=>{}));let i=he("gemini-api",e);return i&&o.push(gl(i).then(a=>{a.length&&(t["gemini-api"]=a,t["gemini-cli"]=a)}).catch(()=>{})),await Promise.all(o),jt.data=t,jt.ts=Date.now(),t}function ki(e){let t=Bt(),o=N(),n={aiEngine:o.aiEngine||null,claudeCodeModel:o.claudeCodeModel||null,anthropicApiModel:o.anthropicApiModel||null,openaiApiModel:o.openaiApiModel||null,hubspotUploadMode:o.hubspotUploadMode||"api",hubspotAccounts:(o.hubspotAccounts||[]).map(r=>({portalId:r.portalId,portalName:r.portalName,dataCenter:r.dataCenter})),activeHubSpotAccount:o.activeHubSpotAccount||null,enabledCLITools:o.enabledCLITools||[]},s=ut().length,i=Cn().length,a=ot();hl().then(r=>{d(e,200,{version:a,environment:t,config:n,models:r,sessionCount:s,localThemeCount:i})}).catch(()=>{d(e,200,{version:a,environment:t,config:n,models:$i,sessionCount:s,localThemeCount:i})})}function Ei(e,t){_(e,o=>{try{let{engine:n,model:s}=JSON.parse(o);if(!["claude-code","anthropic-api","openai-api","gemini-cli","gemini-api","codex-cli"].includes(n)){d(t,400,{error:`Invalid engine: ${n}`});return}let a={aiEngine:n};if(s)switch(n){case"claude-code":a.claudeCodeModel=s;break;case"anthropic-api":a.anthropicApiModel=s;break;case"openai-api":a.openaiApiModel=s;break}z(a),d(t,200,{ok:!0,engine:n})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Ni(e,t){_(e,o=>{try{let{provider:n,apiKey:s}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"provider is required"});return}if(!s){let l={};switch(n){case"anthropic":l.anthropicApiKey="";break;case"openai":l.openaiApiKey="";break;case"gemini":l.geminiApiKey="";break;default:d(t,400,{error:`Unknown provider: ${n}`});return}z(l),d(t,200,{ok:!0,provider:n,deleted:!0});return}let i={};switch(n){case"anthropic":i.anthropicApiKey=s;break;case"openai":i.openaiApiKey=s;break;case"gemini":i.geminiApiKey=s;break;default:d(t,400,{error:`Unknown provider: ${n}`});return}z(i);let a=null;if(!N().aiEngine){let c={anthropic:"anthropic-api",openai:"openai-api",gemini:"gemini-api"}[n];c&&(z({aiEngine:c}),a=c)}d(t,200,{ok:!0,provider:n,autoSelectedEngine:a})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function _i(e,t){_(e,o=>{try{let{tool:n}=JSON.parse(o),s={hubspot:{cmd:"npm install -g @hubspot/cli",desc:"Installing HubSpot CLI"},claude:{cmd:"npm install -g @anthropic-ai/claude-code",desc:"Installing Claude Code"},gemini:{cmd:"npm install -g @google/gemini-cli",desc:"Installing Gemini CLI"},codex:{cmd:process.platform==="darwin"?"brew install --cask codex":"npm install -g @openai/codex",desc:"Installing OpenAI Codex"},gh:{cmd:process.platform==="darwin"?"brew install gh":"npm install -g @cli/gh",desc:"Installing GitHub CLI"}},i=s[n];if(!i){d(t,400,{error:`Unknown tool: ${n}. Valid: ${Object.keys(s).join(", ")}`});return}let a=Ce(i.cmd,i.desc,{timeout:12e4});d(t,200,{ok:!0,jobId:a})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Ri(e,t){_(e,o=>{try{let n=JSON.parse(o||"{}"),s=N(),i=s.hubspotUploadMode||"api";if(n.personalAccessKey)if(i==="api"){Vt(n.personalAccessKey).then(a=>{Ht(n.personalAccessKey,a.portalId,a.portalName,a.dataCenter),d(t,200,{ok:!0,portalName:a.portalName,portalId:a.portalId,dataCenter:a.dataCenter})}).catch(a=>{d(t,400,{error:a instanceof Error?a.message:String(a)})});return}else{if(!$e().found){d(t,400,{error:"HubSpot CLI not installed",needsInstall:!0});return}let r=Ce(`hs auth --pak="${n.personalAccessKey}"`,"Authenticating with HubSpot",{timeout:3e4});d(t,200,{ok:!0,jobId:r});return}if(i==="api"){let a=s.hubspotAccounts||[];if(a.length>0&&!n.force){let r=a.find(l=>l.portalId===s.activeHubSpotAccount)||a[0];d(t,200,{ok:!0,alreadyAuthenticated:!0,portalName:r.portalName,portalId:r.portalId});return}}else{if(!$e().found){d(t,400,{error:"HubSpot CLI not installed",needsInstall:!0});return}let r=ke();if(r.authenticated&&!n.force){d(t,200,{ok:!0,alreadyAuthenticated:!0,portalName:r.portalName,portalId:r.portalId});return}}d(t,200,{needsKey:!0,instructions:"Create a personal access key in HubSpot",url:"https://app.hubspot.com/portal-recommend/l?slug=personal-access-key",steps:["Click the link above to open HubSpot","Select your account","Create a Personal Access Key with CMS permissions","Copy the key and paste it below"]})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Pi(e,t){_(e,o=>{try{let n=JSON.parse(o||"{}");if(!jn().found){d(t,400,{error:"GitHub CLI not installed",needsInstall:!0});return}let i=Dn();if(i.authenticated&&!n.force){d(t,200,{ok:!0,alreadyAuthenticated:!0,username:i.username});return}if(n.token){let r=Ce(`echo "${n.token}" | gh auth login --with-token`,"Authenticating with GitHub",{timeout:3e4});d(t,200,{ok:!0,jobId:r});return}let a=Ce("gh auth login --web --git-protocol https","GitHub authentication (check your browser)",{timeout:3e5});d(t,200,{ok:!0,jobId:a,browserAuthRequired:!0})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Oi(e,t){_(e,o=>{try{let{portalId:n,action:s}=JSON.parse(o);if((N().hubspotUploadMode||"api")==="api"){if(s==="remove"&&n){To(n),d(t,200,{ok:!0});return}if(n){$o(n),d(t,200,{ok:!0});return}}else{if(!$e().found){d(t,400,{error:"HubSpot CLI not installed"});return}if(s==="remove"&&n){let l=Ce(`hs accounts remove ${n}`,`Removing HubSpot account ${n}`,{timeout:15e3});d(t,200,{ok:!0,jobId:l});return}if(n){let l=Ce(`hs accounts use ${n}`,`Switching to HubSpot account ${n}`,{timeout:15e3});d(t,200,{ok:!0,jobId:l});return}}d(t,400,{error:"portalId required"})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Mi(e){let t=Ce("gh auth logout --hostname github.com -y","Logging out of GitHub",{timeout:15e3});d(e,200,{ok:!0,jobId:t})}function ji(e,t){_(e,o=>{try{let{cli:n,apiKey:s}=JSON.parse(o||"{}");switch(n){case"claude":{let i=Ce("CLAUDECODE= claude --print -p 'reply OK'","Authenticating Claude Code (check your browser if prompted)",{timeout:12e4});d(t,200,{ok:!0,jobId:i,hint:"If Claude Code opens a browser window, complete the sign-in there."});break}case"gemini":{let i=Ce("gemini -p 'reply OK'","Authenticating Gemini CLI (check your browser if prompted)",{timeout:12e4});d(t,200,{ok:!0,jobId:i,hint:"If Gemini opens a browser window, complete the sign-in there."});break}case"codex":{if(s&&s.trim()){let i=s.trim();if(process.env.OPENAI_API_KEY=i,z({openaiApiKey:i}),process.platform!=="win32"){let a=`export OPENAI_API_KEY="${i}"`,r=process.env.SHELL?.includes("zsh")?Ii(Ti(),".zshrc"):Ii(Ti(),".bashrc");try{(cl(r)?dl(r,"utf-8"):"").includes("OPENAI_API_KEY")||ul(r,`
|
|
744
|
+
`+e,d+=Gt(),d+=Pi(a),d+="\n\n---\nRemember: respond with a ```vibespot-modules JSON block containing ALL modules. No text-only responses.";let u,p;t==="gemini"?(u="gemini",p=[]):(u="codex",p=["exec","--full-auto"]);let g=0,h=o||(()=>{});h(me[0]);let b=setInterval(()=>{g++;let y=me[Math.min(g,me.length-1)];h(y)},6e3);try{let y=await On(u,p,d,w=>{s(w)});i&&i(y)}finally{clearInterval(b)}}f();import{createWriteStream as ic,mkdirSync as Ji,existsSync as Ts,readFileSync as Es}from"fs";import{join as ot,extname as rc}from"path";import{randomUUID as ac}from"crypto";import lc from"busboy";f();function m(t,e,n){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(n))}function _(t,e){let n=[];t.on("data",s=>n.push(s)),t.on("end",()=>e(Buffer.concat(n).toString("utf-8")))}function $s(t,e,n){_(t,s=>{try{n(JSON.parse(s||"{}"))}catch{m(e,400,{error:"Invalid JSON in request body"})}})}var cc=10*1024*1024,Di=new Set(["image/png","image/jpeg","image/jpg","image/svg+xml","image/webp","image/gif"]),dc=new Set(["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","text/markdown","text/plain"]),uc=new Set([...Di,...dc]);function mc(t){return t.replace(/[^a-zA-Z0-9._-]/g,"_").replace(/_{2,}/g,"_").replace(/^_+|_+$/g,"").toLowerCase()}function pc(t,e){if(!Ts(ot(t,e)))return e;let n=rc(e),s=e.slice(0,-n.length||void 0),o=1;for(;Ts(ot(t,`${s}-${o}${n}`));)o++;return`${s}-${o}${n}`}async function fc(t){let e=(await import("pdf-parse")).default,n=Es(t);return(await e(n)).text}async function gc(t){return(await(await import("mammoth")).extractRawText({path:t})).value}function hc(t){return Es(t,"utf-8")}function Hi(t,e){let n=v();if(!n){m(e,400,{error:"No active session"});return}if(!(t.headers["content-type"]||"").includes("multipart/form-data")){m(e,400,{error:"Expected multipart/form-data"});return}let o=[],i=[],a=0,r=[],l=lc({headers:t.headers,limits:{fileSize:cc,files:10}});l.on("file",(c,d,u)=>{let{filename:p,mimeType:g}=u;if(a++,!uc.has(g)){i.push(`Unsupported file type: ${p} (${g})`),d.resume();return}let h=Di.has(g),b=mc(p),y=ac(),w,I;h?(w=ot(n.themePath,"assets"),Ji(w,{recursive:!0}),I=pc(w,b)):(w=ot(n.themePath,".vibespot","uploads"),Ji(w,{recursive:!0}),I=`${y}-${b}`);let T=ot(w,I),J=ic(T),D=0,R=!1;d.on("data",L=>{D+=L.length}),d.on("limit",()=>{R=!0,i.push(`File too large (>10MB): ${p}`)}),d.pipe(J),r.push(new Promise(L=>{J.on("finish",()=>{if(!R){let Z={id:y,filename:I,originalName:p,type:h?"image":"document",usage:h?"asset":"context",mimeType:g,size:D,addedAt:new Date().toISOString()};o.push(Z),oi(Z)}L()}),J.on("error",()=>{i.push(`Failed to write: ${p}`),L()})}))}),l.on("finish",async()=>{await Promise.all(r);for(let c of o)if(c.type==="document"){let d=ot(n.themePath,".vibespot","uploads",c.filename);try{c.mimeType==="application/pdf"?c.extractedText=await fc(d):c.mimeType==="application/vnd.openxmlformats-officedocument.wordprocessingml.document"?c.extractedText=await gc(d):c.extractedText=hc(d),M.info("upload",`Extracted text from ${c.originalName} (${c.extractedText.length} chars)`)}catch(u){M.warn("upload",`Failed to extract text from ${c.originalName}: ${u}`),c.extractedText=`[Could not extract text from ${c.originalName}]`}}if(a===0){m(e,400,{error:"No files uploaded"});return}m(e,200,{files:o.map(c=>({id:c.id,filename:c.filename,originalName:c.originalName,type:c.type,usage:c.usage,size:c.size})),errors:i.length>0?i:void 0})}),l.on("error",c=>{M.error("upload",`Busboy error: ${c}`),m(e,500,{error:"Upload failed"})}),t.pipe(l)}function Li(t){let e=v();return e?.assets?t.map(n=>{let s=e.assets.find(i=>i.id===n);if(!s)return null;let o={id:s.id,filename:s.filename,originalName:s.originalName,type:s.type,usage:s.usage,mimeType:s.mimeType};if(s.type==="image"){let i=ot(e.themePath,"assets",s.filename);Ts(i)&&(o.base64=Es(i).toString("base64")),o.assetPath=`${e.themeName}/assets/${s.filename}`}else s.type==="document"&&(o.extractedText=s.extractedText);return o}).filter(n=>n!==null):[]}f();f();ee();var Ms=[10,20,40,60,120];async function Gi(t,e){for(let n=0;;n++)try{return await t()}catch(s){let o=s.status,i=s.error?.type;if(!(o===429||i==="rate_limit_error"||s instanceof Error&&s.message.includes("429"))||n>=Ms.length)throw s;let r=Ms[n];M.warn("agent-adapter",`Rate limited (429), attempt ${n+1}/${Ms.length} \u2014 waiting ${r}s`),e&&e(`Rate limited \u2014 retrying in ${r}s...`),await new Promise(l=>setTimeout(l,r*1e3)),e&&e("Retrying...")}}function jn(t){if(t&&typeof t=="object"&&!Array.isArray(t)){let e=t;for(let n of["fieldsJson","metaJson"])e[n]&&typeof e[n]=="object"&&(e[n]=JSON.stringify(e[n]))}return t}var Ns=null;async function yc(){return Ns||(Ns=(await import("@anthropic-ai/sdk")).default),Ns}async function bc(t,e,n){let s=await yc(),o=new s({apiKey:t}),i=n.messages;if(n.structuredOutput){let a={name:n.structuredOutput.name,description:`Return the result as structured JSON matching the ${n.structuredOutput.name} schema.`,input_schema:n.structuredOutput.schema};return Gi(async()=>{let r=await o.messages.create({model:e,max_tokens:n.maxTokens||16e3,system:n.systemPrompt,messages:i,tools:[a],tool_choice:{type:"tool",name:n.structuredOutput.name}});for(let c of r.content)if(c.type==="tool_use")return{type:"structured",data:jn(c.input)};return{type:"text",text:r.content.filter(c=>c.type==="text").map(c=>c.text).join("")}},n.onStatus)}return Gi(async()=>{let a="",r=o.messages.stream({model:e,max_tokens:n.maxTokens||16e3,system:n.systemPrompt,messages:i});for await(let l of r)l.type==="content_block_delta"&&l.delta.type==="text_delta"&&(a+=l.delta.text,n.onChunk&&n.onChunk(l.delta.text));return{type:"text",text:a}},n.onStatus)}function _s(t){let e={...t};if(e.type==="object"&&(e.additionalProperties=!1,e.properties&&typeof e.properties=="object")){let n={};for(let[s,o]of Object.entries(e.properties))n[s]=o&&typeof o=="object"?_s(o):o;e.properties=n}return e.items&&typeof e.items=="object"&&(e.items=_s(e.items)),e}async function Sc(t,e,n){let s=[{role:"system",content:n.systemPrompt},...n.messages.map(l=>({role:l.role,content:typeof l.content=="string"?l.content:l.content.map(c=>({type:"text",text:c.text}))}))],o={model:e,max_tokens:n.maxTokens||16e3,messages:s};n.structuredOutput&&(o.response_format={type:"json_schema",json_schema:{name:n.structuredOutput.name,strict:!0,schema:_s(n.structuredOutput.schema)}});let i=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(o)});if(!i.ok){let l=await i.text(),c=i.status;if(c===429){let d=new Error(`OpenAI rate limit: ${l}`);throw d.status=429,d}throw new Error(`OpenAI API error (${c}): ${l}`)}let r=(await i.json()).choices?.[0]?.message?.content||"";if(n.structuredOutput)try{return{type:"structured",data:jn(JSON.parse(r))}}catch{return M.warn("agent-adapter","OpenAI structured output parse failed, returning raw text"),{type:"text",text:r}}return{type:"text",text:r}}async function vc(t,e,n){let s=e||"gemini-2.5-flash",o=n.messages.map(d=>({role:d.role==="assistant"?"model":"user",parts:typeof d.content=="string"?[{text:d.content}]:d.content.map(u=>({text:u.text}))})),i={systemInstruction:{parts:[{text:n.systemPrompt}]},contents:o,generationConfig:{maxOutputTokens:n.maxTokens||16e3,...n.structuredOutput?{responseMimeType:"application/json",responseSchema:n.structuredOutput.schema}:{}}},a=`https://generativelanguage.googleapis.com/v1beta/models/${s}:generateContent?key=${t}`,r=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!r.ok){let d=await r.text(),u=r.status;if(u===429){let p=new Error(`Gemini rate limit: ${d}`);throw p.status=429,p}throw new Error(`Gemini API error (${u}): ${d}`)}let c=(await r.json()).candidates?.[0]?.content?.parts?.[0]?.text||"";if(n.structuredOutput)try{return{type:"structured",data:jn(JSON.parse(c))}}catch{return M.warn("agent-adapter","Gemini structured output parse failed, returning raw text"),{type:"text",text:c}}return{type:"text",text:c}}function wc(t){switch(t){case"claude-code":{let e=E(),n=["--print"];return e.claudeCodeModel&&n.push("--model",e.claudeCodeModel),{bin:"claude",args:n}}case"gemini-cli":return{bin:"gemini",args:[]};case"codex-cli":return{bin:"codex",args:["exec","--full-auto"]};default:throw new Error(`Not a CLI engine: ${t}`)}}function xc(t){let e=[t.systemPrompt];for(let n of t.messages){let s=n.role==="user"?"User":"Assistant",o=typeof n.content=="string"?n.content:n.content.map(i=>i.text).join(`
|
|
745
|
+
`);e.push(`
|
|
746
|
+
|
|
747
|
+
## ${s}
|
|
748
|
+
${o}`)}if(t.structuredOutput){let n=Ui(t.structuredOutput.schema);e.push(`
|
|
749
|
+
|
|
750
|
+
## Output Format \u2014 CRITICAL
|
|
751
|
+
Respond with a JSON code block. Wrap your JSON in \`\`\`json fences. No prose or explanation before or after the code block.
|
|
752
|
+
|
|
753
|
+
The JSON must match this structure:
|
|
754
|
+
${n}`)}return e.join("")}function Ui(t,e=0){let n=" ".repeat(e),s=t.properties,o=t.required||[];if(!s)return`${n}${JSON.stringify(t)}`;let i=["{"];for(let[a,r]of Object.entries(s)){let l=o.includes(a)?" (required)":"",c=r.type||"any",d=r.description?` \u2014 ${r.description}`:"",u=r.enum?` [${r.enum.join(", ")}]`:"";if(c==="array"&&r.items){let p=r.items.type||"object";i.push(`${n} "${a}": ${c}<${p}>${l}${d}${u}`)}else c==="object"&&r.properties?i.push(`${n} "${a}": ${Ui(r,e+1)}${l}${d}`):i.push(`${n} "${a}": ${c}${l}${d}${u}`)}return i.push(`${n}}`),i.join(`
|
|
755
|
+
`)}function Cc(t){let e=t.trim(),n=je(e);if(n&&typeof n=="object")return n;let s=e.match(/```(?:json|vibespot-modules)?\s*\n([\s\S]*?)```/i);if(s){let r=s[1].trim(),l=je(r);if(l&&typeof l=="object")return l;let c=Lt(r);if(c&&typeof c=="object")return c}let o=e.indexOf("{"),i=e.lastIndexOf("}");if(o!==-1&&i>o){let r=e.slice(o,i+1),l=je(r);if(l&&typeof l=="object")return l;let c=Lt(r);if(c&&typeof c=="object")return c}let a=Lt(e);return a&&typeof a=="object"?a:null}async function Ac(t,e,n){let{bin:s,args:o}=wc(t),i=xc(n),a=await On(s,o,i,n.onChunk);if(!n.structuredOutput)return{type:"text",text:a};let r=Cc(a);return r?{type:"structured",data:jn(r)}:(M.warn("agent-cli",`${t}: failed to parse structured output, returning text`,{outputPreview:a.slice(0,500),outputLength:a.length}),{type:"text",text:a})}var Ic=new Set(["anthropic-api","openai-api","gemini-api"]);async function kc(t,e,n,s){switch(M.info("agent-adapter",`${t} API call`,{model:n,structured:!!s.structuredOutput,schemaName:s.structuredOutput?.name,systemPromptLength:s.systemPrompt.length,messageCount:s.messages.length}),t){case"anthropic-api":return bc(e,n,s);case"openai-api":return Sc(e,n,s);case"gemini-api":return vc(e,n,s);default:throw new Error(`Unsupported API engine: ${t}`)}}async function it(t,e,n,s){return Ic.has(t)?kc(t,e,n,s):(M.info("agent-adapter",`${t} CLI call`,{structured:!!s.structuredOutput,schemaName:s.structuredOutput?.name,systemPromptLength:s.systemPrompt.length,messageCount:s.messages.length}),Ac(t,n,s))}function Fn(t){return t==="anthropic-api"||t==="openai-api"||t==="gemini-api"||t==="claude-code"||t==="gemini-cli"||t==="codex-cli"}function Ut(t){return t==="claude-code"||t==="gemini-cli"||t==="codex-cli"}f();f();function Wi(t,e,n){let s=e.length>0?`Current template modules (in page order):
|
|
756
|
+
${e.map((i,a)=>`${a+1}. ${i}`).join(`
|
|
757
|
+
`)}`:"No modules yet (new page).",o=n.length>0?`
|
|
758
|
+
|
|
759
|
+
Module library (reusable from other templates):
|
|
760
|
+
${n.map(i=>`- ${i.name} (used in: ${i.usedIn.join(", ")})`).join(`
|
|
761
|
+
`)}`:"";return`You are the Intent Analyzer for vibeSpot, a HubSpot CMS page builder.
|
|
762
|
+
|
|
763
|
+
Your job: classify the user's request and plan which modules need work. You do NOT generate module code \u2014 you only plan.
|
|
764
|
+
|
|
765
|
+
## Theme: "${t}"
|
|
766
|
+
|
|
767
|
+
${s}${o}
|
|
768
|
+
|
|
769
|
+
## Classification Rules
|
|
770
|
+
|
|
771
|
+
1. **create** \u2014 User wants a new page from scratch (e.g., "build me a landing page for...")
|
|
772
|
+
2. **modify** \u2014 User wants to change existing modules (e.g., "make the hero button red", "update the pricing")
|
|
773
|
+
3. **add** \u2014 User wants new modules added to the existing page (e.g., "add a testimonials section")
|
|
774
|
+
4. **remove** \u2014 User wants modules removed (e.g., "remove the footer")
|
|
775
|
+
5. **rearrange** \u2014 User wants to reorder modules (e.g., "move pricing above features")
|
|
776
|
+
6. **style_change** \u2014 User wants design system changes that affect shared CSS/multiple modules (e.g., "change the color scheme to blue")
|
|
777
|
+
7. **question** \u2014 User is asking a question, not requesting changes (e.g., "what modules do I have?"). Provide the answer directly.
|
|
778
|
+
|
|
779
|
+
## Key Rules
|
|
780
|
+
|
|
781
|
+
- For **modify**: list only the modules that actually need changes in \`affectedModules\`. Everything else goes in \`unchangedModules\`.
|
|
782
|
+
- For **add**: new modules go in \`newModules\` with a descriptive name, brief description, and position index (0-based).
|
|
783
|
+
- For **reuse**: if the user references a module from the library, put it in \`reuseModules\` with the source template name. Reused modules are copied as-is \u2014 their structure (fields, HTML, CSS) MUST NOT change.
|
|
784
|
+
- For **style_change**: set \`designSystemChanges: true\`. All modules become affected since they need the updated design system.
|
|
785
|
+
- For **question**: set \`intent: "question"\` and provide the answer in the \`answer\` field. The pipeline will short-circuit.
|
|
786
|
+
- \`guidesNeeded\` determines which reference guides downstream stages receive. Only include what's actually needed:
|
|
787
|
+
- "design" \u2014 for new pages, layout changes, design system work
|
|
788
|
+
- "content" \u2014 for new pages, content-heavy changes
|
|
789
|
+
- "conversion" \u2014 for any module code generation
|
|
790
|
+
- "hubspot_rules" \u2014 for any module code generation
|
|
791
|
+
- "humanify" \u2014 when generating user-facing copy
|
|
792
|
+
|
|
793
|
+
## Compound Requests
|
|
794
|
+
|
|
795
|
+
If the user asks for multiple things (e.g., "make hero taller AND add testimonials"), capture ALL parts:
|
|
796
|
+
- Affected existing modules in \`affectedModules\`
|
|
797
|
+
- New modules in \`newModules\`
|
|
798
|
+
- Set the broadest applicable intent (prefer "modify" + newModules over splitting)`}var Bi={type:"object",properties:{intent:{type:"string",enum:["create","modify","add","remove","rearrange","style_change","question"]},affectedModules:{type:"array",items:{type:"string"},description:"Names of existing modules that need changes"},unchangedModules:{type:"array",items:{type:"string"},description:"Names of existing modules that stay as-is"},newModules:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:{type:"string"},position:{type:"number"}},required:["name","description","position"]},description:"New modules to create"},reuseModules:{type:"array",items:{type:"object",properties:{name:{type:"string"},sourceTemplate:{type:"string"},position:{type:"number"}},required:["name","sourceTemplate","position"]},description:"Modules to copy from the library (immutable structure)"},guidesNeeded:{type:"array",items:{type:"string",enum:["design","content","conversion","hubspot_rules","humanify"]}},designSystemChanges:{type:"boolean",description:"True if shared CSS / design system needs regeneration"},answer:{type:"string",description:'For "question" intent only \u2014 the answer to return directly'}},required:["intent","affectedModules","unchangedModules","newModules","guidesNeeded","designSystemChanges"]};async function Ki(t,e,n,s,o,i,a){i({type:"agent_step",step:"analyzing",label:"Analyzing your request..."});let r=e.modules.map(u=>u.moduleName),l=Wi(e.themeName,r,a),c=await it(n,s,o,{systemPrompt:l,messages:[{role:"user",content:t}],structuredOutput:{schema:Bi,name:"pipeline_plan"},maxTokens:2e3});if(c.type!=="structured"){M.warn("intent-analyzer","Did not get structured output, falling back");let u=e.modules.length===0;return{intent:u?"create":"modify",affectedModules:u?[]:r,unchangedModules:[],newModules:[],guidesNeeded:["design","content","conversion","hubspot_rules","humanify"],designSystemChanges:u}}let d=c.data;return d.affectedModules=d.affectedModules||[],d.unchangedModules=d.unchangedModules||[],d.newModules=d.newModules||[],d.guidesNeeded=d.guidesNeeded||[],M.info("intent-analyzer","Plan",{intent:d.intent,affected:d.affectedModules.length,unchanged:d.unchangedModules.length,new:d.newModules.length,reuse:d.reuseModules?.length||0,designSystem:d.designSystemChanges}),i({type:"agent_decision",step:"analyzing",decision:$c(d)}),d}function $c(t){let e=[`Intent: ${t.intent}`];return t.affectedModules.length>0&&e.push(`Modifying: ${t.affectedModules.join(", ")}`),t.unchangedModules.length>0&&e.push(`Unchanged: ${t.unchangedModules.join(", ")}`),t.newModules.length>0&&e.push(`New: ${t.newModules.map(n=>n.name).join(", ")}`),t.reuseModules?.length&&e.push(`Reuse: ${t.reuseModules.map(n=>`${n.name} from ${n.sourceTemplate}`).join(", ")}`),t.designSystemChanges&&e.push("Design system changes: yes"),e.join(" | ")}f();f();function Vi(t,e){let n=[];return n.push(`You are the Design System Architect for vibeSpot, a HubSpot CMS page builder.
|
|
799
|
+
|
|
800
|
+
Your job: create a complete, production-ready CSS design system for a landing page theme. You produce the :root custom properties, shared utility/component CSS, and optional shared JS (scroll animations). Downstream agents will use YOUR CSS classes and variables to build individual modules.
|
|
801
|
+
|
|
802
|
+
## Theme: "${t}"
|
|
803
|
+
|
|
804
|
+
## Output Requirements
|
|
805
|
+
|
|
806
|
+
### cssVariables
|
|
807
|
+
A flat object mapping CSS custom property names to values. Every variable your CSS references MUST be defined here. Include ALL of these categories:
|
|
808
|
+
|
|
809
|
+
**Colors** (at minimum):
|
|
810
|
+
- --${t}-color-bg: page background
|
|
811
|
+
- --${t}-color-surface: card/section background
|
|
812
|
+
- --${t}-color-dark: dark section background
|
|
813
|
+
- --${t}-color-dark-surface: card bg inside dark sections
|
|
814
|
+
- --${t}-color-text: primary text color
|
|
815
|
+
- --${t}-color-text-inverse: text on dark backgrounds
|
|
816
|
+
- --${t}-color-text-muted: secondary/muted text
|
|
817
|
+
- --${t}-color-primary: primary brand color
|
|
818
|
+
- --${t}-color-primary-dark: darker variant for hover states
|
|
819
|
+
- --${t}-color-accent: accent/highlight color
|
|
820
|
+
- --${t}-color-accent-light: light tint for pill/badge backgrounds
|
|
821
|
+
- --${t}-color-border: default border color
|
|
822
|
+
- --${t}-color-border-hover: border on hover
|
|
823
|
+
|
|
824
|
+
**Typography**:
|
|
825
|
+
- --${t}-font-display: display/heading font stack (system fonts only)
|
|
826
|
+
- --${t}-font-body: body text font stack (system fonts only)
|
|
827
|
+
- --${t}-size-h1 through --${t}-size-h3: heading sizes using clamp()
|
|
828
|
+
- --${t}-size-body, --${t}-size-lg, --${t}-size-small, --${t}-size-label
|
|
829
|
+
- --${t}-leading-tight, --${t}-leading-snug, --${t}-leading-body: line heights
|
|
830
|
+
- --${t}-tracking-tight, --${t}-tracking-wide: letter spacing
|
|
831
|
+
|
|
832
|
+
**Spacing**:
|
|
833
|
+
- --${t}-space-xs through --${t}-space-xl, --${t}-space-section
|
|
834
|
+
- --${t}-max-width: content max-width (1152-1280px)
|
|
835
|
+
|
|
836
|
+
**Effects**:
|
|
837
|
+
- --${t}-radius-sm, --${t}-radius-md, --${t}-radius-lg, --${t}-radius-full
|
|
838
|
+
- --${t}-shadow-card-hover, --${t}-shadow-button
|
|
839
|
+
- --${t}-transition-fast, --${t}-transition-base, --${t}-transition-slow
|
|
840
|
+
|
|
841
|
+
### sharedCss
|
|
842
|
+
Complete CSS file content. MUST include:
|
|
843
|
+
1. A \`:root {}\` block with ALL variables from cssVariables
|
|
844
|
+
2. Reset (box-sizing, margin, padding)
|
|
845
|
+
3. Body styles referencing your variables
|
|
846
|
+
4. Typography rules (h1-h6, p)
|
|
847
|
+
5. Layout utilities (.${t}-container, .${t}-section, .${t}-section--dark)
|
|
848
|
+
6. Grid system (.${t}-grid, .${t}-grid--2/3/4 with responsive breakpoints)
|
|
849
|
+
7. Card component (.${t}-card with hover lift)
|
|
850
|
+
8. Button component (.${t}-btn, .${t}-btn--primary, .${t}-btn--secondary)
|
|
851
|
+
CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus \u2014 HubSpot overrides link hover styles
|
|
852
|
+
9. Pill/badge (.${t}-pill)
|
|
853
|
+
10. Decorative elements (at least one background treatment: grid pattern, noise, gradient orb)
|
|
854
|
+
11. Scroll animation CSS ([data-animate], [data-animate-stagger]) with 3s CSS-only fallback
|
|
855
|
+
12. Section label (.${t}-label) \u2014 uppercase, letter-spacing, accent color
|
|
856
|
+
13. Stat number styling
|
|
857
|
+
14. Responsive mobile styles (@media max-width: 767px)
|
|
858
|
+
|
|
859
|
+
### sharedJs (optional)
|
|
860
|
+
IntersectionObserver-based scroll animation JS. Wrap in IIFE.
|
|
861
|
+
|
|
862
|
+
## CSS Rules \u2014 CRITICAL
|
|
863
|
+
- All classes MUST use prefix "${t}-"
|
|
864
|
+
- Use BEM naming: ${t}-module__element--modifier
|
|
865
|
+
- Use system font stacks ONLY (no Google Fonts @import, no external CDN)
|
|
866
|
+
- Every var() reference in CSS must have a matching declaration in :root
|
|
867
|
+
- No Tailwind, no Sass, no PostCSS
|
|
868
|
+
- Use clamp() for fluid typography sizing
|
|
869
|
+
|
|
870
|
+
## Font Strategy
|
|
871
|
+
Use system font stacks that approximate the desired aesthetic. Pick TWO stacks:
|
|
872
|
+
- Display: for headings (e.g., Georgia, "Times New Roman", serif for editorial)
|
|
873
|
+
- Body: for text (e.g., system-ui, -apple-system, "Segoe UI", sans-serif)
|
|
874
|
+
|
|
875
|
+
Good system font stacks by style:
|
|
876
|
+
| Style | Display Stack | Body Stack |
|
|
877
|
+
|-------|--------------|------------|
|
|
878
|
+
| Editorial | Georgia, Cambria, "Times New Roman", serif | system-ui, -apple-system, "Segoe UI", sans-serif |
|
|
879
|
+
| Modern | system-ui, -apple-system, sans-serif | "Segoe UI", Roboto, sans-serif |
|
|
880
|
+
| Warm | Optima, Candara, "Noto Sans", sans-serif | "Trebuchet MS", system-ui, sans-serif |
|
|
881
|
+
| Monospace/Tech | "SF Mono", "Cascadia Code", "Fira Code", monospace | system-ui, sans-serif |
|
|
882
|
+
| Geometric | Futura, "Century Gothic", "Trebuchet MS", sans-serif | system-ui, sans-serif |`),n.push(`
|
|
883
|
+
|
|
884
|
+
## Design Guide
|
|
885
|
+
${Tc()}`),e?.styleguide&&n.push(`
|
|
886
|
+
|
|
887
|
+
## Brand Style Guide
|
|
888
|
+
${e.styleguide}`),n.join("")}var Yi={type:"object",properties:{cssVariables:{type:"object",description:"CSS custom property name \u2192 value map. Every var() used in sharedCss must be defined here."},sharedCss:{type:"string",description:"Complete shared CSS file. MUST start with :root {} block defining all cssVariables, followed by reset, typography, layout, components, animations, and responsive styles."},sharedJs:{type:"string",description:"Optional shared JS for scroll animations (IntersectionObserver). Wrap in IIFE. Empty string if not needed."},aesthetic:{type:"string",description:"Brief description of the chosen aesthetic direction (e.g., 'dark luxury with warm gold accents')"}},required:["cssVariables","sharedCss","aesthetic"]};function zi(t,e,n,s){let o=[];return o.push(`You are the Module Planner for vibeSpot, a HubSpot CMS page builder.
|
|
889
|
+
|
|
890
|
+
Your job: plan the modules for a landing page. You define what each module contains (content brief) and how it should be laid out. You do NOT write module code \u2014 downstream Module Developers handle that.
|
|
891
|
+
|
|
892
|
+
The Design System has already been created. Your module plans MUST reference the existing CSS classes and variables.
|
|
893
|
+
|
|
894
|
+
## Theme: "${t}"
|
|
895
|
+
|
|
896
|
+
## Available CSS Classes
|
|
897
|
+
The following shared CSS classes are available for modules to use. Reference these in your layoutNotes:
|
|
898
|
+
|
|
899
|
+
\`\`\`css
|
|
900
|
+
${e}
|
|
901
|
+
\`\`\`
|
|
902
|
+
|
|
903
|
+
## Output Rules
|
|
904
|
+
- Module names: descriptive, title-case (e.g., "Hero Banner", "Pricing Cards")
|
|
905
|
+
- Content briefs: describe the actual copy/content each module needs (headlines, body text, CTAs, stats)
|
|
906
|
+
- Layout notes: describe the visual layout using the available CSS classes above
|
|
907
|
+
- Reference specific CSS classes from the shared CSS in your layout notes (e.g., "Use ${t}-grid--3 for card layout, ${t}-section--dark for background")
|
|
908
|
+
- moduleOrder: list module names in the order they should appear on the page`),(!s||s.includes("content"))&&o.push(`
|
|
909
|
+
|
|
910
|
+
## Content & Copywriting Guide
|
|
911
|
+
${Ec()}`),n?.brandvoice&&o.push(`
|
|
912
|
+
|
|
913
|
+
## Brand Voice
|
|
914
|
+
${n.brandvoice}`),n?.humanify!==!1&&s?.includes("humanify")&&o.push(`
|
|
915
|
+
|
|
916
|
+
## Anti-AI Copy Rules
|
|
917
|
+
${Mc()}`),o.join("")}var qi={type:"object",properties:{modules:{type:"array",items:{type:"object",properties:{name:{type:"string",description:"Module name in title-case"},description:{type:"string",description:"What this module does"},contentBrief:{type:"string",description:"Specific content: headlines, body copy, stats, CTAs"},layoutNotes:{type:"string",description:"Visual layout approach referencing shared CSS classes"}},required:["name","description","contentBrief","layoutNotes"]}},moduleOrder:{type:"array",items:{type:"string"},description:"Module names in page display order"},narrative:{type:"string",description:"Brief description of the page story/flow"}},required:["modules","moduleOrder","narrative"]};function Tc(){return`### Design Philosophy
|
|
918
|
+
You are a senior UI designer. Every page must look professionally designed, not like AI output.
|
|
919
|
+
Avoid "AI slop": purple gradients on white, cookie-cutter card grids, no personality.
|
|
920
|
+
|
|
921
|
+
Before designing, decide on:
|
|
922
|
+
- **Aesthetic direction**: minimal editorial? bold brutalist? warm organic? luxury dark-mode? Pick one and commit.
|
|
923
|
+
- **One memorable element**: unusual layout, clever animation, striking color, unexpected typography.
|
|
924
|
+
- **Audience**: the audience shapes the vibe. A SaaS dashboard \u2260 a restaurant landing page.
|
|
925
|
+
|
|
926
|
+
When a user gives a simple prompt like "build me a landing page for a coffee shop," internally expand it:
|
|
927
|
+
- Pick an aesthetic (warm, editorial, slightly vintage)
|
|
928
|
+
- Pick specific colors (cream bg #faf7f2, espresso #3c1e0e, gold accent #c4956a)
|
|
929
|
+
- Decide hero style (full-bleed image background, overlaid text)
|
|
930
|
+
- Choose layout approach (asymmetric sections, large visual areas)
|
|
931
|
+
- Add texture (subtle paper/grain noise overlay)
|
|
932
|
+
- Set animations (scroll-triggered reveals)
|
|
933
|
+
The user gives the "what," you decide the "how it should look and feel."
|
|
934
|
+
|
|
935
|
+
### Typography Scale
|
|
936
|
+
Include these in the CSS custom properties:
|
|
937
|
+
\`\`\`
|
|
938
|
+
h1: clamp(2.5rem, 5vw, 4.5rem) /* Hero headlines \u2014 BIG */
|
|
939
|
+
h2: clamp(1.75rem, 3vw, 3rem) /* Section headings */
|
|
940
|
+
h3: clamp(1.25rem, 2vw, 1.75rem) /* Card titles, subheadings */
|
|
941
|
+
body: 1rem - 1.125rem /* 16-18px body text */
|
|
942
|
+
small: 0.875rem /* Captions, labels */
|
|
943
|
+
line-height: 1.1-1.2 for headings, 1.5-1.7 for body
|
|
944
|
+
letter-spacing: -0.02em to -0.04em for large headings (tighter = more premium)
|
|
945
|
+
\`\`\`
|
|
946
|
+
|
|
947
|
+
### Color Palettes
|
|
948
|
+
Pick a dominant (70%), secondary (25%), accent (5%). Ensure WCAG AA contrast (4.5:1 body, 3:1 large text).
|
|
949
|
+
|
|
950
|
+
\`\`\`
|
|
951
|
+
DARK LUXURY: --bg: #0a0a0a; --surface: #141414; --text: #e8e8e8; --primary: #c9a84c; --accent: #e8d5a3
|
|
952
|
+
WARM EARTH: --bg: #faf7f2; --surface: #f0ebe3; --text: #2d2418; --primary: #8b5e3c; --accent: #c4956a
|
|
953
|
+
COOL MINIMAL: --bg: #fafafa; --surface: #f1f1f1; --text: #1a1a1a; --primary: #0055ff; --accent: #00c4ff
|
|
954
|
+
FOREST: --bg: #0f1a0f; --surface: #1a2e1a; --text: #d4e8d0; --primary: #4ade80; --accent: #22c55e
|
|
955
|
+
EDITORIAL CREAM:--bg: #fffdf5; --surface: #f5f0e8; --text: #1c1917; --primary: #dc2626; --accent: #f97316
|
|
956
|
+
NOIR: --bg: #000000; --surface: #111111; --text: #ffffff; --primary: #ffffff; --accent: #666666
|
|
957
|
+
\`\`\`
|
|
958
|
+
|
|
959
|
+
### Layout Patterns
|
|
960
|
+
1. **Split hero**: Content left, visual right (50/50 or 60/40)
|
|
961
|
+
2. **Full-bleed hero**: Edge-to-edge background with centered content overlay
|
|
962
|
+
3. **Bento grid**: Asymmetric grid with mixed card sizes (span-2, span-1)
|
|
963
|
+
4. **Staggered/offset**: Content blocks not perfectly aligned, adds dynamism
|
|
964
|
+
5. **Overlapping elements**: Cards/images that break grid lines, overlap sections
|
|
965
|
+
6. **Scroll-based reveal**: Content appears as you scroll
|
|
966
|
+
|
|
967
|
+
### Background Treatments (pick 1-2 per page)
|
|
968
|
+
Include these in shared CSS:
|
|
969
|
+
- **Subtle grid pattern**: linear-gradient with thin lines at 60px intervals
|
|
970
|
+
- **Noise texture overlay**: SVG feTurbulence filter at 0.03 opacity, fixed position
|
|
971
|
+
- **Gradient orb/blob**: 600px radial-gradient circle, blurred 80px, absolute positioned
|
|
972
|
+
- **Radial gradient on sections**: radial-gradient(ellipse at top, rgba(primary, 0.05), transparent 70%)
|
|
973
|
+
- **Background alternation**: alternate section backgrounds every 2-3 sections to create visual "chapters"
|
|
974
|
+
|
|
975
|
+
### Micro-Interactions (include in shared CSS)
|
|
976
|
+
- **Card hover**: translateY(-4px) + box-shadow 20px 40px rgba(0,0,0,0.1), transition 0.3s ease
|
|
977
|
+
- **Button hover**: translateY(-1px) + box-shadow 4px 12px rgba(primary, 0.3), transition 0.2s
|
|
978
|
+
- **Link underline**: pseudo-element width 0 \u2192 100% on hover, transition 0.3s
|
|
979
|
+
- **Scroll animations**: data-animate elements start opacity:0 translateY(20px), animate to visible via IntersectionObserver. Include CSS fallback: elements become visible after 3s even if JS fails.
|
|
980
|
+
- **Stagger children**: transition-delay: calc(var(--index) * 100ms)
|
|
981
|
+
|
|
982
|
+
### Component Requirements
|
|
983
|
+
- **Hero**: Visually dominant headline (largest on page), subheading with lower contrast, clear CTA with hover, visual interest (gradient/image/pattern/animation), min 80vh. Every hero needs a "wow."
|
|
984
|
+
- **Navigation**: Sticky with backdrop-blur-md bg-white/80, logo left, CTA right, active state indicator, smooth transition on scroll (shrink, shadow, bg change)
|
|
985
|
+
- **Cards**: Subtle border OR shadow (not both heavy), rounded-xl to rounded-2xl, consistent padding, hover lift. Optional: subtle gradient border with pseudo-element
|
|
986
|
+
- **Buttons**: Primary filled + secondary outlined/ghost, generous padding (px-6 py-3 min). CRITICAL: Re-declare color, text-decoration:none, and font-family on :hover/:focus/:active \u2014 HubSpot overrides link hover styles
|
|
987
|
+
- **Footer**: Darker than page, multi-column (3-4 cols), stacked on mobile, subtle separator from main content
|
|
988
|
+
|
|
989
|
+
### Spacing
|
|
990
|
+
- Section padding: 80-128px vertical
|
|
991
|
+
- Content max-width: 1152-1280px centered
|
|
992
|
+
- Card padding: 24-32px, gap: 24-32px
|
|
993
|
+
- Between heading and body: 16-24px
|
|
994
|
+
- Generous whitespace = premium. Cramped = amateur.
|
|
995
|
+
- Mobile: always responsive, use clamp() for fluid sizing
|
|
996
|
+
|
|
997
|
+
### Quality Checklist
|
|
998
|
+
- [ ] Color palette has personality (not generic blue/purple on white)
|
|
999
|
+
- [ ] Typography scale is consistent (headings use clamp(), body 16-18px)
|
|
1000
|
+
- [ ] Spacing is generous (sections have 80px+ padding)
|
|
1001
|
+
- [ ] At least one "wow" element (animation, unusual layout, bold color)
|
|
1002
|
+
- [ ] Backgrounds aren't flat (subtle pattern, gradient, or texture)
|
|
1003
|
+
- [ ] Hover states exist (cards lift, buttons shift, links animate)
|
|
1004
|
+
- [ ] Scroll animations present with CSS fallback
|
|
1005
|
+
- [ ] Mobile responsive (works at 375px)
|
|
1006
|
+
- [ ] Contrast ratios pass WCAG AA
|
|
1007
|
+
- [ ] Page feels cohesive (one aesthetic direction, not a Frankenstein)
|
|
1008
|
+
|
|
1009
|
+
### Anti-Patterns
|
|
1010
|
+
|
|
1011
|
+
| Don't | Do Instead |
|
|
1012
|
+
|-------|-----------|
|
|
1013
|
+
| Purple gradient on white | Choose a palette with personality |
|
|
1014
|
+
| Symmetric 3-col grids for everything | Mix layouts: bento, split, offset, overlapping |
|
|
1015
|
+
| Flat white/gray backgrounds | Add subtle texture, gradient, or pattern |
|
|
1016
|
+
| Tiny padding between sections | Use 80-128px for breathing room |
|
|
1017
|
+
| All animations same speed | Stagger with increasing delays |
|
|
1018
|
+
| Skip hover/focus states | Every interactive element needs feedback |
|
|
1019
|
+
| Use \`<br>\` tags for spacing | Use proper margin/padding |
|
|
1020
|
+
| Put everything in a shadowed card | Vary: full-bleed, contained, floating |`}function Ec(){return`### Mandatory Page Sections (generate all)
|
|
1021
|
+
1. **Navigation Bar** \u2014 Logo, 4-5 nav links, CTA button, sticky on scroll
|
|
1022
|
+
2. **Hero** \u2014 Badge/pill, primary headline, subheadline, primary + secondary CTA, trust signals, visual element
|
|
1023
|
+
3. **Social Proof Bar** \u2014 Logo strip of 4-6 clients OR stats bar (compact, py-8)
|
|
1024
|
+
4. **Features/Services** \u2014 Section label + headline, 3-6 cards with icon/title/description/metric
|
|
1025
|
+
5. **How It Works** \u2014 3-4 numbered steps with titles, descriptions, visuals, connected flow
|
|
1026
|
+
6. **Testimonials** \u2014 At least 3 with full quotes, names, roles, ratings
|
|
1027
|
+
7. **Pricing/Value** \u2014 Pricing tiers or key metrics in large text with context + CTA
|
|
1028
|
+
8. **FAQ** \u2014 4-6 real questions with specific, helpful answers
|
|
1029
|
+
9. **Final CTA** \u2014 Strong headline, subtext, primary + secondary buttons, visually distinct
|
|
1030
|
+
10. **Footer** \u2014 Brand name, 3-4 link columns with 3-5 links each, contact info, social icons, copyright
|
|
1031
|
+
|
|
1032
|
+
### Optional Sections (include 1-2 when they fit)
|
|
1033
|
+
- Comparison table ("Us vs. Them")
|
|
1034
|
+
- Case study highlight
|
|
1035
|
+
- Team/About strip
|
|
1036
|
+
- Blog/Resource teasers
|
|
1037
|
+
- Partners/Integrations logo grid
|
|
1038
|
+
|
|
1039
|
+
### Headline Rules \u2014 The "Bar Test"
|
|
1040
|
+
Every headline should pass this test: if you shouted it across a bar, would someone turn their head?
|
|
1041
|
+
|
|
1042
|
+
| Don't | Do Instead |
|
|
1043
|
+
|-------|-----------|
|
|
1044
|
+
| "Our Services" | "What We Actually Do" |
|
|
1045
|
+
| "How It Works" | "Unclogged in 3 Steps" |
|
|
1046
|
+
| "Pricing" | "Cheaper Than Your Uber Eats Habit" |
|
|
1047
|
+
| "Testimonials" | "Don't Take Our Word For It" |
|
|
1048
|
+
| "Get Started" | "Blocked Drain? Text Us a Photo." |
|
|
1049
|
+
| "Features" | "Everything You Get, Nothing You Don't" |
|
|
1050
|
+
|
|
1051
|
+
### CTA Button Copy
|
|
1052
|
+
Never use "Submit" or "Learn More." Tie CTAs to specific outcomes:
|
|
1053
|
+
"Book Now \u2014 From \u20AC49 \u2192" \xB7 "Start Free Trial \xB7 No Card Required" \xB7 "Get My Custom Quote in 10 Min" \xB7 "Join 2,000+ Happy Customers"
|
|
1054
|
+
|
|
1055
|
+
### Minimum Content Quantities
|
|
1056
|
+
|
|
1057
|
+
| Element | Min | Why |
|
|
1058
|
+
|---------|-----|-----|
|
|
1059
|
+
| Testimonials | 3 | One looks fake, two looks thin |
|
|
1060
|
+
| Feature cards | 4 | Three is a wireframe |
|
|
1061
|
+
| FAQ items | 4 | Fewer looks like hiding something |
|
|
1062
|
+
| Process steps | 3 | Natural narrative arc |
|
|
1063
|
+
| Stats/metrics | 3 | Singles look accidental |
|
|
1064
|
+
| Footer columns | 3 | Fewer = side project |
|
|
1065
|
+
| Nav links | 4-5 | Establishes depth |
|
|
1066
|
+
| CTA repetitions | 3 | Hero, mid-page, closing |
|
|
1067
|
+
|
|
1068
|
+
### Business Type Content Templates
|
|
1069
|
+
|
|
1070
|
+
**Local Service** (plumber, electrician, cleaner): Hero = pain point + speed promise. Must-have: service area, response time, pricing. CTAs: phone, WhatsApp, booking. Stats: response time, jobs done, satisfaction.
|
|
1071
|
+
|
|
1072
|
+
**SaaS/Tech Product**: Hero = outcome-first ("Save 10hrs/week"). Must-have: feature grid, integration logos, product visual. CTAs: free trial, demo. Stats: performance, customer count, uptime.
|
|
1073
|
+
|
|
1074
|
+
**Restaurant/Food**: Hero = sensory/emotional ("Farm-to-table since 2019"). Must-have: menu highlights with prices, hours, location. CTAs: reserve, order, menu. Stats: years open, dishes served.
|
|
1075
|
+
|
|
1076
|
+
**E-commerce/DTC**: Hero = benefit + social proof ("Join 50K+ happy sleepers"). Must-have: features, comparison, guarantee. CTAs: shop, add to cart, "Try risk-free." Stats: units sold, return rate.
|
|
1077
|
+
|
|
1078
|
+
**Agency/Consultancy**: Hero = expertise + outcome ("Scaled 40+ brands past \u20AC1M"). Must-have: services, case studies, process. CTAs: book call, see cases, get proposal. Stats: clients, revenue, years.
|
|
1079
|
+
|
|
1080
|
+
### Content Density \u2014 Never Leave Empty Space
|
|
1081
|
+
At every viewport-height (100vh), the user should see:
|
|
1082
|
+
- At least one piece of **specific data** (number, price, time, rating)
|
|
1083
|
+
- At least one piece of **social proof** (quote, logo, rating, customer count)
|
|
1084
|
+
- At least one **visual element** (icon, illustration, decorative shape, gradient block)
|
|
1085
|
+
- A clear sense of **what section they're in** (label + headline visible)
|
|
1086
|
+
|
|
1087
|
+
Every card must contain ALL of: icon/visual, title (3-6 words), description (2-3 sentences with specific detail), optional link/metric. Never generate a card that is just a title and one sentence.
|
|
1088
|
+
|
|
1089
|
+
### Content Rhythm & Visual Pacing
|
|
1090
|
+
Alternate section density \u2014 don't make every section the same weight:
|
|
1091
|
+
- HERO: Full, rich, attention-grabbing (100vh)
|
|
1092
|
+
- TRUST BAR: Compact (py-8 to py-12)
|
|
1093
|
+
- FEATURES: Dense, multi-card grid (tall section)
|
|
1094
|
+
- HOW IT WORKS: Medium, 3-4 steps with breathing room
|
|
1095
|
+
- TESTIMONIALS: Dense, 3+ cards
|
|
1096
|
+
- PRICING: Medium, 2-3 focused cards
|
|
1097
|
+
- FAQ: Compact (accordion saves space)
|
|
1098
|
+
- FINAL CTA: Full width, bold, short (50vh max)
|
|
1099
|
+
- FOOTER: Dense with links, compact
|
|
1100
|
+
|
|
1101
|
+
Alternate backgrounds every 2-3 sections to create visual "chapters." Sprinkle trust signals throughout (not just one section).
|
|
1102
|
+
|
|
1103
|
+
### Body Copy Rules
|
|
1104
|
+
- Never write generic filler. Every sentence needs a SPECIFIC detail.
|
|
1105
|
+
- Invent plausible specifics: neighborhood names, "48 hours" not "quickly", "\u20AC49" not "affordable"
|
|
1106
|
+
- Keep paragraphs to 2-3 sentences max
|
|
1107
|
+
- Aim for 6th-grade reading level
|
|
1108
|
+
- Include section labels (UPPERCASE, letter-spacing 0.1em, accent color, 2-3 words) above every headline`}function Mc(){return`### Banned Punctuation
|
|
1109
|
+
- **Em dashes (\u2014)**: NEVER use. Biggest AI tell. Replace with periods, commas, or parentheses.
|
|
1110
|
+
- **Semicolons**: Feel academic, not conversational. Use periods instead.
|
|
1111
|
+
- **Exclamation marks**: One per page maximum. Zero is ideal for B2B.
|
|
1112
|
+
|
|
1113
|
+
### Banned Words
|
|
1114
|
+
**HARD BANNED (always rewrite):**
|
|
1115
|
+
delve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone
|
|
1116
|
+
|
|
1117
|
+
**SOFT BANNED (rewrite unless truly specific):**
|
|
1118
|
+
seamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)
|
|
1119
|
+
|
|
1120
|
+
### Banned Openers
|
|
1121
|
+
"In today's", "In an era", "In the realm", "Whether you're", "Are you tired", "Imagine a world", "Picture this", "Here's the thing", "Let's face it", "Look no further", "Say goodbye to", "Gone are the days", "It's no secret", "At its core", "At the end of the day", "When it comes to"
|
|
1122
|
+
|
|
1123
|
+
### Banned Closers
|
|
1124
|
+
"The future of [X] is here", "Your journey starts here", "Join the revolution", "Experience the difference", "See what's possible", "Ready to take the next step"
|
|
1125
|
+
|
|
1126
|
+
### Banned Structures
|
|
1127
|
+
- "It's not about X, it's about Y" (second biggest AI tell after em dashes)
|
|
1128
|
+
- "It's not just X, it's Y"
|
|
1129
|
+
- "[X]. Here's why." / "[X]. And it matters."
|
|
1130
|
+
- "Despite the challenges"
|
|
1131
|
+
- Tricolon abuse ("Fast, reliable, revolutionary") \u2014 once per page max
|
|
1132
|
+
|
|
1133
|
+
### Positive Rules
|
|
1134
|
+
- Be concrete, not abstract: "42 minutes" not "fast", "\u20AC29/month" not "affordable"
|
|
1135
|
+
- Use plain short words: use > utilize, start > commence, help > facilitate
|
|
1136
|
+
- Vary sentence length aggressively: mix 3-word, 12-word, and 25-word sentences
|
|
1137
|
+
- Front-load the benefit in the first 5 words
|
|
1138
|
+
- Write like you'd explain it in a bar \u2014 if you wouldn't say it holding a beer, rewrite it`}async function Xi(t,e,n,s,o,i,a){a({type:"agent_step",step:"designing",label:"Creating design system..."});let r=Vi(n.themeName,n.brandAssets),l=`## User Request
|
|
1139
|
+
${t}`;n.modules.length>0&&e.designSystemChanges&&(l+=`
|
|
1140
|
+
|
|
1141
|
+
## Current Shared CSS (update this)
|
|
1142
|
+
\`\`\`css
|
|
1143
|
+
${n.sharedCss}
|
|
1144
|
+
\`\`\``);let c=await it(s,o,i,{systemPrompt:r,messages:[{role:"user",content:l}],structuredOutput:{schema:Yi,name:"design_system"},maxTokens:16e3}),d;c.type!=="structured"?(M.warn("page-architect","Design system: did not get structured output, using fallback"),d={cssVariables:{},sharedCss:n.sharedCss||"",sharedJs:n.sharedJs||"",aesthetic:"default"}):(d=c.data,M.info("page-architect","Design system created",{aesthetic:d.aesthetic,varCount:Object.keys(d.cssVariables||{}).length,cssLength:d.sharedCss?.length||0}));let u=d.sharedCss||"",p=d.cssVariables;p&&typeof p=="object"&&Object.keys(p).length>0&&(u.includes(":root")||(u=`:root {
|
|
1145
|
+
${Object.entries(p).map(([I,T])=>` ${I.startsWith("--")?I:`--${I}`}: ${T};`).join(`
|
|
1146
|
+
`)}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
${u}`)),a({type:"agent_decision",step:"designing",decision:`Design system: ${d.aesthetic||"created"} | ${Object.keys(p||{}).length} variables, ${u.length} chars CSS`}),a({type:"design_system_ready",sharedCss:u,sharedJs:d.sharedJs||"",aesthetic:d.aesthetic||""}),a({type:"agent_step",step:"designing",label:"Planning modules..."});let g=zi(n.themeName,u,n.brandAssets,e.guidesNeeded),h=`## User Request
|
|
1150
|
+
${t}`;e.newModules.length>0&&(h+=`
|
|
1151
|
+
|
|
1152
|
+
## Planned Modules
|
|
1153
|
+
${e.newModules.map((w,I)=>`${I+1}. **${w.name}** \u2014 ${w.description}`).join(`
|
|
1154
|
+
`)}`),n.modules.length>0&&!e.designSystemChanges&&(h+=`
|
|
1155
|
+
|
|
1156
|
+
## Existing Modules (keeping)
|
|
1157
|
+
${n.modules.map(w=>`- ${w.moduleName}`).join(`
|
|
1158
|
+
`)}`);let b=await it(s,o,i,{systemPrompt:g,messages:[{role:"user",content:h}],structuredOutput:{schema:qi,name:"module_plan"},maxTokens:8e3}),y;return b.type!=="structured"?(M.warn("page-architect","Module planner: did not get structured output, using fallback"),y={modules:e.newModules.map(w=>({name:w.name,description:w.description,contentBrief:"Generate appropriate content",layoutNotes:"Use responsive layout"})),moduleOrder:e.newModules.map(w=>w.name),narrative:"Page generated from user request"}):(y=b.data,M.info("page-architect","Module plan",{moduleCount:y.modules.length})),a({type:"agent_decision",step:"designing",decision:`Page: ${y.narrative} | ${y.modules.length} modules planned`}),{designSystem:{cssVariables:d.cssVariables||{},sharedCss:u,sharedJs:d.sharedJs},modules:y.modules,moduleOrder:y.moduleOrder,narrative:y.narrative}}f();f();function Qi(t){let e=0,n=[];return async function(o){e>=t&&await new Promise(i=>n.push(i)),e++;try{return await o()}finally{e--,n.length>0&&n.shift()()}}}f();function Zi(t,e,n,s){let o=[];return o.push(`You are a Module Developer for vibeSpot, a HubSpot CMS page builder.
|
|
1159
|
+
|
|
1160
|
+
Your job: generate ONE HubSpot CMS module. You receive a module specification and must produce the complete module code.
|
|
1161
|
+
|
|
1162
|
+
## Theme: "${t}"
|
|
1163
|
+
|
|
1164
|
+
## Output Rules \u2014 CRITICAL
|
|
1165
|
+
You produce a single module with these fields:
|
|
1166
|
+
- **moduleName**: Exact module name (title-case, e.g., "Hero Banner")
|
|
1167
|
+
- **fieldsJson**: Valid JSON string \u2014 the module's fields.json content
|
|
1168
|
+
- **metaJson**: Valid JSON string \u2014 must include host_template_types: ["PAGE"], is_available_for_new_content: true
|
|
1169
|
+
- **moduleHtml**: HubL template ({{ module.field_name }} syntax)
|
|
1170
|
+
- **moduleCss**: Vanilla CSS (no Tailwind, no Sass, no CDN imports)
|
|
1171
|
+
- **moduleJs**: Optional vanilla JS wrapped in IIFE, or null
|
|
1172
|
+
|
|
1173
|
+
## CSS Rules
|
|
1174
|
+
- All CSS classes must use prefix "${t}-"
|
|
1175
|
+
- Use BEM naming: ${t}-moduleName__element--modifier
|
|
1176
|
+
- Reference the theme's CSS custom properties (shown below)
|
|
1177
|
+
- No CDN imports (@import url(), external <link> tags)
|
|
1178
|
+
- Use system font stacks \u2014 no Google Fonts
|
|
1179
|
+
|
|
1180
|
+
## Field Rules
|
|
1181
|
+
- Use "type": "text" (NEVER "textarea" \u2014 it's deprecated)
|
|
1182
|
+
- NEVER use "name": "name" (reserved) \u2014 use "item_name" instead
|
|
1183
|
+
- NEVER use "name": "label" (reserved) \u2014 use "section_label" instead
|
|
1184
|
+
- NEVER put literal \\n in field defaults
|
|
1185
|
+
- Wrap style fields in a "styles" group with "tab": "STYLE"
|
|
1186
|
+
- Color fields: type "color", default { "color": "#hex", "opacity": 100 }
|
|
1187
|
+
- Link fields: type "link", default { "url": { "href": "#", "type": "EXTERNAL" }, "open_in_new_tab": false, "no_follow": false }
|
|
1188
|
+
- Image fields: type "image", default { "src": "https://placehold.co/800x600/1a1a2e/ffffff?text=Replace+in+HubSpot", "alt": "Placeholder", "width": 800, "height": 600 }
|
|
1189
|
+
- For repeater groups, use "occurrence": { "min": 0, "max": 100 }
|
|
1190
|
+
|
|
1191
|
+
## Images & Assets
|
|
1192
|
+
- Use get_asset_url("${t}/assets/filename.ext") for uploaded assets
|
|
1193
|
+
- For placeholder images, use image fields with placehold.co defaults
|
|
1194
|
+
- Size placeholders appropriately (hero: 1920x800, cards: 600x400, icons: 200x200)
|
|
1195
|
+
|
|
1196
|
+
## Navigation & Anchors
|
|
1197
|
+
- Add id attribute on module root element: id="module-name-lowercased"
|
|
1198
|
+
- For nav modules, use anchor links (#features, #pricing, etc.)
|
|
1199
|
+
- Include smooth scroll behavior in nav click handlers
|
|
1200
|
+
|
|
1201
|
+
## metaJson Template
|
|
1202
|
+
{ "host_template_types": ["PAGE"], "is_available_for_new_content": true }`),e&&o.push(`
|
|
1203
|
+
|
|
1204
|
+
## Theme Shared CSS (use these custom properties)
|
|
1205
|
+
\`\`\`css
|
|
1206
|
+
${e}
|
|
1207
|
+
\`\`\``),(!n||n.includes("hubspot_rules"))&&o.push(`
|
|
1208
|
+
|
|
1209
|
+
## HubSpot CMS Rules
|
|
1210
|
+
${Ve()}`),(!n||n.includes("conversion"))&&o.push(`
|
|
1211
|
+
|
|
1212
|
+
## Conversion Guide
|
|
1213
|
+
${re()}`),s?.humanify!==!1&&n?.includes("humanify")&&o.push(`
|
|
1214
|
+
|
|
1215
|
+
## Anti-AI Copy Rules
|
|
1216
|
+
${Nc()}`),o.join("")}function Nc(){return`### Banned Punctuation
|
|
1217
|
+
- **Em dashes (\u2014)**: NEVER use. Replace with periods, commas, or parentheses. Hyphens for compounds fine.
|
|
1218
|
+
- **Semicolons**: Use periods instead in marketing copy.
|
|
1219
|
+
- **Exclamation marks**: One per page max. Zero ideal for B2B.
|
|
1220
|
+
|
|
1221
|
+
### Banned Words
|
|
1222
|
+
**HARD BANNED:**
|
|
1223
|
+
delve, tapestry, multifaceted, utilize, harness, bolster, underscore, illuminate, facilitate, fostering, garner, pivotal, commence, endeavor, myriad, plethora, pertinent, aforementioned, wherein, henceforth, beacon, synergy, paradigm, bespoke, holistic, spearhead, embark, reimagine, cultivate, cornerstone
|
|
1224
|
+
|
|
1225
|
+
**SOFT BANNED (rewrite unless truly earned):**
|
|
1226
|
+
seamless, cutting-edge, groundbreaking, game-changer, revolutionary, transformative, innovative, robust, comprehensive, foundational, nuanced, landscape (abstract), realm, catalyst, empower, elevate, unlock, streamline, optimize, curated, navigate (abstract)
|
|
1227
|
+
|
|
1228
|
+
### Banned Openers
|
|
1229
|
+
Never start a heading or paragraph with: "In today's", "In an era", "In the realm", "Whether you're", "Are you tired", "Imagine a world", "Picture this", "Here's the thing", "Let's face it", "Look no further", "Say goodbye to", "Gone are the days", "It's no secret", "At its core", "At the end of the day", "When it comes to"
|
|
1230
|
+
|
|
1231
|
+
### Banned Closers
|
|
1232
|
+
Never end with: "The future of [X] is here", "Your journey starts here", "Join the revolution", "Experience the difference", "See what's possible", "Ready to take the next step"
|
|
1233
|
+
|
|
1234
|
+
### Banned Structures
|
|
1235
|
+
- "It's not about X, it's about Y" \u2014 just state Y
|
|
1236
|
+
- "It's not just X, it's Y" \u2014 just state Y
|
|
1237
|
+
- "[X]. Here's why." / "[X]. And it matters."
|
|
1238
|
+
- "Despite the challenges"
|
|
1239
|
+
- Tricolon abuse ("Fast, reliable, revolutionary") \u2014 max once per page
|
|
1240
|
+
|
|
1241
|
+
### Positive Writing Rules
|
|
1242
|
+
- Be concrete: "42 minutes" not "fast", "\u20AC29/month" not "affordable", "2,847 teams" not "thousands"
|
|
1243
|
+
- Use plain words: use > utilize, start > commence, help > facilitate, enough > sufficient
|
|
1244
|
+
- Vary sentence length: mix 3-word, 12-word, and 25-word sentences
|
|
1245
|
+
- Front-load the benefit in the first 5 words
|
|
1246
|
+
- Write like you'd say it in a bar. If you wouldn't say it holding a beer, rewrite it.
|
|
1247
|
+
- One adjective per noun max. Zero is often better.
|
|
1248
|
+
|
|
1249
|
+
### Testimonial Rules
|
|
1250
|
+
- Include a specific problem that was solved
|
|
1251
|
+
- Include a concrete detail (time saved, money saved, specific task)
|
|
1252
|
+
- Keep slightly imperfect (fragments OK, mild hedging like "honestly didn't think")
|
|
1253
|
+
- Full names, specific roles (not "John D., CEO")
|
|
1254
|
+
- Never start with "This product is..." \u2014 start with the person's situation
|
|
1255
|
+
- Vary length and voice across testimonials`}function er(t,e,n){let s=[];return s.push(`## User Request
|
|
1256
|
+
${t}`),s.push(`
|
|
1257
|
+
|
|
1258
|
+
## Module Specification
|
|
1259
|
+
- **Name**: ${e.name}
|
|
1260
|
+
- **Description**: ${e.description}
|
|
1261
|
+
- **Content Brief**: ${e.contentBrief}
|
|
1262
|
+
- **Layout Notes**: ${e.layoutNotes}`),n&&(s.push(`
|
|
1263
|
+
|
|
1264
|
+
## Existing Module Code (modify this)
|
|
1265
|
+
**fields.json:**
|
|
1266
|
+
\`\`\`json
|
|
1267
|
+
${n.fieldsJson}
|
|
1268
|
+
\`\`\`
|
|
1269
|
+
|
|
1270
|
+
**module.html:**
|
|
1271
|
+
\`\`\`html
|
|
1272
|
+
${n.moduleHtml}
|
|
1273
|
+
\`\`\`
|
|
1274
|
+
|
|
1275
|
+
**module.css:**
|
|
1276
|
+
\`\`\`css
|
|
1277
|
+
${n.moduleCss}
|
|
1278
|
+
\`\`\``),n.moduleJs&&s.push(`
|
|
1279
|
+
**module.js:**
|
|
1280
|
+
\`\`\`js
|
|
1281
|
+
${n.moduleJs}
|
|
1282
|
+
\`\`\``)),s.join("")}var tr={type:"object",properties:{moduleName:{type:"string"},fieldsJson:{type:"string",description:"Complete fields.json content as a JSON string"},metaJson:{type:"string",description:"Complete meta.json content as a JSON string"},moduleHtml:{type:"string",description:"Complete module.html HubL template content"},moduleCss:{type:"string",description:"Complete module.css vanilla CSS content"},moduleJs:{type:"string",description:"Optional module.js vanilla JS content, or empty string if not needed"}},required:["moduleName","fieldsJson","metaJson","moduleHtml","moduleCss"]};async function nr(t,e,n,s,o,i,a,r,l,c,d){l({type:"agent_step",step:"developing",label:`Generating ${e.length} module${e.length===1?"":"s"}...`});let u=Zi(s,n,c,d),p=Qi(r),g=e.length,h=e.map((y,w)=>p(async()=>{l({type:"module_progress",module:y.name,status:"generating",current:w+1,total:g});let I="";for(let T=0;T<2;T++)try{T>0&&(M.warn("module-developer",`${y.name}: retrying after failure (attempt ${T+1})`),l({type:"module_progress",module:y.name,status:"retrying",current:w+1,total:g}));let J=await sr(t,y,u,o,i,a);return l({type:"module_progress",module:y.name,status:"complete",current:w+1,total:g,moduleFiles:J}),{moduleName:y.name,module:J}}catch(J){I=J instanceof Error?J.message:typeof J=="object"&&J!==null?JSON.stringify(J):String(J),M.error("module-developer",`Failed: ${y.name} (attempt ${T+1})`,{error:I})}return l({type:"module_progress",module:y.name,status:"failed",current:w+1,total:g}),{moduleName:y.name,error:I}}));return(await Promise.allSettled(h)).map(y=>y.status==="fulfilled"?y.value:{moduleName:"unknown",error:y.reason instanceof Error?y.reason.message:String(y.reason)})}async function sr(t,e,n,s,o,i,a=0){let r=er(t,e,e.existingCode),l=await it(s,o,i,{systemPrompt:n,messages:[{role:"user",content:r}],structuredOutput:{schema:tr,name:"module_output"},maxTokens:16e3});if(l.type!=="structured"){if(a<2)return M.warn("module-developer",`${e.name}: no structured output, retry ${a+1}`),sr(t,e,n,s,o,i,a+1);throw new Error(`Module "${e.name}" failed to produce structured output after ${a+1} attempts`)}let c=l.data,d=typeof c.fieldsJson=="string"?c.fieldsJson:JSON.stringify(c.fieldsJson,null,2),u=typeof c.metaJson=="string"?c.metaJson:JSON.stringify(c.metaJson,null,2);return{moduleName:e.name,fieldsJson:d,metaJson:u,moduleHtml:String(c.moduleHtml||""),moduleCss:String(c.moduleCss||""),moduleJs:c.moduleJs?String(c.moduleJs):void 0}}f();function ir(t,e,n){return n({type:"agent_step",step:"quality_check",label:"Quality check \u2014 fixing common issues..."}),t.map(s=>{let o=[],i={...s};i.fieldsJson=or(i.fieldsJson,i.moduleName,"fieldsJson",o),i.metaJson=or(i.metaJson,i.moduleName,"metaJson",o),i.fieldsJson=_c(i.fieldsJson,i.moduleName,o),i.fieldsJson=Pc(i.fieldsJson,i.moduleName,o),i.moduleCss=Rc(i.moduleCss,i.moduleName,"moduleCss",o),Oc(i.moduleCss,i.moduleName,e,o),i.moduleHtml=jc(i.moduleHtml,i.moduleName,o),i.metaJson=Fc(i.metaJson,i.moduleName,o);let a=o.every(r=>r.autoFixed);return o.length>0&&M.info("validator",`${i.moduleName}: ${o.length} issues`,{autoFixed:o.filter(r=>r.autoFixed).length,unfixed:o.filter(r=>!r.autoFixed).length}),{module:i,issues:o,valid:a}})}function or(t,e,n,s){return!t||t.trim()===""?(s.push({module:e,field:n,message:`Empty ${n}`,autoFixed:n==="metaJson"}),n==="metaJson"?JSON.stringify({host_template_types:["PAGE"],is_available_for_new_content:!0}):t):(je(t)===null&&s.push({module:e,field:n,message:`Invalid JSON in ${n}`,autoFixed:!1}),t)}function _c(t,e,n){let s=t;return/"name"\s*:\s*"name"/g.test(s)&&(n.push({module:e,field:"fieldsJson",message:'"name" is a reserved field name \u2192 renamed to "item_name"',autoFixed:!0}),s=s.replace(/"name"\s*:\s*"name"/g,'"name": "item_name"')),/"name"\s*:\s*"label"/g.test(s)&&(n.push({module:e,field:"fieldsJson",message:'"label" is a reserved field name \u2192 renamed to "section_label"',autoFixed:!0}),s=s.replace(/"name"\s*:\s*"label"/g,'"name": "section_label"')),s}function Pc(t,e,n){let s=t;return/"type"\s*:\s*"textarea"/g.test(s)&&(n.push({module:e,field:"fieldsJson",message:'"textarea" is deprecated \u2192 changed to "text"',autoFixed:!0}),s=s.replace(/"type"\s*:\s*"textarea"/g,'"type": "text"')),s}function Rc(t,e,n,s){if(!t)return t;let o=t,i=/@import\s+url\([^)]*(?:fonts\.googleapis|cdnjs|unpkg|jsdelivr)[^)]*\)\s*;?/gi;return i.test(o)&&(s.push({module:e,field:n,message:"CDN @import removed (external imports not allowed)",autoFixed:!0}),o=o.replace(i,"/* CDN import removed */")),o}function Oc(t,e,n,s){if(!t)return;let o=/\.([a-zA-Z][\w-]*)/g,i,a=0;for(;(i=o.exec(t))!==null;){let r=i[1];r.startsWith(n+"-")||r==="visible"||r==="active"||r==="scroll-animate"||r.startsWith("body-wrapper")||r.startsWith("dnd-")||r.startsWith("row-")||a++}a>3&&s.push({module:e,field:"moduleCss",message:`${a} CSS classes without "${n}-" prefix`,autoFixed:!1})}function jc(t,e,n){if(!t)return t;let s=t,o=/\{%[-~]?\s*(if|for|block|macro|endif|endfor|endblock|endmacro)\b[^%]*%\}/g,i=[],a;for(;(a=o.exec(s))!==null;){let c=a[1],d=!c.startsWith("end"),u=d?c:c.replace("end","");i.push({tag:c,isOpen:d,baseTag:u,start:a.index,end:a.index+a[0].length})}let r=[],l=[];for(let c=0;c<i.length;c++)if(i[c].isOpen)r.push(c);else{let d=-1;for(let u=r.length-1;u>=0;u--)if(i[r[u]].baseTag===i[c].baseTag){d=u;break}d!==-1?r.splice(d,1):l.push(c)}if(l.length>0){for(let c=l.length-1;c>=0;c--){let d=i[l[c]];s=s.slice(0,d.start)+`<!-- removed orphan {% ${d.tag} %} -->`+s.slice(d.end)}n.push({module:e,field:"moduleHtml",message:`Removed ${l.length} orphan closing tag${l.length===1?"":"s"} with no matching opener`,autoFixed:!0})}if(r.length>0){let c=r.map(u=>i[u].baseTag),d=c.reverse().map(u=>`{% end${u} %}`).join(`
|
|
1283
|
+
`);s=`${s}
|
|
1284
|
+
${d}`,n.push({module:e,field:"moduleHtml",message:`Added ${c.length} missing closing tag${c.length===1?"":"s"}: ${c.map(u=>`{% end${u} %}`).join(", ")}`,autoFixed:!0})}return/\bnow\(\)/.test(s)&&(s=s.replace(/\bnow\(\)/g,"local_dt"),n.push({module:e,field:"moduleHtml",message:"Replaced now() with local_dt (now() is not valid HubL)",autoFixed:!0})),s}function Fc(t,e,n){let s=je(t);if(!s||typeof s!="object")return t;let o=s,i=!1;return o.host_template_types||(o.host_template_types=["PAGE"],i=!0),o.is_available_for_new_content===void 0&&(o.is_available_for_new_content=!0,i=!0),i?(n.push({module:e,field:"metaJson",message:"Added missing meta.json required fields",autoFixed:!0}),JSON.stringify(o,null,2)):t}import{execSync as Jc}from"child_process";async function rr(t,e,n,s,o,i,a,r){let l=Date.now(),c=i;if(Ut(n)){let O={"claude-code":"claude","gemini-cli":"gemini","codex-cli":"codex"}[n];if(O)try{Jc(`command -v ${O}`,{stdio:"ignore"})}catch{throw new Error(`CLI engine "${n}" requires "${O}" to be installed and on your PATH.`)}}let d=await Ki(t,e,n,s,o,a,r);if(d.intent==="question"&&d.answer){let H=Date.now()-l;return a({type:"pipeline_complete",modulesGenerated:0,modulesUnchanged:e.modules.length,durationMs:H}),{modules:[...e.modules],moduleOrder:e.moduleOrder,sharedCss:e.sharedCss,sharedJs:e.sharedJs,assistantMessage:d.answer,stats:{modulesGenerated:0,modulesUnchanged:e.modules.length,modulesFailed:0,durationMs:H}}}let u=null,p=e.sharedCss,g=e.sharedJs;(d.intent==="create"||d.designSystemChanges)&&(u=await Xi(t,d,e,n,s,o,a),p=u.designSystem.sharedCss||p,g=u.designSystem.sharedJs||g,a({type:"blueprint_ready",moduleOrder:u.moduleOrder,sharedCss:p,sharedJs:g}));let b=[];if(u)for(let H of u.modules)b.push({name:H.name,description:H.description,contentBrief:H.contentBrief,layoutNotes:H.layoutNotes});else{for(let H of d.newModules)b.push({name:H.name,description:H.description,contentBrief:"Generate appropriate content based on the user request",layoutNotes:"Use responsive layout matching the existing design system"});for(let H of d.affectedModules){let O=e.modules.find(te=>te.moduleName===H);O&&b.push({name:H,description:`Modify existing module: ${H}`,contentBrief:"Apply the user's requested changes",layoutNotes:"Preserve existing layout unless changes are requested",existingCode:O})}}let y=[],w=[];if(b.length>0){let H=await nr(t,b,p,e.themeName,n,s,o,c,a,d.guidesNeeded,e.brandAssets);for(let O of H)O.module?y.push(O.module):w.push(O.moduleName)}let I=null;if(y.length>0){I=ir(y,e.themeName,a),y=I.map(O=>O.module);let H=I.reduce((O,te)=>O+te.issues.length,0);if(H>0){let O=I.reduce((ie,Ks)=>ie+Ks.issues.filter(It=>It.autoFixed).length,0);M.info("pipeline",`Quality check: ${H} issues, ${O} auto-fixed`);let te=I.flatMap(ie=>ie.issues).map(ie=>`${ie.autoFixed?"\u2713":"\u26A0"} ${ie.module}: ${ie.message}`).join(`
|
|
1285
|
+
`);a({type:"agent_decision",step:"quality_check",decision:`${H} issues found, ${O} auto-fixed
|
|
1286
|
+
${te}`})}else a({type:"agent_decision",step:"quality_check",decision:"All modules passed quality checks"})}let T=Dc(e,d,y,u,r),J=Hc(e,d,u,T),D=Date.now()-l,R=y.length,L=d.unchangedModules.length,Z=I?I.flatMap(H=>H.issues):[],G=Lc(d,R,L,w,D,u,Z);return w.length>0?a({type:"pipeline_partial",succeeded:y.map(H=>H.moduleName),failed:w,durationMs:D}):a({type:"pipeline_complete",modulesGenerated:R,modulesUnchanged:L,durationMs:D}),{modules:T,moduleOrder:J,sharedCss:p,sharedJs:g,assistantMessage:G,stats:{modulesGenerated:R,modulesUnchanged:L,modulesFailed:w.length,durationMs:D}}}function Dc(t,e,n,s,o){let i=[],a=new Set;for(let r of n)i.push(r),a.add(r.moduleName);for(let r of e.unchangedModules){if(a.has(r))continue;let l=t.modules.find(c=>c.moduleName===r);l&&(i.push(l),a.add(r))}if(e.reuseModules)for(let r of e.reuseModules){if(a.has(r.name))continue;let l=o.find(c=>c.name===r.name&&c.module);l&&l.module&&(i.push(l.module),a.add(r.name))}return i}function Hc(t,e,n,s){if(n?.moduleOrder?.length)return n.moduleOrder;if(e.intent==="create")return s.map(r=>r.moduleName);let o=[...t.moduleOrder],i=[...e.newModules.map(r=>({name:r.name,position:r.position})),...(e.reuseModules||[]).map(r=>({name:r.name,position:r.position}))].sort((r,l)=>r.position-l.position);for(let r of i){let l=Math.min(r.position,o.length);o.splice(l,0,r.name)}let a=new Set(s.map(r=>r.moduleName));return o.filter(r=>a.has(r))}function Lc(t,e,n,s,o,i,a){let r=Math.round(o/1e3),l=[];if(t.intent==="create")l.push(`Created ${e} module${e===1?"":"s"} in ${r}s.`);else if(t.intent==="modify"||t.intent==="style_change")l.push(`Updated ${e} module${e===1?"":"s"} in ${r}s.`),n>0&&l.push(`${n} module${n===1?"":"s"} unchanged.`);else if(t.intent==="add"){let u=t.newModules.map(p=>p.name).join(", ");l.push(`Added ${u} in ${r}s.`)}else t.intent==="remove"?l.push(`Removed modules in ${r}s.`):t.intent==="rearrange"&&l.push(`Rearranged modules in ${r}s.`);i?.narrative&&l.push(`
|
|
1287
|
+
|
|
1288
|
+
${i.narrative}`),s.length>0&&l.push(`
|
|
1289
|
+
|
|
1290
|
+
**Failed:** ${s.join(", ")}. You can retry these individually.`);let c=a.filter(u=>!u.autoFixed),d=a.filter(u=>u.autoFixed);if(d.length>0||c.length>0){let u=[];d.length>0&&u.push(`**Auto-fixed:** ${d.map(p=>`${p.module}: ${p.message}`).join(", ")}`),c.length>0&&u.push(`**Warnings:** ${c.map(p=>`${p.module}: ${p.message}`).join(", ")}`),l.push(`
|
|
1291
|
+
|
|
1292
|
+
${u.join(`
|
|
1293
|
+
`)}`)}return l.join("")}var Rs=null;function ar(t){Rs=t}var rt=null;function Wt(){return rt!==null}function xt(t){if(rt){let e=v();if(!e||e.id!==rt){M.warn("ai-handler","Session changed during generation \u2014 discarding AI output");return}}ze("assistant",t),_i(t,Rs||void 0),j()}async function Os(t,e,n,s){let o=v();if(!o)throw new Error("No active session");rt=o.id;let a=s?.length?Li(s):void 0;try{let r=E(),l=r.aiEngine||js();switch(l){case"anthropic-api":case"api":{let c=ge("anthropic-api",r);if(!c)throw new Error("Anthropic API key not configured. Open Settings to add one.");await Ri(t,c,o.themeName,r.anthropicApiModel||"claude-sonnet-4-6",e,n,xt,a);break}case"openai-api":{let c=ge("openai-api",r);if(!c)throw new Error("OpenAI API key not configured. Open Settings to add one.");await Oi(t,c,o.themeName,r.openaiApiModel||"gpt-4o",e,n,xt,a);break}case"gemini-api":{let c=ge("gemini-api",r);if(!c)throw new Error("Gemini API key not configured. Open Settings to add one.");await ji(t,c,o.themeName,e,n,xt,a);break}case"claude-code":await Fi(t,o.themeName,e,n,xt,a);break;case"gemini-cli":await ks("gemini",t,o.themeName,e,n,xt,a);break;case"codex-cli":await ks("codex",t,o.themeName,e,n,xt,a);break;default:throw new Error(`Unknown AI engine: ${l}. Open Settings to configure one.`)}}finally{rt=null,Rs=null}}function js(){let t=E();if(t.anthropicApiKey||process.env.ANTHROPIC_API_KEY)return"anthropic-api";if(t.openaiApiKey||process.env.OPENAI_API_KEY)return"openai-api";if(t.geminiApiKey||process.env.GEMINI_API_KEY||process.env.GOOGLE_AI_API_KEY)return"gemini-api";try{return Ps("claude --version",{stdio:"pipe"}),"claude-code"}catch{}try{return Ps("gemini --version",{stdio:"pipe"}),"gemini-cli"}catch{}try{return Ps("codex --version",{stdio:"pipe"}),"codex-cli"}catch{}throw new Error("No AI engine available. Open Settings to configure one.")}function Gc(){let t=v(),e=Se(),n=e?[...e.modules]:[...t.modules],s=e?[...e.moduleOrder]:[...t.moduleOrder];return{modules:n,moduleOrder:s,sharedCss:e?.sharedCss||t.sharedCss,sharedJs:e?.sharedJs||t.sharedJs,messages:[...t.messages],themeName:t.themeName,themePath:t.themePath,brandAssets:t.brandAssets?{...t.brandAssets}:void 0}}function Uc(t){let e=t.aiEngine||js();if(!Fn(e))throw new Error("Agentic pipeline is not available for this engine.");if(Ut(e)){let o="";return e==="claude-code"&&(o=t.claudeCodeModel||""),{engine:e,apiKey:"",model:o}}let n=ge(e,t);if(!n)throw new Error(`API key not configured for ${e}. Open Settings to add one.`);let s;switch(e){case"anthropic-api":s=t.anthropicApiModel||"claude-sonnet-4-6";break;case"openai-api":s=t.openaiApiModel||"gpt-4o";break;case"gemini-api":s="gemini-2.5-flash";break;default:s=""}return{engine:e,apiKey:n,model:s}}async function lr(t,e,n){let s=v();if(!s)throw new Error("No active session");let o=s.id;rt=o;try{let i=E(),{engine:a,apiKey:r,model:l}=Uc(i),c=i.agenticConcurrency||20,d=Gc(),u=qe(),p=new Set(d.modules.map(y=>y.moduleName)),g=u.filter(y=>!p.has(y.module.moduleName)).map(y=>({name:y.module.moduleName,usedIn:y.usedIn})),h=await rr(t,d,a,r,l,c,e,g),b=v();if(!b||b.id!==o)throw M.warn("ai-handler","Session changed during agentic generation \u2014 discarding output"),new Error("Session changed during generation");return h}finally{rt=null}}function cr(t,e){Oe({modules:t.modules,sharedCss:t.sharedCss,sharedJs:t.sharedJs}),ht(t.moduleOrder),ze("assistant",t.assistantMessage,e),j()}function dr(){let t=E(),e=t.aiEngine||js();return Fn(e)?t.agenticMode===void 0?{useAgentic:!1,needsPrompt:!0}:{useAgentic:t.agenticMode,needsPrompt:!1}:{useAgentic:!1,needsPrompt:!1,reason:"Agentic pipeline is not available for this engine."}}ee();f();import{spawn as Fs}from"child_process";var Qe=new Map;function ur(t,e,n){t.stdout?.on("data",o=>{e.output+=o.toString()}),t.stderr?.on("data",o=>{e.output+=o.toString()}),t.on("close",o=>{e.status=o===0?"completed":"failed",e.exitCode=o,e.completedAt=Date.now()}),t.on("error",o=>{e.status="failed",e.output+=`
|
|
1294
|
+
Process error: ${o.message}`,e.completedAt=Date.now()}),setTimeout(()=>{e.status==="running"&&(t.kill(),e.status="failed",e.output+=`
|
|
1295
|
+
Process timed out`,e.completedAt=Date.now())},n||3e5)}function Bt(t,e,n,s){let o=`job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,i={id:o,command:`${t} ${e.join(" ")}`,description:n,status:"running",output:"",exitCode:null,startedAt:Date.now(),completedAt:null};Qe.set(o,i);let a=Fs(t,e,{cwd:s?.cwd,stdio:[s?.stdin?"pipe":"ignore","pipe","pipe"],env:{...process.env,...s?.env},shell:process.platform==="win32"});return s?.stdin&&a.stdin&&(a.stdin.write(s.stdin),a.stdin.end()),ur(a,i,s?.timeout),o}function at(t,e,n){let s=`job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,o={id:s,command:t,description:e,status:"running",output:"",exitCode:null,startedAt:Date.now(),completedAt:null};Qe.set(s,o);let i=t.split(" "),a=Fs(i[0],i.slice(1),{cwd:n?.cwd,stdio:["ignore","pipe","pipe"],env:{...process.env,...n?.env},shell:!0});return ur(a,o,n?.timeout),s}function Jn(t){return Qe.get(t)}function Wc(){let t=Date.now()-18e5;for(let[e,n]of Qe)n.completedAt&&n.completedAt<t&&Qe.delete(e)}setInterval(Wc,600*1e3);function Dn(t,e,n){let s=`job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,o={id:s,command:t,description:e,status:"running",output:"",exitCode:null,startedAt:Date.now(),completedAt:null,listeners:new Set};Qe.set(s,o);let i=t.split(" "),a=Fs(i[0],i.slice(1),{cwd:n?.cwd,stdio:["ignore","pipe","pipe"],env:{...process.env,...n?.env},shell:!0}),r=c=>{for(let d of o.listeners)try{d(c)}catch{}};a.stdout?.on("data",c=>{let d=c.toString();o.output+=d,r(d)}),a.stderr?.on("data",c=>{let d=c.toString();o.output+=d,r(d)}),a.on("close",c=>{o.status=c===0?"completed":"failed",o.exitCode=c,o.completedAt=Date.now(),o.listeners.clear()}),a.on("error",c=>{o.status="failed",o.output+=`
|
|
1296
|
+
Process error: ${c.message}`,o.completedAt=Date.now(),o.listeners.clear()});let l=n?.timeout||3e5;return setTimeout(()=>{o.status==="running"&&(a.kill(),o.status="failed",o.output+=`
|
|
1297
|
+
Process timed out`,o.completedAt=Date.now(),o.listeners.clear())},l),s}function mr(t,e){let n=Qe.get(t);if(!n||!("listeners"in n))return;let s=n;if(s.output)try{e(s.output)}catch{}s.listeners.add(e)}function pr(t,e){let n=Qe.get(t);!n||!("listeners"in n)||n.listeners.delete(e)}X();f();import{existsSync as Ct,readdirSync as fr,rmSync as Bc}from"fs";import{join as At,basename as Kc}from"path";import{homedir as Vc}from"os";import{execFileSync as gr}from"child_process";ee();pn();tt();ee();X();var hr=process.platform==="win32"?{shell:!0}:{},$e=At(Vc(),"vibespot-themes"),Hn=null,Yc=5e3;function Ln(){if(Hn&&Date.now()-Hn.ts<Yc)return Hn.data;let t=[];if(Ct($e))try{for(let e of fr($e,{withFileTypes:!0}))if(e.isDirectory()){let n=At($e,e.name,"theme.json");if(Ct(n)){let s=0,o=At($e,e.name,"modules");if(Ct(o))try{s=fr(o,{withFileTypes:!0}).filter(i=>i.isDirectory()).length}catch{}t.push({name:e.name,moduleCount:s})}}}catch{}return Hn={data:t,ts:Date.now()},t}function yr(t){let e=v(),n=on(),s=!1;try{gr("hs",["--version"],{encoding:"utf-8",stdio:"pipe",...hr}),s=!0}catch{}let o=bt().sort((a,r)=>r.updatedAt-a.updatedAt).slice(0,10),i=Ln();m(t,200,{hasActiveSession:!!e,activeSession:e?{id:e.id,themeName:e.themeName,moduleCount:e.modules.length}:null,hsInstalled:s,aiAvailable:n.availableEngines.length>0,availableEngines:n.availableEngines,activeEngine:n.activeEngine,sessions:o,localThemes:i})}function br(t,e){_(t,n=>{try{if(Wt()){m(e,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{name:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"Theme name is required"});return}let o=s.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,""),i=At($e,o);we($e),Ct(i)&&Bc(i,{recursive:!0,force:!0}),mn(i,o),Ft(i,o),j(),m(e,200,{ok:!0,themeName:o,themePath:i})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Sr(t,e){_(t,n=>{try{if(Wt()){m(e,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{name:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"Theme name is required"});return}let o=s.replace(/^\/+|\/+$/g,"");if(!o){m(e,400,{error:"Theme name is required"});return}let i=he(),a=E(),r=o.includes("/")||o.includes("@")?o.replace(/[@/]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""):o,l=At($e,r);we($e),a.hubspotUploadMode==="cli"||!i?(gr("hs",["cms","fetch",o,l],{encoding:"utf-8",stdio:"pipe",...hr}),Ft(l,r),Ht(l),j(),m(e,200,{ok:!0,themeName:r,themePath:l,moduleCount:v()?.modules.length||0})):Rt(i,o,l).then(()=>{Ft(l,r),Ht(l),j(),m(e,200,{ok:!0,themeName:r,themePath:l,moduleCount:v()?.modules.length||0})}).catch(c=>{m(e,500,{error:c instanceof Error?c.message:String(c)})})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function vr(t,e){_(t,n=>{try{if(Wt()){m(e,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{path:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"Theme path is required"});return}let o=s;if(Ct(o)||(o=At($e,s)),!Ct(o)){m(e,400,{error:`Theme folder not found: ${s}`});return}let i=Kc(o);Ft(o,i),Ht(o),j(),m(e,200,{ok:!0,themeName:i,themePath:o,moduleCount:v()?.modules.length||0})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function wr(t,e){_(t,n=>{try{if(Wt()){m(e,409,{error:"Cannot switch projects while AI is generating.",generating:!0});return}let{sessionId:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"Session ID is required"});return}let o=_n(s);if(!o){m(e,404,{error:"Session not found"});return}m(e,200,{ok:!0,themeName:o.themeName,themePath:o.themePath,moduleCount:o.modules.length,messageCount:o.messages.length})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function xr(t,e){_(t,n=>{try{let{apiKey:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"API key is required"});return}Y({anthropicApiKey:s}),m(e,200,{ok:!0})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Cr(t){let e=he();if(!e){m(t,200,{themes:[],error:"No HubSpot account connected"});return}(async()=>{let n=await uo(e);if(n.length===0){m(t,200,{themes:[]});return}let s=[],o=n.map(async r=>{let l=r.path||r.name;try{let c=await ln(e,`${l}/theme.json`);c&&!c.folder&&s.push({name:r.name,path:l})}catch{}});await Promise.all(o),s.sort((r,l)=>r.name.localeCompare(l.name));let i=Ln(),a=new Set(i.map(r=>r.name));m(t,200,{themes:s.map(r=>({...r,existsLocally:a.has(r.name)}))})})().catch(n=>{m(t,200,{themes:[],error:n instanceof Error?n.message:String(n)})})}f();import{existsSync as zc,readFileSync as qc,appendFileSync as Xc}from"fs";import{join as Ar}from"path";import{homedir as Ir}from"os";ee();tt();X();var Kt={data:{},ts:0},Qc=600*1e3,kr={"claude-code":[{id:"sonnet",label:"Claude Sonnet (default)"},{id:"opus",label:"Claude Opus"},{id:"haiku",label:"Claude Haiku"}],"codex-cli":[{id:"o4-mini",label:"o4 Mini (default)"},{id:"o3",label:"o3"},{id:"gpt-4o",label:"GPT-4o"}]};async function Zc(t){let e=await fetch("https://api.anthropic.com/v1/models",{headers:{"x-api-key":t,"anthropic-version":"2023-06-01"}});return e.ok?(await e.json()).data.filter(s=>!s.id.startsWith("claude-3-")&&!s.id.startsWith("claude-2")).map(s=>({id:s.id,label:s.display_name})):[]}async function ed(t){let e=await fetch("https://api.openai.com/v1/models",{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return[];let n=await e.json(),s=/^(gpt-4o|gpt-4o-mini|o[1-4](-mini)?|o[1-4]-pro)$/;return n.data.filter(o=>s.test(o.id)).sort((o,i)=>o.id.localeCompare(i.id)).map(o=>({id:o.id,label:o.id}))}async function td(t){let e=await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${t}`);return e.ok?(await e.json()).models.filter(s=>s.name.includes("gemini-2")).map(s=>({id:s.name.replace("models/",""),label:s.displayName})):[]}async function nd(){if(Date.now()-Kt.ts<Qc&&Object.keys(Kt.data).length>0)return Kt.data;let t=E(),e={...kr},n=[],s=ge("anthropic-api",t);s&&n.push(Zc(s).then(a=>{a.length&&(e["anthropic-api"]=a)}).catch(()=>{}));let o=ge("openai-api",t);o&&n.push(ed(o).then(a=>{a.length&&(e["openai-api"]=a)}).catch(()=>{}));let i=ge("gemini-api",t);return i&&n.push(td(i).then(a=>{a.length&&(e["gemini-api"]=a,e["gemini-cli"]=a)}).catch(()=>{})),await Promise.all(n),Kt.data=e,Kt.ts=Date.now(),e}function $r(t){let e=on(),n=E(),s={aiEngine:n.aiEngine||null,claudeCodeModel:n.claudeCodeModel||null,anthropicApiModel:n.anthropicApiModel||null,openaiApiModel:n.openaiApiModel||null,hubspotUploadMode:n.hubspotUploadMode||"api",hubspotAccounts:(n.hubspotAccounts||[]).map(r=>({portalId:r.portalId,portalName:r.portalName,dataCenter:r.dataCenter})),activeHubSpotAccount:n.activeHubSpotAccount||null,enabledCLITools:n.enabledCLITools||[],agenticMode:n.agenticMode,agenticConcurrency:n.agenticConcurrency},o=bt().length,i=Ln().length,a=ut();nd().then(r=>{m(t,200,{version:a,environment:e,config:s,models:r,sessionCount:o,localThemeCount:i})}).catch(()=>{m(t,200,{version:a,environment:e,config:s,models:kr,sessionCount:o,localThemeCount:i})})}function Tr(t,e){_(t,n=>{try{let{engine:s,model:o}=JSON.parse(n);if(!["claude-code","anthropic-api","openai-api","gemini-cli","gemini-api","codex-cli"].includes(s)){m(e,400,{error:`Invalid engine: ${s}`});return}let a={aiEngine:s};if(o)switch(s){case"claude-code":a.claudeCodeModel=o;break;case"anthropic-api":a.anthropicApiModel=o;break;case"openai-api":a.openaiApiModel=o;break}Y(a),m(e,200,{ok:!0,engine:s})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Er(t,e){_(t,n=>{try{let{provider:s,apiKey:o}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"provider is required"});return}if(!o){let l={};switch(s){case"anthropic":l.anthropicApiKey="";break;case"openai":l.openaiApiKey="";break;case"gemini":l.geminiApiKey="";break;default:m(e,400,{error:`Unknown provider: ${s}`});return}Y(l),m(e,200,{ok:!0,provider:s,deleted:!0});return}let i={};switch(s){case"anthropic":i.anthropicApiKey=o;break;case"openai":i.openaiApiKey=o;break;case"gemini":i.geminiApiKey=o;break;default:m(e,400,{error:`Unknown provider: ${s}`});return}Y(i);let a=null;if(!E().aiEngine){let c={anthropic:"anthropic-api",openai:"openai-api",gemini:"gemini-api"}[s];c&&(Y({aiEngine:c}),a=c)}m(e,200,{ok:!0,provider:s,autoSelectedEngine:a})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Mr(t,e){_(t,n=>{try{let{tool:s}=JSON.parse(n),o={hubspot:{cmd:"npm install -g @hubspot/cli",desc:"Installing HubSpot CLI"},claude:{cmd:"npm install -g @anthropic-ai/claude-code",desc:"Installing Claude Code"},gemini:{cmd:"npm install -g @google/gemini-cli",desc:"Installing Gemini CLI"},codex:{cmd:process.platform==="darwin"?"brew install --cask codex":"npm install -g @openai/codex",desc:"Installing OpenAI Codex"},gh:{cmd:process.platform==="darwin"?"brew install gh":"npm install -g @cli/gh",desc:"Installing GitHub CLI"}},i=o[s];if(!i){m(e,400,{error:`Unknown tool: ${s}. Valid: ${Object.keys(o).join(", ")}`});return}let a=at(i.cmd,i.desc,{timeout:12e4});m(e,200,{ok:!0,jobId:a})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Nr(t,e){_(t,n=>{try{let s=JSON.parse(n||"{}"),o=E(),i=o.hubspotUploadMode||"api";if(s.personalAccessKey)if(i==="api"){an(s.personalAccessKey).then(a=>{Qt(s.personalAccessKey,a.portalId,a.portalName,a.dataCenter),m(e,200,{ok:!0,portalName:a.portalName,portalId:a.portalId,dataCenter:a.dataCenter})}).catch(a=>{m(e,400,{error:a instanceof Error?a.message:String(a)})});return}else{if(!Me().found){m(e,400,{error:"HubSpot CLI not installed",needsInstall:!0});return}let r=Bt("hs",["auth",`--pak=${s.personalAccessKey}`],"Authenticating with HubSpot",{timeout:3e4});m(e,200,{ok:!0,jobId:r});return}if(i==="api"){let a=o.hubspotAccounts||[];if(a.length>0&&!s.force){let r=a.find(l=>l.portalId===o.activeHubSpotAccount)||a[0];m(e,200,{ok:!0,alreadyAuthenticated:!0,portalName:r.portalName,portalId:r.portalId});return}}else{if(!Me().found){m(e,400,{error:"HubSpot CLI not installed",needsInstall:!0});return}let r=Ne();if(r.authenticated&&!s.force){m(e,200,{ok:!0,alreadyAuthenticated:!0,portalName:r.portalName,portalId:r.portalId});return}}m(e,200,{needsKey:!0,instructions:"Create a personal access key in HubSpot",url:"https://app.hubspot.com/portal-recommend/l?slug=personal-access-key",steps:["Click the link above to open HubSpot","Select your account","Create a Personal Access Key with CMS permissions","Copy the key and paste it below"]})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function _r(t,e){_(t,n=>{try{let s=JSON.parse(n||"{}");if(!Qn().found){m(e,400,{error:"GitHub CLI not installed",needsInstall:!0});return}let i=Zn();if(i.authenticated&&!s.force){m(e,200,{ok:!0,alreadyAuthenticated:!0,username:i.username});return}if(s.token){let r=Bt("gh",["auth","login","--with-token"],"Authenticating with GitHub",{timeout:3e4,stdin:s.token});m(e,200,{ok:!0,jobId:r});return}let a=at("gh auth login --web --git-protocol https","GitHub authentication (check your browser)",{timeout:3e5});m(e,200,{ok:!0,jobId:a,browserAuthRequired:!0})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Pr(t,e){_(t,n=>{try{let{portalId:s,action:o}=JSON.parse(n);if((E().hubspotUploadMode||"api")==="api"){if(o==="remove"&&s){eo(s),m(e,200,{ok:!0});return}if(s){to(s),m(e,200,{ok:!0});return}}else{if(!Me().found){m(e,400,{error:"HubSpot CLI not installed"});return}let l=String(s).replace(/[^0-9]/g,"");if(!l){m(e,400,{error:"Invalid portalId"});return}if(o==="remove"){let c=Bt("hs",["accounts","remove",l],`Removing HubSpot account ${l}`,{timeout:15e3});m(e,200,{ok:!0,jobId:c});return}if(l){let c=Bt("hs",["accounts","use",l],`Switching to HubSpot account ${l}`,{timeout:15e3});m(e,200,{ok:!0,jobId:c});return}}m(e,400,{error:"portalId required"})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Rr(t){let e=at("gh auth logout --hostname github.com -y","Logging out of GitHub",{timeout:15e3});m(t,200,{ok:!0,jobId:e})}function Or(t,e){_(t,n=>{try{let{cli:s,apiKey:o}=JSON.parse(n||"{}");switch(s){case"claude":{let i=at("CLAUDECODE= claude --print -p 'reply OK'","Authenticating Claude Code (check your browser if prompted)",{timeout:12e4});m(e,200,{ok:!0,jobId:i,hint:"If Claude Code opens a browser window, complete the sign-in there."});break}case"gemini":{let i=at("gemini -p 'reply OK'","Authenticating Gemini CLI (check your browser if prompted)",{timeout:12e4});m(e,200,{ok:!0,jobId:i,hint:"If Gemini opens a browser window, complete the sign-in there."});break}case"codex":{if(o&&o.trim()){let i=o.trim();if(process.env.OPENAI_API_KEY=i,Y({openaiApiKey:i}),process.platform!=="win32"){let a=/^[A-Za-z0-9_\-.:]+$/.test(i)?i:"";if(a){let r=`export OPENAI_API_KEY="${a}"`,l=process.env.SHELL?.includes("zsh")?Ar(Ir(),".zshrc"):Ar(Ir(),".bashrc");try{(zc(l)?qc(l,"utf-8"):"").includes("OPENAI_API_KEY")||Xc(l,`
|
|
742
1298
|
# Added by vibeSpot
|
|
743
|
-
${
|
|
744
|
-
`)}catch{}}d(t,200,{ok:!0,message:"API key saved"})}else{let i=Ce("codex login","Authenticating Codex CLI (check your browser if prompted)",{timeout:12e4});d(t,200,{ok:!0,jobId:i,hint:"Complete the sign-in in your browser."})}break}default:d(t,400,{error:`Unknown CLI: ${n}`})}}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Di(e,t){_(e,o=>{try{let{mode:n}=JSON.parse(o);if(n!=="api"&&n!=="cli"){d(t,400,{error:`Invalid mode: ${n}. Must be "api" or "cli".`});return}z({hubspotUploadMode:n}),d(t,200,{ok:!0,mode:n})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Ji(e,t){_(e,o=>{try{let{toolId:n,enabled:s}=JSON.parse(o);if(!n||typeof s!="boolean"){d(t,400,{error:"toolId (string) and enabled (boolean) required"});return}ko(n,s),d(t,200,{ok:!0,toolId:n,enabled:s})}catch(n){d(t,400,{error:n instanceof Error?n.message:String(n)})}})}function Fi(e,t){let o=e.replace("/api/settings/job/","");if(!o){d(t,400,{error:"Job ID required"});return}let n=vn(o);if(!n){d(t,404,{error:"Job not found"});return}d(t,200,{id:n.id,status:n.status,description:n.description,output:n.output,exitCode:n.exitCode,startedAt:n.startedAt,completedAt:n.completedAt})}h();import{existsSync as yl,rmSync as bl}from"fs";import{join as Sl}from"path";function Hi(e,t,o){if(e==="GET"){let n=$(),s=ut().sort((i,a)=>a.updatedAt-i.updatedAt);d(o,200,{activeTheme:n?{id:n.id,themeName:n.themeName}:null,sessions:s});return}if(e==="DELETE"){_(t,n=>{try{let{sessionId:s,deleteFiles:i}=JSON.parse(n);Fs(s,i),d(o,200,{ok:!0})}catch(s){d(o,500,{error:s instanceof Error?s.message:String(s)})}});return}d(o,405,{error:"Method not allowed"})}function Li(e,t){_(e,o=>{try{let{sessionId:n}=JSON.parse(o),s=bn(n);if(!s){d(t,404,{error:"Session not found"});return}d(t,200,{ok:!0,themeName:s.themeName,themePath:s.themePath})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Gi(e,t){_(e,o=>{try{let{themeName:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"Theme name is required"});return}let s=Sl(Ae,n);if(!yl(s)){d(t,404,{error:"Theme not found on disk"});return}bl(s,{recursive:!0,force:!0}),d(t,200,{ok:!0})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Ui(e,t){_(e,o=>{try{let{sessionId:n,newName:s}=JSON.parse(o);if(!n||!s||typeof s!="string"){d(t,400,{error:"sessionId and newName are required"});return}let i=s.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/^-|-$/g,"").replace(/-{2,}/g,"-");if(!i){d(t,400,{error:"Invalid name"});return}let a=Hs(n,i);a.ok?d(t,200,{ok:!0,newName:i}):d(t,400,{error:a.error})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}h();import{existsSync as $n,readFileSync as Tl,rmSync as go}from"fs";import{join as Pe,basename as $l}from"path";import{execSync as kl}from"child_process";Z();Q();function Ki(e){let t=$();if(!t){d(e,404,{error:"No active session"});return}let o=dt();d(e,200,{themeName:t.themeName,themePath:t.themePath,templates:t.templates.map(n=>({id:n.id,label:n.label,pageType:n.pageType,moduleCount:n.modules.length,messageCount:n.messages.length})),activeTemplateId:t.activeTemplateId,moduleLibrary:o.map(n=>({moduleName:n.module.moduleName,usedIn:n.usedIn})),brandAssets:{hasStyleguide:!!t.brandAssets?.styleguide,hasBrandvoice:!!t.brandAssets?.brandvoice,humanify:t.brandAssets?.humanify!==!1}})}function Bi(e){let t=$();if(!t){d(e,404,{error:"No active session"});return}let o=t.themePath;if(!$n(o)){d(e,404,{error:"Theme directory not found"});return}let n=t.themeName||"theme",s=Pe(o,".."),i=$l(o);try{let a=`${n}.zip`,r=Pe(s,a);$n(r)&&go(r),kl(`zip -r "${a}" "${i}" -x "${i}/.git/*" "${i}/.vibespot/*" "${i}/node_modules/*"`,{cwd:s,timeout:3e4});let l=Tl(r);go(r),e.writeHead(200,{"Content-Type":"application/zip","Content-Disposition":`attachment; filename="${a}"`,"Content-Length":l.length}),e.end(l)}catch(a){V.error("download-zip","Failed to create zip archive",a),d(e,500,{error:"Failed to create zip archive"})}}function Yi(e,t,o){let n=$();if(!n){d(o,404,{error:"No active session"});return}if(e==="GET"){d(o,200,{templates:n.templates.map(s=>({id:s.id,label:s.label,pageType:s.pageType,moduleCount:s.modules.length})),activeTemplateId:n.activeTemplateId});return}if(e==="POST"){_(t,s=>{try{let{pageType:i,label:a}=JSON.parse(s);if(!i||!a){d(o,400,{error:"pageType and label are required"});return}if(!["landing_page","blog_post","website_page","module_only"].includes(i)){d(o,400,{error:`Invalid pageType: ${i}`});return}let l=Ns(i,a);j(),d(o,200,{ok:!0,template:{id:l.id,label:l.label,pageType:l.pageType}})}catch(i){d(o,500,{error:i instanceof Error?i.message:String(i)})}});return}if(e==="DELETE"){_(t,s=>{try{let{templateId:i}=JSON.parse(s);if(!i){d(o,400,{error:"templateId is required"});return}if(!Ps(i)){d(o,404,{error:"Template not found"});return}j(),d(o,200,{ok:!0})}catch(i){d(o,500,{error:i instanceof Error?i.message:String(i)})}});return}d(o,405,{error:"Method not allowed"})}function Vi(e,t){_(e,o=>{try{let{templateId:n}=JSON.parse(o);if(!n){d(t,400,{error:"templateId is required"});return}if(!zn(n)){d(t,404,{error:"Template not found"});return}j();let i=$();d(t,200,{ok:!0,modules:be().map(a=>a.moduleName),messageCount:i?.messages.length||0})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function zi(e,t){_(e,o=>{try{let{templateId:n,newLabel:s}=JSON.parse(o);if(!n||!s||typeof s!="string"){d(t,400,{error:"templateId and newLabel are required"});return}if(!Rs(n,s.trim())){d(t,404,{error:"Template not found"});return}j(),d(t,200,{ok:!0,newLabel:s.trim()})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function qi(e,t){_(e,o=>{try{let{templateId:n,label:s}=JSON.parse(o);if(!n){d(t,400,{error:"templateId is required"});return}let i=_s(n,s);if(!i){d(t,404,{error:"Template not found"});return}j(),d(t,200,{ok:!0,template:{id:i.id,label:i.label,pageType:i.pageType,moduleCount:i.modules.length}})}catch(n){d(t,500,{error:n instanceof Error?n.message:String(n)})}})}function Xi(e){let t=dt();d(e,200,{modules:t.map(o=>({moduleName:o.module.moduleName,usedIn:o.usedIn,fieldsJson:o.module.fieldsJson}))})}function Qi(e,t,o){let n=$();if(!n){d(o,404,{error:"No active session"});return}_(t,s=>{try{let{moduleName:i}=JSON.parse(s);if(!i){d(o,400,{error:"moduleName is required"});return}let r=dt().find(u=>u.module.moduleName===i);if(!r){d(o,404,{error:`Module "${i}" not found in library`});return}let l={...r.module};n.modules.find(u=>u.moduleName===l.moduleName)||(n.modules.push(l),n.moduleOrder.push(l.moduleName),n.updatedAt=Date.now()),j(),d(o,200,{ok:!0})}catch(i){d(o,500,{error:i instanceof Error?i.message:String(i)})}})}function Zi(e,t,o){let n=$();if(!n){d(o,404,{error:"No active session"});return}if(e==="GET"){d(o,200,{styleguide:n.brandAssets?.styleguide||null,brandvoice:n.brandAssets?.brandvoice||null});return}if(e==="POST"){_(t,s=>{try{let{type:i,content:a}=JSON.parse(s);if(!i){d(o,400,{error:"type is required"});return}if(n.brandAssets||(n.brandAssets={}),i==="humanify"){n.brandAssets.humanify=a==="on",n.updatedAt=Date.now(),j(),d(o,200,{ok:!0});return}if(!a){d(o,400,{error:"content is required"});return}if(i!=="styleguide"&&i!=="brandvoice"){d(o,400,{error:`Invalid type: ${i}. Must be "styleguide" or "brandvoice"`});return}n.brandAssets[i]=a,n.updatedAt=Date.now();let r=Pe(n.themePath,".vibespot");ge(r),M(Pe(r,`${i}.md`),a),j(),d(o,200,{ok:!0})}catch(i){d(o,500,{error:i instanceof Error?i.message:String(i)})}});return}if(e==="DELETE"){_(t,s=>{try{let{type:i}=JSON.parse(s);if(i!=="styleguide"&&i!=="brandvoice"){d(o,400,{error:`Invalid type: ${i}`});return}n.brandAssets&&delete n.brandAssets[i],n.updatedAt=Date.now();let a=Pe(n.themePath,".vibespot",`${i}.md`);$n(a)&&go(a),j(),d(o,200,{ok:!0})}catch(i){d(o,500,{error:i instanceof Error?i.message:String(i)})}});return}d(o,405,{error:"Method not allowed"})}function er(e,t){let o=$();if(!o){d(t,404,{error:"No active session"});return}_(e,n=>{(async()=>{try{let{sourcePath:s}=n?JSON.parse(n):{},i=s||o.themePath,{extractDesignContext:a}=await Promise.resolve().then(()=>(fo(),po)),r=await a(i);o.brandAssets||(o.brandAssets={}),o.brandAssets.styleguide=r,o.updatedAt=Date.now();let l=Pe(o.themePath,".vibespot");ge(l),M(Pe(l,"styleguide.md"),r),j(),d(t,200,{ok:!0,styleguide:r})}catch(s){d(t,500,{error:s instanceof Error?s.message:String(s)})}})()})}function tr(e,t){let o=$();if(!o){d(t,404,{error:"No active session"});return}_(e,n=>{(async()=>{try{let{source:s,themeName:i,localPath:a}=JSON.parse(n),r;if(s==="hubspot"){if(!i){d(t,400,{error:"themeName is required for HubSpot import"});return}let p=ue();if(!p){d(t,400,{error:"No HubSpot account connected"});return}let f=i.replace(/^\/+|\/+$/g,"");if(!f){d(t,400,{error:"Invalid theme name"});return}let g=f.replace(/[@/]/g,"_").replace(/_+/g,"_"),{homedir:y}=await import("os"),S=Pe(y(),"vibespot-themes",".references",g);ge(S);let{fetchTheme:w}=await Promise.resolve().then(()=>(en(),Go));await w(p,f,S),r=S}else if(s==="local"){if(!a){d(t,400,{error:"localPath is required for local import"});return}if(!$n(a)){d(t,400,{error:`Path not found: ${a}`});return}r=a}else{d(t,400,{error:"source must be 'hubspot' or 'local'"});return}let{extractDesignContext:l}=await Promise.resolve().then(()=>(fo(),po)),c=await l(r);o.brandAssets||(o.brandAssets={}),o.brandAssets.styleguide=c,o.updatedAt=Date.now();let u=Pe(o.themePath,".vibespot");ge(u),M(Pe(u,"styleguide.md"),c),j(),d(t,200,{ok:!0,styleguide:c,source:r})}catch(s){d(t,500,{error:s instanceof Error?s.message:String(s)})}})()})}h();import{join as El}from"path";function nr(e,t){let o=$();if(!o){d(t,404,{error:"No active session"});return}d(t,200,{id:o.id,themeName:o.themeName,themePath:o.themePath,messageCount:o.messages.length,moduleCount:o.modules.length,moduleOrder:o.moduleOrder})}function or(e,t,o){let n=$();if(!n){d(o,404,{error:"No active session"});return}if(e==="GET"){let s=be();d(o,200,{modules:s.map(i=>({moduleName:i.moduleName,fieldsJson:i.fieldsJson,moduleHtml:i.moduleHtml,moduleCss:i.moduleCss,moduleJs:i.moduleJs||null})),sharedCss:n.sharedCss,sharedJs:n.sharedJs});return}if(e==="DELETE"){_(t,s=>{let{moduleName:i,deleteEntirely:a}=JSON.parse(s);a?js(i):Ds(i),j(),d(o,200,{ok:!0})});return}d(o,405,{error:"Method not allowed"})}function sr(e,t){let o=$();if(!o){d(t,404,{error:"No active session"});return}_(e,n=>{try{let s=JSON.parse(n);if(s.shared){if(s.shared==="css")o.sharedCss=s.content;else if(s.shared==="js")o.sharedJs=s.content;else{d(t,400,{error:"Invalid shared type"});return}let c=Fe();c&&(s.shared==="css"?c.sharedCss=s.content:c.sharedJs=s.content),o.updatedAt=Date.now(),j(),We(),d(t,200,{ok:!0});return}let{moduleName:i,fileType:a,content:r}=s;if(!i||!a){d(t,400,{error:"moduleName and fileType required"});return}let l=o.modules.find(c=>c.moduleName===i);if(!l){d(t,404,{error:`Module "${i}" not found`});return}switch(a){case"html":l.moduleHtml=r;break;case"css":l.moduleCss=r;break;case"js":l.moduleJs=r||void 0;break;case"fields":try{JSON.parse(r)}catch{d(t,400,{error:"Invalid JSON in fields.json"});return}l.fieldsJson=r;break;default:d(t,400,{error:`Invalid fileType: ${a}`});return}o.updatedAt=Date.now(),j(),We(),d(t,200,{ok:!0})}catch(s){d(t,400,{error:String(s)})}})}function ir(e,t){_(e,o=>{let{order:n}=JSON.parse(o);Array.isArray(n)?(Ms(n),j(),d(t,200,{ok:!0})):d(t,400,{error:"order must be an array"})})}async function rr(e){let t=$();if(!t){d(e,404,{error:"No active session"});return}try{We();let o=mn(t.themePath),n=wn(`hs cms upload "${t.themePath}" "${t.themeName}"`,"Uploading to HubSpot",{cwd:El(t.themePath,".."),timeout:18e4});d(e,200,{ok:!0,jobId:n,fixes:o})}catch(o){d(e,500,{error:String(o)})}}function ar(e,t){_(e,o=>{try{let{moduleName:n,fieldPath:s,value:i}=JSON.parse(o);Js(n,s,i),j(),d(t,200,{ok:!0})}catch(n){d(t,400,{error:String(n)})}})}function lr(e,t){_(e,o=>{try{let{url:n}=JSON.parse(o);if(!n||typeof n!="string"){d(t,400,{error:"url is required"});return}let s=Ho(n),i=s.components.map(r=>`- ${r.name}: ${r.description}`).join(`
|
|
745
|
-
`),a={sourceDir:
|
|
1299
|
+
${r}
|
|
1300
|
+
`)}catch{}}}m(e,200,{ok:!0,message:"API key saved"})}else{let i=at("codex login","Authenticating Codex CLI (check your browser if prompted)",{timeout:12e4});m(e,200,{ok:!0,jobId:i,hint:"Complete the sign-in in your browser."})}break}default:m(e,400,{error:`Unknown CLI: ${s}`})}}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function jr(t,e){_(t,n=>{try{let{mode:s}=JSON.parse(n);if(s!=="api"&&s!=="cli"){m(e,400,{error:`Invalid mode: ${s}. Must be "api" or "cli".`});return}Y({hubspotUploadMode:s}),m(e,200,{ok:!0,mode:s})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Fr(t,e){_(t,n=>{try{let{toolId:s,enabled:o}=JSON.parse(n);if(!s||typeof o!="boolean"){m(e,400,{error:"toolId (string) and enabled (boolean) required"});return}no(s,o),m(e,200,{ok:!0,toolId:s,enabled:o})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Jr(t,e){_(t,n=>{try{let s=JSON.parse(n),o=["agenticMode","agenticConcurrency"],i={};for(let a of o)a in s&&(i[a]=s[a]);if(Object.keys(i).length===0){m(e,400,{error:"No valid settings fields provided"});return}Y(i),m(e,200,{ok:!0,updated:Object.keys(i)})}catch(s){m(e,400,{error:s instanceof Error?s.message:String(s)})}})}function Dr(t,e){let n=t.replace("/api/settings/job/","");if(!n){m(e,400,{error:"Job ID required"});return}let s=Jn(n);if(!s){m(e,404,{error:"Job not found"});return}m(e,200,{id:s.id,status:s.status,description:s.description,output:s.output,exitCode:s.exitCode,startedAt:s.startedAt,completedAt:s.completedAt})}f();import{existsSync as sd,rmSync as od}from"fs";import{join as id}from"path";function Hr(t,e,n){if(t==="GET"){let s=v(),o=bt().sort((i,a)=>a.updatedAt-i.updatedAt);m(n,200,{activeTheme:s?{id:s.id,themeName:s.themeName}:null,sessions:o});return}if(t==="DELETE"){_(e,s=>{try{let{sessionId:o,deleteFiles:i}=JSON.parse(s);gi(o,i),m(n,200,{ok:!0})}catch(o){m(n,500,{error:o instanceof Error?o.message:String(o)})}});return}m(n,405,{error:"Method not allowed"})}function Lr(t,e){_(t,n=>{try{let{sessionId:s}=JSON.parse(n),o=_n(s);if(!o){m(e,404,{error:"Session not found"});return}m(e,200,{ok:!0,themeName:o.themeName,themePath:o.themePath})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Gr(t,e){_(t,n=>{try{let{themeName:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"Theme name is required"});return}let o=id($e,s);if(!sd(o)){m(e,404,{error:"Theme not found on disk"});return}od(o,{recursive:!0,force:!0}),m(e,200,{ok:!0})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Ur(t,e){_(t,n=>{try{let{sessionId:s,newName:o}=JSON.parse(n);if(!s||!o||typeof o!="string"){m(e,400,{error:"sessionId and newName are required"});return}let i=o.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/^-|-$/g,"").replace(/-{2,}/g,"-");if(!i){m(e,400,{error:"Invalid name"});return}let a=hi(s,i);a.ok?m(e,200,{ok:!0,newName:i}):m(e,400,{error:a.error})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}f();import{existsSync as Bn,readFileSync as md,rmSync as Gs}from"fs";import{join as Je,basename as pd}from"path";import{execFileSync as fd}from"child_process";ee();X();var gd=process.platform==="win32"?{shell:!0}:{};function Br(t){let e=v();if(!e){m(t,404,{error:"No active session"});return}let n=qe();m(t,200,{themeName:e.themeName,themePath:e.themePath,templates:e.templates.map(s=>({id:s.id,label:s.label,pageType:s.pageType,moduleCount:s.modules.length,messageCount:s.messages.length})),activeTemplateId:e.activeTemplateId,moduleLibrary:n.map(s=>({moduleName:s.module.moduleName,usedIn:s.usedIn})),brandAssets:{hasStyleguide:!!e.brandAssets?.styleguide,hasBrandvoice:!!e.brandAssets?.brandvoice,humanify:e.brandAssets?.humanify!==!1}})}function Kr(t){let e=v();if(!e){m(t,404,{error:"No active session"});return}let n=e.themePath;if(!Bn(n)){m(t,404,{error:"Theme directory not found"});return}let s=e.themeName||"theme",o=Je(n,".."),i=pd(n);try{let a=`${s}.zip`,r=Je(o,a);Bn(r)&&Gs(r),fd("zip",["-r",a,i,"-x",`${i}/.git/*`,`${i}/.vibespot/*`,`${i}/node_modules/*`],{cwd:o,timeout:3e4,...gd});let l=md(r);Gs(r),t.writeHead(200,{"Content-Type":"application/zip","Content-Disposition":`attachment; filename="${a}"`,"Content-Length":l.length}),t.end(l)}catch(a){M.error("download-zip","Failed to create zip archive",a),m(t,500,{error:"Failed to create zip archive"})}}function Vr(t,e,n){let s=v();if(!s){m(n,404,{error:"No active session"});return}if(t==="GET"){m(n,200,{templates:s.templates.map(o=>({id:o.id,label:o.label,pageType:o.pageType,moduleCount:o.modules.length})),activeTemplateId:s.activeTemplateId});return}if(t==="POST"){_(e,o=>{try{let{pageType:i,label:a}=JSON.parse(o);if(!i||!a){m(n,400,{error:"pageType and label are required"});return}if(!["landing_page","blog_post","website_page","module_only"].includes(i)){m(n,400,{error:`Invalid pageType: ${i}`});return}let l=di(i,a);j(),m(n,200,{ok:!0,template:{id:l.id,label:l.label,pageType:l.pageType}})}catch(i){m(n,500,{error:i instanceof Error?i.message:String(i)})}});return}if(t==="DELETE"){_(e,o=>{try{let{templateId:i}=JSON.parse(o);if(!i){m(n,400,{error:"templateId is required"});return}if(!pi(i)){m(n,404,{error:"Template not found"});return}j(),m(n,200,{ok:!0})}catch(i){m(n,500,{error:i instanceof Error?i.message:String(i)})}});return}m(n,405,{error:"Method not allowed"})}function Yr(t,e){_(t,n=>{try{let{templateId:s}=JSON.parse(n);if(!s){m(e,400,{error:"templateId is required"});return}if(!ls(s)){m(e,404,{error:"Template not found"});return}j();let i=v();m(e,200,{ok:!0,modules:ue().map(a=>a.moduleName),messageCount:i?.messages.length||0})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function zr(t,e){_(t,n=>{try{let{templateId:s,newLabel:o}=JSON.parse(n);if(!s||!o||typeof o!="string"){m(e,400,{error:"templateId and newLabel are required"});return}if(!mi(s,o.trim())){m(e,404,{error:"Template not found"});return}j(),m(e,200,{ok:!0,newLabel:o.trim()})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function qr(t,e){_(t,n=>{try{let{templateId:s,label:o}=JSON.parse(n);if(!s){m(e,400,{error:"templateId is required"});return}let i=ui(s,o);if(!i){m(e,404,{error:"Template not found"});return}j(),m(e,200,{ok:!0,template:{id:i.id,label:i.label,pageType:i.pageType,moduleCount:i.modules.length}})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function Xr(t){let e=qe();m(t,200,{modules:e.map(n=>({moduleName:n.module.moduleName,usedIn:n.usedIn,fieldsJson:n.module.fieldsJson}))})}function Qr(t,e,n){let s=v();if(!s){m(n,404,{error:"No active session"});return}_(e,o=>{try{let{moduleName:i}=JSON.parse(o);if(!i){m(n,400,{error:"moduleName is required"});return}let r=qe().find(d=>d.module.moduleName===i);if(!r){m(n,404,{error:`Module "${i}" not found in library`});return}let l={...r.module};s.modules.find(d=>d.moduleName===l.moduleName)||(s.modules.push(l),s.moduleOrder.push(l.moduleName),s.updatedAt=Date.now()),j(),m(n,200,{ok:!0})}catch(i){m(n,500,{error:i instanceof Error?i.message:String(i)})}})}function Zr(t,e,n){let s=v();if(!s){m(n,404,{error:"No active session"});return}if(t==="GET"){m(n,200,{styleguide:s.brandAssets?.styleguide||null,brandvoice:s.brandAssets?.brandvoice||null});return}if(t==="POST"){_(e,o=>{try{let{type:i,content:a}=JSON.parse(o);if(!i){m(n,400,{error:"type is required"});return}if(s.brandAssets||(s.brandAssets={}),i==="humanify"){s.brandAssets.humanify=a==="on",s.updatedAt=Date.now(),j(),m(n,200,{ok:!0});return}if(!a){m(n,400,{error:"content is required"});return}if(i!=="styleguide"&&i!=="brandvoice"){m(n,400,{error:`Invalid type: ${i}. Must be "styleguide" or "brandvoice"`});return}s.brandAssets[i]=a,s.updatedAt=Date.now();let r=Je(s.themePath,".vibespot");we(r),F(Je(r,`${i}.md`),a),j(),m(n,200,{ok:!0})}catch(i){m(n,500,{error:i instanceof Error?i.message:String(i)})}});return}if(t==="DELETE"){_(e,o=>{try{let{type:i}=JSON.parse(o);if(i!=="styleguide"&&i!=="brandvoice"){m(n,400,{error:`Invalid type: ${i}`});return}s.brandAssets&&delete s.brandAssets[i],s.updatedAt=Date.now();let a=Je(s.themePath,".vibespot",`${i}.md`);Bn(a)&&Gs(a),j(),m(n,200,{ok:!0})}catch(i){m(n,500,{error:i instanceof Error?i.message:String(i)})}});return}m(n,405,{error:"Method not allowed"})}function ea(t,e){let n=v();if(!n){m(e,404,{error:"No active session"});return}_(t,s=>{(async()=>{try{let{sourcePath:o}=s?JSON.parse(s):{},i=o||n.themePath,{extractDesignContext:a}=await Promise.resolve().then(()=>(Ls(),Hs)),r=await a(i);n.brandAssets||(n.brandAssets={}),n.brandAssets.styleguide=r,n.updatedAt=Date.now();let l=Je(n.themePath,".vibespot");we(l),F(Je(l,"styleguide.md"),r),j(),m(e,200,{ok:!0,styleguide:r})}catch(o){m(e,500,{error:o instanceof Error?o.message:String(o)})}})()})}function ta(t,e){let n=v();if(!n){m(e,404,{error:"No active session"});return}_(t,s=>{(async()=>{try{let{source:o,themeName:i,localPath:a}=JSON.parse(s),r;if(o==="hubspot"){if(!i){m(e,400,{error:"themeName is required for HubSpot import"});return}let u=he();if(!u){m(e,400,{error:"No HubSpot account connected"});return}let p=i.replace(/^\/+|\/+$/g,"");if(!p){m(e,400,{error:"Invalid theme name"});return}let g=p.replace(/[@/]/g,"_").replace(/_+/g,"_"),{homedir:h}=await import("os"),b=Je(h(),"vibespot-themes",".references",g);we(b);let{fetchTheme:y}=await Promise.resolve().then(()=>(pn(),yo));await y(u,p,b),r=b}else if(o==="local"){if(!a){m(e,400,{error:"localPath is required for local import"});return}if(!Bn(a)){m(e,400,{error:`Path not found: ${a}`});return}r=a}else{m(e,400,{error:"source must be 'hubspot' or 'local'"});return}let{extractDesignContext:l}=await Promise.resolve().then(()=>(Ls(),Hs)),c=await l(r);n.brandAssets||(n.brandAssets={}),n.brandAssets.styleguide=c,n.updatedAt=Date.now();let d=Je(n.themePath,".vibespot");we(d),F(Je(d,"styleguide.md"),c),j(),m(e,200,{ok:!0,styleguide:c,source:r})}catch(o){m(e,500,{error:o instanceof Error?o.message:String(o)})}})()})}f();import{join as hd}from"path";function na(t,e){let n=v();if(!n){m(e,404,{error:"No active session"});return}m(e,200,{id:n.id,themeName:n.themeName,themePath:n.themePath,messageCount:n.messages.length,moduleCount:n.modules.length,moduleOrder:n.moduleOrder})}function sa(t,e,n){let s=v();if(!s){m(n,404,{error:"No active session"});return}if(t==="GET"){let o=ue();m(n,200,{modules:o.map(i=>({moduleName:i.moduleName,fieldsJson:i.fieldsJson,moduleHtml:i.moduleHtml,moduleCss:i.moduleCss,moduleJs:i.moduleJs||null})),sharedCss:s.sharedCss,sharedJs:s.sharedJs});return}if(t==="DELETE"){$s(e,n,o=>{o.deleteEntirely?ii(o.moduleName):ri(o.moduleName),j(),m(n,200,{ok:!0})});return}m(n,405,{error:"Method not allowed"})}function oa(t,e){let n=v();if(!n){m(e,404,{error:"No active session"});return}_(t,s=>{try{let o=JSON.parse(s);if(o.shared){if(o.shared==="css")n.sharedCss=o.content;else if(o.shared==="js")n.sharedJs=o.content;else{m(e,400,{error:"Invalid shared type"});return}let c=Se();c&&(o.shared==="css"?c.sharedCss=o.content:c.sharedJs=o.content),n.updatedAt=Date.now(),j(),Xe(),m(e,200,{ok:!0});return}let{moduleName:i,fileType:a,content:r}=o;if(!i||!a){m(e,400,{error:"moduleName and fileType required"});return}let l=n.modules.find(c=>c.moduleName===i);if(!l){m(e,404,{error:`Module "${i}" not found`});return}switch(a){case"html":l.moduleHtml=r;break;case"css":l.moduleCss=r;break;case"js":l.moduleJs=r||void 0;break;case"fields":try{JSON.parse(r)}catch{m(e,400,{error:"Invalid JSON in fields.json"});return}l.fieldsJson=r;break;default:m(e,400,{error:`Invalid fileType: ${a}`});return}n.updatedAt=Date.now(),j(),Xe(),m(e,200,{ok:!0})}catch(o){m(e,400,{error:String(o)})}})}function ia(t,e){$s(t,e,n=>{Array.isArray(n.order)?(ht(n.order),j(),m(e,200,{ok:!0})):m(e,400,{error:"order must be an array"})})}async function ra(t){let e=v();if(!e){m(t,404,{error:"No active session"});return}try{Xe();let n=An(e.themePath),s=Dn(`hs cms upload "${e.themePath}" "${e.themeName}"`,"Uploading to HubSpot",{cwd:hd(e.themePath,".."),timeout:18e4});m(t,200,{ok:!0,jobId:s,fixes:n})}catch(n){m(t,500,{error:String(n)})}}function aa(t,e){_(t,n=>{try{let{moduleName:s,fieldPath:o,value:i}=JSON.parse(n);ai(s,o,i),j(),m(e,200,{ok:!0})}catch(s){m(e,400,{error:String(s)})}})}function la(t,e){_(t,n=>{try{let{url:s}=JSON.parse(n);if(!s||typeof s!="string"){m(e,400,{error:"url is required"});return}let o=go(s),i=o.components.map(r=>`- ${r.name}: ${r.description}`).join(`
|
|
1301
|
+
`),a={sourceDir:o.sourceDir,componentCount:o.components.length,components:o.components.map(r=>({name:r.name,description:r.description})),hasTailwind:o.hasTailwind,cssVarCount:o.cssVarCount,fonts:o.fonts,interactions:o.interactions,conversionPrompt:`Import and convert the React landing page from ${s} to native HubSpot modules.
|
|
746
1302
|
|
|
747
|
-
Source analysis found ${
|
|
1303
|
+
Source analysis found ${o.components.length} components:
|
|
748
1304
|
${i}
|
|
749
1305
|
|
|
750
|
-
Design system: ${
|
|
751
|
-
Fonts: ${
|
|
752
|
-
Interactions: ${
|
|
1306
|
+
Design system: ${o.hasTailwind?"Tailwind CSS":"Custom CSS"}, ${o.cssVarCount} CSS variables
|
|
1307
|
+
Fonts: ${o.fonts.length>0?o.fonts.join(", "):"System fonts"}
|
|
1308
|
+
Interactions: ${o.interactions.join(", ")}
|
|
753
1309
|
|
|
754
|
-
Read the React source files from ${
|
|
755
|
-
`}))},onFileComplete:
|
|
756
|
-
`}))},onFileError:(u
|
|
757
|
-
`}))},onProgress:(u
|
|
758
|
-
`),errors:
|
|
1310
|
+
Read the React source files from ${o.sourceDir} and convert each component to a HubSpot module. Preserve the design, layout, colors, and content. Generate fields.json so marketers can edit all text, images, colors, and links in the HubSpot page editor.`};m(e,200,a)}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}function ca(t,e){let n=v();if(!n){m(e,404,{error:"No active session"});return}if(!ke()){m(e,200,{available:!1,commits:[]});return}let o=new URL(t.url||"/","http://localhost").searchParams.get("templateId"),i=o?ti(n.themePath,o,50):ei(n.themePath,50);m(e,200,{available:!0,commits:i,filtered:!!o})}function da(t,e){_(t,n=>{try{let s=v();if(!s){m(e,404,{error:"No active session"});return}let{hash:o,templateId:i}=JSON.parse(n);if(!o||typeof o!="string"){m(e,400,{error:"Commit hash is required"});return}if(ze("assistant",`Rolled back to version ${o.slice(0,7)}.`),i){let a=s.templates.find(c=>c.id===i);if(!a){m(e,404,{error:"Template not found"});return}let r=a.moduleOrder.map(c=>`modules/${c}.module`);a.templateFile&&r.push(a.templateFile);let l=si(s.themePath,i,o,r);if(!l.success){m(e,500,{error:l.error||"Rollback failed"});return}vi()}else{let a=ni(s.themePath,o);if(!a.success){m(e,500,{error:a.error||"Rollback failed"});return}Si()}j(),m(e,200,{ok:!0,modules:ue().map(a=>a.moduleName)})}catch(s){m(e,500,{error:s instanceof Error?s.message:String(s)})}})}var pa={".html":"text/html",".css":"text/css",".js":"application/javascript",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif",".ico":"image/x-icon",".woff2":"font/woff2"};function fa(t){let{port:e,uiDir:n}=t,s=yd((i,a)=>vd(i,a,n)),o=new Sd({server:s});return o.on("connection",i=>xd(i)),new Promise((i,a)=>{s.on("error",r=>{r.code==="EADDRINUSE"?s.listen(e+1,()=>{i({port:e+1,close:()=>{s.close(),o.close()}})}):a(r)}),s.listen(e,()=>{i({port:e,close:()=>{s.close(),o.close()}})})})}function vd(t,e,n){let s=new URL(t.url||"/",`http://${t.headers.host}`),o=t.method||"GET";if(e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("X-Frame-Options","DENY"),e.setHeader("X-XSS-Protection","1; mode=block"),e.setHeader("Referrer-Policy","strict-origin-when-cross-origin"),s.pathname.startsWith("/api/")){wd(o,s.pathname,t,e);return}if(s.pathname==="/preview"){let i=Mi();e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(i);return}if(s.pathname==="/module-preview"){let i=s.searchParams.get("module")||"",a=Ni(i);e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(a||"<!-- module not found -->");return}if(s.pathname.startsWith("/theme-assets/")){Cd(s.pathname.slice(14),e);return}Ad(s.pathname,n,t,e)}function wd(t,e,n,s){let o=n.headers.origin||"";if(/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(o)&&s.setHeader("Access-Control-Allow-Origin",o),s.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"),s.setHeader("Access-Control-Allow-Headers","Content-Type"),t==="OPTIONS"){s.writeHead(204),s.end();return}switch(e){case"/api/session":na(t,s);break;case"/api/modules":sa(t,n,s);break;case"/api/modules/reorder":ia(n,s);break;case"/api/modules/code":oa(n,s);break;case"/api/upload":ra(s);break;case"/api/upload-files":t==="POST"?Hi(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/field":aa(n,s);break;case"/api/import":la(n,s);break;case"/api/setup":yr(s);break;case"/api/setup/create":br(n,s);break;case"/api/setup/fetch":Sr(n,s);break;case"/api/setup/open":vr(n,s);break;case"/api/setup/resume":wr(n,s);break;case"/api/setup/apikey":xr(n,s);break;case"/api/setup/remote-themes":t==="GET"?Cr(s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/status":t==="GET"?$r(s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/engine":t==="POST"?Tr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/apikey":t==="POST"?Er(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/install":t==="POST"?Mr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/hs-auth":t==="POST"?Nr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/gh-auth":t==="POST"?_r(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/hs-switch":t==="POST"?Pr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/gh-logout":t==="POST"?Rr(s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/cli-auth":t==="POST"?Or(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/hs-mode":t==="POST"?jr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings/cli-toggle":t==="POST"?Fr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/settings":t==="POST"?Jr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/changelog":t==="GET"?m(s,200,{changelog:qs()}):m(s,405,{error:"Method not allowed"});break;case"/api/themes":Hr(t,n,s);break;case"/api/themes/switch":t==="POST"?Lr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/themes/delete-local":t==="POST"?Gr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/themes/rename":t==="POST"?Ur(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/history":t==="GET"?ca(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/rollback":t==="POST"?da(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/dashboard":t==="GET"?Br(s):m(s,405,{error:"Method not allowed"});break;case"/api/templates":Vr(t,n,s);break;case"/api/templates/activate":t==="POST"?Yr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/templates/rename":t==="POST"?zr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/templates/clone":t==="POST"?qr(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/module-library":t==="GET"?Xr(s):m(s,405,{error:"Method not allowed"});break;case"/api/brand-assets":Zr(t,n,s);break;case"/api/brand-assets/extract":t==="POST"?ea(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/brand-assets/import-reference":t==="POST"?ta(n,s):m(s,405,{error:"Method not allowed"});break;case"/api/download-zip":t==="GET"?Kr(s):m(s,405,{error:"Method not allowed"});break;default:e.startsWith("/api/settings/job/")&&t==="GET"?Dr(e,s):e.match(/^\/api\/templates\/[^/]+\/add-module$/)&&t==="POST"?Qr(e,n,s):m(s,404,{error:"Not found"})}}function xd(t){t.on("message",async n=>{let s;try{s=JSON.parse(n.toString())}catch{t.send(JSON.stringify({type:"error",message:"Invalid JSON"}));return}switch(s.type){case"chat":{let o=String(s.message||"");if(!o.trim())return;ze("user",o),j();let i=Array.isArray(s.fileIds)?s.fileIds:void 0,a=dr();a.needsPrompt&&t.send(JSON.stringify({type:"agentic_prompt"}));try{if(a.useAgentic){let l=[],c=[],d=await lr(o,u=>{if(u.type==="module_progress"&&u.moduleFiles){let{moduleFiles:p,...g}=u;t.send(JSON.stringify(g))}else t.send(JSON.stringify(u));if(u.type==="agent_step")l.push({step:u.step,label:u.label});else if(u.type==="agent_decision"){let p=l[l.length-1];p&&(p.decisions||(p.decisions=[]),p.decisions.push(u.decision))}else u.type==="design_system_ready"?Oe({sharedCss:u.sharedCss,sharedJs:u.sharedJs}):u.type==="blueprint_ready"?(Oe({sharedCss:u.sharedCss,sharedJs:u.sharedJs}),ht(u.moduleOrder),t.send(JSON.stringify({type:"modules_updated",modules:ue().map(p=>p.moduleName)}))):u.type==="module_progress"&&u.status==="complete"&&u.moduleFiles?(Oe({modules:[{moduleName:u.module,fieldsJson:u.moduleFiles.fieldsJson,metaJson:u.moduleFiles.metaJson,moduleHtml:u.moduleFiles.moduleHtml,moduleCss:u.moduleFiles.moduleCss,moduleJs:u.moduleFiles.moduleJs}]}),t.send(JSON.stringify({type:"modules_updated",modules:ue().map(p=>p.moduleName)})),c.push({name:u.module,status:"complete"})):u.type==="module_progress"&&u.status==="failed"&&c.push({name:u.module,status:"failed"})},i);cr(d,{steps:l,modules:c,stats:d.stats})}else ar(l=>{t.send(JSON.stringify({type:"parse_warning",message:l}))}),await Os(o,l=>{t.send(JSON.stringify({type:"stream",content:l}))},l=>{t.send(JSON.stringify({type:"stream_status",content:l}))},i);let r=v();if(r){Xe();let l=Se(),c=null;if(l){let d=l.moduleOrder.map(u=>`modules/${u}.module`);l.templateFile&&d.push(l.templateFile),l.sharedCss&&d.push(`css/${r.themeName}-theme.css`),l.sharedJs&&d.push(`js/${r.themeName}-animations.js`),c=Zo(r.themePath,l.id,o,d)}else c=rs(r.themePath,o);c&&t.send(JSON.stringify({type:"version_created",hash:c}))}t.send(JSON.stringify({type:"generation_complete"})),t.send(JSON.stringify({type:"modules_updated",modules:ue().map(l=>l.moduleName)}))}catch(r){t.send(JSON.stringify({type:"error",message:r instanceof Error?r.message:String(r)}))}break}case"start_upload":{let o=v();if(!o){t.send(JSON.stringify({type:"error",message:"No active session"}));break}try{Xe();let i=An(o.themePath);if(i.length>0&&t.send(JSON.stringify({type:"upload_status",phase:"autofix",fixes:i})),(E().hubspotUploadMode||"api")==="api"){let l=he();if(!l){t.send(JSON.stringify({type:"upload_failed",output:"No HubSpot account configured. Open Settings \u2192 HubSpot to add one.",errors:[{file:"",message:"No HubSpot account configured",fixable:!1}]}));break}t.send(JSON.stringify({type:"upload_started",jobId:"api-upload"}));let c=await In(l,o.themePath,o.themeName,{onFileStart:d=>{t.send(JSON.stringify({type:"upload_output",chunk:`Uploading ${d}
|
|
1311
|
+
`}))},onFileComplete:d=>{t.send(JSON.stringify({type:"upload_output",chunk:` \u2713 ${d}
|
|
1312
|
+
`}))},onFileError:(d,u)=>{t.send(JSON.stringify({type:"upload_output",chunk:` \u2717 ${d}: ${u.message}
|
|
1313
|
+
`}))},onProgress:(d,u)=>{t.send(JSON.stringify({type:"upload_progress",completed:d,total:u}))}});if(c.success){let d=Ze();t.send(JSON.stringify({type:"upload_complete",output:`Uploaded ${c.uploaded} files`,portalId:d?.portalId||"",dataCenter:d?.dataCenter||"na1",themeName:o.themeName}))}else{let d=xn(c.errors);t.send(JSON.stringify({type:"upload_failed",output:c.errors.map(u=>`${u.file}: ${u.message}`).join(`
|
|
1314
|
+
`),errors:d}))}}else{let l=Dn(`hs cms upload "${o.themePath}" "${o.themeName}"`,"Uploading to HubSpot",{cwd:Kn(o.themePath,".."),timeout:18e4});t.send(JSON.stringify({type:"upload_started",jobId:l}));let c=u=>{t.send(JSON.stringify({type:"upload_output",chunk:u}))};mr(l,c);let d=setInterval(()=>{let u=Jn(l);if(!(!u||u.status==="running"))if(clearInterval(d),pr(l,c),u.status==="completed"){let p=Ne(),g=p.portalId?Et(p.portalId):"na1";t.send(JSON.stringify({type:"upload_complete",output:u.output,portalId:p.portalId||"",dataCenter:g,themeName:o.themeName}))}else{let p=Cn(u.output);t.send(JSON.stringify({type:"upload_failed",output:u.output,errors:p,exitCode:u.exitCode}))}},500)}}catch(i){t.send(JSON.stringify({type:"error",message:i instanceof Error?i.message:String(i)}))}break}case"upload_fix_with_ai":{let o=String(s.errorContext||"");if(!o.trim()){t.send(JSON.stringify({type:"error",message:"No error context provided"}));break}let i=`The HubSpot upload ("hs cms upload") failed. Below is the upload log output containing the errors.
|
|
759
1315
|
|
|
760
1316
|
IMPORTANT: Be verbose in your response. For each error:
|
|
761
1317
|
1. State exactly which file has the problem and what the error is
|
|
@@ -768,9 +1324,9 @@ CRITICAL: After fixing the reported errors, scan ALL other module files in the t
|
|
|
768
1324
|
After fixing all errors, summarize the changes you made.
|
|
769
1325
|
|
|
770
1326
|
Upload log:
|
|
771
|
-
${
|
|
772
|
-
`));let
|
|
773
|
-
`));try{process.platform==="darwin"?
|
|
774
|
-
Saving session...`)),j(),
|
|
775
|
-
`)),a(),setTimeout(()=>process.exit(0),500)})})}catch(
|
|
1327
|
+
${o}`;ze("user",i),j(),t.send(JSON.stringify({type:"upload_fix_started"}));try{await Os(i,r=>{t.send(JSON.stringify({type:"stream",content:r})),t.send(JSON.stringify({type:"upload_fix_stream",content:r}))});let a=v();if(a){Xe();let r=rs(a.themePath,"AI fix: upload errors");r&&t.send(JSON.stringify({type:"version_created",hash:r}))}t.send(JSON.stringify({type:"upload_fix_complete"})),t.send(JSON.stringify({type:"modules_updated",modules:ue().map(r=>r.moduleName)}))}catch(a){t.send(JSON.stringify({type:"upload_failed",output:a instanceof Error?a.message:String(a),errors:[{file:"AI fix",message:a instanceof Error?a.message:String(a),fixable:!1}]}))}break}case"ping":t.send(JSON.stringify({type:"pong"}));break;default:t.send(JSON.stringify({type:"error",message:`Unknown type: ${s.type}`}))}});let e=v();if(e){let n=E(),s={"claude-code":"Claude Code","anthropic-api":"Anthropic API","openai-api":"OpenAI API","gemini-cli":"Gemini CLI","gemini-api":"Gemini API","codex-cli":"Codex CLI",api:"Anthropic API"},o=Se();t.send(JSON.stringify({type:"init",sessionId:e.id,themeName:e.themeName,modules:ue().map(i=>i.moduleName),messageCount:e.messages.length,messages:e.messages,gitAvailable:ke(),engine:n.aiEngine?s[n.aiEngine]||n.aiEngine:"",templateId:o?.id||null,pageType:o?.pageType||null,templates:(e.templates||[]).map(i=>({id:i.id,label:i.label,pageType:i.pageType,moduleCount:i.modules.length}))}))}else t.send(JSON.stringify({type:"needs_setup"}))}function Cd(t,e){let n=v();if(!n){e.writeHead(404,{"Content-Type":"text/plain"}),e.end("No session");return}let s=Kn(n.themePath,"assets",t);if(!Ws(s)){e.writeHead(404,{"Content-Type":"text/plain"}),e.end("Asset not found");return}let o=ma(s),i=pa[o]||"application/octet-stream",a=Us(s);e.writeHead(200,{"Content-Type":i,"Cache-Control":"no-cache"}),e.end(a)}var ua=new Map;function Ad(t,e,n,s){let i=Kn(e,t==="/"?"/index.html":t);if(!Ws(i)){let c=Kn(e,"index.html");if(Ws(c)){let d=Us(c);s.writeHead(200,{"Content-Type":"text/html","Cache-Control":"no-cache"}),s.end(d)}else s.writeHead(404,{"Content-Type":"text/plain"}),s.end("Not found");return}let a=ma(i),r=pa[a]||"application/octet-stream",l=a===".html";try{let c=ua.get(i);if(!c){let u=Us(i),p='"'+bd("md5").update(u).digest("hex").slice(0,16)+'"';c={buffer:u,etag:p,contentType:r},ua.set(i,c)}if(n.headers["if-none-match"]===c.etag){s.writeHead(304),s.end();return}s.writeHead(200,{"Content-Type":c.contentType,"Cache-Control":l?"no-cache":"public, max-age=3600",ETag:c.etag}),s.end(c.buffer)}catch{s.writeHead(500,{"Content-Type":"text/plain"}),s.end("Internal Server Error")}}var kd=4200;async function ga(){let t=Yn.hex("#e8613a"),e=Yn.dim;console.log(""),console.log(t(" v vibeSpot")),console.log(e(` Starting...
|
|
1328
|
+
`));let n=$d();n||(console.error(Yn.red(" Could not find UI assets. Is the package installed correctly?")),process.exit(1));try{let{port:s,close:o}=await fa({port:kd,uiDir:n}),i=`http://localhost:${s}`;console.log(t(` v ${i}`)),console.log(e(` Press Ctrl+C to stop
|
|
1329
|
+
`));try{process.platform==="darwin"?Bs("open",[i],{stdio:"ignore"}):process.platform==="win32"?Bs("cmd",["/c","start","",i],{stdio:"ignore"}):Bs("xdg-open",[i],{stdio:"ignore"})}catch{}await new Promise(a=>{process.on("SIGINT",()=>{console.log(e(`
|
|
1330
|
+
Saving session...`)),j(),o(),console.log(e(` Goodbye!
|
|
1331
|
+
`)),a(),setTimeout(()=>process.exit(0),500)})})}catch(s){console.error(Yn.red(` Failed to start: ${s instanceof Error?s.message:String(s)}`)),process.exit(1)}}function $d(){let t=[Vn(import.meta.dirname,"../../ui"),Vn(import.meta.dirname,"../ui"),Vn(process.cwd(),"ui")];for(let e of t)if(Id(Vn(e,"index.html")))return e;return null}X();function ha(){let t=new Td;return t.name("vibespot").description("AI-powered HubSpot CMS landing page builder").version(ut()).action(ga),t.command("wizard").description("Classic CLI wizard \u2014 step-by-step conversion flow").action(Ko),t.command("init").description("Check and install required tools").action(Vo),t.command("convert").description("Convert a React project to HubSpot modules").action(Yo),t.command("upload").description("Upload theme to HubSpot").action(zo),t.command("doctor").description("Diagnose environment issues").action(qo),t}var Ed=ha();Ed.parseAsync(process.argv).catch(t=>{console.error(t),process.exit(1)});
|
|
776
1332
|
//# sourceMappingURL=index.js.map
|