@lovek4d/kickflip 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTEXT.md +7 -7
- package/SKILL.md +4 -4
- package/bin/event-lines.mjs +1 -1
- package/bin/install.mjs +3 -3
- package/bin/wait-events.mjs +2 -2
- package/board/bin/board.mjs +1 -1
- package/board/domain.mjs +7 -7
- package/board/headless.mjs +8 -8
- package/board/hub-lines.mjs +1 -1
- package/board/public/app-needs.js +1 -1
- package/board/public/app.html +2 -0
- package/board/public/app.js +1 -0
- package/board/public/col-resize.js +73 -0
- package/board/public/hub.css +1 -0
- package/board/public/prototype.html +1 -0
- package/board/public/sidebar.css +8 -2
- package/board/public/style.css +32 -10
- package/board/public/ui-cards.js +3 -3
- package/board/public/ui-sidebar.js +11 -3
- package/board/public/ui-threads.js +2 -1
- package/board/public/ui.js +1 -1
- package/board/public/view-hub.js +25 -2
- package/board/render-check.mjs +8 -8
- package/board/server.mjs +18 -18
- package/board/store.mjs +29 -29
- package/board/tickets.mjs +3 -3
- package/hooks-plugin/hooks/board-messages.mjs +53 -0
- package/hooks-plugin/hooks/hooks.json +10 -0
- package/hub/boards.mjs +4 -4
- package/hub/home.mjs +1 -1
- package/hub/hub.mjs +11 -11
- package/hub/pr-report.mjs +2 -2
- package/hub/schedule.mjs +1 -1
- package/hub/try.mjs +10 -10
- package/hub/upgrade.mjs +1 -1
- package/package.json +1 -1
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(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,
|
|
3
|
-
`).filter(Boolean).map(t=>JSON.parse(t))}catch{return[]}},"readLines"),He=c(e=>
|
|
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
|
|
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,confirmedDrops as Ga,plans as Ka,providerOf as za,archivedOf as ut,buildBoard as $e,movesOf as Va,visualsDue as Qa,withHub as Ya,DONE_KEPT as dt,ORCHESTRATOR_PUSH_AFTER as Xa,orchestratorWaits as Za,triageWaitOf as qa,waitsStep as en,PROTOTYPE_WAIT as tn,prototypeWaits as an,idleStep as nn,idleSummary as rn,ITEM_EVENTS as sn,QUESTION_REOPEN as Ie,slimDone as ft,needsAnswer as on,ownerActions as cn,picksNoTake as ln,pullOf as ht,arrivalsOf as un,rebasesDue as dn,refOf as Re,roundsDue as fn,strictlyIdle as hn,threadOf as pn,triageOf as wn}from"./domain.mjs";import{localDay as mn,quietAt as gn,quietMiddleOf as yn,quietOf as pt}from"../hub/schedule.mjs";import{cardPRs as kn,prKeyOf as wt}from"../shared/card-prs.mjs";import{dropQueued as bn}from"../hub/push.mjs";import{closeSync as Sn,createReadStream as vn,existsSync as mt,mkdirSync as On,openSync as jn,readdirSync as An,statSync as gt,watch as yt}from"node:fs";import{PROTOTYPE_TEMPLATE as Pn,esc as Tn,prototypePage as En,versionOf as Ne}from"./prototype-page.mjs";import{BOARD_CLI as $n,landByBuilder as kt,launchBuilder as ne,launchedAgents as In,pidAlive as bt,resumeBrief as Rn,resumeBuilder as Nn,reviveBuilders as _n,stopAgents as xn,stopRuns as Dn}from"./headless.mjs";import{replyFilesOf as Bn,dropAskCard as Hn,fileAsk as Fn,fileAsks as Cn,retitleAskCard as Wn,dropPause as Ln,markPaused as St,openRuns as Un,requestPause as vt,resumePaused as Mn,BoardError as $,publishUnscreened as Jn,readScreening as Gn,startHelper as Kn,startVisual as zn,openAgents as Ot,isLanding as Vn,implementAsIs as Qn,stageImplement as Yn,stageReply as Xn,planAgain as Zn,heldMerge as qn,opensPRs as ei,tapMerge as ti,addTopic as ai,appendStatus as B,archiveCards as ni,archiveCount as ii,archiveRank as ri,readArchive as jt,boardConfig as si,abandonLaunch as oi,cutOffBuilds as ci,abandonPlanner as ke,landable as li,relaunchLander as ui,migrateFollowups as di,migratePhases as fi,migrateStatuses as hi,moveCard as pi,stageImplementAsks as wi,startPlanner as mi,stripDeadFields as gi,pickUp as yi,startRebase as ki,startTappedBuild as _e,sendPush as bi,recordOwnerTap as Si,takeHoldStopped as vi,clearStall as Oi,mergeCard as ji,readPrototypes as Ai,finishHandshake as Pi,itemSources as be,readAskLinks as xe,readBoardRules as Ti,readBoardSettings as Ei,readHandshake as $i,readHidden as Ii,readWaveNotes as Ri,readMarks as Ni,readNotes as _i,readWorking as Se,readUpload as xi,addReviewers as Di,removeReviewers as Bi,markRead as At,resolveNoteAnswers as Hi,settleAnswers as Fi,splitSharedNotes as Ci,unrecorded as Wi,saveBoardRules as Li,saveBoardSettings as Ui,saveBoardShare as Mi,saveUpload as Ji,setBoardPause as Gi,setHidden as Pt,settingsOf as H,threadFile as Ki,wavesDir as zi}from"./store.mjs";import{changed as Vi,readHubRules as Qi}from"../hub/rules.mjs";import{GITHUB_STATE as Yi,boardAt as X,listenState as Xi,linksOf as Zi}from"../hub/boards.mjs";import{limitedUntil as qi}from"../shared/gh.mjs";import{pendingFor as er,shown as tr}from"../hub/prompts.mjs";import{nodeBin as ar}from"../bin/install.mjs";import{routeRecord as F}from"../hub/routes.mjs";import{CHOICES as nr,FOREVER as Tt,durationEnd as Et}from"../hub/duration.mjs";import{VIEW_PAGES as ir,shellHtml as rr}from"../hub/shell.mjs";import{crossSite as sr,isJSON as or}from"../starter/cross-site.mjs";import{shareEntries as cr}from"../hub/share.mjs";import{readUsageGate as K,launchHeld as V,pauseReasonFor as De,pausedWords as lr,pausesRunning as $t,boardPausedUntil as ur}from"../shared/usage-gate.mjs";import{runLimit as dr}from"../hub/usage.mjs";import{audit as fr,boardAudit as hr}from"../shared/audit.mjs";import{reviewWaits as pr}from"../shared/skills.mjs";import{boardRepo as Be,mainRefOf as wr}from"../hub/board-repo.mjs";import{listBuildTargets as mr}from"./build-script.mjs";import{permissionsView as gr,setPermission as yr}from"./permissions.mjs";import{bumpOut as kr}from"./stop.mjs";import{SlackError as br,SLACK_NEEDS_TOKEN as It,savedSlack as Rt,slackStatus as Nt}from"../hub/slack.mjs";import{collaborators as Sr,githubUser as vr}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 Or(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(Or,"pageHeaders");const o=h.resolve(process.argv[2]||process.cwd()),jr=[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=>fr(hr(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"},Ar=Ua(Wa.gzip),Pr=/^(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(Pr.test(r["content-type"]||"")&&(r.vary="accept-encoding",s.length>=1024&&/\bgzip\b/.test(e.req?.headers["accept-encoding"]||"")))try{s=await Ar(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 Tr(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(Tr,"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=>Ki(o,e),"threadFile"),Fe=c(async()=>{const e=await Ii(o);return(await U(C)).filter(t=>!e.has(t.id))},"shownEvents");async function W(e,t=64*1024){if(!or(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 Er=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"]),$r="This board is shared with you to look at only.",Ir="Merge and Board settings stay the owner\u2019s.";async function Rr(e,t){const a=e.headers["x-kickflip-shared"];return a?cr(await L()).find(r=>r.login===a)?.level!=="full"||!["/_claude/events","/api/upload"].includes(t)||e.method!=="POST"?{refused:$r}:{by:{login:a,name:e.headers["tailscale-user-name"]||null}}:null}c(Rr,"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 Wi(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))&&ln(a.get(String(l.id)),l.choice),"again"),r=await ve(t.map(l=>["owner-answer",n(l)?{...l,prototype_again:!0}:l]));await Hi(o,t);const{prototype:s}=await Fi(o,t);N++;for(const l of s)await te(l,"prototype");return r}c(Wt,"recordAnswers");async function Nr(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(Nr,"answersRefused");async function _r(e){const t=(await _()).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(_r,"reopenRefused");async function xr(e){const t=(await _()).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(xr,"reaskRefused");async function se(e){const a=(await Promise.all((await Bn(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 Dr(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(Dr,"listEvents");const Br=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(()=>si(o),"config");async function Hr(){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(Hr,"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 Fr(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(Fr,"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:ei({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 Fr(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(Yi,{})).prs||{},p=qi({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 kn(v,m))l[wt(I)]=u.has(`${k}/${v.id}`)?null:Br(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 x=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&&(x[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:x,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 Hr(),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 Gn(o)}}c(Jt,"snapshot");async function Cr(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(Cr,"archivedCards");const Z=z.slice(1)||h.basename(o);let N=0,oe=null;async function _(){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(_,"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 an(await _(),e))t.has(a.request)||Date.now()-Date.parse(a.since)<tn||await M("prototype-overdue",a)}catch(e){console.error("Prototype check failed:",e.message)}finally{We=!1}}}c(Gt,"checkPrototypeWaits");async function Wr(e){const t=await $i(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 Pi(o),null):n}c(Wr,"setupOf");const Kt=nr.map(([e,t])=>({value:e,label:t,ms:e===Tt?null:Et(new Date(0),e)-new Date(0)})),Lr=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`)),Lr).unref())]),"withinWait");async function Ur(){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(Sr(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(Ur,"reviewerPeople");async function Mr(e){const t=Rt();if(!t)throw new $(It);return Promise.all(e.map(async a=>{const n=await vr(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(Mr,"withDMs");async function ce(){const e=new Date().toISOString(),{title:t,reviewers:a}=await L(),n=X(o),r=n?Xi(n):"deaf",s=Ya(await _(),{listen:r,prompts:n?er(n.route).map(tr):[]});s.items=s.items.map(u=>u.worktree&&!mt(u.worktree)?{...u,worktree:null}:u);const l=n?Zi(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 Wr(l),holdChoices:Kt,arch:n?_a(await xa(o),s.items,s.owner,{repo:Da(Be(n.route)),waits:pr()}):null}}c(ce,"board");async function Jr(e){["GET","HEAD"].includes(e.method)||N++;const t={...await ce(),doneArchived:await ii(o)};t.owner=t.owner.filter(r=>r.state!=="resolved");const a=await Ni(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(Jr,"buildPageBoard");const Le=h.join(j,"sync.json");let q=await $a(Le),Ue=q.n,Me=Promise.resolve(),Oe=null;function Gr(){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(Gr,"saveSync");async function Kr(e){const t=await Jr(e);for((!Oe||t.generatedAt>=Oe.generatedAt)&&({sync:q,board:Oe}=Ra(q,t));Ue<q.n;)await Gr();return{sync:q,board:Oe}}c(Kr,"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 Kr(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 zr(){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(zr,"runReview");async function Vr(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(Vr,"itemOf");async function Qr(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(Qr,"askLinksOf");async function Yr(e){const{item:t,agents:a,questions:n}=e.startsWith("ask:")?{item:null,agents:{},questions:[]}:await Vr(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}`)]),x=(await Fe()).filter(i=>E.has(i.data?.key)&&sn.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 pn(e,{lines:g,events:await Promise.all(x.map(se)),owner:s,working:await Se(o),fromAsk:S,links:u?await Qr(S):[],agents:a,moved:p})}c(Yr,"thread");async function Qt(e){for(const t of jr){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 Xr(e){const t=/^\/prototype-([a-z0-9-]+)\.html$/.exec(e)?.[1];return t&&(await Ai(o)).find(a=>a.topic===t)||null}c(Xr,"recordedPrototype");async function Zr(e,t){try{const a=decodeURIComponent(String(e.headers["x-filename"]||""));return f(t,200,await Ji(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(Zr,"upload");const qr=/^(text\/html|image\/svg\+xml|application\/(xhtml\+)?xml|text\/xml)$/;async function es(e,t,a){const n=await xi(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",...qr.test(n.type)?{"content-security-policy":"sandbox"}:{}}),e.method==="HEAD"){t.end();return}vn(n.path).pipe(t)}c(es,"sendUpload");const Je=new Set;function ts(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
|
-
`),Je.add(t),e.on("close",()=>Je.delete(t))}c(
|
|
6
|
+
`),Je.add(t),e.on("close",()=>Je.delete(t))}c(ts,"stream");const Yt=c(e=>{for(const t of Je)t.write(e)},"broadcast");setInterval(()=>Yt(`: ping
|
|
7
7
|
|
|
8
|
-
`),25e3).unref();const
|
|
8
|
+
`),25e3).unref();const as=100,ns=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
|
-
`),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
|
|
14
|
-
`),await b.rename(a+".tmp",a)}c(Pe,"trackLaunch");async function
|
|
15
|
-
`),await b.rename(e+".tmp",e))}catch(e){console.error("Headless builder check failed:",e.message)}finally{Xe=!1}}}c(
|
|
16
|
-
`),await b.rename(s+".tmp",s)},"save");if(S.size&&await E(),!n&&!u.size)return;const
|
|
17
|
-
`),await b.rename(a+".tmp",a)}}c(
|
|
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
|
|
19
|
-
`),await b.rename(l+".tmp",l)),u.length>0}c(
|
|
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
|
|
21
|
-
`),await b.rename(e+".tmp",e))}c(
|
|
22
|
-
`),await b.rename(e+".tmp",e)),(s.added.length||s.again.length)&&await M("orchestrator-waits",{added:s.added,again:s.again})}c(
|
|
23
|
-
`),await b.rename(e+".tmp",e))}c(
|
|
11
|
+
`),ma(),pa(),wa(),ga(),tt(),nt(),ue(),rt()},Math.max(0,Math.min(as,Xt+ns-t)))}c(Ge,"changed");const is=new Set(["node_modules","dd","DerivedData"]),Ke=c(e=>String(e).endsWith(".tmp")||String(e).split(/[\\/]/).some(t=>t.startsWith(".")||is.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 rs(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"?os:ss)(e,r)}catch{r();return}a&&Ge()},"start");s()}c(rs,"watchTree");function ss(e,t){const a=yt(e,{recursive:!0},(n,r)=>{ea(e,r),ze(e)||t()});return a.on("error",t),()=>a.close()}c(ss,"watchRecursive");function os(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 An(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(os,"watchEachFolder");async function cs(){const e=(await L()).items==="waves"?[zi()]:(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))))rs(a)}c(cs,"watchBoard");const ls=Ha.createServer(async(e,t)=>{try{if(e.method!=="GET"&&e.method!=="HEAD"&&N++,e.method!=="GET"&&e.method!=="HEAD"&&sr(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 Rr(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 Ur());if(n==="/api/questions"){const i=await _(),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||ri(w)<i);return f(t,200,{cards:await Cr(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 ts(e,t);if(n==="/_claude/needs")return f(t,200,on(await ce()));if(n==="/api/working"){const i=await _(),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 Yr(String(a.searchParams.get("key")||"")));if(n==="/api/notes"&&a.searchParams.has("wave"))try{return f(t,200,await Ri(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 _i(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&&!Er.has(d))return f(t,403,{error:Ir});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 _()).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 Oi(o,m),I&&(v?.ready||v?.lane==="todo"))return await Qn(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 Yn(o,m,i):v?.lane==="todo"&&v.phase==="ready"&&await Xn(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 Nr(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 qn(o,String(i.key||"")).catch(()=>null)){const m=String(i.key);try{await ti(o,m)}catch(v){if(v instanceof $)return f(t,400,{error:v.message});throw v}return Ss(m),f(t,200,{id:null,board:await G(e,w)})}if(d==="card-merge"){try{await ji(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 Vn(o,m)&&(I=[]),v=await Si(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&&(xn(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 Bi(o,I.map(O=>O.trim())),v.length&&await Di(o,await Mr(v))}catch(O){if(O instanceof $||O instanceof br)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 xr(String(i.question||""));if(m)return f(t,400,{error:m})}if(d===Ie){const m=await _r(String(i.question||""));if(m)return f(t,400,{error:m})}const k=d==="ask"?Ct():void 0;k&&await Fn(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 Wn(o,String(i.id||"")),d==="ask-withdrawn"&&await Hn(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 Zn(o,String(i.key),v),await te(String(i.key),v,m)}return d==="visual-request"&&i.key&&await na((await _()).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 Ti(o),hub:Qi()});if(n==="/api/rules"&&e.method==="POST"){const i=await W(e);let d;try{d=await Li(o,i.rules,i.base??[])}catch(k){if(k instanceof $)return f(t,400,{error:k.message});throw k}const w=Vi(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 Ei(o),paused_until:ur(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 mr(d.repo,wr(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 Gi(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 yr(d,o,w.id,w.on),f(t,200,await gr(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 Mi(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 Ui(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 zr())}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 _()).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 Dr(a.searchParams.get("kind"),Math.min(500,Number(a.searchParams.get("limit")||100))));if(n==="/api/upload"&&e.method==="POST")return await Zr(e,t);if(e.method!=="GET"&&e.method!=="HEAD"){t.writeHead(405).end();return}if(n.startsWith("/uploads/"))return await es(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=ir.has(n.slice(1)),l=s?null:await Xr(n),u=s?null:l?Pn: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(rr({touchIcon:await _t()})):l?Buffer.from(await En(o,l)):await b.readFile(u);!s&&S===".html"&&(y=(await Or(y.toString("utf8"))).replaceAll("{{title}}",Tn((await L()).title)).replaceAll("{{touchIcon}}",await _t())),!s&&S===".html"&&(y=Buffer.from(await Tr(y,n)));const E=S===".html"?`W/"${La.createHash("sha1").update(y).digest("base64url").slice(0,20)}"`:`W/"${Ne(p)}"`,x=S!==".html"&&a.searchParams.get("v")===Ne(p),g={"content-type":F(Dt,S)||"application/octet-stream","cache-control":x?"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 _()).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 yi(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 us()}catch(e){console.error("Auto check failed:",e.message)}}while(Qe)})().finally(()=>{le=null}),le)}c(ue,"checkAuto");async function us(){const e=H(await L()),t=await U(C),{items:a,reviews:n}=await _(),r=await As(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 ys(),D||await hs(fn(a)),D||await vs(a,oa(a),u),D||await Os(a,u);const y=await ms(a,p);if(!D)for(const O of y.slice(0,u.free))u.free--,await gs(O);const{idle:E,pullable:x,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 ps(d),e.auto_pull&&!D&&await fs(un(a,{cap:e.max_planners}).map(O=>a.find(ge=>ge.key===O))),await js(e,t,a,P);const m=x.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 kr(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:ds(ge),reason:ya,inputs:{cap:l,pullable:x,waiting:g}}),Ae(ge)},"startPull");if(await Ts(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(us,"runAuto");function Q(e="claude"){const t=V(K({dir:o}),e);return t&&(D=!0),t}c(Q,"launchHeldNow");const ds=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 oi(o,e.key,`the launch failed: ${a.message}`)}return null}c(Ae,"handOff");async function fs(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(fs,"launchArrivals");async function hs(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(hs,"launchRounds");async function ps(e){for(const{key:t,name:a}of e){if(Q())return;N++;try{const n=await Kn(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(ps,"launchHelpers");let aa=Promise.resolve();function na(e,t){const a=aa.then(()=>ws(e,t));return aa=a.catch(()=>{}),a}c(na,"launchVisuals");async function ws(e,t){for(const{key:a,request:n}of Qa(e,t)){if(Q())return;try{const r=await zn(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(ws,"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 mi(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 vi(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 ms(e,t){const a={usageHeld:c(r=>$t(t,r),"usageHeld")},n=Ja(e,a);n.drop.length&&(N++,n.drop=Ga(n.drop,(await _()).items,a));for(const r of n.ask)await vt(o,r,{cause:"usage",words:lr(t)})&&T({call:"pause",key:r,result:"asked",reason:De(t,"claude"),inputs:{gate:t}});for(const r of n.drop)await Ln(o,r),T({call:"pause",key:r,result:"dropped",reason:"Its cause cleared before its builder reached a stopping point.",inputs:{}});for(const r of n.stop){await St(o,r);const s=In((await Un(o,r)).filter(u=>!Ka(u)))[0],l=s?await Dn([s.pid]):[];T({call:"pause",key:r,result:"hard stop",reason:"Its builder reached no stopping point within 15 minutes of the pause.",inputs:{stopped:l.map(u=>u.pid)}})}return(n.ask.length||n.drop.length||n.stop.length)&&N++,sa=n.ask.length>0||e.some(r=>r.builder?.pause),n.resume}c(ms,"checkPauses");async function gs(e){const t=await Mn(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=za(n.kind)??"claude";try{const{pid:u,log:S}=await Nn(o,{key:e,kind:l,model:n.model,session:{id:n.session.id},brief:c(p=>Rn({...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(gs,"resumeBuild");async function ys(){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(ys,"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 ks(){try{const e=await A(h.join(j,"headless-builders.json"),{});for(const t of(await ci(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(ks,"launchCutOffBuilds");const oa=c(e=>new Set(e.filter(t=>t.builder?.stopped).map(t=>t.key)),"stoppedKeys");let Xe=!1;async function bs(){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:x,log:g}=await ne(o,p);return T({...E,result:y.pid===null?"launched":"relaunched"}),{pid:x,log:g}}catch(x){return T({...E,result:"failed",reason:`${E.reason} The launch failed: ${x.message}`}),await B(o,p.key,"relaunch-failed",{error:x.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 _n(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?dr(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(bs,"reviveHeadless");function Ss(e){const t=h.join(ae.homedir(),".claude","kickflip","logs");On(t,{recursive:!0});const a=jn(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):[ar(),$n],r=Fa(n[0],[...n.slice(1),"--site",Z,"merge",e],{detached:!0,stdio:["ignore",a,a]});Sn(a),r.on("error",s=>B(o,e,"land-failed",{why:`the land couldn't start (${s.message})`}).catch(()=>{})),r.unref()}c(Ss,"runHeldMerge");async function vs(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 ui(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 x=ca(e,(await li(o)).filter(g=>y[g.key]?.verified!==g.verified));if(x.length){for(const g of x){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(vs,"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 Os(e,t){const a=h.join(j,"rebases.json"),n=await A(a,{}),{base:r}=Sa(o),s=ca(e,dn(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 ki(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(Os,"launchRebases");async function ua(){const e=yn(new Date,pt(await A(h.join(ae.homedir(),".claude","kickflip","settings.json"),{})));return e&&mn(e)}c(ua,"triageNight");async function js(e,t,a,n){const r=h.join(j,"triaged.json"),s=await A(r,{}),{waiting:l,seen:u}=wn(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 ai(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(js,"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 As(e){const t=await da(e),a=pt(await A(h.join(ae.homedir(),".claude","kickflip","settings.json"),{}));return t&&gn(new Date,a)?t.data.cards||[]:[]}c(As,"triageHold");async function Ps(){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(Ps,"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 Ts({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(Ts,"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 bi(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 Es()}catch(e){console.error("Action push check failed:",e.message)}}while(et)})().finally(()=>{fe=null}),fe)}c(tt,"checkActions");async function Es(){const e=h.join(j,"action-pushes.json"),t=await A(e,null),a=cn(await _()),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 bn(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(Es,"pushActions");let he=null,at=!1;function nt(){return he?(at=!0,he):(he=(async()=>{do{at=!1;try{await $s()}catch(e){console.error("Orchestrator wait check failed:",e.message)}}while(at)})().finally(()=>{he=null}),he)}c(nt,"checkWaits");async function $s(){const e=h.join(j,"orchestrator-waits.json"),t=await A(e,null),a=t?.waits||[],n=await da(await U(C)),r=[...Za(await _()),...n?[qa(n,F(await Se(o),n.id))]:[]],s=en(a,r,new Date),l=[];for(const u of s.waits)u.kind!=="triage"&&!u.picked&&!u.pushed&&Date.now()-Date.parse(u.since)>=Xa?(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($s,"stepWaits");let pe=null,it=!1;function rt(){return pe?(it=!0,pe):(pe=(async()=>{do{it=!1;try{await Is()}catch(e){console.error("Idle check failed:",e.message)}}while(it)})().finally(()=>{pe=null}),pe)}c(rt,"checkIdle");async function Is(){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 _(),r=hn(n,{pulled:await ha(Ze)}),{state:s,push:l}=nn(a,{idle:r,now:new Date});l&&await qe({kind:"reminder",title:`${t.title||Z} has gone idle`,body:rn(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(Is,"stepIdle");let we=null,st=!1;function pa(){return we?(st=!0,we):(we=(async()=>{do{st=!1;try{const e=Va(await _());for(const t of e)await pi(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 Rs()}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 _()).unfiled.length)return;for(const t of await Cn(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 _()).items.map(e=>e.key))}catch(e){console.error("Read marks failed:",e.message)}}c(ga,"markNewCards");async function Rs(){if(!ut(await _()).length)return;const e=ut(await ce()),t=e.length?await ni(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(Rs,"archiveDone");try{for(const e of await di(o))console.log(e)}catch(e){console.error("follow-up migration:",e.message)}try{for(const e of await hi(o))console.log(e)}catch(e){console.error("status migration:",e.message)}try{for(const e of await fi(o))console.log(e)}catch(e){console.error("phase migration:",e.message)}try{for(const e of await wi(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 gi(o)}catch(e){console.error("dead-field strip:",e.message)}await ks(),await ma({starting:!0}),await pa(),await wa(),await ga(),await cs(),setInterval(()=>{N++,Gt(),bs(),Jn(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():Ps().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(),ls.listen(xt,"127.0.0.1",()=>console.log(`site on http://127.0.0.1:${xt}/ (prefix ${z||"none"})`));
|