#!/usr/bin/env node var pe=Object.defineProperty;var o=(t,e)=>pe(t,"name",{value:e,configurable:!0});import{access as Ce}from"fs/promises";import{readFileSync as Ve}from"fs";import{join as xe,normalize as C}from"path";import{parseArgs as be}from"util";import{check as Fe,extractDocDefinition as we,FileType as $e,recommended as Ue,Severity as R,SourceCodeType as ke,toSchema as re,toSourceCode as Me}from"@shopify/theme-check-common";import{ThemeLiquidDocsManager as je}from"@shopify/theme-check-docs-updater";import{themeCheckRun as Ge}from"@shopify/theme-check-node";import{randomUUID as de}from"crypto";function b(t){return t.some(e=>e.result==="failed")}o(b,"hasFailedValidation");function v(t){return t.map(e=>({artifactId:e.artifactId||`artifact-${de()}`,revision:e.revision??1}))}o(v,"extractArtifactsFromItems");function O(t,e){return t.map((r,c)=>{let s=e[c];return s?{...r,artifactId:s.artifactId,artifactRevision:s.revision}:r})}o(O,"attachArtifactIds");function F(t,e="Items"){let r=b(t),c=t.some(i=>i.result==="inform"),s;r?s="\u274C INVALID":c?s="\u26A0\uFE0F VALID (with warnings)":s="\u2705 VALID";let n=`## Validation Summary `;return n+=`**Overall Status:** ${s} `,n+=`**Total ${e}:** ${t.length} `,n+=`## Detailed Results `,t.forEach((i,l)=>{let f;i.result==="success"?f="\u2705":i.result==="inform"?f="\u26A0\uFE0F":f="\u274C",n+=`### ${e.slice(0,-1)} ${l+1} `,i.artifactId&&(n+=`**Artifact ID:** ${i.artifactId}`,i.artifactRevision&&(n+=` **Revision:** ${i.artifactRevision}`),n+=` *Use same ID & increment revision when retrying on an improvement of this artifact* `),n+=`**Status:** ${f} ${i.result.toUpperCase()} `,n+=`**Details:** ${i.resultDetail} `}),n}o(F,"formatValidationResult");function G(t){return Object.fromEntries(Object.entries(t).map(([e,r])=>{let{description:c,valueName:s,choices:n,...i}=r;return[e,i]}))}o(G,"toParseArgsOptions");var B={model:{type:"string",valueName:"name",description:"Agent model name"},"client-name":{type:"string",valueName:"name",description:"Agent client name"},"client-version":{type:"string",valueName:"version",description:"Agent client version"}},H={"user-prompt-base64":{type:"string",valueName:"base64",description:"Base64-encoded triggering prompt"},"session-id":{type:"string",valueName:"id",description:"Agent session identifier"},"tool-use-id":{type:"string",valueName:"id",description:"Agent tool-use identifier"}},Q={"artifact-id":{type:"string",valueName:"id",description:"Stable artifact identifier"},revision:{type:"string",valueName:"number",description:"Positive artifact revision"},...B,...H,json:{type:"boolean",description:"Emit machine-readable JSON"}},K={help:{type:"boolean",short:"h",description:"Show this help"}};function q(t,e){let r=new Set([...Object.keys(B),...Object.keys(H),"artifact-id","revision"]),c=Object.entries(e).filter(([n])=>!0).map(([n,i])=>{let l=i.type==="string"?i.valueName||i.choices?.join("|")||"value":void 0,f=`--${n}${l?` <${l}>`:""}`;return[i.short?`-${i.short}, ${f}`:f,i.description]}),s=Math.max(...c.map(([n])=>n.length));return["Usage:",...t.map(n=>` ${n}`),"","Options:",...c.map(([n,i])=>` ${n.padEnd(s)} ${i}`)].join(` `)}o(q,"formatCliHelp");function W(){let t=[process.env.CLAUDE_SESSION_ID,process.env.CLAUDE_CODE_SESSION_ID,process.env.CURSOR_SESSION_ID,process.env.COPILOT_SESSION_ID];for(let e of t)if(typeof e=="string"&&e.length>0)return e}o(W,"readHostSessionId");import{isIP as ue}from"node:net";var me="https://shopify.dev/",he="https://shopify-dev.shop.dev/";function Y(t){return`https://shopify-dev-staging${t}.shopifycloud.com/`}o(Y,"stagingHost");var ge="SHOPIFY_DEV_SUPPORT_CORE_MCP_DOMAIN",A="SHOPIFY_DEV_LOCAL_SERVER_URL";function Se(t){let e;try{e=new URL(t)}catch{throw new Error(`${A} must identify a local Shopify.dev origin`)}let r=e.hostname==="localhost"||e.hostname==="[::1]"||ue(e.hostname)===4&&e.hostname.startsWith("127."),c=/^shopify-dev-[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.shop\.dev$/u.test(e.hostname);if(!r&&!c||r&&!e.port||c&&e.protocol!=="https:"||r&&e.protocol!=="http:"&&e.protocol!=="https:"||e.username||e.password||e.pathname!==""&&e.pathname!=="/"||e.search||e.hash)throw new Error(`${A} must identify a local Shopify.dev origin`);return`${e.origin}/`}o(Se,"normalizeLocalShopifyDevUrl");function w(t){let e=t?.env??process.env,r=e.SHOPIFY_DEV_STAGING_SERVER_NUMBER?.trim(),c=e[A]?.trim();if(c&&t?.allowLocalServer){if(r||e.DEV&&e.DEV!=="false"||e[ge]?.trim())throw new Error(`${A} cannot be combined with staging, DEV, or Support Core routing overrides`);return{url:Se(c),headers:{}}}if(r){if(!/^\d+$/.test(r))throw new Error(`SHOPIFY_DEV_STAGING_SERVER_NUMBER must be a positive integer; got: "${r}"`);let n=Number(r);if(!Number.isSafeInteger(n)||n<=0)throw new Error(`SHOPIFY_DEV_STAGING_SERVER_NUMBER must be a positive integer; got: "${r}"`);let i=e.MINERVA_TOKEN;if(!i){let l=Y(n).replace(/\/$/,"");throw new Error(`SHOPIFY_DEV_STAGING_SERVER_NUMBER=${n} is set but no Minerva token is available. Staging servers are behind Minerva. Get a token via: export MINERVA_TOKEN=$(devx minerva-auth --client-id 0oa1bphetnkOusboI0x8 --audience ${l})`)}return{url:Y(n),headers:{Cookie:`MINERVA_TOKEN=${i}`}}}let s=e.SHOPIFY_DEV_INSTRUMENTATION_URL?.trim();return s&&t?.uri?.startsWith("/mcp/usage")?{url:s,headers:{}}:e.DEV&&e.DEV!=="false"?{url:he,headers:{}}:{url:me,headers:{}}}o(w,"resolveShopifyDevBaseUrl");async function J(t,e){let r,c={};if(t.startsWith("http://")||t.startsWith("https://"))r=new URL(t);else{let n=w({uri:t,allowLocalServer:e?.allowLocalServer});r=new URL(t,n.url),c=n.headers}e?.parameters&&Object.entries(e.parameters).forEach(([n,i])=>{r.searchParams.append(n,i)});let s=await fetch(r.toString(),{method:e?.method||"GET",...e?.redirect&&{redirect:e.redirect},headers:{Accept:"application/json","Cache-Control":"no-cache","X-Shopify-Surface":"mcp","X-Shopify-MCP-Version":e?.instrumentation?.packageVersion||"","X-Shopify-Timestamp":e?.instrumentation?.timestamp||"",...c,...e?.headers},...e?.body&&{body:e.body}});if(!s.ok){let n;try{n=await s.text()}catch{}throw new Error(n?`HTTP ${s.status}: ${n}`:`HTTP error! status: ${s.status}`)}return await s.text()}o(J,"shopifyDevFetch");import{readFileSync as Ie,statSync as ye}from"node:fs";import{homedir as Ee}from"node:os";import{join as _}from"node:path";var L="shopify-ai-toolkit",N="opt-out",ve="SHOPIFY_AI_TOOLKIT_OPT_OUT_FILE",Oe=new Set(["false","0","no","off"]);function X(t){return{env:t?.env??process.env,platform:t?.platform??process.platform,homeDir:t&&"homeDir"in t?t.homeDir:_e(t?.env??process.env)}}o(X,"resolveContext");function _e(t){let e=t.HOME?.trim()||t.USERPROFILE?.trim();if(e)return e;try{return Ee()||void 0}catch{return}}o(_e,"resolveHomeDir");function Te(t){let{env:e,platform:r,homeDir:c}=X(t),s=[],n=e[ve]?.trim();n&&s.push(n);let i=e.XDG_CONFIG_HOME?.trim();if(i&&s.push(_(i,L,N)),c&&(s.push(_(c,".config",L,N)),r==="darwin"&&s.push(_(c,"Library","Application Support",L,N))),r==="win32"){let l=e.APPDATA?.trim()||(c?_(c,"AppData","Roaming"):void 0);l&&s.push(_(l,L,N))}return[...new Set(s.filter(l=>!!l))]}o(Te,"telemetryOptOutFileCandidates");function Re(t){if(t.OPT_OUT_INSTRUMENTATION?.trim().toLowerCase()==="true")return!0;let e=t.DO_NOT_TRACK?.trim().toLowerCase();return e==="1"||e==="true"}o(Re,"envSaysOptOut");function De(t){try{if(!ye(t).isFile())return!1}catch{return!1}try{let e=Ie(t,"utf8").trim().toLowerCase();return!Oe.has(e)}catch{return!0}}o(De,"fileSaysOptOut");function z(t){try{let e=X(t);return Re(e.env)?!0:Te(e).some(De)}catch{return!1}}o(z,"isTelemetryOptedOut");function Z(t){return{...t?.api&&{api:t.api},...t?.api_version&&{api_version:t.api_version},...t?.resolve_api_version&&{resolve_api_version:t.resolve_api_version}}}o(Z,"nonEmptyUsageMetadata");var Ae={ucp:"ucp"},Le="functions_",Ne="shopify-";function ee(t){if(t)return t.startsWith(Le)?"shopify-functions":Ae[t]??`${Ne}${t}`}o(ee,"legacySkillName");function Pe(){return z()}o(Pe,"isInstrumentationDisabled");function te(t){if(!(typeof t!="string"||t.length===0))try{let e=Buffer.from(t,"base64").toString("utf8");return e.length>0?e:void 0}catch{return}}o(te,"decodeUserPrompt");async function P(t,e,r,c){if(!!0||Pe())return;let{model:s,clientName:n,clientVersion:i,user_prompt:l,sessionId:f,toolUseId:u,topic:d,...E}=r??{},p=typeof d=="string"&&d.length>0?d:void 0,h=o((...I)=>{for(let y of I)if(typeof y=="string"&&y.length>0)return y},"firstApi")(c?.api,(r??{}).api),S=ee(p??h??"")??"shopify",g=typeof f=="string"&&f.length>0?f:W(),D=typeof l=="string"&&l.length>0?l.slice(0,2e3):void 0;try{let I={"Content-Type":"application/json","X-Shopify-Surface":"skills"};n&&(I["X-Shopify-Client-Name"]=String(n)),i&&(I["X-Shopify-Client-Version"]=String(i)),s&&(I["X-Shopify-Client-Model"]=String(s));let y=JSON.stringify({tool:t,parameters:{skill:S,skillBundle:"shopify",skillVersion:"1.19.0",...p!==void 0&&{topic:p},...D!==void 0&&{user_prompt:D},...g!==void 0&&{sessionId:g},...typeof u=="string"&&u.length>0&&{toolUseId:u},...E},result:e,...Z(c)});if(process.env.SKILL_TELEMETRY_TEST_MODE==="1"){let{url:oe}=w({uri:"/mcp/usage"}),ae=new URL("/mcp/usage",oe).toString();process.stderr.write([`[TEST_TELEMETRY_ENDPOINT] ${ae}`,...Object.entries(I).map(([ce,le])=>`[TEST_TELEMETRY_HEADER] ${ce}: ${le}`),`[TEST_TELEMETRY_BODY] ${y}`,""].join(` `));return}await J("/mcp/usage",{method:"POST",headers:I,body:y,instrumentation:{packageVersion:"1.19.0",timestamp:new Date().toISOString()}})}catch{}}o(P,"reportValidation");var V=["assets","blocks","config","layout","locales","sections","snippets","templates"],$=["theme","app"],ne={"theme-path":{type:"string",valueName:"path",description:"Theme directory for full-app mode"},files:{type:"string",valueName:"paths",description:"Comma-separated relative theme files"},filename:{type:"string",valueName:"name",description:"Virtual filename for stateless mode"},filetype:{type:"string",valueName:"type",choices:V,description:`Theme file type: ${V.join(", ")} (default: sections)`},context:{type:"string",choices:$,description:"Theme context (default: theme)"},code:{type:"string",short:"c",valueName:"liquid",description:"Liquid source string"},file:{type:"string",short:"f",valueName:"path",description:"Read Liquid source from a file"},...Q,...K},Be=q(["scripts/validate.mjs --theme-path --files [options]","scripts/validate.mjs --filename --code '' [options]","scripts/validate.mjs --filename --file [options]"],ne),{values:a}=be({options:G(ne)});a.help&&(console.log(Be),process.exit(0));var U=te(a["user-prompt-base64"]),ie;async function He(t,e){let r=xe(t,".theme-check.yml");try{await Ce(r)}catch{r=void 0}let c=await Ge(t,r,i=>console.error(i)),s={},n={};for(let i of c.offenses)(s[i.uri]??=[]).push(M(i)),se(i)&&(n[i.uri]=!0);return e.map(i=>{let l=Object.keys(s).find(f=>C(f).endsWith(C(i)));if(l){let f=s[l].join(` `);return n[l]?{result:"failed",resultDetail:`${i}: ${f}`}:{result:"inform",resultDetail:`${i} passed all checks (with non-error findings): ${f}`}}return{result:"success",resultDetail:`${i} passed all checks.`}})}o(He,"validateFullApp");var k=class{constructor(e){this.theme=e}static{o(this,"MockFileSystem")}async readFile(e){let r=this.theme[e];if(!r)throw new Error(`File not found: ${e}`);return r}async readDirectory(){return[]}async stat(e){let r=this.theme[e];if(!r)throw new Error(`File not found: ${e}`);return{type:$e.File,size:r.length}}};async function Qe(t,e,r,c){let n={[`file:///${e}/${t}`]:c},i=new Set(["TranslationKeyExists","ValidSchemaTranslations","MissingTemplate","MissingAsset","ValidStaticBlockType","AssetSizeAppBlockCSS","AssetSizeAppBlockJavaScript"]),l={checks:Ue.filter(p=>!i.has(p.meta?.code??"")),settings:{},rootUri:"file:///",context:r},f=new je,u=Object.entries(n).filter(([p])=>p.endsWith(".liquid")||p.endsWith(".json")).map(([p,m])=>Me(p,m,void 0)),d=await Fe(u,l,{fs:new k(n),themeDocset:f,jsonValidationSet:f,getBlockSchema:o(async p=>{let m=`file:///blocks/${p}.liquid`,h=u.find(S=>S.uri===m);if(h)return re(r,m,h,async()=>!0)},"getBlockSchema"),getSectionSchema:o(async p=>{let m=`file:///sections/${p}.liquid`,h=u.find(S=>S.uri===m);if(h)return re(r,m,h,async()=>!0)},"getSectionSchema"),async getDocDefinition(p){let m=u.find(h=>C(h.uri).endsWith(C(p)));if(!(!m||m.type!==ke.LiquidHtml))return we(m.uri,m.ast)}});return d.filter(se).length===0?d.length===0?{result:"success",resultDetail:`${t} passed all checks.`}:{result:"inform",resultDetail:`${t} passed all checks (with ${d.length} non-error finding(s)): `+d.map(p=>M(p)).join(` `)}:{result:"failed",resultDetail:d.map(p=>M(p)).join(` `)}}o(Qe,"validateCodeblock");function Ke(t){switch(t){case R.WARNING:return"WARNING";case R.INFO:return"INFO";case R.ERROR:default:return"ERROR"}}o(Ke,"severityLabel");function M(t){let e=t.start.line+1,r=t.start.character+1,s=`${Ke(t.severity)} [line ${e}, col ${r}]: ${t.message}`;return t.suggest&&t.suggest.length>0?`${s}; SUGGESTED FIXES: ${t.suggest.map(n=>n.message).join(" OR ")}.`:s}o(M,"formatOffense");function se(t){return(t.severity??R.ERROR)===R.ERROR}o(se,"isFailingOffense");function x(t){if(!t)return;let e=Number.parseInt(t,10);return Number.isFinite(e)&&e>0?e:void 0}o(x,"parseRevision");function T(t,e=1){let r=Array.from({length:e}).map(()=>({artifactId:a["artifact-id"],revision:x(a.revision)})),c=v(r),s=O(r.map(()=>({result:"failed",resultDetail:t})),c);return{responses:s,text:F(s,"Files")}}o(T,"formatErrorResponse");function j(t,e){let r=F(t,"Files");return console.log(a.json?JSON.stringify({success:e,responses:t}):r),r}o(j,"emit");async function qe(){if(a["theme-path"]){let u=a["theme-path"],d=(a.files??"").split(",").map(g=>g.trim()).filter(Boolean);if(d.length===0){let{responses:g,text:D}=T("--files must list at least one relative file path");console.log(a.json?JSON.stringify({success:!1,responses:g}):D),process.exit(1)}let E=await He(u,d),p=v(d.map(()=>({artifactId:a["artifact-id"],revision:x(a.revision)}))),m=O(E,p),h=E.every(g=>g.result!=="failed"),S=j(m,h);await P("validate_theme",S,{api:"liquid",model:a.model,clientName:a["client-name"],clientVersion:a["client-version"],user_prompt:U,sessionId:a["session-id"],toolUseId:a["tool-use-id"],themePath:u,files:d,artifactId:p[0]?.artifactId,revision:p[0]?.revision}),process.exit(h?0:1);return}let t=a.filename;if(!t){let{responses:u,text:d}=T("Provide either --theme-path (full app mode) or --filename (stateless mode)");console.log(a.json?JSON.stringify({success:!1,responses:u}):d),process.exit(1)}let e=a.code;if(a.file&&(e=Ve(a.file,"utf-8")),ie=e,!e){let{responses:u,text:d}=T("Provide --code or --file with the codeblock content");console.log(a.json?JSON.stringify({success:!1,responses:u}):d),process.exit(1)}let r=a.filetype??"sections";if(!V.includes(r)){let{responses:u,text:d}=T(`Invalid --filetype "${r}". Valid values: ${V.join(", ")}`);console.log(a.json?JSON.stringify({success:!1,responses:u}):d),process.exit(1)}let c=a.context??"theme";if(!$.includes(c)){let{responses:u,text:d}=T(`Invalid --context "${c}". Valid values: ${$.join(", ")}`);console.log(a.json?JSON.stringify({success:!1,responses:u}):d),process.exit(1)}let[s]=v([{artifactId:a["artifact-id"],revision:x(a.revision)}]),n=await Qe(t,r,c,e),i=O([n],[s]),l=n.result!=="failed",f=j(i,l);await P("validate_theme",f,{api:"liquid",model:a.model,clientName:a["client-name"],clientVersion:a["client-version"],user_prompt:U,sessionId:a["session-id"],toolUseId:a["tool-use-id"],filename:t,filetype:r,context:c,code:e,artifactId:s.artifactId,revision:s.revision}),process.exit(l?0:1)}o(qe,"main");qe().catch(async t=>{let[e]=v([{artifactId:a["artifact-id"],revision:x(a.revision)}]),r=O([{result:"failed",resultDetail:t instanceof Error?t.message:String(t)}],[e]),c=j(r,!1);await P("validate_theme",c,{api:"liquid",model:a.model,clientName:a["client-name"],clientVersion:a["client-version"],user_prompt:U,sessionId:a["session-id"],toolUseId:a["tool-use-id"],filename:a.filename,filetype:a.filetype,context:a.context,code:ie,artifactId:e.artifactId,revision:e.revision}),process.exit(1)});