@grum-os/cli 0.3.39 → 0.3.40

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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Keep a repository on the design contract your team reviewed in [Grum](https://app.grum.so).
6
6
 
7
- Grum reads the tokens the repository declares (CSS custom properties, a Tailwind v4 `@theme`, SCSS variables) and the button looks it defines. The team can add tokens in Grum before the code has them, and move others outside the contract. That reviewed set is what `grum` checks against. It compares your working tree to the contract, lists only drift that is new on this branch, and can send a new item to the project's owners for review, with a picture of how it renders.
7
+ Grum reads the tokens the repository declares (CSS custom properties, a Tailwind v4 `@theme`, SCSS variables), and the button and text input designs it defines. The team can add tokens in Grum before the code has them, and move others outside the contract. That reviewed set is what `grum` checks against. It compares your working tree to the contract, lists only drift that is new on this branch, and can send a new item to the project's owners for review, with a picture of how it renders.
8
8
 
9
9
  Requires Node.js 22 or newer.
10
10
 
@@ -60,7 +60,7 @@ The menu and the commands above stay as they are. `--agent` is an extra way in,
60
60
 
61
61
  Accepting Cursor in the menu puts Grum inside the agent. The agent follows the same contract a person sees in Grum, and it can ask the owners to allow something the contract does not have.
62
62
 
63
- **Before it writes UI**, it reads the contract: color, typography, spacing, and radius tokens, plus button components, each look, and the prop that selects it.
63
+ **Before it writes UI**, it reads the contract: color, typography, spacing, and radius tokens, plus button components and looks, approved text inputs, and the rules for using them.
64
64
 
65
65
  **Before it finishes**, it checks what it changed. Drift already on the default branch stays off the list. New drift comes back with a fix.
66
66
 
@@ -79,7 +79,7 @@ These are the files setup writes. They merge into what is already there, and a s
79
79
 
80
80
  | File | What it does |
81
81
  | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
- | `.cursor/mcp.json` | Tools the agent calls: `grum_contract`, `grum_check`, `grum_request_review`, `grum_review_status`. |
82
+ | `.cursor/mcp.json` | Tools the agent calls: `grum_contract`, `grum_check`, `grum_request_review`, `grum_add_to_contract`, `grum_snooze_review`, `grum_review_status`, `grum_setup`, and `grum_doctor`. |
83
83
  | `.cursor/rules/grum.mdc` | Tells the agent to read the contract before writing UI and to check before it stops. The contract itself stays in Grum, so this file does not go stale. |
84
84
  | `.cursor/hooks.json` | After an edit, checks that block. The stop hook sends the agent back on blocking drift. The shell hook can refuse `git commit` and `git push`. |
85
85
  | pre-commit | Optional. When you say yes, adds `grum check --staged`. Husky's hook is committed with the repo. Git's own pre-commit hook stays on this computer. A lefthook repo gets the snippet to paste. |
@@ -101,7 +101,7 @@ Without step 2 the tools still work and the agent still follows the guidance, bu
101
101
 
102
102
  | File | What it does |
103
103
  | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
- | `.codex/config.toml` | Adds the same four tools. Only Grum's own block is written; the rest of the file is left as it is, and a file that is not valid TOML stops setup. |
104
+ | `.codex/config.toml` | Adds the same eight tools. Only Grum's own block is written; the rest of the file is left as it is, and a file that is not valid TOML stops setup. |
105
105
  | `.codex/hooks.json` | Three hooks beside any of yours: after an edit, before a shell command, and at stop. They behave as Cursor's do, including [blocking](#fail-the-check). |
106
106
  | `AGENTS.md` | A marked block that tells the agent to read the contract, check before it stops, and ask before keeping an off-contract style. |
107
107
 
@@ -115,7 +115,7 @@ Claude Code runs a project's tools and hooks only in a folder you trust, so one
115
115
 
116
116
  | File | What it does |
117
117
  | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
118
- | `.mcp.json` | Adds the same four tools. Other servers in the file are left as they are. |
118
+ | `.mcp.json` | Adds the same eight tools. Other servers in the file are left as they are. |
119
119
  | `.claude/settings.json` | Three hooks beside any of yours: after an edit, before a shell command, and at stop. They behave as Cursor's do, including [blocking](#fail-the-check). Also approves the **grum** server. |
120
120
  | `CLAUDE.md` | A marked block that tells the agent to read the contract, check before it stops, and ask before keeping an off-contract style. |
121
121
 
@@ -156,6 +156,7 @@ A check prints the report and exits 0, including when you are signed out, offlin
156
156
  | Spacing | A margin, padding, or gap off the scale |
157
157
  | Radius | A radius off the scale |
158
158
  | Buttons | A look the contract does not have, a look whose state changed, a missing state, a raw `<button>` outside the button component, or a variant the component lacks |
159
+ | Inputs | A text input that does not match an approved input, or differs from one in structure, appearance, or states |
159
160
 
160
161
  ## Review one item yourself
161
162
 
@@ -183,7 +184,7 @@ Clears the capture-browser sign-in saved for this repository.
183
184
  grum contract
184
185
  ```
185
186
 
186
- Prints the packet the Cursor agent reads before writing styles: tokens by category (with a separate dark value when there is one), button components, each look and the prop that selects it, and the repository's written rules about those buttons.
187
+ Prints the contract packet agents read before writing UI: tokens by category (with a separate dark value when there is one), button components and looks, approved text inputs, team guidelines, and review decisions.
187
188
 
188
189
  ## Fail the check
189
190
 
@@ -241,19 +242,23 @@ Store it as a secret. Do not commit it. The token acts as you, in every workspac
241
242
 
242
243
  The menu covers check, setup, and sign-in. These are the same actions as flags, for scripts and CI.
243
244
 
244
- | Command | What it does |
245
- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
246
- | `grum` | The menu. Check, setup (including Cursor), or sign in. |
247
- | `grum login` / `grum logout` | Sign this computer in to Grum, or out. |
248
- | `grum setup [--root <dir>]` | Sign in, link this repository, and ask about Cursor and the pre-commit check. |
249
- | `grum check [flags]` | Check the working tree. See the flags below. |
250
- | `grum request <key> [<key>...] [--reason "..."] [--url <page>] [--live] [--browser chrome | safari] [--no-capture]` | Ask the owners to allow drift items; items on one page capture together. |
251
- | `grum uninstall [--yes]` | Remove Grum from this repository and this computer. See [Uninstall](#uninstall). |
252
- | `grum capture reset` | Clear this repository's saved capture-browser sign-ins. |
253
- | `grum contract` | Print the contract packet. |
254
- | `grum init cursor [--remove] [--force] [--local-only] [--pre-commit]` | Write or remove the Cursor files without the menu. `--pre-commit` adds the commit check. `--no-pre-commit` removes it. |
255
- | `grum init codex [--remove] [--force] [--local-only] [--pre-commit]` | The same for Codex: `.codex/config.toml`, `.codex/hooks.json` and the `AGENTS.md` block. |
256
- | `grum init claude [--remove] [--force] [--local-only] [--pre-commit]` | The same for Claude Code: `.mcp.json`, `.claude/settings.json` and the `CLAUDE.md` block. |
245
+ | Command | What it does |
246
+ | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
247
+ | `grum` | The menu. Check, setup (including Cursor), or sign in. |
248
+ | `grum login` / `grum logout` | Sign this computer in to Grum, or out. |
249
+ | `grum setup [--root <dir>]` | Sign in, link this repository, and ask about Cursor and the pre-commit check. Use `--agent <agent> --json` for agent setup. |
250
+ | `grum doctor [--agent <agent> --json]` | Check setup readiness without changing anything. |
251
+ | `grum scan [--root <dir>]` | Start a fresh scan for the linked workspace and follow its progress. |
252
+ | `grum check [flags]` | Check the working tree. See the flags below. |
253
+ | `grum request <key> [<key>...] [--reason "..."] [--url <page>] [--live] [--browser chrome | safari] [--no-capture]` | Ask the owners to allow drift items; items on one page capture together. |
254
+ | `grum add-token ...` / `grum add-button ...` | For owners, add a token or approved button design to the contract. |
255
+ | `grum snooze <key> [<key>...]` | Pause questions about existing drift on this branch for the configured period. |
256
+ | `grum uninstall [--yes]` | Remove Grum from this repository and this computer. See [Uninstall](#uninstall). |
257
+ | `grum capture reset` | Clear this repository's saved capture-browser sign-ins. |
258
+ | `grum contract` | Print the contract packet. |
259
+ | `grum init cursor [--remove] [--force] [--local-only] [--pre-commit]` | Write or remove the Cursor files without the menu. `--pre-commit` adds the commit check. `--no-pre-commit` removes it. |
260
+ | `grum init codex [--remove] [--force] [--local-only] [--pre-commit]` | The same for Codex: `.codex/config.toml`, `.codex/hooks.json` and the `AGENTS.md` block. |
261
+ | `grum init claude [--remove] [--force] [--local-only] [--pre-commit]` | The same for Claude Code: `.mcp.json`, `.claude/settings.json` and the `CLAUDE.md` block. |
257
262
 
258
263
  ```sh
259
264
  grum check --staged # drift in the blocks staged for commit
@@ -272,6 +277,8 @@ grum check --strict # like --ci, and also fail while a review is
272
277
 
273
278
  `grum hook` and `grum mcp` are the entry points Cursor, Codex and Claude Code run. You do not call them yourself.
274
279
 
280
+ For `setup` and `doctor`, `<agent>` is `cursor`, `codex`, `claude` or `generic`.
281
+
275
282
  ## Uninstall
276
283
 
277
284
  ```sh
package/content.md5 CHANGED
@@ -1 +1 @@
1
- f02d8c8020ae408b614eacde9aab16bb
1
+ 62a2e64d1373e651dc499fce541a5d89
package/grum.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- globalThis.__GRUM_VERSION__="0.3.39";
2
+ globalThis.__GRUM_VERSION__="0.3.40";
3
3
  import{chmodSync as u1,existsSync as Tt,readdirSync as pb,rmSync as $o,statSync as p1}from"node:fs";import{homedir as bb}from"node:os";import{fileURLToPath as d1}from"node:url";import te from"node:path";import{createInterface as f1,emitKeypressEvents as g1}from"node:readline";var Eo=(e,t,n)=>Math.min(n,Math.max(t,e)),Ro=e=>Eo(Math.round(e),0,255).toString(16).padStart(2,"0").toUpperCase();function To({r:e,g:t,b:n,a:r}){let o=`#${Ro(e)}${Ro(t)}${Ro(n)}`,s=Math.round(Eo(r,0,1)*255);return s>=255?o:`${o}${Ro(s)}`}function Ib(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 Io(e,t){let n=e.trim();return n.endsWith("%")?parseFloat(n)/100*t:parseFloat(n)}function Eb(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 Ao(e,t){return e==="none"?0:Io(e,t)}function yu(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=Eo(u,0,1);return(p<=.0031308?12.92*p:1.055*p**(1/2.4)-.055)*255});return[l,a,c]}function bt(e){let t=e.trim().toLowerCase();if(t.startsWith("#"))return Ib(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?Ao(r[3],1):1;if(n[1].startsWith("ok")){let p=Eo(Ao(r[0],1),0,1),d=Ao(r[1],.4);if(n[1]==="oklab"){let b=Ao(r[2],.4);if([p,d,b,o].some(Number.isNaN))return null;let[w,y,x]=yu(p,d,b);return{r:w,g:y,b:x,a:o}}let f=r[2];if(f!=="none"&&!/^-?[\d.]+(deg)?$/.test(f))return null;let g=(f==="none"?0:parseFloat(f))*Math.PI/180;if([p,d,g,o].some(Number.isNaN))return null;let[m,S,h]=yu(p,d*Math.cos(g),d*Math.sin(g));return{r:m,g:S,b:h,a:o}}if(n[1].startsWith("rgb")){let[p,d,f]=r.slice(0,3).map(g=>Io(g,255));return[p,d,f,o].some(Number.isNaN)?null:{r:p,g:d,b:f,a:o}}let s=parseFloat(r[0]),i=Io(r[1],1),l=Io(r[2],1);if([s,i,l,o].some(Number.isNaN))return null;let[a,c,u]=Eb((s%360+360)%360,i,l);return{r:a,g:c,b:u,a:o}}function pe(e){let t=bt(e);return t?To(t):null}function bu({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 Ti(e){return`--color-${e.replace(/^#/,"").slice(0,6).toLowerCase()}`}function _o(e,t){let n=bt(e),r=bt(t);if(!n||!r)return 1/0;let[o,s,i]=bu(n),[l,a,c]=bu(r);return Math.hypot(o-l,s-a,i-c)}var br=/#(?:[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 wu(e){let t=e.lastIndexOf("-");return t<=0?null:_i[e.slice(0,t)]?.[e.slice(t+1)]??null}function Ln(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 Tb(e,t){if(!e)return t;let n=Ln(e);return Ln(t).flatMap(r=>n.map(o=>r.includes("&")?r.replaceAll("&",o):`${o} ${r}`)).join(", ")}var _b=/^@(?:-\w+-)?(?:keyframes|font-face|property)\b/i;function Oo(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":Tb(d?.resolved??null,a),isAt:p,skip:(c?.skip??!1)||p&&_b.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 Mo(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 ku=e=>e.replace(/\\(.)/g,"$1");function Ob(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 Oi=/\*|[a-z][\w-]*|\.((?:\\.|[\w-])+)|#((?:\\.|[\w-])+)|\[\s*([\w-]+)\s*(?:([~|^$*]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\]\s]*)))?\s*[is]?\s*\]|::([\w-]+)|:([\w-]+)(\((?:[^()]|\([^()]*\))*\))?/giy;function rt(e){let t=[];Oi.lastIndex=0;let n=0;for(;n<e.length;){Oi.lastIndex=n;let r=Oi.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:ku(r[1])});else if(r[2]!==void 0)t.push({kind:"id",raw:o,name:ku(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 de(e){let t=Ob(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=rt(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 Dn(e){return e.map(t=>t.raw).join("")}function wr(e){return e.map((t,n)=>{let r=Dn(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 Su=e=>e.replace(/([a-z\d])([A-Z])/g,"$1-$2").split(/[^a-z0-9]+/i).filter(Boolean).map(t=>t.toLowerCase());function xu(e,t){let n=e.toLowerCase();return t.some(r=>n.includes(r.toLowerCase()))}function Mi(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"&&Su(o.name).some(s=>r.has(s)))}var Mb=new Set(["html","body"]),jb=new Set(["svg","path","use","img","picture","video","i","em","strong","b","small","kbd","sup","sub"]);function Po(e,t){for(let n of e)switch(n.kind){case"tag":if(t.elements.includes(n.name))return!0;break;case"class":if(xu(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&&xu(n.value,t.classKeywords))return!0;break;case"pseudo":if((n.name==="is"||n.name==="where")&&n.args){let r=oe(n.args).map(de);if(r.length>0&&r.every(o=>o!==null&&Po(o[o.length-1].simples,t)))return!0}break;default:break}return!1}function $u(e){return e.some(n=>n.kind==="universal")?e.length===1:e.some(n=>n.kind==="tag"&&Mb.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 Cu(e){return e.some(t=>t.kind==="tag"&&jb.has(t.name))}var jo=["hover","focus","active","selected","current","loading","disabled"],Pb={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"},Lb={hover:"hover",focus:"focus","focus-visible":"focus","focus-within":"focus",active:"active",checked:"selected",disabled:"disabled"},Db={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"},Nb=new Set(["active","selected","pressed","checked","loading","busy","disabled","open","current","hover","focus","show","expanded","collapsed"]),vu=e=>Pb[e.toLowerCase()]??e.toLowerCase(),Ot={hover:"__gs-hover",focus:"__gs-focus",active:"__gs-active"};function Ru(e){if(e.kind==="pseudo"){let t=Lb[e.name];if(!t)return null;let n=Ot[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:vu(e.value),marker:{classes:[],attrs:{"data-state":e.value}}};let t=Db[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]:Nb.has(e.name.toLowerCase())?e.name:null;return n?{state:vu(n),marker:{classes:[e.name],attrs:{}}}:null}return null}function Fb(e){return e.kind!=="pseudo"||e.name!=="not"||!e.args?!1:oe(e.args).every(t=>{let n=rt(t);return n!==null&&n.every(r=>Ru(r)!==null)})}function wt(e){let t=[],n=new Map;for(let s of e){if(Fb(s))continue;let i=Ru(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)=>De(s)-De(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 De(e){if(e==="default")return-1;let t=jo.indexOf(e);return t===-1?jo.length:t}function ot(e){return e.length?e.join(" + "):"default"}function ji(e){return e.replace(/:hover\b/g,`.${Ot.hover}`).replace(/:focus(?:-visible|-within)?\b/g,`.${Ot.focus}`).replace(/:active\b/g,`.${Ot.active}`)}var Ub=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"]),Gb=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"]),zb=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 kr(e){let t=e.toLowerCase();return Ub.has(t)?"paint":Gb.has(t)?"layout":zb.has(t)?"hint":"box"}var Bb={shadow:/^box-shadow$/,cursor:/^cursor$/,rounded:/^border(?:-[a-z]+)*-radius$/,radius:/^border(?:-[a-z]+)*-radius$/};function Au(e,t){let n=e.filter(s=>s.kind==="class");if(n.length!==1||e.length!==1)return!1;let r=Su(n[0].name)[0],o=r?Bb[r]:void 0;return o?t.length>0&&t.every(s=>o.test(s.property.toLowerCase())):!1}function xr(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 Wb(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 Hb=/(?<![\w#-])-?\d*\.?\d+(?:px|rem|em|vw|vh|vmin|vmax|ch|ex|%)/gi;function Lo(e){let t=Wb(e);return new RegExp(br.source,"i").test(t)?!0:[...t.matchAll(Hb)].some(n=>parseFloat(n[0])!==0)}function Iu(e,t){let n=pe(e),r=pe(t);return n||r?n===r:e.trim().toLowerCase()===t.trim().toLowerCase()}function Vb(e){let t=de(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 qb=(e,t)=>[...t].some(n=>e.startsWith(`${n}--`));function Jb(e,t,n){let r="no";for(let o of oe(e.selector)){let s=de(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)||qb(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 Eu(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 Tu(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 xr(a))s(p,l(d.name,d.fallback,c,u));return Lo(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=[],g=[];for(let h of r.get(a)??[]){let b=Jb(h,u,t);b==="proven"?f.push(h):b==="possible"&&g.push(h)}let m=n.get(a),S=o();f.length||(m!==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,...g]){if(m!==void 0&&Iu(h.value,m)){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&&(Lo(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=Vb(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 _u={color:"Color",typography:"Typography",spacing:"Spacing",radius:"Radius",shadow:"Shadows",other:"Other"},Kb=/\.(css|scss|sass|less|pcss)$/i,Xb=/^(?::root|html|body|:host)?(?:\[data-[\w-]+(?:[~|^$*]?=[^\]]*)?\]|\.(?:(?:light|dark)(?:-mode)?|theme-[\w-]+))*$/i;function Ou(e){let t=e.split(",").map(n=>n.trim());return t.length>0&&t.every(n=>n!==""&&Xb.test(n))}function tn(e){return Kb.test(e)}function Zb(...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 Mt(e){let t=n=>(n??"").replace(/\s+/g," ").trim();return Zb(e.file,t(e.selector),t(e.context),e.property.toLowerCase(),t(e.value).toLowerCase())}function Pi(e){let t=e.evidence;return t?t.observed.length?"observed":t.definitions.some(n=>n.useCount>0)?"implemented":"defined":"unverified"}var Yb=200,Lu=/\.html?$/i,Qb=/\.(m?[jt]sx?|vue|svelte|astro)$/i,ew=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]),tw=new Set(["script","style","textarea","title"]),nw=new Set(["li","p","option","tr","td","th","dt","dd"]),Do=/\{\{|\$\{|<%|\{%/;function rw(e){return`/${e}`.replace(/^\/(?:public|static|dist|site|docs)\//,"/").replace(/(^|\/)index\.html?$/i,"$1")}function ow(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]===`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@grum-os/cli",
3
- "version": "0.3.39",
4
- "contentHash": "f02d8c8020ae408b614eacde9aab16bb",
3
+ "version": "0.3.40",
4
+ "contentHash": "62a2e64d1373e651dc499fce541a5d89",
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": [