chief-clancy 0.8.16 → 0.8.17
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.
|
@@ -43,9 +43,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
43
43
|
`;Mu(i,p,"utf8")}function Uu(e){let t=Bu(e,".clancy","progress.txt"),r;try{r=vg(t,"utf8")}catch{return[]}let n=[];for(let o of r.split(`
|
|
44
44
|
`)){let s=o.trim();if(!s)continue;let i=s.split(" | ");if(i.length<4)continue;let a=i[0];if(i[1]==="BRIEF"||i[1]==="APPROVE_BRIEF"){n.push({timestamp:a,key:i[2],summary:i.slice(3).join(" | "),status:i[1]});continue}let u=i[1],l,p,f,m=[];for(let h=2;h<i.length;h++){let b=i[h],y=b.match(/^pr:(\d+)$/),z=b.match(/^parent:(.+)$/);y?p=parseInt(y[1],10):z?f=z[1]:h>=3&&!l&&b===b.toUpperCase()&&b.length>1?l=b:m.push(b)}l&&(l==="APPROVE"&&(l="APPROVE_PLAN"),n.push({timestamp:a,key:u,summary:m.join(" | "),status:l,...p!=null&&{prNumber:p},...f!=null&&{parent:f}}))}return n}function Fu(e,t){let r=Uu(e),n=t.toLowerCase(),o=0;for(let s of r)s.key.toLowerCase()===n&&s.status==="REWORK"&&o++;return o}function j(e,t){let r=Uu(e),n=new Map;for(let o of r)n.set(o.key,o);return[...n.values()].filter(o=>o.status===t)}function Ig(e){return e.startsWith("epic/")||e.startsWith("milestone/")}function Ye(e,t,r,n,o){let s=[],i=r?Ig(r):!1;switch(e.provider){case"github":s.push(i?`Part of ${t.key}`:`Closes ${t.key}`),o&&!i&&s.push(`Closes ${o}`);break;case"jira":s.push(`**Jira:** [${t.key}](${e.env.JIRA_BASE_URL}/browse/${t.key})`);break;case"linear":s.push(`**Linear:** ${t.key}`);break}return s.push(""),t.description&&(s.push("## Description"),s.push(""),s.push(t.description),s.push("")),n&&(s.push("## \u26A0 Verification Warning"),s.push(""),s.push(n),s.push(""),s.push("This PR may need manual fixes before merging."),s.push("")),s.push("---"),s.push("*Created by [Clancy](https://github.com/Pushedskydiver/clancy)*"),s.push(""),s.push("---"),s.push("<details>"),s.push("<summary><strong>Rework instructions</strong> (click to expand)</summary>"),s.push(""),s.push("To request changes:"),s.push("- **Code comments** \u2014 leave inline comments on specific lines. These are always picked up automatically."),s.push("- **General feedback** \u2014 reply with a comment starting with `Rework:` followed by what needs fixing. Comments without the `Rework:` prefix are treated as discussion."),s.push(""),s.push("Example: `Rework: The form validation doesn't handle empty passwords`"),s.push(""),s.push("</details>"),s.join(`
|
|
45
45
|
`)}function Hu(e,t,r,n){let o=[];o.push(`## ${e} \u2014 ${t}`),o.push(""),o.push("### Children"),o.push("");for(let s of r){let i=s.prNumber?` (#${s.prNumber})`:"";o.push(`- ${s.key} \u2014 ${s.summary}${i}`)}if(n==="github"){o.push(""),o.push("### Closes"),o.push("");let s=r.map(i=>i.key).filter(i=>i.startsWith("#"));e.startsWith("#")&&s.unshift(e),s.length>0&&o.push(s.map(i=>`Closes ${i}`).join(", "))}return o.push(""),o.push("---"),o.push("*Created by [Clancy](https://github.com/Pushedskydiver/clancy)*"),o.join(`
|
|
46
|
-
`)}import{execFileSync as Pg}from"node:child_process";function qu(e){let t=e.trim().replace(/\.git$/,""),r=t.match(/^git@([^:]+):(.+)$/),n=t.match(/^(?:https?|ssh):\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+)$/),o=r?.[1]??n?.[1],s=r?.[2]??n?.[2];if(!(!o||!s))return{hostname:o,path:s}}function Ag(e){let t=qu(e);if(!t)return{host:"unknown",url:e};let{hostname:r,path:n}=t;switch(Eg(r)){case"github":{let s=n.split("/");return s.length>=2?{host:"github",owner:s[0],repo:s[1],hostname:r}:{host:"unknown",url:e}}case"gitlab":return{host:"gitlab",projectPath:n,hostname:r};case"bitbucket":{let s=n.match(/^scm\/([^/]+)\/(.+)$/);if(s)return{host:"bitbucket-server",projectKey:s[1],repoSlug:s[2],hostname:r};let i=n.split("/");return i.length>=2?{host:"bitbucket",workspace:i[0],repoSlug:i[1],hostname:r}:{host:"unknown",url:e}}case"azure":return{host:"azure",url:e};default:return{host:"unknown",url:e}}}function Eg(e){let t=e.toLowerCase();return t==="github.com"||t.includes("github")?"github":t==="gitlab.com"||t.includes("gitlab")?"gitlab":t==="bitbucket.org"||t.includes("bitbucket")?"bitbucket":t.includes("dev.azure")||t.includes("visualstudio")?"azure":"unknown"}function ce(e){let t;try{t=Pg("git",["remote","get-url","origin"],{encoding:"utf8"}).trim()}catch{return{host:"none"}}return t?e?Rg(t,e):Ag(t):{host:"none"}}function Rg(e,t){let r=qu(e);if(!r)return{host:"unknown",url:e};let{hostname:n,path:o}=r;switch(t.toLowerCase()){case"github":{let s=o.split("/");return s.length>=2?{host:"github",owner:s[0],repo:s[1],hostname:n}:{host:"unknown",url:e}}case"gitlab":return{host:"gitlab",projectPath:o,hostname:n};case"bitbucket":{let s=o.split("/");return s.length>=2?{host:"bitbucket",workspace:s[0],repoSlug:s[1],hostname:n}:{host:"unknown",url:e}}case"bitbucket-server":{let s=o.match(/^scm\/([^/]+)\/(.+)$/);if(s)return{host:"bitbucket-server",projectKey:s[1],repoSlug:s[2],hostname:n};let i=o.split("/");return i.length>=2?{host:"bitbucket-server",projectKey:i[0],repoSlug:i[1],hostname:n}:{host:"unknown",url:e}}default:return{host:"unknown",url:e}}}function St(e,t){if(t)return t.replace(/\/$/,"");switch(e.host){case"github":return e.hostname==="github.com"?"https://api.github.com":`https://${e.hostname}/api/v3`;case"gitlab":return`https://${e.hostname}/api/v4`;case"bitbucket":return"https://api.bitbucket.org/2.0";case"bitbucket-server":return`https://${e.hostname}/rest/api/1.0`;default:return}}var Cg=I({id:xe(),links:I({html:w(I({href:w(_())}))}),participants:K(I({state:w(_()),role:_()}))}),Ju=I({values:K(Cg)}),Ng=I({content:I({raw:_()}),inline:w(I({path:w(_())})),created_on:_(),user:w(I({nickname:w(_())}))}),ga=I({values:K(Ng)}),Og=I({id:xe(),links:I({self:w(K(I({href:w(_())})))}),reviewers:K(I({status:_()}))}),Ku=I({values:K(Og)}),Lg=I({text:_(),anchor:w(I({path:w(_())})),createdDate:xe(),author:w(I({slug:w(_())}))}),Zg=I({action:_(),comment:w(Lg)}),ya=I({values:K(Zg)});async function Ae(e,t,r,n,o){let s=new AbortController,i=setTimeout(()=>s.abort(),3e4);try{let a=await fetch(e,{method:"POST",headers:{...t,"Content-Type":"application/json"},body:JSON.stringify(r),signal:s.signal});if(!a.ok){let p=await a.text().catch(()=>""),f=o?.(a.status,p)??!1;return{ok:!1,error:`HTTP ${a.status}${p?`: ${p.slice(0,200)}`:""}`,alreadyExists:f}}let u=await a.json(),l=n(u);return!l.url&&!l.number?{ok:!1,error:"PR created but response missing URL and number"}:{ok:!0,url:l.url,number:l.number}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a)}}finally{clearTimeout(i)}}function Ge(e,t){return`Basic ${Buffer.from(`${e}:${t}`).toString("base64")}`}function ie(e){return e.trim().toLowerCase().startsWith("rework:")}function Ee(e){return e.trim().replace(/^rework:\s*/i,"").trim()}async function Yu(e,t,r,n,o,s,i,a){return Ae(`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests`,{Authorization:Ge(e,t)},{title:i,description:a,source:{branch:{name:o}},destination:{branch:{name:s}},close_source_branch:!0},u=>{let l=u;return{url:l.links?.html?.href??"",number:l.id??0}},(u,l)=>u===409&&l.includes("already exists"))}async function Gu(e,t,r,n,o,s,i,a){return Ae(`${t}/projects/${r}/repos/${n}/pull-requests`,{Authorization:`Bearer ${e}`},{title:i,description:a,fromRef:{id:`refs/heads/${o}`,repository:{slug:n,project:{key:r}}},toRef:{id:`refs/heads/${s}`,repository:{slug:n,project:{key:r}}}},u=>{let l=u;return{url:l.links?.self?.[0]?.href??"",number:l.id??0}},(u,l)=>u===409&&(l.includes("already exists")||l.includes("Only one pull request")))}async function Wu(e,t,r,n,o,s){try{return(await fetch(`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${o}/comments`,{method:"POST",headers:{Authorization:Ge(e,t),"Content-Type":"application/json"},body:JSON.stringify({content:{raw:s}})})).ok}catch{return!1}}async function Vu(e,t,r,n,o,s){try{return(await fetch(`${t}/projects/${r}/repos/${n}/pull-requests/${o}/comments`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({text:s})})).ok}catch{return!1}}async function Qu(e,t,r,n,o,s){try{let i=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests?q=source.branch.name="${o}"&state=OPEN`,a=await fetch(i,{headers:{Authorization:Ge(e,t)}});if(!a.ok)return;let u=Ju.parse(await a.json());if(u.values.length===0)return;let l=u.values[0],p=l.links.html?.href??"",f=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${l.id}/comments?pagelen=100`,m=await fetch(f,{headers:{Authorization:Ge(e,t)}});if(!m.ok)return;let h=ga.parse(await m.json()),b=s?h.values.filter(Y=>Y.created_on>s):h.values,y=b.some(Y=>Y.inline!=null),z=b.some(Y=>Y.inline==null&&ie(Y.content.raw));return{changesRequested:y||z,prNumber:l.id,prUrl:p}}catch{return}}async function Xu(e,t,r,n,o,s){try{let i=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${o}/comments?pagelen=100`,a=await fetch(i,{headers:{Authorization:Ge(e,t)}});if(!a.ok)return[];let u=ga.parse(await a.json()),l=s?u.values.filter(f=>f.created_on>s):u.values,p=[];for(let f of l)if(f.inline!=null){let m=f.inline.path?`[${f.inline.path}] `:"";p.push(`${m}${f.content.raw}`)}else ie(f.content.raw)&&p.push(Ee(f.content.raw));return p}catch{return[]}}async function el(e,t,r,n,o,s){try{let i=`${t}/projects/${r}/repos/${n}/pull-requests?state=OPEN&at=refs/heads/${o}`,a=await fetch(i,{headers:{Authorization:`Bearer ${e}`}});if(!a.ok)return;let u=Ku.parse(await a.json());if(u.values.length===0)return;let l=u.values[0],p=l.links.self?.[0]?.href??"",f=`${t}/projects/${r}/repos/${n}/pull-requests/${l.id}/activities?limit=100`,m=await fetch(f,{headers:{Authorization:`Bearer ${e}`}});if(!m.ok)return;let h=ya.parse(await m.json()),b=s?Date.parse(s):void 0,y=h.values.filter(C=>C.action==="COMMENTED"&&C.comment&&(b==null||C.comment.createdDate>b)),z=y.some(C=>C.comment.anchor!=null),H=y.some(C=>C.comment.anchor==null&&ie(C.comment.text));return{changesRequested:z||H,prNumber:l.id,prUrl:p}}catch{return}}async function tl(e,t,r,n,o,s){try{let i=`${t}/projects/${r}/repos/${n}/pull-requests/${o}/activities?limit=100`,a=await fetch(i,{headers:{Authorization:`Bearer ${e}`}});if(!a.ok)return[];let u=ya.parse(await a.json()),l=s?Date.parse(s):void 0,p=[];for(let f of u.values){if(f.action!=="COMMENTED"||!f.comment||l!=null&&f.comment.createdDate<=l)continue;let m=f.comment;if(m.anchor!=null){let h=m.anchor.path?`[${m.anchor.path}] `:"";p.push(`${h}${m.text}`)}else ie(m.text)&&p.push(Ee(m.text))}return p}catch{return[]}}var jg=c.object({number:c.number(),title:c.string(),body:c.optional(c.nullable(c.string())),pull_request:c.optional(c.unknown()),milestone:c.optional(c.nullable(c.object({title:c.string()}))),labels:c.optional(c.array(c.object({name:c.optional(c.string())})))}),rl=c.array(jg),Dg=c.object({id:c.number(),body:c.optional(c.nullable(c.string())),created_at:c.string(),user:c.optional(c.object({login:c.string()}))}),ba=c.array(Dg),Mg=c.object({number:c.number(),html_url:c.string(),state:c.string()}),nl=c.array(Mg),Bg=c.object({state:c.string(),user:c.object({login:c.string()}),submitted_at:c.string()}),ol=c.array(Bg),Ug=c.object({body:c.optional(c.nullable(c.string())),path:c.optional(c.string()),created_at:c.optional(c.string()),user:c.optional(c.object({login:c.string()}))}),_a=c.array(Ug);var D="https://api.github.com";async function Ar(e,t,r,n){try{let o=new AbortController,s=setTimeout(()=>o.abort(),1e4),i=await fetch(e,{headers:t,signal:o.signal});if(clearTimeout(s),i.ok)return{ok:!0};let a=r[i.status];return a?{ok:!1,error:a}:{ok:!1,error:`\u2717 HTTP ${i.status}`}}catch{return{ok:!1,error:n}}}function U(e){return{Authorization:`Bearer ${e}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"}}function Se(e){return{Authorization:`Basic ${e}`,Accept:"application/json"}}function Er(e){return e?.trimStart().startsWith("[clancy]")??!1}async function sl(e,t,r,n,o=D,s){try{let i=U(e),a=await fetch(`${o}/repos/${t}/pulls?head=${n}:${r}&state=open`,{headers:i});if(!a.ok)return;let u=nl.parse(await a.json());if(u.length===0)return;let l=u[0],p=s?`&since=${s}`:"",[f,m]=await Promise.all([fetch(`${o}/repos/${t}/pulls/${l.number}/comments?per_page=100${p}`,{headers:i}),fetch(`${o}/repos/${t}/issues/${l.number}/comments?per_page=100${p}`,{headers:i})]);if(!f.ok||!m.ok)return;let h=_a.parse(await f.json()),b=ba.parse(await m.json()),y=h.filter(O=>!Er(O.body)),z=b.filter(O=>!Er(O.body)),H=y.length>0,Y=z.some(O=>O.body&&ie(O.body)),C=H||Y,M;if(!C)try{let O=await fetch(`${o}/repos/${t}/pulls/${l.number}/reviews?per_page=100`,{headers:i});if(O.ok){let Da=ol.parse(await O.json()),jt=new Map;for(let le of Da)le.state==="PENDING"||le.state==="DISMISSED"||jt.set(le.user.login,le.state);let Ma=[...jt.entries()].filter(([,le])=>le==="CHANGES_REQUESTED");Ma.length>0&&(C=!0,M=Ma.map(([le])=>le))}}catch{}return{changesRequested:C,prNumber:l.number,prUrl:l.html_url,reviewers:M}}catch{return}}async function il(e,t,r,n=D,o){try{let s=U(e),i=o?`&since=${o}`:"",[a,u]=await Promise.all([fetch(`${n}/repos/${t}/pulls/${r}/comments?per_page=100${i}`,{headers:s}),fetch(`${n}/repos/${t}/issues/${r}/comments?per_page=100${i}`,{headers:s})]);if(!a.ok||!u.ok)return[];let l=_a.parse(await a.json()),p=ba.parse(await u.json()),f=l.filter(b=>!Er(b.body)),m=p.filter(b=>!Er(b.body)),h=[];for(let b of f){if(!b.body)continue;let y=b.path?`[${b.path}] `:"";h.push(`${y}${b.body}`)}for(let b of m)b.body&&ie(b.body)&&h.push(Ee(b.body));return h}catch{return[]}}async function al(e,t,r,n,o=D){try{return(await fetch(`${o}/repos/${t}/issues/${r}/comments`,{method:"POST",headers:{...U(e),"Content-Type":"application/json"},body:JSON.stringify({body:n})})).ok}catch{return!1}}async function cl(e,t,r,n,o=D){try{return(await fetch(`${o}/repos/${t}/pulls/${r}/requested_reviewers`,{method:"POST",headers:{...U(e),"Content-Type":"application/json"},body:JSON.stringify({reviewers:n})})).ok}catch{return!1}}async function ul(e,t,r,n,o,s,i=D){return Ae(`${i}/repos/${t}/pulls`,U(e),{title:o,head:r,base:n,body:s},a=>{let u=a;return{url:u.html_url??"",number:u.number??0}},(a,u)=>a===422&&u.includes("already exists"))}var Fg=I({iid:xe(),web_url:_(),detailed_merge_status:w(_())}),ll=K(Fg),Hg=I({body:_(),resolvable:Je(),resolved:w(Je()),system:Je(),type:w(Sr(_())),created_at:w(_()),position:w(I({new_path:w(_())})),author:w(I({username:_()}))}),qg=I({id:w(_()),notes:K(Hg)}),wa=K(qg);async function pl(e,t,r,n,o,s,i){let a=encodeURIComponent(r);return Ae(`${t}/projects/${a}/merge_requests`,{"PRIVATE-TOKEN":e},{source_branch:n,target_branch:o,title:s,description:i,remove_source_branch:!0},u=>{let l=u;return{url:l.web_url??"",number:l.iid??0}},(u,l)=>u===409&&l.includes("already exists"))}async function fl(e,t,r,n,o){try{let s=encodeURIComponent(r);return(await fetch(`${t}/projects/${s}/merge_requests/${n}/notes`,{method:"POST",headers:{"PRIVATE-TOKEN":e,"Content-Type":"application/json"},body:JSON.stringify({body:o})})).ok}catch{return!1}}async function dl(e,t,r,n,o){let s=encodeURIComponent(r),i=0;for(let a of o)try{(await fetch(`${t}/projects/${s}/merge_requests/${n}/discussions/${a}`,{method:"PUT",headers:{"PRIVATE-TOKEN":e,"Content-Type":"application/json"},body:JSON.stringify({resolved:!0})})).ok&&i++}catch{}return i}async function ml(e,t,r,n,o){try{let s=encodeURIComponent(r),i=`${t}/projects/${s}/merge_requests?source_branch=${n}&state=opened`,a=await fetch(i,{headers:{"PRIVATE-TOKEN":e}});if(!a.ok)return;let u=ll.parse(await a.json());if(u.length===0)return;let l=u[0],p=`${t}/projects/${s}/merge_requests/${l.iid}/discussions?per_page=100`,f=await fetch(p,{headers:{"PRIVATE-TOKEN":e}});if(!f.ok)return;let m=wa.parse(await f.json()),h=!1;for(let b of m){for(let y of b.notes)if(!y.system&&!(o&&y.created_at&&y.created_at<=o)){if(y.type==="DiffNote"&&y.resolvable!==!1&&y.resolved!==!0){h=!0;break}if(ie(y.body)){h=!0;break}}if(h)break}return{changesRequested:h,prNumber:l.iid,prUrl:l.web_url}}catch{return}}async function hl(e,t,r,n,o){try{let s=encodeURIComponent(r),i=`${t}/projects/${s}/merge_requests/${n}/discussions?per_page=100`,a=await fetch(i,{headers:{"PRIVATE-TOKEN":e}});if(!a.ok)return{comments:[],discussionIds:[]};let u=wa.parse(await a.json()),l=[],p=[];for(let f of u){let m=!1;for(let h of f.notes)if(!h.system&&!(o&&h.created_at&&h.created_at<=o))if(h.type==="DiffNote"&&h.resolvable!==!1&&h.resolved!==!0){let b=h.position?.new_path?`[${h.position.new_path}] `:"";l.push(`${b}${h.body}`),m=!0}else ie(h.body)&&(l.push(Ee(h.body)),m=!0);m&&f.id&&p.push(f.id)}return{comments:l,discussionIds:p}}catch{return{comments:[],discussionIds:[]}}}function vt(e,t){let r=Z(e);switch(t.host){case"github":if(r.GITHUB_TOKEN)return{token:r.GITHUB_TOKEN};break;case"gitlab":if(r.GITLAB_TOKEN)return{token:r.GITLAB_TOKEN};break;case"bitbucket":if(r.BITBUCKET_USER&&r.BITBUCKET_TOKEN)return{token:r.BITBUCKET_TOKEN,username:r.BITBUCKET_USER};break;case"bitbucket-server":if(r.BITBUCKET_TOKEN)return{token:r.BITBUCKET_TOKEN};break}}async function Re(e,t,r,n,o,s){let i=vt(e,t);if(!i)return;let a=St(t,Z(e).CLANCY_GIT_API_URL);if(a)switch(t.host){case"github":return ul(i.token,`${t.owner}/${t.repo}`,r,n,o,s,a);case"gitlab":return pl(i.token,a,t.projectPath,r,n,o,s);case"bitbucket":return Yu(i.username,i.token,t.workspace,t.repoSlug,r,n,o,s);case"bitbucket-server":return Gu(i.token,a,t.projectKey,t.repoSlug,r,n,o,s);default:return}}function Rr(e,t,r){let n=encodeURIComponent(t),o=encodeURIComponent(r);if(e.host==="github")return`https://${e.hostname}/${e.owner}/${e.repo}/compare/${o}...${n}`;if(e.host==="gitlab")return`https://${e.hostname}/${e.projectPath}/-/merge_requests/new?merge_request[source_branch]=${n}&merge_request[target_branch]=${o}`;if(e.host==="bitbucket")return`https://${e.hostname}/${e.workspace}/${e.repoSlug}/pull-requests/new?source=${n}&dest=${o}`;if(e.host==="bitbucket-server")return`https://${e.hostname}/projects/${e.projectKey}/repos/${e.repoSlug}/pull-requests?create&sourceBranch=refs/heads/${n}&targetBranch=refs/heads/${o}`}function $a(e,t){let r=Ua(e),n=Xe(e);if(r)return Mt(e)?!0:(console.log(v(`\u26A0 Epic branch ${e} exists on remote but could not be fetched.`)),!1);if(n)return console.log(Oe(`\u2717 Epic branch ${e} exists locally but not on remote.`)),console.log(v(" This may contain work from a previous Clancy version that squash-merged locally.")),console.log($(" To preserve this work, push it manually:")),console.log($(` git push -u origin ${e}`)),console.log($(" Then re-run /clancy:once to continue.")),!1;try{if(gl("git",["fetch","origin",t],{encoding:"utf8",stdio:["pipe","pipe","pipe"],timeout:15e3}),gl("git",["checkout","-b",e,`origin/${t}`],{encoding:"utf8"}),et(e))console.log(N(` \u2713 Created epic branch ${e}`));else return console.log(v(`\u26A0 Created ${e} locally but could not push to origin.`)),!1;return!0}catch(o){return console.log(Oe(`\u2717 Could not create epic branch: ${o instanceof Error?o.message:String(o)}`)),!1}}async function xa(e,t,r,n,o,s=!1,i,a,u){if(!et(r)){console.log(v(`\u26A0 Could not push ${r} to origin.`)),console.log($(" The branch is still available locally. Push manually:")),console.log($(` git push -u origin ${r}`)),s||R(process.cwd(),t.key,t.title,"PUSH_FAILED",void 0,i),L(n);let y=Ce(Date.now()-o);return console.log(""),console.log(v(`\u26A0 ${t.key} implemented but push failed`)+$(` (${y})`)),!1}console.log(N(` \u2713 Pushed ${r}`));let p;try{let y=Kg(process.cwd(),".clancy","verify-attempt.txt"),z=Jg(y,"utf8").trim(),H=parseInt(z,10);H>0&&(p=`Verification checks did not fully pass (${H} attempt(s)). Review carefully.`)}catch{}let f=Z(e).CLANCY_GIT_PLATFORM,m=ce(f),h=`feat(${t.key}): ${t.title}`,b=Ye(e,{key:t.key,title:t.title,description:t.description,provider:e.provider},n,p,u);if(m.host!=="none"&&m.host!=="unknown"&&m.host!=="azure"){let y=await Re(e,m,r,n,h,b);if(y?.ok)console.log(N(` \u2713 PR created: ${y.url}`)),s||R(process.cwd(),t.key,t.title,"PR_CREATED",y.number,i);else if(y&&!y.ok&&y.alreadyExists)console.log(v(` \u26A0 A PR/MR already exists for ${r}. Branch pushed.`)),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i);else if(y&&!y.ok){console.log(v(` \u26A0 PR/MR creation failed: ${y.error}`));let z=Rr(m,r,n);console.log(z?$(` Create one manually: ${z}`):$(" Branch pushed \u2014 create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i)}else{let z=Rr(m,r,n);console.log(z?$(` Create a PR: ${z}`):$(" Branch pushed to remote. Create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i)}}else m.host==="none"?(console.log(v(`\u26A0 No git remote configured. Branch available locally: ${r}`)),s||R(process.cwd(),t.key,t.title,"LOCAL",void 0,i)):(console.log($(" Branch pushed to remote. Create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i));if(e.provider!=="github"&&a){let y=e.env.CLANCY_STATUS_REVIEW??e.env.CLANCY_STATUS_DONE;y&&await a.transitionTicket(t,y)}return L(n),!0}async function yl(e,t,r,n,o,s){console.log(""),console.log(N(`\u{1F389} All children of ${t} are done!`)),console.log($(` Creating epic PR: ${n} \u2192 ${o}`));let i=j(process.cwd(),"PR_CREATED"),a=j(process.cwd(),"DONE"),u=j(process.cwd(),"REWORK"),l=j(process.cwd(),"PUSHED"),p=[...i,...a,...u,...l].filter(H=>H.parent===t),f=Z(e).CLANCY_GIT_PLATFORM,m=ce(f),h=`feat(${t}): ${r}`,b=Hu(t,r,p,e.provider);if(m.host==="none"||m.host==="unknown"||m.host==="azure")return console.log(v("\u26A0 Cannot create epic PR \u2014 no supported git remote detected.")),console.log($(` Push manually: git push origin ${n}`)),console.log($(` Then create a PR targeting ${o}`)),!1;let y=await Re(e,m,n,o,h,b);if(y?.ok){if(console.log(N(` \u2713 Epic PR created: ${y.url}`)),R(process.cwd(),t,r,"EPIC_PR_CREATED",y.number),e.provider!=="github"&&s){let H=e.env.CLANCY_STATUS_REVIEW??e.env.CLANCY_STATUS_DONE;H&&await s.transitionTicket({key:t,title:r,description:"",parentInfo:"none",blockers:"None"},H)}return!0}if(y&&!y.ok&&y.alreadyExists)return console.log(v(` \u26A0 An epic PR already exists for ${n}.`)),R(process.cwd(),t,r,"EPIC_PR_CREATED"),!0;console.log(v(`\u26A0 Epic PR creation failed: ${y?.error??"unknown error"}`)),console.log($(" Create it manually:")),console.log($(` Branch: ${n} \u2192 ${o}`));let z=Rr(m,n,o);return z&&console.log($(` ${z}`)),!1}async function bl(e){let t=e.board,r=e.ticket,n=e.ticketBranch,o=e.targetBranch,s=e.baseBranch,i=e.hasParent,a=e.isRework??!1;e.originalBranch=Ba();let u=!1;if(i&&!a){let p=await t.fetchChildrenStatus(r.parentInfo,r.linearIssueId);p&&p.total===1&&(u=!0)}e.skipEpicBranch=u;let l=i&&!u?o:s;if(e.effectiveTarget=l,a){if(i&&!u){if(!$a(o,s))return e.originalBranch&&L(e.originalBranch),!1}else Yr(l,s);Mt(n)?L(n):(L(l),L(n,!0))}else if(i&&!u){if(!$a(o,s))return e.originalBranch&&L(e.originalBranch),!1;L(o),L(n,!0)}else Yr(s,s),L(s),L(n,!0);try{Ja(e.cwd,{pid:process.pid,ticketKey:r.key,ticketTitle:r.title,ticketBranch:n,targetBranch:l,parentKey:r.parentInfo,description:(r.description??"").slice(0,2e3),startedAt:new Date().toISOString()}),e.lockOwner=!0}catch{console.log($(" (warning: could not write lock file \u2014 crash recovery disabled)"))}return!0}function Yg(e){return e.includes("hooks.slack.com")}function Gg(e){return JSON.stringify({text:e})}function Wg(e){return JSON.stringify({type:"message",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:{$schema:"http://adaptivecards.io/schemas/adaptive-card.json",type:"AdaptiveCard",version:"1.4",body:[{type:"TextBlock",text:e,wrap:!0}]}}]})}async function _l(e,t){let r=Yg(e)?Gg(t):Wg(t);try{let n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:r});n.ok||console.warn(`\u26A0 Notification failed: HTTP ${n.status}`)}catch{console.warn("\u26A0 Notification failed: could not reach webhook")}}async function wl(e){let t=e.config,r=e.ticket,n=Ce(Date.now()-e.startTime);console.log(""),console.log(N(`\u{1F3C1} ${r.key} complete`)+$(` (${n})`)),console.log($(` "Bake 'em away, toys."`));let o=t.env.CLANCY_NOTIFY_WEBHOOK;return o&&await _l(o,`\u2713 Clancy completed [${r.key}] ${r.title}`),!0}import{appendFileSync as Vg,mkdirSync as Qg}from"node:fs";import{dirname as Xg,join as ey}from"node:path";function $l(e,t,r,n=6600){let o=ey(e,".clancy","costs.log");Qg(Xg(o),{recursive:!0});let s=new Date(r).getTime(),i=Date.now(),a=Number.isNaN(s)?0:Math.max(0,i-s),u=Math.round(a/6e4),l=Math.round(u*n),f=`${new Date().toISOString()} | ${t} | ${u}min | ~${l} tokens (estimated)
|
|
46
|
+
`)}import{execFileSync as Pg}from"node:child_process";function qu(e){let t=e.trim().replace(/\.git$/,""),r=t.match(/^git@([^:]+):(.+)$/),n=t.match(/^(?:https?|ssh):\/\/(?:[^@]+@)?([^/:]+)(?::\d+)?\/(.+)$/),o=r?.[1]??n?.[1],s=r?.[2]??n?.[2];if(!(!o||!s))return{hostname:o,path:s}}function Ag(e){let t=qu(e);if(!t)return{host:"unknown",url:e};let{hostname:r,path:n}=t;switch(Eg(r)){case"github":{let s=n.split("/");return s.length>=2?{host:"github",owner:s[0],repo:s[1],hostname:r}:{host:"unknown",url:e}}case"gitlab":return{host:"gitlab",projectPath:n,hostname:r};case"bitbucket":{let s=n.match(/^scm\/([^/]+)\/(.+)$/);if(s)return{host:"bitbucket-server",projectKey:s[1],repoSlug:s[2],hostname:r};let i=n.split("/");return i.length>=2?{host:"bitbucket",workspace:i[0],repoSlug:i[1],hostname:r}:{host:"unknown",url:e}}case"azure":return{host:"azure",url:e};default:return{host:"unknown",url:e}}}function Eg(e){let t=e.toLowerCase();return t==="github.com"||t.includes("github")?"github":t==="gitlab.com"||t.includes("gitlab")?"gitlab":t==="bitbucket.org"||t.includes("bitbucket")?"bitbucket":t.includes("dev.azure")||t.includes("visualstudio")?"azure":"unknown"}function ce(e){let t;try{t=Pg("git",["remote","get-url","origin"],{encoding:"utf8"}).trim()}catch{return{host:"none"}}return t?e?Rg(t,e):Ag(t):{host:"none"}}function Rg(e,t){let r=qu(e);if(!r)return{host:"unknown",url:e};let{hostname:n,path:o}=r;switch(t.toLowerCase()){case"github":{let s=o.split("/");return s.length>=2?{host:"github",owner:s[0],repo:s[1],hostname:n}:{host:"unknown",url:e}}case"gitlab":return{host:"gitlab",projectPath:o,hostname:n};case"bitbucket":{let s=o.split("/");return s.length>=2?{host:"bitbucket",workspace:s[0],repoSlug:s[1],hostname:n}:{host:"unknown",url:e}}case"bitbucket-server":{let s=o.match(/^scm\/([^/]+)\/(.+)$/);if(s)return{host:"bitbucket-server",projectKey:s[1],repoSlug:s[2],hostname:n};let i=o.split("/");return i.length>=2?{host:"bitbucket-server",projectKey:i[0],repoSlug:i[1],hostname:n}:{host:"unknown",url:e}}default:return{host:"unknown",url:e}}}function St(e,t){if(t)return t.replace(/\/$/,"");switch(e.host){case"github":return e.hostname==="github.com"?"https://api.github.com":`https://${e.hostname}/api/v3`;case"gitlab":return`https://${e.hostname}/api/v4`;case"bitbucket":return"https://api.bitbucket.org/2.0";case"bitbucket-server":return`https://${e.hostname}/rest/api/1.0`;default:return}}var Cg=I({id:xe(),links:I({html:w(I({href:w(_())}))}),participants:K(I({state:w(_()),role:_()}))}),Ju=I({values:K(Cg)}),Ng=I({content:I({raw:_()}),inline:w(I({path:w(_())})),created_on:_(),user:w(I({nickname:w(_())}))}),ga=I({values:K(Ng)}),Og=I({id:xe(),links:I({self:w(K(I({href:w(_())})))}),reviewers:K(I({status:_()}))}),Ku=I({values:K(Og)}),Lg=I({text:_(),anchor:w(I({path:w(_())})),createdDate:xe(),author:w(I({slug:w(_())}))}),Zg=I({action:_(),comment:w(Lg)}),ya=I({values:K(Zg)});async function Ae(e,t,r,n,o){let s=new AbortController,i=setTimeout(()=>s.abort(),3e4);try{let a=await fetch(e,{method:"POST",headers:{...t,"Content-Type":"application/json"},body:JSON.stringify(r),signal:s.signal});if(!a.ok){let p=await a.text().catch(()=>""),f=o?.(a.status,p)??!1;return{ok:!1,error:`HTTP ${a.status}${p?`: ${p.slice(0,200)}`:""}`,alreadyExists:f}}let u=await a.json(),l=n(u);return!l.url&&!l.number?{ok:!1,error:"PR created but response missing URL and number"}:{ok:!0,url:l.url,number:l.number}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a)}}finally{clearTimeout(i)}}function Ge(e,t){return`Basic ${Buffer.from(`${e}:${t}`).toString("base64")}`}function ie(e){return e.trim().toLowerCase().startsWith("rework:")}function Ee(e){return e.trim().replace(/^rework:\s*/i,"").trim()}async function Yu(e,t,r,n,o,s,i,a){return Ae(`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests`,{Authorization:Ge(e,t)},{title:i,description:a,source:{branch:{name:o}},destination:{branch:{name:s}},close_source_branch:!0},u=>{let l=u;return{url:l.links?.html?.href??"",number:l.id??0}},(u,l)=>u===409&&l.includes("already exists"))}async function Gu(e,t,r,n,o,s,i,a){return Ae(`${t}/projects/${r}/repos/${n}/pull-requests`,{Authorization:`Bearer ${e}`},{title:i,description:a,fromRef:{id:`refs/heads/${o}`,repository:{slug:n,project:{key:r}}},toRef:{id:`refs/heads/${s}`,repository:{slug:n,project:{key:r}}}},u=>{let l=u;return{url:l.links?.self?.[0]?.href??"",number:l.id??0}},(u,l)=>u===409&&(l.includes("already exists")||l.includes("Only one pull request")))}async function Wu(e,t,r,n,o,s){try{return(await fetch(`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${o}/comments`,{method:"POST",headers:{Authorization:Ge(e,t),"Content-Type":"application/json"},body:JSON.stringify({content:{raw:s}})})).ok}catch{return!1}}async function Vu(e,t,r,n,o,s){try{return(await fetch(`${t}/projects/${r}/repos/${n}/pull-requests/${o}/comments`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({text:s})})).ok}catch{return!1}}async function Qu(e,t,r,n,o,s){try{let i=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests?q=source.branch.name="${o}"&state=OPEN`,a=await fetch(i,{headers:{Authorization:Ge(e,t)}});if(!a.ok)return;let u=Ju.parse(await a.json());if(u.values.length===0)return;let l=u.values[0],p=l.links.html?.href??"",f=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${l.id}/comments?pagelen=100`,m=await fetch(f,{headers:{Authorization:Ge(e,t)}});if(!m.ok)return;let h=ga.parse(await m.json()),b=s?h.values.filter(Y=>Y.created_on>s):h.values,y=b.some(Y=>Y.inline!=null),z=b.some(Y=>Y.inline==null&&ie(Y.content.raw));return{changesRequested:y||z,prNumber:l.id,prUrl:p}}catch{return}}async function Xu(e,t,r,n,o,s){try{let i=`https://api.bitbucket.org/2.0/repositories/${r}/${n}/pullrequests/${o}/comments?pagelen=100`,a=await fetch(i,{headers:{Authorization:Ge(e,t)}});if(!a.ok)return[];let u=ga.parse(await a.json()),l=s?u.values.filter(f=>f.created_on>s):u.values,p=[];for(let f of l)if(f.inline!=null){let m=f.inline.path?`[${f.inline.path}] `:"";p.push(`${m}${f.content.raw}`)}else ie(f.content.raw)&&p.push(Ee(f.content.raw));return p}catch{return[]}}async function el(e,t,r,n,o,s){try{let i=`${t}/projects/${r}/repos/${n}/pull-requests?state=OPEN&at=refs/heads/${o}`,a=await fetch(i,{headers:{Authorization:`Bearer ${e}`}});if(!a.ok)return;let u=Ku.parse(await a.json());if(u.values.length===0)return;let l=u.values[0],p=l.links.self?.[0]?.href??"",f=`${t}/projects/${r}/repos/${n}/pull-requests/${l.id}/activities?limit=100`,m=await fetch(f,{headers:{Authorization:`Bearer ${e}`}});if(!m.ok)return;let h=ya.parse(await m.json()),b=s?Date.parse(s):void 0,y=h.values.filter(C=>C.action==="COMMENTED"&&C.comment&&(b==null||C.comment.createdDate>b)),z=y.some(C=>C.comment.anchor!=null),H=y.some(C=>C.comment.anchor==null&&ie(C.comment.text));return{changesRequested:z||H,prNumber:l.id,prUrl:p}}catch{return}}async function tl(e,t,r,n,o,s){try{let i=`${t}/projects/${r}/repos/${n}/pull-requests/${o}/activities?limit=100`,a=await fetch(i,{headers:{Authorization:`Bearer ${e}`}});if(!a.ok)return[];let u=ya.parse(await a.json()),l=s?Date.parse(s):void 0,p=[];for(let f of u.values){if(f.action!=="COMMENTED"||!f.comment||l!=null&&f.comment.createdDate<=l)continue;let m=f.comment;if(m.anchor!=null){let h=m.anchor.path?`[${m.anchor.path}] `:"";p.push(`${h}${m.text}`)}else ie(m.text)&&p.push(Ee(m.text))}return p}catch{return[]}}var jg=c.object({number:c.number(),title:c.string(),body:c.optional(c.nullable(c.string())),pull_request:c.optional(c.unknown()),milestone:c.optional(c.nullable(c.object({title:c.string()}))),labels:c.optional(c.array(c.object({name:c.optional(c.string())})))}),rl=c.array(jg),Dg=c.object({id:c.number(),body:c.optional(c.nullable(c.string())),created_at:c.string(),user:c.optional(c.object({login:c.string()}))}),ba=c.array(Dg),Mg=c.object({number:c.number(),html_url:c.string(),state:c.string()}),nl=c.array(Mg),Bg=c.object({state:c.string(),user:c.object({login:c.string()}),submitted_at:c.string()}),ol=c.array(Bg),Ug=c.object({body:c.optional(c.nullable(c.string())),path:c.optional(c.string()),created_at:c.optional(c.string()),user:c.optional(c.object({login:c.string()}))}),_a=c.array(Ug);var D="https://api.github.com";async function Ar(e,t,r,n){try{let o=new AbortController,s=setTimeout(()=>o.abort(),1e4),i=await fetch(e,{headers:t,signal:o.signal});if(clearTimeout(s),i.ok)return{ok:!0};let a=r[i.status];return a?{ok:!1,error:a}:{ok:!1,error:`\u2717 HTTP ${i.status}`}}catch{return{ok:!1,error:n}}}function U(e){return{Authorization:`Bearer ${e}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"}}function Se(e){return{Authorization:`Basic ${e}`,Accept:"application/json"}}function Er(e){return e?.trimStart().startsWith("[clancy]")??!1}async function sl(e,t,r,n,o=D,s){try{let i=U(e),a=await fetch(`${o}/repos/${t}/pulls?head=${n}:${r}&state=open`,{headers:i});if(!a.ok)return;let u=nl.parse(await a.json());if(u.length===0)return;let l=u[0],p=s?`&since=${s}`:"",[f,m]=await Promise.all([fetch(`${o}/repos/${t}/pulls/${l.number}/comments?per_page=100${p}`,{headers:i}),fetch(`${o}/repos/${t}/issues/${l.number}/comments?per_page=100${p}`,{headers:i})]);if(!f.ok||!m.ok)return;let h=_a.parse(await f.json()),b=ba.parse(await m.json()),y=h.filter(O=>!Er(O.body)),z=b.filter(O=>!Er(O.body)),H=y.length>0,Y=z.some(O=>O.body&&ie(O.body)),C=H||Y,M;if(!C)try{let O=await fetch(`${o}/repos/${t}/pulls/${l.number}/reviews?per_page=100`,{headers:i});if(O.ok){let Da=ol.parse(await O.json()),jt=new Map;for(let le of Da)le.state==="PENDING"||le.state==="DISMISSED"||jt.set(le.user.login,le.state);let Ma=[...jt.entries()].filter(([,le])=>le==="CHANGES_REQUESTED");Ma.length>0&&(C=!0,M=Ma.map(([le])=>le))}}catch{}return{changesRequested:C,prNumber:l.number,prUrl:l.html_url,reviewers:M}}catch{return}}async function il(e,t,r,n=D,o){try{let s=U(e),i=o?`&since=${o}`:"",[a,u]=await Promise.all([fetch(`${n}/repos/${t}/pulls/${r}/comments?per_page=100${i}`,{headers:s}),fetch(`${n}/repos/${t}/issues/${r}/comments?per_page=100${i}`,{headers:s})]);if(!a.ok||!u.ok)return[];let l=_a.parse(await a.json()),p=ba.parse(await u.json()),f=l.filter(b=>!Er(b.body)),m=p.filter(b=>!Er(b.body)),h=[];for(let b of f){if(!b.body)continue;let y=b.path?`[${b.path}] `:"";h.push(`${y}${b.body}`)}for(let b of m)b.body&&ie(b.body)&&h.push(Ee(b.body));return h}catch{return[]}}async function al(e,t,r,n,o=D){try{return(await fetch(`${o}/repos/${t}/issues/${r}/comments`,{method:"POST",headers:{...U(e),"Content-Type":"application/json"},body:JSON.stringify({body:n})})).ok}catch{return!1}}async function cl(e,t,r,n,o=D){try{return(await fetch(`${o}/repos/${t}/pulls/${r}/requested_reviewers`,{method:"POST",headers:{...U(e),"Content-Type":"application/json"},body:JSON.stringify({reviewers:n})})).ok}catch{return!1}}async function ul(e,t,r,n,o,s,i=D){return Ae(`${i}/repos/${t}/pulls`,U(e),{title:o,head:r,base:n,body:s},a=>{let u=a;return{url:u.html_url??"",number:u.number??0}},(a,u)=>a===422&&u.includes("already exists"))}var Fg=I({iid:xe(),web_url:_(),detailed_merge_status:w(_())}),ll=K(Fg),Hg=I({body:_(),resolvable:Je(),resolved:w(Je()),system:Je(),type:w(Sr(_())),created_at:w(_()),position:w(I({new_path:w(_())})),author:w(I({username:_()}))}),qg=I({id:w(_()),notes:K(Hg)}),wa=K(qg);async function pl(e,t,r,n,o,s,i){let a=encodeURIComponent(r);return Ae(`${t}/projects/${a}/merge_requests`,{"PRIVATE-TOKEN":e},{source_branch:n,target_branch:o,title:s,description:i,remove_source_branch:!0},u=>{let l=u;return{url:l.web_url??"",number:l.iid??0}},(u,l)=>u===409&&l.includes("already exists"))}async function fl(e,t,r,n,o){try{let s=encodeURIComponent(r);return(await fetch(`${t}/projects/${s}/merge_requests/${n}/notes`,{method:"POST",headers:{"PRIVATE-TOKEN":e,"Content-Type":"application/json"},body:JSON.stringify({body:o})})).ok}catch{return!1}}async function dl(e,t,r,n,o){let s=encodeURIComponent(r),i=0;for(let a of o)try{(await fetch(`${t}/projects/${s}/merge_requests/${n}/discussions/${a}`,{method:"PUT",headers:{"PRIVATE-TOKEN":e,"Content-Type":"application/json"},body:JSON.stringify({resolved:!0})})).ok&&i++}catch{}return i}async function ml(e,t,r,n,o){try{let s=encodeURIComponent(r),i=`${t}/projects/${s}/merge_requests?source_branch=${n}&state=opened`,a=await fetch(i,{headers:{"PRIVATE-TOKEN":e}});if(!a.ok)return;let u=ll.parse(await a.json());if(u.length===0)return;let l=u[0],p=`${t}/projects/${s}/merge_requests/${l.iid}/discussions?per_page=100`,f=await fetch(p,{headers:{"PRIVATE-TOKEN":e}});if(!f.ok)return;let m=wa.parse(await f.json()),h=!1;for(let b of m){for(let y of b.notes)if(!y.system&&!(o&&y.created_at&&y.created_at<=o)){if(y.type==="DiffNote"&&y.resolvable!==!1&&y.resolved!==!0){h=!0;break}if(ie(y.body)){h=!0;break}}if(h)break}return{changesRequested:h,prNumber:l.iid,prUrl:l.web_url}}catch{return}}async function hl(e,t,r,n,o){try{let s=encodeURIComponent(r),i=`${t}/projects/${s}/merge_requests/${n}/discussions?per_page=100`,a=await fetch(i,{headers:{"PRIVATE-TOKEN":e}});if(!a.ok)return{comments:[],discussionIds:[]};let u=wa.parse(await a.json()),l=[],p=[];for(let f of u){let m=!1;for(let h of f.notes)if(!h.system&&!(o&&h.created_at&&h.created_at<=o))if(h.type==="DiffNote"&&h.resolvable!==!1&&h.resolved!==!0){let b=h.position?.new_path?`[${h.position.new_path}] `:"";l.push(`${b}${h.body}`),m=!0}else ie(h.body)&&(l.push(Ee(h.body)),m=!0);m&&f.id&&p.push(f.id)}return{comments:l,discussionIds:p}}catch{return{comments:[],discussionIds:[]}}}function vt(e,t){let r=Z(e);switch(t.host){case"github":if(r.GITHUB_TOKEN)return{token:r.GITHUB_TOKEN};break;case"gitlab":if(r.GITLAB_TOKEN)return{token:r.GITLAB_TOKEN};break;case"bitbucket":if(r.BITBUCKET_USER&&r.BITBUCKET_TOKEN)return{token:r.BITBUCKET_TOKEN,username:r.BITBUCKET_USER};break;case"bitbucket-server":if(r.BITBUCKET_TOKEN)return{token:r.BITBUCKET_TOKEN};break}}async function Re(e,t,r,n,o,s){let i=vt(e,t);if(!i)return;let a=St(t,Z(e).CLANCY_GIT_API_URL);if(a)switch(t.host){case"github":return ul(i.token,`${t.owner}/${t.repo}`,r,n,o,s,a);case"gitlab":return pl(i.token,a,t.projectPath,r,n,o,s);case"bitbucket":return Yu(i.username,i.token,t.workspace,t.repoSlug,r,n,o,s);case"bitbucket-server":return Gu(i.token,a,t.projectKey,t.repoSlug,r,n,o,s);default:return}}function Rr(e,t,r){let n=encodeURIComponent(t),o=encodeURIComponent(r);if(e.host==="github")return`https://${e.hostname}/${e.owner}/${e.repo}/compare/${o}...${n}`;if(e.host==="gitlab")return`https://${e.hostname}/${e.projectPath}/-/merge_requests/new?merge_request[source_branch]=${n}&merge_request[target_branch]=${o}`;if(e.host==="bitbucket")return`https://${e.hostname}/${e.workspace}/${e.repoSlug}/pull-requests/new?source=${n}&dest=${o}`;if(e.host==="bitbucket-server")return`https://${e.hostname}/projects/${e.projectKey}/repos/${e.repoSlug}/pull-requests?create&sourceBranch=refs/heads/${n}&targetBranch=refs/heads/${o}`}function $a(e,t){let r=Ua(e),n=Xe(e);if(r)return Mt(e)?!0:(console.log(v(`\u26A0 Epic branch ${e} exists on remote but could not be fetched.`)),!1);if(n)return console.log(Oe(`\u2717 Epic branch ${e} exists locally but not on remote.`)),console.log(v(" This may contain work from a previous Clancy version that squash-merged locally.")),console.log($(" To preserve this work, push it manually:")),console.log($(` git push -u origin ${e}`)),console.log($(" Then re-run /clancy:once to continue.")),!1;try{if(gl("git",["fetch","origin",t],{encoding:"utf8",stdio:["pipe","pipe","pipe"],timeout:15e3}),gl("git",["checkout","-b",e,`origin/${t}`],{encoding:"utf8"}),et(e))console.log(N(` \u2713 Created epic branch ${e}`));else return console.log(v(`\u26A0 Created ${e} locally but could not push to origin.`)),!1;return!0}catch(o){return console.log(Oe(`\u2717 Could not create epic branch: ${o instanceof Error?o.message:String(o)}`)),!1}}async function xa(e,t,r,n,o,s=!1,i,a,u){if(!et(r)){console.log(v(`\u26A0 Could not push ${r} to origin.`)),console.log($(" The branch is still available locally. Push manually:")),console.log($(` git push -u origin ${r}`)),s||R(process.cwd(),t.key,t.title,"PUSH_FAILED",void 0,i),L(n);let y=Ce(Date.now()-o);return console.log(""),console.log(v(`\u26A0 ${t.key} implemented but push failed`)+$(` (${y})`)),!1}console.log(N(` \u2713 Pushed ${r}`));let p;try{let y=Kg(process.cwd(),".clancy","verify-attempt.txt"),z=Jg(y,"utf8").trim(),H=parseInt(z,10);H>0&&(p=`Verification checks did not fully pass (${H} attempt(s)). Review carefully.`)}catch{}let f=Z(e).CLANCY_GIT_PLATFORM,m=ce(f),h=`feat(${t.key}): ${t.title}`,b=Ye(e,{key:t.key,title:t.title,description:t.description,provider:e.provider},n,p,u);if(m.host!=="none"&&m.host!=="unknown"&&m.host!=="azure"){let y=await Re(e,m,r,n,h,b);if(y?.ok)console.log(N(` \u2713 PR created: ${y.url}`)),s||R(process.cwd(),t.key,t.title,"PR_CREATED",y.number,i);else if(y&&!y.ok&&y.alreadyExists)console.log(v(` \u26A0 A PR/MR already exists for ${r}. Branch pushed.`)),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i);else if(y&&!y.ok){console.log(v(` \u26A0 PR/MR creation failed: ${y.error}`));let z=Rr(m,r,n);console.log(z?$(` Create one manually: ${z}`):$(" Branch pushed \u2014 create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i)}else{let z=Rr(m,r,n);console.log(z?$(` Create a PR: ${z}`):$(" Branch pushed to remote. Create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i)}}else m.host==="none"?(console.log(v(`\u26A0 No git remote configured. Branch available locally: ${r}`)),s||R(process.cwd(),t.key,t.title,"LOCAL",void 0,i)):(console.log($(" Branch pushed to remote. Create a PR/MR manually.")),s||R(process.cwd(),t.key,t.title,"PUSHED",void 0,i));if(e.provider!=="github"&&a){let y=e.env.CLANCY_STATUS_REVIEW??e.env.CLANCY_STATUS_DONE;y&&await a.transitionTicket(t,y)}return L(n),!0}async function yl(e,t,r,n,o,s){console.log(""),console.log(N(`\u{1F389} All children of ${t} are done!`)),console.log($(` Creating epic PR: ${n} \u2192 ${o}`));let i=j(process.cwd(),"PR_CREATED"),a=j(process.cwd(),"DONE"),u=j(process.cwd(),"REWORK"),l=j(process.cwd(),"PUSHED"),p=[...i,...a,...u,...l].filter(H=>H.parent===t),f=Z(e).CLANCY_GIT_PLATFORM,m=ce(f),h=`feat(${t}): ${r}`,b=Hu(t,r,p,e.provider);if(m.host==="none"||m.host==="unknown"||m.host==="azure")return console.log(v("\u26A0 Cannot create epic PR \u2014 no supported git remote detected.")),console.log($(` Push manually: git push origin ${n}`)),console.log($(` Then create a PR targeting ${o}`)),!1;let y=await Re(e,m,n,o,h,b);if(y?.ok){if(console.log(N(` \u2713 Epic PR created: ${y.url}`)),R(process.cwd(),t,r,"EPIC_PR_CREATED",y.number),e.provider!=="github"&&s){let H=e.env.CLANCY_STATUS_REVIEW??e.env.CLANCY_STATUS_DONE;H&&await s.transitionTicket({key:t,title:r,description:"",parentInfo:"none",blockers:"None"},H)}return!0}if(y&&!y.ok&&y.alreadyExists)return console.log(v(` \u26A0 An epic PR already exists for ${n}.`)),R(process.cwd(),t,r,"EPIC_PR_CREATED"),!0;console.log(v(`\u26A0 Epic PR creation failed: ${y?.error??"unknown error"}`)),console.log($(" Create it manually:")),console.log($(` Branch: ${n} \u2192 ${o}`));let z=Rr(m,n,o);return z&&console.log($(` ${z}`)),!1}async function bl(e){let t=e.board,r=e.ticket,n=e.ticketBranch,o=e.targetBranch,s=e.baseBranch,i=e.hasParent,a=e.isRework??!1;e.originalBranch=Ba();let u=!1;if(i&&!a){let p=await t.fetchChildrenStatus(r.parentInfo,r.linearIssueId,r.key);p&&p.total===1&&(u=!0)}e.skipEpicBranch=u;let l=i&&!u?o:s;if(e.effectiveTarget=l,a){if(i&&!u){if(!$a(o,s))return e.originalBranch&&L(e.originalBranch),!1}else Yr(l,s);Mt(n)?L(n):(L(l),L(n,!0))}else if(i&&!u){if(!$a(o,s))return e.originalBranch&&L(e.originalBranch),!1;L(o),L(n,!0)}else Yr(s,s),L(s),L(n,!0);try{Ja(e.cwd,{pid:process.pid,ticketKey:r.key,ticketTitle:r.title,ticketBranch:n,targetBranch:l,parentKey:r.parentInfo,description:(r.description??"").slice(0,2e3),startedAt:new Date().toISOString()}),e.lockOwner=!0}catch{console.log($(" (warning: could not write lock file \u2014 crash recovery disabled)"))}return!0}function Yg(e){return e.includes("hooks.slack.com")}function Gg(e){return JSON.stringify({text:e})}function Wg(e){return JSON.stringify({type:"message",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:{$schema:"http://adaptivecards.io/schemas/adaptive-card.json",type:"AdaptiveCard",version:"1.4",body:[{type:"TextBlock",text:e,wrap:!0}]}}]})}async function _l(e,t){let r=Yg(e)?Gg(t):Wg(t);try{let n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:r});n.ok||console.warn(`\u26A0 Notification failed: HTTP ${n.status}`)}catch{console.warn("\u26A0 Notification failed: could not reach webhook")}}async function wl(e){let t=e.config,r=e.ticket,n=Ce(Date.now()-e.startTime);console.log(""),console.log(N(`\u{1F3C1} ${r.key} complete`)+$(` (${n})`)),console.log($(` "Bake 'em away, toys."`));let o=t.env.CLANCY_NOTIFY_WEBHOOK;return o&&await _l(o,`\u2713 Clancy completed [${r.key}] ${r.title}`),!0}import{appendFileSync as Vg,mkdirSync as Qg}from"node:fs";import{dirname as Xg,join as ey}from"node:path";function $l(e,t,r,n=6600){let o=ey(e,".clancy","costs.log");Qg(Xg(o),{recursive:!0});let s=new Date(r).getTime(),i=Date.now(),a=Number.isNaN(s)?0:Math.max(0,i-s),u=Math.round(a/6e4),l=Math.round(u*n),f=`${new Date().toISOString()} | ${t} | ${u}min | ~${l} tokens (estimated)
|
|
47
47
|
`;Vg(o,f,"utf8")}function xl(e){let t=e.config,r=e.ticket;try{let n=Ut(e.cwd);if(n){let o=Number(t.env.CLANCY_TOKEN_RATE??"6600");$l(e.cwd,r.key,n.startedAt,Number.isFinite(o)&&o>0?o:6600)}}catch{}return!0}import{mkdirSync as ty,readFileSync as ry,renameSync as ny,writeFileSync as oy}from"node:fs";import{join as kl}from"node:path";function Sl(e){return kl(e,".clancy","quality.json")}function vl(e){try{let t=ry(Sl(e),"utf8"),r=JSON.parse(t);if(r&&typeof r.tickets=="object"&&r.tickets!==null&&!Array.isArray(r.tickets))return ka(r),r}catch{}return{tickets:{},summary:{totalTickets:0,avgReworkCycles:0,avgVerificationRetries:0,avgDuration:0}}}function ka(e){let t=Object.values(e.tickets),r=t.length;if(r===0){e.summary={totalTickets:0,avgReworkCycles:0,avgVerificationRetries:0,avgDuration:0};return}let n=0,o=0,s=0,i=0;for(let a of t)n+=a.reworkCycles,o+=a.verificationRetries,a.duration!=null&&(s+=a.duration,i++);e.summary={totalTickets:r,avgReworkCycles:Math.round(n/r*100)/100,avgVerificationRetries:Math.round(o/r*100)/100,avgDuration:i>0?Math.round(s/i*100)/100:0}}function zl(e,t){let r=Sl(e),n=r+".tmp";ty(kl(e,".clancy"),{recursive:!0}),oy(n,JSON.stringify(t,null,2)+`
|
|
48
|
-
`,"utf8"),ny(n,r)}function Tl(e,t){return e.tickets[t]||(e.tickets[t]={reworkCycles:0,verificationRetries:0}),e.tickets[t]}function Il(e,t){try{let r=vl(e),n=Tl(r,t);n.reworkCycles++,ka(r),zl(e,r)}catch{}}function Pl(e,t,r){try{let n=vl(e),o=Tl(n,t);o.deliveredAt=new Date().toISOString(),o.duration=r,ka(n),zl(e,n)}catch{}}function We(e,t){return e==="github"?`feature/issue-${t.replace("#","")}`:`feature/${t.toLowerCase()}`}function Ve(e,t,r){return
|
|
48
|
+
`,"utf8"),ny(n,r)}function Tl(e,t){return e.tickets[t]||(e.tickets[t]={reworkCycles:0,verificationRetries:0}),e.tickets[t]}function Il(e,t){try{let r=vl(e),n=Tl(r,t);n.reworkCycles++,ka(r),zl(e,r)}catch{}}function Pl(e,t,r){try{let n=vl(e),o=Tl(n,t);o.deliveredAt=new Date().toISOString(),o.duration=r,ka(n),zl(e,n)}catch{}}function We(e,t){return e==="github"?`feature/issue-${t.replace("#","")}`:`feature/${t.toLowerCase()}`}function Ve(e,t,r){if(!r)return t;if(e==="github"){let n=r.match(/^#(\d+)$/);return n?`epic/${n[1]}`:`milestone/${r.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}`}return`epic/${r.toLowerCase()}`}async function Al(e){let t=j(process.cwd(),"PR_CREATED"),r=j(process.cwd(),"REWORK"),n=j(process.cwd(),"PUSHED"),o=j(process.cwd(),"PUSH_FAILED"),s=[...t,...r,...n,...o];if(s.length===0)return;let i=Z(e).CLANCY_GIT_PLATFORM,a=ce(i);if(a.host==="none"||a.host==="unknown"||a.host==="azure")return;let u=vt(e,a);if(!u)return;let l=St(a,Z(e).CLANCY_GIT_API_URL);if(!l)return;let p=s.slice(0,5);for(let f of p){let m=We(e.provider,f.key),h;if(f.timestamp){let y=new Date(f.timestamp.replace(" ","T")+"Z");h=Number.isNaN(y.getTime())?void 0:y.toISOString()}let b;switch(a.host){case"github":b=await sl(u.token,`${a.owner}/${a.repo}`,m,a.owner,l,h);break;case"gitlab":b=await ml(u.token,l,a.projectPath,m,h);break;case"bitbucket":b=await Qu(u.username,u.token,a.workspace,a.repoSlug,m,h);break;case"bitbucket-server":b=await el(u.token,l,a.projectKey,a.repoSlug,m,h);break}if(b?.changesRequested){let y=[],z;switch(a.host){case"github":y=await il(u.token,`${a.owner}/${a.repo}`,b.prNumber,l,h);break;case"gitlab":{let Y=await hl(u.token,l,a.projectPath,b.prNumber,h);y=Y.comments,z=Y.discussionIds;break}case"bitbucket":y=await Xu(u.username,u.token,a.workspace,a.repoSlug,b.prNumber,h);break;case"bitbucket-server":y=await tl(u.token,l,a.projectKey,a.repoSlug,b.prNumber,h);break}return{ticket:{key:f.key,title:f.summary,description:f.summary,parentInfo:f.parent??"none",blockers:"None"},feedback:y,prNumber:b.prNumber,discussionIds:z,reviewers:b.reviewers??[]}}}}function sy(e){if(e.length===0)return"[clancy] Rework pushed addressing reviewer feedback.";let t=e.length,r=e.slice(0,3).map(o=>`- ${o.slice(0,80)}`).join(`
|
|
49
49
|
`),n=e.length>3?`
|
|
50
50
|
- ...`:"";return`[clancy] Rework pushed addressing ${t} feedback item${t!==1?"s":""}.
|
|
51
51
|
|
|
@@ -119,7 +119,7 @@ If the ${e.provider==="github"?"issue":"ticket"} IS implementable, continue:${e.
|
|
|
119
119
|
4. Commit your work following the conventions in GIT.md
|
|
120
120
|
5. When done, confirm you are finished.`}function Fl(e){let t=e.config,r=e.ticket,n=e.isRework??!1,o=e.targetBranch,s=t.env.CLANCY_TDD==="true",i;n?i=Bl({key:r.key,title:r.title,description:r.description,provider:t.provider,feedbackComments:e.prFeedback??[],previousContext:Fa(o),tdd:s}):i=Ul({provider:t.provider,key:r.key,title:r.title,description:r.description,parentInfo:r.parentInfo,blockers:t.provider!=="github"?r.blockers:void 0,tdd:s}),process.env.CLANCY_ONCE_ACTIVE="1";let a;try{a=Zl(i,t.env.CLANCY_MODEL)}finally{delete process.env.CLANCY_ONCE_ACTIVE}return a?!0:(console.log(v("\u26A0 Claude session exited with an error. Skipping merge.")),!1)}import{execFileSync as Sa}from"node:child_process";import{existsSync as ly,readFileSync as py}from"node:fs";import{join as fy}from"node:path";function dy(e){let t={};for(let r of e.split(`
|
|
121
121
|
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let o=n.indexOf("=");if(o===-1)continue;let s=n.slice(0,o).trim(),i=n.slice(o+1).trim();i.length>=2&&(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),t[s]=i}return t}function Hl(e){let t=fy(e,".clancy",".env");if(!ly(t))return;let r=py(t,"utf8");return dy(r)}function my(e){try{return Sa("which",[e],{stdio:"ignore"}),!0}catch{return!1}}function hy(){try{return Sa("git",["rev-parse","--git-dir"],{stdio:"ignore"}),!0}catch{return!1}}function Cr(e){for(let n of["claude","git"])if(!my(n))return{ok:!1,error:`\u2717 ${n} is required but not found`};let t=Hl(e);if(!t)return{ok:!1,error:"\u2717 .clancy/.env not found \u2014 run /clancy:init first"};if(!hy())return{ok:!1,error:"\u2717 Not inside a git repository"};let r;try{Sa("git",["ls-remote","origin","HEAD"],{encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]})}catch{r="\u26A0 Could not reach origin. PR creation and rework detection may not work."}if(Dt()){let n="\u26A0 Working directory has uncommitted changes \u2014 they will be included in the branch";r=r?`${r}
|
|
122
|
-
${n}`:n}return{ok:!0,env:t,warning:r}}import{execFileSync as zt}from"node:child_process";function ql(e){let t=e.ticketBranch;if(!Xe(t))return;let r;try{r=zt("git",["rev-parse","--abbrev-ref","HEAD"],{encoding:"utf8"}).trim()}catch{return}try{L(t)}catch{return}let n=!1,o=!1;try{n=Dt()}catch{}try{o=zt("git",["log",`origin/${t}..${t}`,"--oneline"],{encoding:"utf8"}).trim().length>0}catch{try{o=zt("git",["log",`origin/${e.targetBranch}..${t}`,"--oneline"],{encoding:"utf8"}).trim().length>0}catch{}}try{L(r)}catch{}if(!(!n&&!o))return{branch:t,hasUncommitted:n,hasUnpushed:o}}async function Jl(e,t,r){try{if(L(r.branch),r.hasUncommitted)try{zt("git",["add","-A"],{encoding:"utf8"}),zt("git",["commit","-m",`fix(${t.ticketKey}): resume after crash`],{encoding:"utf8"}),console.log(N(` \u2713 Committed in-progress work for ${t.ticketKey}`))}catch{return console.log(v(` \u26A0 Could not commit changes on ${r.branch}`)),!1}if(!et(r.branch))return console.log(v(` \u26A0 Could not push ${r.branch} to origin`)),!1;console.log(N(` \u2713 Pushed ${r.branch}`));let o=Z(e).CLANCY_GIT_PLATFORM,s=ce(o),i=`feat(${t.ticketKey}): ${t.ticketTitle}`,a=Ye(e,{key:t.ticketKey,title:t.ticketTitle,description:"",provider:e.provider},t.targetBranch);if(s.host!=="none"&&s.host!=="unknown"&&s.host!=="azure"){let u=await Re(e,s,r.branch,t.targetBranch,i,a);u?.ok?(console.log(N(` \u2713 PR created: ${u.url}`)),R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",u.number,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0)):(console.log($(" Branch pushed \u2014 create a PR manually.")),R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",void 0,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0))}else R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",void 0,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0);try{L(t.targetBranch)}catch{}return!0}catch(n){return console.log(v(` \u26A0 Resume failed: ${n instanceof Error?n.message:String(n)}`)),!1}}async function Kl(e){let t=Ut(e.cwd);if(!t)return!0;if(!Ka(t))return console.log(v(`\u26A0 Another Clancy session is running (PID ${t.pid}, ticket ${t.ticketKey}). Aborting.`)),!1;console.log($(` Stale lock found (PID ${t.pid}, ticket ${t.ticketKey}). Cleaning up...`)),Ft(e.cwd),Ht(e.cwd);try{let r=ql(t);if(r)if(e.isAfk){console.log(v(` \u21BB Resuming crashed session: ${t.ticketKey} on ${r.branch}`));let n=Cr(e.cwd);if(n.ok){let o=Pr(n.env);if(typeof o!="string"&&await Jl(o,t,r))return console.log(N(` \u2713 Resumed ${t.ticketKey}`)),!1}}else console.log(v(` Found in-progress work on ${r.branch}.`+(r.hasUncommitted?" Has uncommitted changes.":"")+(r.hasUnpushed?" Has unpushed commits.":""))),console.log($(" Run in AFK mode (CLANCY_AFK_MODE=1) to auto-resume, or handle manually."))}catch{}return!0}async function Yl(e){let t=e.config;if(!t)return!0;try{let r=j(e.cwd,"PUSHED"),n=j(e.cwd,"PR_CREATED"),o=new Set(n.map(l=>l.key)),s=r.filter(l=>!o.has(l.key));if(!s.length)return!0;let i=Z(t).CLANCY_GIT_PLATFORM,a=ce(i);if(a.host==="none"||a.host==="unknown"||a.host==="azure"){for(let l of s){console.log($(` Skipping PR retry for ${l.key} \u2014 remote host "${a.host}" does not support PR creation`));let p=l.parent&&l.parent!=="none"?l.parent:void 0;R(e.cwd,l.key,l.summary,"PR_CREATED",void 0,p)}return!0}let u=t.env.CLANCY_BASE_BRANCH??"main";for(let l of s){console.log(v(` \u21BB Retrying PR creation for ${l.key} (previously pushed)`));let p=l.parent&&l.parent!=="none"?l.parent:void 0,f=We(t.provider,l.key),m=Ve(t.provider,u,p),h=`feat(${l.key}): ${l.summary}`,b=Ye(t,{key:l.key,title:l.summary,description:l.summary,provider:t.provider},m),y=await Re(t,a,f,m,h,b);y?.ok?(console.log(N(` \u2713 PR created: ${y.url}`)),R(e.cwd,l.key,l.summary,"PR_CREATED",y.number,p)):y&&!y.ok&&y.alreadyExists?(console.log($(` PR already exists for ${l.key}`)),R(e.cwd,l.key,l.summary,"PR_CREATED",void 0,p)):console.log(v(` \u26A0 PR retry failed for ${l.key}${y&&!y.ok?`: ${y.error}`:""} \u2014 create manually`))}}catch{}return!0}var va=c.object({id:c.number(),url:c.optional(c.string())}),Gl=c.object({workItems:c.array(va)}),gy=c.object({rel:c.string(),url:c.string(),attributes:c.optional(c.object({name:c.optional(c.string())}))}),yy=c.object({"System.Title":c.optional(c.string()),"System.Description":c.optional(c.nullable(c.string())),"System.State":c.optional(c.string()),"System.Tags":c.optional(c.nullable(c.string())),"System.AssignedTo":c.optional(c.nullable(c.object({displayName:c.optional(c.string()),uniqueName:c.optional(c.string())}))),"System.WorkItemType":c.optional(c.string())}),za=c.object({id:c.number(),fields:yy,relations:c.optional(c.nullable(c.array(gy)))}),Wl=c.object({value:c.array(za),count:c.optional(c.number())}),Vl=c.object({id:c.string(),name:c.string(),state:c.optional(c.string())}),by=c.object({source:c.optional(c.nullable(va)),target:c.optional(c.nullable(va))}),Ql=c.object({workItemRelations:c.optional(c.array(by))});var Qe="7.1";function Xl(e){return`Basic ${btoa(`:${e}`)}`}function Pt(e){return!(e.includes("'")||e.includes("\\")||e.includes("--")||e.includes(";")||e.includes("/*")||/[^\x20-\x7E\t\n\r]/.test(e))}function Tt(e){if(!Pt(e))throw new Error(`Unsafe WIQL value: ${e.slice(0,50)}`);return e}function At(e,t){return`https://dev.azure.com/${encodeURIComponent(e)}/${encodeURIComponent(t)}/_apis`}function Et(e){return{Authorization:Xl(e),"Content-Type":"application/json"}}function _y(e){return{Authorization:Xl(e),"Content-Type":"application/json-patch+json"}}async function ep(e,t,r){let n;try{n=await fetch(`https://dev.azure.com/${encodeURIComponent(e)}/_apis/projects/${encodeURIComponent(t)}?api-version=${Qe}`,{headers:Et(r)})}catch{return{ok:!1,error:"\u2717 Could not reach Azure DevOps \u2014 check network"}}if(!n.ok)return n.status===401||n.status===403?{ok:!1,error:"\u2717 Azure DevOps auth failed \u2014 check AZDO_PAT"}:{ok:!1,error:`\u2717 Azure DevOps API returned HTTP ${n.status}`};try{let o=await n.json(),s=Vl.safeParse(o);if(s.success&&s.data.id)return{ok:!0}}catch{}return{ok:!1,error:"\u2717 Azure DevOps auth failed \u2014 check AZDO_PAT"}}async function tp(e,t,r,n){try{let o=await fetch(`${At(e,t)}/wit/wiql?api-version=${Qe}`,{method:"POST",headers:Et(r),body:JSON.stringify({query:n})});if(!o.ok)return console.warn(`\u26A0 Azure DevOps WIQL returned HTTP ${o.status}`),[];let s=await o.json(),i=Gl.safeParse(s);return i.success?i.data.workItems.map(a=>a.id):(console.warn(`\u26A0 Unexpected Azure DevOps WIQL response: ${i.error.message}`),[])}catch(o){return console.warn(`\u26A0 Azure DevOps WIQL request failed: ${o instanceof Error?o.message:String(o)}`),[]}}async function Ta(e,t,r,n){if(!n.length)return[];let o=[];for(let s=0;s<n.length;s+=200){let a=n.slice(s,s+200).join(",");try{let u=await fetch(`${At(e,t)}/wit/workitems?ids=${a}&$expand=relations&api-version=${Qe}`,{headers:Et(r)});if(!u.ok){console.warn(`\u26A0 Azure DevOps work items batch returned HTTP ${u.status}`);continue}let l=await u.json(),p=Wl.safeParse(l);if(!p.success){console.warn(`\u26A0 Unexpected Azure DevOps work items response: ${p.error.message}`);continue}o.push(...p.data.value)}catch(u){console.warn(`\u26A0 Azure DevOps work items fetch failed: ${u instanceof Error?u.message:String(u)}`)}}return o}async function It(e,t,r,n){try{let o=await fetch(`${At(e,t)}/wit/workitems/${n}?$expand=relations&api-version=${Qe}`,{headers:Et(r)});if(!o.ok)return;let s=await o.json(),i=za.safeParse(s);return i.success?i.data:void 0}catch{return}}async function Nr(e,t,r,n,o){try{return(await fetch(`${At(e,t)}/wit/workitems/${n}?api-version=${Qe}`,{method:"PATCH",headers:_y(r),body:JSON.stringify(o)})).ok}catch{return!1}}function Or(e){return e?e.split(";").map(t=>t.trim()).filter(Boolean):[]}function Ia(e){return e.join("; ")}function rp(e){let t=e.match(/workItems\/(\d+)/i);if(!t)return;let r=parseInt(t[1],10);return Number.isNaN(r)?void 0:r}async function np(e,t,r,n,o,s,i=5){let a=`SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '${Tt(t)}' AND [System.State] = '${Tt(n)}' AND [System.AssignedTo] = @Me`;o&&(a+=` AND [System.WorkItemType] = '${Tt(o)}'`),a+=" ORDER BY [System.CreatedDate] ASC";let u=await tp(e,t,r,a);if(!u.length)return[];let l=u.slice(0,i*2),f=(await Ta(e,t,r,l)).map(m=>wy(m));return s&&(f=f.filter(m=>!m.labels?.some(h=>h==="clancy:hitl"))),f.slice(0,i)}function wy(e){let t=Or(e.fields["System.Tags"]),r;if(e.relations){for(let n of e.relations)if(n.rel==="System.LinkTypes.Hierarchy-Reverse"){r=rp(n.url);break}}return{key:`azdo-${e.id}`,title:e.fields["System.Title"]??"",description:e.fields["System.Description"]??"",provider:"azdo",workItemId:e.id,parentId:r,labels:t}}var Pa=new Set(["Done","Closed","Completed","Resolved"]);async function op(e,t,r,n){try{let o=await It(e,t,r,n);if(!o)return!1;let s=(o.relations??[]).filter(i=>i.rel==="System.LinkTypes.Dependency-Reverse");if(!s.length)return!1;for(let i of s){let a=rp(i.url);if(a===void 0)continue;let u=await It(e,t,r,a);if(!u)continue;let l=u.fields["System.State"];if(!l||!Pa.has(l))return!0}return!1}catch{return!1}}async function sp(e,t,r,n,o){try{if(o){let s=`Epic: ${o}`,i=await $y(e,t,r,s);if(i&&i.total>0)return i}return await xy(e,t,r,n)}catch{return}}async function $y(e,t,r,n){try{let o=`SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '${Tt(t)}' AND [System.Description] CONTAINS '${Tt(n)}'`,s=await tp(e,t,r,o);if(!s.length)return{total:0,incomplete:0};let i=await Ta(e,t,r,s),a=i.length,u=i.filter(l=>{let p=l.fields["System.State"];return!p||!Pa.has(p)}).length;return{total:a,incomplete:u}}catch{return}}async function xy(e,t,r,n){try{if(Number.isNaN(n))return;let o=`SELECT [System.Id] FROM WorkItemLinks WHERE [Source].[System.Id] = ${n} AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward' MODE (MustContain)`,s=await fetch(`${At(e,t)}/wit/wiql?api-version=${Qe}`,{method:"POST",headers:Et(r),body:JSON.stringify({query:o})});if(!s.ok)return;let i=await s.json(),a=Ql.safeParse(i);if(!a.success)return;let l=(a.data.workItemRelations??[]).filter(h=>h.target?.id!==void 0&&h.target.id!==n).map(h=>h.target.id);if(!l.length)return{total:0,incomplete:0};let p=await Ta(e,t,r,l),f=p.length,m=p.filter(h=>{let b=h.fields["System.State"];return!b||!Pa.has(b)}).length;return{total:f,incomplete:m}}catch{return}}function ip(e){let t=e.AZDO_ORG,r=e.AZDO_PROJECT,n=e.AZDO_PAT,o=e.CLANCY_AZDO_STATUS??"New",s=e.CLANCY_AZDO_WIT;return{async ping(){return ep(t,r,n)},validateInputs(){if(!t.trim())return"\u2717 AZDO_ORG must not be empty";if(!r.trim())return"\u2717 AZDO_PROJECT must not be empty";if(!n.trim())return"\u2717 AZDO_PAT must not be empty";if(!Pt(r))return"\u2717 AZDO_PROJECT contains unsafe characters for WIQL queries";if(!Pt(o))return"\u2717 CLANCY_AZDO_STATUS contains unsafe characters for WIQL queries";if(s&&!Pt(s))return"\u2717 CLANCY_AZDO_WIT contains unsafe characters for WIQL queries"},async fetchTicket(i){return(await this.fetchTickets(i))[0]},async fetchTickets(i){let a=await np(t,r,n,o,s,i.excludeHitl);if(i.buildLabel){let u=i.buildLabel;a=a.filter(l=>l.labels?.includes(u))}return a.map(u=>({key:u.key,title:u.title,description:u.description,parentInfo:u.parentId?`azdo-${u.parentId}`:"none",blockers:"None",issueId:String(u.workItemId),labels:u.labels??[],status:o}))},async fetchBlockerStatus(i){let a=Lr(i.key);return a===void 0?!1:op(t,r,n,a)},async fetchChildrenStatus(i,a){let u=a??i.replace("azdo-",""),l=parseInt(u,10);if(!Number.isNaN(l))return sp(t,r,n,l,i)},async transitionTicket(i,a){let u=Lr(i.key);if(u===void 0)return!1;let l=await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.State",value:a}]);return l&&console.log(` \u2192 Transitioned to ${a}`),l},async ensureLabel(i){},async addLabel(i,a){try{let u=Lr(i);if(u===void 0)return;let l=await It(t,r,n,u);if(!l)return;let p=Or(l.fields["System.Tags"]);if(p.includes(a))return;let f=[...p,a];await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.Tags",value:Ia(f)}])}catch(u){console.warn(`\u26A0 addLabel failed: ${u instanceof Error?u.message:String(u)}`)}},async removeLabel(i,a){try{let u=Lr(i);if(u===void 0)return;let l=await It(t,r,n,u);if(!l)return;let p=Or(l.fields["System.Tags"]);if(!p.includes(a))return;let f=p.filter(m=>m!==a);await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.Tags",value:Ia(f)}])}catch(u){console.warn(`\u26A0 removeLabel failed: ${u instanceof Error?u.message:String(u)}`)}},sharedEnv(){return e}}}function Lr(e){let t=parseInt(e.replace("azdo-",""),10);return Number.isNaN(t)?void 0:t}var ky=c.object({login:c.string()}),Sy=/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/,Zr;function jr(e){return Sy.test(e)}async function cp(e,t){return Ar(`${D}/repos/${t}`,U(e),{401:"\u2717 GitHub auth failed \u2014 check GITHUB_TOKEN",403:"\u2717 GitHub permission denied",404:`\u2717 GitHub repo "${t}" not found`},"\u2717 Could not reach GitHub \u2014 check network")}async function up(e,t){if(Zr)return Zr;try{let r=await fetch(`${t??D}/user`,{headers:U(e)});if(!r.ok){let s=r.status===401||r.status===403?' Fine-grained PATs need "Account permissions \u2192 read" (or classic PATs need read:user scope).':"";return console.warn(`\u26A0 GitHub /user returned HTTP ${r.status} \u2014 falling back to @me.${s}`),"@me"}let n=await r.json(),o=ky.safeParse(n);if(o.success)return Zr=o.data.login,Zr;console.warn("\u26A0 Unexpected GitHub /user response \u2014 falling back to @me")}catch(r){console.warn(`\u26A0 GitHub /user request failed: ${r instanceof Error?r.message:String(r)} \u2014 falling back to @me`)}return"@me"}async function lp(e,t,r,n,o,s=5){let i,a=new URLSearchParams({state:"open",assignee:n??"@me",per_page:"10"});r&&a.set("labels",r);try{i=await fetch(`${D}/repos/${t}/issues?${a}`,{headers:U(e)})}catch(f){return console.warn(`\u26A0 GitHub API request failed: ${f instanceof Error?f.message:String(f)}`),[]}if(!i.ok)return console.warn(`\u26A0 GitHub API returned HTTP ${i.status}`),[];let u;try{u=await i.json()}catch{return console.warn("\u26A0 GitHub API returned invalid JSON"),[]}let l=rl.safeParse(u);if(!l.success)return console.warn(`\u26A0 Unexpected GitHub response shape: ${l.error.message}`),[];let p=l.data.filter(f=>!f.pull_request);return o&&(p=p.filter(f=>!f.labels?.some(m=>m.name==="clancy:hitl"))),p.slice(0,s).map(f=>({key:`#${f.number}`,title:f.title,description:f.body??"",provider:"github",milestone:f.milestone?.title,labels:f.labels?.map(m=>m.name).filter(m=>!!m)}))}async function pp(e,t,r,n){if(!jr(t))return!1;let o=/Blocked by #(\d+)/gi,s=new Set,i;for(;(i=o.exec(n))!==null;){let a=parseInt(i[1],10);!Number.isNaN(a)&&a!==r&&s.add(a)}if(!s.size)return!1;try{for(let a of s){let u=await fetch(`${D}/repos/${t}/issues/${a}`,{headers:U(e)});if(!u.ok)continue;if((await u.json()).state!=="closed")return!0}return!1}catch{return!1}}async function fp(e,t,r){if(jr(t))try{let n=await ap(e,t,`Epic: #${r}`);return n&&n.total>0?n:await ap(e,t,`Parent: #${r}`)}catch{return}}async function ap(e,t,r){let n=U(e),o=`"${r}" repo:${t} is:issue`,s=new URLSearchParams({q:o,per_page:"1"}),i=await fetch(`${D}/search/issues?${s}`,{headers:n});if(!i.ok)return;let u=(await i.json()).total_count??0;if(u===0)return{total:0,incomplete:0};let l=`"${r}" repo:${t} is:issue is:open`,p=new URLSearchParams({q:l,per_page:"1"}),f=await fetch(`${D}/search/issues?${p}`,{headers:n});if(!f.ok)return{total:u,incomplete:u};let h=(await f.json()).total_count??0;return{total:u,incomplete:h}}var vy=/^(?:Epic|Parent): (#\d+)/m;function zy(e){return e.match(vy)?.[1]}function dp(e){return{async ping(){return cp(e.GITHUB_TOKEN,e.GITHUB_REPO)},validateInputs(){if(!jr(e.GITHUB_REPO))return"\u2717 GITHUB_REPO format is invalid \u2014 expected owner/repo"},async fetchTicket(t){return(await this.fetchTickets(t))[0]},async fetchTickets(t){let r=await up(e.GITHUB_TOKEN);return(await lp(e.GITHUB_TOKEN,e.GITHUB_REPO,t.buildLabel??e.CLANCY_LABEL,r,t.excludeHitl)).map(o=>({key:o.key,title:o.title,description:o.description,parentInfo:o.milestone??zy(o.description)??"none",blockers:"None",labels:o.labels??[],status:"open"}))},async fetchBlockerStatus(t){let r=parseInt(t.key.replace("#",""),10);return Number.isNaN(r)?!1:pp(e.GITHUB_TOKEN,e.GITHUB_REPO,r,t.description)},async fetchChildrenStatus(t){let r=parseInt(t.replace("#",""),10);if(!Number.isNaN(r))return fp(e.GITHUB_TOKEN,e.GITHUB_REPO,r)},async transitionTicket(){return!1},async ensureLabel(t){try{let r=U(e.GITHUB_TOKEN),n=await fetch(`${D}/repos/${e.GITHUB_REPO}/labels/${encodeURIComponent(t)}`,{headers:r});if(n.ok)return;if(n.status===404){let o=await fetch(`${D}/repos/${e.GITHUB_REPO}/labels`,{method:"POST",headers:{...r,"Content-Type":"application/json"},body:JSON.stringify({name:t,color:"0075ca"})});!o.ok&&o.status!==422&&console.warn(`\u26A0 ensureLabel create returned HTTP ${o.status}`)}else console.warn(`\u26A0 ensureLabel GET returned HTTP ${n.status}`)}catch(r){console.warn(`\u26A0 ensureLabel failed: ${r instanceof Error?r.message:String(r)}`)}},async addLabel(t,r){try{await this.ensureLabel(r);let n=parseInt(t.replace("#",""),10);if(Number.isNaN(n))return;let o=U(e.GITHUB_TOKEN),s=await fetch(`${D}/repos/${e.GITHUB_REPO}/issues/${n}/labels`,{method:"POST",headers:{...o,"Content-Type":"application/json"},body:JSON.stringify({labels:[r]})});s.ok||console.warn(`\u26A0 addLabel returned HTTP ${s.status}`)}catch(n){console.warn(`\u26A0 addLabel failed: ${n instanceof Error?n.message:String(n)}`)}},async removeLabel(t,r){try{let n=parseInt(t.replace("#",""),10);if(Number.isNaN(n))return;let o=U(e.GITHUB_TOKEN),s=await fetch(`${D}/repos/${e.GITHUB_REPO}/issues/${n}/labels/${encodeURIComponent(r)}`,{method:"DELETE",headers:o});!s.ok&&s.status!==404&&console.warn(`\u26A0 removeLabel returned HTTP ${s.status}`)}catch(n){console.warn(`\u26A0 removeLabel failed: ${n instanceof Error?n.message:String(n)}`)}},sharedEnv(){return e}}}var Ty=c.object({type:c.optional(c.object({name:c.optional(c.string())})),inwardIssue:c.optional(c.object({key:c.optional(c.string())}))}),Iy=c.object({summary:c.string(),description:c.optional(c.unknown()),issuelinks:c.optional(c.array(Ty)),parent:c.optional(c.object({key:c.optional(c.string())})),customfield_10014:c.optional(c.nullable(c.string())),labels:c.optional(c.array(c.string()))}),Py=c.object({key:c.string(),fields:Iy}),mp=c.object({total:c.optional(c.number()),isLast:c.optional(c.boolean()),issues:c.array(Py)}),Ay=c.object({id:c.string(),name:c.string()}),hp=c.object({transitions:c.array(Ay)}),Ey=c.object({type:c.optional(c.object({name:c.optional(c.string())})),inwardIssue:c.optional(c.object({key:c.optional(c.string()),fields:c.optional(c.object({status:c.optional(c.object({statusCategory:c.optional(c.object({key:c.optional(c.string())}))}))}))}))}),gp=c.object({fields:c.optional(c.object({issuelinks:c.optional(c.array(Ey))}))}),Aa=c.object({fields:c.optional(c.object({labels:c.optional(c.array(c.string()))}))});var Ry=/^[a-zA-Z0-9 _\-'.]+$/,Ea=/^[A-Z][A-Z0-9]+-\d+$/;function bp(e,t){return Buffer.from(`${e}:${t}`).toString("base64")}function Rt(e){return Ry.test(e)}async function _p(e,t,r){return Ar(`${e}/rest/api/3/project/${t}`,Se(r),{401:"\u2717 Jira auth failed \u2014 check credentials",403:"\u2717 Jira permission denied for this project",404:`\u2717 Jira project "${t}" not found`},"\u2717 Could not reach Jira \u2014 check network")}function Cy(e,t,r,n,o){let s=[`project="${e}"`];return r&&s.push("sprint in openSprints()"),n&&s.push(`labels = "${n}"`),o&&s.push('labels != "clancy:hitl"'),s.push("assignee=currentUser()"),s.push(`status="${t}"`),s.join(" AND ")+" ORDER BY priority ASC"}function Ny(e){if(!e||typeof e!="object")return"";let t=[];function r(n){if(typeof n=="string"){t.push(n);return}if(Array.isArray(n)){for(let o of n)r(o);return}if(n&&typeof n=="object")for(let o of Object.values(n))r(o)}return r(e),t.join(" ")}async function wp(e,t,r,n,o,s,i,a=5){let u=Cy(r,n,o,s,i),l;try{l=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:u,maxResults:a,fields:["summary","description","issuelinks","parent","customfield_10014","labels"]})})}catch(m){return console.warn(`\u26A0 Jira API request failed: ${m instanceof Error?m.message:String(m)}`),[]}if(!l.ok)return console.warn(`\u26A0 Jira API returned HTTP ${l.status}`),[];let p;try{p=await l.json()}catch{return console.warn("\u26A0 Jira API returned invalid JSON"),[]}let f=mp.safeParse(p);return f.success?f.data.issues.map(m=>{let h=m.fields,b=(h.issuelinks??[]).filter(z=>z.type?.name==="Blocks"&&z.inwardIssue?.key).map(z=>z.inwardIssue?.key).filter(z=>!!z),y=h.parent?.key??h.customfield_10014??void 0;return{key:m.key,title:h.summary,description:Ny(h.description),provider:"jira",epicKey:y,blockers:b,labels:h.labels}}):(console.warn(`\u26A0 Unexpected Jira response shape: ${f.error.message}`),[])}async function $p(e,t,r){if(!Ea.test(r))return!1;try{let n=await fetch(`${e}/rest/api/3/issue/${r}?fields=issuelinks`,{headers:Se(t)});if(!n.ok)return!1;let o=await n.json(),s=gp.safeParse(o);return s.success?(s.data.fields?.issuelinks??[]).some(a=>{if(a.type?.name!=="Blocks"||!a.inwardIssue?.key)return!1;let u=a.inwardIssue.fields?.status?.statusCategory?.key;return u?u!=="done":!1}):!1}catch{return!1}}async function xp(e,t,r){if(Ea.test(r))try{let n=r.split("-")[0],o=await yp(e,t,`project = "${n}" AND description ~ "Epic: ${r}"`);return o&&o.total>0?o:await yp(e,t,`parent = ${r}`)}catch{return}}async function yp(e,t,r){let n=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:r,maxResults:0})});if(!n.ok)return;let s=(await n.json()).total??0;if(s===0)return{total:0,incomplete:0};let i=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:`${r} AND statusCategory != "done"`,maxResults:0})});if(!i.ok)return;let u=(await i.json()).total??0;return{total:s,incomplete:u}}async function Oy(e,t,r,n){if(!Ea.test(r))return;let o;try{o=await fetch(`${e}/rest/api/3/issue/${r}/transitions`,{headers:Se(t)})}catch(u){console.warn(`\u26A0 Jira transitions request failed: ${u instanceof Error?u.message:String(u)}`);return}if(!o.ok)return;let s;try{s=await o.json()}catch{console.warn("\u26A0 Jira transitions returned invalid JSON");return}let i=hp.safeParse(s);if(!i.success){console.warn(`\u26A0 Unexpected Jira transitions response: ${i.error.message}`);return}return i.data.transitions.find(u=>u.name===n)?.id}async function kp(e,t,r,n){try{let o=await Oy(e,t,r,n);return o?(await fetch(`${e}/rest/api/3/issue/${r}/transitions`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({transition:{id:o}})})).ok:(console.warn(`\u26A0 Jira transition "${n}" not found for ${r}`),!1)}catch{return!1}}function Sp(e){let t=bp(e.JIRA_USER,e.JIRA_API_TOKEN);return{async ping(){return _p(e.JIRA_BASE_URL,e.JIRA_PROJECT_KEY,t)},validateInputs(){if(!Rt(e.JIRA_PROJECT_KEY))return"\u2717 JIRA_PROJECT_KEY contains invalid characters";if(e.CLANCY_LABEL_BUILD&&!Rt(e.CLANCY_LABEL_BUILD))return"\u2717 CLANCY_LABEL_BUILD contains invalid characters";if(e.CLANCY_LABEL&&!Rt(e.CLANCY_LABEL))return"\u2717 CLANCY_LABEL contains invalid characters";if(e.CLANCY_JQL_STATUS&&!Rt(e.CLANCY_JQL_STATUS))return"\u2717 CLANCY_JQL_STATUS contains invalid characters"},async fetchTicket(r){return(await this.fetchTickets(r))[0]},async fetchTickets(r){let n=await wp(e.JIRA_BASE_URL,t,e.JIRA_PROJECT_KEY,e.CLANCY_JQL_STATUS??"To Do",e.CLANCY_JQL_SPRINT,r.buildLabel??e.CLANCY_LABEL,r.excludeHitl),o=e.CLANCY_JQL_STATUS??"To Do";return n.map(s=>{let i=s.blockers.length?`Blocked by: ${s.blockers.join(", ")}`:"None";return{key:s.key,title:s.title,description:s.description,parentInfo:s.epicKey??"none",blockers:i,labels:s.labels??[],status:o}})},async fetchBlockerStatus(r){return $p(e.JIRA_BASE_URL,t,r.key)},async fetchChildrenStatus(r){return xp(e.JIRA_BASE_URL,t,r)},async transitionTicket(r,n){let o=await kp(e.JIRA_BASE_URL,t,r.key,n);return o&&console.log(` \u2192 Transitioned to ${n}`),o},async ensureLabel(r){},async addLabel(r,n){try{if(!/^[A-Z][A-Z0-9]+-\d+$/.test(r))return;let o=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}?fields=labels`,{headers:{Authorization:t,Accept:"application/json"}});if(!o.ok){console.warn(`\u26A0 addLabel GET failed: HTTP ${o.status}`);return}let i=Aa.parse(await o.json()).fields?.labels??[];if(i.includes(n))return;let a=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}`,{method:"PUT",headers:{Authorization:t,"Content-Type":"application/json"},body:JSON.stringify({fields:{labels:[...i,n]}})});a.ok||console.warn(`\u26A0 addLabel PUT returned HTTP ${a.status}`)}catch(o){console.warn(`\u26A0 addLabel failed: ${o instanceof Error?o.message:String(o)}`)}},async removeLabel(r,n){try{if(!/^[A-Z][A-Z0-9]+-\d+$/.test(r))return;let o=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}?fields=labels`,{headers:{Authorization:t,Accept:"application/json"}});if(!o.ok){console.warn(`\u26A0 removeLabel GET failed: HTTP ${o.status}`);return}let i=Aa.parse(await o.json()).fields?.labels??[];if(!i.includes(n))return;let a=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}`,{method:"PUT",headers:{Authorization:t,"Content-Type":"application/json"},body:JSON.stringify({fields:{labels:i.filter(u=>u!==n)}})});a.ok||console.warn(`\u26A0 removeLabel PUT returned HTTP ${a.status}`)}catch(o){console.warn(`\u26A0 removeLabel failed: ${o instanceof Error?o.message:String(o)}`)}},sharedEnv(){return e}}}var Ly=c.object({id:c.string(),identifier:c.string(),title:c.string(),description:c.optional(c.nullable(c.string())),parent:c.optional(c.nullable(c.object({identifier:c.string(),title:c.optional(c.string())}))),labels:c.optional(c.object({nodes:c.array(c.object({name:c.string()}))}))}),vp=c.object({data:c.optional(c.object({viewer:c.optional(c.object({assignedIssues:c.optional(c.object({nodes:c.array(Ly)}))}))}))}),zp=c.object({data:c.optional(c.object({viewer:c.optional(c.object({id:c.optional(c.string())}))}))}),Tp=c.object({data:c.optional(c.object({workflowStates:c.optional(c.object({nodes:c.array(c.object({id:c.string()}))}))}))}),Ip=c.object({data:c.optional(c.object({issueUpdate:c.optional(c.object({success:c.optional(c.boolean())}))}))}),Zy=c.object({type:c.string(),relatedIssue:c.optional(c.object({state:c.optional(c.object({type:c.optional(c.string())}))}))}),Pp=c.object({data:c.optional(c.object({issue:c.optional(c.object({relations:c.optional(c.object({nodes:c.array(Zy)}))}))}))}),jy=c.object({state:c.optional(c.object({type:c.optional(c.string())}))}),Ap=c.object({data:c.optional(c.object({issueSearch:c.optional(c.object({nodes:c.array(jy)}))}))}),Ep=c.object({data:c.optional(c.object({team:c.optional(c.object({labels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.string()}))}))}))}))}),Rp=c.object({data:c.optional(c.object({issueLabels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.string()}))}))}))}),Cp=c.object({data:c.optional(c.object({issueLabelCreate:c.optional(c.object({issueLabel:c.optional(c.object({id:c.string()})),success:c.optional(c.boolean())}))}))}),Ra=c.object({data:c.optional(c.object({issueSearch:c.optional(c.object({nodes:c.array(c.object({id:c.string(),labels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.optional(c.string())}))}))}))}))}))});var Dy=/^[a-zA-Z0-9_-]+$/,Np="https://api.linear.app/graphql";function Op(e){return Dy.test(e)}async function V(e,t,r){let n;try{n=await fetch(Np,{method:"POST",headers:{Authorization:e,"Content-Type":"application/json"},body:JSON.stringify({query:t,variables:r})})}catch(o){console.warn(`\u26A0 Linear API request failed: ${o instanceof Error?o.message:String(o)}`);return}if(!n.ok){console.warn(`\u26A0 Linear API returned HTTP ${n.status}`);return}try{return await n.json()}catch{console.warn("\u26A0 Linear API returned invalid JSON");return}}async function Lp(e){let t;try{t=await fetch(Np,{method:"POST",headers:{Authorization:e,"Content-Type":"application/json"},body:JSON.stringify({query:"{ viewer { id } }"})})}catch{return{ok:!1,error:"\u2717 Could not reach Linear \u2014 check network"}}if(!t.ok)return t.status===401||t.status===403?{ok:!1,error:"\u2717 Linear auth failed \u2014 check LINEAR_API_KEY"}:{ok:!1,error:`\u2717 Linear API returned HTTP ${t.status}`};try{let r=await t.json(),n=zp.safeParse(r);if(n.success&&n.data.data?.viewer?.id)return{ok:!0}}catch{}return{ok:!1,error:"\u2717 Linear auth failed \u2014 check LINEAR_API_KEY"}}async function Zp(e,t,r=5){let n=e.CLANCY_LABEL?.trim(),o=!!n,s=o?"labels: { name: { eq: $label } }":"",i=["$teamId: String!",...o?["$label: String!"]:[]],a=['state: { type: { eq: "unstarted" } }',"team: { id: { eq: $teamId } }",s].filter(Boolean),u=`
|
|
122
|
+
${n}`:n}return{ok:!0,env:t,warning:r}}import{execFileSync as zt}from"node:child_process";function ql(e){let t=e.ticketBranch;if(!Xe(t))return;let r;try{r=zt("git",["rev-parse","--abbrev-ref","HEAD"],{encoding:"utf8"}).trim()}catch{return}try{L(t)}catch{return}let n=!1,o=!1;try{n=Dt()}catch{}try{o=zt("git",["log",`origin/${t}..${t}`,"--oneline"],{encoding:"utf8"}).trim().length>0}catch{try{o=zt("git",["log",`origin/${e.targetBranch}..${t}`,"--oneline"],{encoding:"utf8"}).trim().length>0}catch{}}try{L(r)}catch{}if(!(!n&&!o))return{branch:t,hasUncommitted:n,hasUnpushed:o}}async function Jl(e,t,r){try{if(L(r.branch),r.hasUncommitted)try{zt("git",["add","-A"],{encoding:"utf8"}),zt("git",["commit","-m",`fix(${t.ticketKey}): resume after crash`],{encoding:"utf8"}),console.log(N(` \u2713 Committed in-progress work for ${t.ticketKey}`))}catch{return console.log(v(` \u26A0 Could not commit changes on ${r.branch}`)),!1}if(!et(r.branch))return console.log(v(` \u26A0 Could not push ${r.branch} to origin`)),!1;console.log(N(` \u2713 Pushed ${r.branch}`));let o=Z(e).CLANCY_GIT_PLATFORM,s=ce(o),i=`feat(${t.ticketKey}): ${t.ticketTitle}`,a=Ye(e,{key:t.ticketKey,title:t.ticketTitle,description:"",provider:e.provider},t.targetBranch);if(s.host!=="none"&&s.host!=="unknown"&&s.host!=="azure"){let u=await Re(e,s,r.branch,t.targetBranch,i,a);u?.ok?(console.log(N(` \u2713 PR created: ${u.url}`)),R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",u.number,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0)):(console.log($(" Branch pushed \u2014 create a PR manually.")),R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",void 0,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0))}else R(process.cwd(),t.ticketKey,t.ticketTitle,"RESUMED",void 0,t.parentKey&&t.parentKey!=="none"?t.parentKey:void 0);try{L(t.targetBranch)}catch{}return!0}catch(n){return console.log(v(` \u26A0 Resume failed: ${n instanceof Error?n.message:String(n)}`)),!1}}async function Kl(e){let t=Ut(e.cwd);if(!t)return!0;if(!Ka(t))return console.log(v(`\u26A0 Another Clancy session is running (PID ${t.pid}, ticket ${t.ticketKey}). Aborting.`)),!1;console.log($(` Stale lock found (PID ${t.pid}, ticket ${t.ticketKey}). Cleaning up...`)),Ft(e.cwd),Ht(e.cwd);try{let r=ql(t);if(r)if(e.isAfk){console.log(v(` \u21BB Resuming crashed session: ${t.ticketKey} on ${r.branch}`));let n=Cr(e.cwd);if(n.ok){let o=Pr(n.env);if(typeof o!="string"&&await Jl(o,t,r))return console.log(N(` \u2713 Resumed ${t.ticketKey}`)),!1}}else console.log(v(` Found in-progress work on ${r.branch}.`+(r.hasUncommitted?" Has uncommitted changes.":"")+(r.hasUnpushed?" Has unpushed commits.":""))),console.log($(" Run in AFK mode (CLANCY_AFK_MODE=1) to auto-resume, or handle manually."))}catch{}return!0}async function Yl(e){let t=e.config;if(!t)return!0;try{let r=j(e.cwd,"PUSHED"),n=j(e.cwd,"PR_CREATED"),o=new Set(n.map(l=>l.key)),s=r.filter(l=>!o.has(l.key));if(!s.length)return!0;let i=Z(t).CLANCY_GIT_PLATFORM,a=ce(i);if(a.host==="none"||a.host==="unknown"||a.host==="azure"){for(let l of s){console.log($(` Skipping PR retry for ${l.key} \u2014 remote host "${a.host}" does not support PR creation`));let p=l.parent&&l.parent!=="none"?l.parent:void 0;R(e.cwd,l.key,l.summary,"PR_CREATED",void 0,p)}return!0}let u=t.env.CLANCY_BASE_BRANCH??"main";for(let l of s){console.log(v(` \u21BB Retrying PR creation for ${l.key} (previously pushed)`));let p=l.parent&&l.parent!=="none"?l.parent:void 0,f=We(t.provider,l.key),m=Ve(t.provider,u,p),h=`feat(${l.key}): ${l.summary}`,b=Ye(t,{key:l.key,title:l.summary,description:l.summary,provider:t.provider},m),y=await Re(t,a,f,m,h,b);y?.ok?(console.log(N(` \u2713 PR created: ${y.url}`)),R(e.cwd,l.key,l.summary,"PR_CREATED",y.number,p)):y&&!y.ok&&y.alreadyExists?(console.log($(` PR already exists for ${l.key}`)),R(e.cwd,l.key,l.summary,"PR_CREATED",void 0,p)):console.log(v(` \u26A0 PR retry failed for ${l.key}${y&&!y.ok?`: ${y.error}`:""} \u2014 create manually`))}}catch{}return!0}var va=c.object({id:c.number(),url:c.optional(c.string())}),Gl=c.object({workItems:c.array(va)}),gy=c.object({rel:c.string(),url:c.string(),attributes:c.optional(c.object({name:c.optional(c.string())}))}),yy=c.object({"System.Title":c.optional(c.string()),"System.Description":c.optional(c.nullable(c.string())),"System.State":c.optional(c.string()),"System.Tags":c.optional(c.nullable(c.string())),"System.AssignedTo":c.optional(c.nullable(c.object({displayName:c.optional(c.string()),uniqueName:c.optional(c.string())}))),"System.WorkItemType":c.optional(c.string())}),za=c.object({id:c.number(),fields:yy,relations:c.optional(c.nullable(c.array(gy)))}),Wl=c.object({value:c.array(za),count:c.optional(c.number())}),Vl=c.object({id:c.string(),name:c.string(),state:c.optional(c.string())}),by=c.object({source:c.optional(c.nullable(va)),target:c.optional(c.nullable(va))}),Ql=c.object({workItemRelations:c.optional(c.array(by))});var Qe="7.1";function Xl(e){return`Basic ${btoa(`:${e}`)}`}function Pt(e){return!(e.includes("'")||e.includes("\\")||e.includes("--")||e.includes(";")||e.includes("/*")||/[^\x20-\x7E\t\n\r]/.test(e))}function Tt(e){if(!Pt(e))throw new Error(`Unsafe WIQL value: ${e.slice(0,50)}`);return e}function At(e,t){return`https://dev.azure.com/${encodeURIComponent(e)}/${encodeURIComponent(t)}/_apis`}function Et(e){return{Authorization:Xl(e),"Content-Type":"application/json"}}function _y(e){return{Authorization:Xl(e),"Content-Type":"application/json-patch+json"}}async function ep(e,t,r){let n;try{n=await fetch(`https://dev.azure.com/${encodeURIComponent(e)}/_apis/projects/${encodeURIComponent(t)}?api-version=${Qe}`,{headers:Et(r)})}catch{return{ok:!1,error:"\u2717 Could not reach Azure DevOps \u2014 check network"}}if(!n.ok)return n.status===401||n.status===403?{ok:!1,error:"\u2717 Azure DevOps auth failed \u2014 check AZDO_PAT"}:{ok:!1,error:`\u2717 Azure DevOps API returned HTTP ${n.status}`};try{let o=await n.json(),s=Vl.safeParse(o);if(s.success&&s.data.id)return{ok:!0}}catch{}return{ok:!1,error:"\u2717 Azure DevOps auth failed \u2014 check AZDO_PAT"}}async function tp(e,t,r,n){try{let o=await fetch(`${At(e,t)}/wit/wiql?api-version=${Qe}`,{method:"POST",headers:Et(r),body:JSON.stringify({query:n})});if(!o.ok)return console.warn(`\u26A0 Azure DevOps WIQL returned HTTP ${o.status}`),[];let s=await o.json(),i=Gl.safeParse(s);return i.success?i.data.workItems.map(a=>a.id):(console.warn(`\u26A0 Unexpected Azure DevOps WIQL response: ${i.error.message}`),[])}catch(o){return console.warn(`\u26A0 Azure DevOps WIQL request failed: ${o instanceof Error?o.message:String(o)}`),[]}}async function Ta(e,t,r,n){if(!n.length)return[];let o=[];for(let s=0;s<n.length;s+=200){let a=n.slice(s,s+200).join(",");try{let u=await fetch(`${At(e,t)}/wit/workitems?ids=${a}&$expand=relations&api-version=${Qe}`,{headers:Et(r)});if(!u.ok){console.warn(`\u26A0 Azure DevOps work items batch returned HTTP ${u.status}`);continue}let l=await u.json(),p=Wl.safeParse(l);if(!p.success){console.warn(`\u26A0 Unexpected Azure DevOps work items response: ${p.error.message}`);continue}o.push(...p.data.value)}catch(u){console.warn(`\u26A0 Azure DevOps work items fetch failed: ${u instanceof Error?u.message:String(u)}`)}}return o}async function It(e,t,r,n){try{let o=await fetch(`${At(e,t)}/wit/workitems/${n}?$expand=relations&api-version=${Qe}`,{headers:Et(r)});if(!o.ok)return;let s=await o.json(),i=za.safeParse(s);return i.success?i.data:void 0}catch{return}}async function Nr(e,t,r,n,o){try{return(await fetch(`${At(e,t)}/wit/workitems/${n}?api-version=${Qe}`,{method:"PATCH",headers:_y(r),body:JSON.stringify(o)})).ok}catch{return!1}}function Or(e){return e?e.split(";").map(t=>t.trim()).filter(Boolean):[]}function Ia(e){return e.join("; ")}function rp(e){let t=e.match(/workItems\/(\d+)/i);if(!t)return;let r=parseInt(t[1],10);return Number.isNaN(r)?void 0:r}async function np(e,t,r,n,o,s,i=5){let a=`SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '${Tt(t)}' AND [System.State] = '${Tt(n)}' AND [System.AssignedTo] = @Me`;o&&(a+=` AND [System.WorkItemType] = '${Tt(o)}'`),a+=" ORDER BY [System.CreatedDate] ASC";let u=await tp(e,t,r,a);if(!u.length)return[];let l=u.slice(0,i*2),f=(await Ta(e,t,r,l)).map(m=>wy(m));return s&&(f=f.filter(m=>!m.labels?.some(h=>h==="clancy:hitl"))),f.slice(0,i)}function wy(e){let t=Or(e.fields["System.Tags"]),r;if(e.relations){for(let n of e.relations)if(n.rel==="System.LinkTypes.Hierarchy-Reverse"){r=rp(n.url);break}}return{key:`azdo-${e.id}`,title:e.fields["System.Title"]??"",description:e.fields["System.Description"]??"",provider:"azdo",workItemId:e.id,parentId:r,labels:t}}var Pa=new Set(["Done","Closed","Completed","Resolved"]);async function op(e,t,r,n){try{let o=await It(e,t,r,n);if(!o)return!1;let s=(o.relations??[]).filter(i=>i.rel==="System.LinkTypes.Dependency-Reverse");if(!s.length)return!1;for(let i of s){let a=rp(i.url);if(a===void 0)continue;let u=await It(e,t,r,a);if(!u)continue;let l=u.fields["System.State"];if(!l||!Pa.has(l))return!0}return!1}catch{return!1}}async function sp(e,t,r,n,o){try{if(o){let s=`Epic: ${o}`,i=await $y(e,t,r,s);if(i&&i.total>0)return i}return await xy(e,t,r,n)}catch{return}}async function $y(e,t,r,n){try{let o=`SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '${Tt(t)}' AND [System.Description] CONTAINS '${Tt(n)}'`,s=await tp(e,t,r,o);if(!s.length)return{total:0,incomplete:0};let i=await Ta(e,t,r,s),a=i.length,u=i.filter(l=>{let p=l.fields["System.State"];return!p||!Pa.has(p)}).length;return{total:a,incomplete:u}}catch{return}}async function xy(e,t,r,n){try{if(Number.isNaN(n))return;let o=`SELECT [System.Id] FROM WorkItemLinks WHERE [Source].[System.Id] = ${n} AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward' MODE (MustContain)`,s=await fetch(`${At(e,t)}/wit/wiql?api-version=${Qe}`,{method:"POST",headers:Et(r),body:JSON.stringify({query:o})});if(!s.ok)return;let i=await s.json(),a=Ql.safeParse(i);if(!a.success)return;let l=(a.data.workItemRelations??[]).filter(h=>h.target?.id!==void 0&&h.target.id!==n).map(h=>h.target.id);if(!l.length)return{total:0,incomplete:0};let p=await Ta(e,t,r,l),f=p.length,m=p.filter(h=>{let b=h.fields["System.State"];return!b||!Pa.has(b)}).length;return{total:f,incomplete:m}}catch{return}}function ip(e){let t=e.AZDO_ORG,r=e.AZDO_PROJECT,n=e.AZDO_PAT,o=e.CLANCY_AZDO_STATUS??"New",s=e.CLANCY_AZDO_WIT;return{async ping(){return ep(t,r,n)},validateInputs(){if(!t.trim())return"\u2717 AZDO_ORG must not be empty";if(!r.trim())return"\u2717 AZDO_PROJECT must not be empty";if(!n.trim())return"\u2717 AZDO_PAT must not be empty";if(!Pt(r))return"\u2717 AZDO_PROJECT contains unsafe characters for WIQL queries";if(!Pt(o))return"\u2717 CLANCY_AZDO_STATUS contains unsafe characters for WIQL queries";if(s&&!Pt(s))return"\u2717 CLANCY_AZDO_WIT contains unsafe characters for WIQL queries"},async fetchTicket(i){return(await this.fetchTickets(i))[0]},async fetchTickets(i){let a=await np(t,r,n,o,s,i.excludeHitl);if(i.buildLabel){let u=i.buildLabel;a=a.filter(l=>l.labels?.includes(u))}return a.map(u=>({key:u.key,title:u.title,description:u.description,parentInfo:u.parentId?`azdo-${u.parentId}`:"none",blockers:"None",issueId:String(u.workItemId),labels:u.labels??[],status:o}))},async fetchBlockerStatus(i){let a=Lr(i.key);return a===void 0?!1:op(t,r,n,a)},async fetchChildrenStatus(i,a){let u=a??i.replace("azdo-",""),l=parseInt(u,10);if(!Number.isNaN(l))return sp(t,r,n,l,i)},async transitionTicket(i,a){let u=Lr(i.key);if(u===void 0)return!1;let l=await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.State",value:a}]);return l&&console.log(` \u2192 Transitioned to ${a}`),l},async ensureLabel(i){},async addLabel(i,a){try{let u=Lr(i);if(u===void 0)return;let l=await It(t,r,n,u);if(!l)return;let p=Or(l.fields["System.Tags"]);if(p.includes(a))return;let f=[...p,a];await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.Tags",value:Ia(f)}])}catch(u){console.warn(`\u26A0 addLabel failed: ${u instanceof Error?u.message:String(u)}`)}},async removeLabel(i,a){try{let u=Lr(i);if(u===void 0)return;let l=await It(t,r,n,u);if(!l)return;let p=Or(l.fields["System.Tags"]);if(!p.includes(a))return;let f=p.filter(m=>m!==a);await Nr(t,r,n,u,[{op:"replace",path:"/fields/System.Tags",value:Ia(f)}])}catch(u){console.warn(`\u26A0 removeLabel failed: ${u instanceof Error?u.message:String(u)}`)}},sharedEnv(){return e}}}function Lr(e){let t=parseInt(e.replace("azdo-",""),10);return Number.isNaN(t)?void 0:t}var ky=c.object({login:c.string()}),Sy=/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/,Zr;function jr(e){return Sy.test(e)}async function cp(e,t){return Ar(`${D}/repos/${t}`,U(e),{401:"\u2717 GitHub auth failed \u2014 check GITHUB_TOKEN",403:"\u2717 GitHub permission denied",404:`\u2717 GitHub repo "${t}" not found`},"\u2717 Could not reach GitHub \u2014 check network")}async function up(e,t){if(Zr)return Zr;try{let r=await fetch(`${t??D}/user`,{headers:U(e)});if(!r.ok){let s=r.status===401||r.status===403?' Fine-grained PATs need "Account permissions \u2192 read" (or classic PATs need read:user scope).':"";return console.warn(`\u26A0 GitHub /user returned HTTP ${r.status} \u2014 falling back to @me.${s}`),"@me"}let n=await r.json(),o=ky.safeParse(n);if(o.success)return Zr=o.data.login,Zr;console.warn("\u26A0 Unexpected GitHub /user response \u2014 falling back to @me")}catch(r){console.warn(`\u26A0 GitHub /user request failed: ${r instanceof Error?r.message:String(r)} \u2014 falling back to @me`)}return"@me"}async function lp(e,t,r,n,o,s=5){let i,a=new URLSearchParams({state:"open",assignee:n??"@me",per_page:"10"});r&&a.set("labels",r);try{i=await fetch(`${D}/repos/${t}/issues?${a}`,{headers:U(e)})}catch(f){return console.warn(`\u26A0 GitHub API request failed: ${f instanceof Error?f.message:String(f)}`),[]}if(!i.ok)return console.warn(`\u26A0 GitHub API returned HTTP ${i.status}`),[];let u;try{u=await i.json()}catch{return console.warn("\u26A0 GitHub API returned invalid JSON"),[]}let l=rl.safeParse(u);if(!l.success)return console.warn(`\u26A0 Unexpected GitHub response shape: ${l.error.message}`),[];let p=l.data.filter(f=>!f.pull_request);return o&&(p=p.filter(f=>!f.labels?.some(m=>m.name==="clancy:hitl"))),p.slice(0,s).map(f=>({key:`#${f.number}`,title:f.title,description:f.body??"",provider:"github",milestone:f.milestone?.title,labels:f.labels?.map(m=>m.name).filter(m=>!!m)}))}async function pp(e,t,r,n){if(!jr(t))return!1;let o=/Blocked by #(\d+)/gi,s=new Set,i;for(;(i=o.exec(n))!==null;){let a=parseInt(i[1],10);!Number.isNaN(a)&&a!==r&&s.add(a)}if(!s.size)return!1;try{for(let a of s){let u=await fetch(`${D}/repos/${t}/issues/${a}`,{headers:U(e)});if(!u.ok)continue;if((await u.json()).state!=="closed")return!0}return!1}catch{return!1}}async function fp(e,t,r,n){if(jr(t))try{let o=await ap(e,t,`Epic: #${r}`);if(o&&o.total>0)return o;let s=await ap(e,t,`Parent: #${r}`);return s&&s.total>0?s:!o||!s?void 0:n?{total:1,incomplete:1}:s}catch{return}}async function ap(e,t,r){let n=U(e),o=`"${r}" repo:${t} is:issue`,s=new URLSearchParams({q:o,per_page:"1"}),i=await fetch(`${D}/search/issues?${s}`,{headers:n});if(!i.ok)return;let u=(await i.json()).total_count??0;if(u===0)return{total:0,incomplete:0};let l=`"${r}" repo:${t} is:issue is:open`,p=new URLSearchParams({q:l,per_page:"1"}),f=await fetch(`${D}/search/issues?${p}`,{headers:n});if(!f.ok)return{total:u,incomplete:u};let h=(await f.json()).total_count??0;return{total:u,incomplete:h}}var vy=/^(?:Epic|Parent): (#\d+)/m;function zy(e){return e.match(vy)?.[1]}function dp(e){return{async ping(){return cp(e.GITHUB_TOKEN,e.GITHUB_REPO)},validateInputs(){if(!jr(e.GITHUB_REPO))return"\u2717 GITHUB_REPO format is invalid \u2014 expected owner/repo"},async fetchTicket(t){return(await this.fetchTickets(t))[0]},async fetchTickets(t){let r=await up(e.GITHUB_TOKEN);return(await lp(e.GITHUB_TOKEN,e.GITHUB_REPO,t.buildLabel??e.CLANCY_LABEL,r,t.excludeHitl)).map(o=>({key:o.key,title:o.title,description:o.description,parentInfo:o.milestone??zy(o.description)??"none",blockers:"None",labels:o.labels??[],status:"open"}))},async fetchBlockerStatus(t){let r=parseInt(t.key.replace("#",""),10);return Number.isNaN(r)?!1:pp(e.GITHUB_TOKEN,e.GITHUB_REPO,r,t.description)},async fetchChildrenStatus(t,r,n){let o=parseInt(t.replace("#",""),10);if(!Number.isNaN(o))return fp(e.GITHUB_TOKEN,e.GITHUB_REPO,o,n)},async transitionTicket(){return!1},async ensureLabel(t){try{let r=U(e.GITHUB_TOKEN),n=await fetch(`${D}/repos/${e.GITHUB_REPO}/labels/${encodeURIComponent(t)}`,{headers:r});if(n.ok)return;if(n.status===404){let o=await fetch(`${D}/repos/${e.GITHUB_REPO}/labels`,{method:"POST",headers:{...r,"Content-Type":"application/json"},body:JSON.stringify({name:t,color:"0075ca"})});!o.ok&&o.status!==422&&console.warn(`\u26A0 ensureLabel create returned HTTP ${o.status}`)}else console.warn(`\u26A0 ensureLabel GET returned HTTP ${n.status}`)}catch(r){console.warn(`\u26A0 ensureLabel failed: ${r instanceof Error?r.message:String(r)}`)}},async addLabel(t,r){try{await this.ensureLabel(r);let n=parseInt(t.replace("#",""),10);if(Number.isNaN(n))return;let o=U(e.GITHUB_TOKEN),s=await fetch(`${D}/repos/${e.GITHUB_REPO}/issues/${n}/labels`,{method:"POST",headers:{...o,"Content-Type":"application/json"},body:JSON.stringify({labels:[r]})});s.ok||console.warn(`\u26A0 addLabel returned HTTP ${s.status}`)}catch(n){console.warn(`\u26A0 addLabel failed: ${n instanceof Error?n.message:String(n)}`)}},async removeLabel(t,r){try{let n=parseInt(t.replace("#",""),10);if(Number.isNaN(n))return;let o=U(e.GITHUB_TOKEN),s=await fetch(`${D}/repos/${e.GITHUB_REPO}/issues/${n}/labels/${encodeURIComponent(r)}`,{method:"DELETE",headers:o});!s.ok&&s.status!==404&&console.warn(`\u26A0 removeLabel returned HTTP ${s.status}`)}catch(n){console.warn(`\u26A0 removeLabel failed: ${n instanceof Error?n.message:String(n)}`)}},sharedEnv(){return e}}}var Ty=c.object({type:c.optional(c.object({name:c.optional(c.string())})),inwardIssue:c.optional(c.object({key:c.optional(c.string())}))}),Iy=c.object({summary:c.string(),description:c.optional(c.unknown()),issuelinks:c.optional(c.array(Ty)),parent:c.optional(c.object({key:c.optional(c.string())})),customfield_10014:c.optional(c.nullable(c.string())),labels:c.optional(c.array(c.string()))}),Py=c.object({key:c.string(),fields:Iy}),mp=c.object({total:c.optional(c.number()),isLast:c.optional(c.boolean()),issues:c.array(Py)}),Ay=c.object({id:c.string(),name:c.string()}),hp=c.object({transitions:c.array(Ay)}),Ey=c.object({type:c.optional(c.object({name:c.optional(c.string())})),inwardIssue:c.optional(c.object({key:c.optional(c.string()),fields:c.optional(c.object({status:c.optional(c.object({statusCategory:c.optional(c.object({key:c.optional(c.string())}))}))}))}))}),gp=c.object({fields:c.optional(c.object({issuelinks:c.optional(c.array(Ey))}))}),Aa=c.object({fields:c.optional(c.object({labels:c.optional(c.array(c.string()))}))});var Ry=/^[a-zA-Z0-9 _\-'.]+$/,Ea=/^[A-Z][A-Z0-9]+-\d+$/;function bp(e,t){return Buffer.from(`${e}:${t}`).toString("base64")}function Rt(e){return Ry.test(e)}async function _p(e,t,r){return Ar(`${e}/rest/api/3/project/${t}`,Se(r),{401:"\u2717 Jira auth failed \u2014 check credentials",403:"\u2717 Jira permission denied for this project",404:`\u2717 Jira project "${t}" not found`},"\u2717 Could not reach Jira \u2014 check network")}function Cy(e,t,r,n,o){let s=[`project="${e}"`];return r&&s.push("sprint in openSprints()"),n&&s.push(`labels = "${n}"`),o&&s.push('labels != "clancy:hitl"'),s.push("assignee=currentUser()"),s.push(`status="${t}"`),s.join(" AND ")+" ORDER BY priority ASC"}function Ny(e){if(!e||typeof e!="object")return"";let t=[];function r(n){if(typeof n=="string"){t.push(n);return}if(Array.isArray(n)){for(let o of n)r(o);return}if(n&&typeof n=="object")for(let o of Object.values(n))r(o)}return r(e),t.join(" ")}async function wp(e,t,r,n,o,s,i,a=5){let u=Cy(r,n,o,s,i),l;try{l=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:u,maxResults:a,fields:["summary","description","issuelinks","parent","customfield_10014","labels"]})})}catch(m){return console.warn(`\u26A0 Jira API request failed: ${m instanceof Error?m.message:String(m)}`),[]}if(!l.ok)return console.warn(`\u26A0 Jira API returned HTTP ${l.status}`),[];let p;try{p=await l.json()}catch{return console.warn("\u26A0 Jira API returned invalid JSON"),[]}let f=mp.safeParse(p);return f.success?f.data.issues.map(m=>{let h=m.fields,b=(h.issuelinks??[]).filter(z=>z.type?.name==="Blocks"&&z.inwardIssue?.key).map(z=>z.inwardIssue?.key).filter(z=>!!z),y=h.parent?.key??h.customfield_10014??void 0;return{key:m.key,title:h.summary,description:Ny(h.description),provider:"jira",epicKey:y,blockers:b,labels:h.labels}}):(console.warn(`\u26A0 Unexpected Jira response shape: ${f.error.message}`),[])}async function $p(e,t,r){if(!Ea.test(r))return!1;try{let n=await fetch(`${e}/rest/api/3/issue/${r}?fields=issuelinks`,{headers:Se(t)});if(!n.ok)return!1;let o=await n.json(),s=gp.safeParse(o);return s.success?(s.data.fields?.issuelinks??[]).some(a=>{if(a.type?.name!=="Blocks"||!a.inwardIssue?.key)return!1;let u=a.inwardIssue.fields?.status?.statusCategory?.key;return u?u!=="done":!1}):!1}catch{return!1}}async function xp(e,t,r){if(Ea.test(r))try{let n=r.split("-")[0],o=await yp(e,t,`project = "${n}" AND description ~ "Epic: ${r}"`);return o&&o.total>0?o:await yp(e,t,`parent = ${r}`)}catch{return}}async function yp(e,t,r){let n=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:r,maxResults:0})});if(!n.ok)return;let s=(await n.json()).total??0;if(s===0)return{total:0,incomplete:0};let i=await fetch(`${e}/rest/api/3/search/jql`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({jql:`${r} AND statusCategory != "done"`,maxResults:0})});if(!i.ok)return;let u=(await i.json()).total??0;return{total:s,incomplete:u}}async function Oy(e,t,r,n){if(!Ea.test(r))return;let o;try{o=await fetch(`${e}/rest/api/3/issue/${r}/transitions`,{headers:Se(t)})}catch(u){console.warn(`\u26A0 Jira transitions request failed: ${u instanceof Error?u.message:String(u)}`);return}if(!o.ok)return;let s;try{s=await o.json()}catch{console.warn("\u26A0 Jira transitions returned invalid JSON");return}let i=hp.safeParse(s);if(!i.success){console.warn(`\u26A0 Unexpected Jira transitions response: ${i.error.message}`);return}return i.data.transitions.find(u=>u.name===n)?.id}async function kp(e,t,r,n){try{let o=await Oy(e,t,r,n);return o?(await fetch(`${e}/rest/api/3/issue/${r}/transitions`,{method:"POST",headers:{...Se(t),"Content-Type":"application/json"},body:JSON.stringify({transition:{id:o}})})).ok:(console.warn(`\u26A0 Jira transition "${n}" not found for ${r}`),!1)}catch{return!1}}function Sp(e){let t=bp(e.JIRA_USER,e.JIRA_API_TOKEN);return{async ping(){return _p(e.JIRA_BASE_URL,e.JIRA_PROJECT_KEY,t)},validateInputs(){if(!Rt(e.JIRA_PROJECT_KEY))return"\u2717 JIRA_PROJECT_KEY contains invalid characters";if(e.CLANCY_LABEL_BUILD&&!Rt(e.CLANCY_LABEL_BUILD))return"\u2717 CLANCY_LABEL_BUILD contains invalid characters";if(e.CLANCY_LABEL&&!Rt(e.CLANCY_LABEL))return"\u2717 CLANCY_LABEL contains invalid characters";if(e.CLANCY_JQL_STATUS&&!Rt(e.CLANCY_JQL_STATUS))return"\u2717 CLANCY_JQL_STATUS contains invalid characters"},async fetchTicket(r){return(await this.fetchTickets(r))[0]},async fetchTickets(r){let n=await wp(e.JIRA_BASE_URL,t,e.JIRA_PROJECT_KEY,e.CLANCY_JQL_STATUS??"To Do",e.CLANCY_JQL_SPRINT,r.buildLabel??e.CLANCY_LABEL,r.excludeHitl),o=e.CLANCY_JQL_STATUS??"To Do";return n.map(s=>{let i=s.blockers.length?`Blocked by: ${s.blockers.join(", ")}`:"None";return{key:s.key,title:s.title,description:s.description,parentInfo:s.epicKey??"none",blockers:i,labels:s.labels??[],status:o}})},async fetchBlockerStatus(r){return $p(e.JIRA_BASE_URL,t,r.key)},async fetchChildrenStatus(r){return xp(e.JIRA_BASE_URL,t,r)},async transitionTicket(r,n){let o=await kp(e.JIRA_BASE_URL,t,r.key,n);return o&&console.log(` \u2192 Transitioned to ${n}`),o},async ensureLabel(r){},async addLabel(r,n){try{if(!/^[A-Z][A-Z0-9]+-\d+$/.test(r))return;let o=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}?fields=labels`,{headers:{Authorization:t,Accept:"application/json"}});if(!o.ok){console.warn(`\u26A0 addLabel GET failed: HTTP ${o.status}`);return}let i=Aa.parse(await o.json()).fields?.labels??[];if(i.includes(n))return;let a=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}`,{method:"PUT",headers:{Authorization:t,"Content-Type":"application/json"},body:JSON.stringify({fields:{labels:[...i,n]}})});a.ok||console.warn(`\u26A0 addLabel PUT returned HTTP ${a.status}`)}catch(o){console.warn(`\u26A0 addLabel failed: ${o instanceof Error?o.message:String(o)}`)}},async removeLabel(r,n){try{if(!/^[A-Z][A-Z0-9]+-\d+$/.test(r))return;let o=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}?fields=labels`,{headers:{Authorization:t,Accept:"application/json"}});if(!o.ok){console.warn(`\u26A0 removeLabel GET failed: HTTP ${o.status}`);return}let i=Aa.parse(await o.json()).fields?.labels??[];if(!i.includes(n))return;let a=await fetch(`${e.JIRA_BASE_URL}/rest/api/3/issue/${encodeURIComponent(r)}`,{method:"PUT",headers:{Authorization:t,"Content-Type":"application/json"},body:JSON.stringify({fields:{labels:i.filter(u=>u!==n)}})});a.ok||console.warn(`\u26A0 removeLabel PUT returned HTTP ${a.status}`)}catch(o){console.warn(`\u26A0 removeLabel failed: ${o instanceof Error?o.message:String(o)}`)}},sharedEnv(){return e}}}var Ly=c.object({id:c.string(),identifier:c.string(),title:c.string(),description:c.optional(c.nullable(c.string())),parent:c.optional(c.nullable(c.object({identifier:c.string(),title:c.optional(c.string())}))),labels:c.optional(c.object({nodes:c.array(c.object({name:c.string()}))}))}),vp=c.object({data:c.optional(c.object({viewer:c.optional(c.object({assignedIssues:c.optional(c.object({nodes:c.array(Ly)}))}))}))}),zp=c.object({data:c.optional(c.object({viewer:c.optional(c.object({id:c.optional(c.string())}))}))}),Tp=c.object({data:c.optional(c.object({workflowStates:c.optional(c.object({nodes:c.array(c.object({id:c.string()}))}))}))}),Ip=c.object({data:c.optional(c.object({issueUpdate:c.optional(c.object({success:c.optional(c.boolean())}))}))}),Zy=c.object({type:c.string(),relatedIssue:c.optional(c.object({state:c.optional(c.object({type:c.optional(c.string())}))}))}),Pp=c.object({data:c.optional(c.object({issue:c.optional(c.object({relations:c.optional(c.object({nodes:c.array(Zy)}))}))}))}),jy=c.object({state:c.optional(c.object({type:c.optional(c.string())}))}),Ap=c.object({data:c.optional(c.object({issueSearch:c.optional(c.object({nodes:c.array(jy)}))}))}),Ep=c.object({data:c.optional(c.object({team:c.optional(c.object({labels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.string()}))}))}))}))}),Rp=c.object({data:c.optional(c.object({issueLabels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.string()}))}))}))}),Cp=c.object({data:c.optional(c.object({issueLabelCreate:c.optional(c.object({issueLabel:c.optional(c.object({id:c.string()})),success:c.optional(c.boolean())}))}))}),Ra=c.object({data:c.optional(c.object({issueSearch:c.optional(c.object({nodes:c.array(c.object({id:c.string(),labels:c.optional(c.object({nodes:c.array(c.object({id:c.string(),name:c.optional(c.string())}))}))}))}))}))});var Dy=/^[a-zA-Z0-9_-]+$/,Np="https://api.linear.app/graphql";function Op(e){return Dy.test(e)}async function V(e,t,r){let n;try{n=await fetch(Np,{method:"POST",headers:{Authorization:e,"Content-Type":"application/json"},body:JSON.stringify({query:t,variables:r})})}catch(o){console.warn(`\u26A0 Linear API request failed: ${o instanceof Error?o.message:String(o)}`);return}if(!n.ok){console.warn(`\u26A0 Linear API returned HTTP ${n.status}`);return}try{return await n.json()}catch{console.warn("\u26A0 Linear API returned invalid JSON");return}}async function Lp(e){let t;try{t=await fetch(Np,{method:"POST",headers:{Authorization:e,"Content-Type":"application/json"},body:JSON.stringify({query:"{ viewer { id } }"})})}catch{return{ok:!1,error:"\u2717 Could not reach Linear \u2014 check network"}}if(!t.ok)return t.status===401||t.status===403?{ok:!1,error:"\u2717 Linear auth failed \u2014 check LINEAR_API_KEY"}:{ok:!1,error:`\u2717 Linear API returned HTTP ${t.status}`};try{let r=await t.json(),n=zp.safeParse(r);if(n.success&&n.data.data?.viewer?.id)return{ok:!0}}catch{}return{ok:!1,error:"\u2717 Linear auth failed \u2014 check LINEAR_API_KEY"}}async function Zp(e,t,r=5){let n=e.CLANCY_LABEL?.trim(),o=!!n,s=o?"labels: { name: { eq: $label } }":"",i=["$teamId: String!",...o?["$label: String!"]:[]],a=['state: { type: { eq: "unstarted" } }',"team: { id: { eq: $teamId } }",s].filter(Boolean),u=`
|
|
123
123
|
query(${i.join(", ")}) {
|
|
124
124
|
viewer {
|
|
125
125
|
assignedIssues(
|
package/package.json
CHANGED