@grum-os/cli 0.3.32 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/content.md5 +1 -1
  2. package/grum.mjs +3 -3
  3. package/package.json +2 -2
package/content.md5 CHANGED
@@ -1 +1 @@
1
- 94659f41027e21a43b2e3a771d627544
1
+ 5f97e2b69ce4ff4cd7da9393c7579505
package/grum.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- globalThis.__GRUM_VERSION__="0.3.32";
2
+ globalThis.__GRUM_VERSION__="0.3.33";
3
3
  import{chmodSync as KA,existsSync as mt,readdirSync as Yy,rmSync as xo}from"node:fs";import{homedir as sb}from"node:os";import{fileURLToPath as XA}from"node:url";import te from"node:path";import{createInterface as ZA,emitKeypressEvents as YA}from"node:readline";var Ro=(e,t,n)=>Math.min(n,Math.max(t,e)),So=e=>Ro(Math.round(e),0,255).toString(16).padStart(2,"0").toUpperCase();function Ao({r:e,g:t,b:n,a:r}){let o=`#${So(e)}${So(t)}${So(n)}`,s=Math.round(Ro(r,0,1)*255);return s>=255?o:`${o}${So(s)}`}function mb(e){let t=e.replace(/^#/,"");if(!/^[0-9a-f]+$/i.test(t)||![3,4,6,8].includes(t.length))return null;let n=t.length<=4?[...t].map(s=>s+s).join(""):t,r=parseInt(n.slice(0,6),16),o=n.length===8?parseInt(n.slice(6,8),16)/255:1;return{r:r>>16&255,g:r>>8&255,b:r&255,a:o}}function Co(e,t){let n=e.trim();return n.endsWith("%")?parseFloat(n)/100*t:parseFloat(n)}function hb(e,t,n){let r=s=>(s+e/30)%12,o=s=>n-t*Math.min(n,1-n)*Math.max(-1,Math.min(r(s)-3,9-r(s),1));return[o(0)*255,o(8)*255,o(4)*255]}function $o(e,t){return e==="none"?0:Co(e,t)}function au(e,t,n){let r=(e+.3963377774*t+.2158037573*n)**3,o=(e-.1055613458*t-.0638541728*n)**3,s=(e-.0894841775*t-1.291485548*n)**3,i=[4.0767416621*r-3.3077115913*o+.2309699292*s,-1.2684380046*r+2.6097574011*o-.3413193965*s,-.0041960863*r-.7034186147*o+1.707614701*s],[l,a,c]=i.map(u=>{let p=Ro(u,0,1);return(p<=.0031308?12.92*p:1.055*p**(1/2.4)-.055)*255});return[l,a,c]}function ht(e){let t=e.trim().toLowerCase();if(t.startsWith("#"))return mb(t);let n=t.match(/^(rgba?|hsla?|oklch|oklab)\(([^)]*)\)$/);if(!n?.[1]||n[2]==null)return null;let r=n[2].split(/[\s,/]+/).filter(Boolean);if(r.length<3)return null;let o=r[3]!=null?$o(r[3],1):1;if(n[1].startsWith("ok")){let p=Ro($o(r[0],1),0,1),d=$o(r[1],.4);if(n[1]==="oklab"){let b=$o(r[2],.4);if([p,d,b,o].some(Number.isNaN))return null;let[k,y,x]=au(p,d,b);return{r:k,g:y,b:x,a:o}}let f=r[2];if(f!=="none"&&!/^-?[\d.]+(deg)?$/.test(f))return null;let m=(f==="none"?0:parseFloat(f))*Math.PI/180;if([p,d,m,o].some(Number.isNaN))return null;let[g,S,h]=au(p,d*Math.cos(m),d*Math.sin(m));return{r:g,g:S,b:h,a:o}}if(n[1].startsWith("rgb")){let[p,d,f]=r.slice(0,3).map(m=>Co(m,255));return[p,d,f,o].some(Number.isNaN)?null:{r:p,g:d,b:f,a:o}}let s=parseFloat(r[0]),i=Co(r[1],1),l=Co(r[2],1);if([s,i,l,o].some(Number.isNaN))return null;let[a,c,u]=hb((s%360+360)%360,i,l);return{r:a,g:c,b:u,a:o}}function ue(e){let t=ht(e);return t?Ao(t):null}function lu({r:e,g:t,b:n}){let r=p=>{let d=p/255;return d<=.04045?d/12.92:((d+.055)/1.055)**2.4},[o,s,i]=[r(e),r(t),r(n)],l=(o*.4124+s*.3576+i*.1805)/.95047,a=o*.2126+s*.7152+i*.0722,c=(o*.0193+s*.1192+i*.9505)/1.08883,u=p=>p>.008856?Math.cbrt(p):7.787*p+16/116;return[116*u(a)-16,500*(u(l)-u(a)),200*(u(a)-u(c))]}function Si(e){return`--color-${e.replace(/^#/,"").slice(0,6).toLowerCase()}`}function Io(e,t){let n=ht(e),r=ht(t);if(!n||!r)return 1/0;let[o,s,i]=lu(n),[l,a,c]=lu(r);return Math.hypot(o-l,s-a,i-c)}var mr=/#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})\b|\b(?:rgba?|hsla?|oklch|oklab)\([^)]*\)/gi;var $i={slate:{50:"#f8fafc",100:"#f1f5f9",200:"#e2e8f0",300:"#cbd5e1",400:"#94a3b8",500:"#64748b",600:"#475569",700:"#334155",800:"#1e293b",900:"#0f172a",950:"#020617"},gray:{50:"#f9fafb",100:"#f3f4f6",200:"#e5e7eb",300:"#d1d5db",400:"#9ca3af",500:"#6b7280",600:"#4b5563",700:"#374151",800:"#1f2937",900:"#111827",950:"#030712"},zinc:{50:"#fafafa",100:"#f4f4f5",200:"#e4e4e7",300:"#d4d4d8",400:"#a1a1aa",500:"#71717a",600:"#52525b",700:"#3f3f46",800:"#27272a",900:"#18181b",950:"#09090b"},neutral:{50:"#fafafa",100:"#f5f5f5",200:"#e5e5e5",300:"#d4d4d4",400:"#a3a3a3",500:"#737373",600:"#525252",700:"#404040",800:"#262626",900:"#171717",950:"#0a0a0a"},stone:{50:"#fafaf9",100:"#f5f5f4",200:"#e7e5e4",300:"#d6d3d1",400:"#a8a29e",500:"#78716c",600:"#57534e",700:"#44403c",800:"#292524",900:"#1c1917",950:"#0c0a09"},red:{50:"#fef2f2",100:"#fee2e2",200:"#fecaca",300:"#fca5a5",400:"#f87171",500:"#ef4444",600:"#dc2626",700:"#b91c1c",800:"#991b1b",900:"#7f1d1d",950:"#450a0a"},orange:{50:"#fff7ed",100:"#ffedd5",200:"#fed7aa",300:"#fdba74",400:"#fb923c",500:"#f97316",600:"#ea580c",700:"#c2410c",800:"#9a3412",900:"#7c2d12",950:"#431407"},amber:{50:"#fffbeb",100:"#fef3c7",200:"#fde68a",300:"#fcd34d",400:"#fbbf24",500:"#f59e0b",600:"#d97706",700:"#b45309",800:"#92400e",900:"#78350f",950:"#451a03"},yellow:{50:"#fefce8",100:"#fef9c3",200:"#fef08a",300:"#fde047",400:"#facc15",500:"#eab308",600:"#ca8a04",700:"#a16207",800:"#854d0e",900:"#713f12",950:"#422006"},lime:{50:"#f7fee7",100:"#ecfccb",200:"#d9f99d",300:"#bef264",400:"#a3e635",500:"#84cc16",600:"#65a30d",700:"#4d7c0f",800:"#3f6212",900:"#365314",950:"#1a2e05"},green:{50:"#f0fdf4",100:"#dcfce7",200:"#bbf7d0",300:"#86efac",400:"#4ade80",500:"#22c55e",600:"#16a34a",700:"#15803d",800:"#166534",900:"#14532d",950:"#052e16"},emerald:{50:"#ecfdf5",100:"#d1fae5",200:"#a7f3d0",300:"#6ee7b7",400:"#34d399",500:"#10b981",600:"#059669",700:"#047857",800:"#065f46",900:"#064e3b",950:"#022c22"},teal:{50:"#f0fdfa",100:"#ccfbf1",200:"#99f6e4",300:"#5eead4",400:"#2dd4bf",500:"#14b8a6",600:"#0d9488",700:"#0f766e",800:"#115e59",900:"#134e4a",950:"#042f2e"},cyan:{50:"#ecfeff",100:"#cffafe",200:"#a5f3fc",300:"#67e8f9",400:"#22d3ee",500:"#06b6d4",600:"#0891b2",700:"#0e7490",800:"#155e75",900:"#164e63",950:"#083344"},sky:{50:"#f0f9ff",100:"#e0f2fe",200:"#bae6fd",300:"#7dd3fc",400:"#38bdf8",500:"#0ea5e9",600:"#0284c7",700:"#0369a1",800:"#075985",900:"#0c4a6e",950:"#082f49"},blue:{50:"#eff6ff",100:"#dbeafe",200:"#bfdbfe",300:"#93c5fd",400:"#60a5fa",500:"#3b82f6",600:"#2563eb",700:"#1d4ed8",800:"#1e40af",900:"#1e3a8a",950:"#172554"},indigo:{50:"#eef2ff",100:"#e0e7ff",200:"#c7d2fe",300:"#a5b4fc",400:"#818cf8",500:"#6366f1",600:"#4f46e5",700:"#4338ca",800:"#3730a3",900:"#312e81",950:"#1e1b4b"},violet:{50:"#f5f3ff",100:"#ede9fe",200:"#ddd6fe",300:"#c4b5fd",400:"#a78bfa",500:"#8b5cf6",600:"#7c3aed",700:"#6d28d9",800:"#5b21b6",900:"#4c1d95",950:"#2e1065"},purple:{50:"#faf5ff",100:"#f3e8ff",200:"#e9d5ff",300:"#d8b4fe",400:"#c084fc",500:"#a855f7",600:"#9333ea",700:"#7e22ce",800:"#6b21a8",900:"#581c87",950:"#3b0764"},fuchsia:{50:"#fdf4ff",100:"#fae8ff",200:"#f5d0fe",300:"#f0abfc",400:"#e879f9",500:"#d946ef",600:"#c026d3",700:"#a21caf",800:"#86198f",900:"#701a75",950:"#4a044e"},pink:{50:"#fdf2f8",100:"#fce7f3",200:"#fbcfe8",300:"#f9a8d4",400:"#f472b6",500:"#ec4899",600:"#db2777",700:"#be185d",800:"#9d174d",900:"#831843",950:"#500724"},rose:{50:"#fff1f2",100:"#ffe4e6",200:"#fecdd3",300:"#fda4af",400:"#fb7185",500:"#f43f5e",600:"#e11d48",700:"#be123c",800:"#9f1239",900:"#881337",950:"#4c0519"}};function cu(e){let t=e.lastIndexOf("-");return t<=0?null:$i[e.slice(0,t)]?.[e.slice(t+1)]??null}function On(e){let t=[],n=0,r=0;for(let o=0;o<e.length;o++){let s=e[o];s==="("||s==="["?n++:s===")"||s==="]"?n=Math.max(0,n-1):s===","&&n===0&&(t.push(e.slice(r,o).trim()),r=o+1)}return t.push(e.slice(r).trim()),t.filter(Boolean)}function yb(e,t){if(!e)return t;let n=On(e);return On(t).flatMap(r=>n.map(o=>r.includes("&")?r.replaceAll("&",o):`${o} ${r}`)).join(", ")}var bb=/^@(?:-\w+-)?(?:keyframes|font-face|property)\b/i;function Eo(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,i=>i.replace(/[^\n]/g," ")),n=[],r=[],o=0,s=1;for(let i=0;i<t.length;i++){let l=t[i];if(l===`
4
4
  `&&s++,l==="{"){let a=t.slice(o,i).trim(),c=r[r.length-1];c&&(c.own+=t.slice(c.segStart,o));let u=/^@theme\b/i.test(a),p=a.startsWith("@")&&!u,d=[...r].reverse().find(f=>!f.isAt)??null;r.push({raw:a,resolved:p?a:u?":root":yb(d?.resolved??null,a),isAt:p,skip:(c?.skip??!1)||p&&bb.test(a),line:s,own:"",segStart:i+1}),o=i+1}else if(l==="}"){let a=r.pop();if(a){a.own+=t.slice(a.segStart,i),!a.isAt&&!a.skip&&n.push({selector:a.resolved,body:a.own,line:a.line,context:r.filter(u=>u.isAt).map(u=>u.raw.replace(/\s+/g," ")).join(" \xB7 ")});let c=r[r.length-1];c&&(c.segStart=i+1)}o=i+1}else l===";"&&(o=i+1)}return n}function To(e){let t=[],n=0,r=0,o=s=>{let i=s.indexOf(":");if(i<=0)return;let l=s.slice(0,i).trim(),a=s.slice(i+1).replace(/\s+/g," ").trim();/^-{0,2}[a-z][\w-]*$/i.test(l)&&a&&t.push({property:l,value:a})};for(let s=0;s<e.length;s++){let i=e[s];i==="("?n++:i===")"?n=Math.max(0,n-1):i===";"&&n===0&&(o(e.slice(r,s)),r=s+1)}return o(e.slice(r)),t}var uu=e=>e.replace(/\\(.)/g,"$1");function wb(e){let t=e;for(;;){let n=/:(?:global|local)\(/i.exec(t);if(!n)break;let r=0,o=-1;for(let i=n.index+n[0].length-1;i<t.length;i++){let l=t[i];if(l==="(")r++;else if(l===")"&&--r===0){o=i;break}}if(o<0)break;let s=t.slice(n.index+n[0].length,o).trim();t=`${t.slice(0,n.index)}${s}${t.slice(o+1)}`}return t.replace(/:(?:global|local)(?=\s|$)/gi,"").trim()}var Ci=/\*|[a-z][\w-]*|\.((?:\\.|[\w-])+)|#((?:\\.|[\w-])+)|\[\s*([\w-]+)\s*(?:([~|^$*]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\]\s]*)))?\s*[is]?\s*\]|::([\w-]+)|:([\w-]+)(\((?:[^()]|\([^()]*\))*\))?/giy;function nt(e){let t=[];Ci.lastIndex=0;let n=0;for(;n<e.length;){Ci.lastIndex=n;let r=Ci.exec(e);if(!r||r.index!==n)return null;let o=r[0];if(n+=o.length,o==="*")t.push({kind:"universal",raw:o});else if(r[1]!==void 0)t.push({kind:"class",raw:o,name:uu(r[1])});else if(r[2]!==void 0)t.push({kind:"id",raw:o,name:uu(r[2])});else if(r[3]!==void 0)t.push({kind:"attr",raw:o,name:r[3],op:r[4]??null,value:r[4]?r[5]??r[6]??r[7]??"":null});else if(r[8]!==void 0)t.push({kind:"pseudoElement",raw:o,name:r[8].toLowerCase()});else if(r[9]!==void 0){let s=r[9].toLowerCase();/^(?:before|after|first-line|first-letter)$/.test(s)?t.push({kind:"pseudoElement",raw:o,name:s}):t.push({kind:"pseudo",raw:o,name:s,args:r[10]?r[10].slice(1,-1).trim():null})}else if(t.length===0)t.push({kind:"tag",raw:o,name:o.toLowerCase()});else return null}return t.length?t:null}function pe(e){let t=wb(e),n=[],r=0,o=0,s=null,i=!0,l=a=>{let c=t.slice(o,a).trim();if(!c)return!0;let u=nt(c);return u?(n.push({combinator:i?null:s??" ",simples:u}),i=!1,s=null,!0):!1};for(let a=0;a<t.length;a++){let c=t[a];if(c==="("||c==="[")r++;else if(c===")"||c==="]")r=Math.max(0,r-1);else if(r===0&&(c===">"||c==="+"||c==="~")){if(!l(a)||s)return null;s=c,o=a+1}else if(r===0&&/\s/.test(c)){if(!l(a))return null;o=a+1}}return!l(t.length)||s?null:n.length?n:null}function jn(e){return e.map(t=>t.raw).join("")}function hr(e){return e.map((t,n)=>{let r=jn(t.simples);return n===0?r:t.combinator===" "?` ${r}`:` ${t.combinator} ${r}`}).join("")}function oe(e){let t=[],n=0,r=0;for(let o=0;o<e.length;o++){let s=e[o];s==="("||s==="["?n++:s===")"||s==="]"?n=Math.max(0,n-1):s===","&&n===0&&(t.push(e.slice(r,o).trim()),r=o+1)}return t.push(e.slice(r).trim()),t.filter(Boolean)}var fu=e=>e.replace(/([a-z\d])([A-Z])/g,"$1-$2").split(/[^a-z0-9]+/i).filter(Boolean).map(t=>t.toLowerCase());function pu(e,t){let n=e.toLowerCase();return t.some(r=>n.includes(r.toLowerCase()))}function Ri(e,t){let n=t.excludeClassKeywords;if(!n?.length)return!1;let r=new Set(n.flatMap(o=>[o.toLowerCase(),`${o.toLowerCase()}s`]));return e.some(o=>o.kind==="class"&&fu(o.name).some(s=>r.has(s)))}var kb=new Set(["html","body"]),xb=new Set(["svg","path","use","img","picture","video","i","em","strong","b","small","kbd","sup","sub"]);function Oo(e,t){for(let n of e)switch(n.kind){case"tag":if(t.elements.includes(n.name))return!0;break;case"class":if(pu(n.name,t.classKeywords))return!0;break;case"attr":if(n.name==="role"&&n.value!==null&&t.roles.includes(n.value.toLowerCase())||n.name==="data-component"&&n.value!==null&&pu(n.value,t.classKeywords))return!0;break;case"pseudo":if((n.name==="is"||n.name==="where")&&n.args){let r=oe(n.args).map(pe);if(r.length>0&&r.every(o=>o!==null&&Oo(o[o.length-1].simples,t)))return!0}break;default:break}return!1}function gu(e){return e.some(n=>n.kind==="universal")?e.length===1:e.some(n=>n.kind==="tag"&&kb.has(n.name))?!0:!e.some(n=>n.kind==="tag"||n.kind==="class"||n.kind==="attr"||n.kind==="id"||n.kind==="pseudo"&&(n.name==="is"||n.name==="where"))}function mu(e){return e.some(t=>t.kind==="tag"&&xb.has(t.name))}var _o=["hover","focus","active","selected","current","loading","disabled"],vb={hover:"hover",hovered:"hover",focus:"focus",focused:"focus","focus-visible":"focus","focus-within":"focus",active:"selected",pressed:"selected",selected:"selected",checked:"selected",on:"selected",current:"current",loading:"loading",busy:"loading",pending:"loading",disabled:"disabled"},Sb={hover:"hover",focus:"focus","focus-visible":"focus","focus-within":"focus",active:"active",checked:"selected",disabled:"disabled"},$b={disabled:"disabled","aria-disabled":"disabled","data-disabled":"disabled","aria-pressed":"selected","aria-selected":"selected","aria-expanded":"selected","aria-checked":"selected","data-active":"selected","data-selected":"selected","data-pressed":"selected","data-checked":"selected","data-highlighted":"selected","aria-current":"current","aria-busy":"loading","data-loading":"loading","data-busy":"loading","data-hover":"hover","data-hovered":"hover","data-focus":"focus","data-focused":"focus","data-focus-visible":"focus","data-open":"open","data-expanded":"open"},Cb=new Set(["active","selected","pressed","checked","loading","busy","disabled","open","current","hover","focus","show","expanded","collapsed"]),du=e=>vb[e.toLowerCase()]??e.toLowerCase(),Tt={hover:"__gs-hover",focus:"__gs-focus",active:"__gs-active"};function hu(e){if(e.kind==="pseudo"){let t=Sb[e.name];if(!t)return null;let n=Tt[t];return{state:t,marker:n?{classes:[n],attrs:{}}:{classes:[],attrs:{[e.name==="checked"?"checked":"disabled"]:""}}}}if(e.kind==="attr"){if(e.name==="data-state"&&e.value!==null)return{state:du(e.value),marker:{classes:[],attrs:{"data-state":e.value}}};let t=$b[e.name];if(!t||e.value!==null&&/^(?:false|off|0)$/i.test(e.value))return null;let n=e.value??(e.name.startsWith("aria-")?"true":"");return{state:t,marker:{classes:[],attrs:{[e.name]:n}}}}if(e.kind==="class"){let t=/^(?:is|has)-(.+)$/i.exec(e.name),n=t?t[1]:Cb.has(e.name.toLowerCase())?e.name:null;return n?{state:du(n),marker:{classes:[e.name],attrs:{}}}:null}return null}function Rb(e){return e.kind!=="pseudo"||e.name!=="not"||!e.args?!1:oe(e.args).every(t=>{let n=nt(t);return n!==null&&n.every(r=>hu(r)!==null)})}function yt(e){let t=[],n=new Map;for(let s of e){if(Rb(s))continue;let i=hu(s);if(!i){t.push(s);continue}let l=n.get(i.state)??{classes:[],attrs:{}};for(let a of i.marker.classes)l.classes.includes(a)||l.classes.push(a);Object.assign(l.attrs,i.marker.attrs),n.set(i.state,l)}let r=[...n.keys()].sort((s,i)=>Pe(s)-Pe(i)||s.localeCompare(i)),o={classes:[],attrs:{}};for(let s of r){let i=n.get(s);o.classes.push(...i.classes),Object.assign(o.attrs,i.attrs)}return{identity:t,states:r,marker:o}}function Pe(e){if(e==="default")return-1;let t=_o.indexOf(e);return t===-1?_o.length:t}function rt(e){return e.length?e.join(" + "):"default"}function Ai(e){return e.replace(/:hover\b/g,`.${Tt.hover}`).replace(/:focus(?:-visible|-within)?\b/g,`.${Tt.focus}`).replace(/:active\b/g,`.${Tt.active}`)}var Ab=new Set(["background","background-color","background-image","background-position","background-size","background-repeat","background-clip","background-origin","background-attachment","color","border-color","border-top-color","border-right-color","border-bottom-color","border-left-color","outline","outline-color","outline-offset","box-shadow","filter","backdrop-filter","opacity","fill","stroke","caret-color","accent-color","text-decoration-color","mix-blend-mode"]),Ib=new Set(["width","min-width","max-width","flex","flex-grow","flex-shrink","flex-basis","align-self","justify-self","order","margin","margin-top","margin-right","margin-bottom","margin-left","margin-inline","margin-block","margin-inline-start","margin-inline-end","margin-block-start","margin-block-end","position","inset","inset-inline","inset-block","top","right","bottom","left","z-index","float","clear","grid-column","grid-row","grid-area","place-self"]),Eb=new Set(["cursor","user-select","pointer-events","transition","transition-property","transition-duration","transition-timing-function","transition-delay","will-change","touch-action","-webkit-tap-highlight-color","appearance","-webkit-appearance","scroll-margin","scroll-margin-top"]);function yr(e){let t=e.toLowerCase();return Ab.has(t)?"paint":Ib.has(t)?"layout":Eb.has(t)?"hint":"box"}var Tb={shadow:/^box-shadow$/,cursor:/^cursor$/,rounded:/^border(?:-[a-z]+)*-radius$/,radius:/^border(?:-[a-z]+)*-radius$/};function yu(e,t){let n=e.filter(s=>s.kind==="class");if(n.length!==1||e.length!==1)return!1;let r=fu(n[0].name)[0],o=r?Tb[r]:void 0;return o?t.length>0&&t.every(s=>o.test(s.property.toLowerCase())):!1}function br(e){let t=[],n=/var\(\s*(--[\w-]+)\s*(,)?/g;for(let r of e.matchAll(n)){let o=null;if(r[2]){let s=1,i=r.index+r[0].length,l=i;for(;l<e.length;l++)if(e[l]==="(")s++;else if(e[l]===")"&&--s===0)break;o=e.slice(i,l).trim()}t.push({name:r[1],fallback:o,index:r.index})}return t}function _b(e){let t="",n=0;for(;n<e.length;){let r=e.indexOf("var(",n);if(r<0)return t+e.slice(n);t+=e.slice(n,r);let o=0,s=r+3;for(;s<e.length;s++)if(e[s]==="(")o++;else if(e[s]===")"&&--o===0)break;n=s+1}return t}var Ob=/(?<![\w#-])-?\d*\.?\d+(?:px|rem|em|vw|vh|vmin|vmax|ch|ex|%)/gi;function jo(e){let t=_b(e);return new RegExp(mr.source,"i").test(t)?!0:[...t.matchAll(Ob)].some(n=>parseFloat(n[0])!==0)}function bu(e,t){let n=ue(e),r=ue(t);return n||r?n===r:e.trim().toLowerCase()===t.trim().toLowerCase()}function jb(e){let t=pe(e);if(!t)return null;let n=o=>new Set(t[o].simples.flatMap(s=>s.kind==="class"?[s.name]:[])),r=[n(t.length-1)];for(let o=t.length-2;o>=0;o--){let s=t[o+1].combinator;(s===" "||s===">")&&r.push(n(o))}for(let o of[...r])for(let s of o){let i=s.match(/^(.+?)__/)?.[1];i&&r.push(new Set([i]))}return r}var Mb=(e,t)=>[...t].some(n=>e.startsWith(`${n}--`));function Pb(e,t,n){let r="no";for(let o of oe(e.selector)){let s=pe(o);if(!s)return"possible";let i=s[s.length-1].simples,l=i.flatMap(u=>u.kind==="class"?[u.name]:[]),a=s.length>1||e.context!==""||i.some(u=>u.kind!=="class"&&u.kind!=="tag"),c="no";if(l.length?t.some(u=>l.every(p=>u.has(p)))?c=a?"possible":"proven":(t.some(u=>l.every(p=>u.has(p)||Mb(p,u)))||n.some(u=>l.every(p=>u.includes(p))&&t.some(p=>u.some(d=>p.has(d)))))&&(c="possible"):c="possible",c==="proven")return c;c==="possible"&&(r=c)}return r}function wu(e){let t=[];for(let n of e.matchAll(/\bclass(?:Name)?\s*=\s*\{?\s*["'`]([^"'`]+)["'`]/g)){let r=n[1].split(/\s+/).filter(o=>/^[\w-]+$/.test(o));r.length>1&&t.push(r)}return t}function ku(e,t,n){let r=new Map;for(let a of e)r.set(a.name,[...r.get(a.name)??[],a]);let o=()=>({covered:!0,tokens:[],through:[],failing:[],reason:null}),s=(a,c)=>{a.tokens.push(...c.tokens.filter(u=>!a.tokens.includes(u))),a.through.push(...c.through.filter(u=>!a.through.includes(u))),a.failing.push(...c.failing.filter(u=>!a.failing.includes(u))),c.covered||(a.covered=!1,a.reason??=c.reason)};function i(a,c,u){let p=o();for(let d of br(a))s(p,l(d.name,d.fallback,c,u));return jo(a)&&(p.covered=!1,p.reason??="set to a hard-coded value"),p}function l(a,c,u,p){if(p.has(a))return{...o(),covered:!1,reason:"refers to itself"};let d=new Set([...p,a]),f=[],m=[];for(let h of r.get(a)??[]){let b=Pb(h,u,t);b==="proven"?f.push(h):b==="possible"&&m.push(h)}let g=n.get(a),S=o();f.length||(g!==void 0?S.tokens.push(a):c==null?(S.covered=!1,S.reason="no declaration is proven to apply here"):s(S,i(c,u,d)));for(let h of[...f,...m]){if(g!==void 0&&bu(h.value,g)){S.through.push(h),S.tokens.includes(a)||S.tokens.push(a);continue}let b=i(h.value,u,d);S.through.push(h),!b.covered&&(jo(h.value)||!b.failing.length)&&b.failing.unshift(h),s(S,b)}return S}return(a,c,u)=>{let p=o();if(u==null)return p.covered=n.has(a),p.covered?p.tokens.push(a):p.reason="read outside a stylesheet rule",p;for(let d of oe(u)){let f=jb(d);if(!f){s(p,{...o(),covered:!1,reason:"selector Grum can't read"});continue}s(p,l(a,c,f,new Set))}return p}}var xu={color:"Color",typography:"Typography",spacing:"Spacing",radius:"Radius",shadow:"Shadows",other:"Other"},Db=/\.(css|scss|sass|less|pcss)$/i,Lb=/^(?::root|html|body|:host)?(?:\[data-[\w-]+(?:[~|^$*]?=[^\]]*)?\]|\.(?:(?:light|dark)(?:-mode)?|theme-[\w-]+))*$/i;function vu(e){let t=e.split(",").map(n=>n.trim());return t.length>0&&t.every(n=>n!==""&&Lb.test(n))}function Qt(e){return Db.test(e)}function Nb(...e){let t=e.map(r=>r??"").join("\0"),n=5381;for(let r=0;r<t.length;r++)n=(n*33^t.charCodeAt(r))>>>0;return n.toString(36)}function _t(e){let t=n=>(n??"").replace(/\s+/g," ").trim();return Nb(e.file,t(e.selector),t(e.context),e.property.toLowerCase(),t(e.value).toLowerCase())}function Ii(e){let t=e.evidence;return t?t.observed.length?"observed":t.definitions.some(n=>n.useCount>0)?"implemented":"defined":"unverified"}var Fb=200,Ru=/\.html?$/i,Ub=/\.(m?[jt]sx?|vue|svelte|astro)$/i,Gb=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]),Bb=new Set(["script","style","textarea","title"]),zb=new Set(["li","p","option","tr","td","th","dt","dd"]),Mo=/\{\{|\$\{|<%|\{%/;function Wb(e){return`/${e}`.replace(/^\/(?:public|static|dist|site|docs)\//,"/").replace(/(^|\/)index\.html?$/i,"$1")}function Vb(e){let t=e.split(`
5
5
  `),n={tag:"#document",id:null,classes:[],attrs:{},line:0,snippet:"",parent:null,children:[],dynamic:!1},r=[],o="",s=n,i=1,l=0,a=c=>{for(let u=l;u<c&&u<e.length;u++)e[u]===`
@@ -73,7 +73,7 @@ URL: `;function ms(e,t=gn){return typeof e=="string"&&e.length<=t&&VS.test(e)?e:
73
73
  const signInControls = Array.from(document.querySelectorAll('button,input[type="submit"],[role="button"]')).filter(visible).filter((node) => /\b(sign in|log in|login|continue with|sign in with|verify|send code|use passkey)\b/i.test(controlText(node)));
74
74
  const authFrame = Array.from(document.querySelectorAll('iframe[src]')).filter(visible).some((node) => /\b(login|signin|sign-in|oauth|authorize|authenticate|twofa|2fa|mfa|verify|verification|challenge|captcha)\b/i.test(node.getAttribute('src') || ''));
75
75
  return password || oneTimeCode || authFrame || (signInHeading && signInControls.length > 0) || signInControls.length >= 2;
76
- })()`}function Kn(e,t){let n=new Map;for(let r of e)!r.branch&&!n.has(r.driftKey)&&n.set(r.driftKey,r);for(let r of e)t&&r.branch===t&&n.set(r.driftKey,r);return n}function Vf(e,t){if(t.dismissed.has(e.key))return"dismissed";let n=t.requests.get(e.key);if(n)return n.status==="approved"?"allowed":n.status;let r=t.existing.get(e.key);return r!=null&&e.uses<=r?"existing":"open"}function Hf(e){return e==="open"||e==="rejected"}var r$=Object.keys(Bi),o$=/^#[0-9A-F]{6}([0-9A-F]{2})?$/;function zt(e,t){return typeof e=="string"&&e.trim()&&e.length<=t?e.trim():null}function s$(e){if(!Array.isArray(e)||!e.length)return null;let t=[];for(let n of e.slice(0,12)){let r=zt(n?.file,300);if(!r||!Number.isInteger(n?.line)||n.line<0||typeof n?.snippet!="string")return null;t.push({file:r,line:n.line,snippet:n.snippet.slice(0,300)})}return t}function i$(e){if(!e||typeof e!="object")return null;let t=e,n=t.issue??{},r=zt(t.projectId,100),o=zt(n.key,300),s=zt(n.title,300),i=zt(n.found,300),l=s$(n.places),a=n.area,c=n.kind;if(!r||!o||!s||!i||!l||!Ui.includes(a)||!r$.includes(c)||!o.startsWith(`${a}:${c}:`)&&!(a==="border"&&o.startsWith(`color:${c}:`))||!Number.isInteger(n.uses)||n.uses<1)return null;let u=typeof n.foundColor=="string"&&o$.test(n.foundColor)?n.foundColor:null;return{projectId:r,issue:{key:o,area:a,kind:c,title:s,found:i,foundColor:u,uses:Math.min(n.uses,1e6),places:l,...a==="input"&&(p=>p?{input:p}:{})(df(n.input))},reason:typeof t.reason=="string"&&t.reason.trim().slice(0,1e3)||null,branch:zt(t.branch,200),observed:(p=>p&&/^[0-9a-f]{1,16}$/.test(p)?p:null)(zt(t.observed,16)),capture:(p=>Bt(p)?p:null)(hn(t.capture)),locate:mn(t.locate),...(p=>p?{provenance:p}:{})(jf(t.provenance))}}var kl=/\.(?:css|scss|sass|less|html?|[cm]?[jt]sx?|vue|svelte|astro)$/i,a$=2e6;function qf(e){let t=i$(e);if(!t||t.issue.area!=="button"||!e||typeof e!="object")return null;let n=e,r=typeof n.name=="string"?Qp(n.name):null,o=zt(n.url,2e3);if(!r||!o||!Array.isArray(n.files)||!n.files.length||n.files.length>1e3)return null;let s=new Map,i=0;for(let l of n.files){if(!Array.isArray(l)||l.length!==2)return null;let[a,c]=l;if(typeof a!="string"||a.length>300||a.startsWith("/")||a.includes("\\")||a.split("/").some(u=>u===".."||u===".")||!kl.test(a)||dn(a)||typeof c!="string"||s.has(a)||(i+=new TextEncoder().encode(c).length,i>a$))return null;s.set(a,c)}return{...t,name:r,url:o,files:s}}function ws(e){if(!e||typeof e!="object")return null;let t=e;return t.schema!==1||typeof t.apiOrigin!="string"||!t.project||typeof t.project.id!="string"||!Array.isArray(t.contract)||!Array.isArray(t.dismissed)||!Array.isArray(t.requests)||!t.existing||typeof t.existing!="object"?null:{...t,buttons:rp(t.buttons),buttonSpecs:l$(t.buttonSpecs),inputSpecs:of(t.inputSpecs)}}function l$(e){if(!e||typeof e!="object")return null;let{specs:t,merged:n}=e;return!Array.isArray(t)||!n||typeof n!="object"?null:t.every(o=>o&&typeof o.id=="string"&&typeof o.name=="string"&&Array.isArray(o.states)&&Array.isArray(o.sizes))?{specs:t,merged:n}:null}function zr(e,t,n=null){let r=Kn(t.requests,n),o={dismissed:new Set(t.dismissed),requests:r,existing:new Map(Object.entries(t.existing))},s=[],i=0,l=[...zu(e,t.contract).issues,...t.buttons?xd(e,t.buttons,t.buttonSpecs??void 0):[],...t.inputSpecs?rf(e,t.inputSpecs):[]];for(let a of l){let c=Vf(a,o);if(c==="existing"||c==="dismissed"){i++;continue}let u=r.get(a.key);s.push({...a,coverage:c,note:u?.note??null,reviewUrl:u?.url??null})}return{findings:s,known:i,present:l.map(a=>a.key)}}function Xn(e){let t=`${e.key}|${e.uses}|${e.places.map(r=>`${r.file}:${r.line}:${r.snippet}`).join(`
76
+ })()`}function Kn(e,t){let n=new Map;for(let r of e)!r.branch&&!n.has(r.driftKey)&&n.set(r.driftKey,r);for(let r of e)t&&r.branch===t&&n.set(r.driftKey,r);return n}function Vf(e,t){if(t.dismissed.has(e.key))return"dismissed";let n=t.requests.get(e.key);if(n)return n.status==="approved"?"allowed":n.status;let r=t.existing.get(e.key);return r!=null&&e.uses<=r?"existing":"open"}function Hf(e){return e==="open"||e==="rejected"}var r$=Object.keys(Bi),o$=/^#[0-9A-F]{6}([0-9A-F]{2})?$/;function zt(e,t){return typeof e=="string"&&e.trim()&&e.length<=t?e.trim():null}function s$(e){if(!Array.isArray(e)||!e.length)return null;let t=[];for(let n of e.slice(0,12)){let r=zt(n?.file,300);if(!r||!Number.isInteger(n?.line)||n.line<0||typeof n?.snippet!="string")return null;t.push({file:r,line:n.line,snippet:n.snippet.slice(0,300)})}return t}function i$(e){if(!e||typeof e!="object")return null;let t=e,n=t.issue??{},r=zt(t.projectId,100),o=zt(n.key,300),s=zt(n.title,300),i=zt(n.found,300),l=s$(n.places),a=n.area,c=n.kind;if(!r||!o||!s||!i||!l||!Ui.includes(a)||!r$.includes(c)||!o.startsWith(`${a}:${c}:`)&&!(a==="border"&&o.startsWith(`color:${c}:`))||!Number.isInteger(n.uses)||n.uses<1)return null;let u=typeof n.foundColor=="string"&&o$.test(n.foundColor)?n.foundColor:null;return{projectId:r,issue:{key:o,area:a,kind:c,title:s,found:i,foundColor:u,uses:Math.min(n.uses,1e6),places:l,...a==="input"&&(p=>p?{input:p}:{})(df(n.input))},reason:typeof t.reason=="string"&&t.reason.trim().slice(0,1e3)||null,branch:zt(t.branch,200),observed:(p=>p&&/^[0-9a-f]{1,16}$/.test(p)?p:null)(zt(t.observed,16)),capture:(p=>Bt(p)?p:null)(hn(t.capture)),locate:mn(t.locate),...(p=>p?{provenance:p}:{})(jf(t.provenance))}}var kl=/\.(?:css|scss|sass|less|html?|[cm]?[jt]sx?|vue|svelte|astro)$/i,a$=2e6;function qf(e){let t=i$(e);if(!t||t.issue.area!=="button"||!e||typeof e!="object")return null;let n=e,r=typeof n.name=="string"?Qp(n.name):null,o=zt(n.url,2e3);if(!r||!o||!Array.isArray(n.files)||!n.files.length||n.files.length>1e3)return null;let s=new Map,i=0;for(let l of n.files){if(!Array.isArray(l)||l.length!==2)return null;let[a,c]=l;if(typeof a!="string"||a.length>300||a.startsWith("/")||a.includes("\\")||a.split("/").some(u=>u===".."||u===".")||!kl.test(a)||dn(a)||typeof c!="string"||s.has(a)||(i+=new TextEncoder().encode(c).length,i>a$))return null;s.set(a,c)}return{...t,name:r,url:o,files:s}}function ws(e){if(!e||typeof e!="object")return null;let t=e;return t.schema!==1||typeof t.apiOrigin!="string"||!t.project||typeof t.project.id!="string"||!Array.isArray(t.contract)||!Array.isArray(t.dismissed)||!Array.isArray(t.requests)||!t.existing||typeof t.existing!="object"?null:{...t,buttons:rp(t.buttons),buttonSpecs:l$(t.buttonSpecs),inputSpecs:of(t.inputSpecs)}}function l$(e){if(!e||typeof e!="object")return null;let{specs:t,merged:n,rules:r}=e;if(!Array.isArray(t)||!n||typeof n!="object")return null;let o=t.every(i=>i&&typeof i.id=="string"&&typeof i.name=="string"&&Array.isArray(i.states)&&Array.isArray(i.sizes)),s=Object.fromEntries(Object.entries(r&&typeof r=="object"?r:{}).flatMap(([i,l])=>{let a=Array.isArray(l)?l.filter(c=>typeof c=="string"):[];return a.length?[[i,a]]:[]}));return o?{specs:t,merged:n,...Object.keys(s).length?{rules:s}:{}}:null}function zr(e,t,n=null){let r=Kn(t.requests,n),o={dismissed:new Set(t.dismissed),requests:r,existing:new Map(Object.entries(t.existing))},s=[],i=0,l=[...zu(e,t.contract).issues,...t.buttons?xd(e,t.buttons,t.buttonSpecs??void 0):[],...t.inputSpecs?rf(e,t.inputSpecs):[]];for(let a of l){let c=Vf(a,o);if(c==="existing"||c==="dismissed"){i++;continue}let u=r.get(a.key);s.push({...a,coverage:c,note:u?.note??null,reviewUrl:u?.url??null})}return{findings:s,known:i,present:l.map(a=>a.key)}}function Xn(e){let t=`${e.key}|${e.uses}|${e.places.map(r=>`${r.file}:${r.line}:${r.snippet}`).join(`
77
77
  `)}`,n=5381;for(let r=0;r<t.length;r++)n=(n*33^t.charCodeAt(r))>>>0;return n.toString(16).padStart(8,"0")}function xl(e,t){if(!t.length)return e;let n=new Set(t.map(r=>r.replace(/^\.\//,"")));return e.filter(r=>r.places.some(o=>n.has(o.file)))}var ks={start:1,end:Number.MAX_SAFE_INTEGER};function vl(e){let t=new Map,n=null;for(let r of e.split(`
