@zibby/skills 0.1.86 → 0.1.88

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.
@@ -1,5 +1,6 @@
1
1
  export namespace artifactSkill {
2
2
  let id: string;
3
+ let meta: any;
3
4
  let serverName: string;
4
5
  let allowedTools: string[];
5
6
  let description: string;
package/dist/artifact.js CHANGED
@@ -1,4 +1,4 @@
1
- import{existsSync as y,readFileSync as b}from"node:fs";import{homedir as k}from"node:os";import{join as O,dirname as g,resolve as _}from"node:path";import{fileURLToPath as v}from"node:url";function S(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let i=g(v(import.meta.url)),t=_(i,"..","bin","mcp-skill.mjs");return y(t)?t:null}function l(){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 i=O(k(),".zibby","config.json");return y(i)&&JSON.parse(b(i,"utf-8")).sessionToken||null}catch{return null}}function p(){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 T(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function h(i){return`${T()}:artifact:${i}`}async function u(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact write failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function N(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/artifacts/${encodeURIComponent(i)}`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact get failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function f(i,t){let e=l();if(!e)return;let r=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:h(i),content:JSON.stringify(t)})});if(!r.ok){let n=await r.text().catch(()=>"");throw new Error(`artifact index write failed (${r.status}): ${n.slice(0,200)}`)}}async function P(i){let t=l();if(!t)return null;let e=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:h(i)})});if(!e.ok)return null;let r=await e.json().catch(()=>null);if(!r?.found||!r?.memory?.content)return null;try{return JSON.parse(r.memory.content)}catch{return null}}function m(i){return typeof i?.html=="string"&&i.html.length>0?{format:"html",content:i.html}:typeof i?.markdown=="string"&&i.markdown.length>0?{format:"markdown",content:i.markdown}:null}var L={id:"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)
1
+ import{SKILL_META as b}from"@zibby/skill-ids";import{existsSync as y,readFileSync as k}from"node:fs";import{homedir as O}from"node:os";import{join as g,dirname as _,resolve as v}from"node:path";import{fileURLToPath as S}from"node:url";function T(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let i=_(S(import.meta.url)),t=v(i,"..","bin","mcp-skill.mjs");return y(t)?t:null}function l(){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 i=g(O(),".zibby","config.json");return y(i)&&JSON.parse(k(i,"utf-8")).sessionToken||null}catch{return null}}function p(){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 N(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function h(i){return`${N()}:artifact:${i}`}async function u(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/credits/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact write failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function P(i){let t=l();if(!t)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let e=await fetch(`${p()}/credits/artifacts/${encodeURIComponent(i)}`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});if(!e.ok){let r=await e.text().catch(()=>"");throw new Error(`artifact get failed (${e.status}): ${r.slice(0,300)}`)}return e.json()}async function f(i,t){let e=l();if(!e)return;let r=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:h(i),content:JSON.stringify(t)})});if(!r.ok){let n=await r.text().catch(()=>"");throw new Error(`artifact index write failed (${r.status}): ${n.slice(0,200)}`)}}async function R(i){let t=l();if(!t)return null;let e=await fetch(`${p()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:h(i)})});if(!e.ok)return null;let r=await e.json().catch(()=>null);if(!r?.found||!r?.memory?.content)return null;try{return JSON.parse(r.memory.content)}catch{return null}}function m(i){return typeof i?.html=="string"&&i.html.length>0?{format:"html",content:i.html}:typeof i?.markdown=="string"&&i.markdown.length>0?{format:"markdown",content:i.markdown}:null}var x={id:"artifact",meta:b.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)
2
2
  You can PUBLISH a standalone page \u2014 a status report, a plan, a comparison table,
3
3
  a dashboard-y summary, a diagram, a "here's what I found" write-up \u2014 and get back
4
4
  a shareable URL. The page is a self-contained HTML (or Markdown) document; it is
@@ -19,4 +19,4 @@ Tools:
19
19
  To recall WHAT YOU HAVE ALREADY PUBLISHED, use your kv-memory tool
20
20
  kv_recall_prefix with keyPrefix "artifact:" \u2014 each entry is the index record
21
21
  { id, title, url, kind, createdAt, summary } for a page you made. (Publishing
22
- records this automatically; you don't store it yourself.)`,resolve(){let i=S();if(!i)return{command:null,args:[],env:{},description:this.description};let t={};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]&&(t[e]=process.env[e]);return{type:"stdio",command:"node",args:[i,"../dist/artifact.js","artifactSkill"],env:t,description:this.description,alwaysLoad:!0}},async handleToolCall(i,t){try{switch(i){case"artifact_publish":{let e=typeof t?.title=="string"?t.title.trim():"";if(!e)return JSON.stringify({error:"title is required"});let r=m(t);if(!r)return JSON.stringify({error:"provide exactly one of html or markdown (non-empty string)"});let n={title:e,[r.format]:r.content};typeof t?.kind=="string"&&t.kind.trim()&&(n.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(n.favicon=t.favicon.trim());let o=await u(n),a=o?.id,s=o?.url;if(!a||!s)return JSON.stringify({error:"artifact write returned no id/url",response:o});let d={id:a,title:e,url:s,kind:n.kind||null,createdAt:o.createdAt||new Date().toISOString(),summary:typeof t?.summary=="string"&&t.summary.trim()?t.summary.trim():e};try{await f(a,d)}catch(c){return JSON.stringify({id:a,url:s,indexWarning:c.message})}return JSON.stringify({id:a,url:s})}case"artifact_update":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=m(t),n=typeof t?.title=="string"?t.title.trim():"";if(!r&&!n)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or html|markdown"});let o={id:e};n&&(o.title=n),r&&(o[r.format]=r.content),typeof t?.kind=="string"&&t.kind.trim()&&(o.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(o.favicon=t.favicon.trim());let a=await u(o),s=a?.url;if(!s)return JSON.stringify({error:"artifact update returned no url",response:a});let d=await P(e)||{},c={...d,id:e,url:s,title:n||d.title||"Untitled",kind:o.kind||d.kind||null,createdAt:d.createdAt||a.createdAt||new Date().toISOString(),updatedAt:a.updatedAt||new Date().toISOString()};typeof t?.summary=="string"&&t.summary.trim()?c.summary=t.summary.trim():c.summary||(c.summary=c.title);try{await f(e,c)}catch(w){return JSON.stringify({id:e,url:s,indexWarning:w.message})}return JSON.stringify({id:e,url:s})}case"artifact_get":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=await N(e);return JSON.stringify(r)}default:return JSON.stringify({error:`Unknown tool: ${i}`})}}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. Pass a title and EITHER html OR markdown. Keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 the page is sandboxed on view and external URLs are blocked. Returns { id, url }.",input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},html:{type:"string",description:"The page content as a self-contained HTML document (or fragment). Provide this OR markdown, not both."},markdown:{type:"string",description:"The page content as Markdown (rendered to HTML on serve). Provide this OR html, not both."},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."}},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 html|markdown). 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)."},html:{type:"string",description:"New HTML content (optional). Provide this OR markdown."},markdown:{type:"string",description:"New Markdown content (optional). Provide this OR 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"]}}]};export{L as artifactSkill};
22
+ records this automatically; you don't store it yourself.)`,resolve(){let i=T();if(!i)return{command:null,args:[],env:{},description:this.description};let t={};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]&&(t[e]=process.env[e]);return{type:"stdio",command:"node",args:[i,"../dist/artifact.js","artifactSkill"],env:t,description:this.description,alwaysLoad:!0}},async handleToolCall(i,t){try{switch(i){case"artifact_publish":{let e=typeof t?.title=="string"?t.title.trim():"";if(!e)return JSON.stringify({error:"title is required"});let r=m(t);if(!r)return JSON.stringify({error:"provide exactly one of html or markdown (non-empty string)"});let n={title:e,[r.format]:r.content};typeof t?.kind=="string"&&t.kind.trim()&&(n.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(n.favicon=t.favicon.trim());let a=await u(n),o=a?.id,s=a?.url;if(!o||!s)return JSON.stringify({error:"artifact write returned no id/url",response:a});let d={id:o,title:e,url:s,kind:n.kind||null,createdAt:a.createdAt||new Date().toISOString(),summary:typeof t?.summary=="string"&&t.summary.trim()?t.summary.trim():e};try{await f(o,d)}catch(c){return JSON.stringify({id:o,url:s,indexWarning:c.message})}return JSON.stringify({id:o,url:s})}case"artifact_update":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=m(t),n=typeof t?.title=="string"?t.title.trim():"";if(!r&&!n)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or html|markdown"});let a={id:e};n&&(a.title=n),r&&(a[r.format]=r.content),typeof t?.kind=="string"&&t.kind.trim()&&(a.kind=t.kind.trim()),typeof t?.favicon=="string"&&t.favicon.trim()&&(a.favicon=t.favicon.trim());let o=await u(a),s=o?.url;if(!s)return JSON.stringify({error:"artifact update returned no url",response:o});let d=await R(e)||{},c={...d,id:e,url:s,title:n||d.title||"Untitled",kind:a.kind||d.kind||null,createdAt:d.createdAt||o.createdAt||new Date().toISOString(),updatedAt:o.updatedAt||new Date().toISOString()};typeof t?.summary=="string"&&t.summary.trim()?c.summary=t.summary.trim():c.summary||(c.summary=c.title);try{await f(e,c)}catch(w){return JSON.stringify({id:e,url:s,indexWarning:w.message})}return JSON.stringify({id:e,url:s})}case"artifact_get":{let e=typeof t?.id=="string"?t.id.trim():"";if(!e)return JSON.stringify({error:"id is required"});let r=await P(e);return JSON.stringify(r)}default:return JSON.stringify({error:`Unknown tool: ${i}`})}}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. Pass a title and EITHER html OR markdown. Keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 the page is sandboxed on view and external URLs are blocked. Returns { id, url }.",input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},html:{type:"string",description:"The page content as a self-contained HTML document (or fragment). Provide this OR markdown, not both."},markdown:{type:"string",description:"The page content as Markdown (rendered to HTML on serve). Provide this OR html, not both."},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."}},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 html|markdown). 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)."},html:{type:"string",description:"New HTML content (optional). Provide this OR markdown."},markdown:{type:"string",description:"New Markdown content (optional). Provide this OR 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"]}}]};export{x as artifactSkill};
package/dist/index.js CHANGED
@@ -759,7 +759,7 @@ Tools:
759
759
  - sqlite_query: (sqlite stores) Run a SELECT (optionally with \`params\` for safe
760
760
  binding). Returns { columns, rows }. Read-only \u2014 never changes data.
761
761
  Pick the tool that matches the store's TYPE; using a dataset tool on a sqlite
762
- store (or vice-versa) is rejected.`,resolve(){let r=Jd();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);for(let t of Object.keys(process.env))/^ZIBBY_STORE__.+$/.test(t)&&process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/datasetStore.js","datasetStoreSkill"],env:e,description:this.description,alwaysLoad:!1}},async handleToolCall(r,e){try{switch(r){case"dataset_append":{if(e?.record==null||typeof e.record!="object"||Array.isArray(e.record))return JSON.stringify({error:"record is required (a JSON object)"});let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});let n=typeof e?.agent=="string"&&e.agent.trim()?e.agent.trim():xi(),i={record:e.record,agent:n};typeof e?.description=="string"&&e.description.trim()&&(i.description=e.description.trim());let s=await yr(t.storeId,"append",i);return JSON.stringify({...s,store:t.name,storeId:t.storeId})}case"dataset_query":{let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});let n={};for(let s of["select","where","groupBy","orderBy","limit","since","until","agent"])e?.[s]!=null&&(n[s]=e[s]);let i=await yr(t.storeId,"query",n);return JSON.stringify({...i,store:t.name,storeId:t.storeId})}case"ensure_store":{let t=typeof e?.name=="string"?e.name.trim():"";if(!t)return JSON.stringify({error:"name is required"});let n=typeof e?.type=="string"&&e.type.trim()?e.type.trim().toLowerCase():"sqlite",i=typeof e?.description=="string"?e.description.trim():"",s=await Dd({name:t,type:n,description:i,namespace:xi()});return s?.storeId&&($i[t]=s.storeId),JSON.stringify({...s,store:t})}case"sqlite_exec":case"sqlite_query":{let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});if(typeof e?.sql!="string"||!e.sql.trim())return JSON.stringify({error:"sql is required (a non-empty SQL string)"});let n={sql:e.sql};Array.isArray(e?.params)&&(n.params=e.params);let i=await yr(t.storeId,"sql",n);return JSON.stringify({...i,store:t.name,storeId:t.storeId})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:t.message})}},tools:[{name:"dataset_append",description:"Append ONE structured JSON record to a bound store, durably. Records persist across your stateless runs and are auto-tagged with your agent type so you can filter to your own writes later. Use to accumulate data you will query/aggregate (e.g. per-run metrics, processed items). Append ONE record per call.",input_schema:{type:"object",properties:{store:{type:"string",description:'The logical store NAME to write to (e.g. "scorecards"), taken from the AVAILABLE STORES list \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this and it defaults to that store; you can ONLY write to a bound store name.'},record:{type:"object",description:'An arbitrary JSON object \u2014 one row of data. Its keys become queryable fields (e.g. {"repo":"owner/x","stars":1200}). One record per call.'},description:{type:"string",description:"Optional, informational note about this write. The store already exists from deploy, so this is not required and does not create anything."},agent:{type:"string",description:"Optional writing-agent tag. Defaults to your own agent type \u2014 leave unset to auto-tag."}},required:["record"]}},{name:"dataset_query",description:"Run a SQL-style query over a bound store to build reports: select/aggregate (count|sum|avg|min|max), filter, group, order, limit, and bound by month. Returns { columns, rows }. Use this to compute summaries/aggregations from records you appended earlier.",input_schema:{type:"object",properties:{store:{type:"string",description:'The logical store NAME to query (e.g. "scorecards"), taken from the AVAILABLE STORES list \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this and it defaults to that store.'},select:{type:"array",description:"Columns to return. Each item is { field?, agg?, as? }. agg \u2208 count|sum|avg|min|max; omit field for count(*). Omit `select` entirely to return raw rows."},where:{type:"array",description:"Filters, ANDed. Each item is { field, op, value }; op \u2208 eq|ne|gt|gte|lt|lte|like. `field` is a JSON key of the stored record."},groupBy:{type:"array",description:"Field names to group by (array of strings) for aggregation."},orderBy:{type:"array",description:"Sort spec. Each item is { field|as, dir }; dir \u2208 asc|desc."},limit:{type:"number",description:"Maximum number of rows to return."},since:{type:"string",description:"Inclusive lower bound month, 'yyyy-MM' (e.g. '2026-01')."},until:{type:"string",description:"Inclusive upper bound month, 'yyyy-MM' (e.g. '2026-06')."},agent:{type:"string",description:"Filter to records written by one agent namespace. Omit to query across all writers."}},required:[]}},{name:"ensure_store",description:'Create (or reuse) a store ON DEMAND for THIS agent \u2014 use when you need a store that was NOT declared/bound at deploy. Idempotent by name and private to this agent: calling again with the same name returns the SAME store (safe to call at the start of every run). Returns { storeId }. For type "sqlite" you then define schema with sqlite_exec (CREATE TABLE IF NOT EXISTS) and read/write via sqlite_exec/sqlite_query using this name.',input_schema:{type:"object",properties:{name:{type:"string",description:'A short logical name for the store (e.g. "linkedin_posts"). Letters, digits, _ and - only.'},type:{type:"string",enum:["sqlite","dataset"],description:'Store type. "sqlite" (default) = a mutable relational DB whose schema YOU define with SQL. "dataset" = append-only JSON records for later aggregation/analytics.'},description:{type:"string",description:"What this store is for (shown in the Storage UI)."}},required:["name"]}},{name:"sqlite_exec",description:"For SQLITE-type stores: run SQL that CHANGES data \u2014 CREATE TABLE (use IF NOT EXISTS), INSERT, UPDATE, DELETE (one or more statements in one call). The store is a real, mutable SQLite database that persists across your stateless runs. Returns { rowsModified, wrote }. Use this to build schema on the fly and to update rows / track changing state (e.g. a queue with a status column).",input_schema:{type:"object",properties:{store:{type:"string",description:"The logical store NAME (a sqlite-type store from AVAILABLE STORES) \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this."},sql:{type:"string",description:'The SQL to run. May contain multiple statements separated by ";". Prefer CREATE TABLE IF NOT EXISTS for idempotent schema.'},params:{type:"array",description:'Optional positional bind params for a SINGLE parameterized statement (safe binding of values), e.g. sql "UPDATE t SET s=? WHERE id=?" with params ["done", 1].'}},required:["sql"]}},{name:"sqlite_query",description:"For SQLITE-type stores: run a read-only SELECT (optionally with `params` for safe binding) against the store's SQLite database. Returns { columns, rows }. Never changes data. Use to read back rows/state you stored earlier.",input_schema:{type:"object",properties:{store:{type:"string",description:"The logical store NAME (a sqlite-type store from AVAILABLE STORES) \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this."},sql:{type:"string",description:"A single SELECT statement. Use ? placeholders + `params` for any values."},params:{type:"array",description:'Optional positional bind params for the SELECT, e.g. ["linkedin_personal"].'}},required:["sql"]}}]};import{existsSync as qi,readFileSync as Bd}from"node:fs";import{homedir as Md}from"node:os";import{join as Fd,dirname as Gd,resolve as Kd}from"node:path";import{fileURLToPath as Hd}from"node:url";function zd(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Gd(Hd(import.meta.url)),e=Kd(r,"..","bin","mcp-skill.mjs");return qi(e)?e:null}function Tt(){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=Fd(Md(),".zibby","config.json");return qi(r)&&JSON.parse(Bd(r,"utf-8")).sessionToken||null}catch{return null}}function Rt(){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 Yd(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function Di(r){return`${Yd()}:artifact:${r}`}async function Ci(r){let e=Tt();if(!e)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let t=await fetch(`${Rt()}/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!t.ok){let n=await t.text().catch(()=>"");throw new Error(`artifact write failed (${t.status}): ${n.slice(0,300)}`)}return t.json()}async function Wd(r){let e=Tt();if(!e)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let t=await fetch(`${Rt()}/artifacts/${encodeURIComponent(r)}`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(!t.ok){let n=await t.text().catch(()=>"");throw new Error(`artifact get failed (${t.status}): ${n.slice(0,300)}`)}return t.json()}async function Ui(r,e){let t=Tt();if(!t)return;let n=await fetch(`${Rt()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:Di(r),content:JSON.stringify(e)})});if(!n.ok){let i=await n.text().catch(()=>"");throw new Error(`artifact index write failed (${n.status}): ${i.slice(0,200)}`)}}async function Zd(r){let e=Tt();if(!e)return null;let t=await fetch(`${Rt()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:Di(r)})});if(!t.ok)return null;let n=await t.json().catch(()=>null);if(!n?.found||!n?.memory?.content)return null;try{return JSON.parse(n.memory.content)}catch{return null}}function Ji(r){return typeof r?.html=="string"&&r.html.length>0?{format:"html",content:r.html}:typeof r?.markdown=="string"&&r.markdown.length>0?{format:"markdown",content:r.markdown}:null}var Bi={id:"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)
762
+ store (or vice-versa) is rejected.`,resolve(){let r=Jd();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);for(let t of Object.keys(process.env))/^ZIBBY_STORE__.+$/.test(t)&&process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/datasetStore.js","datasetStoreSkill"],env:e,description:this.description,alwaysLoad:!1}},async handleToolCall(r,e){try{switch(r){case"dataset_append":{if(e?.record==null||typeof e.record!="object"||Array.isArray(e.record))return JSON.stringify({error:"record is required (a JSON object)"});let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});let n=typeof e?.agent=="string"&&e.agent.trim()?e.agent.trim():xi(),i={record:e.record,agent:n};typeof e?.description=="string"&&e.description.trim()&&(i.description=e.description.trim());let s=await yr(t.storeId,"append",i);return JSON.stringify({...s,store:t.name,storeId:t.storeId})}case"dataset_query":{let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});let n={};for(let s of["select","where","groupBy","orderBy","limit","since","until","agent"])e?.[s]!=null&&(n[s]=e[s]);let i=await yr(t.storeId,"query",n);return JSON.stringify({...i,store:t.name,storeId:t.storeId})}case"ensure_store":{let t=typeof e?.name=="string"?e.name.trim():"";if(!t)return JSON.stringify({error:"name is required"});let n=typeof e?.type=="string"&&e.type.trim()?e.type.trim().toLowerCase():"sqlite",i=typeof e?.description=="string"?e.description.trim():"",s=await Dd({name:t,type:n,description:i,namespace:xi()});return s?.storeId&&($i[t]=s.storeId),JSON.stringify({...s,store:t})}case"sqlite_exec":case"sqlite_query":{let t=hr(e?.store);if(t.error)return JSON.stringify({error:t.error});if(typeof e?.sql!="string"||!e.sql.trim())return JSON.stringify({error:"sql is required (a non-empty SQL string)"});let n={sql:e.sql};Array.isArray(e?.params)&&(n.params=e.params);let i=await yr(t.storeId,"sql",n);return JSON.stringify({...i,store:t.name,storeId:t.storeId})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:t.message})}},tools:[{name:"dataset_append",description:"Append ONE structured JSON record to a bound store, durably. Records persist across your stateless runs and are auto-tagged with your agent type so you can filter to your own writes later. Use to accumulate data you will query/aggregate (e.g. per-run metrics, processed items). Append ONE record per call.",input_schema:{type:"object",properties:{store:{type:"string",description:'The logical store NAME to write to (e.g. "scorecards"), taken from the AVAILABLE STORES list \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this and it defaults to that store; you can ONLY write to a bound store name.'},record:{type:"object",description:'An arbitrary JSON object \u2014 one row of data. Its keys become queryable fields (e.g. {"repo":"owner/x","stars":1200}). One record per call.'},description:{type:"string",description:"Optional, informational note about this write. The store already exists from deploy, so this is not required and does not create anything."},agent:{type:"string",description:"Optional writing-agent tag. Defaults to your own agent type \u2014 leave unset to auto-tag."}},required:["record"]}},{name:"dataset_query",description:"Run a SQL-style query over a bound store to build reports: select/aggregate (count|sum|avg|min|max), filter, group, order, limit, and bound by month. Returns { columns, rows }. Use this to compute summaries/aggregations from records you appended earlier.",input_schema:{type:"object",properties:{store:{type:"string",description:'The logical store NAME to query (e.g. "scorecards"), taken from the AVAILABLE STORES list \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this and it defaults to that store.'},select:{type:"array",description:"Columns to return. Each item is { field?, agg?, as? }. agg \u2208 count|sum|avg|min|max; omit field for count(*). Omit `select` entirely to return raw rows."},where:{type:"array",description:"Filters, ANDed. Each item is { field, op, value }; op \u2208 eq|ne|gt|gte|lt|lte|like. `field` is a JSON key of the stored record."},groupBy:{type:"array",description:"Field names to group by (array of strings) for aggregation."},orderBy:{type:"array",description:"Sort spec. Each item is { field|as, dir }; dir \u2208 asc|desc."},limit:{type:"number",description:"Maximum number of rows to return."},since:{type:"string",description:"Inclusive lower bound month, 'yyyy-MM' (e.g. '2026-01')."},until:{type:"string",description:"Inclusive upper bound month, 'yyyy-MM' (e.g. '2026-06')."},agent:{type:"string",description:"Filter to records written by one agent namespace. Omit to query across all writers."}},required:[]}},{name:"ensure_store",description:'Create (or reuse) a store ON DEMAND for THIS agent \u2014 use when you need a store that was NOT declared/bound at deploy. Idempotent by name and private to this agent: calling again with the same name returns the SAME store (safe to call at the start of every run). Returns { storeId }. For type "sqlite" you then define schema with sqlite_exec (CREATE TABLE IF NOT EXISTS) and read/write via sqlite_exec/sqlite_query using this name.',input_schema:{type:"object",properties:{name:{type:"string",description:'A short logical name for the store (e.g. "linkedin_posts"). Letters, digits, _ and - only.'},type:{type:"string",enum:["sqlite","dataset"],description:'Store type. "sqlite" (default) = a mutable relational DB whose schema YOU define with SQL. "dataset" = append-only JSON records for later aggregation/analytics.'},description:{type:"string",description:"What this store is for (shown in the Storage UI)."}},required:["name"]}},{name:"sqlite_exec",description:"For SQLITE-type stores: run SQL that CHANGES data \u2014 CREATE TABLE (use IF NOT EXISTS), INSERT, UPDATE, DELETE (one or more statements in one call). The store is a real, mutable SQLite database that persists across your stateless runs. Returns { rowsModified, wrote }. Use this to build schema on the fly and to update rows / track changing state (e.g. a queue with a status column).",input_schema:{type:"object",properties:{store:{type:"string",description:"The logical store NAME (a sqlite-type store from AVAILABLE STORES) \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this."},sql:{type:"string",description:'The SQL to run. May contain multiple statements separated by ";". Prefer CREATE TABLE IF NOT EXISTS for idempotent schema.'},params:{type:"array",description:'Optional positional bind params for a SINGLE parameterized statement (safe binding of values), e.g. sql "UPDATE t SET s=? WHERE id=?" with params ["done", 1].'}},required:["sql"]}},{name:"sqlite_query",description:"For SQLITE-type stores: run a read-only SELECT (optionally with `params` for safe binding) against the store's SQLite database. Returns { columns, rows }. Never changes data. Use to read back rows/state you stored earlier.",input_schema:{type:"object",properties:{store:{type:"string",description:"The logical store NAME (a sqlite-type store from AVAILABLE STORES) \u2014 pick by description. NOT an id. If exactly one store is bound you may omit this."},sql:{type:"string",description:"A single SELECT statement. Use ? placeholders + `params` for any values."},params:{type:"array",description:'Optional positional bind params for the SELECT, e.g. ["linkedin_personal"].'}},required:["sql"]}}]};import{SKILL_META as Bd}from"@zibby/skill-ids";import{existsSync as qi,readFileSync as Md}from"node:fs";import{homedir as Fd}from"node:os";import{join as Gd,dirname as Kd,resolve as Hd}from"node:path";import{fileURLToPath as zd}from"node:url";function Yd(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Kd(zd(import.meta.url)),e=Hd(r,"..","bin","mcp-skill.mjs");return qi(e)?e:null}function Tt(){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=Gd(Fd(),".zibby","config.json");return qi(r)&&JSON.parse(Md(r,"utf-8")).sessionToken||null}catch{return null}}function Rt(){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 Wd(){return(typeof process.env.WORKFLOW_TYPE=="string"?process.env.WORKFLOW_TYPE.trim():"")||"agent"}function Di(r){return`${Wd()}:artifact:${r}`}async function Ci(r){let e=Tt();if(!e)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let t=await fetch(`${Rt()}/credits/artifacts`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!t.ok){let n=await t.text().catch(()=>"");throw new Error(`artifact write failed (${t.status}): ${n.slice(0,300)}`)}return t.json()}async function Zd(r){let e=Tt();if(!e)throw new Error("No backend credential (PROJECT_API_TOKEN). Artifacts are only available inside a Zibby run.");let t=await fetch(`${Rt()}/credits/artifacts/${encodeURIComponent(r)}`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(!t.ok){let n=await t.text().catch(()=>"");throw new Error(`artifact get failed (${t.status}): ${n.slice(0,300)}`)}return t.json()}async function Ui(r,e){let t=Tt();if(!t)return;let n=await fetch(`${Rt()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({op:"store",scope:Di(r),content:JSON.stringify(e)})});if(!n.ok){let i=await n.text().catch(()=>"");throw new Error(`artifact index write failed (${n.status}): ${i.slice(0,200)}`)}}async function Vd(r){let e=Tt();if(!e)return null;let t=await fetch(`${Rt()}/credits/review-memory`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({op:"recall",scope:Di(r)})});if(!t.ok)return null;let n=await t.json().catch(()=>null);if(!n?.found||!n?.memory?.content)return null;try{return JSON.parse(n.memory.content)}catch{return null}}function Ji(r){return typeof r?.html=="string"&&r.html.length>0?{format:"html",content:r.html}:typeof r?.markdown=="string"&&r.markdown.length>0?{format:"markdown",content:r.markdown}:null}var Bi={id:"artifact",meta:Bd.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)
763
763
  You can PUBLISH a standalone page \u2014 a status report, a plan, a comparison table,
