@maind-dev/cli 0.132.1 → 0.132.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/build/index.js +2 -2
- package/build/meta.json +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -243,7 +243,7 @@ maind: and re-run \`maind launch claude\`. (Or launch without routing: \`maind l
|
|
|
243
243
|
`,stderr:""}}let n=["maind: dry-run \u2014 nothing installed, written, or spawned.",`maind: config ${e.configPath} (${e.configSource})`,`maind: switchyard detected: ${e.switchyardDetected?"yes":"no"}`,`maind: would run: ${e.argv.join(" ")}`].join(`
|
|
244
244
|
`);return{stdout:e.toml??`(using existing config: ${e.configPath})
|
|
245
245
|
`,stderr:n+`
|
|
246
|
-
`}}a(rx,"renderDryRun");import{createRequire as uL}from"node:module";function dL(){return"0.132.
|
|
246
|
+
`}}a(rx,"renderDryRun");import{createRequire as uL}from"node:module";function dL(){return"0.132.2";try{return uL(import.meta.url)("../package.json").version}catch{return"0.0.0-unknown"}}a(dL,"resolveVersion");var pe=dL();var kL=5e3;function it(e){process.stderr.write(`maind: ${e}
|
|
247
247
|
`)}a(it,"err");function wL(e){return e instanceof Error?e.message:String(e)}a(wL,"msg");var vL=new Set(["--config","--route","--mcp-url"]);function sx(e){let t=a(o=>{let s=e.indexOf(o);if(!(s===-1||s+1>=e.length))return e[s+1]},"get"),n=a(o=>e.includes(o),"has"),r=null;for(let o=0;o<e.length;o++){let s=e[o];if(s.startsWith("-")){vL.has(s)&&o++;continue}r=s;break}return{agent:r,dryRun:n("--dry-run"),config:t("--config")??null,route:t("--route")??Gv,direct:n("--direct"),noFallback:n("--no-fallback"),noInstall:n("--no-install"),json:n("--json"),mcpUrl:t("--mcp-url")??null}}a(sx,"parseLaunchArgs");var xL={probe(e,t){let n=ox(e,t,{stdio:["ignore","pipe","pipe"],encoding:"utf8",timeout:1e4}),r=n.status===0&&!n.error,o=typeof n.stdout=="string"?n.stdout.trim():"";return{ok:r,stdout:o.length>0?o:null}},install(e){return ox("uv",[...e],{stdio:["ignore","inherit","inherit"]}).status},spawnInteractive(e){let t=Date.now();return new Promise(n=>{let r=!1,o=fL(e[0],e.slice(1),{stdio:"inherit",env:process.env}),s=a(()=>{},"onSigint"),i=a(d=>()=>{try{o.kill(d)}catch{}},"makeForward"),l=i("SIGTERM"),c=i("SIGHUP");process.on("SIGINT",s),process.on("SIGTERM",l),process.on("SIGHUP",c);let u=a(()=>{process.removeListener("SIGINT",s),process.removeListener("SIGTERM",l),process.removeListener("SIGHUP",c)},"cleanup");o.on("error",d=>{r||(r=!0,u(),n({code:null,signal:null,elapsedMs:Date.now()-t,spawnError:d.message}))}),o.on("exit",(d,f)=>{r||(r=!0,u(),n({code:d,signal:f,elapsedMs:Date.now()-t,spawnError:null}))})})},async fetchTemplate(e,t){let n;try{n=_e()}catch(r){if(r instanceof Se)return null;throw r}try{let r=new Re({apiKey:n.apiKey,deviceId:n.deviceId,clientVersion:pe,mcpUrl:e??void 0,projectKey:t}),o=new Promise(i=>{setTimeout(()=>i(null),kL).unref()}),s=r.callTool("get_template",{template_id:Vv}).then(i=>typeof i.template_body=="string"&&i.template_body.trim().length>0?i.template_body:null).catch(()=>null);return await Promise.race([s,o])}catch{return null}},writeConfig(e,t){let n=_L(e);n&&n!=="."&&hL(n,{recursive:!0}),yL(e,t,"utf8")},readFile(e){try{return gL(e,"utf8")}catch{return null}},fileExists(e){return mL(e)},repoRoot(){try{let e=pL("git",["rev-parse","--show-toplevel"],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:5e3}).trim();return e.length>0?e:null}catch{return null}},projectKey(){return ze().projectKey},userConfigDir(){return Dr()},homeDir(){return bL()},env:process.env,platform:process.platform};function SL(e,t){return e!==null?Df(e,".maind",Ef):Df(t,Ef)}a(SL,"resolveRoutesPath");async function ix(e,t=xL){let n=Wv(e.agent);if(n.kind==="unknown")return it(`usage: maind launch <agent> [--dry-run] [--config <path>] [--route <name>] [--direct] [--no-fallback] [--no-install] \u2014 supported: ${Af.join(", ")}`),2;if(n.kind==="planned")return it(`agent '${n.agent}' is planned but not yet supported \u2014 v1 supports: ${Af.join(", ")}.`),2;if(e.json&&!e.dryRun)return it("--json is only valid together with --dry-run (stdout belongs to the agent session)."),2;let r=n.agent;if(e.direct)return it(nx()),ax(t,Of(r));let o=qv(t.env);o.warning!==null&&it(o.warning);let s,i,l;if(e.config!==null){if(!t.fileExists(e.config))return it(`config not found: ${e.config}`),2;let f=t.readFile(e.config);if(f===null||Xv(f,e.route)===null)return it(`config ${e.config} is not valid routes TOML with a [routes.${e.route}] entry.`),2;s=e.config,i=null,l="flag"}else{let f=await t.fetchTemplate(e.mcpUrl,t.projectKey()),p=f===null?null:Yv(f,e.route);if(i=zv(e.route,o,p),p!==null)l="template";else{l="builtin";let m=f===null?"no curated template (offline, unauthenticated, or not published)":`curated template unusable (missing route '${e.route}', or it pins its own targets)`;it(`using builtin routing policy (${m}).`)}s=SL(t.repoRoot(),t.userConfigDir())}let c=La,u=t.probe(La,["--version"]).ok;if(e.dryRun){let f={agent:r,configPath:s,configSource:l,toml:i,argv:Cf(c,r,e.route,s),backendKind:o.kind,switchyardDetected:u},p=rx(f,e.json);return p.stderr.length>0&&process.stderr.write(p.stderr),process.stdout.write(p.stdout),0}if(!u){if(e.noInstall)return it(`switchyard not found (--no-install). Install it with:
|
|
248
248
|
maind: ${Fa}`),1;if(!t.probe("uv",["--version"]).ok)return it(Qv()),1;if(it(Zv()),t.install(Hv),t.probe(La,["--version"]).ok)u=!0;else{let f=Df(t.homeDir(),".local","bin",t.platform==="win32"?"switchyard.exe":"switchyard");t.probe(f,["--version"]).ok&&(c=f,u=!0)}if(!u)return Nf(t,e,r,ex())}if(l!=="flag"&&i!==null)try{t.writeConfig(s,i)}catch(f){return it(`could not write ${s}: ${wL(f)}`),1}let d=await t.spawnInteractive(Cf(c,r,e.route,s));return d.spawnError!==null?Nf(t,e,r,`could not start switchyard (${d.spawnError})`):Jv(d.code,d.signal,d.elapsedMs)==="fallback-eligible"?Nf(t,e,r,`switchyard exited with code ${d.code} after ${(d.elapsedMs/1e3).toFixed(1)}s`):If(d.code,d.signal)}a(ix,"runLaunch");async function Nf(e,t,n,r){return t.noFallback?(it(`${r} (--no-fallback set \u2014 not degrading to a direct spawn).`),1):(process.stderr.write(tx(r)+`
|
|
249
249
|
`),ax(e,Of(n)))}a(Nf,"degradeOrFail");async function ax(e,t){let n=await e.spawnInteractive(t);return n.spawnError!==null?(it(`'${t[0]}' not found on PATH \u2014 install the agent CLI first (${n.spawnError}).`),1):If(n.code,n.signal)}a(ax,"spawnAndPropagate");import{execFileSync as _o}from"node:child_process";import{cpSync as qU,existsSync as Zp,mkdirSync as zU,mkdtempSync as Qp,readdirSync as eA,readFileSync as tA,realpathSync as YU,rmSync as nA,statSync as XU,writeFileSync as Us}from"node:fs";import{homedir as JU,tmpdir as em}from"node:os";import{basename as ZU,dirname as sm,join as lt,resolve as im}from"node:path";function lx(e,t,n=()=>null){if(e!==null)return e.trim()==="0"?{kind:"none",reason:"no required checks"}:{kind:"watch"};let r=t();if(r===null)return{kind:"unknown",reason:"neither query could be answered"};if(r.trim()==="0")return{kind:"none",reason:"no checks at all"};let o=n();return o!==null&&o.trim()==="0"?{kind:"none",reason:"checks exist, none required",checks:Number.parseInt(r.trim(),10)}:{kind:"unknown",reason:`${r.trim()} check(s) exist, but the required-query failed`}}a(lx,"classifyCheckCount");function cx(e){switch(e.kind){case"watch":return"";case"none":return e.reason==="no checks at all"?"this repository reports NO checks at all \u2014 the merge rests on the local gate alone.":e.reason==="checks exist, none required"?`${e.checks??"some"} check(s) run on this PR, but none is required (no branch protection) \u2014 they are not waited for; the merge rests on the local gate.`:"no required checks are configured \u2014 nothing to wait for.";case"unknown":return`could not establish the required checks: ${e.reason}.`}}a(cx,"checkCountLine");function ux(e,t){let n=new Set(t);return e.filter(r=>!n.has(r))}a(ux,"missingRequiredChecks");function dx(e){if(e===null)return null;try{let t=JSON.parse(e);if(!Array.isArray(t))return null;let n=[];for(let r of t){let o=r;if(o?.type!=="required_status_checks")continue;let s=o.parameters?.required_status_checks;if(Array.isArray(s))for(let i of s){let l=i?.context;typeof l=="string"&&!n.includes(l)&&n.push(l)}}return n}catch{return null}}a(dx,"requiredContextsFromRules");import{execFileSync as RL}from"node:child_process";function EL(e,t){if(e===0)return{kind:"clean"};if(e!==1)return{kind:"unknown",reason:e===129?"this git does not know `merge-tree --write-tree` (needs git 2.38+)":`git merge-tree failed (exit ${e??"signal"})`};let n=t.split(`
|
|
@@ -371,7 +371,7 @@ Fallback: run the MCP calls from an agent session (work_object_create/link_ref/r
|
|
|
371
371
|
`;up(w,sl(T,I,e.utcTodayIso,E.map(L=>L.body)))}for(let w of R)GF(_r(t,w));p.push({dir:_.dir,name:ap(_.dir),from:k,to:I,level:A,..._.tag?{tag:lp(_.tag,I)}:{}})}if((Pe(t,["status","--porcelain"])??"").trim()!==""){let _=rn(t,["add","-A"]);if(!_.ok)return{kind:"failed",reason:`git add failed: ${_.output}`};let y=rn(t,["commit","-F","-"],AS(p,u.map(b=>b.run)));if(!y.ok)return{kind:"failed",reason:"the release commit was refused",lines:[y.output,"the files are written but not committed \u2014 inspect, then discard them (git checkout -- . && git clean -fd) and re-run promote."]}}let m=Pe(t,["rev-parse","HEAD"])?.trim()??"",g=m!==s;return f.push(...d?.releases??[],...p),{kind:"released",releases:f,regenerated:u.map(_=>_.run),handBumped:c,head:m,kept:d?.sha??null,moved:g}}a(FS,"prepareRelease");function US(e,t,n){let r=[];for(let o of n){let s=lo(Pe(e,["show",`${t}:${o.dir}/package.json`]));s!==o.from?r.push(`${o.name} is at ${s??"?"} on ${t} now, not ${o.from} \u2014 ${o.to} may be taken`):o.tag&&Pe(e,["rev-parse","-q","--verify",`refs/tags/${o.tag}`])!==null&&r.push(`the tag ${o.tag} already exists`)}return r}a(US,"takenClaims");function BS(e,t,n){return rn(e,["push",t,`HEAD:refs/heads/${n}`])}a(BS,"pushHead");function KS(e,t){return rn(e,["reset","--keep",t])}a(KS,"rollBackTo");function HS(e,t,n,r,o){let s=[];for(let i of r){if(!i.tag)continue;let l=`${i.name} ${i.to} (#${o})`,c=`git -C ${e} tag -a ${i.tag} ${n} -m "${l}" && git -C ${e} push ${t} refs/tags/${i.tag}`,u=rn(e,["tag","-a",i.tag,n,"-m",l]);if(!u.ok){s.push({tag:i.tag,ok:!1,command:c,output:u.output});continue}let d=rn(e,["push",t,`refs/tags/${i.tag}`]),f=`git -C ${e} push ${t} refs/tags/${i.tag}`;s.push({tag:i.tag,ok:d.ok,command:f,...d.ok?{}:{output:d.output}})}return s}a(HS,"tagReleases");function GS(e){return e.kind==="none"||e.kind==="retargeted"}a(GS,"mayDeleteBranch");function al(e,t,n,r){let o=e.openPrsOnBase(t,n);if(o===null)return{kind:"unknown"};if(o.length===0)return{kind:"none"};let s=[],i=[];for(let l of o)(e.retargetPr(t,l.number,r)?s:i).push(l);return i.length===0?{kind:"retargeted",prs:s}:{kind:"retarget-failed",retargeted:s,failed:i}}a(al,"retargetStacked");function VS(e,t){if(e.kind==="none")return[];let n=Cn,r=t.slug??"<owner>/<repo>",o=`${t.remote}/${t.base}`,s=`git -C ${n(t.mainRoot)} push ${n(t.remote)} --delete ${n(t.branch)}`,i=[];if(e.kind==="unknown")return i.push(`the remote branch ${t.branch} was KEPT \u2014 the open pull requests based on it could not be listed, and deleting it would close them instead of retargeting them. See what is stacked on it:`,` gh pr list --repo ${r} --base ${n(t.branch)} --state open`,`retarget each one to ${t.base} (gh pr edit <n> --repo ${r} --base ${n(t.base)}) and rebase it onto ${o}`+(t.mergedHead?` from ${t.mergedHead.slice(0,12)}`:"")+`; once nothing is based on ${t.branch} any more, delete it: ${s}`),i;let l=e.kind==="retargeted"?e.prs:e.retargeted;if(l.length>0&&i.push(`retargeted ${l.map(d=>`#${d.number}`).join(", ")} from ${t.branch} to ${t.base} before deleting the branch \u2014 deleting it first would have closed ${l.length===1?"it":"them"}.`),e.kind==="retarget-failed"){let d=e.failed.map(f=>`gh pr edit ${f.number} --repo ${r} --base ${n(t.base)}`);i.push(`the remote branch ${t.branch} was KEPT \u2014 ${e.failed.map(f=>`#${f.number}`).join(", ")} could not be retargeted to ${t.base}, and deleting the branch would close ${e.failed.length===1?"it":"them"}. Retarget, then delete the branch:`,` ${[...d,s].join(" && ")}`)}let c=e.kind==="retargeted"?e.prs:[...e.retargeted,...e.failed],u=c.length===1;i.push(`the stacked pull request${u?" still carries":"s still carry"} the commits of #${t.mergedPr}, which the squash merge rewrote \u2014 rebase ${u?"its":"each one's"} own commits onto ${o}:`);for(let d of c){let f=t.worktreeFor(d.headRefName);if(f&&t.mergedHead){let p=`git -C ${n(f)}`;i.push(` \u2022 #${d.number} (${d.headRefName}):`,` ${p} fetch ${n(t.remote)} && ${p} rebase --onto ${n(o)} ${t.mergedHead} && ${p} push --force-with-lease ${n(t.remote)} ${n(d.headRefName)}`)}else i.push(` \u2022 #${d.number} (${d.headRefName}): `+(f?`in ${f}`:"not checked out on this machine \u2014 in a checkout of it")+`, rebase onto ${o}`+(t.mergedHead?` from ${t.mergedHead}`:` from the old head of ${t.branch}`)+" (git rebase --onto), then push with --force-with-lease.")}return i}a(VS,"stackedPrLines");var WF="([A-Z][A-Z0-9]{0,5})-0*([0-9]{1,9})",qF=new RegExp(`(?<![A-Za-z0-9])${WF}(?![A-Za-z0-9])`,"g"),zF=20;function WS(e){let t=[];for(let n of e.matchAll(qF)){let r=`${n[1].toUpperCase()}-${Number.parseInt(n[2],10)}`;if(t.includes(r)||t.push(r),t.length>=zF)break}return t}a(WS,"keysInText");function YF(e){return WS(e.toUpperCase())}a(YF,"keysInBranch");async function qS(e,t){let n=[{tier:"branch",keys:YF(e.branch)},{tier:"title",keys:WS(e.title)}];for(let{tier:r,keys:o}of n){let s=new Map;for(let i of o){let l=null;try{l=await t(i)}catch{l=null}l!==null&&!s.has(l.id)&&s.set(l.id,{key:i,object:l})}if(s.size===1){let i=[...s.values()][0];return{kind:"links",tier:r,key:i.key,object:i.object}}if(s.size>1)return{kind:"ambiguous",tier:r,keys:[...s.values()].map(i=>i.key)}}return{kind:"none"}}a(qS,"forecastConnectorLink");function zS(e){let{forecast:t,boundId:n}=e;if(n===null||t.kind!=="links"||t.object.id===n)return null;let r=e.boundKey??n,o=t.object.key??t.key;return[`${t.tier==="branch"?`the branch name names ${t.key}`:`the pull request title "${e.title}"${e.titleFromFlag?"":" (the oldest commit of the branch)"} names ${t.key}`} \u2014 GitHub's connector links a new pull request to the object it names, so this one would land on ${o}.`,`this branch is bound to ${r}. Nothing was pushed.`,t.tier==="branch"?` if the work belongs to ${r}: ship it from a branch whose name does not name another object.`:` if the work belongs to ${r}: ${e.shipCommand} --title "<type>(<scope>): <subject>" (a title naming ${r} or none)${e.titleFromFlag?"":", or reword that commit"}.`,` if it belongs to ${o}: maind object bind --object ${t.object.id}, then ship again.`]}a(zS,"forecastConflictLines");var XF=/^[a-z]+(\([^()\n]+\))?!?: \S/,JF=/ \(#\d+\)$/;function pp(e){return XF.test(e.replace(JF,"").trim())}a(pp,"isConventional");var ZF=5,QF=.7;function Ns(e){let t=e.map(r=>r.trim()).filter(r=>r.length>0);return t.length<ZF?null:t.filter(pp).length/t.length>=QF}a(Ns,"repoUsesConventional");function Ds(e){let t=e.filter(s=>s.subject.trim().length>0),n=t[0];if(n===void 0)return null;let r=[];n.body.trim().length>0&&r.push(n.body.trim());let o=t.slice(1);return o.length>0&&r.push(["Further commits:",...o.map(s=>`- ${s.subject.trim()}`)].join(`
|
|
372
372
|
`)),{title:n.subject.trim(),body:r.join(`
|
|
373
373
|
|
|
374
|
-
`)}}a(Ds,"prTextFrom");function co(e,t,n){if(t!==!0)return{kind:"ok"};if(pp(e))return{kind:"ok"};let r=Ds(n)?.title??null;return{kind:"not-conventional",title:e,suggestion:r!==null&&pp(r)?r:null}}a(co,"judgeTitle");function e1(e){return e==="dashboard"||e==="terminal"||e==="carried"?e:null}a(e1,"readVia");function t1(e){if(typeof e!="object"||e===null)return null;let t=e.head_sha;return typeof t=="string"?t:null}a(t1,"readCarriedFrom");function gp(e){let t=e1(e.answered_via),n=t==="dashboard"&&e.verified!==!1;return{answeredVia:t,carriedFrom:t1(e.carried_from),verified:n,mfa:n&&typeof e.mfa=="boolean"?e.mfa:null}}a(gp,"readProof");function n1(e){return{answeredVia:e.answeredVia,carriedFrom:e.carriedFrom,verified:e.verified,mfa:e.mfa}}a(n1,"proofOf");function uo(e){return e.answeredVia==="terminal"?" in a terminal (reported, not verified)":e.answeredVia==="carried"?` \u2014 carried from ${e.carriedFrom?e.carriedFrom.slice(0,7):"an earlier head"} across promote's own commits (reported: checked by the CLI, not verified)`:e.verified?` in a browser session (verified${e.mfa===!0?", with a second factor":e.mfa===!1?", without a second factor":""})`:e.answeredVia==="dashboard"?" (reported, not verified)":" through a channel this CLI does not know (not shown as verified)"}a(uo,"viaPhrase");function js(e,t){return t?`${e}/planning/${t}`:e}a(js,"approvalPageUrl");function cl(e){if(e.ok!==!0)return null;let t=e.state;if(t!=="not_required"&&t!=="none"&&t!=="pending"&&t!=="approved"&&t!=="declined")return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof e.decision=="object"&&e.decision!==null?e.decision:{},o=typeof e.pr=="object"&&e.pr!==null?e.pr:null;return{state:t,objectKey:typeof n.key=="string"?n.key:null,...gp(r),pr:o&&typeof o.state=="string"?{state:o.state,headSha:typeof o.head_sha=="string"?o.head_sha:null}:null}}a(cl,"readApprovalState");var ul={timeoutMs:30*6e4,intervalMs:15e3},r1=24*60*6e4,o1=4;function YS(e){let t=/^\s*(\d+)\s*([smh]?)\s*$/i.exec(e);if(!t)return null;let n=Number(t[1]),r=(t[2]??"").toLowerCase(),o=r==="s"?n*1e3:r==="h"?n*36e5:n*6e4;return o>0&&o<=r1?o:null}a(YS,"parseWaitDuration");function hp(e){return e%36e5===0?`${e/36e5} h`:e%6e4===0?`${e/6e4} min`:`${Math.round(e/1e3)} s`}a(hp,"humanDuration");async function s1(e,t,n){let r=n.now()+t.timeoutMs;n.note(`waiting for a person to approve PR #${e.prNumber} at ${e.headSha.slice(0,7)} on ${e.url} (up to ${hp(t.timeoutMs)}, checking every ${hp(t.intervalMs)}).`);let o=0;for(;;){let s=await n.readState();if(s.kind==="unknown_tool")return{kind:"failed",reason:"the server cannot read merge approvals yet (older maind server) \u2014 --wait needs it"};if(s.kind==="invalid_params")return{kind:"failed",reason:s.reason};let i=s.kind==="ok"?cl(s.body):null;if(i===null){if(o+=1,o>=o1)return{kind:"failed",reason:s.kind==="failed"?s.reason:typeof s.body.error=="string"?s.body.error:"unreadable answer"}}else switch(o=0,i.state){case"approved":return{kind:"approved",via:i.answeredVia??"unknown",proof:n1(i)};case"declined":return{kind:"declined"};case"not_required":return{kind:"approved",via:"not_required"};case"none":case"pending":{let c=n.currentHead();if(c!==null&&c!==e.headSha)return{kind:"moved",head:c};let u=n.baseConflict?.()??null;if(u!==null&&u.length>0)return{kind:"base-conflict",files:u};if(i.state==="none")return{kind:"gone",prState:i.pr?.state??null};break}}let l=r-n.now();if(l<=0)return{kind:"timeout"};await n.sleep(Math.min(t.intervalMs,l))}}a(s1,"waitForApproval");function i1(e){if(e.ok!==!0)return null;let t=e.state;if(t!=="not_required"&&t!=="approved"&&t!=="declined"&&t!=="pending")return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof e.decision=="object"&&e.decision!==null?e.decision:{};return{state:t,objectKey:typeof n.key=="string"?n.key:null,question:typeof r.question=="string"?r.question:null,terminalAccepted:e.terminal_accepted!==!1,...gp(r)}}a(i1,"readApprovalAnswer");function XS(e,t){if(e.ok!==!0)return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof n.key=="string"?n.key:null,o=typeof e.decision=="object"&&e.decision!==null?e.decision:{},s=typeof e.hint=="string"?e.hint:null;switch(e.state){case"approved":{if(e.carried!==!0)return{kind:"own",objectKey:r};let i=typeof e.carried_from=="object"&&e.carried_from!==null?e.carried_from:{};return{kind:"carries",from:typeof i.head_sha=="string"?i.head_sha:null,via:typeof i.via_sha=="string"?i.via_sha:null,links:typeof e.links=="number"?e.links:0,objectKey:r}}case"declined":{let i=o.head_sha;return typeof i=="string"&&i!==t?{kind:"declined",at:i,objectKey:r}:{kind:"own",objectKey:r}}case"none":return{kind:"none",reason:typeof e.reason=="string"?e.reason:null,hint:s,objectKey:r};case"not_required":return{kind:"own",objectKey:r};default:return null}}a(XS,"readCarryAnswer");function dl(e){let t=e.via??e.from,n=e.links,r=t?`the approval of ${t.slice(0,7)}`:"an earlier approval",o=e.from&&t&&e.from!==t?` (itself carried from ${e.from.slice(0,7)})`:"";return`${r}${o} carries: the ${n} commit${n===1?"":"s"} on top ${n===1?"is":"are"} promote's own and re-derive exactly`}a(dl,"carriesPhrase");function a1(e,t){if(e===null)return!1;let n=/^\s*#?\s*(\d+)\s*$/.exec(e);return n!==null&&Number(n[1])===t}a(a1,"confirmsPrNumber");async function mp(e,t,n){let r=e.headSha.slice(0,7),o=await s1({prNumber:e.prNumber,headSha:e.headSha,url:n},t.wait.options,{readState:a(()=>t.call("work_object_merge_approval_state",{objectId:e.objectId,ref:e.ref,headSha:e.headSha}),"readState"),currentHead:t.wait.currentHead,...t.wait.baseConflict?{baseConflict:t.wait.baseConflict}:{},sleep:t.wait.sleep,now:t.wait.now,note:t.note});switch(o.kind){case"approved":return o.via==="not_required"?(t.note("the organization no longer asks for merge approvals \u2014 continuing."),{kind:"proceed",via:"not_required"}):(t.note(`merge of PR #${e.prNumber} at ${r} approved${uo(o.proof)}.`),{kind:"proceed",via:o.via});case"declined":return t.note(`a person chose "Don't merge" for PR #${e.prNumber} at ${r} on ${n} \u2014 nothing was merged.`),{kind:"stop"};case"timeout":return t.note(`still not approved after ${hp(t.wait.options.timeoutMs)} \u2014 nothing was merged. The question keeps waiting on ${n}; run promote again (with --wait to keep waiting).`),{kind:"pending",url:n};case"moved":return t.note(`PR #${e.prNumber} moved to ${o.head.slice(0,7)} while waiting \u2014 an approval of ${r} would not cover it. Nothing was merged; run promote again, it asks for the new head.`),{kind:"stop"};case"base-conflict":{let s=o.files.length>3?`${o.files.slice(0,3).join(", ")} and ${o.files.length-3} more`:o.files.join(", ");t.note(`${e.base} moved while waiting, and PR #${e.prNumber} at ${r} now conflicts with it in ${s}. Stopped waiting \u2014 an approval of this head could not be merged.`),t.note("Merge the base into the branch, resolve, push, and run promote again: the open question would not cover the resolution. Nothing was merged.");try{await t.tellObject?.(`**Don't approve PR #${e.prNumber} at \`${r}\` yet** \u2014 \`${e.base}\` moved while the merge approval was waiting, and this head now conflicts with it in ${s}. An approval of it could not be merged; the resolution is a new head that will be asked for again. (maind feature promote --wait stopped waiting.)`)}catch{}return{kind:"stop"}}case"gone":return t.note(`the merge question for PR #${e.prNumber} disappeared without an answer`+(o.prState?` \u2014 the pull request is ${o.prState}`:"")+". Nothing was merged."),{kind:"stop"};case"failed":return t.note(`stopped waiting: ${o.reason}. Nothing was merged; the question still waits on ${n}.`),{kind:"stop"}}}a(mp,"awaitAnswer");async function l1(e,t,n){let r=e.carry?.links??[],o=e.carry?.stopLine??null;if(r.length===0)return o&&t.note(`no earlier approval carries \u2014 ${o}.`),null;let s=await t.call("work_object_carry_merge_approval",{...n,chain:r});if(s.kind==="unknown_tool")return t.note("the server cannot carry an approval across promote's own commits yet (older maind server) \u2014 asking for this head."),null;if(s.kind==="failed"||s.kind==="invalid_params")return t.note(`an earlier approval could not be carried (${s.reason}) \u2014 asking for this head.`),null;let i=XS(s.body,e.headSha);if(i===null){let c=typeof s.body.error=="string"?s.body.error:"unreadable answer";return t.note(`an earlier approval could not be carried (${c}) \u2014 asking for this head.`),null}let l=e.headSha.slice(0,7);switch(i.kind){case"carries":return t.note(`merge of PR #${e.prNumber} at ${l} approved \u2014 ${dl(i)} (recorded as carried: reported, checked by the CLI, not verified).`),{kind:"proceed",via:"carried"};case"declined":{let c=js(e.dashboardUrl,i.objectKey);return t.note(`a person chose "Don't merge" for ${i.at.slice(0,7)} on ${c}, and ${l} only adds promote's own commits to it \u2014 nothing was merged.`),t.note("A new push asks again; or discuss the answer at the object."),{kind:"stop"}}case"none":return i.reason!=="no_approval"&&i.hint?t.note(i.hint):o&&t.note(`no earlier approval carries \u2014 ${o}.`),null;case"own":return null}}a(l1,"tryCarry");function ll(e,t,n){return e.objectId!==null?`in a browser session on ${t} ("Waiting on you")`:n?"by running promote in their own terminal \u2014 no browser page shows a change request without a work object yet (ADR-393 Phase 4)":"nowhere yet: this organization counts only browser answers, and no browser page shows a change request without a work object (ADR-393 Phase 4)"}a(ll,"whereToApprove");async function JS(e,t){let n={objectId:e.objectId,ref:e.ref,headSha:e.headSha,base:e.base},r=e.headSha.slice(0,7),o=await l1(e,t,n);if(o!==null)return o;let s=await t.call("work_object_merge_approval",n);if(s.kind==="unknown_tool")return t.note("the server does not know merge approvals yet (older maind server) \u2014 merging without the approval gate. Update the server to have it enforced."),{kind:"proceed",via:"server_without_gate"};if(s.kind==="failed"||s.kind==="invalid_params")return t.note(`the merge approval could not be checked (${s.reason}) \u2014 nothing was merged.`),{kind:"stop"};let i=i1(s.body);if(i===null){let u=typeof s.body.error=="string"?s.body.error:"unreadable answer",d=typeof s.body.hint=="string"?` ${s.body.hint}`:"";return t.note(`the merge approval could not be checked (${u}).${d} Nothing was merged.`),{kind:"stop"}}let l=js(e.dashboardUrl,i.objectKey);switch(i.state){case"not_required":return{kind:"proceed",via:"not_required"};case"approved":return t.note(`merge of PR #${e.prNumber} at ${r} approved${uo(i)}.`),{kind:"proceed",via:i.answeredVia??"unknown"};case"declined":return t.note(`a person chose "Don't merge" for PR #${e.prNumber} at ${r} on ${l} \u2014 nothing was merged.`),t.note("A new push asks again; or change the answer's premise and discuss it at the object."),{kind:"stop"};case"pending":break}let c=t.wait;if(t.isTty()&&i.terminalAccepted){t.note(`your organization asks a person to approve every merge. PR #${e.prNumber} at ${r} into ${e.base}.`);let u=await t.prompt(`Type the pull request number (${e.prNumber}) to approve the merge, or press Enter to `+(c?e.objectId!==null?"wait for an approval in a browser session: ":"wait for someone to approve it in their own terminal: ":"stop: "));if(!a1(u,e.prNumber))return c?mp(e,{...t,wait:c},l):(t.note(e.objectId!==null?`not approved \u2014 nothing was merged. The question also waits on ${l}.`:"not approved \u2014 nothing was merged."),{kind:"stop"});let d=await t.call("work_object_record_terminal_approval",n);if(d.kind==="ok"&&d.body.ok!==!0&&d.body.error==="terminal_not_accepted")return t.note(`this organization counts only approvals given in a browser session \u2014 nothing was recorded from this terminal. A person approves it ${ll(e,l,!1)}.`),c?mp(e,{...t,wait:c},l):(t.note("Then run promote again \u2014 or run it with --wait. Nothing was merged."),{kind:"pending",url:l});if(d.kind!=="ok"||d.body.ok!==!0||d.body.state!=="approved"){let f=d.kind==="ok"?d.body.state==="declined"?`a person chose "Don't merge" on ${l} in the meantime`:String(d.body.error??"unreadable answer"):d.kind==="failed"||d.kind==="invalid_params"?d.reason:"the server does not know terminal approvals";return t.note(`the approval could not be recorded (${f}) \u2014 nothing was merged.`),{kind:"stop"}}if(d.body.recorded!==!0){let f=typeof d.body.decision=="object"&&d.body.decision!==null?d.body.decision:{},p=gp(f);return t.note(`merge of PR #${e.prNumber} at ${r} was already approved${uo(p)} \u2014 that answer stands; nothing was recorded from this terminal.`),{kind:"proceed",via:p.answeredVia??"unknown"}}return t.note("approved in this terminal \u2014 recorded as reported, not verified (only a browser session gives a verified answer)."),{kind:"proceed",via:"terminal"}}return t.note(`your organization asks a person to approve every merge \u2014 PR #${e.prNumber} at ${r} is not approved yet.`),c?(t.note(`A person approves it ${ll(e,l,i.terminalAccepted)}; promote merges as soon as they do.`),mp(e,{...t,wait:c},l)):(e.objectId===null||!i.terminalAccepted?t.note(`A person approves it ${ll(e,l,i.terminalAccepted)}.`):t.note(`A person approves it ${ll(e,l,!0)}, or runs promote in their own terminal.`),t.note("Then run promote again \u2014 or run it with --wait, which merges once approved. Nothing was merged. A new push to the branch asks again."),{kind:"pending",url:l})}a(JS,"runMergeApproval");function ZS(e,t){let n=t.headSha.slice(0,7),r=[],o=`PR #${t.prNumber} at ${n}`,s,i=e.state==="none"||e.state==="pending"?t.carry??null:null;switch(e.state){case"not_required":r.push(`${o}: this organization does not ask for merge approvals.`),s=0;break;case"approved":r.push(`${o}: approved${uo(e)}.`),s=0;break;case"declined":r.push(`${o}: a person chose "Don't merge" on ${t.url}.`),s=1;break;case"pending":case"none":if(i?.kind==="carries"){r.push(`${o}: not answered for this head, but ${dl(i)} \u2014 promote merges without asking.`),s=0;break}if(i?.kind==="declined"){r.push(`${o}: a person chose "Don't merge" for ${i.at.slice(0,7)} on ${t.url}, and this head only adds promote's own commits to it.`),s=1;break}r.push(e.state==="pending"?`${o}: waiting for a person to approve it in a browser session on ${t.url} ("Waiting on you").`:`${o}: nobody has been asked for this head yet \u2014 promote asks when it gets there (promote --wait waits for the answer).`),s=3;break}let l=i?bp(i):null;return l&&r.push(` ${l}`),e.pr?.headSha&&e.pr.headSha!==t.headSha&&r.push(` GitHub's connector last saw the pull request at ${e.pr.headSha.slice(0,7)} \u2014 an answer for ${n} would not cover that.`),t.localHead!==null&&t.localHead!==t.headSha&&r.push(` the local branch is at ${t.localHead.slice(0,7)}, not at the pull request head \u2014 push first; an approval covers only the pushed head.`),{lines:r,exitCode:s}}a(ZS,"approvalStateLines");async function yp(e,t,n){if(n.links.length===0)return n.stopLine?{kind:"stops",line:n.stopLine}:null;let r=await e("work_object_merge_approval_state",{...t,chain:n.links});if(r.kind==="unknown_tool")return{kind:"unavailable",why:"the server cannot read merge approvals yet"};if(r.kind==="failed"||r.kind==="invalid_params")return{kind:"unavailable",why:r.reason};let o=XS(r.body,t.headSha);return o===null?{kind:"unavailable",why:typeof r.body.error=="string"?r.body.error:"unreadable answer"}:o}a(yp,"previewCarry");function bp(e){switch(e.kind){case"stops":return`no earlier approval carries \u2014 ${e.line}.`;case"unavailable":return`whether an earlier approval carries could not be read (${e.why}).`;case"none":return e.reason!=="no_approval"&&e.hint?e.hint:null;default:return null}}a(bp,"carryNote");var c1=[5e3,2e4,45e3];function u1(e){return e.kind==="failed"&&/\b429\b|rate limit/i.test(e.reason)}a(u1,"isRateLimited");function QS(e,t){let n=t.delays??c1,r=t.now??Date.now;return async(o,s)=>{let i=0;for(let l=0;;l++){let c=await e(o,s);if(c.kind==="failed"&&c.quota!==void 0){let d=i<3?Rw(c.quota,r(),t.maxQuotaWaitMs??0):null;if(d===null)return c;i+=1,t.note(`${ka(c.quota,r())} \u2014 waiting ${Math.ceil(d/6e4)} min for it; nothing is lost.`),await t.sleep(d);continue}if(!u1(c)||l>=n.length)return c;let u=n[l];t.note(`the maind server is rate-limiting this IP (429) \u2014 retrying in ${Math.round(u/1e3)} s; nothing is lost.`),await t.sleep(u)}}}a(QS,"retryOnRateLimit");function d1(e){return/^[A-Za-z0-9_./:@-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`}a(d1,"shellQuoteArg");var f1={ok:"ok ",fail:"FAILED ",note:"note "};function eR(e){return e.map(t=>`${f1[t.verdict]} ${t.name}${t.detail?` \u2014 ${t.detail}`:""}`)}a(eR,"checkRowLines");function tR(e,t,n){let r=e.filter(o=>o.verdict==="fail");return r.length===0?n?.kind==="asks"?{lines:["promote would get past every check. The merge waits for a person's approval, and promote asks for it only when it runs \u2014 so run it yourself:",` ${n.waitCommand}`,`The question then appears on ${n.url}; promote merges as soon as a person approves it there (or types the PR number in its terminal).`],exitCode:0}:n?.kind==="given"?{lines:["promote would get past every check, and the merge approval is in \u2014 it is the go. Run:",` ${t}`],exitCode:0}:{lines:["promote would get past every check. Hand over exactly this command:",` ${t}`],exitCode:0}:{lines:[`promote would stop at ${r.length===1?"this check":`these ${r.length} checks`}: ${r.map(o=>o.name).join(", ")}.`,`Fix ${r.length===1?"it":"them"} before handing over a promote command \u2014 nothing was changed.`],exitCode:1}}a(tR,"checkVerdictLines");function _p(e,t,n){let o=[n&&e.startsWith("maind ")?`cd ${d1(n.cwd)} && npx -y @maind-dev/cli@${n.cliVersion} ${e.slice(6)}`:e,"--yes"];return t.closesObject&&o.push("--closes-object"),t.progressOnly&&o.push("--progress-only"),t.wait&&o.push("--wait"),o.join(" ")}a(_p,"promoteCommandFrom");function nR(e,t){let n="merge-approval";if("failure"in e)return{name:n,verdict:"note",detail:`not readable (${e.failure}) \u2014 promote asks the server itself`};switch(e.state){case"not_required":return{name:n,verdict:"ok",detail:"this organization does not ask for one"};case"approved":return{name:n,verdict:"ok",detail:`approved for this head${uo(e)}`};case"declined":return{name:n,verdict:"fail",detail:`a person chose "Don't merge" on ${t.url}`};case"pending":case"none":{let r=t.carry??null;if(r?.kind==="carries")return{name:n,verdict:"ok",detail:`${dl(r)} \u2014 promote records it and merges without asking`};if(r?.kind==="declined")return{name:n,verdict:"fail",detail:`a person chose "Don't merge" for ${r.at.slice(0,7)} on ${t.url}, and this head only adds promote's own commits to it`};let o=r?bp(r):null;return{name:n,verdict:"note",detail:`not asked yet \u2014 promote asks when it runs: the question then appears on ${t.url}, where a person approves in a browser session (in a terminal they can also type ${t.prNumber})`+(t.wait?"; --wait waits for the answer":"; without a terminal, add --wait \u2014 otherwise promote stops with exit 3 instead of waiting")+(o?`; ${o.replace(/\.$/,"")}`:"")}}}}a(nR,"approvalRow");function rR(e,t){let n="required-checks";if(t===null)return{name:n,verdict:"note",detail:"not readable \u2014 promote waits for them itself"};let r=[];try{let l=JSON.parse(t);r=Array.isArray(l.statusCheckRollup)?l.statusCheckRollup:[]}catch{return{name:n,verdict:"note",detail:"not readable \u2014 promote waits for them itself"}}if(e.length===0)return{name:n,verdict:"note",detail:"no required checks could be determined \u2014 promote decides at the merge"};let o=new Map;for(let l of r){let c=typeof l.name=="string"?l.name:typeof l.context=="string"?l.context:null;c&&o.set(c,l)}let s=[],i=[];for(let l of e){let c=o.get(l);if(!c){i.push(`${l} (not registered)`);continue}let u=String(c.conclusion??c.state??"").toUpperCase();["FAILURE","ERROR","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE"].includes(u)?s.push(l):u!=="SUCCESS"&&u!=="NEUTRAL"&&u!=="SKIPPED"&&i.push(l)}return s.length>0?{name:n,verdict:"fail",detail:`red on the pull request head: ${s.join(", ")}`}:i.length>0?{name:n,verdict:"note",detail:`still running: ${i.join(", ")} \u2014 promote waits for them`}:{name:n,verdict:"ok",detail:`green: ${e.join(", ")}`}}a(rR,"requiredChecksRow");import{readFileSync as fR}from"node:fs";import{join as pR}from"node:path";var kp={branchPrefix:"feat/",remote:"origin",baseBranch:null,postCreate:[],postMerge:null,regenerate:[]},kr=".maind/feature.json";function wp(e){return!e||e.trim().length===0?"a feature name is required":e.startsWith("-")?`"${e}" must not start with a dash`:e.includes("..")?`"${e}" must not contain ".."`:/^[A-Za-z0-9._-]+$/.test(e)?null:`"${e}" may only contain letters, digits, dot, underscore and dash`}a(wp,"validateFeatureName");function fl(e,t){return`${e.replace(/[/\\]+$/,"")}-${t}`}a(fl,"worktreePathFor");function vp(e,t){return`${e.branchPrefix}${t}`}a(vp,"branchFor");function oR(e){return!e||e.trim().length===0?"a branch name is required":e.startsWith("-")?`"${e}" must not start with a dash`:e.includes("..")?`"${e}" must not contain ".."`:e.startsWith("/")||e.endsWith("/")||e.includes("//")?`"${e}" must not start or end with "/" or contain "//"`:e.endsWith(".lock")||e.endsWith(".")?`"${e}" must not end with ".lock" or "."`:/^[A-Za-z0-9._/-]+$/.test(e)?null:`"${e}" may only contain letters, digits, dot, underscore, dash and slash`}a(oR,"validateBranchName");function p1(e,t){return fl(e,t.replace(/\//g,"-"))}a(p1,"worktreePathForBranch");function Ue(e,t){return t.byBranch?`maind feature ${e} --branch ${t.branch}`:`maind feature ${e} ${t.label}`}a(Ue,"featureCommand");function sR(e,t){if(t.branch!==void 0){let o=oR(t.branch);if(o)return{kind:"invalid",reason:o};let s=e.worktreeList(t.repoRoot),i=s[0]?.path??null,l=s.find(c=>c.branch===t.branch);return l&&(l.path===i||l.path===t.repoRoot)?{kind:"invalid",reason:`${t.branch} is checked out in the main checkout (${l.path}) \u2014 ship/promote/done work on a branch in its own worktree`}:{kind:"ok",target:{label:t.branch,branch:t.branch,worktree:l?.path??p1(t.repoRoot,t.branch),byBranch:!0}}}let n=t.name??"",r=wp(n);return r?{kind:"invalid",reason:r}:{kind:"ok",target:{label:n,branch:vp(t.cfg,n),worktree:fl(t.repoRoot,n),byBranch:!1}}}a(sR,"resolveFeatureTarget");function iR(e,t){return e.worktreeAddExisting?e.exists(t.path)?{kind:"path-exists",path:t.path}:(e.fetch(t.repoRoot,t.remote),!e.branchExists(t.repoRoot,t.branch)&&!e.refExists(t.repoRoot,`${t.remote}/${t.branch}`)?{kind:"no-branch",branch:t.branch,remote:t.remote}:e.worktreeAddExisting(t.repoRoot,t.path,t.branch)?{kind:"ok",path:t.path,branch:t.branch}:{kind:"worktree-failed",path:t.path,branch:t.branch}):{kind:"unsupported"}}a(iR,"planCheckout");function aR(e,t){let n=wp(t.name);if(n)return{kind:"invalid-name",reason:n};let r=fl(t.repoRoot,t.name);if(e.exists(r))return{kind:"path-exists",path:r};let o=vp(t.cfg,t.name);if(e.branchExists(t.repoRoot,o))return{kind:"branch-exists",branch:o};let s=t.cfg.baseBranch??e.defaultBranch(t.repoRoot);if(!s)return{kind:"no-base-branch"};e.fetch(t.repoRoot,t.cfg.remote);let i=`${t.cfg.remote}/${s}`;return e.refExists(t.repoRoot,i)?e.worktreeAdd(t.repoRoot,r,o,i)?{kind:"ok",path:r,branch:o,startPoint:i,provisioned:t.requirements}:{kind:"worktree-failed",path:r,branch:o}:{kind:"start-point-missing",startPoint:i,remote:t.cfg.remote}}a(aR,"planStart");function pl(e,t){if(t.target){let u=oR(t.target.branch);if(u)return{kind:"invalid-name",reason:u}}else{let u=wp(t.name);if(u)return{kind:"invalid-name",reason:u}}let n=t.target?.worktree??fl(t.repoRoot,t.name),r=t.target?.branch??vp(t.cfg,t.name),o=e.branchExists(t.repoRoot,r),s=e.exists(n);if(!o&&!s)return{kind:"ok",removedWorktree:!1,deletedBranch:!1};let i=e.openPr(t.repoRoot,r);if(i)return{kind:"pr-open",branch:r,prNumber:i.number,url:i.url};let l=null;if(o){let u=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:r,sha:e.branchHead(t.repoRoot,r),side:"local"});if(u.kind==="not-landed")return{kind:"not-landed",branch:r,path:s?n:null,reason:u.reason};l=u.via}let c=!1;if(s){if(e.isDirty(n)!==!1)return{kind:"dirty",path:n};if(t.dryRun)return{kind:"would-remove",path:n,via:l};if(!e.worktreeRemove(t.repoRoot,n))return{kind:"remove-failed",path:n};c=!0}return t.dryRun?{kind:"would-remove",path:null,via:l}:!o||l===null?{kind:"ok",removedWorktree:c,deletedBranch:!1}:e.branchDelete(t.repoRoot,r,!0)?{kind:"ok",removedWorktree:c,deletedBranch:!0,via:l}:{kind:"branch-unverified",branch:r,reason:"git refused to delete the branch"}}a(pl,"planDone");function xp(e,t){let{branch:n,sha:r}=t,o=t.cfg.baseBranch??e.defaultBranch(t.repoRoot);if(r&&o&&e.isAncestor(t.repoRoot,r,`${t.cfg.remote}/${o}`)===!0)return{kind:"landed",via:"fast-forward"};let s=e.mergedPr(t.repoRoot,n),i=s?s.state!=="MERGED"?`its PR is ${s.state}, not MERGED`:!r||!s.headSha||s.headSha!==r?`the merged PR's head (${s.headSha??"unknown"}) is not the ${t.side} head (${r??"unknown"})`:null:`${n} is not contained in ${t.cfg.remote}/${o??"<default branch>"} and no merged PR carries it`;return i?{kind:"not-landed",reason:i}:{kind:"landed",via:"squash-proof"}}a(xp,"proveLanded");function lR(e,t){let n=[];for(let r of e.worktreeList(t.repoRoot))!r.branch||!r.branch.startsWith(t.cfg.branchPrefix)||n.push({name:r.branch.slice(t.cfg.branchPrefix.length),branch:r.branch,path:r.path});return n.sort((r,o)=>r.name.localeCompare(o.name))}a(lR,"listFeatureWorktrees");function cR(e,t){let n=t.real??(u=>u),r=e.worktreeList(t.repoRoot),o=r.slice(1),s=[];for(let u of o){let d=n(u.path);if(!u.branch){s.push({path:u.path,branch:null,outcome:"kept",reason:"detached HEAD \u2014 no branch to prove a merge for"});continue}if(so(n(t.cwd),d)){s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"you are standing in it \u2014 run the sweep from the main checkout"});continue}if(t.liveAgentCwds.some(p=>so(n(p),d))){s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"a live agent session stands in it"});continue}let f=pl(e,{repoRoot:t.repoRoot,name:u.branch,cfg:t.cfg,target:{branch:u.branch,worktree:u.path},dryRun:t.dryRun});switch(f.kind){case"ok":f.removedWorktree?s.push({path:u.path,branch:u.branch,outcome:"removed",via:f.via??null}):s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"git no longer sees the worktree"});break;case"would-remove":s.push({path:u.path,branch:u.branch,outcome:"would-remove",via:f.via});break;case"branch-unverified":s.push({path:u.path,branch:u.branch,outcome:"removed",via:null,branchKept:f.reason});break;case"pr-open":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:`PR #${f.prNumber} is still open`});break;case"not-landed":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:f.reason});break;case"dirty":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"uncommitted or untracked changes"});break;case"remove-failed":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"git refused to remove it"});break;case"invalid-name":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:f.reason});break}}let i=t.orphanDirs?t.orphanDirs(t.repoRoot,r.map(u=>u.path)):[],l=m1(e,t,r),c=t.remote?h1(e,t,r):void 0;return{entries:s,orphans:i,branches:l,...c?{remoteBranches:c}:{}}}a(cR,"planSweep");function uR(e,t,n){let r=t.cfg.baseBranch??e.defaultBranch(t.repoRoot),o=new Set(n.flatMap(s=>s.branch?[s.branch]:[]));return r&&o.add(r),o}a(uR,"untouchable");function dR(e,t,n){let r=e.openPr(t,n);return r===void 0?'could not ask whether a PR is open (gh/glab did not answer) \u2014 if this repository has no pull requests, declare "forge": "none" in .maind/feature.json':r?`PR #${r.number} is still open`:null}a(dR,"openPrReason");function m1(e,t,n){let r=e.localBranches?.(t.repoRoot);if(!r)return[];let o=uR(e,t,n),s=[];for(let i of r){if(o.has(i))continue;let l=dR(e,t.repoRoot,i);if(l){s.push({branch:i,outcome:"kept",reason:l});continue}let c=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:i,sha:e.branchHead(t.repoRoot,i),side:"local"});c.kind==="not-landed"?s.push({branch:i,outcome:"kept",reason:c.reason}):t.dryRun?s.push({branch:i,outcome:"would-delete",via:c.via}):e.branchDelete(t.repoRoot,i,!0)?s.push({branch:i,outcome:"deleted",via:c.via}):s.push({branch:i,outcome:"kept",reason:"git refused to delete the branch"})}return s}a(m1,"sweepLocalBranches");function h1(e,t,n){let r=t.cfg.remote,o=e.remoteBranches?.(t.repoRoot,r);if(!o||!e.deleteRemoteBranch)return[];let s=uR(e,t,n),i=t.cfg.baseBranch??e.defaultBranch(t.repoRoot),l=[];for(let{branch:c,sha:u}of o){if(s.has(c))continue;let d=dR(e,t.repoRoot,c);if(d){l.push({branch:c,outcome:"kept",reason:d});continue}let f=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:c,sha:u,side:"remote"});if(f.kind==="not-landed"){l.push({branch:c,outcome:"kept",reason:f.reason});continue}if(t.dryRun){l.push({branch:c,outcome:"would-delete",via:f.via});continue}if(!i||!e.openPrsOnBase||!e.retargetPr){l.push({branch:c,outcome:"kept",reason:"stacked PRs could not be checked"});continue}let p=al({openPrsOnBase:e.openPrsOnBase,retargetPr:e.retargetPr},t.repoRoot,c,i);p.kind==="unknown"?l.push({branch:c,outcome:"kept",reason:"could not list the PRs stacked on it"}):p.kind==="retarget-failed"?l.push({branch:c,outcome:"kept",reason:`PR ${p.failed.map(m=>`#${m.number}`).join(", ")} stacked on it could not be moved to ${i}`}):e.deleteRemoteBranch(t.repoRoot,r,c)?l.push({branch:c,outcome:"deleted",via:f.via}):l.push({branch:c,outcome:"kept",reason:`git refused to delete ${r}/${c}`})}return l}a(h1,"sweepRemoteBranches");function Sp(e){let t=[];if(e===null||typeof e!="object"||Array.isArray(e))return{problems:["the manifest must be a JSON object"]};let n=e,r={...kp},o=a((s,i)=>{if(!(s in n)||n[s]===void 0||n[s]===null)return;let l=n[s];if(typeof l!="string"){t.push(`"${s}" must be a string, got ${fo(l)}`);return}if(!i&&l.trim().length===0){t.push(`"${s}" must not be empty`);return}r[s]=l},"str");if(o("branchPrefix",!1),o("remote",!1),"forge"in n&&n.forge!==void 0&&(n.forge==="none"?r.forge="none":t.push(`"forge" can only be "none" (a repository without pull requests), got ${fo(n.forge)}`)),"baseBranch"in n&&n.baseBranch!==void 0){let s=n.baseBranch;s===null?r.baseBranch=null:typeof s=="string"&&s.trim().length>0?r.baseBranch=s:t.push(`"baseBranch" must be a non-empty string or null, got ${fo(s)}`)}if("postCreate"in n&&n.postCreate!==void 0&&n.postCreate!==null){let s=n.postCreate;!Array.isArray(s)||s.some(i=>typeof i!="string"||i.trim().length===0)?t.push(`"postCreate" must be an array of non-empty strings, got ${fo(s)}`):r.postCreate=s}if("postMerge"in n&&n.postMerge!==void 0&&n.postMerge!==null){let s=n.postMerge,i=a(l=>{if(l===null||typeof l!="object"||Array.isArray(l))return!0;let c=l;if(typeof c.when!="string"||c.when.trim().length===0)return!0;let u=a(d=>typeof d=="string"&&d.trim().length>0,"nonEmpty");return!!(c.run!==void 0&&!u(c.run)||c.note!==void 0&&!u(c.note)||c.run===void 0&&c.note===void 0||c.cwd!==void 0&&typeof c.cwd!="string"||c.each!==void 0&&c.each!=="dir"||c.each!=="dir"&&[c.run,c.note].some(d=>typeof d=="string"&&d.includes("{name}")))},"bad");!Array.isArray(s)||s.some(i)?t.push(`"postMerge" must be an array of {when, run?, note?, cwd?, each?: "dir"} with a non-empty when and at least one of run/note ({name} only together with each: "dir"), got ${fo(s)}`):r.postMerge=s.map(l=>({when:l.when,...typeof l.run=="string"?{run:l.run}:{},...typeof l.note=="string"?{note:l.note}:{},...typeof l.cwd=="string"?{cwd:l.cwd}:{},...l.each==="dir"?{each:"dir"}:{}}))}if("regenerate"in n&&n.regenerate!==void 0&&n.regenerate!==null){let s=n.regenerate,i=a(c=>typeof c=="string"&&c.trim().length>0,"nonEmpty"),l=a(c=>c===null||typeof c!="object"||Array.isArray(c)||!i(c.when)||!i(c.run),"bad");!Array.isArray(s)||s.some(l)?t.push(`"regenerate" must be an array of {when, run} with non-empty strings, got ${fo(s)}`):r.regenerate=s.map(c=>({when:c.when,run:c.run}))}return t.length>0?{problems:t}:{cfg:r}}a(Sp,"parseFeatureConfig");function fo(e){return e===null?"null":Array.isArray(e)?"an array":typeof e}a(fo,"describe");function mR(e){let t=pR(e,kr),n;try{n=fR(t,"utf8")}catch{return{kind:"defaults",cfg:{...kp}}}let r;try{r=JSON.parse(n)}catch(s){return{kind:"invalid",path:t,problems:[`not valid JSON: ${s.message}`]}}let o=Sp(r);return"problems"in o?{kind:"invalid",path:t,problems:o.problems}:{kind:"loaded",cfg:o.cfg,path:t}}a(mR,"loadFeatureConfig");function g1(e){return e.split(`
|
|
374
|
+
`)}}a(Ds,"prTextFrom");function co(e,t,n){if(t!==!0)return{kind:"ok"};if(pp(e))return{kind:"ok"};let r=Ds(n)?.title??null;return{kind:"not-conventional",title:e,suggestion:r!==null&&pp(r)?r:null}}a(co,"judgeTitle");function e1(e){return e==="dashboard"||e==="terminal"||e==="carried"?e:null}a(e1,"readVia");function t1(e){if(typeof e!="object"||e===null)return null;let t=e.head_sha;return typeof t=="string"?t:null}a(t1,"readCarriedFrom");function gp(e){let t=e1(e.answered_via),n=t==="dashboard"&&e.verified!==!1;return{answeredVia:t,carriedFrom:t1(e.carried_from),verified:n,mfa:n&&typeof e.mfa=="boolean"?e.mfa:null}}a(gp,"readProof");function n1(e){return{answeredVia:e.answeredVia,carriedFrom:e.carriedFrom,verified:e.verified,mfa:e.mfa}}a(n1,"proofOf");function uo(e){return e.answeredVia==="terminal"?" in a terminal (reported, not verified)":e.answeredVia==="carried"?` \u2014 carried from ${e.carriedFrom?e.carriedFrom.slice(0,7):"an earlier head"} across promote's own commits (reported: checked by the CLI, not verified)`:e.verified?` in a browser session (verified${e.mfa===!0?", with a second factor":e.mfa===!1?", without a second factor":""})`:e.answeredVia==="dashboard"?" (reported, not verified)":" through a channel this CLI does not know (not shown as verified)"}a(uo,"viaPhrase");function js(e,t){return t?`${e}/planning/${t}`:e}a(js,"approvalPageUrl");function cl(e){if(e.ok!==!0)return null;let t=e.state;if(t!=="not_required"&&t!=="none"&&t!=="pending"&&t!=="approved"&&t!=="declined")return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof e.decision=="object"&&e.decision!==null?e.decision:{},o=typeof e.pr=="object"&&e.pr!==null?e.pr:null;return{state:t,objectKey:typeof n.key=="string"?n.key:null,...gp(r),pr:o&&typeof o.state=="string"?{state:o.state,headSha:typeof o.head_sha=="string"?o.head_sha:null}:null}}a(cl,"readApprovalState");var ul={timeoutMs:30*6e4,intervalMs:15e3},r1=24*60*6e4,o1=4;function YS(e){let t=/^\s*(\d+)\s*([smh]?)\s*$/i.exec(e);if(!t)return null;let n=Number(t[1]),r=(t[2]??"").toLowerCase(),o=r==="s"?n*1e3:r==="h"?n*36e5:n*6e4;return o>0&&o<=r1?o:null}a(YS,"parseWaitDuration");function hp(e){return e%36e5===0?`${e/36e5} h`:e%6e4===0?`${e/6e4} min`:`${Math.round(e/1e3)} s`}a(hp,"humanDuration");async function s1(e,t,n){let r=n.now()+t.timeoutMs;n.note(`waiting for a person to approve PR #${e.prNumber} at ${e.headSha.slice(0,7)} on ${e.url} (up to ${hp(t.timeoutMs)}, checking every ${hp(t.intervalMs)}).`);let o=0;for(;;){let s=await n.readState();if(s.kind==="unknown_tool")return{kind:"failed",reason:"the server cannot read merge approvals yet (older maind server) \u2014 --wait needs it"};if(s.kind==="invalid_params")return{kind:"failed",reason:s.reason};let i=s.kind==="ok"?cl(s.body):null;if(i===null){if(o+=1,o>=o1)return{kind:"failed",reason:s.kind==="failed"?s.reason:typeof s.body.error=="string"?s.body.error:"unreadable answer"}}else switch(o=0,i.state){case"approved":return{kind:"approved",via:i.answeredVia??"unknown",proof:n1(i)};case"declined":return{kind:"declined"};case"not_required":return{kind:"approved",via:"not_required"};case"none":case"pending":{let c=n.currentHead();if(c!==null&&c!==e.headSha)return{kind:"moved",head:c};let u=n.baseConflict?.()??null;if(u!==null&&u.length>0)return{kind:"base-conflict",files:u};if(i.state==="none")return{kind:"gone",prState:i.pr?.state??null};break}}let l=r-n.now();if(l<=0)return{kind:"timeout"};await n.sleep(Math.min(t.intervalMs,l))}}a(s1,"waitForApproval");function i1(e){if(e.ok!==!0)return null;let t=e.state;if(t!=="not_required"&&t!=="approved"&&t!=="declined"&&t!=="pending")return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof e.decision=="object"&&e.decision!==null?e.decision:{};return{state:t,objectKey:typeof n.key=="string"?n.key:null,question:typeof r.question=="string"?r.question:null,terminalAccepted:e.terminal_accepted!==!1,...gp(r)}}a(i1,"readApprovalAnswer");function XS(e,t){if(e.ok!==!0)return null;let n=typeof e.object=="object"&&e.object!==null?e.object:{},r=typeof n.key=="string"?n.key:null,o=typeof e.decision=="object"&&e.decision!==null?e.decision:{},s=typeof e.hint=="string"?e.hint:null;switch(e.state){case"approved":{if(e.carried!==!0)return{kind:"own",objectKey:r};let i=typeof e.carried_from=="object"&&e.carried_from!==null?e.carried_from:{};return{kind:"carries",from:typeof i.head_sha=="string"?i.head_sha:null,via:typeof i.via_sha=="string"?i.via_sha:null,links:typeof e.links=="number"?e.links:0,objectKey:r}}case"declined":{let i=o.head_sha;return typeof i=="string"&&i!==t?{kind:"declined",at:i,objectKey:r}:{kind:"own",objectKey:r}}case"none":return{kind:"none",reason:typeof e.reason=="string"?e.reason:null,hint:s,objectKey:r};case"not_required":return{kind:"own",objectKey:r};default:return null}}a(XS,"readCarryAnswer");function dl(e){let t=e.via??e.from,n=e.links,r=t?`the approval of ${t.slice(0,7)}`:"an earlier approval",o=e.from&&t&&e.from!==t?` (itself carried from ${e.from.slice(0,7)})`:"";return`${r}${o} carries: the ${n} commit${n===1?"":"s"} on top ${n===1?"is":"are"} promote's own and re-derive exactly`}a(dl,"carriesPhrase");function a1(e,t){if(e===null)return!1;let n=/^\s*#?\s*(\d+)\s*$/.exec(e);return n!==null&&Number(n[1])===t}a(a1,"confirmsPrNumber");async function mp(e,t,n){let r=e.headSha.slice(0,7),o=await s1({prNumber:e.prNumber,headSha:e.headSha,url:n},t.wait.options,{readState:a(()=>t.call("work_object_merge_approval_state",{objectId:e.objectId,ref:e.ref,headSha:e.headSha}),"readState"),currentHead:t.wait.currentHead,...t.wait.baseConflict?{baseConflict:t.wait.baseConflict}:{},sleep:t.wait.sleep,now:t.wait.now,note:t.note});switch(o.kind){case"approved":return o.via==="not_required"?(t.note("the organization no longer asks for merge approvals \u2014 continuing."),{kind:"proceed",via:"not_required"}):(t.note(`merge of PR #${e.prNumber} at ${r} approved${uo(o.proof)}.`),{kind:"proceed",via:o.via});case"declined":return t.note(`a person chose "Don't merge" for PR #${e.prNumber} at ${r} on ${n} \u2014 nothing was merged.`),{kind:"stop"};case"timeout":return t.note(`still not approved after ${hp(t.wait.options.timeoutMs)} \u2014 nothing was merged. The question keeps waiting on ${n}; run promote again (with --wait to keep waiting).`),{kind:"pending",url:n};case"moved":return t.note(`PR #${e.prNumber} moved to ${o.head.slice(0,7)} while waiting \u2014 an approval of ${r} would not cover it. Nothing was merged; run promote again, it asks for the new head.`),{kind:"stop"};case"base-conflict":{let s=o.files.length>3?`${o.files.slice(0,3).join(", ")} and ${o.files.length-3} more`:o.files.join(", ");t.note(`${e.base} moved while waiting, and PR #${e.prNumber} at ${r} now conflicts with it in ${s}. Stopped waiting \u2014 an approval of this head could not be merged.`),t.note("Merge the base into the branch, resolve, push, and run promote again: the open question would not cover the resolution. Nothing was merged.");try{await t.tellObject?.(`**Don't approve PR #${e.prNumber} at \`${r}\` yet** \u2014 \`${e.base}\` moved while the merge approval was waiting, and this head now conflicts with it in ${s}. An approval of it could not be merged; the resolution is a new head that will be asked for again. (maind feature promote --wait stopped waiting.)`)}catch{}return{kind:"stop"}}case"gone":return t.note(`the merge question for PR #${e.prNumber} disappeared without an answer`+(o.prState?` \u2014 the pull request is ${o.prState}`:"")+". Nothing was merged."),{kind:"stop"};case"failed":return t.note(`stopped waiting: ${o.reason}. Nothing was merged; the question still waits on ${n}.`),{kind:"stop"}}}a(mp,"awaitAnswer");async function l1(e,t,n){let r=e.carry?.links??[],o=e.carry?.stopLine??null;if(r.length===0)return o&&t.note(`no earlier approval carries \u2014 ${o}.`),null;let s=await t.call("work_object_carry_merge_approval",{...n,chain:r});if(s.kind==="unknown_tool")return t.note("the server cannot carry an approval across promote's own commits yet (older maind server) \u2014 asking for this head."),null;if(s.kind==="failed"||s.kind==="invalid_params")return t.note(`an earlier approval could not be carried (${s.reason}) \u2014 asking for this head.`),null;let i=XS(s.body,e.headSha);if(i===null){let c=typeof s.body.error=="string"?s.body.error:"unreadable answer";return t.note(`an earlier approval could not be carried (${c}) \u2014 asking for this head.`),null}let l=e.headSha.slice(0,7);switch(i.kind){case"carries":return t.note(`merge of PR #${e.prNumber} at ${l} approved \u2014 ${dl(i)} (recorded as carried: reported, checked by the CLI, not verified).`),{kind:"proceed",via:"carried"};case"declined":{let c=js(e.dashboardUrl,i.objectKey);return t.note(`a person chose "Don't merge" for ${i.at.slice(0,7)} on ${c}, and ${l} only adds promote's own commits to it \u2014 nothing was merged.`),t.note("A new push asks again; or discuss the answer at the object."),{kind:"stop"}}case"none":return i.reason!=="no_approval"&&i.hint?t.note(i.hint):o&&t.note(`no earlier approval carries \u2014 ${o}.`),null;case"own":return null}}a(l1,"tryCarry");function ll(e,t,n){return e.objectId!==null?`in a browser session on ${t} ("Waiting on you")`:n?"by running promote in their own terminal \u2014 no browser page shows a change request without a work object yet (ADR-393 Phase 4)":"nowhere yet: this organization counts only browser answers, and no browser page shows a change request without a work object (ADR-393 Phase 4)"}a(ll,"whereToApprove");async function JS(e,t){let n={objectId:e.objectId,ref:e.ref,headSha:e.headSha,base:e.base},r=e.headSha.slice(0,7),o=await l1(e,t,n);if(o!==null)return o;let s=await t.call("work_object_merge_approval",n);if(s.kind==="unknown_tool")return t.note("the server does not know merge approvals yet (older maind server) \u2014 merging without the approval gate. Update the server to have it enforced."),{kind:"proceed",via:"server_without_gate"};if(s.kind==="failed"||s.kind==="invalid_params")return t.note(`the merge approval could not be checked (${s.reason}) \u2014 nothing was merged.`),{kind:"stop"};let i=i1(s.body);if(i===null){let u=typeof s.body.error=="string"?s.body.error:"unreadable answer",d=typeof s.body.hint=="string"?` ${s.body.hint}`:"";return t.note(`the merge approval could not be checked (${u}).${d} Nothing was merged.`),{kind:"stop"}}let l=js(e.dashboardUrl,i.objectKey);switch(i.state){case"not_required":return{kind:"proceed",via:"not_required"};case"approved":return t.note(`merge of PR #${e.prNumber} at ${r} approved${uo(i)}.`),{kind:"proceed",via:i.answeredVia??"unknown"};case"declined":return t.note(`a person chose "Don't merge" for PR #${e.prNumber} at ${r} on ${l} \u2014 nothing was merged.`),t.note("A new push asks again; or change the answer's premise and discuss it at the object."),{kind:"stop"};case"pending":break}let c=t.wait;if(t.isTty()&&i.terminalAccepted){t.note(`your organization asks a person to approve every merge. PR #${e.prNumber} at ${r} into ${e.base}.`);let u=await t.prompt(`Type the pull request number (${e.prNumber}) to approve the merge, or press Enter to `+(c?e.objectId!==null?"wait for an approval in a browser session: ":"wait for someone to approve it in their own terminal: ":"stop: "));if(!a1(u,e.prNumber))return c?mp(e,{...t,wait:c},l):(t.note(e.objectId!==null?`not approved \u2014 nothing was merged. The question also waits on ${l}.`:"not approved \u2014 nothing was merged."),{kind:"stop"});let d=await t.call("work_object_record_terminal_approval",n);if(d.kind==="ok"&&d.body.ok!==!0&&d.body.error==="terminal_not_accepted")return t.note(`this organization counts only approvals given in a browser session \u2014 nothing was recorded from this terminal. A person approves it ${ll(e,l,!1)}.`),c?mp(e,{...t,wait:c},l):(t.note("Then run promote again \u2014 or run it with --wait. Nothing was merged."),{kind:"pending",url:l});if(d.kind!=="ok"||d.body.ok!==!0||d.body.state!=="approved"){let f=d.kind==="ok"?d.body.state==="declined"?`a person chose "Don't merge" on ${l} in the meantime`:String(d.body.error??"unreadable answer"):d.kind==="failed"||d.kind==="invalid_params"?d.reason:"the server does not know terminal approvals";return t.note(`the approval could not be recorded (${f}) \u2014 nothing was merged.`),{kind:"stop"}}if(d.body.recorded!==!0){let f=typeof d.body.decision=="object"&&d.body.decision!==null?d.body.decision:{},p=gp(f);return t.note(`merge of PR #${e.prNumber} at ${r} was already approved${uo(p)} \u2014 that answer stands; nothing was recorded from this terminal.`),{kind:"proceed",via:p.answeredVia??"unknown"}}return t.note("approved in this terminal \u2014 recorded as reported, not verified (only a browser session gives a verified answer)."),{kind:"proceed",via:"terminal"}}return t.note(`your organization asks a person to approve every merge \u2014 PR #${e.prNumber} at ${r} is not approved yet.`),c?(t.note(`A person approves it ${ll(e,l,i.terminalAccepted)}; promote merges as soon as they do.`),mp(e,{...t,wait:c},l)):(e.objectId===null||!i.terminalAccepted?t.note(`A person approves it ${ll(e,l,i.terminalAccepted)}.`):t.note(`A person approves it ${ll(e,l,!0)}, or runs promote in their own terminal.`),t.note("Then run promote again \u2014 or run it with --wait, which merges once approved. Nothing was merged. A new push to the branch asks again."),{kind:"pending",url:l})}a(JS,"runMergeApproval");function ZS(e,t){let n=t.headSha.slice(0,7),r=[],o=`PR #${t.prNumber} at ${n}`,s,i=e.state==="none"||e.state==="pending"?t.carry??null:null;switch(e.state){case"not_required":r.push(`${o}: this organization does not ask for merge approvals.`),s=0;break;case"approved":r.push(`${o}: approved${uo(e)}.`),s=0;break;case"declined":r.push(`${o}: a person chose "Don't merge" on ${t.url}.`),s=1;break;case"pending":case"none":if(i?.kind==="carries"){r.push(`${o}: not answered for this head, but ${dl(i)} \u2014 promote merges without asking.`),s=0;break}if(i?.kind==="declined"){r.push(`${o}: a person chose "Don't merge" for ${i.at.slice(0,7)} on ${t.url}, and this head only adds promote's own commits to it.`),s=1;break}r.push(e.state==="pending"?`${o}: waiting for a person to approve it in a browser session on ${t.url} ("Waiting on you").`:`${o}: nobody has been asked for this head yet \u2014 promote asks when it gets there (promote --wait waits for the answer).`),s=3;break}let l=i?bp(i):null;return l&&r.push(` ${l}`),e.pr?.headSha&&e.pr.headSha!==t.headSha&&r.push(` GitHub's connector last saw the pull request at ${e.pr.headSha.slice(0,7)} \u2014 an answer for ${n} would not cover that.`),t.localHead!==null&&t.localHead!==t.headSha&&r.push(` the local branch is at ${t.localHead.slice(0,7)}, not at the pull request head \u2014 push first; an approval covers only the pushed head.`),{lines:r,exitCode:s}}a(ZS,"approvalStateLines");async function yp(e,t,n){if(n.links.length===0)return n.stopLine?{kind:"stops",line:n.stopLine}:null;let r=await e("work_object_merge_approval_state",{...t,chain:n.links});if(r.kind==="unknown_tool")return{kind:"unavailable",why:"the server cannot read merge approvals yet"};if(r.kind==="failed"||r.kind==="invalid_params")return{kind:"unavailable",why:r.reason};let o=XS(r.body,t.headSha);return o===null?{kind:"unavailable",why:typeof r.body.error=="string"?r.body.error:"unreadable answer"}:o}a(yp,"previewCarry");function bp(e){switch(e.kind){case"stops":return`no earlier approval carries \u2014 ${e.line}.`;case"unavailable":return`whether an earlier approval carries could not be read (${e.why}).`;case"none":return e.reason!=="no_approval"&&e.hint?e.hint:null;default:return null}}a(bp,"carryNote");var c1=[5e3,2e4,45e3];function u1(e){return e.kind==="failed"&&/\b429\b|rate limit/i.test(e.reason)}a(u1,"isRateLimited");function QS(e,t){let n=t.delays??c1,r=t.now??Date.now;return async(o,s)=>{let i=0;for(let l=0;;l++){let c=await e(o,s);if(c.kind==="failed"&&c.quota!==void 0){let d=i<3?Rw(c.quota,r(),t.maxQuotaWaitMs??0):null;if(d===null)return c;i+=1,t.note(`${ka(c.quota,r())} \u2014 waiting ${Math.ceil(d/6e4)} min for it; nothing is lost.`),await t.sleep(d);continue}if(!u1(c)||l>=n.length)return c;let u=n[l];t.note(`the maind server is rate-limiting this IP (429) \u2014 retrying in ${Math.round(u/1e3)} s; nothing is lost.`),await t.sleep(u)}}}a(QS,"retryOnRateLimit");function d1(e){return/^[A-Za-z0-9_./:@-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`}a(d1,"shellQuoteArg");var f1={ok:"ok ",fail:"FAILED ",note:"note "};function eR(e){return e.map(t=>`${f1[t.verdict]} ${t.name}${t.detail?` \u2014 ${t.detail}`:""}`)}a(eR,"checkRowLines");function tR(e,t,n){let r=e.filter(o=>o.verdict==="fail");return r.length===0?n?.kind==="asks"?{lines:["promote would get past every check. The merge waits for a person's approval, and promote asks for it only when it runs \u2014 so run it yourself:",` ${n.waitCommand}`,`The question then appears on ${n.url}; promote merges as soon as a person approves it there.`],exitCode:0}:n?.kind==="given"?{lines:["promote would get past every check, and the merge approval is in \u2014 it is the go. Run:",` ${t}`],exitCode:0}:{lines:["promote would get past every check. Hand over exactly this command:",` ${t}`],exitCode:0}:{lines:[`promote would stop at ${r.length===1?"this check":`these ${r.length} checks`}: ${r.map(o=>o.name).join(", ")}.`,`Fix ${r.length===1?"it":"them"} before handing over a promote command \u2014 nothing was changed.`],exitCode:1}}a(tR,"checkVerdictLines");function _p(e,t,n){let o=[n&&e.startsWith("maind ")?`cd ${d1(n.cwd)} && npx -y @maind-dev/cli@${n.cliVersion} ${e.slice(6)}`:e,"--yes"];return t.closesObject&&o.push("--closes-object"),t.progressOnly&&o.push("--progress-only"),t.wait&&o.push("--wait"),o.join(" ")}a(_p,"promoteCommandFrom");function nR(e,t){let n="merge-approval";if("failure"in e)return{name:n,verdict:"note",detail:`not readable (${e.failure}) \u2014 promote asks the server itself`};switch(e.state){case"not_required":return{name:n,verdict:"ok",detail:"this organization does not ask for one"};case"approved":return{name:n,verdict:"ok",detail:`approved for this head${uo(e)}`};case"declined":return{name:n,verdict:"fail",detail:`a person chose "Don't merge" on ${t.url}`};case"pending":case"none":{let r=t.carry??null;if(r?.kind==="carries")return{name:n,verdict:"ok",detail:`${dl(r)} \u2014 promote records it and merges without asking`};if(r?.kind==="declined")return{name:n,verdict:"fail",detail:`a person chose "Don't merge" for ${r.at.slice(0,7)} on ${t.url}, and this head only adds promote's own commits to it`};let o=r?bp(r):null;return{name:n,verdict:"note",detail:`not asked yet \u2014 promote asks when it runs: the question then appears on ${t.url}, where a person approves in a browser session`+(t.wait?"; --wait waits for the answer":"; without a terminal, add --wait \u2014 otherwise promote stops with exit 3 instead of waiting")+(o?`; ${o.replace(/\.$/,"")}`:"")}}}}a(nR,"approvalRow");function rR(e,t){let n="required-checks";if(t===null)return{name:n,verdict:"note",detail:"not readable \u2014 promote waits for them itself"};let r=[];try{let l=JSON.parse(t);r=Array.isArray(l.statusCheckRollup)?l.statusCheckRollup:[]}catch{return{name:n,verdict:"note",detail:"not readable \u2014 promote waits for them itself"}}if(e.length===0)return{name:n,verdict:"note",detail:"no required checks could be determined \u2014 promote decides at the merge"};let o=new Map;for(let l of r){let c=typeof l.name=="string"?l.name:typeof l.context=="string"?l.context:null;c&&o.set(c,l)}let s=[],i=[];for(let l of e){let c=o.get(l);if(!c){i.push(`${l} (not registered)`);continue}let u=String(c.conclusion??c.state??"").toUpperCase();["FAILURE","ERROR","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE"].includes(u)?s.push(l):u!=="SUCCESS"&&u!=="NEUTRAL"&&u!=="SKIPPED"&&i.push(l)}return s.length>0?{name:n,verdict:"fail",detail:`red on the pull request head: ${s.join(", ")}`}:i.length>0?{name:n,verdict:"note",detail:`still running: ${i.join(", ")} \u2014 promote waits for them`}:{name:n,verdict:"ok",detail:`green: ${e.join(", ")}`}}a(rR,"requiredChecksRow");import{readFileSync as fR}from"node:fs";import{join as pR}from"node:path";var kp={branchPrefix:"feat/",remote:"origin",baseBranch:null,postCreate:[],postMerge:null,regenerate:[]},kr=".maind/feature.json";function wp(e){return!e||e.trim().length===0?"a feature name is required":e.startsWith("-")?`"${e}" must not start with a dash`:e.includes("..")?`"${e}" must not contain ".."`:/^[A-Za-z0-9._-]+$/.test(e)?null:`"${e}" may only contain letters, digits, dot, underscore and dash`}a(wp,"validateFeatureName");function fl(e,t){return`${e.replace(/[/\\]+$/,"")}-${t}`}a(fl,"worktreePathFor");function vp(e,t){return`${e.branchPrefix}${t}`}a(vp,"branchFor");function oR(e){return!e||e.trim().length===0?"a branch name is required":e.startsWith("-")?`"${e}" must not start with a dash`:e.includes("..")?`"${e}" must not contain ".."`:e.startsWith("/")||e.endsWith("/")||e.includes("//")?`"${e}" must not start or end with "/" or contain "//"`:e.endsWith(".lock")||e.endsWith(".")?`"${e}" must not end with ".lock" or "."`:/^[A-Za-z0-9._/-]+$/.test(e)?null:`"${e}" may only contain letters, digits, dot, underscore, dash and slash`}a(oR,"validateBranchName");function p1(e,t){return fl(e,t.replace(/\//g,"-"))}a(p1,"worktreePathForBranch");function Ue(e,t){return t.byBranch?`maind feature ${e} --branch ${t.branch}`:`maind feature ${e} ${t.label}`}a(Ue,"featureCommand");function sR(e,t){if(t.branch!==void 0){let o=oR(t.branch);if(o)return{kind:"invalid",reason:o};let s=e.worktreeList(t.repoRoot),i=s[0]?.path??null,l=s.find(c=>c.branch===t.branch);return l&&(l.path===i||l.path===t.repoRoot)?{kind:"invalid",reason:`${t.branch} is checked out in the main checkout (${l.path}) \u2014 ship/promote/done work on a branch in its own worktree`}:{kind:"ok",target:{label:t.branch,branch:t.branch,worktree:l?.path??p1(t.repoRoot,t.branch),byBranch:!0}}}let n=t.name??"",r=wp(n);return r?{kind:"invalid",reason:r}:{kind:"ok",target:{label:n,branch:vp(t.cfg,n),worktree:fl(t.repoRoot,n),byBranch:!1}}}a(sR,"resolveFeatureTarget");function iR(e,t){return e.worktreeAddExisting?e.exists(t.path)?{kind:"path-exists",path:t.path}:(e.fetch(t.repoRoot,t.remote),!e.branchExists(t.repoRoot,t.branch)&&!e.refExists(t.repoRoot,`${t.remote}/${t.branch}`)?{kind:"no-branch",branch:t.branch,remote:t.remote}:e.worktreeAddExisting(t.repoRoot,t.path,t.branch)?{kind:"ok",path:t.path,branch:t.branch}:{kind:"worktree-failed",path:t.path,branch:t.branch}):{kind:"unsupported"}}a(iR,"planCheckout");function aR(e,t){let n=wp(t.name);if(n)return{kind:"invalid-name",reason:n};let r=fl(t.repoRoot,t.name);if(e.exists(r))return{kind:"path-exists",path:r};let o=vp(t.cfg,t.name);if(e.branchExists(t.repoRoot,o))return{kind:"branch-exists",branch:o};let s=t.cfg.baseBranch??e.defaultBranch(t.repoRoot);if(!s)return{kind:"no-base-branch"};e.fetch(t.repoRoot,t.cfg.remote);let i=`${t.cfg.remote}/${s}`;return e.refExists(t.repoRoot,i)?e.worktreeAdd(t.repoRoot,r,o,i)?{kind:"ok",path:r,branch:o,startPoint:i,provisioned:t.requirements}:{kind:"worktree-failed",path:r,branch:o}:{kind:"start-point-missing",startPoint:i,remote:t.cfg.remote}}a(aR,"planStart");function pl(e,t){if(t.target){let u=oR(t.target.branch);if(u)return{kind:"invalid-name",reason:u}}else{let u=wp(t.name);if(u)return{kind:"invalid-name",reason:u}}let n=t.target?.worktree??fl(t.repoRoot,t.name),r=t.target?.branch??vp(t.cfg,t.name),o=e.branchExists(t.repoRoot,r),s=e.exists(n);if(!o&&!s)return{kind:"ok",removedWorktree:!1,deletedBranch:!1};let i=e.openPr(t.repoRoot,r);if(i)return{kind:"pr-open",branch:r,prNumber:i.number,url:i.url};let l=null;if(o){let u=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:r,sha:e.branchHead(t.repoRoot,r),side:"local"});if(u.kind==="not-landed")return{kind:"not-landed",branch:r,path:s?n:null,reason:u.reason};l=u.via}let c=!1;if(s){if(e.isDirty(n)!==!1)return{kind:"dirty",path:n};if(t.dryRun)return{kind:"would-remove",path:n,via:l};if(!e.worktreeRemove(t.repoRoot,n))return{kind:"remove-failed",path:n};c=!0}return t.dryRun?{kind:"would-remove",path:null,via:l}:!o||l===null?{kind:"ok",removedWorktree:c,deletedBranch:!1}:e.branchDelete(t.repoRoot,r,!0)?{kind:"ok",removedWorktree:c,deletedBranch:!0,via:l}:{kind:"branch-unverified",branch:r,reason:"git refused to delete the branch"}}a(pl,"planDone");function xp(e,t){let{branch:n,sha:r}=t,o=t.cfg.baseBranch??e.defaultBranch(t.repoRoot);if(r&&o&&e.isAncestor(t.repoRoot,r,`${t.cfg.remote}/${o}`)===!0)return{kind:"landed",via:"fast-forward"};let s=e.mergedPr(t.repoRoot,n),i=s?s.state!=="MERGED"?`its PR is ${s.state}, not MERGED`:!r||!s.headSha||s.headSha!==r?`the merged PR's head (${s.headSha??"unknown"}) is not the ${t.side} head (${r??"unknown"})`:null:`${n} is not contained in ${t.cfg.remote}/${o??"<default branch>"} and no merged PR carries it`;return i?{kind:"not-landed",reason:i}:{kind:"landed",via:"squash-proof"}}a(xp,"proveLanded");function lR(e,t){let n=[];for(let r of e.worktreeList(t.repoRoot))!r.branch||!r.branch.startsWith(t.cfg.branchPrefix)||n.push({name:r.branch.slice(t.cfg.branchPrefix.length),branch:r.branch,path:r.path});return n.sort((r,o)=>r.name.localeCompare(o.name))}a(lR,"listFeatureWorktrees");function cR(e,t){let n=t.real??(u=>u),r=e.worktreeList(t.repoRoot),o=r.slice(1),s=[];for(let u of o){let d=n(u.path);if(!u.branch){s.push({path:u.path,branch:null,outcome:"kept",reason:"detached HEAD \u2014 no branch to prove a merge for"});continue}if(so(n(t.cwd),d)){s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"you are standing in it \u2014 run the sweep from the main checkout"});continue}if(t.liveAgentCwds.some(p=>so(n(p),d))){s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"a live agent session stands in it"});continue}let f=pl(e,{repoRoot:t.repoRoot,name:u.branch,cfg:t.cfg,target:{branch:u.branch,worktree:u.path},dryRun:t.dryRun});switch(f.kind){case"ok":f.removedWorktree?s.push({path:u.path,branch:u.branch,outcome:"removed",via:f.via??null}):s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"git no longer sees the worktree"});break;case"would-remove":s.push({path:u.path,branch:u.branch,outcome:"would-remove",via:f.via});break;case"branch-unverified":s.push({path:u.path,branch:u.branch,outcome:"removed",via:null,branchKept:f.reason});break;case"pr-open":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:`PR #${f.prNumber} is still open`});break;case"not-landed":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:f.reason});break;case"dirty":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"uncommitted or untracked changes"});break;case"remove-failed":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:"git refused to remove it"});break;case"invalid-name":s.push({path:u.path,branch:u.branch,outcome:"kept",reason:f.reason});break}}let i=t.orphanDirs?t.orphanDirs(t.repoRoot,r.map(u=>u.path)):[],l=m1(e,t,r),c=t.remote?h1(e,t,r):void 0;return{entries:s,orphans:i,branches:l,...c?{remoteBranches:c}:{}}}a(cR,"planSweep");function uR(e,t,n){let r=t.cfg.baseBranch??e.defaultBranch(t.repoRoot),o=new Set(n.flatMap(s=>s.branch?[s.branch]:[]));return r&&o.add(r),o}a(uR,"untouchable");function dR(e,t,n){let r=e.openPr(t,n);return r===void 0?'could not ask whether a PR is open (gh/glab did not answer) \u2014 if this repository has no pull requests, declare "forge": "none" in .maind/feature.json':r?`PR #${r.number} is still open`:null}a(dR,"openPrReason");function m1(e,t,n){let r=e.localBranches?.(t.repoRoot);if(!r)return[];let o=uR(e,t,n),s=[];for(let i of r){if(o.has(i))continue;let l=dR(e,t.repoRoot,i);if(l){s.push({branch:i,outcome:"kept",reason:l});continue}let c=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:i,sha:e.branchHead(t.repoRoot,i),side:"local"});c.kind==="not-landed"?s.push({branch:i,outcome:"kept",reason:c.reason}):t.dryRun?s.push({branch:i,outcome:"would-delete",via:c.via}):e.branchDelete(t.repoRoot,i,!0)?s.push({branch:i,outcome:"deleted",via:c.via}):s.push({branch:i,outcome:"kept",reason:"git refused to delete the branch"})}return s}a(m1,"sweepLocalBranches");function h1(e,t,n){let r=t.cfg.remote,o=e.remoteBranches?.(t.repoRoot,r);if(!o||!e.deleteRemoteBranch)return[];let s=uR(e,t,n),i=t.cfg.baseBranch??e.defaultBranch(t.repoRoot),l=[];for(let{branch:c,sha:u}of o){if(s.has(c))continue;let d=dR(e,t.repoRoot,c);if(d){l.push({branch:c,outcome:"kept",reason:d});continue}let f=xp(e,{repoRoot:t.repoRoot,cfg:t.cfg,branch:c,sha:u,side:"remote"});if(f.kind==="not-landed"){l.push({branch:c,outcome:"kept",reason:f.reason});continue}if(t.dryRun){l.push({branch:c,outcome:"would-delete",via:f.via});continue}if(!i||!e.openPrsOnBase||!e.retargetPr){l.push({branch:c,outcome:"kept",reason:"stacked PRs could not be checked"});continue}let p=al({openPrsOnBase:e.openPrsOnBase,retargetPr:e.retargetPr},t.repoRoot,c,i);p.kind==="unknown"?l.push({branch:c,outcome:"kept",reason:"could not list the PRs stacked on it"}):p.kind==="retarget-failed"?l.push({branch:c,outcome:"kept",reason:`PR ${p.failed.map(m=>`#${m.number}`).join(", ")} stacked on it could not be moved to ${i}`}):e.deleteRemoteBranch(t.repoRoot,r,c)?l.push({branch:c,outcome:"deleted",via:f.via}):l.push({branch:c,outcome:"kept",reason:`git refused to delete ${r}/${c}`})}return l}a(h1,"sweepRemoteBranches");function Sp(e){let t=[];if(e===null||typeof e!="object"||Array.isArray(e))return{problems:["the manifest must be a JSON object"]};let n=e,r={...kp},o=a((s,i)=>{if(!(s in n)||n[s]===void 0||n[s]===null)return;let l=n[s];if(typeof l!="string"){t.push(`"${s}" must be a string, got ${fo(l)}`);return}if(!i&&l.trim().length===0){t.push(`"${s}" must not be empty`);return}r[s]=l},"str");if(o("branchPrefix",!1),o("remote",!1),"forge"in n&&n.forge!==void 0&&(n.forge==="none"?r.forge="none":t.push(`"forge" can only be "none" (a repository without pull requests), got ${fo(n.forge)}`)),"baseBranch"in n&&n.baseBranch!==void 0){let s=n.baseBranch;s===null?r.baseBranch=null:typeof s=="string"&&s.trim().length>0?r.baseBranch=s:t.push(`"baseBranch" must be a non-empty string or null, got ${fo(s)}`)}if("postCreate"in n&&n.postCreate!==void 0&&n.postCreate!==null){let s=n.postCreate;!Array.isArray(s)||s.some(i=>typeof i!="string"||i.trim().length===0)?t.push(`"postCreate" must be an array of non-empty strings, got ${fo(s)}`):r.postCreate=s}if("postMerge"in n&&n.postMerge!==void 0&&n.postMerge!==null){let s=n.postMerge,i=a(l=>{if(l===null||typeof l!="object"||Array.isArray(l))return!0;let c=l;if(typeof c.when!="string"||c.when.trim().length===0)return!0;let u=a(d=>typeof d=="string"&&d.trim().length>0,"nonEmpty");return!!(c.run!==void 0&&!u(c.run)||c.note!==void 0&&!u(c.note)||c.run===void 0&&c.note===void 0||c.cwd!==void 0&&typeof c.cwd!="string"||c.each!==void 0&&c.each!=="dir"||c.each!=="dir"&&[c.run,c.note].some(d=>typeof d=="string"&&d.includes("{name}")))},"bad");!Array.isArray(s)||s.some(i)?t.push(`"postMerge" must be an array of {when, run?, note?, cwd?, each?: "dir"} with a non-empty when and at least one of run/note ({name} only together with each: "dir"), got ${fo(s)}`):r.postMerge=s.map(l=>({when:l.when,...typeof l.run=="string"?{run:l.run}:{},...typeof l.note=="string"?{note:l.note}:{},...typeof l.cwd=="string"?{cwd:l.cwd}:{},...l.each==="dir"?{each:"dir"}:{}}))}if("regenerate"in n&&n.regenerate!==void 0&&n.regenerate!==null){let s=n.regenerate,i=a(c=>typeof c=="string"&&c.trim().length>0,"nonEmpty"),l=a(c=>c===null||typeof c!="object"||Array.isArray(c)||!i(c.when)||!i(c.run),"bad");!Array.isArray(s)||s.some(l)?t.push(`"regenerate" must be an array of {when, run} with non-empty strings, got ${fo(s)}`):r.regenerate=s.map(c=>({when:c.when,run:c.run}))}return t.length>0?{problems:t}:{cfg:r}}a(Sp,"parseFeatureConfig");function fo(e){return e===null?"null":Array.isArray(e)?"an array":typeof e}a(fo,"describe");function mR(e){let t=pR(e,kr),n;try{n=fR(t,"utf8")}catch{return{kind:"defaults",cfg:{...kp}}}let r;try{r=JSON.parse(n)}catch(s){return{kind:"invalid",path:t,problems:[`not valid JSON: ${s.message}`]}}let o=Sp(r);return"problems"in o?{kind:"invalid",path:t,problems:o.problems}:{kind:"loaded",cfg:o.cfg,path:t}}a(mR,"loadFeatureConfig");function g1(e){return e.split(`
|
|
375
375
|
`).map(t=>t.trim()).filter(t=>t.length>0&&!t.startsWith("#"))}a(g1,"parseWorktreeRequirements");function Rp(e){try{return g1(fR(pR(e,".maind","worktree-requirements"),"utf8"))}catch{return[]}}a(Rp,"loadWorktreeRequirements");import{execFileSync as y1}from"node:child_process";import{readFileSync as b1,writeFileSync as _1}from"node:fs";import{isAbsolute as k1,resolve as w1}from"node:path";var v1="maind-postcreate-head";function po(e,t){try{return y1("git",t,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:3e4}).trim()}catch{return null}}a(po,"git");function hR(e){let t=po(e,["rev-parse","--git-path",v1]);return t?k1(t)?t:w1(e,t):null}a(hR,"postCreateMarkPath");function gR(e){let t=po(e,["rev-parse","HEAD"]),n=hR(e);if(!(!t||!n))try{_1(n,`${t}
|
|
376
376
|
`)}catch{}}a(gR,"recordPostCreate");function x1(e){let t=hR(e);if(!t)return null;try{let n=b1(t,"utf8").trim();return/^[0-9a-f]{40,64}$/.test(n)?n:null}catch{return null}}a(x1,"readMark");function S1(e,t=()=>!1){return e.some(n=>n.trim()!==""&&!/\.(md|mdx|markdown)$/i.test(n)&&!t(n))}a(S1,"changesNeedReinstall");function R1(e,t){if(e===null||t===null)return!1;try{let n=JSON.parse(e),r=JSON.parse(t);if(!n||!r||typeof n!="object"||typeof r!="object"||Array.isArray(n)||Array.isArray(r))return!1;let o=a(s=>JSON.stringify({...s,version:void 0}),"strip");return o(n)===o(r)}catch{return!1}}a(R1,"onlyVersionDiffers");function Ep(e){let t=po(e,["rev-parse","HEAD"]);if(!t)return{stale:!0,reason:"the worktree's HEAD is unreadable"};let n=x1(e);if(!n)return{stale:!0,reason:"no record of postCreate in this worktree"};if(n===t)return{stale:!1};let r=po(e,["-c","core.quotepath=off","diff","--name-only",n,t]);if(r===null)return{stale:!0,reason:`the tree moved since postCreate (${n.slice(0,9)}), and git cannot say how`};let o=r.split(`
|
|
377
377
|
`).filter(Boolean);return S1(o,a(i=>/(^|\/)package\.json$/.test(i)&&R1(po(e,["show",`${n}:${i}`]),po(e,["show",`${t}:${i}`])),"versionOnly"))?{stale:!0,reason:`the tree moved since postCreate ran (${n.slice(0,9)} \u2192 ${t.slice(0,9)}, ${o.length} file(s), not only docs)`}:{stale:!1}}a(Ep,"installFreshness");import{execFileSync as hE,spawnSync as aU}from"node:child_process";import{existsSync as lU,readFileSync as cU}from"node:fs";import{join as vl}from"node:path";var E1={"migration-first":"push the migration before the code goes live","code-first":"release and deploy the code before you push the migration","any-order":"either order works"};function yR(e){return e.verdict==="declared"&&e.declared!==null?[`note rollout \u2014 ${e.declared.order}: ${E1[e.declared.order]} (${e.declared.reason})`]:e.verdict==="undeclared"?e.findings.map(t=>`note rollout \u2014 ${t.title}`):[]}a(yR,"rolloutRows");var bR="skipping the gate";function _R(e,t){return e!==0?"fail":t.includes(bR)?"not-checked":"pass"}a(_R,"classifyGate");function kR(e){let t=e.split(`
|