@zibby/skills 0.2.19 → 0.2.20

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/dist/artifact.js CHANGED
@@ -1,6 +1,6 @@
1
- import{SKILL_META as We}from"@zibby/skill-ids";import{existsSync as ie,readFileSync as Fe}from"node:fs";import{homedir as Ze}from"node:os";import{join as Ye,dirname as Ge,resolve as Ke}from"node:path";import{fileURLToPath as ze}from"node:url";import{existsSync as it}from"node:fs";import{dirname as ot,resolve as lt}from"node:path";import{fileURLToPath as ht}from"node:url";var w={ZERO_WIDTH:"zero-width-bar",ROW_COUNT:"data-row-count",ROW_MISSING:"data-row-missing",BAR_COUNT:"data-bar-count",BAR_PROPORTION:"bar-proportion",VALUE_MISMATCH:"data-value-mismatch",PLACEHOLDER_TEXT:"placeholder-text",OVERFLOW:"overflow-clipped"},he=5,de=20,ue=.04,pe=3,fe=.5;function k(r,n){if(n==null||n<0)return null;let e=1;for(let t=0;t<n&&t<r.length;t+=1)r.charCodeAt(t)===10&&(e+=1);return e}function v(r,n,e){let t=n[e.code]||0;t>=he||r.length>=de||(n[e.code]=t+1,r.push(e))}var ge={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" ",ndash:"\u2013",mdash:"\u2014",hellip:"\u2026"};function Z(r){return String(r).replace(/&(#x?[0-9a-f]+|[a-z]+);/gi,(n,e)=>{if(e[0]==="#"){let a=e[1]==="x"||e[1]==="X"?parseInt(e.slice(2),16):parseInt(e.slice(1),10);return Number.isFinite(a)?String.fromCodePoint(a):n}let t=ge[String(e).toLowerCase()];return t??n})}function O(r){return Z(String(r??"")).replace(/\s+/g," ").trim().toLowerCase()}function W(r){return Math.round(r*10)/10}var me=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),M=new Set(["script","style","textarea"]),ye=new Set(["span","a","em","strong","b","i","u","s","small","code","label","abbr","cite","q","sub","sup","mark","time","var","kbd","samp","font","tt"]),be=/^<\s*(\/?)\s*([a-zA-Z][a-zA-Z0-9:_.-]*)((?:"[^"]*"|'[^']*'|[^>"'])*?)(\/?)>/;function we(r){let n={},e=/([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'=<>`]+))|([a-zA-Z_:][-a-zA-Z0-9_:.]*)/g,t;for(;(t=e.exec(r))!==null;)t[1]?n[t[1].toLowerCase()]=t[3]!=null?t[3]:t[4]!=null?t[4]:t[5]==null?"":t[5]:t[6]&&(n[t[6].toLowerCase()]="");return n}function ke(r){let n={tag:"#root",attrs:{},children:[],parent:null,start:0},e=n,t=[n],a=0,o=(i,s)=>{if(s<=i)return;let c=r.slice(i,s);c.trim()&&e.children.push({tag:"#text",attrs:{},children:[],parent:e,start:i,value:c})};for(;a<r.length;){let i=r.indexOf("<",a);if(i<0){o(a,r.length);break}if(o(a,i),r.startsWith("<!--",i)){let h=r.indexOf("-->",i);a=h<0?r.length:h+3;continue}if(r.startsWith("<!",i)||r.startsWith("<?",i)){let h=r.indexOf(">",i);a=h<0?r.length:h+1;continue}let s=be.exec(r.slice(i));if(!s){a=i+1;continue}let c=s[0],l=s[2].toLowerCase();if(s[1]){for(let h=t.length-1;h>0;h-=1)if(t[h].tag===l){t.length=h,e=t[h-1];break}a=i+c.length;continue}let u={tag:l,attrs:we(s[3]||""),children:[],parent:e,start:i};if(e.children.push(u),a=i+c.length,!(s[4]||me.has(l))){if(M.has(l)){let h=r.slice(a),d=new RegExp(`</\\s*${l}\\s*>`,"i").exec(h);u.raw=d?h.slice(0,d.index):h,a+=d?d.index+d[0].length:h.length;continue}t.push(u),e=u}}return n}function N(r,n){for(let e of r.children)e.tag!=="#text"&&(n(e),N(e,n))}function L(r){let n=[],e=t=>{for(let a of t.children)a.tag==="#text"?n.push(a.value||""):(n.push(" "),e(a),n.push(" "))};return e(r),O(n.join(""))}function ve(r){let n=[],e=t=>{for(let a of t.children)a.tag==="#text"?n.push({value:a.value||"",start:a.start,parent:t}):a.tag!=="pre"&&a.tag!=="code"&&e(a)};return e(r),n}function _(r){let n={};for(let e of String(r||"").split(";")){let t=e.indexOf(":");t<0||(n[e.slice(0,t).trim().toLowerCase()]=e.slice(t+1).trim().replace(/\s*!important$/i,""))}return n}function Se(r){let n=[];return N(r,e=>{if(e.tag!=="style"||!e.raw)return;let t=e.raw.replace(/@[a-z-]+[^{]*\{/gi," "),a=/([^{}]+)\{([^{}]*)\}/g,o;for(;(o=a.exec(t))!==null;){let i=_(o[2]);for(let s of o[1].split(",")){let c=s.trim();if(!c||/[\s>+~:[\]*]/.test(c))continue;let l=[],u=null,h=null,d=!0,p=/([.#]?)([A-Za-z_][-\w]*)/g,f;for(;(f=p.exec(c))!==null;)if(f[1]===".")l.push(f[2].toLowerCase());else if(f[1]==="#")u=f[2];else if(f.index===0)h=f[2].toLowerCase();else{d=!1;break}d&&(h||u||l.length)&&n.push({classes:l,id:u,tag:h,decls:i})}}}),n}function Y(r){return String(r.attrs.class||"").split(/\s+/).filter(Boolean).map(n=>n.toLowerCase())}function Ne(r,n,e){if(r.tag&&r.tag!==n.tag||r.id&&r.id!==n.attrs.id)return!1;for(let t of r.classes)if(e.indexOf(t)<0)return!1;return!0}function S(r,n,e){let t=_(r.attrs.style)[n];if(t!=null&&t!=="")return t.toLowerCase();let a=Y(r),o=null;for(let i of e){if(!Ne(i,r,a))continue;let s=i.decls[n];s!=null&&s!==""&&(o=s.toLowerCase())}return o}function F(r,n){let e=S(r,"display",n);return e||(ye.has(r.tag)?"inline":"block")}function U(r){if(!r)return null;let n=/^(-?\d+(?:\.\d+)?)px$/.exec(String(r).trim());return n?Number(n[1]):null}function G(r){if(!r)return null;let n=/^(-?\d+(?:\.\d+)?)%$/.exec(String(r).trim());return n?Number(n[1]):null}function P(r){return r!=null&&typeof r=="object"&&!Array.isArray(r)}function Te(r){if(!r.length||!P(r[0]))return r.map(t=>t==null?"":String(t));let n=null,e=0;for(let t of Object.keys(r[0])){let a=r.map(i=>P(i)&&i[t]!=null?String(i[t]):"");if(a.some(i=>i.length<2))continue;let o=new Set(a.map(O)).size;if(o>e&&(e=o,n=a),o===r.length)break}return n||r.map((t,a)=>String(P(t)?Object.values(t)[0]==null?a:Object.values(t)[0]:t))}function Oe(r){let n=[],e=[],t=new Set,a=(i,s)=>{if(!(s.length<2)){if(s.every(P)){n.push({path:i,records:s,identities:Te(s)});let c=Object.keys(s[0]),l=c.filter(h=>s.every(d=>typeof d[h]=="number"&&Number.isFinite(d[h]))),u=c.filter(h=>s.every(d=>typeof d[h]=="string"));l.length===1&&u.length>=1&&e.push({path:i,labels:s.map(h=>String(h[u[0]])),values:s.map(h=>Number(h[l[0]]))});return}s.every(c=>typeof c=="number"&&Number.isFinite(c))&&e.push({path:i,labels:s.map((c,l)=>`#${l}`),values:s.slice()})}},o=(i,s,c)=>{if(!(c>6||i==null||typeof i!="object"||t.has(i))){if(t.add(i),Array.isArray(i)){a(s,i);for(let l=0;l<i.length&&l<200;l+=1)o(i[l],`${s}[${l}]`,c+1);return}for(let l of Object.keys(i))o(i[l],s?`${s}.${l}`:l,c+1)}};return o(r,"",0),{collections:n,series:e}}function Re(r){let n=String(r||"").match(/[MmLlHhVvZz]|-?\d*\.?\d+(?:e-?\d+)?/gi);if(!n)return null;let e=0,t=0,a="",o=1/0,i=-1/0,s=1/0,c=-1/0,l=()=>{!Number.isFinite(e)||!Number.isFinite(t)||(e<o&&(o=e),e>i&&(i=e),t<s&&(s=t),t>c&&(c=t))},u=0,h=0;for(;u<n.length&&h++<2e4;){let d=n[u];if(/[A-Za-z]/.test(d)&&(a=d,u+=1,a==="Z"||a==="z"))continue;if(u>=n.length)break;let p=()=>{let f=Number(n[u]);return u+=1,f};switch(a){case"M":e=p(),t=p(),l(),a="L";break;case"m":e+=p(),t+=p(),l(),a="l";break;case"L":e=p(),t=p(),l();break;case"l":e+=p(),t+=p(),l();break;case"H":e=p(),l();break;case"h":e+=p(),l();break;case"V":t=p(),l();break;case"v":t+=p(),l();break;default:u+=1;break}}return!Number.isFinite(o)||!Number.isFinite(s)?null:{w:i-o,h:c-s}}var Ee=/(^|[-_])(bar|gauge|meter|fill|progress|track)([-_]|$)/i;function xe(r){let n=r.parent;for(let e=0;n&&e<3;e+=1,n=n.parent){let t=L(n);if(t)return t}return""}function K(r,n){if(Y(r).some(a=>Ee.test(a)))return!0;let e=_(r.attrs.style);return!!(e.background||e["background-color"]||S(r,"background",n)||S(r,"background-color",n))&&L(r)===""}function _e(r){let n=new Map;for(let t of r)for(let a=t.node.parent;a;a=a.parent)n.set(a,(n.get(a)||0)+1);let e=new Map;for(let t of r){let a=null;for(let o=t.node.parent;o;o=o.parent)if((n.get(o)||0)>=2){a=o;break}a&&(e.has(a)||e.set(a,[]),e.get(a).push(t))}return[...e.values()].filter(t=>t.length>=2)}function Ae(r,n){let e=new Map,t=[];N(r,o=>{if(o.tag==="path"||o.tag==="rect"){let u=o.attrs.ecmeta_series_index;if(u==null)return;let h=null;if(o.tag==="rect"){let f=Number(o.attrs.width),b=Number(o.attrs.height);Number.isFinite(f)&&Number.isFinite(b)&&(h={w:f,h:b})}else h=Re(o.attrs.d||"");if(!h)return;let d=o.attrs.ecmeta_data_index,p=String(u);e.has(p)||e.set(p,[]),e.get(p).push({bar:{node:o,extent:NaN,unit:"user",dataIndex:d==null||d===""?null:Number(d),context:"",start:o.start},box:h});return}if(M.has(o.tag)||o.tag==="svg")return;let i=_(o.attrs.style),s=i.width==null?i["inline-size"]:i.width;if(s==null||s==="")return;let c=G(s),l=U(s);c==null&&l==null||K(o,n)&&t.push({node:o,extent:c??l,unit:c==null?"px":"%",dataIndex:null,context:xe(o),start:o.start})});let a=[];for(let o of e.values()){if(o.length<2)continue;let i=l=>Math.max(...l)-Math.min(...l),s=i(o.map(l=>l.box.h))>=i(o.map(l=>l.box.w));for(let l of o)l.bar.extent=s?l.box.h:l.box.w;let c=o.map(l=>l.bar).sort((l,u)=>l.dataIndex!=null&&u.dataIndex!=null?l.dataIndex-u.dataIndex:l.start-u.start);a.push(c)}for(let o of _e(t)){let i=o.filter(l=>l.unit==="%"),s=o.filter(l=>l.unit==="px"),c=i.length>=s.length?i:s;c.length>=2&&a.push(c)}return a}function Ie(r){let n=[];return N(r,e=>{if(e.tag!=="table")return;let t=[];N(e,a=>{if(a.tag!=="tr")return;let o=a.children.filter(i=>i.tag==="td"||i.tag==="th");o.length&&o.every(i=>i.tag==="th")||t.push(a)}),n.push({node:e,rows:t,rowTexts:t.map(L),start:e.start})}),n}function Ce(r,n,e,t,a,o,i){let s=L(n);for(let c of t){let l=c.records.length;if(l<3)continue;let u=[],h=0;for(let g of c.identities){let y=O(g);!y||y.length<2||(h+=1,s.includes(y)||u.push(String(g)))}if(h<3)continue;let d=h-u.length,p=h?d/h:1,f=null,b=0;for(let g of e){if(!g.rows.length)continue;let y=0,E=new Set;for(let $ of g.rowTexts){let x=!1;c.identities.forEach((le,ce)=>{let J=O(le);J.length>=2&&$.indexOf(J)>=0&&(x=!0,E.add(ce))}),x&&(y+=1)}y<Math.max(2,Math.ceil(g.rows.length*.5))||E.size<Math.ceil(y*.8)||y>b&&(b=y,f=g)}let m=f!=null;i.push({path:c.path||"(root)",records:l,identitiesSearched:h,presentInPage:d,renderedFraction:Number(p.toFixed(3)),boundTableRows:m?f.rows.length:null}),!(p<fe)&&(u.length&&v(a,o,{code:w.ROW_MISSING,line:m?k(r,f.start):1,message:`The data at \`${c.path||"(root)"}\` has ${l} records but ${u.length} of them appear NOWHERE on the page: ${u.slice(0,6).map(g=>JSON.stringify(String(g).slice(0,40))).join(", ")}${u.length>6?`, +${u.length-6} more`:""}.`,hint:"Render every record you were given, or say in the page that it is a partial view and how many were left out. A silently truncated table reads as the complete answer."}),u.length===0&&m&&f.rows.length!==l&&v(a,o,{code:w.ROW_COUNT,line:k(r,f.start),message:`The table renders ${f.rows.length} body row(s) but the data at \`${c.path||"(root)"}\` has ${l} records \u2014 ${Math.abs(l-f.rows.length)} ${l>f.rows.length?"missing":"extra"}.`,hint:'Emit exactly one <tr> per record. If you meant to show only some of them, state the cut ("top 10 of 23") in the page itself.'}))}}function $e(r,n){let e=new Array(r.length).fill(-1);if(r.every(o=>o.dataIndex!=null)){let o=0;if(r.forEach((i,s)=>{let c=i.dataIndex;c>=0&&c<n.values.length&&(e[s]=c,o+=1)}),o===r.length)return e}e.fill(-1);let t=new Set,a=0;if(r.forEach((o,i)=>{for(let s=0;s<n.labels.length;s+=1){if(t.has(s))continue;let c=O(n.labels[s]);if(c.length>=2&&o.context.indexOf(c)>=0){e[i]=s,t.add(s),a+=1;break}}}),a===r.length)return e;if(r.length!==n.values.length)return null;for(let o=0;o<r.length;o+=1)e[o]=o;return e}function Pe(r,n){let e=null,t=0;for(let o of n){let i=o.labels.filter(s=>{let c=O(s);return c.length>=2&&r.some(l=>l.context.indexOf(c)>=0)}).length;i>t&&(t=i,e=o)}if(t>=2)return e;let a=n.filter(o=>o.values.length===r.length);return a.length===1?a[0]:null}function Le(r,n,e,t,a,o){o.barGroups=n.map(i=>i.length),o.boundBars=0;for(let i of n){let s=Pe(i,e);if(!s)continue;if(s.values.length!==i.length){v(t,a,{code:w.BAR_COUNT,line:k(r,i[0].start),message:`The chart draws ${i.length} bar(s) but the series at \`${s.path||"(root)"}\` has ${s.values.length} value(s).`,hint:"Draw one bar per value. A dropped bar silently removes a whole category from the reader's view of the data."});continue}let c=$e(i,s);if(!c||c.some(d=>d<0))continue;o.boundBars+=i.length;let l=[];for(let d=0;d<i.length;d+=1)i[d].extent>0&&s.values[c[d]]>0&&l.push(d);if(l.length>=pe){let d=l.map(g=>i[g].extent/s.values[c[g]]).sort((g,y)=>g-y),p=Math.floor(d.length/2),f=d.length%2?d[p]:(d[p-1]+d[p])/2,b=Math.max(...s.values.map(g=>Math.abs(g))),m=f*b;if(f>0&&m>0)for(let g of l){let y=s.values[c[g]],E=f*y,$=Math.abs(i[g].extent-E)/m;if($<=ue)continue;let x=i[g].unit==="%"?"%":"";v(t,a,{code:w.BAR_PROPORTION,line:k(r,i[g].start),message:`The bar for ${JSON.stringify(s.labels[c[g]])} (value ${y}) is drawn at ${W(i[g].extent)}${x}, but every other bar in this chart puts it at ${W(E)}${x} \u2014 off by ${Math.round($*100)}% of the full-scale bar. The chart misrepresents the data.`,hint:"Every bar in one chart must use the SAME value\u2192length scale. Compute each length from its datum with one formula (value / max * 100), never by writing the numbers out by hand."})}}let u=s.values.reduce((d,p)=>d+p,0),h=Math.max(...s.values.map(d=>Math.abs(d)));for(let d=0;d<i.length;d+=1){let p=s.values[c[d]],f=i[d].context;if(!f)continue;let b=(f.match(/-?\d[\d,]*(?:\.\d+)?/g)||[]).map(m=>Number(m.replace(/,/g,""))).filter(m=>Number.isFinite(m));b.length&&(b.some(m=>Math.abs(m-p)<1e-9)||u>0&&b.some(m=>Math.abs(m-p/u*100)<.6)||h>0&&b.some(m=>Math.abs(m-p/h*100)<.6)||v(t,a,{code:w.VALUE_MISMATCH,line:k(r,i[d].start),message:`The row for ${JSON.stringify(s.labels[c[d]])} prints ${b.slice(0,3).join(", ")} but its datum is ${p}.`,hint:"Print the figure your tools returned, unchanged, and use that same figure everywhere it appears on the page."}))}}}var He=/^(flex|inline-flex|grid|inline-grid)$/;function Be(r,n,e,t,a,o){let i=0;N(n,s=>{if(M.has(s.tag))return;let c=_(s.attrs.style).width;if(!c)return;let l=G(c);if(l==null||l<=0||!K(s,e))return;i+=1;let u=F(s,e);if(u==="none"){v(t,a,{code:w.ZERO_WIDTH,line:k(r,s.start),message:`<${s.tag}> is given style="width:${c}" but its effective display is \`none\` \u2014 it is not rendered at all.`,hint:"Remove the display:none rule that matches this element, or remove the element. A width on a display:none box is dead code."});return}if(u!=="inline")return;let h=s.parent;if(h&&h.tag!=="#root"&&He.test(F(h,e)))return;let d=S(s,"position",e);if(d==="absolute"||d==="fixed")return;let p=S(s,"float",e);p==="left"||p==="right"||v(t,a,{code:w.ZERO_WIDTH,line:k(r,s.start),message:`<${s.tag}${s.attrs.class?` class="${s.attrs.class}"`:""}> is given style="width:${c}" but its effective display is \`inline\` \u2014 CSS IGNORES width on a non-replaced inline box, so this bar renders with no length at all. The source looks right; the page will not.`,hint:"Give the bar display:block or display:inline-block (or make its container display:flex). A <span> is inline by DEFAULT \u2014 that alone is enough to swallow the width."})}),o.percentWidthBars=i}function Ue(r,n,e,t,a){N(n,o=>{let i=U(S(o,"width",e));if(!(i==null||i<=0))for(let s=o.parent;s&&s.tag!=="#root";s=s.parent){let c=U(S(s,"width",e));if(c==null||c<=0)continue;if(i<=c)return;let l=S(s,"overflow",e)||S(s,"overflow-x",e);if(l!=="hidden"&&l!=="clip")return;v(t,a,{code:w.OVERFLOW,line:k(r,o.start),message:`<${o.tag}> is ${i}px wide inside a ${c}px ancestor with overflow:${l} \u2014 ${Math.round(i-c)}px of it is clipped and can never be seen.`,hint:"Size the child from its container (a percentage, or max-width:100%), or widen the container. Content the reader cannot see is content you did not publish."});return}})}var Me=[{re:/\[object (?:Object|Array|Promise|Map|Set|Null|Undefined)\]/g,what:"an object stringified instead of a field being picked off it"},{re:/\$\{[^}\n]{0,80}\}/g,what:"an un-substituted JavaScript template placeholder"},{re:/\{\{[^}\n]{0,80}\}\}/g,what:"an un-substituted moustache placeholder"},{re:/(?:^|[^\w.])(NaN)(?![\w])/g,what:"NaN \u2014 an arithmetic result that was never a number"},{re:/(?:^|[^\w.])(-?Infinity)(?![\w])/g,what:"Infinity \u2014 a division by zero reached the page"}],je=/^(undefined|null)$/i,De=new Set(["td","th","li","span","div","strong","em","b"]);function Je(r,n,e,t,a){let o=0;for(let i of ve(n)){let s=i.value;o+=s.length;for(let l of Me){l.re.lastIndex=0;let u;for(;(u=l.re.exec(s))!==null;){let h=u[1]==null?u[0]:u[1];v(e,t,{code:w.PLACEHOLDER_TEXT,line:k(r,i.start+u.index+u[0].indexOf(h)),message:`The page prints ${JSON.stringify(h.slice(0,60))} as VISIBLE TEXT \u2014 ${l.what}. A reader sees this literally.`,hint:"Format the value before it reaches the page: pick the field you meant off the object, and guard the arithmetic so a missing input renders as an em dash rather than NaN."}),u.index===l.re.lastIndex&&(l.re.lastIndex+=1)}}let c=Z(s).trim();je.test(c)&&De.has(i.parent.tag)&&v(e,t,{code:w.PLACEHOLDER_TEXT,line:k(r,i.start),message:`A <${i.parent.tag}> contains only the literal text "${c}" \u2014 the value it was meant to hold was ${c}.`,hint:'Render an em dash (\u2014) or "n/a" for a missing value; never let the JavaScript literal reach the reader.'})}a.textCharsScanned=o}function z({html:r,data:n}){let e=typeof r=="string"?r:"",t=[],a={};if(!e.trim())return{ok:!1,defects:[{code:"empty-content",line:1,message:"No html was passed to the check.",hint:"Pass the exact page source you are about to publish."}],checked:{tables:0,bodyRows:0,bars:0,barGroups:[],boundBars:0,seriesInData:0,collections:[]}};let o=ke(e),i=Se(o),s=Ie(o),c=Ae(o,i),l=Oe(n),u={tables:s.length,bodyRows:s.reduce((h,d)=>h+d.rows.length,0),bars:c.reduce((h,d)=>h+d.length,0),barGroups:[],boundBars:0,seriesInData:l.series.length,collections:[]};return Ce(e,o,s,l.collections,t,a,u.collections),Le(e,c,l.series,t,a,u),Be(e,o,i,t,a,u),Ue(e,o,i,t,a),Je(e,o,t,a,u),t.sort((h,d)=>(h.line||0)-(d.line||0)),{ok:t.length===0,defects:t,checked:u}}function Ve(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Ge(ze(import.meta.url)),n=Ke(r,"..","bin","mcp-skill.mjs");return ie(n)?n:null}function I(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let r=Ye(Ze(),".zibby","config.json");return ie(r)&&JSON.parse(Fe(r,"utf-8")).sessionToken||null}catch{return null}}function C(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function qe(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function se(r){return`${qe()}:artifact:${r}`}var R="headings, paragraphs, **bold**, *italic*, `inline code`, fenced code blocks, bullet + numbered lists, blockquotes, horizontal rules, links, and GitHub-style pipe tables (including |---:| alignment)",B="raw HTML (tags are escaped and print as literal text), images, nested lists, footnotes and strikethrough",A=2,T=0;function bt(){T=0}function D(r,n){return JSON.stringify({published:!1,stop:!0,attemptsUsed:A,defects:n||[],error:r,instruction:`STOP. You have used both publish attempts and the page is still rejected. Do NOT call artifact_publish or artifact_update again in this turn. Tell the user plainly that you could not produce a clean page, say in one line what was wrong, and give them the content directly in the chat instead. A broken link is worse than an honest "I couldn't".`})}function Xe(r,n,e){return JSON.stringify({published:!1,stop:!1,attemptsRemaining:A-T,defects:e||[],error:n,instruction:"The page was NOT published and no URL exists. Fix EXACTLY the defects listed above and call once more \u2014 you get ONE more attempt, then this tool will stop you."+(r==="html"?` You are on the \`html\` path, where you author the entire document and every mistake ships. Unless this page genuinely needs a custom visual layout, the fastest and most reliable fix is to re-send the SAME content as \`markdown\` \u2014 Zibby then generates the document skeleton, CSS and tables for you and these defects become impossible. Markdown supports ${R}.`:` Markdown supports ${R}; it does NOT support ${B}.`)})}var V="The page WAS published and the url is valid. This only means the data\u2194render pre-flight could not reach a verdict \u2014 nothing is known to be wrong. Open the page yourself if the figures matter.";function Qe(r){let n=new Set,e=(r||[]).map((a,o)=>{let i=a.line?` line ${a.line}:`:"",s="";return a.hint&&!n.has(a.code)&&(n.add(a.code),s=` \u2014 ${a.hint}`),`${o+1}. [${a.code}]${i} ${a.message}${s}`}),t=e.length;return`Artifact NOT published and NOTHING was sent \u2014 the page was checked against the \`data\` you passed and disagrees with it in ${t} place${t===1?"":"s"}:
1
+ import{SKILL_META as We}from"@zibby/skill-ids";import{existsSync as oe,readFileSync as Fe}from"node:fs";import{homedir as Ze}from"node:os";import{join as Ye,dirname as Ve,resolve as Ge}from"node:path";import{fileURLToPath as Ke}from"node:url";import{existsSync as ot}from"node:fs";import{dirname as st,resolve as lt}from"node:path";import{fileURLToPath as dt}from"node:url";var w={ZERO_WIDTH:"zero-width-bar",ROW_COUNT:"data-row-count",ROW_MISSING:"data-row-missing",BAR_COUNT:"data-bar-count",BAR_PROPORTION:"bar-proportion",VALUE_MISMATCH:"data-value-mismatch",PLACEHOLDER_TEXT:"placeholder-text",OVERFLOW:"overflow-clipped"},de=5,he=20,ue=.04,pe=3,fe=.5;function k(r,n){if(n==null||n<0)return null;let e=1;for(let t=0;t<n&&t<r.length;t+=1)r.charCodeAt(t)===10&&(e+=1);return e}function v(r,n,e){let t=n[e.code]||0;t>=de||r.length>=he||(n[e.code]=t+1,r.push(e))}var ge={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:" ",ndash:"\u2013",mdash:"\u2014",hellip:"\u2026"};function Z(r){return String(r).replace(/&(#x?[0-9a-f]+|[a-z]+);/gi,(n,e)=>{if(e[0]==="#"){let a=e[1]==="x"||e[1]==="X"?parseInt(e.slice(2),16):parseInt(e.slice(1),10);return Number.isFinite(a)?String.fromCodePoint(a):n}let t=ge[String(e).toLowerCase()];return t??n})}function O(r){return Z(String(r??"")).replace(/\s+/g," ").trim().toLowerCase()}function W(r){return Math.round(r*10)/10}var me=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),M=new Set(["script","style","textarea"]),ye=new Set(["span","a","em","strong","b","i","u","s","small","code","label","abbr","cite","q","sub","sup","mark","time","var","kbd","samp","font","tt"]),be=/^<\s*(\/?)\s*([a-zA-Z][a-zA-Z0-9:_.-]*)((?:"[^"]*"|'[^']*'|[^>"'])*?)(\/?)>/;function we(r){let n={},e=/([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'=<>`]+))|([a-zA-Z_:][-a-zA-Z0-9_:.]*)/g,t;for(;(t=e.exec(r))!==null;)t[1]?n[t[1].toLowerCase()]=t[3]!=null?t[3]:t[4]!=null?t[4]:t[5]==null?"":t[5]:t[6]&&(n[t[6].toLowerCase()]="");return n}function ke(r){let n={tag:"#root",attrs:{},children:[],parent:null,start:0},e=n,t=[n],a=0,s=(o,i)=>{if(i<=o)return;let c=r.slice(o,i);c.trim()&&e.children.push({tag:"#text",attrs:{},children:[],parent:e,start:o,value:c})};for(;a<r.length;){let o=r.indexOf("<",a);if(o<0){s(a,r.length);break}if(s(a,o),r.startsWith("<!--",o)){let d=r.indexOf("-->",o);a=d<0?r.length:d+3;continue}if(r.startsWith("<!",o)||r.startsWith("<?",o)){let d=r.indexOf(">",o);a=d<0?r.length:d+1;continue}let i=be.exec(r.slice(o));if(!i){a=o+1;continue}let c=i[0],l=i[2].toLowerCase();if(i[1]){for(let d=t.length-1;d>0;d-=1)if(t[d].tag===l){t.length=d,e=t[d-1];break}a=o+c.length;continue}let u={tag:l,attrs:we(i[3]||""),children:[],parent:e,start:o};if(e.children.push(u),a=o+c.length,!(i[4]||me.has(l))){if(M.has(l)){let d=r.slice(a),h=new RegExp(`</\\s*${l}\\s*>`,"i").exec(d);u.raw=h?d.slice(0,h.index):d,a+=h?h.index+h[0].length:d.length;continue}t.push(u),e=u}}return n}function T(r,n){for(let e of r.children)e.tag!=="#text"&&(n(e),T(e,n))}function $(r){let n=[],e=t=>{for(let a of t.children)a.tag==="#text"?n.push(a.value||""):(n.push(" "),e(a),n.push(" "))};return e(r),O(n.join(""))}function ve(r){let n=[],e=t=>{for(let a of t.children)a.tag==="#text"?n.push({value:a.value||"",start:a.start,parent:t}):a.tag!=="pre"&&a.tag!=="code"&&e(a)};return e(r),n}function _(r){let n={};for(let e of String(r||"").split(";")){let t=e.indexOf(":");t<0||(n[e.slice(0,t).trim().toLowerCase()]=e.slice(t+1).trim().replace(/\s*!important$/i,""))}return n}function Se(r){let n=[];return T(r,e=>{if(e.tag!=="style"||!e.raw)return;let t=e.raw.replace(/@[a-z-]+[^{]*\{/gi," "),a=/([^{}]+)\{([^{}]*)\}/g,s;for(;(s=a.exec(t))!==null;){let o=_(s[2]);for(let i of s[1].split(",")){let c=i.trim();if(!c||/[\s>+~:[\]*]/.test(c))continue;let l=[],u=null,d=null,h=!0,p=/([.#]?)([A-Za-z_][-\w]*)/g,f;for(;(f=p.exec(c))!==null;)if(f[1]===".")l.push(f[2].toLowerCase());else if(f[1]==="#")u=f[2];else if(f.index===0)d=f[2].toLowerCase();else{h=!1;break}h&&(d||u||l.length)&&n.push({classes:l,id:u,tag:d,decls:o})}}}),n}function Y(r){return String(r.attrs.class||"").split(/\s+/).filter(Boolean).map(n=>n.toLowerCase())}function Te(r,n,e){if(r.tag&&r.tag!==n.tag||r.id&&r.id!==n.attrs.id)return!1;for(let t of r.classes)if(e.indexOf(t)<0)return!1;return!0}function S(r,n,e){let t=_(r.attrs.style)[n];if(t!=null&&t!=="")return t.toLowerCase();let a=Y(r),s=null;for(let o of e){if(!Te(o,r,a))continue;let i=o.decls[n];i!=null&&i!==""&&(s=i.toLowerCase())}return s}function F(r,n){let e=S(r,"display",n);return e||(ye.has(r.tag)?"inline":"block")}function U(r){if(!r)return null;let n=/^(-?\d+(?:\.\d+)?)px$/.exec(String(r).trim());return n?Number(n[1]):null}function V(r){if(!r)return null;let n=/^(-?\d+(?:\.\d+)?)%$/.exec(String(r).trim());return n?Number(n[1]):null}function P(r){return r!=null&&typeof r=="object"&&!Array.isArray(r)}function Ne(r){if(!r.length||!P(r[0]))return r.map(t=>t==null?"":String(t));let n=null,e=0;for(let t of Object.keys(r[0])){let a=r.map(o=>P(o)&&o[t]!=null?String(o[t]):"");if(a.some(o=>o.length<2))continue;let s=new Set(a.map(O)).size;if(s>e&&(e=s,n=a),s===r.length)break}return n||r.map((t,a)=>String(P(t)?Object.values(t)[0]==null?a:Object.values(t)[0]:t))}function Oe(r){let n=[],e=[],t=new Set,a=(o,i)=>{if(!(i.length<2)){if(i.every(P)){n.push({path:o,records:i,identities:Ne(i)});let c=Object.keys(i[0]),l=c.filter(d=>i.every(h=>typeof h[d]=="number"&&Number.isFinite(h[d]))),u=c.filter(d=>i.every(h=>typeof h[d]=="string"));l.length===1&&u.length>=1&&e.push({path:o,labels:i.map(d=>String(d[u[0]])),values:i.map(d=>Number(d[l[0]]))});return}i.every(c=>typeof c=="number"&&Number.isFinite(c))&&e.push({path:o,labels:i.map((c,l)=>`#${l}`),values:i.slice()})}},s=(o,i,c)=>{if(!(c>6||o==null||typeof o!="object"||t.has(o))){if(t.add(o),Array.isArray(o)){a(i,o);for(let l=0;l<o.length&&l<200;l+=1)s(o[l],`${i}[${l}]`,c+1);return}for(let l of Object.keys(o))s(o[l],i?`${i}.${l}`:l,c+1)}};return s(r,"",0),{collections:n,series:e}}function xe(r){let n=String(r||"").match(/[MmLlHhVvZz]|-?\d*\.?\d+(?:e-?\d+)?/gi);if(!n)return null;let e=0,t=0,a="",s=1/0,o=-1/0,i=1/0,c=-1/0,l=()=>{!Number.isFinite(e)||!Number.isFinite(t)||(e<s&&(s=e),e>o&&(o=e),t<i&&(i=t),t>c&&(c=t))},u=0,d=0;for(;u<n.length&&d++<2e4;){let h=n[u];if(/[A-Za-z]/.test(h)&&(a=h,u+=1,a==="Z"||a==="z"))continue;if(u>=n.length)break;let p=()=>{let f=Number(n[u]);return u+=1,f};switch(a){case"M":e=p(),t=p(),l(),a="L";break;case"m":e+=p(),t+=p(),l(),a="l";break;case"L":e=p(),t=p(),l();break;case"l":e+=p(),t+=p(),l();break;case"H":e=p(),l();break;case"h":e+=p(),l();break;case"V":t=p(),l();break;case"v":t+=p(),l();break;default:u+=1;break}}return!Number.isFinite(s)||!Number.isFinite(i)?null:{w:o-s,h:c-i}}var Re=/(^|[-_])(bar|gauge|meter|fill|progress|track)([-_]|$)/i;function Ee(r){let n=r.parent;for(let e=0;n&&e<3;e+=1,n=n.parent){let t=$(n);if(t)return t}return""}function G(r,n){if(Y(r).some(a=>Re.test(a)))return!0;let e=_(r.attrs.style);return!!(e.background||e["background-color"]||S(r,"background",n)||S(r,"background-color",n))&&$(r)===""}function _e(r){let n=new Map;for(let t of r)for(let a=t.node.parent;a;a=a.parent)n.set(a,(n.get(a)||0)+1);let e=new Map;for(let t of r){let a=null;for(let s=t.node.parent;s;s=s.parent)if((n.get(s)||0)>=2){a=s;break}a&&(e.has(a)||e.set(a,[]),e.get(a).push(t))}return[...e.values()].filter(t=>t.length>=2)}function Ae(r,n){let e=new Map,t=[];T(r,s=>{if(s.tag==="path"||s.tag==="rect"){let u=s.attrs.ecmeta_series_index;if(u==null)return;let d=null;if(s.tag==="rect"){let f=Number(s.attrs.width),b=Number(s.attrs.height);Number.isFinite(f)&&Number.isFinite(b)&&(d={w:f,h:b})}else d=xe(s.attrs.d||"");if(!d)return;let h=s.attrs.ecmeta_data_index,p=String(u);e.has(p)||e.set(p,[]),e.get(p).push({bar:{node:s,extent:NaN,unit:"user",dataIndex:h==null||h===""?null:Number(h),context:"",start:s.start},box:d});return}if(M.has(s.tag)||s.tag==="svg")return;let o=_(s.attrs.style),i=o.width==null?o["inline-size"]:o.width;if(i==null||i==="")return;let c=V(i),l=U(i);c==null&&l==null||G(s,n)&&t.push({node:s,extent:c??l,unit:c==null?"px":"%",dataIndex:null,context:Ee(s),start:s.start})});let a=[];for(let s of e.values()){if(s.length<2)continue;let o=l=>Math.max(...l)-Math.min(...l),i=o(s.map(l=>l.box.h))>=o(s.map(l=>l.box.w));for(let l of s)l.bar.extent=i?l.box.h:l.box.w;let c=s.map(l=>l.bar).sort((l,u)=>l.dataIndex!=null&&u.dataIndex!=null?l.dataIndex-u.dataIndex:l.start-u.start);a.push(c)}for(let s of _e(t)){let o=s.filter(l=>l.unit==="%"),i=s.filter(l=>l.unit==="px"),c=o.length>=i.length?o:i;c.length>=2&&a.push(c)}return a}function Ie(r){let n=[];return T(r,e=>{if(e.tag!=="table")return;let t=[];T(e,a=>{if(a.tag!=="tr")return;let s=a.children.filter(o=>o.tag==="td"||o.tag==="th");s.length&&s.every(o=>o.tag==="th")||t.push(a)}),n.push({node:e,rows:t,rowTexts:t.map($),start:e.start})}),n}function Ce(r,n,e,t,a,s,o){let i=$(n);for(let c of t){let l=c.records.length;if(l<3)continue;let u=[],d=0;for(let g of c.identities){let y=O(g);!y||y.length<2||(d+=1,i.includes(y)||u.push(String(g)))}if(d<3)continue;let h=d-u.length,p=d?h/d:1,f=null,b=0;for(let g of e){if(!g.rows.length)continue;let y=0,R=new Set;for(let L of g.rowTexts){let E=!1;c.identities.forEach((le,ce)=>{let J=O(le);J.length>=2&&L.indexOf(J)>=0&&(E=!0,R.add(ce))}),E&&(y+=1)}y<Math.max(2,Math.ceil(g.rows.length*.5))||R.size<Math.ceil(y*.8)||y>b&&(b=y,f=g)}let m=f!=null;o.push({path:c.path||"(root)",records:l,identitiesSearched:d,presentInPage:h,renderedFraction:Number(p.toFixed(3)),boundTableRows:m?f.rows.length:null}),!(p<fe)&&(u.length&&v(a,s,{code:w.ROW_MISSING,line:m?k(r,f.start):1,message:`The data at \`${c.path||"(root)"}\` has ${l} records but ${u.length} of them appear NOWHERE on the page: ${u.slice(0,6).map(g=>JSON.stringify(String(g).slice(0,40))).join(", ")}${u.length>6?`, +${u.length-6} more`:""}.`,hint:"Render every record you were given, or say in the page that it is a partial view and how many were left out. A silently truncated table reads as the complete answer."}),u.length===0&&m&&f.rows.length!==l&&v(a,s,{code:w.ROW_COUNT,line:k(r,f.start),message:`The table renders ${f.rows.length} body row(s) but the data at \`${c.path||"(root)"}\` has ${l} records \u2014 ${Math.abs(l-f.rows.length)} ${l>f.rows.length?"missing":"extra"}.`,hint:'Emit exactly one <tr> per record. If you meant to show only some of them, state the cut ("top 10 of 23") in the page itself.'}))}}function Le(r,n){let e=new Array(r.length).fill(-1);if(r.every(s=>s.dataIndex!=null)){let s=0;if(r.forEach((o,i)=>{let c=o.dataIndex;c>=0&&c<n.values.length&&(e[i]=c,s+=1)}),s===r.length)return e}e.fill(-1);let t=new Set,a=0;if(r.forEach((s,o)=>{for(let i=0;i<n.labels.length;i+=1){if(t.has(i))continue;let c=O(n.labels[i]);if(c.length>=2&&s.context.indexOf(c)>=0){e[o]=i,t.add(i),a+=1;break}}}),a===r.length)return e;if(r.length!==n.values.length)return null;for(let s=0;s<r.length;s+=1)e[s]=s;return e}function Pe(r,n){let e=null,t=0;for(let s of n){let o=s.labels.filter(i=>{let c=O(i);return c.length>=2&&r.some(l=>l.context.indexOf(c)>=0)}).length;o>t&&(t=o,e=s)}if(t>=2)return e;let a=n.filter(s=>s.values.length===r.length);return a.length===1?a[0]:null}function $e(r,n,e,t,a,s){s.barGroups=n.map(o=>o.length),s.boundBars=0;for(let o of n){let i=Pe(o,e);if(!i)continue;if(i.values.length!==o.length){v(t,a,{code:w.BAR_COUNT,line:k(r,o[0].start),message:`The chart draws ${o.length} bar(s) but the series at \`${i.path||"(root)"}\` has ${i.values.length} value(s).`,hint:"Draw one bar per value. A dropped bar silently removes a whole category from the reader's view of the data."});continue}let c=Le(o,i);if(!c||c.some(h=>h<0))continue;s.boundBars+=o.length;let l=[];for(let h=0;h<o.length;h+=1)o[h].extent>0&&i.values[c[h]]>0&&l.push(h);if(l.length>=pe){let h=l.map(g=>o[g].extent/i.values[c[g]]).sort((g,y)=>g-y),p=Math.floor(h.length/2),f=h.length%2?h[p]:(h[p-1]+h[p])/2,b=Math.max(...i.values.map(g=>Math.abs(g))),m=f*b;if(f>0&&m>0)for(let g of l){let y=i.values[c[g]],R=f*y,L=Math.abs(o[g].extent-R)/m;if(L<=ue)continue;let E=o[g].unit==="%"?"%":"";v(t,a,{code:w.BAR_PROPORTION,line:k(r,o[g].start),message:`The bar for ${JSON.stringify(i.labels[c[g]])} (value ${y}) is drawn at ${W(o[g].extent)}${E}, but every other bar in this chart puts it at ${W(R)}${E} \u2014 off by ${Math.round(L*100)}% of the full-scale bar. The chart misrepresents the data.`,hint:"Every bar in one chart must use the SAME value\u2192length scale. Compute each length from its datum with one formula (value / max * 100), never by writing the numbers out by hand."})}}let u=i.values.reduce((h,p)=>h+p,0),d=Math.max(...i.values.map(h=>Math.abs(h)));for(let h=0;h<o.length;h+=1){let p=i.values[c[h]],f=o[h].context;if(!f)continue;let b=(f.match(/-?\d[\d,]*(?:\.\d+)?/g)||[]).map(m=>Number(m.replace(/,/g,""))).filter(m=>Number.isFinite(m));b.length&&(b.some(m=>Math.abs(m-p)<1e-9)||u>0&&b.some(m=>Math.abs(m-p/u*100)<.6)||d>0&&b.some(m=>Math.abs(m-p/d*100)<.6)||v(t,a,{code:w.VALUE_MISMATCH,line:k(r,o[h].start),message:`The row for ${JSON.stringify(i.labels[c[h]])} prints ${b.slice(0,3).join(", ")} but its datum is ${p}.`,hint:"Print the figure your tools returned, unchanged, and use that same figure everywhere it appears on the page."}))}}}var He=/^(flex|inline-flex|grid|inline-grid)$/;function Be(r,n,e,t,a,s){let o=0;T(n,i=>{if(M.has(i.tag))return;let c=_(i.attrs.style).width;if(!c)return;let l=V(c);if(l==null||l<=0||!G(i,e))return;o+=1;let u=F(i,e);if(u==="none"){v(t,a,{code:w.ZERO_WIDTH,line:k(r,i.start),message:`<${i.tag}> is given style="width:${c}" but its effective display is \`none\` \u2014 it is not rendered at all.`,hint:"Remove the display:none rule that matches this element, or remove the element. A width on a display:none box is dead code."});return}if(u!=="inline")return;let d=i.parent;if(d&&d.tag!=="#root"&&He.test(F(d,e)))return;let h=S(i,"position",e);if(h==="absolute"||h==="fixed")return;let p=S(i,"float",e);p==="left"||p==="right"||v(t,a,{code:w.ZERO_WIDTH,line:k(r,i.start),message:`<${i.tag}${i.attrs.class?` class="${i.attrs.class}"`:""}> is given style="width:${c}" but its effective display is \`inline\` \u2014 CSS IGNORES width on a non-replaced inline box, so this bar renders with no length at all. The source looks right; the page will not.`,hint:"Give the bar display:block or display:inline-block (or make its container display:flex). A <span> is inline by DEFAULT \u2014 that alone is enough to swallow the width."})}),s.percentWidthBars=o}function Ue(r,n,e,t,a){T(n,s=>{let o=U(S(s,"width",e));if(!(o==null||o<=0))for(let i=s.parent;i&&i.tag!=="#root";i=i.parent){let c=U(S(i,"width",e));if(c==null||c<=0)continue;if(o<=c)return;let l=S(i,"overflow",e)||S(i,"overflow-x",e);if(l!=="hidden"&&l!=="clip")return;v(t,a,{code:w.OVERFLOW,line:k(r,s.start),message:`<${s.tag}> is ${o}px wide inside a ${c}px ancestor with overflow:${l} \u2014 ${Math.round(o-c)}px of it is clipped and can never be seen.`,hint:"Size the child from its container (a percentage, or max-width:100%), or widen the container. Content the reader cannot see is content you did not publish."});return}})}var Me=[{re:/\[object (?:Object|Array|Promise|Map|Set|Null|Undefined)\]/g,what:"an object stringified instead of a field being picked off it"},{re:/\$\{[^}\n]{0,80}\}/g,what:"an un-substituted JavaScript template placeholder"},{re:/\{\{[^}\n]{0,80}\}\}/g,what:"an un-substituted moustache placeholder"},{re:/(?:^|[^\w.])(NaN)(?![\w])/g,what:"NaN \u2014 an arithmetic result that was never a number"},{re:/(?:^|[^\w.])(-?Infinity)(?![\w])/g,what:"Infinity \u2014 a division by zero reached the page"}],je=/^(undefined|null)$/i,De=new Set(["td","th","li","span","div","strong","em","b"]);function Je(r,n,e,t,a){let s=0;for(let o of ve(n)){let i=o.value;s+=i.length;for(let l of Me){l.re.lastIndex=0;let u;for(;(u=l.re.exec(i))!==null;){let d=u[1]==null?u[0]:u[1];v(e,t,{code:w.PLACEHOLDER_TEXT,line:k(r,o.start+u.index+u[0].indexOf(d)),message:`The page prints ${JSON.stringify(d.slice(0,60))} as VISIBLE TEXT \u2014 ${l.what}. A reader sees this literally.`,hint:"Format the value before it reaches the page: pick the field you meant off the object, and guard the arithmetic so a missing input renders as an em dash rather than NaN."}),u.index===l.re.lastIndex&&(l.re.lastIndex+=1)}}let c=Z(i).trim();je.test(c)&&De.has(o.parent.tag)&&v(e,t,{code:w.PLACEHOLDER_TEXT,line:k(r,o.start),message:`A <${o.parent.tag}> contains only the literal text "${c}" \u2014 the value it was meant to hold was ${c}.`,hint:'Render an em dash (\u2014) or "n/a" for a missing value; never let the JavaScript literal reach the reader.'})}a.textCharsScanned=s}function K({html:r,data:n}){let e=typeof r=="string"?r:"",t=[],a={};if(!e.trim())return{ok:!1,defects:[{code:"empty-content",line:1,message:"No html was passed to the check.",hint:"Pass the exact page source you are about to publish."}],checked:{tables:0,bodyRows:0,bars:0,barGroups:[],boundBars:0,seriesInData:0,collections:[]}};let s=ke(e),o=Se(s),i=Ie(s),c=Ae(s,o),l=Oe(n),u={tables:i.length,bodyRows:i.reduce((d,h)=>d+h.rows.length,0),bars:c.reduce((d,h)=>d+h.length,0),barGroups:[],boundBars:0,seriesInData:l.series.length,collections:[]};return Ce(e,s,i,l.collections,t,a,u.collections),$e(e,c,l.series,t,a,u),Be(e,s,o,t,a,u),Ue(e,s,o,t,a),Je(e,s,t,a,u),t.sort((d,h)=>(d.line||0)-(h.line||0)),{ok:t.length===0,defects:t,checked:u}}function ze(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Ve(Ke(import.meta.url)),n=Ge(r,"..","bin","mcp-skill.mjs");return oe(n)?n:null}function I(){if(process.env.PROJECT_API_TOKEN)return process.env.PROJECT_API_TOKEN;if(process.env.ZIBBY_USER_TOKEN)return process.env.ZIBBY_USER_TOKEN;try{let r=Ye(Ze(),".zibby","config.json");return oe(r)&&JSON.parse(Fe(r,"utf-8")).sessionToken||null}catch{return null}}function C(){return process.env.ZIBBY_ACCOUNT_API_URL?process.env.ZIBBY_ACCOUNT_API_URL.replace(/\/$/,""):(process.env.ZIBBY_ENV||"prod")==="local"?"http://localhost:3001":process.env.ZIBBY_PROD_ACCOUNT_API_URL||"https://api-prod.zibby.app"}function qe(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function ie(r){return`${qe()}:artifact:${r}`}var x="headings, paragraphs, **bold**, *italic*, `inline code`, fenced code blocks, bullet + numbered lists, blockquotes, horizontal rules, links, and GitHub-style pipe tables (including |---:| alignment)",B="raw HTML (tags are escaped and print as literal text), images, nested lists, footnotes and strikethrough",A=2,N=0;function bt(){N=0}function D(r,n){return JSON.stringify({published:!1,stop:!0,attemptsUsed:A,defects:n||[],error:r,instruction:`STOP. You have used both publish attempts and the page is still rejected. Do NOT call artifact_publish or artifact_update again in this turn. Tell the user plainly that you could not produce a clean page, say in one line what was wrong, and give them the content directly in the chat instead. A broken link is worse than an honest "I couldn't".`})}function Xe(r,n,e){return JSON.stringify({published:!1,stop:!1,attemptsRemaining:A-N,defects:e||[],error:n,instruction:"The page was NOT published and no URL exists. Fix EXACTLY the defects listed above and call once more \u2014 you get ONE more attempt, then this tool will stop you."+(r==="html"?` You are on the \`html\` path, where you author the entire document and every mistake ships. Unless this page genuinely needs a custom visual layout, the fastest and most reliable fix is to re-send the SAME content as \`markdown\` \u2014 Zibby then generates the document skeleton, CSS and tables for you and these defects become impossible. Markdown supports ${x}.`:` Markdown supports ${x}; it does NOT support ${B}.`)})}var z="The page WAS published and the url is valid. This only means the data\u2194render pre-flight could not reach a verdict \u2014 nothing is known to be wrong. Open the page yourself if the figures matter.";function Qe(r){let n=new Set,e=(r||[]).map((a,s)=>{let o=a.line?` line ${a.line}:`:"",i="";return a.hint&&!n.has(a.code)&&(n.add(a.code),i=` \u2014 ${a.hint}`),`${s+1}. [${a.code}]${o} ${a.message}${i}`}),t=e.length;return`Artifact NOT published and NOTHING was sent \u2014 the page was checked against the \`data\` you passed and disagrees with it in ${t} place${t===1?"":"s"}:
2
2
  ${e.join(`
3
- `)}`}function et(r,n){let e;try{e=z({html:r,data:n})}catch(o){return{unverified:{status:"unverified",reason:`the data\u2194render pre-flight threw and was skipped: ${String(o?.message||o).slice(0,200)}`,note:V}}}if(!e?.ok)return{rejected:{message:Qe(e?.defects),defects:e?.defects||[]}};let t=e.checked||{};return(Number(t.tables)||0)+(Number(t.bars)||0)===0?{unverified:{status:"unverified",reason:"the pre-flight found no tables and no bars in this page, so it had nothing to compare against your data \u2014 a clean result here proves nothing",checked:t,note:V}}:null}function q(r,n,e){return T+=1,{__rejected:!0,payload:T>=A?D(n,e):Xe(r,n,e)}}async function tt(r){let n=I();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${C()}/credits/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!e.ok){let t=await e.text().catch(()=>""),a=null;try{a=JSON.parse(t)}catch{}let o=new Error(e.status===422&&a?.error?a.error:`artifact write failed (${e.status}): ${t.slice(0,300)}`);throw o.status=e.status,Array.isArray(a?.defects)&&(o.defects=a.defects),o}return e.json()}async function X(r,n,e){let t=e?et(e.html,e.data):null;if(t?.rejected)return q(n,t.rejected.message,t.rejected.defects);try{let a=await tt(r);return T=0,t?.unverified&&(a.__preflightNote=t.unverified),a}catch(a){if(a?.status!==422)throw a;return q(n,a.message,a.defects)}}async function oe(r){let n=I();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${C()}/credits/artifacts/${encodeURIComponent(r)}`,{method:"GET",headers:{Authorization:`Bearer ${n}`}});if(!e.ok){let t=await e.text().catch(()=>"");throw new Error(`artifact get failed (${e.status}): ${t.slice(0,300)}`)}return e.json()}async function Q(r,n,e={}){let t=I();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let a=await fetch(`${C()}/credits/artifacts/${encodeURIComponent(r)}/${n}`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok){let o=await a.text().catch(()=>""),i=null;try{i=JSON.parse(o)}catch{}throw new Error(i?.error||`artifact ${n} failed (${a.status}): ${o.slice(0,300)}`)}return a.json()}var j=60;function ee(r,n){let e=Math.max(0,n-Math.floor(j/2)),a=r.slice(e,e+j).replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t");return(e>0?"\u2026":"")+a+(e+j<r.length?"\u2026":"")}function nt(r,n){if(r===n)return null;let e=Math.min(r.length,n.length),t=0;for(;t<e&&r[t]===n[t];)t+=1;return{kind:t===e?n.length<r.length?"truncated":"longer":"diverged",sentChars:r.length,storedChars:n.length,delta:n.length-r.length,firstDiffAt:t,sentExcerpt:ee(r,t),storedExcerpt:ee(n,t)}}var H="Read-back compares the STORED SOURCE with what was sent (transport/storage integrity only). It does NOT check that the page renders correctly.";async function te(r,n){let e;try{e=await oe(r)}catch(o){return{status:"unverified",scope:H,reason:`read-back request failed: ${String(o?.message||o).slice(0,200)}`,note:"The page WAS published and the url is valid \u2014 this only means the extra integrity check could not run. No action needed."}}let t=e?.content;if(typeof t!="string")return{status:"unverified",scope:H,reason:"read-back returned no content field",note:"The page WAS published and the url is valid \u2014 this only means the extra integrity check could not run. No action needed."};if(t===""&&n!=="")return{status:"unverified",scope:H,reason:"read-back returned empty content, which the backend also returns when its object-store read errors \u2014 indistinguishable, so this is inconclusive rather than a mismatch",note:"The page WAS published and the url is valid. If the page looks blank when opened, re-send the same content with artifact_update."};let a=nt(n,t);return a?{status:"mismatch",scope:H,...a,note:`The stored page source is NOT what was sent (${a.kind}: sent ${a.sentChars} chars, stored ${a.storedChars}, first difference at character ${a.firstDiffAt}). The url exists and the page is live, so do NOT re-publish a new one. You may call artifact_update ONCE with the identical content to rewrite the blob; if it differs again, stop and tell the user the published page may be incomplete rather than retrying.`}:null}async function ne(r,n){let e=I();if(!e)return;let t=await fetch(`${C()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:se(r),content:JSON.stringify(n)})});if(!t.ok){let a=await t.text().catch(()=>"");throw new Error(`artifact index write failed (${t.status}): ${a.slice(0,200)}`)}}async function rt(r){let n=I();if(!n)return null;let e=await fetch(`${C()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:se(r)})});if(!e.ok)return null;let t=await e.json().catch(()=>null);if(!t?.found||!t?.memory?.content)return null;try{return JSON.parse(t.memory.content)}catch{return null}}function re(r){let n=typeof r?.html=="string"&&r.html.length>0,e=typeof r?.markdown=="string"&&r.markdown.length>0;return n&&e?{error:"Pass EITHER markdown OR html, not both \u2014 they are different rendering paths. Use markdown unless the page needs a custom visual layout."}:e?{format:"markdown",content:r.markdown}:n?{format:"html",content:r.html}:null}function ae(r,n){let e=r?.data;if(e==null)return null;if(!n||n.format!=="html")return{error:"`data` is only meaningful together with `html`. The pre-flight reads a rendered HTML document (tables, bars, CSS), so on the markdown path it would check nothing while reporting a clean page \u2014 that is worse than not checking. Re-send WITHOUT `data`. Do NOT switch to `html` just to get the check: on the markdown path Zibby generates the document, so the collapsed-bar and broken-geometry defects it looks for cannot happen there in the first place."};let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch{return{error:'`data` was a string but not valid JSON \u2014 pass the tool results as a JSON object (wrap a bare array as {"rows": [...]}).'}}return{preflight:{html:n.content,data:t}}}var wt={id:"artifact",callsBackend:!0,meta:We.artifact,serverName:"artifact",allowedTools:["mcp__artifact__*"],description:"Artifacts \u2014 publish a self-contained, shareable HTML/Markdown page to Zibby and get back a URL; the index of what you published is your memory.",promptFragment:`## Artifacts (publish a shareable page, remember what you made)
3
+ `)}`}function et(r,n){let e;try{e=K({html:r,data:n})}catch(s){return{unverified:{status:"unverified",reason:`the data\u2194render pre-flight threw and was skipped: ${String(s?.message||s).slice(0,200)}`,note:z}}}if(!e?.ok)return{rejected:{message:Qe(e?.defects),defects:e?.defects||[]}};let t=e.checked||{};return(Number(t.tables)||0)+(Number(t.bars)||0)===0?{unverified:{status:"unverified",reason:"the pre-flight found no tables and no bars in this page, so it had nothing to compare against your data \u2014 a clean result here proves nothing",checked:t,note:z}}:null}function q(r,n,e){return N+=1,{__rejected:!0,payload:N>=A?D(n,e):Xe(r,n,e)}}async function tt(r){let n=I();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${C()}/credits/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!e.ok){let t=await e.text().catch(()=>""),a=null;try{a=JSON.parse(t)}catch{}let s=new Error(e.status===422&&a?.error?a.error:`artifact write failed (${e.status}): ${t.slice(0,300)}`);throw s.status=e.status,Array.isArray(a?.defects)&&(s.defects=a.defects),s}return e.json()}async function X(r,n,e){let t=e?et(e.html,e.data):null;if(t?.rejected)return q(n,t.rejected.message,t.rejected.defects);try{let a=await tt(r);return N=0,t?.unverified&&(a.__preflightNote=t.unverified),a}catch(a){if(a?.status!==422)throw a;return q(n,a.message,a.defects)}}async function se(r){let n=I();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${C()}/credits/artifacts/${encodeURIComponent(r)}`,{method:"GET",headers:{Authorization:`Bearer ${n}`}});if(!e.ok){let t=await e.text().catch(()=>"");throw new Error(`artifact get failed (${e.status}): ${t.slice(0,300)}`)}return e.json()}async function Q(r,n,e={}){let t=I();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let a=await fetch(`${C()}/credits/artifacts/${encodeURIComponent(r)}/${n}`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok){let s=await a.text().catch(()=>""),o=null;try{o=JSON.parse(s)}catch{}throw new Error(o?.error||`artifact ${n} failed (${a.status}): ${s.slice(0,300)}`)}return a.json()}var j=60;function ee(r,n){let e=Math.max(0,n-Math.floor(j/2)),a=r.slice(e,e+j).replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t");return(e>0?"\u2026":"")+a+(e+j<r.length?"\u2026":"")}function nt(r,n){if(r===n)return null;let e=Math.min(r.length,n.length),t=0;for(;t<e&&r[t]===n[t];)t+=1;return{kind:t===e?n.length<r.length?"truncated":"longer":"diverged",sentChars:r.length,storedChars:n.length,delta:n.length-r.length,firstDiffAt:t,sentExcerpt:ee(r,t),storedExcerpt:ee(n,t)}}var H="Read-back compares the STORED SOURCE with what was sent (transport/storage integrity only). It does NOT check that the page renders correctly.";async function te(r,n){let e;try{e=await se(r)}catch(s){return{status:"unverified",scope:H,reason:`read-back request failed: ${String(s?.message||s).slice(0,200)}`,note:"The page WAS published and the url is valid \u2014 this only means the extra integrity check could not run. No action needed."}}let t=e?.content;if(typeof t!="string")return{status:"unverified",scope:H,reason:"read-back returned no content field",note:"The page WAS published and the url is valid \u2014 this only means the extra integrity check could not run. No action needed."};if(t===""&&n!=="")return{status:"unverified",scope:H,reason:"read-back returned empty content, which the backend also returns when its object-store read errors \u2014 indistinguishable, so this is inconclusive rather than a mismatch",note:"The page WAS published and the url is valid. If the page looks blank when opened, re-send the same content with artifact_update."};let a=nt(n,t);return a?{status:"mismatch",scope:H,...a,note:`The stored page source is NOT what was sent (${a.kind}: sent ${a.sentChars} chars, stored ${a.storedChars}, first difference at character ${a.firstDiffAt}). The url exists and the page is live, so do NOT re-publish a new one. You may call artifact_update ONCE with the identical content to rewrite the blob; if it differs again, stop and tell the user the published page may be incomplete rather than retrying.`}:null}async function ne(r,n){let e=I();if(!e)return;let t=await fetch(`${C()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:ie(r),content:JSON.stringify(n)})});if(!t.ok){let a=await t.text().catch(()=>"");throw new Error(`artifact index write failed (${t.status}): ${a.slice(0,200)}`)}}async function rt(r){let n=I();if(!n)return null;let e=await fetch(`${C()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:ie(r)})});if(!e.ok)return null;let t=await e.json().catch(()=>null);if(!t?.found||!t?.memory?.content)return null;try{return JSON.parse(t.memory.content)}catch{return null}}function re(r){let n=typeof r?.html=="string"&&r.html.length>0,e=typeof r?.markdown=="string"&&r.markdown.length>0,t=typeof r?.text=="string"&&r.text.length>0;return t&&(n||e)?{error:"Pass exactly ONE of markdown, html or text \u2014 text is raw source stored as-is (code/logs/JSON/CSV); markdown and html are rendered documents."}:n&&e?{error:"Pass EITHER markdown OR html, not both \u2014 they are different rendering paths. Use markdown unless the page needs a custom visual layout."}:e?{format:"markdown",content:r.markdown}:n?{format:"html",content:r.html}:t?{format:"text",content:r.text}:null}function ae(r,n){let e=r?.data;if(e==null)return null;if(!n||n.format!=="html")return{error:"`data` is only meaningful together with `html`. The pre-flight reads a rendered HTML document (tables, bars, CSS), so on the markdown path it would check nothing while reporting a clean page \u2014 that is worse than not checking. Re-send WITHOUT `data`. Do NOT switch to `html` just to get the check: on the markdown path Zibby generates the document, so the collapsed-bar and broken-geometry defects it looks for cannot happen there in the first place."};let t=e;if(typeof t=="string")try{t=JSON.parse(t)}catch{return{error:'`data` was a string but not valid JSON \u2014 pass the tool results as a JSON object (wrap a bare array as {"rows": [...]}).'}}return{preflight:{html:n.content,data:t}}}var wt={id:"artifact",callsBackend:!0,meta:We.artifact,serverName:"artifact",allowedTools:["mcp__artifact__*"],description:"Artifacts \u2014 publish a self-contained, shareable HTML/Markdown page to Zibby and get back a URL; the index of what you published is your memory.",promptFragment:`## Artifacts (publish a shareable page, remember what you made)
4
4
  You can PUBLISH a standalone page \u2014 a status report, a plan, a comparison table,
5
5
  a dashboard-y summary, a diagram, a "here's what I found" write-up \u2014 and get back
6
6
  a shareable URL.
@@ -8,7 +8,7 @@ a shareable URL.
8
8
  ### WRITE IT AS MARKDOWN. That is the default, and it is not a stylistic hint.
9
9
  Pass \`markdown\` and Zibby generates the whole document for you \u2014 doctype, head,
10
10
  CSS, typography, dark mode, table styling. You supply only the CONTENT. Markdown
11
- supports ${R}. It does NOT support ${B}.
11
+ supports ${x}. It does NOT support ${B}.
12
12
 
13
13
  Pass \`html\` ONLY when the page genuinely needs a visual layout Markdown cannot
14
14
  express \u2014 a bar chart, a gauge, a custom grid, an SVG diagram. On the html path
@@ -66,9 +66,13 @@ Tools:
66
66
  page renders a dataset (see above). Optional \`kind\` (e.g. "report", "plan",
67
67
  "dashboard"), \`favicon\` (an emoji), and \`summary\` (one line for your own
68
68
  index). Returns { id, url }. Share the url; keep the id if you'll update it
69
- later.
69
+ later. To publish RAW SOURCE instead of a page \u2014 code, a script, logs, JSON
70
+ data, CSV \u2014 pass \`text\` + \`contentType\` (text/plain | application/json |
71
+ text/csv): it is stored verbatim and shown in a read-only code viewer. For
72
+ code, make the \`title\` the filename (e.g. "deploy.sh") and set kind "code".
70
73
  - artifact_update: Revise an EXISTING page by \`id\` (same url, new version). Pass
71
- the fields to change (\`title\`, \`markdown\`|\`html\`).
74
+ the fields to change (\`title\`, \`markdown\`|\`html\`|\`text\` \u2014 text only onto a
75
+ text artifact, same type).
72
76
  - artifact_get: Fetch one artifact by \`id\` \u2192 { metadata, content } so you can
73
77
  reuse / edit / re-publish it.
74
78
  - artifact_share: Make a page readable WITHOUT a Zibby login \u2192 { url, password }.
@@ -99,4 +103,4 @@ sharing it silently.
99
103
  To recall WHAT YOU HAVE ALREADY PUBLISHED, use your kv-memory tool
100
104
  kv_recall_prefix with keyPrefix "artifact:" \u2014 each entry is the index record
101
105
  { id, title, url, kind, createdAt, summary } for a page you made. (Publishing
102
- records this automatically; you don't store it yourself.)`,resolve(){let r=Ve();if(!r)return{command:null,args:[],env:{},description:this.description};let n={};for(let e of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[e]&&(n[e]=process.env[e]);return{type:"stdio",command:"node",args:[r,"../dist/artifact.js","artifactSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,n){try{switch(r){case"artifact_publish":{if(T>=A)return D("Publish budget already spent this turn.",[]);let e=typeof n?.title=="string"?n.title.trim():"";if(!e)return JSON.stringify({error:"title is required"});let t=re(n);if(!t)return JSON.stringify({error:"provide exactly one of markdown (preferred) or html (non-empty string)"});if(t.error)return JSON.stringify({error:t.error});let a=ae(n,t);if(a?.error)return JSON.stringify({error:a.error});let o={title:e,[t.format]:t.content};typeof n?.kind=="string"&&n.kind.trim()&&(o.kind=n.kind.trim()),typeof n?.favicon=="string"&&n.favicon.trim()&&(o.favicon=n.favicon.trim()),n?.share===!0&&(o.share=!0);let i=await X(o,t.format,a?.preflight);if(i?.__rejected)return i.payload;let s=i?.id,c=i?.url;if(!s||!c)return JSON.stringify({error:"artifact write returned no id/url",response:i});let l={id:s,url:c};typeof i.rn=="string"&&i.rn&&(l.rn=i.rn),i.shared&&(l.shared=!0,l.shareUrl=i.shareUrl,l.password=i.password,l.instruction="Send BOTH the shareUrl and the password in your reply. Copy the password EXACTLY \u2014 it is case-sensitive, shown once, and cannot be looked up again (artifact_share issues a new one, which kills this one)."),i.__preflightNote&&(l.preflight=i.__preflightNote);let u=await te(s,t.content);u&&(l.readBack=u);let h={id:s,title:e,url:c,kind:o.kind||null,createdAt:i.createdAt||new Date().toISOString(),summary:typeof n?.summary=="string"&&n.summary.trim()?n.summary.trim():e};try{await ne(s,h)}catch(d){l.indexWarning=d.message}return JSON.stringify(l)}case"artifact_update":{if(T>=A)return D("Publish budget already spent this turn.",[]);let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=re(n);if(t?.error)return JSON.stringify({error:t.error});let a=typeof n?.title=="string"?n.title.trim():"";if(!t&&!a)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or markdown|html"});let o=ae(n,t);if(o?.error)return JSON.stringify({error:o.error});let i={id:e};a&&(i.title=a),t&&(i[t.format]=t.content),typeof n?.kind=="string"&&n.kind.trim()&&(i.kind=n.kind.trim()),typeof n?.favicon=="string"&&n.favicon.trim()&&(i.favicon=n.favicon.trim());let s=await X(i,t?.format,o?.preflight);if(s?.__rejected)return s.payload;let c=s?.url;if(!c)return JSON.stringify({error:"artifact update returned no url",response:s});let l={id:e,url:c};if(typeof s.rn=="string"&&s.rn&&(l.rn=s.rn),s.__preflightNote&&(l.preflight=s.__preflightNote),t){let d=await te(e,t.content);d&&(l.readBack=d)}let u=await rt(e)||{},h={...u,id:e,url:c,title:a||u.title||"Untitled",kind:i.kind||u.kind||null,createdAt:u.createdAt||s.createdAt||new Date().toISOString(),updatedAt:s.updatedAt||new Date().toISOString()};typeof n?.summary=="string"&&n.summary.trim()?h.summary=n.summary.trim():h.summary||(h.summary=h.title);try{await ne(e,h)}catch(d){l.indexWarning=d.message}return JSON.stringify(l)}case"artifact_get":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await oe(e);return JSON.stringify(t)}case"artifact_share":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await Q(e,"protect");return JSON.stringify({id:t.id,url:t.url,password:t.password,shared:!0,instruction:"Send BOTH the url and the password to the person who asked. Copy the password EXACTLY \u2014 it is case-sensitive and cannot be recovered later (call artifact_share again to issue a new one)."})}case"artifact_unshare":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await Q(e,"unprotect");return JSON.stringify({id:t.id,shared:!1,wasShared:t.wasShared})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"artifact_publish",description:`Publish a NEW self-contained, shareable page (report/plan/table/dashboard/diagram/write-up) and get back a shareable URL. USE \`markdown\` \u2014 it is the default path and Zibby generates the entire document for you (doctype, head, CSS, dark mode, table styling); you supply only the content, so the page cannot come out malformed. Markdown supports ${R}; it does NOT support ${B}. Use \`html\` ONLY when the page needs a visual layout Markdown cannot express (a bar chart, a gauge, a custom grid, an SVG diagram) \u2014 on that path you author the whole document and every mistake ships verbatim. Never pass both. Every figure on the page must come from a tool result or this conversation \u2014 do not do arithmetic in prose and print the result, and use the same number everywhere it appears. The content MUST come from the current conversation or data you fetched this turn; if you are not sure what the page should be about, ASK instead of calling this tool, and never publish a demo/sample/placeholder or an account-overview page as a stand-in. On the html path keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) and send real tags (never &lt;escaped&gt; markup) \u2014 the page is sandboxed on view with no network at all, so every external URL is dead. PASS \`data\` WHENEVER THE HTML PAGE RENDERS A DATASET \u2014 a table of records, a bar chart, a row of meters. Give it the raw tool results the page was built from, unchanged; the page is then checked AGAINST that data before anything is sent, catching a table short of rows, a bar drawn at the wrong length, and leftover \${\u2026} or [object Object]. It costs you nothing when the page is right. Skip it for prose pages, and never pass it with \`markdown\` (that is an error, not a no-op). The content is VALIDATED before any URL exists: if it is broken \u2014 by that data check or by the server \u2014 you get the exact defects and line numbers back, you get ONE retry, and then the tool stops you. Both kinds of rejection share the SAME two attempts. Returns { id, url }. PASS \`share: true\` WHEN THE READERS HAVE NO ZIBBY ACCOUNT \u2014 you get back { shareUrl, password } in this same call, instead of publishing a link they cannot open and then having to call artifact_share. The password is GENERATED; you never invent one. Without \`share\` the page is member-only, which is the default and is correct for anything internal.`,input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},markdown:{type:"string",description:`PREFERRED. The page content as Markdown \u2014 Zibby renders it into a styled, self-contained document, so you never author a skeleton. Supports ${R}. Does NOT support ${B}. Provide this OR html, never both.`},html:{type:"string",description:"The EXCEPTION path \u2014 only for a layout Markdown cannot express (bar chart, gauge, custom grid, SVG diagram). A self-contained HTML document or fragment, served VERBATIM: you own the doctype, head, CSS and every tag. All assets must be inline (<style>/<script>, data: URIs). Provide this OR markdown, never both."},data:{type:"object",description:'Optional, and worth passing WHENEVER the html page renders a dataset (a table of records, a bar chart, meters): the raw tool results the page was built from, as JSON, UNCHANGED \u2014 do not summarise it and do not write your own expectations, the check derives what must be true by itself (wrap a bare array as {"rows": [...]}). The page is then verified against it BEFORE publishing: row counts, bar lengths vs their values, bars whose width the CSS throws away, and placeholder residue. If they disagree nothing is sent and you get the defects back, using one of your two attempts. Only valid with `html` \u2014 passing it with `markdown` is an error.'},kind:{type:"string",description:'Optional label for what this is, e.g. "report", "plan", "dashboard", "diagram". Stored in your index.'},favicon:{type:"string",description:'Optional emoji used as the browser-tab icon, e.g. "\u{1F4CA}".'},summary:{type:"string",description:"Optional one-line summary for your own index (defaults to the title). Helps you recall later what this page was."},share:{type:"boolean",description:"Set true to publish the page WITH a password link in one step \u2014 the response then also carries { shareUrl, password }, which you send together. Use it whenever the people who asked are in a chat channel and have no Zibby account. Omit it (the default) for anything internal: the page is then readable only by logged-in members of this project, and no password exists."}},required:["title"]}},{name:"artifact_update",description:"Revise an EXISTING artifact by id \u2014 the shareable URL stays the same, the content is replaced (new version). Pass the fields to change (title and/or markdown|html). Prefer `markdown` for the same reason artifact_publish does: Zibby generates the document, so it cannot come out malformed. An update is validated exactly like a publish \u2014 including the optional `data` check when you send new `html` that renders a dataset \u2014 and if the new content is broken it is REFUSED, the page keeps its last good version, and the rejection shares the same two attempts as publish. Returns { id, url }.",input_schema:{type:"object",properties:{id:{type:"string",description:'The id of the artifact to update (from a prior artifact_publish, or your kv-memory "artifact:" index).'},title:{type:"string",description:"New title (optional)."},markdown:{type:"string",description:`PREFERRED. New Markdown content (optional) \u2014 Zibby renders the document skeleton. Supports ${R}. Provide this OR html.`},html:{type:"string",description:"New HTML content (optional), served verbatim \u2014 only for layouts Markdown cannot express. Provide this OR markdown."},data:{type:"object",description:"Optional \u2014 same as on artifact_publish: when the new `html` renders a dataset, pass the raw tool results it was built from and the page is checked against them before anything is sent. Only valid alongside new `html`."},kind:{type:"string",description:"Optional updated kind label."},favicon:{type:"string",description:"Optional updated emoji favicon."},summary:{type:"string",description:"Optional updated one-line index summary."}},required:["id"]}},{name:"artifact_get",description:'Fetch ONE artifact you published, by id \u2192 { metadata, content }. Use to reuse / edit / re-publish a page. To LIST what you have published, use your kv-memory tool kv_recall_prefix with keyPrefix "artifact:".',input_schema:{type:"object",properties:{id:{type:"string",description:"The artifact id."}},required:["id"]}},{name:"artifact_share",description:"Make an artifact viewable WITHOUT a Zibby login, by password. Returns { url, password } \u2014 a link anyone can open plus the password that opens it. USE THIS when the people who need to read the page do not have Zibby accounts \u2014 the usual case is a Lark or Slack channel where you were asked for something and the channel members are not Zibby users. The normal artifact url only opens for logged-in members of this project, so for those recipients it is a dead link; this is what makes it readable. You MUST then send BOTH the url and the password in your reply, and copy the password EXACTLY (it is case-sensitive). It is shown ONCE and cannot be looked up afterwards \u2014 if it is lost, call this again to issue a new one, which also stops the old one working. Works on any artifact you published, old or new. Calling it again on an already-shared artifact replaces the password. Use artifact_unshare to revoke.",input_schema:{type:"object",properties:{id:{type:"string",description:'The id of the artifact to share (from artifact_publish, or your kv-memory "artifact:" index).'}},required:["id"]}},{name:"artifact_unshare",description:`REMOVE an artifact's password \u2014 this is the tool for "take the password off", "remove the password", "stop sharing" and "revoke the link", which are all the same act. The page goes back to the DEFAULT it was published with: viewable only by logged-in members of this project, with no password anywhere. Immediate and total: anyone who already entered the password loses access too, not just new visitors. Safe to call on an artifact that was never shared. Note what this does NOT do: there is no way to leave a page open to anyone with the link and no password. Removing the password closes the anonymous door; it does not prop it open. Say that plainly if you are asked for a passwordless public link.`,input_schema:{type:"object",properties:{id:{type:"string",description:"The artifact id to stop sharing."}},required:["id"]}}]};export{nt as __compareStoredSource,bt as __resetPublishBudget,wt as artifactSkill};
106
+ records this automatically; you don't store it yourself.)`,resolve(){let r=ze();if(!r)return{command:null,args:[],env:{},description:this.description};let n={};for(let e of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[e]&&(n[e]=process.env[e]);return{type:"stdio",command:"node",args:[r,"../dist/artifact.js","artifactSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,n){try{switch(r){case"artifact_publish":{if(N>=A)return D("Publish budget already spent this turn.",[]);let e=typeof n?.title=="string"?n.title.trim():"";if(!e)return JSON.stringify({error:"title is required"});let t=re(n);if(!t)return JSON.stringify({error:"provide exactly one of markdown (preferred) or html or text (non-empty string)"});if(t.error)return JSON.stringify({error:t.error});let a=ae(n,t);if(a?.error)return JSON.stringify({error:a.error});if(t.format==="text"&&!(typeof n?.contentType=="string"?n.contentType.trim():""))return JSON.stringify({error:"contentType is required with text \u2014 text/plain for code/logs, application/json for JSON data, text/csv for CSV"});let s={title:e,[t.format]:t.content};t.format==="text"&&(s.contentType=n.contentType.trim()),typeof n?.kind=="string"&&n.kind.trim()&&(s.kind=n.kind.trim()),typeof n?.favicon=="string"&&n.favicon.trim()&&(s.favicon=n.favicon.trim()),n?.share===!0&&(s.share=!0);let o=await X(s,t.format,a?.preflight);if(o?.__rejected)return o.payload;let i=o?.id,c=o?.url;if(!i||!c)return JSON.stringify({error:"artifact write returned no id/url",response:o});let l={id:i,url:c};typeof o.rn=="string"&&o.rn&&(l.rn=o.rn),o.shared&&(l.shared=!0,l.shareUrl=o.shareUrl,l.password=o.password,l.instruction="Send BOTH the shareUrl and the password in your reply. Copy the password EXACTLY \u2014 it is case-sensitive, shown once, and cannot be looked up again (artifact_share issues a new one, which kills this one)."),o.__preflightNote&&(l.preflight=o.__preflightNote);let u=await te(i,t.content);u&&(l.readBack=u);let d={id:i,title:e,url:c,kind:s.kind||null,createdAt:o.createdAt||new Date().toISOString(),summary:typeof n?.summary=="string"&&n.summary.trim()?n.summary.trim():e};t.format==="text"?(d.contentType=typeof o.contentType=="string"?o.contentType:s.contentType,d.bytes=typeof o.bytes=="number"?o.bytes:Buffer.byteLength(t.content,"utf8")):d.format=t.format;try{await ne(i,d)}catch(h){l.indexWarning=h.message}return JSON.stringify(l)}case"artifact_update":{if(N>=A)return D("Publish budget already spent this turn.",[]);let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=re(n);if(t?.error)return JSON.stringify({error:t.error});let a=typeof n?.title=="string"?n.title.trim():"";if(!t&&!a)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or markdown|html"});let s=ae(n,t);if(s?.error)return JSON.stringify({error:s.error});let o={id:e};a&&(o.title=a),t&&(o[t.format]=t.content),typeof n?.kind=="string"&&n.kind.trim()&&(o.kind=n.kind.trim()),typeof n?.favicon=="string"&&n.favicon.trim()&&(o.favicon=n.favicon.trim());let i=await X(o,t?.format,s?.preflight);if(i?.__rejected)return i.payload;let c=i?.url;if(!c)return JSON.stringify({error:"artifact update returned no url",response:i});let l={id:e,url:c};if(typeof i.rn=="string"&&i.rn&&(l.rn=i.rn),i.__preflightNote&&(l.preflight=i.__preflightNote),t){let h=await te(e,t.content);h&&(l.readBack=h)}let u=await rt(e)||{},d={...u,id:e,url:c,title:a||u.title||"Untitled",kind:o.kind||u.kind||null,createdAt:u.createdAt||i.createdAt||new Date().toISOString(),updatedAt:i.updatedAt||new Date().toISOString()};t&&(t.format==="text"?(d.contentType=typeof i.contentType=="string"?i.contentType:u.contentType||null,d.bytes=typeof i.bytes=="number"?i.bytes:Buffer.byteLength(t.content,"utf8")):d.format=t.format),typeof n?.summary=="string"&&n.summary.trim()?d.summary=n.summary.trim():d.summary||(d.summary=d.title);try{await ne(e,d)}catch(h){l.indexWarning=h.message}return JSON.stringify(l)}case"artifact_get":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await se(e);return JSON.stringify(t)}case"artifact_share":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await Q(e,"protect");return JSON.stringify({id:t.id,url:t.url,password:t.password,shared:!0,instruction:"Send BOTH the url and the password to the person who asked. Copy the password EXACTLY \u2014 it is case-sensitive and cannot be recovered later (call artifact_share again to issue a new one)."})}case"artifact_unshare":{let e=typeof n?.id=="string"?n.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let t=await Q(e,"unprotect");return JSON.stringify({id:t.id,shared:!1,wasShared:t.wasShared})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"artifact_publish",description:`Publish a NEW self-contained, shareable page (report/plan/table/dashboard/diagram/write-up) and get back a shareable URL. USE \`markdown\` \u2014 it is the default path and Zibby generates the entire document for you (doctype, head, CSS, dark mode, table styling); you supply only the content, so the page cannot come out malformed. Markdown supports ${x}; it does NOT support ${B}. Use \`html\` ONLY when the page needs a visual layout Markdown cannot express (a bar chart, a gauge, a custom grid, an SVG diagram) \u2014 on that path you author the whole document and every mistake ships verbatim. Never pass both. Use \`text\` (+ required \`contentType\`) to publish RAW SOURCE as-is \u2014 code, a script, logs, JSON data, CSV. It is stored verbatim, never rendered as a page, and shown in a read-only code viewer with a download link. contentType: text/plain for code/logs/scripts, application/json for JSON data, text/csv for CSV. For code, put the FILENAME in \`title\` (e.g. "deploy.sh") \u2014 viewers derive the language label from the extension \u2014 and set kind: "code". Exactly one of markdown|html|text per call. Every figure on the page must come from a tool result or this conversation \u2014 do not do arithmetic in prose and print the result, and use the same number everywhere it appears. The content MUST come from the current conversation or data you fetched this turn; if you are not sure what the page should be about, ASK instead of calling this tool, and never publish a demo/sample/placeholder or an account-overview page as a stand-in. On the html path keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) and send real tags (never &lt;escaped&gt; markup) \u2014 the page is sandboxed on view with no network at all, so every external URL is dead. PASS \`data\` WHENEVER THE HTML PAGE RENDERS A DATASET \u2014 a table of records, a bar chart, a row of meters. Give it the raw tool results the page was built from, unchanged; the page is then checked AGAINST that data before anything is sent, catching a table short of rows, a bar drawn at the wrong length, and leftover \${\u2026} or [object Object]. It costs you nothing when the page is right. Skip it for prose pages, and never pass it with \`markdown\` (that is an error, not a no-op). The content is VALIDATED before any URL exists: if it is broken \u2014 by that data check or by the server \u2014 you get the exact defects and line numbers back, you get ONE retry, and then the tool stops you. Both kinds of rejection share the SAME two attempts. Returns { id, url }. PASS \`share: true\` WHEN THE READERS HAVE NO ZIBBY ACCOUNT \u2014 you get back { shareUrl, password } in this same call, instead of publishing a link they cannot open and then having to call artifact_share. The password is GENERATED; you never invent one. Without \`share\` the page is member-only, which is the default and is correct for anything internal.`,input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},markdown:{type:"string",description:`PREFERRED. The page content as Markdown \u2014 Zibby renders it into a styled, self-contained document, so you never author a skeleton. Supports ${x}. Does NOT support ${B}. Provide this OR html, never both.`},html:{type:"string",description:"The EXCEPTION path \u2014 only for a layout Markdown cannot express (bar chart, gauge, custom grid, SVG diagram). A self-contained HTML document or fragment, served VERBATIM: you own the doctype, head, CSS and every tag. All assets must be inline (<style>/<script>, data: URIs). Provide this OR markdown, never both."},text:{type:"string",description:'RAW SOURCE published as-is \u2014 code, a script, logs, JSON data, CSV. Stored verbatim (never rendered as an HTML page) and shown in a read-only code viewer with a download link. Requires `contentType`. For code, make `title` the filename (e.g. "deploy.sh" \u2014 the viewer derives the language from the extension) and set kind: "code". Provide this OR markdown OR html.'},contentType:{type:"string",description:"REQUIRED with `text`, meaningless otherwise. One of: text/plain (code, scripts, logs), application/json (JSON data), text/csv (CSV). Nothing else is accepted \u2014 HTML and Markdown are published through their own fields, never as text."},data:{type:"object",description:'Optional, and worth passing WHENEVER the html page renders a dataset (a table of records, a bar chart, meters): the raw tool results the page was built from, as JSON, UNCHANGED \u2014 do not summarise it and do not write your own expectations, the check derives what must be true by itself (wrap a bare array as {"rows": [...]}). The page is then verified against it BEFORE publishing: row counts, bar lengths vs their values, bars whose width the CSS throws away, and placeholder residue. If they disagree nothing is sent and you get the defects back, using one of your two attempts. Only valid with `html` \u2014 passing it with `markdown` is an error.'},kind:{type:"string",description:'Optional label for what this is, e.g. "report", "plan", "dashboard", "diagram". Stored in your index.'},favicon:{type:"string",description:'Optional emoji used as the browser-tab icon, e.g. "\u{1F4CA}".'},summary:{type:"string",description:"Optional one-line summary for your own index (defaults to the title). Helps you recall later what this page was."},share:{type:"boolean",description:"Set true to publish the page WITH a password link in one step \u2014 the response then also carries { shareUrl, password }, which you send together. Use it whenever the people who asked are in a chat channel and have no Zibby account. Omit it (the default) for anything internal: the page is then readable only by logged-in members of this project, and no password exists."}},required:["title"]}},{name:"artifact_update",description:"Revise an EXISTING artifact by id \u2014 the shareable URL stays the same, the content is replaced (new version). Pass the fields to change (title and/or markdown|html|text). `text` only replaces an artifact that was PUBLISHED as text, and it keeps its original contentType \u2014 the server refuses a type change and any doc\u2194text conversion. Prefer `markdown` for the same reason artifact_publish does: Zibby generates the document, so it cannot come out malformed. An update is validated exactly like a publish \u2014 including the optional `data` check when you send new `html` that renders a dataset \u2014 and if the new content is broken it is REFUSED, the page keeps its last good version, and the rejection shares the same two attempts as publish. Returns { id, url }.",input_schema:{type:"object",properties:{id:{type:"string",description:'The id of the artifact to update (from a prior artifact_publish, or your kv-memory "artifact:" index).'},title:{type:"string",description:"New title (optional)."},markdown:{type:"string",description:`PREFERRED. New Markdown content (optional) \u2014 Zibby renders the document skeleton. Supports ${x}. Provide this OR html.`},html:{type:"string",description:"New HTML content (optional), served verbatim \u2014 only for layouts Markdown cannot express. Provide this OR markdown."},text:{type:"string",description:"New raw text content (optional) \u2014 only for an artifact that was PUBLISHED as text. It keeps its original contentType; the server refuses a type change or a doc\u2194text conversion. Provide at most one of markdown|html|text."},data:{type:"object",description:"Optional \u2014 same as on artifact_publish: when the new `html` renders a dataset, pass the raw tool results it was built from and the page is checked against them before anything is sent. Only valid alongside new `html`."},kind:{type:"string",description:"Optional updated kind label."},favicon:{type:"string",description:"Optional updated emoji favicon."},summary:{type:"string",description:"Optional updated one-line index summary."}},required:["id"]}},{name:"artifact_get",description:'Fetch ONE artifact you published, by id \u2192 { metadata, content }. Use to reuse / edit / re-publish a page. To LIST what you have published, use your kv-memory tool kv_recall_prefix with keyPrefix "artifact:".',input_schema:{type:"object",properties:{id:{type:"string",description:"The artifact id."}},required:["id"]}},{name:"artifact_share",description:"Make an artifact viewable WITHOUT a Zibby login, by password. Returns { url, password } \u2014 a link anyone can open plus the password that opens it. USE THIS when the people who need to read the page do not have Zibby accounts \u2014 the usual case is a Lark or Slack channel where you were asked for something and the channel members are not Zibby users. The normal artifact url only opens for logged-in members of this project, so for those recipients it is a dead link; this is what makes it readable. You MUST then send BOTH the url and the password in your reply, and copy the password EXACTLY (it is case-sensitive). It is shown ONCE and cannot be looked up afterwards \u2014 if it is lost, call this again to issue a new one, which also stops the old one working. Works on any artifact you published, old or new. Calling it again on an already-shared artifact replaces the password. Use artifact_unshare to revoke.",input_schema:{type:"object",properties:{id:{type:"string",description:'The id of the artifact to share (from artifact_publish, or your kv-memory "artifact:" index).'}},required:["id"]}},{name:"artifact_unshare",description:`REMOVE an artifact's password \u2014 this is the tool for "take the password off", "remove the password", "stop sharing" and "revoke the link", which are all the same act. The page goes back to the DEFAULT it was published with: viewable only by logged-in members of this project, with no password anywhere. Immediate and total: anyone who already entered the password loses access too, not just new visitors. Safe to call on an artifact that was never shared. Note what this does NOT do: there is no way to leave a page open to anyone with the link and no password. Removing the password closes the anonymous door; it does not prop it open. Say that plainly if you are asked for a passwordless public link.`,input_schema:{type:"object",properties:{id:{type:"string",description:"The artifact id to stop sharing."}},required:["id"]}}]};export{nt as __compareStoredSource,bt as __resetPublishBudget,wt as artifactSkill};