devin-bugs 0.2.0 → 0.3.0

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.
Files changed (2) hide show
  1. package/dist/cli.js +21 -20
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
- var m=Object.defineProperty;var f=(z)=>z;function g(z,Q){this[z]=f.bind(null,Q)}var h=(z,Q)=>{for(var X in Q)m(z,X,{get:Q[X],enumerable:!0,configurable:!0,set:g.bind(Q,X)})};var j=(z,Q)=>()=>(z&&(Q=z(z=0)),Q);import{homedir as C}from"node:os";import{join as U}from"node:path";var R="https://app.devin.ai/api",_="https://app.devin.ai",d,c,B,Pz,q=300;var O=j(()=>{d=U(C(),".config","devin-bugs"),c=U(C(),".cache","devin-bugs"),B=U(d,"token.json"),Pz=U(c,"browser-profile")});var E={};h(E,{getToken:()=>K,forceReauth:()=>N,clearAuth:()=>Mz});import{existsSync as D,mkdirSync as n,readFileSync as o,writeFileSync as s,unlinkSync as a}from"node:fs";import{dirname as t}from"node:path";import{createServer as r}from"node:http";import{execFile as e}from"node:child_process";function zz(z){let Q=z.replace(/-/g,"+").replace(/_/g,"/");return Buffer.from(Q,"base64").toString("utf-8")}function Qz(z){let Q=z.split(".");if(Q.length!==3)throw Error("Invalid JWT format");let X=JSON.parse(zz(Q[1]));if(typeof X.exp!=="number")throw Error("JWT missing exp claim");return X.exp*1000}function Xz(z){if(!D(z))n(z,{recursive:!0})}function Yz(){try{if(!D(B))return null;let z=o(B,"utf-8"),Q=JSON.parse(z);if(!Q.accessToken||!Q.expiresAt)return null;return Q}catch{return null}}function A(z){Xz(t(B));let Q=Qz(z),X={accessToken:z,obtainedAt:Date.now(),expiresAt:Q};return s(B,JSON.stringify(X,null,2)),X}function P(){try{if(D(B))a(B)}catch{}}function Zz(z){return z.expiresAt-Date.now()>q*1000}function $z(z){let Q=process.platform==="darwin"?{cmd:"open",args:[z]}:process.platform==="win32"?{cmd:"cmd",args:["/c","start","",z]}:{cmd:"xdg-open",args:[z]};e(Q.cmd,Q.args,(X)=>{if(X)console.error("\x1B[33m▸ Could not open browser automatically.\x1B[0m"),console.error(` Open this URL manually: ${z}
4
- `)})}function Jz(z){return`<!DOCTYPE html>
3
+ var y=Object.defineProperty;var f=(z)=>z;function u(z,Q){this[z]=f.bind(null,Q)}var h=(z,Q)=>{for(var X in Q)y(z,X,{get:Q[X],enumerable:!0,configurable:!0,set:u.bind(Q,X)})};var x=(z,Q)=>()=>(z&&(Q=z(z=0)),Q);import{homedir as _}from"node:os";import{join as K}from"node:path";var q="https://app.devin.ai/api",P="https://app.devin.ai",o,d,L,Ez,A=300;var I=x(()=>{o=K(_(),".config","devin-bugs"),d=K(_(),".cache","devin-bugs"),L=K(o,"token.json"),Ez=K(d,"browser-profile")});var b={};h(b,{getToken:()=>U,forceReauth:()=>S,clearAuth:()=>Wz});import{existsSync as D,mkdirSync as c,readFileSync as s,writeFileSync as t,unlinkSync as a}from"node:fs";import{dirname as r}from"node:path";import{createServer as e}from"node:http";import{execFile as zz}from"node:child_process";function w(z){let Q=z.replace(/-/g,"+").replace(/_/g,"/");return Buffer.from(Q,"base64").toString("utf-8")}function Qz(z){let Q=z.split(".");if(Q.length!==3)throw Error("Invalid JWT format");let X=JSON.parse(w(Q[1]));if(typeof X.exp!=="number")throw Error("JWT missing exp claim");return X.exp*1000}function Xz(z){if(!D(z))c(z,{recursive:!0})}function Yz(){try{if(!D(L))return null;let z=s(L,"utf-8"),Q=JSON.parse(z);if(!Q.accessToken||!Q.expiresAt)return null;return Q}catch{return null}}function N(z,Q){Xz(r(L));let X=Qz(z),Z={accessToken:z,obtainedAt:Date.now(),expiresAt:X,...Q&&Object.keys(Q).length>0?{auth0Cache:Q}:{}};return t(L,JSON.stringify(Z,null,2)),Z}async function Zz(z){if(!z.auth0Cache)return null;let Q=null,X=null,Z=null;for(let[Y,J]of Object.entries(z.auth0Cache)){if(!Y.startsWith("@@auth0spajs@@"))continue;let G=Y.split("::"),W=G[1],B=G[2];try{let V=JSON.parse(J);if(V.body?.refresh_token){Q=V.body.refresh_token,X=W??null,Z=B??null;break}}catch{continue}}if(!Q||!X)return null;let $=null;for(let Y of Object.keys(z.auth0Cache))if(Y.includes("https://")&&Y.includes("auth0")){let J=Y.match(/https:\/\/([^/]+)/);if(J?.[1]){$=J[1];break}}if(!$)try{let Y=JSON.parse(w(z.accessToken.split(".")[1]));if(typeof Y.iss==="string")$=new URL(Y.iss).hostname}catch{}if(!$)return console.error("\x1B[33m▸ Could not determine Auth0 domain for token refresh.\x1B[0m"),null;try{console.error("\x1B[33m▸ Refreshing token...\x1B[0m");let Y=await fetch(`https://${$}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"refresh_token",client_id:X,refresh_token:Q,...Z?{audience:Z}:{}})});if(!Y.ok){let G=await Y.text().catch(()=>"");return console.error(`\x1B[33m▸ Refresh failed (${Y.status}): ${G.slice(0,100)}\x1B[0m`),null}let J=await Y.json();if(!J.access_token)return null;if(J.refresh_token&&z.auth0Cache)for(let[G,W]of Object.entries(z.auth0Cache)){if(!G.startsWith("@@auth0spajs@@"))continue;try{let B=JSON.parse(W);if(B.body?.refresh_token){B.body.refresh_token=J.refresh_token,B.body.access_token=J.access_token,z.auth0Cache[G]=JSON.stringify(B);break}}catch{continue}}return console.error(`\x1B[32m✓ Token refreshed!\x1B[0m
4
+ `),J.access_token}catch(Y){return console.error(`\x1B[33m▸ Refresh error: ${Y instanceof Error?Y.message:Y}\x1B[0m`),null}}function E(){try{if(D(L))a(L)}catch{}}function $z(z){return z.expiresAt-Date.now()>A*1000}function Jz(z){let Q=process.platform==="darwin"?{cmd:"open",args:[z]}:process.platform==="win32"?{cmd:"cmd",args:["/c","start","",z]}:{cmd:"xdg-open",args:[z]};zz(Q.cmd,Q.args,(X)=>{if(X)console.error("\x1B[33m▸ Could not open browser automatically.\x1B[0m"),console.error(` Open this URL manually: ${z}
5
+ `)})}function Mz(z){return`<!DOCTYPE html>
5
6
  <html lang="en">
6
7
  <head>
7
8
  <meta charset="utf-8">
@@ -66,7 +67,7 @@ var m=Object.defineProperty;var f=(z)=>z;function g(z,Q){this[z]=f.bind(null,Q)}
66
67
  <div class="step">
67
68
  <div class="step-num">1</div>
68
69
  <div class="step-text">
69
- <a href="${_}" target="_blank" rel="noopener">
70
+ <a href="${P}" target="_blank" rel="noopener">
70
71
  Open app.devin.ai</a> and log in with GitHub
71
72
  </div>
72
73
  </div>
@@ -75,7 +76,7 @@ var m=Object.defineProperty;var f=(z)=>z;function g(z,Q){this[z]=f.bind(null,Q)}
75
76
  <div class="step-num">2</div>
76
77
  <div class="step-text">
77
78
  Open the browser console (<strong>F12</strong> → Console tab) and paste:
78
- <code id="snippet" onclick="copySnippet()">fetch('http://localhost:${z}/callback',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token:await __HACK__getAccessToken()})}).then(()=>document.title='✓ Token sent!')</code>
79
+ <code id="snippet" onclick="copySnippet()">{let t=await __HACK__getAccessToken(),c={};for(let i=0;i&lt;localStorage.length;i++){let k=localStorage.key(i);if(k&amp;&amp;k.includes('auth0'))c[k]=localStorage.getItem(k)}fetch('http://localhost:${z}/callback',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token:t,auth0Cache:c})}).then(()=>document.title='✓ Token sent!')}</code>
79
80
  </div>
