@zibby/skills 0.1.64 → 0.1.68

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,4 +1,4 @@
1
- import{existsSync as S}from"fs";import{fileURLToPath as v}from"url";import{dirname as E,resolve as T}from"path";var w=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",PLANE:"plane",LINEAR:"linear",FIGMA:"figma",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business"}),x=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark",connectPath:"/integrations?provider=lark"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"}});function O(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let n=E(v(import.meta.url)),i=T(n,"..","bin","mcp-skill.mjs");return S(i)?i:null}var P=process.env.LINEAR_API_URL||"https://api.linear.app/graphql";function R(){if(process.env.LINEAR_OAUTH_TOKEN)return`Bearer ${process.env.LINEAR_OAUTH_TOKEN}`;let n=process.env.LINEAR_API_KEY;if(!n)throw new Error("Linear is not connected: set LINEAR_API_KEY (personal API key) or LINEAR_OAUTH_TOKEN.");return n}async function f(n,i={}){let e=await fetch(P,{method:"POST",headers:{Authorization:R(),"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:i})});if(!e.ok){let s=await e.text().catch(()=>"");throw new Error(`Linear API ${e.status}: ${s.slice(0,300)}`)}let t=await e.json().catch(()=>null);if(!t)throw new Error("Linear API returned a non-JSON body");if(Array.isArray(t.errors)&&t.errors.length){let s=t.errors.map(a=>a?.message||String(a)).join("; ");throw new Error(`Linear GraphQL error: ${s.slice(0,300)}`)}return t.data}function h(n){return String(n||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function $(n,i){let e=h(n),t=h(i);if(!e||!t)return 0;if(e===t)return 1;if(e.length===1||t.length===1)return e===t?1:0;let s=d=>{let c=new Map;for(let y=0;y<d.length-1;y++){let g=d.slice(y,y+2);c.set(g,(c.get(g)||0)+1)}return c},a=s(e),r=s(t),o=0,l=0,m=0;for(let d of a.values())l+=d;for(let d of r.values())m+=d;for(let[d,c]of a.entries())o+=Math.min(c,r.get(d)||0);return 2*o/Math.max(1,l+m)}function C(n,i){let e=Array.isArray(n)?n:[];if(!e.length)return{state:null,strategy:"no-states"};let t=h(i);if(!t)return{state:null,strategy:"no-target"};let s=e.find(o=>h(o.name)===t);if(s)return{state:s,strategy:"exact"};let a={backlog:["backlog"],unstarted:["todo","unstarted","open"],started:["inprogress","started","doing","wip"],completed:["done","completed","closed","resolved","fixed"],canceled:["canceled","cancelled","wontfix","won'tfix"],triage:["triage"]};for(let[o,l]of Object.entries(a)){if(!l.some(d=>h(d)===t))continue;let m=e.find(d=>d.type===o);if(m)return{state:m,strategy:"type-alias"}}let r=e.map(o=>({s:o,score:$(i,o.name)})).sort((o,l)=>l.score-o.score);return r[0]&&r[0].score>=.5?{state:r[0].s,strategy:"fuzzy"}:{state:null,strategy:"no-match"}}var q=`
1
+ import{existsSync as S}from"fs";import{fileURLToPath as v}from"url";import{dirname as E,resolve as O}from"path";var w=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",FIGMA:"figma",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),x=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark",connectPath:"/integrations?provider=lark"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});function T(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let n=E(v(import.meta.url)),i=O(n,"..","bin","mcp-skill.mjs");return S(i)?i:null}var P=process.env.LINEAR_API_URL||"https://api.linear.app/graphql";function R(){if(process.env.LINEAR_OAUTH_TOKEN)return`Bearer ${process.env.LINEAR_OAUTH_TOKEN}`;let n=process.env.LINEAR_API_KEY;if(!n)throw new Error("Linear is not connected: set LINEAR_API_KEY (personal API key) or LINEAR_OAUTH_TOKEN.");return n}async function f(n,i={}){let e=await fetch(P,{method:"POST",headers:{Authorization:R(),"Content-Type":"application/json"},body:JSON.stringify({query:n,variables:i})});if(!e.ok){let s=await e.text().catch(()=>"");throw new Error(`Linear API ${e.status}: ${s.slice(0,300)}`)}let t=await e.json().catch(()=>null);if(!t)throw new Error("Linear API returned a non-JSON body");if(Array.isArray(t.errors)&&t.errors.length){let s=t.errors.map(a=>a?.message||String(a)).join("; ");throw new Error(`Linear GraphQL error: ${s.slice(0,300)}`)}return t.data}function h(n){return String(n||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function $(n,i){let e=h(n),t=h(i);if(!e||!t)return 0;if(e===t)return 1;if(e.length===1||t.length===1)return e===t?1:0;let s=d=>{let u=new Map;for(let y=0;y<d.length-1;y++){let g=d.slice(y,y+2);u.set(g,(u.get(g)||0)+1)}return u},a=s(e),r=s(t),o=0,l=0,m=0;for(let d of a.values())l+=d;for(let d of r.values())m+=d;for(let[d,u]of a.entries())o+=Math.min(u,r.get(d)||0);return 2*o/Math.max(1,l+m)}function C(n,i){let e=Array.isArray(n)?n:[];if(!e.length)return{state:null,strategy:"no-states"};let t=h(i);if(!t)return{state:null,strategy:"no-target"};let s=e.find(o=>h(o.name)===t);if(s)return{state:s,strategy:"exact"};let a={backlog:["backlog"],unstarted:["todo","unstarted","open"],started:["inprogress","started","doing","wip"],completed:["done","completed","closed","resolved","fixed"],canceled:["canceled","cancelled","wontfix","won'tfix"],triage:["triage"]};for(let[o,l]of Object.entries(a)){if(!l.some(d=>h(d)===t))continue;let m=e.find(d=>d.type===o);if(m)return{state:m,strategy:"type-alias"}}let r=e.map(o=>({s:o,score:$(i,o.name)})).sort((o,l)=>l.score-o.score);return r[0]&&r[0].score>=.5?{state:r[0].s,strategy:"fuzzy"}:{state:null,strategy:"no-match"}}var G=`
2
2
  id
3
3
  identifier
4
4
  number
@@ -30,7 +30,7 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
30
30
 
31
31
  ### Notes
32
32
  - Always resolve a team first when you need states or want to create/move issues by state name \u2014 states only make sense within their team.
33
- - Issue identifier (ENG-123) and internal id (uuid) are both accepted by get/update tools.`,resolve(){let n={};for(let e of this.envKeys)process.env[e]&&(n[e]=process.env[e]);process.env.LINEAR_API_URL&&(n.LINEAR_API_URL=process.env.LINEAR_API_URL);let i=O();return i?{type:"stdio",command:"node",args:[i,"../dist/linear.js","linearSkill"],env:n,description:this.description,alwaysLoad:!0}:{command:null,args:[],env:n,description:this.description}},async handleToolCall(n,i){try{switch(n){case"linear_list_teams":{let t=(await f(`
33
+ - Issue identifier (ENG-123) and internal id (uuid) are both accepted by get/update tools.`,resolve(){let n={};for(let e of this.envKeys)process.env[e]&&(n[e]=process.env[e]);process.env.LINEAR_API_URL&&(n.LINEAR_API_URL=process.env.LINEAR_API_URL);let i=T();return i?{type:"stdio",command:"node",args:[i,"../dist/linear.js","linearSkill"],env:n,description:this.description,alwaysLoad:!0}:{command:null,args:[],env:n,description:this.description}},async handleToolCall(n,i){try{switch(n){case"linear_list_teams":{let t=(await f(`
34
34
  query Teams($first: Int) {
35
35
  teams(first: $first) {
36
36
  nodes { id key name description }
@@ -43,7 +43,7 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
43
43
  states { nodes { id name type color position } }
44
44
  }
45
45
  }
46
- `,{teamId:s}))?.team,m=(l?.states?.nodes||[]).slice().sort((d,c)=>(d.position||0)-(c.position||0));return JSON.stringify({team:l?{id:l.id,key:l.key,name:l.name}:null,count:m.length,states:m})}let r=(await f(`
46
+ `,{teamId:s}))?.team,m=(l?.states?.nodes||[]).slice().sort((d,u)=>(d.position||0)-(u.position||0));return JSON.stringify({team:l?{id:l.id,key:l.key,name:l.name}:null,count:m.length,states:m})}let r=(await f(`
47
47
  query AllStates($first: Int) {
48
48
  workflowStates(first: $first) {
49
49
  nodes { id name type color team { id key name } }
@@ -55,7 +55,7 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
55
55
  nodes { id name color team { id key } }
56
56
  }
57
57
  }
58
- `,{first:i?.limit||100,filter:e?{team:{id:{eq:e}}}:void 0}))?.issueLabels?.nodes||[];return JSON.stringify({count:s.length,labels:s})}case"linear_list_issues":{let{teamId:e,teamKey:t,stateId:s,stateName:a,label:r,assigneeId:o,updatedAfter:l,limit:m}=i||{},d={},c=e;!c&&t&&(c=await N(t)),c&&(d.team={id:{eq:c}}),s?d.state={id:{eq:s}}:a&&(d.state={name:{eqIgnoreCase:a}}),r&&(d.labels={name:{eqIgnoreCase:r}}),o&&(d.assignee={id:{eq:o}}),l&&(d.updatedAt={gt:l});let g=((await f(`
58
+ `,{first:i?.limit||100,filter:e?{team:{id:{eq:e}}}:void 0}))?.issueLabels?.nodes||[];return JSON.stringify({count:s.length,labels:s})}case"linear_list_issues":{let{teamId:e,teamKey:t,stateId:s,stateName:a,label:r,assigneeId:o,updatedAfter:l,limit:m}=i||{},d={},u=e;!u&&t&&(u=await N(t)),u&&(d.team={id:{eq:u}}),s?d.state={id:{eq:s}}:a&&(d.state={name:{eqIgnoreCase:a}}),r&&(d.labels={name:{eqIgnoreCase:r}}),o&&(d.assignee={id:{eq:o}}),l&&(d.updatedAt={gt:l});let g=((await f(`
59
59
  query Issues($first: Int, $filter: IssueFilter, $orderBy: PaginationOrderBy) {
60
60
  issues(first: $first, filter: $filter, orderBy: $orderBy) {
61
61
  nodes {
@@ -67,7 +67,7 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
67
67
  }
68
68
  }
69
69
  }
70
- `,{first:m||30,filter:Object.keys(d).length?d:void 0,orderBy:"updatedAt"}))?.issues?.nodes||[]).map(u=>({id:u.id,identifier:u.identifier,number:u.number,title:u.title,url:u.url,priority:u.priority,state:u.state?.name,stateType:u.state?.type,assignee:u.assignee?.displayName||null,labels:(u.labels?.nodes||[]).map(p=>p.name),team:u.team?.key,createdAt:u.createdAt,updatedAt:u.updatedAt}));return JSON.stringify({count:g.length,issues:g})}case"linear_get_issue":{let e=i?.issueId||i?.identifier||i?.issueKey;if(!e)return JSON.stringify({error:"issueId or identifier is required"});let t=await I(e);return JSON.stringify(t?{id:t.id,identifier:t.identifier,number:t.number,title:t.title,description:t.description||"",url:t.url,priority:t.priority,state:t.state?.name,stateId:t.state?.id,stateType:t.state?.type,assignee:t.assignee?.displayName||t.assignee?.name||null,assigneeId:t.assignee?.id||null,labels:(t.labels?.nodes||[]).map(s=>s.name),team:t.team?{id:t.team.id,key:t.team.key,name:t.team.name}:null,createdAt:t.createdAt,updatedAt:t.updatedAt}:{error:`Issue not found: ${e}`})}case"linear_get_comments":{let e=i?.issueId||i?.identifier||i?.issueKey;if(!e)return JSON.stringify({error:"issueId or identifier is required"});let t=await I(e,`
70
+ `,{first:m||30,filter:Object.keys(d).length?d:void 0,orderBy:"updatedAt"}))?.issues?.nodes||[]).map(c=>({id:c.id,identifier:c.identifier,number:c.number,title:c.title,url:c.url,priority:c.priority,state:c.state?.name,stateType:c.state?.type,assignee:c.assignee?.displayName||null,labels:(c.labels?.nodes||[]).map(p=>p.name),team:c.team?.key,createdAt:c.createdAt,updatedAt:c.updatedAt}));return JSON.stringify({count:g.length,issues:g})}case"linear_get_issue":{let e=i?.issueId||i?.identifier||i?.issueKey;if(!e)return JSON.stringify({error:"issueId or identifier is required"});let t=await I(e);return JSON.stringify(t?{id:t.id,identifier:t.identifier,number:t.number,title:t.title,description:t.description||"",url:t.url,priority:t.priority,state:t.state?.name,stateId:t.state?.id,stateType:t.state?.type,assignee:t.assignee?.displayName||t.assignee?.name||null,assigneeId:t.assignee?.id||null,labels:(t.labels?.nodes||[]).map(s=>s.name),team:t.team?{id:t.team.id,key:t.team.key,name:t.team.name}:null,createdAt:t.createdAt,updatedAt:t.updatedAt}:{error:`Issue not found: ${e}`})}case"linear_get_comments":{let e=i?.issueId||i?.identifier||i?.issueKey;if(!e)return JSON.stringify({error:"issueId or identifier is required"});let t=await I(e,`
71
71
  id identifier
72
72
  comments(first: ${Number(i?.limit)||50}) {
73
73
  nodes { id body createdAt updatedAt user { id name displayName } }
@@ -83,14 +83,14 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
83
83
  id identifier
84
84
  state { id name type }
85
85
  team { id key states { nodes { id name type position } } }
86
- `);if(!o)return JSON.stringify({error:`Issue not found: ${e}`});let l=t,m=t?{strategy:"explicit-id"}:null;if(!l){let y=String(s||a||r||"").trim(),g=(o.team?.states?.nodes||[]).slice().sort((p,L)=>(p.position||0)-(L.position||0));if(!y)return JSON.stringify({ok:!1,error:"stateId or stateName/toStatus is required",issue:o.identifier,availableStates:g.map(p=>({id:p.id,name:p.name,type:p.type}))});let u=C(g,y);if(!u.state)return JSON.stringify({ok:!1,error:`No workflow state matches "${y}" in team ${o.team?.key}`,issue:o.identifier,availableStates:g.map(p=>({id:p.id,name:p.name,type:p.type}))});l=u.state.id,m={strategy:u.strategy,matchedName:u.state.name}}let c=(await f(`
86
+ `);if(!o)return JSON.stringify({error:`Issue not found: ${e}`});let l=t,m=t?{strategy:"explicit-id"}:null;if(!l){let y=String(s||a||r||"").trim(),g=(o.team?.states?.nodes||[]).slice().sort((p,L)=>(p.position||0)-(L.position||0));if(!y)return JSON.stringify({ok:!1,error:"stateId or stateName/toStatus is required",issue:o.identifier,availableStates:g.map(p=>({id:p.id,name:p.name,type:p.type}))});let c=C(g,y);if(!c.state)return JSON.stringify({ok:!1,error:`No workflow state matches "${y}" in team ${o.team?.key}`,issue:o.identifier,availableStates:g.map(p=>({id:p.id,name:p.name,type:p.type}))});l=c.state.id,m={strategy:c.strategy,matchedName:c.state.name}}let u=(await f(`
87
87
  mutation MoveIssue($id: String!, $input: IssueUpdateInput!) {
88
88
  issueUpdate(id: $id, input: $input) {
89
89
  success
90
90
  issue { id identifier state { id name type } }
91
91
  }
92
92
  }
93
- `,{id:o.id,input:{stateId:l}}))?.issueUpdate;return JSON.stringify({ok:!!c?.success,issue:c?.issue?.identifier||o.identifier,stateAfter:c?.issue?.state?.name||null,stateTypeAfter:c?.issue?.state?.type||null,resolution:m})}case"linear_link_attachment":{let e=i?.issueId||i?.identifier||i?.issueKey,{url:t,title:s,subtitle:a}=i||{};if(!e||!t)return JSON.stringify({error:"issueId/identifier and url are required"});let r=await I(e,"id identifier");if(!r)return JSON.stringify({error:`Issue not found: ${e}`});let l=(await f(`
93
+ `,{id:o.id,input:{stateId:l}}))?.issueUpdate;return JSON.stringify({ok:!!u?.success,issue:u?.issue?.identifier||o.identifier,stateAfter:u?.issue?.state?.name||null,stateTypeAfter:u?.issue?.state?.type||null,resolution:m})}case"linear_link_attachment":{let e=i?.issueId||i?.identifier||i?.issueKey,{url:t,title:s,subtitle:a}=i||{};if(!e||!t)return JSON.stringify({error:"issueId/identifier and url are required"});let r=await I(e,"id identifier");if(!r)return JSON.stringify({error:`Issue not found: ${e}`});let l=(await f(`
94
94
  mutation LinkAttachment($input: AttachmentCreateInput!) {
95
95
  attachmentCreate(input: $input) {
96
96
  success
@@ -101,7 +101,7 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
101
101
  query TeamByKey($filter: TeamFilter) {
102
102
  teams(first: 1, filter: $filter) { nodes { id key } }
103
103
  }
104
- `,{filter:{key:{eq:n}}}))?.teams?.nodes?.[0]?.id||null}async function I(n,i=q){let e=String(n).trim(),t=/^([A-Za-z][A-Za-z0-9]*)-(\d+)$/.exec(e);if(t){let a=t[1].toUpperCase(),r=Number(t[2]);return(await f(`
104
+ `,{filter:{key:{eq:n}}}))?.teams?.nodes?.[0]?.id||null}async function I(n,i=G){let e=String(n).trim(),t=/^([A-Za-z][A-Za-z0-9]*)-(\d+)$/.exec(e);if(t){let a=t[1].toUpperCase(),r=Number(t[2]);return(await f(`
105
105
  query IssueByIdentifier($filter: IssueFilter) {
106
106
  issues(first: 1, filter: $filter) {
107
107
  nodes { ${i} }
@@ -111,4 +111,4 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
111
111
  query IssueById($id: String!) {
112
112
  issue(id: $id) { ${i} }
113
113
  }
114
- `,{id:e}))?.issue||null}var G={triage:"todo",backlog:"todo",unstarted:"todo",started:"in_progress",completed:"done",canceled:"done"},J=/\b(blocked|on[\s-]?hold|waiting|stuck)\b/i;function k(n){return n?n.name&&J.test(n.name)?"blocked":G[n.type]||"unknown":"unknown"}function b(n){let i=JSON.parse(n);if(i&&i.error)throw new Error(i.error);return i}function A(n){let i=n.state||null,e=n.stateType||null;return{id:String(n.id||n.identifier||""),key:n.identifier||String(n.id||""),title:n.title||"",body:n.description||"",state:i,stateCategory:k(i?{name:i,type:e}:null),assignee:n.assignee||null,url:n.url||null,_raw:n}}var K={id:"linear",toStateCategory:k,toNeutral:A,async listCandidates(n={}){let i=n.ctx||{},e={teamId:i.teamId,teamKey:i.teamKey,stateName:n.state,label:Array.isArray(n.labels)?n.labels[0]:n.labels,assigneeId:i.assigneeId,updatedAfter:n.updatedAfter,limit:n.limit};return(b(await _.handleToolCall("linear_list_issues",e)).issues||[]).map(A)},async getTicket(n){if(!n)throw new Error("key is required");let i=JSON.parse(await _.handleToolCall("linear_get_issue",{identifier:n}));return i.error?null:A(i)},async getComments(n){if(!n)throw new Error("key is required");return(b(await _.handleToolCall("linear_get_comments",{identifier:n})).comments||[]).map(e=>({id:String(e.id),author:e.author||"Unknown",body:e.body||"",createdAt:e.createdAt||null,updatedAt:e.updatedAt||null,_raw:e}))},async addComment(n,i){if(!n||!i)throw new Error("key and body are required");let e=b(await _.handleToolCall("linear_add_comment",{identifier:n,body:i}));return{ok:!!e.ok,id:e.commentId||null}},async transition(n,i){if(!n)throw new Error("key is required");let e=JSON.parse(await _.handleToolCall("linear_update_state",{identifier:n,toStatus:i}));if(!e.ok)return{ok:!1,error:e.error||"state update failed",_raw:e};let t=e.stateAfter||null;return{ok:!0,stateAfter:t,stateCategoryAfter:k(t?{name:t,type:e.stateTypeAfter}:null),_raw:e}},async linkPullRequest(n,i,e){if(!n||!i)throw new Error("key and prUrl are required");try{if(b(await _.handleToolCall("linear_link_attachment",{identifier:n,url:i,title:e||i})).ok)return{ok:!0,via:"attachment"};throw new Error("attachmentCreate returned ok:false")}catch(t){let s=`${e?`${e}: `:"Linked PR: "}${i}`;return{ok:!!b(await _.handleToolCall("linear_add_comment",{identifier:n,body:s})).ok,via:"comment",error:String(t?.message||t)}}}},D=K;export{D as default,K as linearAdapter};
114
+ `,{id:e}))?.issue||null}var q={triage:"todo",backlog:"todo",unstarted:"todo",started:"in_progress",completed:"done",canceled:"done"},J=/\b(blocked|on[\s-]?hold|waiting|stuck)\b/i;function k(n){return n?n.name&&J.test(n.name)?"blocked":q[n.type]||"unknown":"unknown"}function b(n){let i=JSON.parse(n);if(i&&i.error)throw new Error(i.error);return i}function A(n){let i=n.state||null,e=n.stateType||null;return{id:String(n.id||n.identifier||""),key:n.identifier||String(n.id||""),title:n.title||"",body:n.description||"",state:i,stateCategory:k(i?{name:i,type:e}:null),assignee:n.assignee||null,url:n.url||null,_raw:n}}var K={id:"linear",toStateCategory:k,toNeutral:A,async listCandidates(n={}){let i=n.ctx||{},e={teamId:i.teamId,teamKey:i.teamKey,stateName:n.state,label:Array.isArray(n.labels)?n.labels[0]:n.labels,assigneeId:i.assigneeId,updatedAfter:n.updatedAfter,limit:n.limit};return(b(await _.handleToolCall("linear_list_issues",e)).issues||[]).map(A)},async getTicket(n){if(!n)throw new Error("key is required");let i=JSON.parse(await _.handleToolCall("linear_get_issue",{identifier:n}));return i.error?null:A(i)},async getComments(n){if(!n)throw new Error("key is required");return(b(await _.handleToolCall("linear_get_comments",{identifier:n})).comments||[]).map(e=>({id:String(e.id),author:e.author||"Unknown",body:e.body||"",createdAt:e.createdAt||null,updatedAt:e.updatedAt||null,_raw:e}))},async addComment(n,i){if(!n||!i)throw new Error("key and body are required");let e=b(await _.handleToolCall("linear_add_comment",{identifier:n,body:i}));return{ok:!!e.ok,id:e.commentId||null}},async transition(n,i){if(!n)throw new Error("key is required");let e=JSON.parse(await _.handleToolCall("linear_update_state",{identifier:n,toStatus:i}));if(!e.ok)return{ok:!1,error:e.error||"state update failed",_raw:e};let t=e.stateAfter||null;return{ok:!0,stateAfter:t,stateCategoryAfter:k(t?{name:t,type:e.stateTypeAfter}:null),_raw:e}},async linkPullRequest(n,i,e){if(!n||!i)throw new Error("key and prUrl are required");try{if(b(await _.handleToolCall("linear_link_attachment",{identifier:n,url:i,title:e||i})).ok)return{ok:!0,via:"attachment"};throw new Error("attachmentCreate returned ok:false")}catch(t){let s=`${e?`${e}: `:"Linked PR: "}${i}`;return{ok:!!b(await _.handleToolCall("linear_add_comment",{identifier:n,body:s})).ok,via:"comment",error:String(t?.message||t)}}}},H=K;export{H as default,K as linearAdapter};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "0.1.64",
3
+ "version": "0.1.68",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",