@coderot-ai/cli 1.0.0 → 1.0.1
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/coderot.js +19 -19
- package/package.json +1 -1
package/dist/coderot.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{existsSync as
|
|
3
|
-
`)})}var ct=new Set(["FunctionDeclaration","FunctionExpression","ArrowFunctionExpression"]),Ot=new Set(["MethodDefinition","TSAbstractMethodDefinition","Property","PropertyDefinition"]),lt=new Set(["IfStatement","ConditionalExpression","SwitchStatement","ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement","CatchClause"]),
|
|
4
|
-
`&&r.push(i+1);let p=i=>{let d=0,y=r.length-1;for(;d<y;){let $=d+y+1>>1;r[$]<=i?d=$:y=$-1}return d+1},
|
|
5
|
-
`))m.push(m.at(-1)+(/\S/.test(i)?1:0));let u=[],b=new Set,g=new Set,f=i=>i.type==="ParenthesizedExpression"?f(i.expression):jt(i)?(b.add(i),[...f(i.left),i.operator,...f(i.right)]):[],v=(i,d,y,$,F,w,C=0,L=0)=>{(i.type==="ClassDeclaration"||i.type==="ClassExpression")&&($=i.id?.name??"<anonymous>"),(i.type==="StaticBlock"||(d.type==="PropertyDefinition"||d.type==="AccessorProperty")&&d.value===i)&&([F,w]=[!1,void 0]);let
|
|
6
|
-
`)}),close:()=>(n.stdin.end(),
|
|
7
|
-
${N.prettifyError(o.error)}`);return o.data}async function mt(e,n,s){let o=(await pt(e,s.join("\0"),"check-attr","--stdin","-z",`--source=${n}`,"linguist-generated","linguist-vendored")).split("\0"),
|
|
8
|
-
`)){let[,a="",h=""]=/^ (\S+) ([\d,-]+)$/.exec(
|
|
9
|
-
`)[0]:e).replace(/ \(#\d+\)$/,"").trim(),Bt=(e,n)=>ht(e,n).replace(/^(\[[^\]]*\]\s*)*/,""),Wt=(e,n)=>/^((bug|hot)?fix(es|ed|ing)?|bugs?|revert(s|ed)?)\b/i.test(Bt(e,n)),qt=(e,n)=>/^revert\b/i.test(Bt(e,n))?/This reverts commit ([0-9a-f]{40,64})/.exec(n)?.[1]:void 0;async function Ut(e,n,s={}){if(!n.length)return;let o=[...Object.entries(ft),...Object.entries(s)],
|
|
10
|
-
`).map(p=>({commit:p.slice(0,p.indexOf(" ")),time:Number(p.slice(p.indexOf(" ")+1))})),
|
|
11
|
-
`).includes(
|
|
12
|
-
`))if(m.startsWith("diff --git "))[p,
|
|
13
|
-
`)){let v=/^([0-9a-f]{40,64}) \d+ \d+/.exec(f);v?a.add(g=v[1]):f==="boundary"&&h.add(g)}}return[...a].filter(m=>!h.has(m))}var
|
|
2
|
+
import{existsSync as Rn,mkdirSync as Tn,readFileSync as Nn,rmSync as Ln}from"node:fs";import{join as D}from"node:path";import{parseArgs as In}from"node:util";import{z as t}from"zod";var _=1,me=t.strictObject({name:t.string(),kind:t.string(),start:t.int().positive(),end:t.int().positive(),depth:t.int().nonnegative(),lambda:t.boolean(),cyclomatic:t.int().positive(),cognitive:t.int().nonnegative(),sloc:t.int().nonnegative(),parameters:t.int().nonnegative(),nesting:t.int().nonnegative()}),ue=t.strictObject({kind:t.enum(["line","block","doc"]),start:t.int().positive(),end:t.int().positive()}),fe=t.strictObject({parsed:t.boolean(),functions:t.array(me),unmeasuredFunctions:t.int().nonnegative(),errorLines:t.array(t.int().positive()),inactiveLines:t.int().nonnegative(),sloc:t.int().nonnegative(),comments:t.array(ue)}),de=t.strictObject({path:t.string(),start:t.int().positive(),end:t.int().positive()}),ge=t.strictObject({locations:t.array(de).min(2)}),he=t.strictObject({commit:t.string(),committedAt:t.iso.datetime(),files:t.array(t.strictObject({path:t.string(),blob:t.string(),test:t.boolean()})),skipped:t.strictObject({generated:t.int().nonnegative(),vendored:t.int().nonnegative(),excluded:t.int().nonnegative()}),markup:t.record(t.string(),t.strictObject({files:t.int().nonnegative(),lines:t.int().nonnegative()})),agentFiles:t.array(t.string()),clones:t.array(ge).optional()}),Ct=t.strictObject({reviewers:t.int().nonnegative(),approved:t.boolean(),inline:t.int().nonnegative(),conversation:t.int().nonnegative(),botInline:t.int().nonnegative()}),ye=t.strictObject({commit:t.string(),committedAt:t.iso.datetime(),files:t.array(t.strictObject({path:t.string(),added:t.int().nonnegative(),deleted:t.int().nonnegative(),ai:t.record(t.string(),t.int().positive()).optional()})),ai:t.array(t.strictObject({source:t.enum(["git-ai","trailer","bot","branch"]),agent:t.string()})),authors:t.array(t.string()),agentFiles:t.boolean(),lineLevel:t.boolean(),title:t.string().optional(),fix:t.boolean(),reverts:t.string().optional(),fixed:t.array(t.string()).optional(),pr:t.strictObject({number:t.int().positive(),files:t.int().nonnegative(),added:t.int().nonnegative(),deleted:t.int().nonnegative(),commits:t.int().nonnegative().optional(),review:Ct.optional()}).optional()}),Dn=t.strictObject({schemaVersion:t.literal(_),...he.shape,blobs:t.record(t.string(),fe)}),Et=t.strictObject({schemaVersion:t.literal(_),commits:t.array(ye)}),rt=e=>e.reduce((n,s)=>n+s,0);var st=t.strictObject({commit:t.string(),committedAt:t.iso.datetime(),title:t.string(),pr:t.int().positive().optional(),size:t.int().nonnegative().optional(),review:Ct.optional(),agents:t.array(t.string()),fix:t.boolean(),added:t.int().nonnegative(),deleted:t.int().nonnegative()}),it=t.strictObject({count:t.int().nonnegative(),medianSize:t.int().nonnegative(),large:t.int().nonnegative(),fixes:t.int().nonnegative(),settled:t.int().nonnegative(),fixedLater:t.int().nonnegative(),reviewKnown:t.int().nonnegative(),noHumanReview:t.int().nonnegative(),approvedNoComment:t.int().nonnegative(),growth:t.int(),growthLines:t.int().nonnegative(),growthTrimmed:t.int(),growthLinesTrimmed:t.int().nonnegative()}),$t=t.record(t.enum(["ai","other"]),it),zn=t.strictObject({schemaVersion:t.literal(_),repo:t.string(),snapshots:t.array(t.strictObject({commit:t.string(),committedAt:t.iso.datetime(),sloc:t.int().nonnegative(),testSloc:t.int().nonnegative(),cognitive:t.int().nonnegative(),duplicated:t.int().nonnegative().optional()})),months:t.array(t.strictObject({month:t.string(),commits:t.int().nonnegative(),aiCommits:t.int().nonnegative(),added:t.int().nonnegative(),aiAdded:t.int().nonnegative(),agents:t.record(t.string(),t.int().positive()),attributed:t.int().nonnegative(),prs:$t})),jumps:t.array(t.strictObject({metric:t.enum(["complexity","duplication"]),from:t.int().nonnegative(),to:t.int().nonnegative(),files:t.array(t.strictObject({path:t.string(),delta:t.int()})),commits:t.array(st)})),folders:t.array(t.strictObject({path:t.string(),sloc:t.array(t.int().nonnegative()),cognitive:t.array(t.int().nonnegative()),commits:t.int().nonnegative(),aiCommits:t.int().nonnegative()})),hotspots:t.array(t.strictObject({path:t.string(),commits:t.int().positive(),aiCommits:t.int().nonnegative(),cognitive:t.int().nonnegative()})),prs:$t,prsBy:t.record(t.enum(["large","small","fixed","unfixed","recent","human","unknown"]),it),prsByAgent:t.record(t.string(),it),biggest:t.array(st),worstSince:t.iso.datetime(),worst:t.array(t.strictObject({path:t.string(),before:t.int().nonnegative().optional(),after:t.int().nonnegative(),from:t.strictObject({path:t.string(),how:t.enum(["renamed","copied","moved"])}).optional(),commits:t.array(st)})),lookSince:t.int().nonnegative(),look:t.array(t.strictObject({folder:t.string(),rise:t.int().positive(),commits:t.int().nonnegative(),aiCommits:t.int().nonnegative(),file:t.strictObject({path:t.string(),before:t.int().nonnegative().optional(),after:t.int().nonnegative()}).optional()}))});import{readFile as Ze}from"node:fs/promises";import{join as tn,posix as en}from"node:path";import{parse as nn}from"yaml";import{z as N}from"zod";import{execFile as be,execFileSync as we,spawn as ve}from"node:child_process";import{mkdirSync as xe,mkdtempSync as Se,readFileSync as $e,rmSync as Ce,writeFileSync as Ee}from"node:fs";import{tmpdir as Oe}from"node:os";import{dirname as je,extname as J,join as X}from"node:path";import{createInterface as Pe}from"node:readline";import{fileURLToPath as Pt}from"node:url";import{promisify as Fe}from"node:util";import{parseSync as Ae,visitorKeys as Ft}from"oxc-parser";var At=new Set([".ts",".tsx",".mts",".cts",".js",".jsx",".mjs",".cjs"]),kt=e=>J(e)===".cs"||At.has(J(e)),ke=/(^|\/)(node_modules|bower_components|vendor|third[-_]?party|wwwroot\/lib(_npm)?)\//i,Me=/(^|\/)(obj|dist)\/|\.(designer|generated|g|g\.i)\.cs$|(^|\/)TemporaryGeneratedFile_[^/]*$|\.min\.[cm]?js$/i,Re=/^\uFEFF?(\s|\/\/.*|\/\*[\s\S]*?\*\/|#.*)*/,Te=/<auto-?generated|@generated|(file|content) (is|was) (auto-?|automatically )?generated|^\W*generated (by|from)|^(?=.*do not (edit|modify))(?=.*(generated|copied|from )).*/im,Ne=/^\/\/[#@] sourceMappingURL=|^\/\*! For license information please see \S+ \*\//m,Le=new Set([".razor",".cshtml",".vue",".svelte",".astro"]),Ie=/(^|\/)((\w+[-_])?(tests?|__tests__|e2e)|(tests?|e2e)[-_]\w+)\/|\.(test|spec)[.-][^/]*$|Tests?(\.cs$|\/)/;function Z(e,n,s={}){return s.vendored??ke.test(e)?"vendored":s.generated??Me.test(e)?"generated":Le.has(J(e))?"markup":kt(e)?s.generated===void 0&&(Te.test(n.match(Re)[0])||At.has(J(e))&&Ne.test(n))?"generated":Ie.test(e)?"test":"code":"other"}async function Mt(e,n,s={}){if(!kt(e))throw new Error(`Unsupported file type: ${e}`);let o=J(e)===".cs"?await Be(n,s.csharpSymbols??[]):Ve(e,n),c=o.functions.filter(r=>!o.errorLines.some(p=>r.start<=p&&p<=r.end));return{...o,functions:c,unmeasuredFunctions:o.functions.length-c.length}}function Rt(){G?.stdin.end(),G=void 0}var _e=Pt(import.meta.resolve("jscpd/run-jscpd.js"));async function Tt(e){if(!e.length)return[];let n=Se(X(Oe(),"coderot-dup-"));try{for(let l of e)xe(je(X(n,"src",l.path)),{recursive:!0}),Ee(X(n,"src",l.path),l.source);await Fe(be)(process.execPath,[_e,"src","-f","csharp,typescript,tsx,javascript,jsx","--cross-formats","typescript,tsx;javascript,jsx","-k","50","-l","5","--mode","weak","--ignore-pattern","using\\s+(static\\s+)?[\\w.]+(\\s*=\\s*[\\w.<>]+)?;,\\[assembly:[^\\]]*\\]","--no-gitignore","-r","json","-o","out","-s"],{cwd:n});let{duplicates:s}=JSON.parse($e(X(n,"out","jscpd-report.json"),"utf8")),o=l=>({path:l.name.replaceAll("\\","/"),start:l.start,end:l.end}),c=l=>`${l.path}:${l.start}-${l.end}`,r=new Map;for(let l of s){let[a,h]=[o(l.firstFile),o(l.secondFile)],[m,u]=[r.get(c(a))??[a],r.get(c(h))??[h]];if(m===u)continue;let b=[...m,...u];for(let g of b)r.set(c(g),b)}let p=(l,a)=>l.path<a.path?-1:l.path>a.path?1:l.start-a.start;return[...new Set(r.values())].map(l=>({locations:l.sort(p)})).sort((l,a)=>p(l.locations[0],a.locations[0]))}finally{Ce(n,{recursive:!0,force:!0})}}var De=Pt(new URL("./roslyn/bin/Release/net8.0/Coderot.Roslyn.dll",import.meta.url)),K=new Map,G,ze=0,at;function He(){if(at??=[...Ge().matchAll(/^Microsoft\.NETCore\.App (\S+)/gm)].map(s=>s[1]),!at.some(s=>Number(s.split(".")[0])>=8))throw new Error(`C# files need the .NET runtime 8 or newer (found: ${at.join(", ")||"none"}). In GitHub Actions: actions/setup-dotnet`);let e=s=>{G===n&&(G=void 0);for(let o of K.values())o.reject(s);K.clear()},n=ve("dotnet",[De],{stdio:["pipe","pipe","inherit"]});return n.on("error",e),n.on("exit",s=>e(new Error(`C# sidecar exited with code ${s}`))),Pe({input:n.stdout}).on("line",s=>{let{id:o,...c}=JSON.parse(s);K.get(o)?.resolve(c),K.delete(o)}),n}function Ge(){try{return we("dotnet",["--list-runtimes"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]})}catch{return""}}function Be(e,n){G??=He();let s=ze++;return new Promise((o,c)=>{K.set(s,{resolve:o,reject:c}),G.stdin.write(JSON.stringify({id:s,source:e,symbols:n})+`
|
|
3
|
+
`)})}var ct=new Set(["FunctionDeclaration","FunctionExpression","ArrowFunctionExpression"]),Ot=new Set(["MethodDefinition","TSAbstractMethodDefinition","Property","PropertyDefinition"]),lt=new Set(["IfStatement","ConditionalExpression","SwitchStatement","ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement","CatchClause"]),We=new Set(["IfStatement","ConditionalExpression","ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement","CatchClause","LogicalExpression"]),qe=new Set(["IfStatement","SwitchStatement","TryStatement","ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement","WithStatement"]),Ue=e=>We.has(e.type)||e.type==="SwitchCase"&&e.test||e.type==="AssignmentExpression"&&["&&=","||=","??="].includes(e.operator),Ke=(e,n,s)=>e.type==="IfStatement"?n==="consequent"||n==="alternate"&&s?.type!=="IfStatement":e.type==="ConditionalExpression"?n!=="test":e.type==="SwitchStatement"?n==="cases":lt.has(e.type)&&n==="body",jt=e=>e.type==="LogicalExpression"&&e.operator!=="??",Je=(e,n)=>n?e.type==="MemberExpression"&&e.object.type==="ThisExpression"&&!e.computed?(e.property.type==="PrivateIdentifier"?"#":"")+e.property.name:void 0:e.type==="Identifier"?e.name:void 0;function Nt(e){return lt.has(e.type)?!0:ct.has(e.type)?!1:(Ft[e.type]??[]).some(n=>[e[n]].flat().some(s=>s&&Nt(s)))}function Ve(e,n){let{program:s,errors:o,comments:c}=Ae(e,n,/\.[mc]?js$/.test(e)?{lang:"jsx"}:{}),r=[0];for(let i=0;i<n.length;i++)n[i]===`
|
|
4
|
+
`&&r.push(i+1);let p=i=>{let d=0,y=r.length-1;for(;d<y;){let $=d+y+1>>1;r[$]<=i?d=$:y=$-1}return d+1},l=i=>n.slice(i.start,i.end),a=[],h=0;for(let i of c)a.push(n.slice(h,i.start),n.slice(i.start,i.end).replace(/[^\n]/g,"")),h=i.end;let m=[0];for(let i of[...a,n.slice(h)].join("").split(`
|
|
5
|
+
`))m.push(m.at(-1)+(/\S/.test(i)?1:0));let u=[],b=new Set,g=new Set,f=i=>i.type==="ParenthesizedExpression"?f(i.expression):jt(i)?(b.add(i),[...f(i.left),i.operator,...f(i.right)]):[],v=(i,d,y,$,F,w,C=0,L=0)=>{(i.type==="ClassDeclaration"||i.type==="ClassExpression")&&($=i.id?.name??"<anonymous>"),(i.type==="StaticBlock"||(d.type==="PropertyDefinition"||d.type==="AccessorProperty")&&d.value===i)&&([F,w]=[!1,void 0]);let R=!1;if(ct.has(i.type)&&i.body){let j=Ot.has(d.type)?d:null,k=j?.kind==="get"||j?.kind==="set",T=i.type==="ArrowFunctionExpression"||i.type==="FunctionExpression"&&!k&&!j?.method&&j?.type!=="MethodDefinition";if(!(T&&F)){let x=j?.kind==="constructor"?$:k?`${l(j.key)}.${j.kind}`:i.id?.name??(j&&l(j.key))??(d.type==="VariableDeclarator"?d.id.name??l(d.id):d.type==="AssignmentExpression"?l(d.left):"<anonymous>"),O=j??i,[E,M]=[p(O.start),p(O.end)],H=i.params.filter(U=>U.type!=="Identifier"||U.name!=="this").length;w={name:x,kind:j?.type??i.type,start:E,end:M,depth:y,lambda:T,cyclomatic:1,cognitive:0,sloc:m[M]-m[E-1],parameters:H,nesting:0},u.push(w),R=!0,F=Nt(i.body)}}let q=i.type==="IfStatement"&&d.type==="IfStatement"&&d.alternate===i,z=R?0:L+(qe.has(i.type)&&!q?1:0);w&&(w.nesting=Math.max(w.nesting,z),Ue(i)&&w.cyclomatic++,i.type==="IfStatement"?w.cognitive+=(q?1:1+C)+(i.alternate&&i.alternate.type!=="IfStatement"?1:0):lt.has(i.type)?w.cognitive+=1+C:(i.type==="BreakStatement"||i.type==="ContinueStatement")&&i.label?w.cognitive++:jt(i)&&!b.has(i)?w.cognitive+=f(i).filter((j,k,T)=>j!==T[k-1]).length:i.type==="CallExpression"&&Je(i.callee,Ot.has(w.kind))===w.name&&!g.has(w)&&(g.add(w),w.cognitive++));let Y=R?0:ct.has(i.type)?C+1:C;for(let j of Ft[i.type]??[]){let k=i[j],T=Y+(Ke(i,j,k)?1:0);if(Array.isArray(k))for(let x of k)x&&v(x,i,y+(R?1:0),$,F,w,T,z);else k&&v(k,i,y+(R?1:0),$,F,w,T,z)}};v(s,s,0,"",!1);let S=!(o.length&&!s.body.length);return{parsed:S,functions:u,errorLines:o.map(i=>p(i.labels[0]?.start??0)),inactiveLines:0,sloc:S?m.at(-1):0,comments:S?c.map(i=>({kind:i.type==="Line"?"line":/^\*[^*/]/.test(i.value)?"doc":"block",start:p(i.start),end:p(i.end)})):[]}}import{execFile as Qe,spawn as Ye}from"node:child_process";import{promisify as Xe}from"node:util";var P=(e,...n)=>pt(e,"",...n);async function pt(e,n,...s){let o=Xe(Qe)("git",s,{cwd:e,encoding:"utf8",maxBuffer:1073741824});return o.child.stdin.on("error",()=>{}).end(n),(await o).stdout}function Lt(e){let n=Ye("git",["cat-file","--batch"],{cwd:e,stdio:["pipe","pipe","inherit"]}),s=[],o=p=>s.splice(0).forEach(l=>l.reject(p));n.on("error",o),n.on("exit",p=>o(new Error(`git cat-file exited with code ${p}`)));let c=new Promise(p=>n.on("close",p)),r=Buffer.alloc(0);return n.stdout.on("data",p=>{r=Buffer.concat([r,p]);for(let l;(l=r.indexOf(10))>=0;){let a=r.subarray(0,l).toString();if(a.endsWith(" missing")){s.shift()?.reject(new Error(`git object ${a}`)),r=r.subarray(l+1);continue}let h=Number(a.split(" ")[2]);if(r.length<l+h+2)return;s.shift()?.resolve(r.subarray(l+1,l+1+h)),r=r.subarray(l+h+2)}}),{read:p=>new Promise((l,a)=>{s.push({resolve:l,reject:a}),n.stdin.write(p+`
|
|
6
|
+
`)}),close:()=>(n.stdin.end(),c)}}var V=N.array(N.string()).default([]),tt=N.strictObject({include:V,exclude:V,tests:V,ai:N.record(N.string(),N.strictObject({emails:V,branches:V})).default({}),since:N.string().optional(),interval:N.string().optional()});async function It(e){let n=tn((await P(e,"rev-parse","--show-toplevel")).trim(),".coderot.yml"),s=await Ze(n,"utf8").catch(c=>c.code==="ENOENT"?"":Promise.reject(c)),o=tt.safeParse(nn(s)??{});if(!o.success)throw new Error(`${n}:
|
|
7
|
+
${N.prettifyError(o.error)}`);return o.data}async function mt(e,n,s){let o=(await pt(e,s.join("\0"),"check-attr","--stdin","-z",`--source=${n}`,"linguist-generated","linguist-vendored")).split("\0"),c=new Map;for(let r=0;r+2<o.length;r+=3){if(o[r+2]==="unspecified")continue;let p=c.get(o[r])??{};p[o[r+1]==="linguist-generated"?"generated":"vendored"]=!["unset","false"].includes(o[r+2]),c.set(o[r],p)}return c}function et(e,n,s,o){let c=Z(n,s,o),r=p=>p.some(l=>en.matchesGlob(n,l));return c==="generated"||c==="vendored"?c:r(e.exclude)||e.include.length&&!r(e.include)?"excluded":c==="code"&&r(e.tests)?"test":c}var ut="author { __typename login }",Dt=`reviews(first: 50, after: $after) { pageInfo { hasNextPage endCursor } nodes { ${ut} state comments { totalCount } } }`,on=`commits { totalCount } ${ut} ${Dt} comments(first: 100) { nodes { ${ut} } }`,_t=e=>!e||e.__typename==="Bot"||/(bot|\[bot\])$/i.test(e.login);function zt(e,n){let s=new Map(n.flatMap(o=>o.pr?.review?[[o.pr.number,o.pr]]:[]));for(let o of e){let c=o.pr&&s.get(o.pr.number);c&&Object.assign(o.pr,{review:c.review,commits:o.pr.commits??c.commits})}}async function Ht(e,n,s,o="https://api.github.com/graphql"){let[c,r]=n.split("/"),p=async(u,b=null,g=0)=>{let f=await fetch(o,{method:"POST",headers:{authorization:`bearer ${s}`,"content-type":"application/json"},body:JSON.stringify({query:`query($owner: String!, $name: String!, $after: String) { repository(owner: $owner, name: $name) { ${u} } }`,variables:{owner:c,name:r,after:b}})});if((f.status===403||f.status===429)&&g<4&&/rate limit/i.test(await f.text()))return await new Promise(i=>setTimeout(i,Number(f.headers.get("retry-after")??60*2**g)*1e3)),p(u,b,g+1);if(!f.ok)throw new Error(`HTTP ${f.status}`);let v=await f.json(),S=v.errors?.find(i=>i.type!=="NOT_FOUND")?.message??(v.data?.repository?void 0:`no repository ${n}`);if(S)throw new Error(S);return v.data.repository},l=async u=>{let b;try{b=await p(u.map((g,f)=>`p${f}: pullRequest(number: ${g.pr.number}) { ${on} }`).join(" "))}catch(g){if(u.length===1||!/^HTTP 50[24]$/.test(g.message))throw g;let f=Math.ceil(u.length/2);return await l(u.slice(0,f)),l(u.slice(f))}for(let[g,f]of u.entries()){let v=b[`p${g}`];if(!v)continue;let S=v.reviews.nodes;for(let y=v.reviews.pageInfo;y.hasNextPage;){let $=(await p(`p0: pullRequest(number: ${f.pr.number}) { ${Dt} }`,y.endCursor)).p0.reviews;S.push(...$.nodes),y=$.pageInfo}let i=y=>!_t(y)&&y.login!==v.author?.login,d=S.filter(y=>i(y.author));f.pr.commits??=v.commits.totalCount,f.pr.review={reviewers:new Set(d.map(y=>y.author.login)).size,approved:d.some(y=>y.state==="APPROVED"),inline:rt(d.map(y=>y.comments.totalCount)),conversation:v.comments.nodes.filter(y=>i(y.author)).length,botInline:rt(S.filter(y=>_t(y.author)).map(y=>y.comments.totalCount))}}},a=e.filter(u=>u.pr&&!u.pr.review),h=[];for(let u=0;u<a.length;u+=50)h.push(a.slice(u,u+50));let m;await Promise.all(Array.from({length:4},async()=>{for(let u;!m&&(u=h.shift());)await l(u).catch(b=>{m??=b.message})})),m&&console.warn(`Commits per squash merge and review attention left out for ${a.filter(u=>!u.pr.review).length} of ${a.length} PRs (${m}). In GitHub Actions, give the workflow \`pull-requests: read\``)}import{createHash as yn}from"node:crypto";import{closeSync as bn,existsSync as wn,openSync as vn,readdirSync as xn,writeSync as B}from"node:fs";import{availableParallelism as Sn}from"node:os";import{extname as wt,join as $n}from"node:path";import{createHash as rn}from"node:crypto";var ft={"claude-code":{emails:["noreply@anthropic.com","claude[bot]@users.noreply.github.com"],branches:["claude/"],tools:["claude","claude-web"]},"copilot-cli":{emails:["223556219+copilot@users.noreply.github.com"],branches:[],tools:["github-copilot-cli"]},"copilot-coding-agent":{emails:["198982749+copilot@users.noreply.github.com"],branches:["copilot/"],tools:[]},"copilot-review":{emails:["175728472+copilot@users.noreply.github.com"],branches:[],tools:[]},copilot:{emails:["copilot@users.noreply.github.com","copilot@github.com"],branches:[],tools:["github-copilot","copilot"]},cursor:{emails:["cursoragent@cursor.com","cursor[bot]@users.noreply.github.com"],branches:["cursor/"],tools:["cursor","cursor-agent"]},codex:{emails:["noreply@openai.com","codex@openai.com","chatgpt-codex-connector[bot]@users.noreply.github.com"],branches:["codex/"],tools:["codex","codex-cloud"]},gemini:{emails:["noreply@google.com","gemini-code-assist[bot]@users.noreply.github.com"],branches:[],tools:["gemini"]},aider:{emails:["aider@aider.chat","noreply@aider.chat"],branches:[],tools:[]},openhands:{emails:["openhands@all-hands.dev"],branches:[],tools:[]},devin:{emails:["devin-ai-integration[bot]@users.noreply.github.com"],branches:["devin/"],tools:["devin"]},charlie:{emails:["charlie@charlielabs.ai","charliecreates[bot]@users.noreply.github.com"],branches:[],tools:[]},grok:{emails:["noreply@x.ai"],branches:[],tools:[]},"muse-spark":{emails:["noreply@meta.com"],branches:[],tools:[]},jules:{emails:["google-labs-jules[bot]@users.noreply.github.com"],branches:[],tools:[]},"amazon-q":{emails:["amazon-q-developer[bot]@users.noreply.github.com"],branches:[],tools:[]},"factory-droid":{emails:["factory-droid[bot]@users.noreply.github.com","droid@factory.ai"],branches:[],tools:["droid"]}};var dt=(e,n)=>e.get(n.toLowerCase())??e.get(n.toLowerCase().replace(/^\d+\+/,"")),an=(e,n)=>e.toLowerCase()==="copilot@github.com"&&n>=Date.UTC(2026,3,15)/1e3&&n<Date.UTC(2026,4,20)/1e3,cn=[["AGENTS.md",/(^|\/)AGENTS?\.md$/],["Claude Code",/(^|\/)CLAUDE(\.local)?\.md$|^\.claude\//],["GitHub Copilot",/^\.github\/(copilot-instructions\.md$|instructions\/|prompts\/|chatmodes\/|agents\/|workflows\/copilot-setup-steps\.ya?ml$)/],["Gemini",/(^|\/)GEMINI\.md$|^\.gemini\//],["Cursor",/^\.cursorrules$|^\.cursor\/rules\//],["Windsurf",/^\.windsurfrules$|^\.windsurf\/rules\//],["Cline",/^\.clinerules(\/|$)/],["Roo Code",/^\.roorules$|^\.roo\/rules/],["Kiro",/^\.kiro\/steering\//],["Junie",/^\.junie\/guidelines\.md$/],["Aider",/^\.aider\.conf\.ya?ml$/],["Continue",/^\.continue\/rules\//],["Amazon Q",/^\.amazonq\/rules\//],["Augment",/^\.augment-guidelines$|^\.augment\/rules\//],["Warp",/(^|\/)WARP\.md$/],["MCP servers",/^\.mcp\.json$/]],gt=e=>cn.some(([,n])=>n.test(e)),ln=e=>rn("sha256").update(e.toLowerCase().replace(/^\d+\+/,"")).digest("hex"),pn=new Map(Object.entries(ft).flatMap(([e,n])=>n.tools.map(s=>[s,e])));function mn(e){let[n="",s=""]=e.split(/^---$/m),o;try{o=JSON.parse(s)}catch{return}if(!String(o?.schema_version).startsWith("authorship/3."))return;let c={...o.prompts,...o.sessions},r={},p="";for(let l of n.split(`
|
|
8
|
+
`)){let[,a="",h=""]=/^ (\S+) ([\d,-]+)$/.exec(l)??[];if(!a){l&&l[0]!==" "&&(p=l.replace(/^"(.*)"$/,"$1"));continue}let m=c[a.split("::")[0]]?.agent_id?.tool;if(typeof m!="string")continue;let u=pn.get(m)??m,b=r[p]??={};for(let g of h.split(",")){let[f=0,v=f]=g.split("-").map(Number);b[u]=(b[u]??0)+v-f+1}}return r}function un(e){return/^(?:Merge pull request #\d+ from [^\s/]+\/(\S+)|Merged in (\S+) \(pull request|Merge branch '([^']+)'|Merge remote-tracking branch '[^/']+\/([^']+)')/.exec(e)?.slice(1).find(Boolean)}function Gt(e){let n=/^Merge pull request #(\d+) from |\(#(\d+)\)$/.exec(e);return n?Number(n.slice(1).find(Boolean)):void 0}var ht=(e,n)=>(/^Merge pull request #\d+ from /.test(e)?n.split(`
|
|
9
|
+
`)[0]:e).replace(/ \(#\d+\)$/,"").trim(),Bt=(e,n)=>ht(e,n).replace(/^(\[[^\]]*\]\s*)*/,""),Wt=(e,n)=>/^((bug|hot)?fix(es|ed|ing)?|bugs?|revert(s|ed)?)\b/i.test(Bt(e,n)),qt=(e,n)=>/^revert\b/i.test(Bt(e,n))?/This reverts commit ([0-9a-f]{40,64})/.exec(n)?.[1]:void 0;async function Ut(e,n,s={}){if(!n.length)return;let o=[...Object.entries(ft),...Object.entries(s)],c=new Map(o.flatMap(([a,h])=>h.emails.map(m=>[m.toLowerCase(),a])));await P(e,"rev-parse","-q","--verify","refs/notes/ai").catch(()=>"")||await P(e,"fetch","-q","origin","refs/notes/ai:refs/notes/ai").catch(()=>{});let r=(await P(e,"log","-1","--format=%P",n.at(-1).commit)).trim().split(" ")[0],p=new Map;for(let a of(await P(e,"log","--notes=ai","--format=%x01%H %P%x02%at%x02%ae%x02%ce%x02%N%x02%B","HEAD",...r?[`^${r}`]:[])).split("").slice(1)){let[h="",m="",u="",b="",g="",f=""]=a.split(""),[v="",...S]=h.trim().split(" "),i=g.trim()?mn(g):void 0;g.trim()&&!i&&console.warn(`The Git AI note on ${v} isn't authorship/3.x, so it's left out`);let d=[];for(let w of[u,b]){let C=dt(c,w);C&&d.push({source:"bot",agent:C})}let y=[...f.matchAll(/^co-authored-by:.*<([^>]+)>/gim)].map(([,w=""])=>w);for(let w of y){let C=dt(c,w);C&&!an(w,Number(m))&&d.push({source:"trailer",agent:C})}let $=[u,...y].filter(w=>w&&!dt(c,w)&&!w.includes("[bot]")).map(ln),F=un(f);for(let[w,C]of o)F&&C.branches.some(L=>F.startsWith(L))&&d.push({source:"branch",agent:w});p.set(v,{parents:S,signals:d,people:$,lines:i})}let l=new Set(n.map(a=>a.commit));for(let a of n.toReversed()){let{parents:h,signals:m,people:u}=p.get(a.commit),b=[...m],g=new Set(u),f=[],v=!0,S=d=>{d.lines?f.push(d.lines):d.parents.length<2&&(v=!1)};S(p.get(a.commit));let i=0;for(let d=h.slice(1),y;y=d.pop();)l.has(y)||!p.has(y)||(l.add(y),i++,b.push(...p.get(y).signals),p.get(y).people.forEach($=>g.add($)),S(p.get(y)),d.push(...p.get(y).parents));a.lineLevel=v&&f.length>0;for(let d of a.files){let y={};for(let w of f)for(let[C,L]of Object.entries(w[d.path]??{}))y[C]=(y[C]??0)+L;let $=Math.min(1,d.added/Object.values(y).reduce((w,C)=>w+C,0)),F=Object.entries(y).map(([w,C])=>[w,Math.floor(C*$)]).filter(([,w])=>w>0);F.length&&(d.ai=Object.fromEntries(F)),b.push(...F.map(([w])=>({source:"git-ai",agent:w})))}a.ai=b.filter((d,y)=>b.findIndex($=>$.source===d.source&&$.agent===d.agent)===y),a.authors=[...g].sort(),a.pr&&h.length>1&&(a.pr.commits=i)}}import{posix as fn}from"node:path";var Kt=[[/^net(\d{2,3})$/,"NETFRAMEWORK","NET",["20","35","40","45","451","452","46","461","462","47","471","472","48","481"]],[/^netstandard(\d\.\d)$/,"NETSTANDARD","NETSTANDARD",["1.0","1.1","1.2","1.3","1.4","1.5","1.6","2.0","2.1"]],[/^netcoreapp(\d\.\d)$/,"NETCOREAPP","NETCOREAPP",["1.0","1.1","2.0","2.1","2.2","3.0","3.1"]]],Jt=(e,n)=>n.map(s=>`${e}${s.replace(".","_")}_OR_GREATER`);function dn(e){let n=e.trim().toLowerCase(),s=/^net(\d+)\.(\d+)(?:-([a-z]+))?/.exec(n);if(s&&Number(s[1])>=5){let o=Number(s[1]);return{rank:1e3+o,symbols:["NET",`NET${o}_${s[2]}`,"NETCOREAPP",...Array.from({length:o-4},(c,r)=>`NET${r+5}_0_OR_GREATER`),...Jt("NETCOREAPP",Kt[2][3]),...s[3]?[s[3].toUpperCase()]:[]]}}for(let[o,[c,r,p,l]]of Kt.entries()){let a=l.indexOf(c.exec(n)?.[1]??"");if(a>=0)return{rank:o*100+a,symbols:[r,p+l[a].replace(".","_"),...Jt(p,l.slice(0,a+1))]}}}function gn(e){e=e.replace(/<!--[\s\S]*?-->/g,"");let n=o=>/\bCondition\s*=\s*"([^"]*)"/.exec(o)?.[1],s=[];for(let[,o,c]of e.matchAll(/<PropertyGroup\b([^>]*?)(?<!\/)>([\s\S]*?)<\/PropertyGroup>/g))for(let[,r,p,l]of c.matchAll(/<(\w+)\b([^>]*?)>([^<]*)<\/\1>/g))s.push({name:r.toLowerCase(),value:l.trim(),conditions:[n(o),n(p)].filter(a=>a!==void 0)});return{assignments:s,imports:[...e.matchAll(/<Import\b[^>]*?\bProject\s*=\s*"([^"]*)"/g)].map(o=>o[1])}}var yt=new Map,bt=(e,n,s,o=0)=>e.replace(/\$\((\w+)\)/g,(c,r)=>n.get(r.toLowerCase())??(o<8?bt(s.get(r.toLowerCase())??"",n,s,o+1):"")),hn=e=>e.split(/\s+or\s+/i).some(n=>n.split(/\s+and\s+/i).every(s=>{let o=/^[\s(]*'([^']*)'\s*(==|!=)\s*'([^']*)'[\s)]*$/.exec(s);return!!o&&o[1].trim().toLowerCase()===o[3].trim().toLowerCase()==(o[2]==="==")}));function Vt(e,n,s){let o=new Map(n);for(let{assignments:c}of e)for(let r of c)!n.has(r.name)&&r.conditions.every(p=>hn(bt(p,o,s)))&&o.set(r.name,bt(r.value,o,s));return o}var I=e=>e.slice(0,Math.max(e.lastIndexOf("/"),0)),Qt=(e,n)=>e?`${e}/${n}`:n;function*Yt(e){for(;;){if(yield e,!e)return;e=I(e)}}var Xt=new Map([["configuration","Release"],["platform","AnyCPU"]]);async function Zt(e,n){let s=new Map;await Promise.all([...e].filter(([a])=>/\.(csproj|props|targets)$/.test(a)).map(async([a,h])=>{yt.has(h)||yt.set(h,gn(await n(h))),s.set(a,yt.get(h))}));let o=new Map,c=new Map;for(let a of e.keys())if(a.endsWith(".csproj")&&!c.has(I(a))&&c.set(I(a),a),!!a.endsWith(".props"))for(let h of s.get(a).assignments)!h.conditions.length&&!o.has(h.name)&&o.set(h.name,h.value);let r=(a,h)=>[...Yt(a)].map(m=>Qt(m,h)).find(m=>s.has(m)),p=(a,h)=>{if(!a||h.has(a)||!s.has(a))return[];h.add(a);let m=I(a);return[...s.get(a).imports.flatMap(u=>{let b=/Directory\.Build\.(props|targets)/i.exec(u);if(b&&u.includes("$("))return m?p(r(I(m),b[0]),h):[];let g=u.replace(/\$\(MSBuildThisFileDirectory\)/gi,"").replaceAll("\\","/");return g.includes("$(")?[]:p(fn.normalize(Qt(m,g)),h)}),s.get(a)]},l=new Map;return a=>{let h=[...Yt(I(a))].map(d=>c.get(d)).find(d=>d),m=I(h||a);if(l.has(m))return l.get(m);let u=new Set,b=[...p(r(m,"Directory.Build.props"),u),...p(h,u),...p(r(m,"Directory.Build.targets"),u)],g=Vt(b,Xt,o),[f]=(g.get("targetframeworks")||g.get("targetframework")||"").split(";").map(d=>({t:d.trim(),f:dn(d)})).filter(d=>d.f).sort((d,y)=>y.f.rank-d.f.rank),S=((f?Vt(b,new Map([...Xt,["targetframework",f.t]]),o):g).get("defineconstants")??"").split(/[;,]/).map(d=>d.trim()).filter(d=>/^\w+$/.test(d)),i=[...new Set(["RELEASE","TRACE",...f?.f.symbols??[],...S])].sort();return l.set(m,i),i}}var Cn={d:86400,w:7*86400,m:365/12*86400,y:365*86400};function W(e){let n=/^(\d+)([dwmy])$/.exec(e);if(!n)throw new Error(`Expected a duration like 90d, 1w, 12m or 1y, got "${e}"`);return Number(n[1])*Cn[n[2]]}async function En(e,n,s){let o=(await P(e,"log","--first-parent","--format=%H %ct","HEAD")).trim().split(`
|
|
10
|
+
`).map(p=>({commit:p.slice(0,p.indexOf(" ")),time:Number(p.slice(p.indexOf(" ")+1))})),c=o[0].time,r=new Map;for(let p=c;p>=c-n;p-=s){let l=o.find(a=>a.time<=p);l&&r.set(l.commit,l)}return[...r.values()]}var On=/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|packages\.lock\.json)$/;async function jn(e,n,s,o=tt.parse({}),c=0,r=!1){let p=/^0{40}$|^0{64}$/.test(s??""),l;s&&!p&&(l=(await P(e,"rev-parse","--verify",`${s}^{commit}`).catch(()=>"")).trim(),(await P(e,"log","--first-parent","--format=%H","HEAD")).trim().split(`
|
|
11
|
+
`).includes(l)||(console.warn(`--since-commit ${s} isn't on HEAD's first-parent history (a force push?), so churn covers the time window`),l=void 0));let a=[],h=[],m=(await P(e,"log","--first-parent","--diff-merges=first-parent","--numstat","-z",...l?[`^${l}`]:p?[]:[`--since=${n}`],"--format=%x01%H %ct %B","HEAD")).split("\0");for(let g=0;g<m.length;g++){let f=/^\x01(\w+) (\d+) ([^\n]*?)\s*(?:\n|$)(.*)$/s.exec(m[g]);if(f){let y=Gt(f[3]);a.push({commit:f[1],committedAt:new Date(Number(f[2])*1e3).toISOString(),files:[],ai:[],lineLevel:!1,authors:[],agentFiles:!1,fix:Wt(f[3],f[4]),reverts:qt(f[3],f[4]),...r&&{title:ht(f[3],f[4])}}),y&&(a.at(-1).pr={number:y,files:0,added:0,deleted:0});continue}let[,v,S,i]=/^\n?(\d+|-)\t(\d+|-)\t(.*)$/s.exec(m[g])??[];if(!v||!S)continue;let d=i||m[g+=2];gt(d)&&(a.at(-1).agentFiles=!0),!(v==="-"||Number(v)+Number(S)===0)&&h.push({c:a.at(-1),path:d,added:Number(v),deleted:Number(S)})}let u=h.length?await mt(e,"HEAD",[...new Set(h.map(g=>g.path))]):new Map;for(let{c:g,path:f,added:v,deleted:S}of h){let i=et(o,f,"",u.get(f));(i==="code"||i==="test")&&g.files.push({path:f,added:v,deleted:S}),g.pr&&i!=="generated"&&i!=="vendored"&&i!=="excluded"&&!On.test(f)&&(g.pr.files++,g.pr.added+=v,g.pr.deleted+=S)}let b=a.filter(g=>g.fix&&Date.parse(g.committedAt)/1e3>=c);return await Promise.all(Array.from({length:Sn()},async()=>{for(let g;g=b.pop();)g.fixed=await Pn(e,g,new Set(g.files.filter(f=>et(o,f.path,"",u.get(f.path))==="code").map(f=>f.path)))})),a}async function Pn(e,n,s){let o=new Map,c="",r="",p=!1;if(s.size){for(let m of(await P(e,"-c","core.quotePath=false","diff-tree","-p","-U0","-M","--no-prefix","--diff-merges=first-parent","--root",n.commit)).split(`
|
|
12
|
+
`))if(m.startsWith("diff --git "))[p,c,r]=[!0,"",""];else if(p&&m.startsWith("--- "))c=m.slice(4).replace(/\t$/,"");else if(p&&m.startsWith("+++ "))r=m.slice(4).replace(/\t$/,"");else if(m.startsWith("@@ ")){p=!1;let[,u,b="1"]=/^@@ -(\d+)(?:,(\d+))? /.exec(m)??[];c!=="/dev/null"&&s.has(r==="/dev/null"?c:r)&&b!=="0"&&(o.get(c)??o.set(c,[]).get(c)).push(`${u},+${b}`)}}let l=Date.parse(n.committedAt)/1e3-30*86400,a=new Set,h=new Set;for(let[m,u]of o)for(let b=0;b<u.length;b+=1e3){let g="";for(let f of(await P(e,"blame","--first-parent","--porcelain",`--since=${l}`,...u.slice(b,b+1e3).flatMap(v=>["-L",v]),`${n.commit}^`,"--",m)).split(`
|
|
13
|
+
`)){let v=/^([0-9a-f]{40,64}) \d+ \d+/.exec(f);v?a.add(g=v[1]):f==="boundary"&&h.add(g)}}return[...a].filter(m=>!h.has(m))}var Fn=e=>e.split(`
|
|
14
14
|
`).length-(e.endsWith(`
|
|
15
|
-
`)?1:0);function vt(e,n){let s=
|
|
15
|
+
`)?1:0);function vt(e,n){let s=vn(e,"w");try{let o=(r,p)=>JSON.stringify(r,null,2).replaceAll(`
|
|
16
16
|
`,`
|
|
17
|
-
`+" ".repeat(p)),
|
|
18
|
-
`),
|
|
17
|
+
`+" ".repeat(p)),c=Object.keys(n).filter(r=>n[r]!==void 0);B(s,`{
|
|
18
|
+
`),c.forEach((r,p)=>{let l=n[r],a=p<c.length-1?`,
|
|
19
19
|
`:`
|
|
20
|
-
`,h=typeof
|
|
20
|
+
`,h=typeof l!="object"||l===null?[]:Array.isArray(l)?l.map(m=>["",m]):Object.entries(l).map(([m,u])=>[`${JSON.stringify(m)}: `,u]);if(!h.length)return B(s,` ${JSON.stringify(r)}: ${o(l,1)}${a}`);B(s,` ${JSON.stringify(r)}: ${Array.isArray(l)?"[":"{"}
|
|
21
21
|
`),h.forEach(([m,u],b)=>B(s,` ${m}${o(u,2)}${b<h.length-1?",":""}
|
|
22
|
-
`)),B(s,` ${Array.isArray(
|
|
23
|
-
`)}finally{
|
|
24
|
-
coderot ci [--since 12m] [--interval 1w] [--out report] [--titles]`);var nt=ot?await
|
|
22
|
+
`)),B(s,` ${Array.isArray(l)?"]":"}"}${a}`)}),B(s,`}
|
|
23
|
+
`)}finally{bn(s)}}function te(e){let n=(s,o)=>!wn(s)||xn(s).every(c=>c.startsWith(".")||o.test(c));return n(e,/^(snapshots|commits\.json)$/)&&n($n(e,"snapshots"),/^\d{4}-\d\d-\d\d-[0-9a-f]{7}\.json$/)}async function ee(e,n,s,o,c,r=tt.parse({}),p=!1){if(c==="")throw new Error("--since-commit requires a commit SHA");if((await P(e,"rev-parse","--is-shallow-repository")).trim()==="true")throw new Error("This is a shallow clone, so its history is missing. Fetch all of it: in GitHub Actions, set `fetch-depth: 0` on actions/checkout");let l=new Map,a=new Map,h=Lt(e),m=async u=>(await h.read(u)).toString("utf8");try{let u=await En(e,c?0:n,s),b=-1;for(let{commit:v,time:S}of u){let i=new Map,d=[];for(let x of(await P(e,"ls-tree","-r","-z","--full-tree",v)).split("\0")){let[,O,E,M]=/^(\d+) blob (\w+)\t(.*)$/s.exec(x)??[];M&>(M)&&d.push(M),E&&M&&O!=="120000"&&i.set(M,E)}let y=[...i].filter(([x])=>/(^|\/)\.gitattributes$/.test(x)),$=(await Promise.all(y.map(([,x])=>m(x)))).some(x=>x.includes("linguist-"))?await mt(e,v,[...i.keys()]):new Map,F=await Zt(i,m),w=[],C=new Map,L=[],R={generated:0,vendored:0,excluded:0};for(let[x,O]of i){let E=et(r,x,"",$.get(x));if((E==="generated"||E==="vendored"||E==="excluded")&&R[E]++,E==="markup"&&(a.has(O)||a.set(O,m(O).then(Fn)),L.push({ext:wt(x),lines:a.get(O)})),E!=="code"&&E!=="test")continue;let M=wt(x)===".cs"?F(x):void 0,H=O+wt(x)+(M?":"+yn("sha256").update(M.join(";")).digest("hex").slice(0,8):"");!l.has(H)&&!C.has(H)&&C.set(H,m(O).then(U=>Z(x,U,$.get(x))==="generated"?void 0:Mt(x,U,{csharpSymbols:M}))),w.push({path:x,blob:H,test:E==="test",sha:O})}let q={};for(let[x,O]of C){let E=await O;E&&(q[x]=E),l.set(x,!!E)}let z=[],Y=[];for(let{sha:x,...O}of w){if(!l.get(O.blob)){R.generated++;continue}z.push(O),Y.push({path:O.path,sha:x})}let j={};for(let{ext:x,lines:O}of L){let E=j[x]??={files:0,lines:0};E.files++,E.lines+=await O}let k=Math.floor((u[0].time-S)/W("1m")),T=k===b?void 0:await Tt(await Promise.all(Y.map(async x=>({path:x.path,source:await m(x.sha)}))));b=k,await o({schemaVersion:_,commit:v,committedAt:new Date(S*1e3).toISOString(),files:z,skipped:R,markup:j,agentFiles:d,clones:T,blobs:q})}let g=u.at(-1).time,f=await jn(e,g-W("90d"),c,r,c?0:g-W("31d"),p);return await Ut(e,f,r.ai),f}finally{Rt(),await h.close()}}import{readFileSync as An}from"node:fs";import{gzipSync as kn}from"node:zlib";var ne=(e=process.env)=>e.CODEROT_API??"https://api.coderot.ai";async function Mn(e=process.env){if(e.CODEROT_TOKEN)return e.CODEROT_TOKEN;if(!e.ACTIONS_ID_TOKEN_REQUEST_URL)throw new Error("No credentials for coderot: in GitHub Actions, add `id-token: write` to the workflow's permissions; in other CIs, set CODEROT_TOKEN to the repo's upload token");let n=await fetch(`${e.ACTIONS_ID_TOKEN_REQUEST_URL}&audience=coderot`,{headers:{authorization:`bearer ${e.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`}});if(!n.ok)throw new Error(`GitHub's OIDC token: HTTP ${n.status} ${await n.text()}`);return(await n.json()).value}async function oe(e,n={},s=5e3){for(let o=1;;o++){let c=await Mn().then(r=>fetch(e,{...n,headers:{...n.headers,authorization:`Bearer ${r}`}})).catch(r=>r);if(o===3||c instanceof Response&&c.status<500){if(c instanceof Error)throw c;if(!c.ok)throw new Error(`${e}: HTTP ${c.status} ${await c.text()}`);return c}await new Promise(r=>setTimeout(r,s*o))}}async function se(e=ne(),n){return(await(await oe(`${e}/v1/reports/head`,{},n)).json()).commit}async function ie(e,n=ne(),s){for(let o of e){let c=kn(An(o)),r={"content-encoding":"gzip","content-type":"application/json"};await oe(`${n}/v1/reports`,{method:"POST",body:c,headers:r},s),console.log(`sent ${o} (${Math.round(c.length/1024)} kB)`)}}var{positionals:ae,values:A}=In({allowPositionals:!0,options:{since:{type:"string"},interval:{type:"string"},"since-commit":{type:"string"},out:{type:"string",default:"report"},titles:{type:"boolean",default:!1}}}),ot=ae[0]==="ci";if(ae[0]!=="analyze"&&!ot)throw new Error(`Usage: coderot analyze [--since 12m] [--interval 1w] [--since-commit <sha>] [--out report] [--titles]
|
|
24
|
+
coderot ci [--since 12m] [--interval 1w] [--out report] [--titles]`);var nt=ot?await se()??void 0:A["since-commit"];ot&&console.log(nt?`coderot has commits up to ${nt}: analyzing the ones after it`:"First run: backfilling");var xt=await It(process.cwd());if(!te(A.out))throw new Error(`${A.out} holds files coderot didn't write: pick another folder with --out`);Ln(D(A.out,"snapshots"),{recursive:!0,force:!0});Tn(D(A.out,"snapshots"),{recursive:!0});var St=[],ce=0,le=0,pe=0,Q=await ee(process.cwd(),W(A.since??xt.since??"12m"),W(A.interval??xt.interval??"1w"),e=>{St.push(D(A.out,"snapshots",`${e.committedAt.slice(0,10)}-${e.commit.slice(0,7)}.json`)),vt(St.at(-1),e),ce++||(le=e.files.length),pe+=Object.keys(e.blobs).length},nt,xt,A.titles),re=Et.safeParse(JSON.parse(Rn(D(A.out,"commits.json"))?Nn(D(A.out,"commits.json"),"utf8"):"{}"));re.success&&zt(Q,re.data.commits);process.env.GITHUB_TOKEN&&process.env.GITHUB_REPOSITORY&&await Ht(Q,process.env.GITHUB_REPOSITORY,process.env.GITHUB_TOKEN);vt(D(A.out,"commits.json"),{schemaVersion:_,commits:Q});console.log(`${ce} snapshots, ${le} files at HEAD, ${pe} file versions analyzed, ${Q.filter(e=>e.ai.length).length} of ${Q.length} commits with AI signals${nt?" (incremental)":""} \u2192 ${A.out}`);ot&&await ie([...St,D(A.out,"commits.json")]);
|