@zibby/skills 0.1.35 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/bin/mcp-skill.mjs +138 -0
- package/dist/github.d.ts +11 -1
- package/dist/github.js +3 -3
- package/dist/gitlab.d.ts +9 -0
- package/dist/gitlab.js +2 -2
- package/dist/index.js +48 -48
- package/dist/linear.d.ts +11 -0
- package/dist/linear.js +16 -16
- package/dist/package.json +2 -1
- package/dist/trackers/github-adapter.js +3 -3
- package/dist/trackers/index.js +11 -11
- package/dist/trackers/linear-adapter.js +14 -14
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var k=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"}),
|
|
1
|
+
import{existsSync as S}from"fs";import{fileURLToPath as v}from"url";import{dirname as E,resolve as T}from"path";var k=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"}),K=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"}});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 $(){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:$(),"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 I(n){return String(n||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function R(n,i){let e=I(n),t=I(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=I(i);if(!t)return{state:null,strategy:"no-target"};let s=e.find(o=>I(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=>I(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:R(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=`
|
|
2
2
|
id
|
|
3
3
|
identifier
|
|
4
4
|
number
|
|
@@ -12,7 +12,7 @@ var k=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab"
|
|
|
12
12
|
assignee { id name displayName email }
|
|
13
13
|
labels { nodes { id name color } }
|
|
14
14
|
team { id key name }
|
|
15
|
-
`,
|
|
15
|
+
`,_={id:"linear",serverName:"linear",allowedTools:["mcp__linear__*"],requiresIntegration:k.LINEAR,envKeys:["LINEAR_API_KEY","LINEAR_OAUTH_TOKEN"],description:"Linear \u2014 issues, comments, workflow states (GraphQL API key)",promptFragment:`## Linear (connected)
|
|
16
16
|
You have direct access to the user's Linear workspace (GraphQL API). Tools:
|
|
17
17
|
|
|
18
18
|
### Discovery
|
|
@@ -30,13 +30,13 @@ 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
|
|
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(`
|
|
34
34
|
query Teams($first: Int) {
|
|
35
35
|
teams(first: $first) {
|
|
36
36
|
nodes { id key name description }
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
`,{first:i?.limit||50}))?.teams?.nodes||[];return JSON.stringify({count:
|
|
39
|
+
`,{first:i?.limit||50}))?.teams?.nodes||[];return JSON.stringify({count:t.length,teams:t})}case"linear_list_states":{let{teamId:e,teamKey:t}=i||{},s=e;if(!s&&t&&(s=await N(t)),s){let l=(await f(`
|
|
40
40
|
query States($teamId: String!) {
|
|
41
41
|
team(id: $teamId) {
|
|
42
42
|
id key name
|
|
@@ -49,13 +49,13 @@ You have direct access to the user's Linear workspace (GraphQL API). Tools:
|
|
|
49
49
|
nodes { id name type color team { id key name } }
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
`,{first:i?.limit||200}))?.workflowStates?.nodes||[];return JSON.stringify({scope:"workspace",count:r.length,states:r})}case"linear_list_labels":{let{teamId:
|
|
52
|
+
`,{first:i?.limit||200}))?.workflowStates?.nodes||[];return JSON.stringify({scope:"workspace",count:r.length,states:r})}case"linear_list_labels":{let{teamId:e}=i||{},s=(await f(`
|
|
53
53
|
query Labels($first: Int, $filter: IssueLabelFilter) {
|
|
54
54
|
issueLabels(first: $first, filter: $filter) {
|
|
55
55
|
nodes { id name color team { id key } }
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
`,{first:i?.limit||100,filter:
|
|
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(`
|
|
59
59
|
query Issues($first: Int, $filter: IssueFilter, $orderBy: PaginationOrderBy) {
|
|
60
60
|
issues(first: $first, filter: $filter, orderBy: $orderBy) {
|
|
61
61
|
nodes {
|
|
@@ -67,41 +67,41 @@ 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
|
|
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 h(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 h(e,`
|
|
71
71
|
id identifier
|
|
72
72
|
comments(first: ${Number(i?.limit)||50}) {
|
|
73
73
|
nodes { id body createdAt updatedAt user { id name displayName } }
|
|
74
74
|
}
|
|
75
|
-
`);if(!
|
|
75
|
+
`);if(!t)return JSON.stringify({error:`Issue not found: ${e}`});let s=(t.comments?.nodes||[]).map(a=>({id:a.id,author:a.user?.displayName||a.user?.name||"Unknown",body:a.body||"",createdAt:a.createdAt,updatedAt:a.updatedAt})).sort((a,r)=>String(r.createdAt).localeCompare(String(a.createdAt)));return JSON.stringify({count:s.length,issue:t.identifier,comments:s})}case"linear_add_comment":{let e=i?.issueId||i?.identifier||i?.issueKey,t=i?.body;if(!e||!t)return JSON.stringify({error:"issueId/identifier and body are required"});let s=await h(e,"id identifier");if(!s)return JSON.stringify({error:`Issue not found: ${e}`});let r=(await f(`
|
|
76
76
|
mutation AddComment($input: CommentCreateInput!) {
|
|
77
77
|
commentCreate(input: $input) {
|
|
78
78
|
success
|
|
79
79
|
comment { id url createdAt }
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
`,{input:{issueId:s.id,body:
|
|
82
|
+
`,{input:{issueId:s.id,body:t}}))?.commentCreate;return JSON.stringify({ok:!!r?.success,commentId:r?.comment?.id,url:r?.comment?.url})}case"linear_update_state":{let e=i?.issueId||i?.identifier||i?.issueKey,{stateId:t,stateName:s,toStatus:a,status:r}=i||{};if(!e)return JSON.stringify({error:"issueId or identifier is required"});let o=await h(e,`
|
|
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: ${
|
|
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(`
|
|
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
|
|
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 h(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
|
|
97
97
|
attachment { id url title }
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
`,{input:{issueId:r.id,url:
|
|
100
|
+
`,{input:{issueId:r.id,url:t,title:s||t,subtitle:a||void 0}}))?.attachmentCreate;return JSON.stringify({ok:!!l?.success,attachmentId:l?.attachment?.id,url:l?.attachment?.url})}default:return JSON.stringify({error:`Unknown tool: ${n}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"linear_list_teams",description:"List Linear teams (id, key, name). Needed to scope workflow states and issue queries.",input_schema:{type:"object",properties:{limit:{type:"number",description:"Max teams (default: 50)"}}}},{name:"linear_list_states",description:"List a team's workflow states (id, name, type: backlog|unstarted|started|completed|canceled|triage). Linear states are PER-TEAM. Omit team to list all states across the workspace.",input_schema:{type:"object",properties:{teamId:{type:"string",description:"Team uuid"},teamKey:{type:"string",description:"Team key (e.g. ENG); resolved to an id if teamId omitted"},limit:{type:"number",description:"Max states when listing workspace-wide (default: 200)"}}}},{name:"linear_list_labels",description:"List issue labels, optionally scoped to a team.",input_schema:{type:"object",properties:{teamId:{type:"string",description:"Optional team uuid to scope labels"},limit:{type:"number",description:"Max labels (default: 100)"}}}},{name:"linear_list_issues",description:"List/poll Linear issues filtered by team, state, label, assignee, and an updatedAfter cursor. Returns newest-updated first.",input_schema:{type:"object",properties:{teamId:{type:"string",description:"Team uuid"},teamKey:{type:"string",description:"Team key (e.g. ENG); resolved if teamId omitted"},stateId:{type:"string",description:"Filter by workflow state uuid"},stateName:{type:"string",description:"Filter by state name (case-insensitive)"},label:{type:"string",description:"Filter by label name (case-insensitive)"},assigneeId:{type:"string",description:"Filter by assignee uuid"},updatedAfter:{type:"string",description:"ISO-8601 timestamp; only issues updated after this (polling cursor)"},limit:{type:"number",description:"Max issues (default: 30)"}}}},{name:"linear_get_issue",description:"Get a single Linear issue by identifier (e.g. ENG-123) or internal uuid \u2014 title, description, state, labels, assignee, url.",input_schema:{type:"object",properties:{identifier:{type:"string",description:"Issue identifier, e.g. ENG-123"},issueId:{type:"string",description:"Internal issue uuid (alternative to identifier)"}}}},{name:"linear_get_comments",description:"Get comments on a Linear issue (newest first).",input_schema:{type:"object",properties:{identifier:{type:"string",description:"Issue identifier, e.g. ENG-123"},issueId:{type:"string",description:"Internal issue uuid (alternative to identifier)"},limit:{type:"number",description:"Max comments (default: 50)"}}}},{name:"linear_add_comment",description:"Add a comment to a Linear issue (markdown supported).",input_schema:{type:"object",properties:{identifier:{type:"string",description:"Issue identifier, e.g. ENG-123"},issueId:{type:"string",description:"Internal issue uuid (alternative to identifier)"},body:{type:"string",description:"Comment body (markdown)"}},required:["body"]}},{name:"linear_update_state",description:"Move a Linear issue to a different workflow state. Pass a state NAME (toStatus/stateName) and the tool resolves it to the issue's team's matching state id (exact -> type-alias -> fuzzy), or pass stateId directly. Linear has no transitions \u2014 this sets the state.",input_schema:{type:"object",properties:{identifier:{type:"string",description:"Issue identifier, e.g. ENG-123"},issueId:{type:"string",description:"Internal issue uuid (alternative to identifier)"},stateId:{type:"string",description:"Target workflow state uuid (skips name resolution)"},stateName:{type:"string",description:'Target state name (e.g. "In Progress", "Done")'},toStatus:{type:"string",description:"Alias for stateName"}}}},{name:"linear_link_attachment",description:"Attach a URL (e.g. a GitHub PR) to a Linear issue via native attachments. Use this for PR links; fall back to linear_add_comment if it fails.",input_schema:{type:"object",properties:{identifier:{type:"string",description:"Issue identifier, e.g. ENG-123"},issueId:{type:"string",description:"Internal issue uuid (alternative to identifier)"},url:{type:"string",description:"The URL to attach (e.g. a PR link)"},title:{type:"string",description:"Attachment title (defaults to the URL)"},subtitle:{type:"string",description:"Optional attachment subtitle"}},required:["url"]}}]};async function N(n){return(await f(`
|
|
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
|
|
104
|
+
`,{filter:{key:{eq:n}}}))?.teams?.nodes?.[0]?.id||null}async function h(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(`
|
|
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:
|
|
114
|
+
`,{id:e}))?.issue||null}var J={triage:"todo",backlog:"todo",unstarted:"todo",started:"in_progress",completed:"done",canceled:"done"},G=/\b(blocked|on[\s-]?hold|waiting|stuck)\b/i;function w(n){return n?n.name&&G.test(n.name)?"blocked":J[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:w(i?{name:i,type:e}:null),assignee:n.assignee||null,url:n.url||null,_raw:n}}var x={id:"linear",toStateCategory:w,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:w(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)}}}},Y=x;export{Y as default,x as linearAdapter};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zibby/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"description": "Built-in skill definitions for Zibby test automation framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./bin/mcp-skill.mjs": "./bin/mcp-skill.mjs",
|
|
13
14
|
"./bin/mcp-sentry.mjs": "./bin/mcp-sentry.mjs",
|
|
14
15
|
"./bin/mcp-lark.mjs": "./bin/mcp-lark.mjs",
|
|
15
16
|
"./bin/mcp-slack.mjs": "./bin/mcp-slack.mjs",
|