78
78
  `)){if(r.startsWith("+++ ")){let a=r.slice(4).replace(/^"|"$/g,"");n=a==="/dev/null"?null:a.replace(/^b\//,"");continue}let o=/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/.exec(r);if(!o||n===null)continue;let s=Number(o[1]),i=o[2]===void 0?1:Number(o[2]),l=i?{start:s,end:s+i-1}:{start:Math.max(s,1),end:s+1};t.set(n,[...t.get(n)??[],l])}return t}function Jf(e,t){let n=[];for(let r of e)for(let o of t){let s=Math.max(r.start,o.start),i=Math.min(r.end,o.end);s<=i&&n.push({start:s,end:i})}return n}var c$=/^(?![\s}\])`]|\/\/|\/\*|\*|<\/|<!--|-->)\S/,u$=/^\s*(\/\/|\/\*|\*|<!--)/;function p$(e){let t=e.split(`
79
79
  `),n=[0];for(let r=1;r<t.length;r++){if(!c$.test(t[r]))continue;let o=r;for(;o>0&&t[o-1].trim()&&u$.test(t[o-1]);)o--;o>n[n.length-1]&&n.push(o)}return n.map((r,o)=>({start:r+1,end:o+1<n.length?n[o+1]:t.length}))}function Kf(e,t){return t.some(n=>n.start<=1&&n.end>=Number.MAX_SAFE_INTEGER)?[ks]:p$(e).filter(n=>t.some(r=>r.start<=n.end&&r.end>=n.start))}function Wr(e,t){let n=r=>(t.get(r.file)??[]).some(o=>r.line>=o.start&&r.line<=o.end);return e.flatMap(r=>{let o=r.places.filter(n);return o.length?[{...r,places:o}]:[]})}var xs=e=>e.filter(t=>Hf(t.coverage));function Vr(e){if(e.fix)return e.fix;let t=e.suggestion;return t?t.exists?`Use ${t.token} (${t.value}${t.alpha!=null?` at ${Math.round(t.alpha*100)}% opacity`:""}) instead of ${e.found}.${t.change?` Not the same look: ${t.change}`:""}`:e.kind==="undefined"?`${t.token} isn't in the contract. Use a contract token, or add it to the repository's tokens.`:`No contract token matches ${e.found}. Add it as a token (${t.token}), or ask for review.`:`Use a ${e.area} token from the contract instead of ${e.found}.`}var d$={open:"Not sent for review",pending:"Review requested",allowed:"Allowed in review",rejected:"Rejected \u2014 needs a fix",dismissed:"Ignored on the Drift page",existing:"Already on the default branch"};function f$(e){let t=Ii(e);return t==="implemented"?"used in code":t==="defined"?e.evidence?.definitions.some(n=>n.unknown)?"use not traced":"definition only":"in code"}function Xf(e,t,n){let r=`${t.files} files checked${t.skipped?`, ${t.skipped} skipped`:""}${t.truncated?", stopped at the size limit":""}`,o=t.known?` ${t.known} known issue${t.known===1?" is":"s are"} on ${t.project}'s Drift page.`:"",s=t.readNote?` ${t.readNote}`:"";if(!e.length)return`No new drift in code against ${t.project}'s contract (${r}).${o}${s}`;let i=new Map;for(let d of e){let f=d.places[0]?.file??"(no file)";i.set(f,[...i.get(f)??[],d])}let l=[""];for(let[d,f]of[...i].sort(([m],[g])=>m.localeCompare(g))){l.push(d);for(let m of f){let g=m.places.find(h=>h.file===d)??m.places[0];l.push(` ${g?`${g.line}: `:""}${m.title} [${zi(m)}, ${f$(m)}, ${d$[m.coverage]}]`),l.push(` fix: ${Vr(m)}`);let S=n?.get(m.key);S&&l.push(` ${S}`),m.places.length>1&&l.push(` also: ${m.places.slice(1,4).map(h=>`${h.file}:${h.line}`).join(", ")}${m.places.length>4?` +${m.places.length-4} more`:""}`),m.coverage==="rejected"&&(m.note&&l.push(` reviewer: ${m.note.replace(/\s+/g," ").trim()}`),l.push(" resolve: Change the code to use the contract. A rejected style isn't offered for review again.")),m.reviewUrl&&m.coverage!=="open"&&l.push(` review: ${m.reviewUrl}`)}l.push("")}let a=e.filter(d=>d.coverage==="open").length,c=e.filter(d=>d.coverage==="rejected").length,u=e.filter(d=>d.coverage==="pending").length,p=[a&&`${a} not sent for review`,c&&`${c} rejected`,u&&`${u} waiting for review`].filter(Boolean);return l.push(`${e.length} new drift item${e.length===1?"":"s"} in code against ${t.project}'s contract${p.length?`: ${p.join(", ")}`:""} (${r}).${o}`,"This check reads source files, not the live site."),t.readNote&&l.push(t.readNote),l.join(`
@@ -81,7 +81,7 @@ URL: `;function ms(e,t=gn){return typeof e=="string"&&e.length<=t&&VS.test(e)?e:
81
81
  3. I'm still iterating. Pause this question for ${r} ${r===1?"hour":"hours"}.`:"";return`This style isn't in your project's design contract.
82
82
 
83
83
  1. Use an existing token or button.
84
- ${n}${o}`}var Ss=(e,t)=>`If the Grum tools (grum_contract, grum_check, grum_request_review) aren't in your tool list, ${t?`first search for them by name (${t==="codex"?"Codex":"Claude Code"} loads MCP tools on demand, so they may not be listed yet); only if that search finds nothing, `:""}say so in the first sentence of your reply, before any other work, and tell the user to ${t==="codex"?$s:t==="claude"?Sl:Ye} Then carry on: read .grum/contract.json and follow it. To run Grum from a shell, run only this exact command: \`${e}\` (for example \`${e} check\`, \`${e} request <key> --reason "..." --url "<page>"\`). Do not search the computer for a grum program, and do not read the CLI's own files.`,Ye=`switch on "grum" in Cursor Settings > Tools & MCP (Cursor keeps a project's new tools off until then).`,$s="trust this folder when Codex asks. Grum will not appear in Settings > MCP servers or Settings > Hooks until you do. Then in Settings > MCP servers, turn on the server named grum. If Codex labels it with this folder's name, pick the one whose command runs grum.mjs.",Sl='trust this folder in Claude Code, approve the "grum" MCP server if it asks (/mcp shows whether it is connected), then start a new session.',Cs=e=>`${e==="codex"?"Codex":"Cursor"}'s sandbox can't open a browser, so this can't be captured from the shell. Call the grum_request_review tool instead; it runs outside the sandbox. If that tool isn't in your list, tell the user: ${e==="codex"?$s:Ye}`,Zn="Report this to the user and stop. Do not retry unless the user asks or the thing it names has changed, and do not poll or inspect browser tabs or processes.",Rs=e=>`Use the contract's tokens for color, typography, spacing and radius instead of literal values, and its buttons instead of a raw <button> or one-off classes: render the listed component with one of its looks, selected the way the contract says. Do not add a button look, variant or state, or change one. When the contract has no token or button for the job, read .grum/contract.json and ask the user once. If project.role is owner, choice 2 is "Add the token or button to the contract as a new design pattern": for a missing token, confirm the token name with them, then call grum_add_to_contract once (or run \`${e} add-token <name> --category <color|typography|spacing|radius|other> --value <v> --key <driftKey> --url "<page>"\`) and use that token in the code. ${Hr} The shell equivalent for a button is \`${e} add-button "<name>" --key <buttonDriftKey> --url "<page>"\`. Otherwise choice 2 is "Keep it and request review": call grum_request_review once with all drift keys that render on the same page, their reason and that page URL (or run \`${e} request <key> [<key>...] --reason "..." --url "<page>"\` once); Grum captures the page once for the reviewer. Offer choice 3 only when grum_check has returned current drift keys for the change: "I'm still iterating. Pause this question for N hours", where N is snoozeHours on the contract (24 if it is absent). If no drift exists yet, do not offer a timed snooze or call grum_snooze_review. If the user defers an unimplemented change, leave the code unchanged and stop; do not introduce drift just to obtain a key. If the user chooses choice 3 for existing drift, call grum_snooze_review with those drift keys (or run \`${e} snooze <key> [<key>...]\`). A snooze pauses the question for the files those keys touch on this branch. It does not schedule a reminder or notification; the question can return on a later check after it expires. It is not approval: grum check still lists the drift, and a snooze does not unblock a commit or push. Do not write the off-contract value until they answer, and if they already answered in this conversation, follow that choice. If a review request fails: ${Zn} A failed request is not approval and does not revoke their choice. A rejected style is not offered again: change the code to use the contract. Never edit .grum files to make a check pass.`,Yn=(e,t)=>[`The design contract for this repository (its color, typography, spacing and radius tokens, its buttons and its text inputs) lives in Grum. Before writing or restyling UI, including any button or input, call the ${t.tool} \`grum_contract\`.`,"Before you stop, call `grum_check` and resolve new drift with contract tokens or the user's chosen review path. Pending or allowed reviews cover drift; a failed review request must be reported and is not approval. `grum_check` reads source files: its drift is in code, and says whether code uses it, not that the live site shows it.",`To set up or repair Grum, call \`grum_doctor\` or run \`${e} doctor --agent ${t.host??"cursor"} --json\` and follow its JSON. Do not ask the user to open a terminal or type a command.`,Ss(e,t.host),...t.extra??[],Rs(e)];function $l(e,t){let n={};for(let r of e.contract){let o=xu[r.category];(n[o]??=[]).push({name:r.name,value:r.resolved,...r.darkValue?{dark:r.darkValue}:{}})}return{about:{product:"Grum design contract",what:"The design tokens this repository declares, as last reviewed in Grum. Styles should use them; drift from them is checked deterministically.",howToUse:["Reference tokens by name (var(--token), the Tailwind utility generated from it, or the SCSS variable) instead of literal colors, sizes and radii.",...e.buttons?["For a button, use a component from `buttons.components` (Button when there is one) and a look from `buttons.looks`; `selectedBy` says which prop value picks it. Follow `buttons.rules`; they are the repository's written rules, quoted."]:[],...e.buttons&&e.buttonSpecs?["`buttons.approved` are the buttons the team approved, with their states and sizes. Use one as it is: a button that overrides one, or matches none, is drift."]:[],...e.inputSpecs?.specs.length?["For a text input, render one of `inputs.approved` the way its `use` says, as it is: don't restyle it with className or style, add or remove a state (focus, invalid, disabled), or write a raw <input> with its look. An input that overrides one, or matches none, is drift."]:[],`Run grum_check (or \`${t} check\`) before you finish. Drift already on the default branch is not yours to fix unless asked.`,Rs(t)]},project:e.project,scannedAt:e.scan.finishedAt,snoozeHours:e.snoozeHours??24,tokens:n,...e.buttons?{buttons:(r=>({components:r.components,looks:r.looks,rules:r.rules,...e.buttonSpecs?{approved:e.buttonSpecs.specs.map(o=>({name:o.name,role:o.role,...o.select?{select:o.select}:{},sizes:o.sizes.map(s=>({label:s.label,...s.select?{select:s.select}:{},values:Object.fromEntries(s.declarations.map(i=>[i.property,i.value]))})),states:Object.fromEntries(o.states.map(s=>[s.context?`${s.state} @ ${s.context}`:s.state,Object.fromEntries(s.declarations.map(i=>[i.property,i.value]))]))}))}:{}}))(np(e.buttons).spec)}:{},...e.inputSpecs?.specs.length?{inputs:{approved:e.inputSpecs.specs.map(sf)}}:{},reviews:e.requests.map(r=>({key:r.driftKey,status:r.status,note:r.note}))}}var g$=new Set(["apply_patch","Edit","Write","MultiEdit"]);function Yf(e){let t=e.tool_input;return typeof t=="string"?{command:t}:t&&typeof t=="object"?t:{}}var Cl=e=>typeof e.session_id=="string"?e.session_id:"",Qf=(e,t)=>({conversation_id:Cl(e),loop_count:e.stop_hook_active===!0?1:0,branch:t});function m$(e){let t=[],n=null,r=null,o=()=>{n?.edits&&r&&(r.from.length||r.to.length)&&n.edits.push({old_string:r.from.join(`
84
+ ${n}${o}`}var Ss=(e,t)=>`If the Grum tools (grum_contract, grum_check, grum_request_review) aren't in your tool list, ${t?`first search for them by name (${t==="codex"?"Codex":"Claude Code"} loads MCP tools on demand, so they may not be listed yet); only if that search finds nothing, `:""}say so in the first sentence of your reply, before any other work, and tell the user to ${t==="codex"?$s:t==="claude"?Sl:Ye} Then carry on: read .grum/contract.json and follow it. To run Grum from a shell, run only this exact command: \`${e}\` (for example \`${e} check\`, \`${e} request <key> --reason "..." --url "<page>"\`). Do not search the computer for a grum program, and do not read the CLI's own files.`,Ye=`switch on "grum" in Cursor Settings > Tools & MCP (Cursor keeps a project's new tools off until then).`,$s="trust this folder when Codex asks. Grum will not appear in Settings > MCP servers or Settings > Hooks until you do. Then in Settings > MCP servers, turn on the server named grum. If Codex labels it with this folder's name, pick the one whose command runs grum.mjs.",Sl='trust this folder in Claude Code, approve the "grum" MCP server if it asks (/mcp shows whether it is connected), then start a new session.',Cs=e=>`${e==="codex"?"Codex":"Cursor"}'s sandbox can't open a browser, so this can't be captured from the shell. Call the grum_request_review tool instead; it runs outside the sandbox. If that tool isn't in your list, tell the user: ${e==="codex"?$s:Ye}`,Zn="Report this to the user and stop. Do not retry unless the user asks or the thing it names has changed, and do not poll or inspect browser tabs or processes.",Rs=e=>`Use the contract's tokens for color, typography, spacing and radius instead of literal values, and its buttons instead of a raw <button> or one-off classes: render the listed component with one of its looks, selected the way the contract says. Do not add a button look, variant or state, or change one. When the contract has no token or button for the job, read .grum/contract.json and ask the user once. If project.role is owner, choice 2 is "Add the token or button to the contract as a new design pattern": for a missing token, confirm the token name with them, then call grum_add_to_contract once (or run \`${e} add-token <name> --category <color|typography|spacing|radius|other> --value <v> --key <driftKey> --url "<page>"\`) and use that token in the code. ${Hr} The shell equivalent for a button is \`${e} add-button "<name>" --key <buttonDriftKey> --url "<page>"\`. Otherwise choice 2 is "Keep it and request review": call grum_request_review once with all drift keys that render on the same page, their reason and that page URL (or run \`${e} request <key> [<key>...] --reason "..." --url "<page>"\` once); Grum captures the page once for the reviewer. Offer choice 3 only when grum_check has returned current drift keys for the change: "I'm still iterating. Pause this question for N hours", where N is snoozeHours on the contract (24 if it is absent). If no drift exists yet, do not offer a timed snooze or call grum_snooze_review. If the user defers an unimplemented change, leave the code unchanged and stop; do not introduce drift just to obtain a key. If the user chooses choice 3 for existing drift, call grum_snooze_review with those drift keys (or run \`${e} snooze <key> [<key>...]\`). A snooze pauses the question for the files those keys touch on this branch. It does not schedule a reminder or notification; the question can return on a later check after it expires. It is not approval: grum check still lists the drift, and a snooze does not unblock a commit or push. Do not write the off-contract value until they answer, and if they already answered in this conversation, follow that choice. If a review request fails: ${Zn} A failed request is not approval and does not revoke their choice. A rejected style is not offered again: change the code to use the contract. Never edit .grum files to make a check pass.`,Yn=(e,t)=>[`The design contract for this repository (its color, typography, spacing and radius tokens, its buttons and its text inputs) lives in Grum. Before writing or restyling UI, including any button or input, call the ${t.tool} \`grum_contract\`.`,"Before you stop, call `grum_check` and resolve new drift with contract tokens or the user's chosen review path. Pending or allowed reviews cover drift; a failed review request must be reported and is not approval. `grum_check` reads source files: its drift is in code, and says whether code uses it, not that the live site shows it.",`To set up or repair Grum, call \`grum_doctor\` or run \`${e} doctor --agent ${t.host??"cursor"} --json\` and follow its JSON. Do not ask the user to open a terminal or type a command.`,Ss(e,t.host),...t.extra??[],Rs(e)];function $l(e,t){let n={};for(let r of e.contract){let o=xu[r.category];(n[o]??=[]).push({name:r.name,value:r.resolved,...r.darkValue?{dark:r.darkValue}:{}})}return{about:{product:"Grum design contract",what:"The design tokens this repository declares, as last reviewed in Grum. Styles should use them; drift from them is checked deterministically.",howToUse:["Reference tokens by name (var(--token), the Tailwind utility generated from it, or the SCSS variable) instead of literal colors, sizes and radii.",...e.buttons?["For a button, use a component from `buttons.components` (Button when there is one) and a look from `buttons.looks`; `selectedBy` says which prop value picks it. Follow `buttons.rules`; they are the repository's written rules, quoted. A button in `buttons.approved` may also carry `rules`: the team's own usage rules for it. Follow those too."]:[],...e.buttons&&e.buttonSpecs?["`buttons.approved` are the buttons the team approved, with their states and sizes. Use one as it is: a button that overrides one, or matches none, is drift."]:[],...e.inputSpecs?.specs.length?["For a text input, render one of `inputs.approved` the way its `use` says, as it is: don't restyle it with className or style, add or remove a state (focus, invalid, disabled), or write a raw <input> with its look. An input that overrides one, or matches none, is drift."]:[],`Run grum_check (or \`${t} check\`) before you finish. Drift already on the default branch is not yours to fix unless asked.`,Rs(t)]},project:e.project,scannedAt:e.scan.finishedAt,snoozeHours:e.snoozeHours??24,tokens:n,...e.buttons?{buttons:(r=>({components:r.components,looks:r.looks,rules:r.rules,...e.buttonSpecs?{approved:e.buttonSpecs.specs.map(o=>({name:o.name,role:o.role,...o.select?{select:o.select}:{},...e.buttonSpecs?.rules?.[o.id]?.length?{rules:e.buttonSpecs.rules[o.id]}:{},sizes:o.sizes.map(s=>({label:s.label,...s.select?{select:s.select}:{},values:Object.fromEntries(s.declarations.map(i=>[i.property,i.value]))})),states:Object.fromEntries(o.states.map(s=>[s.context?`${s.state} @ ${s.context}`:s.state,Object.fromEntries(s.declarations.map(i=>[i.property,i.value]))]))}))}:{}}))(np(e.buttons).spec)}:{},...e.inputSpecs?.specs.length?{inputs:{approved:e.inputSpecs.specs.map(sf)}}:{},reviews:e.requests.map(r=>({key:r.driftKey,status:r.status,note:r.note}))}}var g$=new Set(["apply_patch","Edit","Write","MultiEdit"]);function Yf(e){let t=e.tool_input;return typeof t=="string"?{command:t}:t&&typeof t=="object"?t:{}}var Cl=e=>typeof e.session_id=="string"?e.session_id:"",Qf=(e,t)=>({conversation_id:Cl(e),loop_count:e.stop_hook_active===!0?1:0,branch:t});function m$(e){let t=[],n=null,r=null,o=()=>{n?.edits&&r&&(r.from.length||r.to.length)&&n.edits.push({old_string:r.from.join(`
85
85
  `),new_string:r.to.join(`
86
86
  `)}),r=null};for(let s of e.split(/\r?\n/)){let i=s.match(/^\*\*\*\s+(Add File|Update File|Delete File|Move to):\s*(.+?)\s*$/);if(i){o();let[,a,c]=i;if(a==="Move to"&&n){n.file=c,delete n.edits;continue}n=a==="Delete File"?null:{file:c,...a==="Update File"?{edits:[]}:{}},n&&t.push(n);continue}if(!n?.edits)continue;if(s.startsWith("*** ")){o();continue}if(s.startsWith("@@")){o();continue}r??={from:[],to:[]};let l=s.slice(1);s.startsWith("+")?r.to.push(l):s.startsWith("-")?r.from.push(l):s.startsWith(" ")&&(r.from.push(l),r.to.push(l))}return o(),t}function eg(e){if(typeof e.tool_name!="string"||!g$.has(e.tool_name))return[];let t=Yf(e),n=[];for(let o of["command","patch","input"]){let s=t[o];typeof s=="string"&&n.push(...m$(s))}for(let o of["file_path","path"]){let s=t[o];if(typeof s!="string"||!s)continue;let{old_string:i,new_string:l,edits:a}=t;n.push({file:s,...typeof i=="string"&&typeof l=="string"?{edits:[{old_string:i,new_string:l}]}:Array.isArray(a)?{edits:a}:{}})}let r=new Set;return n.filter(o=>!r.has(o.file)&&!!r.add(o.file))}function tg(e){if(e.tool_name!=="Bash")return"";let t=Yf(e).command;return typeof t=="string"?t:""}var ng=e=>e.followup_message?{decision:"block",reason:e.followup_message}:{};function rg(e){return e.permission!=="deny"?{}:{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:[e.user_message,e.agent_message].filter(Boolean).join(`
87
87
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@grum-os/cli",
3
- "version": "0.3.32",
4
- "contentHash": "94659f41027e21a43b2e3a771d627544",
3
+ "version": "0.3.33",
4
+ "contentHash": "5f97e2b69ce4ff4cd7da9393c7579505",
5
5
  "description": "Keep a repository on the design contract reviewed in Grum: a local drift check, review requests, Cursor hooks and an MCP server.",
6
6
  "homepage": "https://www.grum.so",
7
7
  "keywords": [