devin-bugs 0.3.0 → 0.4.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.
- package/dist/cli.js +33 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @bun
|
|
3
|
-
var
|
|
4
|
-
`),
|
|
5
|
-
`)})}function
|
|
3
|
+
var p=Object.defineProperty;var i=(z)=>z;function l(z,Q){this[z]=i.bind(null,Q)}var d=(z,Q)=>{for(var X in Q)p(z,X,{get:Q[X],enumerable:!0,configurable:!0,set:l.bind(Q,X)})};var j=(z,Q)=>()=>(z&&(Q=z(z=0)),Q);import{homedir as P}from"node:os";import{join as L}from"node:path";var _="https://app.devin.ai/api",w="https://app.devin.ai",e,zz,V,mz,R=300;var D=j(()=>{e=L(P(),".config","devin-bugs"),zz=L(P(),".cache","devin-bugs"),V=L(e,"token.json"),mz=L(zz,"browser-profile")});var v={};d(v,{getToken:()=>U,forceReauth:()=>H,clearAuth:()=>Fz});import{existsSync as N,mkdirSync as Qz,readFileSync as Xz,writeFileSync as Yz,unlinkSync as Zz}from"node:fs";import{dirname as $z}from"node:path";import{createServer as Jz}from"node:http";import{execFile as Wz}from"node:child_process";function S(z){let Q=z.replace(/-/g,"+").replace(/_/g,"/");return Buffer.from(Q,"base64").toString("utf-8")}function Gz(z){let Q=z.split(".");if(Q.length!==3)throw Error("Invalid JWT format");let X=JSON.parse(S(Q[1]));if(typeof X.exp!=="number")throw Error("JWT missing exp claim");return X.exp*1000}function Mz(z){if(!N(z))Qz(z,{recursive:!0})}function Bz(){try{if(!N(V))return null;let z=Xz(V,"utf-8"),Q=JSON.parse(z);if(!Q.accessToken||!Q.expiresAt)return null;return Q}catch{return null}}function I(z,Q){Mz($z(V));let X=Gz(z),Z={accessToken:z,obtainedAt:Date.now(),expiresAt:X,...Q&&Object.keys(Q).length>0?{auth0Cache:Q}:{}};return Yz(V,JSON.stringify(Z,null,2)),Z}async function Kz(z){if(!z.auth0Cache)return null;let Q=null,X=null,Z=null;for(let[$,W]of Object.entries(z.auth0Cache)){if(!$.startsWith("@@auth0spajs@@"))continue;let M=$.split("::"),G=M[1],B=M[2];try{let K=JSON.parse(W);if(K.body?.refresh_token){Q=K.body.refresh_token,X=G??null,Z=B??null;break}}catch{continue}}if(!Q||!X)return null;let J=null;try{let $=JSON.parse(S(z.accessToken.split(".")[1]));if(typeof $.iss==="string")J=new URL($.iss).hostname}catch{}if(!J)return console.error("\x1B[33m▸ Could not determine Auth0 domain for token refresh.\x1B[0m"),null;try{let $=`https://${J}/oauth/token`;console.error(`\x1B[33m▸ Refreshing token via ${$}\x1B[0m`);let W=await fetch($,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"refresh_token",client_id:X,refresh_token:Q,...Z?{audience:Z}:{}})});if(!W.ok){let G=await W.text().catch(()=>"");return console.error(`\x1B[33m▸ Refresh failed (${W.status}): ${G.slice(0,100)}\x1B[0m`),null}let M=await W.json();if(!M.access_token)return null;if(M.refresh_token&&z.auth0Cache)for(let[G,B]of Object.entries(z.auth0Cache)){if(!G.startsWith("@@auth0spajs@@"))continue;try{let K=JSON.parse(B);if(K.body?.refresh_token){K.body.refresh_token=M.refresh_token,K.body.access_token=M.access_token,z.auth0Cache[G]=JSON.stringify(K);break}}catch{continue}}return console.error(`\x1B[32m✓ Token refreshed!\x1B[0m
|
|
4
|
+
`),M.access_token}catch($){return console.error(`\x1B[33m▸ Refresh error: ${$ instanceof Error?$.message:$}\x1B[0m`),null}}function C(){try{if(N(V))Zz(V)}catch{}}function Vz(z){return z.expiresAt-Date.now()>R*1000}function Lz(z){let Q=process.platform==="darwin"?{cmd:"open",args:[z]}:process.platform==="win32"?{cmd:"cmd",args:["/c","start","",z]}:{cmd:"xdg-open",args:[z]};Wz(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 Uz(z){return`<!DOCTYPE html>
|
|
6
6
|
<html lang="en">
|
|
7
7
|
<head>
|
|
8
8
|
<meta charset="utf-8">
|
|
@@ -67,7 +67,7 @@ var y=Object.defineProperty;var f=(z)=>z;function u(z,Q){this[z]=f.bind(null,Q)}
|
|
|
67
67
|
<div class="step">
|
|
68
68
|
<div class="step-num">1</div>
|
|
69
69
|
<div class="step-text">
|
|
70
|
-
<a href="${
|
|
70
|
+
<a href="${w}" target="_blank" rel="noopener">
|
|
71
71
|
Open app.devin.ai</a> and log in with GitHub
|
|
72
72
|
</div>
|
|
73
73
|
</div>
|
|
@@ -115,32 +115,44 @@ var y=Object.defineProperty;var f=(z)=>z;function u(z,Q){this[z]=f.bind(null,Q)}
|
|
|
115
115
|
poll();
|
|
116
116
|
</script>
|
|
117
117
|
</body>
|
|
118
|
-
</html>`}function
|
|
119
|
-
`),
|
|
120
|
-
`),
|
|
121
|
-
`),
|
|
122
|
-
|
|
123
|
-
`)[0]?.
|
|
118
|
+
</html>`}function E(){return new Promise((z,Q)=>{let X=null,Z=Jz((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 W="";J.on("data",(M)=>W+=M.toString()),J.on("end",()=>{try{let M=JSON.parse(W);if(typeof M.token==="string"&&M.token.length>20){X=M.token;let G=M.auth0Cache;if(G&&Object.keys(G).length>0)console.error(`\x1B[36m▸ Captured ${Object.keys(G).length} Auth0 cache entries\x1B[0m`);$.writeHead(200,{"Content-Type":"application/json"}),$.end(JSON.stringify({ok:!0})),setTimeout(()=>{Z.close(),z({token:X,auth0Cache:G,server:Z})},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 W=Z.address().port;$.writeHead(200,{"Content-Type":"text/html"}),$.end(Uz(W));return}$.writeHead(404),$.end("Not found")});Z.listen(0,"127.0.0.1",()=>{let $=Z.address().port;console.error("\x1B[33m▸ Opening browser for Devin login...\x1B[0m"),console.error(` Local server: http://localhost:${$}
|
|
119
|
+
`),Lz(`http://localhost:${$}`),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 J=Bz();if(J&&Vz(J))return J.accessToken;if(J?.auth0Cache){let $=await Kz(J);if($)return I($,J.auth0Cache),$}}let{token:X,auth0Cache:Z}=await E();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
|
|
120
|
+
`),I(X,Z),X}async function H(){C();let{token:z,auth0Cache:Q}=await E();return console.error(`\x1B[32m✓ Authentication successful!\x1B[0m
|
|
121
|
+
`),I(z,Q),z}function Fz(){C(),console.error("Cleared cached token.")}var x=j(()=>{D()});import{parseArgs as Rz}from"util";import{execFileSync as n}from"node:child_process";var o=/(?:https?:\/\/)?github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/,c=/^([^/#]+)\/([^/#]+)#(\d+)$/,s=/^([^/#]+)\/([^/#]+)\/pull\/(\d+)$/,t=/^#?(\d+)$/,a=/(?:https?:\/\/)?app\.devin\.ai\/review\/([^/]+)\/([^/]+)\/pull\/(\d+)/;function r(){try{let z=n("git",["remote","get-url","origin"],{encoding:"utf-8",timeout:3000,stdio:["pipe","pipe","pipe"]}).trim(),Q=z.match(/github\.com\/([^/]+)\/([^/.]+)/);if(Q)return{owner:Q[1],repo:Q[2]};let X=z.match(/github\.com:([^/]+)\/([^/.]+)/);if(X)return{owner:X[1],repo:X[2]}}catch{}return null}function A(z){let Q=z.match(o)??z.match(a)??z.match(c)??z.match(s);if(Q){let[,Z,J,$]=Q;return{owner:Z,repo:J,number:parseInt($,10),prPath:`github.com/${Z}/${J}/pull/${$}`}}let X=z.match(t);if(X){let Z=r();if(Z){let J=X[1];return{owner:Z.owner,repo:Z.repo,number:parseInt(J,10),prPath:`github.com/${Z.owner}/${Z.repo}/pull/${J}`}}throw Error(`Cannot resolve PR #${X[1]} — not in a GitHub repo.
|
|
122
|
+
`+`Use the full form: owner/repo#${X[1]}`)}throw Error(`Invalid PR reference: ${z}
|
|
123
|
+
Expected: owner/repo#123, #123 (in a git repo), or https://github.com/owner/repo/pull/123`)}x();D();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 T(z,Q){let X=`${_}/${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 J=await Z.text().catch(()=>"");throw new O(Z.status,J)}return Z.json()}async function q(z,Q){return T(`pr-review/digest?pr_path=${encodeURIComponent(z)}`,Q)}async function k(z,Q){return T(`pr-review/jobs?pr_path=${encodeURIComponent(z)}`,Q)}function b(z){if(z.jobs.length===0)return{status:"no_review",message:"No Devin review has been triggered for this PR."};let Q=z.jobs.sort((Z,J)=>new Date(J.created_at).getTime()-new Date(Z.created_at).getTime())[0],X=["lifeguard","groups","copy_detection","display_info"];if(Q.status==="completed"){if(Q.versions[Q.versions.length-1]?.metadata.is_finished)return{status:"completed",message:"Devin review complete.",stages:{completed:X,total:X}}}if(Q.status==="running"||Q.status==="completed"&&Q.versions.length>0){let Z=Q.versions[Q.versions.length-1],J=Z?.metadata.completed??[],$={lifeguard:"Bug detection",groups:"File grouping",copy_detection:"Copy detection",display_info:"Finalizing"};if(!Z?.metadata.is_finished){let W=X.find((G)=>!J.includes(G));return{status:"running",message:`Devin review in progress: ${W?$[W]??W:"Processing"} (${J.length}/${X.length} stages)`,stages:{completed:J,total:X}}}return{status:"completed",message:"Devin review complete.",stages:{completed:J,total:X}}}if(Q.status==="failed")return{status:"failed",message:"Devin review failed."};return{status:"running",message:"Devin review pending...",stages:{completed:[],total:X}}}function Oz(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 Dz(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 Iz(z){return z.match(/\*\*(.+?)\*\*/)?.[1]?.trim()??z.split(`
|
|
124
|
+
`)[0]?.slice(0,120).trim()??""}function Nz(z){return z.split(`
|
|
124
125
|
`).slice(1).join(`
|
|
125
|
-
`).trim()}function
|
|
126
|
-
`)}function
|
|
127
|
-
${
|
|
126
|
+
`).trim()}function Hz(z){return z.match(/(?:recommendation|suggested fix|fix):\s*(.+?)(?:\n\n|\n#+|\n🔴|\n🟡|$)/is)?.[1]?.trim()??""}function xz(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 qz(z,Q){let X=Oz(Q.hidden_header),Z=Q.body??"";if(!Z&&!X)return null;let J=X?.id??String(Q.devin_review_id??""),$=xz(J,Z);return{filePath:X?.file_path??"",startLine:X?.start_line??null,endLine:X?.end_line??null,side:X?.side??"RIGHT",title:Iz(Z),description:Nz(Z),severity:Dz(Z),recommendation:Hz(Z),needsInvestigation:Z.toLowerCase().includes("needs investigation"),type:$,isResolved:z.is_resolved,isOutdated:z.is_outdated,htmlUrl:Q.html_url??null}}function jz(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 y(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 J of Z.comments){if(!jz(J))continue;let $=qz(Z,J);if($){X.push($);break}}}if(!Q?.includeAnalysis)return X.filter((Z)=>Z.type==="lifeguard-bug");return X}var Y={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 Az(z){let Q=z.toUpperCase();switch(z.toLowerCase()){case"severe":case"critical":return`${Y.bgRed}${Y.white}${Y.bold} ${Q} ${Y.reset}`;case"warning":return`${Y.bgYellow}${Y.bold} ${Q} ${Y.reset}`;default:return`${Y.bgBlue}${Y.white} ${Q} ${Y.reset}`}}function Pz(z){if(z==="lifeguard-bug")return`${Y.red}${Y.bold}BUG${Y.reset}`;return`${Y.cyan}${Y.bold}INFO${Y.reset}`}function _z(z){if(!z.filePath)return"";let Q=`${Y.cyan}${z.filePath}${Y.reset}`;if(z.startLine==null)return Q;let X=z.endLine!=null&&z.endLine!==z.startLine?`${Y.dim}:${z.startLine}-${z.endLine}${Y.reset}`:`${Y.dim}:${z.startLine}${Y.reset}`;return`${Q}${X}`}function wz(z,Q,X){let Z=" ".repeat(Q),J=z.split(/\s+/),$=[],W="";for(let M of J)if(W.length+M.length+1>X-Q)$.push(Z+W),W=M;else W=W?`${W} ${M}`:M;if(W)$.push(Z+W);return $.join(`
|
|
127
|
+
`)}function m(z,Q){let X=[],Z=z.filter((W)=>W.type==="lifeguard-bug").length,J=z.filter((W)=>W.type==="lifeguard-analysis").length,$=[];if(Z>0)$.push(`${Y.red}${Y.bold}${Z} bug${Z===1?"":"s"}${Y.reset}`);if(J>0)$.push(`${Y.cyan}${J} suggestion${J===1?"":"s"}${Y.reset}`);if($.length===0)return X.push(`
|
|
128
|
+
${Y.green}${Y.bold}No unresolved bugs${Y.reset} in ${Y.dim}${Q.owner}/${Q.repo}#${Q.number}${Y.reset}
|
|
128
129
|
`),X.join(`
|
|
129
130
|
`);X.push(`
|
|
130
|
-
${
|
|
131
|
-
`);for(let
|
|
131
|
+
${$.join(", ")} in ${Y.dim}${Q.owner}/${Q.repo}#${Q.number}${Y.reset}
|
|
132
|
+
`);for(let W of z){let M=Pz(W.type),G=_z(W),B=Az(W.severity);if(X.push(` ${M} ${G} ${B}`),W.title)X.push(` ${Y.bold}${Y.white}${W.title}${Y.reset}`);if(W.description&&W.description!==W.title){let K=W.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(`
|
|
132
133
|
|
|
133
134
|
`)[0].split(`
|
|
134
|
-
`).filter((
|
|
135
|
-
`)}function
|
|
135
|
+
`).filter((h)=>h.trim()).join(" ").trim();if(K)X.push(wz(`${Y.dim}${K}${Y.reset}`,2,100))}if(W.recommendation)X.push(` ${Y.green}→ ${W.recommendation}${Y.reset}`);X.push("")}return X.join(`
|
|
136
|
+
`)}function f(z,Q){let X=[],Z=`${Y.dim}${Q.owner}/${Q.repo}#${Q.number}${Y.reset}`,J={lifeguard:"Bug detection",groups:"File grouping",copy_detection:"Copy detection",display_info:"Finalizing"};switch(z.status){case"no_review":X.push(`
|
|
137
|
+
${Y.yellow}No Devin review${Y.reset} for ${Z}`),X.push(` ${Y.dim}Devin hasn't been triggered on this PR yet.${Y.reset}
|
|
138
|
+
`);break;case"running":{let $=z.stages;if(X.push(`
|
|
139
|
+
${Y.yellow}${Y.bold}Devin review in progress${Y.reset} for ${Z}
|
|
140
|
+
`),$){for(let W of $.total){let M=$.completed.includes(W),G=J[W]??W,B=M?`${Y.green}✓${Y.reset}`:`${Y.yellow}○${Y.reset}`;X.push(` ${B} ${M?Y.dim:Y.white}${G}${Y.reset}`)}X.push("")}X.push(` ${Y.dim}Bugs will appear once the review completes.${Y.reset}
|
|
141
|
+
`);break}case"completed":X.push(`
|
|
142
|
+
${Y.green}${Y.bold}No unresolved bugs${Y.reset} in ${Z}`),X.push(` ${Y.dim}Devin review complete — all clear.${Y.reset}
|
|
143
|
+
`);break;case"failed":X.push(`
|
|
144
|
+
${Y.red}Devin review failed${Y.reset} for ${Z}`),X.push(` ${Y.dim}The review job encountered an error.${Y.reset}
|
|
145
|
+
`);break}return X.join(`
|
|
146
|
+
`)}function g(z){return JSON.stringify(z,null,2)}var Sz=`
|
|
136
147
|
\x1B[1mdevin-bugs\x1B[0m \u2014 Extract unresolved bugs from Devin AI code reviews
|
|
137
148
|
|
|
138
149
|
\x1B[1mUsage:\x1B[0m
|
|
139
150
|
devin-bugs <pr> [options]
|
|
140
151
|
|
|
141
152
|
\x1B[1mArguments:\x1B[0m
|
|
142
|
-
pr GitHub PR URL or
|
|
153
|
+
pr GitHub PR URL, shorthand, or just a number (in a git repo)
|
|
143
154
|
Examples: owner/repo#123
|
|
155
|
+
49 (infers repo from git remote)
|
|
144
156
|
https://github.com/owner/repo/pull/123
|
|
145
157
|
https://app.devin.ai/review/owner/repo/pull/123
|
|
146
158
|
|
|
@@ -162,6 +174,6 @@ Expected: owner/repo#123 or https://github.com/owner/repo/pull/123`);let[,X,Z,$]
|
|
|
162
174
|
devin-bugs owner/repo#46 --json
|
|
163
175
|
devin-bugs owner/repo#46 --all --raw
|
|
164
176
|
DEVIN_TOKEN=xxx devin-bugs owner/repo#46
|
|
165
|
-
`;function
|
|
166
|
-
`);try{let B=JSON.parse(Buffer.from(
|
|
167
|
-
`),
|
|
177
|
+
`;function u(){console.log(Sz)}function Cz(){console.log("devin-bugs 0.4.0")}async function Ez(){let z;try{z=Rz({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(G){console.error(`\x1B[31mError:\x1B[0m ${G.message}`),process.exit(1)}let{values:Q,positionals:X}=z;if(Q.help){u();return}if(Q.version){Cz();return}if(Q.logout){let{clearAuth:G}=await Promise.resolve().then(() => (x(),v));G();return}if(Q.login){let G=await U({noCache:Q["no-cache"]});console.error("\x1B[32m\u2713 Authenticated successfully.\x1B[0m"),console.error(` Token cached for future use.
|
|
178
|
+
`);try{let B=JSON.parse(Buffer.from(G.split(".")[1],"base64url").toString()),K=new Date(B.exp*1000);console.error(` Expires: ${K.toLocaleString()}`)}catch{}return}if(X.length===0)console.error(`\x1B[31mError:\x1B[0m Missing PR argument.
|
|
179
|
+
`),u(),process.exit(1);let Z=X[0],J;try{J=A(Z)}catch(G){console.error(`\x1B[31mError:\x1B[0m ${G.message}`),process.exit(1)}let $;try{$=await U({noCache:Q["no-cache"]})}catch(G){console.error(`\x1B[31mAuth error:\x1B[0m ${G.message}`),process.exit(1)}let W;try{W=await q(J.prPath,$)}catch(G){if(G instanceof F){console.error("\x1B[33m\u25B8 Token expired, re-authenticating...\x1B[0m");try{$=await H(),W=await q(J.prPath,$)}catch(B){console.error(`\x1B[31mError:\x1B[0m ${B.message}`),process.exit(1)}}else if(G instanceof O){if(G.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 ${G.status}:\x1B[0m ${G.body}`);process.exit(1)}else throw G}if(Q.raw){console.log(JSON.stringify(W,null,2));return}let M=y(W,{includeAnalysis:Q.all});if(M.length===0)try{let G=await k(J.prPath,$),B=b(G);if(Q.json)console.log(JSON.stringify({bugs:[],status:B},null,2));else console.log(f(B,J));return}catch{}if(Q.json)console.log(g(M));else console.log(m(M,J))}Ez().catch((z)=>{console.error(`\x1B[31mFatal error:\x1B[0m ${z.message??z}`),process.exit(1)});
|