@lovek4d/kickflip 0.7.2 → 0.7.4

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.
@@ -48,9 +48,11 @@
48
48
  const keyButton = (it) => it.keyButton ? `<button type="button" class="btn quiet" data-settings-open="${esc(it.id)}" aria-haspopup="dialog">${esc(it.keyButton)}</button>` : '';
49
49
  const missingLinks = (it) => it.fix || it.waits
50
50
  ? `<span class="links">${it.fix ? `<span class="rd-fix">${fixHtml(it.fix)}</span>` : ''}${it.waits ? `<span class="rd-waits">${esc(it.waits)}</span>` : ''}</span>` : '';
51
- const rows = integrations.map((it) => row({ icon: tile(it.id, it.ready ? 'var(--color-quiet)' : 'var(--color-notice)'), title: esc(it.name), wrap: true, trail: keyButton(it),
51
+ // GitHub's row while its rate limit holds is red, a fault, and says when it resets
52
+ // (docs/adr/2026-11-09-1626-gh-refuses-while-githubs-hourly-limit-holds.md).
53
+ const rows = integrations.map((it) => row({ icon: tile(it.id, it.ready ? 'var(--color-quiet)' : it.limited ? 'var(--color-danger)' : 'var(--color-notice)'), title: esc(it.name), wrap: true, trail: keyButton(it),
52
54
  sub: it.ready ? `<span class="st ok">ready</span> · ${esc(it.detail)}${devices(it)}`
53
- : `<span class="st wait">not ready</span> · ${esc(it.missing)}${missingLinks(it)}` })).join('');
55
+ : `${it.limited ? '<span class="st down">rate limited</span>' : '<span class="st wait">not ready</span>'} · ${esc(it.missing)}${missingLinks(it)}` })).join('');
54
56
  return `<h2 class="label" id="integrations">Integrations</h2><div class="group">${rows}</div>
55
57
  <p class="note">A feature that needs an integration stays off until it’s ready, and says why.</p>
56
58
  `;
@@ -153,7 +153,7 @@
153
153
  }
154
154
 
155
155
  // A toast names a topic by its card's ref, the title up to its colon; an ask by its time.
156
- const toastName = (t) => (t.askedAt ? topicName(t) : String(t.title).match(/^(\S+):/)?.[1] || t.title);
156
+ const toastName = (t) => String(t.title).match(/^(\S+):/)?.[1] || t.title;
157
157
  // The topic a tap on this page acts for, the pills before it and its name, which moveOn reads once it is recorded.