80
81
  </div>
81
82
 
@@ -114,24 +115,24 @@ var m=Object.defineProperty;var f=(z)=>z;function g(z,Q){this[z]=f.bind(null,Q)}
114
115
  poll();
115
116
  </script>
116
117
  </body>
117
- </html>`}function w(){return new Promise((z,Q)=>{let X=null,Y=r((J,$)=>{if($.setHeader("Access-Control-Allow-Origin","*"),$.setHeader("Access-Control-Allow-Methods","POST, GET, OPTIONS"),$.setHeader("Access-Control-Allow-Headers","Content-Type"),J.method==="OPTIONS"){$.writeHead(204),$.end();return}if(J.method==="GET"&&J.url==="/status"){$.writeHead(200,{"Content-Type":"application/json"}),$.end(JSON.stringify({received:X!==null}));return}if(J.method==="POST"&&J.url==="/callback"){let M="";J.on("data",(G)=>M+=G.toString()),J.on("end",()=>{try{let G=JSON.parse(M);if(typeof G.token==="string"&&G.token.length>20){X=G.token,$.writeHead(200,{"Content-Type":"application/json"}),$.end(JSON.stringify({ok:!0})),setTimeout(()=>{Y.close(),z({token:X,server:Y})},500);return}}catch{}$.writeHead(400,{"Content-Type":"application/json"}),$.end(JSON.stringify({error:"Invalid token"}))});return}if(J.method==="GET"&&(J.url==="/"||J.url==="/login")){let M=Y.address().port;$.writeHead(200,{"Content-Type":"text/html"}),$.end(Jz(M));return}$.writeHead(404),$.end("Not found")});Y.listen(0,"127.0.0.1",()=>{let $=Y.address().port;console.error("\x1B[33m▸ Opening browser for Devin login...\x1B[0m"),console.error(` Local server: http://localhost:${$}
118
- `),$z(`http://localhost:${$}`),setTimeout(()=>{if(!X)Y.close(),Q(Error("Login timed out after 5 minutes."))},300000)}),Y.on("error",Q)})}async function K(z){let Q=process.env.DEVIN_TOKEN;if(Q&&Q.length>0)return Q;if(!z?.noCache){let Y=Yz();if(Y&&Zz(Y))return Y.accessToken}let{token:X}=await w();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
119
- `),A(X),X}async function N(){P();let{token:z}=await w();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
120
- `),A(z),z}function Mz(){P(),console.error("Cleared cached token.")}var S=j(()=>{O()});import{parseArgs as Hz}from"util";var u=/(?:https?:\/\/)?github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/,p=/^([^/#]+)\/([^/#]+)#(\d+)$/,l=/^([^/#]+)\/([^/#]+)\/pull\/(\d+)$/,i=/(?:https?:\/\/)?app\.devin\.ai\/review\/([^/]+)\/([^/]+)\/pull\/(\d+)/;function x(z){let Q=z.match(u)??z.match(i)??z.match(p)??z.match(l);if(!Q)throw Error(`Invalid PR reference: ${z}
121
- Expected: owner/repo#123 or https://github.com/owner/repo/pull/123`);let[,X,Y,J]=Q;return{owner:X,repo:Y,number:parseInt(J,10),prPath:`github.com/${X}/${Y}/pull/${J}`}}S();O();class F extends Error{constructor(){super("Authentication expired. Re-authenticating...");this.name="AuthExpiredError"}}class I extends Error{status;body;constructor(z,Q){super(`Devin API error ${z}: ${Q}`);this.status=z;this.body=Q;this.name="ApiError"}}async function Wz(z,Q){let X=`${R}/${z}`,Y=await fetch(X,{headers:{Authorization:`Bearer ${Q}`,Accept:"application/json"}});if(!Y.ok){if(Y.status===401||Y.status===403)throw new F;let J=await Y.text().catch(()=>"");throw new I(Y.status,J)}return Y.json()}async function H(z,Q){return Wz(`pr-review/digest?pr_path=${encodeURIComponent(z)}`,Q)}function Gz(z){if(!z)return null;let Q=z.match(/<!--\s*devin-review-comment\s*(\{.+\})\s*-->/);if(!Q?.[1])return null;try{let X=JSON.parse(Q[1]);return{id:String(X.id??""),file_path:String(X.file_path??""),start_line:typeof X.start_line==="number"?X.start_line:0,end_line:typeof X.end_line==="number"?X.end_line:0,side:X.side==="LEFT"?"LEFT":"RIGHT"}}catch{return null}}function Bz(z){if(z.startsWith("\uD83D\uDD34"))return"severe";if(z.startsWith("\uD83D\uDFE1"))return"warning";if(z.startsWith("\uD83D\uDFE2"))return"info";return"info"}function Vz(z){return z.match(/\*\*(.+?)\*\*/)?.[1]?.trim()??z.split(`
122
- `)[0]?.slice(0,120).trim()??""}function Lz(z){return z.split(`
118
+ </html>`}function C(){return new Promise((z,Q)=>{let X=null,Z=e(($,Y)=>{if(Y.setHeader("Access-Control-Allow-Origin","*"),Y.setHeader("Access-Control-Allow-Methods","POST, GET, OPTIONS"),Y.setHeader("Access-Control-Allow-Headers","Content-Type"),$.method==="OPTIONS"){Y.writeHead(204),Y.end();return}if($.method==="GET"&&$.url==="/status"){Y.writeHead(200,{"Content-Type":"application/json"}),Y.end(JSON.stringify({received:X!==null}));return}if($.method==="POST"&&$.url==="/callback"){let J="";$.on("data",(G)=>J+=G.toString()),$.on("end",()=>{try{let G=JSON.parse(J);if(typeof G.token==="string"&&G.token.length>20){X=G.token;let W=G.auth0Cache;if(W&&Object.keys(W).length>0)console.error(`\x1B[36m▸ Captured ${Object.keys(W).length} Auth0 cache entries\x1B[0m`);Y.writeHead(200,{"Content-Type":"application/json"}),Y.end(JSON.stringify({ok:!0})),setTimeout(()=>{Z.close(),z({token:X,auth0Cache:W,server:Z})},500);return}}catch{}Y.writeHead(400,{"Content-Type":"application/json"}),Y.end(JSON.stringify({error:"Invalid token"}))});return}if($.method==="GET"&&($.url==="/"||$.url==="/login")){let J=Z.address().port;Y.writeHead(200,{"Content-Type":"text/html"}),Y.end(Mz(J));return}Y.writeHead(404),Y.end("Not found")});Z.listen(0,"127.0.0.1",()=>{let Y=Z.address().port;console.error("\x1B[33m▸ Opening browser for Devin login...\x1B[0m"),console.error(` Local server: http://localhost:${Y}
119
+ `),Jz(`http://localhost:${Y}`),setTimeout(()=>{if(!X)Z.close(),Q(Error("Login timed out after 5 minutes."))},300000)}),Z.on("error",Q)})}async function U(z){let Q=process.env.DEVIN_TOKEN;if(Q&&Q.length>0)return Q;if(!z?.noCache){let $=Yz();if($&&$z($))return $.accessToken;if($?.auth0Cache){let Y=await Zz($);if(Y)return N(Y,$.auth0Cache),Y}}let{token:X,auth0Cache:Z}=await C();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
120
+ `),N(X,Z),X}async function S(){E();let{token:z,auth0Cache:Q}=await C();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
121
+ `),N(z,Q),z}function Wz(){E(),console.error("Cleared cached token.")}var H=x(()=>{I()});import{parseArgs as jz}from"util";var p=/(?:https?:\/\/)?github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/,l=/^([^/#]+)\/([^/#]+)#(\d+)$/,i=/^([^/#]+)\/([^/#]+)\/pull\/(\d+)$/,n=/(?:https?:\/\/)?app\.devin\.ai\/review\/([^/]+)\/([^/]+)\/pull\/(\d+)/;function R(z){let Q=z.match(p)??z.match(n)??z.match(l)??z.match(i);if(!Q)throw Error(`Invalid PR reference: ${z}
122
+ Expected: owner/repo#123 or https://github.com/owner/repo/pull/123`);let[,X,Z,$]=Q;return{owner:X,repo:Z,number:parseInt($,10),prPath:`github.com/${X}/${Z}/pull/${$}`}}H();I();class F extends Error{constructor(){super("Authentication expired. Re-authenticating...");this.name="AuthExpiredError"}}class O extends Error{status;body;constructor(z,Q){super(`Devin API error ${z}: ${Q}`);this.status=z;this.body=Q;this.name="ApiError"}}async function Gz(z,Q){let X=`${q}/${z}`,Z=await fetch(X,{headers:{Authorization:`Bearer ${Q}`,Accept:"application/json"}});if(!Z.ok){if(Z.status===401||Z.status===403)throw new F;let $=await Z.text().catch(()=>"");throw new O(Z.status,$)}return Z.json()}async function j(z,Q){return Gz(`pr-review/digest?pr_path=${encodeURIComponent(z)}`,Q)}function Bz(z){if(!z)return null;let Q=z.match(/<!--\s*devin-review-comment\s*(\{.+\})\s*-->/);if(!Q?.[1])return null;try{let X=JSON.parse(Q[1]);return{id:String(X.id??""),file_path:String(X.file_path??""),start_line:typeof X.start_line==="number"?X.start_line:0,end_line:typeof X.end_line==="number"?X.end_line:0,side:X.side==="LEFT"?"LEFT":"RIGHT"}}catch{return null}}function Vz(z){if(z.startsWith("\uD83D\uDD34"))return"severe";if(z.startsWith("\uD83D\uDFE1"))return"warning";if(z.startsWith("\uD83D\uDFE2"))return"info";return"info"}function Lz(z){return z.match(/\*\*(.+?)\*\*/)?.[1]?.trim()??z.split(`
123
+ `)[0]?.slice(0,120).trim()??""}function Kz(z){return z.split(`
123
124
  `).slice(1).join(`
124
- `).trim()}function Uz(z){return z.match(/(?:recommendation|suggested fix|fix):\s*(.+?)(?:\n\n|\n#+|\n🔴|\n🟡|$)/is)?.[1]?.trim()??""}function Kz(z,Q){if(z.startsWith("BUG_"))return"lifeguard-bug";if(z.startsWith("ANALYSIS_")||z.startsWith("INFO_"))return"lifeguard-analysis";let X=Q.toLowerCase();if(X.includes("potential bug")||X.includes("\uD83D\uDD34")||X.includes("bug:")||X.includes("race condition")||X.includes("vulnerability")||X.includes("double-charge")||X.includes("sql injection"))return"lifeguard-bug";return"lifeguard-analysis"}function Fz(z,Q){let X=Gz(Q.hidden_header),Y=Q.body??"";if(!Y&&!X)return null;let J=X?.id??String(Q.devin_review_id??""),$=Kz(J,Y);return{filePath:X?.file_path??"",startLine:X?.start_line??null,endLine:X?.end_line??null,side:X?.side??"RIGHT",title:Vz(Y),description:Lz(Y),severity:Bz(Y),recommendation:Uz(Y),needsInvestigation:Y.toLowerCase().includes("needs investigation"),type:$,isResolved:z.is_resolved,isOutdated:z.is_outdated,htmlUrl:Q.html_url??null}}function Iz(z){return z.devin_review_id!=null||z.hidden_header?.includes("devin-review-comment")===!0||z.author?.login==="devin-ai-integration"||z.author?.login==="devin-ai-integration[bot]"||z.author?.login==="devin-ai[bot]"}function b(z,Q){let X=[];for(let Y of z.review_threads){if(!Q?.includeResolved&&Y.is_resolved)continue;if(!Q?.includeOutdated&&Y.is_outdated)continue;for(let J of Y.comments){if(!Iz(J))continue;let $=Fz(Y,J);if($){X.push($);break}}}if(!Q?.includeAnalysis)return X.filter((Y)=>Y.type==="lifeguard-bug");return X}var Z={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",white:"\x1B[37m",bgRed:"\x1B[41m",bgYellow:"\x1B[43m",bgBlue:"\x1B[44m"};function Oz(z){let Q=z.toUpperCase();switch(z.toLowerCase()){case"severe":case"critical":return`${Z.bgRed}${Z.white}${Z.bold} ${Q} ${Z.reset}`;case"warning":return`${Z.bgYellow}${Z.bold} ${Q} ${Z.reset}`;default:return`${Z.bgBlue}${Z.white} ${Q} ${Z.reset}`}}function Dz(z){if(z==="lifeguard-bug")return`${Z.red}${Z.bold}BUG${Z.reset}`;return`${Z.cyan}${Z.bold}INFO${Z.reset}`}function Nz(z){if(!z.filePath)return"";let Q=`${Z.cyan}${z.filePath}${Z.reset}`;if(z.startLine==null)return Q;let X=z.endLine!=null&&z.endLine!==z.startLine?`${Z.dim}:${z.startLine}-${z.endLine}${Z.reset}`:`${Z.dim}:${z.startLine}${Z.reset}`;return`${Q}${X}`}function Sz(z,Q,X){let Y=" ".repeat(Q),J=z.split(/\s+/),$=[],M="";for(let G of J)if(M.length+G.length+1>X-Q)$.push(Y+M),M=G;else M=M?`${M} ${G}`:G;if(M)$.push(Y+M);return $.join(`
125
- `)}function T(z,Q){let X=[],Y=z.filter((M)=>M.type==="lifeguard-bug").length,J=z.filter((M)=>M.type==="lifeguard-analysis").length,$=[];if(Y>0)$.push(`${Z.red}${Z.bold}${Y} bug${Y===1?"":"s"}${Z.reset}`);if(J>0)$.push(`${Z.cyan}${J} suggestion${J===1?"":"s"}${Z.reset}`);if($.length===0)return X.push(`
126
- ${Z.green}${Z.bold}No unresolved bugs${Z.reset} in ${Z.dim}${Q.owner}/${Q.repo}#${Q.number}${Z.reset}
125
+ `).trim()}function Uz(z){return z.match(/(?:recommendation|suggested fix|fix):\s*(.+?)(?:\n\n|\n#+|\n🔴|\n🟡|$)/is)?.[1]?.trim()??""}function Fz(z,Q){if(z.startsWith("BUG_"))return"lifeguard-bug";if(z.startsWith("ANALYSIS_")||z.startsWith("INFO_"))return"lifeguard-analysis";let X=Q.toLowerCase();if(X.includes("potential bug")||X.includes("\uD83D\uDD34")||X.includes("bug:")||X.includes("race condition")||X.includes("vulnerability")||X.includes("double-charge")||X.includes("sql injection"))return"lifeguard-bug";return"lifeguard-analysis"}function Oz(z,Q){let X=Bz(Q.hidden_header),Z=Q.body??"";if(!Z&&!X)return null;let $=X?.id??String(Q.devin_review_id??""),Y=Fz($,Z);return{filePath:X?.file_path??"",startLine:X?.start_line??null,endLine:X?.end_line??null,side:X?.side??"RIGHT",title:Lz(Z),description:Kz(Z),severity:Vz(Z),recommendation:Uz(Z),needsInvestigation:Z.toLowerCase().includes("needs investigation"),type:Y,isResolved:z.is_resolved,isOutdated:z.is_outdated,htmlUrl:Q.html_url??null}}function Iz(z){return z.devin_review_id!=null||z.hidden_header?.includes("devin-review-comment")===!0||z.author?.login==="devin-ai-integration"||z.author?.login==="devin-ai-integration[bot]"||z.author?.login==="devin-ai[bot]"}function v(z,Q){let X=[];for(let Z of z.review_threads){if(!Q?.includeResolved&&Z.is_resolved)continue;if(!Q?.includeOutdated&&Z.is_outdated)continue;for(let $ of Z.comments){if(!Iz($))continue;let Y=Oz(Z,$);if(Y){X.push(Y);break}}}if(!Q?.includeAnalysis)return X.filter((Z)=>Z.type==="lifeguard-bug");return X}var M={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m",white:"\x1B[37m",bgRed:"\x1B[41m",bgYellow:"\x1B[43m",bgBlue:"\x1B[44m"};function Nz(z){let Q=z.toUpperCase();switch(z.toLowerCase()){case"severe":case"critical":return`${M.bgRed}${M.white}${M.bold} ${Q} ${M.reset}`;case"warning":return`${M.bgYellow}${M.bold} ${Q} ${M.reset}`;default:return`${M.bgBlue}${M.white} ${Q} ${M.reset}`}}function Dz(z){if(z==="lifeguard-bug")return`${M.red}${M.bold}BUG${M.reset}`;return`${M.cyan}${M.bold}INFO${M.reset}`}function Sz(z){if(!z.filePath)return"";let Q=`${M.cyan}${z.filePath}${M.reset}`;if(z.startLine==null)return Q;let X=z.endLine!=null&&z.endLine!==z.startLine?`${M.dim}:${z.startLine}-${z.endLine}${M.reset}`:`${M.dim}:${z.startLine}${M.reset}`;return`${Q}${X}`}function Hz(z,Q,X){let Z=" ".repeat(Q),$=z.split(/\s+/),Y=[],J="";for(let G of $)if(J.length+G.length+1>X-Q)Y.push(Z+J),J=G;else J=J?`${J} ${G}`:G;if(J)Y.push(Z+J);return Y.join(`
126
+ `)}function T(z,Q){let X=[],Z=z.filter((J)=>J.type==="lifeguard-bug").length,$=z.filter((J)=>J.type==="lifeguard-analysis").length,Y=[];if(Z>0)Y.push(`${M.red}${M.bold}${Z} bug${Z===1?"":"s"}${M.reset}`);if($>0)Y.push(`${M.cyan}${$} suggestion${$===1?"":"s"}${M.reset}`);if(Y.length===0)return X.push(`
127
+ ${M.green}${M.bold}No unresolved bugs${M.reset} in ${M.dim}${Q.owner}/${Q.repo}#${Q.number}${M.reset}
127
128
  `),X.join(`
128
129
  `);X.push(`
129
- ${$.join(", ")} in ${Z.dim}${Q.owner}/${Q.repo}#${Q.number}${Z.reset}
130
- `);for(let M of z){let G=Dz(M.type),W=Nz(M),V=Oz(M.severity);if(X.push(` ${G} ${W} ${V}`),M.title)X.push(` ${Z.bold}${Z.white}${M.title}${Z.reset}`);if(M.description&&M.description!==M.title){let L=M.description.replace(/<details>[\s\S]*?<\/details>/g,"").replace(/<!--[\s\S]*?-->/g,"").replace(/^\[.*?\]\(.*?\)$/gm,"").replace(/<a[\s\S]*?<\/a>/g,"").replace(/<picture>[\s\S]*?<\/picture>/g,"").replace(/<img[^>]*>/g,"").replace(/^---\s*$/gm,"").replace(/^\*Was this helpful\?.*$/gm,"").replace(/^#+\s*.+$/gm,"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").trim().split(`
130
+ ${Y.join(", ")} in ${M.dim}${Q.owner}/${Q.repo}#${Q.number}${M.reset}
131
+ `);for(let J of z){let G=Dz(J.type),W=Sz(J),B=Nz(J.severity);if(X.push(` ${G} ${W} ${B}`),J.title)X.push(` ${M.bold}${M.white}${J.title}${M.reset}`);if(J.description&&J.description!==J.title){let V=J.description.replace(/<details>[\s\S]*?<\/details>/g,"").replace(/<!--[\s\S]*?-->/g,"").replace(/^\[.*?\]\(.*?\)$/gm,"").replace(/<a[\s\S]*?<\/a>/g,"").replace(/<picture>[\s\S]*?<\/picture>/g,"").replace(/<img[^>]*>/g,"").replace(/^---\s*$/gm,"").replace(/^\*Was this helpful\?.*$/gm,"").replace(/^#+\s*.+$/gm,"").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").trim().split(`
131
132
 
132
133
  `)[0].split(`
133
- `).filter((y)=>y.trim()).join(" ").trim();if(L)X.push(Sz(`${Z.dim}${L}${Z.reset}`,2,100))}if(M.recommendation)X.push(` ${Z.green}→ ${M.recommendation}${Z.reset}`);X.push("")}return X.join(`
134
- `)}function v(z){return JSON.stringify(z,null,2)}var jz=`
134
+ `).filter((m)=>m.trim()).join(" ").trim();if(V)X.push(Hz(`${M.dim}${V}${M.reset}`,2,100))}if(J.recommendation)X.push(` ${M.green}→ ${J.recommendation}${M.reset}`);X.push("")}return X.join(`
135
+ `)}function k(z){return JSON.stringify(z,null,2)}var xz=`
135
136
  \x1B[1mdevin-bugs\x1B[0m \u2014 Extract unresolved bugs from Devin AI code reviews
136
137
 
137
138
  \x1B[1mUsage:\x1B[0m
@@ -161,6 +162,6 @@ Expected: owner/repo#123 or https://github.com/owner/repo/pull/123`);let[,X,Y,J]
161
162
  devin-bugs owner/repo#46 --json
162
163
  devin-bugs owner/repo#46 --all --raw
163
164
  DEVIN_TOKEN=xxx devin-bugs owner/repo#46
164
- `;function k(){console.log(jz)}function xz(){console.log("devin-bugs 0.1.0")}async function Cz(){let z;try{z=Hz({allowPositionals:!0,options:{json:{type:"boolean",default:!1},all:{type:"boolean",default:!1},raw:{type:"boolean",default:!1},"no-cache":{type:"boolean",default:!1},login:{type:"boolean",default:!1},logout:{type:"boolean",default:!1},help:{type:"boolean",short:"h",default:!1},version:{type:"boolean",short:"v",default:!1}}})}catch(W){console.error(`\x1B[31mError:\x1B[0m ${W.message}`),process.exit(1)}let{values:Q,positionals:X}=z;if(Q.help){k();return}if(Q.version){xz();return}if(Q.logout){let{clearAuth:W}=await Promise.resolve().then(() => (S(),E));W();return}if(Q.login){let W=await K({noCache:Q["no-cache"]});console.error("\x1B[32m\u2713 Authenticated successfully.\x1B[0m"),console.error(` Token cached for future use.
165
- `);try{let V=JSON.parse(Buffer.from(W.split(".")[1],"base64url").toString()),L=new Date(V.exp*1000);console.error(` Expires: ${L.toLocaleString()}`)}catch{}return}if(X.length===0)console.error(`\x1B[31mError:\x1B[0m Missing PR argument.
166
- `),k(),process.exit(1);let Y=X[0],J;try{J=x(Y)}catch(W){console.error(`\x1B[31mError:\x1B[0m ${W.message}`),process.exit(1)}let $;try{$=await K({noCache:Q["no-cache"]})}catch(W){console.error(`\x1B[31mAuth error:\x1B[0m ${W.message}`),process.exit(1)}let M;try{M=await H(J.prPath,$)}catch(W){if(W instanceof F){console.error("\x1B[33m\u25B8 Token expired, re-authenticating...\x1B[0m");try{$=await N(),M=await H(J.prPath,$)}catch(V){console.error(`\x1B[31mError:\x1B[0m ${V.message}`),process.exit(1)}}else if(W instanceof I){if(W.status===404)console.error(`\x1B[31mError:\x1B[0m PR not found or no Devin review exists for ${J.owner}/${J.repo}#${J.number}`);else console.error(`\x1B[31mAPI error ${W.status}:\x1B[0m ${W.body}`);process.exit(1)}else throw W}if(Q.raw){console.log(JSON.stringify(M,null,2));return}let G=b(M,{includeAnalysis:Q.all});if(Q.json)console.log(v(G));else console.log(T(G,J))}Cz().catch((z)=>{console.error(`\x1B[31mFatal error:\x1B[0m ${z.message??z}`),process.exit(1)});
165
+ `;function g(){console.log(xz)}function Rz(){console.log("devin-bugs 0.3.0")}async function _z(){let z;try{z=jz({allowPositionals:!0,options:{json:{type:"boolean",default:!1},all:{type:"boolean",default:!1},raw:{type:"boolean",default:!1},"no-cache":{type:"boolean",default:!1},login:{type:"boolean",default:!1},logout:{type:"boolean",default:!1},help:{type:"boolean",short:"h",default:!1},version:{type:"boolean",short:"v",default:!1}}})}catch(W){console.error(`\x1B[31mError:\x1B[0m ${W.message}`),process.exit(1)}let{values:Q,positionals:X}=z;if(Q.help){g();return}if(Q.version){Rz();return}if(Q.logout){let{clearAuth:W}=await Promise.resolve().then(() => (H(),b));W();return}if(Q.login){let W=await U({noCache:Q["no-cache"]});console.error("\x1B[32m\u2713 Authenticated successfully.\x1B[0m"),console.error(` Token cached for future use.
166
+ `);try{let B=JSON.parse(Buffer.from(W.split(".")[1],"base64url").toString()),V=new Date(B.exp*1000);console.error(` Expires: ${V.toLocaleString()}`)}catch{}return}if(X.length===0)console.error(`\x1B[31mError:\x1B[0m Missing PR argument.
167
+ `),g(),process.exit(1);let Z=X[0],$;try{$=R(Z)}catch(W){console.error(`\x1B[31mError:\x1B[0m ${W.message}`),process.exit(1)}let Y;try{Y=await U({noCache:Q["no-cache"]})}catch(W){console.error(`\x1B[31mAuth error:\x1B[0m ${W.message}`),process.exit(1)}let J;try{J=await j($.prPath,Y)}catch(W){if(W instanceof F){console.error("\x1B[33m\u25B8 Token expired, re-authenticating...\x1B[0m");try{Y=await S(),J=await j($.prPath,Y)}catch(B){console.error(`\x1B[31mError:\x1B[0m ${B.message}`),process.exit(1)}}else if(W instanceof O){if(W.status===404)console.error(`\x1B[31mError:\x1B[0m PR not found or no Devin review exists for ${$.owner}/${$.repo}#${$.number}`);else console.error(`\x1B[31mAPI error ${W.status}:\x1B[0m ${W.body}`);process.exit(1)}else throw W}if(Q.raw){console.log(JSON.stringify(J,null,2));return}let G=v(J,{includeAnalysis:Q.all});if(Q.json)console.log(k(G));else console.log(T(G,$))}_z().catch((z)=>{console.error(`\x1B[31mFatal error:\x1B[0m ${z.message??z}`),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devin-bugs",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI to extract unresolved bugs from Devin AI code reviews",
5
5
  "type": "module",
6
6
  "bin": {