oh-my-pr 2.11.1 → 2.12.2

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/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![Node.js 22+](https://img.shields.io/badge/Node.js-22%2B-green.svg)](https://nodejs.org/)
14
14
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
15
15
 
16
- Oh-my-pr babysits your PRs from your local machine, reads all PR comments and CI/CD logs, and gets your PR ready for merge to main. It uses your local Claude Code or Codex to address any issues identified in the PR or CI/CD pipeline and to ensure that any documentation is up to date. You can push a PR, walk away, and come back to a clean PR ready to be merged.
16
+ Oh-my-pr babysits your PRs from your local machine, reads all PR comments and CI/CD logs, resolves conflicts, and gets your PR ready for merge to main. It uses your local Claude Code or Codex to address any issues identified in the PR or CI/CD pipeline and to ensure that any documentation is up to date. You can push a PR, walk away, and come back to a clean PR ready to be merged.
17
17
 
18
18
  <img width="1365" height="686" alt="Code Factory dashboard" src="https://github.com/user-attachments/assets/66dfa082-c732-4989-8b05-f19aa550acb5" />
19
19
 
package/dist/index.cjs CHANGED
@@ -920,7 +920,7 @@ ${Bt}`:Bt}function Ni(t,e){return[t,...e].join(" ")}function lr(t){return t.stde
920
920
  `)).join(`
921
921
  `);return["Respond with ONLY valid JSON and nothing else.","Schema:",'{"shouldRelease":boolean,"reason":string,"bump":"patch"|"minor"|"major"|null,"title":string|null,"notes":string|null}',"","You are deciding whether a merged set of pull requests should be published as a GitHub release.","Be conservative. Release only when the merged changes are meaningful enough for users or operators to care about.","",`Repository: ${t.repo}`,`Release branch: ${t.baseBranch}`,`Latest release tag: ${t.latestTag??"none"}`,"",`Trigger PR: #${t.triggerPr.number} "${t.triggerPr.title}"`,`Trigger merged at: ${t.triggerPr.mergedAt}`,`Trigger merge SHA: ${t.triggerPr.mergeSha}`,"","Merged PRs included in this candidate release:",e||"(none)","","Decision rules:","- shouldRelease=true only when the merged changes are worth announcing as a GitHub release.","- bump must be null when shouldRelease=false.","- bump must be one of patch, minor, or major when shouldRelease=true.","- Use patch for fixes or small improvements, minor for additive user-facing features, and major for breaking changes.","- title should be short and release-note ready when shouldRelease=true, else null.","- notes should be GitHub-release Markdown focused on user-visible/operator-visible impact when shouldRelease=true, else null.","- notes must have exactly TWO sections in this order:"," 1. '## Why This Matters' \u2014 a user-friendly, value-driven summary at the top that explains how the release makes users' lives better."," 2. '## Detailed Changes' \u2014 a line-by-line changelog of the included changes in plain English, more detailed than a headline but not deeply technical.","- notes should mention the key merged PRs in plain language, not internal process commentary.","- Prefer user outcomes, workflow improvements, and visible behavior over implementation details."].join(`
922
922
  `)}function Yv(t){let e=bC(t.trim());if(!e||typeof e!="object")throw new Error(`Could not parse release evaluation JSON: ${t.slice(0,500)}`);let a=e;if(typeof a.shouldRelease!="boolean")throw new Error("Release evaluation missing boolean 'shouldRelease'");if(typeof a.reason!="string"||a.reason.trim().length===0)throw new Error("Release evaluation missing string 'reason'");if(!a.shouldRelease)return{shouldRelease:!1,reason:a.reason.trim(),bump:null,title:null,notes:null};if(a.bump!=="patch"&&a.bump!=="minor"&&a.bump!=="major")throw new Error("Release evaluation returned invalid 'bump'");if(typeof a.title!="string"||a.title.trim().length===0)throw new Error("Release evaluation missing non-empty 'title'");if(typeof a.notes!="string"||a.notes.trim().length===0)throw new Error("Release evaluation missing non-empty 'notes'");return{shouldRelease:!0,reason:a.reason.trim(),bump:a.bump,title:a.title.trim(),notes:a.notes.trim()}}function bC(t){try{return JSON.parse(t)}catch{}let e=t.indexOf("{"),a=t.lastIndexOf("}");if(e===-1||a===-1||a<=e)return null;try{return JSON.parse(t.slice(e,a+1))}catch{return null}}var Li=class{storage;github;evaluateRelease;scheduleBackgroundJob;inProgress=new Set;repoLocks=new Map;constructor(e,a){this.storage=e,this.github=a.github,this.evaluateRelease=a.evaluateRelease??eb,this.scheduleBackgroundJob=a.scheduleBackgroundJob}getActiveRunCount(){return this.inProgress.size}async waitForIdle(e=12e4){let a=Date.now();for(;this.inProgress.size>0;){if(Date.now()-a>=e)return!1;await SC(50)}return!0}async enqueueMergedPullReleaseEvaluation(e){let a=await this.storage.getReleaseRunByTrigger(e.repo,e.triggerPrNumber,e.triggerMergeSha);if(a)return RC(a.status)||this.scheduleProcessing(a.id),a;let r=await this.storage.createReleaseRun({repo:e.repo,baseBranch:e.baseBranch,triggerPrNumber:e.triggerPrNumber,triggerPrTitle:e.triggerPrTitle,triggerPrUrl:e.triggerPrUrl,triggerMergeSha:e.triggerMergeSha,triggerMergedAt:e.triggerMergedAt,status:"detected",decisionReason:null,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:[],targetSha:e.triggerMergeSha,githubReleaseId:null,githubReleaseUrl:null,error:null,completedAt:null});return this.scheduleProcessing(r.id),r}async retryReleaseRun(e){if(!await this.storage.getReleaseRun(e))return;let r=await this.storage.updateReleaseRun(e,{status:"detected",error:null,completedAt:null});if(r)return this.scheduleProcessing(e),r}async processReleaseRun(e){let a=await this.storage.getReleaseRun(e);if(a)return this.withRepoLock(a.repo,async()=>{if(this.inProgress.has(e))return this.storage.getReleaseRun(e);this.inProgress.add(e);try{let r=await this.storage.getReleaseRun(e);if(!r)return;if(r.status==="published"||r.status==="skipped")return r;let n=Me(r.repo);if(!n)return this.failRun(e,`Invalid repository slug: ${r.repo}`);let i=await this.storage.getConfig();if(!i.autoCreateReleases)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:"Automatic release creation is disabled in settings",completedAt:new Date().toISOString()})??void 0;let s=await this.storage.getRepoSettings(r.repo);if(s&&!s.autoCreateReleases)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:`Automatic release creation is disabled for ${r.repo}`,completedAt:new Date().toISOString()})??void 0;await this.storage.updateReleaseRun(e,{status:"evaluating",error:null,completedAt:null});let o=await this.github.buildOctokit(i),u=await this.github.findLatestSemverReleaseTag(o,n),c=yC(r),l=await this.loadIncludedPulls(o,n,r,c),p=l.map(xC),d=await this.evaluateRelease({preferredAgent:i.codingAgent,repo:r.repo,baseBranch:r.baseBranch,latestTag:u,triggerPr:c,includedPulls:l});if(!d.shouldRelease)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:d.reason,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:p,targetSha:r.triggerMergeSha,completedAt:new Date().toISOString()})??void 0;if(!d.bump)throw new Error("Release evaluation approved publishing but did not provide a semver bump");let g=this.github.bumpReleaseTag(u,d.bump),v=wC(d,g),b=d.notes??`Release ${g}`;await this.storage.updateReleaseRun(e,{status:"proposed",decisionReason:d.reason,recommendedBump:d.bump,proposedVersion:g,releaseTitle:v,releaseNotes:b,includedPrs:p,targetSha:r.triggerMergeSha});let h=this.github.findReleaseByTag?await this.github.findReleaseByTag(o,n,g):null;if(h)return await this.storage.updateReleaseRun(e,{status:"published",githubReleaseId:h.id,githubReleaseUrl:h.url,completedAt:new Date().toISOString()})??void 0;await this.storage.updateReleaseRun(e,{status:"publishing"});let E=await this.github.createGitHubRelease(o,n,{tagName:g,targetCommitish:r.triggerMergeSha,name:v,body:b});return await this.storage.updateReleaseRun(e,{status:"published",githubReleaseId:E.id,githubReleaseUrl:E.url,completedAt:new Date().toISOString()})??void 0}catch(r){return this.failRun(e,_C(r))}finally{this.inProgress.delete(e)}})}async loadIncludedPulls(e,a,r,n){if(!this.github.listMergedPullsForReleaseCandidate)return[n];let i=await this.github.listMergedPullsForReleaseCandidate(e,a,{baseBranch:r.baseBranch,untilMergedAt:r.triggerMergedAt,triggerPr:n}),s=new Map;for(let o of i)s.set(o.mergeSha||`${o.repo}#${o.number}`,o);return s.has(n.mergeSha)||s.set(n.mergeSha,n),Array.from(s.values()).sort((o,u)=>o.mergedAt.localeCompare(u.mergedAt))}async failRun(e,a){return this.storage.updateReleaseRun(e,{status:"error",error:a,completedAt:new Date().toISOString()})}async withRepoLock(e,a){let r=this.repoLocks.get(e)??Promise.resolve(),n,i=new Promise(o=>{n=()=>o()}),s=r.then(()=>i);this.repoLocks.set(e,s),await r;try{return await a()}finally{n?.(),this.repoLocks.get(e)===s&&this.repoLocks.delete(e)}}scheduleProcessing(e){if(this.scheduleBackgroundJob){this.scheduleBackgroundJob("process_release_run",e,nt("process_release_run",e),{releaseRunId:e}).catch(a=>{console.error(`Failed to schedule release run ${e}:`,a)});return}this.processReleaseRun(e).catch(()=>{})}};function yC(t){return{number:t.triggerPrNumber,title:t.triggerPrTitle,url:t.triggerPrUrl,author:"unknown",repo:t.repo,mergedAt:t.triggerMergedAt,mergeSha:t.triggerMergeSha}}function xC(t){return{number:t.number,title:t.title,url:t.url,author:t.author,mergedAt:t.mergedAt,mergeSha:t.mergeSha}}function wC(t,e){let a=t.title?.trim();return a?a.startsWith(e)?a:`${e} - ${a}`:e}function _C(t){return(t instanceof Error?t.message:String(t)).trim().slice(0,2e3)}function RC(t){return t==="skipped"||t==="published"}function SC(t){return new Promise(e=>setTimeout(e,t))}var kC=["fix_submitted","escalated"],EC={monitoring:["failed","escalated"],failed:["fixing","escalated"],fixing:["fix_submitted","escalated"],fix_submitted:[],escalated:[]},ji=class{constructor(e,a=()=>new Date){this.storage=e;this.clock=a}async createSession(e){return this.storage.createDeploymentHealingSession({...e,deploymentId:null,deploymentLog:null,fixBranch:null,fixPrNumber:null,fixPrUrl:null,state:"monitoring",error:null,completedAt:null})}async ensureSession(e){let a=await this.storage.getDeploymentHealingSessionByRepoAndMergeSha(e.repo,e.mergeSha);return a||this.createSession(e)}async transitionTo(e,a,r={}){let n=await this.storage.getDeploymentHealingSession(e);if(!n)throw new Error(`Deployment healing session not found: ${e}`);if(n.state===a){let o=await this.storage.updateDeploymentHealingSession(e,r);if(!o)throw new Error(`Deployment healing session not found: ${e}`);return o}if(!EC[n.state].includes(a))throw new Error(`Illegal deployment healing transition: ${n.state} -> ${a}`);let i={...r,state:a};kC.includes(a)&&(i.completedAt=r.completedAt??this.clock().toISOString());let s=await this.storage.updateDeploymentHealingSession(e,i);if(!s)throw new Error(`Deployment healing session not found: ${e}`);return s}};var xe=class extends Error{statusCode;constructor(e,a){super(a),this.name="AppRuntimeError",this.statusCode=e}};function tb(t){return t instanceof Error?t.message:String(t)}function ye(t,e){if(t===void 0)throw new xe(404,e);return t}function rb(t={}){let e=t.storage??xh(),a=new ab.EventEmitter,r=t.backgroundJobQueue??new Pi(e),n,i=async(...f)=>{let w=await r.enqueue(...f);return n.wake(),w},s=t.deploymentHealingManager??new ji(e),o=t.releaseManager??new Li(e,{github:{buildOctokit:rt,findLatestSemverReleaseTag:Uh,bumpReleaseTag:$h,listMergedPullsForReleaseCandidate:async(f,w,x)=>{let S=await ev(f,w,{baseRef:x.baseBranch}),A=Date.parse(x.untilMergedAt);return S.filter(F=>!Number.isFinite(A)||Date.parse(F.mergedAt)<=A).map(F=>({number:F.number,title:F.title,url:F.url,author:F.author,repo:F.repo,mergedAt:F.mergedAt,mergeSha:F.mergeCommitSha??`${F.repo}#${F.number}`}))},findReleaseByTag:async(f,w,x)=>{let A=(await wi(f,w)).find(F=>!F.draft&&F.tagName===x);return A?{id:A.id,url:A.htmlUrl,tagName:A.tagName,name:A.name}:null},createGitHubRelease:async(f,w,x)=>{let S=await Bh(f,w,{tagName:x.tagName,targetCommitish:x.targetCommitish,name:x.name,body:x.body});return{id:S.id,url:S.htmlUrl,tagName:S.tagName,name:S.name}}},scheduleBackgroundJob:i}),u=t.babysitter??new Fi(e,void 0,void 0,o,i,s);n=t.backgroundJobDispatcher??new Di({storage:e,queue:r,handlers:Kv({storage:e,babysitter:u,releaseManager:o,deploymentHealingManager:s})});let c=null,l=0,p=t.watcherScheduler??Qv(async()=>{await i("sync_watched_repos","runtime:1",nt("sync_watched_repos","runtime:1"))},f=>{console.error("Repository babysitter watcher failed",f)}),d=p.run,g=t.startBackgroundServices??!0,v=t.startWatcher??g,b=!1,h=()=>{a.emit("change")},E=async()=>({...await e.getRuntimeState(),activeRuns:n.getActiveRunCount()}),L=async f=>{let[w,x,S]=await Promise.all([n.waitForIdle(f),u.waitForIdle(f),o.waitForIdle(f)]);return w&&x&&S},T=async()=>{let f=await e.getConfig(),w=Math.max(1e4,f.pollIntervalMs||12e4);c&&l===w||(c&&(clearInterval(c),c=null),l=w,c=setInterval(()=>{d()},w))},P=async(f,w)=>{await i("babysit_pr",f,nt("babysit_pr",f),{preferredAgent:w})},N={async start(){b||(b=!0,g&&await n.start(),v&&(await T(),u.resumeInterruptedRuns(),d()))},stop(){b=!1,n.stop(),c&&(clearInterval(c),c=null)},subscribe(f){return a.on("change",f),()=>{a.off("change",f)}},getRuntimeSnapshot:E,async setDrainMode(f){let w=await e.updateRuntimeState({drainMode:f.enabled,drainRequestedAt:f.enabled?new Date().toISOString():null,drainReason:f.enabled?f.reason??null:null});if(f.enabled&&f.waitForIdle){let S=await L(f.timeoutMs??12e4),A=await E();return h(),{...w,...A,drained:S}}let x=await E();return h(),{...w,...x}},async listRepos(){let f=await e.getConfig(),w=await e.getPRs();return Array.from(new Set([...f.watchedRepos,...w.map(x=>x.repo)])).sort((x,S)=>x.localeCompare(S))},async listRepoSettings(){let[f,w]=await Promise.all([e.listRepoSettings(),e.getPRs()]),x=new Map(f.map(S=>[S.repo,S]));for(let S of w)x.has(S.repo)||x.set(S.repo,{repo:S.repo,autoCreateReleases:!0});return Array.from(x.values()).sort((S,A)=>S.repo.localeCompare(A.repo))},async addRepo(f){let w=Me(f);if(!w)throw new xe(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let x=$t(w),S=await e.getConfig();return S.watchedRepos.includes(x)||await e.updateConfig({watchedRepos:[...S.watchedRepos,x].sort((A,F)=>A.localeCompare(F))}),d(),h(),{repo:x}},async updateRepoSettings(f,w){let x=Me(f);if(!x)throw new xe(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let S=$t(x),A=await e.updateRepoSettings(S,w);return h(),A},async syncRepos(){if((await e.getRuntimeState()).drainMode)throw new xe(409,"Drain mode is enabled. Sync-triggered runs are blocked until drain mode is disabled.");return await p.runAndReportErrors(),h(),{ok:!0}},async listPRs(f="active"){return f==="archived"?e.getArchivedPRs():e.getPRs()},async getPR(f){return await e.getPR(f)??null},async addPR(f){let w;try{({url:w}=lh.parse({url:f}))}catch(Y){throw Y instanceof m.ZodError?new xe(400,Y.errors[0]?.message??"Invalid PR URL"):Y}let x=bi(w);if(!x)throw new xe(400,"Invalid GitHub PR URL. Expected: https://github.com/owner/repo/pull/123");let S=`${x.owner}/${x.repo}`,A=await e.getPRByRepoAndNumber(S,x.number);if(A)return A;let F=await e.getConfig(),R=await rt(F),U=await xi(R,x),z=await e.addPR({number:x.number,title:U.title,repo:S,branch:U.branch,author:U.author,url:U.url,status:"watching",feedbackItems:[],accepted:0,rejected:0,flagged:0,testsPassed:null,lintPassed:null,lastChecked:null});return await e.addLog(z.id,"info",`Registered PR #${x.number} from ${S}`),await e.addLog(z.id,"info",`Repository ${S} added to auto-babysit watch list`),F.watchedRepos.includes(S)||await e.updateConfig({watchedRepos:[...F.watchedRepos,S].sort((Y,Z)=>Y.localeCompare(Z))}),await P(z.id,F.codingAgent),h(),z},async removePR(f){if(!await e.removePR(f))throw new xe(404,"PR not found");return h(),{ok:!0}},async setPRWatchEnabled(f,w){let x=ye(await e.getPR(f),"PR not found"),S=await e.updatePR(x.id,{watchEnabled:w}),A=ye(S,"PR not found");return x.watchEnabled!==w&&(await e.addLog(x.id,"info",w?"Background watch resumed":"Background watch paused"),w&&d()),h(),A},async setWatchEnabled(f,w){return N.setPRWatchEnabled(f,w)},async fetchPRFeedback(f){let w=ye(await e.getPR(f),"PR not found");await e.updatePR(w.id,{status:"processing",lastChecked:new Date().toISOString()}),await e.addLog(w.id,"info","Syncing GitHub comments/reviews...");try{let x=await u.syncFeedbackForPR(w.id);return h(),x}catch(x){let S=tb(x);throw await e.updatePR(w.id,{status:"error",lastChecked:new Date().toISOString()}),await e.addLog(w.id,"error",`Fetch failed: ${S}`),x}},async triagePR(f){let w=ye(await e.getPR(f),"PR not found");await e.updatePR(w.id,{status:"processing"}),await e.addLog(w.id,"info","Triaging feedback...");let x=w.feedbackItems.map(U=>{if(U.decision)return U;let z=U.body.toLowerCase();return z.includes("lgtm")||z.includes("looks good")?xa(U,!1,"Acknowledgement, no code change requested"):z.includes("please")||z.includes("should")||z.includes("fix")||z.includes("error")||z.includes("fail")?{...xa(U,!0,"Likely actionable request"),action:U.body}:kv(U,"Unclear actionability, flagged for manual review")}),S=x.filter(U=>U.decision==="accept").length,A=x.filter(U=>U.decision==="reject").length,F=x.filter(U=>U.decision==="flag").length,R=await e.updatePR(w.id,{feedbackItems:x,accepted:S,rejected:A,flagged:F,status:"watching"});return await e.addLog(w.id,"info",`Triage complete: ${S} accept, ${A} reject, ${F} flag`),h(),ye(R,"PR not found")},async applyPR(f){let w=ye(await e.getPR(f),"PR not found");if((await e.getRuntimeState()).drainMode)throw new xe(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let S=await e.getConfig();await e.updatePR(w.id,{status:"processing"}),await e.addLog(w.id,"info",`Launching autonomous babysitter run using ${S.codingAgent}`),await P(w.id,S.codingAgent);let A=await e.getPR(w.id);return h(),ye(A,"PR disappeared after apply run")},async babysitPR(f){let w=ye(await e.getPR(f),"PR not found");if((await e.getRuntimeState()).drainMode)throw new xe(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let S=await e.getConfig();await e.addLog(w.id,"info",`Manual babysitter trigger using ${S.codingAgent}`),await P(w.id,S.codingAgent);let A=await e.getPR(w.id);return h(),ye(A,"PR disappeared after babysit run")},async queueBabysit(f){return N.babysitPR(f)},async setFeedbackDecision(f,w,x){let S=ye(await e.getPR(f),"PR not found"),A=await Bv({storage:e,pr:S,feedbackId:w,decision:x});return h(),ye(A,"PR not found")},async retryFeedback(f,w){let x=await u.retryFeedbackItem(f,w);if(x.kind==="pr_not_found")throw new xe(404,"PR not found");if(x.kind==="feedback_not_found")throw new xe(404,"Feedback item not found");if(x.kind==="feedback_not_retryable")throw new xe(400,"Only failed or warning items can be retried");await e.addLog(f,"info",`Feedback item ${w} queued for retry`);let S=await e.getConfig();return await P(f,S.codingAgent),h(),x.updated},async listPRQuestions(f){return ye(await e.getPR(f),"PR not found"),e.getQuestions(f)},async askQuestion(f,w){ye(await e.getPR(f),"PR not found");let x;try{x=dh.parse({question:w})}catch(A){throw A instanceof m.ZodError?new xe(400,A.errors[0]?.message??"Invalid question"):A}let S=await e.addQuestion(f,x.question);try{await i("answer_pr_question",S.id,nt("answer_pr_question",S.id),{prId:f})}catch(A){let F=tb(A);throw await e.updateQuestion(S.id,{status:"error",error:F.trim().slice(0,2e3)}),A}return h(),S},async listLogs(f){return e.getLogs(f)},async getOnboardingStatus(){let f=await e.getConfig();return Lh(f,f.watchedRepos)},async installReviewWorkflow(f,w){let x=await e.getConfig();return jh(x,f,w)},async listHealingSessions(){return e.listHealingSessions()},async getHealingSession(f){return ye(await e.getHealingSession(f),"Healing session not found")},async listDeploymentHealingSessions(f){return e.listDeploymentHealingSessions(f?{repo:f}:void 0)},async getDeploymentHealingSession(f){return ye(await e.getDeploymentHealingSession(f),"Deployment healing session not found")},async getConfig(){return e.getConfig()},async updateConfig(f){let w=await e.updateConfig(f);return v&&b&&await T(),h(),w},async listSocialChangelogs(){return e.getSocialChangelogs()},async getSocialChangelog(f){return ye(await e.getSocialChangelog(f),"Changelog not found")},async listReleaseRuns(){return e.listReleaseRuns()},async getReleaseRun(f){return ye(await e.getReleaseRun(f),"Release run not found")},async retryReleaseRun(f){let w=await o.retryReleaseRun(f);if(!w)throw new xe(404,"Release run not found");return h(),w}};return N}function nb(t){return t instanceof xe}function TC(t){return t instanceof Error?t.message:String(t)}function Q(t,e){if(e instanceof m.ZodError){t.status(400).json({error:e.errors[0]?.message??"Invalid request"});return}if(e instanceof ce){t.status(e.statusCode).json({error:e.message});return}if(nb(e)){t.status(e.statusCode).json({error:e.message});return}t.status(500).json({error:TC(e)})}function ib(t){return{...t,githubToken:t.githubToken?`***${t.githubToken.slice(-4)}`:""}}async function sb(t,e,a={}){let r=a.runtime??rb(a);return await r.start(),t.on("close",()=>{r.stop()}),e.get("/api/runtime",async(n,i)=>{i.json(await r.getRuntimeSnapshot())}),e.post("/api/runtime/drain",async(n,i)=>{try{let s=m.object({enabled:m.boolean(),reason:m.string().optional(),waitForIdle:m.boolean().optional(),timeoutMs:m.number().int().positive().max(6e5).optional()}).parse(n.body),o=await r.setDrainMode(s);if(s.enabled&&s.waitForIdle&&o.drained===!1)return i.status(202).json(o);i.json(o)}catch(s){Q(i,s)}}),e.get("/api/repos",async(n,i)=>{i.json(await r.listRepos())}),e.get("/api/repos/settings",async(n,i)=>{i.json(await r.listRepoSettings())}),e.post("/api/repos",async(n,i)=>{try{let{repo:s}=m.object({repo:m.string().min(1)}).parse(n.body);i.status(201).json(await r.addRepo(s))}catch(s){Q(i,s)}}),e.patch("/api/repos/settings",async(n,i)=>{try{let{repo:s,autoCreateReleases:o}=m.object({repo:m.string().min(1),autoCreateReleases:m.boolean()}).parse(n.body);i.json(await r.updateRepoSettings(s,{autoCreateReleases:o}))}catch(s){Q(i,s)}}),e.post("/api/repos/sync",async(n,i)=>{try{i.json(await r.syncRepos())}catch(s){Q(i,s)}}),e.get("/api/prs",async(n,i)=>{i.json(await r.listPRs("active"))}),e.get("/api/prs/archived",async(n,i)=>{i.json(await r.listPRs("archived"))}),e.get("/api/prs/:id",async(n,i)=>{let s=await r.getPR(n.params.id);if(!s)return i.status(404).json({error:"PR not found"});i.json(s)}),e.post("/api/prs",async(n,i)=>{try{i.status(201).json(await r.addPR(n.body?.url))}catch(s){Q(i,s)}}),e.delete("/api/prs/:id",async(n,i)=>{try{i.json(await r.removePR(n.params.id))}catch(s){Q(i,s)}}),e.patch("/api/prs/:id/watch",async(n,i)=>{try{let{enabled:s}=m.object({enabled:m.boolean()}).parse(n.body);i.json(await r.setPRWatchEnabled(n.params.id,s))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/fetch",async(n,i)=>{try{i.json(await r.fetchPRFeedback(n.params.id))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/triage",async(n,i)=>{try{i.json(await r.triagePR(n.params.id))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/apply",async(n,i)=>{try{i.json(await r.applyPR(n.params.id))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/babysit",async(n,i)=>{try{i.json(await r.babysitPR(n.params.id))}catch(s){Q(i,s)}}),e.patch("/api/prs/:id/feedback/:feedbackId",async(n,i)=>{try{let{decision:s}=m.object({decision:m.enum(["accept","reject","flag"])}).parse(n.body);i.json(await r.setFeedbackDecision(n.params.id,n.params.feedbackId,s))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/feedback/:feedbackId/retry",async(n,i)=>{try{i.json(await r.retryFeedback(n.params.id,n.params.feedbackId))}catch(s){Q(i,s)}}),e.get("/api/prs/:id/questions",async(n,i)=>{try{i.json(await r.listPRQuestions(n.params.id))}catch(s){Q(i,s)}}),e.post("/api/prs/:id/questions",async(n,i)=>{try{i.status(201).json(await r.askQuestion(n.params.id,n.body?.question))}catch(s){Q(i,s)}}),e.get("/api/logs",async(n,i)=>{let s=typeof n.query.prId=="string"?n.query.prId:void 0;i.json(await r.listLogs(s))}),e.get("/api/onboarding/status",async(n,i)=>{try{i.json(await r.getOnboardingStatus())}catch(s){Q(i,s)}}),e.post("/api/onboarding/install-review",async(n,i)=>{try{let{repo:s,tool:o}=m.object({repo:m.string().min(1),tool:m.enum(["claude","codex"])}).parse(n.body);i.json(await r.installReviewWorkflow(s,o))}catch(s){Q(i,s)}}),e.get("/api/healing-sessions",async(n,i)=>{try{i.json(await r.listHealingSessions())}catch(s){Q(i,s)}}),e.get("/api/healing-sessions/:id",async(n,i)=>{try{i.json(await r.getHealingSession(n.params.id))}catch(s){Q(i,s)}}),e.get("/api/deployment-healing-sessions",async(n,i)=>{try{let s=typeof n.query.repo=="string"?n.query.repo:void 0;i.json(await r.listDeploymentHealingSessions(s))}catch(s){Q(i,s)}}),e.get("/api/deployment-healing-sessions/:id",async(n,i)=>{try{i.json(await r.getDeploymentHealingSession(n.params.id))}catch(s){Q(i,s)}}),e.get("/api/config",async(n,i)=>{i.json(ib(await r.getConfig()))}),e.get("/api/changelogs",async(n,i)=>{try{i.json(await r.listSocialChangelogs())}catch(s){Q(i,s)}}),e.get("/api/changelogs/:id",async(n,i)=>{try{i.json(await r.getSocialChangelog(n.params.id))}catch(s){Q(i,s)}}),e.get("/api/releases",async(n,i)=>{try{i.json(await r.listReleaseRuns())}catch(s){Q(i,s)}}),e.get("/api/releases/:id",async(n,i)=>{try{i.json(await r.getReleaseRun(n.params.id))}catch(s){Q(i,s)}}),e.post("/api/releases/:id/retry",async(n,i)=>{try{i.json(await r.retryReleaseRun(n.params.id))}catch(s){Q(i,s)}}),e.patch("/api/config",async(n,i)=>{try{let s=fi.partial().parse(n.body);i.json(ib(await r.updateConfig(s)))}catch(s){Q(i,s)}}),t}var ob=Ie(rc(),1),cb=Ie(require("fs"),1),nu=Ie(require("path"),1);function ub(t){let e=nu.default.resolve(__dirname,"public");if(!cb.default.existsSync(e))throw new Error(`Could not find the build directory: ${e}, make sure to build the client first`);t.use(ob.default.static(e)),t.use("/{*path}",(a,r)=>{r.sendFile(nu.default.resolve(e,"index.html"))})}var AC=new Set(["127.0.0.1","::1","::ffff:127.0.0.1","localhost"]);function CC(t){if(!t)return!1;let e=t.replace(/^\[|\]$/g,"");return AC.has(e)?!0:e.startsWith("::ffff:")?e.slice(7).startsWith("127."):!!e.startsWith("127.")}function lb(t,e,a){let r=t.ip??t.socket?.remoteAddress;if(!CC(r)){e.status(403).json({error:"Forbidden",message:"Code Factory only accepts connections from the local machine. External access is not permitted."});return}a()}var db=require("http"),mb=Ie(require("open"),1),wa=(0,Hi.default)(),pb=(0,db.createServer)(wa);wa.use(Hi.default.json({verify:(t,e,a)=>{t.rawBody=a}}));wa.use(Hi.default.urlencoded({extended:!1}));wa.use("/api",lb);function fb(t,e="express"){let a=new Date().toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0});console.log(`${a} [${e}] ${t}`)}(async()=>{await sb(pb,wa),wa.use((e,a,r,n)=>{let i=e,s=i.status||i.statusCode||500,o=i.message||"Internal Server Error";return console.error("Internal Server Error:",e),r.headersSent?n(e):r.status(s).json({message:o})}),ub(wa);let t=parseInt(process.env.PORT||"5001",10);pb.listen({port:t,host:"0.0.0.0"},()=>{let e=`http://localhost:${t}`;console.log(`
923
- oh-my-pr v2.11.1
923
+ oh-my-pr v2.12.2
924
924
  Dashboard: ${e}
925
925
  `),process.env.TAURI_DEV||(0,mb.default)(e).catch(r=>{fb(`Could not open browser automatically: ${r.message}`)})})})();0&&(module.exports={log});
926
926
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-pr",
3
- "version": "2.11.1",
3
+ "version": "2.12.2",
4
4
  "description": "Autonomous GitHub PR babysitter that watches repos, triages review feedback, and dispatches AI agents to fix code locally",
5
5
  "keywords": [
6
6
  "github",