158
158
  function leaving() {
159
159
  const from = soloId ? topicOf(soloId)?.id : topicId;
package/board/server.mjs CHANGED
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
- var ka=Object.defineProperty;var c=(e,t)=>ka(e,"name",{value:t,configurable:!0});async function ct(e,t){let a;try{a=await b.open(p.join(e,"data","try",`${t}.log`),"r");const{size:n}=await a.stat(),i=Math.max(0,n-64*1024),s=Buffer.alloc(n-i),{bytesRead:l}=await a.read(s,0,s.length,i),u=s.subarray(0,l).toString("utf8").split(/\r?\n/);return i&&u.shift(),u.at(-1)===""&&u.pop(),u.slice(-40)}catch(n){if(n.code==="ENOENT")return[];throw n}finally{await a?.close()}}c(ct,"tryLogTail");import{topicItems as ba}from"./review.mjs";import{boardWorktree as Ee,builderRepo as Sa,helperWorktree as va,moveQueuedLand as Oa,prWorktree as ja}from"./worktree.mjs";import{transcriptMovedAt as Pa,transcriptCommandRunning as Aa,readRunTranscript as Ea}from"./transcript.mjs";import{fromHost as Ta}from"./on-host.mjs";import{readSync as $a,writeSync as Ra,stamp as _a,patchSince as Ia}from"./sync.mjs";import{archOf as Na,readReviews as xa,repoName as Da,startReview as Ca}from"./arch-review.mjs";import Ba from"node:http";import b from"node:fs/promises";import ae from"node:os";import p from"node:path";import{execFile as ut,spawn as Ha}from"node:child_process";import{fileURLToPath as Fa}from"node:url";import dt from"node:vm";import La from"node:zlib";import Ua from"node:crypto";import{promisify as Wa}from"node:util";import{PAUSE_CLEARS as Ma,pauseWork as Ja,plans as Ga,providerOf as Ka,archivedOf as ft,buildBoard as Te,movesOf as za,visualsDue as Va,withHub as Qa,DONE_KEPT as ht,ORCHESTRATOR_PUSH_AFTER as Ya,orchestratorWaits as Xa,triageWaitOf as Za,waitsStep as qa,PROTOTYPE_WAIT as en,prototypeWaits as tn,idleStep as an,idleSummary as nn,ITEM_EVENTS as rn,QUESTION_REOPEN as $e,slimDone as pt,needsAnswer as sn,ownerActions as on,picksNoTake as ln,pullOf as wt,arrivalsOf as cn,rebasesDue as un,refOf as Re,roundsDue as dn,strictlyIdle as fn,threadOf as hn,triageOf as pn}from"./domain.mjs";import{localDay as wn,quietAt as mn,quietMiddleOf as gn,quietOf as mt}from"../hub/schedule.mjs";import{cardPRs as yn,prKeyOf as gt}from"../shared/card-prs.mjs";import{dropQueued as kn}from"../hub/push.mjs";import{closeSync as bn,createReadStream as Sn,existsSync as yt,mkdirSync as vn,openSync as On,readdirSync as jn,statSync as kt,watch as bt}from"node:fs";import{PROTOTYPE_TEMPLATE as Pn,esc as An,prototypePage as En,versionOf as _e}from"./prototype-page.mjs";import{BOARD_CLI as Tn,landByBuilder as St,launchBuilder as ne,launchedAgents as $n,pidAlive as vt,resumeBrief as Rn,resumeBuilder as _n,reviveBuilders as In,stopAgents as Nn,stopRuns as xn}from"./headless.mjs";import{replyFilesOf as Dn,dropPause as Cn,markPaused as Ot,openRuns as Bn,requestPause as jt,resumePaused as Hn,BoardError as $,publishUnscreened as Fn,readScreening as Ln,startHelper as Un,startVisual as Wn,openAgents as Pt,isLanding as Mn,implementAsIs as Jn,stageImplement as Gn,stageReply as Kn,planAgain as zn,heldMerge as Vn,opensPRs as Qn,tapMerge as Yn,addTopic as Xn,appendStatus as C,archiveCards as Zn,archiveCount as qn,archiveRank as ei,readArchive as At,boardConfig as ti,abandonLaunch as ai,cutOffBuilds as ni,abandonPlanner as ke,landable as ii,relaunchLander as ri,migrateFollowups as si,migratePhases as oi,migrateStatuses as li,moveCard as ci,stageImplementAsks as ui,startPlanner as di,stripDeadFields as fi,pickUp as hi,startRebase as pi,startTappedBuild as Ie,sendPush as wi,recordOwnerTap as mi,takeHoldStopped as gi,clearStall as yi,mergeCard as ki,readPrototypes as bi,finishHandshake as Si,itemSources as be,readAskLinks as Ne,readAskProgress as vi,readAskTitles as Oi,readBoardRules as ji,readBoardSettings as Pi,readHandshake as Ai,readHidden as Ei,readWaveNotes as Ti,readMarks as $i,readNotes as Ri,readWorking as Se,readUpload as _i,addReviewers as Ii,removeReviewers as Ni,markRead as Et,resolveNoteAnswers as xi,settleAnswers as Di,splitSharedNotes as Ci,unrecorded as Bi,saveBoardRules as Hi,saveBoardSettings as Fi,saveBoardShare as Li,saveUpload as Ui,setBoardPause as Wi,setHidden as Tt,settingsOf as B,threadFile as Mi,wavesDir as Ji}from"./store.mjs";import{changed as Gi,readHubRules as Ki}from"../hub/rules.mjs";import{boardAt as X,listenState as zi,linksOf as Vi}from"../hub/boards.mjs";import{pendingFor as Qi,shown as Yi}from"../hub/prompts.mjs";import{routeRecord as M}from"../hub/routes.mjs";import{CHOICES as Xi,FOREVER as $t,durationEnd as Rt}from"../hub/duration.mjs";import{VIEW_PAGES as Zi,shellHtml as qi}from"../hub/shell.mjs";import{crossSite as er,isJSON as tr}from"../starter/cross-site.mjs";import{shareEntries as ar}from"../hub/share.mjs";import{readUsageGate as K,launchHeld as V,pauseReasonFor as xe,pausedWords as nr,pausesRunning as _t,boardPausedUntil as ir}from"../shared/usage-gate.mjs";import{runLimit as rr}from"../hub/usage.mjs";import{audit as sr,boardAudit as or}from"../shared/audit.mjs";import{reviewWaits as lr}from"../shared/skills.mjs";import{boardRepo as De,mainRefOf as cr}from"../hub/board-repo.mjs";import{listBuildTargets as ur}from"./build-script.mjs";import{permissionsView as dr,setPermission as fr}from"./permissions.mjs";import{bumpOut as hr}from"./stop.mjs";import{SlackError as pr,SLACK_NEEDS_TOKEN as It,savedSlack as Nt,slackStatus as xt}from"../hub/slack.mjs";import{collaborators as wr,githubUser as mr}from"../hub/pr-report.mjs";const ie=p.dirname(Fa(import.meta.url));async function Dt(){const e=await b.stat(p.join(ie,"..","hub","icon-180.png")).catch(()=>null);return`/_hub/icon-180.png?v=${e?Math.floor(e.mtimeMs).toString(36):"0"}`}c(Dt,"touchIcon");async function gr(e){if(!e.includes("<!--header "))return e;const t=dt.createContext({});return dt.runInContext(await b.readFile(p.join(ie,"public","ui-page-header.js"),"utf8"),t),e.replace(/<!--header (\{.*?\})-->/g,(a,n)=>t.BoardUI.header(JSON.parse(n)))}c(gr,"pageHeaders");const o=p.resolve(process.argv[2]||process.cwd()),yr=[p.join(o,"public"),p.join(ie,"public"),p.join(ie,"..","starter","public"),p.join(ie,"..","shared")],v=p.join(o,"data"),j=c(e=>sr(or(o),e),"logCall"),F=p.join(v,"events.jsonl"),re=p.join(v,"replies"),Ct=Number(process.env.PORT||4600),z=(process.env.PREFIX||"").replace(/\/+$/,""),Bt={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".txt":"text/plain; charset=utf-8",".md":"text/markdown; charset=utf-8",".plist":"text/xml; charset=utf-8",".ipa":"application/octet-stream",".apk":"application/vnd.android.package-archive"},kr=Wa(La.gzip),br=/^(text\/|application\/json|image\/svg\+xml)/;async function Ht(e,t,a,n){!["GET","HEAD"].includes(e.req?.method)&&!e.req.boardRead&&R++;const i={...a};let s=Buffer.isBuffer(n)?n:Buffer.from(n??"");if(br.test(i["content-type"]||"")&&(i.vary="accept-encoding",s.length>=1024&&/\bgzip\b/.test(e.req?.headers["accept-encoding"]||"")))try{s=await kr(s),i["content-encoding"]="gzip"}catch{}i["content-length"]=s.length,e.writeHead(t,i),e.end(e.req?.method==="HEAD"?void 0:s)}c(Ht,"send");const h=c((e,t,a,n={})=>Ht(e,t,{"content-type":Bt[".json"],"cache-control":"no-store",...n},JSON.stringify(a)),"json"),Ft=/\b(src|href)="([\w.][\w./-]*\.(?:js|css))"/g;async function Sr(e,t){const a=t.endsWith("/")?t:p.posix.dirname(t),n=[...new Set([...e.matchAll(Ft)].map(s=>s[2]))],i=new Map(await Promise.all(n.map(async s=>{const l=await Yt(p.posix.join(a,s)),u=l?await b.stat(l).catch(()=>null):null;return[s,u?_e(u):null]})));return e.replace(Ft,(s,l,u)=>i.get(u)?`${l}="${u}?v=${i.get(u)}"`:s)}c(Sr,"versionAssets");const Lt=c((e,t)=>String(e||"").split(",").map(a=>a.trim().replace(/^W\//,"")).includes(t.replace(/^W\//,"")),"sameTag"),O=c(async(e,t)=>{try{return JSON.parse(await b.readFile(e,"utf8"))}catch{return t}},"readJSON"),J=c(async e=>{try{return(await b.readFile(e,"utf8")).split(`
3
- `).filter(Boolean).map(t=>JSON.parse(t))}catch{return[]}},"readLines"),Ce=c(e=>Mi(o,e),"threadFile"),Be=c(async()=>{const e=await Ei(o);return(await J(F)).filter(t=>!e.has(t.id))},"shownEvents");async function L(e,t=64*1024){if(!tr(e.headers))throw Object.assign(new Error("Send the body as application/json."),{status:403});let a="";for await(const n of e)if(a+=n,a.length>t)throw Object.assign(new Error("too large"),{status:413});return a?JSON.parse(a):{}}c(L,"readBody");const vr=new Set(["owner-answer","owner-answers","prototype-request","reask-request",$e,"grill-more","visual-request","item-message","ask","ask-edited","ask-withdrawn","ask-bump","ask-done","card-build","card-hold","card-done","card-bump","card-unbump","plan-waves"]),Or="This board is shared with you to look at only.",jr="Merge and Board settings stay the owner\u2019s.";async function Pr(e,t){const a=e.headers["x-kickflip-shared"];return a?ar(await W()).find(i=>i.login===a)?.level!=="full"||!["/_claude/events","/api/upload"].includes(t)||e.method!=="POST"?{refused:Or}:{by:{login:a,name:e.headers["tailscale-user-name"]||null}}:null}c(Pr,"sharedWriter");const U=c(async(e,t)=>(await He([[e,t]]))[0],"appendEvent");async function He(e){const t=new Date().toISOString(),a=e.map(([n,i])=>({id:"e"+Date.now().toString(36)+Math.random().toString(36).slice(2,7),ts:t,kind:n,data:i}));return await b.mkdir(re,{recursive:!0}),await b.appendFile(F,a.map(n=>JSON.stringify(n)+`
4
- `).join("")),R++,qt=ea(F),a.map(n=>n.id)}c(He,"appendEvents");async function Ut(e){const t=await Bi(o,e),a=new Map(((await O(p.join(v,"owner.json"),{items:[]})).items||[]).map(l=>[l.id,l]));if(!t.length)return[];const n=c(l=>a.has(String(l.id))&&ln(a.get(String(l.id)),l.choice),"again"),i=await He(t.map(l=>["owner-answer",n(l)?{...l,prototype_again:!0}:l]));await xi(o,t);const{prototype:s}=await Di(o,t);R++;for(const l of s)await te(l,"prototype");return i}c(Ut,"recordAnswers");async function Ar(e){if(!Array.isArray(e)||!e.length)return"Send at least one answer.";const t=new Set(((await O(p.join(v,"owner.json"),{items:[]})).items||[]).map(n=>n.id)),a=e.filter(n=>!t.has(String(n?.id)));return a.length?`${a.map(n=>String(n?.id)).join(", ")} ${a.length===1?"is":"are"} no longer asked here. Reload to see what is, then send again.`:null}c(Ar,"answersRefused");async function Er(e){const t=(await x()).owner.find(a=>a.id===e);return t?["open","requested"].includes(t.state)?`${e} has no answer sent, so there is nothing to reopen.`:null:`${e} is no longer asked here. Reload to see what is.`}c(Er,"reopenRefused");async function Tr(e){const t=(await x()).owner.find(a=>a.id===e);return t?t.state!=="resolved"?`${e} isn\u2019t resolved, so there is nothing to ask again.`:null:`${e} is no longer asked here. Reload to see what is.`}c(Tr,"reaskRefused");async function se(e){const a=(await Promise.all((await Dn(re,e.id)).map(async({name:n,ext:i})=>{const s=p.join(re,n);try{const[l,u]=await Promise.all([b.readFile(s,"utf8"),b.stat(s)]);return[{[i]:i==="json"?JSON.parse(l):l,at:u.mtime.toISOString()}]}catch{return[]}}))).flat();return{...e,reply:a.at(-1)||null,replies:a}}c(se,"withReply");async function $r(e,t){const a=await Se(o);let n=await Be();return e&&(n=n.filter(i=>i.kind===e)),Promise.all(n.reverse().slice(0,t).map(async i=>{const s=await se(i);return s.reply||!M(a,i.id)?s:{...s,working:M(a,i.id)}}))}c($r,"listEvents");const Fe=new Map,Rr="number,title,url,state,isDraft,reviewDecision,mergeStateStatus,mergeable,statusCheckRollup,mergedAt,headRefName,headRefOid,updatedAt,latestReviews";function _r(e,t){return new Promise(a=>{ut("gh",["pr","view",String(t),"--repo",e,"--json",Rr],{timeout:2e4},(n,i)=>{if(n)return a(null);try{a(JSON.parse(i))}catch{a(null)}})})}c(_r,"ghPR");function Ir(e,t){return new Promise(a=>{ut("gh",["api",`repos/${e}/pulls/${t}/comments?per_page=100`],{timeout:2e4},(n,i)=>{if(n)return a(0);try{const s=JSON.parse(i),l=new Set(s.map(u=>u.in_reply_to_id).filter(Boolean));a(s.filter(u=>!u.in_reply_to_id&&/codex/i.test(u.user?.login||"")&&!l.has(u.id)).length)}catch{a(0)}})})}c(Ir,"ghCodexOpen");function Nr(e){const t=e||[];let a=0,n=0;for(const i of t){const s=(i.conclusion||i.state||"").toUpperCase(),l=(i.status||"").toUpperCase();["FAILURE","ERROR","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE"].includes(s)?a++:(l&&l!=="COMPLETED"||s==="PENDING"||s==="EXPECTED")&&n++}return{total:t.length,fail:a,pending:n,state:a?"fail":n?"pending":t.length?"pass":"none"}}c(Nr,"checksOf");async function xr(e,t){const a=`${e}#${t}`;let n=Fe.get(a);return n||(n={at:0,value:null,loading:null},Fe.set(a,n)),Date.now()-n.at>45e3&&!n.loading&&(n.loading=Promise.all([_r(e,t),Ir(e,t)]).then(([i,s])=>{if(i){const{statusCheckRollup:l,latestReviews:u,...k}=i,w=Object.fromEntries((u||[]).map(A=>[A.author?.login,A.state])),y={...k,checks:Nr(l),reviews:w,codexOpen:s};JSON.stringify(y)!==JSON.stringify(n.value)&&R++,n.value=y}n.at=Date.now(),n.loading=null})),!n.value&&n.loading&&await n.loading,n.value}c(xr,"prState");const W=c(()=>ti(o),"config");async function Dr(){const e=await b.readdir(re).catch(()=>[]),t=await Promise.all(e.map(n=>b.stat(p.join(re,n)).then(i=>i.mtimeMs,()=>0))),a=Math.max(0,...t);return a?new Date(a).toISOString():null}c(Dr,"latestReply");async function Wt(e){let t=[];try{t=(await b.readdir(e)).filter(i=>i.endsWith(".json"))}catch{}const a=await Promise.all(t.map(i=>O(p.join(e,i),null))),n=Object.create(null);return t.forEach((i,s)=>{if(!a[s])return;const[l,u="main"]=p.basename(i,".json").split("@");(n[l]||=Object.create(null))[u]=a[s]}),n}c(Wt,"readAgents");async function Le(){const e=X(o);return{cfg:await W(),github:e?.github,local:e?.local,hub:e?.hub,remote:e?.remote,branch:e?.branch,topicItems:ba(),hostBusy:K({dir:o}).machine.state==="busy"}}c(Le,"outsideData");async function Mt(){return(await Promise.all((await be(o)).map(async e=>{const t=await O(e.file,null);return!t||!Array.isArray(t.items)?null:{slug:e.slug,itemFile:t,agents:await Wt(p.join(e.dir,"agents"))}}))).filter(Boolean)}c(Mt,"liveFiles");async function Cr(e){const t=await Mt();for(const a of e){const n=t.find(i=>i.slug===a.file);n?n.itemFile.items.push(a.item):t.push({slug:a.file,itemFile:{items:[a.item]},agents:{}})}return t}c(Cr,"withArchived");async function Jt({cfg:e,github:t,local:a,hub:n,remote:i,branch:s},l){const u=e.items==="waves";l??=await Mt();const k=Object.create(null);return await Promise.all(l.flatMap(({slug:w,itemFile:y})=>y.items.map(async A=>{k[`${w}/${A.id}`]=await J(Ce(`${w}/${A.id}`))}))),{github:t,remote:i,branch:s,reviewers:Qn({github:t,local:a,hub:n},B(e,{hub:n}))?e.reviewers||[]:[],fromWaves:u,autoMerge:B(e).auto_merge,skipDraft:B(e).skip_draft,autoBuild:B(e).auto_build,tickets:B(e).tickets,files:l,threads:k,working:await Se(o),autoReview:B(e).auto_review,incoming:await O(p.join(v,"incoming.json"),null),reviewRecords:await O(p.join(v,"reviews.json"),{})}}c(Jt,"itemSnapshot");async function Gt(e,t=[]){const a=await Jt(e,t.length?await Cr(t):void 0),{files:n,threads:i,working:s}=a,l=Object.create(null),u=new Set(t.map(d=>d.key));await Promise.all(n.flatMap(({slug:d,itemFile:f})=>{const P=a.github||f.repo;return f.items.flatMap(m=>yn(m,P).map(async S=>{l[gt(S)]=u.has(`${d}/${m.id}`)?Fe.get(gt(S))?.value??null:await xr(S.repo,S.number)}))}));const k=new Set(["ask","owner-answer","grill-more","visual-request","item-message","wave-triage","overnight-triage"]),w=await Promise.all((await Be()).map(d=>k.has(d.kind)||M(s,d.id)?se(d):{...d,reply:null}));await Promise.all(w.filter(d=>d.kind==="ask").map(async d=>{const f=await J(Ce(`ask:${d.id}`));f.length&&(i[`ask:${d.id}`]=f)}));const y=Object.create(null),A=Object.create(null);for(const{agents:d}of n)for(const f of Object.values(d||{}).flatMap(P=>Object.values(P))){const P=f.worktree?.path,m=P&&!f.done?Pa(P):null;m!==null&&(y[P]=m),P&&!f.done&&Aa(P)&&(A[P]=!0)}const _=Object.create(null),g=await O(p.join(v,"headless-builders.json"),{}),r=await O(p.join(v,"landers.json"),{});for(const[d,f]of[...Object.entries(r),...Object.entries(g)])f?.pid===null&&M(g,d)===f&&(_[d]="held"),Number.isInteger(f?.pid)&&(vt(f.pid)?_[d]=!0:f.handedBack?_[d]=!1:delete _[d]);return{...a,moved:y,commandRunning:A,alive:_,try:await O(p.join(v,"try.json"),null),main:await O(p.join(v,"main.json"),null),prs:l,events:w,askLinks:await Ne(o),askProgress:await vi(o),askTitles:await Oi(o),checked:await O(p.join(v,"checked.json"),null),watcher:await O(p.join(v,"watcher.json"),null),replyAt:await Dr(),owner:(d=>({...d,items:[...d.items||[],...t.flatMap(f=>f.questions||[])]}))(await O(p.join(v,"owner.json"),{items:[]})),topicItems:e.topicItems,prSlack:await O(p.join(v,"pr-slack.json"),{}),hostBusy:e.hostBusy,screening:await Ln(o)}}c(Gt,"snapshot");async function Br(e){if(!e.length)return[];const t=new Map(Te(await Gt(await Le(),e)).items.map(a=>[a.key,a]));return e.map(a=>{const n=t.get(a.key);return pt({...n,sortAt:a.closed_at,worktree:n.worktree&&!yt(n.worktree)?null:n.worktree})})}c(Br,"archivedCards");const Z=z.slice(1)||p.basename(o);let R=0,oe=null;async function x(){const e=await Le(),t=JSON.stringify([R,e]);if(oe?.key!==t){const a=Gt(e).then(Te);oe={key:t,built:a},a.catch(()=>{oe?.built===a&&(oe=null)})}return oe.built}c(x,"sharedBoard");let Ue=!1;async function Kt(){if(!Ue){Ue=!0;try{const e=await J(F),t=new Set(e.filter(a=>a.kind==="prototype-overdue").map(a=>a.data?.request));for(const a of tn(await x(),e))t.has(a.request)||Date.now()-Date.parse(a.since)<en||await U("prototype-overdue",a)}catch(e){console.error("Prototype check failed:",e.message)}finally{Ue=!1}}}c(Kt,"checkPrototypeWaits");async function Hr(e){const t=await Ai(o);if(!t?.ping?.id||t.done_at)return null;const a=e?.find(i=>i.link==="github"),n={events:t.ack?.id===t.ping.id,...a||!e?{github:!!a?.ok}:{}};return n.events&&n.github!==!1?(await Si(o),null):n}c(Hr,"setupOf");const zt=Xi.map(([e,t])=>({value:e,label:t,ms:e===$t?null:Rt(new Date(0),e)-new Date(0)})),Fr=Number(process.env.KICKFLIP_PEOPLE_WAIT_MS)||12e3,Vt=c((e,t)=>Promise.race([e,new Promise((a,n)=>setTimeout(()=>n(new Error(`${t} did not answer in time`)),Fr).unref())]),"withinWait");async function Lr(){const e={slack:null,github:[]},t=Nt(),a=X(o)?.github,[n,i]=await Promise.allSettled([!t||xt().refused?Promise.resolve(null):Vt(t.people(),"Slack"),a?Vt(wr(a),"GitHub"):Promise.resolve([])]);if(!t||xt().refused)e.slackProblem=It;else if(n.status==="fulfilled")e.slack=n.value;else{const s=n.reason;e.slackProblem=s.message==="Slack: missing_scope"?"Slack needs the users:read scope; update the app on Hub \u203A Integrations \u203A Slack.":s.message}return i.status==="fulfilled"?e.github=i.value:e.githubProblem=`GitHub: ${i.reason.message}`,e}c(Lr,"reviewerPeople");async function Ur(e){const t=Nt();if(!t)throw new $(It);return Promise.all(e.map(async a=>{const n=await mr(a.github.trim());if(!n)throw new $(`GitHub has no user ${a.github.trim()}.`);return{name:a.name.trim(),slack_user:a.slack_user.trim(),dm:await t.openDM(a.slack_user.trim()),github:n}}))}c(Ur,"withDMs");async function le(){const e=new Date().toISOString(),{title:t,reviewers:a}=await W(),n=X(o),i=n?zi(n):"deaf",s=Qa(await x(),{listen:i,prompts:n?Qi(n.route).map(Yi):[]});s.items=s.items.map(u=>u.worktree&&!yt(u.worktree)?{...u,worktree:null}:u);const l=n?Vi(n,{listen:i}):null;return{generatedAt:e,title:t,reviewers:(a||[]).map(u=>u?.name).filter(u=>typeof u=="string"),reviewerGithub:Object.fromEntries((a||[]).filter(u=>typeof u?.name=="string").map(u=>[u.name,u.github])),...s,links:l,setup:await Hr(l),holdChoices:zt,arch:n?Na(await xa(o),s.items,s.owner,{repo:Da(De(n.route)),waits:lr()}):null}}c(le,"board");async function Wr(e){["GET","HEAD"].includes(e.method)||R++;const t={...await le(),doneArchived:await qn(o)};t.owner=t.owner.filter(i=>i.state!=="resolved");const a=await $i(o);t.items=t.items.map(i=>({...i.lane==="done"?pt(i):i,readAt:a[i.key]||null}));const n=new Set(t.items.map(i=>i.key));return t.ownerTopics=t.ownerTopics.map(({title:i,summary:s,askedAt:l,...u})=>n.has(u.card)?u:{...u,title:i,summary:s,...l?{askedAt:l}:{}}),t}c(Wr,"buildPageBoard");const We=p.join(v,"sync.json");let q=await $a(We),Me=q.n,Je=Promise.resolve(),ve=null;function Mr(){return Je=Je.then(async()=>{const e=q;e.n!==Me&&(await b.mkdir(v,{recursive:!0}),await Ra(We,e),Me=e.n)}),Je}c(Mr,"saveSync");async function Jr(e){const t=await Wr(e);for((!ve||t.generatedAt>=ve.generatedAt)&&({sync:q,board:ve}=_a(q,t));Me<q.n;)await Mr();return{sync:q,board:ve}}c(Jr,"pageBoard");async function G(e,t){if(new URL(e.url,"http://site").searchParams.get("board")==="0")return;const a=await Ta(e),{sync:n,board:i}=await Jr(e);return e.boardRead=!0,{...(/^\d+$/.test(t)?Ia(n,i,Number(t)):null)||i,onHost:a}}c(G,"answerBoard");let Qt=Promise.resolve();function Gr(){const e=Qt.then(async()=>{const t=X(o);if(!t)throw Object.assign(new $("This board isn\u2019t in the hub\u2019s manifest."),{status:404});const{arch:a}=await le();if(a.last?.open)return{key:a.last.key,ref:a.last.ref,existing:!0};const{key:n,data:i}=await Ca(o,t),s=await U("arch-review",i);return{key:n,id:s,existing:!1}});return Qt=e.catch(()=>{}),e}c(Gr,"runReview");async function Kr(e){const[t,a]=e.split("/"),n=(await be(o)).find(u=>u.slug===t),i=n?await O(n.file,null):null,s=!i?.items?.some(u=>u.id===a)&&(await At(o)).find(u=>u.key===e),l=Array.isArray(i?.items)&&i.items.find(u=>u.id===a)||s?.item||null;return{item:l,questions:s?.questions||[],agents:l&&n?M(await Wt(p.join(n.dir,"agents")),a)||{}:{}}}c(Kr,"itemOf");async function zr(e){const t=M(await Ne(o),e),{tickets:a}=B(await W()),n=[];for(const i of await be(o)){const s=await O(i.file,null);for(const l of Array.isArray(s?.items)?s.items:[]){const u=`${i.slug}/${l.id}`;l.from_ask===e?n.unshift({key:u,ref:Re(s,l,a),became:!0}):u===t&&n.push({key:u,ref:Re(s,l,a),became:!1})}}return n}c(zr,"askLinksOf");async function Vr(e){const{item:t,agents:a,questions:n}=e.startsWith("ask:")?{item:null,agents:{},questions:[]}:await Kr(e),i=await O(p.join(v,"owner.json"),{items:[]}),s={...i,items:[...i.items||[],...n]},l=new Set(s.items.filter(r=>r.topic===`item:${e}`).map(r=>r.id)),u=e.startsWith("ask:"),k=u?e.slice(4):t?.from_ask||null,w=u?[]:Object.entries(await Ne(o)).filter(([,r])=>r===e).map(([r])=>r),y=[k,...w].filter(Boolean),A=new Set([e,...y.map(r=>`ask:${r}`)]),_=(await Be()).filter(r=>A.has(r.data?.key)&&rn.includes(r.kind)||r.kind==="owner-answer"&&l.has(r.data?.id)||r.kind===$e&&l.has(r.data?.question)||r.kind==="ask"&&y.includes(r.id)||r.kind==="ask-edited"),g=(await Promise.all([...A].map(r=>J(Ce(r))))).flat();return hn(e,{lines:g,events:await Promise.all(_.map(se)),owner:s,working:await Se(o),fromAsk:k,links:u?await zr(k):[],agents:a,moved:w})}c(Vr,"thread");async function Yt(e){for(const t of yr){let a=p.resolve(t,"."+e);if(a!==t&&!a.startsWith(t+p.sep))return null;if((await b.stat(a).catch(()=>null))?.isDirectory()&&(a=p.join(a,"index.html")),await b.stat(a).then(i=>i.isFile(),()=>!1))return a}return null}c(Yt,"staticFile");async function Qr(e){const t=/^\/prototype-([a-z0-9-]+)\.html$/.exec(e)?.[1];return t&&(await bi(o)).find(a=>a.topic===t)||null}c(Qr,"recordedPrototype");async function Yr(e,t){try{const a=decodeURIComponent(String(e.headers["x-filename"]||""));return h(t,200,await Ui(o,e,{name:a,type:e.headers["content-type"],length:Number(e.headers["content-length"])||0}))}catch(a){if(!(a instanceof $)&&!(a instanceof URIError))throw a;return e.resume(),h(t,a.status||400,{error:a.message})}}c(Yr,"upload");const Xr=/^(text\/html|image\/svg\+xml|application\/(xhtml\+)?xml|text\/xml)$/;async function Zr(e,t,a){const n=await _i(o,a);if(!n){t.writeHead(404).end();return}if(t.writeHead(200,{"content-type":n.type.startsWith("text/")?`${n.type}; charset=utf-8`:n.type,"content-length":n.size,"content-disposition":`inline; filename*=UTF-8''${encodeURIComponent(n.name)}`,"cache-control":"private, max-age=86400","x-content-type-options":"nosniff",...Xr.test(n.type)?{"content-security-policy":"sandbox"}:{}}),e.method==="HEAD"){t.end();return}Sn(n.path).pipe(t)}c(Zr,"sendUpload");const Ge=new Set;function qr(e,t){t.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-store",connection:"keep-alive"}),t.write(`retry: 3000
2
+ var ka=Object.defineProperty;var c=(e,t)=>ka(e,"name",{value:t,configurable:!0});async function ct(e,t){let a;try{a=await b.open(h.join(e,"data","try",`${t}.log`),"r");const{size:n}=await a.stat(),r=Math.max(0,n-64*1024),s=Buffer.alloc(n-r),{bytesRead:l}=await a.read(s,0,s.length,r),u=s.subarray(0,l).toString("utf8").split(/\r?\n/);return r&&u.shift(),u.at(-1)===""&&u.pop(),u.slice(-40)}catch(n){if(n.code==="ENOENT")return[];throw n}finally{await a?.close()}}c(ct,"tryLogTail");import{topicItems as ba}from"./review.mjs";import{boardWorktree as Ee,builderRepo as Sa,helperWorktree as va,moveQueuedLand as Oa,prWorktree as ja}from"./worktree.mjs";import{transcriptMovedAt as Aa,transcriptCommandRunning as Pa,readRunTranscript as Ta}from"./transcript.mjs";import{fromHost as Ea}from"./on-host.mjs";import{readSync as $a,writeSync as Ia,stamp as Ra,patchSince as Na}from"./sync.mjs";import{archOf as _a,readReviews as xa,repoName as Da,startReview as Ba}from"./arch-review.mjs";import Ha from"node:http";import b from"node:fs/promises";import ae from"node:os";import h from"node:path";import{spawn as Fa}from"node:child_process";import{fileURLToPath as Ca}from"node:url";import lt from"node:vm";import Wa from"node:zlib";import La from"node:crypto";import{promisify as Ua}from"node:util";import{PAUSE_CLEARS as Ma,pauseWork as Ja,plans as Ga,providerOf as Ka,archivedOf as ut,buildBoard as $e,movesOf as za,visualsDue as Va,withHub as Qa,DONE_KEPT as dt,ORCHESTRATOR_PUSH_AFTER as Ya,orchestratorWaits as Xa,triageWaitOf as Za,waitsStep as qa,PROTOTYPE_WAIT as en,prototypeWaits as tn,idleStep as an,idleSummary as nn,ITEM_EVENTS as rn,QUESTION_REOPEN as Ie,slimDone as ft,needsAnswer as sn,ownerActions as on,picksNoTake as cn,pullOf as ht,arrivalsOf as ln,rebasesDue as un,refOf as Re,roundsDue as dn,strictlyIdle as fn,threadOf as hn,triageOf as pn}from"./domain.mjs";import{localDay as wn,quietAt as mn,quietMiddleOf as gn,quietOf as pt}from"../hub/schedule.mjs";import{cardPRs as yn,prKeyOf as wt}from"../shared/card-prs.mjs";import{dropQueued as kn}from"../hub/push.mjs";import{closeSync as bn,createReadStream as Sn,existsSync as mt,mkdirSync as vn,openSync as On,readdirSync as jn,statSync as gt,watch as yt}from"node:fs";import{PROTOTYPE_TEMPLATE as An,esc as Pn,prototypePage as Tn,versionOf as Ne}from"./prototype-page.mjs";import{BOARD_CLI as En,landByBuilder as kt,launchBuilder as ne,launchedAgents as $n,pidAlive as bt,resumeBrief as In,resumeBuilder as Rn,reviveBuilders as Nn,stopAgents as _n,stopRuns as xn}from"./headless.mjs";import{replyFilesOf as Dn,dropAskCard as Bn,fileAsk as Hn,fileAsks as Fn,retitleAskCard as Cn,dropPause as Wn,markPaused as St,openRuns as Ln,requestPause as vt,resumePaused as Un,BoardError as $,publishUnscreened as Mn,readScreening as Jn,startHelper as Gn,startVisual as Kn,openAgents as Ot,isLanding as zn,implementAsIs as Vn,stageImplement as Qn,stageReply as Yn,planAgain as Xn,heldMerge as Zn,opensPRs as qn,tapMerge as ei,addTopic as ti,appendStatus as B,archiveCards as ai,archiveCount as ni,archiveRank as ii,readArchive as jt,boardConfig as ri,abandonLaunch as si,cutOffBuilds as oi,abandonPlanner as ke,landable as ci,relaunchLander as li,migrateFollowups as ui,migratePhases as di,migrateStatuses as fi,moveCard as hi,stageImplementAsks as pi,startPlanner as wi,stripDeadFields as mi,pickUp as gi,startRebase as yi,startTappedBuild as _e,sendPush as ki,recordOwnerTap as bi,takeHoldStopped as Si,clearStall as vi,mergeCard as Oi,readPrototypes as ji,finishHandshake as Ai,itemSources as be,readAskLinks as xe,readBoardRules as Pi,readBoardSettings as Ti,readHandshake as Ei,readHidden as $i,readWaveNotes as Ii,readMarks as Ri,readNotes as Ni,readWorking as Se,readUpload as _i,addReviewers as xi,removeReviewers as Di,markRead as At,resolveNoteAnswers as Bi,settleAnswers as Hi,splitSharedNotes as Fi,unrecorded as Ci,saveBoardRules as Wi,saveBoardSettings as Li,saveBoardShare as Ui,saveUpload as Mi,setBoardPause as Ji,setHidden as Pt,settingsOf as H,threadFile as Gi,wavesDir as Ki}from"./store.mjs";import{changed as zi,readHubRules as Vi}from"../hub/rules.mjs";import{GITHUB_STATE as Qi,boardAt as X,listenState as Yi,linksOf as Xi}from"../hub/boards.mjs";import{limitedUntil as Zi}from"../shared/gh.mjs";import{pendingFor as qi,shown as er}from"../hub/prompts.mjs";import{nodeBin as tr}from"../bin/install.mjs";import{routeRecord as F}from"../hub/routes.mjs";import{CHOICES as ar,FOREVER as Tt,durationEnd as Et}from"../hub/duration.mjs";import{VIEW_PAGES as nr,shellHtml as ir}from"../hub/shell.mjs";import{crossSite as rr,isJSON as sr}from"../starter/cross-site.mjs";import{shareEntries as or}from"../hub/share.mjs";import{readUsageGate as K,launchHeld as V,pauseReasonFor as De,pausedWords as cr,pausesRunning as $t,boardPausedUntil as lr}from"../shared/usage-gate.mjs";import{runLimit as ur}from"../hub/usage.mjs";import{audit as dr,boardAudit as fr}from"../shared/audit.mjs";import{reviewWaits as hr}from"../shared/skills.mjs";import{boardRepo as Be,mainRefOf as pr}from"../hub/board-repo.mjs";import{listBuildTargets as wr}from"./build-script.mjs";import{permissionsView as mr,setPermission as gr}from"./permissions.mjs";import{bumpOut as yr}from"./stop.mjs";import{SlackError as kr,SLACK_NEEDS_TOKEN as It,savedSlack as Rt,slackStatus as Nt}from"../hub/slack.mjs";import{collaborators as br,githubUser as Sr}from"../hub/pr-report.mjs";const ie=h.dirname(Ca(import.meta.url));async function _t(){const e=await b.stat(h.join(ie,"..","hub","icon-180.png")).catch(()=>null);return`/_hub/icon-180.png?v=${e?Math.floor(e.mtimeMs).toString(36):"0"}`}c(_t,"touchIcon");async function vr(e){if(!e.includes("<!--header "))return e;const t=lt.createContext({});return lt.runInContext(await b.readFile(h.join(ie,"public","ui-page-header.js"),"utf8"),t),e.replace(/<!--header (\{.*?\})-->/g,(a,n)=>t.BoardUI.header(JSON.parse(n)))}c(vr,"pageHeaders");const o=h.resolve(process.argv[2]||process.cwd()),Or=[h.join(o,"public"),h.join(ie,"public"),h.join(ie,"..","starter","public"),h.join(ie,"..","shared")],j=h.join(o,"data"),T=c(e=>dr(fr(o),e),"logCall"),C=h.join(j,"events.jsonl"),re=h.join(j,"replies"),xt=Number(process.env.PORT||4600),z=(process.env.PREFIX||"").replace(/\/+$/,""),Dt={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".txt":"text/plain; charset=utf-8",".md":"text/markdown; charset=utf-8",".plist":"text/xml; charset=utf-8",".ipa":"application/octet-stream",".apk":"application/vnd.android.package-archive"},jr=Ua(Wa.gzip),Ar=/^(text\/|application\/json|image\/svg\+xml)/;async function Bt(e,t,a,n){!["GET","HEAD"].includes(e.req?.method)&&!e.req.boardRead&&N++;const r={...a};let s=Buffer.isBuffer(n)?n:Buffer.from(n??"");if(Ar.test(r["content-type"]||"")&&(r.vary="accept-encoding",s.length>=1024&&/\bgzip\b/.test(e.req?.headers["accept-encoding"]||"")))try{s=await jr(s),r["content-encoding"]="gzip"}catch{}r["content-length"]=s.length,e.writeHead(t,r),e.end(e.req?.method==="HEAD"?void 0:s)}c(Bt,"send");const f=c((e,t,a,n={})=>Bt(e,t,{"content-type":Dt[".json"],"cache-control":"no-store",...n},JSON.stringify(a)),"json"),Ht=/\b(src|href)="([\w.][\w./-]*\.(?:js|css))"/g;async function Pr(e,t){const a=t.endsWith("/")?t:h.posix.dirname(t),n=[...new Set([...e.matchAll(Ht)].map(s=>s[2]))],r=new Map(await Promise.all(n.map(async s=>{const l=await Qt(h.posix.join(a,s)),u=l?await b.stat(l).catch(()=>null):null;return[s,u?Ne(u):null]})));return e.replace(Ht,(s,l,u)=>r.get(u)?`${l}="${u}?v=${r.get(u)}"`:s)}c(Pr,"versionAssets");const Ft=c((e,t)=>String(e||"").split(",").map(a=>a.trim().replace(/^W\//,"")).includes(t.replace(/^W\//,"")),"sameTag"),A=c(async(e,t)=>{try{return JSON.parse(await b.readFile(e,"utf8"))}catch{return t}},"readJSON"),U=c(async e=>{try{return(await b.readFile(e,"utf8")).split(`
3
+ `).filter(Boolean).map(t=>JSON.parse(t))}catch{return[]}},"readLines"),He=c(e=>Gi(o,e),"threadFile"),Fe=c(async()=>{const e=await $i(o);return(await U(C)).filter(t=>!e.has(t.id))},"shownEvents");async function W(e,t=64*1024){if(!sr(e.headers))throw Object.assign(new Error("Send the body as application/json."),{status:403});let a="";for await(const n of e)if(a+=n,a.length>t)throw Object.assign(new Error("too large"),{status:413});return a?JSON.parse(a):{}}c(W,"readBody");const Tr=new Set(["owner-answer","owner-answers","prototype-request","reask-request",Ie,"grill-more","visual-request","item-message","ask","ask-edited","ask-withdrawn","card-build","card-hold","card-done","card-bump","card-unbump","plan-waves"]),Er="This board is shared with you to look at only.",$r="Merge and Board settings stay the owner\u2019s.";async function Ir(e,t){const a=e.headers["x-kickflip-shared"];return a?or(await L()).find(r=>r.login===a)?.level!=="full"||!["/_claude/events","/api/upload"].includes(t)||e.method!=="POST"?{refused:Er}:{by:{login:a,name:e.headers["tailscale-user-name"]||null}}:null}c(Ir,"sharedWriter");const M=c(async(e,t)=>(await ve([[e,t]]))[0],"appendEvent"),Ct=c(()=>"e"+Date.now().toString(36)+Math.random().toString(36).slice(2,7),"newEventId");async function ve(e){const t=new Date().toISOString(),a=e.map(([n,r,s=Ct()])=>({id:s,ts:t,kind:n,data:r}));return await b.mkdir(re,{recursive:!0}),await b.appendFile(C,a.map(n=>JSON.stringify(n)+`
4
+ `).join("")),N++,Zt=qt(C),a.map(n=>n.id)}c(ve,"appendEvents");async function Wt(e){const t=await Ci(o,e),a=new Map(((await A(h.join(j,"owner.json"),{items:[]})).items||[]).map(l=>[l.id,l]));if(!t.length)return[];const n=c(l=>a.has(String(l.id))&&cn(a.get(String(l.id)),l.choice),"again"),r=await ve(t.map(l=>["owner-answer",n(l)?{...l,prototype_again:!0}:l]));await Bi(o,t);const{prototype:s}=await Hi(o,t);N++;for(const l of s)await te(l,"prototype");return r}c(Wt,"recordAnswers");async function Rr(e){if(!Array.isArray(e)||!e.length)return"Send at least one answer.";const t=new Set(((await A(h.join(j,"owner.json"),{items:[]})).items||[]).map(n=>n.id)),a=e.filter(n=>!t.has(String(n?.id)));return a.length?`${a.map(n=>String(n?.id)).join(", ")} ${a.length===1?"is":"are"} no longer asked here. Reload to see what is, then send again.`:null}c(Rr,"answersRefused");async function Nr(e){const t=(await x()).owner.find(a=>a.id===e);return t?["open","requested"].includes(t.state)?`${e} has no answer sent, so there is nothing to reopen.`:null:`${e} is no longer asked here. Reload to see what is.`}c(Nr,"reopenRefused");async function _r(e){const t=(await x()).owner.find(a=>a.id===e);return t?t.state!=="resolved"?`${e} isn\u2019t resolved, so there is nothing to ask again.`:null:`${e} is no longer asked here. Reload to see what is.`}c(_r,"reaskRefused");async function se(e){const a=(await Promise.all((await Dn(re,e.id)).map(async({name:n,ext:r})=>{const s=h.join(re,n);try{const[l,u]=await Promise.all([b.readFile(s,"utf8"),b.stat(s)]);return[{[r]:r==="json"?JSON.parse(l):l,at:u.mtime.toISOString()}]}catch{return[]}}))).flat();return{...e,reply:a.at(-1)||null,replies:a}}c(se,"withReply");async function xr(e,t){const a=await Se(o);let n=await Fe();return e&&(n=n.filter(r=>r.kind===e)),Promise.all(n.reverse().slice(0,t).map(async r=>{const s=await se(r);return s.reply||!F(a,r.id)?s:{...s,working:F(a,r.id)}}))}c(xr,"listEvents");const Dr=c((e,t)=>e?{title:e.title,url:e.url,state:e.state,isDraft:e.isDraft,reviewDecision:e.reviewDecision,mergeStateStatus:e.mergeStateStatus,mergeable:e.mergeable,checks:e.checks,mergedAt:e.mergedAt,headRefName:e.headRefName,headRefOid:e.headRefOid,updatedAt:e.updatedAt,reviews:e.reviews,codexOpen:e.codexOpen,limited:t}:null,"prViewOf"),L=c(()=>ri(o),"config");async function Br(){const e=await b.readdir(re).catch(()=>[]),t=await Promise.all(e.map(n=>b.stat(h.join(re,n)).then(r=>r.mtimeMs,()=>0))),a=Math.max(0,...t);return a?new Date(a).toISOString():null}c(Br,"latestReply");async function Lt(e){let t=[];try{t=(await b.readdir(e)).filter(r=>r.endsWith(".json"))}catch{}const a=await Promise.all(t.map(r=>A(h.join(e,r),null))),n=Object.create(null);return t.forEach((r,s)=>{if(!a[s])return;const[l,u="main"]=h.basename(r,".json").split("@");(n[l]||=Object.create(null))[u]=a[s]}),n}c(Lt,"readAgents");async function Ce(){const e=X(o);return{cfg:await L(),github:e?.github,local:e?.local,hub:e?.hub,remote:e?.remote,branch:e?.branch,topicItems:ba(),hostBusy:K({dir:o}).machine.state==="busy"}}c(Ce,"outsideData");async function Ut(){return(await Promise.all((await be(o)).map(async e=>{const t=await A(e.file,null);return!t||!Array.isArray(t.items)?null:{slug:e.slug,itemFile:t,agents:await Lt(h.join(e.dir,"agents"))}}))).filter(Boolean)}c(Ut,"liveFiles");async function Hr(e){const t=await Ut();for(const a of e){const n=t.find(r=>r.slug===a.file);n?n.itemFile.items.push(a.item):t.push({slug:a.file,itemFile:{items:[a.item]},agents:{}})}return t}c(Hr,"withArchived");async function Mt({cfg:e,github:t,local:a,hub:n,remote:r,branch:s},l){const u=e.items==="waves";l??=await Ut();const S=Object.create(null);return await Promise.all(l.flatMap(({slug:p,itemFile:y})=>y.items.map(async E=>{S[`${p}/${E.id}`]=await U(He(`${p}/${E.id}`))}))),{github:t,remote:r,branch:s,reviewers:qn({github:t,local:a,hub:n},H(e,{hub:n}))?e.reviewers||[]:[],fromWaves:u,autoMerge:H(e).auto_merge,skipDraft:H(e).skip_draft,autoBuild:H(e).auto_build,tickets:H(e).tickets,files:l,threads:S,working:await Se(o),autoReview:H(e).auto_review,incoming:await A(h.join(j,"incoming.json"),null),reviewRecords:await A(h.join(j,"reviews.json"),{})}}c(Mt,"itemSnapshot");async function Jt(e,t=[]){const a=await Mt(e,t.length?await Hr(t):void 0),{files:n,threads:r,working:s}=a,l=Object.create(null),u=new Set(t.map(k=>k.key)),S=(await A(Qi,{})).prs||{},p=Zi({budget:"graphql"});for(const{slug:k,itemFile:P}of n){const m=a.github||P.repo;for(const v of P.items)for(const I of yn(v,m))l[wt(I)]=u.has(`${k}/${v.id}`)?null:Dr(F(S,wt(I)),p)}const y=new Set(["ask","owner-answer","grill-more","visual-request","item-message","wave-triage","overnight-triage"]),E=await Promise.all((await Fe()).map(k=>y.has(k.kind)||F(s,k.id)?se(k):{...k,reply:null}));await Promise.all(E.filter(k=>k.kind==="ask").map(async k=>{const P=await U(He(`ask:${k.id}`));P.length&&(r[`ask:${k.id}`]=P)}));const _=Object.create(null),g=Object.create(null);for(const{agents:k}of n)for(const P of Object.values(k||{}).flatMap(m=>Object.values(m))){const m=P.worktree?.path,v=m&&!P.done?Aa(m):null;v!==null&&(_[m]=v),m&&!P.done&&Pa(m)&&(g[m]=!0)}const i=Object.create(null),d=await A(h.join(j,"headless-builders.json"),{}),w=await A(h.join(j,"landers.json"),{});for(const[k,P]of[...Object.entries(w),...Object.entries(d)])P?.pid===null&&F(d,k)===P&&(i[k]="held"),Number.isInteger(P?.pid)&&(bt(P.pid)?i[k]=!0:P.handedBack?i[k]=!1:delete i[k]);return{...a,moved:_,commandRunning:g,alive:i,try:await A(h.join(j,"try.json"),null),main:await A(h.join(j,"main.json"),null),prs:l,events:E,askLinks:await xe(o),checked:await A(h.join(j,"checked.json"),null),watcher:await A(h.join(j,"watcher.json"),null),replyAt:await Br(),owner:(k=>({...k,items:[...k.items||[],...t.flatMap(P=>P.questions||[])]}))(await A(h.join(j,"owner.json"),{items:[]})),topicItems:e.topicItems,prSlack:await A(h.join(j,"pr-slack.json"),{}),hostBusy:e.hostBusy,screening:await Jn(o)}}c(Jt,"snapshot");async function Fr(e){if(!e.length)return[];const t=new Map($e(await Jt(await Ce(),e)).items.map(a=>[a.key,a]));return e.map(a=>{const n=t.get(a.key);return ft({...n,sortAt:a.closed_at,worktree:n.worktree&&!mt(n.worktree)?null:n.worktree})})}c(Fr,"archivedCards");const Z=z.slice(1)||h.basename(o);let N=0,oe=null;async function x(){const e=await Ce(),t=JSON.stringify([N,e]);if(oe?.key!==t){const a=Jt(e).then($e);oe={key:t,built:a},a.catch(()=>{oe?.built===a&&(oe=null)})}return oe.built}c(x,"sharedBoard");let We=!1;async function Gt(){if(!We){We=!0;try{const e=await U(C),t=new Set(e.filter(a=>a.kind==="prototype-overdue").map(a=>a.data?.request));for(const a of tn(await x(),e))t.has(a.request)||Date.now()-Date.parse(a.since)<en||await M("prototype-overdue",a)}catch(e){console.error("Prototype check failed:",e.message)}finally{We=!1}}}c(Gt,"checkPrototypeWaits");async function Cr(e){const t=await Ei(o);if(!t?.ping?.id||t.done_at)return null;const a=e?.find(r=>r.link==="github"),n={events:t.ack?.id===t.ping.id,...a||!e?{github:!!a?.ok}:{}};return n.events&&n.github!==!1?(await Ai(o),null):n}c(Cr,"setupOf");const Kt=ar.map(([e,t])=>({value:e,label:t,ms:e===Tt?null:Et(new Date(0),e)-new Date(0)})),Wr=Number(process.env.KICKFLIP_PEOPLE_WAIT_MS)||12e3,zt=c((e,t)=>Promise.race([e,new Promise((a,n)=>setTimeout(()=>n(new Error(`${t} did not answer in time`)),Wr).unref())]),"withinWait");async function Lr(){const e={slack:null,github:[]},t=Rt(),a=X(o)?.github,[n,r]=await Promise.allSettled([!t||Nt().refused?Promise.resolve(null):zt(t.people(),"Slack"),a?zt(br(a),"GitHub"):Promise.resolve([])]);if(!t||Nt().refused)e.slackProblem=It;else if(n.status==="fulfilled")e.slack=n.value;else{const s=n.reason;e.slackProblem=s.message==="Slack: missing_scope"?"Slack needs the users:read scope; update the app on Hub \u203A Integrations \u203A Slack.":s.message}return r.status==="fulfilled"?e.github=r.value:e.githubProblem=`GitHub: ${r.reason.message}`,e}c(Lr,"reviewerPeople");async function Ur(e){const t=Rt();if(!t)throw new $(It);return Promise.all(e.map(async a=>{const n=await Sr(a.github.trim());if(!n)throw new $(`GitHub has no user ${a.github.trim()}.`);return{name:a.name.trim(),slack_user:a.slack_user.trim(),dm:await t.openDM(a.slack_user.trim()),github:n}}))}c(Ur,"withDMs");async function ce(){const e=new Date().toISOString(),{title:t,reviewers:a}=await L(),n=X(o),r=n?Yi(n):"deaf",s=Qa(await x(),{listen:r,prompts:n?qi(n.route).map(er):[]});s.items=s.items.map(u=>u.worktree&&!mt(u.worktree)?{...u,worktree:null}:u);const l=n?Xi(n,{listen:r}):null;return{generatedAt:e,title:t,reviewers:(a||[]).map(u=>u?.name).filter(u=>typeof u=="string"),reviewerGithub:Object.fromEntries((a||[]).filter(u=>typeof u?.name=="string").map(u=>[u.name,u.github])),...s,links:l,setup:await Cr(l),holdChoices:Kt,arch:n?_a(await xa(o),s.items,s.owner,{repo:Da(Be(n.route)),waits:hr()}):null}}c(ce,"board");async function Mr(e){["GET","HEAD"].includes(e.method)||N++;const t={...await ce(),doneArchived:await ni(o)};t.owner=t.owner.filter(r=>r.state!=="resolved");const a=await Ri(o);t.items=t.items.map(r=>({...r.lane==="done"?ft(r):r,readAt:a[r.key]||null}));const n=new Set(t.items.map(r=>r.key));return t.ownerTopics=t.ownerTopics.map(({title:r,summary:s,askedAt:l,...u})=>n.has(u.card)?u:{...u,title:r,summary:s,...l?{askedAt:l}:{}}),t}c(Mr,"buildPageBoard");const Le=h.join(j,"sync.json");let q=await $a(Le),Ue=q.n,Me=Promise.resolve(),Oe=null;function Jr(){return Me=Me.then(async()=>{const e=q;e.n!==Ue&&(await b.mkdir(j,{recursive:!0}),await Ia(Le,e),Ue=e.n)}),Me}c(Jr,"saveSync");async function Gr(e){const t=await Mr(e);for((!Oe||t.generatedAt>=Oe.generatedAt)&&({sync:q,board:Oe}=Ra(q,t));Ue<q.n;)await Jr();return{sync:q,board:Oe}}c(Gr,"pageBoard");async function G(e,t){if(new URL(e.url,"http://site").searchParams.get("board")==="0")return;const a=await Ea(e),{sync:n,board:r}=await Gr(e);return e.boardRead=!0,{...(/^\d+$/.test(t)?Na(n,r,Number(t)):null)||r,onHost:a}}c(G,"answerBoard");let Vt=Promise.resolve();function Kr(){const e=Vt.then(async()=>{const t=X(o);if(!t)throw Object.assign(new $("This board isn\u2019t in the hub\u2019s manifest."),{status:404});const{arch:a}=await ce();if(a.last?.open)return{key:a.last.key,ref:a.last.ref,existing:!0};const{key:n,data:r}=await Ba(o,t),s=await M("arch-review",r);return{key:n,id:s,existing:!1}});return Vt=e.catch(()=>{}),e}c(Kr,"runReview");async function zr(e){const[t,a]=e.split("/"),n=(await be(o)).find(u=>u.slug===t),r=n?await A(n.file,null):null,s=!r?.items?.some(u=>u.id===a)&&(await jt(o)).find(u=>u.key===e),l=Array.isArray(r?.items)&&r.items.find(u=>u.id===a)||s?.item||null;return{item:l,questions:s?.questions||[],agents:l&&n?F(await Lt(h.join(n.dir,"agents")),a)||{}:{}}}c(zr,"itemOf");async function Vr(e){const t=F(await xe(o),e),{tickets:a}=H(await L()),n=[];for(const r of await be(o)){const s=await A(r.file,null);for(const l of Array.isArray(s?.items)?s.items:[]){const u=`${r.slug}/${l.id}`;l.from_ask===e?n.unshift({key:u,ref:Re(s,l,a),became:!0}):u===t&&n.push({key:u,ref:Re(s,l,a),became:!1})}}return n}c(Vr,"askLinksOf");async function Qr(e){const{item:t,agents:a,questions:n}=e.startsWith("ask:")?{item:null,agents:{},questions:[]}:await zr(e),r=await A(h.join(j,"owner.json"),{items:[]}),s={...r,items:[...r.items||[],...n]},l=new Set(s.items.filter(i=>i.topic===`item:${e}`).map(i=>i.id)),u=e.startsWith("ask:"),S=u?e.slice(4):t?.from_ask||null,p=u?[]:Object.entries(await xe(o)).filter(([,i])=>i===e).map(([i])=>i),y=[S,...p].filter(Boolean),E=new Set([e,...y.map(i=>`ask:${i}`)]),_=(await Fe()).filter(i=>E.has(i.data?.key)&&rn.includes(i.kind)||i.kind==="owner-answer"&&l.has(i.data?.id)||i.kind===Ie&&l.has(i.data?.question)||i.kind==="ask"&&y.includes(i.id)||i.kind==="ask-edited"),g=(await Promise.all([...E].map(i=>U(He(i))))).flat();return hn(e,{lines:g,events:await Promise.all(_.map(se)),owner:s,working:await Se(o),fromAsk:S,links:u?await Vr(S):[],agents:a,moved:p})}c(Qr,"thread");async function Qt(e){for(const t of Or){let a=h.resolve(t,"."+e);if(a!==t&&!a.startsWith(t+h.sep))return null;if((await b.stat(a).catch(()=>null))?.isDirectory()&&(a=h.join(a,"index.html")),await b.stat(a).then(r=>r.isFile(),()=>!1))return a}return null}c(Qt,"staticFile");async function Yr(e){const t=/^\/prototype-([a-z0-9-]+)\.html$/.exec(e)?.[1];return t&&(await ji(o)).find(a=>a.topic===t)||null}c(Yr,"recordedPrototype");async function Xr(e,t){try{const a=decodeURIComponent(String(e.headers["x-filename"]||""));return f(t,200,await Mi(o,e,{name:a,type:e.headers["content-type"],length:Number(e.headers["content-length"])||0}))}catch(a){if(!(a instanceof $)&&!(a instanceof URIError))throw a;return e.resume(),f(t,a.status||400,{error:a.message})}}c(Xr,"upload");const Zr=/^(text\/html|image\/svg\+xml|application\/(xhtml\+)?xml|text\/xml)$/;async function qr(e,t,a){const n=await _i(o,a);if(!n){t.writeHead(404).end();return}if(t.writeHead(200,{"content-type":n.type.startsWith("text/")?`${n.type}; charset=utf-8`:n.type,"content-length":n.size,"content-disposition":`inline; filename*=UTF-8''${encodeURIComponent(n.name)}`,"cache-control":"private, max-age=86400","x-content-type-options":"nosniff",...Zr.test(n.type)?{"content-security-policy":"sandbox"}:{}}),e.method==="HEAD"){t.end();return}Sn(n.path).pipe(t)}c(qr,"sendUpload");const Je=new Set;function es(e,t){t.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-store",connection:"keep-alive"}),t.write(`retry: 3000
5
5
 
6
- `),Ge.add(t),e.on("close",()=>Ge.delete(t))}c(qr,"stream");const Xt=c(e=>{for(const t of Ge)t.write(e)},"broadcast");setInterval(()=>Xt(`: ping
6
+ `),Je.add(t),e.on("close",()=>Je.delete(t))}c(es,"stream");const Yt=c(e=>{for(const t of Je)t.write(e)},"broadcast");setInterval(()=>Yt(`: ping
7
7
 
8
- `),25e3).unref();const es=100,ts=500;let Oe=null,Zt=0;function Ke({read:e=!1}={}){e||R++;const t=Date.now();Oe||(Zt=t),clearTimeout(Oe),Oe=setTimeout(()=>{Oe=null,Xt(`event: change
8
+ `),25e3).unref();const ts=100,as=500;let je=null,Xt=0;function Ge({read:e=!1}={}){e||N++;const t=Date.now();je||(Xt=t),clearTimeout(je),je=setTimeout(()=>{je=null,Yt(`event: change
9
9
  data: ${JSON.stringify({at:new Date().toISOString()})}
10
10
 
11
- `),wa(),ma(),ga(),at(),it(),ue(),st()},Math.max(0,Math.min(es,Zt+ts-t)))}c(Ke,"changed");const as=new Set(["node_modules","dd","DerivedData"]),ze=c(e=>String(e).endsWith(".tmp")||String(e).split(/[\\/]/).some(t=>t.startsWith(".")||as.has(t)),"ignored");let qt=null;const ea=c(e=>{try{const t=kt(e);return`${t.size}:${t.mtimeMs}`}catch{return null}},"statOf");function ta(e,t){if(!t)return Ke();if(ze(t))return;const a=p.join(e,String(t));a!==We&&Ke({read:a===F&&ea(F)===qt})}c(ta,"heard");const Ve=c(e=>{try{return kt(e).isDirectory()}catch{return!1}},"isFolder");function ns(e){let t=c(()=>{},"stop"),a=!1,n=null;const i=c(()=>{n||(t(),a=!0,n=setTimeout(()=>{n=null,s()},2e3).unref())},"retry"),s=c(()=>{try{t=(process.platform==="linux"?rs:is)(e,i)}catch{i();return}a&&Ke()},"start");s()}c(ns,"watchTree");function is(e,t){const a=bt(e,{recursive:!0},(n,i)=>{ta(e,i),Ve(e)||t()});return a.on("error",t),()=>a.close()}c(is,"watchRecursive");function rs(e,t){const a=new Map,n=c(s=>{for(const[l,u]of a)(l===s||l.startsWith(s+p.sep))&&(u.close(),a.delete(l))},"unwatch"),i=c(s=>{if(a.has(s))return;const l=bt(s,(u,k)=>{if(ta(s,k),s===e&&!Ve(e))return t();if(!k||ze(k))return;const w=p.join(s,String(k));if(!Ve(w))n(w);else try{i(w)}catch{n(w)}});a.set(s,l),l.on("error",()=>s===e?t():n(s));for(const u of jn(s,{withFileTypes:!0}))if(!(!u.isDirectory()||ze(u.name)))try{i(p.join(s,u.name))}catch{n(p.join(s,u.name))}},"watchFolder");try{i(e)}catch(s){throw n(e),s}return()=>n(e)}c(rs,"watchEachFolder");async function ss(){const e=(await W()).items==="waves"?[Ji()]:(await be(o)).map(a=>a.dir),t=[...new Set([v,...e].map(a=>p.resolve(a)))];for(const a of t.filter(n=>!t.some(i=>i!==n&&n.startsWith(i+p.sep))))ns(a)}c(ss,"watchBoard");const os=Ba.createServer(async(e,t)=>{try{if(e.method!=="GET"&&e.method!=="HEAD"&&R++,e.method!=="GET"&&e.method!=="HEAD"&&er(e.headers)){t.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}),t.end("Requests must come from the app\u2019s own pages.");return}const a=new URL(e.url,"http://site");let n=a.pathname;z&&(n===z||n.startsWith(z+"/"))&&(n=n.slice(z.length)||"/");const i=e.method!=="GET"&&e.method!=="HEAD"?await Pr(e,n):null;if(i?.refused)return h(t,403,{error:i.refused});if(n==="/_claude/health")return h(t,200,{ok:!0});if(n==="/api/board"){const r=await G(e,String(a.searchParams.get("since")||"")),d=`W/"${r.version}"`;if(Lt(e.headers["if-none-match"],d)){t.writeHead(304,{etag:d,"cache-control":"no-store",vary:"accept-encoding"}).end();return}return h(t,200,r,{etag:d})}if(n==="/api/reviewer-people")return h(t,200,await Lr());if(n==="/api/questions"){const r=await x(),d=new Set(r.ownerTopics.find(f=>f.id===String(a.searchParams.get("topic")??""))?.questions||[]);return h(t,200,{questions:r.owner.filter(f=>d.has(f.id)&&f.state==="resolved")})}if(n==="/api/done"){const r=String(a.searchParams.get("before")||""),d=(await At(o)).filter(f=>!r||ei(f)<r);return h(t,200,{cards:await Br(d.slice(0,ht)),left:Math.max(0,d.length-ht)})}if(n==="/api/links")return h(t,200,{links:(await le()).links});if(n==="/api/stream"&&e.method==="GET")return qr(e,t);if(n==="/_claude/needs")return h(t,200,sn(await le()));if(n==="/api/working"){const r=await x(),d=new Map(r.items.filter(f=>f.kind==="work").map(f=>[f.key,f.ref]));return h(t,200,{working:r.workingOn.map(f=>({id:f.id,kind:f.kind,card:d.get(f.key)||null,since:f.since}))})}if(n==="/api/thread")return h(t,200,await Vr(String(a.searchParams.get("key")||"")));if(n==="/api/notes"&&a.searchParams.has("wave"))try{return h(t,200,await Ti(o,String(a.searchParams.get("wave")),B(await W()).tickets))}catch(r){if(r instanceof $)return h(t,404,{error:r.message});throw r}if(n==="/api/notes"||n==="/api/plan"){const r=String(a.searchParams.get("item")||"");try{const{itemFile:d,item:f,markdown:P}=await Ri(o,r);return h(t,200,{key:r,ref:Re(d,f,B(await W()).tickets),title:f.title,markdown:P})}catch(d){if(d instanceof $)return h(t,404,{error:d.message});throw d}}if(n==="/_claude/events"&&e.method==="POST"){const r=await L(e,524288),d=String(r.kind||"message").slice(0,64);delete r.kind;const f=String(r.since||"");if(delete r.since,i&&!vr.has(d))return h(t,403,{error:jr});for(const m of[r,...Array.isArray(r.answers)?r.answers.filter(S=>S&&typeof S=="object"):[]])delete m.shared_by,i&&(m.shared_by=i.by);if(d==="card-hold"&&r.hold&&r.for!==void 0){const m=Rt(new Date,r.for);if(!m)return h(t,400,{error:"That hold ends in the past or more than 30 days on. Pick another time."});delete r.for,m!==$t&&(r.until=m.toISOString())}if(d==="card-build"){const m=String(r.key||""),S=await Ie(o,m,E=>Ee(o,m.split("/")[1],E));return h(t,200,{id:await je(S),board:await G(e,f)})}if(d==="item-message"){const m=String(r.key||""),S=(await x()).items.find(H=>H.key===m&&H.kind==="work"),E=!String(r.text||"").trim()&&!(Array.isArray(r.files)&&r.files.length);if(S?.stalled&&await yi(o,m),E&&(S?.ready||S?.lane==="todo"))return await Jn(o,m,i?i.by.name||i.by.login:void 0),R++,h(t,200,{id:null,board:await G(e,f)});S?.lane==="todo"&&r.intent==="build"?await Gn(o,m,r):S?.lane==="todo"&&S.phase==="ready"&&await Kn(o,m,r),R++}if(d==="owner-answer")return h(t,200,{id:(await Ut([r]))[0]??null,board:await G(e,f)});if(d==="owner-answers"){const m=await Ar(r.answers);return m?h(t,400,{error:m}):h(t,200,{ids:await Ut(r.answers),board:await G(e,f)})}if(d==="card-merge"&&await Vn(o,String(r.key||"")).catch(()=>null)){const m=String(r.key);try{await Yn(o,m)}catch(S){if(S instanceof $)return h(t,400,{error:S.message});throw S}return ks(m),h(t,200,{id:null,board:await G(e,f)})}if(d==="card-merge"){try{await ki(o,String(r.key||""),{tapped:!0})}catch(m){if(m instanceof $)return h(t,400,{error:m.message});throw m}return h(t,200,{id:null,board:await G(e,f)})}if(d==="card-hold"||d==="card-done"){const m=String(r.key||"");let S,E;try{E=d==="card-hold"&&!r.hold?[]:await Pt(o,m),d==="card-done"&&await Mn(o,m)&&(E=[]),S=await mi(o,d,r)}catch(I){return h(t,I instanceof $?400:500,{error:`Couldn\u2019t write ${m} to its item file: ${I.message}`})}if(!S)return h(t,400,{error:`${m} is already in Done, so its item file stays as it is.`});const H=await Pt(o,m);E=E.filter(I=>!H.some(T=>T.pid===I.pid)),E.length&&(Nn(E),await C(o,m,"builder-stopped")),d==="card-hold"&&!r.hold&&await sa(m)}if(d==="reviewers"){const m=c(T=>T===void 0?[]:Array.isArray(T)?T:null,"list"),S=m(r.add),E=m(r.remove),H=c(T=>typeof T=="string"&&T.trim(),"text");if(!S||!E||!S.length&&!E.length)return h(t,400,{error:"Send the people to add and the names to remove, each as a list."});if(!E.every(H)||!S.every(T=>H(T?.name)&&H(T.slack_user)&&H(T.github)))return h(t,400,{error:"Each person to add needs a name, a Slack member and a GitHub login."});try{E.length&&await Ni(o,E.map(T=>T.trim())),S.length&&await Ii(o,await Ur(S))}catch(T){if(T instanceof $||T instanceof pr)return h(t,400,{error:T.message});throw T}const I=await He([...E.length?[["reviewers-removed",{remove:E}]]:[],...S.length?[["reviewers-added",{add:S.map(T=>T.name)}]]:[]]);return h(t,200,{id:I.at(-1),board:await G(e,f)})}if(d==="reask-request"){const m=await Tr(String(r.question||""));if(m)return h(t,400,{error:m})}if(d===$e){const m=await Er(String(r.question||""));if(m)return h(t,400,{error:m})}const P=await U(d,r);if((d==="card-bump"||d==="card-unbump")&&Oa(o,String(r.key||""),d==="card-bump"?new Date().toISOString():null),(d==="grill-more"||d==="prototype-request")&&r.key){const m={...r.text?{text:String(r.text)}:{},...Array.isArray(r.files)&&r.files.length?{files:r.files}:{},...r.question?{question:String(r.question),note:r.note?String(r.note):void 0,choice:r.choice??void 0}:{}},S=d==="grill-more"?"grill":"prototype";await zn(o,String(r.key),S),await te(String(r.key),S,m)}return d==="visual-request"&&r.key&&await ia((await x()).items,await J(F)),d==="ask-withdrawn"&&await Tt(o,String(r.id||""),!0),h(t,200,{id:P,board:await G(e,f)})}if(n==="/api/rules"&&e.method==="GET")return h(t,200,{board:await ji(o),hub:Ki()});if(n==="/api/rules"&&e.method==="POST"){const r=await L(e);let d;try{d=await Hi(o,r.rules,r.base??[])}catch(P){if(P instanceof $)return h(t,400,{error:P.message});throw P}const f=Gi(d.changes)?await U("rules",{scope:"board",...d.changes}):null;return h(t,200,{rules:d.rules,id:f})}if(n==="/api/settings"&&e.method==="GET")return h(t,200,{settings:await Pi(o),paused_until:ir(o),pause_choices:zt});if(n==="/api/build-targets"&&e.method==="GET"){const r=X(o);if(!r)return h(t,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const d=De(r.route);return h(t,200,{targets:await ur(d.repo,cr(d))})}if(n==="/api/pause"&&e.method==="POST"){const r=await L(e);if(!r||typeof r!="object"||Array.isArray(r)||Object.keys(r).join()!=="pause"||typeof r.pause!="string")return h(t,400,{error:"Send only {pause: a duration, a date and time, or off}."});let d;try{d=await Wi(o,r.pause)}catch(f){if(f instanceof $)return h(t,400,{error:f.message});throw f}return d&&(Xe=!0),ue(),h(t,200,{paused_until:d})}if(n==="/api/permissions"){const r=X(o);if(!r)return h(t,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const d=De(r.route).repo,f=e.method==="POST"?await L(e):null;try{return f&&await fr(d,o,f.id,f.on),h(t,200,await dr(d,o))}catch(P){if(P instanceof $)return h(t,400,{error:P.message});throw P}}if(n==="/api/try/main/log"&&e.method==="GET")return h(t,200,{lines:await ct(o,"main")});if(n==="/api/try/prototypes"&&e.method==="GET"){const r=(await O(p.join(o,"data","try.json"),{}))?.prototypes||{};return h(t,200,{state:r.state||null,https:r.state==="ready"&&r.https||null,since:r.state==="ready"&&r.since||null,lines:r.state==="failed"?await ct(o,"prototypes"):[]})}if(n==="/api/log"&&e.method==="GET"){const r=a.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(r)||r.startsWith("."))return h(t,400,{error:"file takes a log file\u2019s name."});let d;try{d=await b.open(p.join(ae.homedir(),".claude","kickflip","logs",r),"r");const{size:f}=await d.stat(),P=Math.max(0,f-256*1024),m=Buffer.alloc(f-P),{bytesRead:S}=await d.read(m,0,m.length,P),E=m.subarray(0,S).toString("utf8");return h(t,200,{text:P?E.slice(E.indexOf(`
12
- `)+1):E,truncated:P>0,size:f})}catch(f){if(f.code==="ENOENT")return h(t,404,{error:"No such log."});throw f}finally{await d?.close()}}if(n==="/api/transcript"&&e.method==="GET"){const r=a.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(r)||r.startsWith("."))return h(t,400,{error:"file takes a log file\u2019s name."});if(!/^\d+$/.test(a.searchParams.get("from")||""))return h(t,400,{error:"from takes a byte offset."});try{return h(t,200,await Ea(p.join(ae.homedir(),".claude","kickflip","logs",r),Number(a.searchParams.get("from"))))}catch(d){if(d.code==="ENOENT")return h(t,404,{error:"No such log."});throw d}}if(n==="/api/try/main/pull"&&e.method==="POST"){const r=await L(e);if(!r||typeof r!="object"||Array.isArray(r)||Object.keys(r).length)return h(t,400,{error:"Send an empty object."});try{const d=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/pull`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:z.slice(1)||p.basename(o)}),signal:AbortSignal.timeout(3e4)});return h(t,d.status,await d.json())}catch(d){return h(t,502,{error:`Could not pull: ${d.message}`})}}if(n==="/api/try"&&e.method==="POST"){const r=await L(e);if(!r||typeof r!="object"||Array.isArray(r)||Object.keys(r).some(d=>d!=="key")||typeof r.key!="string")return h(t,400,{error:"Send only the card key."});try{const d=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/build`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:z.slice(1)||p.basename(o),key:r.key}),signal:AbortSignal.timeout(1e4)});return h(t,d.status,await d.json())}catch(d){return h(t,502,{error:`Could not start the build: ${d.message}`})}}if(n==="/api/share"&&e.method==="POST")try{return h(t,200,await Li(o,await L(e)))}catch(r){if(r instanceof $)return h(t,400,{error:r.message});throw r}if(n==="/api/settings"&&e.method==="POST"){const r=await L(e);let d;try{d=await Fi(o,r)}catch(m){if(m instanceof $)return h(t,400,{error:m.message});throw m}const f=d.changed.filter(m=>m!=="idle_notification"),P=f.length?await U("settings",Object.fromEntries(f.map(m=>[m,d.settings[m]??null]))):null;return d.changed.includes("auto_merge")&&d.settings.auto_merge&&await U("check-github",{reason:"auto-merge"}),h(t,200,{settings:d.settings,id:P})}if(n==="/api/arch-review"&&e.method==="POST"){await L(e);try{return h(t,200,await Gr())}catch(r){if(r instanceof $)return h(t,r.status||400,{error:r.message});throw r}}if(n==="/api/hide"&&e.method==="POST"){const r=await L(e);try{await Tt(o,String(r.id||""),r.hidden!==!1)}catch(d){if(d instanceof $)return h(t,400,{error:d.message});throw d}return h(t,200,{ok:!0})}if(n==="/api/read"&&e.method==="POST"){const{marks:r}=await L(e);return r&&typeof r=="object"&&!Array.isArray(r)&&Object.values(r).every(f=>typeof f=="string"&&!Number.isNaN(Date.parse(f)))?(await Et(o,(await x()).items.map(f=>f.key),r),h(t,200,{ok:!0})):h(t,400,{error:"Send {marks: {card key: ISO time}}."})}if(n==="/_claude/events"&&e.method==="GET")return h(t,200,await $r(a.searchParams.get("kind"),Math.min(500,Number(a.searchParams.get("limit")||100))));if(n==="/api/upload"&&e.method==="POST")return await Yr(e,t);if(e.method!=="GET"&&e.method!=="HEAD"){t.writeHead(405).end();return}if(n.startsWith("/uploads/"))return await Zr(e,t,decodeURIComponent(n.slice(9)));if(n==="/plan.html"){t.writeHead(302,{location:`notes.html${a.search}`,"cache-control":"no-store"}).end();return}if(n==="/index.html"){t.writeHead(302,{location:`./${a.search}`,"cache-control":"no-store"}).end();return}const s=Zi.has(n.slice(1)),l=s?null:await Qr(n),u=s?null:l?Pn:n==="/prototype.html"?null:await Yt(decodeURIComponent(n));if(!s&&!u){t.writeHead(404).end();return}const k=s?".html":p.extname(u).toLowerCase(),w=s?null:await b.stat(u);let y=s?Buffer.from(qi({touchIcon:await Dt()})):l?Buffer.from(await En(o,l)):await b.readFile(u);!s&&k===".html"&&(y=(await gr(y.toString("utf8"))).replaceAll("{{title}}",An((await W()).title)).replaceAll("{{touchIcon}}",await Dt())),!s&&k===".html"&&(y=Buffer.from(await Sr(y,n)));const A=k===".html"?`W/"${Ua.createHash("sha1").update(y).digest("base64url").slice(0,20)}"`:`W/"${_e(w)}"`,_=k!==".html"&&a.searchParams.get("v")===_e(w),g={"content-type":M(Bt,k)||"application/octet-stream","cache-control":_?"public, max-age=31536000, immutable":"no-cache",etag:A};if(Lt(e.headers["if-none-match"],A)){t.writeHead(304,{etag:A,"cache-control":g["cache-control"],vary:"accept-encoding"}).end();return}return await Ht(t,200,g,y)}catch(a){t.headersSent||(a.status===403?t.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}).end(a.message):a instanceof $?h(t,a.status||400,{error:a.message}):t.writeHead(a.status||(a.code==="ENOENT"?404:400)).end())}});let Qe=!1;async function aa(){if(!Qe){Qe=!0;try{const e=Date.now(),t=(await x()).items.filter(n=>n.heldUntil&&n.lane!=="done"&&Date.parse(n.heldUntil)<=e),a=t.length?await J(F):[];for(const{key:n,heldUntil:i}of t)await hi(o,n,i,{announce:!a.some(s=>s.kind==="card-unblocked"&&s.data?.key===n&&s.data?.until===i)}),await sa(n),j({call:"pick-up",key:n,result:"resumed",reason:"Its hold ended.",inputs:{heldUntil:i}})}catch(e){console.error("Pick-up check failed:",e.message)}finally{Qe=!1}}}c(aa,"checkPickUps");let ce=null,Ye=!1,D=!1,Xe=!1;function ue(){return ce?(Ye=!0,ce):(ce=(async()=>{do{Ye=!1;try{await ls()}catch(e){console.error("Auto check failed:",e.message)}}while(Ye)})().finally(()=>{ce=null}),ce)}c(ue,"checkAuto");async function ls(){const e=B(await W()),t=await J(F),{items:a,reviews:n}=await x(),i=await Os(t),s=await pa(qe),l=e.max_implementers,u={free:wt(a,{cap:l,pulled:s,hold:i}).free},k=u.free,w=K({dir:o});Xe&&w.state!=="paused"&&await U("usage-resumed",{window:"manual"}),Xe=w.manual==="board",D=V(w,"claude"),D||await ms(),D||await ds(dn(a)),D||await bs(a,la(a),u),D||await Ss(a,u);const y=await ps(a,w);if(!D)for(const N of y.slice(0,u.free))u.free--,await ws(N);const{idle:A,pullable:_,waiting:g,pulls:r,helpers:d,bumpOuts:f,pastCap:P}=wt(a,{cap:l,pulled:s,hold:i,taken:k-u.free}),m=A&&!D;D||await ia(a,t),D||await fs(d),e.auto_pull&&!D&&await us(cn(a,{cap:e.max_planners}).map(N=>a.find(ge=>ge.key===N))),await vs(e,t,a,m);const S=_.filter(N=>i.includes(N));S.length&&j({call:"pull",result:"held",reason:"An overnight triage waits for the session's reply and names these cards, so the pull leaves them until it is answered or quiet hours end.",inputs:{cards:S}});const E=c(N=>V(N,"claude")?[]:N.near&&!N.near.going?r.slice(0,1):r,"pullNow"),H=c(async N=>{for(const ye of f.filter(Y=>Y.by===N)){const Y={call:"bump-out",key:ye.key,reason:`${N} is bumped and every implementer slot is full; this was the newest unbumped build.`,inputs:{by:N,cap:l}};try{j({...Y,result:"pausing",inputs:{...Y.inputs,lines:await hr(o,ye.key,N)}})}catch(Ae){if(!(Ae instanceof $))throw Ae;j({...Y,result:"refused",reason:`${Y.reason} The bump-out refused: ${Ae.message}`})}finally{R++}}const ge=await Ie(o,N,ye=>Ee(o,N.split("/")[1],ye),"hub"),ya=P.includes(N)?"It is bumped, so it starts past the cap.":"An implementer slot was free and it was the top ready card in Todo.";return j({call:"pull",key:N,result:cs(ge),reason:ya,inputs:{cap:l,pullable:_,waiting:g}}),je(ge)},"startPull");if(await Ps(qe,e.auto_pull,g,D?[]:E(K({dir:o})),H))return;const{due:I,repo:T}=n;!m||!I||(j({call:"pr-review",result:"asked",reason:`The board is idle and PR #${I.number} touches its work.`,inputs:{repo:T,pr:I.number,head:I.head,touches:I.touches,asked:!!I.asked}}),await U("pr-review",{pr:I.number,head:I.head,repo:T,title:I.title,author:I.author,url:I.url,touches:I.touches,asked:!!I.asked}))}c(ls,"runAuto");function Q(e="claude"){const t=V(K({dir:o}),e);return t&&(D=!0),t}c(Q,"launchHeldNow");const cs=c(e=>e?e.bumped?"bumped":"started":"refused","startResult");async function je(e){if(R++,!e||e.bumped)return null;const t={kind:e.kind,model:e.model,relaunched:!1};try{const a=K({dir:o});if(V(a,e.kind))j({call:"launch-held",key:e.key,result:"held",reason:xe(a,e.kind),inputs:{kind:e.kind,model:e.model??null,gate:a}}),a.machine.state==="busy"&&await C(o,e.key,"host-busy"),await Pe(e.key,{...t,pid:null});else{const{pid:n,log:i}=await ne(o,e);await Pe(e.key,{...t,pid:n,log:i})}}catch(a){await ai(o,e.key,`the launch failed: ${a.message}`)}return null}c(je,"handOff");async function us(e){for(const{key:t,phase:a}of e){if(Q())return;const n=a==="prototyping"?"prototype":"grill";j({call:"arrival-planner",key:t,result:n,reason:a==="prototyping"?"It landed in Todo filed prototyping.":"It landed in Todo, not filed prototyping.",inputs:{phase:a}}),await te(t,n),R++}}c(us,"launchArrivals");async function ds(e){for(const{key:t,kind:a}of e){if(Q())return;j({call:"round-planner",key:t,result:a,reason:"It was sent back from staging or ready for another round.",inputs:{kind:a}}),await te(t,a),R++}}c(ds,"launchRounds");async function fs(e){for(const{key:t,name:a}of e){if(Q())return;R++;try{const n=await Un(o,t,a,i=>va(t.split("/")[1],a,i));if(!n)continue;j({call:"helper",key:t,result:"launching",reason:"A helper was asked for and pullOf let it launch.",inputs:{name:a,model:n.model,worktree:n.worktree}});try{await ne(o,n)}catch(i){await ke(o,t,["helper-failed",{name:a,error:i.message}],a)}}catch(n){if(!(n instanceof $))throw n;await ke(o,t,["helper-failed",{name:a,error:n.message}],a)}}}c(fs,"launchHelpers");let na=Promise.resolve();function ia(e,t){const a=na.then(()=>hs(e,t));return na=a.catch(()=>{}),a}c(ia,"launchVisuals");async function hs(e,t){for(const{key:a,request:n}of Va(e,t)){if(Q())return;try{const i=await Wn(o,a,n);if(!i)continue;const{text:s,files:l}=n.data,u={...s?{text:String(s)}:{},...Array.isArray(l)&&l.length?{files:l}:{}};try{await ne(o,{...i,...u})}catch(k){await ke(o,a,["planner-failed",{kind:"visual agent",error:k.message}],i.agent)}}catch(i){if(!(i instanceof $))throw i;await C(o,a,"planner-failed",{kind:"visual agent",error:i.message})}}}c(hs,"drawVisuals");const ee=p.join(v,"queued-planners.json");async function ra(e){await b.mkdir(v,{recursive:!0}),await b.writeFile(ee+".tmp",JSON.stringify(e)+`
13
- `),await b.rename(ee+".tmp",ee)}c(ra,"saveQueuedPlanners");async function te(e,t,a={}){const n=K({dir:o});if(V(n,"claude")){D=!0,await ra({...await O(ee,{}),[e]:{kind:t,ask:a}}),j({call:"planner-queued",key:e,result:t,reason:xe(n,"claude"),inputs:{kind:t,gate:n}}),await C(o,e,n.machine.state==="busy"?"host-busy":"planner-held",{kind:t});return}try{const i=await di(o,e,t);if(!i)return;try{await ne(o,{...i,...a})}catch(s){await ke(o,e,["planner-failed",{kind:t,error:s.message}])}}catch(i){if(!(i instanceof $))throw i;await C(o,e,"planner-failed",{kind:t,error:i.message})}}c(te,"launchPlanner");async function sa(e){const t=await gi(o,e);t&&(j({call:"resume",key:e,result:t.kind,reason:"Its hold stopped this run.",inputs:t}),t.kind==="grill"||t.kind==="prototype"?await te(e,t.kind):t.label==="Builder"&&await je(await Ie(o,e,a=>Ee(o,e.split("/")[1],a),"hub")))}c(sa,"resumeRun");let oa=!1;async function ps(e,t){const a=Ja(e,{usageHeld:c(n=>_t(t,n),"usageHeld")});for(const n of a.ask)await jt(o,n,{cause:"usage",words:nr(t)})&&j({call:"pause",key:n,result:"asked",reason:xe(t,"claude"),inputs:{gate:t}});for(const n of a.drop)await Cn(o,n),j({call:"pause",key:n,result:"dropped",reason:"Its cause cleared before its builder reached a stopping point.",inputs:{}});for(const n of a.stop){await Ot(o,n);const i=$n((await Bn(o,n)).filter(l=>!Ga(l)))[0],s=i?await xn([i.pid]):[];j({call:"pause",key:n,result:"hard stop",reason:"Its builder reached no stopping point within 15 minutes of the pause.",inputs:{stopped:s.map(l=>l.pid)}})}return(a.ask.length||a.drop.length||a.stop.length)&&R++,oa=a.ask.length>0||e.some(n=>n.builder?.pause),a.resume}c(ps,"checkPauses");async function ws(e){const t=await Hn(o,e);if(!t)return;const{pause:a,record:n}=t,i=Ma[a.cause][1];R++;const s={call:"resume",key:e,reason:`Nothing pauses it any more: ${i}.`,inputs:{pause:a}};if(!n.session?.pid)return j({...s,result:"woke the orchestrator"}),U("builder-resume",{key:e,cleared:i});const l=Ka(n.kind)??"claude";try{const{pid:u,log:k}=await _n(o,{key:e,kind:l,model:n.model,session:{id:n.session.id},brief:c(w=>Rn({...w,cleared:i}),"brief")});await Pe(e,{kind:l,model:n.model,pid:u,log:k,relaunched:!1}),j({...s,result:"resumed"})}catch(u){j({...s,result:"failed",reason:`${s.reason} The launch failed: ${u.message}`}),await C(o,e,"resume-failed",{error:u.message})}}c(ws,"resumeBuild");async function ms(){for(const[e,{kind:t,ask:a}]of Object.entries(await O(ee,{}))){if(Q())return;const{[e]:n,...i}=await O(ee,{});await ra(i),j({call:"planner-queued",key:e,result:"launching",reason:"The hold it waited on has lifted.",inputs:{kind:t}}),await te(e,t,a),R++}}c(ms,"launchQueuedPlanners");async function Pe(e,t){const a=p.join(v,"headless-builders.json"),n=await O(a,{});t?n[e]=t:delete n[e],await b.mkdir(v,{recursive:!0}),await b.writeFile(a+".tmp",JSON.stringify(n)+`
14
- `),await b.rename(a+".tmp",a)}c(Pe,"trackLaunch");async function gs(){try{const e=await O(p.join(v,"headless-builders.json"),{});for(const t of(await ni(o)).filter(a=>!e[a.key]))j({call:"launch-cut-off",key:t.key,result:"launching",reason:"A restart cut off the launch after its build started.",inputs:{kind:t.kind,model:t.model??null}}),await je(t)}catch(e){console.error("Cut-off launch check failed:",e.message)}}c(gs,"launchCutOffBuilds");const la=c(e=>new Set(e.filter(t=>t.builder?.stopped).map(t=>t.key)),"stoppedKeys");let Ze=!1;async function ys(){if(!Ze){Ze=!0;try{const e=p.join(v,"headless-builders.json"),t=await O(e,{});if(!Object.keys(t).length)return;const{items:a}=Te(await Jt(await Le())),n=new Set(a.filter(w=>w.lane!=="done"&&w.builder&&!w.builder.planner).map(w=>w.key)),i=c(async w=>{j({call:"relaunch",key:w.key,result:"handed back",reason:"Its builder exited twice with its record open.",inputs:t[w.key]}),await C(o,w.key,"exited-twice")},"handBack"),s=c(async w=>{const y=t[w.key],A={call:"relaunch",key:w.key,inputs:y,reason:y.pid===null?"Its launch was held by a pause, a busy host or a start near the line, which has lifted.":"Its builder exited with its record open, for the first time."};try{const{pid:_,log:g}=await ne(o,w);return j({...A,result:y.pid===null?"launched":"relaunched"}),{pid:_,log:g}}catch(_){return j({...A,result:"failed",reason:`${A.reason} The launch failed: ${_.message}`}),await C(o,w.key,"relaunch-failed",{error:_.message}),{pid:null}}},"launch"),l=c(async(w,y)=>{j({call:"relaunch",key:w.key,result:"paused",reason:`Its run stopped at the usage limit, which resets at ${y}.`,inputs:t[w.key]}),await jt(o,w.key,{cause:"usage",words:"usage limit",until:y}),await Ot(o,w.key),R++},"atLimit"),u=new Set(a.filter(w=>w.builder?.pause?.paused).map(w=>w.key)),k=await In(t,n,{launch:s,handBack:i,held:c(w=>V(K({dir:o}),w),"held"),stopped:la(a),paused:u,limitedUntil:c(w=>w.log?rr(w.log):null,"limitedUntil"),atLimit:l});JSON.stringify(k)!==JSON.stringify(t)&&(await b.writeFile(e+".tmp",JSON.stringify(k)+`
15
- `),await b.rename(e+".tmp",e))}catch(e){console.error("Headless builder check failed:",e.message)}finally{Ze=!1}}}c(ys,"reviveHeadless");function ks(e){const t=p.join(ae.homedir(),".claude","kickflip","logs");vn(t,{recursive:!0});const a=On(p.join(t,`merge-${e.split("/")[1]}-${new Date().toISOString().replace(/[:.]/g,"-")}.log`),"a"),n=process.env.KICKFLIP_MERGE_CMD?JSON.parse(process.env.KICKFLIP_MERGE_CMD):[process.execPath,Tn],i=Ha(n[0],[...n.slice(1),"--site",Z,"merge",e],{detached:!0,stdio:["ignore",a,a]});bn(a),i.on("error",s=>C(o,e,"land-failed",{why:`the land couldn't start (${s.message})`}).catch(()=>{})),i.unref()}c(ks,"runHeldMerge");async function bs(e,t,a){const n=e.some(g=>g.waitsOn==="checking"),i=Object.fromEntries(e.map(g=>[g.key,g.waitsOn==="checking"?"checking":g.lane!=="done"&&g.agents?.some(r=>r.started&&!r.done)?"building":null])),s=p.join(v,"landers.json"),l=Object.fromEntries(Object.entries(await O(s,{})).map(([g,r])=>[g,typeof r=="string"?{verified:r,pid:null}:r])),u=new Set,k=new Set,w={};for(const[g,r]of Object.entries(l))if(!(!r.pid||vt(r.pid)||t.has(g))){if(i[g]==="checking"){r.resumed&&(w[g]={verified:null,pid:null,fresh:!0},j({call:"lander",key:g,result:"fresh next",reason:"Its resumed builder exited without taking the land up.",inputs:r}));continue}if(i[g]!=="building"){k.add(g);continue}await ri(o,g),u.add(g),j({call:"lander",key:g,result:"handed back again",reason:"Its lander exited with its builder still open.",inputs:r})}const y={...Object.fromEntries(Object.entries(l).filter(([g])=>!u.has(g)&&!k.has(g))),...w},A=c(async()=>{await b.writeFile(s+".tmp",JSON.stringify(y)+`
16
- `),await b.rename(s+".tmp",s)},"save");if(k.size&&await A(),!n&&!u.size)return;const _=ca(e,(await ii(o)).filter(g=>y[g.key]?.verified!==g.verified));if(_.length){for(const g of _){if(Q(g.kind??"claude"))break;if(!ua(e,a,g.key))continue;const r={call:"lander",key:g.key,reason:"Its build was verified and waits to land.",inputs:{verified:g.verified,before:y[g.key]??null}};try{const{pid:d,resumed:f}=await St(o,g,{fresh:y[g.key]?.fresh});y[g.key]={verified:g.verified,pid:d,...f?{resumed:f}:{}},j({...r,result:f?"resumed its builder":"launched"}),await Pe(g.key,null)}catch(d){j({...r,result:"failed",reason:`${r.reason} The launch failed: ${d.message}`}),y[g.key]={verified:g.verified,pid:null},await C(o,g.key,"lander-failed",{error:d.message})}}await A()}}c(bs,"launchLanders");const de=c((e,t)=>e.find(a=>a.key===t)?.bumpedAt||"","bumpOf"),ca=c((e,t)=>[...t.filter(a=>de(e,a.key)).sort((a,n)=>de(e,a.key).localeCompare(de(e,n.key))),...t.filter(a=>!de(e,a.key))],"byBump");function ua(e,t,a){return!de(e,a)&&t.free<=0?!1:(t.free--,!0)}c(ua,"takeSlot");async function Ss(e,t){const a=p.join(v,"rebases.json"),n=await O(a,{}),{base:i}=Sa(o),s=ca(e,un(e,Object.fromEntries(Object.entries(n).map(([l,u])=>[l,u.conflict])),i));if(s.length)try{for(const l of s){if(Q())break;if(!ua(e,t,l.key))continue;const u=l.key.split("/")[1],k={call:"rebase-run",key:l.key,reason:`Its PR #${l.pr} conflicts with ${i}.`,inputs:{pr:l.pr,conflict:l.conflict,before:n[l.key]??null}};n[l.key]={conflict:l.conflict,pid:null};let w;try{w=await pi(o,l.key,{pr:l.pr,conflict:l.conflict,base:i},()=>ja(o,u,l.branch))}catch(y){if(!(y instanceof $))throw y;j({...k,result:"refused",reason:`${k.reason} The start refused: ${y.message}`}),await C(o,l.key,"rebase-failed",{pr:l.pr,error:y.message});continue}finally{R++}try{n[l.key].pid=(await St(o,w)).pid,j({...k,result:"launched"})}catch(y){j({...k,result:"failed",reason:`${k.reason} The launch failed: ${y.message}`}),await C(o,l.key,"rebase-launch-failed",{error:y.message})}}}finally{await b.writeFile(a+".tmp",JSON.stringify(n)+`
17
- `),await b.rename(a+".tmp",a)}}c(Ss,"launchRebases");async function da(){const e=gn(new Date,mt(await O(p.join(ae.homedir(),".claude","kickflip","settings.json"),{})));return e&&wn(e)}c(da,"triageNight");async function vs(e,t,a,n){const i=p.join(v,"triaged.json"),s=await O(i,{}),{waiting:l,seen:u}=pn(a),k=c(async()=>{await b.mkdir(v,{recursive:!0}),await b.writeFile(i+".tmp",JSON.stringify({seen:u})+`
18
- `),await b.rename(i+".tmp",i)},"save"),w=t.filter(g=>g.kind==="overnight-triage").at(-1);if(w&&!(await se(w)).reply){s.seen!==u&&await k();return}if(!e.auto_triage||!n||l.length<2||u===s.seen)return;const y=await da();if(!y||w?.data?.date===y)return;const A=`overnight-triage-${y}`;((await O(p.join(v,"owner.json"),{topics:[]})).topics||[]).some(g=>g.id===A)||await Xn(o,{id:A,title:`Overnight triage ${y}`}),j({call:"triage",result:"sent",reason:`Auto triage is on, ${l.length} cards wait on answers, and tonight's triage hadn't gone out.`,inputs:{date:y,cards:l}}),await U("overnight-triage",{date:y,topic:A,cards:l}),await k()}c(vs,"checkTriage");async function fa(e){const t=e.filter(a=>a.kind==="overnight-triage").at(-1);return t&&!(await se(t)).reply?t:null}c(fa,"unansweredTriage");async function Os(e){const t=await fa(e),a=mt(await O(p.join(ae.homedir(),".claude","kickflip","settings.json"),{}));return t&&mn(new Date,a)?t.data.cards||[]:[]}c(Os,"triageHold");async function js(){if(!B(await W()).auto_triage)return!1;const e=await da();return!!e&&(await J(F)).filter(t=>t.kind==="overnight-triage").at(-1)?.data?.date!==e}c(js,"triageDue");const qe={file:"pulled.json",line:"pulled"},ha=c(e=>e.keys||(e.key?[e.key]:[]),"markedOf"),pa=c(async({file:e})=>ha(await O(p.join(v,e),{})),"sentOf");async function Ps({file:e,line:t},a,n,i,s){const l=p.join(v,e),u=a?[...n,...i]:[];for(const k of a?i:[])await C(o,k,t),await s(k);return JSON.stringify(ha(await O(l,{})))!==JSON.stringify(u)&&(await b.mkdir(v,{recursive:!0}),await b.writeFile(l+".tmp",JSON.stringify({keys:u,at:new Date().toISOString()})+`
19
- `),await b.rename(l+".tmp",l)),u.length>0}c(Ps,"sendPulls");async function et(e,{key:t,reason:a,inputs:n}){const i={call:"push",key:t,reason:a,inputs:{tag:e.tag,title:e.title,...n}};try{await wi(o,e),j({...i,result:"sent"})}catch(s){if(!(s instanceof $))throw s;const l=s.message.split(`
20
- `).at(-2)||s.message;j({...i,result:"not sent",reason:`${a} Not sent: ${l}`}),console.error(`Push ${e.tag} not sent:`,l)}}c(et,"sendOwnPush");let fe=null,tt=!1;function at(){return fe?(tt=!0,fe):(fe=(async()=>{do{tt=!1;try{await As()}catch(e){console.error("Action push check failed:",e.message)}}while(tt)})().finally(()=>{fe=null}),fe)}c(at,"checkActions");async function As(){const e=p.join(v,"action-pushes.json"),t=await O(e,null),a=on(await x()),n=Object.fromEntries(a.map(i=>[i.tag,{kind:i.kind,ids:i.questions.map(s=>s.id)}]));if(t){for(const s of a){const l=M(t,s.tag),u=s.questions.filter(y=>!l?.ids?.includes(y.id));if(l?.kind===s.kind&&!u.length)continue;const k=u.length?`questions.html#${encodeURIComponent(u[0].id)}`:s.url,w=l?l.kind!==s.kind?`What waits on the owner changed from ${l.kind} to ${s.kind}.`:"A new question waits on the owner.":"Something new waits on the owner.";await et({kind:"action",title:s.title,body:u[0]?.question||s.body,url:`/${Z}/${k}`,tag:s.tag,now:s.now},{key:s.tag.startsWith("item-")?s.tag.slice(5):void 0,reason:w,inputs:{was:l??null,kind:s.kind,fresh:u.map(y=>y.id)}})}const i=Object.keys(t).filter(s=>!M(n,s));i.length&&await kn(i,Z)}JSON.stringify(n)!==JSON.stringify(t)&&(await b.mkdir(v,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify(n)+`
21
- `),await b.rename(e+".tmp",e))}c(As,"pushActions");let he=null,nt=!1;function it(){return he?(nt=!0,he):(he=(async()=>{do{nt=!1;try{await Es()}catch(e){console.error("Orchestrator wait check failed:",e.message)}}while(nt)})().finally(()=>{he=null}),he)}c(it,"checkWaits");async function Es(){const e=p.join(v,"orchestrator-waits.json"),t=await O(e,null),a=t?.waits||[],n=await fa(await J(F)),i=[...Xa(await x()),...n?[Za(n,M(await Se(o),n.id))]:[]],s=qa(a,i,new Date),l=[];for(const u of s.waits)u.kind!=="triage"&&!u.picked&&!u.pushed&&Date.now()-Date.parse(u.since)>=Ya?(await et({kind:"reminder",title:`${u.ref} waits on the orchestrator`,body:`${u.title}: ${u.next}`,url:`/${Z}/#selectedCard=${encodeURIComponent(u.key)}`,tag:`wait-${u.key}`,now:!0},{key:u.key,reason:`It has waited on the orchestrator since ${u.since} and nobody picked it up.`,inputs:{kind:u.kind,since:u.since}}),l.push({...u,pushed:!0})):l.push(u);(!t||JSON.stringify(l)!==JSON.stringify(a))&&(await b.mkdir(v,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify({waits:l})+`
22
- `),await b.rename(e+".tmp",e)),(s.added.length||s.again.length)&&await U("orchestrator-waits",{added:s.added,again:s.again})}c(Es,"stepWaits");let pe=null,rt=!1;function st(){return pe?(rt=!0,pe):(pe=(async()=>{do{rt=!1;try{await Ts()}catch(e){console.error("Idle check failed:",e.message)}}while(rt)})().finally(()=>{pe=null}),pe)}c(st,"checkIdle");async function Ts(){const e=p.join(v,"idle.json"),t=await W();if(!B(t).idle_notification)return b.rm(e,{force:!0});const a=await O(e,{}),n=await x(),i=fn(n,{pulled:await pa(qe)}),{state:s,push:l}=an(a,{idle:i,now:new Date});l&&await et({kind:"reminder",title:`${t.title||Z} has gone idle`,body:nn(n.items,a.last),url:`/${Z}/`,tag:"idle",now:!0},{reason:"The board has been strictly idle long enough, with idle notifications on.",inputs:{previous:a}}),JSON.stringify(s)!==JSON.stringify(a)&&(await b.mkdir(v,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify(s)+`
23
- `),await b.rename(e+".tmp",e))}c(Ts,"stepIdle");let we=null,ot=!1;function wa(){return we?(ot=!0,we):(we=(async()=>{do{ot=!1;try{const e=za(await x());for(const t of e)await ci(o,t.key,t.to,t.why,t.phase,t.from);e.length&&R++}catch(e){console.error("Lane settle failed:",e.message)}}while(ot)})().finally(()=>{we=null}),we)}c(wa,"settleLanes");let me=null,lt=!1;function ma(){return me?(lt=!0,me):(me=(async()=>{do{lt=!1;try{await $s()}catch(e){console.error("Done archive failed:",e.message)}}while(lt)})().finally(()=>{me=null}),me)}c(ma,"checkArchive");async function ga(){try{await Et(o,(await x()).items.map(e=>e.key))}catch(e){console.error("Read marks failed:",e.message)}}c(ga,"markNewCards");async function $s(){if(!ft(await x()).length)return;const e=ft(await le()),t=e.length?await Zn(o,e.map(a=>({key:a.key,closed_at:a.sortAt,prs:a.prs}))):[];t.length&&console.log(`Done archive: ${t.length} card${t.length===1?"":"s"} moved`)}c($s,"archiveDone");try{for(const e of await si(o))console.log(e)}catch(e){console.error("follow-up migration:",e.message)}try{for(const e of await li(o))console.log(e)}catch(e){console.error("status migration:",e.message)}try{for(const e of await oi(o))console.log(e)}catch(e){console.error("phase migration:",e.message)}try{for(const e of await ui(o))console.log(e)}catch(e){console.error("staging migration:",e.message)}try{for(const e of await Ci(o))console.log(e)}catch(e){console.error("notes split:",e.message)}try{await fi(o)}catch(e){console.error("dead-field strip:",e.message)}await gs(),await wa(),await ma(),await ga(),await ss(),setInterval(()=>{R++,Kt(),ys(),Fn(o).then(t=>{t.length&&R++},t=>console.error("Screen publish failed:",t.message)),aa();const e=K({dir:o});D&&!V(e,"claude")||oa||!D&&_t(e,"claude")?ue():js().then(t=>{t&&ue()},t=>console.error("Triage check failed:",t.message)),at(),it(),st()},6e4).unref(),await Kt(),await aa(),await ue(),at(),it(),st(),os.listen(Ct,"127.0.0.1",()=>console.log(`site on http://127.0.0.1:${Ct}/ (prefix ${z||"none"})`));
11
+ `),ma(),pa(),wa(),ga(),tt(),nt(),ue(),rt()},Math.max(0,Math.min(ts,Xt+as-t)))}c(Ge,"changed");const ns=new Set(["node_modules","dd","DerivedData"]),Ke=c(e=>String(e).endsWith(".tmp")||String(e).split(/[\\/]/).some(t=>t.startsWith(".")||ns.has(t)),"ignored");let Zt=null;const qt=c(e=>{try{const t=gt(e);return`${t.size}:${t.mtimeMs}`}catch{return null}},"statOf");function ea(e,t){if(!t)return Ge();if(Ke(t))return;const a=h.join(e,String(t));a!==Le&&Ge({read:a===C&&qt(C)===Zt})}c(ea,"heard");const ze=c(e=>{try{return gt(e).isDirectory()}catch{return!1}},"isFolder");function is(e){let t=c(()=>{},"stop"),a=!1,n=null;const r=c(()=>{n||(t(),a=!0,n=setTimeout(()=>{n=null,s()},2e3).unref())},"retry"),s=c(()=>{try{t=(process.platform==="linux"?ss:rs)(e,r)}catch{r();return}a&&Ge()},"start");s()}c(is,"watchTree");function rs(e,t){const a=yt(e,{recursive:!0},(n,r)=>{ea(e,r),ze(e)||t()});return a.on("error",t),()=>a.close()}c(rs,"watchRecursive");function ss(e,t){const a=new Map,n=c(s=>{for(const[l,u]of a)(l===s||l.startsWith(s+h.sep))&&(u.close(),a.delete(l))},"unwatch"),r=c(s=>{if(a.has(s))return;const l=yt(s,(u,S)=>{if(ea(s,S),s===e&&!ze(e))return t();if(!S||Ke(S))return;const p=h.join(s,String(S));if(!ze(p))n(p);else try{r(p)}catch{n(p)}});a.set(s,l),l.on("error",()=>s===e?t():n(s));for(const u of jn(s,{withFileTypes:!0}))if(!(!u.isDirectory()||Ke(u.name)))try{r(h.join(s,u.name))}catch{n(h.join(s,u.name))}},"watchFolder");try{r(e)}catch(s){throw n(e),s}return()=>n(e)}c(ss,"watchEachFolder");async function os(){const e=(await L()).items==="waves"?[Ki()]:(await be(o)).map(a=>a.dir),t=[...new Set([j,...e].map(a=>h.resolve(a)))];for(const a of t.filter(n=>!t.some(r=>r!==n&&n.startsWith(r+h.sep))))is(a)}c(os,"watchBoard");const cs=Ha.createServer(async(e,t)=>{try{if(e.method!=="GET"&&e.method!=="HEAD"&&N++,e.method!=="GET"&&e.method!=="HEAD"&&rr(e.headers)){t.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}),t.end("Requests must come from the app\u2019s own pages.");return}const a=new URL(e.url,"http://site");let n=a.pathname;z&&(n===z||n.startsWith(z+"/"))&&(n=n.slice(z.length)||"/");const r=e.method!=="GET"&&e.method!=="HEAD"?await Ir(e,n):null;if(r?.refused)return f(t,403,{error:r.refused});if(n==="/_claude/health")return f(t,200,{ok:!0});if(n==="/api/board"){const i=await G(e,String(a.searchParams.get("since")||"")),d=`W/"${i.version}"`;if(Ft(e.headers["if-none-match"],d)){t.writeHead(304,{etag:d,"cache-control":"no-store",vary:"accept-encoding"}).end();return}return f(t,200,i,{etag:d})}if(n==="/api/reviewer-people")return f(t,200,await Lr());if(n==="/api/questions"){const i=await x(),d=new Set(i.ownerTopics.find(w=>w.id===String(a.searchParams.get("topic")??""))?.questions||[]);return f(t,200,{questions:i.owner.filter(w=>d.has(w.id)&&w.state==="resolved")})}if(n==="/api/done"){const i=String(a.searchParams.get("before")||""),d=(await jt(o)).filter(w=>!i||ii(w)<i);return f(t,200,{cards:await Fr(d.slice(0,dt)),left:Math.max(0,d.length-dt)})}if(n==="/api/links")return f(t,200,{links:(await ce()).links});if(n==="/api/stream"&&e.method==="GET")return es(e,t);if(n==="/_claude/needs")return f(t,200,sn(await ce()));if(n==="/api/working"){const i=await x(),d=new Map(i.items.filter(w=>w.kind==="work").map(w=>[w.key,w.ref]));return f(t,200,{working:i.workingOn.map(w=>({id:w.id,kind:w.kind,card:d.get(w.key)||null,since:w.since}))})}if(n==="/api/thread")return f(t,200,await Qr(String(a.searchParams.get("key")||"")));if(n==="/api/notes"&&a.searchParams.has("wave"))try{return f(t,200,await Ii(o,String(a.searchParams.get("wave")),H(await L()).tickets))}catch(i){if(i instanceof $)return f(t,404,{error:i.message});throw i}if(n==="/api/notes"||n==="/api/plan"){const i=String(a.searchParams.get("item")||"");try{const{itemFile:d,item:w,markdown:k}=await Ni(o,i);return f(t,200,{key:i,ref:Re(d,w,H(await L()).tickets),title:w.title,markdown:k})}catch(d){if(d instanceof $)return f(t,404,{error:d.message});throw d}}if(n==="/_claude/events"&&e.method==="POST"){const i=await W(e,524288),d=String(i.kind||"message").slice(0,64);delete i.kind;const w=String(i.since||"");if(delete i.since,r&&!Tr.has(d))return f(t,403,{error:$r});for(const m of[i,...Array.isArray(i.answers)?i.answers.filter(v=>v&&typeof v=="object"):[]])delete m.shared_by,r&&(m.shared_by=r.by);if(d==="card-hold"&&i.hold&&i.for!==void 0){const m=Et(new Date,i.for);if(!m)return f(t,400,{error:"That hold ends in the past or more than 30 days on. Pick another time."});delete i.for,m!==Tt&&(i.until=m.toISOString())}if(d==="card-build"){const m=String(i.key||""),v=await _e(o,m,I=>Ee(o,m.split("/")[1],I));return f(t,200,{id:await Ae(v),board:await G(e,w)})}if(d==="item-message"){const m=String(i.key||""),v=(await x()).items.find(R=>R.key===m&&R.kind==="work"),I=!String(i.text||"").trim()&&!(Array.isArray(i.files)&&i.files.length);if(v?.stalled&&await vi(o,m),I&&(v?.ready||v?.lane==="todo"))return await Vn(o,m,r?r.by.name||r.by.login:void 0),N++,f(t,200,{id:null,board:await G(e,w)});v?.lane==="todo"&&i.intent==="build"?await Qn(o,m,i):v?.lane==="todo"&&v.phase==="ready"&&await Yn(o,m,i),N++}if(d==="owner-answer")return f(t,200,{id:(await Wt([i]))[0]??null,board:await G(e,w)});if(d==="owner-answers"){const m=await Rr(i.answers);return m?f(t,400,{error:m}):f(t,200,{ids:await Wt(i.answers),board:await G(e,w)})}if(d==="card-merge"&&await Zn(o,String(i.key||"")).catch(()=>null)){const m=String(i.key);try{await ei(o,m)}catch(v){if(v instanceof $)return f(t,400,{error:v.message});throw v}return bs(m),f(t,200,{id:null,board:await G(e,w)})}if(d==="card-merge"){try{await Oi(o,String(i.key||""),{tapped:!0})}catch(m){if(m instanceof $)return f(t,400,{error:m.message});throw m}return f(t,200,{id:null,board:await G(e,w)})}if(d==="card-hold"||d==="card-done"){const m=String(i.key||"");let v,I;try{I=d==="card-hold"&&!i.hold?[]:await Ot(o,m),d==="card-done"&&await zn(o,m)&&(I=[]),v=await bi(o,d,i)}catch(J){return f(t,J instanceof $?400:500,{error:`Couldn\u2019t write ${m} to its item file: ${J.message}`})}if(!v)return f(t,400,{error:`${m} is already in Done, so its item file stays as it is.`});const R=await Ot(o,m);I=I.filter(J=>!R.some(O=>O.pid===J.pid)),I.length&&(_n(I),await B(o,m,"builder-stopped")),d==="card-hold"&&!i.hold&&await ra(m)}if(d==="reviewers"){const m=c(O=>O===void 0?[]:Array.isArray(O)?O:null,"list"),v=m(i.add),I=m(i.remove),R=c(O=>typeof O=="string"&&O.trim(),"text");if(!v||!I||!v.length&&!I.length)return f(t,400,{error:"Send the people to add and the names to remove, each as a list."});if(!I.every(R)||!v.every(O=>R(O?.name)&&R(O.slack_user)&&R(O.github)))return f(t,400,{error:"Each person to add needs a name, a Slack member and a GitHub login."});try{I.length&&await Di(o,I.map(O=>O.trim())),v.length&&await xi(o,await Ur(v))}catch(O){if(O instanceof $||O instanceof kr)return f(t,400,{error:O.message});throw O}const J=await ve([...I.length?[["reviewers-removed",{remove:I}]]:[],...v.length?[["reviewers-added",{add:v.map(O=>O.name)}]]:[]]);return f(t,200,{id:J.at(-1),board:await G(e,w)})}if(d==="reask-request"){const m=await _r(String(i.question||""));if(m)return f(t,400,{error:m})}if(d===Ie){const m=await Nr(String(i.question||""));if(m)return f(t,400,{error:m})}const k=d==="ask"?Ct():void 0;k&&await Hn(o,k,{id:k,ts:new Date().toISOString(),kind:d,data:i});const P=(await ve([[d,i,k]]))[0];if(d==="ask-edited"&&await Cn(o,String(i.id||"")),d==="ask-withdrawn"&&await Bn(o,String(i.id||"")),(d==="card-bump"||d==="card-unbump")&&Oa(o,String(i.key||""),d==="card-bump"?new Date().toISOString():null),(d==="grill-more"||d==="prototype-request")&&i.key){const m={...i.text?{text:String(i.text)}:{},...Array.isArray(i.files)&&i.files.length?{files:i.files}:{},...i.question?{question:String(i.question),note:i.note?String(i.note):void 0,choice:i.choice??void 0}:{}},v=d==="grill-more"?"grill":"prototype";await Xn(o,String(i.key),v),await te(String(i.key),v,m)}return d==="visual-request"&&i.key&&await na((await x()).items,await U(C)),d==="ask-withdrawn"&&await Pt(o,String(i.id||""),!0),f(t,200,{id:P,board:await G(e,w)})}if(n==="/api/rules"&&e.method==="GET")return f(t,200,{board:await Pi(o),hub:Vi()});if(n==="/api/rules"&&e.method==="POST"){const i=await W(e);let d;try{d=await Wi(o,i.rules,i.base??[])}catch(k){if(k instanceof $)return f(t,400,{error:k.message});throw k}const w=zi(d.changes)?await M("rules",{scope:"board",...d.changes}):null;return f(t,200,{rules:d.rules,id:w})}if(n==="/api/settings"&&e.method==="GET")return f(t,200,{settings:await Ti(o),paused_until:lr(o),pause_choices:Kt});if(n==="/api/build-targets"&&e.method==="GET"){const i=X(o);if(!i)return f(t,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const d=Be(i.route);return f(t,200,{targets:await wr(d.repo,pr(d))})}if(n==="/api/pause"&&e.method==="POST"){const i=await W(e);if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).join()!=="pause"||typeof i.pause!="string")return f(t,400,{error:"Send only {pause: a duration, a date and time, or off}."});let d;try{d=await Ji(o,i.pause)}catch(w){if(w instanceof $)return f(t,400,{error:w.message});throw w}return d&&(Ye=!0),ue(),f(t,200,{paused_until:d})}if(n==="/api/permissions"){const i=X(o);if(!i)return f(t,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const d=Be(i.route).repo,w=e.method==="POST"?await W(e):null;try{return w&&await gr(d,o,w.id,w.on),f(t,200,await mr(d,o))}catch(k){if(k instanceof $)return f(t,400,{error:k.message});throw k}}if(n==="/api/try/main/log"&&e.method==="GET")return f(t,200,{lines:await ct(o,"main")});if(n==="/api/try/prototypes"&&e.method==="GET"){const i=(await A(h.join(o,"data","try.json"),{}))?.prototypes||{};return f(t,200,{state:i.state||null,https:i.state==="ready"&&i.https||null,since:i.state==="ready"&&i.since||null,lines:i.state==="failed"?await ct(o,"prototypes"):[]})}if(n==="/api/log"&&e.method==="GET"){const i=a.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(i)||i.startsWith("."))return f(t,400,{error:"file takes a log file\u2019s name."});let d;try{d=await b.open(h.join(ae.homedir(),".claude","kickflip","logs",i),"r");const{size:w}=await d.stat(),k=Math.max(0,w-256*1024),P=Buffer.alloc(w-k),{bytesRead:m}=await d.read(P,0,P.length,k),v=P.subarray(0,m).toString("utf8");return f(t,200,{text:k?v.slice(v.indexOf(`
12
+ `)+1):v,truncated:k>0,size:w})}catch(w){if(w.code==="ENOENT")return f(t,404,{error:"No such log."});throw w}finally{await d?.close()}}if(n==="/api/transcript"&&e.method==="GET"){const i=a.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(i)||i.startsWith("."))return f(t,400,{error:"file takes a log file\u2019s name."});if(!/^\d+$/.test(a.searchParams.get("from")||""))return f(t,400,{error:"from takes a byte offset."});try{return f(t,200,await Ta(h.join(ae.homedir(),".claude","kickflip","logs",i),Number(a.searchParams.get("from"))))}catch(d){if(d.code==="ENOENT")return f(t,404,{error:"No such log."});throw d}}if(n==="/api/try/main/pull"&&e.method==="POST"){const i=await W(e);if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).length)return f(t,400,{error:"Send an empty object."});try{const d=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/pull`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:z.slice(1)||h.basename(o)}),signal:AbortSignal.timeout(3e4)});return f(t,d.status,await d.json())}catch(d){return f(t,502,{error:`Could not pull: ${d.message}`})}}if(n==="/api/try"&&e.method==="POST"){const i=await W(e);if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).some(d=>d!=="key")||typeof i.key!="string")return f(t,400,{error:"Send only the card key."});try{const d=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/build`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:z.slice(1)||h.basename(o),key:i.key}),signal:AbortSignal.timeout(1e4)});return f(t,d.status,await d.json())}catch(d){return f(t,502,{error:`Could not start the build: ${d.message}`})}}if(n==="/api/share"&&e.method==="POST")try{return f(t,200,await Ui(o,await W(e)))}catch(i){if(i instanceof $)return f(t,400,{error:i.message});throw i}if(n==="/api/settings"&&e.method==="POST"){const i=await W(e);let d;try{d=await Li(o,i)}catch(P){if(P instanceof $)return f(t,400,{error:P.message});throw P}const w=d.changed.filter(P=>P!=="idle_notification"),k=w.length?await M("settings",Object.fromEntries(w.map(P=>[P,d.settings[P]??null]))):null;return d.changed.includes("auto_merge")&&d.settings.auto_merge&&await M("check-github",{reason:"auto-merge"}),f(t,200,{settings:d.settings,id:k})}if(n==="/api/arch-review"&&e.method==="POST"){await W(e);try{return f(t,200,await Kr())}catch(i){if(i instanceof $)return f(t,i.status||400,{error:i.message});throw i}}if(n==="/api/hide"&&e.method==="POST"){const i=await W(e);try{await Pt(o,String(i.id||""),i.hidden!==!1)}catch(d){if(d instanceof $)return f(t,400,{error:d.message});throw d}return f(t,200,{ok:!0})}if(n==="/api/read"&&e.method==="POST"){const{marks:i}=await W(e);return i&&typeof i=="object"&&!Array.isArray(i)&&Object.values(i).every(w=>typeof w=="string"&&!Number.isNaN(Date.parse(w)))?(await At(o,(await x()).items.map(w=>w.key),i),f(t,200,{ok:!0})):f(t,400,{error:"Send {marks: {card key: ISO time}}."})}if(n==="/_claude/events"&&e.method==="GET")return f(t,200,await xr(a.searchParams.get("kind"),Math.min(500,Number(a.searchParams.get("limit")||100))));if(n==="/api/upload"&&e.method==="POST")return await Xr(e,t);if(e.method!=="GET"&&e.method!=="HEAD"){t.writeHead(405).end();return}if(n.startsWith("/uploads/"))return await qr(e,t,decodeURIComponent(n.slice(9)));if(n==="/plan.html"){t.writeHead(302,{location:`notes.html${a.search}`,"cache-control":"no-store"}).end();return}if(n==="/index.html"){t.writeHead(302,{location:`./${a.search}`,"cache-control":"no-store"}).end();return}const s=nr.has(n.slice(1)),l=s?null:await Yr(n),u=s?null:l?An:n==="/prototype.html"?null:await Qt(decodeURIComponent(n));if(!s&&!u){t.writeHead(404).end();return}const S=s?".html":h.extname(u).toLowerCase(),p=s?null:await b.stat(u);let y=s?Buffer.from(ir({touchIcon:await _t()})):l?Buffer.from(await Tn(o,l)):await b.readFile(u);!s&&S===".html"&&(y=(await vr(y.toString("utf8"))).replaceAll("{{title}}",Pn((await L()).title)).replaceAll("{{touchIcon}}",await _t())),!s&&S===".html"&&(y=Buffer.from(await Pr(y,n)));const E=S===".html"?`W/"${La.createHash("sha1").update(y).digest("base64url").slice(0,20)}"`:`W/"${Ne(p)}"`,_=S!==".html"&&a.searchParams.get("v")===Ne(p),g={"content-type":F(Dt,S)||"application/octet-stream","cache-control":_?"public, max-age=31536000, immutable":"no-cache",etag:E};if(Ft(e.headers["if-none-match"],E)){t.writeHead(304,{etag:E,"cache-control":g["cache-control"],vary:"accept-encoding"}).end();return}return await Bt(t,200,g,y)}catch(a){t.headersSent||(a.status===403?t.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}).end(a.message):a instanceof $?f(t,a.status||400,{error:a.message}):t.writeHead(a.status||(a.code==="ENOENT"?404:400)).end())}});let Ve=!1;async function ta(){if(!Ve){Ve=!0;try{const e=Date.now(),t=(await x()).items.filter(n=>n.heldUntil&&n.lane!=="done"&&Date.parse(n.heldUntil)<=e),a=t.length?await U(C):[];for(const{key:n,heldUntil:r}of t)await gi(o,n,r,{announce:!a.some(s=>s.kind==="card-unblocked"&&s.data?.key===n&&s.data?.until===r)}),await ra(n),T({call:"pick-up",key:n,result:"resumed",reason:"Its hold ended.",inputs:{heldUntil:r}})}catch(e){console.error("Pick-up check failed:",e.message)}finally{Ve=!1}}}c(ta,"checkPickUps");let le=null,Qe=!1,D=!1,Ye=!1;function ue(){return le?(Qe=!0,le):(le=(async()=>{do{Qe=!1;try{await ls()}catch(e){console.error("Auto check failed:",e.message)}}while(Qe)})().finally(()=>{le=null}),le)}c(ue,"checkAuto");async function ls(){const e=H(await L()),t=await U(C),{items:a,reviews:n}=await x(),r=await js(t),s=await ha(Ze),l=e.max_implementers,u={free:ht(a,{cap:l,pulled:s,hold:r}).free},S=u.free,p=K({dir:o});Ye&&p.state!=="paused"&&await M("usage-resumed",{window:"manual"}),Ye=p.manual==="board",D=V(p,"claude"),D||await gs(),D||await fs(dn(a)),D||await Ss(a,oa(a),u),D||await vs(a,u);const y=await ws(a,p);if(!D)for(const O of y.slice(0,u.free))u.free--,await ms(O);const{idle:E,pullable:_,waiting:g,pulls:i,helpers:d,bumpOuts:w,pastCap:k}=ht(a,{cap:l,pulled:s,hold:r,taken:S-u.free}),P=E&&!D;D||await na(a,t),D||await hs(d),e.auto_pull&&!D&&await ds(ln(a,{cap:e.max_planners}).map(O=>a.find(ge=>ge.key===O))),await Os(e,t,a,P);const m=_.filter(O=>r.includes(O));m.length&&T({call:"pull",result:"held",reason:"An overnight triage waits for the session's reply and names these cards, so the pull leaves them until it is answered or quiet hours end.",inputs:{cards:m}});const v=c(O=>V(O,"claude")?[]:O.near&&!O.near.going?i.slice(0,1):i,"pullNow"),I=c(async O=>{for(const ye of w.filter(Y=>Y.by===O)){const Y={call:"bump-out",key:ye.key,reason:`${O} is bumped and every implementer slot is full; this was the newest unbumped build.`,inputs:{by:O,cap:l}};try{T({...Y,result:"pausing",inputs:{...Y.inputs,lines:await yr(o,ye.key,O)}})}catch(Te){if(!(Te instanceof $))throw Te;T({...Y,result:"refused",reason:`${Y.reason} The bump-out refused: ${Te.message}`})}finally{N++}}const ge=await _e(o,O,ye=>Ee(o,O.split("/")[1],ye),"hub"),ya=k.includes(O)?"It is bumped, so it starts past the cap.":"An implementer slot was free and it was the top ready card in Todo.";return T({call:"pull",key:O,result:us(ge),reason:ya,inputs:{cap:l,pullable:_,waiting:g}}),Ae(ge)},"startPull");if(await Ps(Ze,e.auto_pull,g,D?[]:v(K({dir:o})),I))return;const{due:R,repo:J}=n;!P||!R||(T({call:"pr-review",result:"asked",reason:`The board is idle and PR #${R.number} touches its work.`,inputs:{repo:J,pr:R.number,head:R.head,touches:R.touches,asked:!!R.asked}}),await M("pr-review",{pr:R.number,head:R.head,repo:J,title:R.title,author:R.author,url:R.url,touches:R.touches,asked:!!R.asked}))}c(ls,"runAuto");function Q(e="claude"){const t=V(K({dir:o}),e);return t&&(D=!0),t}c(Q,"launchHeldNow");const us=c(e=>e?e.bumped?"bumped":"started":"refused","startResult");async function Ae(e){if(N++,!e||e.bumped)return null;const t={kind:e.kind,model:e.model,relaunched:!1};try{const a=K({dir:o});if(V(a,e.kind))T({call:"launch-held",key:e.key,result:"held",reason:De(a,e.kind),inputs:{kind:e.kind,model:e.model??null,gate:a}}),a.machine.state==="busy"&&await B(o,e.key,"host-busy"),await Pe(e.key,{...t,pid:null});else{const{pid:n,log:r}=await ne(o,e);await Pe(e.key,{...t,pid:n,log:r})}}catch(a){await si(o,e.key,`the launch failed: ${a.message}`)}return null}c(Ae,"handOff");async function ds(e){for(const{key:t,phase:a}of e){if(Q())return;const n=a==="prototyping"?"prototype":"grill";T({call:"arrival-planner",key:t,result:n,reason:a==="prototyping"?"It landed in Todo filed prototyping.":"It landed in Todo, not filed prototyping.",inputs:{phase:a}}),await te(t,n),N++}}c(ds,"launchArrivals");async function fs(e){for(const{key:t,kind:a}of e){if(Q())return;T({call:"round-planner",key:t,result:a,reason:"It was sent back from staging or ready for another round.",inputs:{kind:a}}),await te(t,a),N++}}c(fs,"launchRounds");async function hs(e){for(const{key:t,name:a}of e){if(Q())return;N++;try{const n=await Gn(o,t,a,r=>va(t.split("/")[1],a,r));if(!n)continue;T({call:"helper",key:t,result:"launching",reason:"A helper was asked for and pullOf let it launch.",inputs:{name:a,model:n.model,worktree:n.worktree}});try{await ne(o,n)}catch(r){await ke(o,t,["helper-failed",{name:a,error:r.message}],a)}}catch(n){if(!(n instanceof $))throw n;await ke(o,t,["helper-failed",{name:a,error:n.message}],a)}}}c(hs,"launchHelpers");let aa=Promise.resolve();function na(e,t){const a=aa.then(()=>ps(e,t));return aa=a.catch(()=>{}),a}c(na,"launchVisuals");async function ps(e,t){for(const{key:a,request:n}of Va(e,t)){if(Q())return;try{const r=await Kn(o,a,n);if(!r)continue;const{text:s,files:l}=n.data,u={...s?{text:String(s)}:{},...Array.isArray(l)&&l.length?{files:l}:{}};try{await ne(o,{...r,...u})}catch(S){await ke(o,a,["planner-failed",{kind:"visual agent",error:S.message}],r.agent)}}catch(r){if(!(r instanceof $))throw r;await B(o,a,"planner-failed",{kind:"visual agent",error:r.message})}}}c(ps,"drawVisuals");const ee=h.join(j,"queued-planners.json");async function ia(e){await b.mkdir(j,{recursive:!0}),await b.writeFile(ee+".tmp",JSON.stringify(e)+`
13
+ `),await b.rename(ee+".tmp",ee)}c(ia,"saveQueuedPlanners");async function te(e,t,a={}){const n=K({dir:o});if(V(n,"claude")){D=!0,await ia({...await A(ee,{}),[e]:{kind:t,ask:a}}),T({call:"planner-queued",key:e,result:t,reason:De(n,"claude"),inputs:{kind:t,gate:n}}),await B(o,e,n.machine.state==="busy"?"host-busy":"planner-held",{kind:t});return}try{const r=await wi(o,e,t);if(!r)return;try{await ne(o,{...r,...a})}catch(s){await ke(o,e,["planner-failed",{kind:t,error:s.message}])}}catch(r){if(!(r instanceof $))throw r;await B(o,e,"planner-failed",{kind:t,error:r.message})}}c(te,"launchPlanner");async function ra(e){const t=await Si(o,e);t&&(T({call:"resume",key:e,result:t.kind,reason:"Its hold stopped this run.",inputs:t}),t.kind==="grill"||t.kind==="prototype"?await te(e,t.kind):t.label==="Builder"&&await Ae(await _e(o,e,a=>Ee(o,e.split("/")[1],a),"hub")))}c(ra,"resumeRun");let sa=!1;async function ws(e,t){const a=Ja(e,{usageHeld:c(n=>$t(t,n),"usageHeld")});for(const n of a.ask)await vt(o,n,{cause:"usage",words:cr(t)})&&T({call:"pause",key:n,result:"asked",reason:De(t,"claude"),inputs:{gate:t}});for(const n of a.drop)await Wn(o,n),T({call:"pause",key:n,result:"dropped",reason:"Its cause cleared before its builder reached a stopping point.",inputs:{}});for(const n of a.stop){await St(o,n);const r=$n((await Ln(o,n)).filter(l=>!Ga(l)))[0],s=r?await xn([r.pid]):[];T({call:"pause",key:n,result:"hard stop",reason:"Its builder reached no stopping point within 15 minutes of the pause.",inputs:{stopped:s.map(l=>l.pid)}})}return(a.ask.length||a.drop.length||a.stop.length)&&N++,sa=a.ask.length>0||e.some(n=>n.builder?.pause),a.resume}c(ws,"checkPauses");async function ms(e){const t=await Un(o,e);if(!t)return;const{pause:a,record:n}=t,r=Ma[a.cause][1];N++;const s={call:"resume",key:e,reason:`Nothing pauses it any more: ${r}.`,inputs:{pause:a}};if(!n.session?.pid)return T({...s,result:"woke the orchestrator"}),M("builder-resume",{key:e,cleared:r});const l=Ka(n.kind)??"claude";try{const{pid:u,log:S}=await Rn(o,{key:e,kind:l,model:n.model,session:{id:n.session.id},brief:c(p=>In({...p,cleared:r}),"brief")});await Pe(e,{kind:l,model:n.model,pid:u,log:S,relaunched:!1}),T({...s,result:"resumed"})}catch(u){T({...s,result:"failed",reason:`${s.reason} The launch failed: ${u.message}`}),await B(o,e,"resume-failed",{error:u.message})}}c(ms,"resumeBuild");async function gs(){for(const[e,{kind:t,ask:a}]of Object.entries(await A(ee,{}))){if(Q())return;const{[e]:n,...r}=await A(ee,{});await ia(r),T({call:"planner-queued",key:e,result:"launching",reason:"The hold it waited on has lifted.",inputs:{kind:t}}),await te(e,t,a),N++}}c(gs,"launchQueuedPlanners");async function Pe(e,t){const a=h.join(j,"headless-builders.json"),n=await A(a,{});t?n[e]=t:delete n[e],await b.mkdir(j,{recursive:!0}),await b.writeFile(a+".tmp",JSON.stringify(n)+`
14
+ `),await b.rename(a+".tmp",a)}c(Pe,"trackLaunch");async function ys(){try{const e=await A(h.join(j,"headless-builders.json"),{});for(const t of(await oi(o)).filter(a=>!e[a.key]))T({call:"launch-cut-off",key:t.key,result:"launching",reason:"A restart cut off the launch after its build started.",inputs:{kind:t.kind,model:t.model??null}}),await Ae(t)}catch(e){console.error("Cut-off launch check failed:",e.message)}}c(ys,"launchCutOffBuilds");const oa=c(e=>new Set(e.filter(t=>t.builder?.stopped).map(t=>t.key)),"stoppedKeys");let Xe=!1;async function ks(){if(!Xe){Xe=!0;try{const e=h.join(j,"headless-builders.json"),t=await A(e,{});if(!Object.keys(t).length)return;const{items:a}=$e(await Mt(await Ce())),n=new Set(a.filter(p=>p.lane!=="done"&&p.builder&&!p.builder.planner).map(p=>p.key)),r=c(async p=>{T({call:"relaunch",key:p.key,result:"handed back",reason:"Its builder exited twice with its record open.",inputs:t[p.key]}),await B(o,p.key,"exited-twice")},"handBack"),s=c(async p=>{const y=t[p.key],E={call:"relaunch",key:p.key,inputs:y,reason:y.pid===null?"Its launch was held by a pause, a busy host or a start near the line, which has lifted.":"Its builder exited with its record open, for the first time."};try{const{pid:_,log:g}=await ne(o,p);return T({...E,result:y.pid===null?"launched":"relaunched"}),{pid:_,log:g}}catch(_){return T({...E,result:"failed",reason:`${E.reason} The launch failed: ${_.message}`}),await B(o,p.key,"relaunch-failed",{error:_.message}),{pid:null}}},"launch"),l=c(async(p,y)=>{T({call:"relaunch",key:p.key,result:"paused",reason:`Its run stopped at the usage limit, which resets at ${y}.`,inputs:t[p.key]}),await vt(o,p.key,{cause:"usage",words:"usage limit",until:y}),await St(o,p.key),N++},"atLimit"),u=new Set(a.filter(p=>p.builder?.pause?.paused).map(p=>p.key)),S=await Nn(t,n,{launch:s,handBack:r,held:c(p=>V(K({dir:o}),p),"held"),stopped:oa(a),paused:u,limitedUntil:c(p=>p.log?ur(p.log):null,"limitedUntil"),atLimit:l});JSON.stringify(S)!==JSON.stringify(t)&&(await b.writeFile(e+".tmp",JSON.stringify(S)+`
15
+ `),await b.rename(e+".tmp",e))}catch(e){console.error("Headless builder check failed:",e.message)}finally{Xe=!1}}}c(ks,"reviveHeadless");function bs(e){const t=h.join(ae.homedir(),".claude","kickflip","logs");vn(t,{recursive:!0});const a=On(h.join(t,`merge-${e.split("/")[1]}-${new Date().toISOString().replace(/[:.]/g,"-")}.log`),"a"),n=process.env.KICKFLIP_MERGE_CMD?JSON.parse(process.env.KICKFLIP_MERGE_CMD):[tr(),En],r=Fa(n[0],[...n.slice(1),"--site",Z,"merge",e],{detached:!0,stdio:["ignore",a,a]});bn(a),r.on("error",s=>B(o,e,"land-failed",{why:`the land couldn't start (${s.message})`}).catch(()=>{})),r.unref()}c(bs,"runHeldMerge");async function Ss(e,t,a){const n=e.some(g=>g.waitsOn==="checking"),r=Object.fromEntries(e.map(g=>[g.key,g.waitsOn==="checking"?"checking":g.lane!=="done"&&g.agents?.some(i=>i.started&&!i.done)?"building":null])),s=h.join(j,"landers.json"),l=Object.fromEntries(Object.entries(await A(s,{})).map(([g,i])=>[g,typeof i=="string"?{verified:i,pid:null}:i])),u=new Set,S=new Set,p={};for(const[g,i]of Object.entries(l))if(!(!i.pid||bt(i.pid)||t.has(g))){if(r[g]==="checking"){i.resumed&&(p[g]={verified:null,pid:null,fresh:!0},T({call:"lander",key:g,result:"fresh next",reason:"Its resumed builder exited without taking the land up.",inputs:i}));continue}if(r[g]!=="building"){S.add(g);continue}await li(o,g),u.add(g),T({call:"lander",key:g,result:"handed back again",reason:"Its lander exited with its builder still open.",inputs:i})}const y={...Object.fromEntries(Object.entries(l).filter(([g])=>!u.has(g)&&!S.has(g))),...p},E=c(async()=>{await b.writeFile(s+".tmp",JSON.stringify(y)+`
16
+ `),await b.rename(s+".tmp",s)},"save");if(S.size&&await E(),!n&&!u.size)return;const _=ca(e,(await ci(o)).filter(g=>y[g.key]?.verified!==g.verified));if(_.length){for(const g of _){if(Q(g.kind??"claude"))break;if(!la(e,a,g.key))continue;const i={call:"lander",key:g.key,reason:"Its build was verified and waits to land.",inputs:{verified:g.verified,before:y[g.key]??null}};try{const{pid:d,resumed:w}=await kt(o,g,{fresh:y[g.key]?.fresh});y[g.key]={verified:g.verified,pid:d,...w?{resumed:w}:{}},T({...i,result:w?"resumed its builder":"launched"}),await Pe(g.key,null)}catch(d){T({...i,result:"failed",reason:`${i.reason} The launch failed: ${d.message}`}),y[g.key]={verified:g.verified,pid:null},await B(o,g.key,"lander-failed",{error:d.message})}}await E()}}c(Ss,"launchLanders");const de=c((e,t)=>e.find(a=>a.key===t)?.bumpedAt||"","bumpOf"),ca=c((e,t)=>[...t.filter(a=>de(e,a.key)).sort((a,n)=>de(e,a.key).localeCompare(de(e,n.key))),...t.filter(a=>!de(e,a.key))],"byBump");function la(e,t,a){return!de(e,a)&&t.free<=0?!1:(t.free--,!0)}c(la,"takeSlot");async function vs(e,t){const a=h.join(j,"rebases.json"),n=await A(a,{}),{base:r}=Sa(o),s=ca(e,un(e,Object.fromEntries(Object.entries(n).map(([l,u])=>[l,u.conflict])),r));if(s.length)try{for(const l of s){if(Q())break;if(!la(e,t,l.key))continue;const u=l.key.split("/")[1],S={call:"rebase-run",key:l.key,reason:`Its PR #${l.pr} conflicts with ${r}.`,inputs:{pr:l.pr,conflict:l.conflict,before:n[l.key]??null}};n[l.key]={conflict:l.conflict,pid:null};let p;try{p=await yi(o,l.key,{pr:l.pr,conflict:l.conflict,base:r},()=>ja(o,u,l.branch))}catch(y){if(!(y instanceof $))throw y;T({...S,result:"refused",reason:`${S.reason} The start refused: ${y.message}`}),await B(o,l.key,"rebase-failed",{pr:l.pr,error:y.message});continue}finally{N++}try{n[l.key].pid=(await kt(o,p)).pid,T({...S,result:"launched"})}catch(y){T({...S,result:"failed",reason:`${S.reason} The launch failed: ${y.message}`}),await B(o,l.key,"rebase-launch-failed",{error:y.message})}}}finally{await b.writeFile(a+".tmp",JSON.stringify(n)+`
17
+ `),await b.rename(a+".tmp",a)}}c(vs,"launchRebases");async function ua(){const e=gn(new Date,pt(await A(h.join(ae.homedir(),".claude","kickflip","settings.json"),{})));return e&&wn(e)}c(ua,"triageNight");async function Os(e,t,a,n){const r=h.join(j,"triaged.json"),s=await A(r,{}),{waiting:l,seen:u}=pn(a),S=c(async()=>{await b.mkdir(j,{recursive:!0}),await b.writeFile(r+".tmp",JSON.stringify({seen:u})+`
18
+ `),await b.rename(r+".tmp",r)},"save"),p=t.filter(g=>g.kind==="overnight-triage").at(-1);if(p&&!(await se(p)).reply){s.seen!==u&&await S();return}if(!e.auto_triage||!n||l.length<2||u===s.seen)return;const y=await ua();if(!y||p?.data?.date===y)return;const E=`overnight-triage-${y}`;((await A(h.join(j,"owner.json"),{topics:[]})).topics||[]).some(g=>g.id===E)||await ti(o,{id:E,title:`Overnight triage ${y}`}),T({call:"triage",result:"sent",reason:`Auto triage is on, ${l.length} cards wait on answers, and tonight's triage hadn't gone out.`,inputs:{date:y,cards:l}}),await M("overnight-triage",{date:y,topic:E,cards:l}),await S()}c(Os,"checkTriage");async function da(e){const t=e.filter(a=>a.kind==="overnight-triage").at(-1);return t&&!(await se(t)).reply?t:null}c(da,"unansweredTriage");async function js(e){const t=await da(e),a=pt(await A(h.join(ae.homedir(),".claude","kickflip","settings.json"),{}));return t&&mn(new Date,a)?t.data.cards||[]:[]}c(js,"triageHold");async function As(){if(!H(await L()).auto_triage)return!1;const e=await ua();return!!e&&(await U(C)).filter(t=>t.kind==="overnight-triage").at(-1)?.data?.date!==e}c(As,"triageDue");const Ze={file:"pulled.json",line:"pulled"},fa=c(e=>e.keys||(e.key?[e.key]:[]),"markedOf"),ha=c(async({file:e})=>fa(await A(h.join(j,e),{})),"sentOf");async function Ps({file:e,line:t},a,n,r,s){const l=h.join(j,e),u=a?[...n,...r]:[];for(const S of a?r:[])await B(o,S,t),await s(S);return JSON.stringify(fa(await A(l,{})))!==JSON.stringify(u)&&(await b.mkdir(j,{recursive:!0}),await b.writeFile(l+".tmp",JSON.stringify({keys:u,at:new Date().toISOString()})+`
19
+ `),await b.rename(l+".tmp",l)),u.length>0}c(Ps,"sendPulls");async function qe(e,{key:t,reason:a,inputs:n}){const r={call:"push",key:t,reason:a,inputs:{tag:e.tag,title:e.title,...n}};try{await ki(o,e),T({...r,result:"sent"})}catch(s){if(!(s instanceof $))throw s;const l=s.message.split(`
20
+ `).at(-2)||s.message;T({...r,result:"not sent",reason:`${a} Not sent: ${l}`}),console.error(`Push ${e.tag} not sent:`,l)}}c(qe,"sendOwnPush");let fe=null,et=!1;function tt(){return fe?(et=!0,fe):(fe=(async()=>{do{et=!1;try{await Ts()}catch(e){console.error("Action push check failed:",e.message)}}while(et)})().finally(()=>{fe=null}),fe)}c(tt,"checkActions");async function Ts(){const e=h.join(j,"action-pushes.json"),t=await A(e,null),a=on(await x()),n=Object.fromEntries(a.map(r=>[r.tag,{kind:r.kind,ids:r.questions.map(s=>s.id)}]));if(t){for(const s of a){const l=F(t,s.tag),u=s.questions.filter(y=>!l?.ids?.includes(y.id));if(l?.kind===s.kind&&!u.length)continue;const S=u.length?`questions.html#${encodeURIComponent(u[0].id)}`:s.url,p=l?l.kind!==s.kind?`What waits on the owner changed from ${l.kind} to ${s.kind}.`:"A new question waits on the owner.":"Something new waits on the owner.";await qe({kind:"action",title:s.title,body:u[0]?.question||s.body,url:`/${Z}/${S}`,tag:s.tag,now:s.now},{key:s.tag.startsWith("item-")?s.tag.slice(5):void 0,reason:p,inputs:{was:l??null,kind:s.kind,fresh:u.map(y=>y.id)}})}const r=Object.keys(t).filter(s=>!F(n,s));r.length&&await kn(r,Z)}JSON.stringify(n)!==JSON.stringify(t)&&(await b.mkdir(j,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify(n)+`
21
+ `),await b.rename(e+".tmp",e))}c(Ts,"pushActions");let he=null,at=!1;function nt(){return he?(at=!0,he):(he=(async()=>{do{at=!1;try{await Es()}catch(e){console.error("Orchestrator wait check failed:",e.message)}}while(at)})().finally(()=>{he=null}),he)}c(nt,"checkWaits");async function Es(){const e=h.join(j,"orchestrator-waits.json"),t=await A(e,null),a=t?.waits||[],n=await da(await U(C)),r=[...Xa(await x()),...n?[Za(n,F(await Se(o),n.id))]:[]],s=qa(a,r,new Date),l=[];for(const u of s.waits)u.kind!=="triage"&&!u.picked&&!u.pushed&&Date.now()-Date.parse(u.since)>=Ya?(await qe({kind:"reminder",title:`${u.ref} waits on the orchestrator`,body:`${u.title}: ${u.next}`,url:`/${Z}/#selectedCard=${encodeURIComponent(u.key)}`,tag:`wait-${u.key}`,now:!0},{key:u.key,reason:`It has waited on the orchestrator since ${u.since} and nobody picked it up.`,inputs:{kind:u.kind,since:u.since}}),l.push({...u,pushed:!0})):l.push(u);(!t||JSON.stringify(l)!==JSON.stringify(a))&&(await b.mkdir(j,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify({waits:l})+`
22
+ `),await b.rename(e+".tmp",e)),(s.added.length||s.again.length)&&await M("orchestrator-waits",{added:s.added,again:s.again})}c(Es,"stepWaits");let pe=null,it=!1;function rt(){return pe?(it=!0,pe):(pe=(async()=>{do{it=!1;try{await $s()}catch(e){console.error("Idle check failed:",e.message)}}while(it)})().finally(()=>{pe=null}),pe)}c(rt,"checkIdle");async function $s(){const e=h.join(j,"idle.json"),t=await L();if(!H(t).idle_notification)return b.rm(e,{force:!0});const a=await A(e,{}),n=await x(),r=fn(n,{pulled:await ha(Ze)}),{state:s,push:l}=an(a,{idle:r,now:new Date});l&&await qe({kind:"reminder",title:`${t.title||Z} has gone idle`,body:nn(n.items,a.last),url:`/${Z}/`,tag:"idle",now:!0},{reason:"The board has been strictly idle long enough, with idle notifications on.",inputs:{previous:a}}),JSON.stringify(s)!==JSON.stringify(a)&&(await b.mkdir(j,{recursive:!0}),await b.writeFile(e+".tmp",JSON.stringify(s)+`
23
+ `),await b.rename(e+".tmp",e))}c($s,"stepIdle");let we=null,st=!1;function pa(){return we?(st=!0,we):(we=(async()=>{do{st=!1;try{const e=za(await x());for(const t of e)await hi(o,t.key,t.to,t.why,t.phase,t.from);e.length&&N++}catch(e){console.error("Lane settle failed:",e.message)}}while(st)})().finally(()=>{we=null}),we)}c(pa,"settleLanes");let me=null,ot=!1;function wa(){return me?(ot=!0,me):(me=(async()=>{do{ot=!1;try{await Is()}catch(e){console.error("Done archive failed:",e.message)}}while(ot)})().finally(()=>{me=null}),me)}c(wa,"checkArchive");async function ma({starting:e=!1}={}){try{if(!e&&!(await x()).unfiled.length)return;for(const t of await Fn(o))console.log(`filed ask card ${t}`)}catch(t){console.error("Ask filing failed:",t.message)}}c(ma,"fileNewAsks");async function ga(){try{await At(o,(await x()).items.map(e=>e.key))}catch(e){console.error("Read marks failed:",e.message)}}c(ga,"markNewCards");async function Is(){if(!ut(await x()).length)return;const e=ut(await ce()),t=e.length?await ai(o,e.map(a=>({key:a.key,closed_at:a.sortAt,prs:a.prs}))):[];t.length&&console.log(`Done archive: ${t.length} card${t.length===1?"":"s"} moved`)}c(Is,"archiveDone");try{for(const e of await ui(o))console.log(e)}catch(e){console.error("follow-up migration:",e.message)}try{for(const e of await fi(o))console.log(e)}catch(e){console.error("status migration:",e.message)}try{for(const e of await di(o))console.log(e)}catch(e){console.error("phase migration:",e.message)}try{for(const e of await pi(o))console.log(e)}catch(e){console.error("staging migration:",e.message)}try{for(const e of await Fi(o))console.log(e)}catch(e){console.error("notes split:",e.message)}try{await mi(o)}catch(e){console.error("dead-field strip:",e.message)}await ys(),await ma({starting:!0}),await pa(),await wa(),await ga(),await os(),setInterval(()=>{N++,Gt(),ks(),Mn(o).then(t=>{t.length&&N++},t=>console.error("Screen publish failed:",t.message)),ta();const e=K({dir:o});D&&!V(e,"claude")||sa||!D&&$t(e,"claude")?ue():As().then(t=>{t&&ue()},t=>console.error("Triage check failed:",t.message)),tt(),nt(),rt()},6e4).unref(),await Gt(),await ta(),await ue(),tt(),nt(),rt(),cs.listen(xt,"127.0.0.1",()=>console.log(`site on http://127.0.0.1:${xt}/ (prefix ${z||"none"})`));