@zibby/skills 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact.js +1 -1
- package/dist/chartRender.js +1 -1
- package/dist/chat-notify.js +3 -3
- package/dist/chatProgress.js +3 -3
- package/dist/code-scan.js +1 -1
- package/dist/codeStats.js +2 -2
- package/dist/codebaseMemory.js +1 -1
- package/dist/discord.js +1 -1
- package/dist/figma.js +1 -1
- package/dist/gbrain.js +2 -2
- package/dist/git-write.js +9 -9
- package/dist/github.js +5 -5
- package/dist/gitlab.js +1 -1
- package/dist/googleDocs.js +1 -1
- package/dist/hubspot.js +1 -1
- package/dist/index.js +130 -130
- package/dist/jira.js +7 -7
- package/dist/kvMemory.js +1 -1
- package/dist/lark.js +1 -1
- package/dist/larkAttendance.js +1 -1
- package/dist/larkDocs.js +1 -1
- package/dist/linear.js +1 -1
- package/dist/linkedin.js +2 -2
- package/dist/notion.js +3 -3
- package/dist/opendesign.js +2 -2
- package/dist/package.json +4 -4
- package/dist/reportCheck.js +1 -1
- package/dist/sentry.js +2 -2
- package/dist/slack.js +1 -1
- package/dist/socialCard.js +1 -1
- package/dist/trackers/github-adapter.js +5 -5
- package/dist/trackers/index.js +33 -33
- package/dist/trackers/jira-adapter.js +7 -7
- package/dist/trackers/linear-adapter.js +3 -3
- package/dist/vikunja.js +1 -1
- package/package.json +4 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{existsSync as Ee}from"fs";import{dirname as Re,resolve as Le}from"path";import{fileURLToPath as Ce}from"url";import{resolveIntegrationToken as Pe,clearTokenCache as qe}from"@zibby/core/backend-client.js";var Y=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),Ye=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 Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},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"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},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"}});var E={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function pe(s,n,e=process.env){let t=Number(e?.[s]);return Number.isFinite(t)&&t>0?Math.min(6e5,Math.max(1e3,Math.floor(t))):n}function de(s="api",n=process.env){let e=E[s]||E.api;return pe(e.knob,e.fallback,n)}function P(s){return s?.name==="TimeoutError"||s?.name==="AbortError"}function me(s){try{return new URL(String(s?.url??s)).host||"unknown host"}catch{return"unknown host"}}function fe(s,n){if(!s)return n;let e=new AbortController,t=a=>{e.signal.aborted||e.abort(a)},i=()=>t(s.reason),r=()=>t(n.reason);return e.signal.addEventListener("abort",()=>{s.removeEventListener("abort",i),n.removeEventListener("abort",r)},{once:!0}),s.aborted?t(s.reason):n.aborted?t(n.reason):(s.addEventListener("abort",i,{once:!0}),n.addEventListener("abort",r,{once:!0})),e.signal}async function Q(s,n={},e={}){let t=e.kind||"api",i=(E[t]||E.api).knob,r=e.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(e.timeoutMs))):de(t),a=n?.signal;if(a?.aborted)throw a.reason??new DOMException("This operation was aborted","AbortError");let o=AbortSignal.timeout(r),c=fe(a,o);try{return await fetch(s,{...n,signal:c})}catch(l){throw o.aborted&&P(l)?new Error(`${e.what||"request"} TIMED OUT after ${r}ms against ${me(s)} (${i})`):l}}var he={NOTE:"info",TIP:"success",IMPORTANT:"note",WARNING:"warning",CAUTION:"error"},re={info:"NOTE",success:"TIP",note:"IMPORTANT",warning:"WARNING",error:"CAUTION"},ye=2e5,ie=6,ge=/^```([A-Za-z0-9_+.-]{0,32})\s*$/,ke=/^(#{1,6})[ \t]+(.*?)\s*$/,be=/^(?:-{3,}|\*{3,}|_{3,})\s*$/,X=/^>(?: ?(.*))?$/,q=/^[-*][ \t]+\[([ xX])\][ \t]+(.*)$/,Z=/^[-*][ \t]+(.*)$/,V=/^(\d{1,3})[.)][ \t]+(.*)$/,ee=/^\|.*\|\s*$/,_e=/^\|?\s*:?-{1,}:?\s*(?:\|\s*:?-{1,}:?\s*)*\|?\s*$/,Ie=/^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i;function te(s){let n=s.trim().replace(/^\|/,"").replace(/\|$/,""),e=[],t="";for(let i=0;i<n.length;i++){let r=n[i];if(r==="\\"&&n[i+1]==="|"){t+="|",i++;continue}if(r==="|"){e.push(t.trim()),t="";continue}t+=r}return e.push(t.trim()),e}function oe(s){let e=String(s??"").slice(0,ye).replace(/\r\n?/g,`
|
|
2
2
|
`).split(`
|
|
3
|
-
`);return
|
|
4
|
-
`)});continue}if(be.test(i)){e.push({t:"rule"}),t++;continue}if(r=ke.exec(i)){let a=Math.min(ie,r[1].length);e.push({t:"heading",level:a,c:b(r[2])}),t++;continue}if(X.test(i)&&n<3){let a=[];for(;t<s.length&&(r=X.exec(s[t]));)a.push(r[1]??""),t++;let o=a.findIndex(l=>l.trim()!==""),c=o>=0?Ie.exec(a[o].trim()):null;if(c){let l=
|
|
5
|
-
`).map(n=>({type:"paragraph",...n?{content:[{type:"text",text:n}]}:{}}))}}function ae(s){try{let n=
|
|
3
|
+
`);return M(e,0)}function M(s,n){let e=[],t=0;for(;t<s.length;){let i=s[t];if(i.trim()===""){t++;continue}if(/^[ \t]/.test(i)){e.push({t:"paragraph",c:b(i)}),t++;continue}let r;if(r=ge.exec(i)){let a=r[1]||"",o=[];for(t++;t<s.length&&!/^```\s*$/.test(s[t]);)o.push(s[t]),t++;t++,e.push({t:"code",lang:a,v:o.join(`
|
|
4
|
+
`)});continue}if(be.test(i)){e.push({t:"rule"}),t++;continue}if(r=ke.exec(i)){let a=Math.min(ie,r[1].length);e.push({t:"heading",level:a,c:b(r[2])}),t++;continue}if(X.test(i)&&n<3){let a=[];for(;t<s.length&&(r=X.exec(s[t]));)a.push(r[1]??""),t++;let o=a.findIndex(l=>l.trim()!==""),c=o>=0?Ie.exec(a[o].trim()):null;if(c){let l=he[c[1].toUpperCase()];e.push({t:"panel",kind:l,c:M(a.slice(o+1),n+1)})}else e.push({t:"quote",c:M(a,n+1)});continue}if(q.test(i)){let a=[];for(;t<s.length&&(r=q.exec(s[t]));)a.push({checked:r[1]!==" ",c:b(r[2])}),t++;e.push({t:"task",items:a});continue}if(Z.test(i)){let a=[];for(;t<s.length&&!q.test(s[t])&&(r=Z.exec(s[t]));)a.push([{t:"paragraph",c:b(r[1])}]),t++;e.push({t:"bullet",items:a});continue}if(r=V.exec(i)){let a=Math.max(1,parseInt(r[1],10)||1),o=[];for(;t<s.length&&(r=V.exec(s[t]));)o.push([{t:"paragraph",c:b(r[2])}]),t++;e.push({t:"ordered",start:a,items:o});continue}if(ee.test(i)&&t+1<s.length&&_e.test(s[t+1])){let a=te(i).map(b);t+=2;let o=[];for(;t<s.length&&ee.test(s[t]);)o.push(te(s[t]).map(b)),t++;e.push({t:"table",header:a,rows:o});continue}e.push({t:"paragraph",c:b(i)}),t++}return e}var je=/^https?:\/\/[^\s<>()[\]]+/,ve=/^\[([^\]\n]{1,300})\]\((https?:\/\/[^\s)]{1,2000})\)/,we=/[.,;:!?'"]+$/;function Se(s,n){return n<=0?!0:/[\s([{"'“‘>:—–-]/.test(s[n-1])}function Te(s,n){return n>=s.length?!0:/[\s)\]}"'”’.,;:!?—–-]/.test(s[n])}function Ae(s,n,e){let t=n;for(;t<s.length;){let i=s.indexOf(e,t);if(i<0)return-1;let r=s[i-1],a=s[i+e.length],o=e.length===1&&(r===e||a===e);if(i>n&&r&&!/\s/.test(r)&&!o&&Te(s,i+e.length))return i;t=i+1}return-1}function b(s){let n=String(s??""),e=[],t="",i=()=>{t&&(e.push({t:"text",v:t}),t="")},r=0;for(;r<n.length;){let a=n[r];if(a==="`"){let o=1;for(;n[r+o]==="`";)o++;let c="`".repeat(o),l=n.indexOf(c,r+o);if(l>r+o-1&&l!==-1){let u=n.slice(r+o,l);if(u.trim()){i(),e.push({t:"code",v:u.length>1&&u.startsWith(" ")&&u.endsWith(" ")?u.slice(1,-1):u}),r=l+o;continue}}t+=c,r+=o;continue}if(a==="["){let o=ve.exec(n.slice(r));if(o){i(),e.push({t:"link",href:o[2],c:b(o[1])}),r+=o[0].length;continue}}if((a==="*"||a==="_"||a==="~")&&Se(n,r)){let o=n[r+1]===a;if(a==="~"&&!o){t+=a,r++;continue}let c=o?a+a:a,l=r+c.length;if(l<n.length&&!/\s/.test(n[l])){let u=Ae(n,l,c);if(u>l){i();let d=b(n.slice(l,u));e.push(a==="~"?{t:"strike",c:d}:o?{t:"strong",c:d}:{t:"em",c:d}),r=u+c.length;continue}}}if(a==="h"&&(r===0||/[\s(<]/.test(n[r-1]))){let o=je.exec(n.slice(r));if(o){let c=o[0],l=we.exec(c);if(l&&(c=c.slice(0,-l[0].length)),c.length>8){i(),e.push({t:"link",href:c,c:[{t:"text",v:c}]}),r+=c.length;continue}}}t+=a,r++}return i(),e}function T(s,n=[]){let e=[];for(let t of s)switch(t.t){case"text":t.v&&e.push({type:"text",text:t.v,...n.length?{marks:n}:{}});break;case"br":e.push({type:"hardBreak"});break;case"code":e.push({type:"text",text:t.v,marks:[{type:"code"},...n.filter(i=>i.type==="link")]});break;case"strong":e.push(...T(t.c,R(n,{type:"strong"})));break;case"em":e.push(...T(t.c,R(n,{type:"em"})));break;case"strike":e.push(...T(t.c,R(n,{type:"strike"})));break;case"link":e.push(...T(t.c,R(n,{type:"link",attrs:{href:t.href}})));break}return e}function R(s,n){return s.some(e=>e.type===n.type)?s:[...s,n]}var K=0;function ne(){return K=(K+1)%1e6,`m${Date.now().toString(36)}-${K}`}var A=s=>{let n=T(s);return n.length?{type:"paragraph",content:n}:{type:"paragraph"}};function S(s,n){let e=[];for(let t of s)switch(t.t){case"paragraph":e.push(A(t.c));break;case"heading":if(n==="quote"||n==="item")e.push(A([{t:"strong",c:t.c}]));else{let i=T(t.c);i.length&&e.push({type:"heading",attrs:{level:t.level},content:i})}break;case"bullet":e.push({type:"bulletList",content:t.items.map(i=>({type:"listItem",content:L(S(i,"item"))}))});break;case"ordered":e.push({type:"orderedList",attrs:{order:t.start},content:t.items.map(i=>({type:"listItem",content:L(S(i,"item"))}))});break;case"task":n==="quote"||n==="item"?e.push({type:"bulletList",content:t.items.map(i=>({type:"listItem",content:[A([{t:"text",v:i.checked?"\u2611 ":"\u2610 "},...i.c])]}))}):e.push({type:"taskList",attrs:{localId:ne()},content:t.items.map(i=>({type:"taskItem",attrs:{localId:ne(),state:i.checked?"DONE":"TODO"},content:xe(T(i.c))}))});break;case"code":e.push({type:"codeBlock",attrs:t.lang?{language:t.lang}:{},...t.v?{content:[{type:"text",text:t.v}]}:{}});break;case"quote":n==="quote"||n==="item"?e.push(...S(t.c,n)):e.push({type:"blockquote",content:L(S(t.c,"quote"))});break;case"panel":n!=="doc"?e.push(...S(t.c,n)):e.push({type:"panel",attrs:{panelType:t.kind},content:L(S(t.c,"panel"))});break;case"rule":if(n==="quote"||n==="item")break;e.push({type:"rule"});break;case"table":{if(n!=="doc"){for(let r of[...t.header?[t.header]:[],...t.rows])e.push(A(Ne(r)));break}let i=[];t.header&&i.push({type:"tableRow",content:t.header.map(r=>({type:"tableHeader",attrs:{},content:[A(r)]}))});for(let r of t.rows)i.push({type:"tableRow",content:r.map(a=>({type:"tableCell",attrs:{},content:[A(a)]}))});i.length&&e.push({type:"table",attrs:{isNumberColumnEnabled:!1,layout:"default"},content:i});break}}return e}function L(s){return s.length?s:[{type:"paragraph"}]}function xe(s){return s.length?s:[{type:"text",text:" "}]}function Ne(s){let n=[];return s.forEach((e,t)=>{t&&n.push({t:"text",v:" \xB7 "}),n.push(...e)}),n}function B(s){return{type:"doc",version:1,content:String(s??"").split(`
|
|
5
|
+
`).map(n=>({type:"paragraph",...n?{content:[{type:"text",text:n}]}:{}}))}}function ae(s){try{let n=S(oe(s),"doc");return{type:"doc",version:1,content:n.length?n:[{type:"paragraph"}]}}catch{return B(s)}}function v(s){let n="";for(let e of s)switch(e.t){case"text":n+=e.v;break;case"br":n+=`
|
|
6
6
|
`;break;case"code":n+=`\`${e.v}\``;break;case"strong":n+=`**${v(e.c)}**`;break;case"em":n+=`_${v(e.c)}_`;break;case"strike":n+=`~~${v(e.c)}~~`;break;case"link":{let t=v(e.c);n+=!t||t===e.href?e.href:`[${t}](${e.href})`;break}}return n}function N(s){let n="";for(let e of s)switch(e.t){case"paragraph":n+=`${v(e.c)}
|
|
7
7
|
`;break;case"heading":n+=`${"#".repeat(Math.max(1,Math.min(ie,e.level)))} ${v(e.c)}
|
|
8
8
|
`;break;case"bullet":for(let t of e.items)n+=`- ${N(t).trim()}
|
|
@@ -18,7 +18,7 @@ ${se(N(e.c))}`;break;case"rule":n+=`---
|
|
|
18
18
|
`);for(let i of e.rows)n+=t(i);break}}return n}function Oe(s){return s.replace(/^\n+/,"").replace(/\s+$/,"")}function se(s){return s.replace(/\n$/,"").split(`
|
|
19
19
|
`).map(n=>n?`> ${n}`:">").join(`
|
|
20
20
|
`)+`
|
|
21
|
-
`}function x(s){let n=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"text":{let t={t:"text",v:String(e.text??"")},i=Array.isArray(e.marks)?e.marks:[];i.some(r=>r?.type==="code")&&(t={t:"code",v:String(e.text??"")});for(let r of i)!r||r.type==="code"||(r.type==="strong"?t={t:"strong",c:[t]}:r.type==="em"?t={t:"em",c:[t]}:r.type==="strike"?t={t:"strike",c:[t]}:r.type==="link"&&r.attrs?.href&&(t={t:"link",href:String(r.attrs.href),c:[t]}));n.push(t);break}case"hardBreak":n.push({t:"br"});break;case"mention":n.push({t:"text",v:String(e.attrs?.text||(e.attrs?.id?`@${e.attrs.id}`:"@someone"))});break;case"emoji":n.push({t:"text",v:String(e.attrs?.text||e.attrs?.shortName||"")});break;case"status":n.push({t:"text",v:String(e.attrs?.text||"")});break;case"date":n.push({t:"text",v:e.attrs?.timestamp?new Date(Number(e.attrs.timestamp)).toISOString().slice(0,10):""});break;case"inlineCard":{let t=String(e.attrs?.url||"");t&&n.push({t:"link",href:t,c:[{t:"text",v:t}]});break}default:Array.isArray(e.content)?n.push(...x(e.content)):typeof e.text=="string"&&n.push({t:"text",v:e.text})}return n}function _(s){let n=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"paragraph":n.push({t:"paragraph",c:x(e.content)});break;case"heading":n.push({t:"heading",level:Number(e.attrs?.level)||1,c:x(e.content)});break;case"bulletList":n.push({t:"bullet",items:(e.content||[]).map(t=>_(t?.content))});break;case"orderedList":n.push({t:"ordered",start:Number(e.attrs?.order)||1,items:(e.content||[]).map(t=>_(t?.content))});break;case"taskList":n.push({t:"task",items:(e.content||[]).map(t=>({checked:t?.attrs?.state==="DONE",c:x(t?.content)}))});break;case"decisionList":n.push({t:"bullet",items:(e.content||[]).map(t=>[{t:"paragraph",c:x(t?.content)}])});break;case"codeBlock":n.push({t:"code",lang:String(e.attrs?.language||""),v:(e.content||[]).map(t=>String(t?.text??"")).join("")});break;case"blockquote":n.push({t:"quote",c:_(e.content)});break;case"panel":{let t=String(e.attrs?.panelType||"info");n.push({t:"panel",kind:t in re?t:"info",c:_(e.content)});break}case"rule":n.push({t:"rule"});break;case"table":{let t=(e.content||[]).filter(o=>o?.type==="tableRow"),i=o=>(o.content||[]).map(c=>{let l=_(c?.content);return b(N(l).trim().replace(/\n+/g," "))}),r=o=>(o.content||[]).length>0&&(o.content||[]).every(c=>c?.type==="tableHeader"),a=t.length&&r(t[0])?i(t[0]):null;n.push({t:"table",header:a,rows:t.slice(a?1:0).map(i)});break}case"mediaSingle":case"mediaGroup":case"media":case"mediaInline":n.push({t:"paragraph",c:[{t:"text",v:"[attachment]"}]});break;case"expand":case"nestedExpand":{let t=String(e.attrs?.title||"").trim();t&&n.push({t:"paragraph",c:[{t:"strong",c:[{t:"text",v:t}]}]}),n.push(..._(e.content));break}default:{let t=Array.isArray(e.content)?e.content:[];t.some(i=>i?.type==="text"||i?.type==="hardBreak")?n.push({t:"paragraph",c:x(t)}):t.length?n.push(..._(t)):typeof e.text=="string"&&e.text&&n.push({t:"paragraph",c:[{t:"text",v:e.text}]})}}return n}function $e(s){try{if(Array.isArray(s))return _(s);if(s&&typeof s=="object"){let n=s;return n.type==="doc"||!n.type&&Array.isArray(n.content)?_(n.content):_([n])}if(typeof s=="string")return oe(s)}catch{}return[]}function B(s){try{return Oe(N($e(s)))}catch{return""}}var Ce=Re(import.meta.url);function qe(){if(process.env.MCP_JIRA_PATH)return process.env.MCP_JIRA_PATH;try{return Ce.resolve("@zibby/mcp-jira/index.js")}catch{return null}}function Pe(s){return ae(s)}async function U(s,n){try{return await s(Pe(n))}catch(e){let t=Number(e?.status);if(!(t>=400&&t<500))throw e;return console.warn(`[jira] rich body rejected (${t}) \u2014 retrying as plain text: ${String(e?.message||e).slice(0,200)}`),await s(M(n))}}function O(s){return String(s||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function D(s){return O(s).replace(/[a-z0-9]+/g,"")}function C(s,n){let e=O(s),t=O(n);if(!e||!t)return 0;if(e===t)return 1;if(e.length===1||t.length===1)return e===t?1:0;let i=u=>{let d=new Map;for(let m=0;m<u.length-1;m++){let g=u.slice(m,m+2);d.set(g,(d.get(g)||0)+1)}return d},r=i(e),a=i(t),o=0,c=0,l=0;for(let u of r.values())c+=u;for(let u of a.values())l+=u;for(let[u,d]of r.entries()){let m=a.get(u)||0;o+=Math.min(d,m)}return 2*o/Math.max(1,c+l)}function w(s){return String(s||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function Ke(s,n=[]){let e=Array.isArray(n)?n:[];if(e.length===0)return{requested:s||null,resolved:null,strategy:"none"};let t=e.filter(o=>!o.subtask),i=t.length>0?t:e,r=w(s);if(r){let o=i.find(u=>w(u.name)===r);if(o)return{requested:s,resolved:o,strategy:"exact"};let c={task:["task","\u4EFB\u52A1","\u4E8B\u9879","to do","todo"],story:["story","\u7528\u6237\u6545\u4E8B","\u9700\u6C42"],bug:["bug","\u7F3A\u9677","\u95EE\u9898"],improvement:["improvement","\u4F18\u5316","\u6539\u8FDB"],epic:["epic","\u53F2\u8BD7"]};for(let u of Object.values(c)){if(!u.some(m=>w(m)===r))continue;let d=i.find(m=>u.some(g=>w(g)===w(m.name)));if(d)return{requested:s,resolved:d,strategy:"alias"}}let l=i.map(u=>({t:u,score:C(s,u.name)})).sort((u,d)=>d.score-u.score);if(l[0]&&l[0].score>=.5)return{requested:s,resolved:l[0].t,strategy:"fuzzy"}}let a=["task","story","bug","improvement","epic"];for(let o of a){let c=i.find(l=>w(l.name)===o);if(c)return{requested:s||null,resolved:c,strategy:"default-preferred"}}return{requested:s||null,resolved:i[0],strategy:"default-first"}}async function ce(s){let n=`projectKeys=${encodeURIComponent(s)}&expand=projects.issuetypes`,e=await f(`/rest/api/3/issue/createmeta?${n}`),t=Array.isArray(e?.projects)?e.projects:[],r=t.find(o=>String(o?.key||"").toUpperCase()===String(s||"").toUpperCase())||t[0]||null;return(Array.isArray(r?.issuetypes)?r.issuetypes:[]).map(o=>({id:o.id,name:o.name,subtask:!!o.subtask,description:o.description||null}))}async function le(s,n){if(!s)throw new Error("projectKey is required");let e="sprint is not EMPTY";n==="active"?e="sprint in openSprints()":n==="closed"?e="sprint in closedSprints()":n==="future"&&(e="sprint in futureSprints()");let t=`project = ${s} AND ${e} ORDER BY updated DESC`,i=`jql=${encodeURIComponent(t)}&maxResults=100&fields=customfield_10020`,r=await f(`/rest/api/3/search/jql?${i}`),a=new Map;for(let o of r.issues||[])for(let c of o.fields?.customfield_10020||[])c&&!a.has(c.id)&&a.set(c.id,{id:c.id,name:c.name,state:c.state,boardId:c.boardId||null,startDate:c.startDate||null,endDate:c.endDate||null,goal:c.goal||null});return[...a.values()].sort((o,c)=>{let l={active:0,future:1,closed:2},u=(l[o.state]??3)-(l[c.state]??3);return u!==0?u:String(c.startDate||"").localeCompare(String(o.startDate||""))})}function Je(s,{sprintId:n,sprintName:e,target:t}={}){let i=Array.isArray(s)?s:[];if(!i.length)return{sprint:null,selectedBy:"none"};if(n!=null&&String(n).trim()!=="")return{sprint:i.find(o=>String(o.id)===String(n))||null,selectedBy:"id"};if(e&&String(e).trim()){let a=String(e).trim(),o=i.find(l=>String(l.name||"").toLowerCase()===a.toLowerCase());if(o)return{sprint:o,selectedBy:"name-exact"};let c=i.map(l=>({s:l,score:C(a,l.name||"")})).sort((l,u)=>u.score-l.score);return c[0]&&c[0].score>=.5?{sprint:c[0].s,selectedBy:"name-fuzzy"}:{sprint:null,selectedBy:"name-none"}}let r=String(t||"current").trim().toLowerCase();return r==="active"||r==="current"||r==="latest"?{sprint:i[0],selectedBy:r}:{sprint:i[0],selectedBy:"default"}}function Me(s,n){let e=s?.fields?.customfield_10020;return Array.isArray(e)?e.some(t=>String(t?.id)===String(n)):!1}async function Be({issueKey:s,projectKey:n,sprintId:e,attempts:t=3,delayMs:i=450}){let r=[];for(let a=0;a<t;a++){try{let o=`project = ${n} AND key = ${s} AND sprint = ${e}`,c=`jql=${encodeURIComponent(o)}&maxResults=1&fields=key,status`,l=await f(`/rest/api/3/search/jql?${c}`);if(Number(l?.total||0)>0)return r.push({attempt:a+1,jql:!0,issueField:null}),{ok:!0,method:"jql",traces:r};let d=await f(`/rest/api/3/issue/${s}?fields=customfield_10020,status`),m=Me(d,e);if(r.push({attempt:a+1,jql:!1,issueField:m}),m)return{ok:!0,method:"issue_field",traces:r}}catch(o){r.push({attempt:a+1,error:String(o?.message||o)})}a<t-1&&await new Promise(o=>setTimeout(o,i))}return{ok:!1,method:"none",traces:r}}async function G({issueKey:s,projectKey:n,sprintId:e,sprintName:t,target:i}){if(!s)return{ok:!1,error:"issueKey is required"};let r=n;if(!r&&(r=(await f(`/rest/api/3/issue/${s}?fields=project`))?.fields?.project?.key||null,!r))return{ok:!1,error:`Could not resolve project for ${s}`};let a=await le(r,"active");if(!a.length)return{ok:!1,error:`No assignable active sprint found for project ${r}`};let{sprint:o,selectedBy:c}=Je(a,{sprintId:e,sprintName:t,target:i});if(!o)return{ok:!1,error:`No matching sprint found in ${r}`,requested:{sprintId:e??null,sprintName:t??null,target:i??"current"},availableSprints:a.map(d=>({id:d.id,name:d.name,state:d.state}))};await f(`/rest/api/3/issue/${s}`,{method:"PUT",body:{fields:{customfield_10020:Number(o.id)}}});let l=await Be({issueKey:s,projectKey:r,sprintId:o.id}),u=l.ok;return{ok:u,issueKey:s,projectKey:r,sprintId:o.id,sprintName:o.name,selectedBy:c,verifiedBy:l.method,verified:u,verificationTrace:l.traces,warning:u?null:`Sprint assignment attempted but verification did not find ${s} in sprint ${o.id}`}}async function Ue(){let s=process.env.JIRA_API_TOKEN,n=process.env.JIRA_EMAIL,e=process.env.JIRA_BASE_URL;if(s&&n&&e)return{authType:"token",apiToken:s,email:n,baseUrl:e};let t=await Ee("jira");return t?.cloudId?{authType:"oauth",accessToken:t.token,cloudId:t.cloudId}:{authType:"token",apiToken:t?.token,email:n||t?.email||"",baseUrl:e||t?.instanceUrl||t?.baseUrl||process.env.ATLASSIAN_INSTANCE_URL||""}}function De(s,n,e={}){let t=s||{},i={Accept:"application/json",...e.body?{"Content-Type":"application/json"}:{},...e.headers};if(t.authType==="token"){let r=String(t.baseUrl||t.instanceUrl||"").trim().replace(/\/+$/,""),a=t.apiToken||t.accessToken||"";if(!r)throw new Error("Jira token connection has no base URL \u2014 reconnect Jira with its instance URL, or set JIRA_BASE_URL.");if(!t.email)throw new Error("Jira token connection has no account email \u2014 reconnect Jira with the account email, or set JIRA_EMAIL.");if(!a)throw new Error("Jira token connection has no API token \u2014 reconnect Jira, or set JIRA_API_TOKEN.");return{url:`${r}${n}`,headers:{Authorization:"Basic "+Buffer.from(`${t.email}:${a}`).toString("base64"),...i}}}if(typeof t.accessToken!="string"||!t.accessToken)throw new Error(`Invalid jira token type: ${typeof t.accessToken}`);if(!t.cloudId)throw new Error("Invalid jira cloudId: missing");return{url:`https://api.atlassian.com/ex/jira/${t.cloudId}${n}`,headers:{Authorization:`Bearer ${t.accessToken}`,...i}}}async function f(s,n={}){let e=async()=>{let t=await Ue(),{url:i,headers:r}=De(t,s,n),a=await Q(i,{method:n.method||"GET",headers:r,body:n.body?JSON.stringify(n.body):void 0,signal:n.signal},{kind:/\/search\b/.test(String(s))?"job":"api",what:`Jira ${n.method||"GET"} ${s}`}),o=()=>a.text().catch(l=>{if(q(l)||n.signal?.aborted)throw l;return""});if(!a.ok){let l=await o(),u=new Error(`Jira API ${a.status}: ${l.slice(0,300)}`);throw u.status=a.status,u}let c=await o();if(!c||!c.trim())return{};try{return JSON.parse(c)}catch{return{raw:c}}};try{return await e()}catch(t){if(n.signal?.aborted)throw t;let i=String(t?.message||t||"").toLowerCase();if(!(i.includes("token")||i.includes("401")||i.includes("403")||i.includes("substring")))throw t;return Le("jira"),e()}}var $={id:"jira",callsBackend:!0,serverName:"jira",allowedTools:["mcp__jira__*"],requiresIntegration:Y.JIRA,envKeys:["ATLASSIAN_ACCESS_TOKEN","ATLASSIAN_CLOUD_ID"],description:"Zibby Jira MCP Server (OAuth Bearer)",promptFragment:`## Jira
|
|
21
|
+
`}function x(s){let n=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"text":{let t={t:"text",v:String(e.text??"")},i=Array.isArray(e.marks)?e.marks:[];i.some(r=>r?.type==="code")&&(t={t:"code",v:String(e.text??"")});for(let r of i)!r||r.type==="code"||(r.type==="strong"?t={t:"strong",c:[t]}:r.type==="em"?t={t:"em",c:[t]}:r.type==="strike"?t={t:"strike",c:[t]}:r.type==="link"&&r.attrs?.href&&(t={t:"link",href:String(r.attrs.href),c:[t]}));n.push(t);break}case"hardBreak":n.push({t:"br"});break;case"mention":n.push({t:"text",v:String(e.attrs?.text||(e.attrs?.id?`@${e.attrs.id}`:"@someone"))});break;case"emoji":n.push({t:"text",v:String(e.attrs?.text||e.attrs?.shortName||"")});break;case"status":n.push({t:"text",v:String(e.attrs?.text||"")});break;case"date":n.push({t:"text",v:e.attrs?.timestamp?new Date(Number(e.attrs.timestamp)).toISOString().slice(0,10):""});break;case"inlineCard":{let t=String(e.attrs?.url||"");t&&n.push({t:"link",href:t,c:[{t:"text",v:t}]});break}default:Array.isArray(e.content)?n.push(...x(e.content)):typeof e.text=="string"&&n.push({t:"text",v:e.text})}return n}function _(s){let n=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"paragraph":n.push({t:"paragraph",c:x(e.content)});break;case"heading":n.push({t:"heading",level:Number(e.attrs?.level)||1,c:x(e.content)});break;case"bulletList":n.push({t:"bullet",items:(e.content||[]).map(t=>_(t?.content))});break;case"orderedList":n.push({t:"ordered",start:Number(e.attrs?.order)||1,items:(e.content||[]).map(t=>_(t?.content))});break;case"taskList":n.push({t:"task",items:(e.content||[]).map(t=>({checked:t?.attrs?.state==="DONE",c:x(t?.content)}))});break;case"decisionList":n.push({t:"bullet",items:(e.content||[]).map(t=>[{t:"paragraph",c:x(t?.content)}])});break;case"codeBlock":n.push({t:"code",lang:String(e.attrs?.language||""),v:(e.content||[]).map(t=>String(t?.text??"")).join("")});break;case"blockquote":n.push({t:"quote",c:_(e.content)});break;case"panel":{let t=String(e.attrs?.panelType||"info");n.push({t:"panel",kind:t in re?t:"info",c:_(e.content)});break}case"rule":n.push({t:"rule"});break;case"table":{let t=(e.content||[]).filter(o=>o?.type==="tableRow"),i=o=>(o.content||[]).map(c=>{let l=_(c?.content);return b(N(l).trim().replace(/\n+/g," "))}),r=o=>(o.content||[]).length>0&&(o.content||[]).every(c=>c?.type==="tableHeader"),a=t.length&&r(t[0])?i(t[0]):null;n.push({t:"table",header:a,rows:t.slice(a?1:0).map(i)});break}case"mediaSingle":case"mediaGroup":case"media":case"mediaInline":n.push({t:"paragraph",c:[{t:"text",v:"[attachment]"}]});break;case"expand":case"nestedExpand":{let t=String(e.attrs?.title||"").trim();t&&n.push({t:"paragraph",c:[{t:"strong",c:[{t:"text",v:t}]}]}),n.push(..._(e.content));break}default:{let t=Array.isArray(e.content)?e.content:[];t.some(i=>i?.type==="text"||i?.type==="hardBreak")?n.push({t:"paragraph",c:x(t)}):t.length?n.push(..._(t)):typeof e.text=="string"&&e.text&&n.push({t:"paragraph",c:[{t:"text",v:e.text}]})}}return n}function $e(s){try{if(Array.isArray(s))return _(s);if(s&&typeof s=="object"){let n=s;return n.type==="doc"||!n.type&&Array.isArray(n.content)?_(n.content):_([n])}if(typeof s=="string")return oe(s)}catch{}return[]}function J(s){try{return Oe(N($e(s)))}catch{return""}}function Ke(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let s=Re(Ce(import.meta.url)),n=Le(s,"..","bin","mcp-skill.mjs");return Ee(n)?n:null}function Me(s){return ae(s)}async function U(s,n){try{return await s(Me(n))}catch(e){let t=Number(e?.status);if(!(t>=400&&t<500))throw e;return console.warn(`[jira] rich body rejected (${t}) \u2014 retrying as plain text: ${String(e?.message||e).slice(0,200)}`),await s(B(n))}}function O(s){return String(s||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function D(s){return O(s).replace(/[a-z0-9]+/g,"")}function C(s,n){let e=O(s),t=O(n);if(!e||!t)return 0;if(e===t)return 1;if(e.length===1||t.length===1)return e===t?1:0;let i=u=>{let d=new Map;for(let f=0;f<u.length-1;f++){let y=u.slice(f,f+2);d.set(y,(d.get(y)||0)+1)}return d},r=i(e),a=i(t),o=0,c=0,l=0;for(let u of r.values())c+=u;for(let u of a.values())l+=u;for(let[u,d]of r.entries()){let f=a.get(u)||0;o+=Math.min(d,f)}return 2*o/Math.max(1,c+l)}function w(s){return String(s||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function Be(s,n=[]){let e=Array.isArray(n)?n:[];if(e.length===0)return{requested:s||null,resolved:null,strategy:"none"};let t=e.filter(o=>!o.subtask),i=t.length>0?t:e,r=w(s);if(r){let o=i.find(u=>w(u.name)===r);if(o)return{requested:s,resolved:o,strategy:"exact"};let c={task:["task","\u4EFB\u52A1","\u4E8B\u9879","to do","todo"],story:["story","\u7528\u6237\u6545\u4E8B","\u9700\u6C42"],bug:["bug","\u7F3A\u9677","\u95EE\u9898"],improvement:["improvement","\u4F18\u5316","\u6539\u8FDB"],epic:["epic","\u53F2\u8BD7"]};for(let u of Object.values(c)){if(!u.some(f=>w(f)===r))continue;let d=i.find(f=>u.some(y=>w(y)===w(f.name)));if(d)return{requested:s,resolved:d,strategy:"alias"}}let l=i.map(u=>({t:u,score:C(s,u.name)})).sort((u,d)=>d.score-u.score);if(l[0]&&l[0].score>=.5)return{requested:s,resolved:l[0].t,strategy:"fuzzy"}}let a=["task","story","bug","improvement","epic"];for(let o of a){let c=i.find(l=>w(l.name)===o);if(c)return{requested:s||null,resolved:c,strategy:"default-preferred"}}return{requested:s||null,resolved:i[0],strategy:"default-first"}}async function ce(s){let n=`projectKeys=${encodeURIComponent(s)}&expand=projects.issuetypes`,e=await m(`/rest/api/3/issue/createmeta?${n}`),t=Array.isArray(e?.projects)?e.projects:[],r=t.find(o=>String(o?.key||"").toUpperCase()===String(s||"").toUpperCase())||t[0]||null;return(Array.isArray(r?.issuetypes)?r.issuetypes:[]).map(o=>({id:o.id,name:o.name,subtask:!!o.subtask,description:o.description||null}))}async function le(s,n){if(!s)throw new Error("projectKey is required");let e="sprint is not EMPTY";n==="active"?e="sprint in openSprints()":n==="closed"?e="sprint in closedSprints()":n==="future"&&(e="sprint in futureSprints()");let t=`project = ${s} AND ${e} ORDER BY updated DESC`,i=`jql=${encodeURIComponent(t)}&maxResults=100&fields=customfield_10020`,r=await m(`/rest/api/3/search/jql?${i}`),a=new Map;for(let o of r.issues||[])for(let c of o.fields?.customfield_10020||[])c&&!a.has(c.id)&&a.set(c.id,{id:c.id,name:c.name,state:c.state,boardId:c.boardId||null,startDate:c.startDate||null,endDate:c.endDate||null,goal:c.goal||null});return[...a.values()].sort((o,c)=>{let l={active:0,future:1,closed:2},u=(l[o.state]??3)-(l[c.state]??3);return u!==0?u:String(c.startDate||"").localeCompare(String(o.startDate||""))})}function Je(s,{sprintId:n,sprintName:e,target:t}={}){let i=Array.isArray(s)?s:[];if(!i.length)return{sprint:null,selectedBy:"none"};if(n!=null&&String(n).trim()!=="")return{sprint:i.find(o=>String(o.id)===String(n))||null,selectedBy:"id"};if(e&&String(e).trim()){let a=String(e).trim(),o=i.find(l=>String(l.name||"").toLowerCase()===a.toLowerCase());if(o)return{sprint:o,selectedBy:"name-exact"};let c=i.map(l=>({s:l,score:C(a,l.name||"")})).sort((l,u)=>u.score-l.score);return c[0]&&c[0].score>=.5?{sprint:c[0].s,selectedBy:"name-fuzzy"}:{sprint:null,selectedBy:"name-none"}}let r=String(t||"current").trim().toLowerCase();return r==="active"||r==="current"||r==="latest"?{sprint:i[0],selectedBy:r}:{sprint:i[0],selectedBy:"default"}}function Ue(s,n){let e=s?.fields?.customfield_10020;return Array.isArray(e)?e.some(t=>String(t?.id)===String(n)):!1}async function De({issueKey:s,projectKey:n,sprintId:e,attempts:t=3,delayMs:i=450}){let r=[];for(let a=0;a<t;a++){try{let o=`project = ${n} AND key = ${s} AND sprint = ${e}`,c=`jql=${encodeURIComponent(o)}&maxResults=1&fields=key,status`,l=await m(`/rest/api/3/search/jql?${c}`);if(Number(l?.total||0)>0)return r.push({attempt:a+1,jql:!0,issueField:null}),{ok:!0,method:"jql",traces:r};let d=await m(`/rest/api/3/issue/${s}?fields=customfield_10020,status`),f=Ue(d,e);if(r.push({attempt:a+1,jql:!1,issueField:f}),f)return{ok:!0,method:"issue_field",traces:r}}catch(o){r.push({attempt:a+1,error:String(o?.message||o)})}a<t-1&&await new Promise(o=>setTimeout(o,i))}return{ok:!1,method:"none",traces:r}}async function G({issueKey:s,projectKey:n,sprintId:e,sprintName:t,target:i}){if(!s)return{ok:!1,error:"issueKey is required"};let r=n;if(!r&&(r=(await m(`/rest/api/3/issue/${s}?fields=project`))?.fields?.project?.key||null,!r))return{ok:!1,error:`Could not resolve project for ${s}`};let a=await le(r,"active");if(!a.length)return{ok:!1,error:`No assignable active sprint found for project ${r}`};let{sprint:o,selectedBy:c}=Je(a,{sprintId:e,sprintName:t,target:i});if(!o)return{ok:!1,error:`No matching sprint found in ${r}`,requested:{sprintId:e??null,sprintName:t??null,target:i??"current"},availableSprints:a.map(d=>({id:d.id,name:d.name,state:d.state}))};await m(`/rest/api/3/issue/${s}`,{method:"PUT",body:{fields:{customfield_10020:Number(o.id)}}});let l=await De({issueKey:s,projectKey:r,sprintId:o.id}),u=l.ok;return{ok:u,issueKey:s,projectKey:r,sprintId:o.id,sprintName:o.name,selectedBy:c,verifiedBy:l.method,verified:u,verificationTrace:l.traces,warning:u?null:`Sprint assignment attempted but verification did not find ${s} in sprint ${o.id}`}}async function Ge(){let s=process.env.JIRA_API_TOKEN,n=process.env.JIRA_EMAIL,e=process.env.JIRA_BASE_URL;if(s&&n&&e)return{authType:"token",apiToken:s,email:n,baseUrl:e};let t=await Pe("jira");return(t?.authType?t.authType!=="token":!!t?.cloudId)?{authType:"oauth",accessToken:t.token,cloudId:t.cloudId}:{authType:"token",apiToken:t?.token,email:n||t?.email||"",baseUrl:e||t?.baseUrl||t?.instanceUrl||process.env.ATLASSIAN_INSTANCE_URL||""}}function Fe(s,n,e={}){let t=s||{},i={Accept:"application/json",...e.body?{"Content-Type":"application/json"}:{},...e.headers};if(t.authType==="token"){let r=String(t.baseUrl||t.instanceUrl||"").trim().replace(/\/+$/,""),a=t.apiToken||t.accessToken||"";if(!r)throw new Error("Jira token connection has no base URL \u2014 reconnect Jira with its instance URL, or set JIRA_BASE_URL.");if(!t.email)throw new Error("Jira token connection has no account email \u2014 reconnect Jira with the account email, or set JIRA_EMAIL.");if(!a)throw new Error("Jira token connection has no API token \u2014 reconnect Jira, or set JIRA_API_TOKEN.");return{url:`${r}${n}`,headers:{Authorization:"Basic "+Buffer.from(`${t.email}:${a}`).toString("base64"),...i}}}if(typeof t.accessToken!="string"||!t.accessToken)throw new Error(`Invalid jira token type: ${typeof t.accessToken}`);if(!t.cloudId)throw new Error("Invalid jira cloudId: missing");return{url:`https://api.atlassian.com/ex/jira/${t.cloudId}${n}`,headers:{Authorization:`Bearer ${t.accessToken}`,...i}}}async function m(s,n={}){let e=async()=>{let t=await Ge(),{url:i,headers:r}=Fe(t,s,n),a=await Q(i,{method:n.method||"GET",headers:r,body:n.body?JSON.stringify(n.body):void 0,signal:n.signal},{kind:/\/search\b/.test(String(s))?"job":"api",what:`Jira ${n.method||"GET"} ${s}`}),o=()=>a.text().catch(l=>{if(P(l)||n.signal?.aborted)throw l;return""});if(!a.ok){let l=await o(),u=new Error(`Jira API ${a.status}: ${l.slice(0,300)}`);throw u.status=a.status,u}let c=await o();if(!c||!c.trim())return{};try{return JSON.parse(c)}catch{return{raw:c}}};try{return await e()}catch(t){if(n.signal?.aborted)throw t;let i=String(t?.message||t||"").toLowerCase();if(!(i.includes("token")||i.includes("401")||i.includes("403")||i.includes("substring")))throw t;return qe("jira"),e()}}var $={id:"jira",callsBackend:!0,serverName:"jira",allowedTools:["mcp__jira__*"],requiresIntegration:Y.JIRA,envKeys:["ATLASSIAN_ACCESS_TOKEN","ATLASSIAN_CLOUD_ID"],description:"Zibby Jira MCP Server (OAuth Bearer)",promptFragment:`## Jira
|
|
22
22
|
You have direct access to the user's Jira. Use these tools proactively:
|
|
23
23
|
|
|
24
24
|
### Issue tools
|
|
@@ -68,6 +68,6 @@ When user asks to move/transition ticket status:
|
|
|
68
68
|
3. Pick the correct transition from returned list (match by "to" status name, not guesswork), then call jira_transition_issue with transitionId.
|
|
69
69
|
4. Call jira_get_issue(issueKey) to verify final status before claiming success.
|
|
70
70
|
5. If target wording differs (e.g. \u5DF2\u7ECF\u9A8C\u6536 vs \u5DF2\u9A8C\u6536), try toStatus first; only ask user to confirm when no reasonable match exists.
|
|
71
|
-
6. IMPORTANT: When target is clear, complete transition + verification in SAME turn. Do NOT stop after listing options.`,resolve(){let s=qe();if(!s)return null;let n={};for(let e of this.envKeys)process.env[e]&&(n[e]=process.env[e]);process.env.ATLASSIAN_INSTANCE_URL&&(n.ATLASSIAN_INSTANCE_URL=process.env.ATLASSIAN_INSTANCE_URL);for(let e of["JIRA_API_TOKEN","JIRA_EMAIL","JIRA_BASE_URL"])process.env[e]&&(n[e]=process.env[e]);return{command:"node",args:[s],env:n,description:this.description}},async handleToolCall(s,n){try{switch(s){case"jira_list_projects":{let e=await f("/rest/api/3/project"),t=(Array.isArray(e)?e:[]).map(i=>({id:i.id,key:i.key,name:i.name,style:i.style}));return JSON.stringify({count:t.length,projects:t})}case"jira_list_statuses":{let{projectKey:e}=n||{};if(e){let r=await f(`/rest/api/3/project/${encodeURIComponent(e)}/statuses`),a=Array.isArray(r)?r:[],o=new Map;for(let l of a)for(let u of l.statuses||[])u?.id&&(o.has(u.id)||o.set(u.id,{id:u.id,name:u.name,category:u.statusCategory?.name||null}));let c=[...o.values()].sort((l,u)=>String(l.name).localeCompare(String(u.name)));return JSON.stringify({scope:"project",projectKey:e,count:c.length,statuses:c})}let t=await f("/rest/api/3/status"),i=(Array.isArray(t)?t:[]).map(r=>({id:r.id,name:r.name,category:r.statusCategory?.name||null})).sort((r,a)=>String(r.name).localeCompare(String(a.name)));return JSON.stringify({scope:"global",count:i.length,statuses:i})}case"jira_list_issue_types":{let{projectKey:e}=n||{};if(!e)return JSON.stringify({error:"projectKey is required"});let t=await ce(e);return JSON.stringify({projectKey:e,count:t.length,issueTypes:t})}case"jira_search":{let e=n.jql||"",t=n.maxResults||20;e.replace(/\s*ORDER\s+BY\s+.*/i,"").trim()||(e=`created >= -365d ${e}`.trim());let r=`jql=${encodeURIComponent(e)}&maxResults=${t}&fields=summary,status,assignee,priority,updated,issuetype,project`,o=((await f(`/rest/api/3/search/jql?${r}`)).issues||[]).map(c=>({key:c.key,project:c.fields?.project?.key,summary:c.fields?.summary,status:c.fields?.status?.name,assignee:c.fields?.assignee?.displayName||"Unassigned",priority:c.fields?.priority?.name,type:c.fields?.issuetype?.name}));return JSON.stringify({count:o.length,issues:o})}case"jira_get_issue":{let e=n.issueKey;if(!e)return JSON.stringify({error:"issueKey is required"});let t=await f(`/rest/api/3/issue/${e}`);return JSON.stringify({key:t.key,project:t.fields?.project?.key,summary:t.fields?.summary,description:t.fields?.description?B(t.fields.description):t.fields?.description??null,status:t.fields?.status?.name,assignee:t.fields?.assignee?.displayName||"Unassigned",priority:t.fields?.priority?.name,type:t.fields?.issuetype?.name,labels:t.fields?.labels,created:t.fields?.created,updated:t.fields?.updated})}case"jira_create_issue":{let{projectKey:e,summary:t,issueType:i,description:r,priority:a,labels:o,assigneeId:c,moveToSprint:l,moveToActiveSprint:u,sprintId:d,sprintName:m,target:g}=n;if(!e||!t)return JSON.stringify({error:"projectKey and summary are required"});let p={requested:i||null,resolved:null,strategy:"none"},h=[];try{h=await ce(e),p=Ke(i,h)}catch{}let y={project:{key:e},summary:t,issuetype:p?.resolved?.id?{id:p.resolved.id}:{name:i||"Task"}};a&&(y.priority={name:a}),o?.length&&(y.labels=o),c&&(y.assignee={id:c});let I=r?await U(j=>f("/rest/api/3/issue",{method:"POST",body:{fields:{...y,description:j}}}),r):await f("/rest/api/3/issue",{method:"POST",body:{fields:y}}),k={ok:!0,key:I.key,id:I.id,self:I.self};return p?.resolved&&(k.issueType=p.resolved.name,k.issueTypeResolution=p.strategy,p.strategy!=="exact"&&p.requested&&w(p.requested)!==w(p.resolved.name)&&(k.issueTypeWarning=`Requested "${p.requested}" is not available in ${e}; used "${p.resolved.name}" instead.`)),h.length>0&&(k.availableIssueTypes=h.map(j=>j.name)),(l||u)&&(k.sprintMove=await G({issueKey:I.key,projectKey:e,sprintId:d,sprintName:m,target:g})),JSON.stringify(k)}case"jira_list_sprints":{let{projectKey:e,state:t}=n,i=await le(e,t);return JSON.stringify({count:i.length,sprints:i})}case"jira_move_to_active_sprint":{let{issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a}=n||{},o=await G({issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a||"current"});return JSON.stringify(o)}case"jira_move_issue_to_sprint":{let{issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a}=n||{},o=await G({issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a});return JSON.stringify(o)}case"jira_get_sprint_issues":{let{sprintName:e,sprintId:t,projectKey:i,status:r,maxResults:a}=n;if(!e&&!t)return JSON.stringify({error:"sprintName or sprintId is required"});let o=a||50,c=t?`sprint = ${t}`:`sprint = "${e}"`,l=i?`project = ${i} AND `:"",u=r?` AND status = "${r}"`:"",d=`${l}${c}${u} ORDER BY status ASC, priority DESC`,m=`jql=${encodeURIComponent(d)}&maxResults=${o}&fields=summary,status,assignee,priority,issuetype,project`,g=await f(`/rest/api/3/search/jql?${m}`),p=(g.issues||[]).map(y=>({key:y.key,project:y.fields?.project?.key,summary:y.fields?.summary,status:y.fields?.status?.name,assignee:y.fields?.assignee?.displayName||"Unassigned",priority:y.fields?.priority?.name,type:y.fields?.issuetype?.name})),h={};for(let y of p)h[y.status]=(h[y.status]||0)+1;return JSON.stringify({count:p.length,total:g.total||p.length,statusCounts:h,issues:p})}case"jira_get_comments":{let{issueKey:e,maxResults:t}=n;if(!e)return JSON.stringify({error:"issueKey is required"});let r=await f(`/rest/api/3/issue/${e}/comment?maxResults=${t||50}&orderBy=-created`),a=(r.comments||[]).map(o=>{let c=o.body?.content?B(o.body):"";return{id:o.id,author:o.author?.displayName||"Unknown",body:c,created:o.created,updated:o.updated}});return JSON.stringify({count:a.length,total:r.total||a.length,comments:a})}case"jira_add_comment":{let{issueKey:e,body:t}=n;return!e||!t?JSON.stringify({error:"issueKey and body are required"}):(await U(i=>f(`/rest/api/3/issue/${e}/comment`,{method:"POST",body:{body:i}}),t),JSON.stringify({ok:!0,issueKey:e}))}case"jira_edit_issue":{let{issueKey:e,fields:t}=n;if(!e||!t)return JSON.stringify({error:"issueKey and fields are required"});if(typeof t.description=="string"){let{description:i,...r}=t;await U(a=>f(`/rest/api/3/issue/${e}`,{method:"PUT",body:{fields:{...r,description:a}}}),i)}else await f(`/rest/api/3/issue/${e}`,{method:"PUT",body:{fields:t}});return JSON.stringify({ok:!0,issueKey:e})}case"jira_transition_issue":{let{issueKey:e,transitionId:t,toStatus:i,statusName:r,status:a}=n;if(!e)return JSON.stringify({error:"issueKey is required"});let o=String(i||r||a||"").trim();if(!t&&!o){let d=((await f(`/rest/api/3/issue/${e}/transitions`)).transitions||[]).map(m=>({id:m.id,name:m.name,to:m.to?.name}));return JSON.stringify({ok:!1,error:"transitionId or toStatus is required",issueKey:e,availableTransitions:d})}let c=t;if(!c){let d=(await f(`/rest/api/3/issue/${e}/transitions`)).transitions||[],m=O(o),g=d.find(p=>O(p?.name||"")===m||O(p?.to?.name||"")===m);if(!g){let p=D(o);p.length>=2&&(g=d.find(h=>{let y=D(h?.name||""),I=D(h?.to?.name||""),k=y.length>=2&&(y.includes(p)||p.includes(y)),j=I.length>=2&&(I.includes(p)||p.includes(I));return k||j}))}if(!g){let p=d.map(k=>{let j=C(o,k?.name||""),ue=C(o,k?.to?.name||"");return{t:k,score:Math.max(j,ue)}}).sort((k,j)=>j.score-k.score),h=p[0],y=p[1];h&&h.score>=.45&&(!y||h.score-y.score>=.12)&&(g=h.t)}if(!g?.id)return JSON.stringify({ok:!1,error:`No transition matches target status: "${o}"`,issueKey:e,availableTransitions:d.map(p=>({id:p.id,name:p.name,to:p.to?.name}))});c=g.id}await f(`/rest/api/3/issue/${e}/transitions`,{method:"POST",body:{transition:{id:c}}});let l=await f(`/rest/api/3/issue/${e}?fields=status`);return JSON.stringify({ok:!0,issueKey:e,transitionId:c,statusAfter:l?.fields?.status?.name||null})}default:return JSON.stringify({error:`Unknown tool: ${s}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"jira_list_projects",description:"List all Jira projects accessible to the user",input_schema:{type:"object",properties:{}}},{name:"jira_list_statuses",description:"List Jira statuses. Use projectKey to get statuses applicable in that project workflow.",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Optional project key (e.g. PROJ). If omitted, returns global status catalog."}}}},{name:"jira_list_issue_types",description:"List issue types allowed for issue creation in the given project.",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"}},required:["projectKey"]}},{name:"jira_search",description:"Search Jira issues using JQL",input_schema:{type:"object",properties:{jql:{type:"string",description:'JQL query string, e.g. "project = PROJ AND status = Open"'},maxResults:{type:"number",description:"Max results to return (default 20)"}},required:["jql"]}},{name:"jira_get_issue",description:"Get details of a specific Jira issue",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"}},required:["issueKey"]}},{name:"jira_create_issue",description:"Create a new Jira issue",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"},summary:{type:"string",description:"Issue title/summary"},issueType:{type:"string",description:"Issue type (default: Task). Common: Task, Bug, Story, Epic"},description:{type:"string",description:"Issue description. Markdown is rendered (headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes, > [!NOTE]/[!TIP]/[!WARNING]/[!CAUTION] panels)."},priority:{type:"string",description:"Priority name, e.g. High, Medium, Low"},labels:{type:"array",items:{type:"string"},description:"Array of label strings"},assigneeId:{type:"string",description:"Atlassian account ID to assign to"},moveToSprint:{type:"boolean",description:"If true, move created issue to a sprint and verify."},moveToActiveSprint:{type:"boolean",description:"Backward-compatible alias for moveToSprint."},sprintId:{type:"number",description:"Optional sprint id for placement."},sprintName:{type:"string",description:"Optional sprint name for placement."},target:{type:"string",description:"Placement target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["projectKey","summary"]}},{name:"jira_list_sprints",description:"List sprints for a Jira project (returns sprint names, IDs, states, dates)",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"},state:{type:"string",description:"Filter: active, closed, future. Omit for all."}},required:["projectKey"]}},{name:"jira_get_sprint_issues",description:"Get all issues in a sprint, optionally filtered by status column name",input_schema:{type:"object",properties:{sprintName:{type:"string",description:"Sprint name (from jira_list_sprints). Use this OR sprintId."},sprintId:{type:"number",description:"Sprint ID (from jira_list_sprints). Use this OR sprintName."},projectKey:{type:"string",description:"Project key to scope the search (optional)"},status:{type:"string",description:'Filter by status name (e.g. "\u8FDB\u884C\u4E2D", "\u6D4B\u8BD5", "Done")'},maxResults:{type:"number",description:"Max issues to return (default 50)"}}}},{name:"jira_move_to_active_sprint",description:"Backward-compatible alias: move issue to sprint target and verify membership.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},projectKey:{type:"string",description:"Optional project key. If omitted, inferred from issue."},sprintId:{type:"number",description:"Optional sprint id."},sprintName:{type:"string",description:"Optional sprint name."},target:{type:"string",description:"Target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["issueKey"]}},{name:"jira_move_issue_to_sprint",description:"Move an issue to a sprint by id/name/target and verify membership.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},projectKey:{type:"string",description:"Optional project key. If omitted, inferred from issue."},sprintId:{type:"number",description:"Optional sprint id."},sprintName:{type:"string",description:"Optional sprint name."},target:{type:"string",description:"Target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["issueKey"]}},{name:"jira_get_comments",description:"Get comments on a Jira issue (newest first)",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},maxResults:{type:"number",description:"Max comments to return (default 50)"}},required:["issueKey"]}},{name:"jira_add_comment",description:"Add a comment to a Jira issue",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},body:{type:"string",description:"Comment text. Markdown is rendered: headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes, > [!NOTE]/[!TIP]/[!WARNING]/[!CAUTION] panels."}},required:["issueKey","body"]}},{name:"jira_edit_issue",description:"Update fields on a Jira issue (summary, description, story points, labels, priority). A string `description` is Markdown and is rendered.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},fields:{type:"object",description:"Object of field names to values",additionalProperties:!0}},required:["issueKey","fields"]}},{name:"jira_transition_issue",description:"Move a Jira issue to a different status. Always pass toStatus when user gave a target; only pass issueKey alone when you explicitly need to list transitions.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},transitionId:{type:"string",description:"Transition ID to perform (optional if toStatus is provided)"},toStatus:{type:"string",description:'Target status/column name (e.g. "\u5DF2\u7ECF\u9A8C\u6536", "Done", "In Progress"). If provided, tool resolves matching transition automatically.'}},required:["issueKey"]}}]};var Ge={new:"todo",indeterminate:"in_progress",done:"done"},Fe=/\b(blocked|on[\s-]?hold|waiting|stuck)\b/i;function z(s){if(!s)return"unknown";if(s.name&&Fe.test(s.name))return"blocked";let n=s.statusCategory?.key;return Ge[n]||"unknown"}function H(s){if(!s)return"";if(Array.isArray(s))return s.map(H).join("");if(s.type==="text")return s.text||"";if(s.type==="hardBreak"||s.type==="rule")return`
|
|
71
|
+
6. IMPORTANT: When target is clear, complete transition + verification in SAME turn. Do NOT stop after listing options.`,resolve(){let s=Ke();if(!s)return{command:null,args:[],env:{},description:this.description};let n={};for(let e of this.envKeys)process.env[e]&&(n[e]=process.env[e]);process.env.ATLASSIAN_INSTANCE_URL&&(n.ATLASSIAN_INSTANCE_URL=process.env.ATLASSIAN_INSTANCE_URL);for(let e of["JIRA_API_TOKEN","JIRA_EMAIL","JIRA_BASE_URL"])process.env[e]&&(n[e]=process.env[e]);return{type:"stdio",command:"node",args:[s,"../dist/jira.js","jiraSkill"],env:n,description:this.description}},async handleToolCall(s,n){try{switch(s){case"jira_list_projects":{let e=await m("/rest/api/3/project"),t=(Array.isArray(e)?e:[]).map(i=>({id:i.id,key:i.key,name:i.name,style:i.style}));return JSON.stringify({count:t.length,projects:t})}case"jira_list_statuses":{let{projectKey:e}=n||{};if(e){let r=await m(`/rest/api/3/project/${encodeURIComponent(e)}/statuses`),a=Array.isArray(r)?r:[],o=new Map;for(let l of a)for(let u of l.statuses||[])u?.id&&(o.has(u.id)||o.set(u.id,{id:u.id,name:u.name,category:u.statusCategory?.name||null}));let c=[...o.values()].sort((l,u)=>String(l.name).localeCompare(String(u.name)));return JSON.stringify({scope:"project",projectKey:e,count:c.length,statuses:c})}let t=await m("/rest/api/3/status"),i=(Array.isArray(t)?t:[]).map(r=>({id:r.id,name:r.name,category:r.statusCategory?.name||null})).sort((r,a)=>String(r.name).localeCompare(String(a.name)));return JSON.stringify({scope:"global",count:i.length,statuses:i})}case"jira_list_issue_types":{let{projectKey:e}=n||{};if(!e)return JSON.stringify({error:"projectKey is required"});let t=await ce(e);return JSON.stringify({projectKey:e,count:t.length,issueTypes:t})}case"jira_search":{let e=n.jql||"",t=n.maxResults||20;e.replace(/\s*ORDER\s+BY\s+.*/i,"").trim()||(e=`created >= -365d ${e}`.trim());let r=`jql=${encodeURIComponent(e)}&maxResults=${t}&fields=summary,status,assignee,priority,updated,issuetype,project`,o=((await m(`/rest/api/3/search/jql?${r}`)).issues||[]).map(c=>({key:c.key,project:c.fields?.project?.key,summary:c.fields?.summary,status:c.fields?.status?.name,assignee:c.fields?.assignee?.displayName||"Unassigned",priority:c.fields?.priority?.name,type:c.fields?.issuetype?.name}));return JSON.stringify({count:o.length,issues:o})}case"jira_get_issue":{let e=n.issueKey;if(!e)return JSON.stringify({error:"issueKey is required"});let t=await m(`/rest/api/3/issue/${e}`);return JSON.stringify({key:t.key,project:t.fields?.project?.key,summary:t.fields?.summary,description:t.fields?.description?J(t.fields.description):t.fields?.description??null,status:t.fields?.status?.name,assignee:t.fields?.assignee?.displayName||"Unassigned",priority:t.fields?.priority?.name,type:t.fields?.issuetype?.name,labels:t.fields?.labels,created:t.fields?.created,updated:t.fields?.updated})}case"jira_create_issue":{let{projectKey:e,summary:t,issueType:i,description:r,priority:a,labels:o,assigneeId:c,moveToSprint:l,moveToActiveSprint:u,sprintId:d,sprintName:f,target:y}=n;if(!e||!t)return JSON.stringify({error:"projectKey and summary are required"});let p={requested:i||null,resolved:null,strategy:"none"},g=[];try{g=await ce(e),p=Be(i,g)}catch{}let h={project:{key:e},summary:t,issuetype:p?.resolved?.id?{id:p.resolved.id}:{name:i||"Task"}};a&&(h.priority={name:a}),o?.length&&(h.labels=o),c&&(h.assignee={id:c});let I=r?await U(j=>m("/rest/api/3/issue",{method:"POST",body:{fields:{...h,description:j}}}),r):await m("/rest/api/3/issue",{method:"POST",body:{fields:h}}),k={ok:!0,key:I.key,id:I.id,self:I.self};return p?.resolved&&(k.issueType=p.resolved.name,k.issueTypeResolution=p.strategy,p.strategy!=="exact"&&p.requested&&w(p.requested)!==w(p.resolved.name)&&(k.issueTypeWarning=`Requested "${p.requested}" is not available in ${e}; used "${p.resolved.name}" instead.`)),g.length>0&&(k.availableIssueTypes=g.map(j=>j.name)),(l||u)&&(k.sprintMove=await G({issueKey:I.key,projectKey:e,sprintId:d,sprintName:f,target:y})),JSON.stringify(k)}case"jira_list_sprints":{let{projectKey:e,state:t}=n,i=await le(e,t);return JSON.stringify({count:i.length,sprints:i})}case"jira_move_to_active_sprint":{let{issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a}=n||{},o=await G({issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a||"current"});return JSON.stringify(o)}case"jira_move_issue_to_sprint":{let{issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a}=n||{},o=await G({issueKey:e,projectKey:t,sprintId:i,sprintName:r,target:a});return JSON.stringify(o)}case"jira_get_sprint_issues":{let{sprintName:e,sprintId:t,projectKey:i,status:r,maxResults:a}=n;if(!e&&!t)return JSON.stringify({error:"sprintName or sprintId is required"});let o=a||50,c=t?`sprint = ${t}`:`sprint = "${e}"`,l=i?`project = ${i} AND `:"",u=r?` AND status = "${r}"`:"",d=`${l}${c}${u} ORDER BY status ASC, priority DESC`,f=`jql=${encodeURIComponent(d)}&maxResults=${o}&fields=summary,status,assignee,priority,issuetype,project`,y=await m(`/rest/api/3/search/jql?${f}`),p=(y.issues||[]).map(h=>({key:h.key,project:h.fields?.project?.key,summary:h.fields?.summary,status:h.fields?.status?.name,assignee:h.fields?.assignee?.displayName||"Unassigned",priority:h.fields?.priority?.name,type:h.fields?.issuetype?.name})),g={};for(let h of p)g[h.status]=(g[h.status]||0)+1;return JSON.stringify({count:p.length,total:y.total||p.length,statusCounts:g,issues:p})}case"jira_get_comments":{let{issueKey:e,maxResults:t}=n;if(!e)return JSON.stringify({error:"issueKey is required"});let r=await m(`/rest/api/3/issue/${e}/comment?maxResults=${t||50}&orderBy=-created`),a=(r.comments||[]).map(o=>{let c=o.body?.content?J(o.body):"";return{id:o.id,author:o.author?.displayName||"Unknown",body:c,created:o.created,updated:o.updated}});return JSON.stringify({count:a.length,total:r.total||a.length,comments:a})}case"jira_add_comment":{let{issueKey:e,body:t}=n;return!e||!t?JSON.stringify({error:"issueKey and body are required"}):(await U(i=>m(`/rest/api/3/issue/${e}/comment`,{method:"POST",body:{body:i}}),t),JSON.stringify({ok:!0,issueKey:e}))}case"jira_edit_issue":{let{issueKey:e,fields:t}=n;if(!e||!t)return JSON.stringify({error:"issueKey and fields are required"});if(typeof t.description=="string"){let{description:i,...r}=t;await U(a=>m(`/rest/api/3/issue/${e}`,{method:"PUT",body:{fields:{...r,description:a}}}),i)}else await m(`/rest/api/3/issue/${e}`,{method:"PUT",body:{fields:t}});return JSON.stringify({ok:!0,issueKey:e})}case"jira_transition_issue":{let{issueKey:e,transitionId:t,toStatus:i,statusName:r,status:a}=n;if(!e)return JSON.stringify({error:"issueKey is required"});let o=String(i||r||a||"").trim();if(!t&&!o){let d=((await m(`/rest/api/3/issue/${e}/transitions`)).transitions||[]).map(f=>({id:f.id,name:f.name,to:f.to?.name}));return JSON.stringify({ok:!1,error:"transitionId or toStatus is required",issueKey:e,availableTransitions:d})}let c=t;if(!c){let d=(await m(`/rest/api/3/issue/${e}/transitions`)).transitions||[],f=O(o),y=d.find(p=>O(p?.name||"")===f||O(p?.to?.name||"")===f);if(!y){let p=D(o);p.length>=2&&(y=d.find(g=>{let h=D(g?.name||""),I=D(g?.to?.name||""),k=h.length>=2&&(h.includes(p)||p.includes(h)),j=I.length>=2&&(I.includes(p)||p.includes(I));return k||j}))}if(!y){let p=d.map(k=>{let j=C(o,k?.name||""),ue=C(o,k?.to?.name||"");return{t:k,score:Math.max(j,ue)}}).sort((k,j)=>j.score-k.score),g=p[0],h=p[1];g&&g.score>=.45&&(!h||g.score-h.score>=.12)&&(y=g.t)}if(!y?.id)return JSON.stringify({ok:!1,error:`No transition matches target status: "${o}"`,issueKey:e,availableTransitions:d.map(p=>({id:p.id,name:p.name,to:p.to?.name}))});c=y.id}await m(`/rest/api/3/issue/${e}/transitions`,{method:"POST",body:{transition:{id:c}}});let l=await m(`/rest/api/3/issue/${e}?fields=status`);return JSON.stringify({ok:!0,issueKey:e,transitionId:c,statusAfter:l?.fields?.status?.name||null})}default:return JSON.stringify({error:`Unknown tool: ${s}`})}}catch(e){return JSON.stringify({error:e.message})}},tools:[{name:"jira_list_projects",description:"List all Jira projects accessible to the user",input_schema:{type:"object",properties:{}}},{name:"jira_list_statuses",description:"List Jira statuses. Use projectKey to get statuses applicable in that project workflow.",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Optional project key (e.g. PROJ). If omitted, returns global status catalog."}}}},{name:"jira_list_issue_types",description:"List issue types allowed for issue creation in the given project.",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"}},required:["projectKey"]}},{name:"jira_search",description:"Search Jira issues using JQL",input_schema:{type:"object",properties:{jql:{type:"string",description:'JQL query string, e.g. "project = PROJ AND status = Open"'},maxResults:{type:"number",description:"Max results to return (default 20)"}},required:["jql"]}},{name:"jira_get_issue",description:"Get details of a specific Jira issue",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"}},required:["issueKey"]}},{name:"jira_create_issue",description:"Create a new Jira issue",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"},summary:{type:"string",description:"Issue title/summary"},issueType:{type:"string",description:"Issue type (default: Task). Common: Task, Bug, Story, Epic"},description:{type:"string",description:"Issue description. Markdown is rendered (headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes, > [!NOTE]/[!TIP]/[!WARNING]/[!CAUTION] panels)."},priority:{type:"string",description:"Priority name, e.g. High, Medium, Low"},labels:{type:"array",items:{type:"string"},description:"Array of label strings"},assigneeId:{type:"string",description:"Atlassian account ID to assign to"},moveToSprint:{type:"boolean",description:"If true, move created issue to a sprint and verify."},moveToActiveSprint:{type:"boolean",description:"Backward-compatible alias for moveToSprint."},sprintId:{type:"number",description:"Optional sprint id for placement."},sprintName:{type:"string",description:"Optional sprint name for placement."},target:{type:"string",description:"Placement target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["projectKey","summary"]}},{name:"jira_list_sprints",description:"List sprints for a Jira project (returns sprint names, IDs, states, dates)",input_schema:{type:"object",properties:{projectKey:{type:"string",description:"Project key, e.g. PROJ"},state:{type:"string",description:"Filter: active, closed, future. Omit for all."}},required:["projectKey"]}},{name:"jira_get_sprint_issues",description:"Get all issues in a sprint, optionally filtered by status column name",input_schema:{type:"object",properties:{sprintName:{type:"string",description:"Sprint name (from jira_list_sprints). Use this OR sprintId."},sprintId:{type:"number",description:"Sprint ID (from jira_list_sprints). Use this OR sprintName."},projectKey:{type:"string",description:"Project key to scope the search (optional)"},status:{type:"string",description:'Filter by status name (e.g. "\u8FDB\u884C\u4E2D", "\u6D4B\u8BD5", "Done")'},maxResults:{type:"number",description:"Max issues to return (default 50)"}}}},{name:"jira_move_to_active_sprint",description:"Backward-compatible alias: move issue to sprint target and verify membership.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},projectKey:{type:"string",description:"Optional project key. If omitted, inferred from issue."},sprintId:{type:"number",description:"Optional sprint id."},sprintName:{type:"string",description:"Optional sprint name."},target:{type:"string",description:"Target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["issueKey"]}},{name:"jira_move_issue_to_sprint",description:"Move an issue to a sprint by id/name/target and verify membership.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},projectKey:{type:"string",description:"Optional project key. If omitted, inferred from issue."},sprintId:{type:"number",description:"Optional sprint id."},sprintName:{type:"string",description:"Optional sprint name."},target:{type:"string",description:"Target when sprintId/sprintName omitted: current|active|latest (default: current)."}},required:["issueKey"]}},{name:"jira_get_comments",description:"Get comments on a Jira issue (newest first)",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},maxResults:{type:"number",description:"Max comments to return (default 50)"}},required:["issueKey"]}},{name:"jira_add_comment",description:"Add a comment to a Jira issue",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},body:{type:"string",description:"Comment text. Markdown is rendered: headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes, > [!NOTE]/[!TIP]/[!WARNING]/[!CAUTION] panels."}},required:["issueKey","body"]}},{name:"jira_edit_issue",description:"Update fields on a Jira issue (summary, description, story points, labels, priority). A string `description` is Markdown and is rendered.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},fields:{type:"object",description:"Object of field names to values",additionalProperties:!0}},required:["issueKey","fields"]}},{name:"jira_transition_issue",description:"Move a Jira issue to a different status. Always pass toStatus when user gave a target; only pass issueKey alone when you explicitly need to list transitions.",input_schema:{type:"object",properties:{issueKey:{type:"string",description:"Issue key, e.g. PROJ-123"},transitionId:{type:"string",description:"Transition ID to perform (optional if toStatus is provided)"},toStatus:{type:"string",description:'Target status/column name (e.g. "\u5DF2\u7ECF\u9A8C\u6536", "Done", "In Progress"). If provided, tool resolves matching transition automatically.'}},required:["issueKey"]}}]};var We={new:"todo",indeterminate:"in_progress",done:"done"},ze=/\b(blocked|on[\s-]?hold|waiting|stuck)\b/i;function z(s){if(!s)return"unknown";if(s.name&&ze.test(s.name))return"blocked";let n=s.statusCategory?.key;return We[n]||"unknown"}function H(s){if(!s)return"";if(Array.isArray(s))return s.map(H).join("");if(s.type==="text")return s.text||"";if(s.type==="hardBreak"||s.type==="rule")return`
|
|
72
72
|
`;let n=s.content?H(s.content):"";return/^(paragraph|heading|listItem|blockquote|codeBlock|panel)$/.test(s.type)&&(n+=`
|
|
73
|
-
`),n}function F(s,n){let e=s.fields||{},t=e.status||null,i=null;if(s.self&&s.key)try{i=`${new URL(s.self).origin}/browse/${s.key}`}catch{i=null}return!i&&n&&s.key&&(i=`${String(n).replace(/\/+$/,"")}/browse/${s.key}`),{id:String(s.id??s.key??""),key:s.key,title:e.summary||"",body:typeof e.description=="string"?e.description:H(e.description?.content).trim(),state:t?.name||null,stateCategory:z(t),assignee:e.assignee?.displayName||null,url:i,_raw:s}}function W(s){let n=JSON.parse(s);if(n&&n.error)throw new Error(n.error);return n}var
|
|
73
|
+
`),n}function F(s,n){let e=s.fields||{},t=e.status||null,i=null;if(s.self&&s.key)try{i=`${new URL(s.self).origin}/browse/${s.key}`}catch{i=null}return!i&&n&&s.key&&(i=`${String(n).replace(/\/+$/,"")}/browse/${s.key}`),{id:String(s.id??s.key??""),key:s.key,title:e.summary||"",body:typeof e.description=="string"?e.description:H(e.description?.content).trim(),state:t?.name||null,stateCategory:z(t),assignee:e.assignee?.displayName||null,url:i,_raw:s}}function W(s){let n=JSON.parse(s);if(n&&n.error)throw new Error(n.error);return n}var He={id:"jira",toStateCategory:z,toNeutral:F,async listCandidates(s={}){let n=s.query||"";if(!n.trim()){let r=[];if(s.state&&r.push(`status = "${s.state}"`),s.labels){let a=Array.isArray(s.labels)?s.labels:[s.labels];for(let o of a)r.push(`labels = "${o}"`)}s.updatedAfter&&r.push(`updated >= "${s.updatedAfter}"`),n=r.length?`${r.join(" AND ")} ORDER BY updated DESC`:"ORDER BY updated DESC"}let e=Number(s.limit)||30,t=`jql=${encodeURIComponent(n)}&maxResults=${e}&fields=summary,description,status,assignee,labels,project,updated,created`;return((await m(`/rest/api/3/search/jql?${t}`)).issues||[]).map(r=>F(r))},async getTicket(s){if(!s)throw new Error("key is required");let n=await m(`/rest/api/3/issue/${encodeURIComponent(s)}`);return!n||!n.key?null:F(n)},async getComments(s){if(!s)throw new Error("key is required");return(W(await $.handleToolCall("jira_get_comments",{issueKey:s})).comments||[]).map(e=>({id:String(e.id),author:e.author||"Unknown",body:e.body||"",createdAt:e.created||null,updatedAt:e.updated||null,_raw:e}))},async addComment(s,n){if(!s||!n)throw new Error("key and body are required");return{ok:!!W(await $.handleToolCall("jira_add_comment",{issueKey:s,body:n})).ok,id:null}},async transition(s,n){if(!s)throw new Error("key is required");let e=JSON.parse(await $.handleToolCall("jira_transition_issue",{issueKey:s,toStatus:n}));if(!e.ok)return{ok:!1,error:e.error||"transition failed",_raw:e};let t=e.statusAfter||null;return{ok:!0,stateAfter:t,stateCategoryAfter:z(t?{name:t}:null),_raw:e}},async linkPullRequest(s,n,e){if(!s||!n)throw new Error("key and prUrl are required");try{return await m(`/rest/api/3/issue/${encodeURIComponent(s)}/remotelink`,{method:"POST",body:{object:{url:n,title:e||n,icon:{url16x16:"https://github.com/favicon.ico",title:"GitHub"}}}}),{ok:!0,via:"remotelink"}}catch(t){let i=`${e?`${e}: `:"Linked PR: "}${n}`;return{ok:!!W(await $.handleToolCall("jira_add_comment",{issueKey:s,body:i})).ok,via:"comment",error:String(t?.message||t)}}}},ct=He;export{ct as default,He as jiraAdapter};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{existsSync as R}from"fs";import{fileURLToPath as $}from"url";import{dirname as C,resolve as K}from"path";var
|
|
1
|
+
import{existsSync as R}from"fs";import{fileURLToPath as $}from"url";import{dirname as C,resolve as K}from"path";var S=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),H=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 Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},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"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},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"}});var k={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function E(t,n,e=process.env){let i=Number(e?.[t]);return Number.isFinite(i)&&i>0?Math.min(6e5,Math.max(1e3,Math.floor(i))):n}function O(t="api",n=process.env){let e=k[t]||k.api;return E(e.knob,e.fallback,n)}function v(t){return t?.name==="TimeoutError"||t?.name==="AbortError"}function P(t){try{return new URL(String(t?.url??t)).host||"unknown host"}catch{return"unknown host"}}function M(t,n){if(!t)return n;let e=new AbortController,i=s=>{e.signal.aborted||e.abort(s)},a=()=>i(t.reason),r=()=>i(n.reason);return e.signal.addEventListener("abort",()=>{t.removeEventListener("abort",a),n.removeEventListener("abort",r)},{once:!0}),t.aborted?i(t.reason):n.aborted?i(n.reason):(t.addEventListener("abort",a,{once:!0}),n.addEventListener("abort",r,{once:!0})),e.signal}async function L(t,n={},e={}){let i=e.kind||"api",a=(k[i]||k.api).knob,r=e.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(e.timeoutMs))):O(i),s=n?.signal;if(s?.aborted)throw s.reason??new DOMException("This operation was aborted","AbortError");let o=AbortSignal.timeout(r),d=M(s,o);try{return await fetch(t,{...n,signal:d})}catch(m){throw o.aborted&&v(m)?new Error(`${e.what||"request"} TIMED OUT after ${r}ms against ${P(t)} (${a})`):m}}function U(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=C($(import.meta.url)),n=K(t,"..","bin","mcp-skill.mjs");return R(n)?n:null}var x=process.env.LINEAR_API_URL||"https://api.linear.app/graphql";function G(){if(process.env.LINEAR_OAUTH_TOKEN)return`Bearer ${process.env.LINEAR_OAUTH_TOKEN}`;let t=process.env.LINEAR_API_KEY;if(!t)throw new Error("Linear is not connected: set LINEAR_API_KEY (personal API key) or LINEAR_OAUTH_TOKEN.");return t}async function f(t,n={}){let e=await L(x,{method:"POST",headers:{Authorization:G(),"Content-Type":"application/json"},body:JSON.stringify({query:t,variables:n})},{kind:"api",what:"Linear GraphQL"});if(!e.ok){let a=await e.text().catch(()=>"");throw new Error(`Linear API ${e.status}: ${a.slice(0,300)}`)}let i=await e.json().catch(()=>null);if(!i)throw new Error("Linear API returned a non-JSON body");if(Array.isArray(i.errors)&&i.errors.length){let a=i.errors.map(r=>r?.message||String(r)).join("; ");throw new Error(`Linear GraphQL error: ${a.slice(0,300)}`)}return i.data}function I(t){return String(t||"").toLowerCase().replace(/\s+/g,"").replace(/[()\-_::"'`]/g,"")}function q(t,n){let e=I(t),i=I(n);if(!e||!i)return 0;if(e===i)return 1;if(e.length===1||i.length===1)return e===i?1:0;let a=l=>{let u=new Map;for(let y=0;y<l.length-1;y++){let g=l.slice(y,y+2);u.set(g,(u.get(g)||0)+1)}return u},r=a(e),s=a(i),o=0,d=0,m=0;for(let l of r.values())d+=l;for(let l of s.values())m+=l;for(let[l,u]of r.entries())o+=Math.min(u,s.get(l)||0);return 2*o/Math.max(1,d+m)}function J(t,n){let e=Array.isArray(t)?t:[];if(!e.length)return{state:null,strategy:"no-states"};let i=I(n);if(!i)return{state:null,strategy:"no-target"};let a=e.find(o=>I(o.name)===i);if(a)return{state:a,strategy:"exact"};let r={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,d]of Object.entries(r)){if(!d.some(l=>I(l)===i))continue;let m=e.find(l=>l.type===o);if(m)return{state:m,strategy:"type-alias"}}let s=e.map(o=>({s:o,score:q(n,o.name)})).sort((o,d)=>d.score-o.score);return s[0]&&s[0].score>=.5?{state:s[0].s,strategy:"fuzzy"}:{state:null,strategy:"no-match"}}var j=`
|
|
2
2
|
id
|
|
3
3
|
identifier
|
|
4
4
|
number
|
|
@@ -12,7 +12,7 @@ import{existsSync as R}from"fs";import{fileURLToPath as $}from"url";import{dirna
|
|
|
12
12
|
assignee { id name displayName email }
|
|
13
13
|
labels { nodes { id name color } }
|
|
14
14
|
team { id key name }
|
|
15
|
-
`,_={id:"linear",serverName:"linear",allowedTools:["mcp__linear__*"],requiresIntegration:
|
|
15
|
+
`,_={id:"linear",serverName:"linear",allowedTools:["mcp__linear__*"],requiresIntegration:S.LINEAR,envKeys:["LINEAR_API_KEY","LINEAR_OAUTH_TOKEN"],description:"Linear \u2014 issues, comments, workflow states (GraphQL API key)",promptFragment:`## Linear
|
|
16
16
|
You have direct access to the user's Linear workspace (GraphQL API). Tools:
|
|
17
17
|
|
|
18
18
|
### Discovery
|
|
@@ -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 t={};for(let e of this.envKeys)process.env[e]&&(t[e]=process.env[e]);process.env.LINEAR_API_URL&&(t.LINEAR_API_URL=process.env.LINEAR_API_URL);let n=U();return n?{type:"stdio",command:"node",args:[n,"../dist/linear.js","linearSkill"],env:t,description:this.description
|
|
33
|
+
- Issue identifier (ENG-123) and internal id (uuid) are both accepted by get/update tools.`,resolve(){let t={};for(let e of this.envKeys)process.env[e]&&(t[e]=process.env[e]);process.env.LINEAR_API_URL&&(t.LINEAR_API_URL=process.env.LINEAR_API_URL);let n=U();return n?{type:"stdio",command:"node",args:[n,"../dist/linear.js","linearSkill"],env:t,description:this.description}:{command:null,args:[],env:t,description:this.description}},async handleToolCall(t,n){try{switch(t){case"linear_list_teams":{let i=(await f(`
|
|
34
34
|
query Teams($first: Int) {
|
|
35
35
|
teams(first: $first) {
|
|
36
36
|
nodes { id key name description }
|
package/dist/vikunja.js
CHANGED
|
@@ -39,4 +39,4 @@ You can operate the user's Vikunja board directly. Tools:
|
|
|
39
39
|
### Notes
|
|
40
40
|
- Address tasks by their NUMERIC id. The '#1' identifier shown in the UI is per-project and not unique.
|
|
41
41
|
- Descriptions may come back as plain text converted from HTML \u2014 that is expected.
|
|
42
|
-
- Priority accepts a number 0-5 or a word: low, medium, high, urgent, now.`,resolve(){let n={};for(let t of this.envKeys)process.env[t]&&(n[t]=process.env[t]);let e=qt();return e?{type:"stdio",command:"node",args:[e,"../dist/vikunja.js","vikunjaSkill"],env:n,description:this.description
|
|
42
|
+
- Priority accepts a number 0-5 or a word: low, medium, high, urgent, now.`,resolve(){let n={};for(let t of this.envKeys)process.env[t]&&(n[t]=process.env[t]);let e=qt();return e?{type:"stdio",command:"node",args:[e,"../dist/vikunja.js","vikunjaSkill"],env:n,description:this.description}:{command:null,args:[],env:n,description:this.description}},async handleToolCall(n,e){try{let t=e||{};switch(n){case"vikunja_list_projects":{let r=await k("GET","/projects"),i=(Array.isArray(r)?r:[]).map(s=>({id:String(s.id),title:s.title||"",description:ct(s.description)}));return JSON.stringify({count:i.length,projects:i})}case"vikunja_list_buckets":{let{viewId:r,buckets:i}=await at(t.projectId);return JSON.stringify({viewId:r,count:i.length,buckets:i.map(s=>({id:s.id,title:s.title,taskCount:s.count??null}))})}case"vikunja_list_labels":{let r=await k("GET","/labels",{query:{s:t.search}}),i=(Array.isArray(r)?r:[]).map(s=>({id:s.id,title:s.title}));return JSON.stringify({count:i.length,labels:i})}case"vikunja_list_tasks":{let{origin:r}=await L(),i=[];if(t.label){let o=await C(t.label);if(!o)return JSON.stringify({count:0,tasks:[],note:`No label named "${t.label}" exists.`});i.push(`labels in ${o}`)}(t.done===!0||t.done===!1)&&i.push(`done = ${t.done}`);let s=await k("GET",`/projects/${encodeURIComponent(String(t.projectId))}/tasks`,{query:{...i.length?{filter:i.join(" && ")}:{},sort_by:"updated",order_by:"desc",per_page:Math.min(Number(t.limit)||25,50)}}),a=(Array.isArray(s)?s:[]).map(o=>R(o,r));return JSON.stringify({count:a.length,tasks:a})}case"vikunja_get_task":{let{origin:r}=await L(),i=await k("GET",`/tasks/${encodeURIComponent(String(t.taskId))}`);return JSON.stringify({task:R(i,r)})}case"vikunja_create_task":{let{origin:r}=await L(),i=ot(t.priority),s=await k("PUT",`/projects/${encodeURIComponent(String(t.projectId))}/tasks`,{body:{title:t.title,...t.description?{description:K(t.description)}:{},...i!==null?{priority:i}:{},...t.dueDate?{due_date:t.dueDate}:{}}});if(!s?.id)throw new Error("Vikunja created no task (no id in the response)");let a=[];for(let d of Array.isArray(t.labels)?t.labels:[])try{let c=await C(d,{create:!0});c&&await k("PUT",`/tasks/${s.id}/labels`,{body:{label_id:c}})}catch(c){a.push(`${d}: ${c?.message||c}`)}let o=await k("GET",`/tasks/${s.id}`);return JSON.stringify({created:R(o||s,r),...a.length?{labelErrors:a}:{}})}case"vikunja_update_task":{let{origin:r}=await L(),i=encodeURIComponent(String(t.taskId)),s=await k("GET",`/tasks/${i}`);if(!s?.id)throw new Error(`No task ${t.taskId}`);let a=ot(t.priority),o=await k("POST",`/tasks/${i}`,{body:{...s,...t.title!==void 0?{title:t.title}:{},...t.description!==void 0?{description:K(t.description)}:{},...a!==null?{priority:a}:{},...t.done!==void 0?{done:!!t.done}:{},...t.dueDate!==void 0?{due_date:t.dueDate}:{}}});return JSON.stringify({updated:R(o,r)})}case"vikunja_move_task":{let r=await k("GET",`/tasks/${encodeURIComponent(String(t.taskId))}`);if(!r?.id)throw new Error(`No task ${t.taskId}`);let i=t.projectId??r.project_id,s=String(t.column||"").trim().toLowerCase();if(!s)throw new Error("No target column given.");let{viewId:a,buckets:o}=await at(i),d=o.find(c=>(c?.title||"").trim().toLowerCase()===s);return d?.id?(await k("POST",`/projects/${encodeURIComponent(String(i))}/views/${a}/buckets/${d.id}/tasks`,{body:{task_id:Number(r.id)}}),JSON.stringify({moved:!0,taskId:String(r.id),column:d.title})):JSON.stringify({moved:!1,error:`No column named "${t.column}".`,availableColumns:o.map(c=>c.title)})}case"vikunja_add_label":{let r=await C(t.label,{create:t.create!==!1});return r?(await k("PUT",`/tasks/${encodeURIComponent(String(t.taskId))}/labels`,{body:{label_id:r}}),JSON.stringify({ok:!0,taskId:String(t.taskId),label:t.label})):JSON.stringify({ok:!1,error:`No label named "${t.label}" and creating it was declined.`})}case"vikunja_remove_label":{let r=await C(t.label);return r?(await k("DELETE",`/tasks/${encodeURIComponent(String(t.taskId))}/labels/${r}`),JSON.stringify({ok:!0,taskId:String(t.taskId),label:t.label})):JSON.stringify({ok:!1,error:`No label named "${t.label}".`})}case"vikunja_add_comment":{let r=await k("PUT",`/tasks/${encodeURIComponent(String(t.taskId))}/comments`,{body:{comment:K(t.comment)}});return JSON.stringify({ok:!0,commentId:r?.id??null})}case"vikunja_assign":{let r=await k("GET","/users",{query:{s:t.username}}),i=(Array.isArray(r)?r:[]).find(s=>(s?.username||"").toLowerCase()===String(t.username||"").toLowerCase());return i?.id?(await k("PUT",`/tasks/${encodeURIComponent(String(t.taskId))}/assignees`,{body:{user_id:i.id}}),JSON.stringify({ok:!0,taskId:String(t.taskId),assignee:i.username})):JSON.stringify({ok:!1,error:`No user "${t.username}".`})}default:return JSON.stringify({error:`Unknown vikunja tool: ${n}`})}}catch(t){return JSON.stringify({error:t?.message||String(t)})}},tools:[{name:"vikunja_list_projects",description:"List Vikunja projects/boards (id, title). Everything else is project-scoped \u2014 start here when you do not already have a project id.",input_schema:{type:"object",properties:{}}},{name:"vikunja_list_buckets",description:"List a project's kanban COLUMNS (bucket id + title). Call this before moving a card so you use a column that exists.",input_schema:{type:"object",properties:{projectId:{type:"string",description:"Project id"}},required:["projectId"]}},{name:"vikunja_list_labels",description:"List labels (id, title). Optionally narrow with a search string.",input_schema:{type:"object",properties:{search:{type:"string",description:"Optional server-side search on the label title"}}}},{name:"vikunja_list_tasks",description:"List a project's tasks, newest-updated first. Optionally filter by label title and/or done-state.",input_schema:{type:"object",properties:{projectId:{type:"string",description:"Project id"},label:{type:"string",description:"Only tasks carrying this label title"},done:{type:"boolean",description:"Filter on completion state"},limit:{type:"number",description:"Max tasks (default 25, max 50)"}},required:["projectId"]}},{name:"vikunja_get_task",description:'Get one task by its NUMERIC id (the "#1" identifier shown in the UI is per-project and not unique).',input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"}},required:["taskId"]}},{name:"vikunja_create_task",description:"Create a task in a project. Labels are attached after creation and fail soft \u2014 the task survives a label error.",input_schema:{type:"object",properties:{projectId:{type:"string",description:"Project id"},title:{type:"string",description:"Task title"},description:{type:"string",description:"Task body. Markdown is rendered (headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes)."},priority:{type:"string",description:"A number 0-5 or a word: low, medium, high, urgent, now"},labels:{type:"array",items:{type:"string"},description:"Label titles; created if missing"},dueDate:{type:"string",description:"RFC3339 timestamp"}},required:["projectId","title"]}},{name:"vikunja_update_task",description:"Change a task's title, description, priority, due date, or done state. To move it to another COLUMN use vikunja_move_task \u2014 a column is not a field on the task.",input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},title:{type:"string"},description:{type:"string"},priority:{type:"string",description:"A number 0-5 or a word: low, medium, high, urgent, now"},done:{type:"boolean"},dueDate:{type:"string",description:"RFC3339 timestamp"}},required:["taskId"]}},{name:"vikunja_move_task",description:`Move a card to a kanban COLUMN by name (e.g. "Doing"). Vikunja has no task.status: a column is a bucket on the project's kanban view, so this resolves the view and puts the task in the matching bucket. Returns the available column names when the name does not match.`,input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},column:{type:"string",description:"Target column (bucket) title"},projectId:{type:"string",description:"Optional; defaults to the task's own project"}},required:["taskId","column"]}},{name:"vikunja_add_label",description:"Attach a label to a task by title. Creates the label when it does not exist (pass create=false to refuse instead).",input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},label:{type:"string",description:"Label title"},create:{type:"boolean",description:"Create the label if missing (default true)"}},required:["taskId","label"]}},{name:"vikunja_remove_label",description:"Detach a label from a task by title.",input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},label:{type:"string",description:"Label title"}},required:["taskId","label"]}},{name:"vikunja_add_comment",description:"Add a comment to a task.",input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},comment:{type:"string",description:"Comment body. Markdown is rendered (headings, **bold**, `code`, lists, - [ ] tasks, links, tables, > quotes)."}},required:["taskId","comment"]}},{name:"vikunja_assign",description:"Assign a user to a task by username.",input_schema:{type:"object",properties:{taskId:{type:"string",description:"Numeric task id"},username:{type:"string",description:"Vikunja username"}},required:["taskId","username"]}}]};export{Yt as clearVikunjaCredsCache,Zt as vikunjaSkill,ct as vkPlainText,ot as vkPriority,K as vkRichHtml};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zibby/skills",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Built-in skill definitions for the Zibby agent-workflow framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -117,10 +117,10 @@
|
|
|
117
117
|
"dependencies": {
|
|
118
118
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
119
119
|
"@resvg/resvg-js": "^2.6.2",
|
|
120
|
-
"@zibby/agent-workflow": "2.0.
|
|
120
|
+
"@zibby/agent-workflow": "2.0.9",
|
|
121
121
|
"@zibby/bin-oxlint": "^1.73.0",
|
|
122
122
|
"@zibby/bin-semgrep": "^1.169.0",
|
|
123
|
-
"@zibby/skill-ids": "2.0.
|
|
123
|
+
"@zibby/skill-ids": "2.0.9",
|
|
124
124
|
"echarts": "^6.1.0",
|
|
125
125
|
"mem0ai": "npm:@zibby/mem0ai@^3.0.5",
|
|
126
126
|
"zod": "^3.23.0 || ^4.0.0"
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
"optionalDependencies": {
|
|
138
|
-
"@zibby/mcp-browser": "2.0.
|
|
138
|
+
"@zibby/mcp-browser": "2.0.9",
|
|
139
139
|
"@zibby/mcp-memory": "*"
|
|
140
140
|
},
|
|
141
141
|
"devDependencies": {
|