764
764
  a dashboard-y summary, a diagram, a "here's what I found" write-up \u2014 and get back
765
765
  a shareable URL. The page is a self-contained HTML (or Markdown) document; it is
@@ -780,17 +780,17 @@ Tools:
780
780
  To recall WHAT YOU HAVE ALREADY PUBLISHED, use your kv-memory tool
781
781
  kv_recall_prefix with keyPrefix "artifact:" \u2014 each entry is the index record
782
782
  { id, title, url, kind, createdAt, summary } for a page you made. (Publishing
783
- records this automatically; you don't store it yourself.)`,resolve(){let r=zd();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/artifact.js","artifactSkill"],env:e,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){try{switch(r){case"artifact_publish":{let t=typeof e?.title=="string"?e.title.trim():"";if(!t)return JSON.stringify({error:"title is required"});let n=Ji(e);if(!n)return JSON.stringify({error:"provide exactly one of html or markdown (non-empty string)"});let i={title:t,[n.format]:n.content};typeof e?.kind=="string"&&e.kind.trim()&&(i.kind=e.kind.trim()),typeof e?.favicon=="string"&&e.favicon.trim()&&(i.favicon=e.favicon.trim());let s=await Ci(i),o=s?.id,a=s?.url;if(!o||!a)return JSON.stringify({error:"artifact write returned no id/url",response:s});let c={id:o,title:t,url:a,kind:i.kind||null,createdAt:s.createdAt||new Date().toISOString(),summary:typeof e?.summary=="string"&&e.summary.trim()?e.summary.trim():t};try{await Ui(o,c)}catch(d){return JSON.stringify({id:o,url:a,indexWarning:d.message})}return JSON.stringify({id:o,url:a})}case"artifact_update":{let t=typeof e?.id=="string"?e.id.trim():"";if(!t)return JSON.stringify({error:"id is required"});let n=Ji(e),i=typeof e?.title=="string"?e.title.trim():"";if(!n&&!i)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or html|markdown"});let s={id:t};i&&(s.title=i),n&&(s[n.format]=n.content),typeof e?.kind=="string"&&e.kind.trim()&&(s.kind=e.kind.trim()),typeof e?.favicon=="string"&&e.favicon.trim()&&(s.favicon=e.favicon.trim());let o=await Ci(s),a=o?.url;if(!a)return JSON.stringify({error:"artifact update returned no url",response:o});let c=await Zd(t)||{},d={...c,id:t,url:a,title:i||c.title||"Untitled",kind:s.kind||c.kind||null,createdAt:c.createdAt||o.createdAt||new Date().toISOString(),updatedAt:o.updatedAt||new Date().toISOString()};typeof e?.summary=="string"&&e.summary.trim()?d.summary=e.summary.trim():d.summary||(d.summary=d.title);try{await Ui(t,d)}catch(l){return JSON.stringify({id:t,url:a,indexWarning:l.message})}return JSON.stringify({id:t,url:a})}case"artifact_get":{let t=typeof e?.id=="string"?e.id.trim():"";if(!t)return JSON.stringify({error:"id is required"});let n=await Wd(t);return JSON.stringify(n)}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:t.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. Pass a title and EITHER html OR markdown. Keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 the page is sandboxed on view and external URLs are blocked. Returns { id, url }.",input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},html:{type:"string",description:"The page content as a self-contained HTML document (or fragment). Provide this OR markdown, not both."},markdown:{type:"string",description:"The page content as Markdown (rendered to HTML on serve). Provide this OR html, not both."},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."}},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 html|markdown). 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)."},html:{type:"string",description:"New HTML content (optional). Provide this OR markdown."},markdown:{type:"string",description:"New Markdown content (optional). Provide this OR 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"]}}]};import{createRequire as Vd}from"node:module";import{existsSync as Ki,mkdirSync as Qd,writeFileSync as Mi}from"node:fs";import{dirname as Hi,join as Je,resolve as zi}from"node:path";import{fileURLToPath as Yi}from"node:url";var Wi=Vd(import.meta.url),gr=null;function Xd(){return gr||(gr=Wi("echarts")),gr}var _r=null;function ep(){return _r||(_r=Wi("@resvg/resvg-js")),_r}var tp=800,rp=600,np=4096,ip=16,Fi=60,Zi="Noto Sans";function sp(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Hi(Yi(import.meta.url)),e=zi(r,"..","bin","mcp-skill.mjs");return Ki(e)?e:null}function op(){let r=Hi(Yi(import.meta.url)),e=zi(r,"..","assets","fonts");return["NotoSans-Regular.ttf","NotoSans-Bold.ttf"].map(t=>Je(e,t)).filter(t=>Ki(t))}function ap(){let r=process.env.ZIBBY_NODE_SESSION_PATH,e=process.env.ZIBBY_SESSION_PATH,t=r||(e?Je(e,"chart-render"):Je(process.cwd(),".zibby","output","charts"));return Qd(t,{recursive:!0}),t}function Ne(r){return r!=null&&typeof r=="object"&&!Array.isArray(r)}function Gi(r,e){let t=Number(r);return Number.isFinite(t)?Math.max(ip,Math.min(np,Math.round(t))):e}function br(r){return typeof r!="string"||r.length<=Fi?r:`${r.slice(0,Fi-1)}\u2026`}function At(r){if(Array.isArray(r))for(let e=0;e<r.length;e++){let t=r[e];typeof t=="string"?r[e]=br(t):Ne(t)&&typeof t.name=="string"&&(t.name=br(t.name))}}function cp(r){for(let n of["xAxis","yAxis"]){let i=Array.isArray(r[n])?r[n]:r[n]?[r[n]]:[];for(let s of i)Ne(s)&&At(s.data)}let e=Array.isArray(r.radar)?r.radar:r.radar?[r.radar]:[];for(let n of e)Ne(n)&&At(n.indicator);Ne(r.legend)&&At(r.legend.data);let t=Array.isArray(r.series)?r.series:r.series?[r.series]:[];for(let n of t)Ne(n)&&(typeof n.name=="string"&&(n.name=br(n.name)),At(n.data))}function lp(r){let e=JSON.parse(JSON.stringify(r));return e.animation=!1,e.backgroundColor==null&&(e.backgroundColor="#fff"),Ne(e.textStyle)||(e.textStyle={}),e.textStyle.fontFamily==null&&(e.textStyle.fontFamily=Zi),cp(e),e}function dp(r){return typeof r!="string"?null:r.trim().replace(/\.(svg|png)$/i,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^[.-]+|[.-]+$/g,"")||null}function pp(r,e,t){let i=Xd().init(null,null,{renderer:"svg",ssr:!0,width:e,height:t});try{return i.setOption(r),i.renderToSVGString()}finally{i.dispose()}}function up(r){let{Resvg:e}=ep();return new e(r,{font:{fontFiles:op(),loadSystemFonts:!0,defaultFontFamily:Zi}}).render().asPng()}var Vi={id:"chart-render",serverName:"chart_render",allowedTools:["mcp__chart_render__*"],description:"Chart render \u2014 local server-side chart rendering (Apache ECharts SVG SSR + resvg PNG); data never leaves the box",promptFragment:`## Chart Render (local, no external service)
783
+ records this automatically; you don't store it yourself.)`,resolve(){let r=Yd();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/artifact.js","artifactSkill"],env:e,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){try{switch(r){case"artifact_publish":{let t=typeof e?.title=="string"?e.title.trim():"";if(!t)return JSON.stringify({error:"title is required"});let n=Ji(e);if(!n)return JSON.stringify({error:"provide exactly one of html or markdown (non-empty string)"});let i={title:t,[n.format]:n.content};typeof e?.kind=="string"&&e.kind.trim()&&(i.kind=e.kind.trim()),typeof e?.favicon=="string"&&e.favicon.trim()&&(i.favicon=e.favicon.trim());let s=await Ci(i),o=s?.id,a=s?.url;if(!o||!a)return JSON.stringify({error:"artifact write returned no id/url",response:s});let c={id:o,title:t,url:a,kind:i.kind||null,createdAt:s.createdAt||new Date().toISOString(),summary:typeof e?.summary=="string"&&e.summary.trim()?e.summary.trim():t};try{await Ui(o,c)}catch(d){return JSON.stringify({id:o,url:a,indexWarning:d.message})}return JSON.stringify({id:o,url:a})}case"artifact_update":{let t=typeof e?.id=="string"?e.id.trim():"";if(!t)return JSON.stringify({error:"id is required"});let n=Ji(e),i=typeof e?.title=="string"?e.title.trim():"";if(!n&&!i)return JSON.stringify({error:"nothing to update \u2014 pass a new title and/or html|markdown"});let s={id:t};i&&(s.title=i),n&&(s[n.format]=n.content),typeof e?.kind=="string"&&e.kind.trim()&&(s.kind=e.kind.trim()),typeof e?.favicon=="string"&&e.favicon.trim()&&(s.favicon=e.favicon.trim());let o=await Ci(s),a=o?.url;if(!a)return JSON.stringify({error:"artifact update returned no url",response:o});let c=await Vd(t)||{},d={...c,id:t,url:a,title:i||c.title||"Untitled",kind:s.kind||c.kind||null,createdAt:c.createdAt||o.createdAt||new Date().toISOString(),updatedAt:o.updatedAt||new Date().toISOString()};typeof e?.summary=="string"&&e.summary.trim()?d.summary=e.summary.trim():d.summary||(d.summary=d.title);try{await Ui(t,d)}catch(l){return JSON.stringify({id:t,url:a,indexWarning:l.message})}return JSON.stringify({id:t,url:a})}case"artifact_get":{let t=typeof e?.id=="string"?e.id.trim():"";if(!t)return JSON.stringify({error:"id is required"});let n=await Zd(t);return JSON.stringify(n)}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:t.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. Pass a title and EITHER html OR markdown. Keep all CSS/JS/images INLINE (inline <style>/<script>, data: URIs) \u2014 the page is sandboxed on view and external URLs are blocked. Returns { id, url }.",input_schema:{type:"object",properties:{title:{type:"string",description:"The page title (also the browser tab title)."},html:{type:"string",description:"The page content as a self-contained HTML document (or fragment). Provide this OR markdown, not both."},markdown:{type:"string",description:"The page content as Markdown (rendered to HTML on serve). Provide this OR html, not both."},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."}},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 html|markdown). 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)."},html:{type:"string",description:"New HTML content (optional). Provide this OR markdown."},markdown:{type:"string",description:"New Markdown content (optional). Provide this OR 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"]}}]};import{createRequire as Qd}from"node:module";import{existsSync as Ki,mkdirSync as Xd,writeFileSync as Mi}from"node:fs";import{dirname as Hi,join as Je,resolve as zi}from"node:path";import{fileURLToPath as Yi}from"node:url";var Wi=Qd(import.meta.url),gr=null;function ep(){return gr||(gr=Wi("echarts")),gr}var _r=null;function tp(){return _r||(_r=Wi("@resvg/resvg-js")),_r}var rp=800,np=600,ip=4096,sp=16,Fi=60,Zi="Noto Sans";function op(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Hi(Yi(import.meta.url)),e=zi(r,"..","bin","mcp-skill.mjs");return Ki(e)?e:null}function ap(){let r=Hi(Yi(import.meta.url)),e=zi(r,"..","assets","fonts");return["NotoSans-Regular.ttf","NotoSans-Bold.ttf"].map(t=>Je(e,t)).filter(t=>Ki(t))}function cp(){let r=process.env.ZIBBY_NODE_SESSION_PATH,e=process.env.ZIBBY_SESSION_PATH,t=r||(e?Je(e,"chart-render"):Je(process.cwd(),".zibby","output","charts"));return Xd(t,{recursive:!0}),t}function Ne(r){return r!=null&&typeof r=="object"&&!Array.isArray(r)}function Gi(r,e){let t=Number(r);return Number.isFinite(t)?Math.max(sp,Math.min(ip,Math.round(t))):e}function br(r){return typeof r!="string"||r.length<=Fi?r:`${r.slice(0,Fi-1)}\u2026`}function At(r){if(Array.isArray(r))for(let e=0;e<r.length;e++){let t=r[e];typeof t=="string"?r[e]=br(t):Ne(t)&&typeof t.name=="string"&&(t.name=br(t.name))}}function lp(r){for(let n of["xAxis","yAxis"]){let i=Array.isArray(r[n])?r[n]:r[n]?[r[n]]:[];for(let s of i)Ne(s)&&At(s.data)}let e=Array.isArray(r.radar)?r.radar:r.radar?[r.radar]:[];for(let n of e)Ne(n)&&At(n.indicator);Ne(r.legend)&&At(r.legend.data);let t=Array.isArray(r.series)?r.series:r.series?[r.series]:[];for(let n of t)Ne(n)&&(typeof n.name=="string"&&(n.name=br(n.name)),At(n.data))}function dp(r){let e=JSON.parse(JSON.stringify(r));return e.animation=!1,e.backgroundColor==null&&(e.backgroundColor="#fff"),Ne(e.textStyle)||(e.textStyle={}),e.textStyle.fontFamily==null&&(e.textStyle.fontFamily=Zi),lp(e),e}function pp(r){return typeof r!="string"?null:r.trim().replace(/\.(svg|png)$/i,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^[.-]+|[.-]+$/g,"")||null}function up(r,e,t){let i=ep().init(null,null,{renderer:"svg",ssr:!0,width:e,height:t});try{return i.setOption(r),i.renderToSVGString()}finally{i.dispose()}}function mp(r){let{Resvg:e}=tp();return new e(r,{font:{fontFiles:ap(),loadSystemFonts:!0,defaultFontFamily:Zi}}).render().asPng()}var Vi={id:"chart-render",serverName:"chart_render",allowedTools:["mcp__chart_render__*"],description:"Chart render \u2014 local server-side chart rendering (Apache ECharts SVG SSR + resvg PNG); data never leaves the box",promptFragment:`## Chart Render (local, no external service)
784
784
  You can render charts LOCALLY with the chart_render tool \u2014 pass a standard
785
785
  Apache ECharts option object as \`spec\` (any chart type: bar, line, pie,
786
786
  radar, scatter, heatmap, \u2026). It renders server-side to SVG/PNG files in the
787
787
  run's output folder (auto-attached to the run as artifacts) and returns the
788
788
  file paths. No browser, no external chart service \u2014 the data never leaves
789
- the machine. Don't set animation (it's forced off). Default 800\xD7600 PNG.`,resolve({sessionPath:r,nodeName:e}={}){let t=sp();if(!t)return{command:null,args:[],env:{},description:this.description};let n={},i=r&&e?Je(r,e):null;i&&(n.ZIBBY_NODE_SESSION_PATH=i),r&&(n.ZIBBY_SESSION_PATH=r);for(let s of["ZIBBY_NODE_SESSION_PATH","ZIBBY_SESSION_PATH"])!n[s]&&process.env[s]&&(n[s]=process.env[s]);return{type:"stdio",command:"node",args:[t,"../dist/chartRender.js","chartRenderSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){if(r!=="chart_render")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=e?.spec;if(!Ne(t))return JSON.stringify({error:"spec must be a plain ECharts option OBJECT (e.g. { xAxis: {...}, yAxis: {...}, series: [...] }) \u2014 got "+(t===null?"null":Array.isArray(t)?"an array":typeof t)+". Pass the option object itself, not a string or an array."});let n=Gi(e?.width,tp),i=Gi(e?.height,rp),s=["svg","png","both"].includes(e?.output)?e.output:"png",o=dp(e?.filename)||`chart-${Date.now()}`,a;try{a=pp(lp(t),n,i)}catch(l){return JSON.stringify({error:`Chart render failed: ${l.message}. The spec must be a valid Apache ECharts option (check series[].type, and that xAxis/yAxis/radar match the series type). Fix the spec and retry.`})}if(typeof a!="string"||!a.includes("<svg"))return JSON.stringify({error:"Chart render produced no SVG \u2014 the spec likely describes an empty chart (no series?). Add at least one series and retry."});let c=ap(),d=[];if(s==="svg"||s==="both"){let l=Je(c,`${o}.svg`);Mi(l,a,"utf-8"),d.push({path:l,format:"svg",bytes:Buffer.byteLength(a,"utf-8")})}if(s==="png"||s==="both"){let l;try{l=up(a)}catch(u){return JSON.stringify({error:`PNG rasterization failed: ${u.message}. Retry with output:'svg' if you only need the vector.`})}let p=Je(c,`${o}.png`);Mi(p,l),d.push({path:p,format:"png",bytes:l.length})}return JSON.stringify({ok:!0,width:n,height:i,files:d})}catch(t){return JSON.stringify({error:`chart_render failed: ${t.message}`})}},tools:[{name:"chart_render",description:'Render a chart LOCALLY (no external service) from a standard Apache ECharts option and save it as SVG/PNG files in the run output folder. Pass the raw ECharts option as `spec` \u2014 every ECharts chart type works (bar, line, pie, radar, scatter, heatmap, \u2026). Returns the written file path(s). animation is forced off; background defaults to white. Bar example: {"xAxis":{"type":"category","data":["Q1","Q2"]},"yAxis":{},"series":[{"type":"bar","data":[12,30]}]}. Radar example: {"legend":{"data":["A","B"]},"radar":{"indicator":[{"name":"speed","max":10},{"name":"cost","max":10},{"name":"quality","max":10}]},"series":[{"type":"radar","data":[{"name":"A","value":[7,4,9]},{"name":"B","value":[5,8,6]}]}]}.',input_schema:{type:"object",properties:{spec:{type:"object",description:"The Apache ECharts option object, passed through as-is (series, xAxis/yAxis, radar, legend, title, \u2026)."},width:{type:"number",description:"Image width in px (default 800, max 4096)."},height:{type:"number",description:"Image height in px (default 600, max 4096)."},output:{type:"string",enum:["svg","png","both"],description:"Which file(s) to write (default 'png')."},filename:{type:"string",description:"Optional file basename (no extension); defaults to chart-<timestamp>."}},required:["spec"]}}]};import{createRequire as mp}from"node:module";import{existsSync as ts,mkdirSync as fp,writeFileSync as Qi}from"node:fs";import{dirname as rs,join as De,resolve as ns}from"node:path";import{fileURLToPath as is}from"node:url";var hp=mp(import.meta.url),kr=null;function yp(){return kr||(kr=hp("@resvg/resvg-js")),kr}var ss=1200,gp=627,_p=4096,bp=320,kp="#3b82f6",Oe="Noto Sans",wp=3;function Sp(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=rs(is(import.meta.url)),e=ns(r,"..","bin","mcp-skill.mjs");return ts(e)?e:null}function vp(){let r=rs(is(import.meta.url)),e=ns(r,"..","assets","fonts");return["NotoSans-Regular.ttf","NotoSans-Bold.ttf"].map(t=>De(e,t)).filter(t=>ts(t))}function Ip(){let r=process.env.ZIBBY_NODE_SESSION_PATH,e=process.env.ZIBBY_SESSION_PATH,t=r||(e?De(e,"social-card"):De(process.cwd(),".zibby","output","social-cards"));return fp(t,{recursive:!0}),t}function Xi(r,e){let t=Number(r);return Number.isFinite(t)?Math.max(bp,Math.min(_p,Math.round(t))):e}function qe(r){return String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function Np(r){return typeof r!="string"?null:r.trim().replace(/\.(png|svg)$/i,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^[.-]+|[.-]+$/g,"")||null}function Op(r,e){if(typeof r!="string")return e;let t=r.trim();return/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(t)?t:e}function os(r,e,t){return String(r).length*e*t}function wr(r,e,t,n){let i=String(r).trim().split(/\s+/).filter(Boolean),s=[],o="";for(let a of i){let c=o?`${o} ${a}`:a;o&&os(c,t,n)>e?(s.push(o),o=a):o=c}return o&&s.push(o),s.length?s:[""]}function Tp(r){return r==="light"?{bg:"#ffffff",fg:"#0b0f14",muted:"#5b6673",pillText:"#0b0f14",checkFg:"#ffffff",divider:"#e2e6ea"}:{bg:"#0b0f14",fg:"#e9edf1",muted:"#8b98a5",pillText:"#e9edf1",checkFg:"#0b0f14",divider:"#22303c"}}function es(r,e,t,n,i){let s=(Array.isArray(r)?r:[]).map(g=>String(g??"").trim()).filter(Boolean).slice(0,wp);if(s.length<2)return null;let o=24,a=54,c=26,d=90,l=17,p=s.map(g=>Math.max(96,os(g,o,.58)+c*2)),u=p.reduce((g,_)=>g+_,0)+d*(s.length-1),m=e-u/2,f=t+a/2,h=[];for(let g=0;g<s.length;g++){let _=p[g];if(h.push(`<rect x="${m.toFixed(1)}" y="${t}" width="${_.toFixed(1)}" height="${a}" rx="${a/2}" fill="${i}" fill-opacity="0.12" stroke="${i}" stroke-opacity="0.55" stroke-width="1.5"/>`,`<text x="${(m+_/2).toFixed(1)}" y="${(f+o*.35).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${o}" font-weight="700" fill="${n.pillText}">${qe(s[g])}</text>`),m+=_,g<s.length-1){let b=m,y=m+d,k=(b+y)/2;h.push(`<line x1="${b.toFixed(1)}" y1="${f}" x2="${y.toFixed(1)}" y2="${f}" stroke="${i}" stroke-opacity="0.55" stroke-width="2"/>`,`<circle cx="${k.toFixed(1)}" cy="${f}" r="${l}" fill="${i}"/>`,`<path d="M ${(k-8).toFixed(1)} ${f.toFixed(1)} l 5 5 l 9 -11" fill="none" stroke="${n.checkFg}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>`),m=y}}return{svg:h.join(`
790
- `),height:a}}function Rp(r){let{width:e,height:t,theme:n,accent:i}=r,s=Tp(n),o=e/2,a=Math.round(e*.07),c=e-a*2,d=String(r.headline).trim(),l=r.eyebrow?String(r.eyebrow).trim().toUpperCase():"",p=r.subhead?String(r.subhead).trim():"",u=r.stat?String(r.stat).trim():"",m=r.footer?String(r.footer).trim():"",f=e/ss,h=Math.round(22*f),g=Math.round(29*f),_=p?wr(p,c,g,.52):[],b=Math.round(g*1.28),y=4,k=Math.round(26*f),T=Math.round(20*f),K=Math.round(24*f),Re=Math.round(30*f),R=!!(u||m),O=a,D=R?Math.round(110*f):a,x=t-O-D,ge=es(r.diagram,o,0,s,i),Cr=(L,de)=>{let Me=y+k;return l&&(Me+=h+T),Me+=de.length*Math.round(L*1.16),_.length&&(Me+=K+_.length*b),ge&&(Me+=Re+ge.height),Me},qt=[76,66,58,50,44,38].map(L=>Math.round(L*f)),Fs=4,Ae=qt[qt.length-1],Dt=wr(d,c,Ae,.6);for(let L of qt){let de=wr(d,c,L,.6);if(de.length<=Fs&&Cr(L,de)<=x){Ae=L,Dt=de;break}}let Gs=Math.round(Ae*1.16),Ks=Cr(Ae,Dt),H=O+Math.max(0,(x-Ks)/2),se=[];se.push(`<rect x="0" y="0" width="${e}" height="${t}" fill="${s.bg}"/>`);let Ur=Math.round(60*f);se.push(`<rect x="${(o-Ur/2).toFixed(1)}" y="${H}" width="${Ur}" height="${y}" rx="2" fill="${i}"/>`),H+=y+k,l&&(se.push(`<text x="${o}" y="${(H+h*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${h}" font-weight="700" letter-spacing="3" fill="${i}">${qe(l)}</text>`),H+=h+T);for(let L of Dt)se.push(`<text x="${o}" y="${(H+Ae*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${Ae}" font-weight="700" fill="${s.fg}">${qe(L)}</text>`),H+=Gs;if(_.length){H+=K;for(let L of _)se.push(`<text x="${o}" y="${(H+g*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${g}" font-weight="400" fill="${s.muted}">${qe(L)}</text>`),H+=b}if(ge){H+=Re;let L=es(r.diagram,o,H,s,i);L&&se.push(L.svg)}if(R){let L=Math.round(24*f),de=t-Math.round(46*f);m&&se.push(`<text x="${a}" y="${de}" text-anchor="start" font-family="${Oe}" font-size="${L}" font-weight="400" fill="${s.muted}">${qe(m)}</text>`),u&&se.push(`<text x="${e-a}" y="${de}" text-anchor="end" font-family="${Oe}" font-size="${L}" font-weight="700" fill="${i}">${qe(u)}</text>`)}return`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${t}" viewBox="0 0 ${e} ${t}">
789
+ the machine. Don't set animation (it's forced off). Default 800\xD7600 PNG.`,resolve({sessionPath:r,nodeName:e}={}){let t=op();if(!t)return{command:null,args:[],env:{},description:this.description};let n={},i=r&&e?Je(r,e):null;i&&(n.ZIBBY_NODE_SESSION_PATH=i),r&&(n.ZIBBY_SESSION_PATH=r);for(let s of["ZIBBY_NODE_SESSION_PATH","ZIBBY_SESSION_PATH"])!n[s]&&process.env[s]&&(n[s]=process.env[s]);return{type:"stdio",command:"node",args:[t,"../dist/chartRender.js","chartRenderSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){if(r!=="chart_render")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=e?.spec;if(!Ne(t))return JSON.stringify({error:"spec must be a plain ECharts option OBJECT (e.g. { xAxis: {...}, yAxis: {...}, series: [...] }) \u2014 got "+(t===null?"null":Array.isArray(t)?"an array":typeof t)+". Pass the option object itself, not a string or an array."});let n=Gi(e?.width,rp),i=Gi(e?.height,np),s=["svg","png","both"].includes(e?.output)?e.output:"png",o=pp(e?.filename)||`chart-${Date.now()}`,a;try{a=up(dp(t),n,i)}catch(l){return JSON.stringify({error:`Chart render failed: ${l.message}. The spec must be a valid Apache ECharts option (check series[].type, and that xAxis/yAxis/radar match the series type). Fix the spec and retry.`})}if(typeof a!="string"||!a.includes("<svg"))return JSON.stringify({error:"Chart render produced no SVG \u2014 the spec likely describes an empty chart (no series?). Add at least one series and retry."});let c=cp(),d=[];if(s==="svg"||s==="both"){let l=Je(c,`${o}.svg`);Mi(l,a,"utf-8"),d.push({path:l,format:"svg",bytes:Buffer.byteLength(a,"utf-8")})}if(s==="png"||s==="both"){let l;try{l=mp(a)}catch(u){return JSON.stringify({error:`PNG rasterization failed: ${u.message}. Retry with output:'svg' if you only need the vector.`})}let p=Je(c,`${o}.png`);Mi(p,l),d.push({path:p,format:"png",bytes:l.length})}return JSON.stringify({ok:!0,width:n,height:i,files:d})}catch(t){return JSON.stringify({error:`chart_render failed: ${t.message}`})}},tools:[{name:"chart_render",description:'Render a chart LOCALLY (no external service) from a standard Apache ECharts option and save it as SVG/PNG files in the run output folder. Pass the raw ECharts option as `spec` \u2014 every ECharts chart type works (bar, line, pie, radar, scatter, heatmap, \u2026). Returns the written file path(s). animation is forced off; background defaults to white. Bar example: {"xAxis":{"type":"category","data":["Q1","Q2"]},"yAxis":{},"series":[{"type":"bar","data":[12,30]}]}. Radar example: {"legend":{"data":["A","B"]},"radar":{"indicator":[{"name":"speed","max":10},{"name":"cost","max":10},{"name":"quality","max":10}]},"series":[{"type":"radar","data":[{"name":"A","value":[7,4,9]},{"name":"B","value":[5,8,6]}]}]}.',input_schema:{type:"object",properties:{spec:{type:"object",description:"The Apache ECharts option object, passed through as-is (series, xAxis/yAxis, radar, legend, title, \u2026)."},width:{type:"number",description:"Image width in px (default 800, max 4096)."},height:{type:"number",description:"Image height in px (default 600, max 4096)."},output:{type:"string",enum:["svg","png","both"],description:"Which file(s) to write (default 'png')."},filename:{type:"string",description:"Optional file basename (no extension); defaults to chart-<timestamp>."}},required:["spec"]}}]};import{createRequire as fp}from"node:module";import{existsSync as ts,mkdirSync as hp,writeFileSync as Qi}from"node:fs";import{dirname as rs,join as De,resolve as ns}from"node:path";import{fileURLToPath as is}from"node:url";var yp=fp(import.meta.url),kr=null;function gp(){return kr||(kr=yp("@resvg/resvg-js")),kr}var ss=1200,_p=627,bp=4096,kp=320,wp="#3b82f6",Oe="Noto Sans",Sp=3;function vp(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=rs(is(import.meta.url)),e=ns(r,"..","bin","mcp-skill.mjs");return ts(e)?e:null}function Ip(){let r=rs(is(import.meta.url)),e=ns(r,"..","assets","fonts");return["NotoSans-Regular.ttf","NotoSans-Bold.ttf"].map(t=>De(e,t)).filter(t=>ts(t))}function Np(){let r=process.env.ZIBBY_NODE_SESSION_PATH,e=process.env.ZIBBY_SESSION_PATH,t=r||(e?De(e,"social-card"):De(process.cwd(),".zibby","output","social-cards"));return hp(t,{recursive:!0}),t}function Xi(r,e){let t=Number(r);return Number.isFinite(t)?Math.max(kp,Math.min(bp,Math.round(t))):e}function qe(r){return String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function Op(r){return typeof r!="string"?null:r.trim().replace(/\.(png|svg)$/i,"").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^[.-]+|[.-]+$/g,"")||null}function Tp(r,e){if(typeof r!="string")return e;let t=r.trim();return/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(t)?t:e}function os(r,e,t){return String(r).length*e*t}function wr(r,e,t,n){let i=String(r).trim().split(/\s+/).filter(Boolean),s=[],o="";for(let a of i){let c=o?`${o} ${a}`:a;o&&os(c,t,n)>e?(s.push(o),o=a):o=c}return o&&s.push(o),s.length?s:[""]}function Rp(r){return r==="light"?{bg:"#ffffff",fg:"#0b0f14",muted:"#5b6673",pillText:"#0b0f14",checkFg:"#ffffff",divider:"#e2e6ea"}:{bg:"#0b0f14",fg:"#e9edf1",muted:"#8b98a5",pillText:"#e9edf1",checkFg:"#0b0f14",divider:"#22303c"}}function es(r,e,t,n,i){let s=(Array.isArray(r)?r:[]).map(g=>String(g??"").trim()).filter(Boolean).slice(0,Sp);if(s.length<2)return null;let o=24,a=54,c=26,d=90,l=17,p=s.map(g=>Math.max(96,os(g,o,.58)+c*2)),u=p.reduce((g,_)=>g+_,0)+d*(s.length-1),m=e-u/2,f=t+a/2,h=[];for(let g=0;g<s.length;g++){let _=p[g];if(h.push(`<rect x="${m.toFixed(1)}" y="${t}" width="${_.toFixed(1)}" height="${a}" rx="${a/2}" fill="${i}" fill-opacity="0.12" stroke="${i}" stroke-opacity="0.55" stroke-width="1.5"/>`,`<text x="${(m+_/2).toFixed(1)}" y="${(f+o*.35).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${o}" font-weight="700" fill="${n.pillText}">${qe(s[g])}</text>`),m+=_,g<s.length-1){let b=m,y=m+d,k=(b+y)/2;h.push(`<line x1="${b.toFixed(1)}" y1="${f}" x2="${y.toFixed(1)}" y2="${f}" stroke="${i}" stroke-opacity="0.55" stroke-width="2"/>`,`<circle cx="${k.toFixed(1)}" cy="${f}" r="${l}" fill="${i}"/>`,`<path d="M ${(k-8).toFixed(1)} ${f.toFixed(1)} l 5 5 l 9 -11" fill="none" stroke="${n.checkFg}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>`),m=y}}return{svg:h.join(`
790
+ `),height:a}}function Ap(r){let{width:e,height:t,theme:n,accent:i}=r,s=Rp(n),o=e/2,a=Math.round(e*.07),c=e-a*2,d=String(r.headline).trim(),l=r.eyebrow?String(r.eyebrow).trim().toUpperCase():"",p=r.subhead?String(r.subhead).trim():"",u=r.stat?String(r.stat).trim():"",m=r.footer?String(r.footer).trim():"",f=e/ss,h=Math.round(22*f),g=Math.round(29*f),_=p?wr(p,c,g,.52):[],b=Math.round(g*1.28),y=4,k=Math.round(26*f),T=Math.round(20*f),K=Math.round(24*f),Re=Math.round(30*f),R=!!(u||m),O=a,D=R?Math.round(110*f):a,x=t-O-D,ge=es(r.diagram,o,0,s,i),Cr=(L,de)=>{let Me=y+k;return l&&(Me+=h+T),Me+=de.length*Math.round(L*1.16),_.length&&(Me+=K+_.length*b),ge&&(Me+=Re+ge.height),Me},qt=[76,66,58,50,44,38].map(L=>Math.round(L*f)),Fs=4,Ae=qt[qt.length-1],Dt=wr(d,c,Ae,.6);for(let L of qt){let de=wr(d,c,L,.6);if(de.length<=Fs&&Cr(L,de)<=x){Ae=L,Dt=de;break}}let Gs=Math.round(Ae*1.16),Ks=Cr(Ae,Dt),H=O+Math.max(0,(x-Ks)/2),se=[];se.push(`<rect x="0" y="0" width="${e}" height="${t}" fill="${s.bg}"/>`);let Ur=Math.round(60*f);se.push(`<rect x="${(o-Ur/2).toFixed(1)}" y="${H}" width="${Ur}" height="${y}" rx="2" fill="${i}"/>`),H+=y+k,l&&(se.push(`<text x="${o}" y="${(H+h*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${h}" font-weight="700" letter-spacing="3" fill="${i}">${qe(l)}</text>`),H+=h+T);for(let L of Dt)se.push(`<text x="${o}" y="${(H+Ae*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${Ae}" font-weight="700" fill="${s.fg}">${qe(L)}</text>`),H+=Gs;if(_.length){H+=K;for(let L of _)se.push(`<text x="${o}" y="${(H+g*.82).toFixed(1)}" text-anchor="middle" font-family="${Oe}" font-size="${g}" font-weight="400" fill="${s.muted}">${qe(L)}</text>`),H+=b}if(ge){H+=Re;let L=es(r.diagram,o,H,s,i);L&&se.push(L.svg)}if(R){let L=Math.round(24*f),de=t-Math.round(46*f);m&&se.push(`<text x="${a}" y="${de}" text-anchor="start" font-family="${Oe}" font-size="${L}" font-weight="400" fill="${s.muted}">${qe(m)}</text>`),u&&se.push(`<text x="${e-a}" y="${de}" text-anchor="end" font-family="${Oe}" font-size="${L}" font-weight="700" fill="${i}">${qe(u)}</text>`)}return`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${t}" viewBox="0 0 ${e} ${t}">
791
791
  ${se.join(`
792
792
  `)}
793
- </svg>`}function Ap(r){let{Resvg:e}=yp();return new e(r,{font:{fontFiles:vp(),loadSystemFonts:!0,defaultFontFamily:Oe}}).render().asPng()}var as={id:"social-card",serverName:"social_card",allowedTools:["mcp__social_card__*"],description:'Social card \u2014 render a branded LinkedIn "concept card" PNG locally (bold headline + eyebrow + optional stat/footer/diagram); nothing leaves the box',promptFragment:`## Social Card (branded concept card, local)
793
+ </svg>`}function xp(r){let{Resvg:e}=gp();return new e(r,{font:{fontFiles:Ip(),loadSystemFonts:!0,defaultFontFamily:Oe}}).render().asPng()}var as={id:"social-card",serverName:"social_card",allowedTools:["mcp__social_card__*"],description:'Social card \u2014 render a branded LinkedIn "concept card" PNG locally (bold headline + eyebrow + optional stat/footer/diagram); nothing leaves the box',promptFragment:`## Social Card (branded concept card, local)
794
794
  You can render a BRANDED concept card PNG with the social_card_render tool \u2014 the
795
795
  clean, high-signal LinkedIn "concept card" style (bold headline + small uppercase
796
796
  eyebrow + optional stat/footer + optional A\u2014\u2713\u2014B diagram). Give it STRUCTURED
@@ -804,15 +804,15 @@ fields that capture the ONE key idea of your post:
804
804
  It renders server-side to a PNG in the run's output folder and returns
805
805
  { ok:true, files:[{ path, format:'png', bytes }] }. Pass the returned \`path\` as
806
806
  \`imagePath\` when you draft/publish a LinkedIn post to ATTACH the card as the
807
- post image. No browser, no external service \u2014 the data never leaves the machine.`,resolve({sessionPath:r,nodeName:e}={}){let t=Sp();if(!t)return{command:null,args:[],env:{},description:this.description};let n={},i=r&&e?De(r,e):null;i&&(n.ZIBBY_NODE_SESSION_PATH=i),r&&(n.ZIBBY_SESSION_PATH=r);for(let s of["ZIBBY_NODE_SESSION_PATH","ZIBBY_SESSION_PATH"])!n[s]&&process.env[s]&&(n[s]=process.env[s]);return{type:"stdio",command:"node",args:[t,"../dist/socialCard.js","socialCardSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){if(r!=="social_card_render")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=e?.headline;if(typeof t!="string"||!t.trim())return JSON.stringify({error:"headline (the big bold line) is required and must be a non-empty string."});let n=Xi(e?.width,ss),i=Xi(e?.height,gp),s=e?.theme==="light"?"light":"dark",o=Op(e?.accent,kp),a;try{a=Rp({width:n,height:i,theme:s,accent:o,headline:t,eyebrow:e?.eyebrow,subhead:e?.subhead,stat:e?.stat,footer:e?.footer,diagram:e?.diagram})}catch(m){return JSON.stringify({error:`Card layout failed: ${m.message}`})}let c;try{c=Ap(a)}catch(m){return JSON.stringify({error:`PNG rasterization failed: ${m.message}`})}let d=Ip(),l=Np(e?.filename)||`social-card-${Date.now()}`,p=De(d,`${l}.png`);Qi(p,c);let u=[{path:p,format:"png",bytes:c.length}];if(e?.output==="both"||e?.output==="svg"){let m=De(d,`${l}.svg`);Qi(m,a,"utf-8"),u.push({path:m,format:"svg",bytes:Buffer.byteLength(a,"utf-8")})}return JSON.stringify({ok:!0,width:n,height:i,theme:s,files:u})}catch(t){return JSON.stringify({error:`social_card_render failed: ${t.message}`})}},tools:[{name:"social_card_render",description:'Render a BRANDED "concept card" PNG LOCALLY (no external service) from structured fields \u2014 the clean LinkedIn concept-card style: bold headline + small uppercase eyebrow + optional supporting subhead, stat, footer, and an A\u2014\u2713\u2014B diagram. Returns { ok:true, files:[{ path, format:"png", bytes }] }. Pass the returned `path` as `imagePath` to a LinkedIn post tool to attach it as the post image. Example: { "headline":"No more copy-paste between AI tools", "eyebrow":"MY WORKFLOW", "stat":"11.8k stars", "footer":"Apache 2.0", "diagram":["Claude Code","Codex"] }.',input_schema:{type:"object",properties:{headline:{type:"string",description:"REQUIRED. The big, bold line \u2014 the single key idea/takeaway. Word-wrapped + auto-sized."},eyebrow:{type:"string",description:'Small uppercase kicker above the headline (e.g. "MY WORKFLOW", "OPEN SOURCE"). Rendered uppercased in the accent color.'},subhead:{type:"string",description:"One supporting line under the headline (muted)."},stat:{type:"string",description:'Optional stat, shown bottom-right in the accent color (e.g. "11.8k stars", "3x faster").'},footer:{type:"string",description:'Optional footer, shown bottom-left (muted) (e.g. "Apache 2.0", a repo name).'},diagram:{type:"array",items:{type:"string"},description:'Optional 2-3 short labels rendered as a chain of pills joined by a checkmark (A \u2014\u2713\u2014 B), e.g. ["Claude Code","Codex"].'},theme:{type:"string",enum:["dark","light"],description:"Card theme (default 'dark' \u2014 near-black bg, off-white text)."},accent:{type:"string",description:'Accent hex color (e.g. "#f97316"). Defaults to a tasteful blue (#3b82f6).'},width:{type:"number",description:"Image width in px (default 1200 \u2014 LinkedIn 1.91:1 link image)."},height:{type:"number",description:"Image height in px (default 627)."},filename:{type:"string",description:"Optional file basename (no extension); defaults to social-card-<timestamp>."},output:{type:"string",enum:["png","both"],description:"Which file(s) to write (default 'png'; 'both' also writes the .svg)."}},required:["headline"]}}]};import{spawnSync as xp}from"node:child_process";import{existsSync as q,readdirSync as ds,statSync as ps,writeFileSync as vr,mkdirSync as Ir}from"node:fs";import{dirname as Sr,extname as Lt,join as $,relative as Ep,resolve as jt}from"node:path";import{tmpdir as Nr}from"node:os";import{fileURLToPath as jp}from"node:url";import{SKILL_META as Lp}from"@zibby/skill-ids";import{binPath as $p}from"@zibby/bin-oxlint";import{binPath as Pp}from"@zibby/bin-semgrep";function Cp(){if(process.env.OXLINT_BIN)return process.env.OXLINT_BIN;try{let r=$p();if(r&&q(r))return r}catch{}return"oxlint"}function Up(){if(process.env.SEMGREP_CORE_BIN)return process.env.SEMGREP_CORE_BIN;try{let r=Pp();if(r&&q(r))return r}catch{}return"semgrep-core"}function Jp(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Sr(jp(import.meta.url)),e=jt(r,"..","bin","mcp-skill.mjs");return q(e)?e:null}var us=new Set(["node_modules",".git","dist","build","out","vendor","target",".venv","venv","__pycache__",".next",".turbo","coverage",".zibby"]),qp=400,Dp={plugins:["react","typescript","unicorn","oxc"],categories:{correctness:"error",suspicious:"warn"},rules:{"react/react-in-jsx-scope":"off","react/jsx-max-depth":"off","react/no-array-index-key":"off","react/jsx-key":"error","react/no-unknown-property":"error","no-unused-vars":"warn",eqeqeq:"warn"}},Bp=[".oxlintrc.json",".oxlintrc","oxlint.json"],xt=null;function Mp(){if(xt&&q(xt))return xt;try{let r=$(Nr(),"zibby-code-scan");Ir(r,{recursive:!0});let e=$(r,"oxlintrc.curated.json");return vr(e,JSON.stringify(Dp),"utf-8"),xt=e,e}catch{return null}}var ms={".java":"java",".py":"python",".go":"go",".rb":"ruby",".php":"php"},cs=Object.keys(ms),fs={rules:[{id:"zibby-java-command-injection",languages:["java"],severity:"ERROR",message:"Command execution (Runtime.exec / ProcessBuilder) \u2014 command injection risk if the argument is attacker-influenced. Validate/allow-list the input or avoid a shell.",patterns:[{"pattern-either":[{pattern:"Runtime.getRuntime().exec(...)"},{pattern:"new ProcessBuilder(...)"}]}]},{id:"zibby-python-subprocess-shell",languages:["python"],severity:"ERROR",message:"subprocess call with shell=True \u2014 command injection risk. Pass an argv list and shell=False.",pattern:"subprocess.$F(..., shell=True, ...)"},{id:"zibby-python-yaml-load",languages:["python"],severity:"WARNING",message:"yaml.load without a safe loader can instantiate arbitrary Python objects. Use yaml.safe_load.",pattern:"yaml.load(...)"},{id:"zibby-go-command-injection",languages:["go"],severity:"WARNING",message:"os/exec with a non-constant command \u2014 verify the value is not attacker-controlled (command injection).",pattern:"exec.Command($CMD, ...)"},{id:"zibby-ruby-command-injection",languages:["ruby"],severity:"ERROR",message:"Shell/eval execution (system / eval) \u2014 command injection risk if the argument is attacker-influenced.",patterns:[{"pattern-either":[{pattern:"system(...)"},{pattern:"eval(...)"}]}]},{id:"zibby-php-command-injection",languages:["php"],severity:"ERROR",message:"Shell/eval execution (system / exec / shell_exec) \u2014 command injection risk if the argument is attacker-influenced.",patterns:[{"pattern-either":[{pattern:"system(...);"},{pattern:"exec(...);"},{pattern:"shell_exec(...);"}]}]}]},Fp=[".semgrep.yml",".semgrep.yaml","semgrep.yml","semgrep.yaml"],Et=null;function Gp(){if(Et&&q(Et))return Et;try{let r=$(Nr(),"zibby-code-scan");Ir(r,{recursive:!0});let e=$(r,"semgrep.curated.rules.json");return vr(e,JSON.stringify(fs),"utf-8"),Et=e,e}catch{return null}}function Kp(r){return Fp.map(t=>$(r,t)).find(t=>q(t))||Gp()}function Hp(r){let e=[];for(let t of Array.isArray(r)?r:[]){if(typeof t!="string"||!t)continue;let n=ms[Lt(t).toLowerCase()];if(!n)continue;let i=t.replace(/\\/g,"/");e.push(["CodeTarget",{path:{fpath:i,ppath:`/${i.replace(/^\/+/,"")}`},analyzer:n,products:["sast"]}])}return["Targets",e]}var zp=0;function Yp(r){let e=Hp(r),t=e[1].length,n=$(Nr(),"zibby-code-scan");Ir(n,{recursive:!0});let i=$(n,`semgrep.targets.${process.pid}.${zp++}.json`);return vr(i,JSON.stringify(e),"utf-8"),{path:i,count:t}}function ls(r){let e=typeof r=="string"?r.toUpperCase():"";return e==="ERROR"?"error":e==="INFO"||e==="INVENTORY"||e==="EXPERIMENT"?"info":"warning"}var Wp=Object.fromEntries(fs.rules.map(r=>[r.id,r.severity]));function Zp(r,e){if(e)return ls(e);let t=Wp[r];return t?ls(t):"warning"}function Vp(r){let e=String(r||""),t=e.indexOf("{");if(t<0)return[];let n;try{n=JSON.parse(e.slice(t))}catch{return[]}return(n&&Array.isArray(n.results)?n.results:[]).map(s=>{if(!s||typeof s!="object")return null;let o=s.start&&typeof s.start=="object"?s.start:{},a=s.extra&&typeof s.extra=="object"?s.extra:{};return{file:s.path||"",line:Number.isFinite(o.line)?o.line:"",severity:Zp(s.check_id,a.severity),rule:s.check_id||"",message:(a.message||"").trim()}}).filter(s=>s&&(s.file||s.message))}function Qp(r,e,t=4e3){let n=new Set(e.map(o=>o.toLowerCase())),i=[r],s=0;for(;i.length;){let o=i.pop(),a;try{a=ds(o,{withFileTypes:!0})}catch{continue}for(let c of a){if(++s>t)return!1;if(c.isDirectory())!us.has(c.name)&&!c.name.startsWith(".")&&i.push($(o,c.name));else if(c.isFile()&&n.has(Lt(c.name).toLowerCase()))return!0}}return!1}function Xp(r){let e=String(r||"").trim();if(!e)return[];let t;try{t=JSON.parse(e)}catch{return[]}return(Array.isArray(t)?t:t&&Array.isArray(t.diagnostics)?t.diagnostics:[]).map(i=>{if(!i||typeof i!="object")return null;let s=Array.isArray(i.labels)&&i.labels.length?i.labels[0]:null,o=s&&s.span?s.span:null;return{file:i.filename||o&&o.filename||"",line:o&&Number.isFinite(o.line)?o.line:"",severity:i.severity||"warning",rule:i.code||"",message:i.message||""}}).filter(i=>i&&(i.file||i.message))}function eu(r){let e=String(r||"").trim();if(!e)return[];let t;try{t=JSON.parse(e)}catch{return[]}return Array.isArray(t)?t.map(n=>n&&typeof n=="object"?{file:n.filename||"",line:n.location&&Number.isFinite(n.location.row)?n.location.row:"",severity:"warning",rule:n.code||"",message:n.message||""}:null).filter(n=>n&&(n.file||n.message)):[]}function tu(r){let e=String(r||"").trim();if(!e)return[];let t=[];for(let n of e.split(`
808
- `)){let i=n.trim();if(!i)continue;let s;try{s=JSON.parse(i)}catch{continue}if(!s||typeof s!="object")continue;let o=s.location||{};t.push({file:o.file||"",line:Number.isFinite(o.line)?o.line:"",severity:s.severity||"warning",rule:s.code||"",message:s.message||""})}return t.filter(n=>n.file||n.message)}var ru=[{id:"oxlint",detect:r=>q($(r,"package.json")),langs:[".ts",".tsx",".js",".jsx",".mjs",".cjs"],bin:()=>Cp(),args:(r,e={})=>{let t=e.baseDir||".",i=Bp.some(o=>q($(t,o)))?null:Mp();return["--format","json",...i?["--config",i]:[],...r]},parse:Xp},{id:"semgrep",detect:r=>Qp(r,cs),langs:cs,bin:()=>Up(),args:(r,e={})=>{let t=e.baseDir||".",n=Kp(t),{path:i}=Yp(r);return[...n?["-rules",n]:[],"-targets",i,"-json"]},parse:Vp},{id:"ruff",detect:r=>q($(r,"pyproject.toml"))||q($(r,"requirements.txt"))||q($(r,"setup.py")),langs:[".py"],bin:()=>process.env.RUFF_BIN||"ruff",args:r=>["check","--output-format","json",...r],parse:eu},{id:"staticcheck",detect:r=>q($(r,"go.mod")),langs:[".go"],bin:()=>process.env.STATICCHECK_BIN||"staticcheck",args:r=>["-f","json",...r],parse:tu}];function nu(r,e,t){let n=[],i=new Set(e.map(o=>o.toLowerCase())),s=[r];for(;s.length&&n.length<t;){let o=s.pop(),a;try{a=ds(o,{withFileTypes:!0})}catch{continue}for(let c of a){if(n.length>=t)break;c.isDirectory()?!us.has(c.name)&&!c.name.startsWith(".")&&s.push($(o,c.name)):c.isFile()&&i.has(Lt(c.name).toLowerCase())&&n.push($(o,c.name))}}return n}function iu(r,e,t){let n=t.map(a=>Ep(e,a)).filter(Boolean);if(!n.length)return{scanner:r.id,skipped:"no matching files"};let i=r.bin(),s=xp(i,r.args(n,{baseDir:e}),{cwd:e,encoding:"utf-8",timeout:180*1e3,maxBuffer:32*1024*1024});if(s.error){let a=s.error.code==="ENOENT"?`binary not installed (${i})`:String(s.error.message||s.error);return{scanner:r.id,skipped:a}}let o=[];try{let a=r.parse(s.stdout,s.stderr,s.status);o=Array.isArray(a)?a.filter(Boolean):[]}catch{o=[]}return{scanner:r.id,filesScanned:n.length,findings:o}}var hs={id:"code-scan",serverName:"code_scan",meta:Lp["code-scan"],allowedTools:["mcp__code_scan__*"],description:"Code scan \u2014 run the RIGHT deterministic linter/analyzer for a checked-out repo (auto-detects the stack: JS/TS\u2192oxlint; Java/Python/Go/Ruby/PHP\u2192semgrep) and return structured findings. Fully local; the code never leaves the box.",promptFragment:`## Code Scan (deterministic linter, auto-detects the stack)
807
+ post image. No browser, no external service \u2014 the data never leaves the machine.`,resolve({sessionPath:r,nodeName:e}={}){let t=vp();if(!t)return{command:null,args:[],env:{},description:this.description};let n={},i=r&&e?De(r,e):null;i&&(n.ZIBBY_NODE_SESSION_PATH=i),r&&(n.ZIBBY_SESSION_PATH=r);for(let s of["ZIBBY_NODE_SESSION_PATH","ZIBBY_SESSION_PATH"])!n[s]&&process.env[s]&&(n[s]=process.env[s]);return{type:"stdio",command:"node",args:[t,"../dist/socialCard.js","socialCardSkill"],env:n,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){if(r!=="social_card_render")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=e?.headline;if(typeof t!="string"||!t.trim())return JSON.stringify({error:"headline (the big bold line) is required and must be a non-empty string."});let n=Xi(e?.width,ss),i=Xi(e?.height,_p),s=e?.theme==="light"?"light":"dark",o=Tp(e?.accent,wp),a;try{a=Ap({width:n,height:i,theme:s,accent:o,headline:t,eyebrow:e?.eyebrow,subhead:e?.subhead,stat:e?.stat,footer:e?.footer,diagram:e?.diagram})}catch(m){return JSON.stringify({error:`Card layout failed: ${m.message}`})}let c;try{c=xp(a)}catch(m){return JSON.stringify({error:`PNG rasterization failed: ${m.message}`})}let d=Np(),l=Op(e?.filename)||`social-card-${Date.now()}`,p=De(d,`${l}.png`);Qi(p,c);let u=[{path:p,format:"png",bytes:c.length}];if(e?.output==="both"||e?.output==="svg"){let m=De(d,`${l}.svg`);Qi(m,a,"utf-8"),u.push({path:m,format:"svg",bytes:Buffer.byteLength(a,"utf-8")})}return JSON.stringify({ok:!0,width:n,height:i,theme:s,files:u})}catch(t){return JSON.stringify({error:`social_card_render failed: ${t.message}`})}},tools:[{name:"social_card_render",description:'Render a BRANDED "concept card" PNG LOCALLY (no external service) from structured fields \u2014 the clean LinkedIn concept-card style: bold headline + small uppercase eyebrow + optional supporting subhead, stat, footer, and an A\u2014\u2713\u2014B diagram. Returns { ok:true, files:[{ path, format:"png", bytes }] }. Pass the returned `path` as `imagePath` to a LinkedIn post tool to attach it as the post image. Example: { "headline":"No more copy-paste between AI tools", "eyebrow":"MY WORKFLOW", "stat":"11.8k stars", "footer":"Apache 2.0", "diagram":["Claude Code","Codex"] }.',input_schema:{type:"object",properties:{headline:{type:"string",description:"REQUIRED. The big, bold line \u2014 the single key idea/takeaway. Word-wrapped + auto-sized."},eyebrow:{type:"string",description:'Small uppercase kicker above the headline (e.g. "MY WORKFLOW", "OPEN SOURCE"). Rendered uppercased in the accent color.'},subhead:{type:"string",description:"One supporting line under the headline (muted)."},stat:{type:"string",description:'Optional stat, shown bottom-right in the accent color (e.g. "11.8k stars", "3x faster").'},footer:{type:"string",description:'Optional footer, shown bottom-left (muted) (e.g. "Apache 2.0", a repo name).'},diagram:{type:"array",items:{type:"string"},description:'Optional 2-3 short labels rendered as a chain of pills joined by a checkmark (A \u2014\u2713\u2014 B), e.g. ["Claude Code","Codex"].'},theme:{type:"string",enum:["dark","light"],description:"Card theme (default 'dark' \u2014 near-black bg, off-white text)."},accent:{type:"string",description:'Accent hex color (e.g. "#f97316"). Defaults to a tasteful blue (#3b82f6).'},width:{type:"number",description:"Image width in px (default 1200 \u2014 LinkedIn 1.91:1 link image)."},height:{type:"number",description:"Image height in px (default 627)."},filename:{type:"string",description:"Optional file basename (no extension); defaults to social-card-<timestamp>."},output:{type:"string",enum:["png","both"],description:"Which file(s) to write (default 'png'; 'both' also writes the .svg)."}},required:["headline"]}}]};import{spawnSync as Ep}from"node:child_process";import{existsSync as q,readdirSync as ds,statSync as ps,writeFileSync as vr,mkdirSync as Ir}from"node:fs";import{dirname as Sr,extname as Lt,join as $,relative as jp,resolve as jt}from"node:path";import{tmpdir as Nr}from"node:os";import{fileURLToPath as Lp}from"node:url";import{SKILL_META as $p}from"@zibby/skill-ids";import{binPath as Pp}from"@zibby/bin-oxlint";import{binPath as Cp}from"@zibby/bin-semgrep";function Up(){if(process.env.OXLINT_BIN)return process.env.OXLINT_BIN;try{let r=Pp();if(r&&q(r))return r}catch{}return"oxlint"}function Jp(){if(process.env.SEMGREP_CORE_BIN)return process.env.SEMGREP_CORE_BIN;try{let r=Cp();if(r&&q(r))return r}catch{}return"semgrep-core"}function qp(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Sr(Lp(import.meta.url)),e=jt(r,"..","bin","mcp-skill.mjs");return q(e)?e:null}var us=new Set(["node_modules",".git","dist","build","out","vendor","target",".venv","venv","__pycache__",".next",".turbo","coverage",".zibby"]),Dp=400,Bp={plugins:["react","typescript","unicorn","oxc"],categories:{correctness:"error",suspicious:"warn"},rules:{"react/react-in-jsx-scope":"off","react/jsx-max-depth":"off","react/no-array-index-key":"off","react/jsx-key":"error","react/no-unknown-property":"error","no-unused-vars":"warn",eqeqeq:"warn"}},Mp=[".oxlintrc.json",".oxlintrc","oxlint.json"],xt=null;function Fp(){if(xt&&q(xt))return xt;try{let r=$(Nr(),"zibby-code-scan");Ir(r,{recursive:!0});let e=$(r,"oxlintrc.curated.json");return vr(e,JSON.stringify(Bp),"utf-8"),xt=e,e}catch{return null}}var ms={".java":"java",".py":"python",".go":"go",".rb":"ruby",".php":"php"},cs=Object.keys(ms),fs={rules:[{id:"zibby-java-command-injection",languages:["java"],severity:"ERROR",message:"Command execution (Runtime.exec / ProcessBuilder) \u2014 command injection risk if the argument is attacker-influenced. Validate/allow-list the input or avoid a shell.",patterns:[{"pattern-either":[{pattern:"Runtime.getRuntime().exec(...)"},{pattern:"new ProcessBuilder(...)"}]}]},{id:"zibby-python-subprocess-shell",languages:["python"],severity:"ERROR",message:"subprocess call with shell=True \u2014 command injection risk. Pass an argv list and shell=False.",pattern:"subprocess.$F(..., shell=True, ...)"},{id:"zibby-python-yaml-load",languages:["python"],severity:"WARNING",message:"yaml.load without a safe loader can instantiate arbitrary Python objects. Use yaml.safe_load.",pattern:"yaml.load(...)"},{id:"zibby-go-command-injection",languages:["go"],severity:"WARNING",message:"os/exec with a non-constant command \u2014 verify the value is not attacker-controlled (command injection).",pattern:"exec.Command($CMD, ...)"},{id:"zibby-ruby-command-injection",languages:["ruby"],severity:"ERROR",message:"Shell/eval execution (system / eval) \u2014 command injection risk if the argument is attacker-influenced.",patterns:[{"pattern-either":[{pattern:"system(...)"},{pattern:"eval(...)"}]}]},{id:"zibby-php-command-injection",languages:["php"],severity:"ERROR",message:"Shell/eval execution (system / exec / shell_exec) \u2014 command injection risk if the argument is attacker-influenced.",patterns:[{"pattern-either":[{pattern:"system(...);"},{pattern:"exec(...);"},{pattern:"shell_exec(...);"}]}]}]},Gp=[".semgrep.yml",".semgrep.yaml","semgrep.yml","semgrep.yaml"],Et=null;function Kp(){if(Et&&q(Et))return Et;try{let r=$(Nr(),"zibby-code-scan");Ir(r,{recursive:!0});let e=$(r,"semgrep.curated.rules.json");return vr(e,JSON.stringify(fs),"utf-8"),Et=e,e}catch{return null}}function Hp(r){return Gp.map(t=>$(r,t)).find(t=>q(t))||Kp()}function zp(r){let e=[];for(let t of Array.isArray(r)?r:[]){if(typeof t!="string"||!t)continue;let n=ms[Lt(t).toLowerCase()];if(!n)continue;let i=t.replace(/\\/g,"/");e.push(["CodeTarget",{path:{fpath:i,ppath:`/${i.replace(/^\/+/,"")}`},analyzer:n,products:["sast"]}])}return["Targets",e]}var Yp=0;function Wp(r){let e=zp(r),t=e[1].length,n=$(Nr(),"zibby-code-scan");Ir(n,{recursive:!0});let i=$(n,`semgrep.targets.${process.pid}.${Yp++}.json`);return vr(i,JSON.stringify(e),"utf-8"),{path:i,count:t}}function ls(r){let e=typeof r=="string"?r.toUpperCase():"";return e==="ERROR"?"error":e==="INFO"||e==="INVENTORY"||e==="EXPERIMENT"?"info":"warning"}var Zp=Object.fromEntries(fs.rules.map(r=>[r.id,r.severity]));function Vp(r,e){if(e)return ls(e);let t=Zp[r];return t?ls(t):"warning"}function Qp(r){let e=String(r||""),t=e.indexOf("{");if(t<0)return[];let n;try{n=JSON.parse(e.slice(t))}catch{return[]}return(n&&Array.isArray(n.results)?n.results:[]).map(s=>{if(!s||typeof s!="object")return null;let o=s.start&&typeof s.start=="object"?s.start:{},a=s.extra&&typeof s.extra=="object"?s.extra:{};return{file:s.path||"",line:Number.isFinite(o.line)?o.line:"",severity:Vp(s.check_id,a.severity),rule:s.check_id||"",message:(a.message||"").trim()}}).filter(s=>s&&(s.file||s.message))}function Xp(r,e,t=4e3){let n=new Set(e.map(o=>o.toLowerCase())),i=[r],s=0;for(;i.length;){let o=i.pop(),a;try{a=ds(o,{withFileTypes:!0})}catch{continue}for(let c of a){if(++s>t)return!1;if(c.isDirectory())!us.has(c.name)&&!c.name.startsWith(".")&&i.push($(o,c.name));else if(c.isFile()&&n.has(Lt(c.name).toLowerCase()))return!0}}return!1}function eu(r){let e=String(r||"").trim();if(!e)return[];let t;try{t=JSON.parse(e)}catch{return[]}return(Array.isArray(t)?t:t&&Array.isArray(t.diagnostics)?t.diagnostics:[]).map(i=>{if(!i||typeof i!="object")return null;let s=Array.isArray(i.labels)&&i.labels.length?i.labels[0]:null,o=s&&s.span?s.span:null;return{file:i.filename||o&&o.filename||"",line:o&&Number.isFinite(o.line)?o.line:"",severity:i.severity||"warning",rule:i.code||"",message:i.message||""}}).filter(i=>i&&(i.file||i.message))}function tu(r){let e=String(r||"").trim();if(!e)return[];let t;try{t=JSON.parse(e)}catch{return[]}return Array.isArray(t)?t.map(n=>n&&typeof n=="object"?{file:n.filename||"",line:n.location&&Number.isFinite(n.location.row)?n.location.row:"",severity:"warning",rule:n.code||"",message:n.message||""}:null).filter(n=>n&&(n.file||n.message)):[]}function ru(r){let e=String(r||"").trim();if(!e)return[];let t=[];for(let n of e.split(`
808
+ `)){let i=n.trim();if(!i)continue;let s;try{s=JSON.parse(i)}catch{continue}if(!s||typeof s!="object")continue;let o=s.location||{};t.push({file:o.file||"",line:Number.isFinite(o.line)?o.line:"",severity:s.severity||"warning",rule:s.code||"",message:s.message||""})}return t.filter(n=>n.file||n.message)}var nu=[{id:"oxlint",detect:r=>q($(r,"package.json")),langs:[".ts",".tsx",".js",".jsx",".mjs",".cjs"],bin:()=>Up(),args:(r,e={})=>{let t=e.baseDir||".",i=Mp.some(o=>q($(t,o)))?null:Fp();return["--format","json",...i?["--config",i]:[],...r]},parse:eu},{id:"semgrep",detect:r=>Xp(r,cs),langs:cs,bin:()=>Jp(),args:(r,e={})=>{let t=e.baseDir||".",n=Hp(t),{path:i}=Wp(r);return[...n?["-rules",n]:[],"-targets",i,"-json"]},parse:Qp},{id:"ruff",detect:r=>q($(r,"pyproject.toml"))||q($(r,"requirements.txt"))||q($(r,"setup.py")),langs:[".py"],bin:()=>process.env.RUFF_BIN||"ruff",args:r=>["check","--output-format","json",...r],parse:tu},{id:"staticcheck",detect:r=>q($(r,"go.mod")),langs:[".go"],bin:()=>process.env.STATICCHECK_BIN||"staticcheck",args:r=>["-f","json",...r],parse:ru}];function iu(r,e,t){let n=[],i=new Set(e.map(o=>o.toLowerCase())),s=[r];for(;s.length&&n.length<t;){let o=s.pop(),a;try{a=ds(o,{withFileTypes:!0})}catch{continue}for(let c of a){if(n.length>=t)break;c.isDirectory()?!us.has(c.name)&&!c.name.startsWith(".")&&s.push($(o,c.name)):c.isFile()&&i.has(Lt(c.name).toLowerCase())&&n.push($(o,c.name))}}return n}function su(r,e,t){let n=t.map(a=>jp(e,a)).filter(Boolean);if(!n.length)return{scanner:r.id,skipped:"no matching files"};let i=r.bin(),s=Ep(i,r.args(n,{baseDir:e}),{cwd:e,encoding:"utf-8",timeout:180*1e3,maxBuffer:32*1024*1024});if(s.error){let a=s.error.code==="ENOENT"?`binary not installed (${i})`:String(s.error.message||s.error);return{scanner:r.id,skipped:a}}let o=[];try{let a=r.parse(s.stdout,s.stderr,s.status);o=Array.isArray(a)?a.filter(Boolean):[]}catch{o=[]}return{scanner:r.id,filesScanned:n.length,findings:o}}var hs={id:"code-scan",serverName:"code_scan",meta:$p["code-scan"],allowedTools:["mcp__code_scan__*"],description:"Code scan \u2014 run the RIGHT deterministic linter/analyzer for a checked-out repo (auto-detects the stack: JS/TS\u2192oxlint; Java/Python/Go/Ruby/PHP\u2192semgrep) and return structured findings. Fully local; the code never leaves the box.",promptFragment:`## Code Scan (deterministic linter, auto-detects the stack)
809
809
  After you've cloned the repo, call \`scan_code\` to get DETERMINISTIC linter
810
810
  findings for WHATEVER stack this repo is \u2014 it auto-detects (JS/TS\u2192oxlint;
811
811
  Java/Python/Go/Ruby/PHP\u2192semgrep) and runs the matching tool. Pass \`files\` (the changed files, ideal for
812
812
  a review) or \`dir\` (a directory to scan). Findings are GROUND-TRUTH CANDIDATES:
813
813
  triage them for THIS change, verify each in context (false positives exist \u2014
814
814
  trace before asserting), fold noise, and turn the real ones into inline
815
- suggestions. Don't hand-lint what the tool already covers, and don't re-run it.`,resolve(){let r=Jp();return r?{type:"stdio",command:"node",args:[r,"../dist/code-scan.js","codeScanSkill"],env:{},description:this.description,alwaysLoad:!0}:{command:null,args:[],env:{},description:this.description}},async handleToolCall(r,e){if(r!=="scan_code")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=Array.isArray(e?.files)?e.files.filter(a=>typeof a=="string"&&a.trim()):null,n;if(e?.dir&&typeof e.dir=="string"?n=jt(e.dir):t&&t.length?n=su(t.map(a=>jt(a))):n=process.cwd(),!q(n)||!ps(n).isDirectory())return JSON.stringify({error:`dir does not exist or is not a directory: ${n}`});let i=t?t.map(a=>jt(n,a)):null,s=[],o=0;for(let a of ru){let c=!1;try{c=!!a.detect(n)}catch{c=!1}if(!c)continue;let d=new Set(a.langs.map(u=>u.toLowerCase())),l=i?i.filter(u=>d.has(Lt(u).toLowerCase())):nu(n,a.langs,qp),p=iu(a,n,l);Array.isArray(p.findings)&&(o+=p.findings.length),s.push(p)}return s.length?JSON.stringify({ok:!0,baseDir:n,totalFindings:o,scanners:s}):JSON.stringify({ok:!0,baseDir:n,scanners:[],totalFindings:0,note:"No known stack detected (no package.json / pyproject / go.mod \u2026). Review by hand."})}catch(t){return JSON.stringify({error:`scan_code failed: ${t.message}`})}},tools:[{name:"scan_code",description:"Run the right deterministic linter for a checked-out repo and return structured findings. Auto-detects the stack (JS/TS \u2192 oxlint; Java/Python/Go/Ruby/PHP \u2192 semgrep OSS) and runs each matching tool, scoped to files in its languages. Pass `files` (e.g. the changed files of the PR \u2014 recommended for a review) OR `dir` (a directory to scan). Returns { scanners: [ { scanner, findings: [ { file, line, severity, rule, message } ] } ] }. Findings are CANDIDATES \u2014 verify each in context before asserting. Best-effort: a stack whose linter is not installed is skipped with a note.",input_schema:{type:"object",properties:{dir:{type:"string",description:"Absolute path to the checked-out repo (or subdirectory) to scan. Defaults to the current working directory."},files:{type:"array",items:{type:"string"},description:"Explicit list of files to scan (paths relative to `dir`, or absolute). Best for a code review \u2014 pass the PR's changed files. When omitted, the whole `dir` is walked (bounded)."}}}}]};function su(r){if(!r.length)return process.cwd();if(r.length===1)return Sr(r[0]);let e=r.map(s=>s.split("/")),t=e[0],n=[];for(let s=0;s<t.length;s++){let o=t[s];if(e.every(a=>a[s]===o))n.push(o);else break}let i=n.join("/");return i&&q(i)&&ps(i).isDirectory()?i:Sr(r[0])}import{dirname as ou,resolve as au}from"path";import{fileURLToPath as cu}from"url";import{existsSync as lu}from"fs";function du(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=ou(cu(import.meta.url)),e=au(r,"..","bin","mcp-skill.mjs");return lu(e)?e:null}function pu(){return process.env.PROJECT_API_TOKEN||process.env.ZIBBY_USER_TOKEN||null}function uu(){return((process.env.PROGRESS_API_URL||"").replace(/\/executions\/?$/,"")||process.env.ZIBBY_ACCOUNT_API_URL||process.env.ZIBBY_PROD_ACCOUNT_API_URL||(process.env.ZIBBY_ENV==="local"?"http://localhost:3001":"https://api-prod.zibby.app")).replace(/\/+$/,"")}var mu={name:"trigger_agent",description:"Trigger another Zibby workflow/agent run in THIS project (fire-and-forget). Call it once per run you want to start \u2014 the agent decides which and how many. Omit workflowType to re-run THIS same agent (self-dispatch, e.g. with a different trigger input). Returns the started run's executionId; does NOT wait for it to finish.",input_schema:{type:"object",properties:{workflowType:{type:"string",description:"Which workflow to trigger (its type/slug in this project). Omit to trigger THIS same agent (self-dispatch)."},input:{type:"object",description:"The trigger payload passed to the target workflow (validated against its state schema)."}},required:[]}},ys={id:"trigger-agent",serverName:"trigger",allowedTools:["mcp__trigger__*"],envKeys:[],description:"Trigger another Zibby workflow/agent run in this project (agent-driven, fire-and-forget; cloud + self-hosted).",promptFragment:"## Trigger another agent (agent-driven)\nYou can start another Zibby agent run yourself with the `trigger_agent`\ntool \u2014 and YOU decide when and how many times to call it. Each call starts ONE\nindependent run (fire-and-forget) and returns its executionId; it does NOT wait for\nthat run to finish.\n- To re-run THIS same agent (self-dispatch) \u2014 e.g. to hand an item to another of\n this agent's scenarios \u2014 OMIT `workflowType` and pass the `input` for that run.\n- To trigger a DIFFERENT agent in the project, pass its `workflowType` + `input`.\nCall it once per run you want to start (loop over your items and call it for each).\nIt never throws \u2014 a failure comes back as { ok:false, error }; log it and move on.",resolve(){let r=du();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","PROJECT_ID","WORKFLOW_TYPE","PROGRESS_API_URL","ZIBBY_ACCOUNT_API_URL","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_USER_TOKEN"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/triggerAgent.js","triggerAgentSkill"],env:e,description:this.description,alwaysLoad:!1}},async handleToolCall(r,e={}){if(r!=="trigger_agent")return JSON.stringify({ok:!1,error:`unknown tool: ${r}`});try{let t=process.env.PROJECT_ID,n=pu(),i=typeof e.workflowType=="string"&&e.workflowType.trim()?e.workflowType.trim():(process.env.WORKFLOW_TYPE||"").trim();if(!t)return JSON.stringify({ok:!1,error:"PROJECT_ID not set \u2014 cannot resolve the target project."});if(!n)return JSON.stringify({ok:!1,error:"PROJECT_API_TOKEN not set \u2014 cannot authenticate the trigger."});if(!i)return JSON.stringify({ok:!1,error:"No workflowType given and WORKFLOW_TYPE is unset \u2014 nothing to trigger."});let s=`${uu()}/projects/${encodeURIComponent(t)}/workflows/${encodeURIComponent(i)}/trigger`,o=new AbortController,a=setTimeout(()=>o.abort(),2e4),c;try{c=await fetch(s,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${n}`},body:JSON.stringify({input:e.input&&typeof e.input=="object"?e.input:{}}),signal:o.signal})}finally{clearTimeout(a)}let d=await c.text().catch(()=>""),l;try{l=d?JSON.parse(d):{}}catch{l={raw:d}}if(!c.ok)return JSON.stringify({ok:!1,error:`trigger failed (HTTP ${c.status})`,detail:l&&(l.error||l.message)||d.slice(0,300)});let p=l.executionId||l.execution?.id||l.id||null;return JSON.stringify({ok:!0,workflowType:i,executionId:p,note:"run started (fire-and-forget)"})}catch(t){return JSON.stringify({ok:!1,error:`trigger_agent failed: ${t?.message||String(t)}`})}},tools:[mu]};import{spawnSync as fu}from"node:child_process";import{existsSync as $t,mkdirSync as gs,readdirSync as hu,writeFileSync as yu}from"node:fs";import{createHash as gu}from"node:crypto";import{join as Pt}from"node:path";import{SKILL_META as _u}from"@zibby/skill-ids";function _s(){return process.env.CBM_BIN||"/usr/local/bin/codebase-memory-mcp"}function bs(){if(process.env.CBM_CACHE_DIR)return process.env.CBM_CACHE_DIR;let r=process.env.WORKSPACE||process.env.ZIBBY_WORKSPACE;return r?Pt(r,".zibby","cbm-cache"):"/tmp/zibby-cbm-cache"}function bu(){let r=process.env.WORKSPACE||process.env.ZIBBY_WORKSPACE||"/workspace",e=Pt(r,".zibby","repos");try{if($t(e)){let t=hu(e,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>Pt(e,n.name));if(t.length===1)return t[0];if(t.length>1)return e}}catch{}return $t(r)?r:null}function ku(r){return gu("sha256").update(r).digest("hex").slice(0,16)}var ks={id:"codebase-memory",serverName:"codebase_memory",meta:_u["codebase-memory"],allowedTools:["mcp__codebase_memory__*"],description:"Codebase memory \u2014 code-graph + semantic index over the checked-out repo (architecture, graph search, dependency trace, change detection)",promptFragment:`## Codebase Memory (code-graph + semantic index over THIS repo)
815
+ suggestions. Don't hand-lint what the tool already covers, and don't re-run it.`,resolve(){let r=qp();return r?{type:"stdio",command:"node",args:[r,"../dist/code-scan.js","codeScanSkill"],env:{},description:this.description,alwaysLoad:!0}:{command:null,args:[],env:{},description:this.description}},async handleToolCall(r,e){if(r!=="scan_code")return JSON.stringify({error:`Unknown tool: ${r}`});try{let t=Array.isArray(e?.files)?e.files.filter(a=>typeof a=="string"&&a.trim()):null,n;if(e?.dir&&typeof e.dir=="string"?n=jt(e.dir):t&&t.length?n=ou(t.map(a=>jt(a))):n=process.cwd(),!q(n)||!ps(n).isDirectory())return JSON.stringify({error:`dir does not exist or is not a directory: ${n}`});let i=t?t.map(a=>jt(n,a)):null,s=[],o=0;for(let a of nu){let c=!1;try{c=!!a.detect(n)}catch{c=!1}if(!c)continue;let d=new Set(a.langs.map(u=>u.toLowerCase())),l=i?i.filter(u=>d.has(Lt(u).toLowerCase())):iu(n,a.langs,Dp),p=su(a,n,l);Array.isArray(p.findings)&&(o+=p.findings.length),s.push(p)}return s.length?JSON.stringify({ok:!0,baseDir:n,totalFindings:o,scanners:s}):JSON.stringify({ok:!0,baseDir:n,scanners:[],totalFindings:0,note:"No known stack detected (no package.json / pyproject / go.mod \u2026). Review by hand."})}catch(t){return JSON.stringify({error:`scan_code failed: ${t.message}`})}},tools:[{name:"scan_code",description:"Run the right deterministic linter for a checked-out repo and return structured findings. Auto-detects the stack (JS/TS \u2192 oxlint; Java/Python/Go/Ruby/PHP \u2192 semgrep OSS) and runs each matching tool, scoped to files in its languages. Pass `files` (e.g. the changed files of the PR \u2014 recommended for a review) OR `dir` (a directory to scan). Returns { scanners: [ { scanner, findings: [ { file, line, severity, rule, message } ] } ] }. Findings are CANDIDATES \u2014 verify each in context before asserting. Best-effort: a stack whose linter is not installed is skipped with a note.",input_schema:{type:"object",properties:{dir:{type:"string",description:"Absolute path to the checked-out repo (or subdirectory) to scan. Defaults to the current working directory."},files:{type:"array",items:{type:"string"},description:"Explicit list of files to scan (paths relative to `dir`, or absolute). Best for a code review \u2014 pass the PR's changed files. When omitted, the whole `dir` is walked (bounded)."}}}}]};function ou(r){if(!r.length)return process.cwd();if(r.length===1)return Sr(r[0]);let e=r.map(s=>s.split("/")),t=e[0],n=[];for(let s=0;s<t.length;s++){let o=t[s];if(e.every(a=>a[s]===o))n.push(o);else break}let i=n.join("/");return i&&q(i)&&ps(i).isDirectory()?i:Sr(r[0])}import{dirname as au,resolve as cu}from"path";import{fileURLToPath as lu}from"url";import{existsSync as du}from"fs";function pu(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=au(lu(import.meta.url)),e=cu(r,"..","bin","mcp-skill.mjs");return du(e)?e:null}function uu(){return process.env.PROJECT_API_TOKEN||process.env.ZIBBY_USER_TOKEN||null}function mu(){return((process.env.PROGRESS_API_URL||"").replace(/\/executions\/?$/,"")||process.env.ZIBBY_ACCOUNT_API_URL||process.env.ZIBBY_PROD_ACCOUNT_API_URL||(process.env.ZIBBY_ENV==="local"?"http://localhost:3001":"https://api-prod.zibby.app")).replace(/\/+$/,"")}var fu={name:"trigger_agent",description:"Trigger another Zibby workflow/agent run in THIS project (fire-and-forget). Call it once per run you want to start \u2014 the agent decides which and how many. Omit workflowType to re-run THIS same agent (self-dispatch, e.g. with a different trigger input). Returns the started run's executionId; does NOT wait for it to finish.",input_schema:{type:"object",properties:{workflowType:{type:"string",description:"Which workflow to trigger (its type/slug in this project). Omit to trigger THIS same agent (self-dispatch)."},input:{type:"object",description:"The trigger payload passed to the target workflow (validated against its state schema)."}},required:[]}},ys={id:"trigger-agent",serverName:"trigger",allowedTools:["mcp__trigger__*"],envKeys:[],description:"Trigger another Zibby workflow/agent run in this project (agent-driven, fire-and-forget; cloud + self-hosted).",promptFragment:"## Trigger another agent (agent-driven)\nYou can start another Zibby agent run yourself with the `trigger_agent`\ntool \u2014 and YOU decide when and how many times to call it. Each call starts ONE\nindependent run (fire-and-forget) and returns its executionId; it does NOT wait for\nthat run to finish.\n- To re-run THIS same agent (self-dispatch) \u2014 e.g. to hand an item to another of\n this agent's scenarios \u2014 OMIT `workflowType` and pass the `input` for that run.\n- To trigger a DIFFERENT agent in the project, pass its `workflowType` + `input`.\nCall it once per run you want to start (loop over your items and call it for each).\nIt never throws \u2014 a failure comes back as { ok:false, error }; log it and move on.",resolve(){let r=pu();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","PROJECT_ID","WORKFLOW_TYPE","PROGRESS_API_URL","ZIBBY_ACCOUNT_API_URL","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_USER_TOKEN"])process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/triggerAgent.js","triggerAgentSkill"],env:e,description:this.description,alwaysLoad:!1}},async handleToolCall(r,e={}){if(r!=="trigger_agent")return JSON.stringify({ok:!1,error:`unknown tool: ${r}`});try{let t=process.env.PROJECT_ID,n=uu(),i=typeof e.workflowType=="string"&&e.workflowType.trim()?e.workflowType.trim():(process.env.WORKFLOW_TYPE||"").trim();if(!t)return JSON.stringify({ok:!1,error:"PROJECT_ID not set \u2014 cannot resolve the target project."});if(!n)return JSON.stringify({ok:!1,error:"PROJECT_API_TOKEN not set \u2014 cannot authenticate the trigger."});if(!i)return JSON.stringify({ok:!1,error:"No workflowType given and WORKFLOW_TYPE is unset \u2014 nothing to trigger."});let s=`${mu()}/projects/${encodeURIComponent(t)}/workflows/${encodeURIComponent(i)}/trigger`,o=new AbortController,a=setTimeout(()=>o.abort(),2e4),c;try{c=await fetch(s,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${n}`},body:JSON.stringify({input:e.input&&typeof e.input=="object"?e.input:{}}),signal:o.signal})}finally{clearTimeout(a)}let d=await c.text().catch(()=>""),l;try{l=d?JSON.parse(d):{}}catch{l={raw:d}}if(!c.ok)return JSON.stringify({ok:!1,error:`trigger failed (HTTP ${c.status})`,detail:l&&(l.error||l.message)||d.slice(0,300)});let p=l.executionId||l.execution?.id||l.id||null;return JSON.stringify({ok:!0,workflowType:i,executionId:p,note:"run started (fire-and-forget)"})}catch(t){return JSON.stringify({ok:!1,error:`trigger_agent failed: ${t?.message||String(t)}`})}},tools:[fu]};import{spawnSync as hu}from"node:child_process";import{existsSync as $t,mkdirSync as gs,readdirSync as yu,writeFileSync as gu}from"node:fs";import{createHash as _u}from"node:crypto";import{join as Pt}from"node:path";import{SKILL_META as bu}from"@zibby/skill-ids";function _s(){return process.env.CBM_BIN||"/usr/local/bin/codebase-memory-mcp"}function bs(){if(process.env.CBM_CACHE_DIR)return process.env.CBM_CACHE_DIR;let r=process.env.WORKSPACE||process.env.ZIBBY_WORKSPACE;return r?Pt(r,".zibby","cbm-cache"):"/tmp/zibby-cbm-cache"}function ku(){let r=process.env.WORKSPACE||process.env.ZIBBY_WORKSPACE||"/workspace",e=Pt(r,".zibby","repos");try{if($t(e)){let t=yu(e,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>Pt(e,n.name));if(t.length===1)return t[0];if(t.length>1)return e}}catch{}return $t(r)?r:null}function wu(r){return _u("sha256").update(r).digest("hex").slice(0,16)}var ks={id:"codebase-memory",serverName:"codebase_memory",meta:bu["codebase-memory"],allowedTools:["mcp__codebase_memory__*"],description:"Codebase memory \u2014 code-graph + semantic index over the checked-out repo (architecture, graph search, dependency trace, change detection)",promptFragment:`## Codebase Memory (code-graph + semantic index over THIS repo)
816
816
  The checked-out repository is indexed into a queryable code graph + semantic
817
817
  index. Reach for these instead of blindly grepping when you need structure,
818
818
  relationships, or "where does X live / what depends on Y":
@@ -824,8 +824,8 @@ relationships, or "where does X live / what depends on Y":
824
824
  - get_code_snippet / search_code: pull the exact code for a node / text match.
825
825
  - index_status / list_projects: confirm the index is present before querying.
826
826
  The repo is indexed for you at the start of the run; if a query comes back
827
- empty, call index_status, and only re-index (index_repository) if needed.`,resolve(){let r=bs();try{gs(r,{recursive:!0})}catch{}let e={CBM_CACHE_DIR:r};return process.env.WORKSPACE&&(e.WORKSPACE=process.env.WORKSPACE),{type:"stdio",command:_s(),args:[],env:e,description:this.description,alwaysLoad:!0}},invokeAgentOptions(){try{let r=bu();if(!r)return{};let e=bs();try{gs(e,{recursive:!0})}catch{}let t=Pt(e,`.cbm-indexed-${ku(r)}`);if($t(t))return{};let n=_s();if(!$t(n)&&!process.env.CBM_BIN)return{};let i=fu(n,["cli","index_repository",JSON.stringify({repo_path:r})],{env:{...process.env,CBM_CACHE_DIR:e},encoding:"utf-8",timeout:300*1e3,maxBuffer:32*1024*1024});try{yu(t,`${new Date().toISOString()} status=${i.status}
828
- `)}catch{}}catch{}return{}}};import{existsSync as ws,readFileSync as wu}from"node:fs";import{homedir as Su}from"node:os";import{join as vu,dirname as Iu,resolve as Nu}from"node:path";import{fileURLToPath as Ou}from"node:url";import{SKILL_META as Tu}from"@zibby/skill-ids";var Ru=3e4,Or=1;function Au(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Iu(Ou(import.meta.url)),e=Nu(r,"..","bin","mcp-skill.mjs");return ws(e)?e:null}function xu(){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=vu(Su(),".zibby","config.json");return ws(r)&&JSON.parse(wu(r,"utf-8")).sessionToken||null}catch{return null}}function Eu(){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 ju(){let r={};for(let[e,t]of Object.entries(process.env)){let n=/^ZIBBY_STORE__(.+)$/.exec(e);if(!n)continue;let i=typeof t=="string"?t.trim():"";i&&(r[n[1]]=i)}return r}function Tr(r){let e=ju(),t=Object.keys(e),n=typeof r=="string"?r.trim():"";return n?Object.prototype.hasOwnProperty.call(e,n)?{storeId:e[n],name:n}:{error:`unknown store '${n}'; available: ${t.join(", ")}`}:t.length===1?{storeId:e[t[0]],name:t[0]}:t.length===0?{error:"no knowledge-base store is bound to this agent"}:{error:`multiple stores are bound; pass \`store\` (one of: ${t.join(", ")})`}}async function Rr(r,e,t){let n=xu();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). The knowledge base is only available inside a Zibby run.");let i=`${Eu()}/datasets/stores/${encodeURIComponent(r)}/${e}`,s={Authorization:`Bearer ${n}`,"Content-Type":"application/json"},o=JSON.stringify(t),a;for(let c=0;c<=Or;c++){let d=new AbortController,l=setTimeout(()=>d.abort(),Ru);try{let p=await fetch(i,{method:"POST",headers:s,body:o,signal:d.signal}),u=await p.text().catch(()=>"");if(p.status>=500&&c<Or){a=new Error(`store ${p.status}`);continue}if(!p.ok)throw new Error(`gbrain ${e} failed (${p.status}): ${u.slice(0,300)}`);try{return JSON.parse(u)}catch{throw new Error(`store returned non-JSON: ${u.slice(0,200)}`)}}catch(p){if(a=p,!((p?.name==="AbortError"||p?.code==="ECONNREFUSED"||/fetch failed|network/i.test(String(p?.message)))&&c<Or))break}finally{clearTimeout(l)}}throw a||new Error(`gbrain ${e} request failed`)}var Ss={id:"gbrain",serverName:"gbrain",allowedTools:["mcp__gbrain__*"],meta:Tu.gbrain,description:"Knowledge base (GBrain) \u2014 ingest source documents into, semantically query, and prune a per-tenant Postgres/pgvector brain (a `postgres`-type store, brokered by the control-plane)",promptFragment:`## Knowledge Base (GBrain \u2014 per-tenant document brain)
827
+ empty, call index_status, and only re-index (index_repository) if needed.`,resolve(){let r=bs();try{gs(r,{recursive:!0})}catch{}let e={CBM_CACHE_DIR:r};return process.env.WORKSPACE&&(e.WORKSPACE=process.env.WORKSPACE),{type:"stdio",command:_s(),args:[],env:e,description:this.description,alwaysLoad:!0}},invokeAgentOptions(){try{let r=ku();if(!r)return{};let e=bs();try{gs(e,{recursive:!0})}catch{}let t=Pt(e,`.cbm-indexed-${wu(r)}`);if($t(t))return{};let n=_s();if(!$t(n)&&!process.env.CBM_BIN)return{};let i=hu(n,["cli","index_repository",JSON.stringify({repo_path:r})],{env:{...process.env,CBM_CACHE_DIR:e},encoding:"utf-8",timeout:300*1e3,maxBuffer:32*1024*1024});try{gu(t,`${new Date().toISOString()} status=${i.status}
828
+ `)}catch{}}catch{}return{}}};import{existsSync as ws,readFileSync as Su}from"node:fs";import{homedir as vu}from"node:os";import{join as Iu,dirname as Nu,resolve as Ou}from"node:path";import{fileURLToPath as Tu}from"node:url";import{SKILL_META as Ru}from"@zibby/skill-ids";var Au=3e4,Or=1;function xu(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let r=Nu(Tu(import.meta.url)),e=Ou(r,"..","bin","mcp-skill.mjs");return ws(e)?e:null}function Eu(){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=Iu(vu(),".zibby","config.json");return ws(r)&&JSON.parse(Su(r,"utf-8")).sessionToken||null}catch{return null}}function ju(){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 Lu(){let r={};for(let[e,t]of Object.entries(process.env)){let n=/^ZIBBY_STORE__(.+)$/.exec(e);if(!n)continue;let i=typeof t=="string"?t.trim():"";i&&(r[n[1]]=i)}return r}function Tr(r){let e=Lu(),t=Object.keys(e),n=typeof r=="string"?r.trim():"";return n?Object.prototype.hasOwnProperty.call(e,n)?{storeId:e[n],name:n}:{error:`unknown store '${n}'; available: ${t.join(", ")}`}:t.length===1?{storeId:e[t[0]],name:t[0]}:t.length===0?{error:"no knowledge-base store is bound to this agent"}:{error:`multiple stores are bound; pass \`store\` (one of: ${t.join(", ")})`}}async function Rr(r,e,t){let n=Eu();if(!n)throw new Error("No backend credential (PROJECT_API_TOKEN). The knowledge base is only available inside a Zibby run.");let i=`${ju()}/datasets/stores/${encodeURIComponent(r)}/${e}`,s={Authorization:`Bearer ${n}`,"Content-Type":"application/json"},o=JSON.stringify(t),a;for(let c=0;c<=Or;c++){let d=new AbortController,l=setTimeout(()=>d.abort(),Au);try{let p=await fetch(i,{method:"POST",headers:s,body:o,signal:d.signal}),u=await p.text().catch(()=>"");if(p.status>=500&&c<Or){a=new Error(`store ${p.status}`);continue}if(!p.ok)throw new Error(`gbrain ${e} failed (${p.status}): ${u.slice(0,300)}`);try{return JSON.parse(u)}catch{throw new Error(`store returned non-JSON: ${u.slice(0,200)}`)}}catch(p){if(a=p,!((p?.name==="AbortError"||p?.code==="ECONNREFUSED"||/fetch failed|network/i.test(String(p?.message)))&&c<Or))break}finally{clearTimeout(l)}}throw a||new Error(`gbrain ${e} request failed`)}var Ss={id:"gbrain",serverName:"gbrain",allowedTools:["mcp__gbrain__*"],meta:Ru.gbrain,description:"Knowledge base (GBrain) \u2014 ingest source documents into, semantically query, and prune a per-tenant Postgres/pgvector brain (a `postgres`-type store, brokered by the control-plane)",promptFragment:`## Knowledge Base (GBrain \u2014 per-tenant document brain)
829
829
  You have a per-tenant KNOWLEDGE BASE (a Postgres + pgvector "brain"), bound as a
830
830
  \`postgres\`-type store in the "AVAILABLE STORES" block below. Ingested documents
831
831
  are addressed by a STABLE \`sourceId\` so re-ingesting the same source UPSERTS
@@ -836,7 +836,7 @@ Tools:
836
836
  Pass deleted:true to remove a source that no longer exists upstream.
837
837
  - gbrain_query({ query, topK, store? }): semantic search; returns the topK most
838
838
  relevant document chunks with their sourceId and relevance score.
839
- - gbrain_delete({ sourceIds, store? }): remove documents by their stable sourceId(s).`,resolve(){let r=Au();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);for(let t of Object.keys(process.env))/^ZIBBY_STORE__.+$/.test(t)&&process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/gbrain.js","gbrainSkill"],env:e,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){try{switch(r){case"gbrain_ingest":{let t=Array.isArray(e?.docs)?e.docs:null;if(!t)return JSON.stringify({error:"docs is required (array of { sourceId, markdown, deleted? })"});if(t.length===0)return JSON.stringify({ok:!0,upserted:0,deleted:0,chunks:0,note:"empty docs \u2014 nothing to ingest"});if(t.find(a=>!a||typeof a.sourceId!="string"||!a.sourceId.trim())!==void 0)return JSON.stringify({error:"every doc requires a non-empty string sourceId"});let i=t.find(a=>a.deleted!==!0&&(typeof a.markdown!="string"||!a.markdown.length));if(i!==void 0)return JSON.stringify({error:`doc "${i.sourceId}" has no markdown (required unless deleted:true)`});let s=Tr(e?.store);if(s.error)return JSON.stringify({error:s.error});let o=await Rr(s.storeId,"ingest",{docs:t});return JSON.stringify({...o,store:s.name,storeId:s.storeId})}case"gbrain_query":{let t=typeof e?.query=="string"?e.query.trim():"";if(!t)return JSON.stringify({error:"query is required"});let n=Tr(e?.store);if(n.error)return JSON.stringify({error:n.error});let i={query:t};Number.isInteger(e?.topK)&&e.topK>0&&(i.topK=Math.min(e.topK,50));let s=await Rr(n.storeId,"query",i);return JSON.stringify({...s,store:n.name,storeId:n.storeId})}case"gbrain_delete":{let t=Array.isArray(e?.sourceIds)?e.sourceIds.filter(s=>typeof s=="string"&&s.trim()):null;if(!t||t.length===0)return JSON.stringify({error:"sourceIds is required (non-empty array of strings)"});let n=Tr(e?.store);if(n.error)return JSON.stringify({error:n.error});let i=await Rr(n.storeId,"delete",{sourceIds:t});return JSON.stringify({...i,store:n.name,storeId:n.storeId})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:String(t?.message||t)})}},tools:[{name:"gbrain_ingest",description:'Upsert documents into the knowledge base. Each doc is { sourceId, markdown, deleted? }; sourceId is a STABLE id (e.g. "owner/repo#docs/x.md" or a Lark doc token) so re-ingesting the same source UPDATES in place instead of duplicating. Set deleted:true to remove a source.',input_schema:{type:"object",properties:{docs:{type:"array",description:"Documents to upsert.",items:{type:"object",properties:{sourceId:{type:"string",description:"Stable source id \u2014 the upsert/delete key."},markdown:{type:"string",description:"The normalized markdown content of the document."},deleted:{type:"boolean",description:"When true, remove this sourceId from the brain (markdown ignored)."}},required:["sourceId"]}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["docs"]}},{name:"gbrain_query",description:"Semantic-search the knowledge base and return the most relevant document chunks (each with its sourceId and relevance score).",input_schema:{type:"object",properties:{query:{type:"string",description:"Natural-language query."},topK:{type:"integer",description:"How many chunks to return (default 8, max 50)."},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["query"]}},{name:"gbrain_delete",description:"Remove documents from the knowledge base by their stable sourceId(s).",input_schema:{type:"object",properties:{sourceIds:{type:"array",description:"Stable source ids to delete.",items:{type:"string"}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["sourceIds"]}}]};import{existsSync as le,readFileSync as tt,readdirSync as Ar,mkdirSync as Lu,writeFileSync as Be,statSync as Ns}from"fs";import{join as A,resolve as xr,relative as Os,dirname as Ts}from"path";import{fileURLToPath as $u}from"url";import{createRequire as Pu}from"module";var Cu=Pu(import.meta.url),Uu=`## Workflow Builder
839
+ - gbrain_delete({ sourceIds, store? }): remove documents by their stable sourceId(s).`,resolve(){let r=xu();if(!r)return{command:null,args:[],env:{},description:this.description};let e={};for(let t of["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV","ZIBBY_PROD_ACCOUNT_API_URL","ZIBBY_USER_TOKEN","WORKFLOW_TYPE"])process.env[t]&&(e[t]=process.env[t]);for(let t of Object.keys(process.env))/^ZIBBY_STORE__.+$/.test(t)&&process.env[t]&&(e[t]=process.env[t]);return{type:"stdio",command:"node",args:[r,"../dist/gbrain.js","gbrainSkill"],env:e,description:this.description,alwaysLoad:!0}},async handleToolCall(r,e){try{switch(r){case"gbrain_ingest":{let t=Array.isArray(e?.docs)?e.docs:null;if(!t)return JSON.stringify({error:"docs is required (array of { sourceId, markdown, deleted? })"});if(t.length===0)return JSON.stringify({ok:!0,upserted:0,deleted:0,chunks:0,note:"empty docs \u2014 nothing to ingest"});if(t.find(a=>!a||typeof a.sourceId!="string"||!a.sourceId.trim())!==void 0)return JSON.stringify({error:"every doc requires a non-empty string sourceId"});let i=t.find(a=>a.deleted!==!0&&(typeof a.markdown!="string"||!a.markdown.length));if(i!==void 0)return JSON.stringify({error:`doc "${i.sourceId}" has no markdown (required unless deleted:true)`});let s=Tr(e?.store);if(s.error)return JSON.stringify({error:s.error});let o=await Rr(s.storeId,"ingest",{docs:t});return JSON.stringify({...o,store:s.name,storeId:s.storeId})}case"gbrain_query":{let t=typeof e?.query=="string"?e.query.trim():"";if(!t)return JSON.stringify({error:"query is required"});let n=Tr(e?.store);if(n.error)return JSON.stringify({error:n.error});let i={query:t};Number.isInteger(e?.topK)&&e.topK>0&&(i.topK=Math.min(e.topK,50));let s=await Rr(n.storeId,"query",i);return JSON.stringify({...s,store:n.name,storeId:n.storeId})}case"gbrain_delete":{let t=Array.isArray(e?.sourceIds)?e.sourceIds.filter(s=>typeof s=="string"&&s.trim()):null;if(!t||t.length===0)return JSON.stringify({error:"sourceIds is required (non-empty array of strings)"});let n=Tr(e?.store);if(n.error)return JSON.stringify({error:n.error});let i=await Rr(n.storeId,"delete",{sourceIds:t});return JSON.stringify({...i,store:n.name,storeId:n.storeId})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(t){return JSON.stringify({error:String(t?.message||t)})}},tools:[{name:"gbrain_ingest",description:'Upsert documents into the knowledge base. Each doc is { sourceId, markdown, deleted? }; sourceId is a STABLE id (e.g. "owner/repo#docs/x.md" or a Lark doc token) so re-ingesting the same source UPDATES in place instead of duplicating. Set deleted:true to remove a source.',input_schema:{type:"object",properties:{docs:{type:"array",description:"Documents to upsert.",items:{type:"object",properties:{sourceId:{type:"string",description:"Stable source id \u2014 the upsert/delete key."},markdown:{type:"string",description:"The normalized markdown content of the document."},deleted:{type:"boolean",description:"When true, remove this sourceId from the brain (markdown ignored)."}},required:["sourceId"]}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["docs"]}},{name:"gbrain_query",description:"Semantic-search the knowledge base and return the most relevant document chunks (each with its sourceId and relevance score).",input_schema:{type:"object",properties:{query:{type:"string",description:"Natural-language query."},topK:{type:"integer",description:"How many chunks to return (default 8, max 50)."},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["query"]}},{name:"gbrain_delete",description:"Remove documents from the knowledge base by their stable sourceId(s).",input_schema:{type:"object",properties:{sourceIds:{type:"array",description:"Stable source ids to delete.",items:{type:"string"}},store:{type:"string",description:"The bound knowledge-base store NAME (from AVAILABLE STORES). Omit if exactly one store is bound."}},required:["sourceIds"]}}]};import{existsSync as le,readFileSync as tt,readdirSync as Ar,mkdirSync as $u,writeFileSync as Be,statSync as Ns}from"fs";import{join as A,resolve as xr,relative as Os,dirname as Ts}from"path";import{fileURLToPath as Pu}from"url";import{createRequire as Cu}from"module";var Uu=Cu(import.meta.url),Ju=`## Workflow Builder
840
840
 
841
841
  You can help users build custom AI workflows using the Zibby workflow framework.
842
842
 
@@ -946,9 +946,9 @@ Call with no arguments to see all available topics.
946
946
  - Workflow names must be kebab-case (e.g., ticket-triage, pr-review).
947
947
  - State flows through: each node's validated output is stored under its name in state (e.g., state.classify_ticket).
948
948
  - Downstream nodes reference upstream outputs in their prompt function (e.g., \\\`\\\${JSON.stringify(state.classify_ticket, null, 2)}\\\`).
949
- - Nodes can declare skills to get MCP tool access \u2014 the framework handles server lifecycle automatically.`,Rs=/^[a-z][a-z0-9-]{0,62}[a-z0-9]$/;function As(r){return`${r.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("")}Workflow`}function et(r){return`${r.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}Node`}function Ju(r){let e=r?.agent;return e?e.provider?e.provider:e.gemini?"gemini":e.codex?"codex":e.claude?"claude":e.cursor?"cursor":process.env.AGENT_TYPE||"cursor":process.env.AGENT_TYPE||"cursor"}async function qu(r){let e=xr(r,".zibby.config.mjs");if(!le(e))return{};try{return(await import(e)).default||{}}catch{return{}}}function Du(){try{let r=Ts(Cu.resolve("@zibby/core/package.json")),e=A(r,"templates","browser-test-automation"),t=tt(A(e,"nodes","preflight.mjs"),"utf-8"),n=tt(A(e,"graph.mjs"),"utf-8");return{preflight:t,graph:n}}catch{return null}}var vs=Ts($u(import.meta.url));function xs(){let r=xr(vs,"..","..","..","docsite","docs");if(le(r))return r;let e=xr(vs,"..","docs");return le(e)?e:null}function Is(){let r=xs();if(!r)return[];try{let e=(t,n="")=>{let i=[];for(let s of Ar(t)){let o=A(t,s);try{if(Ns(o).isDirectory())i=i.concat(e(o,`${n}${s}/`));else if(s.endsWith(".md")){let a=`${n}${s.replace(/\.md$/,"")}`;i.push(a)}}catch{}}return i};return e(r)}catch{return[]}}function Es(r){let e=xs();if(!e)return null;let t=A(e,`${r}.md`);if(!le(t))return null;try{return tt(t,"utf-8")}catch{return null}}function Bu(r){let e=r.nodes.map(o=>{let a=o.inputFields?.length?`Input fields: ${o.inputFields.join(", ")}`:"Input: receives full state",c=o.outputFields?.length?`Output fields: ${o.outputFields.join(", ")}`:"Output: determined by task",d=o.skills?.length?`Skills: ${o.skills.join(", ")}`:"";return`- ${o.name}: ${o.description}. ${a}. ${c}.${d?` ${d}`:""}`}).join(`
949
+ - Nodes can declare skills to get MCP tool access \u2014 the framework handles server lifecycle automatically.`,Rs=/^[a-z][a-z0-9-]{0,62}[a-z0-9]$/;function As(r){return`${r.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("")}Workflow`}function et(r){return`${r.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}Node`}function qu(r){let e=r?.agent;return e?e.provider?e.provider:e.gemini?"gemini":e.codex?"codex":e.claude?"claude":e.cursor?"cursor":process.env.AGENT_TYPE||"cursor":process.env.AGENT_TYPE||"cursor"}async function Du(r){let e=xr(r,".zibby.config.mjs");if(!le(e))return{};try{return(await import(e)).default||{}}catch{return{}}}function Bu(){try{let r=Ts(Uu.resolve("@zibby/core/package.json")),e=A(r,"templates","browser-test-automation"),t=tt(A(e,"nodes","preflight.mjs"),"utf-8"),n=tt(A(e,"graph.mjs"),"utf-8");return{preflight:t,graph:n}}catch{return null}}var vs=Ts(Pu(import.meta.url));function xs(){let r=xr(vs,"..","..","..","docsite","docs");if(le(r))return r;let e=xr(vs,"..","docs");return le(e)?e:null}function Is(){let r=xs();if(!r)return[];try{let e=(t,n="")=>{let i=[];for(let s of Ar(t)){let o=A(t,s);try{if(Ns(o).isDirectory())i=i.concat(e(o,`${n}${s}/`));else if(s.endsWith(".md")){let a=`${n}${s.replace(/\.md$/,"")}`;i.push(a)}}catch{}}return i};return e(r)}catch{return[]}}function Es(r){let e=xs();if(!e)return null;let t=A(e,`${r}.md`);if(!le(t))return null;try{return tt(t,"utf-8")}catch{return null}}function Mu(r){let e=r.nodes.map(o=>{let a=o.inputFields?.length?`Input fields: ${o.inputFields.join(", ")}`:"Input: receives full state",c=o.outputFields?.length?`Output fields: ${o.outputFields.join(", ")}`:"Output: determined by task",d=o.skills?.length?`Skills: ${o.skills.join(", ")}`:"";return`- ${o.name}: ${o.description}. ${a}. ${c}.${d?` ${d}`:""}`}).join(`
950
950
  `),t=r.edges.map(o=>o.condition?`- ${o.from} \u2192 ${o.to} (conditional: ${o.condition})`:`- ${o.from} \u2192 ${o.to}`).join(`
951
- `),n=Du(),i=Es("custom-workflows"),s="";return n&&(s+=`
951
+ `),n=Bu(),i=Es("custom-workflows"),s="";return n&&(s+=`
952
952
  ## Real working examples from the Zibby framework
953
953
 
954
954
  ### Example node (preflight.mjs) \u2014 a prompt-only node with Zod schema and onComplete hook:
@@ -1025,7 +1025,7 @@ Return a JSON object with this exact structure:
1025
1025
  }
1026
1026
  }
1027
1027
 
1028
- IMPORTANT: Return ONLY valid JSON. No markdown fences, no explanation outside the JSON.`}async function js(r,e){let t=await qu(e),n=Ju(t);try{let{invokeAgent:i}=await import("@zibby/core"),s=Bu(r),o=await i(s,{state:{agentType:n,config:t,cwd:e,workspace:e}},{model:t?.agent?.[n]?.model||"auto",workspace:e,config:t,timeout:12e4}),c=(typeof o=="string"?o:o?.raw||JSON.stringify(o?.structured||o)).match(/\{[\s\S]*\}/);if(!c)throw new Error("Agent did not return valid JSON");return JSON.parse(c[0])}catch(i){return console.warn(`Agent code generation failed (${i.message}), using templates`),Mu(r)}}function Mu(r){let e={};for(let t of r.nodes){let n=et(t.name),i=`${t.name.split("_").map(c=>c.charAt(0).toUpperCase()+c.slice(1)).join("")}OutputSchema`,s=t.outputFields?.length?t.outputFields.map(c=>` ${c}: z.string().describe('${c}'),`).join(`
1028
+ IMPORTANT: Return ONLY valid JSON. No markdown fences, no explanation outside the JSON.`}async function js(r,e){let t=await Du(e),n=qu(t);try{let{invokeAgent:i}=await import("@zibby/core"),s=Mu(r),o=await i(s,{state:{agentType:n,config:t,cwd:e,workspace:e}},{model:t?.agent?.[n]?.model||"auto",workspace:e,config:t,timeout:12e4}),c=(typeof o=="string"?o:o?.raw||JSON.stringify(o?.structured||o)).match(/\{[\s\S]*\}/);if(!c)throw new Error("Agent did not return valid JSON");return JSON.parse(c[0])}catch(i){return console.warn(`Agent code generation failed (${i.message}), using templates`),Fu(r)}}function Fu(r){let e={};for(let t of r.nodes){let n=et(t.name),i=`${t.name.split("_").map(c=>c.charAt(0).toUpperCase()+c.slice(1)).join("")}OutputSchema`,s=t.outputFields?.length?t.outputFields.map(c=>` ${c}: z.string().describe('${c}'),`).join(`
1029
1029
  `):` summary: z.string().describe('Summary of the result'),
1030
1030
  status: z.enum(['ok', 'warn', 'error']).describe('Overall status'),`,o=r.edges.filter(c=>c.to===t.name&&c.from!=="START").map(c=>`Previous step (${c.from}): \${JSON.stringify(state.${c.from} || {}, null, 2)}`).join(`
1031
1031
  `),a=o?`${t.description}
@@ -1047,7 +1047,7 @@ export const ${n} = {
1047
1047
  prompt: (state) => \`${a}\`,
1048
1048
  outputSchema: ${i},
1049
1049
  };
1050
- `}}return{nodes:e}}function Fu(r,e,t,n){let i=e.toLowerCase(),s=As(i),o=A(r,".zibby","workflows",i),a=A(o,"nodes");Lu(a,{recursive:!0});let c=t.nodes.map(_=>_.name);for(let _ of t.nodes){let b=n.nodes?.[_.name]?.code;b&&Be(A(a,`${_.name.replace(/_/g,"-")}.mjs`),b,"utf-8")}let d=c.map(_=>{let b=et(_),y=_.replace(/_/g,"-");return`export { ${b} } from './${y}.mjs';`});Be(A(a,"index.mjs"),`${d.join(`
1050
+ `}}return{nodes:e}}function Gu(r,e,t,n){let i=e.toLowerCase(),s=As(i),o=A(r,".zibby","workflows",i),a=A(o,"nodes");$u(a,{recursive:!0});let c=t.nodes.map(_=>_.name);for(let _ of t.nodes){let b=n.nodes?.[_.name]?.code;b&&Be(A(a,`${_.name.replace(/_/g,"-")}.mjs`),b,"utf-8")}let d=c.map(_=>{let b=et(_),y=_.replace(/_/g,"-");return`export { ${b} } from './${y}.mjs';`});Be(A(a,"index.mjs"),`${d.join(`
1051
1051
  `)}
1052
1052
  `,"utf-8");let l=c[0],p=c.map(_=>et(_)).join(", "),u=c.map(_=>` graph.addNode('${_}', ${et(_)});`).join(`
1053
1053
  `),m=t.edges.map(_=>_.condition?` graph.addConditionalEdges('${_.from}', (state) => {
@@ -1073,17 +1073,17 @@ ${m}
1073
1073
  }
1074
1074
  }
1075
1075
  `;Be(A(o,"graph.mjs"),f,"utf-8");let h={name:i,description:t.description||`${s} workflow`,entryClass:s,triggers:{api:!0}};Be(A(o,"workflow.json"),`${JSON.stringify(h,null,2)}
1076
- `,"utf-8");let g=["graph.mjs","workflow.json","nodes/index.mjs",...c.map(_=>`nodes/${_.replace(/_/g,"-")}.mjs`)];return{workflowDir:Os(r,o),files:g,className:s,slug:i}}async function Gu(r){let{name:e,description:t,nodes:n,edges:i}=r;if(!e||!Rs.test(e.toLowerCase()))return JSON.stringify({error:`Invalid workflow name "${e}". Must be kebab-case, 2-64 chars, lowercase letters/numbers/hyphens.`});if(!n||n.length===0)return JSON.stringify({error:"At least one node is required."});let s={name:e.toLowerCase(),description:t||`${As(e.toLowerCase())} workflow`,nodes:n.map(o=>({name:o.name.replace(/-/g,"_"),description:o.description||`Process ${o.name}`,inputFields:o.inputFields||[],outputFields:o.outputFields||[]})),edges:i||[]};if(s.edges.length===0&&s.nodes.length>0){for(let o=0;o<s.nodes.length-1;o++)s.edges.push({from:s.nodes[o].name,to:s.nodes[o+1].name});s.edges.push({from:s.nodes[s.nodes.length-1].name,to:"END"})}return JSON.stringify({ok:!0,spec:s,message:`Workflow "${s.name}" designed with ${s.nodes.length} node(s). Call build_workflow to generate the code.`,preview:{nodes:s.nodes.map(o=>o.name),flow:s.edges.map(o=>o.condition?`${o.from} \u2192(if ${o.condition})\u2192 ${o.to}`:`${o.from} \u2192 ${o.to}`)}})}async function Ku(r,e){let{name:t,spec:n}=r,i=(t||n?.name||"").toLowerCase();if(!i||!Rs.test(i))return JSON.stringify({error:`Invalid workflow name "${i}".`});if(!n||!n.nodes||n.nodes.length===0)return JSON.stringify({error:"spec with nodes is required. Call design_workflow first."});let s=A(e,".zibby","workflows",i);if(le(s))return JSON.stringify({error:`Workflow "${i}" already exists at .zibby/workflows/${i}/. Delete it first or choose a different name.`});let o=await js(n,e),a=Fu(e,i,n,o);return JSON.stringify({ok:!0,...a,message:`Workflow "${i}" created at ${a.workflowDir}/`,nextSteps:[`Test locally: zibby start ${i}`,`Deploy to cloud: zibby deploy ${i} --project <project-id>`,`Tail logs: zibby logs --workflow ${i} --project <project-id>`]})}async function Hu(r,e){let{workflowName:t,nodeName:n,description:i,inputFields:s,outputFields:o}=r,a=(t||"").toLowerCase(),c=(n||"").replace(/-/g,"_"),d=A(e,".zibby","workflows",a);if(!le(d))return JSON.stringify({error:`Workflow "${a}" not found. Create it first with build_workflow.`});let l={name:a,description:"",nodes:[{name:c,description:i||`Process ${c}`,inputFields:s||[],outputFields:o||[]}],edges:[]},u=(await js(l,e)).nodes?.[c]?.code;if(!u)return JSON.stringify({error:"Failed to generate node code."});let m=A(d,"nodes"),f=`${c.replace(/_/g,"-")}.mjs`;Be(A(m,f),u,"utf-8");let h=A(m,"index.mjs"),g=et(c),_=`export { ${g} } from './${c.replace(/_/g,"-")}.mjs';
1077
- `,b=le(h)?tt(h,"utf-8"):"";return b.includes(g)||Be(h,b+_,"utf-8"),JSON.stringify({ok:!0,file:`nodes/${f}`,exportName:g,message:`Node "${c}" added. Update graph.mjs to wire it into the graph.`})}async function zu(r,e){let{name:t,projectId:n}=r,i=(t||"").toLowerCase();if(!i)return JSON.stringify({error:"Workflow name is required."});if(!n)return JSON.stringify({error:"projectId is required."});let s=A(e,".zibby","workflows",i);if(!le(s))return JSON.stringify({error:`Workflow "${i}" not found at .zibby/workflows/${i}/`});try{let{execSync:o}=await import("child_process"),a=o(`node "${A(e,"packages/cli/bin/zibby.js")}" deploy ${i} --project ${n}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:a.trim()})}catch{try{let{execSync:a}=await import("child_process"),c=a(`npx zibby deploy ${i} --project ${n}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:c.trim()})}catch(a){return JSON.stringify({error:`Deploy failed: ${a.message}`})}}}function Yu(r){let e=A(r,".zibby","workflows");if(!le(e))return JSON.stringify({workflows:[],message:"No workflows found. Use build_workflow to create one."});let n=Ar(e).filter(i=>{try{return Ns(A(e,i)).isDirectory()}catch{return!1}}).map(i=>{let s=A(e,i,"workflow.json"),o={};try{o=JSON.parse(tt(s,"utf-8"))}catch{}let a=A(e,i,"nodes"),c=0;try{c=Ar(a).filter(d=>d.endsWith(".mjs")&&d!=="index.mjs").length}catch{}return{name:i,description:o.description||"",nodeCount:c,path:Os(r,A(e,i))}});return JSON.stringify({workflows:n})}var Ls={id:"workflow-builder",description:"Build, scaffold, and deploy custom AI workflows via conversation",envKeys:[],promptFragment:Uu,tools:[{name:"design_workflow",description:"Design a workflow spec (nodes, edges, descriptions) for the user to review before building. Call this after understanding requirements.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name in kebab-case (e.g., ticket-triage)"},description:{type:"string",description:"What the workflow does"},nodes:{type:"array",items:{type:"object",properties:{name:{type:"string",description:"Node name in snake_case (e.g., classify_ticket)"},description:{type:"string",description:"What this node does \u2014 be specific about input/output"},inputFields:{type:"array",items:{type:"string"},description:"Key fields this node reads from state"},outputFields:{type:"array",items:{type:"string"},description:"Key fields this node produces"}},required:["name","description"]},description:"Workflow nodes (processing steps)"},edges:{type:"array",items:{type:"object",properties:{from:{type:"string",description:"Source node name"},to:{type:"string",description:'Target node name (or "END")'},condition:{type:"string",description:"JS expression for conditional routing (optional)"}},required:["from","to"]},description:"Edges connecting nodes. If omitted, nodes are wired linearly."}},required:["name","description","nodes"]}},{name:"build_workflow",description:"Generate real workflow code on disk from a design spec. Uses the configured AI agent for high-quality code generation.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name (from design_workflow)"},spec:{type:"object",description:"The full spec object returned by design_workflow",properties:{name:{type:"string"},description:{type:"string"},nodes:{type:"array",items:{type:"object"}},edges:{type:"array",items:{type:"object"}}}}},required:["name","spec"]}},{name:"add_node",description:"Add a new node to an existing workflow. Generates the node file and updates the barrel export.",input_schema:{type:"object",properties:{workflowName:{type:"string",description:"Existing workflow name (kebab-case)"},nodeName:{type:"string",description:"New node name (snake_case)"},description:{type:"string",description:"What this node does"},inputFields:{type:"array",items:{type:"string"},description:"Fields read from state"},outputFields:{type:"array",items:{type:"string"},description:"Fields produced"}},required:["workflowName","nodeName","description"]}},{name:"deploy_workflow",description:"Deploy a workflow to Zibby Cloud. Returns the trigger URL.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name to deploy"},projectId:{type:"string",description:"Target project ID"}},required:["name","projectId"]}},{name:"list_workflows",description:"List all local workflows in .zibby/workflows/.",input_schema:{type:"object",properties:{}}},{name:"explore_framework_docs",description:"Read Zibby framework documentation on demand. Call this before building complex workflows or when you need details on advanced patterns (middleware, conditional routing, skills, deployment, CLI commands).",input_schema:{type:"object",properties:{topic:{type:"string",description:'Doc topic to read (e.g., "workflow", "custom-workflows", "cli-reference", "packages/core", "packages/skills", "integrations/jira"). Call with no topic to list all available docs.'}}}}],async handleToolCall(r,e,t){let n=t?.options?.workspace||process.cwd();try{switch(r){case"design_workflow":return await Gu(e);case"build_workflow":return await Ku(e,n);case"add_node":return await Hu(e,n);case"deploy_workflow":return await zu(e,n);case"list_workflows":return Yu(n);case"explore_framework_docs":{let i=(e.topic||"").trim();if(!i){let o=Is();return JSON.stringify({available:o,hint:"Call again with a topic to read its content."})}let s=Es(i);if(!s){let o=Is();return JSON.stringify({error:`Doc "${i}" not found.`,available:o})}return JSON.stringify({topic:i,content:s})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(i){return JSON.stringify({error:i.message})}},resolve(){return null}};import{resolveIntegrationToken as Wu}from"@zibby/core/backend-client.js";var jr=Object.freeze({id:"openai_billing",requiresIntegration:I.OPENAI_BILLING,description:"OpenAI organization billing/usage admin API (paste sk-admin-... key)"}),Lr=Object.freeze({id:"anthropic_billing",requiresIntegration:I.ANTHROPIC_BILLING,description:"Anthropic organization cost/usage admin API (paste sk-ant-admin-... key)"}),$r=Object.freeze({id:"cursor_admin",requiresIntegration:I.CURSOR_ADMIN,description:"Cursor Team/Enterprise admin API (paste admin key)"});function $s(r){return Math.floor(r/1e3)}function Er(r){return new Date(r).toISOString().slice(0,10)}function Ps(r){return new Date(r).toISOString()}async function rt(r){let e=await Wu(r);if(!e?.token)throw new Error(`${r} token resolver returned no token`);return e.token}async function Cs({startMs:r,endMs:e,groupBy:t=["project_id","line_item"]}){let n=await rt("openai_billing"),i=[],s=0,o=t.map(c=>`group_by[]=${encodeURIComponent(c)}`).join("&"),a=null;for(let c=0;c<50;c++){let l=`https://api.openai.com/v1/organization/costs?${[`start_time=${$s(r)}`,`end_time=${$s(e)}`,"bucket_width=1d","limit=180",o,a?`page=${encodeURIComponent(a)}`:""].filter(Boolean).join("&")}`,p=await fetch(l,{headers:{Authorization:`Bearer ${n}`}});if(!p.ok){let m=await p.text().catch(()=>"");throw new Error(`OpenAI costs API ${p.status}: ${m.slice(0,200)}`)}let u=await p.json();for(let m of u.data||[]){s+=1;let f=Er((m.start_time||0)*1e3);for(let h of m.results||[])i.push({provider:"openai",day:f,costUsd:Number(h.amount?.value??0),projectId:h.project_id||void 0,apiKeyId:h.api_key_id||void 0,model:h.line_item||void 0})}if(!u.has_more||!u.next_page)break;a=u.next_page}return{ok:!0,items:i,rawBuckets:s}}async function Zu(){let r=await rt("openai_billing"),t=await fetch("https://api.openai.com/v1/organization/projects?limit=100",{headers:{Authorization:`Bearer ${r}`}});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`OpenAI projects API ${t.status}: ${s.slice(0,200)}`)}let n=await t.json(),i=new Map;for(let s of n.data||[])i.set(s.id,s.name);return i}async function Us({startMs:r,endMs:e,groupBy:t=["workspace_id"]}){let n=await rt("anthropic_billing"),i=[],s=0,o=t.map(c=>`group_by[]=${encodeURIComponent(c)}`).join("&"),a=null;for(let c=0;c<50;c++){let l=`https://api.anthropic.com/v1/organizations/cost_report?${[`starting_at=${encodeURIComponent(Ps(r))}`,`ending_at=${encodeURIComponent(Ps(e))}`,"bucket=1d","limit=100",o,a?`page=${encodeURIComponent(a)}`:""].filter(Boolean).join("&")}`,p=await fetch(l,{headers:{"x-api-key":n,"anthropic-version":"2023-06-01"}});if(!p.ok){let m=await p.text().catch(()=>"");throw new Error(`Anthropic cost_report ${p.status}: ${m.slice(0,200)}`)}let u=await p.json();for(let m of u.data||[]){s+=1;let f=(m.starting_at||"").slice(0,10);for(let h of m.results||[])i.push({provider:"anthropic",day:f,costUsd:Number(h.amount??h.cost??0),workspaceId:h.workspace_id||void 0,apiKeyId:h.api_key_id||void 0,model:h.model||void 0,tokensIn:h.uncached_input_tokens!=null?Number(h.uncached_input_tokens):void 0,tokensOut:h.output_tokens!=null?Number(h.output_tokens):void 0,cachedTokens:h.cached_input_tokens!=null?Number(h.cached_input_tokens):void 0})}if(!u.has_more||!u.next_page)break;a=u.next_page}return{ok:!0,items:i,rawBuckets:s}}async function Vu(){let r=await rt("anthropic_billing"),t=await fetch("https://api.anthropic.com/v1/organizations/workspaces?limit=100",{headers:{"x-api-key":r,"anthropic-version":"2023-06-01"}});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`Anthropic workspaces ${t.status}: ${s.slice(0,200)}`)}let n=await t.json(),i=new Map;for(let s of n.data||[])i.set(s.id,s.name);return i}async function Js({startMs:r,endMs:e}){let t=await rt("cursor_admin"),n=Er(r),i=Er(e),s=`https://api.cursor.com/teams/daily-usage-data?startDate=${n}&endDate=${i}`,o=await fetch(s,{headers:{Authorization:`Bearer ${t}`}});if(!o.ok){let l=await o.text().catch(()=>"");throw new Error(`Cursor daily-usage ${o.status}: ${l.slice(0,200)}`)}let a=await o.json(),c=[],d=0;for(let l of a.data||[]){d+=1;let p=l.date;for(let u of l.userMetrics||[]){for(let m of u.modelUsage||[]){let f=Number(m.acceptedLines??0),h=Number(m.suggestedLines??0);c.push({provider:"cursor",day:p,costUsd:Number(m.totalCents??0)/100,userEmail:u.email,model:m.model,requestCount:Number(m.requestCount??0),acceptanceRate:h>0?f/h:void 0})}(!u.modelUsage||u.modelUsage.length===0)&&c.push({provider:"cursor",day:p,costUsd:Number(u.totalCents??0)/100,userEmail:u.email})}}return{ok:!0,items:c,rawBuckets:d}}async function Qu({startMs:r,endMs:e}){let[t,n,i]=await Promise.allSettled([Cs({startMs:r,endMs:e}),Us({startMs:r,endMs:e}),Js({startMs:r,endMs:e})]),s=l=>l.status==="fulfilled"?l.value:{ok:!1,error:l.reason?.message||String(l.reason),items:[]},o=s(t),a=s(n),c=s(i),d=[{provider:"openai",totalUsd:o.items.reduce((l,p)=>l+(p.costUsd||0),0)},{provider:"anthropic",totalUsd:a.items.reduce((l,p)=>l+(p.costUsd||0),0)},{provider:"cursor",totalUsd:c.items.reduce((l,p)=>l+(p.costUsd||0),0)}];return{openai:o,anthropic:a,cursor:c,totals:d}}function Xu(r,e){let t=new Map;for(let n of r){let i=e(n);if(!i)continue;let s=t.get(i)||{key:i,totalUsd:0,count:0};s.totalUsd+=n.costUsd||0,s.count+=1,t.set(i,s)}return[...t.values()].sort((n,i)=>i.totalUsd-n.totalUsd)}function em(r){if(!r.length)return{mean:0,stddev:0};let e=r.reduce((n,i)=>n+i,0)/r.length,t=r.reduce((n,i)=>n+(i-e)**2,0)/r.length;return{mean:e,stddev:Math.sqrt(t)}}import{SKILL_IDS as zg}from"@zibby/skill-ids";import{z as w}from"zod";var nt=["ok","info","warn","critical"],tm=w.object({primary:w.string().min(1).max(200).describe('Headline number or phrase (e.g. "$8,240"). Rendered in large/bold.'),delta:w.object({value:w.string().max(40).describe('Delta vs baseline (e.g. "+12% wow"). Free-form string.'),direction:w.enum(["up","down","flat"]).optional(),severity:w.enum(nt).optional().describe("Color severity for the delta (warn/critical highlights regressions).")}).optional().describe("Optional comparison vs baseline. Renders inline next to primary."),summary:w.string().max(800).optional().describe('One-sentence narrative ("why this number"). Plain prose.')}),rm=w.object({kind:w.literal("trend"),title:w.string().max(120).optional(),labels:w.array(w.string().max(60)).min(2).max(20).describe('Bucket labels (e.g. ["Week-3", "Week-2", "Week-1", "This wk"]).'),values:w.array(w.number()).min(2).max(20).describe("Numeric values, one per label. Must match labels.length."),highlight:w.enum(["last","max","min","none"]).default("last").optional().describe("Which bucket to visually highlight in the rendered card."),severity:w.enum(nt).optional()}),nm=w.object({kind:w.literal("table"),title:w.string().max(120).optional(),headers:w.array(w.string().max(40)).min(1).max(8),rows:w.array(w.array(w.union([w.string().max(200),w.number()])).min(1).max(8)).max(40).describe("2D matrix. Each inner array must have headers.length entries.")}),im=w.object({kind:w.literal("callouts"),title:w.string().max(120).optional(),tone:w.enum(nt).default("info").optional(),items:w.array(w.string().min(1).max(600)).min(1).max(10).describe("Each item renders as a bullet with a severity emoji.")}),sm=w.object({kind:w.literal("breakdown"),title:w.string().max(120).optional(),rows:w.array(w.object({label:w.string().min(1).max(80),value:w.string().min(1).max(80),sub:w.string().max(120).optional(),severity:w.enum(nt).optional()})).min(1).max(20)}),om=w.object({kind:w.literal("paragraph"),title:w.string().max(120).optional(),text:w.string().min(1).max(3e3)}),am=w.discriminatedUnion("kind",[rm,nm,im,sm,om]),it=w.object({title:w.string().min(1).max(200).describe('Card title (e.g. "Weekly AI Spend Report").'),subtitle:w.string().max(200).optional().describe('Date range or smaller header (e.g. "May 13 \u2014 May 20").'),headline:tm,sections:w.array(am).max(20).default([]).superRefine((r,e)=>{r.forEach((t,n)=>{t.kind==="trend"&&t.labels.length!==t.values.length&&e.addIssue({code:w.ZodIssueCode.custom,path:[n,"values"],message:"labels.length must equal values.length"}),t.kind==="table"&&!t.rows.every(i=>i.length===t.headers.length)&&e.addIssue({code:w.ZodIssueCode.custom,path:[n,"rows"],message:"every row must have headers.length entries"})})}),footer:w.object({viewUrl:w.string().url().optional().describe('Optional "View in Zibby" button URL.'),rerunUrl:w.string().url().optional().describe('Optional "Run again" button URL.')}).optional()}),U=Object.freeze({ok:"\u{1F7E2}",info:"\u{1F535}",warn:"\u{1F7E0}",critical:"\u{1F534}"}),Ct=Object.freeze({up:"\u2191",down:"\u2193",flat:"\u2192"}),cm=Object.freeze({ok:"green",info:"blue",warn:"orange",critical:"red"});function Ut(r,e,t=12){if(!Number.isFinite(r)||!Number.isFinite(e)||e<=0)return"";let n=Math.max(0,Math.min(1,r/e)),i=Math.round(n*t);return"\u2593".repeat(i)+"\u2591".repeat(t-i)}function st(r,e){let t=String(r);return t.length>=e?t:t+" ".repeat(e-t.length)}function Jt(r,e){let t=String(r);return t.length>=e?t:" ".repeat(e-t.length)+t}function Bs({headers:r,rows:e}){let t=r.map((o,a)=>{let c=Math.max(String(o).length,...e.map(d=>String(d[a]??"").length));return Math.min(c,32)}),n=o=>o.map((a,c)=>st(a,t[c])).join(" "),i=t.map(o=>"\u2500".repeat(o)).join(" ");return"```\n"+[n(r),i,...e.map(o=>n(o))].join(`
1078
- `)+"\n```"}function lm(r){let e=it.parse(r),t=[];t.push({type:"header",text:{type:"plain_text",text:e.title.slice(0,150),emoji:!0}}),e.subtitle&&t.push({type:"context",elements:[{type:"mrkdwn",text:e.subtitle}]});let n=[`*${e.headline.primary}*`];if(e.headline.delta){let s=Ct[e.headline.delta.direction]||"",o=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${s} ${e.headline.delta.value} ${o}`.trim())}let i=n.join(" ");e.headline.summary&&(i+=`
1076
+ `,"utf-8");let g=["graph.mjs","workflow.json","nodes/index.mjs",...c.map(_=>`nodes/${_.replace(/_/g,"-")}.mjs`)];return{workflowDir:Os(r,o),files:g,className:s,slug:i}}async function Ku(r){let{name:e,description:t,nodes:n,edges:i}=r;if(!e||!Rs.test(e.toLowerCase()))return JSON.stringify({error:`Invalid workflow name "${e}". Must be kebab-case, 2-64 chars, lowercase letters/numbers/hyphens.`});if(!n||n.length===0)return JSON.stringify({error:"At least one node is required."});let s={name:e.toLowerCase(),description:t||`${As(e.toLowerCase())} workflow`,nodes:n.map(o=>({name:o.name.replace(/-/g,"_"),description:o.description||`Process ${o.name}`,inputFields:o.inputFields||[],outputFields:o.outputFields||[]})),edges:i||[]};if(s.edges.length===0&&s.nodes.length>0){for(let o=0;o<s.nodes.length-1;o++)s.edges.push({from:s.nodes[o].name,to:s.nodes[o+1].name});s.edges.push({from:s.nodes[s.nodes.length-1].name,to:"END"})}return JSON.stringify({ok:!0,spec:s,message:`Workflow "${s.name}" designed with ${s.nodes.length} node(s). Call build_workflow to generate the code.`,preview:{nodes:s.nodes.map(o=>o.name),flow:s.edges.map(o=>o.condition?`${o.from} \u2192(if ${o.condition})\u2192 ${o.to}`:`${o.from} \u2192 ${o.to}`)}})}async function Hu(r,e){let{name:t,spec:n}=r,i=(t||n?.name||"").toLowerCase();if(!i||!Rs.test(i))return JSON.stringify({error:`Invalid workflow name "${i}".`});if(!n||!n.nodes||n.nodes.length===0)return JSON.stringify({error:"spec with nodes is required. Call design_workflow first."});let s=A(e,".zibby","workflows",i);if(le(s))return JSON.stringify({error:`Workflow "${i}" already exists at .zibby/workflows/${i}/. Delete it first or choose a different name.`});let o=await js(n,e),a=Gu(e,i,n,o);return JSON.stringify({ok:!0,...a,message:`Workflow "${i}" created at ${a.workflowDir}/`,nextSteps:[`Test locally: zibby start ${i}`,`Deploy to cloud: zibby deploy ${i} --project <project-id>`,`Tail logs: zibby logs --workflow ${i} --project <project-id>`]})}async function zu(r,e){let{workflowName:t,nodeName:n,description:i,inputFields:s,outputFields:o}=r,a=(t||"").toLowerCase(),c=(n||"").replace(/-/g,"_"),d=A(e,".zibby","workflows",a);if(!le(d))return JSON.stringify({error:`Workflow "${a}" not found. Create it first with build_workflow.`});let l={name:a,description:"",nodes:[{name:c,description:i||`Process ${c}`,inputFields:s||[],outputFields:o||[]}],edges:[]},u=(await js(l,e)).nodes?.[c]?.code;if(!u)return JSON.stringify({error:"Failed to generate node code."});let m=A(d,"nodes"),f=`${c.replace(/_/g,"-")}.mjs`;Be(A(m,f),u,"utf-8");let h=A(m,"index.mjs"),g=et(c),_=`export { ${g} } from './${c.replace(/_/g,"-")}.mjs';
1077
+ `,b=le(h)?tt(h,"utf-8"):"";return b.includes(g)||Be(h,b+_,"utf-8"),JSON.stringify({ok:!0,file:`nodes/${f}`,exportName:g,message:`Node "${c}" added. Update graph.mjs to wire it into the graph.`})}async function Yu(r,e){let{name:t,projectId:n}=r,i=(t||"").toLowerCase();if(!i)return JSON.stringify({error:"Workflow name is required."});if(!n)return JSON.stringify({error:"projectId is required."});let s=A(e,".zibby","workflows",i);if(!le(s))return JSON.stringify({error:`Workflow "${i}" not found at .zibby/workflows/${i}/`});try{let{execSync:o}=await import("child_process"),a=o(`node "${A(e,"packages/cli/bin/zibby.js")}" deploy ${i} --project ${n}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:a.trim()})}catch{try{let{execSync:a}=await import("child_process"),c=a(`npx zibby deploy ${i} --project ${n}`,{cwd:e,encoding:"utf-8",timeout:3e4,stdio:["pipe","pipe","pipe"]});return JSON.stringify({ok:!0,output:c.trim()})}catch(a){return JSON.stringify({error:`Deploy failed: ${a.message}`})}}}function Wu(r){let e=A(r,".zibby","workflows");if(!le(e))return JSON.stringify({workflows:[],message:"No workflows found. Use build_workflow to create one."});let n=Ar(e).filter(i=>{try{return Ns(A(e,i)).isDirectory()}catch{return!1}}).map(i=>{let s=A(e,i,"workflow.json"),o={};try{o=JSON.parse(tt(s,"utf-8"))}catch{}let a=A(e,i,"nodes"),c=0;try{c=Ar(a).filter(d=>d.endsWith(".mjs")&&d!=="index.mjs").length}catch{}return{name:i,description:o.description||"",nodeCount:c,path:Os(r,A(e,i))}});return JSON.stringify({workflows:n})}var Ls={id:"workflow-builder",description:"Build, scaffold, and deploy custom AI workflows via conversation",envKeys:[],promptFragment:Ju,tools:[{name:"design_workflow",description:"Design a workflow spec (nodes, edges, descriptions) for the user to review before building. Call this after understanding requirements.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name in kebab-case (e.g., ticket-triage)"},description:{type:"string",description:"What the workflow does"},nodes:{type:"array",items:{type:"object",properties:{name:{type:"string",description:"Node name in snake_case (e.g., classify_ticket)"},description:{type:"string",description:"What this node does \u2014 be specific about input/output"},inputFields:{type:"array",items:{type:"string"},description:"Key fields this node reads from state"},outputFields:{type:"array",items:{type:"string"},description:"Key fields this node produces"}},required:["name","description"]},description:"Workflow nodes (processing steps)"},edges:{type:"array",items:{type:"object",properties:{from:{type:"string",description:"Source node name"},to:{type:"string",description:'Target node name (or "END")'},condition:{type:"string",description:"JS expression for conditional routing (optional)"}},required:["from","to"]},description:"Edges connecting nodes. If omitted, nodes are wired linearly."}},required:["name","description","nodes"]}},{name:"build_workflow",description:"Generate real workflow code on disk from a design spec. Uses the configured AI agent for high-quality code generation.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name (from design_workflow)"},spec:{type:"object",description:"The full spec object returned by design_workflow",properties:{name:{type:"string"},description:{type:"string"},nodes:{type:"array",items:{type:"object"}},edges:{type:"array",items:{type:"object"}}}}},required:["name","spec"]}},{name:"add_node",description:"Add a new node to an existing workflow. Generates the node file and updates the barrel export.",input_schema:{type:"object",properties:{workflowName:{type:"string",description:"Existing workflow name (kebab-case)"},nodeName:{type:"string",description:"New node name (snake_case)"},description:{type:"string",description:"What this node does"},inputFields:{type:"array",items:{type:"string"},description:"Fields read from state"},outputFields:{type:"array",items:{type:"string"},description:"Fields produced"}},required:["workflowName","nodeName","description"]}},{name:"deploy_workflow",description:"Deploy a workflow to Zibby Cloud. Returns the trigger URL.",input_schema:{type:"object",properties:{name:{type:"string",description:"Workflow name to deploy"},projectId:{type:"string",description:"Target project ID"}},required:["name","projectId"]}},{name:"list_workflows",description:"List all local workflows in .zibby/workflows/.",input_schema:{type:"object",properties:{}}},{name:"explore_framework_docs",description:"Read Zibby framework documentation on demand. Call this before building complex workflows or when you need details on advanced patterns (middleware, conditional routing, skills, deployment, CLI commands).",input_schema:{type:"object",properties:{topic:{type:"string",description:'Doc topic to read (e.g., "workflow", "custom-workflows", "cli-reference", "packages/core", "packages/skills", "integrations/jira"). Call with no topic to list all available docs.'}}}}],async handleToolCall(r,e,t){let n=t?.options?.workspace||process.cwd();try{switch(r){case"design_workflow":return await Ku(e);case"build_workflow":return await Hu(e,n);case"add_node":return await zu(e,n);case"deploy_workflow":return await Yu(e,n);case"list_workflows":return Wu(n);case"explore_framework_docs":{let i=(e.topic||"").trim();if(!i){let o=Is();return JSON.stringify({available:o,hint:"Call again with a topic to read its content."})}let s=Es(i);if(!s){let o=Is();return JSON.stringify({error:`Doc "${i}" not found.`,available:o})}return JSON.stringify({topic:i,content:s})}default:return JSON.stringify({error:`Unknown tool: ${r}`})}}catch(i){return JSON.stringify({error:i.message})}},resolve(){return null}};import{resolveIntegrationToken as Zu}from"@zibby/core/backend-client.js";var jr=Object.freeze({id:"openai_billing",requiresIntegration:I.OPENAI_BILLING,description:"OpenAI organization billing/usage admin API (paste sk-admin-... key)"}),Lr=Object.freeze({id:"anthropic_billing",requiresIntegration:I.ANTHROPIC_BILLING,description:"Anthropic organization cost/usage admin API (paste sk-ant-admin-... key)"}),$r=Object.freeze({id:"cursor_admin",requiresIntegration:I.CURSOR_ADMIN,description:"Cursor Team/Enterprise admin API (paste admin key)"});function $s(r){return Math.floor(r/1e3)}function Er(r){return new Date(r).toISOString().slice(0,10)}function Ps(r){return new Date(r).toISOString()}async function rt(r){let e=await Zu(r);if(!e?.token)throw new Error(`${r} token resolver returned no token`);return e.token}async function Cs({startMs:r,endMs:e,groupBy:t=["project_id","line_item"]}){let n=await rt("openai_billing"),i=[],s=0,o=t.map(c=>`group_by[]=${encodeURIComponent(c)}`).join("&"),a=null;for(let c=0;c<50;c++){let l=`https://api.openai.com/v1/organization/costs?${[`start_time=${$s(r)}`,`end_time=${$s(e)}`,"bucket_width=1d","limit=180",o,a?`page=${encodeURIComponent(a)}`:""].filter(Boolean).join("&")}`,p=await fetch(l,{headers:{Authorization:`Bearer ${n}`}});if(!p.ok){let m=await p.text().catch(()=>"");throw new Error(`OpenAI costs API ${p.status}: ${m.slice(0,200)}`)}let u=await p.json();for(let m of u.data||[]){s+=1;let f=Er((m.start_time||0)*1e3);for(let h of m.results||[])i.push({provider:"openai",day:f,costUsd:Number(h.amount?.value??0),projectId:h.project_id||void 0,apiKeyId:h.api_key_id||void 0,model:h.line_item||void 0})}if(!u.has_more||!u.next_page)break;a=u.next_page}return{ok:!0,items:i,rawBuckets:s}}async function Vu(){let r=await rt("openai_billing"),t=await fetch("https://api.openai.com/v1/organization/projects?limit=100",{headers:{Authorization:`Bearer ${r}`}});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`OpenAI projects API ${t.status}: ${s.slice(0,200)}`)}let n=await t.json(),i=new Map;for(let s of n.data||[])i.set(s.id,s.name);return i}async function Us({startMs:r,endMs:e,groupBy:t=["workspace_id"]}){let n=await rt("anthropic_billing"),i=[],s=0,o=t.map(c=>`group_by[]=${encodeURIComponent(c)}`).join("&"),a=null;for(let c=0;c<50;c++){let l=`https://api.anthropic.com/v1/organizations/cost_report?${[`starting_at=${encodeURIComponent(Ps(r))}`,`ending_at=${encodeURIComponent(Ps(e))}`,"bucket=1d","limit=100",o,a?`page=${encodeURIComponent(a)}`:""].filter(Boolean).join("&")}`,p=await fetch(l,{headers:{"x-api-key":n,"anthropic-version":"2023-06-01"}});if(!p.ok){let m=await p.text().catch(()=>"");throw new Error(`Anthropic cost_report ${p.status}: ${m.slice(0,200)}`)}let u=await p.json();for(let m of u.data||[]){s+=1;let f=(m.starting_at||"").slice(0,10);for(let h of m.results||[])i.push({provider:"anthropic",day:f,costUsd:Number(h.amount??h.cost??0),workspaceId:h.workspace_id||void 0,apiKeyId:h.api_key_id||void 0,model:h.model||void 0,tokensIn:h.uncached_input_tokens!=null?Number(h.uncached_input_tokens):void 0,tokensOut:h.output_tokens!=null?Number(h.output_tokens):void 0,cachedTokens:h.cached_input_tokens!=null?Number(h.cached_input_tokens):void 0})}if(!u.has_more||!u.next_page)break;a=u.next_page}return{ok:!0,items:i,rawBuckets:s}}async function Qu(){let r=await rt("anthropic_billing"),t=await fetch("https://api.anthropic.com/v1/organizations/workspaces?limit=100",{headers:{"x-api-key":r,"anthropic-version":"2023-06-01"}});if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`Anthropic workspaces ${t.status}: ${s.slice(0,200)}`)}let n=await t.json(),i=new Map;for(let s of n.data||[])i.set(s.id,s.name);return i}async function Js({startMs:r,endMs:e}){let t=await rt("cursor_admin"),n=Er(r),i=Er(e),s=`https://api.cursor.com/teams/daily-usage-data?startDate=${n}&endDate=${i}`,o=await fetch(s,{headers:{Authorization:`Bearer ${t}`}});if(!o.ok){let l=await o.text().catch(()=>"");throw new Error(`Cursor daily-usage ${o.status}: ${l.slice(0,200)}`)}let a=await o.json(),c=[],d=0;for(let l of a.data||[]){d+=1;let p=l.date;for(let u of l.userMetrics||[]){for(let m of u.modelUsage||[]){let f=Number(m.acceptedLines??0),h=Number(m.suggestedLines??0);c.push({provider:"cursor",day:p,costUsd:Number(m.totalCents??0)/100,userEmail:u.email,model:m.model,requestCount:Number(m.requestCount??0),acceptanceRate:h>0?f/h:void 0})}(!u.modelUsage||u.modelUsage.length===0)&&c.push({provider:"cursor",day:p,costUsd:Number(u.totalCents??0)/100,userEmail:u.email})}}return{ok:!0,items:c,rawBuckets:d}}async function Xu({startMs:r,endMs:e}){let[t,n,i]=await Promise.allSettled([Cs({startMs:r,endMs:e}),Us({startMs:r,endMs:e}),Js({startMs:r,endMs:e})]),s=l=>l.status==="fulfilled"?l.value:{ok:!1,error:l.reason?.message||String(l.reason),items:[]},o=s(t),a=s(n),c=s(i),d=[{provider:"openai",totalUsd:o.items.reduce((l,p)=>l+(p.costUsd||0),0)},{provider:"anthropic",totalUsd:a.items.reduce((l,p)=>l+(p.costUsd||0),0)},{provider:"cursor",totalUsd:c.items.reduce((l,p)=>l+(p.costUsd||0),0)}];return{openai:o,anthropic:a,cursor:c,totals:d}}function em(r,e){let t=new Map;for(let n of r){let i=e(n);if(!i)continue;let s=t.get(i)||{key:i,totalUsd:0,count:0};s.totalUsd+=n.costUsd||0,s.count+=1,t.set(i,s)}return[...t.values()].sort((n,i)=>i.totalUsd-n.totalUsd)}function tm(r){if(!r.length)return{mean:0,stddev:0};let e=r.reduce((n,i)=>n+i,0)/r.length,t=r.reduce((n,i)=>n+(i-e)**2,0)/r.length;return{mean:e,stddev:Math.sqrt(t)}}import{SKILL_IDS as Wg}from"@zibby/skill-ids";import{z as w}from"zod";var nt=["ok","info","warn","critical"],rm=w.object({primary:w.string().min(1).max(200).describe('Headline number or phrase (e.g. "$8,240"). Rendered in large/bold.'),delta:w.object({value:w.string().max(40).describe('Delta vs baseline (e.g. "+12% wow"). Free-form string.'),direction:w.enum(["up","down","flat"]).optional(),severity:w.enum(nt).optional().describe("Color severity for the delta (warn/critical highlights regressions).")}).optional().describe("Optional comparison vs baseline. Renders inline next to primary."),summary:w.string().max(800).optional().describe('One-sentence narrative ("why this number"). Plain prose.')}),nm=w.object({kind:w.literal("trend"),title:w.string().max(120).optional(),labels:w.array(w.string().max(60)).min(2).max(20).describe('Bucket labels (e.g. ["Week-3", "Week-2", "Week-1", "This wk"]).'),values:w.array(w.number()).min(2).max(20).describe("Numeric values, one per label. Must match labels.length."),highlight:w.enum(["last","max","min","none"]).default("last").optional().describe("Which bucket to visually highlight in the rendered card."),severity:w.enum(nt).optional()}),im=w.object({kind:w.literal("table"),title:w.string().max(120).optional(),headers:w.array(w.string().max(40)).min(1).max(8),rows:w.array(w.array(w.union([w.string().max(200),w.number()])).min(1).max(8)).max(40).describe("2D matrix. Each inner array must have headers.length entries.")}),sm=w.object({kind:w.literal("callouts"),title:w.string().max(120).optional(),tone:w.enum(nt).default("info").optional(),items:w.array(w.string().min(1).max(600)).min(1).max(10).describe("Each item renders as a bullet with a severity emoji.")}),om=w.object({kind:w.literal("breakdown"),title:w.string().max(120).optional(),rows:w.array(w.object({label:w.string().min(1).max(80),value:w.string().min(1).max(80),sub:w.string().max(120).optional(),severity:w.enum(nt).optional()})).min(1).max(20)}),am=w.object({kind:w.literal("paragraph"),title:w.string().max(120).optional(),text:w.string().min(1).max(3e3)}),cm=w.discriminatedUnion("kind",[nm,im,sm,om,am]),it=w.object({title:w.string().min(1).max(200).describe('Card title (e.g. "Weekly AI Spend Report").'),subtitle:w.string().max(200).optional().describe('Date range or smaller header (e.g. "May 13 \u2014 May 20").'),headline:rm,sections:w.array(cm).max(20).default([]).superRefine((r,e)=>{r.forEach((t,n)=>{t.kind==="trend"&&t.labels.length!==t.values.length&&e.addIssue({code:w.ZodIssueCode.custom,path:[n,"values"],message:"labels.length must equal values.length"}),t.kind==="table"&&!t.rows.every(i=>i.length===t.headers.length)&&e.addIssue({code:w.ZodIssueCode.custom,path:[n,"rows"],message:"every row must have headers.length entries"})})}),footer:w.object({viewUrl:w.string().url().optional().describe('Optional "View in Zibby" button URL.'),rerunUrl:w.string().url().optional().describe('Optional "Run again" button URL.')}).optional()}),U=Object.freeze({ok:"\u{1F7E2}",info:"\u{1F535}",warn:"\u{1F7E0}",critical:"\u{1F534}"}),Ct=Object.freeze({up:"\u2191",down:"\u2193",flat:"\u2192"}),lm=Object.freeze({ok:"green",info:"blue",warn:"orange",critical:"red"});function Ut(r,e,t=12){if(!Number.isFinite(r)||!Number.isFinite(e)||e<=0)return"";let n=Math.max(0,Math.min(1,r/e)),i=Math.round(n*t);return"\u2593".repeat(i)+"\u2591".repeat(t-i)}function st(r,e){let t=String(r);return t.length>=e?t:t+" ".repeat(e-t.length)}function Jt(r,e){let t=String(r);return t.length>=e?t:" ".repeat(e-t.length)+t}function Bs({headers:r,rows:e}){let t=r.map((o,a)=>{let c=Math.max(String(o).length,...e.map(d=>String(d[a]??"").length));return Math.min(c,32)}),n=o=>o.map((a,c)=>st(a,t[c])).join(" "),i=t.map(o=>"\u2500".repeat(o)).join(" ");return"```\n"+[n(r),i,...e.map(o=>n(o))].join(`
1078
+ `)+"\n```"}function dm(r){let e=it.parse(r),t=[];t.push({type:"header",text:{type:"plain_text",text:e.title.slice(0,150),emoji:!0}}),e.subtitle&&t.push({type:"context",elements:[{type:"mrkdwn",text:e.subtitle}]});let n=[`*${e.headline.primary}*`];if(e.headline.delta){let s=Ct[e.headline.delta.direction]||"",o=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${s} ${e.headline.delta.value} ${o}`.trim())}let i=n.join(" ");e.headline.summary&&(i+=`
1079
1079
  `+e.headline.summary),t.push({type:"section",text:{type:"mrkdwn",text:i}});for(let s of e.sections)switch(t.push({type:"divider"}),s.title&&t.push({type:"section",text:{type:"mrkdwn",text:`*${s.title}*`}}),s.kind){case"trend":{let o=Math.max(...s.values),a=s.labels.map((c,d)=>{let l=s.values[d],p=Ut(l,o),m=(s.highlight==="last"&&d===s.labels.length-1||s.highlight==="max"&&l===o||s.highlight==="min"&&l===Math.min(...s.values))&&s.severity?` ${U[s.severity]}`:"";return`${st(c,10)} ${Jt(l.toLocaleString(),8)} ${p}${m}`});t.push({type:"section",text:{type:"mrkdwn",text:"```\n"+a.join(`
1080
1080
  `)+"\n```"}});break}case"table":{t.push({type:"section",text:{type:"mrkdwn",text:Bs(s)}});break}case"callouts":{let o=U[s.tone||"info"];t.push({type:"section",text:{type:"mrkdwn",text:s.items.map(a=>`${o} ${a}`).join(`
1081
1081
  `)}});break}case"breakdown":{let o=s.rows.map(a=>({type:"mrkdwn",text:`*${a.label}*
1082
1082
  ${a.value}${a.sub?`
1083
- _${a.sub}_`:""}${a.severity?` ${U[a.severity]}`:""}`}));for(let a=0;a<o.length;a+=10)t.push({type:"section",fields:o.slice(a,a+10)});break}case"paragraph":{t.push({type:"section",text:{type:"mrkdwn",text:s.text}});break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let s=[];e.footer.viewUrl&&s.push({type:"button",text:{type:"plain_text",text:"View in Zibby"},url:e.footer.viewUrl,style:"primary"}),e.footer.rerunUrl&&s.push({type:"button",text:{type:"plain_text",text:"Run again"},url:e.footer.rerunUrl}),t.push({type:"divider"}),t.push({type:"actions",elements:s})}return t}function dm(r){let e=it.parse(r),t=[],n=[`**${e.headline.primary}**`];if(e.headline.delta){let o=Ct[e.headline.delta.direction]||"",a=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${o} ${e.headline.delta.value} ${a}`.trim())}let i=n.join(" ");e.headline.summary&&(i+=`
1083
+ _${a.sub}_`:""}${a.severity?` ${U[a.severity]}`:""}`}));for(let a=0;a<o.length;a+=10)t.push({type:"section",fields:o.slice(a,a+10)});break}case"paragraph":{t.push({type:"section",text:{type:"mrkdwn",text:s.text}});break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let s=[];e.footer.viewUrl&&s.push({type:"button",text:{type:"plain_text",text:"View in Zibby"},url:e.footer.viewUrl,style:"primary"}),e.footer.rerunUrl&&s.push({type:"button",text:{type:"plain_text",text:"Run again"},url:e.footer.rerunUrl}),t.push({type:"divider"}),t.push({type:"actions",elements:s})}return t}function pm(r){let e=it.parse(r),t=[],n=[`**${e.headline.primary}**`];if(e.headline.delta){let o=Ct[e.headline.delta.direction]||"",a=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${o} ${e.headline.delta.value} ${a}`.trim())}let i=n.join(" ");e.headline.summary&&(i+=`
1084
1084
  `+e.headline.summary),t.push({tag:"div",text:{tag:"lark_md",content:i}});for(let o of e.sections)switch(t.push({tag:"hr"}),o.title&&t.push({tag:"div",text:{tag:"lark_md",content:`**${o.title}**`}}),o.kind){case"trend":{let a=Math.max(...o.values),c=o.labels.map((d,l)=>{let p=o.values[l],u=Ut(p,a),f=(o.highlight==="last"&&l===o.labels.length-1||o.highlight==="max"&&p===a||o.highlight==="min"&&p===Math.min(...o.values))&&o.severity?` ${U[o.severity]}`:"";return`${st(d,10)} ${Jt(p.toLocaleString(),8)} ${u}${f}`});t.push({tag:"div",text:{tag:"lark_md",content:"```\n"+c.join(`
1085
1085
  `)+"\n```"}});break}case"table":{t.push({tag:"div",text:{tag:"lark_md",content:Bs(o)}});break}case"callouts":{let a=U[o.tone||"info"];t.push({tag:"div",text:{tag:"lark_md",content:o.items.map(c=>`${a} ${c}`).join(`
1086
1086
  `)}});break}case"breakdown":{let a=o.rows.map(c=>{let d=c.severity?` ${U[c.severity]}`:"",l=c.sub?` *${c.sub}*`:"";return`**${c.label}** ${c.value}${l}${d}`});t.push({tag:"div",text:{tag:"lark_md",content:a.join(`
1087
- `)}});break}case"paragraph":{t.push({tag:"div",text:{tag:"lark_md",content:o.text}});break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let o=[];e.footer.viewUrl&&o.push({tag:"button",text:{tag:"plain_text",content:"View in Zibby"},url:e.footer.viewUrl,type:"primary"}),e.footer.rerunUrl&&o.push({tag:"button",text:{tag:"plain_text",content:"Run again"},url:e.footer.rerunUrl,type:"default"}),t.push({tag:"hr"}),t.push({tag:"action",actions:o})}let s="blue";return e.headline.delta?.severity&&(s=cm[e.headline.delta.severity]||"blue"),{config:{wide_screen_mode:!0},header:{title:{tag:"plain_text",content:e.title.slice(0,200)},subtitle:e.subtitle?{tag:"plain_text",content:e.subtitle.slice(0,200)}:void 0,template:s},elements:t}}var pm=Object.freeze({ok:"green_background",info:"blue_background",warn:"orange_background",critical:"red_background"}),qs=Object.freeze({ok:"\u{1F7E2}",info:"\u2139\uFE0F",warn:"\u26A0\uFE0F",critical:"\u{1F6A8}"});function Te(r,e={}){let t={type:"text",text:{content:String(r).slice(0,2e3)}};return e.annotations&&(t.annotations=e.annotations),[t]}function Pr(r,e){let t={object:"block",type:"paragraph",paragraph:{rich_text:Te(r)}};return e&&(t.paragraph.color=e),t}function um(r){return{object:"block",type:"code",code:{rich_text:Te(r),language:"plain text"}}}function mm(r){let e=it.parse(r),t=[];t.push({object:"block",type:"heading_1",heading_1:{rich_text:Te(e.title.slice(0,200))}}),e.subtitle&&t.push(Pr(e.subtitle,"gray_background"));let n=[{type:"text",text:{content:e.headline.primary.slice(0,200)},annotations:{bold:!0}}];if(e.headline.delta){let s=Ct[e.headline.delta.direction]||"",o=e.headline.delta.severity?U[e.headline.delta.severity]:"",a=` ${s} ${e.headline.delta.value} ${o}`.trimEnd();n.push({type:"text",text:{content:a}})}t.push({object:"block",type:"paragraph",paragraph:{rich_text:n}}),e.headline.summary&&t.push(Pr(e.headline.summary));for(let s of e.sections)switch(t.push({object:"block",type:"divider",divider:{}}),s.title&&t.push({object:"block",type:"heading_2",heading_2:{rich_text:Te(s.title)}}),s.kind){case"trend":{let o=Math.max(...s.values),a=Math.min(...s.values),c=s.labels.map((d,l)=>{let p=s.values[l],u=Ut(p,o),f=(s.highlight==="last"&&l===s.labels.length-1||s.highlight==="max"&&p===o||s.highlight==="min"&&p===a)&&s.severity?` ${U[s.severity]}`:"";return`${st(d,10)} ${Jt(p.toLocaleString(),8)} ${u}${f}`});t.push(um(c.join(`
1088
- `)));break}case"table":{let o={object:"block",type:"table_row",table_row:{cells:s.headers.map(c=>Te(c))}},a=s.rows.map(c=>({object:"block",type:"table_row",table_row:{cells:c.map(d=>Te(String(d)))}}));t.push({object:"block",type:"table",table:{table_width:s.headers.length,has_column_header:!0,has_row_header:!1,children:[o,...a]}});break}case"callouts":{let o=s.tone||"info",a=qs[o],c=pm[o];for(let d of s.items)t.push({object:"block",type:"callout",callout:{rich_text:Te(d),icon:{type:"emoji",emoji:a},color:c}});break}case"breakdown":{for(let o of s.rows){let a=[{type:"text",text:{content:`${o.label}: `},annotations:{bold:!0}},{type:"text",text:{content:String(o.value)}}];o.sub&&a.push({type:"text",text:{content:` (${o.sub})`},annotations:{italic:!0}}),o.severity&&a.push({type:"text",text:{content:` ${U[o.severity]}`}}),t.push({object:"block",type:"bulleted_list_item",bulleted_list_item:{rich_text:a}})}break}case"paragraph":{t.push(Pr(s.text));break}}e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)&&(t.push({object:"block",type:"divider",divider:{}}),e.footer.viewUrl&&t.push({object:"block",type:"embed",embed:{url:e.footer.viewUrl}}),e.footer.rerunUrl&&t.push({object:"block",type:"embed",embed:{url:e.footer.rerunUrl}}));let i=e.headline.delta?.severity?qs[e.headline.delta.severity]:void 0;return{blocks:t,title:e.title.slice(0,200),icon:i}}function Ds(r){return String(r??"").replace(/\|/g,"\\|")}function fm(r){let e=it.parse(r),t=[];t.push(`# ${e.title}`),e.subtitle&&t.push("",e.subtitle);let n=[`**${e.headline.primary}**`];if(e.headline.delta){let i=Ct[e.headline.delta.direction]||"",s=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${i} ${e.headline.delta.value} ${s}`.trim())}t.push("",n.join(" ")),e.headline.summary&&t.push("",e.headline.summary);for(let i of e.sections)switch(t.push("","---",""),i.title&&t.push(`## ${i.title}`,""),i.kind){case"trend":{let s=Math.max(...i.values),o=Math.min(...i.values),a=i.labels.map((c,d)=>{let l=i.values[d],p=Ut(l,s),m=(i.highlight==="last"&&d===i.labels.length-1||i.highlight==="max"&&l===s||i.highlight==="min"&&l===o)&&i.severity?` ${U[i.severity]}`:"";return`${st(c,10)} ${Jt(l.toLocaleString(),8)} ${p}${m}`});t.push("```",...a,"```");break}case"table":{t.push(`| ${i.headers.map(Ds).join(" | ")} |`),t.push(`| ${i.headers.map(()=>"---").join(" | ")} |`);for(let s of i.rows)t.push(`| ${s.map(Ds).join(" | ")} |`);break}case"callouts":{let s=U[i.tone||"info"];for(let o of i.items)t.push(`- ${s} ${o}`);break}case"breakdown":{for(let s of i.rows){let o=s.sub?` _${s.sub}_`:"",a=s.severity?` ${U[s.severity]}`:"";t.push(`- **${s.label}**: ${s.value}${o}${a}`)}break}case"paragraph":{t.push(i.text);break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let i=[];e.footer.viewUrl&&i.push(`[View in Zibby](${e.footer.viewUrl})`),e.footer.rerunUrl&&i.push(`[Run again](${e.footer.rerunUrl})`),t.push("","---","",i.join(" \xB7 "))}return t.join(`
1089
- `)}import{createRequire as hm}from"module";import{fileURLToPath as ym}from"url";import{registerHandlers as gm}from"@zibby/core/function-skill-registry.js";import{registerSkill as _m}from"@zibby/agent-workflow";var bm=hm(import.meta.url);function km(){try{return bm.resolve("@zibby/core/function-bridge.js")}catch{return null}}var wm=import.meta.url;function Sm(){let r=Error.prepareStackTrace;try{Error.prepareStackTrace=(n,i)=>i;let t=new Error().stack;for(let n=2;n<t.length;n++){let i=t[n].getFileName();if(i&&i!==wm&&!i.startsWith("node:"))return i.startsWith("file://")?ym(i):i}return null}finally{Error.prepareStackTrace=r}}function vm(r){if(!r||typeof r!="object")return{type:"object",properties:{},required:[]};let e={},t=[];for(let[n,i]of Object.entries(r))if(typeof i=="string")e[n]={type:i},t.push(n);else{let{required:s,...o}=i;e[n]=o,s!==!1&&t.push(n)}return{type:"object",properties:e,required:t}}function Im(r,e,t){if(typeof t.handler!="function")throw new Error(`Skill "${r}" must have a handler function`);let n={[r]:t.handler},i=[{name:r,description:t.description||"",input_schema:vm(t.input)}];return gm(r,n,i),{id:r,type:"function",serverName:r,allowedTools:[`mcp__${r}__*`],description:t.description||`Function skill: ${r}`,envKeys:[],tools:i,resolve(){let s=km();return s?{command:"node",args:[s,e,r]}:null}}}function Nm(r,e){return{id:r,type:"mcp",serverName:e.serverName||r,allowedTools:e.allowedTools||[`mcp__${e.serverName||r}__*`],description:e.description||`MCP skill: ${r}`,envKeys:e.envKeys||[],tools:e.tools||[],resolve:e.resolve,...e.cursorKey&&{cursorKey:e.cursorKey},...e.sessionEnvKey&&{sessionEnvKey:e.sessionEnvKey}}}function Ms(r,e){let t;if("handler"in e){if(typeof e.handler!="function")throw new Error(`Skill "${r}" must have a handler function`);let n=Sm();if(!n)throw new Error(`Could not resolve caller file for skill "${r}".`);t=Im(r,n,e)}else if(typeof e.resolve=="function")t=Nm(r,e);else throw new Error(`Skill "${r}" must have either a handler (function skill) or resolve (MCP skill).`);return _m(t),t}var Om=Ms;import{registerSkill as Qg,getSkill as Xg,hasSkill as e_,getAllSkills as t_,listSkillIds as r_}from"@zibby/agent-workflow";v(Jr);v(Br);v(Ee);v(je);v(zr);v(Wr);v(Vr);v(Qr);v(tn);v(C);v(ee);v(sn);v(mn);v(gn);v(Sn);v(xn);v(En);v(yt);v(Ln);v(ni);v(ue);v(ci);v(Pn);v(Dn);v(Si);v(Ai);v(Pi);v(Bi);v(Vi);v(as);v(hs);v(ys);v(ks);v(Ss);v(Ls);v(jr);v(Lr);v($r);v({...C,id:"slack_notify"});export{I as INTEGRATIONS,eo as INTEGRATION_REGISTRY,nt as REPORT_SEVERITIES,zg as SKILLS,Lr as anthropicBillingSkill,Bi as artifactSkill,Jr as browserSkill,Vi as chartRenderSkill,Si as chatMemorySkill,En as chatNotifySkill,hs as codeScanSkill,ks as codebaseMemorySkill,Dn as coreToolsSkill,$r as cursorAdminSkill,Pi as datasetStoreSkill,sn as discordSkill,Qu as fetchAllProviders,Us as fetchAnthropicCosts,Vu as fetchAnthropicWorkspaces,Js as fetchCursorSpend,Cs as fetchOpenAICosts,Zu as fetchOpenAIProjects,zr as figmaSkill,Om as functionSkill,Ss as gbrainSkill,t_ as getAllSkills,Xg as getSkill,ue as gitSkill,ci as gitWriteSkill,Ee as githubSkill,je as gitlabSkill,Sn as googleDocsSkill,Xu as groupByKey,e_ as hasSkill,Wr as hubspotSkill,Br as jiraSkill,Ai as kvMemorySkill,xn as larkDocsSkill,ee as larkSkill,Vr as linearSkill,gn as linkedinSkill,r_ as listSkillIds,em as meanStddev,Ln as memorySkill,mn as notionSkill,jr as openaiBillingSkill,tn as opendesignSkill,Qr as planeSkill,Qg as registerSkill,it as reportObjectSchema,lm as reportToBlockKit,dm as reportToLarkCard,fm as reportToMarkdown,mm as reportToNotionBlocks,ni as runnerSkill,yt as sentrySkill,Ms as skill,Pn as skillInstallerSkill,C as slackSkill,as as socialCardSkill,ni as testRunnerSkill,Ls as workflowBuilderSkill};
1087
+ `)}});break}case"paragraph":{t.push({tag:"div",text:{tag:"lark_md",content:o.text}});break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let o=[];e.footer.viewUrl&&o.push({tag:"button",text:{tag:"plain_text",content:"View in Zibby"},url:e.footer.viewUrl,type:"primary"}),e.footer.rerunUrl&&o.push({tag:"button",text:{tag:"plain_text",content:"Run again"},url:e.footer.rerunUrl,type:"default"}),t.push({tag:"hr"}),t.push({tag:"action",actions:o})}let s="blue";return e.headline.delta?.severity&&(s=lm[e.headline.delta.severity]||"blue"),{config:{wide_screen_mode:!0},header:{title:{tag:"plain_text",content:e.title.slice(0,200)},subtitle:e.subtitle?{tag:"plain_text",content:e.subtitle.slice(0,200)}:void 0,template:s},elements:t}}var um=Object.freeze({ok:"green_background",info:"blue_background",warn:"orange_background",critical:"red_background"}),qs=Object.freeze({ok:"\u{1F7E2}",info:"\u2139\uFE0F",warn:"\u26A0\uFE0F",critical:"\u{1F6A8}"});function Te(r,e={}){let t={type:"text",text:{content:String(r).slice(0,2e3)}};return e.annotations&&(t.annotations=e.annotations),[t]}function Pr(r,e){let t={object:"block",type:"paragraph",paragraph:{rich_text:Te(r)}};return e&&(t.paragraph.color=e),t}function mm(r){return{object:"block",type:"code",code:{rich_text:Te(r),language:"plain text"}}}function fm(r){let e=it.parse(r),t=[];t.push({object:"block",type:"heading_1",heading_1:{rich_text:Te(e.title.slice(0,200))}}),e.subtitle&&t.push(Pr(e.subtitle,"gray_background"));let n=[{type:"text",text:{content:e.headline.primary.slice(0,200)},annotations:{bold:!0}}];if(e.headline.delta){let s=Ct[e.headline.delta.direction]||"",o=e.headline.delta.severity?U[e.headline.delta.severity]:"",a=` ${s} ${e.headline.delta.value} ${o}`.trimEnd();n.push({type:"text",text:{content:a}})}t.push({object:"block",type:"paragraph",paragraph:{rich_text:n}}),e.headline.summary&&t.push(Pr(e.headline.summary));for(let s of e.sections)switch(t.push({object:"block",type:"divider",divider:{}}),s.title&&t.push({object:"block",type:"heading_2",heading_2:{rich_text:Te(s.title)}}),s.kind){case"trend":{let o=Math.max(...s.values),a=Math.min(...s.values),c=s.labels.map((d,l)=>{let p=s.values[l],u=Ut(p,o),f=(s.highlight==="last"&&l===s.labels.length-1||s.highlight==="max"&&p===o||s.highlight==="min"&&p===a)&&s.severity?` ${U[s.severity]}`:"";return`${st(d,10)} ${Jt(p.toLocaleString(),8)} ${u}${f}`});t.push(mm(c.join(`
1088
+ `)));break}case"table":{let o={object:"block",type:"table_row",table_row:{cells:s.headers.map(c=>Te(c))}},a=s.rows.map(c=>({object:"block",type:"table_row",table_row:{cells:c.map(d=>Te(String(d)))}}));t.push({object:"block",type:"table",table:{table_width:s.headers.length,has_column_header:!0,has_row_header:!1,children:[o,...a]}});break}case"callouts":{let o=s.tone||"info",a=qs[o],c=um[o];for(let d of s.items)t.push({object:"block",type:"callout",callout:{rich_text:Te(d),icon:{type:"emoji",emoji:a},color:c}});break}case"breakdown":{for(let o of s.rows){let a=[{type:"text",text:{content:`${o.label}: `},annotations:{bold:!0}},{type:"text",text:{content:String(o.value)}}];o.sub&&a.push({type:"text",text:{content:` (${o.sub})`},annotations:{italic:!0}}),o.severity&&a.push({type:"text",text:{content:` ${U[o.severity]}`}}),t.push({object:"block",type:"bulleted_list_item",bulleted_list_item:{rich_text:a}})}break}case"paragraph":{t.push(Pr(s.text));break}}e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)&&(t.push({object:"block",type:"divider",divider:{}}),e.footer.viewUrl&&t.push({object:"block",type:"embed",embed:{url:e.footer.viewUrl}}),e.footer.rerunUrl&&t.push({object:"block",type:"embed",embed:{url:e.footer.rerunUrl}}));let i=e.headline.delta?.severity?qs[e.headline.delta.severity]:void 0;return{blocks:t,title:e.title.slice(0,200),icon:i}}function Ds(r){return String(r??"").replace(/\|/g,"\\|")}function hm(r){let e=it.parse(r),t=[];t.push(`# ${e.title}`),e.subtitle&&t.push("",e.subtitle);let n=[`**${e.headline.primary}**`];if(e.headline.delta){let i=Ct[e.headline.delta.direction]||"",s=e.headline.delta.severity?U[e.headline.delta.severity]:"";n.push(`${i} ${e.headline.delta.value} ${s}`.trim())}t.push("",n.join(" ")),e.headline.summary&&t.push("",e.headline.summary);for(let i of e.sections)switch(t.push("","---",""),i.title&&t.push(`## ${i.title}`,""),i.kind){case"trend":{let s=Math.max(...i.values),o=Math.min(...i.values),a=i.labels.map((c,d)=>{let l=i.values[d],p=Ut(l,s),m=(i.highlight==="last"&&d===i.labels.length-1||i.highlight==="max"&&l===s||i.highlight==="min"&&l===o)&&i.severity?` ${U[i.severity]}`:"";return`${st(c,10)} ${Jt(l.toLocaleString(),8)} ${p}${m}`});t.push("```",...a,"```");break}case"table":{t.push(`| ${i.headers.map(Ds).join(" | ")} |`),t.push(`| ${i.headers.map(()=>"---").join(" | ")} |`);for(let s of i.rows)t.push(`| ${s.map(Ds).join(" | ")} |`);break}case"callouts":{let s=U[i.tone||"info"];for(let o of i.items)t.push(`- ${s} ${o}`);break}case"breakdown":{for(let s of i.rows){let o=s.sub?` _${s.sub}_`:"",a=s.severity?` ${U[s.severity]}`:"";t.push(`- **${s.label}**: ${s.value}${o}${a}`)}break}case"paragraph":{t.push(i.text);break}}if(e.footer&&(e.footer.viewUrl||e.footer.rerunUrl)){let i=[];e.footer.viewUrl&&i.push(`[View in Zibby](${e.footer.viewUrl})`),e.footer.rerunUrl&&i.push(`[Run again](${e.footer.rerunUrl})`),t.push("","---","",i.join(" \xB7 "))}return t.join(`
1089
+ `)}import{createRequire as ym}from"module";import{fileURLToPath as gm}from"url";import{registerHandlers as _m}from"@zibby/core/function-skill-registry.js";import{registerSkill as bm}from"@zibby/agent-workflow";var km=ym(import.meta.url);function wm(){try{return km.resolve("@zibby/core/function-bridge.js")}catch{return null}}var Sm=import.meta.url;function vm(){let r=Error.prepareStackTrace;try{Error.prepareStackTrace=(n,i)=>i;let t=new Error().stack;for(let n=2;n<t.length;n++){let i=t[n].getFileName();if(i&&i!==Sm&&!i.startsWith("node:"))return i.startsWith("file://")?gm(i):i}return null}finally{Error.prepareStackTrace=r}}function Im(r){if(!r||typeof r!="object")return{type:"object",properties:{},required:[]};let e={},t=[];for(let[n,i]of Object.entries(r))if(typeof i=="string")e[n]={type:i},t.push(n);else{let{required:s,...o}=i;e[n]=o,s!==!1&&t.push(n)}return{type:"object",properties:e,required:t}}function Nm(r,e,t){if(typeof t.handler!="function")throw new Error(`Skill "${r}" must have a handler function`);let n={[r]:t.handler},i=[{name:r,description:t.description||"",input_schema:Im(t.input)}];return _m(r,n,i),{id:r,type:"function",serverName:r,allowedTools:[`mcp__${r}__*`],description:t.description||`Function skill: ${r}`,envKeys:[],tools:i,resolve(){let s=wm();return s?{command:"node",args:[s,e,r]}:null}}}function Om(r,e){return{id:r,type:"mcp",serverName:e.serverName||r,allowedTools:e.allowedTools||[`mcp__${e.serverName||r}__*`],description:e.description||`MCP skill: ${r}`,envKeys:e.envKeys||[],tools:e.tools||[],resolve:e.resolve,...e.cursorKey&&{cursorKey:e.cursorKey},...e.sessionEnvKey&&{sessionEnvKey:e.sessionEnvKey}}}function Ms(r,e){let t;if("handler"in e){if(typeof e.handler!="function")throw new Error(`Skill "${r}" must have a handler function`);let n=vm();if(!n)throw new Error(`Could not resolve caller file for skill "${r}".`);t=Nm(r,n,e)}else if(typeof e.resolve=="function")t=Om(r,e);else throw new Error(`Skill "${r}" must have either a handler (function skill) or resolve (MCP skill).`);return bm(t),t}var Tm=Ms;import{registerSkill as e_,getSkill as t_,hasSkill as r_,getAllSkills as n_,listSkillIds as i_}from"@zibby/agent-workflow";v(Jr);v(Br);v(Ee);v(je);v(zr);v(Wr);v(Vr);v(Qr);v(tn);v(C);v(ee);v(sn);v(mn);v(gn);v(Sn);v(xn);v(En);v(yt);v(Ln);v(ni);v(ue);v(ci);v(Pn);v(Dn);v(Si);v(Ai);v(Pi);v(Bi);v(Vi);v(as);v(hs);v(ys);v(ks);v(Ss);v(Ls);v(jr);v(Lr);v($r);v({...C,id:"slack_notify"});export{I as INTEGRATIONS,eo as INTEGRATION_REGISTRY,nt as REPORT_SEVERITIES,Wg as SKILLS,Lr as anthropicBillingSkill,Bi as artifactSkill,Jr as browserSkill,Vi as chartRenderSkill,Si as chatMemorySkill,En as chatNotifySkill,hs as codeScanSkill,ks as codebaseMemorySkill,Dn as coreToolsSkill,$r as cursorAdminSkill,Pi as datasetStoreSkill,sn as discordSkill,Xu as fetchAllProviders,Us as fetchAnthropicCosts,Qu as fetchAnthropicWorkspaces,Js as fetchCursorSpend,Cs as fetchOpenAICosts,Vu as fetchOpenAIProjects,zr as figmaSkill,Tm as functionSkill,Ss as gbrainSkill,n_ as getAllSkills,t_ as getSkill,ue as gitSkill,ci as gitWriteSkill,Ee as githubSkill,je as gitlabSkill,Sn as googleDocsSkill,em as groupByKey,r_ as hasSkill,Wr as hubspotSkill,Br as jiraSkill,Ai as kvMemorySkill,xn as larkDocsSkill,ee as larkSkill,Vr as linearSkill,gn as linkedinSkill,i_ as listSkillIds,tm as meanStddev,Ln as memorySkill,mn as notionSkill,jr as openaiBillingSkill,tn as opendesignSkill,Qr as planeSkill,e_ as registerSkill,it as reportObjectSchema,dm as reportToBlockKit,pm as reportToLarkCard,hm as reportToMarkdown,fm as reportToNotionBlocks,ni as runnerSkill,yt as sentrySkill,Ms as skill,Pn as skillInstallerSkill,C as slackSkill,as as socialCardSkill,ni as testRunnerSkill,Ls as workflowBuilderSkill};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "0.1.86",
3
+ "version": "0.1.88",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "0.1.86",
3
+ "version": "0.1.88",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",