@oss-scout/core 0.7.0 → 0.7.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.
@@ -89,7 +89,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
89
89
  `);let u={},l=new o.Events(u);return u.on("secondary-limit",c.onSecondaryRateLimit),u.on("rate-limit",c.onRateLimit),u.on("error",p=>t.log.warn("Error in throttling-plugin limit handler",p)),c.retryLimiter.on("failed",async function(p,m){let[f,k,P]=m.args,{pathname:L}=new URL(P.url,"http://github.test");if(!(L.startsWith("/graphql")&&p.status!==401||p.status===403||p.status===429))return;let H=~~k.retryCount;k.retryCount=H,P.request.retryCount=H;let{wantRetry:R,retryAfter:N=0}=await(async function(){if(/\bsecondary rate\b/i.test(p.message)){let G=Number(p.response.headers["retry-after"])||f.fallbackSecondaryRateRetryAfter;return{wantRetry:await l.trigger("secondary-limit",G,P,t,H),retryAfter:G}}if(p.response.headers!=null&&p.response.headers["x-ratelimit-remaining"]==="0"||(p.response.data?.errors??[]).some(G=>G.type==="RATE_LIMITED")){let G=new Date(~~p.response.headers["x-ratelimit-reset"]*1e3).getTime(),re=Math.max(Math.ceil((G-Date.now())/1e3)+1,0);return{wantRetry:await l.trigger("rate-limit",re,P,t,H),retryAfter:re}}return{}})();if(R)return k.retryCount++,N*f.retryAfterBaseValue}),t.hook.wrap("request",Qk.bind(null,c)),{}}var g_,Yk,cp,rS,m_,f_,Qt,oS,h_=y(()=>{g_=Pe(p_(),1),Yk="0.0.0-development",cp=()=>Promise.resolve();rS=["/orgs/{org}/invitations","/orgs/{org}/invitations/{invitation_id}","/orgs/{org}/teams/{team_slug}/discussions","/orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","/repos/{owner}/{repo}/collaborators/{username}","/repos/{owner}/{repo}/commits/{commit_sha}/comments","/repos/{owner}/{repo}/issues","/repos/{owner}/{repo}/issues/{issue_number}/comments","/repos/{owner}/{repo}/issues/{issue_number}/sub_issue","/repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority","/repos/{owner}/{repo}/pulls","/repos/{owner}/{repo}/pulls/{pull_number}/comments","/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies","/repos/{owner}/{repo}/pulls/{pull_number}/merge","/repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers","/repos/{owner}/{repo}/pulls/{pull_number}/reviews","/repos/{owner}/{repo}/releases","/teams/{team_id}/discussions","/teams/{team_id}/discussions/{discussion_number}/comments"];m_=nS(rS),f_=m_.test.bind(m_),Qt={},oS=function(t,e){Qt.global=new t.Group({id:"octokit-global",maxConcurrent:10,...e}),Qt.auth=new t.Group({id:"octokit-auth",maxConcurrent:1,...e}),Qt.search=new t.Group({id:"octokit-search",maxConcurrent:1,minTime:2e3,...e}),Qt.write=new t.Group({id:"octokit-write",maxConcurrent:1,minTime:1e3,...e}),Qt.notifications=new t.Group({id:"octokit-notifications",maxConcurrent:1,minTime:3e3,...e})};Mi.VERSION=Yk;Mi.triggersNotification=f_});function Bi(t){return t.toLocaleTimeString("en-US",{hour12:!1})}function sS(){return{onRateLimit:(t,e,n,o)=>{let r=e,i=new Date(Date.now()+t*1e3);return o<2?(z(Hi,`Rate limit hit (retry ${o+1}/2, waiting ${t}s, resets at ${Bi(i)}) \u2014 ${r.method} ${r.url}`),!0):(z(Hi,`Rate limit exceeded, not retrying \u2014 ${r.method} ${r.url} (resets at ${Bi(i)})`),!1)},onSecondaryRateLimit:(t,e,n,o)=>{let r=e,i=new Date(Date.now()+t*1e3);return o<3?(z(Hi,`Secondary rate limit hit (retry ${o+1}/3, waiting ${t}s, resets at ${Bi(i)}) \u2014 ${r.method} ${r.url}`),!0):(z(Hi,`Secondary rate limit exceeded, not retrying \u2014 ${r.method} ${r.url} (resets at ${Bi(i)})`),!1)}}}function er(t){if(qi&&v_===t)return qi;let e=sS();return qi=new iS({auth:t,throttle:e}),v_=t,qi}async function Wi(t){let e=er(t),{data:n}=await e.rateLimit.get(),o=n.resources.search;return{remaining:o.remaining,limit:o.limit,resetAt:new Date(o.reset*1e3).toISOString()}}var Hi,iS,qi,v_,Ji=y(()=>{"use strict";d_();h_();he();Hi="github",iS=l_.plugin(Mi),qi=null,v_=null});var __={};ce(__,{bootstrapScout:()=>cS});async function cS(t,e){let n=t.getPreferences().githubUsername;if(!n)throw new Gt("GitHub username not configured. Run `oss-scout setup` first.");let o=await Wi(e);if(j(st,`Rate limit: ${o.remaining}/${o.limit}, resets at ${o.resetAt}`),o.remaining<15)return j(st,"Insufficient rate limit, skipping bootstrap"),{starredRepoCount:0,mergedPRCount:0,closedPRCount:0,openPRCount:0,reposScoredCount:0,skippedDueToRateLimit:!0,errors:[]};let r=er(e),i=[],s=[];try{let m=0;for await(let f of r.paginate.iterator(r.activity.listReposStarredByAuthenticatedUser,{per_page:tr,headers:{accept:"application/vnd.github.v3+json"}})){for(let k of f.data){let P=k;s.push(P.full_name)}if(m++,m>=aS)break}j(st,`Fetched ${s.length} starred repos`),t.setStarredRepos(s)}catch(m){z(st,`Failed to fetch starred repos: ${U(m)}`),i.push("starred repos fetch failed")}let a=0;try{for(let m=1;m<=up;m++){let{data:f}=await r.search.issuesAndPullRequests({q:`is:pr is:merged author:${n}`,per_page:tr,page:m});for(let k of f.items){let P=le(k.html_url);P&&(t.recordMergedPR({url:k.html_url,title:k.title,mergedAt:k.closed_at??new Date().toISOString(),repo:P}),a++)}if(f.items.length<tr)break}j(st,`Imported ${a} merged PRs`)}catch(m){if(ne(m)===401||ae(m))throw m;z(st,`Failed to fetch merged PRs: ${U(m)}`),i.push("merged PR fetch failed")}let c=0;try{for(let m=1;m<=up;m++){let{data:f}=await r.search.issuesAndPullRequests({q:`is:pr is:closed is:unmerged author:${n}`,per_page:tr,page:m});for(let k of f.items){let P=le(k.html_url);P&&(t.recordClosedPR({url:k.html_url,title:k.title,closedAt:k.closed_at??new Date().toISOString(),repo:P}),c++)}if(f.items.length<tr)break}j(st,`Imported ${c} closed PRs`)}catch(m){if(ne(m)===401||ae(m))throw m;z(st,`Failed to fetch closed PRs: ${U(m)}`),i.push("closed PR fetch failed")}let u=0;try{for(let m=1;m<=up;m++){let{data:f}=await r.search.issuesAndPullRequests({q:`is:pr is:open author:${n}`,per_page:tr,page:m});for(let k of f.items){let P=le(k.html_url);P&&(t.recordOpenPR({url:k.html_url,title:k.title,openedAt:k.created_at??new Date().toISOString(),repo:P}),u++)}if(f.items.length<tr)break}j(st,`Imported ${u} open PRs`)}catch(m){if(ne(m)===401||ae(m))throw m;z(st,`Failed to fetch open PRs: ${U(m)}`),i.push("open PR fetch failed")}let l=t.getState(),p=Object.keys(l.repoScores).length;return{starredRepoCount:s.length,mergedPRCount:a,closedPRCount:c,openPRCount:u,reposScoredCount:p,skippedDueToRateLimit:!1,errors:i}}var st,aS,up,tr,b_=y(()=>{"use strict";Ji();he();we();ue();st="bootstrap",aS=5,up=3,tr=100});function Ur(){return lp||(lp=new dp),lp}var y_,k_,$_,uS,w_,dp,lp,Ki=y(()=>{"use strict";he();ue();y_="search-budget",k_=30,$_=60*1e3,uS=4,w_=k_-uS,dp=class{callTimestamps=[];knownRemaining=k_;resetAt=0;totalCalls=0;init(e,n){this.knownRemaining=e,this.resetAt=new Date(n).getTime(),this.callTimestamps=[],this.totalCalls=0,j(y_,`Initialized: ${e} remaining, resets at ${new Date(this.resetAt).toLocaleTimeString()}`)}recordCall(){this.callTimestamps.push(Date.now()),this.totalCalls++,this.pruneOldTimestamps()}pruneOldTimestamps(){let e=Date.now()-$_;for(;this.callTimestamps.length>0&&this.callTimestamps[0]<e;)this.callTimestamps.shift()}getCallsInWindow(){return this.pruneOldTimestamps(),this.callTimestamps.length}getEffectiveBudget(){let e=w_-this.callTimestamps.length,n=this.knownRemaining-this.totalCalls;return Math.max(0,Math.min(e,n))}canAfford(e){return this.pruneOldTimestamps(),this.getEffectiveBudget()>=e}async waitForBudget(){for(;;){if(this.pruneOldTimestamps(),this.getEffectiveBudget()>0)return;let e=this.callTimestamps[0];if(!e)return;let o=e+$_-Date.now();o>0&&(j(y_,`Budget full (${this.callTimestamps.length}/${w_} in window), waiting ${o}ms`),await Ve(o+100))}}getTotalCalls(){return this.totalCalls}},lp=null});var Gn,pp=y(()=>{"use strict";Gn={beginner:["good first issue","help wanted","easy","up-for-grabs","first-timers-only","beginner"],intermediate:["enhancement","feature","feature-request","contributions welcome"],advanced:["proposal","RFC","accepted","design"]}});function S_(t){if(!t.labels||!Array.isArray(t.labels)||t.labels.length===0)return!1;let n=t.labels.map(o=>(typeof o=="string"?o:o.name||"").toLowerCase()).filter(o=>o.length>0);return n.length===0?!1:n.every(o=>lS.has(o))}function pS(t){return!t.labels||!Array.isArray(t.labels)?!1:t.labels.map(o=>(typeof o=="string"?o:o.name||"").toLowerCase()).filter(o=>dS.has(o)).length>=5}function mS(t){return t?/^.+\s+(question|fact|point|item|task|entry|post|challenge|exercise|example|problem|tip|recipe|snippet)\s+#?\d+$/i.test(t):!1}function T_(t){let e=new Set,n=new Map;for(let o of t){let r=le(o.repository_url);if(r){if(pS(o)){e.add(r);continue}o.title&&mS(o.title)&&n.set(r,(n.get(r)||0)+1)}}for(let[o,r]of n)r>=3&&e.add(o);return e}function E_(t,e){let n=new Map,o=[];for(let r of t){let i=n.get(r.issue.repo)||0;i<e&&(o.push(r),n.set(r.issue.repo,i+1))}return o}var lS,dS,mp=y(()=>{"use strict";ue();lS=new Set(["documentation","docs","typo","spelling"]);dS=new Set(["good first issue","hacktoberfest","easy","up-for-grabs","first-timers-only","beginner-friendly","beginner","starter","newbie","low-hanging-fruit","community"])});function x_(t,e){let n=0;return t>=5e3?n+=8:t>=500?n+=5:t>=50&&(n+=3),e>=500?n+=4:e>=50&&(n+=2),n}function P_(t){let e=50;t.repoScore!==null&&(e+=t.repoScore*2),e+=t.repoQualityBonus??0,t.mergedPRCount>0&&(e+=15),t.clearRequirements&&(e+=15);let n=new Date(t.issueUpdatedAt),o=jt(n);return o<=14?e+=15:o<=30&&(e+=Math.round(15*(1-(o-14)/16))),t.hasContributionGuidelines&&(e+=10),t.orgHasMergedPRs&&(e+=5),t.matchesPreferredCategory&&(e+=5),t.hasExistingPR&&(e-=30),t.isClaimed&&(e-=20),t.closedWithoutMergeCount>0&&t.mergedPRCount===0&&(e-=15),Math.max(0,Math.min(100,e))}var I_=y(()=>{"use strict";ue()});function O_(t,e){if(e.length===0)return!1;let n=t.split("/")[0]?.toLowerCase();if(!n)return!1;for(let o of e){let r=fS[o];if(r&&r.some(i=>i.toLowerCase()===n))return!0}return!1}function R_(t){let e=new Set;for(let n of t){let o=gS[n];if(o)for(let r of o)e.add(r)}return[...e]}var gS,fS,gp=y(()=>{"use strict";gS={nonprofit:["nonprofit","social-good","humanitarian","charity","social-impact","civic-tech"],devtools:["developer-tools","devtools","cli","sdk","linter","formatter","build-tool"],infrastructure:["infrastructure","cloud","kubernetes","docker","devops","monitoring","observability"],"web-frameworks":["web-framework","frontend","backend","fullstack","nextjs","react","vue"],"data-ml":["machine-learning","data-science","deep-learning","nlp","data-pipeline","analytics"],education:["education","learning","tutorial","courseware","edtech","teaching"]},fS={nonprofit:["code-for-america","opengovfoundation","ushahidi","hotosm","openfn","democracyearth"],devtools:["eslint","prettier","vitejs","biomejs","oxc-project","ast-grep","turbot"],infrastructure:["kubernetes","hashicorp","grafana","prometheus","open-telemetry","envoyproxy","cncf"],"web-frameworks":["vercel","remix-run","sveltejs","nuxt","astro","redwoodjs","blitz-js"],"data-ml":["huggingface","mlflow","apache","dbt-labs","dagster-io","prefecthq","langchain-ai"],education:["freeCodeCamp","TheOdinProject","exercism","codecademy","oppia","Khan"]}});async function A_(t,e=100,n=10){let o=[];for(let r=1;r<=n;r++){let{data:i}=await t(r);if(o.push(...i),i.length<e)break}return o}var z_=y(()=>{"use strict"});function at(){return fp||(fp=new hp),fp}async function D_(t,e,n){let o=t.getInflight(e);if(o)return j(ye,`Dedup hit for ${e}`),await o;let i=(async()=>{let a={},c=t.get(e);c&&(a["if-none-match"]=c.etag);try{let u=await n(a),l=u.headers?.etag;return l&&t.set(e,l,u.data),u.data}catch(u){if(vS(u)){let l=t.get(e);if(l)return j(ye,`304 cache hit for ${e}`),l.body}throw u}})(),s=t.setInflight(e,i);try{return await i}finally{s()}}async function G_(t,e,n,o){let r=t.getIfFresh(e,n);if(r)return j(ye,`Time-based cache hit for ${e}`),r;let i=await o();return t.set(e,"",i),i}function vS(t){return ne(t)===304}var ze,Xi,C_,ye,hS,hp,fp,jr=y(()=>{"use strict";ze=Pe(require("fs"),1),Xi=Pe(require("path"),1),C_=Pe(require("crypto"),1);ue();he();we();ye="http-cache",hS=1440*60*1e3,hp=class{cacheDir;inflightRequests=new Map;constructor(e){this.cacheDir=e??Is()}keyFor(e){return C_.createHash("sha256").update(e).digest("hex")}pathFor(e){return Xi.join(this.cacheDir,`${this.keyFor(e)}.json`)}getIfFresh(e,n){let o=this.get(e);if(!o)return null;let r=Date.now()-new Date(o.cachedAt).getTime();return!Number.isFinite(r)||r<0||r>n?null:o.body}get(e){let n=this.pathFor(e);try{let o=ze.readFileSync(n,"utf-8"),r=JSON.parse(o);return r.url!==e?(j(ye,`Cache collision detected for ${e}, ignoring`),null):r}catch(o){if(o?.code==="ENOENT")return null;if(o instanceof SyntaxError){j(ye,`Corrupt cache entry, deleting: ${e}`);try{ze.unlinkSync(n)}catch(i){j(ye,`Failed to delete corrupt cache entry: ${U(i)}`)}return null}return z(ye,`Cache read failed for ${e}: ${U(o)}`),null}}set(e,n,o){let r={etag:n,url:e,body:o,cachedAt:new Date().toISOString()};try{ze.writeFileSync(this.pathFor(e),JSON.stringify(r),{encoding:"utf-8",mode:384}),j(ye,`Cached response for ${e}`)}catch(i){z(ye,`Failed to write cache for ${e}: ${U(i)}`)}}getInflight(e){return this.inflightRequests.get(e)}setInflight(e,n){return this.inflightRequests.set(e,n),()=>{this.inflightRequests.delete(e)}}evictStale(e=hS){let n=0;try{let o=ze.readdirSync(this.cacheDir),r=Date.now();for(let i of o){if(!i.endsWith(".json"))continue;let s=Xi.join(this.cacheDir,i);try{let a=ze.readFileSync(s,"utf-8"),c=JSON.parse(a);r-new Date(c.cachedAt).getTime()>e&&(ze.unlinkSync(s),n++)}catch{j(ye,`Removing unreadable cache entry ${i}`);try{ze.unlinkSync(s),n++}catch(a){j(ye,`Failed to remove stale cache entry ${i}: ${U(a)}`)}}}}catch(o){o?.code!=="ENOENT"&&z(ye,`Failed to evict stale cache entries: ${U(o)}`)}return n>0&&j(ye,`Evicted ${n} stale cache entries`),n}clear(){try{let e=ze.readdirSync(this.cacheDir);for(let n of e)n.endsWith(".json")&&ze.unlinkSync(Xi.join(this.cacheDir,n));j(ye,"Cache cleared")}catch(e){e?.code!=="ENOENT"&&z(ye,`Failed to clear cache: ${U(e)}`)}}size(){try{return ze.readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).length}catch(e){return e?.code!=="ENOENT"&&j(ye,`Failed to read cache size: ${U(e)}`),0}}},fp=null});function _S(t){return t.event==="cross-referenced"&&!!t.source?.issue?.pull_request}function bS(t,e){let n=t.source?.issue,o=`${e.owner}/${e.repo}#${e.issueNumber}`;if(!n||typeof n.number!="number")return z(Fr,`Cross-referenced timeline event for ${o} missing source.issue.number \u2014 possible API shape drift`),null;let r=n.user?.login;if(!r)return z(Fr,`Cross-referenced PR #${n.number} for ${o} has no user.login (deleted user?) \u2014 skipping linkedPR metadata`),null;let i=n.html_url;return i?{number:n.number,author:r,state:n.state==="closed"?"closed":"open",merged:!!n.pull_request?.merged_at,url:i}:(z(Fr,`Cross-referenced PR #${n.number} for ${o} missing html_url \u2014 skipping linkedPR metadata`),null)}async function U_(t,e,n,o){try{let r=await A_(a=>t.issues.listEventsForTimeline({owner:e,repo:n,issue_number:o,per_page:100,page:a})),i=0,s=null;for(let a of r){let c=a;_S(c)&&(i++,s??=bS(c,{owner:e,repo:n,issueNumber:o}))}return{passed:i===0,linkedPR:s}}catch(r){if(ne(r)===401||ae(r))throw r;let i=U(r);return z(Fr,`Failed to check for existing PRs on ${e}/${n}#${o}: ${i}. Assuming no existing PR.`),{passed:!0,inconclusive:!0,reason:i,linkedPR:null}}}async function j_(t,e,n){let o=at(),r=`merged-prs:${e}/${n}`,i=o.getIfFresh(r,$S);if(i!=null&&typeof i=="number")return i;try{let s=Ur();await s.waitForBudget();try{let{data:a}=await t.search.issuesAndPullRequests({q:`repo:${e}/${n} is:pr is:merged author:@me`,per_page:1});return o.set(r,"",a.total_count),a.total_count}finally{s.recordCall()}}catch(s){if(ne(s)===401||ae(s))throw s;let a=U(s);return z(Fr,`Could not check merged PRs in ${e}/${n}: ${a}. Defaulting to 0.`),0}}async function F_(t,e,n,o,r){if(r===0)return{passed:!0};try{let s=(await t.paginate(t.issues.listComments,{owner:e,repo:n,issue_number:o,per_page:100},a=>a.data)).slice(-100);for(let a of s){let c=(a.body||"").toLowerCase();if(yS.some(u=>c.includes(u)))return{passed:!1}}return{passed:!0}}catch(i){if(ne(i)===401||ae(i))throw i;let s=U(i);return z(Fr,`Failed to check claim status on ${e}/${n}#${o}: ${s}. Assuming not claimed.`),{passed:!0,inconclusive:!0,reason:s}}}function L_(t){if(!t||t.length<50)return!1;let e=/\d\.|[-*]\s/.test(t),n=/```/.test(t),o=/expect|should|must|want/i.test(t);return[e,n,o,t.length>200].filter(Boolean).length>=2}var Fr,yS,$S,N_=y(()=>{"use strict";z_();we();he();jr();Ki();Fr="issue-eligibility",yS=["i'm working on this","i am working on this","i'll take this","i will take this","working on it","i'd like to work on","i would like to work on","can i work on","may i work on","assigned to me","i'm on it","i'll submit a pr","i will submit a pr","working on a fix","working on a pr"];$S=900*1e3});function V_(){let t=Date.now();for(let[e,n]of ft.entries())t-n.fetchedAt>H_&&ft.delete(e);if(ft.size>Z_){let n=Array.from(ft.entries()).sort((o,r)=>o[1].fetchedAt-r[1].fetchedAt).slice(0,ft.size-Z_);for(let[o]of n)ft.delete(o)}}async function q_(t,e,n){let o=at(),r=`health:${e}/${n}`;try{return await G_(o,r,wS,async()=>{let i=`/repos/${e}/${n}`,s=await D_(o,i,m=>t.repos.get({owner:e,repo:n,headers:m})),{data:a}=await t.repos.listCommits({owner:e,repo:n,per_page:1}),u=a[0]?.commit?.author?.date||s.pushed_at,l=jt(new Date(u));return{repo:`${e}/${n}`,lastCommitAt:u,daysSinceLastCommit:l,openIssuesCount:s.open_issues_count,avgIssueResponseDays:0,ciStatus:"unknown",isActive:l<30,stargazersCount:s.stargazers_count,forksCount:s.forks_count,language:s.language}})}catch(i){let s=U(i);return z(M_,`Error checking project health for ${e}/${n}: ${s}`),{repo:`${e}/${n}`,lastCommitAt:"",daysSinceLastCommit:999,openIssuesCount:0,avgIssueResponseDays:0,ciStatus:"unknown",isActive:!1,checkFailed:!0,failureReason:s}}}async function B_(t,e,n){let o=`${e}/${n}`,r=ft.get(o);if(r&&Date.now()-r.fetchedAt<H_)return r.guidelines;let i=["CONTRIBUTING.md",".github/CONTRIBUTING.md","docs/CONTRIBUTING.md","contributing.md"],s=await Promise.allSettled(i.map(a=>t.repos.getContent({owner:e,repo:n,path:a}).then(({data:c})=>"content"in c?Buffer.from(c.content,"base64").toString("utf-8"):null)));for(let a of s)if(a.status==="rejected"&&(ne(a.reason)===401||ae(a.reason)))throw a.reason;for(let a=0;a<s.length;a++){let c=s[a];if(c.status==="fulfilled"&&c.value){let u=kS(c.value);return ft.set(o,{guidelines:u,fetchedAt:Date.now()}),V_(),u}c.status==="rejected"&&ne(c.reason)!==404&&z(M_,`Unexpected error fetching ${i[a]} from ${e}/${n}: ${U(c.reason)}`)}ft.set(o,{guidelines:void 0,fetchedAt:Date.now()}),V_()}function kS(t){let e={rawContent:t},n=t.toLowerCase();if(n.includes("branch")){let o=t.match(/branch[^\n]*(?:named?|format|convention)[^\n]*[`"]([^`"]+)[`"]/i);o&&(e.branchNamingConvention=o[1])}if(n.includes("conventional commit"))e.commitMessageFormat="conventional commits";else if(n.includes("commit message")){let o=t.match(/commit message[^\n]*[`"]([^`"]+)[`"]/i);o&&(e.commitMessageFormat=o[1])}return n.includes("jest")?e.testFramework="Jest":n.includes("rspec")?e.testFramework="RSpec":n.includes("pytest")?e.testFramework="pytest":n.includes("mocha")&&(e.testFramework="Mocha"),n.includes("eslint")?e.linter="ESLint":n.includes("rubocop")?e.linter="RuboCop":n.includes("prettier")&&(e.formatter="Prettier"),(n.includes("cla")||n.includes("contributor license agreement"))&&(e.claRequired=!0),e}var M_,ft,H_,wS,Z_,W_=y(()=>{"use strict";ue();we();he();jr();M_="repo-health",ft=new Map,H_=3600*1e3,wS=14400*1e3,Z_=100});function xS(t){if(!t)return{matched:!1,matchedKeywords:[]};let e=t.toLowerCase(),n=ES.filter(o=>e.includes(o));return{matched:n.length>0,matchedKeywords:n}}async function IS(t,e,n,o){try{let{data:r}=await t.repos.getContent({owner:e,repo:n,path:o});return"content"in r&&typeof r.content=="string"?{text:Buffer.from(r.content,"base64").toString("utf-8"),transient:!1}:{text:null,transient:!1}}catch(r){let i=ne(r);if(i===404)return{text:null,transient:!1};if(i===401||ae(r))throw r;return z(SS,`Unexpected error fetching ${o} from ${e}/${n}: ${U(r)}`),{text:null,transient:!0}}}async function OS(t,e,n,o){let r=await Promise.allSettled(o.map(s=>IS(t,e,n,s))),i=!1;for(let s of r)if(s.status==="fulfilled"){if(s.value.transient&&(i=!0),s.value.text)return{text:s.value.text,hadTransientFailure:i}}else{if(ae(s.reason)||ne(s.reason)===401)throw s.reason;i=!0}return{text:null,hadTransientFailure:i}}function RS(t){if(!t||typeof t!="object")return!1;let e=t;return!(typeof e.matched!="boolean"||!Array.isArray(e.matchedKeywords)||e.sourceFile!==null&&typeof e.sourceFile!="string")}async function J_(t,e,n,o){let r=at(),i=`anti-llm-policy:${e}/${n}`,s=r.getIfFresh(i,TS);if(RS(s))return s;let a=!1;for(let u of PS){let l,p=!1;if(u.canonical==="CONTRIBUTING.md"&&o?.contributingText!==void 0?l=o.contributingText:{text:l,hadTransientFailure:p}=await OS(t,e,n,u.paths),p&&(a=!0),!l)continue;let{matched:m,matchedKeywords:f}=xS(l);if(m){let k={matched:!0,matchedKeywords:f,sourceFile:u.canonical};return r.set(i,"",k),k}}let c={matched:!1,matchedKeywords:[],sourceFile:null};return a||r.set(i,"",c),c}var SS,TS,ES,PS,K_=y(()=>{"use strict";we();he();jr();SS="anti-llm-policy",TS=3600*1e3,ES=["no ai-generated","no ai generated","no ai-assisted","no ai assisted","no llm-generated","no llm generated","no copilot-generated","no chatgpt-generated","human-authored only","human authored only","human-written only","human written only","ai-free contributions","llm-free contributions","ai-generated code is not allowed","ai-generated code will not be accepted","do not submit ai-generated","do not submit llm-generated","do not use ai to","do not use llms","do not use copilot","do not use chatgpt","without ai assistance","without llm assistance","no use of generative ai","ban on ai-generated","prohibit ai-generated","prohibits ai-generated"];PS=[{canonical:"CONTRIBUTING.md",paths:["CONTRIBUTING.md",".github/CONTRIBUTING.md","docs/CONTRIBUTING.md","contributing.md"]},{canonical:"CODE_OF_CONDUCT.md",paths:["CODE_OF_CONDUCT.md",".github/CODE_OF_CONDUCT.md","docs/CODE_OF_CONDUCT.md","code_of_conduct.md"]},{canonical:"README.md",paths:["README.md","readme.md","Readme.md"]}]});function CS(t){let{issue:e,linkedPRExists:n}=t,o=(e.body??"").slice(0,2e3);return["You triage open-source issues for an autonomous contribution agent.","Classify the issue into exactly one bucket:","- pursue: small, concrete bug or feature with clear acceptance; safe for an autonomous agent to attempt without further design input","- investigate: tractable but needs human reading first (ambiguous scope, design questions, recently-touched files)","- skip: not actionable autonomously (epic, creative, blocked, requires upstream change, requires infra)","","Return JSON only matching the provided schema. Reasons must be short phrases, not sentences. 1-3 reasons total.","","Issue:",`Title: ${e.title}`,`Body: ${o}`,`Labels: ${e.labels.join(", ")}`,`Linked PR exists: ${n}`].join(`
