austere-axi 0.5.1 → 0.5.2

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
@@ -151,8 +151,10 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
151
151
 
152
152
  ## CLI
153
153
 
154
+ Usage errors (unknown command or flag, missing argument) exit 2 before anything runs; failures exit 1. `<command> --help` prints that command's flags and an example.
155
+
154
156
  ```
155
- austere-axi sessions + usage
157
+ austere-axi sessions + command list
156
158
  austere-axi <file> validate + open a review [--no-open] [--reopen]
157
159
  austere-axi poll <file> long-poll for feedback [--cursor n] [--agent-reply "..."] [--after n] [--takeover]
158
160
  austere-axi reply <file> post a reply without polling [--cursor n]
package/dist/austere.js CHANGED
@@ -1607,11 +1607,11 @@ dataLane = Group("DATA STORES WITH A HEADING LONGER THAN THE COLUMN IS WIDE", [c
1607
1607
  <button type="button" data-act="queue" class="primary">Queue</button>
1608
1608
  </span>
1609
1609
  </div>
1610
- </div>`,document.body.appendChild(r);let n=r.querySelector(".austere-hover"),o=r.querySelector(".austere-hover-label"),i=r.querySelector(".austere-selbtn"),l=r.querySelector(".austere-pop"),s=r.querySelector(".austere-pop-path"),u=r.querySelector(".austere-pop-quote"),d=r.querySelector("textarea"),p=null,f=null,m=0,x=null,v=Y=>!!Y?.closest?.(`[${S9}]`),w=Y=>Y?.closest?.("[data-oui-id]")||null;function b(Y,de,se=4){Y.style.left=`${de.left-se}px`,Y.style.top=`${de.top-se}px`,Y.style.width=`${de.width+se*2}px`,Y.style.height=`${de.height+se*2}px`}function k(Y){if(!Y){n.hidden=!0;return}b(n,SX(Y)),o.textContent=`${Y.getAttribute("data-oui-id")} \xB7 ${Y.getAttribute("data-oui-c")}`,n.hidden=!1}function I(Y){a!=="annotate"||!l.hidden||(x=Y.target,!m&&(m=requestAnimationFrame(()=>{m=0,!v(x)&&k(w(x))})))}function L(){let Y=window.getSelection();return!!Y&&!Y.isCollapsed&&Y.toString().trim().length>0}function M(Y){if(a!=="annotate"||v(Y.target))return;if(!l.hidden){Y.preventDefault(),Y.stopPropagation();return}if(L()||!Y.altKey&&Y.target.closest(t$a))return;let de=w(Y.target);de&&(Y.preventDefault(),Y.stopPropagation(),T({target:de,el:Y.target,point:{x:Y.clientX,y:Y.clientY}}))}function _(){if(a!=="annotate"||!l.hidden)return;let Y=window.getSelection();if(!Y||Y.isCollapsed||!Y.rangeCount){i.hidden=!0,f=null;return}let de=Y.toString().trim(),se=Y.getRangeAt(0),H=se.commonAncestorContainer;H.nodeType!==1&&(H=H.parentElement);let ee=w(H);if(!de||!ee||v(H)){i.hidden=!0,f=null;return}let oe=se.getBoundingClientRect();f={target:ee,el:H,selection:vv(de,600),point:{x:oe.left,y:oe.bottom}},i.hidden=!1;let he=i.offsetWidth||140;i.style.left=`${Math.max(8,Math.min(window.innerWidth-he-8,oe.left+oe.width/2-he/2))}px`,i.style.top=`${Math.max(8,Math.min(window.innerHeight-40,oe.bottom+8))}px`}let A=0;function D(){clearTimeout(A),A=setTimeout(_,120)}i.addEventListener("mousedown",Y=>Y.preventDefault()),i.addEventListener("click",Y=>{Y.preventDefault(),Y.stopPropagation(),f&&T(f),i.hidden=!0});function E(){s.textContent="",p.path.forEach((Y,de)=>{let se=document.createElement("button");if(se.type="button",se.className="austere-chip"+(Y.id===p.targetId?" active":""),se.textContent=Y.id,se.title=Y.component,se.addEventListener("click",()=>{p.targetId=Y.id,p.component=Y.component,E(),d.focus()}),s.appendChild(se),de<p.path.length-1){let H=document.createElement("span");H.className="austere-chip-sep",H.textContent="\u203A",s.appendChild(H)}})}function T({target:Y,el:de,selection:se,point:H}){let ee=a$a(Y),oe=r$a(Y,de),he=IEt(Y,de),_e;se||(_e=vv((he||de).innerText||(he||de).textContent,200)||void 0,he&&_e===oe?.part&&(_e=void 0)),p={targetId:Y.getAttribute("data-oui-id"),component:Y.getAttribute("data-oui-c"),path:ee,selection:se||void 0,excerpt:_e,detail:oe},E();let Me=se||_e||oe?.part||"";u.textContent=vv(Me,220),u.hidden=!Me,d.value="",n.hidden=!0,i.hidden=!0,k(Y),n.classList.add("locked"),l.hidden=!1,O(H),d.focus()}function O(Y){if(window.innerWidth<640){l.classList.add("sheet"),l.style.left=l.style.top="";return}l.classList.remove("sheet");let de=l.offsetWidth||360,se=l.offsetHeight||200,H=Y.x+12,ee=Y.y+12;H+de>window.innerWidth-8&&(H=Math.max(8,Y.x-de-12)),ee+se>window.innerHeight-8&&(ee=Math.max(8,Y.y-se-12)),l.style.left=`${H}px`,l.style.top=`${ee}px`}function $(){l.hidden=!0,n.classList.remove("locked"),n.hidden=!0,p=null}function P(){let Y=d.value.trim();if(!p)return;if(!Y&&!p.selection){d.focus(),d.classList.add("shake"),setTimeout(()=>d.classList.remove("shake"),400);return}let de={kind:"annotation",target:p.targetId,component:p.component,path:p.path.map(H=>H.id),note:Y};p.selection&&(de.selection=p.selection),p.excerpt&&!p.selection&&(de.excerpt=p.excerpt),p.detail&&p.targetId===p.path[p.path.length-1].id&&(de.detail=p.detail),e({type:"annotate",item:de});let se=p.targetId;$(),window.getSelection()?.removeAllRanges(),re([se],"queued")}l.addEventListener("click",Y=>{let de=Y.target.closest("[data-act]")?.getAttribute("data-act");de==="cancel"&&$(),de==="queue"&&P()}),d.addEventListener("keydown",Y=>{Y.key==="Enter"&&!Y.shiftKey&&!Y.isComposing?(Y.preventDefault(),P()):Y.key==="Escape"&&(Y.preventDefault(),d.value.trim()||$())}),document.addEventListener("mousemove",I,{passive:!0}),document.addEventListener("click",M,!0),document.addEventListener("mouseup",D),document.addEventListener("keyup",Y=>{(Y.shiftKey||Y.key.startsWith("Arrow"))&&D()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&D()}),document.addEventListener("keydown",Y=>{(Y.metaKey||Y.ctrlKey)&&(Y.key==="i"||Y.key==="I")&&(Y.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{l.hidden&&(n.hidden=!0,i.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{l.hidden&&(n.hidden=!0)});function q(Y,de,se,H=new Set){if(H.has(Y))return[];H.add(Y);let ee=document.querySelectorAll(`[data-oui-id="${CSS.escape(Y)}"]`);return ee.length?Array.from(ee):!de||!se?[]:se(de,Y).flatMap(oe=>q(oe,de,se,H))}function V(Y,de,se){let H=new Set;for(let ee of Y)for(let oe of q(ee,de,se))H.add(oe);return Array.from(H).filter(ee=>!Array.from(H).some(oe=>oe!==ee&&oe.contains(ee)))}function Z(Y,de){let se=document.createElement("div");return se.setAttribute(S9,""),se.className=de,se.style.left=`${Y.left+window.scrollX-4}px`,se.style.top=`${Y.top+window.scrollY-4}px`,se.style.width=`${Y.width+8}px`,se.style.height=`${Y.height+8}px`,se}function X(Y){if(getComputedStyle(Y).display!=="contents")return Y.getBoundingClientRect();let[de,se,H,ee]=[1/0,1/0,-1/0,-1/0],oe=_e=>{!_e.width&&!_e.height||([de,se,H,ee]=[Math.min(de,_e.left),Math.min(se,_e.top),Math.max(H,_e.right),Math.max(ee,_e.bottom)])},he=_e=>{for(let Me of _e.childNodes)if(Me.nodeType===Node.ELEMENT_NODE)getComputedStyle(Me).display==="contents"?he(Me):oe(Me.getBoundingClientRect());else if(Me.nodeType===Node.TEXT_NODE&&Me.textContent.trim()){let rt=document.createRange();rt.selectNodeContents(Me),oe(rt.getBoundingClientRect())}};return he(Y),{left:de,top:se,right:H,bottom:ee}}function ae(Y){let{left:de,top:se,right:H,bottom:ee}=X(Y);for(let oe=Y.parentElement;oe&&oe!==document.body;oe=oe.parentElement){let he=getComputedStyle(oe),_e=he.overflowX!=="visible",Me=he.overflowY!=="visible";if(!_e&&!Me||he.display==="contents")continue;let rt=oe.getBoundingClientRect();_e&&([de,H]=[Math.max(de,rt.left),Math.min(H,rt.right)]),Me&&([se,ee]=[Math.max(se,rt.top),Math.min(ee,rt.bottom)])}return H>de&&ee>se?{left:de,top:se,width:H-de,height:ee-se}:null}function re(Y,de="changed",se,H){let ee=V(Y,se,H);for(let oe of ee){let he=ae(oe);if(!he)continue;let _e=Z(he,`austere-flash ${de}`);document.body.appendChild(_e),setTimeout(()=>_e.remove(),2400)}return ee}function F(Y,de,se){let H=q(Y,de,se);if(!H.length)return!1;let ee=SX(H[0]),oe=ee.top+window.scrollY-Math.max(24,(window.innerHeight-ee.height)/3);return window.scrollTo({top:Math.max(0,oe),behavior:"smooth"}),setTimeout(()=>re([Y],"changed",de,se),350),!0}let le=document.createElement("div");le.setAttribute(S9,""),r.appendChild(le);let ge=null,U=0;function Oe(){U=0;let[Y,de]=ge&&t?t():[],se=[],H=[];for(let ee of ge?V(ge,Y,de):[]){let oe=ae(ee);oe&&(se.push(ee),H.push(oe))}return le.replaceChildren(...H.map(ee=>Z(ee,"austere-mark"))),se}let Be=()=>{ge&&!U&&(U=requestAnimationFrame(Oe))};window.addEventListener("resize",Be,{passive:!0}),new ResizeObserver(Be).observe(document.body),new MutationObserver(Y=>{Y.some(de=>!v(de.target.nodeType===1?de.target:de.target.parentElement))&&Be()}).observe(document.body,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["class","style","hidden","open"]}),document.addEventListener("scroll",Y=>{Y.target!==document&&Be()},{capture:!0,passive:!0});function Fe(Y,de=!1){ge=Y.length?Y:null;let se=Oe();if(de&&se.length){let H=se.map(SX);if(!H.some(ee=>ee.bottom>0&&ee.top<window.innerHeight)){let ee=H[0],oe=ee.top+window.scrollY-Math.max(24,(window.innerHeight-ee.height)/3);window.scrollTo({top:Math.max(0,oe),behavior:"smooth"})}}return se.length}return{setMode(Y){a=Y==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",a==="annotate"),a!=="annotate"&&($(),i.hidden=!0,n.hidden=!0)},flash:re,reveal:F,highlight:Fe}}var LEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.5.1",components:["Mermaid"]},shadcn:{name:"@austere-axi/pack-shadcn",version:"0.5.1",components:["Progress","Avatar","AvatarGroup","Tooltip","HoverCard","Breadcrumb","BreadcrumbItem","Kbd","Skeleton","Calendar","Timeline","TimelineItem"]}};function n$a(e){let t=e.lastIndexOf("@");return t<=0?{name:e,version:""}:{name:e.slice(0,t),version:e.slice(t+1)}}var o$a=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,i$a=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function _Et(e){let t=[];for(let a of String(e||"").split(",")){let[r,n]=a.trim().split(/\s+/);if(!r)continue;let{name:o,version:i}=n$a(r);!o$a.test(o)||!i$a.test(i)||t.some(l=>l.name===o)||t.push(n&&/^sha(256|384|512)-[A-Za-z0-9+/=]+$/.test(n)?{name:o,version:i,integrity:n}:{name:o,version:i})}return t}var MEt="https://cdn.jsdelivr.net/npm";function CEt(e=document){let t=e.querySelector('meta[name="austere-packs"]');return t?_Et(t.getAttribute("content")||""):[]}function AEt(e,t){let a=[...t];for(let[r,n]of Object.entries(LEt))a.some(o=>o.name===n.name)||n.components.some(o=>new RegExp(`\\b${o}\\s*\\(`).test(e))&&a.push({name:n.name,version:n.version,bundled:r});return a}function LX(){return/\/__austere\/$/.test(I2)}function l$a(e){if(e.bundled)return{src:B4(`packs/${e.bundled}/pack.js`)||`${MEt}/austere-axi@${e.version}/dist/packs/${e.bundled}/pack.js`};if(LX()){let t=e.integrity?`?integrity=${encodeURIComponent(e.integrity)}`:"";return{src:`${I2}installed-packs/${encodeURIComponent(`${e.name}@${e.version}`)}/pack.js${t}`}}return{src:`${MEt}/${e.name}@${e.version}/dist/pack.js`,integrity:e.integrity}}function s$a(e,t){return new Promise((a,r)=>{let n=document.createElement("script");n.src=e,n.async=!0,t&&(n.integrity=t,n.crossOrigin="anonymous"),n.onload=()=>a(),n.onerror=()=>r(new Error(`could not load ${e}`)),document.head.appendChild(n)})}async function NEt(e,{onError:t}={}){let a=e.filter(r=>!ys().some(n=>n.name===r.name));return await Promise.all(a.map(r=>{let{src:n,integrity:o}=l$a(r);return s$a(n,o).then(()=>{ys().some(i=>i.name===r.name)||t?.(r,new Error(`${n} loaded but did not register pack ${r.name}`))},i=>t?.(r,i))})),ys()}function TEt(e){for(let t of e){if(!t.css)continue;let a=`austere-pack-css-${t.name.replace(/[^a-z0-9]/gi,"-")}`;if(document.getElementById(a))continue;let r=document.createElement("style");r.id=a,r.textContent=t.css,document.head.appendChild(r)}}var c$a=/[A-Za-z_$]/;function u$a(e){let t=[],a=String(e??""),r=0,n=1,o=a.length;for(;r<o;){let i=a[r];if(i===`
1610
+ </div>`,document.body.appendChild(r);let n=r.querySelector(".austere-hover"),o=r.querySelector(".austere-hover-label"),i=r.querySelector(".austere-selbtn"),l=r.querySelector(".austere-pop"),s=r.querySelector(".austere-pop-path"),u=r.querySelector(".austere-pop-quote"),d=r.querySelector("textarea"),p=null,f=null,m=0,x=null,v=Y=>!!Y?.closest?.(`[${S9}]`),w=Y=>Y?.closest?.("[data-oui-id]")||null;function b(Y,de,se=4){Y.style.left=`${de.left-se}px`,Y.style.top=`${de.top-se}px`,Y.style.width=`${de.width+se*2}px`,Y.style.height=`${de.height+se*2}px`}function k(Y){if(!Y){n.hidden=!0;return}b(n,SX(Y)),o.textContent=`${Y.getAttribute("data-oui-id")} \xB7 ${Y.getAttribute("data-oui-c")}`,n.hidden=!1}function I(Y){a!=="annotate"||!l.hidden||(x=Y.target,!m&&(m=requestAnimationFrame(()=>{m=0,!v(x)&&k(w(x))})))}function L(){let Y=window.getSelection();return!!Y&&!Y.isCollapsed&&Y.toString().trim().length>0}function M(Y){if(a!=="annotate"||v(Y.target))return;if(!l.hidden){Y.preventDefault(),Y.stopPropagation();return}if(L()||!Y.altKey&&Y.target.closest(t$a))return;let de=w(Y.target);de&&(Y.preventDefault(),Y.stopPropagation(),T({target:de,el:Y.target,point:{x:Y.clientX,y:Y.clientY}}))}function _(){if(a!=="annotate"||!l.hidden)return;let Y=window.getSelection();if(!Y||Y.isCollapsed||!Y.rangeCount){i.hidden=!0,f=null;return}let de=Y.toString().trim(),se=Y.getRangeAt(0),H=se.commonAncestorContainer;H.nodeType!==1&&(H=H.parentElement);let ee=w(H);if(!de||!ee||v(H)){i.hidden=!0,f=null;return}let oe=se.getBoundingClientRect();f={target:ee,el:H,selection:vv(de,600),point:{x:oe.left,y:oe.bottom}},i.hidden=!1;let he=i.offsetWidth||140;i.style.left=`${Math.max(8,Math.min(window.innerWidth-he-8,oe.left+oe.width/2-he/2))}px`,i.style.top=`${Math.max(8,Math.min(window.innerHeight-40,oe.bottom+8))}px`}let A=0;function D(){clearTimeout(A),A=setTimeout(_,120)}i.addEventListener("mousedown",Y=>Y.preventDefault()),i.addEventListener("click",Y=>{Y.preventDefault(),Y.stopPropagation(),f&&T(f),i.hidden=!0});function E(){s.textContent="",p.path.forEach((Y,de)=>{let se=document.createElement("button");if(se.type="button",se.className="austere-chip"+(Y.id===p.targetId?" active":""),se.textContent=Y.id,se.title=Y.component,se.addEventListener("click",()=>{p.targetId=Y.id,p.component=Y.component,E(),d.focus()}),s.appendChild(se),de<p.path.length-1){let H=document.createElement("span");H.className="austere-chip-sep",H.textContent="\u203A",s.appendChild(H)}})}function T({target:Y,el:de,selection:se,point:H}){let ee=a$a(Y),oe=r$a(Y,de),he=IEt(Y,de),_e;se||(_e=vv((he||de).innerText||(he||de).textContent,200)||void 0,he&&_e===oe?.part&&(_e=void 0)),p={targetId:Y.getAttribute("data-oui-id"),component:Y.getAttribute("data-oui-c"),path:ee,selection:se||void 0,excerpt:_e,detail:oe},E();let Me=se||_e||oe?.part||"";u.textContent=vv(Me,220),u.hidden=!Me,d.value="",n.hidden=!0,i.hidden=!0,k(Y),n.classList.add("locked"),l.hidden=!1,O(H),d.focus()}function O(Y){if(window.innerWidth<640){l.classList.add("sheet"),l.style.left=l.style.top="";return}l.classList.remove("sheet");let de=l.offsetWidth||360,se=l.offsetHeight||200,H=Y.x+12,ee=Y.y+12;H+de>window.innerWidth-8&&(H=Math.max(8,Y.x-de-12)),ee+se>window.innerHeight-8&&(ee=Math.max(8,Y.y-se-12)),l.style.left=`${H}px`,l.style.top=`${ee}px`}function $(){l.hidden=!0,n.classList.remove("locked"),n.hidden=!0,p=null}function P(){let Y=d.value.trim();if(!p)return;if(!Y&&!p.selection){d.focus(),d.classList.add("shake"),setTimeout(()=>d.classList.remove("shake"),400);return}let de={kind:"annotation",target:p.targetId,component:p.component,path:p.path.map(H=>H.id),note:Y};p.selection&&(de.selection=p.selection),p.excerpt&&!p.selection&&(de.excerpt=p.excerpt),p.detail&&p.targetId===p.path[p.path.length-1].id&&(de.detail=p.detail),e({type:"annotate",item:de});let se=p.targetId;$(),window.getSelection()?.removeAllRanges(),re([se],"queued")}l.addEventListener("click",Y=>{let de=Y.target.closest("[data-act]")?.getAttribute("data-act");de==="cancel"&&$(),de==="queue"&&P()}),d.addEventListener("keydown",Y=>{Y.key==="Enter"&&!Y.shiftKey&&!Y.isComposing?(Y.preventDefault(),P()):Y.key==="Escape"&&(Y.preventDefault(),d.value.trim()||$())}),document.addEventListener("mousemove",I,{passive:!0}),document.addEventListener("click",M,!0),document.addEventListener("mouseup",D),document.addEventListener("keyup",Y=>{(Y.shiftKey||Y.key.startsWith("Arrow"))&&D()}),document.addEventListener("selectionchange",()=>{"ontouchstart"in window&&D()}),document.addEventListener("keydown",Y=>{(Y.metaKey||Y.ctrlKey)&&(Y.key==="i"||Y.key==="I")&&(Y.preventDefault(),e({type:"toggle-mode"}))}),window.addEventListener("scroll",()=>{l.hidden&&(n.hidden=!0,i.hidden=!0)},{passive:!0}),document.addEventListener("mouseleave",()=>{l.hidden&&(n.hidden=!0)});function q(Y,de,se,H=new Set){if(H.has(Y))return[];H.add(Y);let ee=document.querySelectorAll(`[data-oui-id="${CSS.escape(Y)}"]`);return ee.length?Array.from(ee):!de||!se?[]:se(de,Y).flatMap(oe=>q(oe,de,se,H))}function V(Y,de,se){let H=new Set;for(let ee of Y)for(let oe of q(ee,de,se))H.add(oe);return Array.from(H).filter(ee=>!Array.from(H).some(oe=>oe!==ee&&oe.contains(ee)))}function Z(Y,de){let se=document.createElement("div");return se.setAttribute(S9,""),se.className=de,se.style.left=`${Y.left+window.scrollX-4}px`,se.style.top=`${Y.top+window.scrollY-4}px`,se.style.width=`${Y.width+8}px`,se.style.height=`${Y.height+8}px`,se}function X(Y){if(getComputedStyle(Y).display!=="contents")return Y.getBoundingClientRect();let[de,se,H,ee]=[1/0,1/0,-1/0,-1/0],oe=_e=>{!_e.width&&!_e.height||([de,se,H,ee]=[Math.min(de,_e.left),Math.min(se,_e.top),Math.max(H,_e.right),Math.max(ee,_e.bottom)])},he=_e=>{for(let Me of _e.childNodes)if(Me.nodeType===Node.ELEMENT_NODE)getComputedStyle(Me).display==="contents"?he(Me):oe(Me.getBoundingClientRect());else if(Me.nodeType===Node.TEXT_NODE&&Me.textContent.trim()){let rt=document.createRange();rt.selectNodeContents(Me),oe(rt.getBoundingClientRect())}};return he(Y),{left:de,top:se,right:H,bottom:ee}}function ae(Y){let{left:de,top:se,right:H,bottom:ee}=X(Y);for(let oe=Y.parentElement;oe&&oe!==document.body;oe=oe.parentElement){let he=getComputedStyle(oe),_e=he.overflowX!=="visible",Me=he.overflowY!=="visible";if(!_e&&!Me||he.display==="contents")continue;let rt=oe.getBoundingClientRect();_e&&([de,H]=[Math.max(de,rt.left),Math.min(H,rt.right)]),Me&&([se,ee]=[Math.max(se,rt.top),Math.min(ee,rt.bottom)])}return H>de&&ee>se?{left:de,top:se,width:H-de,height:ee-se}:null}function re(Y,de="changed",se,H){let ee=V(Y,se,H);for(let oe of ee){let he=ae(oe);if(!he)continue;let _e=Z(he,`austere-flash ${de}`);document.body.appendChild(_e),setTimeout(()=>_e.remove(),2400)}return ee}function F(Y,de,se){let H=q(Y,de,se);if(!H.length)return!1;let ee=SX(H[0]),oe=ee.top+window.scrollY-Math.max(24,(window.innerHeight-ee.height)/3);return window.scrollTo({top:Math.max(0,oe),behavior:"smooth"}),setTimeout(()=>re([Y],"changed",de,se),350),!0}let le=document.createElement("div");le.setAttribute(S9,""),r.appendChild(le);let ge=null,U=0;function Oe(){U=0;let[Y,de]=ge&&t?t():[],se=[],H=[];for(let ee of ge?V(ge,Y,de):[]){let oe=ae(ee);oe&&(se.push(ee),H.push(oe))}return le.replaceChildren(...H.map(ee=>Z(ee,"austere-mark"))),se}let Be=()=>{ge&&!U&&(U=requestAnimationFrame(Oe))};window.addEventListener("resize",Be,{passive:!0}),new ResizeObserver(Be).observe(document.body),new MutationObserver(Y=>{Y.some(de=>!v(de.target.nodeType===1?de.target:de.target.parentElement))&&Be()}).observe(document.body,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["class","style","hidden","open"]}),document.addEventListener("scroll",Y=>{Y.target!==document&&Be()},{capture:!0,passive:!0});function Fe(Y,de=!1){ge=Y.length?Y:null;let se=Oe();if(de&&se.length){let H=se.map(SX);if(!H.some(ee=>ee.bottom>0&&ee.top<window.innerHeight)){let ee=H[0],oe=ee.top+window.scrollY-Math.max(24,(window.innerHeight-ee.height)/3);window.scrollTo({top:Math.max(0,oe),behavior:"smooth"})}}return se.length}return{setMode(Y){a=Y==="annotate"?"annotate":"explore",document.documentElement.classList.toggle("austere-annotating",a==="annotate"),a!=="annotate"&&($(),i.hidden=!0,n.hidden=!0)},flash:re,reveal:F,highlight:Fe}}var LEt={mermaid:{name:"@austere-axi/pack-mermaid",version:"0.5.2",components:["Mermaid"]},shadcn:{name:"@austere-axi/pack-shadcn",version:"0.5.2",components:["Progress","Avatar","AvatarGroup","Tooltip","HoverCard","Breadcrumb","BreadcrumbItem","Kbd","Skeleton","Calendar","Timeline","TimelineItem"]}};function n$a(e){let t=e.lastIndexOf("@");return t<=0?{name:e,version:""}:{name:e.slice(0,t),version:e.slice(t+1)}}var o$a=/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,i$a=/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;function _Et(e){let t=[];for(let a of String(e||"").split(",")){let[r,n]=a.trim().split(/\s+/);if(!r)continue;let{name:o,version:i}=n$a(r);!o$a.test(o)||!i$a.test(i)||t.some(l=>l.name===o)||t.push(n&&/^sha(256|384|512)-[A-Za-z0-9+/=]+$/.test(n)?{name:o,version:i,integrity:n}:{name:o,version:i})}return t}var MEt="https://cdn.jsdelivr.net/npm";function CEt(e=document){let t=e.querySelector('meta[name="austere-packs"]');return t?_Et(t.getAttribute("content")||""):[]}function AEt(e,t){let a=[...t];for(let[r,n]of Object.entries(LEt))a.some(o=>o.name===n.name)||n.components.some(o=>new RegExp(`\\b${o}\\s*\\(`).test(e))&&a.push({name:n.name,version:n.version,bundled:r});return a}function LX(){return/\/__austere\/$/.test(I2)}function l$a(e){if(e.bundled)return{src:B4(`packs/${e.bundled}/pack.js`)||`${MEt}/austere-axi@${e.version}/dist/packs/${e.bundled}/pack.js`};if(LX()){let t=e.integrity?`?integrity=${encodeURIComponent(e.integrity)}`:"";return{src:`${I2}installed-packs/${encodeURIComponent(`${e.name}@${e.version}`)}/pack.js${t}`}}return{src:`${MEt}/${e.name}@${e.version}/dist/pack.js`,integrity:e.integrity}}function s$a(e,t){return new Promise((a,r)=>{let n=document.createElement("script");n.src=e,n.async=!0,t&&(n.integrity=t,n.crossOrigin="anonymous"),n.onload=()=>a(),n.onerror=()=>r(new Error(`could not load ${e}`)),document.head.appendChild(n)})}async function NEt(e,{onError:t}={}){let a=e.filter(r=>!ys().some(n=>n.name===r.name));return await Promise.all(a.map(r=>{let{src:n,integrity:o}=l$a(r);return s$a(n,o).then(()=>{ys().some(i=>i.name===r.name)||t?.(r,new Error(`${n} loaded but did not register pack ${r.name}`))},i=>t?.(r,i))})),ys()}function TEt(e){for(let t of e){if(!t.css)continue;let a=`austere-pack-css-${t.name.replace(/[^a-z0-9]/gi,"-")}`;if(document.getElementById(a))continue;let r=document.createElement("style");r.id=a,r.textContent=t.css,document.head.appendChild(r)}}var c$a=/[A-Za-z_$]/;function u$a(e){let t=[],a=String(e??""),r=0,n=1,o=a.length;for(;r<o;){let i=a[r];if(i===`
1611
1611
  `){n++,r++;continue}if(i===" "||i===" "||i==="\r"){r++;continue}if(i==="#"||i==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
1612
1612
  `;)r++;continue}let l=r,s=n,u=0,d=null,p=!1;for(;r<o;){let x=a[r];if(d){p?p=!1:x==="\\"?p=!0:x===d&&(d=null),x===`
1613
1613
  `&&n++,r++;continue}if(x==='"'||x==="'"){d=x,r++;continue}if(x==="#"||x==="/"&&a[r+1]==="/"){for(;r<o&&a[r]!==`
1614
1614
  `;)r++;continue}if(x==="("||x==="["||x==="{")u++;else if(x===")"||x==="]"||x==="}")u=Math.max(0,u-1);else if(x===`
1615
- `){if(u===0&&!d$a(a,r+1))break;n++}r++}let f=a.slice(l,r).replace(/\s+$/,""),m=/^\s*([A-Za-z_$][\w$]*)\s*=/.exec(f);m&&c$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function d$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function eh(e){let t=new Map;for(let a of u$a(e))t.set(a.id,a);return t}function REt(e,t){let a=eh(e),r=eh(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||zEt(l.text)!==zEt(i.text))&&n.push(o)}return n}function L9(e,t){let a=new RegExp(`(^|[^\\w$])${f$a(t)}(?![\\w$])`),r=[];for(let[n,o]of e){if(n===t)continue;let i=o.text.slice(o.text.indexOf("=")+1);a.test(p$a(i))&&r.push(n)}return r}function p$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function zEt(e){return e.replace(/\s+/g," ").trim()}function f$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var kv=ne(It(),1),CX="0.5.1",m$a='script[type="text/openui"], script[type="text/openui-lang"]';function h$a(){if(document.getElementById("austere-styles"))return;let e=document.createElement("style");if(e.id="austere-styles",e.textContent=[Coe,Loe,_oe,Moe].join(`
1615
+ `){if(u===0&&!d$a(a,r+1))break;n++}r++}let f=a.slice(l,r).replace(/\s+$/,""),m=/^\s*([A-Za-z_$][\w$]*)\s*=/.exec(f);m&&c$a.test(m[1][0])&&t.push({id:m[1],text:f,line:s,endLine:n})}return t}function d$a(e,t){let a=t;for(;a<e.length&&(e[a]===" "||e[a]===" ");)a++;return e[a]==="?"||e[a]===":"}function eh(e){let t=new Map;for(let a of u$a(e))t.set(a.id,a);return t}function REt(e,t){let a=eh(e),r=eh(t),n=[];for(let[o,i]of r){let l=a.get(o);(!l||zEt(l.text)!==zEt(i.text))&&n.push(o)}return n}function L9(e,t){let a=new RegExp(`(^|[^\\w$])${f$a(t)}(?![\\w$])`),r=[];for(let[n,o]of e){if(n===t)continue;let i=o.text.slice(o.text.indexOf("=")+1);a.test(p$a(i))&&r.push(n)}return r}function p$a(e){return e.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gs,'""')}function zEt(e){return e.replace(/\s+/g," ").trim()}function f$a(e){return e.replace(/[$()*+.?[\\\]^{|}]/g,"\\$&")}var kv=ne(It(),1),CX="0.5.2",m$a='script[type="text/openui"], script[type="text/openui-lang"]';function h$a(){if(document.getElementById("austere-styles"))return;let e=document.createElement("style");if(e.id="austere-styles",e.textContent=[Coe,Loe,_oe,Moe].join(`
1616
1616
  `),document.head.appendChild(e),!document.querySelector('meta[name="viewport"]')){let t=document.createElement("meta");t.name="viewport",t.content="width=device-width, initial-scale=1",document.head.appendChild(t)}document.documentElement.classList.add("austere"),document.documentElement.style.colorScheme="dark"}function PEt(e){if(!e||typeof e!="object")return;let t={};for(let[a,r]of Object.entries(e))if(!a.startsWith("$"))if(r&&typeof r=="object"&&!Array.isArray(r))if("value"in r&&Object.keys(r).length<=3)t[a]=r.value;else{let n=PEt(r);n&&Object.keys(n).length&&(t[a]=n)}else t[a]=r;return Object.keys(t).length?t:void 0}function g$a(){let e=window.parent&&window.parent!==window,t=new Map,a=r=>{e&&window.parent.postMessage({austere:1,...r},"*")};return window.addEventListener("message",r=>{if(r.source!==window.parent)return;let n=r.data;!n||n.austere!==1||typeof n.type!="string"||t.get(n.type)?.forEach(o=>o(n))}),{framed:e,connected:!1,send:a,on(r,n){return t.has(r)||t.set(r,new Set),t.get(r).add(n),()=>t.get(r).delete(n)}}}function $Et(e,t){e.framed?e.send({type:"open-url",url:t}):window.open(t,"_blank","noopener")}function _X(e){let t=document.createElement("div");t.className="austere-toast",t.setAttribute("data-austere-ui",""),t.textContent=e,document.body.appendChild(t),setTimeout(()=>t.classList.add("out"),2600),setTimeout(()=>t.remove(),3100)}function y$a(e){let t=[e.message];return e.form&&t.push(`form: ${JSON.stringify(e.form)}`),e.context!==void 0&&t.push(`context: ${JSON.stringify(e.context)}`),t.join(`
1617
1617
  `)}var MX=null,BEt=new Set,bv=xv,EEt="";function x$a(e,t){BEt.add(e.name);let a=!e.bundled&&LX()?" (`austere-axi check <file>` says why)":"";Rc({source:"pack",component:e.name,message:`pack ${e.name}@${e.version}: ${t.message}${a}`})}function v$a(){let e=ys(),t=e.map(a=>`${a.name}@${a.version}`).join(",");if(t===EEt)return bv;EEt=t,TEt(e);try{bv=e.length?IX(e.flatMap(a=>a.components),{onSkip:(a,r)=>Rc({source:"pack",component:a.name,message:r})}):xv}catch(a){Rc({source:"pack",message:`packs could not be added (${a.message}); rendering without them`}),bv=xv}return window.__austere.library=bv,bv}async function FEt(e){MX||(MX=CEt());let t=AEt(e,MX).filter(a=>!BEt.has(a.name));return t.some(a=>!ys().some(r=>r.name===a.name))&&await NEt(t,{onError:x$a}),v$a()}function b$a({programRef:e,initialLibrary:t,bridge:a,annotator:r,initialState:n}){let[o,i]=(0,Pi.useState)({program:e.current,library:t}),l=o.program,s=(0,Pi.useRef)(null);(0,Pi.useEffect)(()=>a.on("source",async({program:f})=>{if(typeof f!="string"||f===e.current)return;let m=REt(e.current,f);e.current=f,r?.highlight([]);let x=await FEt(f);e.current===f&&(s.current=m,i({program:f,library:x}))}),[a]),(0,Pi.useEffect)(()=>a.on("reveal",({id:f})=>{r?.reveal(f,eh(e.current),L9)}),[a,r]),(0,Pi.useEffect)(()=>{let f=s.current;if(!f)return;s.current=null;let m=setTimeout(()=>{let x=eh(e.current),v=f.filter(w=>w!=="root");r?.flash(v,"changed",x,L9),a.send({type:"changes",ids:v})},120);return()=>clearTimeout(m)},[l,r,a]);let u=(0,Pi.useCallback)(f=>{if(f.type==="open_url"){let v=f.params?.url;typeof v=="string"&&/^(https?:|mailto:)/i.test(v)&&$Et(a,v);return}if(f.type!=="continue_conversation")return;let m={kind:"action",message:f.humanFriendlyMessage||"(button)"},x=PEt(f.formState);if(x&&(m.form=x),f.params?.context!==void 0&&(m.context=f.params.context),a.connected)a.send({type:"action",item:m}),_X("Queued for the agent \u2014 press Send when ready");else{let v=y$a(m);navigator.clipboard?.writeText(v).then(()=>_X("Copied \u2014 paste it to your agent"),()=>_X(v))}},[a]),d=(0,Pi.useCallback)(f=>{a.send({type:"errors",errors:(f||[]).map(m=>({source:m.source,code:m.code,statementId:m.statementId,message:m.message,hint:m.hint}))})},[a]),p=(0,Pi.useCallback)(f=>a.send({type:"state",state:f}),[a]);return(0,kv.jsx)(Soe,{mode:"dark",children:(0,kv.jsx)("main",{className:"austere-page",children:(0,kv.jsx)(b2,{response:l,library:o.library,isStreaming:!1,onAction:u,onError:d,onStateUpdate:p,initialState:n})})})}function DEt(){h$a();let e=document.querySelector(m$a),t=e?e.textContent:"",a=document.createElement("div");a.id="austere-root",document.body.appendChild(a);let r=g$a(),n={current:t},o=null,i=null,l=FEt(t),s=p=>i||(i=l.then(f=>(0,OEt.createRoot)(a).render((0,kv.jsx)(b$a,{programRef:n,initialLibrary:f,bridge:r,annotator:o,initialState:p}))));if(!r.framed){s(void 0);return}let u={program:null,map:null};o=SEt({post:p=>r.send(p),lookup:()=>(u.program!==n.current&&(u={program:n.current,map:eh(n.current)}),[u.map,L9])}),r.on("highlight",({ids:p,scroll:f})=>{o.highlight(Array.isArray(p)?p.filter(m=>typeof m=="string"):[],!!f)}),document.addEventListener("click",p=>{if(p.defaultPrevented||p.button!==0)return;let f=p.target.closest?.("a[href]");if(!f||!/^(https?|mailto):/i.test(f.href)||f.protocol===location.protocol&&f.host===location.host)return;let m=(f.getAttribute("target")||"").toLowerCase();!m||m==="_self"||(p.preventDefault(),$Et(r,f.href))}),nee(p=>r.send({type:"runtime-error",error:p})),r.on("init",async p=>{if(r.connected=!0,o.setMode(p.mode),await s(p.state||void 0),p.scrollY>0){let f=0,m=()=>{window.scrollTo(0,p.scrollY),Math.abs(window.scrollY-p.scrollY)>2&&++f<20&&setTimeout(m,50)};setTimeout(m,30)}r.send({type:"ready",title:document.title,version:CX})}),r.on("mode",p=>o.setMode(p.mode)),window.addEventListener("scroll",()=>r.send({type:"scroll",y:window.scrollY}),{passive:!0}),r.send({type:"hello",title:document.title,version:CX}),setTimeout(()=>s(void 0),600)}window.__austere={version:CX,library:xv,packs:ys};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",DEt,{once:!0}):DEt();})();
package/dist/catalog.json CHANGED
@@ -1 +1 @@
1
- {"format":1,"pack":"core","version":"0.5.1","groups":[{"name":"Layout","notes":["- A page is root = Stack([...sections], \"column\", \"l\"). Each section is a Card([CardHeader(title, subtitle), ...content]).","- Side by side: Stack([a, b], \"row\", \"m\", \"stretch\", \"start\", true). Cards in a row share the width.","- Tabs for alternative views, Accordion for detail the reader may skip, Steps for ordered procedures. Several blocks in one Card: label each with InlineHeader.","- Card children must match its list; anything else (Accordion, Tag, a Callout row) can sit in a Stack inside the Card, or directly in root's Stack."]},{"name":"Content","notes":["- MarkDownRenderer for prose, bullet lists and inline code; one statement per paragraph group so each is annotatable.","- Callout for the one thing the reader must not miss: a risk, a decision needed, a caveat.","- CodeBlock for a whole snippet. Image src may be a path relative to the artifact file.","- Architecture for systems: services, stores, queues, trust boundaries. Name every Node, Edge and Group. Over ~15 nodes, use lanes or split the diagram.","- lanes: for systems split by owner, repo or tier. Groups are the columns, left to right as listed; put every node in one, in reading order (plain groups for a heading without a frame). About 4 columns fit a laptop; wider ones scroll (check warns). Phones get the lanes stacked as bands. Pin row only to put boxes side by side or line them up.","- Diff for code changes: show the hunks that matter, not whole files. Hunk counts are recomputed, so only start lines matter.","- Mermaid for sequences, state machines, ER, gantt and quick flowcharts."]},{"name":"Data","notes":["- Table is COLUMN-oriented: Table([Col(\"Option\", names), Col(\"Effort\", efforts), Col(\"Risk\", risks)]). Col data may mix strings and components, e.g. [\"n/a\", Tag(\"high\", null, \"sm\", \"danger\")]. Cells are plain text (no markdown) and wrap; Col type \"number\" for figures.","- EntityList for key/value facts; OverviewCardBlock + OverviewCardItem + MetricIndicatorInline for a KPI row.","- EditableTable when the reviewer should correct or fill in values; edits arrive with the next @ToAssistant from its Form.","- Icon names are lucide kebab-case (circle-check, triangle-alert, git-branch)."]},{"name":"Charts","notes":["- Charts take column arrays: BarChart(labels, [Series(\"p50\", [..]), Series(\"p99\", [..])], \"grouped\", \"Week\", \"ms\").","- PieChart(labels, values) with plain number arrays."]},{"name":"Forms","notes":["- Form(name, buttons, fields). Define each FormControl as its own statement. Never nest a Form in a Form.","- To ask the reviewer to decide: OptionCards or RadioGroup inside a Form whose button is Button(\"Send decision\", @ToAssistant(\"Decision: rollout strategy\"), \"primary\"). The values arrive with that message as form: {<formName>: {<fieldName>: value}}; no context needed. Reviewers can always annotate and message, so add a notes field only when it belongs with the decision.","- OptionCards(\"pick\", \"single\", [OptionCard(\"a\", \"Option A\", \"why\", Icon(\"zap\")), OptionCard(\"b\", \"Option B\")], {required: true}, \"a\") — the last argument pre-selects.","- rules: {required: true, minLength: 10, ...}; the renderer shows the messages."]},{"name":"Buttons","notes":["- @ToAssistant(message, context?) queues a message to the agent; @OpenUrl(url) opens a link. A Button with no action sends its label.","- Use buttons for follow-ups the reviewer can pick in one click: \"Go with option B\", \"Split this into two PRs\"."]}],"components":[{"name":"Card","group":"Layout","signature":"Card(children: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Tabs | Stack | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[], variant?: \"card\" | \"sunk\" | \"clear\", direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Styled container. variant: \"card\" (default, elevated) | \"sunk\" (recessed) | \"clear\" (transparent). Always full width. Accepts all Stack flex params (default: direction \"column\"). Cards flex to share space in row/wrap layouts.","tier":"core","pack":"core","examples":[]},{"name":"CardHeader","group":"Layout","signature":"CardHeader(title?: string, subtitle?: string)","description":"Header with optional title and subtitle","tier":"core","pack":"core","examples":[]},{"name":"TextContent","group":"Content","signature":"TextContent(text: string, size?: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\")","description":"Text block. Supports markdown. Optional size: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\".","tier":"core","pack":"core","examples":[]},{"name":"MarkDownRenderer","group":"Content","signature":"MarkDownRenderer(textMarkdown: string, variant?: \"clear\" | \"card\" | \"sunk\")","description":"Renders markdown text with optional container variant","tier":"core","pack":"core","examples":[]},{"name":"Callout","group":"Content","signature":"Callout(variant: \"info\" | \"warning\" | \"error\" | \"success\" | \"neutral\", title: string, description: string, visible?: $binding<boolean>)","description":"Callout banner. Optional visible is a reactive $boolean — auto-dismisses after 3s by setting $visible to false.","tier":"core","pack":"core","examples":[]},{"name":"Image","group":"Content","signature":"Image(alt: string, src?: string)","description":"Image with alt text and optional URL","tier":"core","pack":"core","examples":[]},{"name":"CodeBlock","group":"Content","signature":"CodeBlock(language: string, codeString: string)","description":"Syntax-highlighted code block","tier":"core","pack":"core","examples":[]},{"name":"InlineHeader","group":"Layout","signature":"InlineHeader(heading: string, description?: string)","description":"Compact section heading with an optional one-line description, for use inside cards.","tier":"core","pack":"core","examples":[]},{"name":"Table","group":"Data","signature":"Table(columns: Col[])","description":"Data table — column-oriented. Each Col holds its own data array.","tier":"core","pack":"core","examples":[]},{"name":"Col","group":"Data","signature":"Col(label: string, data: any, type?: \"string\" | \"number\" | \"action\")","description":"Column definition — holds label + data array","tier":"core","pack":"core","examples":[]},{"name":"EditableTable","group":"Data","signature":"EditableTable(name?: string, columns?: {type: \"text\" | \"number\" | \"date-single\" | \"select\" | \"url\", key?: string, header?: string, width?: number, options?: {value: string, label: string}[]}[], data?: {id: string, values: (string | number)[]}[])","description":"Spreadsheet-like table whose cells the user can edit inline (text, number, url, date, select columns); edits are saved back as a form field","tier":"extended","pack":"core","examples":[]},{"name":"BarChart","group":"Charts","signature":"BarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string, height?: number)","description":"Vertical bars; use for comparing values across categories with one or more series","tier":"core","pack":"core","examples":[]},{"name":"LineChart","group":"Charts","signature":"LineChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Lines over categories; use for trends and continuous data over time","tier":"core","pack":"core","examples":[]},{"name":"AreaChart","group":"Charts","signature":"AreaChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Filled area under lines; use for cumulative totals or volume trends over time","tier":"extended","pack":"core","examples":[]},{"name":"HorizontalBarChart","group":"Charts","signature":"HorizontalBarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string)","description":"Horizontal bars; prefer when category labels are long or for ranked lists","tier":"extended","pack":"core","examples":[]},{"name":"Series","group":"Charts","signature":"Series(category: string, values: number[])","description":"One data series","tier":"core","pack":"core","examples":[]},{"name":"PieChart","group":"Charts","signature":"PieChart(labels: string[], values: number[], variant?: \"pie\" | \"donut\", appearance?: \"circular\" | \"semiCircular\")","description":"Circular slices; use plucked arrays: PieChart(data.categories, data.values)","tier":"core","pack":"core","examples":[]},{"name":"ScatterChart","group":"Charts","signature":"ScatterChart(datasets: ScatterSeries[], xLabel?: string, yLabel?: string)","description":"X/Y scatter plot; use for correlations, distributions, and clustering","tier":"extended","pack":"core","examples":[]},{"name":"ScatterSeries","group":"Charts","signature":"ScatterSeries(name: string, points: Point[])","description":"Named dataset","tier":"extended","pack":"core","examples":[]},{"name":"Point","group":"Charts","signature":"Point(x: number, y: number, z?: number)","description":"Data point with numeric coordinates","tier":"extended","pack":"core","examples":[]},{"name":"Form","group":"Forms","signature":"Form(name: string, buttons: Buttons, fields?: FormControl[])","description":"Form container with fields and explicit action buttons","tier":"core","pack":"core","examples":[]},{"name":"FormControl","group":"Forms","signature":"FormControl(label: string, input: Input | TextArea | Select | Slider | CheckBoxGroup | RadioGroup | Chips | OptionCards, hint?: string)","description":"Field with label, input component, and optional hint text","tier":"core","pack":"core","examples":[]},{"name":"Label","group":"Forms","signature":"Label(text: string)","description":"Text label","tier":"extended","pack":"core","examples":[]},{"name":"Input","group":"Forms","signature":"Input(name: string, placeholder?: string, type?: \"text\" | \"email\" | \"password\" | \"number\" | \"url\", rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"TextArea","group":"Forms","signature":"TextArea(name: string, placeholder?: string, rows?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"Select","group":"Forms","signature":"Select(name: string, items: SelectItem[], placeholder?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>, size?: \"small\" | \"medium\" | \"large\")","description":"","tier":"core","pack":"core","examples":[]},{"name":"SelectItem","group":"Forms","signature":"SelectItem(value: string, label: string)","description":"Option for Select","tier":"core","pack":"core","examples":[]},{"name":"Slider","group":"Forms","signature":"Slider(name: string, variant: \"continuous\" | \"discrete\", min: number, max: number, step?: number, defaultValue?: number[], label?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<number[]>)","description":"Numeric slider input; supports continuous and discrete (stepped) variants","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxGroup","group":"Forms","signature":"CheckBoxGroup(name: string, items: CheckBoxItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<Record<string, boolean>>)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxItem","group":"Forms","signature":"CheckBoxItem(label: string, description: string, name: string, defaultChecked?: boolean)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"RadioGroup","group":"Forms","signature":"RadioGroup(name: string, items: RadioItem[], defaultValue?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"RadioItem","group":"Forms","signature":"RadioItem(label: string, description: string, value: string)","description":"","tier":"core","pack":"core","examples":[]},{"name":"SwitchGroup","group":"Forms","signature":"SwitchGroup(name: string, items: SwitchItem[], variant?: \"clear\" | \"card\" | \"sunk\", value?: $binding<Record<string, boolean>>)","description":"Group of switch toggles","tier":"extended","pack":"core","examples":[]},{"name":"SwitchItem","group":"Forms","signature":"SwitchItem(label?: string, description?: string, name: string, defaultChecked?: boolean)","description":"Individual switch toggle","tier":"extended","pack":"core","examples":[]},{"name":"ChipItem","group":"Forms","signature":"ChipItem(value: string, label: string, icon?: Icon, disabled?: boolean)","description":"A single selectable chip inside a Chips group, with a value, label and optional icon.","tier":"extended","pack":"core","examples":[]},{"name":"Chips","group":"Forms","signature":"Chips(name: string, type?: \"single\" | \"multiple\", items?: ChipItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of compact selectable chips for choosing one or many short options; the selection is stored under `name`.","tier":"extended","pack":"core","examples":[]},{"name":"OptionCard","group":"Forms","signature":"OptionCard(value: string, title: string, subtitle?: string, topContent?: Icon | Image, disabled?: boolean)","description":"A single selectable card inside an OptionCards group, with a value, title, optional subtitle and an optional Icon or Image on top.","tier":"core","pack":"core","examples":[]},{"name":"OptionCards","group":"Forms","signature":"OptionCards(name: string, type?: \"single\" | \"multiple\", items?: OptionCard[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of selectable cards (title, optional subtitle, optional icon or image) laid out in a responsive grid for choosing one or many options; the selection is stored under `name`.","tier":"core","pack":"core","examples":[]},{"name":"Button","group":"Buttons","signature":"Button(label: string, action?: ActionExpression, variant?: \"primary\" | \"secondary\" | \"tertiary\", type?: \"normal\" | \"destructive\", size?: \"extra-small\" | \"small\" | \"medium\" | \"large\")","description":"Clickable button","tier":"core","pack":"core","examples":[]},{"name":"Buttons","group":"Buttons","signature":"Buttons(buttons: Button[], direction?: \"row\" | \"column\")","description":"Group of Button components. direction: \"row\" (default) | \"column\".","tier":"core","pack":"core","examples":[]},{"name":"Stack","group":"Layout","signature":"Stack(children: any[], direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Flex container. direction: \"row\"|\"column\" (default \"column\"). gap: \"none\"|\"xs\"|\"s\"|\"m\"|\"l\"|\"xl\"|\"2xl\" (default \"m\"). align: \"start\"|\"center\"|\"end\"|\"stretch\"|\"baseline\". justify: \"start\"|\"center\"|\"end\"|\"between\"|\"around\"|\"evenly\".","tier":"core","pack":"core","examples":[]},{"name":"Tabs","group":"Layout","signature":"Tabs(items: TabItem[])","description":"Tabbed container","tier":"core","pack":"core","examples":[]},{"name":"TabItem","group":"Layout","signature":"TabItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is tab label, content is array of components","tier":"core","pack":"core","examples":[]},{"name":"Accordion","group":"Layout","signature":"Accordion(items: AccordionItem[])","description":"Collapsible sections","tier":"core","pack":"core","examples":[]},{"name":"AccordionItem","group":"Layout","signature":"AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is section title","tier":"core","pack":"core","examples":[]},{"name":"Steps","group":"Layout","signature":"Steps(items: StepsItem[])","description":"Step-by-step guide","tier":"core","pack":"core","examples":[]},{"name":"StepsItem","group":"Layout","signature":"StepsItem(title: string, details: string)","description":"title and details text for one step","tier":"core","pack":"core","examples":[]},{"name":"Separator","group":"Layout","signature":"Separator(orientation?: \"horizontal\" | \"vertical\", decorative?: boolean)","description":"Visual divider between content sections","tier":"core","pack":"core","examples":[]},{"name":"TagBlock","group":"Data","signature":"TagBlock(tags: string[], size?: \"sm\" | \"md\" | \"lg\")","description":"tags is an array of strings; optional size sm | md | lg","tier":"extended","pack":"core","examples":[]},{"name":"Tag","group":"Data","signature":"Tag(text: string, icon?: Icon, size?: \"sm\" | \"md\" | \"lg\", variant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\")","description":"Styled tag/badge with optional Icon and variant","tier":"core","pack":"core","examples":[]},{"name":"Icon","group":"Data","signature":"Icon(name: string, category?: string)","description":"A lucide icon by kebab-case name (e.g. 'circle-check'). Optional category picks a topical fallback when the name doesn't resolve.","tier":"core","pack":"core","examples":[]},{"name":"EntityList","group":"Data","signature":"EntityList(rows?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}[], size?: \"small\" | \"default\", header?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}, footer?: {left: string, right: string, rightVariant?: \"text\" | \"number\"})","description":"Two-column key/value rows (left label, right value). size 'default' supports optional header and footer rows; rightVariant 'number' uses tabular numbers.","tier":"core","pack":"core","examples":[]},{"name":"Text","group":"Data","signature":"Text(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Plain text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"core","pack":"core","examples":[]},{"name":"BoldText","group":"Data","signature":"BoldText(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Emphasized (bold) text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"extended","pack":"core","examples":[]},{"name":"IconText","group":"Data","signature":"IconText(icon: Icon, iconVariant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\" | \"inverted\" | \"filled\" | \"soft\", iconSize?: \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"sm\" | \"md\" | \"lg\", title: string, subtitle?: string, bold?: boolean, layout?: \"horizontal\" | \"vertical\")","description":"An icon badge with a title and optional subtitle, laid out horizontally or vertically. iconVariant sets the badge color.","tier":"extended","pack":"core","examples":[]},{"name":"MetricIndicatorInline","group":"Data","signature":"MetricIndicatorInline(value: string, subtext?: string, trend?: {direction: \"up\" | \"down\", value: number})","description":"Headline metric value with an optional +/- percentage trend and subtext, all on one line.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardItem","group":"Data","signature":"OverviewCardItem(id?: string, top: IconText | Text, bottom?: MetricIndicatorInline)","description":"One overview card: a heading slot at the top (IconText, ImageText or Text) and an optional MetricIndicatorInline at the bottom.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardBlock","group":"Data","signature":"OverviewCardBlock(items: OverviewCardItem[], layout?: \"grid\" | \"carousel\", responsive?: boolean, action?: ActionExpression, gap?: number | string)","description":"A grid or horizontal carousel of compact overview cards, each with a heading (icon/image/text) on top and an inline metric below; optionally clickable with a shared action.","tier":"core","pack":"core","examples":[]},{"name":"Architecture","group":"Content","signature":"Architecture(nodes: Node[], edges: Edge[], groups?: Group[], direction?: \"auto\" | \"right\" | \"down\" | \"lanes\")","description":"System diagram laid out automatically: write no coordinates. Every Node, Edge and Group is its own statement and click target. direction auto (default) runs right when that fits the page legibly, else down; lanes makes each top-level Group a column.","tier":"core","pack":"core","examples":["platformMap = Architecture([browser, edge, api, sessions, cache], [toEdge, toApi, readSessions, readCache], [vpc])\nbrowser = Node(\"Browser\", \"external\")\nedge = Node(\"Edge worker\", \"cloud\", \"Cloudflare\")\napi = Node(\"API\", \"backend\", \"Node :8080\")\nsessions = Node(\"Sessions\", \"database\", \"Postgres\", \"new\")\ncache = Node(\"Redis\", \"database\", \"read-through\")\ntoEdge = Edge(browser, edge, \"HTTPS\", \"emphasis\")\ntoApi = Edge(edge, api)\nreadSessions = Edge(api, sessions, \"SQL\")\nreadCache = Edge(api, cache, \"cache\", \"dashed\")\nvpc = Group(\"Platform VPC\", [api, sessions, cache])","ownership = Architecture([web, ordersApi, ledger, ordersDb, billingJob], [placeOrder, writeOrder, readOrders, postLedger], [clients, ordersRepo, billingRepo], \"lanes\")\nweb = Node(\"Web app\", \"frontend\")\nordersApi = Node(\"Orders API\", \"backend\", \"Node :8080\")\nordersDb = Node(\"orders table\", \"database\", \"Postgres\", null, \"success\")\nbillingJob = Node(\"Nightly billing\", \"backend\", \"cron 02:00\")\nledger = Node(\"Ledger\", \"database\", \"append-only\", null, \"info\")\nplaceOrder = Edge(web, ordersApi, \"POST /orders\")\nwriteOrder = Edge(ordersApi, ordersDb, \"insert\")\nreadOrders = Edge(billingJob, ordersDb, \"read unbilled\", null, \"danger\")\npostLedger = Edge(billingJob, ledger, \"post\")\nclients = Group(\"CLIENTS\", [web], \"plain\")\nordersRepo = Group(\"orders-service\", [ordersApi, ordersDb])\nbillingRepo = Group(\"billing\", [billingJob, ledger])"]},{"name":"Node","group":"Content","signature":"Node(label: string, kind?: \"frontend\" | \"backend\" | \"database\" | \"messagebus\" | \"cloud\" | \"security\" | \"external\", detail?: string, tag?: string, tone?: \"info\" | \"success\" | \"warning\" | \"danger\" | \"strong\", row?: number)","description":"A box in an Architecture. kind sets icon and color (default backend); detail is monospace, up to 2 lines (\\n breaks); tag is a small chip such as \"new\"; tone tints it (danger = risk, strong = where it ends); row pins it to a row (1, 2, ...) in lanes.","tier":"core","pack":"core","examples":[]},{"name":"Edge","group":"Content","signature":"Edge(from: Node, to: Node, label?: string, style?: \"solid\" | \"dashed\" | \"emphasis\" | \"security\", tone?: \"info\" | \"success\" | \"warning\" | \"danger\")","description":"An arrow between two Node statements (pass their names, not strings). style: dashed for async, emphasis for the main path, security for auth or trust flows. tone colors it, e.g. danger for a risky write, success for a read.","tier":"core","pack":"core","examples":[]},{"name":"Group","group":"Content","signature":"Group(label: string, members: Node[], kind?: \"region\" | \"security\" | \"plain\")","description":"A boundary around Node statements (a VPC, a cluster, a trust zone); members still sit where their edges put them. A group whose members are a subset of another's is drawn inside it; kind security draws it red, plain draws only its name.","tier":"core","pack":"core","examples":[]},{"name":"Diff","group":"Content","signature":"Diff(patch: string, title?: string)","description":"Unified diff (git diff output, one or more files) with line numbers. patch is a raw multi-line string. Clicked lines reach the agent as file:+line.","tier":"core","pack":"core","examples":["retryPatch = Diff(\"diff --git a/src/retry.ts b/src/retry.ts\n--- a/src/retry.ts\n+++ b/src/retry.ts\n@@ -10,2 +10,3 @@\n- for (let i = 0; i < 3; i++) {\n+ for (let i = 0; i < MAX_ATTEMPTS; i++) {\n+ if (i > 0) await sleep(backoff(i));\")"]},{"name":"FileTree","group":"Data","signature":"FileTree(entries: string[])","description":"File tree, one path per entry, optionally prefixed with a git status code (A new, M modified, D deleted, R old -> new) and suffixed with \" # note\". Folders are inferred. Clicked files and folders reach the agent by path.","tier":"core","pack":"core","examples":["changedFiles = FileTree([\"M src/session/store.ts # read-through to Postgres\", \"A src/session/pg-store.ts\", \"A migrations/0042_sessions.sql # table + expiry index\", \"R src/session/redis.ts -> src/session/redis-cache.ts\", \"D src/session/legacy.ts\", \"M src/config.ts # SESSIONS_PG flag\"])"]},{"name":"Mermaid","group":"Content","signature":"Mermaid(source: string, caption?: string)","description":"Diagram from Mermaid source (flowchart, sequence, state, class, ER, gantt). source is a raw multi-line string. Keep node labels to a few words; clicked nodes reach the agent by label.","tier":"core","pack":"@austere-axi/pack-mermaid","examples":["requestPath = Mermaid(\"flowchart LR\n client[Client] --> api[API]\n api --> pg[(Postgres)]\", \"Request path\")"]},{"name":"Progress","group":"Data","signature":"Progress(value: number, label?: string, max?: number)","description":"Progress bar. value counts toward max (default 100); label names what is progressing. Shows a percentage.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["migration = Progress(64, \"Rows migrated\")"]},{"name":"Avatar","group":"Data","signature":"Avatar(name: string, src?: string, size?: \"md\" | \"lg\")","description":"Round avatar for a person. Shows the initials of name; src is an optional https image URL.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["owner = Avatar(\"Ada Lovelace\")"]},{"name":"AvatarGroup","group":"Data","signature":"AvatarGroup(avatars: Avatar[], max?: number)","description":"Overlapping Avatars, for reviewers or owners. max (default 4) shows the rest as +N.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["team = AvatarGroup([ada, grace, linus], 2)\nada = Avatar(\"Ada Lovelace\")\ngrace = Avatar(\"Grace Hopper\")\nlinus = Avatar(\"Linus Torvalds\")"]},{"name":"Tooltip","group":"Content","signature":"Tooltip(text: string, tip: string)","description":"Inline text with a short hint on hover or focus (a term and its definition). Keep tip to one line.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["term = Tooltip(\"idempotent\", \"Safe to run twice with the same result.\")"]},{"name":"HoverCard","group":"Content","signature":"HoverCard(text: string, title: string, body?: string)","description":"Inline text that shows a small card (title and a line or two of body) on hover or focus, e.g. a person or a service.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["svc = HoverCard(\"billing-api\", \"billing-api\", \"Owns invoices and refunds. On call: payments team.\")"]},{"name":"Breadcrumb","group":"Content","signature":"Breadcrumb(items: BreadcrumbItem[])","description":"Path trail such as Repo / Package / File, from BreadcrumbItems. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["trail = Breadcrumb([repo, pkg, file])\nrepo = BreadcrumbItem(\"austere-axi\")\npkg = BreadcrumbItem(\"packs\")\nfile = BreadcrumbItem(\"shadcn\")"]},{"name":"BreadcrumbItem","group":"Content","signature":"BreadcrumbItem(label: string, href?: string)","description":"One step of a Breadcrumb. href is optional; the last item is the current page.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]},{"name":"Kbd","group":"Content","signature":"Kbd(keys: string)","description":"Keyboard shortcut. keys joins keys with \"+\", e.g. \"Ctrl+K\"; each key is drawn as a key cap.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["shortcut = Kbd(\"Ctrl+Shift+P\")"]},{"name":"Skeleton","group":"Content","signature":"Skeleton(lines?: number, shape?: \"lines\" | \"avatar\")","description":"Grey placeholder block for content not yet written or loaded (default 3 lines). shape avatar adds a round head.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["pending = Skeleton(3, \"avatar\")"]},{"name":"Calendar","group":"Data","signature":"Calendar(month: string, highlights?: string[])","description":"Read-only month grid (not a date picker). month is \"YYYY-MM\"; highlights lists \"YYYY-MM-DD\" or \"YYYY-MM-DD label\" entries to mark, e.g. a freeze or a release. Marked days are click targets.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["window = Calendar(\"2026-11\", [\"2026-11-04 Code freeze\", \"2026-11-12 Release\", \"2026-11-13\"])"]},{"name":"Timeline","group":"Content","signature":"Timeline(items: TimelineItem[])","description":"Vertical history or schedule of TimelineItems, oldest first. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["history = Timeline([found, fixed, rollout])\nfound = TimelineItem(\"Bug found\", \"Oct 28\", \"Retries double-charge on timeout.\")\nfixed = TimelineItem(\"Fix merged\", \"Oct 29\", \"Idempotency key added.\", \"current\")\nrollout = TimelineItem(\"Rollout\", \"Nov 3\", \"\", \"upcoming\")"]},{"name":"TimelineItem","group":"Content","signature":"TimelineItem(title: string, date?: string, detail?: string, state?: \"done\" | \"current\" | \"upcoming\")","description":"One event in a Timeline. state: done (default), current or upcoming.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]}]}
1
+ {"format":1,"pack":"core","version":"0.5.2","groups":[{"name":"Layout","notes":["- A page is root = Stack([...sections], \"column\", \"l\"). Each section is a Card([CardHeader(title, subtitle), ...content]).","- Side by side: Stack([a, b], \"row\", \"m\", \"stretch\", \"start\", true). Cards in a row share the width.","- Tabs for alternative views, Accordion for detail the reader may skip, Steps for ordered procedures. Several blocks in one Card: label each with InlineHeader.","- Card children must match its list; anything else (Accordion, Tag, a Callout row) can sit in a Stack inside the Card, or directly in root's Stack."]},{"name":"Content","notes":["- MarkDownRenderer for prose, bullet lists and inline code; one statement per paragraph group so each is annotatable.","- Callout for the one thing the reader must not miss: a risk, a decision needed, a caveat.","- CodeBlock for a whole snippet. Image src may be a path relative to the artifact file.","- Architecture for systems: services, stores, queues, trust boundaries. Name every Node, Edge and Group. Over ~15 nodes, use lanes or split the diagram.","- lanes: for systems split by owner, repo or tier. Groups are the columns, left to right as listed; put every node in one, in reading order (plain groups for a heading without a frame). About 4 columns fit a laptop; wider ones scroll (check warns). Phones get the lanes stacked as bands. Pin row only to put boxes side by side or line them up.","- Diff for code changes: show the hunks that matter, not whole files. Hunk counts are recomputed, so only start lines matter.","- Mermaid for sequences, state machines, ER, gantt and quick flowcharts."]},{"name":"Data","notes":["- Table is COLUMN-oriented: Table([Col(\"Option\", names), Col(\"Effort\", efforts), Col(\"Risk\", risks)]). Col data may mix strings and components, e.g. [\"n/a\", Tag(\"high\", null, \"sm\", \"danger\")]. Cells are plain text (no markdown) and wrap; Col type \"number\" for figures.","- EntityList for key/value facts; OverviewCardBlock + OverviewCardItem + MetricIndicatorInline for a KPI row.","- EditableTable when the reviewer should correct or fill in values; edits arrive with the next @ToAssistant from its Form.","- Icon names are lucide kebab-case (circle-check, triangle-alert, git-branch)."]},{"name":"Charts","notes":["- Charts take column arrays: BarChart(labels, [Series(\"p50\", [..]), Series(\"p99\", [..])], \"grouped\", \"Week\", \"ms\").","- PieChart(labels, values) with plain number arrays."]},{"name":"Forms","notes":["- Form(name, buttons, fields). Define each FormControl as its own statement. Never nest a Form in a Form.","- To ask the reviewer to decide: OptionCards or RadioGroup inside a Form whose button is Button(\"Send decision\", @ToAssistant(\"Decision: rollout strategy\"), \"primary\"). The values arrive with that message as form: {<formName>: {<fieldName>: value}}; no context needed. Reviewers can always annotate and message, so add a notes field only when it belongs with the decision.","- OptionCards(\"pick\", \"single\", [OptionCard(\"a\", \"Option A\", \"why\", Icon(\"zap\")), OptionCard(\"b\", \"Option B\")], {required: true}, \"a\") — the last argument pre-selects.","- rules: {required: true, minLength: 10, ...}; the renderer shows the messages."]},{"name":"Buttons","notes":["- @ToAssistant(message, context?) queues a message to the agent; @OpenUrl(url) opens a link. A Button with no action sends its label.","- Use buttons for follow-ups the reviewer can pick in one click: \"Go with option B\", \"Split this into two PRs\"."]}],"components":[{"name":"Card","group":"Layout","signature":"Card(children: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Tabs | Stack | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[], variant?: \"card\" | \"sunk\" | \"clear\", direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Styled container. variant: \"card\" (default, elevated) | \"sunk\" (recessed) | \"clear\" (transparent). Always full width. Accepts all Stack flex params (default: direction \"column\"). Cards flex to share space in row/wrap layouts.","tier":"core","pack":"core","examples":[]},{"name":"CardHeader","group":"Layout","signature":"CardHeader(title?: string, subtitle?: string)","description":"Header with optional title and subtitle","tier":"core","pack":"core","examples":[]},{"name":"TextContent","group":"Content","signature":"TextContent(text: string, size?: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\")","description":"Text block. Supports markdown. Optional size: \"small\" | \"default\" | \"large\" | \"small-heavy\" | \"large-heavy\".","tier":"core","pack":"core","examples":[]},{"name":"MarkDownRenderer","group":"Content","signature":"MarkDownRenderer(textMarkdown: string, variant?: \"clear\" | \"card\" | \"sunk\")","description":"Renders markdown text with optional container variant","tier":"core","pack":"core","examples":[]},{"name":"Callout","group":"Content","signature":"Callout(variant: \"info\" | \"warning\" | \"error\" | \"success\" | \"neutral\", title: string, description: string, visible?: $binding<boolean>)","description":"Callout banner. Optional visible is a reactive $boolean — auto-dismisses after 3s by setting $visible to false.","tier":"core","pack":"core","examples":[]},{"name":"Image","group":"Content","signature":"Image(alt: string, src?: string)","description":"Image with alt text and optional URL","tier":"core","pack":"core","examples":[]},{"name":"CodeBlock","group":"Content","signature":"CodeBlock(language: string, codeString: string)","description":"Syntax-highlighted code block","tier":"core","pack":"core","examples":[]},{"name":"InlineHeader","group":"Layout","signature":"InlineHeader(heading: string, description?: string)","description":"Compact section heading with an optional one-line description, for use inside cards.","tier":"core","pack":"core","examples":[]},{"name":"Table","group":"Data","signature":"Table(columns: Col[])","description":"Data table — column-oriented. Each Col holds its own data array.","tier":"core","pack":"core","examples":[]},{"name":"Col","group":"Data","signature":"Col(label: string, data: any, type?: \"string\" | \"number\" | \"action\")","description":"Column definition — holds label + data array","tier":"core","pack":"core","examples":[]},{"name":"EditableTable","group":"Data","signature":"EditableTable(name?: string, columns?: {type: \"text\" | \"number\" | \"date-single\" | \"select\" | \"url\", key?: string, header?: string, width?: number, options?: {value: string, label: string}[]}[], data?: {id: string, values: (string | number)[]}[])","description":"Spreadsheet-like table whose cells the user can edit inline (text, number, url, date, select columns); edits are saved back as a form field","tier":"extended","pack":"core","examples":[]},{"name":"BarChart","group":"Charts","signature":"BarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string, height?: number)","description":"Vertical bars; use for comparing values across categories with one or more series","tier":"core","pack":"core","examples":[]},{"name":"LineChart","group":"Charts","signature":"LineChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Lines over categories; use for trends and continuous data over time","tier":"core","pack":"core","examples":[]},{"name":"AreaChart","group":"Charts","signature":"AreaChart(labels: string[], series: Series[], variant?: \"linear\" | \"natural\" | \"step\", xLabel?: string, yLabel?: string, height?: number)","description":"Filled area under lines; use for cumulative totals or volume trends over time","tier":"extended","pack":"core","examples":[]},{"name":"HorizontalBarChart","group":"Charts","signature":"HorizontalBarChart(labels: string[], series: Series[], variant?: \"grouped\" | \"stacked\", xLabel?: string, yLabel?: string)","description":"Horizontal bars; prefer when category labels are long or for ranked lists","tier":"extended","pack":"core","examples":[]},{"name":"Series","group":"Charts","signature":"Series(category: string, values: number[])","description":"One data series","tier":"core","pack":"core","examples":[]},{"name":"PieChart","group":"Charts","signature":"PieChart(labels: string[], values: number[], variant?: \"pie\" | \"donut\", appearance?: \"circular\" | \"semiCircular\")","description":"Circular slices; use plucked arrays: PieChart(data.categories, data.values)","tier":"core","pack":"core","examples":[]},{"name":"ScatterChart","group":"Charts","signature":"ScatterChart(datasets: ScatterSeries[], xLabel?: string, yLabel?: string)","description":"X/Y scatter plot; use for correlations, distributions, and clustering","tier":"extended","pack":"core","examples":[]},{"name":"ScatterSeries","group":"Charts","signature":"ScatterSeries(name: string, points: Point[])","description":"Named dataset","tier":"extended","pack":"core","examples":[]},{"name":"Point","group":"Charts","signature":"Point(x: number, y: number, z?: number)","description":"Data point with numeric coordinates","tier":"extended","pack":"core","examples":[]},{"name":"Form","group":"Forms","signature":"Form(name: string, buttons: Buttons, fields?: FormControl[])","description":"Form container with fields and explicit action buttons","tier":"core","pack":"core","examples":[]},{"name":"FormControl","group":"Forms","signature":"FormControl(label: string, input: Input | TextArea | Select | Slider | CheckBoxGroup | RadioGroup | Chips | OptionCards, hint?: string)","description":"Field with label, input component, and optional hint text","tier":"core","pack":"core","examples":[]},{"name":"Label","group":"Forms","signature":"Label(text: string)","description":"Text label","tier":"extended","pack":"core","examples":[]},{"name":"Input","group":"Forms","signature":"Input(name: string, placeholder?: string, type?: \"text\" | \"email\" | \"password\" | \"number\" | \"url\", rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"TextArea","group":"Forms","signature":"TextArea(name: string, placeholder?: string, rows?: number, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"Select","group":"Forms","signature":"Select(name: string, items: SelectItem[], placeholder?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>, size?: \"small\" | \"medium\" | \"large\")","description":"","tier":"core","pack":"core","examples":[]},{"name":"SelectItem","group":"Forms","signature":"SelectItem(value: string, label: string)","description":"Option for Select","tier":"core","pack":"core","examples":[]},{"name":"Slider","group":"Forms","signature":"Slider(name: string, variant: \"continuous\" | \"discrete\", min: number, max: number, step?: number, defaultValue?: number[], label?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<number[]>)","description":"Numeric slider input; supports continuous and discrete (stepped) variants","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxGroup","group":"Forms","signature":"CheckBoxGroup(name: string, items: CheckBoxItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<Record<string, boolean>>)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"CheckBoxItem","group":"Forms","signature":"CheckBoxItem(label: string, description: string, name: string, defaultChecked?: boolean)","description":"","tier":"extended","pack":"core","examples":[]},{"name":"RadioGroup","group":"Forms","signature":"RadioGroup(name: string, items: RadioItem[], defaultValue?: string, rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, value?: $binding<string>)","description":"","tier":"core","pack":"core","examples":[]},{"name":"RadioItem","group":"Forms","signature":"RadioItem(label: string, description: string, value: string)","description":"","tier":"core","pack":"core","examples":[]},{"name":"SwitchGroup","group":"Forms","signature":"SwitchGroup(name: string, items: SwitchItem[], variant?: \"clear\" | \"card\" | \"sunk\", value?: $binding<Record<string, boolean>>)","description":"Group of switch toggles","tier":"extended","pack":"core","examples":[]},{"name":"SwitchItem","group":"Forms","signature":"SwitchItem(label?: string, description?: string, name: string, defaultChecked?: boolean)","description":"Individual switch toggle","tier":"extended","pack":"core","examples":[]},{"name":"ChipItem","group":"Forms","signature":"ChipItem(value: string, label: string, icon?: Icon, disabled?: boolean)","description":"A single selectable chip inside a Chips group, with a value, label and optional icon.","tier":"extended","pack":"core","examples":[]},{"name":"Chips","group":"Forms","signature":"Chips(name: string, type?: \"single\" | \"multiple\", items?: ChipItem[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of compact selectable chips for choosing one or many short options; the selection is stored under `name`.","tier":"extended","pack":"core","examples":[]},{"name":"OptionCard","group":"Forms","signature":"OptionCard(value: string, title: string, subtitle?: string, topContent?: Icon | Image, disabled?: boolean)","description":"A single selectable card inside an OptionCards group, with a value, title, optional subtitle and an optional Icon or Image on top.","tier":"core","pack":"core","examples":[]},{"name":"OptionCards","group":"Forms","signature":"OptionCards(name: string, type?: \"single\" | \"multiple\", items?: OptionCard[], rules?: {required?: boolean, email?: boolean, url?: boolean, numeric?: boolean, min?: number, max?: number, minLength?: number, maxLength?: number, pattern?: string}, defaultValue?: string | string[])","description":"A form field of selectable cards (title, optional subtitle, optional icon or image) laid out in a responsive grid for choosing one or many options; the selection is stored under `name`.","tier":"core","pack":"core","examples":[]},{"name":"Button","group":"Buttons","signature":"Button(label: string, action?: ActionExpression, variant?: \"primary\" | \"secondary\" | \"tertiary\", type?: \"normal\" | \"destructive\", size?: \"extra-small\" | \"small\" | \"medium\" | \"large\")","description":"Clickable button","tier":"core","pack":"core","examples":[]},{"name":"Buttons","group":"Buttons","signature":"Buttons(buttons: Button[], direction?: \"row\" | \"column\")","description":"Group of Button components. direction: \"row\" (default) | \"column\".","tier":"core","pack":"core","examples":[]},{"name":"Stack","group":"Layout","signature":"Stack(children: any[], direction?: \"row\" | \"column\", gap?: \"none\" | \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"2xl\", align?: \"start\" | \"center\" | \"end\" | \"stretch\" | \"baseline\", justify?: \"start\" | \"center\" | \"end\" | \"between\" | \"around\" | \"evenly\", wrap?: boolean)","description":"Flex container. direction: \"row\"|\"column\" (default \"column\"). gap: \"none\"|\"xs\"|\"s\"|\"m\"|\"l\"|\"xl\"|\"2xl\" (default \"m\"). align: \"start\"|\"center\"|\"end\"|\"stretch\"|\"baseline\". justify: \"start\"|\"center\"|\"end\"|\"between\"|\"around\"|\"evenly\".","tier":"core","pack":"core","examples":[]},{"name":"Tabs","group":"Layout","signature":"Tabs(items: TabItem[])","description":"Tabbed container","tier":"core","pack":"core","examples":[]},{"name":"TabItem","group":"Layout","signature":"TabItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is tab label, content is array of components","tier":"core","pack":"core","examples":[]},{"name":"Accordion","group":"Layout","signature":"Accordion(items: AccordionItem[])","description":"Collapsible sections","tier":"core","pack":"core","examples":[]},{"name":"AccordionItem","group":"Layout","signature":"AccordionItem(value: string, trigger: string, content: (TextContent | MarkDownRenderer | CardHeader | Callout | CodeBlock | Image | Separator | HorizontalBarChart | PieChart | ScatterChart | AreaChart | BarChart | LineChart | Table | TagBlock | Form | Buttons | Steps | InlineHeader | EntityList | EditableTable | OverviewCardBlock | Architecture | Diff | FileTree | Mermaid | Progress | Avatar | AvatarGroup | Tooltip | HoverCard | Breadcrumb | Kbd | Skeleton | Calendar | Timeline)[])","description":"value is unique id, trigger is section title","tier":"core","pack":"core","examples":[]},{"name":"Steps","group":"Layout","signature":"Steps(items: StepsItem[])","description":"Step-by-step guide","tier":"core","pack":"core","examples":[]},{"name":"StepsItem","group":"Layout","signature":"StepsItem(title: string, details: string)","description":"title and details text for one step","tier":"core","pack":"core","examples":[]},{"name":"Separator","group":"Layout","signature":"Separator(orientation?: \"horizontal\" | \"vertical\", decorative?: boolean)","description":"Visual divider between content sections","tier":"core","pack":"core","examples":[]},{"name":"TagBlock","group":"Data","signature":"TagBlock(tags: string[], size?: \"sm\" | \"md\" | \"lg\")","description":"tags is an array of strings; optional size sm | md | lg","tier":"extended","pack":"core","examples":[]},{"name":"Tag","group":"Data","signature":"Tag(text: string, icon?: Icon, size?: \"sm\" | \"md\" | \"lg\", variant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\")","description":"Styled tag/badge with optional Icon and variant","tier":"core","pack":"core","examples":[]},{"name":"Icon","group":"Data","signature":"Icon(name: string, category?: string)","description":"A lucide icon by kebab-case name (e.g. 'circle-check'). Optional category picks a topical fallback when the name doesn't resolve.","tier":"core","pack":"core","examples":[]},{"name":"EntityList","group":"Data","signature":"EntityList(rows?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}[], size?: \"small\" | \"default\", header?: {left: string, right: string, rightVariant?: \"text\" | \"number\"}, footer?: {left: string, right: string, rightVariant?: \"text\" | \"number\"})","description":"Two-column key/value rows (left label, right value). size 'default' supports optional header and footer rows; rightVariant 'number' uses tabular numbers.","tier":"core","pack":"core","examples":[]},{"name":"Text","group":"Data","signature":"Text(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Plain text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"core","pack":"core","examples":[]},{"name":"BoldText","group":"Data","signature":"BoldText(variant?: \"text\" | \"number\", value: string, subtext?: string, subtextVariant?: \"text\" | \"number\" | \"metric\", size?: \"xs\" | \"sm\" | \"md\" | \"lg\")","description":"Emphasized (bold) text line with optional subtext. variant 'number' uses tabular number styling; subtextVariant 'metric' colors a leading +/- subtext green/red.","tier":"extended","pack":"core","examples":[]},{"name":"IconText","group":"Data","signature":"IconText(icon: Icon, iconVariant?: \"neutral\" | \"info\" | \"success\" | \"warning\" | \"danger\" | \"inverted\" | \"filled\" | \"soft\", iconSize?: \"xs\" | \"s\" | \"m\" | \"l\" | \"xl\" | \"sm\" | \"md\" | \"lg\", title: string, subtitle?: string, bold?: boolean, layout?: \"horizontal\" | \"vertical\")","description":"An icon badge with a title and optional subtitle, laid out horizontally or vertically. iconVariant sets the badge color.","tier":"extended","pack":"core","examples":[]},{"name":"MetricIndicatorInline","group":"Data","signature":"MetricIndicatorInline(value: string, subtext?: string, trend?: {direction: \"up\" | \"down\", value: number})","description":"Headline metric value with an optional +/- percentage trend and subtext, all on one line.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardItem","group":"Data","signature":"OverviewCardItem(id?: string, top: IconText | Text, bottom?: MetricIndicatorInline)","description":"One overview card: a heading slot at the top (IconText, ImageText or Text) and an optional MetricIndicatorInline at the bottom.","tier":"core","pack":"core","examples":[]},{"name":"OverviewCardBlock","group":"Data","signature":"OverviewCardBlock(items: OverviewCardItem[], layout?: \"grid\" | \"carousel\", responsive?: boolean, action?: ActionExpression, gap?: number | string)","description":"A grid or horizontal carousel of compact overview cards, each with a heading (icon/image/text) on top and an inline metric below; optionally clickable with a shared action.","tier":"core","pack":"core","examples":[]},{"name":"Architecture","group":"Content","signature":"Architecture(nodes: Node[], edges: Edge[], groups?: Group[], direction?: \"auto\" | \"right\" | \"down\" | \"lanes\")","description":"System diagram laid out automatically: write no coordinates. Every Node, Edge and Group is its own statement and click target. direction auto (default) runs right when that fits the page legibly, else down; lanes makes each top-level Group a column.","tier":"core","pack":"core","examples":["platformMap = Architecture([browser, edge, api, sessions, cache], [toEdge, toApi, readSessions, readCache], [vpc])\nbrowser = Node(\"Browser\", \"external\")\nedge = Node(\"Edge worker\", \"cloud\", \"Cloudflare\")\napi = Node(\"API\", \"backend\", \"Node :8080\")\nsessions = Node(\"Sessions\", \"database\", \"Postgres\", \"new\")\ncache = Node(\"Redis\", \"database\", \"read-through\")\ntoEdge = Edge(browser, edge, \"HTTPS\", \"emphasis\")\ntoApi = Edge(edge, api)\nreadSessions = Edge(api, sessions, \"SQL\")\nreadCache = Edge(api, cache, \"cache\", \"dashed\")\nvpc = Group(\"Platform VPC\", [api, sessions, cache])","ownership = Architecture([web, ordersApi, ledger, ordersDb, billingJob], [placeOrder, writeOrder, readOrders, postLedger], [clients, ordersRepo, billingRepo], \"lanes\")\nweb = Node(\"Web app\", \"frontend\")\nordersApi = Node(\"Orders API\", \"backend\", \"Node :8080\")\nordersDb = Node(\"orders table\", \"database\", \"Postgres\", null, \"success\")\nbillingJob = Node(\"Nightly billing\", \"backend\", \"cron 02:00\")\nledger = Node(\"Ledger\", \"database\", \"append-only\", null, \"info\")\nplaceOrder = Edge(web, ordersApi, \"POST /orders\")\nwriteOrder = Edge(ordersApi, ordersDb, \"insert\")\nreadOrders = Edge(billingJob, ordersDb, \"read unbilled\", null, \"danger\")\npostLedger = Edge(billingJob, ledger, \"post\")\nclients = Group(\"CLIENTS\", [web], \"plain\")\nordersRepo = Group(\"orders-service\", [ordersApi, ordersDb])\nbillingRepo = Group(\"billing\", [billingJob, ledger])"]},{"name":"Node","group":"Content","signature":"Node(label: string, kind?: \"frontend\" | \"backend\" | \"database\" | \"messagebus\" | \"cloud\" | \"security\" | \"external\", detail?: string, tag?: string, tone?: \"info\" | \"success\" | \"warning\" | \"danger\" | \"strong\", row?: number)","description":"A box in an Architecture. kind sets icon and color (default backend); detail is monospace, up to 2 lines (\\n breaks); tag is a small chip such as \"new\"; tone tints it (danger = risk, strong = where it ends); row pins it to a row (1, 2, ...) in lanes.","tier":"core","pack":"core","examples":[]},{"name":"Edge","group":"Content","signature":"Edge(from: Node, to: Node, label?: string, style?: \"solid\" | \"dashed\" | \"emphasis\" | \"security\", tone?: \"info\" | \"success\" | \"warning\" | \"danger\")","description":"An arrow between two Node statements (pass their names, not strings). style: dashed for async, emphasis for the main path, security for auth or trust flows. tone colors it, e.g. danger for a risky write, success for a read.","tier":"core","pack":"core","examples":[]},{"name":"Group","group":"Content","signature":"Group(label: string, members: Node[], kind?: \"region\" | \"security\" | \"plain\")","description":"A boundary around Node statements (a VPC, a cluster, a trust zone); members still sit where their edges put them. A group whose members are a subset of another's is drawn inside it; kind security draws it red, plain draws only its name.","tier":"core","pack":"core","examples":[]},{"name":"Diff","group":"Content","signature":"Diff(patch: string, title?: string)","description":"Unified diff (git diff output, one or more files) with line numbers. patch is a raw multi-line string. Clicked lines reach the agent as file:+line.","tier":"core","pack":"core","examples":["retryPatch = Diff(\"diff --git a/src/retry.ts b/src/retry.ts\n--- a/src/retry.ts\n+++ b/src/retry.ts\n@@ -10,2 +10,3 @@\n- for (let i = 0; i < 3; i++) {\n+ for (let i = 0; i < MAX_ATTEMPTS; i++) {\n+ if (i > 0) await sleep(backoff(i));\")"]},{"name":"FileTree","group":"Data","signature":"FileTree(entries: string[])","description":"File tree, one path per entry, optionally prefixed with a git status code (A new, M modified, D deleted, R old -> new) and suffixed with \" # note\". Folders are inferred. Clicked files and folders reach the agent by path.","tier":"core","pack":"core","examples":["changedFiles = FileTree([\"M src/session/store.ts # read-through to Postgres\", \"A src/session/pg-store.ts\", \"A migrations/0042_sessions.sql # table + expiry index\", \"R src/session/redis.ts -> src/session/redis-cache.ts\", \"D src/session/legacy.ts\", \"M src/config.ts # SESSIONS_PG flag\"])"]},{"name":"Mermaid","group":"Content","signature":"Mermaid(source: string, caption?: string)","description":"Diagram from Mermaid source (flowchart, sequence, state, class, ER, gantt). source is a raw multi-line string. Keep node labels to a few words; clicked nodes reach the agent by label.","tier":"core","pack":"@austere-axi/pack-mermaid","examples":["requestPath = Mermaid(\"flowchart LR\n client[Client] --> api[API]\n api --> pg[(Postgres)]\", \"Request path\")"]},{"name":"Progress","group":"Data","signature":"Progress(value: number, label?: string, max?: number)","description":"Progress bar. value counts toward max (default 100); label names what is progressing. Shows a percentage.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["migration = Progress(64, \"Rows migrated\")"]},{"name":"Avatar","group":"Data","signature":"Avatar(name: string, src?: string, size?: \"md\" | \"lg\")","description":"Round avatar for a person. Shows the initials of name; src is an optional https image URL.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["owner = Avatar(\"Ada Lovelace\")"]},{"name":"AvatarGroup","group":"Data","signature":"AvatarGroup(avatars: Avatar[], max?: number)","description":"Overlapping Avatars, for reviewers or owners. max (default 4) shows the rest as +N.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["team = AvatarGroup([ada, grace, linus], 2)\nada = Avatar(\"Ada Lovelace\")\ngrace = Avatar(\"Grace Hopper\")\nlinus = Avatar(\"Linus Torvalds\")"]},{"name":"Tooltip","group":"Content","signature":"Tooltip(text: string, tip: string)","description":"Inline text with a short hint on hover or focus (a term and its definition). Keep tip to one line.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["term = Tooltip(\"idempotent\", \"Safe to run twice with the same result.\")"]},{"name":"HoverCard","group":"Content","signature":"HoverCard(text: string, title: string, body?: string)","description":"Inline text that shows a small card (title and a line or two of body) on hover or focus, e.g. a person or a service.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["svc = HoverCard(\"billing-api\", \"billing-api\", \"Owns invoices and refunds. On call: payments team.\")"]},{"name":"Breadcrumb","group":"Content","signature":"Breadcrumb(items: BreadcrumbItem[])","description":"Path trail such as Repo / Package / File, from BreadcrumbItems. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["trail = Breadcrumb([repo, pkg, file])\nrepo = BreadcrumbItem(\"austere-axi\")\npkg = BreadcrumbItem(\"packs\")\nfile = BreadcrumbItem(\"shadcn\")"]},{"name":"BreadcrumbItem","group":"Content","signature":"BreadcrumbItem(label: string, href?: string)","description":"One step of a Breadcrumb. href is optional; the last item is the current page.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]},{"name":"Kbd","group":"Content","signature":"Kbd(keys: string)","description":"Keyboard shortcut. keys joins keys with \"+\", e.g. \"Ctrl+K\"; each key is drawn as a key cap.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["shortcut = Kbd(\"Ctrl+Shift+P\")"]},{"name":"Skeleton","group":"Content","signature":"Skeleton(lines?: number, shape?: \"lines\" | \"avatar\")","description":"Grey placeholder block for content not yet written or loaded (default 3 lines). shape avatar adds a round head.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["pending = Skeleton(3, \"avatar\")"]},{"name":"Calendar","group":"Data","signature":"Calendar(month: string, highlights?: string[])","description":"Read-only month grid (not a date picker). month is \"YYYY-MM\"; highlights lists \"YYYY-MM-DD\" or \"YYYY-MM-DD label\" entries to mark, e.g. a freeze or a release. Marked days are click targets.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["window = Calendar(\"2026-11\", [\"2026-11-04 Code freeze\", \"2026-11-12 Release\", \"2026-11-13\"])"]},{"name":"Timeline","group":"Content","signature":"Timeline(items: TimelineItem[])","description":"Vertical history or schedule of TimelineItems, oldest first. Each item is a click target.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":["history = Timeline([found, fixed, rollout])\nfound = TimelineItem(\"Bug found\", \"Oct 28\", \"Retries double-charge on timeout.\")\nfixed = TimelineItem(\"Fix merged\", \"Oct 29\", \"Idempotency key added.\", \"current\")\nrollout = TimelineItem(\"Rollout\", \"Nov 3\", \"\", \"upcoming\")"]},{"name":"TimelineItem","group":"Content","signature":"TimelineItem(title: string, date?: string, detail?: string, state?: \"done\" | \"current\" | \"upcoming\")","description":"One event in a Timeline. state: done (default), current or upcoming.","tier":"extended","pack":"@austere-axi/pack-shadcn","examples":[]}]}
package/dist/cli.mjs CHANGED
@@ -615,7 +615,7 @@ var init_paths = __esm({
615
615
  HERE = path.dirname(fileURLToPath(import.meta.url));
616
616
  BUNDLED = true;
617
617
  ASSET_DIR = BUNDLED ? HERE : path.resolve(HERE, "../../dist");
618
- VERSION = true ? "0.5.1" : JSON.parse(fs.readFileSync(path.resolve(HERE, "../../package.json"), "utf8")).version;
618
+ VERSION = true ? "0.5.2" : JSON.parse(fs.readFileSync(path.resolve(HERE, "../../package.json"), "utf8")).version;
619
619
  CLI_ENTRY = BUNDLED ? fileURLToPath(import.meta.url) : path.resolve(HERE, "main.js");
620
620
  securedDir = null;
621
621
  }
@@ -29882,8 +29882,8 @@ WHEN TO USE
29882
29882
  Plans, comparisons, decisions, reviews, reports: anything easier to grasp as a page.
29883
29883
  Not for a few sentences of prose.
29884
29884
 
29885
- COMMANDS
29886
- ${BIN2} sessions + this summary
29885
+ COMMANDS (\`${BIN2} <command> --help\` for one command's flags and an example)
29886
+ ${BIN2} sessions + command list
29887
29887
  ${BIN2} <file> open/resume a review [--no-open] [--reopen]
29888
29888
  ${BIN2} poll <file> long-poll for feedback [--cursor n] [--agent-reply "..."] [--agent-reply-file f|-]
29889
29889
  --cursor n acknowledges feedback up to n; --after n returns
@@ -29924,6 +29924,107 @@ ENV
29924
29924
  AUSTERE_AXI_IDLE_TIMEOUT_MS (1800000) AUSTERE_AXI_PERSIST=1 AUSTERE_AXI_ALLOWED_HOSTS AUSTERE_AXI_LINK_URL
29925
29925
  AUSTERE_AXI_NPM_REGISTRY
29926
29926
  `;
29927
+ var COMMANDS = {
29928
+ open: {
29929
+ usage: "<file> [--no-open] [--reopen]",
29930
+ summary: "validate the page and open it for review (`open <file>` is the same)",
29931
+ flags: { "no-open": "do not launch a browser; print the url (and a phone url when Tailscale is up)", reopen: "open a review the user ended or closed again" },
29932
+ example: "plan.html --no-open"
29933
+ },
29934
+ poll: {
29935
+ usage: "poll <file> [--cursor n] [--agent-reply text | --agent-reply-file path|-]",
29936
+ summary: "wait until the reviewer presses Send; returns the feedback batch and a cursor",
29937
+ flags: {
29938
+ cursor: "acknowledge feedback up to n (the cursor the last poll printed)",
29939
+ after: "return everything after n again",
29940
+ "agent-reply": "post a reply first: a small Markdown subset (paragraphs, - lists, `code`, **bold**, links), a line or two",
29941
+ "agent-reply-file": "read the reply from a file, or - for stdin",
29942
+ takeover: "replace a poll that is already listening on this review",
29943
+ "timeout-ms": "return status: timeout after n ms (default: wait for feedback)",
29944
+ owner: "label for this listener, shown to a second poll"
29945
+ },
29946
+ example: 'poll plan.html --cursor 3 --agent-reply "Switched the rollout to dual-write."'
29947
+ },
29948
+ reply: {
29949
+ usage: "reply <file> --agent-reply text | --agent-reply-file path|- [--cursor n]",
29950
+ summary: "post a reply without polling; acknowledges feedback up to the cursor",
29951
+ flags: {
29952
+ "agent-reply": "reply text: a small Markdown subset, a line or two",
29953
+ "agent-reply-file": "read the reply from a file, or - for stdin",
29954
+ cursor: "acknowledge feedback up to n"
29955
+ },
29956
+ example: 'reply plan.html --cursor 3 --agent-reply "Done."'
29957
+ },
29958
+ end: { usage: "end <file>", summary: "stop as the agent; the reviewer can still add notes for later", flags: {}, example: "end plan.html" },
29959
+ close: { usage: "close <file>", summary: "close the review: no more notes until reopen", flags: {}, example: "close plan.html" },
29960
+ reopen: { usage: "reopen <file>", summary: "reopen an ended or closed review", flags: {}, example: "reopen plan.html" },
29961
+ login: {
29962
+ usage: "login [file]",
29963
+ summary: "single-use link that signs a browser in as you (SSH, --no-open, another browser)",
29964
+ flags: {},
29965
+ example: "login plan.html"
29966
+ },
29967
+ feedback: {
29968
+ usage: "feedback <file> [--all] [--peek]",
29969
+ summary: "feedback no agent has answered yet; reading it acknowledges it, reply answers it",
29970
+ flags: { all: "include feedback already answered", peek: "read without acknowledging" },
29971
+ example: "feedback plan.html"
29972
+ },
29973
+ check: {
29974
+ usage: "check <file> [--fix]",
29975
+ summary: "validate the program without a browser: syntax, components, props, references, reachability",
29976
+ flags: { fix: 'declare the packs the page uses (writes the <meta name="austere-packs"> tag)' },
29977
+ example: "check plan.html"
29978
+ },
29979
+ patch: {
29980
+ usage: "patch <file> < statements",
29981
+ summary: "merge statements from stdin: same name replaces, new name adds, `name = null` deletes",
29982
+ flags: {},
29983
+ example: `patch plan.html <<'EOF'
29984
+ intro = Card([CardHeader("New title")])
29985
+ EOF`
29986
+ },
29987
+ spec: {
29988
+ usage: "spec [group|pack...]",
29989
+ summary: "component reference; groups: Layout Content Data Charts Forms Buttons",
29990
+ flags: {},
29991
+ example: "spec Charts Forms"
29992
+ },
29993
+ components: {
29994
+ usage: "components [list [group] | search <words> | show <Name...> | add <pkg|folder|file.tgz> [--trust] | remove <pkg> | new <dir> [--name pkg] | build [dir]]",
29995
+ summary: "every component, including ones spec leaves out; install, remove and write packs",
29996
+ flags: { trust: "install a third-party pack (read its code first)", name: "npm package name for `new`" },
29997
+ example: "components search scatter"
29998
+ },
29999
+ export: {
30000
+ usage: "export <file> [--out path]",
30001
+ summary: "one self-contained HTML file that works offline",
30002
+ flags: { out: "output path (default: <file>.export.html)" },
30003
+ example: "export plan.html --out review.html"
30004
+ },
30005
+ hub: {
30006
+ usage: "hub on|off|status|list|notes|open [words] [--project dir] [--unanswered]",
30007
+ summary: "the owner's home page: every page by project and day, every note",
30008
+ flags: { project: "only pages or notes from this project dir (list, notes)", unanswered: "only notes no agent answered (notes)" },
30009
+ example: "hub notes --unanswered"
30010
+ },
30011
+ share: {
30012
+ usage: 'share <file> --as "<name>" [--auto] | share list [--links] | share revoke <name|id>',
30013
+ summary: "invite link for a teammate on your tailnet; their notes wait for you unless --auto",
30014
+ flags: { as: "the reviewer's name", auto: "send this reviewer's notes straight to the agent", links: "show the links (list)" },
30015
+ example: 'share plan.html --as "Ana"'
30016
+ },
30017
+ stop: { usage: "stop", summary: "stop the background server", flags: {}, example: "stop" },
30018
+ server: {
30019
+ usage: "server [--persist] [--port n] [--verbose]",
30020
+ summary: "run the server in the foreground (under systemd, behind a reverse proxy)",
30021
+ flags: { persist: "never idle out", port: "listen port (default: AUSTERE_AXI_PORT or 4388)", verbose: "log requests", "also-listen": "extra address to listen on (repeatable)" },
30022
+ example: "server --persist"
30023
+ },
30024
+ mcp: { usage: "mcp", summary: "MCP server on stdio: spec check open poll reply patch end as tools", flags: {}, example: "mcp" },
30025
+ help: { usage: "help [command]", summary: "the full workflow, or one command's help", flags: {}, example: "help poll" }
30026
+ };
30027
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["agent-reply", "agent-reply-file", "owner", "out", "port", "also-listen", "timeout-ms", "name", "project", "as", "cursor", "after"]);
29927
30028
  function parseArgs(argv) {
29928
30029
  const flags = {};
29929
30030
  const pos = [];
@@ -29936,10 +30037,12 @@ function parseArgs(argv) {
29936
30037
  if (a.startsWith("--")) {
29937
30038
  const eq = a.indexOf("=");
29938
30039
  const key2 = eq > 0 ? a.slice(2, eq) : a.slice(2);
29939
- const takesValue = ["agent-reply", "agent-reply-file", "owner", "out", "port", "also-listen", "timeout-ms", "name", "project", "as", "cursor", "after"].includes(key2);
29940
30040
  let val = true;
29941
30041
  if (eq > 0) val = a.slice(eq + 1);
29942
- else if (takesValue) val = argv[++i];
30042
+ else if (VALUE_FLAGS.has(key2)) {
30043
+ if (i + 1 >= argv.length) return { flags, pos, error: `--${key2} needs a value` };
30044
+ val = argv[++i];
30045
+ }
29943
30046
  if (key2 === "also-listen") (flags[key2] ||= []).push(val);
29944
30047
  else flags[key2] = val;
29945
30048
  } else if (a === "-h") flags.help = true;
@@ -29952,9 +30055,26 @@ function fail(message, extra = {}) {
29952
30055
  print({ status: "error", error: message, ...extra });
29953
30056
  process.exitCode = 1;
29954
30057
  }
30058
+ function usage(message, cmd) {
30059
+ const spec = cmd && COMMANDS[cmd];
30060
+ print({ status: "error", code: "USAGE", error: message, usage: spec ? `${BIN2} ${spec.usage}` : void 0, help: `${BIN2} ${spec ? `${cmd} ` : ""}--help` });
30061
+ process.exitCode = 2;
30062
+ }
30063
+ function commandHelp(cmd) {
30064
+ const spec = COMMANDS[cmd];
30065
+ const lines = [`${BIN2} ${spec.usage}`, ` ${spec.summary}`];
30066
+ const flags = Object.entries(spec.flags);
30067
+ if (flags.length) {
30068
+ lines.push("", "FLAGS");
30069
+ const width = Math.max(...flags.map(([k]) => k.length + (VALUE_FLAGS.has(k) ? 6 : 0)));
30070
+ for (const [k, text] of flags) lines.push(` ${(`--${k}` + (VALUE_FLAGS.has(k) ? " <val>" : "")).padEnd(width + 4)}${text}`);
30071
+ }
30072
+ lines.push("", "EXAMPLE", ` ${BIN2} ${spec.example}`, "", `Full workflow and file skeleton: ${BIN2} --help`);
30073
+ process.stdout.write(lines.join("\n") + "\n");
30074
+ }
29955
30075
  function needFile(file3, cmd) {
29956
30076
  if (!file3) {
29957
- fail(`${cmd} needs a file: ${BIN2} ${cmd} <file.html>`);
30077
+ usage(`${cmd} needs a file: ${BIN2} ${cmd} <file.html>`, cmd);
29958
30078
  return null;
29959
30079
  }
29960
30080
  const abs = canonicalFile(file3);
@@ -29992,16 +30112,24 @@ async function cmdHome() {
29992
30112
  let sessions = [];
29993
30113
  if (h) {
29994
30114
  const r = await request("GET", "/api/sessions");
29995
- sessions = (r.data.sessions || []).filter((s) => s.status !== "ended").slice(0, 12).map((s) => ({
29996
- file: s.file,
29997
- presence: s.presence,
29998
- browsers: s.browsers,
29999
- pending: s.pending || void 0,
30000
- url: s.links.local
30001
- }));
30002
- }
30003
- process.stdout.write(HELP + "\n");
30004
- print({ server: h ? `running on ${port()} (v${h.version})` : "not running", open_sessions: sessions.length ? sessions : "none" });
30115
+ sessions = (r.data.sessions || []).filter((s) => s.status !== "ended");
30116
+ }
30117
+ const shown = sessions.slice(0, 12).map((s) => ({
30118
+ file: s.file,
30119
+ presence: s.presence,
30120
+ browsers: s.browsers,
30121
+ pending: s.pending || void 0,
30122
+ url: s.links.local
30123
+ }));
30124
+ print({
30125
+ bin: `${BIN2} ${VERSION}`,
30126
+ description: "review pages written in OpenUI Lang; the reviewer annotates them in the browser and notes come back by statement name",
30127
+ server: h ? `running on ${port()} (v${h.version})` : "not running",
30128
+ open_sessions: sessions.length > shown.length ? `${shown.length} of ${sessions.length} shown` : sessions.length,
30129
+ sessions: shown.length ? shown : void 0,
30130
+ commands: Object.fromEntries(Object.entries(COMMANDS).map(([name, c]) => [name === "open" ? "<file>" : name, c.summary])),
30131
+ next: shown.length ? `${BIN2} poll <file> waits for feedback on an open session. ${BIN2} --help: workflow and file skeleton.` : `${BIN2} --help: workflow and file skeleton. ${BIN2} spec: component reference. ${BIN2} <command> --help: one command.`
30132
+ });
30005
30133
  }
30006
30134
  async function cmdOpen(file3, flags) {
30007
30135
  const abs = needFile(file3, "open");
@@ -30140,7 +30268,7 @@ async function cmdReply(file3, flags) {
30140
30268
  const abs = needFile(file3, "reply");
30141
30269
  if (!abs) return;
30142
30270
  const text = await replyText(flags);
30143
- if (!text) return fail('reply needs text: --agent-reply "..." or --agent-reply-file <path|->');
30271
+ if (!text) return usage('reply needs text: --agent-reply "..." or --agent-reply-file <path|->', "reply");
30144
30272
  await ensureServer();
30145
30273
  const r = await request("POST", "/api/reply", { file: abs, text, cursor: seqFlag(flags, "cursor") }, { timeoutMs: 1e4 });
30146
30274
  if (r.status !== 200) return fail(r.data?.error || `reply failed (${r.status})`);
@@ -30205,7 +30333,7 @@ async function cmdCheck(file3, flags = {}) {
30205
30333
  async function cmdPatch(file3) {
30206
30334
  const abs = needFile(file3, "patch");
30207
30335
  if (!abs) return;
30208
- if (process.stdin.isTTY) return fail(`pipe the changed statements on stdin: ${BIN2} patch ${quote(file3)} <<'EOF' ... EOF`);
30336
+ if (process.stdin.isTTY) return usage(`pipe the changed statements on stdin: ${BIN2} patch ${quote(file3)} <<'EOF' ... EOF`, "patch");
30209
30337
  const patch = await readStdin();
30210
30338
  if (!patch.trim()) return fail("stdin was empty");
30211
30339
  const html = fs14.readFileSync(abs, "utf8");
@@ -30253,7 +30381,7 @@ More components (\`${BIN2} spec <pack>\` for signatures). A page using one must
30253
30381
  const parts = text.split(/\n(?=### )/);
30254
30382
  const head = parts[0].split("\n## Component Signatures")[0];
30255
30383
  const picked = parts.filter((p) => p.startsWith("### ") && wanted.has(p.slice(4, p.indexOf("\n")).trim().toLowerCase()));
30256
- if (!picked.length) return fail(`unknown group; one of: Layout Content Data Charts Forms Buttons`);
30384
+ if (!picked.length) return usage(`unknown group; one of: Layout Content Data Charts Forms Buttons, or an installed pack`, "spec");
30257
30385
  const trimmed = picked.map((p) => p.split(/\n## /)[0]);
30258
30386
  process.stdout.write(`${head}
30259
30387
 
@@ -30284,7 +30412,7 @@ async function cmdComponents(sub, args, flags) {
30284
30412
  case "show":
30285
30413
  return printResult(showComponents(args));
30286
30414
  case "add": {
30287
- if (!args[0]) return fail(`add needs a pack: ${BIN2} components add <npm-package>[@version] | <folder> | <file.tgz>`);
30415
+ if (!args[0]) return usage(`add needs a pack: ${BIN2} components add <npm-package>[@version] | <folder> | <file.tgz>`, "components");
30288
30416
  const { addPack: addPack2 } = await Promise.resolve().then(() => (init_install(), install_exports));
30289
30417
  let r;
30290
30418
  try {
@@ -30307,7 +30435,7 @@ async function cmdComponents(sub, args, flags) {
30307
30435
  });
30308
30436
  }
30309
30437
  case "remove": {
30310
- if (!args[0]) return fail(`remove needs a pack name: ${BIN2} components remove <name>`);
30438
+ if (!args[0]) return usage(`remove needs a pack name: ${BIN2} components remove <name>`, "components");
30311
30439
  const { removePack: removePack2 } = await Promise.resolve().then(() => (init_install(), install_exports));
30312
30440
  const r = removePack2(args[0]);
30313
30441
  if (!r) return fail(`${args[0]} is not installed (${BIN2} components list)`);
@@ -30352,7 +30480,7 @@ async function cmdComponents(sub, args, flags) {
30352
30480
  default: {
30353
30481
  const listed = listComponents(sub);
30354
30482
  if (listed.status !== "error") return print(listed);
30355
- return fail(`unknown components command: ${sub}. One of: list search show add remove new build`);
30483
+ return usage(`unknown components command: ${sub}. One of: list search show add remove new build`, "components");
30356
30484
  }
30357
30485
  }
30358
30486
  }
@@ -30514,7 +30642,7 @@ async function cmdHub(sub, args, flags) {
30514
30642
  return print({ status: opened ? "opened" : "open the url", url: url2, sign_in: opened ? void 0 : signInHint() });
30515
30643
  }
30516
30644
  default:
30517
- return fail(`unknown hub command: ${sub}. One of: on off status list notes open`);
30645
+ return usage(`unknown hub command: ${sub}. One of: on off status list notes open`, "hub");
30518
30646
  }
30519
30647
  }
30520
30648
  function ago(ms) {
@@ -30547,7 +30675,7 @@ async function cmdShare(args, flags) {
30547
30675
  }
30548
30676
  if (sub === "revoke") {
30549
30677
  const key2 = more.join(" ");
30550
- if (!key2) return fail(`revoke needs a name or id: ${BIN2} share revoke <name|id> (see ${BIN2} share list)`);
30678
+ if (!key2) return usage(`revoke needs a name or id: ${BIN2} share revoke <name|id> (see ${BIN2} share list)`, "share");
30551
30679
  let revoked;
30552
30680
  if (await serverIfRunning()) {
30553
30681
  const r2 = await request("POST", "/api/share/revoke", { key: key2 });
@@ -30561,7 +30689,7 @@ async function cmdShare(args, flags) {
30561
30689
  }
30562
30690
  const abs = needFile(sub, "share");
30563
30691
  if (!abs) return;
30564
- if (typeof flags.as !== "string" || !flags.as.trim()) return fail(`share needs a reviewer name: ${BIN2} share ${quote(sub)} --as "<name>"`);
30692
+ if (typeof flags.as !== "string" || !flags.as.trim()) return usage(`share needs a reviewer name: ${BIN2} share ${quote(sub)} --as "<name>"`, "share");
30565
30693
  await ensureServer();
30566
30694
  const r = await request("POST", "/api/share", { file: abs, name: flags.as, auto: !!flags.auto });
30567
30695
  if (r.status !== 200) return fail(r.data?.error || `share failed (${r.status})`);
@@ -30618,18 +30746,23 @@ async function main() {
30618
30746
  if (major < MIN_NODE[0] || major === MIN_NODE[0] && minor < MIN_NODE[1]) {
30619
30747
  return fail(`austere-axi needs Node ${MIN_NODE.join(".")} or newer; this is ${process.versions.node}. Install a current Node LTS.`);
30620
30748
  }
30621
- const { flags, pos } = parseArgs(process.argv.slice(2));
30749
+ const { flags, pos, error: error62 } = parseArgs(process.argv.slice(2));
30622
30750
  if (flags.version) return process.stdout.write(VERSION + "\n");
30623
30751
  const [cmd, ...rest] = pos;
30624
- if (flags.help) {
30625
- if (cmd === "reply" || cmd === "poll") {
30626
- return process.stdout.write(
30627
- `${BIN2} ${cmd} <file> --agent-reply "text" | --agent-reply-file <path|->
30628
- Replies render a small Markdown subset (paragraphs, - lists, \`code\`, **bold**, links). Keep them to a line or two.
30629
- `
30630
- );
30631
- }
30632
- return process.stdout.write(HELP);
30752
+ let name = null;
30753
+ if (cmd !== void 0) {
30754
+ if (Object.hasOwn(COMMANDS, cmd)) name = cmd;
30755
+ else if (/\.html?$/i.test(cmd) || fs14.existsSync(cmd)) name = "open";
30756
+ else if (flags.help) return process.stdout.write(HELP);
30757
+ else return usage(`unknown command: ${cmd}. One of: ${Object.keys(COMMANDS).join(" ")}, or a page file`);
30758
+ }
30759
+ if (flags.help) return name && name !== "help" ? commandHelp(name) : process.stdout.write(HELP);
30760
+ if (error62) return usage(error62, name);
30761
+ const allowed = name ? Object.keys(COMMANDS[name].flags) : [];
30762
+ const unknown2 = Object.keys(flags).filter((k) => !allowed.includes(k));
30763
+ if (unknown2.length) {
30764
+ const valid = allowed.length ? `valid: ${allowed.map((k) => `--${k}`).join(" ")}` : "it takes no flags";
30765
+ return usage(`unknown flag${unknown2.length > 1 ? "s" : ""} for ${name ?? BIN2}: ${unknown2.map((k) => `--${k}`).join(" ")}; ${valid}`, name);
30633
30766
  }
30634
30767
  stateDir();
30635
30768
  switch (cmd) {
@@ -30674,10 +30807,11 @@ Replies render a small Markdown subset (paragraphs, - lists, \`code\`, **bold**,
30674
30807
  case "open":
30675
30808
  return cmdOpen(rest[0], flags);
30676
30809
  case "help":
30677
- return process.stdout.write(HELP);
30810
+ if (!rest[0]) return process.stdout.write(HELP);
30811
+ if (!Object.hasOwn(COMMANDS, rest[0])) return usage(`no command named ${rest[0]}. One of: ${Object.keys(COMMANDS).join(" ")}`, "help");
30812
+ return commandHelp(rest[0]);
30678
30813
  default:
30679
- if (/\.html?$/i.test(cmd) || fs14.existsSync(cmd)) return cmdOpen(cmd, flags);
30680
- return fail(`unknown command: ${cmd}. Run ${BIN2} --help`);
30814
+ return cmdOpen(cmd, flags);
30681
30815
  }
30682
30816
  }
30683
30817
  main().catch((err) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "format": 1,
3
3
  "pack": "@austere-axi/pack-mermaid",
4
- "version": "0.5.1",
4
+ "version": "0.5.2",
5
5
  "groups": [
6
6
  {
7
7
  "name": "Content",
@@ -29,7 +29,7 @@
29
29
  }
30
30
  ],
31
31
  "api": 1,
32
- "integrity": "sha384-+rjqzQL1dMOswWGN+uyDvCrzDyi0QN2q3XVGBLY0oGXe1FVFlReMMZ+ABMYIn/z2",
32
+ "integrity": "sha384-1+XBvdGl+fWIMBvzXkUrVl/h2RE3F+O85RZUmqm5BFRdGv/ZvTaU9MOpGIxNmHRu",
33
33
  "size": 5782,
34
34
  "css": true,
35
35
  "defs": {
@@ -22,4 +22,4 @@
22
22
  font-size: 12.5px;
23
23
  color: var(--austere-muted);
24
24
  }
25
- `;var s={components:[N],css:V};var $=l(w());(0,$.registerPack)({name:"@austere-axi/pack-mermaid",version:"0.5.1",api:1,components:[].concat(s&&s.components?s.components:s),css:s&&s.css});})();
25
+ `;var s={components:[N],css:V};var $=l(w());(0,$.registerPack)({name:"@austere-axi/pack-mermaid",version:"0.5.2",api:1,components:[].concat(s&&s.components?s.components:s),css:s&&s.css});})();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "format": 1,
3
3
  "pack": "@austere-axi/pack-shadcn",
4
- "version": "0.5.1",
4
+ "version": "0.5.2",
5
5
  "groups": [
6
6
  {
7
7
  "name": "Data",
@@ -195,7 +195,7 @@
195
195
  }
196
196
  ],
197
197
  "api": 1,
198
- "integrity": "sha384-XfPYLd1SbrtcszTNTsHgrBvwxPyMAmK/UV4upDfK+3UCp+ejV4wKphd6x/kGcrBw",
198
+ "integrity": "sha384-DyzR2GIcDb2W7YXtHpIpAZRP2LbKJQXroF7POadUdFe1rMT1SWWn8nBbli2ty0R0",
199
199
  "size": 16313,
200
200
  "css": true,
201
201
  "defs": {
@@ -66,4 +66,4 @@ rollout = TimelineItem("Rollout", "Nov 3", "", "upcoming")`});var ue=`.sh-muted
66
66
  .sh-timeline-item.is-upcoming .sh-timeline-dot { background: transparent; border: 1px solid var(--austere-muted); }
67
67
  .sh-timeline-body { min-width: 0; font-size: 13.5px; overflow-wrap: anywhere; }
68
68
  .sh-timeline-head { display: flex; flex-wrap: wrap; gap: 4px 12px; }
69
- `;var y={components:[X,M,ae,re,te,oe,H,ne,pe,de,ce,_],css:ue};var he=r(l());(0,he.registerPack)({name:"@austere-axi/pack-shadcn",version:"0.5.1",api:1,components:[].concat(y&&y.components?y.components:y),css:y&&y.css});})();
69
+ `;var y={components:[X,M,ae,re,te,oe,H,ne,pe,de,ce,_],css:ue};var he=r(l());(0,he.registerPack)({name:"@austere-axi/pack-shadcn",version:"0.5.2",api:1,components:[].concat(y&&y.components?y.components:y),css:y&&y.css});})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "austere-axi",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Agent-authored review artifacts in OpenUI Lang: a few hundred tokens instead of a whole HTML page, with statement-level annotations fed back to the agent.",
5
5
  "mcpName": "io.github.MandalorianBatman/austere-axi",
6
6
  "type": "module",