@maind-dev/cli 0.136.0 → 0.136.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -265,7 +265,7 @@ maind: and re-run \`maind launch claude\`. (Or launch without routing: \`maind l
265
265
  `,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(`
266
266
  `);return{stdout:e.toml??`(using existing config: ${e.configPath})
267
267
  `,stderr:n+`
268
- `}}a(aS,"renderDryRun");import{createRequire as r1}from"node:module";function o1(){return"0.136.0";try{return r1(import.meta.url)("../package.json").version}catch{return"0.0.0-unknown"}}a(o1,"resolveVersion");var me=o1();var p1=5e3;function ct(e){process.stderr.write(`maind: ${e}
268
+ `}}a(aS,"renderDryRun");import{createRequire as r1}from"node:module";function o1(){return"0.136.1";try{return r1(import.meta.url)("../package.json").version}catch{return"0.0.0-unknown"}}a(o1,"resolveVersion");var me=o1();var p1=5e3;function ct(e){process.stderr.write(`maind: ${e}
269
269
  `)}a(ct,"err");function m1(e){return e instanceof Error?e.message:String(e)}a(m1,"msg");var h1=new Set(["--config","--route","--mcp-url"]);function cS(e){let t=a(o=>{let i=e.indexOf(o);if(!(i===-1||i+1>=e.length))return e[i+1]},"get"),n=a(o=>e.includes(o),"has"),r=null;for(let o=0;o<e.length;o++){let i=e[o];if(i.startsWith("-")){h1.has(i)&&o++;continue}r=i;break}return{agent:r,dryRun:n("--dry-run"),config:t("--config")??null,route:t("--route")??zx,direct:n("--direct"),noFallback:n("--no-fallback"),noInstall:n("--no-install"),json:n("--json"),mcpUrl:t("--mcp-url")??null}}a(cS,"parseLaunchArgs");var g1={probe(e,t){let n=lS(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 lS("uv",[...e],{stdio:["ignore","inherit","inherit"]}).status},spawnInteractive(e){let t=Date.now();return new Promise(n=>{let r=!1,o=i1(e[0],e.slice(1),{stdio:"inherit",env:process.env}),i=a(()=>{},"onSigint"),s=a(d=>()=>{try{o.kill(d)}catch{}},"makeForward"),l=s("SIGTERM"),c=s("SIGHUP");process.on("SIGINT",i),process.on("SIGTERM",l),process.on("SIGHUP",c);let u=a(()=>{process.removeListener("SIGINT",i),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=ke()}catch(r){if(r instanceof Ae)return null;throw r}try{let r=new Te({apiKey:n.apiKey,deviceId:n.deviceId,clientVersion:me,mcpUrl:e??void 0,projectKey:t}),o=new Promise(s=>{setTimeout(()=>s(null),p1).unref()}),i=r.callTool("get_template",{template_id:Yx}).then(s=>typeof s.template_body=="string"&&s.template_body.trim().length>0?s.template_body:null).catch(()=>null);return await Promise.race([i,o])}catch{return null}},writeConfig(e,t){let n=f1(e);n&&n!=="."&&l1(n,{recursive:!0}),u1(e,t,"utf8")},readFile(e){try{return c1(e,"utf8")}catch{return null}},fileExists(e){return a1(e)},repoRoot(){try{let e=s1("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 Xe().projectKey},userConfigDir(){return Lr()},homeDir(){return d1()},env:process.env,platform:process.platform};function y1(e,t){return e!==null?Rp(e,".maind",yp):Rp(t,yp)}a(y1,"resolveRoutesPath");async function uS(e,t=g1){let n=Xx(e.agent);if(n.kind==="unknown")return ct(`usage: maind launch <agent> [--dry-run] [--config <path>] [--route <name>] [--direct] [--no-fallback] [--no-install] \u2014 supported: ${bp.join(", ")}`),2;if(n.kind==="planned")return ct(`agent '${n.agent}' is planned but not yet supported \u2014 v1 supports: ${bp.join(", ")}.`),2;if(e.json&&!e.dryRun)return ct("--json is only valid together with --dry-run (stdout belongs to the agent session)."),2;let r=n.agent;if(e.direct)return ct(sS()),dS(t,vp(r));let o=Jx(t.env);o.warning!==null&&ct(o.warning);let i,s,l;if(e.config!==null){if(!t.fileExists(e.config))return ct(`config not found: ${e.config}`),2;let f=t.readFile(e.config);if(f===null||eS(f,e.route)===null)return ct(`config ${e.config} is not valid routes TOML with a [routes.${e.route}] entry.`),2;i=e.config,s=null,l="flag"}else{let f=await t.fetchTemplate(e.mcpUrl,t.projectKey()),p=f===null?null:Qx(f,e.route);if(s=Zx(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)`;ct(`using builtin routing policy (${m}).`)}i=y1(t.repoRoot(),t.userConfigDir())}let c=al,u=t.probe(al,["--version"]).ok;if(e.dryRun){let f={agent:r,configPath:i,configSource:l,toml:s,argv:wp(c,r,e.route,i),backendKind:o.kind,switchyardDetected:u},p=aS(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 ct(`switchyard not found (--no-install). Install it with:
270
270
  maind: ${ll}`),1;if(!t.probe("uv",["--version"]).ok)return ct(rS()),1;if(ct(nS()),t.install(Wx),t.probe(al,["--version"]).ok)u=!0;else{let f=Rp(t.homeDir(),".local","bin",t.platform==="win32"?"switchyard.exe":"switchyard");t.probe(f,["--version"]).ok&&(c=f,u=!0)}if(!u)return Sp(t,e,r,oS())}if(l!=="flag"&&s!==null)try{t.writeConfig(i,s)}catch(f){return ct(`could not write ${i}: ${m1(f)}`),1}let d=await t.spawnInteractive(wp(c,r,e.route,i));return d.spawnError!==null?Sp(t,e,r,`could not start switchyard (${d.spawnError})`):tS(d.code,d.signal,d.elapsedMs)==="fallback-eligible"?Sp(t,e,r,`switchyard exited with code ${d.code} after ${(d.elapsedMs/1e3).toFixed(1)}s`):xp(d.code,d.signal)}a(uS,"runLaunch");async function Sp(e,t,n,r){return t.noFallback?(ct(`${r} (--no-fallback set \u2014 not degrading to a direct spawn).`),1):(process.stderr.write(iS(r)+`
271
271
  `),dS(e,vp(n)))}a(Sp,"degradeOrFail");async function dS(e,t){let n=await e.spawnInteractive(t);return n.spawnError!==null?(ct(`'${t[0]}' not found on PATH \u2014 install the agent CLI first (${n.spawnError}).`),1):xp(n.code,n.signal)}a(dS,"spawnAndPropagate");import{execFileSync as Cr}from"node:child_process";import{cpSync as FK,existsSync as oc,mkdirSync as UK,mkdtempSync as Fm,readdirSync as lT,readFileSync as cT,realpathSync as BK,rmSync as uT,statSync as KK,writeFileSync as Qi}from"node:fs";import{homedir as HK,tmpdir as Um}from"node:os";import{basename as GK,dirname as qm,join as ut,resolve as Wm}from"node:path";function fS(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(fS,"classifyCheckCount");function pS(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(pS,"checkCountLine");function mS(e,t){let n=new Set(t);return e.filter(r=>!n.has(r))}a(mS,"missingRequiredChecks");function hS(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 i=o.parameters?.required_status_checks;if(Array.isArray(i))for(let s of i){let l=s?.context;typeof l=="string"&&!n.includes(l)&&n.push(l)}}return n}catch{return null}}a(hS,"requiredContextsFromRules");import{execFileSync as b1}from"node:child_process";function _1(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
  `);return 0}a(Q1,"runResolve");async function Gt(e){let t;try{t=ke()}catch(s){return{ok:!1,reason:s instanceof Ae?s.message:String(s)}}let n=Xe().projectKey;if(!n)return{ok:!1,reason:"not a git repo with a remote \u2014 no project_key."};let r=new Te({apiKey:t.apiKey,deviceId:t.deviceId,clientVersion:me,projectKey:n}),o={session:null},i=Fi(r,n,Ht,o);try{let s=o.session=await Li(r,n,void 0,Ht);if(s===null)return{ok:!1,reason:"no session could be resolved."};let l=await i.callTool("get_my_work_objects",{project_key:n,session_id:s.sessionId,keys:e});return l.ok!==!0||!Array.isArray(l.resolved)?{ok:!1,reason:"server did not answer."}:{ok:!0,refs:e.map(u=>{let d=u.toLowerCase(),f=l.resolved?.find(p=>p.id?.toLowerCase()===d||p.key?.toLowerCase()===d||(p.former_keys??[]).some(m=>m.toLowerCase()===d));return!f||typeof f.id!="string"?{ident:u,unresolved:!0}:{ident:u,id:f.id,key:f.key??null,title:f.title??"",status:f.status??"unknown",anchors:(f.anchors??[]).filter(p=>typeof p.path_prefix=="string"&&(!p.project_key||p.project_key===n)).map(p=>p.path_prefix),...typeof f.doc_duty=="string"?{docDuty:f.doc_duty}:{},...typeof f.doc_missing=="boolean"?{docMissing:f.doc_missing}:{},...f.doc_in_review===!0?{docInReview:!0}:{},...typeof f.lesson_check=="string"?{lessonCheck:f.lesson_check}:{},...typeof f.has_plan=="boolean"?{hasPlan:f.has_plan}:{},...typeof f.availability=="string"?{availability:f.availability}:{},...typeof f.availability_fact=="string"?{availabilityFact:f.availability_fact}:{},..."take"in f?{take:X1(f.take)}:{},...Array.isArray(f.bindings)?{bindings:J1(f.bindings)}:{}}})}}catch(s){return{ok:!1,reason:s instanceof Error?s.message:String(s)}}finally{o.session?.source==="announced"&&await Ui(r,n,o.session.sessionId)}}a(Gt,"lookupObjects");function nU(e,t){try{return eU("git",["-c","core.quotepath=off",...t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:3e4,maxBuffer:16*1024*1024})}catch{return null}}a(nU,"git");function rU(e,t){return gu(nU(e,["log",`--format=${hu}`,`${t}..HEAD`])??"")}a(rU,"branchCommits");async function ml(e,t,n,r={}){try{let o=yu((r.commits??rU)(e,t));if(o.length===0)return[];let i=pl(r.home??rR(),".maind",Us,_u),s=pl(i,Bs(n.id)),l=null;try{l=tU(s,"utf8")}catch{l=null}let c=ku(l),u=wu(o,c);if(u===null)return[];let d=await(r.lookup??Gt)([n.id]),f=d.ok?d.refs[0]:void 0;if(f===void 0||f.unresolved)return[];try{let p=pl(r.home??rR(),".maind",Us,vu);tR(p,{recursive:!0}),nR(pl(p,Bs(n.id)),xu(f.lessonCheck==="on",new Date))}catch{}if(f.lessonCheck!=="on")return[];try{tR(i,{recursive:!0}),nR(s,JSON.stringify({key:n.key,asked:[...c,Ks(u.source)]}))}catch{}return[bu(u.source,u.check)]}catch{return[]}}a(ml,"lessonCheck");import{execFileSync as oU}from"node:child_process";import{mkdirSync as iU,writeFileSync as sU}from"node:fs";import{homedir as aU}from"node:os";import{join as oR}from"node:path";function iR(e){return e.documents>0?{remind:!1,why:"document_in_branch"}:e.decision.kind==="ok"?{remind:!1,why:"doc_decision"}:null}a(iR,"decideLocally");function lU(e){let t=iR(e);if(t)return t;let n=e.server;return n?.status==="passed"||n?.status==="canceled"?{remind:!1,why:"finished"}:n?.duty==="off"?{remind:!1,why:"duty_off"}:n?.inReview?{remind:!1,why:"in_review"}:n&&n.docMissing===!1?{remind:!1,why:"documented_elsewhere"}:{remind:!0,text:Ku(e.key,n?.duty==="require")}}a(lU,"decideDocReminder");function cU(e,t){return e.remind?t?[e.text]:[e.text,"(the server did not answer \u2014 reminded anyway)"]:[]}a(cU,"docReminderLines");function Gp(e,t){try{return oU("git",["-c","core.quotepath=off",...t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:3e4,maxBuffer:16*1024*1024})}catch{return null}}a(Gp,"git");function uU(e){return e===void 0||e.unresolved?null:{duty:e.docDuty??null,docMissing:e.docMissing??null,inReview:e.docInReview===!0,status:e.status??null}}a(uU,"serverOf");async function hl(e,t,n,r={}){try{let o=(Gp(e,["diff","--name-only",`${t}...HEAD`])??"").split(`
372
372
  `).filter(Boolean),i=Mt(o,f=>Gp(e,["show",`HEAD:${f}`]),n.key).length,s=(Gp(e,["log","--reverse","--format=%B%x00",`${t}..HEAD`])??"").split("\0").filter(f=>f.trim()!==""),l={key:n.key,documents:i,decision:Sn(s)};if(iR(l))return[];let c=await(r.lookup??Gt)([n.id]),u=c.ok?uU(c.refs[0]):null,d=lU({...l,server:u});if(!d.remind)return[];try{let f=oR(r.home??aU(),".maind","doc-duty",Hu);iU(f,{recursive:!0}),sU(oR(f,Gu(n.id)),JSON.stringify({said_at:(r.now?.()??new Date).toISOString(),key:n.key}))}catch{}return cU(d,u!==null)}catch{return[]}}a(hl,"docReminder");import{execFileSync as dU}from"node:child_process";function fU(e,t){return e.some(n=>{let r=n.endsWith("/")?n:`${n}/`;return t.some(o=>o===n||o.startsWith(r))})}a(fU,"inArea");function pU(e){let t=/#(\d+)$/.exec(e);return t?`#${t[1]}`:e}a(pU,"refLabel");var sR=80;function Vp(e){return e.length>sR?`${e.slice(0,sR-1)}\u2026`:e}a(Vp,"clip");function mU(e,t,n){let r=[];for(let o of e){if(o.objectId===n||!fU(o.anchors,t))continue;let i=o.key??Vp(o.title),s=[...new Set(o.refs.map(pU))].join(", "),l=o.documents[0];r.push(`since you branched: ${i}${s?` (${s})`:""} landed in this area`+(l?` \u2014 ${l.kind==="decision"?"decision":"read"} ${Vp(l.title)}`:` \u2014 ${Vp(o.title)}`))}return r}a(mU,"landedLines");function hU(e){let t=e?.objects;if(!Array.isArray(t))return[];let n=a(r=>Array.isArray(r)?r.filter(o=>typeof o=="string"):[],"strings");return t.flatMap(r=>{if(typeof r!="object"||r===null)return[];let o=r;if(typeof o.object_id!="string")return[];let i=Array.isArray(o.merges)?o.merges:[],s=Array.isArray(o.documents)?o.documents:[];return[{objectId:o.object_id,key:typeof o.key=="string"?o.key:null,title:typeof o.title=="string"?o.title:"",refs:i.flatMap(l=>typeof l?.ref=="string"?[l.ref]:[]),anchors:n(o.anchors),documents:s.flatMap(l=>{let c=l;return typeof c?.title=="string"?[{title:c.title,kind:typeof c.kind=="string"?c.kind:"other"}]:[]})}]})}a(hU,"parseLandedObjects");function Ki(e,t){try{return dU("git",[...t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}}a(Ki,"git");function gU(e,t){let n=(Ki(e,["rev-list","--first-parent","--reverse",`${t}..HEAD`])??"").split(`
373
373
  `)[0]?.trim();return n&&Ki(e,["show","-s","--format=%cI",`${n}^`])||null}a(gU,"forkPointDate");function yU(e,t){let n=Ki(e,["merge-base",t,"HEAD"]);if(!n)return null;let r=Ki(e,["-c","core.quotepath=off","diff","--name-only",n]);return r===null?null:r.split(`
374
- `).filter(Boolean)}a(yU,"changedPaths");async function gl(e,t,n){let r=gU(e,t);if(r===null)return{kind:"skipped",reason:"the branch has no commit of its own yet"};let o=yU(e,t);if(o===null)return{kind:"skipped",reason:`could not read what the branch changes against ${t}`};if(o.length===0)return{kind:"ok",lines:[]};let i=Ki(e,["config","--get","remote.origin.url"]),s=i?Ge(i):null;if(!s)return{kind:"skipped",reason:"no origin remote \u2014 no project key to ask with"};let l;try{let c=ke();l=new Te({apiKey:c.apiKey,deviceId:c.deviceId,clientVersion:me,projectKey:s})}catch(c){return{kind:"skipped",reason:c instanceof Ae?"no maind API key":String(c.message??c)}}try{let c=await l.callTool("list_landed_work",{project_key:s,since:r});return c?.ok!==!0?{kind:"skipped",reason:`the server did not answer (${String(c?.error??"no reason")})`}:{kind:"ok",lines:mU(hU(c),o,n)}}catch(c){return{kind:"skipped",reason:`the server could not be asked: ${c.message}`}}}a(gl,"landedSince");function aR(e){return e.kind==="skipped"?[`NOT CHECKED landed-since \u2014 ${e.reason}`]:e.lines.map(t=>`note landed-since \u2014 ${t}`)}a(aR,"landedRows");function lR(e){return e.verdict!=="overlap"?[]:[...e.findings.map(t=>t.title)," git merges both migrations cleanly \u2014 check that they still fit together."]}a(lR,"sqlOverlapLines");function cR(e){return e.verdict==="clean"?["ok sql-base-overlap \u2014 the base changed none of this branch's SQL objects."]:e.findings.map(t=>`note sql-base-overlap \u2014 ${t.title}`)}a(cR,"sqlOverlapRows");function yl(e){if(!e)return null;let t=e.indexOf("=");if(t<=0)return null;let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();return!n||!r?null:{gate:n,object:r}}a(yl,"parseOverride");function bU(e){return e==="open"||e==="warn"}a(bU,"objectCarriesOverride");async function uR(e,t,n){let r=[];for(let i of e)t.has(i.gate)||r.push(`--override ${i.gate}: not a gate declared in .maind/gates.json \u2014 only declared gates take an override`+(t.size>0?` (declared: ${[...t].join(", ")})`:""));let o=new Map;for(let i of new Set(e.map(s=>s.object))){let s=null;try{s=await n(i)}catch{s=null}let l=s?.key??i;s===null?r.push(`--override \u2026=${i}: the object could not be resolved \u2014 no override without proof that it is open`):bU(s.status)?o.set(i,s):r.push(`--override \u2026=${l}: the object is ${s.status} \u2014 it no longer tracks a red gate`)}return r.length>0?{kind:"refused",lines:r}:{kind:"ok",resolved:o}}a(uR,"checkOverrides");function dR(e,t,n){let r=new Map(t.map(c=>[c.gate,c])),o=[],i=e.map(c=>{let u=r.get(c.id);if(!u)return c;let d=n.get(u.object);return!d||c.verdict!=="fail"||!c.blocking?c:(o.push({gate:c.id,object:d}),{...c,blocking:!1,note:`red, accepted \u2014 tracked by ${d.key??d.id}`})}),s=new Set(o.map(c=>c.gate)),l=t.filter(c=>!s.has(c.gate)).map(c=>({gate:c.gate,verdict:e.find(u=>u.id===c.gate)?.verdict??"absent"}));return{results:i,used:o,unused:l}}a(dR,"applyOverrides");function fR(e){return e.map(t=>t.verdict==="absent"?`--override ${t.gate}: the gate did not run \u2014 nothing was accepted`:`--override ${t.gate}: the gate is ${t.verdict==="pass"?"green":t.verdict}, not red \u2014 nothing was accepted, nothing recorded`)}a(fR,"unusedOverrideLines");function pR(e,t){if(!e.ok)return{kind:"unchecked",reason:e.reason};let n=t.toLowerCase(),r=e.refs.find(o=>o.unresolved!==!0&&o.id?.toLowerCase()===n);return r===void 0?{kind:"unchecked",reason:"the server did not resolve the bound object"}:typeof r.hasPlan!="boolean"?{kind:"unchecked",reason:"the server does not report has_plan"}:r.hasPlan?{kind:"attached"}:{kind:"absent"}}a(pR,"serverPlanAnswer");var Hi="MAIND_GATE_OBJECT_REASON",_U=/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;function kU(e){if(e===null)return!1;let t=e.trim();return t.length<4?!1:!["1","true","yes","on","skip"].includes(t.toLowerCase())}a(kU,"isReason");function bl(e){let t=wU(e);return t.kind!=="linked"&&kU(e.overrideReason)?{kind:"overridden",reason:e.overrideReason.trim()}:t}a(bl,"classifyWorkObject");function wU(e){if(e.branch===null)return{kind:"detached"};let t=e.objectId?.trim()??"";return t.length===0?{kind:"missing",branch:e.branch}:_U.test(t)?{kind:"linked",objectId:t,objectKey:e.objectKey?.trim()||null,planRevision:e.planRevision?.trim()||null}:{kind:"malformed",branch:e.branch,value:t}}a(wU,"judge");function _l(e){return e.kind==="linked"||e.kind==="overridden"}a(_l,"workObjectPasses");function mR(e,t,n){let r=a(o=>[" Link it, or say why not:",` git config branch.${o}.maindObject <object-uuid>`,` ${t}="<reason>" maind gate`],"fix");switch(e.kind){case"linked":{let o=`work object: ${e.objectKey?`${e.objectKey} \u2014 `:""}${e.objectId}`;if(e.planRevision)return[o,` plan attached (rev ${e.planRevision})`];let i=` maind object plan --object ${e.objectId} --file <plan.md>`,s=n??{kind:"unchecked",reason:"the server was not asked"};switch(s.kind){case"attached":return[o," plan attached (server)"];case"absent":return[o," no plan attached (ADR-334) \u2014 advisory, never a failure. Attach the accepted plan:",i];case"unchecked":return[o,` plan not checked (${s.reason}) \u2014 no local marker; advisory, never a failure. If none is attached:`,i]}}case"overridden":return[`work object check overridden \u2014 reason: ${e.reason}`];case"missing":return[`branch ${e.branch} has no linked work object.`,...r(e.branch)];case"malformed":return[`branch ${e.branch}: "${e.value}" is not an object id.`," The check reads the id, never the display key \u2014 a key can be renamed, an id cannot.",...r(e.branch)];case"detached":return["detached HEAD \u2014 this check needs a branch to read the link from."]}}a(mR,"workObjectLines");function qp(e){return`${e.dir}\0${e.check??""}`}a(qp,"checkKey");function hR(e){return`${e.replace(/[/\\]+$/,"")}-apply`}a(hR,"applyCheckoutPath");function gR(e){return[...new Set(e.filter(t=>t.command!==null).map(t=>t.trigger))].sort()}a(gR,"applyTriggers");function Vt(e){return/^[A-Za-z0-9_./-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`}a(Vt,"shellQuote");function vU(e,t){let n=t.replace(/^\/+|\/+$/g,"");return n.length===0?e:`${e.replace(/\/+$/,"")}/${n}`}a(vU,"joinAbs");function xU(e){return e.endsWith("/")?e:`${e}/`}a(xU,"withPrefix");function SU(e){let t=new Set;for(let r of e){let o=/^((?:[^/]+\/)*?)supabase\/(?:migrations|functions)\//.exec(r);o&&t.add(o[1])}let n=[];for(let r of[...t].sort()){let o=r.replace(/\/$/,"");n.push({when:`${r}supabase/migrations/`,run:"supabase db push",check:"supabase migration list",cwd:o}),n.push({when:`${r}supabase/functions/`,run:"supabase functions deploy {name}",check:"supabase functions list",cwd:o,each:"dir"})}return n}a(SU,"defaultPostMergeSteps");function Wp(e,t,n){let r=e.map(s=>s.trim()).filter(s=>s.length>0),o=t??SU(r),i=[];for(let s of o){let l=xU(s.when),c=r.filter(g=>g.startsWith(l));if(c.length===0)continue;let u=vU(n,s.cwd??""),d=`cd ${Vt(u)} && `,f=a((g,_)=>g===void 0?void 0:g.split("{name}").join(_),"fill");if(s.each!=="dir"){i.push({reason:`${c.length} file(s) under ${l}`+(s.note?` \u2014 ${s.note}`:""),command:s.run?d+s.run:null,trigger:l,dir:u,check:s.run&&s.check?s.check:null});continue}let p=[...new Set(c.map(g=>g.slice(l.length).split("/")[0]).filter(g=>g.length>0))].sort();for(let g of p.filter(_=>!_.startsWith("_"))){let _=f(s.note,g),y=f(s.run,g),b=f(s.check,g);i.push({reason:`${l}${g}/ changed`+(_?` \u2014 ${_}`:""),command:y?d+y:null,trigger:l,dir:u,check:y&&b?b:null})}let m=p.filter(g=>g.startsWith("_"));m.length>0&&i.push({reason:`${m.map(g=>`${l}${g}/`).join(", ")} changed \u2014 shared code, no target of its own: redeploy what imports it`,command:null,trigger:l,dir:u,check:null})}return i}a(Wp,"postMergeActions");function yR(e,t,n={}){if(e.length===0)return[];let r=[],o=n.target??{kind:"main"},i=n.mainRoot??"the main checkout",s=n.ref??t;o.kind==="prepared"?(r.push(`the main checkout (${i}) does not hold the merged ${o.stale.join(", ")} \u2014 it is behind ${s} or changed locally, so the steps run in ${o.path} (detached at ${s}${o.reused?", reused":""}${o.provisioned>0?`, ${o.provisioned} declared path(s) provisioned from .maind/worktree-requirements`:""}).`),o.installFailed&&r.push(" postCreate failed there \u2014 a command that needs the install will fail until it runs."),r.push(` remove it when done: git -C ${Vt(i)} worktree remove ${Vt(o.path)}`)):o.kind==="unavailable"&&(r.push(`the main checkout (${i}) does not hold the merged ${o.stale.join(", ")}, and no apply checkout could be prepared at ${o.path}: ${o.reason}.`),r.push(` the commands below run against a stale tree until it is at ${s} \u2014 bring it there first.`)),r.push(`the merge on ${t} needs steps it did not run \u2014 they are yours (nothing was executed):`);for(let l of e){if(r.push(` \u2022 ${l.reason}`),!l.command)continue;let c=l.check?n.checks?.get(qp(l)):void 0;if(c&&!c.ok){r.push(` \u2716 check failed (${c.exit===null?"did not finish":`exit ${c.exit}`}): ${l.check}`);for(let u of c.tail)r.push(` | ${u}`);r.push(" the command fails the same way until that is fixed \u2014 then:")}else c&&r.push(` \u2713 checked: ${l.check}`);r.push(` ${l.command}`)}return r}a(yR,"postMergeLines");function zp(e,t){let n=e!==null&&e.trim().length>0;return t==="closes"?{kind:"decided",completes:!0}:t==="progress"?{kind:"decided",completes:!1}:n?{kind:"missing",objectId:e.trim()}:{kind:"decided",completes:!1}}a(zp,"decideCompletion");function bR(e,t){return[`${e} is bound to work object ${t}, and the merge will be reported there.`,"Does this pull request FINISH that object? Say so \u2014 neither answer is assumed:"," --closes-object it does: the merge is reported as completion"," --progress-only one step of several: the merge is logged, the object stays open","Nothing was merged."]}a(bR,"missingCompletionLines");function Yp(e){if(!e.required)return null;let t=bl({branch:e.branch,objectId:e.boundObject,objectKey:null,overrideReason:e.overrideReason,planRevision:null});return _l(t)?null:[`${e.branch} has no linked work object, and this repository's gates require one (requireWorkObject, ADR-286).`,"The gate would refuse it at the very end \u2014 so it was not started. Either:"," bind it: maind object bind --object <uuid>",` or say why not: ${Gi}="<reason>" ${e.promoteCommand} --yes`,"Nothing was merged."]}a(Yp,"unboundObjectLines");var Gi=Hi;function uo(e,t){let n=e.replace(/\/+$/,""),r=t.replace(/\/+$/,"");return n===r||n.startsWith(`${r}/`)}a(uo,"cwdInside");function wr(e,t){return`cd ${Vt(e)} && ${t}`}a(wr,"cleanupCommand");function _R(e){let t=[`PR #${e.prNumber} is already merged${e.mergedOn?` (${e.mergedOn})`:""} \u2014 nothing to promote.`];return e.leftover&&t.push("the worktree or local branch is still there \u2014 clean up:",` ${e.cleanup}`),t}a(_R,"alreadyMergedLines");function kR(e){return[`this process runs inside ${e.worktree} \u2014 removing it would pull the directory out from under the calling shell. Nothing was removed. Run it from the main checkout:`,` ${e.cleanup}`]}a(kR,"doneInsideLines");function wR(e){return[`the worktree was kept \u2014 this process runs inside ${e.worktree}, and removing it would pull the directory out from under the calling shell. Clean up from the main checkout:`,` ${wr(e.mainRoot,e.doneCommand)}`,` or, later, for every merged worktree at once: ${wr(e.mainRoot,"maind feature done --merged")}`]}a(wR,"keptWorktreeLines");async function vR(e,t){if(e!==null&&e.trim().length>0)return{kind:"bound",objectId:e.trim()};let n=t.prNumber(),r=t.slug();if(n===null||r===null)return{kind:"unbound"};let o=t.ref?t.ref(r,n):`github:${r}#${n}`,i=null;try{i=await t.lookup(o)}catch{i=null}return i===null?{kind:"unbound"}:(t.write(i.id,i.key),{kind:"recovered",objectId:i.id,key:i.key,ref:o})}a(vR,"recoverBinding");async function Xp(e,t){let n=t.prNumber(),r=t.slug();if(n===null||r===null)return{kind:"no-pr"};let o=t.ref?t.ref(r,n):`github:${r}#${n}`,i=null;try{i=await t.lookup(o)}catch{i=null}if(i!==null)return i.id===e?{kind:"already",ref:o}:{kind:"mismatch",ref:o,other:i};await t.link(e,o);let s=null;try{s=await t.lookup(o)}catch{s=null}return s!==null&&s.id!==e?{kind:"mismatch",ref:o,other:s}:{kind:"link-attempted",ref:o}}a(Xp,"ensurePrLinked");function kl(e){let t=e.bound.key??e.bound.id,n=e.other.key??e.other.id,r=e.prNumber!==null?`PR #${e.prNumber}`:e.ref;return[`${e.branch} is bound to ${t}, but ${r} is linked to ${n} \u2014 the merge would be reported against the wrong object.`,e.phase==="ship"?"the branch is pushed and the pull request is open; promote will refuse it until this is settled.":"Nothing was merged.",` if the work belongs to ${n}: maind object bind --object ${e.other.id}`,` if it belongs to ${t}: the link is wrong \u2014 a person removes it on ${n}'s object page (the connector does not re-create a removed link), then ship again to link ${t}; retitle the pull request if its title names ${n}.`]}a(kl,"linkMismatchLines");function xR(e,t){let n=t?.trim().toUpperCase()??null;return e.filter(r=>r.toUpperCase()!==n)}a(xR,"fanOutKeys");import{execFileSync as RU}from"node:child_process";import{readFileSync as EU,readdirSync as AU}from"node:fs";import{join as Jp}from"node:path";var vr=".maind/gates.json",fo=".maind/gates.d";function SR(e,t,n){if(e===null||typeof e!="object"||Array.isArray(e))return{problem:`${t} must be an object`};let r=e,o=r.id;if(n!==null){if(o!==void 0&&o!==n)return{problem:`${t}: "id" is "${String(o)}", but the file name says "${n}" \u2014 the file name is the id`};o=n}if(typeof o!="string"||o.trim().length===0)return{problem:`${t}.id must be a non-empty string`};let i=r.run;if(typeof i!="string"||i.trim().length===0)return{problem:`${t}.run must be a non-empty command string`};let s=!0;if("blocking"in r&&r.blocking!==void 0&&r.blocking!==null){if(typeof r.blocking!="boolean")return{problem:`${t}.blocking must be a boolean`};s=r.blocking}let l,c=r.when;if(c!=null){if(typeof c!="object"||Array.isArray(c))return{problem:`${t}.when must be an object`};let d=c.paths;if(d!=null){if(!Array.isArray(d)||d.some(f=>typeof f!="string"||f.trim().length===0))return{problem:`${t}.when.paths must be an array of non-empty path prefixes`};l=d}}let u=!1;if("onShip"in r&&r.onShip!==void 0&&r.onShip!==null){if(typeof r.onShip!="boolean")return{problem:`${t}.onShip must be a boolean`};u=r.onShip}return{id:o,run:i,blocking:s,...l?{paths:l}:{},...u?{onShip:u}:{}}}a(SR,"parseGateEntry");function TU(e,t=[]){let n=[],r=e===void 0;if(!r&&(e===null||typeof e!="object"||Array.isArray(e)))return{problems:["the manifest must be a JSON object"]};let o=r?{}:e,i=o.gates;if(i===void 0&&t.length===0)return{problems:[`the manifest needs a "gates" array (or one file per gate under ${fo}/)`]};if(i!==void 0&&!Array.isArray(i))return{problems:['"gates" must be an array']};let s=[],l=new Map,c=a((y,b)=>{if("problem"in y){n.push(y.problem);return}let k=l.get(y.id);if(k!==void 0){n.push(`${b}.id "${y.id}" is declared more than once (also ${k})`);return}l.set(y.id,b),s.push(y)},"take");(Array.isArray(i)?i:[]).forEach((y,b)=>c(SR(y,`gates[${b}]`,null),`gates[${b}]`));for(let y of t){let b=`${fo}/${y.file}`;if(y.parseError!==void 0){n.push(`${b}: not valid JSON: ${y.parseError}`);continue}c(SR(y.raw,b,y.file.replace(/\.json$/,"")),b)}let u=[],d=o.versionedPackages!==void 0,f=o.versionedPackages;if(f!==void 0)if(!Array.isArray(f))n.push('"versionedPackages" must be an array');else{let y=new Set;f.forEach((b,k)=>{let R=`versionedPackages[${k}]`;if(b===null||typeof b!="object"||Array.isArray(b)){n.push(`${R} must be an object`);return}let E=b,C=E.dir;if(typeof C!="string"||C.trim().length===0){n.push(`${R}.dir must be a non-empty path`);return}if(y.has(C)){n.push(`${R}.dir "${C}" is declared more than once`);return}y.add(C);let A=E.mirror;if(A!==void 0&&(typeof A!="string"||A.trim().length===0)){n.push(`${R}.mirror must be a non-empty path relative to dir`);return}let I=E.changelog;if(I!==void 0&&typeof I!="boolean"){n.push(`${R}.changelog must be a boolean`);return}let x=E.numbering;if(x!==void 0&&x!=="hand"&&x!=="promote"){n.push(`${R}.numbering must be "hand" or "promote"`);return}let w=E.tag;if(w!==void 0&&(typeof w!="string"||!w.includes("{version}"))){n.push(`${R}.tag must be a template containing {version}, e.g. "my-pkg-v{version}"`);return}if(w!==void 0&&x!=="promote"){n.push(`${R}.tag only applies with numbering "promote" \u2014 promote is what sets it`);return}let T=E.fragments;if(T!==void 0&&(typeof T!="string"||T.trim().length===0)){n.push(`${R}.fragments must be a non-empty path relative to dir`);return}u.push({dir:C.replace(/\/+$/,""),changelog:I??!0,numbering:x??"hand",fragments:typeof T=="string"?T.replace(/\/+$/,""):"changelog.d",...typeof A=="string"?{mirror:A}:{},...typeof w=="string"?{tag:w}:{}})})}let p=!1,m=o.requireWorkObject;m!==void 0&&(typeof m!="boolean"?n.push('"requireWorkObject" must be a boolean'):p=m);let g=!1,_=o.review;return _!==void 0&&(typeof _!="boolean"?n.push('"review" must be a boolean'):g=_),n.length>0?{problems:n}:{gates:s,versioned:u,versionedDeclared:d,requireWorkObject:p,review:g}}a(TU,"parseGatesManifest");function RR(e,t){let n=e.file(vr),r=e.list(fo).filter(s=>s.endsWith(".json")).sort().map(s=>{let l=e.file(`${fo}/${s}`)??"";try{return{file:s,raw:JSON.parse(l)}}catch(c){return{file:s,parseError:c.message}}});if(n===null&&r.length===0)return{kind:"absent",gates:[],versioned:[],versionedDeclared:!1,requireWorkObject:!1,review:!1};let o;if(n!==null)try{o=JSON.parse(n)}catch(s){return{kind:"invalid",path:t,problems:[`not valid JSON: ${s.message}`]}}let i=TU(o,r);return"problems"in i?{kind:"invalid",path:t,problems:i.problems}:{kind:"loaded",gates:i.gates,versioned:i.versioned,versionedDeclared:i.versionedDeclared,requireWorkObject:i.requireWorkObject,review:i.review,path:t}}a(RR,"readGatesManifest");function xr(e){return RR({file(t){try{return EU(Jp(e,t),"utf8")}catch{return null}},list(t){try{return AU(Jp(e,t),{withFileTypes:!0}).filter(n=>n.isFile()).map(n=>n.name)}catch{return[]}}},Jp(e,vr))}a(xr,"loadGatesManifest");function wl(e,t){let n=a(r=>{try{return RU("git",r,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:6e4,maxBuffer:67108864})}catch{return null}},"git");return RR({file:a(r=>n(["show",`${t}:${r}`]),"file"),list:a(r=>(n(["ls-tree","--name-only",`${t}:${r}`])??"").split(`
374
+ `).filter(Boolean)}a(yU,"changedPaths");async function gl(e,t,n){let r=gU(e,t);if(r===null)return{kind:"skipped",reason:"the branch has no commit of its own yet"};let o=yU(e,t);if(o===null)return{kind:"skipped",reason:`could not read what the branch changes against ${t}`};if(o.length===0)return{kind:"ok",lines:[]};let i=Ki(e,["config","--get","remote.origin.url"]),s=i?Ge(i):null;if(!s)return{kind:"skipped",reason:"no origin remote \u2014 no project key to ask with"};let l;try{let c=ke();l=new Te({apiKey:c.apiKey,deviceId:c.deviceId,clientVersion:me,projectKey:s})}catch(c){return{kind:"skipped",reason:c instanceof Ae?"no maind API key":String(c.message??c)}}try{let c=await l.callTool("list_landed_work",{project_key:s,since:r});return c?.ok!==!0?{kind:"skipped",reason:`the server did not answer (${String(c?.error??"no reason")})`}:{kind:"ok",lines:mU(hU(c),o,n)}}catch(c){return{kind:"skipped",reason:`the server could not be asked: ${c.message}`}}}a(gl,"landedSince");function aR(e){return e.kind==="skipped"?[`NOT CHECKED landed-since \u2014 ${e.reason}`]:e.lines.map(t=>`note landed-since \u2014 ${t}`)}a(aR,"landedRows");function lR(e){return e.verdict!=="overlap"?[]:[...e.findings.map(t=>t.title)," git merges both migrations cleanly \u2014 check that they still fit together."]}a(lR,"sqlOverlapLines");function cR(e){return e.verdict==="clean"?["ok sql-base-overlap \u2014 the base changed none of this branch's SQL objects."]:e.findings.map(t=>`note sql-base-overlap \u2014 ${t.title}`)}a(cR,"sqlOverlapRows");function yl(e){if(!e)return null;let t=e.indexOf("=");if(t<=0)return null;let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();return!n||!r?null:{gate:n,object:r}}a(yl,"parseOverride");function bU(e){return e==="open"||e==="warn"}a(bU,"objectCarriesOverride");async function uR(e,t,n){let r=[];for(let i of e)t.has(i.gate)||r.push(`--override ${i.gate}: not a gate declared in .maind/gates.json \u2014 only declared gates take an override`+(t.size>0?` (declared: ${[...t].join(", ")})`:""));let o=new Map;for(let i of new Set(e.map(s=>s.object))){let s=null;try{s=await n(i)}catch{s=null}let l=s?.key??i;s===null?r.push(`--override \u2026=${i}: the object could not be resolved \u2014 no override without proof that it is open`):bU(s.status)?o.set(i,s):r.push(`--override \u2026=${l}: the object is ${s.status} \u2014 it no longer tracks a red gate`)}return r.length>0?{kind:"refused",lines:r}:{kind:"ok",resolved:o}}a(uR,"checkOverrides");function dR(e,t,n){let r=new Map(t.map(c=>[c.gate,c])),o=[],i=e.map(c=>{let u=r.get(c.id);if(!u)return c;let d=n.get(u.object);return!d||c.verdict!=="fail"||!c.blocking?c:(o.push({gate:c.id,object:d}),{...c,blocking:!1,note:`red, accepted \u2014 tracked by ${d.key??d.id}`})}),s=new Set(o.map(c=>c.gate)),l=t.filter(c=>!s.has(c.gate)).map(c=>({gate:c.gate,verdict:e.find(u=>u.id===c.gate)?.verdict??"absent"}));return{results:i,used:o,unused:l}}a(dR,"applyOverrides");function fR(e){return e.map(t=>t.verdict==="absent"?`--override ${t.gate}: the gate did not run \u2014 nothing was accepted`:`--override ${t.gate}: the gate is ${t.verdict==="pass"?"green":t.verdict}, not red \u2014 nothing was accepted, nothing recorded`)}a(fR,"unusedOverrideLines");function pR(e,t){if(!e.ok)return{kind:"unchecked",reason:e.reason};let n=t.toLowerCase(),r=e.refs.find(o=>o.unresolved!==!0&&o.id?.toLowerCase()===n);return r===void 0?{kind:"unchecked",reason:"the server did not resolve the bound object"}:typeof r.hasPlan!="boolean"?{kind:"unchecked",reason:"the server does not report has_plan"}:r.hasPlan?{kind:"attached"}:{kind:"absent"}}a(pR,"serverPlanAnswer");var Hi="MAIND_GATE_OBJECT_REASON",_U=/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;function kU(e){if(e===null)return!1;let t=e.trim();return t.length<4?!1:!["1","true","yes","on","skip"].includes(t.toLowerCase())}a(kU,"isReason");function bl(e){let t=wU(e);return t.kind!=="linked"&&kU(e.overrideReason)?{kind:"overridden",reason:e.overrideReason.trim()}:t}a(bl,"classifyWorkObject");function wU(e){if(e.branch===null)return{kind:"detached"};let t=e.objectId?.trim()??"";return t.length===0?{kind:"missing",branch:e.branch}:_U.test(t)?{kind:"linked",objectId:t,objectKey:e.objectKey?.trim()||null,planRevision:e.planRevision?.trim()||null}:{kind:"malformed",branch:e.branch,value:t}}a(wU,"judge");function _l(e){return e.kind==="linked"||e.kind==="overridden"}a(_l,"workObjectPasses");function mR(e,t,n){let r=a(o=>[" Link it, or say why not:",` git config branch.${o}.maindObject <object-uuid>`,` ${t}="<reason>" maind gate`],"fix");switch(e.kind){case"linked":{let o=`work object: ${e.objectKey?`${e.objectKey} \u2014 `:""}${e.objectId}`;if(e.planRevision)return[o,` plan attached (rev ${e.planRevision})`];let i=` maind object plan --object ${e.objectId} --file <plan.md>`,s=n??{kind:"unchecked",reason:"the server was not asked"};switch(s.kind){case"attached":return[o," plan attached (server)"];case"absent":return[o," no plan attached (ADR-334) \u2014 advisory, never a failure. Attach the accepted plan:",i];case"unchecked":return[o,` plan not checked (${s.reason}) \u2014 no local marker; advisory, never a failure. If none is attached:`,i]}}case"overridden":return[`work object check overridden \u2014 reason: ${e.reason}`];case"missing":return[`branch ${e.branch} has no linked work object.`,...r(e.branch)];case"malformed":return[`branch ${e.branch}: "${e.value}" is not an object id.`," The check reads the id, never the display key \u2014 a key can be renamed, an id cannot.",...r(e.branch)];case"detached":return["detached HEAD \u2014 this check needs a branch to read the link from."]}}a(mR,"workObjectLines");function qp(e){return`${e.dir}\0${e.check??""}`}a(qp,"checkKey");function hR(e){return`${e.replace(/[/\\]+$/,"")}-apply`}a(hR,"applyCheckoutPath");function gR(e){return[...new Set(e.filter(t=>t.command!==null).map(t=>t.trigger))].sort()}a(gR,"applyTriggers");function Vt(e){return/^[A-Za-z0-9_./-]+$/.test(e)?e:`'${e.replace(/'/g,"'\\''")}'`}a(Vt,"shellQuote");function vU(e,t){let n=t.replace(/^\/+|\/+$/g,"");return n.length===0?e:`${e.replace(/\/+$/,"")}/${n}`}a(vU,"joinAbs");function xU(e){return e.endsWith("/")?e:`${e}/`}a(xU,"withPrefix");function SU(e){let t=new Set;for(let r of e){let o=/^((?:[^/]+\/)*?)supabase\/(?:migrations|functions)\//.exec(r);o&&t.add(o[1])}let n=[];for(let r of[...t].sort()){let o=r.replace(/\/$/,"");n.push({when:`${r}supabase/migrations/`,run:"supabase db push",check:"supabase migration list",cwd:o}),n.push({when:`${r}supabase/functions/`,run:"supabase functions deploy {name}",check:"supabase functions list",cwd:o,each:"dir"})}return n}a(SU,"defaultPostMergeSteps");function Wp(e,t,n){let r=e.map(s=>s.trim()).filter(s=>s.length>0),o=t??SU(r),i=[];for(let s of o){let l=xU(s.when),c=r.filter(g=>g.startsWith(l));if(c.length===0)continue;let u=vU(n,s.cwd??""),d=`cd ${Vt(u)} && `,f=a((g,_)=>g===void 0?void 0:g.split("{name}").join(_),"fill");if(s.each!=="dir"){i.push({reason:`${c.length} file(s) under ${l}`+(s.note?` \u2014 ${s.note}`:""),command:s.run?d+s.run:null,trigger:l,dir:u,check:s.run&&s.check?s.check:null});continue}let p=[...new Set(c.map(g=>g.slice(l.length).split("/")[0]).filter(g=>g.length>0))].sort();for(let g of p.filter(_=>!_.startsWith("_"))){let _=f(s.note,g),y=f(s.run,g),b=f(s.check,g);i.push({reason:`${l}${g}/ changed`+(_?` \u2014 ${_}`:""),command:y?d+y:null,trigger:l,dir:u,check:y&&b?b:null})}let m=p.filter(g=>g.startsWith("_"));m.length>0&&i.push({reason:`${m.map(g=>`${l}${g}/`).join(", ")} changed \u2014 shared code, no target of its own: redeploy what imports it`,command:null,trigger:l,dir:u,check:null})}return i}a(Wp,"postMergeActions");function yR(e,t,n={}){if(e.length===0)return[];let r=[],o=n.target??{kind:"main"},i=n.mainRoot??"the main checkout",s=n.ref??t;o.kind==="prepared"?(r.push(`the main checkout (${i}) does not hold the merged ${o.stale.join(", ")} \u2014 it is behind ${s} or changed locally, so the steps run in ${o.path} (detached at ${s}${o.reused?", reused":""}${o.provisioned>0?`, ${o.provisioned} declared path(s) provisioned from .maind/worktree-requirements`:""}).`),o.installFailed&&r.push(" postCreate failed there \u2014 a command that needs the install will fail until it runs."),r.push(` remove it when done: git -C ${Vt(i)} worktree remove ${Vt(o.path)}`)):o.kind==="unavailable"&&(r.push(`the main checkout (${i}) does not hold the merged ${o.stale.join(", ")}, and no apply checkout could be prepared at ${o.path}: ${o.reason}.`),r.push(` the commands below run against a stale tree until it is at ${s} \u2014 bring it there first.`)),r.push(`the merge on ${t} needs steps it did not run \u2014 they are yours (nothing was executed):`);for(let l of e){if(r.push(` \u2022 ${l.reason}`),!l.command)continue;let c=l.check?n.checks?.get(qp(l)):void 0;if(c&&!c.ok){r.push(` \u2716 check failed (${c.exit===null?"did not finish":`exit ${c.exit}`}): ${l.check}`);for(let u of c.tail)r.push(` | ${u}`);r.push(" the command fails the same way until that is fixed \u2014 then:")}else c&&r.push(` \u2713 checked: ${l.check}`);r.push(` ${l.command}`)}return r}a(yR,"postMergeLines");function zp(e,t){let n=e!==null&&e.trim().length>0;return t==="closes"?{kind:"decided",completes:!0}:t==="progress"?{kind:"decided",completes:!1}:n?{kind:"missing",objectId:e.trim()}:{kind:"decided",completes:!1}}a(zp,"decideCompletion");function bR(e,t){return[`${e} is bound to work object ${t}, and the merge will be reported there.`,"Does this pull request FINISH that object? Say so \u2014 neither answer is assumed:"," --closes-object it does: the merge is reported as completion"," --progress-only one step of several: the merge is logged, the object stays open","Nothing was merged."]}a(bR,"missingCompletionLines");function Yp(e){if(!e.required)return null;let t=bl({branch:e.branch,objectId:e.boundObject,objectKey:null,overrideReason:e.overrideReason,planRevision:null});return _l(t)?null:[`${e.branch} has no linked work object, and this repository's gates require one (requireWorkObject, ADR-286).`,"The gate would refuse it at the very end \u2014 so it was not started. Either:"," bind it: maind object bind --object <uuid>",` or say why not: ${Gi}="<reason>" ${e.promoteCommand} --yes`,"Nothing was merged."]}a(Yp,"unboundObjectLines");var Gi=Hi;function uo(e,t){let n=e.replace(/\/+$/,""),r=t.replace(/\/+$/,"");return n===r||n.startsWith(`${r}/`)}a(uo,"cwdInside");function wr(e,t){return`cd ${Vt(e)} && ${t}`}a(wr,"cleanupCommand");function _R(e){let t=[`PR #${e.prNumber} is already merged${e.mergedOn?` (${e.mergedOn})`:""} \u2014 nothing to promote.`];return e.leftover&&t.push("the worktree or local branch is still there \u2014 clean up:",` ${e.cleanup}`),t}a(_R,"alreadyMergedLines");function kR(e){return[`this process runs inside ${e.worktree} \u2014 removing it would pull the directory out from under the calling shell. Nothing was removed. Run it from the main checkout:`,` ${e.cleanup}`]}a(kR,"doneInsideLines");function wR(e){return[`the worktree was kept \u2014 this process runs inside ${e.worktree}, and removing it would pull the directory out from under the calling shell. Clean up from the main checkout:`,` ${wr(e.mainRoot,e.doneCommand)}`,` or, later, for every merged worktree at once: ${wr(e.mainRoot,"maind feature done --merged")}`]}a(wR,"keptWorktreeLines");async function vR(e,t){if(e!==null&&e.trim().length>0)return{kind:"bound",objectId:e.trim()};let n=t.prNumber(),r=t.slug();if(n===null||r===null)return{kind:"unbound"};let o=t.ref(r,n),i=null;try{i=await t.lookup(o)}catch{i=null}return i===null?{kind:"unbound"}:(t.write(i.id,i.key),{kind:"recovered",objectId:i.id,key:i.key,ref:o})}a(vR,"recoverBinding");async function Xp(e,t){let n=t.prNumber(),r=t.slug();if(n===null||r===null)return{kind:"no-pr"};let o=t.ref(r,n),i=null;try{i=await t.lookup(o)}catch{i=null}if(i!==null)return i.id===e?{kind:"already",ref:o}:{kind:"mismatch",ref:o,other:i};await t.link(e,o);let s=null;try{s=await t.lookup(o)}catch{s=null}return s!==null&&s.id!==e?{kind:"mismatch",ref:o,other:s}:{kind:"link-attempted",ref:o}}a(Xp,"ensurePrLinked");function kl(e){let t=e.bound.key??e.bound.id,n=e.other.key??e.other.id,r=e.prNumber!==null?`PR #${e.prNumber}`:e.ref;return[`${e.branch} is bound to ${t}, but ${r} is linked to ${n} \u2014 the merge would be reported against the wrong object.`,e.phase==="ship"?"the branch is pushed and the pull request is open; promote will refuse it until this is settled.":"Nothing was merged.",` if the work belongs to ${n}: maind object bind --object ${e.other.id}`,` if it belongs to ${t}: the link is wrong \u2014 a person removes it on ${n}'s object page (the connector does not re-create a removed link), then ship again to link ${t}; retitle the pull request if its title names ${n}.`]}a(kl,"linkMismatchLines");function xR(e,t){let n=t?.trim().toUpperCase()??null;return e.filter(r=>r.toUpperCase()!==n)}a(xR,"fanOutKeys");import{execFileSync as RU}from"node:child_process";import{readFileSync as EU,readdirSync as AU}from"node:fs";import{join as Jp}from"node:path";var vr=".maind/gates.json",fo=".maind/gates.d";function SR(e,t,n){if(e===null||typeof e!="object"||Array.isArray(e))return{problem:`${t} must be an object`};let r=e,o=r.id;if(n!==null){if(o!==void 0&&o!==n)return{problem:`${t}: "id" is "${String(o)}", but the file name says "${n}" \u2014 the file name is the id`};o=n}if(typeof o!="string"||o.trim().length===0)return{problem:`${t}.id must be a non-empty string`};let i=r.run;if(typeof i!="string"||i.trim().length===0)return{problem:`${t}.run must be a non-empty command string`};let s=!0;if("blocking"in r&&r.blocking!==void 0&&r.blocking!==null){if(typeof r.blocking!="boolean")return{problem:`${t}.blocking must be a boolean`};s=r.blocking}let l,c=r.when;if(c!=null){if(typeof c!="object"||Array.isArray(c))return{problem:`${t}.when must be an object`};let d=c.paths;if(d!=null){if(!Array.isArray(d)||d.some(f=>typeof f!="string"||f.trim().length===0))return{problem:`${t}.when.paths must be an array of non-empty path prefixes`};l=d}}let u=!1;if("onShip"in r&&r.onShip!==void 0&&r.onShip!==null){if(typeof r.onShip!="boolean")return{problem:`${t}.onShip must be a boolean`};u=r.onShip}return{id:o,run:i,blocking:s,...l?{paths:l}:{},...u?{onShip:u}:{}}}a(SR,"parseGateEntry");function TU(e,t=[]){let n=[],r=e===void 0;if(!r&&(e===null||typeof e!="object"||Array.isArray(e)))return{problems:["the manifest must be a JSON object"]};let o=r?{}:e,i=o.gates;if(i===void 0&&t.length===0)return{problems:[`the manifest needs a "gates" array (or one file per gate under ${fo}/)`]};if(i!==void 0&&!Array.isArray(i))return{problems:['"gates" must be an array']};let s=[],l=new Map,c=a((y,b)=>{if("problem"in y){n.push(y.problem);return}let k=l.get(y.id);if(k!==void 0){n.push(`${b}.id "${y.id}" is declared more than once (also ${k})`);return}l.set(y.id,b),s.push(y)},"take");(Array.isArray(i)?i:[]).forEach((y,b)=>c(SR(y,`gates[${b}]`,null),`gates[${b}]`));for(let y of t){let b=`${fo}/${y.file}`;if(y.parseError!==void 0){n.push(`${b}: not valid JSON: ${y.parseError}`);continue}c(SR(y.raw,b,y.file.replace(/\.json$/,"")),b)}let u=[],d=o.versionedPackages!==void 0,f=o.versionedPackages;if(f!==void 0)if(!Array.isArray(f))n.push('"versionedPackages" must be an array');else{let y=new Set;f.forEach((b,k)=>{let R=`versionedPackages[${k}]`;if(b===null||typeof b!="object"||Array.isArray(b)){n.push(`${R} must be an object`);return}let E=b,C=E.dir;if(typeof C!="string"||C.trim().length===0){n.push(`${R}.dir must be a non-empty path`);return}if(y.has(C)){n.push(`${R}.dir "${C}" is declared more than once`);return}y.add(C);let A=E.mirror;if(A!==void 0&&(typeof A!="string"||A.trim().length===0)){n.push(`${R}.mirror must be a non-empty path relative to dir`);return}let I=E.changelog;if(I!==void 0&&typeof I!="boolean"){n.push(`${R}.changelog must be a boolean`);return}let x=E.numbering;if(x!==void 0&&x!=="hand"&&x!=="promote"){n.push(`${R}.numbering must be "hand" or "promote"`);return}let w=E.tag;if(w!==void 0&&(typeof w!="string"||!w.includes("{version}"))){n.push(`${R}.tag must be a template containing {version}, e.g. "my-pkg-v{version}"`);return}if(w!==void 0&&x!=="promote"){n.push(`${R}.tag only applies with numbering "promote" \u2014 promote is what sets it`);return}let T=E.fragments;if(T!==void 0&&(typeof T!="string"||T.trim().length===0)){n.push(`${R}.fragments must be a non-empty path relative to dir`);return}u.push({dir:C.replace(/\/+$/,""),changelog:I??!0,numbering:x??"hand",fragments:typeof T=="string"?T.replace(/\/+$/,""):"changelog.d",...typeof A=="string"?{mirror:A}:{},...typeof w=="string"?{tag:w}:{}})})}let p=!1,m=o.requireWorkObject;m!==void 0&&(typeof m!="boolean"?n.push('"requireWorkObject" must be a boolean'):p=m);let g=!1,_=o.review;return _!==void 0&&(typeof _!="boolean"?n.push('"review" must be a boolean'):g=_),n.length>0?{problems:n}:{gates:s,versioned:u,versionedDeclared:d,requireWorkObject:p,review:g}}a(TU,"parseGatesManifest");function RR(e,t){let n=e.file(vr),r=e.list(fo).filter(s=>s.endsWith(".json")).sort().map(s=>{let l=e.file(`${fo}/${s}`)??"";try{return{file:s,raw:JSON.parse(l)}}catch(c){return{file:s,parseError:c.message}}});if(n===null&&r.length===0)return{kind:"absent",gates:[],versioned:[],versionedDeclared:!1,requireWorkObject:!1,review:!1};let o;if(n!==null)try{o=JSON.parse(n)}catch(s){return{kind:"invalid",path:t,problems:[`not valid JSON: ${s.message}`]}}let i=TU(o,r);return"problems"in i?{kind:"invalid",path:t,problems:i.problems}:{kind:"loaded",gates:i.gates,versioned:i.versioned,versionedDeclared:i.versionedDeclared,requireWorkObject:i.requireWorkObject,review:i.review,path:t}}a(RR,"readGatesManifest");function xr(e){return RR({file(t){try{return EU(Jp(e,t),"utf8")}catch{return null}},list(t){try{return AU(Jp(e,t),{withFileTypes:!0}).filter(n=>n.isFile()).map(n=>n.name)}catch{return[]}}},Jp(e,vr))}a(xr,"loadGatesManifest");function wl(e,t){let n=a(r=>{try{return RU("git",r,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:6e4,maxBuffer:67108864})}catch{return null}},"git");return RR({file:a(r=>n(["show",`${t}:${r}`]),"file"),list:a(r=>(n(["ls-tree","--name-only",`${t}:${r}`])??"").split(`
375
375
  `).filter(Boolean),"list")},`${t}:${vr}`)}a(wl,"loadGatesManifestAt");import{execFileSync as NU,spawnSync as HR}from"node:child_process";import{existsSync as DU,readFileSync as jU,readdirSync as PU,rmSync as MU,writeFileSync as tm}from"node:fs";import{join as Sr}from"node:path";function ER(e,t){let n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`^#+ +\\[?${n}\\]?( |$|\\])`,"m").test(e)}a(ER,"changelogHasVersionHeading");function AR(e,t){if(e.head===null)return{kind:"unreadable"};if(e.base===null)return{kind:"new-package",to:e.head};if(t.numbering==="promote"&&e.head===(e.mergeBase??e.base))return t.changelog&&e.fragmentAdded===!1?{kind:"fragment-missing",version:e.head}:{kind:"assigned-at-promote",version:e.head,fragment:e.fragmentAdded===!0};if(e.mergeBase!==null&&e.head===e.mergeBase)return{kind:"unchanged",version:e.head,base:e.base};if(e.head===e.base)return{kind:"taken",version:e.head,from:e.mergeBase};if(Lt(e.head,e.base)<=0)return{kind:"not-greater",version:e.head,base:e.base};if(t.changelog&&e.changelogHasHeading===!1)return{kind:"changelog-missing",version:e.head};if(t.mirror&&e.mirrorMatches===!1)return{kind:"mirror-mismatch",version:e.head,mirror:t.mirror};let n=[];return t.changelog&&e.changelogHasHeading===!0&&n.push("changelog entry present"),t.mirror&&e.mirrorMatches===!0&&n.push(`${t.mirror} agrees`),{kind:"ok",from:e.base,to:e.head,proof:n}}a(AR,"classifyVersionClaim");function TR(e){return e.kind==="ok"||e.kind==="new-package"||e.kind==="assigned-at-promote"}a(TR,"versionClaimPasses");function $R(e,t,n){let r=`cd ${t.dir} && npm version <patch|minor> --no-git-tag-version`;switch(e.kind){case"ok":return[`${t.dir}: ${e.from} \u2192 ${e.to}${e.proof.length?`, ${e.proof.join(", ")}`:""}.`];case"new-package":return[`${t.dir}: new on this branch (absent at ${n}), version ${e.to}.`];case"unchanged":return[`${t.dir}: code changed, but the version still reads ${e.version}.`,` ${n} carries ${e.base} \u2014 claim a number GREATER than that.`," Fix (in this PR, not after it):",` ${r}`,...t.changelog?[" + a CHANGELOG.md heading for the new version"]:[],...t.mirror?[` + the same number in ${t.dir}/${t.mirror}`]:[]];case"taken":return[`${t.dir}: the number ${e.version} is TAKEN \u2014 ${n} already carries it.`,` This branch bumped from ${e.from??"?"} to ${e.version}; someone else claimed`," the same number and merged first."," Fix: pull the base, go ONE number further, and leave their changelog block alone \u2014"," your entry moves under the new heading, not into theirs.",` git merge ${n} && ${r}`];case"not-greater":return[`${t.dir}: ${e.version} is NOT greater than ${e.base} (the state on ${n}).`," Either this branch is behind, or the number goes backwards.",` git merge ${n}, then claim a number above ${e.base}.`];case"changelog-missing":return[`${t.dir}: the version moved to ${e.version}, but CHANGELOG.md has no heading for it.`," A changelog describing the PREVIOUS version is exactly the state this gate prevents."];case"mirror-mismatch":return[`${t.dir}: ${e.mirror} does not carry the same number ${e.version}.`,` Both places must agree (package.json + ${e.mirror}).`];case"unreadable":return[`${t.dir}/package.json could not be read \u2014 no verdict, and therefore no pass.`];case"assigned-at-promote":return[`${t.dir}: ${e.version} on this branch \u2014 \`maind feature promote\` assigns the next number right before the merge`+(e.fragment?" and rolls the changelog fragment.":".")];case"fragment-missing":return[`${t.dir}: code changed, and this package keeps a changelog \u2014 but the branch adds no fragment.`," Its number is assigned by `maind feature promote`; the changelog entry travels as a file of its own:",` ${t.dir}/${t.fragments}/<branch-name>.md (a few lines; leave the version and CHANGELOG.md alone)`," Optional front matter sets the step: ---\\nbump: minor\\n--- (default: from the commit types)"]}}a($R,"versionClaimLines");function vl(e,t){let n=`${e.dir.replace(/\/+$/,"")}/`,r=new Set([`${n}package.json`]);return e.mirror&&r.add(`${n}${e.mirror}`),t.some(o=>o.startsWith(n)&&!o.endsWith(".md")&&!r.has(o))}a(vl,"versionClaimApplies");function po(e,t){let n=`${e.dir.replace(/\/+$/,"")}/${e.fragments}/`;if(!t.startsWith(n))return!1;let r=t.slice(n.length);return!r.includes("/")&&/\.md$/i.test(r)&&!/^readme\.md$/i.test(r)}a(po,"isFragmentPath");var xl=["patch","minor","major"],jn="maind-release",Sl="maind-release-withdrawn";function Rl(e){let t=e.replace(/\r\n/g,`
376
376
  `),n=/^---\n([\s\S]*?)\n---\n?/.exec(t),r=null;if(n)for(let i of n[1].split(`
377
377
  `)){let s=/^\s*bump\s*:\s*(\S+)\s*$/.exec(i);if(!s)continue;let l=s[1].toLowerCase();if(!xl.includes(l))return{problem:`bump must be patch, minor or major, got "${s[1]}"`};r=l}let o=(n?t.slice(n[0].length):t).trim();return{bump:r,body:o}}a(Rl,"parseFragment");function CR(e){let t=null;for(let n of e)(t===null||xl.indexOf(n)>xl.indexOf(t))&&(t=n);return t}a(CR,"maxLevel");function OR(e,t){return e.some(r=>/^[a-z]+(\([^)]*\))?!:/.test(r))?/^0\./.test(t)?"minor":"major":e.some(r=>/^feat(\([^)]*\))?:/.test(r))?"minor":"patch"}a(OR,"levelFromSubjects");function Vi(e,t){let n=/^(\d+)\.(\d+)\.(\d+)$/.exec(e.trim());if(!n)return null;let[r,o,i]=[Number(n[1]),Number(n[2]),Number(n[3])],s=t==="major"?`${r+1}.0.0`:t==="minor"?`${r}.${o+1}.0`:`${r}.${o}.${i+1}`;return Lt(s,e)>0?s:null}a(Vi,"nextVersion");function IR(e,t){return xl.find(n=>Vi(e,n)===t)??null}a(IR,"levelBetween");function NR(e,t){return new RegExp(`^##\\s+\\[${t.replace(/\./g,"\\.")}\\]\\s+\u2014\\s+(\\d{4}-\\d{2}-\\d{2})\\s*$`,"m").exec(e.replace(/\r\n/g,`
@@ -521,7 +521,7 @@ ${i.stderr??""}`.split(`
521
521
  `).map(l=>l.trimEnd()).filter(l=>l.trim().length>0);t.set(r,{ok:!1,exit:typeof i.status=="number"?i.status:null,tail:s.length>0?s.slice(-6):[i.message??"no output"]})}}}return t}a(aH,"runPostMergeChecks");var lH="query($owner:String!,$name:String!,$n:Int!){repository(owner:$owner,name:$name){pullRequest(number:$n){commits(last:1){nodes{commit{statusCheckRollup{contexts(first:100){nodes{... on CheckRun{isRequired(pullRequestNumber:$n)} ... on StatusContext{isRequired(pullRequestNumber:$n)}}}}}}}}}}";function Pm(e){let t=xr(e);return t.kind!=="invalid"&&t.requireWorkObject}a(Pm,"requiresWorkObject");var Mm=2,Lm=0;async function Ym(e){if(!e)return process.stderr.write(zm),2;let t=ZK();if(!t)return v("not a git repository."),1;let n=xE(t);if(n.kind==="invalid"){v(`${n.path} is not usable:`);for(let c of n.problems)v(` - ${c}`);return 1}let r=n.cfg,o=pT;if(Km=r.remote,mT=r.forge==="none",e.sub==="list"){for(let c of yE(o,{repoRoot:t,cfg:r}))process.stdout.write(`${c.name} ${c.branch} ${c.path}
522
522
  `);return 0}if(e.sub==="done"&&e.merged){let c=e.dryRun===!0;if(c||K("git",["worktree","prune"],t),e.remote===!0&&!o.fetch(t,r.remote))return v(`could not fetch ${r.remote} \u2014 nothing on the remote was touched.`),1;let u=bE(o,{repoRoot:t,cfg:r,cwd:Wt(process.cwd()),liveAgentCwds:eH(),dryRun:c,real:Wt,orphanDirs:nH,...e.remote===!0?{remote:!0}:{}});for(let p of rH(u,c))v(p);let f=(u.remoteBranches??[]).filter(p=>p.outcome!=="kept").length>0?es(t):null;return f!==null&&f!=="none"&&tT(f.kind,t).read()===!1&&v(FA(f.kind)),0}if(e.sub==="branch-cleanup"){if(r.forge==="none")return v('this repository declares "forge": "none" \u2014 there is no forge setting; maind feature done --merged --remote cleans up.'),1;let c=nc(t,r.remote,"HEAD");if(!c.ok){for(let d of c.lines)v(d);return 1}let u=LA(tT(c.kind,t),e.enable===!0);for(let d of u.lines)v(d);return u.exitCode}if(e.sub==="start"){let c=e.planFile?Wm(e.planFile):null;if(c!==null){let f=Up(c,p=>cT(p,"utf8"));if(!f.ok)return v(f.reason),2}let u=Ml(t),d=gE(o,{repoRoot:t,name:e.name,cfg:r,requirements:u});switch(d.kind){case"invalid-name":return v(d.reason),2;case"path-exists":return v(`${d.path} already exists \u2014 pick another name or remove it first.`),1;case"branch-exists":return v(`branch ${d.branch} already exists.`),1;case"start-point-missing":return v(`${d.startPoint} does not exist \u2014 this repository has no '${d.remote}' remote (or it was never fetched). maind feature needs one: ship opens a pull request against it.`),1;case"no-base-branch":return v(`could not determine the default branch of '${r.remote}'. Set "baseBranch" in ${Er}.`),1;case"worktree-failed":return v(`git refused to create the worktree at ${d.path}.`),1;case"ok":{let f=Vm(t,d.path,d.provisioned);f.length>0&&v(`provisioned ${f.length} declared path(s) from the main checkout.`),rc(r,d.path),v(`${d.branch} from ${d.startPoint}`);let p=hf(t);p&&p.length>0&&v(`${p.length} worktree(s) next to this checkout hold a branch that is gone on the remote \u2014 \`maind feature done --merged --dry-run\` shows which are proven merged.`);let m=null,g=!1,_=[];if(e.object!==void 0){let b=await co(e.object);b===null?(v(`could not resolve ${e.object} to a work object you can see \u2014 the branch is not bound.`),_.push(...aT(d.path)),g=!0):m=b.id}let y=iH({objectId:m,...e.title!==void 0?{title:e.title}:{},planPath:c});y!==null?await St(xt(y),XS(d.path,{quietStdout:!0}))!==0&&(g=!0):g||(c!==null?_.push(...oH(d.path,c)):_.push(...aT(d.path))),process.stdout.write(`${d.path}
523
523
  `);for(let b of _)v(b);return g?1:0}}}let i=mE(o,{repoRoot:t,cfg:r,...e.branch!==void 0?{branch:e.branch}:{name:e.name??""}});if(i.kind==="invalid")return v(i.reason),2;let s=i.target;if(e.sub==="approval"){let c=s.branch;if(r.forge==="none")return v('this repository declares "forge": "none" (.maind/feature.json) \u2014 there is no pull request to open, check or merge.'),v("push and merge with git; maind gate and maind feature done work as usual."),1;let u=nc(t,r.remote,c);if(!u.ok){for(let C of u.lines)v(C);return 1}let d=nT(u),f=d.ops,p=f.prFor(t,c);if(!p||p.state!=="OPEN"||!p.headSha)return v(`${c} has no open pull request \u2014 there is no merge to approve.`),2;let m=K("git",["config","--get",`branch.${c}.maindObject`],t);if(!m)return v(`no work object is bound to ${c} \u2014 this CLI reads merge approvals only for a bound branch (maind object bind --object <uuid>).`),2;let g=f.remoteSlugFor(t);if(!g)return v("the repository slug could not be read \u2014 the merge approval cannot be looked up."),1;let _=await kr("work_object_merge_approval_state",{objectId:m,ref:d.ref(g,p.number),headSha:p.headSha});if(_.kind==="unknown_tool")return v("the server cannot read merge approvals yet (older maind server)."),1;if(_.kind==="failed"||_.kind==="invalid_params")return v(`the merge approval could not be read (${_.reason}).`),1;let y=Il(_.body);if(y===null){let C=typeof _.body.error=="string"?_.body.error:"unreadable answer";return v(`the merge approval could not be read (${C})${typeof _.body.hint=="string"?` \u2014 ${_.body.hint}`:""}.`),1}let b=go(tc(),y.objectKey),k=o.exists(s.worktree)?K("git",["rev-parse","HEAD"],s.worktree):null,R=null;if(y.state==="none"||y.state==="pending"){o.fetch(t,r.remote);let C=Ql(o.exists(s.worktree)?s.worktree:t,p.headSha,`${r.remote}/${p.baseRefName}`,{note:v});R=await lm(kr,{objectId:m,ref:d.ref(g,p.number),headSha:p.headSha,base:p.baseRefName},{links:C.links,stopLine:Zl(C)})}let E=aE(y,{prNumber:p.number,headSha:p.headSha,localHead:k,url:b,carry:R});if(e.json===!0)process.stdout.write(`${JSON.stringify({state:y.state,pr:p.number,head_sha:p.headSha,object_key:y.objectKey,url:b,answered_via:y.answeredVia,verified:y.verified,mfa:y.mfa,carried_from:y.carriedFrom,carry:R,connector_head_sha:y.pr?.headSha??null,local_head_sha:k})}
524
- `);else for(let C of E.lines)v(C);return E.exitCode}if(e.sub==="ship"||e.sub==="promote"){let c=s.branch,u=r.baseBranch??o.defaultBranch(t);if(!u)return v(`could not determine the default branch of '${r.remote}'.`),1;if(r.forge==="none")return v('this repository declares "forge": "none" (.maind/feature.json) \u2014 there is no pull request to open, check or merge.'),v("push and merge with git; maind gate and maind feature done work as usual."),1;let d=nc(t,r.remote,c);if(!d.ok){for(let S of d.lines)v(S);return 1}let f=nT(d),p=f.ops;if(e.sub==="ship"){let S=s.worktree;if(s.byBranch&&!o.exists(S))return v(`no worktree holds ${c} \u2014 nothing to ship from. ${Ke("promote",s)} checks it out for the gate.`),1;if(o.exists(S)){let q=EA(S,r.remote,u);for(let H of q.lines)v(H);if(q.blocked)return v("nothing was pushed."),1;let re=K("git",["config","--get",`branch.${c}.maindObject`],t);for(let H of aR(await gl(S,`${r.remote}/${u}`,re)))v(H);let oe=K("git",["config","--get",`branch.${c}.maindObjectKey`],t);if(re&&oe){for(let H of await ml(S,`${r.remote}/${u}`,{id:re,key:oe}))v(H);for(let H of await hl(S,`${r.remote}/${u}`,{id:re,key:oe}))v(H)}}let G=K("git",["config","--get",`branch.${c}.maindObject`],t),W=K("git",["config","--get",`branch.${c}.maindObjectKey`],t);if(G&&p.prFor(t,c)?.state!=="OPEN"){let re=e.shipTitle??zi(p.branchCommits(t,`${r.remote}/${u}`,c))?.title??"",oe=await eE({branch:c,title:re},co),H=tE({forecast:oe,boundId:G,boundKey:W,title:re,titleFromFlag:e.shipTitle!==void 0,shipCommand:Ke("ship",s)});if(H){for(let _e of H)v(_e);return 1}}let F=$A(p,{repoRoot:t,worktree:S,branch:c,base:u,cfg:r,...e.shipTitle!==void 0?{title:e.shipTitle}:{}});switch(F.kind){case"no-branch":return v(`branch ${F.branch} does not exist.`),1;case"nothing-to-ship":return v(`${F.branch} has no commits of its own against ${r.remote}/${F.base}.`),1;case"push-failed":return v(`git refused to push ${F.branch}.`),1;case"pr-failed":{let q=p.lastPrError?.()??null;return v(`the pull request could not be created for ${F.branch}${q?`: ${q}`:"."}`),v(` the branch IS pushed \u2014 retry: ${Ke("ship",s)}`),1}case"ok":{v(F.created?`opened PR #${F.pr.number}`:`PR #${F.pr.number} was already open`),F.title.kind==="not-conventional"&&(v(`the PR title "${F.title.title}" is not a conventional commit, and this repository's history is.`),v(`it becomes the squash commit on ${u} \u2014 promote will refuse it until it is fixed:`),v(f.titleFix(F.pr.number,p.remoteSlugFor(t),F.title.suggestion??"<type>(<scope>): <subject>")));let q=p.remoteSlugFor(t),re=G,oe=q?f.ref(q,F.pr.number):null;if(oe&&re){let H=await Xp(re,{prNumber:a(()=>F.pr.number,"prNumber"),slug:a(()=>q,"slug"),lookup:Bi,link:a(async(X,$)=>{await St(xt(["link","--object",X,"--ref",$]))},"link")});if(H.kind==="mismatch"){for(let X of kl({branch:c,phase:"ship",prNumber:F.pr.number,ref:oe,bound:{id:re,key:W},other:H.other}))v(X);return 1}let _e=["report","--event","pr_opened","--ref",oe,"--occurred-on",Zi()],Ne=[];if(W){let X=(K("git",["-c","core.quotepath=off","diff","--name-only",`${r.remote}/${u}...HEAD`],S)??"").split(`
524
+ `);else for(let C of E.lines)v(C);return E.exitCode}if(e.sub==="ship"||e.sub==="promote"){let c=s.branch,u=r.baseBranch??o.defaultBranch(t);if(!u)return v(`could not determine the default branch of '${r.remote}'.`),1;if(r.forge==="none")return v('this repository declares "forge": "none" (.maind/feature.json) \u2014 there is no pull request to open, check or merge.'),v("push and merge with git; maind gate and maind feature done work as usual."),1;let d=nc(t,r.remote,c);if(!d.ok){for(let S of d.lines)v(S);return 1}let f=nT(d),p=f.ops;if(e.sub==="ship"){let S=s.worktree;if(s.byBranch&&!o.exists(S))return v(`no worktree holds ${c} \u2014 nothing to ship from. ${Ke("promote",s)} checks it out for the gate.`),1;if(o.exists(S)){let q=EA(S,r.remote,u);for(let H of q.lines)v(H);if(q.blocked)return v("nothing was pushed."),1;let re=K("git",["config","--get",`branch.${c}.maindObject`],t);for(let H of aR(await gl(S,`${r.remote}/${u}`,re)))v(H);let oe=K("git",["config","--get",`branch.${c}.maindObjectKey`],t);if(re&&oe){for(let H of await ml(S,`${r.remote}/${u}`,{id:re,key:oe}))v(H);for(let H of await hl(S,`${r.remote}/${u}`,{id:re,key:oe}))v(H)}}let G=K("git",["config","--get",`branch.${c}.maindObject`],t),W=K("git",["config","--get",`branch.${c}.maindObjectKey`],t);if(G&&p.prFor(t,c)?.state!=="OPEN"){let re=e.shipTitle??zi(p.branchCommits(t,`${r.remote}/${u}`,c))?.title??"",oe=await eE({branch:c,title:re},co),H=tE({forecast:oe,boundId:G,boundKey:W,title:re,titleFromFlag:e.shipTitle!==void 0,shipCommand:Ke("ship",s)});if(H){for(let _e of H)v(_e);return 1}}let F=$A(p,{repoRoot:t,worktree:S,branch:c,base:u,cfg:r,...e.shipTitle!==void 0?{title:e.shipTitle}:{}});switch(F.kind){case"no-branch":return v(`branch ${F.branch} does not exist.`),1;case"nothing-to-ship":return v(`${F.branch} has no commits of its own against ${r.remote}/${F.base}.`),1;case"push-failed":return v(`git refused to push ${F.branch}.`),1;case"pr-failed":{let q=p.lastPrError?.()??null;return v(`the pull request could not be created for ${F.branch}${q?`: ${q}`:"."}`),v(` the branch IS pushed \u2014 retry: ${Ke("ship",s)}`),1}case"ok":{v(F.created?`opened PR #${F.pr.number}`:`PR #${F.pr.number} was already open`),F.title.kind==="not-conventional"&&(v(`the PR title "${F.title.title}" is not a conventional commit, and this repository's history is.`),v(`it becomes the squash commit on ${u} \u2014 promote will refuse it until it is fixed:`),v(f.titleFix(F.pr.number,p.remoteSlugFor(t),F.title.suggestion??"<type>(<scope>): <subject>")));let q=p.remoteSlugFor(t),re=G,oe=q?f.ref(q,F.pr.number):null;if(oe&&re){let H=await Xp(re,{prNumber:a(()=>F.pr.number,"prNumber"),slug:a(()=>q,"slug"),ref:f.ref,lookup:Bi,link:a(async(X,$)=>{await St(xt(["link","--object",X,"--ref",$]))},"link")});if(H.kind==="mismatch"){for(let X of kl({branch:c,phase:"ship",prNumber:F.pr.number,ref:oe,bound:{id:re,key:W},other:H.other}))v(X);return 1}let _e=["report","--event","pr_opened","--ref",oe,"--occurred-on",Zi()],Ne=[];if(W){let X=(K("git",["-c","core.quotepath=off","diff","--name-only",`${r.remote}/${u}...HEAD`],S)??"").split(`
525
525
  `).filter(Boolean),$=K("git",["rev-parse","HEAD"],S),L=Mt(X,he=>jm(S,"HEAD",he),W,q&&$?he=>f.blobUrl(q,$,he):void 0),te=ut(Fm(ut(Um(),"maind-docs-")),"documents.json");Qi(te,JSON.stringify(L)),Ne.push("--documents-json",te)}await St(xt([..._e,...Ne]))!==0&&Ne.length>0&&await St(xt(_e)),await St(xt(["take","--object",re]));let At=K("git",["-c","core.quotepath=off","diff","--name-only",`${r.remote}/${u}...HEAD`],S)??"",nt=OA(At.split(`
526
526
  `));nt.length>0&&await St(xt(["anchor","--object",re,...nt.flatMap(X=>["--anchor",X])])),F.pr.headSha?await iE({objectId:re,ref:oe,prNumber:F.pr.number,headSha:F.pr.headSha,base:u,dashboardUrl:tc()},{call:kr,note:v}):v(`the browser merge approval was not requested: PR #${F.pr.number}'s exact head is not available yet.`)}else oe&&(v(`no work object bound to ${c} \u2014 the PR is not linked (maind object bind --object <uuid>).`),Pm(S)&&(v(`this repository's gates require a work object (requireWorkObject) \u2014 promote will refuse ${c} until it is bound,`),v(` or run it with ${Gi}="<reason>" ${Ke("promote",s)} --yes`)));return v(`next: ${Ke("promote",s)} --check [--closes-object | --progress-only] \u2014 it runs every check promote runs and names the next step; the exact-head browser approval is already on the dashboard when this organization requires one.`),process.stdout.write(`${F.pr.number}
527
527
  `),0}}}let m=p.prFor(t,c);if(m?.state==="MERGED"){let S=o.exists(s.worktree)||o.branchExists(t,c),G=o.worktreeList(t)[0]?.path??t;for(let W of _R({prNumber:m.number,mergedOn:m.mergedAt?.slice(0,10)??null,leftover:S,cleanup:wr(G,Ke("done",s))}))v(W);return process.stdout.write(`${m.number}