90
90
  `)}async function X_(t,e){if(!e.model)return null;let n=e.host??AS,o=e.timeoutMs??15e3,r=e.fetchImpl??fetch,i;try{i=await r(`${n}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:e.model,messages:[{role:"user",content:CS(t)}],stream:!1,format:zS,options:{temperature:.1}}),signal:AbortSignal.timeout(o)})}catch{return null}if(!i.ok)return null;let s;try{s=await i.json()}catch{return null}let a=s?.message?.content;if(typeof a!="string")return null;let c;try{c=JSON.parse(a)}catch{return null}return DS(c)?{decision:c.decision,confidence:c.confidence,reasons:c.reasons,modelVersion:e.model}:null}function Y_(t){return{issue:{title:t.issue.title,labels:t.issue.labels,body:t.issue.body},linkedPRExists:!!t.linkedPR}}function DS(t){if(typeof t!="object"||t===null)return!1;let e=t;return!(e.decision!=="pursue"&&e.decision!=="investigate"&&e.decision!=="skip"||e.confidence!=="high"&&e.confidence!=="medium"&&e.confidence!=="low"||!Array.isArray(e.reasons)||e.reasons.length===0||e.reasons.length>3||!e.reasons.every(n=>typeof n=="string"))}var AS,zS,Q_=y(()=>{"use strict";AS="http://127.0.0.1:11434",zS={type:"object",properties:{decision:{type:"string",enum:["pursue","investigate","skip"]},confidence:{type:"string",enum:["high","medium","low"]},reasons:{type:"array",items:{type:"string"},minItems:1,maxItems:3}},required:["decision","confidence","reasons"]}});var vp,GS,US,Yi,eb=y(()=>{"use strict";ue();we();he();I_();gp();N_();W_();K_();jr();Q_();vp="issue-vetting",GS=3,US=900*1e3,Yi=class{octokit;stateReader;constructor(e,n){this.octokit=e,this.stateReader=n}async vetIssue(e){let n=at(),o=`vet:${e}`,r=n.getIfFresh(o,US);if(r&&typeof r=="object"&&"issue"in r&&"viabilityScore"in r)return j(vp,`Vetting cache hit for ${e}`),r;let i=Os(e);if(!i||i.type!=="issues")throw new ve(`Invalid issue URL: ${e}`);let{owner:s,repo:a,number:c}=i,u=`${s}/${a}`,{data:l}=await this.octokit.issues.get({owner:s,repo:a,issue_number:c}),p=this.stateReader.getReposWithMergedPRs(),m=p.includes(u),[f,k,P,L,q]=await Promise.all([U_(this.octokit,s,a,c),F_(this.octokit,s,a,c,l.comments),q_(this.octokit,s,a),B_(this.octokit,s,a),m?Promise.resolve(0):j_(this.octokit,s,a)]),H=await J_(this.octokit,s,a,{contributingText:L?.rawContent}),R=f.passed,N=k.passed,G=L_(l.body||""),re=P.checkFailed?!0:P.isActive,ee={passedAllChecks:R&&N&&re&&G,checks:{noExistingPR:R,notClaimed:N,projectActive:re,clearRequirements:G,contributionGuidelinesFound:!!L},contributionGuidelines:L,linkedPR:f.linkedPR,notes:[]};R||ee.notes.push("Existing PR found for this issue"),N||ee.notes.push("Issue appears to be claimed by someone"),f.inconclusive&&ee.notes.push(`Could not verify absence of existing PRs: ${f.reason||"API error"}`),k.inconclusive&&ee.notes.push(`Could not verify claim status: ${k.reason||"API error"}`),P.checkFailed?ee.notes.push(`Could not verify project activity: ${P.failureReason||"API error"}`):P.isActive||ee.notes.push("Project may be inactive"),G||ee.notes.push("Issue requirements are unclear"),L||ee.notes.push("No CONTRIBUTING.md found");let Be={id:l.id,url:e,repo:u,number:c,title:l.title,status:"candidate",labels:l.labels.map(xe=>typeof xe=="string"?xe:xe.name||""),createdAt:l.created_at,updatedAt:l.updated_at,vetted:!0,vettingResult:ee},$e=[],pe=[];R||$e.push("Has existing PR"),N||$e.push("Already claimed"),!P.isActive&&!P.checkFailed&&$e.push("Inactive project"),G||$e.push("Unclear requirements"),R&&pe.push("No existing PR"),N&&pe.push("Not claimed"),P.isActive&&!P.checkFailed&&pe.push("Active project"),G&&pe.push("Clear requirements"),L&&pe.push("Has contribution guidelines");let Fe=m?1:q;Fe>0&&pe.push(`Trusted project (${Fe} PR${Fe>1?"s":""} merged)`);let ct=u.split("/")[0],Le=!1;ct&&u.includes("/")&&(Le=p.some(xe=>xe.startsWith(ct+"/")&&xe!==u)),Le&&pe.push(`Org affinity (merged PRs in other ${ct} repos)`);let zt=this.stateReader.getProjectCategories(),ut=O_(u,zt);ut&&pe.push("Matches preferred project category");let Ne;ee.passedAllChecks?Ne="approve":$e.length>2?Ne="skip":Ne="needs_review";let Ct=P.checkFailed||f.inconclusive||k.inconclusive;Ne==="approve"&&Ct&&(Ne="needs_review",ee.notes.push("Recommendation downgraded: one or more checks were inconclusive"));let Ye=x_(P.stargazersCount??0,P.forksCount??0);P.checkFailed&&Ye===0&&ee.notes.push("Repo quality bonus unavailable: could not fetch star/fork counts due to API error");let Dt=this.stateReader.getRepoScore(u),Z=P_({repoScore:Dt,hasExistingPR:!R,isClaimed:!N,clearRequirements:G,hasContributionGuidelines:!!L,issueUpdatedAt:l.updated_at,closedWithoutMergeCount:0,mergedPRCount:Fe,orgHasMergedPRs:Le,repoQualityBonus:Ye,matchesPreferredCategory:ut}),W=this.stateReader.getStarredRepos(),De="normal";Fe>0?De="merged_pr":W.includes(u)&&(De="starred");let Qe=this.stateReader.getSLMTriageConfig?.()??{model:"",host:""},ht=null;if(Qe.model){let xe={model:Qe.model};Qe.host&&(xe.host=Qe.host),ht=await X_(Y_({issue:{...Be,body:l.body??""},linkedPR:f.linkedPR??null}),xe)}let vt={issue:Be,vettingResult:ee,projectHealth:P,antiLLMPolicy:H,slmTriage:ht,recommendation:Ne,reasonsToSkip:$e,reasonsToApprove:pe,viabilityScore:Z,searchPriority:De};return n.set(o,"",vt),vt}async vetIssuesParallel(e,n,o){let r=[],i=new Map,s=0,a=0,c=0;for(let l of e){if(r.length>=n)break;c++;let p=this.vetIssue(l).then(m=>{r.length<n&&(o&&(m.searchPriority=o),r.push(m))}).catch(m=>{s++,ae(m)&&a++,z(vp,`Error vetting issue ${l}:`,U(m))}).finally(()=>i.delete(l));i.set(l,p),i.size>=GS&&await Promise.race(i.values())}await Promise.allSettled(i.values());let u=s===c&&c>0;return u&&z(vp,`All ${c} issue(s) failed vetting. This may indicate a systemic issue (rate limit, auth, network).`),{candidates:r.slice(0,n),allFailed:u,rateLimitHit:a>0}}}});function jS(t,e=0){let n=tb-e+1;if(n<1)return t.length>0&&z(Xe,`Label filtering disabled: ${e} repo/org ORs exceed GitHub's ${tb} operator limit. All ${t.length} label(s) dropped from query.`),[[]];if(t.length<=n)return[t];let o=[];for(let r=0;r<t.length;r+=n)o.push(t.slice(r,r+n));return j(Xe,`Split ${t.length} labels into ${o.length} chunks (${e} ops reserved, max ${n} per chunk)`),o}function FS(t){return t.length===0?"":t.length===1?`label:"${t[0]}"`:`(${t.map(e=>`label:"${e}"`).join(" OR ")})`}function rb(t,e){let n=new Set;for(let o of t)for(let r of Gn[o]??[])n.add(r);for(let o of e)n.add(o);return[...n]}function nb(t){let e=[],n=Math.max(...t.map(o=>o.length),0);for(let o=0;o<n;o++)for(let r of t)o<r.length&&e.push(r[o]);return e}async function bp(t,e){let n=`search:${e.q}:${e.sort}:${e.order}:${e.per_page}`,o=at(),r=o.getIfFresh(n,LS);if(r)return j(Xe,"Search cache hit for query"),r;let i=Ur();await i.waitForBudget();let s;try{s=(await t.search.issuesAndPullRequests(e)).data}finally{i.recordCall()}return s.items.length>0?o.set(n,"",s):j(Xe,"Skipping cache for empty search result (possible rate limit artifact)"),s}async function ob(t,e,n,o){let r=[];for(let i of e){if(r.length>=o*3)break;let[s,a]=i.split("/");if(!(!s||!a))try{let{data:c}=await t.repos.get({owner:s,repo:a});if(!c.pushed_at)continue;let u=new Date(c.pushed_at),l=new Date;if(l.setDate(l.getDate()-30),u<l||(c.stargazers_count??0)<n||c.archived)continue;let{data:p}=await t.issues.listForRepo({owner:s,repo:a,state:"open",sort:"created",direction:"desc",per_page:5}),m=p.filter(f=>!f.pull_request&&!f.assignee);for(let f of m)r.push({html_url:f.html_url,repository_url:`https://api.github.com/repos/${i}`,updated_at:f.updated_at??"",title:f.title,labels:f.labels});await Ve(_p)}catch(c){if(ne(c)===401)throw c;if(ae(c)){z(Xe,`Rate limit hit fetching issues from ${i}:`,U(c));break}z(Xe,`Error fetching issues from ${i}:`,U(c))}}return r}async function yp(t,e,n,o,r,i,s){let a=[],c=0,u=0;for(let m=0;m<n.length&&!(a.length>=r);m++){m>0&&await Ve(_p);let f=n[m],[k,P]=f.split("/");try{let H=(await t.issues.listForRepo({owner:k,repo:P,state:"open",sort:"created",direction:"desc",per_page:5,...o.length>0?{labels:o.join(",")}:{}})).data.filter(R=>!("pull_request"in R)&&!R.assignee).map(R=>({html_url:R.html_url,repository_url:`https://api.github.com/repos/${f}`,updated_at:R.updated_at??"",title:R.title,labels:R.labels}));if(H.length>0){let R=s(H);if(R.length>0){let N=r-a.length,{candidates:G,rateLimitHit:re}=await e.vetIssuesParallel(R.slice(0,N*2).map(ee=>ee.html_url),N,i);a.push(...G),re&&u++}}}catch(L){if(ne(L)===401)throw L;c++,ae(L)&&u++,z(Xe,`Error fetching issues from ${f}:`,U(L))}}let l=c===n.length&&n.length>0,p=u>0;return l&&z(Xe,`All ${n.length} repo(s) failed for ${i} phase. This may indicate a systemic issue (rate limit, auth, network).`),{candidates:a,allReposFailed:l,rateLimitHit:p}}async function ib(t,e,n,o,r){let i=jS(e,n),s=new Set,a=[];for(let c=0;c<i.length;c++){c>0&&await Ve(_p);let u=o(FS(i[c])),l=await bp(t,{q:u,sort:"created",order:"desc",per_page:r});for(let p of l.items)s.has(p.html_url)||(s.add(p.html_url),a.push(p))}return a}async function Qi(t,e,n,o,r,i,s){let a=T_(e);if(a.size>0){let k=e.filter(P=>a.has(le(P.repository_url)??"")).length;j(Xe,`[SPAM_FILTER] Filtered ${k} issues from ${a.size} label-farming repos: ${[...a].join(", ")}`)}let c=n(e).filter(k=>{let P=le(k.repository_url);return!P||a.has(P)?!1:o.every(L=>!L.has(P))}).slice(0,r*2);if(c.length===0)return j(Xe,`[${s}] All ${e.length} items filtered before vetting`),{candidates:[],allVetFailed:!1,rateLimitHit:!1};let{candidates:u,allFailed:l,rateLimitHit:p}=await t.vetIssuesParallel(c.map(k=>k.html_url),r,"normal"),m=u.filter(k=>k.projectHealth.checkFailed?!0:(k.projectHealth.stargazersCount??0)>=i),f=u.length-m.length;return f>0&&j(Xe,`[STAR_FILTER] Filtered ${f} ${s} candidates below ${i} stars`),{candidates:m,allVetFailed:l,rateLimitHit:p}}var Xe,tb,_p,LS,sb=y(()=>{"use strict";pp();we();he();jr();mp();ue();Ki();Xe="search-phases",tb=5,_p=2e3;LS=900*1e3});function NS(t){return e=>e.filter(n=>{let o=le(n.repository_url);if(!o||t.excludedRepos.has(o))return!1;if(t.excludeOrgs.size>0){let s=o.split("/")[0]?.toLowerCase();if(s&&t.excludeOrgs.has(s))return!1}if(t.aiBlocklisted.has(o)||t.lowScoringRepos.has(o)||t.skippedUrls.has(n.html_url))return!1;let r=new Date(n.updated_at);return!(jt(r,t.now)>t.maxAgeDays||!t.includeDocIssues&&S_(n))})}async function ZS(t,e,n,o,r){oe(K,`Phase 0: Searching issues in ${n.length} merged-PR repos (no label filter)...`);let{candidates:i,allReposFailed:s,rateLimitHit:a}=await yp(t,e,n,[],o,"merged_pr",r);return oe(K,`Found ${i.length} candidates from merged-PR repos`),{candidates:i,error:s?"All merged-PR repo fetches failed":null,rateLimitHit:a}}async function VS(t,e,n,o,r,i){oe(K,`Phase 1: Searching issues in ${n.length} starred repos...`);let s=o.slice(0,3),a=n.slice(0,10),{candidates:c,allReposFailed:u,rateLimitHit:l}=await yp(t,e,a,s,r,"starred",i);return oe(K,`Found ${c.length} candidates from starred repos`),{candidates:c,error:u?"All starred repo fetches failed":null,rateLimitHit:l}}async function MS(t,e,n,o,r,i,s,a,c,u,l,p){oe(K,"Phase 2: General issue search...");let m=new Set(l.map(R=>R.issue.repo)),f=[];if(n&&n.length>1){for(let G of n){let re=Gn[G]??[];if(re.length===0){z(K,`Scope "${G}" has no labels, skipping tier`);continue}f.push({tier:G,tierLabels:re})}let R=new Set(n.flatMap(G=>Gn[G]??[])),N=r.filter(G=>!R.has(G));N.length>0&&f.push({tier:"custom",tierLabels:N})}else f.push({tier:"general",tierLabels:o});let k=Math.ceil(s/f.length),P=[],L=null,q=!1;for(let{tier:R,tierLabels:N}of f)try{let G=await ib(t,N,0,$e=>`${i} ${$e}`.replace(/ +/g," ").trim(),k*3);oe(K,`Phase 2 [${R}]: processing ${G.length} items...`);let{candidates:re,allVetFailed:ee,rateLimitHit:Be}=await Qi(e,G,p,[c,u,m],k,a,`Phase 2 [${R}]`);P.push(re);for(let $e of re)m.add($e.issue.repo);ee&&(L=(L?L+"; ":"")+`${R}: all vetting failed`),Be&&(q=!0),oe(K,`Found ${re.length} candidates from ${R} tier`)}catch(G){if(ne(G)===401)throw G;let re=U(G);L=(L?L+"; ":"")+`${R}: ${re}`,ae(G)&&(q=!0),z(K,`Error in ${R} tier search: ${re}`),P.push([])}let H=nb(P);return H.length===0&&L&&z(K,`All ${f.length} scope tiers failed in Phase 2: ${L}`),{candidates:H.slice(0,s),error:L,rateLimitHit:q}}async function HS(t,e,n,o,r,i,s,a,c,u,l){oe(K,"Phase 3: Searching actively maintained repos...");let p=new Set(u.map(H=>H.issue.repo)),m=c.filter(H=>!s.has(H)&&!p.has(H));if(m.length>0){oe(K,`Phase 3: Checking ${m.length} starred repos via REST API...`);let H=await ob(t,m.slice(0,15),o,i);if(H.length>0)try{let{candidates:R,allVetFailed:N,rateLimitHit:G}=await Qi(e,H,l,[s,p],i,o,"Phase 3 (REST)");if(R.length>0)return oe(K,`Found ${R.length} candidates from maintained-repo REST search`),{candidates:R,error:N?"all vetting failed":null,rateLimitHit:G}}catch(R){if(ne(R)===401)throw R;z(K,"Phase 3 REST vetting failed, falling back to Search API:",U(R))}}oe(K,"Phase 3: Falling back to Search API...");let f=new Date;f.setDate(f.getDate()-30);let k=f.toISOString().split("T")[0],P=R_(r),L=P.length>0?`topic:${P[0]}`:"",q=`is:issue is:open no:assignee ${n} ${L} stars:>=${o} pushed:>=${k} archived:false`.replace(/ +/g," ").trim();try{let H=await bp(t,{q,sort:"updated",order:"desc",per_page:i*3});oe(K,`Found ${H.total_count} issues in maintained-repo search, processing top ${H.items.length}...`);let{candidates:R,allVetFailed:N,rateLimitHit:G}=await Qi(e,H.items,l,[s,a,p],i,o,"Phase 3");return oe(K,`Found ${R.length} candidates from maintained-repo search`),{candidates:R,error:N?"all vetting failed":null,rateLimitHit:G}}catch(H){let R=U(H);return z(K,`Error in maintained-repo search: ${R}`),{candidates:[],error:R,rateLimitHit:ae(H)}}}var K,Un,es,jn,ab=y(()=>{"use strict";Ji();Ki();ue();pp();It();we();he();mp();eb();gp();sb();K="issue-discovery",Un=20,es=10;jn=class{constructor(e,n,o){this.preferences=n;this.stateReader=o;this.githubToken=e,this.octokit=er(e),this.vetter=new Yi(this.octokit,this.stateReader)}octokit;githubToken;vetter;rateLimitWarning=null;getStarredRepos(){return this.stateReader.getStarredRepos()}async searchIssues(e={}){let n=this.preferences,o=e.languages||n.languages,r=n.scope,i=e.labels||(r?rb(r,n.labels):n.labels),s=e.maxResults||10,a=n.minStars??50,c=n.interPhaseDelayMs??3e4,u=Rn,l=e.strategies??n.defaultStrategy??["all"],p=new Set(l.includes("all")?u:l),m=[],f=[],k={},P=!1;this.rateLimitWarning=null;let L=Ur(),q=Un-1;try{let Z=await Wi(this.githubToken);if(q=Z.remaining,L.init(Z.remaining,Z.resetAt),Z.remaining<5){let W=new Date(Z.resetAt).toLocaleTimeString("en-US",{hour12:!1});this.rateLimitWarning=`GitHub search API quota low (${Z.remaining}/${Z.limit} remaining, resets at ${W}). Search may be slow.`,z(K,this.rateLimitWarning)}q<es?oe(K,`Search budget critical (${q} remaining) \u2014 running only Phase 0`):q<Un&&oe(K,`Search budget low (${q} remaining) \u2014 skipping heavy phases (2, 3)`)}catch(Z){if(ne(Z)===401)throw Z;L.init(es,new Date(Date.now()+6e4).toISOString()),z(K,"Could not check rate limit \u2014 using conservative budget, skipping heavy phases:",U(Z))}if(q<=0)return this.rateLimitWarning="GitHub search API quota exhausted. Try again after the rate limit resets.",{candidates:[],strategiesUsed:[]};let H=this.stateReader.getReposWithMergedPRs(),R=this.stateReader.getReposWithOpenPRs(),N=this.getStarredRepos(),G=new Set(N),re=new Set(this.deriveLowScoringRepos(n.minRepoScoreThreshold)),Be=o.some(Z=>Z.toLowerCase()==="any")?"":o.map(Z=>`language:${Z}`).join(" "),$e=`is:issue is:open ${Be} no:assignee`.replace(/ +/g," ").trim(),pe=new Set(n.aiPolicyBlocklist);pe.size>0&&j(K,`[AI_POLICY_FILTER] Filtering issues from ${pe.size} blocklisted repo(s): ${[...pe].join(", ")}`);let Fe=NS({excludedRepos:new Set(n.excludeRepos),excludeOrgs:new Set((n.excludeOrgs??[]).map(Z=>Z.toLowerCase())),aiBlocklisted:pe,lowScoringRepos:re,skippedUrls:e.skippedUrls??new Set,maxAgeDays:n.maxIssueAgeDays||90,now:new Date,includeDocIssues:n.includeDocIssues??!0}),ct=new Set,Le=[];for(let Z of[...H,...R])if(!ct.has(Z)&&(ct.add(Z),Le.push(Z),Le.length>=10))break;let zt=new Set(Le);if(Le.length>0&&p.has("merged")){let Z=s-f.length;if(Z>0){let W=await ZS(this.octokit,this.vetter,Le,Z,Fe);f.push(...W.candidates),k[0]=W.error,W.rateLimitHit&&(P=!0)}m.push("merged")}if(f.length<s&&N.length>0&&q>=es&&p.has("starred")){c>0&&(oe(K,`Waiting ${(c/1e3).toFixed(0)}s between phases for rate limit management...`),await Ve(c));let Z=N.filter(W=>!zt.has(W));if(Z.length>0){let W=s-f.length;if(W>0){let De=await VS(this.octokit,this.vetter,Z,i,W,Fe);f.push(...De.candidates),k[1]=De.error,De.rateLimitHit&&(P=!0)}}m.push("starred")}let ut=n.broadPhaseDelayMs??9e4,Ne=n.skipBroadWhenSufficientResults??15;if(f.length<s&&q>=Un&&p.has("broad")){if(Ne>0&&f.length>=Ne)oe(K,`Skipping broad search: already found ${f.length} candidates (threshold: ${Ne})`);else{c>0&&(oe(K,`Waiting ${(c/1e3).toFixed(0)}s between phases for rate limit management...`),await Ve(c)),f.length>0&&ut>0?(oe(K,`Waiting ${(ut/1e3).toFixed(0)}s for rate limit cooldown before broad search...`),await Ve(ut)):f.length===0&&oe(K,"Skipping broad phase delay: no results from previous phases, proceeding immediately");let Z=s-f.length,W=await MS(this.octokit,this.vetter,r,i,n.labels,$e,Z,a,zt,G,f,Fe);f.push(...W.candidates),k[2]=W.error,W.rateLimitHit&&(P=!0)}m.push("broad")}if(f.length<s&&q>=Un&&p.has("maintained")){c>0&&(oe(K,`Waiting ${(c/1e3).toFixed(0)}s between phases for rate limit management...`),await Ve(c));let Z=s-f.length,W=await HS(this.octokit,this.vetter,Be,a,n.projectCategories??[],Z,zt,G,N,f,Fe);f.push(...W.candidates),k[3]=W.error,W.rateLimitHit&&(P=!0),m.push("maintained")}let Ct=q<Un,Ye="";if(q<es?Ye=` Most search phases were skipped due to critically low API quota (${q} remaining).`:Ct&&(Ye=` Some search phases were skipped due to low API quota (${q} remaining).`),f.length===0){let Z=[k[0]?`Phase 0 (merged-PR repos): ${k[0]}`:null,k[1]?`Phase 1 (starred repos): ${k[1]}`:null,k[2]?`Phase 2 (general): ${k[2]}`:null,k[3]?`Phase 3 (maintained repos): ${k[3]}`:null].filter(Boolean),W=Z.length>0?` ${Z.join(". ")}.`:"";if(P||Ct)return this.rateLimitWarning=`Search returned no results due to GitHub API rate limits.${W}${Ye} Try again after the rate limit resets.`,{candidates:[],strategiesUsed:m};throw new ve(`No issue candidates found across all search phases.${W} Try adjusting your search criteria (languages, labels) or check your network connection.`)}(P||Ct)&&(this.rateLimitWarning=`Search results may be incomplete: GitHub API rate limits were hit during search.${Ye} Found ${f.length} candidate${f.length===1?"":"s"} but some search phases were limited. Try again after the rate limit resets for complete results.`),f.sort((Z,W)=>{let De={merged_pr:0,starred:1,normal:2},Qe=De[Z.searchPriority]-De[W.searchPriority];if(Qe!==0)return Qe;let ht={approve:0,needs_review:1,skip:2},vt=ht[Z.recommendation]-ht[W.recommendation];return vt!==0?vt:W.viabilityScore-Z.viabilityScore});let Dt=E_(f,2);return oe(K,`Search complete: ${L.getTotalCalls()} Search API calls used, ${Dt.length} candidates returned`),{candidates:Dt.slice(0,s),strategiesUsed:m}}async vetIssue(e){return this.vetter.vetIssue(e)}deriveLowScoringRepos(e){let n=[],o=new Set([...this.stateReader.getReposWithMergedPRs(),...this.stateReader.getStarredRepos()]);for(let r of o){let i=this.stateReader.getRepoScore(r);i!==null&&i<=e&&n.push(r)}return n}}});function ub(){return Sp.join(Ut(),qS)}function lb(){return Sp.join(Ut(),BS)}function db(t,e){return{version:1,preferences:e.preferences,repoScores:JS(t.repoScores,e.repoScores),starredRepos:KS(t,e),starredReposLastFetched:kp(t.starredReposLastFetched,e.starredReposLastFetched),mergedPRs:wp(t.mergedPRs,e.mergedPRs),closedPRs:wp(t.closedPRs,e.closedPRs),openPRs:wp(t.openPRs??[],e.openPRs??[]),savedResults:XS(t.savedResults??[],e.savedResults??[]),skippedIssues:YS(t.skippedIssues??[],e.skippedIssues??[]),lastSearchAt:kp(t.lastSearchAt,e.lastSearchAt),lastRunAt:kp(t.lastRunAt,e.lastRunAt)??new Date().toISOString(),gistId:e.gistId??t.gistId}}function JS(t,e){let n={...t};for(let[o,r]of Object.entries(e)){let i=n[o];if(!i)n[o]=r;else{let s=i.mergedPRCount+i.closedWithoutMergeCount,a=r.mergedPRCount+r.closedWithoutMergeCount;n[o]=a>=s?r:i}}return n}function KS(t,e){let n=t.starredReposLastFetched,o=e.starredReposLastFetched;return!n&&!o?e.starredRepos.length>=t.starredRepos.length?e.starredRepos:t.starredRepos:n?o&&o>=n?e.starredRepos:t.starredRepos:e.starredRepos}function wp(t,e){let n=new Map;for(let o of t)n.set(o.url,o);for(let o of e)n.set(o.url,o);return[...n.values()]}function XS(t,e){let n=new Map;for(let o of t)n.set(o.issueUrl,o);for(let o of e){let r=n.get(o.issueUrl);(!r||o.lastSeenAt>r.lastSeenAt)&&n.set(o.issueUrl,o)}return[...n.values()]}function YS(t,e){let n=new Map;for(let o of t)n.set(o.url,o);for(let o of e){let r=n.get(o.url);(!r||o.skippedAt>r.skippedAt)&&n.set(o.url,o)}return[...n.values()]}function kp(t,e){return t?e?t>=e?t:e:t:e}var Lr,Sp,de,cb,$p,qS,BS,WS,ts,pb=y(()=>{"use strict";Lr=Pe(require("fs"),1),Sp=Pe(require("path"),1);It();ue();he();we();de="gist-state",cb="oss-scout-state",$p="state.json",qS="gist-id",BS="state-cache.json",WS=5;ts=class{constructor(e){this.octokit=e}gistId=null;async bootstrap(){try{return await this.bootstrapFromApi()}catch(e){return z(de,`API bootstrap failed: ${U(e)}`),this.bootstrapFromCache()}}async push(e){if(this.writeCache(e),!this.gistId)return z(de,"No gist ID \u2014 cannot push"),!1;let n=JSON.stringify(e,null,2);if(n.length>9e5)return z(de,`State too large for gist (${Math.round(n.length/1024)}KB). Consider clearing old results with 'oss-scout results clear'.`),!1;try{return await this.octokit.gists.update({gist_id:this.gistId,files:{[$p]:{content:n}}}),j(de,"State pushed to gist"),!0}catch(o){return z(de,`Failed to push: ${U(o)}`),!1}}async pull(){if(!this.gistId)return null;try{let e=await this.fetchGistState(this.gistId);return e&&this.writeCache(e),e}catch(e){return z(de,`Failed to pull: ${U(e)}`),null}}getGistId(){return this.gistId}async bootstrapFromApi(){let e=this.readCachedGistId();if(e){j(de,`Trying cached gist ID: ${e}`);try{let i=await this.fetchGistState(e);if(i)return this.gistId=e,this.writeCache(i),{gistId:e,state:i,created:!1}}catch(i){j(de,`Cached gist ID invalid: ${U(i)}`)}j(de,"Cached gist ID invalid, searching...")}let n=await this.searchForGist();if(n){j(de,`Found gist via search: ${n}`),this.saveGistId(n),this.gistId=n;let i=await this.fetchGistState(n);return i?(this.writeCache(i),{gistId:n,state:i,created:!1}):(z(de,`Found existing gist ${n} but content failed validation. Using local cache to avoid data loss.`),this.bootstrapFromCache())}j(de,"No existing gist found, creating new one");let o=Ke.parse({version:1}),r=await this.createGist(o);return this.saveGistId(r),this.gistId=r,this.writeCache(o),{gistId:r,state:o,created:!0}}bootstrapFromCache(){let e=this.readCache();if(e){j(de,"Bootstrapped from local cache (degraded mode)");let o=this.readCachedGistId();return o&&(this.gistId=o),{gistId:o??"",state:e,created:!1,degraded:!0}}return j(de,"No cache available, using fresh state (degraded mode)"),{gistId:"",state:Ke.parse({version:1}),created:!1,degraded:!0}}async fetchGistState(e){let{data:n}=await this.octokit.gists.get({gist_id:e}),o=n.files?.[$p];if(!o?.content)return null;try{let r=JSON.parse(o.content);return Ke.parse(r)}catch(r){return z(de,`Gist content failed validation: ${U(r)}`),null}}async searchForGist(){for(let e=1;e<=WS;e++){let{data:n}=await this.octokit.gists.list({per_page:100,page:e});if(n.length===0)break;let o=n.find(r=>r.description===cb);if(o)return o.id}return null}async createGist(e){let{data:n}=await this.octokit.gists.create({description:cb,public:!1,files:{[$p]:{content:JSON.stringify(e,null,2)}}});return n.id}readCachedGistId(){try{return Lr.readFileSync(ub(),"utf-8").trim()||null}catch(e){return e?.code!=="ENOENT"&&z(de,`Failed to read cached gist ID: ${U(e)}`),null}}saveGistId(e){Lr.writeFileSync(ub(),e+`
91
91
  `,{mode:384})}readCache(){try{let e=Lr.readFileSync(lb(),"utf-8");return Ke.parse(JSON.parse(e))}catch(e){return e?.code!=="ENOENT"&&z(de,`Failed to read state cache: ${U(e)}`),null}}writeCache(e){try{Lr.writeFileSync(lb(),JSON.stringify(e,null,2)+`
92
- `,{mode:384})}catch(n){z(de,`Failed to write cache: ${U(n)}`)}}}});var mb={};ce(mb,{OssScout:()=>rs,createScout:()=>je});function QS(t){return{gists:{async get(e){let{data:n}=await t.gists.get(e);if(!n.id)throw new Error("Gist get returned no id");let o=n.files?Object.fromEntries(Object.entries(n.files).map(([r,i])=>[r,i?{content:i.content}:void 0])):null;return{data:{id:n.id,files:o}}},async create(e){let{data:n}=await t.gists.create(e);if(!n.id)throw new Error("Gist create returned no id");return{data:{id:n.id}}},async update(e){let{data:n}=await t.gists.update(e);if(!n.id)throw new Error("Gist update returned no id");return{data:{id:n.id}}},async list(e){let{data:n}=await t.gists.list(e);return{data:n.filter(o=>o.id).map(o=>({id:o.id,description:o.description??null}))}}}}}async function je(t){let e,n=null;if(t.persistence==="provided")e=t.initialState;else if(t.persistence==="gist"){n=new ts(QS(er(t.githubToken)));let o=await n.bootstrap();o.degraded&&z("scout","Gist sync unavailable \u2014 running in offline mode. Changes will only be saved locally.");let r=se();e=db(r,o.state),t.gistId?e.gistId=t.gistId:o.gistId&&(e.gistId=o.gistId)}else e=Ke.parse({version:1});return new rs(t.githubToken,e,n)}var rs,Nr=y(()=>{"use strict";ab();It();pb();Ji();Ot();he();ue();rs=class{constructor(e,n,o=null){this.githubToken=e;this.gistStore=o;this.state=n}state;dirty=!1;async search(e){this.cullExpiredSkips();let n=new Set((this.state.skippedIssues??[]).map(s=>s.url)),o=new jn(this.githubToken,this.state.preferences,this),{candidates:r,strategiesUsed:i}=await o.searchIssues({maxResults:e?.maxResults,strategies:e?.strategies,skippedUrls:n});return this.state.lastSearchAt=new Date().toISOString(),this.dirty=!0,{candidates:r,excludedRepos:this.state.preferences.excludeRepos,aiPolicyBlocklist:this.state.preferences.aiPolicyBlocklist,rateLimitWarning:o.rateLimitWarning??void 0,strategiesUsed:i}}async vetIssue(e){return new jn(this.githubToken,this.state.preferences,this).vetIssue(e)}async vetList(e){let n=this.getSavedResults(),o=e?.concurrency??5,r=[],i=new Map;for(let c of n){let u=this.vetIssue(c.issueUrl).then(l=>{r.push({issueUrl:c.issueUrl,repo:c.repo,number:c.number,title:c.title,status:this.classifyVetResult(l),recommendation:l.recommendation,viabilityScore:l.viabilityScore})}).catch(l=>{let p=l instanceof Error?l.message:String(l),m=p.includes("Not Found")||p.includes("410");r.push({issueUrl:c.issueUrl,repo:c.repo,number:c.number,title:c.title,status:m?"closed":"error",errorMessage:p})}).finally(()=>{i.delete(c.issueUrl)});i.set(c.issueUrl,u),i.size>=o&&await Promise.race(i.values())}await Promise.allSettled(i.values());let s={total:r.length,stillAvailable:r.filter(c=>c.status==="still_available").length,claimed:r.filter(c=>c.status==="claimed").length,closed:r.filter(c=>c.status==="closed").length,hasPR:r.filter(c=>c.status==="has_pr").length,errors:r.filter(c=>c.status==="error").length},a;if(e?.prune){let c=new Set(r.filter(l=>l.status!=="still_available").map(l=>l.issueUrl)),u=(this.state.savedResults??[]).length;this.state.savedResults=(this.state.savedResults??[]).filter(l=>!c.has(l.issueUrl)),a=u-(this.state.savedResults?.length??0),this.dirty=!0}return{results:r,summary:s,prunedCount:a}}classifyVetResult(e){let n=e.vettingResult.checks;return n.noExistingPR?n.notClaimed?"still_available":"claimed":"has_pr"}getReposWithMergedPRs(){let e=new Map;for(let n of this.state.mergedPRs??[]){let o=le(n.url);o&&e.set(o,(e.get(o)??0)+1)}return[...e.entries()].sort((n,o)=>o[1]-n[1]).map(([n])=>n)}getReposWithOpenPRs(){let e=new Map;for(let n of this.state.openPRs??[]){let o=le(n.url);o&&e.set(o,(e.get(o)??0)+1)}return[...e.entries()].sort((n,o)=>o[1]-n[1]).map(([n])=>n)}getStarredRepos(){return this.state.starredRepos}getProjectCategories(){return this.state.preferences.projectCategories}getRepoScore(e){let n=this.state.repoScores[e];return n?n.score:null}getPreferences(){return this.state.preferences}getRepoScoreRecord(e){return this.state.repoScores[e]}recordMergedPR(e){let n=this.state.mergedPRs??[];n.some(o=>o.url===e.url)||(this.state.mergedPRs=[...n,{url:e.url,title:e.title,mergedAt:e.mergedAt}],this.updateRepoScoreFromPRs(e.repo),this.dirty=!0)}recordClosedPR(e){let n=this.state.closedPRs??[];n.some(o=>o.url===e.url)||(this.state.closedPRs=[...n,{url:e.url,title:e.title,closedAt:e.closedAt}],this.updateRepoScoreFromPRs(e.repo),this.dirty=!0)}recordOpenPR(e){let n=this.state.openPRs??[];n.some(o=>o.url===e.url)||(this.state.openPRs=[...n,{url:e.url,title:e.title,openedAt:e.openedAt}],this.dirty=!0)}updateRepoScore(e,n){let r=this.state.repoScores[e]??{repo:e,score:5,mergedPRCount:0,closedWithoutMergeCount:0,avgResponseDays:null,lastEvaluatedAt:new Date().toISOString(),signals:{hasActiveMaintainers:!1,isResponsive:!1,hasHostileComments:!1}},i={...r,...n,repo:e,lastEvaluatedAt:new Date().toISOString(),signals:{...r.signals,...n.signals??{}}};i.score=this.calculateScore(i),this.state.repoScores[e]=i,this.dirty=!0}updatePreferences(e){this.state.preferences={...this.state.preferences,...e},this.dirty=!0}setStarredRepos(e){this.state.starredRepos=e,this.state.starredReposLastFetched=new Date().toISOString(),this.dirty=!0}saveResults(e){let n=new Date().toISOString(),o=new Map((this.state.savedResults??[]).map(r=>[r.issueUrl,r]));for(let r of e){let i=o.get(r.issue.url);o.set(r.issue.url,{issueUrl:r.issue.url,repo:r.issue.repo,number:r.issue.number,title:r.issue.title,labels:r.issue.labels,recommendation:r.recommendation,viabilityScore:r.viabilityScore,searchPriority:r.searchPriority,firstSeenAt:i?.firstSeenAt??n,lastSeenAt:n,lastScore:r.viabilityScore})}this.state.savedResults=[...o.values()],this.dirty=!0}getSavedResults(){return this.state.savedResults??[]}clearResults(){this.state.savedResults=[],this.dirty=!0}skipIssue(e,n){let o=this.state.skippedIssues??[];o.some(r=>r.url===e)||(this.state.skippedIssues=[...o,{url:e,repo:n?.repo??"",number:n?.number??0,title:n?.title??"",skippedAt:new Date().toISOString()}],this.state.savedResults&&(this.state.savedResults=this.state.savedResults.filter(r=>r.issueUrl!==e)),this.dirty=!0)}getSkippedIssues(){return this.state.skippedIssues??[]}unskipIssue(e){this.state.skippedIssues=(this.state.skippedIssues??[]).filter(n=>n.url!==e),this.dirty=!0}clearSkippedIssues(){this.state.skippedIssues=[],this.dirty=!0}cullExpiredSkips(e=90){let n=new Date;n.setDate(n.getDate()-e);let o=(this.state.skippedIssues??[]).length;this.state.skippedIssues=(this.state.skippedIssues??[]).filter(i=>{let s=new Date(i.skippedAt);return isNaN(s.getTime())?!0:s>=n});let r=o-this.state.skippedIssues.length;return r>0&&(this.dirty=!0),r}isDirty(){return this.dirty}async checkpoint(){return this.dirty?(this.state.lastRunAt=new Date().toISOString(),this.gistStore&&!await this.gistStore.push(this.state)?!1:(this.dirty=!1,!0)):!0}getState(){return this.state}updateRepoScoreFromPRs(e){let n=(this.state.mergedPRs??[]).filter(r=>le(r.url)===e).length,o=(this.state.closedPRs??[]).filter(r=>le(r.url)===e).length;this.updateRepoScore(e,{mergedPRCount:n,closedWithoutMergeCount:o,lastMergedAt:n>0?(this.state.mergedPRs??[]).filter(r=>le(r.url)===e).sort((r,i)=>i.mergedAt.localeCompare(r.mergedAt))[0]?.mergedAt:void 0})}calculateScore(e){let n=5;return n+=Math.min(e.mergedPRCount,3),n-=Math.min(e.closedWithoutMergeCount,3),e.signals.isResponsive&&(n+=1),e.signals.hasActiveMaintainers&&(n+=1),e.signals.hasHostileComments&&(n-=2),Math.max(1,Math.min(10,n))}}});var gb={};ce(gb,{runSearch:()=>eT});async function eT(t){let e=Ft(),n=t.state?await je({githubToken:e,persistence:"provided",initialState:t.state}):await je({githubToken:e}),o=await n.search({maxResults:t.maxResults,strategies:t.strategies});return n.saveResults(o.candidates),be(n.getState()),await n.checkpoint()||console.error("Warning: changes saved locally but gist sync failed."),{candidates:o.candidates.map(i=>{let s=n.getRepoScoreRecord(i.issue.repo);return{issue:{repo:i.issue.repo,repoUrl:`https://github.com/${i.issue.repo}`,number:i.issue.number,title:i.issue.title,url:i.issue.url,labels:i.issue.labels},recommendation:i.recommendation,reasonsToApprove:i.reasonsToApprove,reasonsToSkip:i.reasonsToSkip,searchPriority:i.searchPriority,viabilityScore:i.viabilityScore,repoScore:s?{score:s.score,mergedPRCount:s.mergedPRCount,closedWithoutMergeCount:s.closedWithoutMergeCount,isResponsive:s.signals?.isResponsive??!1,lastMergedAt:s.lastMergedAt}:void 0}}),excludedRepos:o.excludedRepos,aiPolicyBlocklist:o.aiPolicyBlocklist,rateLimitWarning:o.rateLimitWarning,strategiesUsed:o.strategiesUsed}}var fb=y(()=>{"use strict";Nr();ue();Ot()});var Tp={};ce(Tp,{runResults:()=>tT,runResultsClear:()=>rT});async function tT(t){return se().savedResults??[]}async function rT(){let t=se();t.savedResults=[],be(t)}var Ep=y(()=>{"use strict";Ot()});var ns={};ce(ns,{getConfigData:()=>sT,runConfigReset:()=>cT,runConfigSet:()=>aT,runConfigShow:()=>iT});function nT(t){let e=t.toLowerCase();if(e==="true"||e==="yes")return!0;if(e==="false"||e==="no")return!1;throw new ve(`Invalid boolean value: "${t}". Use true/false or yes/no.`)}function oT(t,e){let n=parseInt(t,10);if(isNaN(n))throw new ve(`Invalid number for "${e}": "${t}"`);return n}function xp(t){return t.split(",").map(e=>e.trim()).filter(e=>e.length>0)}function vb(t,e){if(e.startsWith("+")){let n=xp(e.slice(1)),o=[...t];for(let r of n)o.includes(r)||o.push(r);return o}if(e.startsWith("-")){let n=new Set(xp(e.slice(1)));return t.filter(o=>!n.has(o))}return xp(e)}function At(t){return t.length>0?t.join(", "):"(none)"}function iT(){let e=se().preferences;console.log(`
92
+ `,{mode:384})}catch(n){z(de,`Failed to write cache: ${U(n)}`)}}}});var mb={};ce(mb,{OssScout:()=>rs,createScout:()=>je});function QS(t){return{gists:{async get(e){let{data:n}=await t.gists.get(e);if(!n.id)throw new Error("Gist get returned no id");let o=n.files?Object.fromEntries(Object.entries(n.files).map(([r,i])=>[r,i?{content:i.content}:void 0])):null;return{data:{id:n.id,files:o}}},async create(e){let{data:n}=await t.gists.create(e);if(!n.id)throw new Error("Gist create returned no id");return{data:{id:n.id}}},async update(e){let{data:n}=await t.gists.update(e);if(!n.id)throw new Error("Gist update returned no id");return{data:{id:n.id}}},async list(e){let{data:n}=await t.gists.list(e);return{data:n.filter(o=>o.id).map(o=>({id:o.id,description:o.description??null}))}}}}}async function je(t){let e,n=null;if(t.persistence==="provided")e=t.initialState;else if(t.persistence==="gist"){n=new ts(QS(er(t.githubToken)));let o=await n.bootstrap();o.degraded&&z("scout","Gist sync unavailable \u2014 running in offline mode. Changes will only be saved locally.");let r=se();e=db(r,o.state),t.gistId?e.gistId=t.gistId:o.gistId&&(e.gistId=o.gistId)}else e=Ke.parse({version:1});return new rs(t.githubToken,e,n)}var rs,Nr=y(()=>{"use strict";ab();It();pb();Ji();Ot();he();ue();rs=class{constructor(e,n,o=null){this.githubToken=e;this.gistStore=o;this.state=n}state;dirty=!1;async search(e){this.cullExpiredSkips();let n=new Set((this.state.skippedIssues??[]).map(s=>s.url)),o=new jn(this.githubToken,this.state.preferences,this),{candidates:r,strategiesUsed:i}=await o.searchIssues({maxResults:e?.maxResults,strategies:e?.strategies,skippedUrls:n});return this.state.lastSearchAt=new Date().toISOString(),this.dirty=!0,{candidates:r,excludedRepos:this.state.preferences.excludeRepos,aiPolicyBlocklist:this.state.preferences.aiPolicyBlocklist,rateLimitWarning:o.rateLimitWarning??void 0,strategiesUsed:i}}async vetIssue(e){return new jn(this.githubToken,this.state.preferences,this).vetIssue(e)}async vetList(e){let n=this.getSavedResults(),o=e?.concurrency??5,r=[],i=new Map;for(let c of n){let u=this.vetIssue(c.issueUrl).then(l=>{r.push({issueUrl:c.issueUrl,repo:c.repo,number:c.number,title:c.title,status:this.classifyVetResult(l),recommendation:l.recommendation,viabilityScore:l.viabilityScore})}).catch(l=>{let p=l instanceof Error?l.message:String(l),m=p.includes("Not Found")||p.includes("410");r.push({issueUrl:c.issueUrl,repo:c.repo,number:c.number,title:c.title,status:m?"closed":"error",errorMessage:p})}).finally(()=>{i.delete(c.issueUrl)});i.set(c.issueUrl,u),i.size>=o&&await Promise.race(i.values())}await Promise.allSettled(i.values());let s={total:r.length,stillAvailable:r.filter(c=>c.status==="still_available").length,claimed:r.filter(c=>c.status==="claimed").length,closed:r.filter(c=>c.status==="closed").length,hasPR:r.filter(c=>c.status==="has_pr").length,errors:r.filter(c=>c.status==="error").length},a;if(e?.prune){let c=new Set(r.filter(l=>l.status!=="still_available").map(l=>l.issueUrl)),u=(this.state.savedResults??[]).length;this.state.savedResults=(this.state.savedResults??[]).filter(l=>!c.has(l.issueUrl)),a=u-(this.state.savedResults?.length??0),this.dirty=!0}return{results:r,summary:s,prunedCount:a}}classifyVetResult(e){let n=e.vettingResult.checks;return n.noExistingPR?n.notClaimed?"still_available":"claimed":"has_pr"}getReposWithMergedPRs(){let e=new Map;for(let n of this.state.mergedPRs??[]){let o=le(n.url);o&&e.set(o,(e.get(o)??0)+1)}return[...e.entries()].sort((n,o)=>o[1]-n[1]).map(([n])=>n)}getReposWithOpenPRs(){let e=new Map;for(let n of this.state.openPRs??[]){let o=le(n.url);o&&e.set(o,(e.get(o)??0)+1)}return[...e.entries()].sort((n,o)=>o[1]-n[1]).map(([n])=>n)}getStarredRepos(){return this.state.starredRepos}getProjectCategories(){return this.state.preferences.projectCategories}getRepoScore(e){let n=this.state.repoScores[e];return n?n.score:null}getSLMTriageConfig(){return{model:this.state.preferences.slmTriageModel??"",host:this.state.preferences.slmTriageHost??""}}getPreferences(){return this.state.preferences}getRepoScoreRecord(e){return this.state.repoScores[e]}recordMergedPR(e){let n=this.state.mergedPRs??[];n.some(o=>o.url===e.url)||(this.state.mergedPRs=[...n,{url:e.url,title:e.title,mergedAt:e.mergedAt}],this.updateRepoScoreFromPRs(e.repo),this.dirty=!0)}recordClosedPR(e){let n=this.state.closedPRs??[];n.some(o=>o.url===e.url)||(this.state.closedPRs=[...n,{url:e.url,title:e.title,closedAt:e.closedAt}],this.updateRepoScoreFromPRs(e.repo),this.dirty=!0)}recordOpenPR(e){let n=this.state.openPRs??[];n.some(o=>o.url===e.url)||(this.state.openPRs=[...n,{url:e.url,title:e.title,openedAt:e.openedAt}],this.dirty=!0)}updateRepoScore(e,n){let r=this.state.repoScores[e]??{repo:e,score:5,mergedPRCount:0,closedWithoutMergeCount:0,avgResponseDays:null,lastEvaluatedAt:new Date().toISOString(),signals:{hasActiveMaintainers:!1,isResponsive:!1,hasHostileComments:!1}},i={...r,...n,repo:e,lastEvaluatedAt:new Date().toISOString(),signals:{...r.signals,...n.signals??{}}};i.score=this.calculateScore(i),this.state.repoScores[e]=i,this.dirty=!0}updatePreferences(e){this.state.preferences={...this.state.preferences,...e},this.dirty=!0}setStarredRepos(e){this.state.starredRepos=e,this.state.starredReposLastFetched=new Date().toISOString(),this.dirty=!0}saveResults(e){let n=new Date().toISOString(),o=new Map((this.state.savedResults??[]).map(r=>[r.issueUrl,r]));for(let r of e){let i=o.get(r.issue.url);o.set(r.issue.url,{issueUrl:r.issue.url,repo:r.issue.repo,number:r.issue.number,title:r.issue.title,labels:r.issue.labels,recommendation:r.recommendation,viabilityScore:r.viabilityScore,searchPriority:r.searchPriority,firstSeenAt:i?.firstSeenAt??n,lastSeenAt:n,lastScore:r.viabilityScore})}this.state.savedResults=[...o.values()],this.dirty=!0}getSavedResults(){return this.state.savedResults??[]}clearResults(){this.state.savedResults=[],this.dirty=!0}skipIssue(e,n){let o=this.state.skippedIssues??[];o.some(r=>r.url===e)||(this.state.skippedIssues=[...o,{url:e,repo:n?.repo??"",number:n?.number??0,title:n?.title??"",skippedAt:new Date().toISOString()}],this.state.savedResults&&(this.state.savedResults=this.state.savedResults.filter(r=>r.issueUrl!==e)),this.dirty=!0)}getSkippedIssues(){return this.state.skippedIssues??[]}unskipIssue(e){this.state.skippedIssues=(this.state.skippedIssues??[]).filter(n=>n.url!==e),this.dirty=!0}clearSkippedIssues(){this.state.skippedIssues=[],this.dirty=!0}cullExpiredSkips(e=90){let n=new Date;n.setDate(n.getDate()-e);let o=(this.state.skippedIssues??[]).length;this.state.skippedIssues=(this.state.skippedIssues??[]).filter(i=>{let s=new Date(i.skippedAt);return isNaN(s.getTime())?!0:s>=n});let r=o-this.state.skippedIssues.length;return r>0&&(this.dirty=!0),r}isDirty(){return this.dirty}async checkpoint(){return this.dirty?(this.state.lastRunAt=new Date().toISOString(),this.gistStore&&!await this.gistStore.push(this.state)?!1:(this.dirty=!1,!0)):!0}getState(){return this.state}updateRepoScoreFromPRs(e){let n=(this.state.mergedPRs??[]).filter(r=>le(r.url)===e).length,o=(this.state.closedPRs??[]).filter(r=>le(r.url)===e).length;this.updateRepoScore(e,{mergedPRCount:n,closedWithoutMergeCount:o,lastMergedAt:n>0?(this.state.mergedPRs??[]).filter(r=>le(r.url)===e).sort((r,i)=>i.mergedAt.localeCompare(r.mergedAt))[0]?.mergedAt:void 0})}calculateScore(e){let n=5;return n+=Math.min(e.mergedPRCount,3),n-=Math.min(e.closedWithoutMergeCount,3),e.signals.isResponsive&&(n+=1),e.signals.hasActiveMaintainers&&(n+=1),e.signals.hasHostileComments&&(n-=2),Math.max(1,Math.min(10,n))}}});var gb={};ce(gb,{runSearch:()=>eT});async function eT(t){let e=Ft(),n=t.state?await je({githubToken:e,persistence:"provided",initialState:t.state}):await je({githubToken:e}),o=await n.search({maxResults:t.maxResults,strategies:t.strategies});return n.saveResults(o.candidates),be(n.getState()),await n.checkpoint()||console.error("Warning: changes saved locally but gist sync failed."),{candidates:o.candidates.map(i=>{let s=n.getRepoScoreRecord(i.issue.repo);return{issue:{repo:i.issue.repo,repoUrl:`https://github.com/${i.issue.repo}`,number:i.issue.number,title:i.issue.title,url:i.issue.url,labels:i.issue.labels},recommendation:i.recommendation,reasonsToApprove:i.reasonsToApprove,reasonsToSkip:i.reasonsToSkip,searchPriority:i.searchPriority,viabilityScore:i.viabilityScore,repoScore:s?{score:s.score,mergedPRCount:s.mergedPRCount,closedWithoutMergeCount:s.closedWithoutMergeCount,isResponsive:s.signals?.isResponsive??!1,lastMergedAt:s.lastMergedAt}:void 0}}),excludedRepos:o.excludedRepos,aiPolicyBlocklist:o.aiPolicyBlocklist,rateLimitWarning:o.rateLimitWarning,strategiesUsed:o.strategiesUsed}}var fb=y(()=>{"use strict";Nr();ue();Ot()});var Tp={};ce(Tp,{runResults:()=>tT,runResultsClear:()=>rT});async function tT(t){return se().savedResults??[]}async function rT(){let t=se();t.savedResults=[],be(t)}var Ep=y(()=>{"use strict";Ot()});var ns={};ce(ns,{getConfigData:()=>sT,runConfigReset:()=>cT,runConfigSet:()=>aT,runConfigShow:()=>iT});function nT(t){let e=t.toLowerCase();if(e==="true"||e==="yes")return!0;if(e==="false"||e==="no")return!1;throw new ve(`Invalid boolean value: "${t}". Use true/false or yes/no.`)}function oT(t,e){let n=parseInt(t,10);if(isNaN(n))throw new ve(`Invalid number for "${e}": "${t}"`);return n}function xp(t){return t.split(",").map(e=>e.trim()).filter(e=>e.length>0)}function vb(t,e){if(e.startsWith("+")){let n=xp(e.slice(1)),o=[...t];for(let r of n)o.includes(r)||o.push(r);return o}if(e.startsWith("-")){let n=new Set(xp(e.slice(1)));return t.filter(o=>!n.has(o))}return xp(e)}function At(t){return t.length>0?t.join(", "):"(none)"}function iT(){let e=se().preferences;console.log(`
93
93
  \u2699\uFE0F oss-scout preferences
94
94
  `),console.log(` githubUsername: ${e.githubUsername||"(not set)"}`),console.log(` languages: ${At(e.languages)}`),console.log(` labels: ${At(e.labels)}`),console.log(` scope: ${e.scope?At(e.scope):"(all)"}`),console.log(` minStars: ${e.minStars}`),console.log(` maxIssueAgeDays: ${e.maxIssueAgeDays}`),console.log(` minRepoScoreThreshold: ${e.minRepoScoreThreshold}`),console.log(` interPhaseDelayMs: ${e.interPhaseDelayMs}ms (${(e.interPhaseDelayMs/1e3).toFixed(0)}s)`),console.log(` includeDocIssues: ${e.includeDocIssues}`),console.log(` projectCategories: ${At(e.projectCategories)}`),console.log(` excludeRepos: ${At(e.excludeRepos)}`),console.log(` excludeOrgs: ${At(e.excludeOrgs)}`),console.log(` aiPolicyBlocklist: ${At(e.aiPolicyBlocklist)}`),console.log(` defaultStrategy: ${e.defaultStrategy?At(e.defaultStrategy):"(all)"}`),console.log(` persistence: ${e.persistence}`),console.log(` broadPhaseDelayMs: ${e.broadPhaseDelayMs}ms (${(e.broadPhaseDelayMs/1e3).toFixed(0)}s)`),console.log(` skipBroadWhenSufficientResults: ${e.skipBroadWhenSufficientResults}`),console.log()}function sT(){return se().preferences}function aT(t,e){let n=hb[t];if(!n)throw new ve(`Unknown config key: "${t}". Valid keys: ${Object.keys(hb).sort().join(", ")}`);let o=se(),r={...o.preferences};switch(n.type){case"string":r[t]=e;break;case"boolean":r[t]=nT(e);break;case"number":r[t]=oT(e,t);break;case"array":{let s=r[t]??[];r[t]=vb(s,e);break}case"enum":{let s=n.validValues;if(!s.includes(e))throw new ve(`Invalid value for "${t}": "${e}". Valid: ${s.join(", ")}`);r[t]=e;break}case"enum-array":{let s=r[t]??[],a=vb(s,e),c=n.validValues,u=a.filter(l=>!c.includes(l));if(u.length>0)throw new ve(`Invalid value(s) for "${t}": ${u.join(", ")}. Valid: ${c.join(", ")}`);t==="scope"?r[t]=a.length>0?a:void 0:r[t]=a;break}}let i=Wt.parse(r);return o.preferences=i,be(o),i}function cT(){let t=se(),e=Wt.parse({});return t.preferences=e,be(t),e}var hb,os=y(()=>{"use strict";Ot();It();we();hb={languages:{type:"array"},labels:{type:"array"},excludeRepos:{type:"array"},excludeOrgs:{type:"array"},aiPolicyBlocklist:{type:"array"},minStars:{type:"number"},maxIssueAgeDays:{type:"number"},minRepoScoreThreshold:{type:"number"},interPhaseDelayMs:{type:"number"},includeDocIssues:{type:"boolean"},scope:{type:"enum-array",validValues:In.options},projectCategories:{type:"enum-array",validValues:Pn.options},persistence:{type:"enum",validValues:Zd.options},defaultStrategy:{type:"enum-array",validValues:On.options},githubUsername:{type:"string"},broadPhaseDelayMs:{type:"number"},skipBroadWhenSufficientResults:{type:"number"}}});var _b={};ce(_b,{runVetList:()=>uT});async function uT(t){let e=Ft(),n=t.state?await je({githubToken:e,persistence:"provided",initialState:t.state}):await je({githubToken:e}),o=await n.vetList({concurrency:t.concurrency,prune:t.prune});return be(n.getState()),await n.checkpoint()||console.error("Warning: changes saved locally but gist sync failed."),o}var bb=y(()=>{"use strict";Nr();ue();Ot()});var Fn={};ce(Fn,{runSkip:()=>lT,runSkipClear:()=>pT,runSkipList:()=>dT,runSkipRemove:()=>mT});async function Pp(t){let e=Hn()??"";return e?je({githubToken:e,persistence:"provided",initialState:t}):je({githubToken:"",persistence:"provided",initialState:t})}async function lT(t){let e=t.state??se(),n=await Pp(e);if(n.getSkippedIssues().some(s=>s.url===t.issueUrl))return{skipped:!1,alreadySkipped:!0};let r=n.getSavedResults().find(s=>s.issueUrl===t.issueUrl),i=r?{repo:r.repo,number:r.number,title:r.title}:gT(t.issueUrl);return n.skipIssue(t.issueUrl,i),be(n.getState()),await n.checkpoint(),{skipped:!0,alreadySkipped:!1}}function dT(t){return(t?.state??se()).skippedIssues??[]}async function pT(){let t=se(),e=await Pp(t);e.clearSkippedIssues(),be(e.getState()),await e.checkpoint()}async function mT(t){let e=se(),n=await Pp(e),o=n.getSkippedIssues().length;n.unskipIssue(t.issueUrl);let r=o!==n.getSkippedIssues().length;return be(n.getState()),await n.checkpoint(),{removed:r}}function gT(t){let e=t.match(/github\.com\/([^/]+\/[^/]+)\/issues\/(\d+)/);if(e)return{repo:e[1],number:parseInt(e[2],10),title:""}}var Ln=y(()=>{"use strict";Ot();Nr();ue()});function wb(t,e,n){if(!e.test(t))throw new ve(`Invalid ${n} URL: ${t}. Expected format: https://github.com/owner/repo/issues/123`)}function kb(t){if(t.length>yb)throw new ve(`URL exceeds maximum length of ${yb} characters`);return t}var $b,yb,Sb=y(()=>{"use strict";we();$b=/^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/\d+$/,yb=2048});var Tb={};ce(Tb,{runVet:()=>fT});async function fT(t){kb(t.issueUrl),wb(t.issueUrl,$b,"issue");let e=Ft(),o=await(t.state?await je({githubToken:e,persistence:"provided",initialState:t.state}):await je({githubToken:e})).vetIssue(t.issueUrl);return{issue:{repo:o.issue.repo,number:o.issue.number,title:o.issue.title,url:o.issue.url,labels:o.issue.labels},recommendation:o.recommendation,reasonsToApprove:o.reasonsToApprove,reasonsToSkip:o.reasonsToSkip,projectHealth:o.projectHealth,vettingResult:o.vettingResult}}var Eb=y(()=>{"use strict";Nr();ue();Sb()});var Qp=Pe(Yp(),1),{program:ST,createCommand:TT,createArgument:ET,createOption:xT,CommanderError:PT,InvalidArgumentError:IT,InvalidOptionArgumentError:OT,Command:em,Argument:RT,Option:AT,Help:zT}=Qp.default;he();ue();function ke(t){let e={success:!0,data:t,timestamp:new Date().toISOString()};return JSON.stringify(e,null,2)}function um(t,e){let n={success:!1,error:t,errorCode:e,timestamp:new Date().toISOString()};return JSON.stringify(n,null,2)}we();Ot();It();function Ce(t,e){e.json?console.log(um(U(t),nm(t))):console.error("Error:",U(t)),process.exit(1)}var qe=new em;qe.name("oss-scout").description("Find open source issues personalized to your contribution history").version(Rs()).option("--debug","Enable debug output");qe.hook("preAction",(t,e)=>{qe.opts().debug&&rm()});qe.command("setup").description("Interactive first-run configuration").option("--json","Output as JSON").action(async t=>{try{let{runSetup:e}=await Promise.resolve().then(()=>(sv(),iv)),n=await e(),o=se();o.preferences=n,be(o),t.json&&console.log(ke(n))}catch(e){Ce(e,t)}});qe.command("bootstrap").description("Import starred repos and PR history from GitHub").option("--json","Output as JSON").action(async t=>{try{let{bootstrapScout:e}=await Promise.resolve().then(()=>(b_(),__)),{createScout:n}=await Promise.resolve().then(()=>(Nr(),mb)),{requireGitHubToken:o}=await Promise.resolve().then(()=>(ue(),cm)),r=o(),i=se(),s=await n({githubToken:r,persistence:"provided",initialState:i}),a=await e(s,r);be(s.getState()),t.json?console.log(ke(a)):a.skippedDueToRateLimit?console.log("Skipped: GitHub API rate limit too low. Try again later."):(console.log(`Imported ${a.mergedPRCount} merged PRs, ${a.closedPRCount} closed PRs, ${a.starredRepoCount} starred repos`),console.log(`Scored ${a.reposScoredCount} repositories`))}catch(e){Ce(e,t)}});qe.command("search [count]").description("Search for contributable issues using multi-strategy discovery").option("--json","Output as JSON").option("--strategy <strategies>",`Search strategies (${Rn.join(",")},all)`,"all").action(async(t,e)=>{try{ev()||console.log("\u{1F4A1} Run `oss-scout setup` to configure your preferences for personalized search results.\n");let{runSearch:n}=await Promise.resolve().then(()=>(fb(),gb)),o=t?parseInt(t,10):10;(isNaN(o)||o<1)&&(console.error("Error: count must be a positive integer"),process.exit(1));let r=se();r.mergedPRs.length===0&&r.starredRepos.length===0&&r.preferences.githubUsername&&console.log("Run `oss-scout bootstrap` to import your starred repos and PR history for better results.\n");let i=(e.strategy??"all").split(",").map(c=>c.trim()).filter(Boolean),s=[];for(let c of i){let u=On.safeParse(c);if(!u.success){let l=[...Rn,"all"].join(", ");console.error('Error: unknown strategy "'+c+'". Valid strategies: '+l),process.exit(1)}s.push(u.data)}let a=await n({maxResults:o,state:r,strategies:s});if(e.json)console.log(ke(a));else{console.log(`
95
95
  Found ${a.candidates.length} issue candidates:
package/dist/scout.d.ts CHANGED
@@ -63,6 +63,14 @@ export declare class OssScout implements ScoutStateReader {
63
63
  getStarredRepos(): string[];
64
64
  getProjectCategories(): ProjectCategory[];
65
65
  getRepoScore(repo: string): number | null;
66
+ /**
67
+ * Optional SLM pre-triage config read from preferences (oss-autopilot#1122).
68
+ * Empty `model` disables the call; the vetter treats it as a no-op.
69
+ */
70
+ getSLMTriageConfig(): {
71
+ model: string;
72
+ host: string;
73
+ };
66
74
  /** Get current preferences (read-only). */
67
75
  getPreferences(): Readonly<ScoutPreferences>;
68
76
  /** Get repo score record for a specific repository. */
package/dist/scout.js CHANGED
@@ -258,6 +258,16 @@ export class OssScout {
258
258
  const score = this.state.repoScores[repo];
259
259
  return score ? score.score : null;
260
260
  }
261
+ /**
262
+ * Optional SLM pre-triage config read from preferences (oss-autopilot#1122).
263
+ * Empty `model` disables the call; the vetter treats it as a no-op.
264
+ */
265
+ getSLMTriageConfig() {
266
+ return {
267
+ model: this.state.preferences.slmTriageModel ?? "",
268
+ host: this.state.preferences.slmTriageHost ?? "",
269
+ };
270
+ }
261
271
  /** Get current preferences (read-only). */
262
272
  getPreferences() {
263
273
  return this.state.preferences;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-scout/core",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Personalized GitHub issue finder with multi-strategy search, deep vetting, and viability scoring — CLI, library, MCP server, and Claude Code plugin",
5
5
  "type": "module",
6
6
  "bin": {