@lovek4d/kickflip 0.5.0 → 0.5.3

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.
Files changed (76) hide show
  1. package/CONTEXT.md +47 -42
  2. package/README.md +1 -1
  3. package/SKILL.md +16 -16
  4. package/bin/kickflip.mjs +26 -17
  5. package/bin/wait-events.mjs +2 -2
  6. package/board/agents.mjs +2 -0
  7. package/board/bin/board.mjs +65 -18
  8. package/board/domain.mjs +7 -7
  9. package/board/finish.mjs +4 -4
  10. package/board/headless.mjs +5 -4
  11. package/board/on-host.mjs +1 -1
  12. package/board/public/app-asks.js +6 -6
  13. package/board/public/app-banner.js +7 -13
  14. package/board/public/app-cards.js +51 -27
  15. package/board/public/app-core.js +26 -8
  16. package/board/public/app-header.js +2 -1
  17. package/board/public/app-lanes.js +55 -23
  18. package/board/public/app-needs.js +129 -4
  19. package/board/public/app-reader.js +1 -1
  20. package/board/public/app-snapshot.js +6 -5
  21. package/board/public/app-threads.js +70 -59
  22. package/board/public/app.js +38 -8
  23. package/board/public/attach.js +2 -2
  24. package/board/public/dialogs.js +9 -39
  25. package/board/public/hub-kit.js +4 -2
  26. package/board/public/linear-key.js +46 -35
  27. package/board/public/new-task.js +17 -38
  28. package/board/public/prototype-kit.js +28 -3
  29. package/board/public/prototype-switcher.js +42 -107
  30. package/board/public/shell.js +7 -5
  31. package/board/public/sidebar.js +44 -5
  32. package/board/public/style.css +108 -37
  33. package/board/public/ui-cards.js +41 -30
  34. package/board/public/ui-header.js +2 -2
  35. package/board/public/ui-order.js +12 -10
  36. package/board/public/ui-questions.js +25 -38
  37. package/board/public/ui-sidebar.js +18 -12
  38. package/board/public/ui-threads.js +3 -3
  39. package/board/public/ui.js +40 -8
  40. package/board/public/view-hub-settings.js +11 -10
  41. package/board/public/view-hub.js +17 -3
  42. package/board/public/view-questions.js +142 -199
  43. package/board/public/view-settings.js +191 -72
  44. package/board/server.mjs +19 -20
  45. package/board/store.mjs +20 -15
  46. package/board/transcript.mjs +2 -0
  47. package/board/worktree.mjs +24 -20
  48. package/hooks-plugin/hooks/board-data-gate.mjs +50 -0
  49. package/hooks-plugin/hooks/hooks.json +9 -0
  50. package/hub/boards.mjs +4 -4
  51. package/hub/front-door.mjs +5 -5
  52. package/hub/github.mjs +3 -3
  53. package/hub/hub.mjs +11 -11
  54. package/hub/linear.mjs +1 -2
  55. package/hub/live.mjs +2 -2
  56. package/hub/locks.mjs +1 -1
  57. package/hub/pr-report.mjs +2 -2
  58. package/hub/presence.mjs +1 -1
  59. package/hub/push.mjs +2 -2
  60. package/hub/readiness.mjs +1 -2
  61. package/hub/review-send.mjs +1 -0
  62. package/hub/secrets.mjs +2 -0
  63. package/hub/self-restart.mjs +2 -0
  64. package/hub/sessions.mjs +2 -2
  65. package/hub/share.mjs +1 -0
  66. package/hub/slack.mjs +2 -2
  67. package/hub/stream.mjs +10 -13
  68. package/hub/try.mjs +6 -6
  69. package/hub/upgrade.mjs +1 -1
  70. package/hub/usage.mjs +3 -3
  71. package/hub/views.mjs +1 -1
  72. package/package.json +2 -1
  73. package/shared/audit.mjs +4 -0
  74. package/shared/fake-gh.mjs +17 -4
  75. package/shared/service.mjs +4 -4
  76. package/shared/usage-gate.mjs +1 -1
package/board/server.mjs CHANGED
@@ -1,25 +1,24 @@
1
1
  #!/usr/bin/env node
2
- var xe=Object.defineProperty;var u=(t,e)=>xe(t,"name",{value:e,configurable:!0});import{topicItems as Re}from"./review.mjs";import{boardWorktree as ft}from"./worktree.mjs";import{fromHost as Ne}from"./on-host.mjs";import{archOf as _e,readReviews as Ie,repoName as De,startReview as Ce}from"./arch-review.mjs";import He from"node:http";import m from"node:fs/promises";import ht from"node:os";import f from"node:path";import{execFile as Ut,spawn as Be}from"node:child_process";import{fileURLToPath as Fe}from"node:url";import tt from"node:vm";import Je from"node:zlib";import Lt from"node:crypto";import{promisify as Me}from"node:util";import{archivedOf as Wt,buildBoard as wt,withHub as Ue,DONE_KEPT as Gt,ORCHESTRATOR_PUSH_AFTER as Le,orchestratorWaits as We,PROTOTYPE_WAIT as Ge,prototypeWaits as Ke,idleStep as ze,idleSummary as Ve,ITEM_EVENTS as Qe,lineupMoved as Xe,slimDone as Kt,needsAnswer as Ye,ownerActions as Ze,picksNoTake as qe,pullOf as ta,refOf as pt,startedByAnswer as ea,strictlyIdle as aa,threadOf as na,triageOf as ia}from"./domain.mjs";import{localDay as ra,quietMiddleOf as sa,quietOf as oa}from"../hub/schedule.mjs";import{cardPRs as ca,prKeyOf as zt}from"../shared/card-prs.mjs";import{dropQueued as la}from"../hub/push.mjs";import{closeSync as ua,createReadStream as da,existsSync as Vt,mkdirSync as fa,openSync as ha,readdirSync as wa,statSync as pa,watch as Qt}from"node:fs";import{BOARD_CLI as ma,launchBuilder as et,pidAlive as ga,reviveBuilders as ya}from"./headless.mjs";import{BoardError as E,heldMerge as ka,opensPRs as Sa,tapMerge as va,addTopic as ba,appendThread as L,archiveCards as Oa,archiveCount as ja,archiveRank as Pa,readArchive as Xt,boardConfig as Ea,changeLineup as $a,abandonLaunch as Ta,abandonPlanner as Aa,landable as xa,relaunchLander as Ra,migrateFollowups as Na,startPlanner as _a,stripDeadFields as Ia,pickUp as Da,startQueuedBuild as Ca,startTappedBuild as Yt,sendPush as mt,recordOwnerTap as Ha,mergeCard as Ba,readPrototypes as Fa,takeFile as Ja,finishHandshake as Ma,itemSources as at,readAskLinks as gt,readAskProgress as Ua,readBoardRules as La,readBoardSettings as Wa,readHandshake as Ga,readHidden as Ka,readLineupNotes as za,readNotes as Va,readWorking as yt,readUpload as Qa,removeReviewers as Xa,resolveNoteAnswers as Ya,saveBoardRules as Za,saveBoardSettings as qa,saveUpload as tn,setHidden as Zt,settingsOf as T,threadFile as en,wavesDir as an}from"./store.mjs";import{changed as nn,readHubRules as rn}from"../hub/rules.mjs";import{boardAt as nt,listenState as sn,linksOf as on}from"../hub/boards.mjs";import{pendingFor as cn,shown as ln}from"../hub/prompts.mjs";import{routeRecord as N}from"../hub/routes.mjs";import{CHOICES as un,FOREVER as qt,durationEnd as te}from"../hub/duration.mjs";import{VIEW_PAGES as dn,shellHtml as fn}from"../hub/shell.mjs";import{crossSite as hn,isJSON as wn}from"../starter/cross-site.mjs";import{readUsageGate as it}from"../shared/usage-gate.mjs";import{reviewWaits as pn}from"../shared/skills.mjs";import{boardRepo as ee}from"../hub/board-repo.mjs";import{permissionsView as mn,setPermission as gn}from"./permissions.mjs";const J=f.dirname(Fe(import.meta.url));async function ae(){const t=await m.stat(f.join(J,"..","hub","icon-180.png")).catch(()=>null);return`/_hub/icon-180.png?v=${t?Math.floor(t.mtimeMs).toString(36):"0"}`}u(ae,"touchIcon");async function yn(t){if(!t.includes("<!--header "))return t;const e=tt.createContext({});return tt.runInContext(await m.readFile(f.join(J,"public","ui-page-header.js"),"utf8"),e),t.replace(/<!--header (\{.*?\})-->/g,(n,a)=>e.BoardUI.header(JSON.parse(a)))}u(yn,"pageHeaders");const d=f.resolve(process.argv[2]||process.cwd()),kn=[f.join(d,"public"),f.join(J,"public"),f.join(J,"..","starter","public"),f.join(J,"..","shared")],S=f.join(d,"data"),F=f.join(S,"events.jsonl"),rt=f.join(S,"replies"),ne=Number(process.env.PORT||4600),H=(process.env.PREFIX||"").replace(/\/+$/,""),ie={".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"},Sn=Me(Je.gzip),vn=/^(text\/|application\/json|image\/svg\+xml)/;async function re(t,e,n,a){["GET","HEAD"].includes(t.req?.method)||I++;const r={...n};let s=Buffer.isBuffer(a)?a:Buffer.from(a??"");if(vn.test(r["content-type"]||"")&&(r.vary="accept-encoding",s.length>=1024&&/\bgzip\b/.test(t.req?.headers["accept-encoding"]||"")))try{s=await Sn(s),r["content-encoding"]="gzip"}catch{}r["content-length"]=s.length,t.writeHead(e,r),t.end(t.req?.method==="HEAD"?void 0:s)}u(re,"send");const w=u((t,e,n,a={})=>re(t,e,{"content-type":ie[".json"],"cache-control":"no-store",...a},JSON.stringify(n)),"json"),st=u(t=>`${Math.floor(t.mtimeMs).toString(36)}-${t.size.toString(36)}`,"versionOf"),se=/\b(src|href)="([\w.][\w./-]*\.(?:js|css))"/g;async function bn(t,e){const n=e.endsWith("/")?e:f.posix.dirname(e),a=[...new Set([...t.matchAll(se)].map(s=>s[2]))],r=new Map(await Promise.all(a.map(async s=>{const i=await ge(f.posix.join(n,s)),o=i?await m.stat(i).catch(()=>null):null;return[s,o?st(o):null]})));return t.replace(se,(s,i,o)=>r.get(o)?`${i}="${o}?v=${r.get(o)}"`:s)}u(bn,"versionAssets");const oe=u((t,e)=>String(t||"").split(",").map(n=>n.trim().replace(/^W\//,"")).includes(e.replace(/^W\//,"")),"sameTag"),b=u(async(t,e)=>{try{return JSON.parse(await m.readFile(t,"utf8"))}catch{return e}},"readJSON"),_=u(async t=>{try{return(await m.readFile(t,"utf8")).split(`
3
- `).filter(Boolean).map(e=>JSON.parse(e))}catch{return[]}},"readLines"),W=u(t=>en(d,t),"threadFile"),kt=u(async()=>{const t=await Ka(d);return(await _(F)).filter(e=>!t.has(e.id))},"shownEvents");async function B(t,e=64*1024){if(!wn(t.headers))throw Object.assign(new Error("Send the body as application/json."),{status:403});let n="";for await(const a of t)if(n+=a,n.length>e)throw Object.assign(new Error("too large"),{status:413});return n?JSON.parse(n):{}}u(B,"readBody");const x=u(async(t,e)=>(await ot([[t,e]]))[0],"appendEvent");async function ot(t){const e=new Date().toISOString(),n=t.map(([a,r])=>({id:"e"+Date.now().toString(36)+Math.random().toString(36).slice(2,7),ts:e,kind:a,data:r}));return await m.mkdir(rt,{recursive:!0}),await m.appendFile(F,n.map(a=>JSON.stringify(a)+`
4
- `).join("")),n.map(a=>a.id)}u(ot,"appendEvents");async function ce(t){const e=new Map(((await b(f.join(S,"owner.json"),{items:[]})).items||[]).map(i=>[i.id,i])),n=u(i=>e.has(String(i.id))&&qe(e.get(String(i.id)),i.choice),"again"),a=await ot(t.map(i=>["owner-answer",n(i)?{...i,prototype_again:!0}:i]));await Ya(d,t),I++;const r=await $(),s=[...new Set(t.map(i=>ea(r,String(i.id))).filter(Boolean))];return s.length&&await ot(s.map(i=>["card-unblocked",{key:i}])),a}u(ce,"recordAnswers");async function On(t){if(!Array.isArray(t)||!t.length)return"Send at least one answer.";const e=new Set(((await b(f.join(S,"owner.json"),{items:[]})).items||[]).map(a=>a.id)),n=t.filter(a=>!e.has(String(a?.id)));return n.length?`${n.map(a=>String(a?.id)).join(", ")} ${n.length===1?"is":"are"} no longer asked here. Reload to see what is, then send again.`:null}u(On,"answersRefused");async function jn(t){const e=(await $()).owner.find(n=>n.id===t);return e?e.state!=="resolved"?`${t} isn\u2019t resolved, so there is nothing to ask again.`:null:`${t} is no longer asked here. Reload to see what is.`}u(jn,"reaskRefused");async function ct(t){for(const e of["html","json"]){const n=f.join(rt,`${t.id}.${e}`);try{const[a,r]=await Promise.all([m.readFile(n,"utf8"),m.stat(n)]);return{...t,reply:{[e]:e==="json"?JSON.parse(a):a,at:r.mtime.toISOString()}}}catch{}}return{...t,reply:null}}u(ct,"withReply");async function Pn(t,e){const n=await yt(d);let a=await kt();return t&&(a=a.filter(r=>r.kind===t)),Promise.all(a.reverse().slice(0,e).map(async r=>{const s=await ct(r);return s.reply||!N(n,r.id)?s:{...s,working:N(n,r.id)}}))}u(Pn,"listEvents");const St=new Map,En="number,title,url,state,isDraft,reviewDecision,mergeStateStatus,mergeable,statusCheckRollup,mergedAt,headRefName,headRefOid,updatedAt,latestReviews";function $n(t,e){return new Promise(n=>{Ut("gh",["pr","view",String(e),"--repo",t,"--json",En],{timeout:2e4},(a,r)=>{if(a)return n(null);try{n(JSON.parse(r))}catch{n(null)}})})}u($n,"ghPR");function Tn(t,e){return new Promise(n=>{Ut("gh",["api",`repos/${t}/pulls/${e}/comments?per_page=100`],{timeout:2e4},(a,r)=>{if(a)return n(0);try{const s=JSON.parse(r),i=new Set(s.map(o=>o.in_reply_to_id).filter(Boolean));n(s.filter(o=>!o.in_reply_to_id&&/codex/i.test(o.user?.login||"")&&!i.has(o.id)).length)}catch{n(0)}})})}u(Tn,"ghCodexOpen");function An(t){const e=t||[];let n=0,a=0;for(const r of e){const s=(r.conclusion||r.state||"").toUpperCase(),i=(r.status||"").toUpperCase();["FAILURE","ERROR","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE"].includes(s)?n++:(i&&i!=="COMPLETED"||s==="PENDING"||s==="EXPECTED")&&a++}return{total:e.length,fail:n,pending:a,state:n?"fail":a?"pending":e.length?"pass":"none"}}u(An,"checksOf");async function xn(t,e){const n=`${t}#${e}`;let a=St.get(n);return a||(a={at:0,value:null,loading:null},St.set(n,a)),Date.now()-a.at>45e3&&!a.loading&&(a.loading=Promise.all([$n(t,e),Tn(t,e)]).then(([r,s])=>{if(r){const{statusCheckRollup:i,latestReviews:o,...g}=r,k=Object.fromEntries((o||[]).map(j=>[j.author?.login,j.state])),y={...g,checks:An(i),reviews:k,codexOpen:s};JSON.stringify(y)!==JSON.stringify(a.value)&&I++,a.value=y}a.at=Date.now(),a.loading=null})),!a.value&&a.loading&&await a.loading,a.value}u(xn,"prState");const R=u(()=>Ea(d),"config");async function Rn(){const t=await m.readdir(rt).catch(()=>[]),e=await Promise.all(t.map(a=>m.stat(f.join(rt,a)).then(r=>r.mtimeMs,()=>0))),n=Math.max(0,...e);return n?new Date(n).toISOString():null}u(Rn,"latestReply");async function le(t){let e=[];try{e=(await m.readdir(t)).filter(r=>r.endsWith(".json"))}catch{}const n=await Promise.all(e.map(r=>b(f.join(t,r),null))),a=Object.create(null);return e.forEach((r,s)=>{if(!n[s])return;const[i,o="main"]=f.basename(r,".json").split("@");(a[i]||=Object.create(null))[o]=n[s]}),a}u(le,"readAgents");async function vt(){const t=nt(d);return{cfg:await R(),github:t?.github,local:t?.local,remote:t?.remote,branch:t?.branch,topicItems:Re()}}u(vt,"outsideData");async function ue(){return(await Promise.all((await at(d)).map(async t=>{const e=await b(t.file,null);return!e||!Array.isArray(e.items)?null:{slug:t.slug,wave:e,agents:await le(f.join(t.dir,"agents"))}}))).filter(Boolean)}u(ue,"liveWaves");async function Nn(t){const e=await ue();for(const n of t){const a=e.find(r=>r.slug===n.wave);a?a.wave.items.push(n.item):e.push({slug:n.wave,wave:{items:[n.item]},agents:{}})}return e}u(Nn,"withArchived");async function de({cfg:t,github:e,local:n,remote:a,branch:r},s){const i=t.items==="waves";s??=await ue();const o=Object.create(null);return await Promise.all(s.flatMap(({slug:g,wave:k})=>k.items.map(async y=>{o[`${g}/${y.id}`]=await _(W(`${g}/${y.id}`))}))),{github:e,remote:a,branch:r,reviewers:Sa({github:e,local:n},T(t))?t.reviewers||[]:[],fromWaves:i,autoMerge:T(t).auto_merge,skipDraft:T(t).skip_draft,autoPull:T(t).auto_pull,autoBuild:T(t).auto_build,tickets:T(t).tickets,waves:s,threads:o,working:await yt(d),autoReview:T(t).auto_review,incoming:await b(f.join(S,"incoming.json"),null),reviewRecords:await b(f.join(S,"reviews.json"),{})}}u(de,"itemSnapshot");async function fe(t,e=[]){const n=await de(t,e.length?await Nn(e):void 0),{waves:a,threads:r,working:s}=n,i=Object.create(null),o=new Set(e.map(y=>y.key));await Promise.all(a.flatMap(({slug:y,wave:j})=>{const A=n.github||j.repo;return j.items.flatMap(c=>ca(c,A).map(async l=>{i[zt(l)]=o.has(`${y}/${c.id}`)?St.get(zt(l))?.value??null:await xn(l.repo,l.number)}))}));const g=new Set(["ask","owner-answer","grill-more","item-message","wave-triage","overnight-triage"]),k=await Promise.all((await kt()).map(y=>g.has(y.kind)||N(s,y.id)?ct(y):{...y,reply:null}));return await Promise.all(k.filter(y=>y.kind==="ask").map(async y=>{const j=await _(W(`ask:${y.id}`));j.length&&(r[`ask:${y.id}`]=j)})),{...n,try:await b(f.join(S,"try.json"),null),main:await b(f.join(S,"main.json"),null),prs:i,events:k,askLinks:await gt(d),askProgress:await Ua(d),checked:await b(f.join(S,"checked.json"),null),watcher:await b(f.join(S,"watcher.json"),null),replyAt:await Rn(),owner:(y=>({...y,items:[...y.items||[],...e.flatMap(j=>j.questions||[])]}))(await b(f.join(S,"owner.json"),{items:[]})),topicItems:t.topicItems}}u(fe,"snapshot");async function _n(t){if(!t.length)return[];const e=new Map(wt(await fe(await vt(),t)).items.map(n=>[n.key,n]));return t.map(n=>{const a=e.get(n.key);return Kt({...a,sortAt:n.closed_at,worktree:a.worktree&&!Vt(a.worktree)?null:a.worktree})})}u(_n,"archivedCards");const M=H.slice(1)||f.basename(d);let I=0,G=null;async function $(){const t=await vt(),e=JSON.stringify([I,t]);if(G?.key!==e){const n=fe(t).then(wt);G={key:e,built:n},n.catch(()=>{G?.built===n&&(G=null)})}return G.built}u($,"sharedBoard");let bt=!1;async function Ot(){if(!bt){bt=!0;try{const t=f.join(S,"builder-stalled.json"),e=await b(t,{}),n=Object.create(null);let a;for(const{key:r,lane:s,builder:i}of(await $()).items)s==="done"||!i?.stalled||(n[r]=i.silentSince,N(e,r)!==i.silentSince&&(a||=await _(F),a.some(o=>o.kind==="builder-stalled"&&o.data?.key===r&&o.data?.silentSince===i.silentSince)||await x("builder-stalled",{key:r,silentSince:i.silentSince})));JSON.stringify(n)!==JSON.stringify(e)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify(n)+`
5
- `),await m.rename(t+".tmp",t))}catch(t){console.error("Builder check failed:",t.message)}finally{bt=!1}}}u(Ot,"checkStalled");let jt=!1;async function he(){if(!jt){jt=!0;try{const t=await _(F),e=new Set(t.filter(n=>n.kind==="prototype-overdue").map(n=>n.data?.request));for(const n of Ke(await $(),t))e.has(n.request)||Date.now()-Date.parse(n.since)<Ge||await x("prototype-overdue",n)}catch(t){console.error("Prototype check failed:",t.message)}finally{jt=!1}}}u(he,"checkPrototypeWaits");async function In(t){const e=await Ga(d);if(!e?.hello?.id||e.done_at)return null;const n=t?.find(r=>r.link==="github"),a={events:e.ack?.id===e.hello.id,...n||!t?{github:!!n?.ok}:{}};return a.events&&a.github!==!1?(await Ma(d),null):a}u(In,"setupOf");const Dn=un.map(([t,e])=>({value:t,label:e,ms:t===qt?null:te(new Date(0),t)-new Date(0)}));async function K(){const t=new Date().toISOString(),{title:e,reviewers:n}=await R(),a=nt(d),r=a?sn(a):"deaf",s=Ue(await $(),{listen:r,prompts:a?cn(a.route).map(ln):[]});s.items=s.items.map(o=>o.worktree&&!Vt(o.worktree)?{...o,worktree:null}:o);const i=a?on(a,{listen:r}):null;return{generatedAt:t,title:e,reviewers:(n||[]).map(o=>o?.name).filter(o=>typeof o=="string"),...s,links:i,setup:await In(i),holdChoices:Dn,arch:a?_e(await Ie(d),s.items,s.owner,{repo:De(ee(a.route)),waits:pn()}):null}}u(K,"board");async function Cn(t){["GET","HEAD"].includes(t.method)||I++;const e={...await K(),onHost:await Ne(t),doneArchived:await ja(d)};e.owner=e.owner.filter(i=>i.state!=="resolved"),e.items=e.items.map(i=>i.lane==="done"?Kt(i):i);const n=new Set(e.items.map(i=>i.key));e.ownerTopics=e.ownerTopics.map(({title:i,summary:o,askedAt:g,...k})=>n.has(k.card)?k:{...k,title:i,summary:o,...g?{askedAt:g}:{}});const{generatedAt:a,...r}=e,s={...e,version:Lt.createHash("sha1").update(JSON.stringify(r)).digest("base64url").slice(0,20)};z.delete(s.version),z.set(s.version,s);for(const i of[...z.keys()].slice(0,-Hn))z.delete(i);return s}u(Cn,"pageBoard");const Hn=8,z=new Map,we=tt.createContext({});tt.runInContext(await m.readFile(new URL("./public/ui-patch.js",import.meta.url),"utf8"),we);const{boardPatch:Bn}=we.BoardUI;async function D(t,e){const n=e&&z.get(e),a=await Cn(t);return n?Bn(n,a):a}u(D,"answerBoard");let pe=Promise.resolve();function Fn(){const t=pe.then(async()=>{const e=nt(d);if(!e)throw Object.assign(new E("This board isn\u2019t in the hub\u2019s manifest."),{status:404});const{arch:n}=await K();if(n.last?.open)return{key:n.last.key,ref:n.last.ref,existing:!0};const{key:a,data:r}=await Ce(d,e),s=await x("arch-review",r);return{key:a,id:s,existing:!1}});return pe=t.catch(()=>{}),t}u(Fn,"runReview");async function Jn(t){const[e,n]=t.split("/"),a=(await at(d)).find(o=>o.slug===e),r=a?await b(a.file,null):null,s=!r?.items?.some(o=>o.id===n)&&(await Xt(d)).find(o=>o.key===t),i=Array.isArray(r?.items)&&r.items.find(o=>o.id===n)||s?.item||null;return{item:i,questions:s?.questions||[],agents:i&&a?N(await le(f.join(a.dir,"agents")),n)||{}:{}}}u(Jn,"itemOf");async function Mn(t){const e=N(await gt(d),t),{tickets:n}=T(await R()),a=[];for(const r of await at(d)){const s=await b(r.file,null);for(const i of Array.isArray(s?.items)?s.items:[]){const o=`${r.slug}/${i.id}`;i.from_ask===t?a.unshift({key:o,ref:pt(s,i,n),became:!0}):o===e&&a.push({key:o,ref:pt(s,i,n),became:!1})}}return a}u(Mn,"askLinksOf");async function Un(t){const{item:e,agents:n,questions:a}=t.startsWith("ask:")?{item:null,agents:{},questions:[]}:await Jn(t),r=await b(f.join(S,"owner.json"),{items:[]}),s={...r,items:[...r.items||[],...a]},i=new Set(s.items.filter(l=>l.topic===`item:${t}`).map(l=>l.id)),o=t.startsWith("ask:"),g=o?t.slice(4):e?.from_ask||null,k=o?[]:Object.entries(await gt(d)).filter(([,l])=>l===t).map(([l])=>l),y=[g,...k].filter(Boolean),j=new Set([t,...y.map(l=>`ask:${l}`)]),A=(await kt()).filter(l=>j.has(l.data?.key)&&Qe.includes(l.kind)||l.kind==="owner-answer"&&i.has(l.data?.id)||l.kind==="ask"&&y.includes(l.id)||l.kind==="ask-edited"),c=(await Promise.all([...j].map(l=>_(W(l))))).flat();return na(t,{lines:c,events:await Promise.all(A.map(ct)),owner:s,working:await yt(d),fromAsk:g,links:o?await Mn(g):[],agents:n,moved:k})}u(Un,"thread");const me=u(t=>String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]),"esc");async function ge(t){for(const e of kn){let n=f.resolve(e,"."+t);if(n!==e&&!n.startsWith(e+f.sep))return null;if((await m.stat(n).catch(()=>null))?.isDirectory()&&(n=f.join(n,"index.html")),await m.stat(n).then(r=>r.isFile(),()=>!1))return n}return null}u(ge,"staticFile");const ye=f.join(J,"public","prototype.html");async function Ln(t){const e=/^\/prototype-([a-z0-9-]+)\.html$/.exec(t)?.[1];return e&&(await Fa(d)).find(n=>n.topic===e)||null}u(Ln,"recordedPrototype");const ke=u(t=>JSON.stringify(t).replace(/</g,"\\u003c").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029"),"scriptJSON");async function Wn(t){const e=await Promise.all(t.takes.map(async s=>{const i=`prototypes/${t.topic}/${s.key}.js`,o=await m.stat(Ja(d,t.topic,s.key)).catch(()=>null);return{key:s.key,label:s.label,src:o?.isFile()?`${i}?v=${st(o)}`:null,file:i}})),n=`${t.title} \xB7 prototype`,a={lead:"back",backHref:t.card?`./#item=${encodeURIComponent(t.card)}`:"./",title:n,controls:!1},r={prototypeTitle:me(n),prototypeHeader:`<!--header ${ke(a).replace(/>/g,"\\u003e")}-->`,prototypeData:ke({topic:t.topic,title:t.title,takes:e})};return(await m.readFile(ye,"utf8")).replace(/\{\{(prototypeTitle|prototypeHeader|prototypeData)\}\}/g,(s,i)=>r[i])}u(Wn,"prototypePage");async function Gn(t,e){try{const n=decodeURIComponent(String(t.headers["x-filename"]||""));return w(e,200,await tn(d,t,{name:n,type:t.headers["content-type"],length:Number(t.headers["content-length"])||0}))}catch(n){if(!(n instanceof E)&&!(n instanceof URIError))throw n;return t.resume(),w(e,n.status||400,{error:n.message})}}u(Gn,"upload");const Kn=/^(text\/html|image\/svg\+xml|application\/(xhtml\+)?xml|text\/xml)$/;async function zn(t,e,n){const a=await Qa(d,n);if(!a){e.writeHead(404).end();return}if(e.writeHead(200,{"content-type":a.type.startsWith("text/")?`${a.type}; charset=utf-8`:a.type,"content-length":a.size,"content-disposition":`inline; filename*=UTF-8''${encodeURIComponent(a.name)}`,"cache-control":"private, max-age=86400","x-content-type-options":"nosniff",...Kn.test(a.type)?{"content-security-policy":"sandbox"}:{}}),t.method==="HEAD"){e.end();return}da(a.path).pipe(e)}u(zn,"sendUpload");const Pt=new Set;function Vn(t,e){e.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-store",connection:"keep-alive"}),e.write(`retry: 3000
2
+ var Fe=Object.defineProperty;var u=(t,e)=>Fe(t,"name",{value:e,configurable:!0});import{topicItems as Me}from"./review.mjs";import{boardWorktree as it,builderRepo as Ue,prWorktree as Je}from"./worktree.mjs";import{transcriptMovedAt as Le,transcriptCommandRunning as We}from"./transcript.mjs";import{fromHost as Ge}from"./on-host.mjs";import{archOf as Ke,readReviews as ze,repoName as Ve,startReview as Qe}from"./arch-review.mjs";import Xe from"node:http";import m from"node:fs/promises";import gt from"node:os";import w from"node:path";import{execFile as Gt,spawn as Ye}from"node:child_process";import{fileURLToPath as Ze}from"node:url";import rt from"node:vm";import qe from"node:zlib";import Kt from"node:crypto";import{promisify as ta}from"node:util";import{archivedOf as zt,buildBoard as yt,withHub as ea,DONE_KEPT as Vt,ORCHESTRATOR_PUSH_AFTER as aa,orchestratorWaits as na,PROTOTYPE_WAIT as ia,prototypeWaits as ra,idleStep as sa,idleSummary as oa,ITEM_EVENTS as ca,lineupMoved as la,slimDone as Qt,needsAnswer as ua,ownerActions as da,picksNoTake as fa,pullOf as ha,arrivalsOf as pa,rebasesDue as wa,refOf as kt,startedByAnswer as ma,strictlyIdle as ga,threadOf as ya,triageOf as ka}from"./domain.mjs";import{localDay as ba,quietMiddleOf as va,quietOf as Sa}from"../hub/schedule.mjs";import{cardPRs as Oa,prKeyOf as Xt}from"../shared/card-prs.mjs";import{dropQueued as ja}from"../hub/push.mjs";import{closeSync as $a,createReadStream as Pa,existsSync as Yt,mkdirSync as Ta,openSync as Aa,readdirSync as Ea,statSync as Ra,watch as Zt}from"node:fs";import{BOARD_CLI as xa,launchBuilder as K,pidAlive as qt,reviveBuilders as Ia,stopAgents as Na}from"./headless.mjs";import{BoardError as P,openAgents as _a,isLanding as Da,askImplement as Ca,heldMerge as Ha,opensPRs as Ba,tapMerge as Fa,addTopic as Ma,appendThread as H,archiveCards as Ua,archiveCount as Ja,archiveRank as La,readArchive as te,boardConfig as Wa,changeLineup as Ga,abandonLaunch as Ka,cutOffBuilds as za,abandonPlanner as Va,landable as Qa,relaunchLander as Xa,migrateFollowups as Ya,dropPlannedStatus as Za,startPlanner as qa,stripDeadFields as tn,pickUp as en,startRebase as an,startReleasedBuild as nn,startTappedBuild as bt,sendPush as rn,recordOwnerTap as sn,mergeCard as on,readPrototypes as cn,takeFile as ln,finishHandshake as un,itemSources as st,readAskLinks as vt,readAskProgress as dn,readBoardRules as fn,readBoardSettings as hn,readHandshake as pn,readHidden as wn,readLineupNotes as mn,readMarks as gn,readNotes as yn,readWorking as St,readUpload as kn,addReviewers as bn,removeReviewers as vn,markRead as ee,resolveNoteAnswers as Sn,settleGates as On,saveBoardRules as jn,saveBoardSettings as $n,saveBoardShare as Pn,saveUpload as Tn,setHidden as ae,settingsOf as N,threadFile as An,wavesDir as En}from"./store.mjs";import{changed as Rn,readHubRules as xn}from"../hub/rules.mjs";import{boardAt as z,listenState as In,linksOf as Nn}from"../hub/boards.mjs";import{pendingFor as _n,shown as Dn}from"../hub/prompts.mjs";import{routeRecord as F}from"../hub/routes.mjs";import{CHOICES as Cn,FOREVER as ne,durationEnd as ie}from"../hub/duration.mjs";import{VIEW_PAGES as Hn,shellHtml as Bn}from"../hub/shell.mjs";import{crossSite as Fn,isJSON as Mn}from"../starter/cross-site.mjs";import{readUsageGate as ot,heldFor as re,pauseReasonFor as Un}from"../shared/usage-gate.mjs";import{audit as Jn,boardAudit as Ln}from"../shared/audit.mjs";import{reviewWaits as Wn}from"../shared/skills.mjs";import{boardRepo as se}from"../hub/board-repo.mjs";import{permissionsView as Gn,setPermission as Kn}from"./permissions.mjs";import{SlackError as oe,SLACK_NEEDS_TOKEN as ce,savedSlack as le,slackStatus as zn}from"../hub/slack.mjs";import{collaborators as Vn,githubUser as Qn}from"../hub/pr-report.mjs";const L=w.dirname(Ze(import.meta.url));async function ue(){const t=await m.stat(w.join(L,"..","hub","icon-180.png")).catch(()=>null);return`/_hub/icon-180.png?v=${t?Math.floor(t.mtimeMs).toString(36):"0"}`}u(ue,"touchIcon");async function Xn(t){if(!t.includes("<!--header "))return t;const e=rt.createContext({});return rt.runInContext(await m.readFile(w.join(L,"public","ui-page-header.js"),"utf8"),e),t.replace(/<!--header (\{.*?\})-->/g,(a,n)=>e.BoardUI.header(JSON.parse(n)))}u(Xn,"pageHeaders");const d=w.resolve(process.argv[2]||process.cwd()),Yn=[w.join(d,"public"),w.join(L,"public"),w.join(L,"..","starter","public"),w.join(L,"..","shared")],S=w.join(d,"data"),A=u(t=>Jn(Ln(d),t),"logCall"),W=w.join(S,"events.jsonl"),ct=w.join(S,"replies"),de=Number(process.env.PORT||4600),M=(process.env.PREFIX||"").replace(/\/+$/,""),fe={".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"},Zn=ta(qe.gzip),qn=/^(text\/|application\/json|image\/svg\+xml)/;async function he(t,e,a,n){["GET","HEAD"].includes(t.req?.method)||x++;const s={...a};let r=Buffer.isBuffer(n)?n:Buffer.from(n??"");if(qn.test(s["content-type"]||"")&&(s.vary="accept-encoding",r.length>=1024&&/\bgzip\b/.test(t.req?.headers["accept-encoding"]||"")))try{r=await Zn(r),s["content-encoding"]="gzip"}catch{}s["content-length"]=r.length,t.writeHead(e,s),t.end(t.req?.method==="HEAD"?void 0:r)}u(he,"send");const p=u((t,e,a,n={})=>he(t,e,{"content-type":fe[".json"],"cache-control":"no-store",...n},JSON.stringify(a)),"json"),lt=u(t=>`${Math.floor(t.mtimeMs).toString(36)}-${t.size.toString(36)}`,"versionOf"),pe=/\b(src|href)="([\w.][\w./-]*\.(?:js|css))"/g;async function ti(t,e){const a=e.endsWith("/")?e:w.posix.dirname(e),n=[...new Set([...t.matchAll(pe)].map(r=>r[2]))],s=new Map(await Promise.all(n.map(async r=>{const o=await Te(w.posix.join(a,r)),i=o?await m.stat(o).catch(()=>null):null;return[r,i?lt(i):null]})));return t.replace(pe,(r,o,i)=>s.get(i)?`${o}="${i}?v=${s.get(i)}"`:r)}u(ti,"versionAssets");const we=u((t,e)=>String(t||"").split(",").map(a=>a.trim().replace(/^W\//,"")).includes(e.replace(/^W\//,"")),"sameTag"),j=u(async(t,e)=>{try{return JSON.parse(await m.readFile(t,"utf8"))}catch{return e}},"readJSON"),U=u(async t=>{try{return(await m.readFile(t,"utf8")).split(`
3
+ `).filter(Boolean).map(e=>JSON.parse(e))}catch{return[]}},"readLines"),V=u(t=>An(d,t),"threadFile"),Ot=u(async()=>{const t=await wn(d);return(await U(W)).filter(e=>!t.has(e.id))},"shownEvents");async function _(t,e=64*1024){if(!Mn(t.headers))throw Object.assign(new Error("Send the body as application/json."),{status:403});let a="";for await(const n of t)if(a+=n,a.length>e)throw Object.assign(new Error("too large"),{status:413});return a?JSON.parse(a):{}}u(_,"readBody");const B=u(async(t,e)=>(await ut([[t,e]]))[0],"appendEvent");async function ut(t){const e=new Date().toISOString(),a=t.map(([n,s])=>({id:"e"+Date.now().toString(36)+Math.random().toString(36).slice(2,7),ts:e,kind:n,data:s}));return await m.mkdir(ct,{recursive:!0}),await m.appendFile(W,a.map(n=>JSON.stringify(n)+`
4
+ `).join("")),x++,a.map(n=>n.id)}u(ut,"appendEvents");async function me(t){const e=new Map(((await j(w.join(S,"owner.json"),{items:[]})).items||[]).map(i=>[i.id,i])),a=u(i=>e.has(String(i.id))&&fa(e.get(String(i.id)),i.choice),"again"),n=await ut(t.map(i=>["owner-answer",a(i)?{...i,prototype_again:!0}:i]));await Sn(d,t);const{prototype:s}=await On(d,t);x++;for(const i of s)await Nt(i,"prototype");const r=await E(),o=[...new Set(t.map(i=>ma(r,String(i.id))).filter(Boolean))];return o.length&&await ut(o.map(i=>["card-unblocked",{key:i}])),n}u(me,"recordAnswers");async function ei(t){if(!Array.isArray(t)||!t.length)return"Send at least one answer.";const e=new Set(((await j(w.join(S,"owner.json"),{items:[]})).items||[]).map(n=>n.id)),a=t.filter(n=>!e.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}u(ei,"answersRefused");async function ai(t){const e=(await E()).owner.find(a=>a.id===t);return e?e.state!=="resolved"?`${t} isn\u2019t resolved, so there is nothing to ask again.`:null:`${t} is no longer asked here. Reload to see what is.`}u(ai,"reaskRefused");async function dt(t){for(const e of["html","json"]){const a=w.join(ct,`${t.id}.${e}`);try{const[n,s]=await Promise.all([m.readFile(a,"utf8"),m.stat(a)]);return{...t,reply:{[e]:e==="json"?JSON.parse(n):n,at:s.mtime.toISOString()}}}catch{}}return{...t,reply:null}}u(dt,"withReply");async function ni(t,e){const a=await St(d);let n=await Ot();return t&&(n=n.filter(s=>s.kind===t)),Promise.all(n.reverse().slice(0,e).map(async s=>{const r=await dt(s);return r.reply||!F(a,s.id)?r:{...r,working:F(a,s.id)}}))}u(ni,"listEvents");const jt=new Map,ii="number,title,url,state,isDraft,reviewDecision,mergeStateStatus,mergeable,statusCheckRollup,mergedAt,headRefName,headRefOid,updatedAt,latestReviews";function ri(t,e){return new Promise(a=>{Gt("gh",["pr","view",String(e),"--repo",t,"--json",ii],{timeout:2e4},(n,s)=>{if(n)return a(null);try{a(JSON.parse(s))}catch{a(null)}})})}u(ri,"ghPR");function si(t,e){return new Promise(a=>{Gt("gh",["api",`repos/${t}/pulls/${e}/comments?per_page=100`],{timeout:2e4},(n,s)=>{if(n)return a(0);try{const r=JSON.parse(s),o=new Set(r.map(i=>i.in_reply_to_id).filter(Boolean));a(r.filter(i=>!i.in_reply_to_id&&/codex/i.test(i.user?.login||"")&&!o.has(i.id)).length)}catch{a(0)}})})}u(si,"ghCodexOpen");function oi(t){const e=t||[];let a=0,n=0;for(const s of e){const r=(s.conclusion||s.state||"").toUpperCase(),o=(s.status||"").toUpperCase();["FAILURE","ERROR","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE"].includes(r)?a++:(o&&o!=="COMPLETED"||r==="PENDING"||r==="EXPECTED")&&n++}return{total:e.length,fail:a,pending:n,state:a?"fail":n?"pending":e.length?"pass":"none"}}u(oi,"checksOf");async function ci(t,e){const a=`${t}#${e}`;let n=jt.get(a);return n||(n={at:0,value:null,loading:null},jt.set(a,n)),Date.now()-n.at>45e3&&!n.loading&&(n.loading=Promise.all([ri(t,e),si(t,e)]).then(([s,r])=>{if(s){const{statusCheckRollup:o,latestReviews:i,...g}=s,y=Object.fromEntries((i||[]).map($=>[$.author?.login,$.state])),b={...g,checks:oi(o),reviews:y,codexOpen:r};JSON.stringify(b)!==JSON.stringify(n.value)&&x++,n.value=b}n.at=Date.now(),n.loading=null})),!n.value&&n.loading&&await n.loading,n.value}u(ci,"prState");const D=u(()=>Wa(d),"config");async function li(){const t=await m.readdir(ct).catch(()=>[]),e=await Promise.all(t.map(n=>m.stat(w.join(ct,n)).then(s=>s.mtimeMs,()=>0))),a=Math.max(0,...e);return a?new Date(a).toISOString():null}u(li,"latestReply");async function ge(t){let e=[];try{e=(await m.readdir(t)).filter(s=>s.endsWith(".json"))}catch{}const a=await Promise.all(e.map(s=>j(w.join(t,s),null))),n=Object.create(null);return e.forEach((s,r)=>{if(!a[r])return;const[o,i="main"]=w.basename(s,".json").split("@");(n[o]||=Object.create(null))[i]=a[r]}),n}u(ge,"readAgents");async function $t(){const t=z(d);return{cfg:await D(),github:t?.github,local:t?.local,remote:t?.remote,branch:t?.branch,topicItems:Me()}}u($t,"outsideData");async function ye(){return(await Promise.all((await st(d)).map(async t=>{const e=await j(t.file,null);return!e||!Array.isArray(e.items)?null:{slug:t.slug,wave:e,agents:await ge(w.join(t.dir,"agents"))}}))).filter(Boolean)}u(ye,"liveWaves");async function ui(t){const e=await ye();for(const a of t){const n=e.find(s=>s.slug===a.wave);n?n.wave.items.push(a.item):e.push({slug:a.wave,wave:{items:[a.item]},agents:{}})}return e}u(ui,"withArchived");async function ke({cfg:t,github:e,local:a,remote:n,branch:s},r){const o=t.items==="waves";r??=await ye();const i=Object.create(null);return await Promise.all(r.flatMap(({slug:g,wave:y})=>y.items.map(async b=>{i[`${g}/${b.id}`]=await U(V(`${g}/${b.id}`))}))),{github:e,remote:n,branch:s,reviewers:Ba({github:e,local:a},N(t))?t.reviewers||[]:[],fromWaves:o,autoMerge:N(t).auto_merge,skipDraft:N(t).skip_draft,autoBuild:N(t).auto_build,tickets:N(t).tickets,waves:r,threads:i,working:await St(d),autoReview:N(t).auto_review,incoming:await j(w.join(S,"incoming.json"),null),reviewRecords:await j(w.join(S,"reviews.json"),{})}}u(ke,"itemSnapshot");async function be(t,e=[]){const a=await ke(t,e.length?await ui(e):void 0),{waves:n,threads:s,working:r}=a,o=Object.create(null),i=new Set(e.map(f=>f.key));await Promise.all(n.flatMap(({slug:f,wave:k})=>{const h=a.github||k.repo;return k.items.flatMap(v=>Oa(v,h).map(async O=>{o[Xt(O)]=i.has(`${f}/${v.id}`)?jt.get(Xt(O))?.value??null:await ci(O.repo,O.number)}))}));const g=new Set(["ask","owner-answer","grill-more","item-message","wave-triage","overnight-triage"]),y=await Promise.all((await Ot()).map(f=>g.has(f.kind)||F(r,f.id)?dt(f):{...f,reply:null}));await Promise.all(y.filter(f=>f.kind==="ask").map(async f=>{const k=await U(V(`ask:${f.id}`));k.length&&(s[`ask:${f.id}`]=k)}));const b=Object.create(null),$=Object.create(null);for(const{agents:f}of n)for(const k of Object.values(f||{}).flatMap(h=>Object.values(h))){const h=k.worktree?.path,v=h&&!k.done?Le(h):null;v!==null&&(b[h]=v),h&&!k.done&&We(h)&&($[h]=!0)}const R=Object.create(null),c=await j(w.join(S,"headless-builders.json"),{}),l=await j(w.join(S,"landers.json"),{});for(const[f,k]of[...Object.entries(l),...Object.entries(c)])Number.isInteger(k?.pid)&&(qt(k.pid)?R[f]=!0:k.handedBack?R[f]=!1:delete R[f]);return{...a,moved:b,commandRunning:$,alive:R,try:await j(w.join(S,"try.json"),null),main:await j(w.join(S,"main.json"),null),prs:o,events:y,askLinks:await vt(d),askProgress:await dn(d),checked:await j(w.join(S,"checked.json"),null),watcher:await j(w.join(S,"watcher.json"),null),replyAt:await li(),owner:(f=>({...f,items:[...f.items||[],...e.flatMap(k=>k.questions||[])]}))(await j(w.join(S,"owner.json"),{items:[]})),topicItems:t.topicItems}}u(be,"snapshot");async function di(t){if(!t.length)return[];const e=new Map(yt(await be(await $t(),t)).items.map(a=>[a.key,a]));return t.map(a=>{const n=e.get(a.key);return Qt({...n,sortAt:a.closed_at,worktree:n.worktree&&!Yt(n.worktree)?null:n.worktree})})}u(di,"archivedCards");const G=M.slice(1)||w.basename(d);let x=0,Q=null;async function E(){const t=await $t(),e=JSON.stringify([x,t]);if(Q?.key!==e){const a=be(t).then(yt);Q={key:e,built:a},a.catch(()=>{Q?.built===a&&(Q=null)})}return Q.built}u(E,"sharedBoard");let Pt=!1;async function ve(){if(!Pt){Pt=!0;try{const t=await U(W),e=new Set(t.filter(a=>a.kind==="prototype-overdue").map(a=>a.data?.request));for(const a of ra(await E(),t))e.has(a.request)||Date.now()-Date.parse(a.since)<ia||await B("prototype-overdue",a)}catch(t){console.error("Prototype check failed:",t.message)}finally{Pt=!1}}}u(ve,"checkPrototypeWaits");async function fi(t){const e=await pn(d);if(!e?.ping?.id||e.done_at)return null;const a=t?.find(s=>s.link==="github"),n={events:e.ack?.id===e.ping.id,...a||!t?{github:!!a?.ok}:{}};return n.events&&n.github!==!1?(await un(d),null):n}u(fi,"setupOf");const hi=Cn.map(([t,e])=>({value:t,label:e,ms:t===ne?null:ie(new Date(0),t)-new Date(0)}));async function pi(){const t={slack:null,github:[]},e=le();if(!e||zn().refused)t.slackProblem=ce;else try{t.slack=await e.people()}catch(n){if(!(n instanceof oe))throw n;t.slackProblem=n.message==="Slack: missing_scope"?"Slack needs the users:read scope; update the app on Hub settings \u203A Slack.":n.message}const a=z(d)?.github;if(a)try{t.github=await Vn(a)}catch(n){t.githubProblem=`GitHub: ${n.message}`}return t}u(pi,"reviewerPeople");async function wi(t){const e=le();if(!e)throw new P(ce);return Promise.all(t.map(async a=>{const n=await Qn(a.github.trim());if(!n)throw new P(`GitHub has no user ${a.github.trim()}.`);return{name:a.name.trim(),slack_user:a.slack_user.trim(),dm:await e.openDM(a.slack_user.trim()),github:n}}))}u(wi,"withDMs");async function X(){const t=new Date().toISOString(),{title:e,reviewers:a}=await D(),n=z(d),s=n?In(n):"deaf",r=ea(await E(),{listen:s,prompts:n?_n(n.route).map(Dn):[]});r.items=r.items.map(i=>i.worktree&&!Yt(i.worktree)?{...i,worktree:null}:i);const o=n?Nn(n,{listen:s}):null;return{generatedAt:t,title:e,reviewers:(a||[]).map(i=>i?.name).filter(i=>typeof i=="string"),reviewerGithub:Object.fromEntries((a||[]).filter(i=>typeof i?.name=="string").map(i=>[i.name,i.github])),...r,links:o,setup:await fi(o),holdChoices:hi,arch:n?Ke(await ze(d),r.items,r.owner,{repo:Ve(se(n.route)),waits:Wn()}):null}}u(X,"board");async function mi(t){["GET","HEAD"].includes(t.method)||x++;const e={...await X(),onHost:await Ge(t),doneArchived:await Ja(d)};e.owner=e.owner.filter(i=>i.state!=="resolved");const a=await gn(d);e.items=e.items.map(i=>({...i.lane==="done"?Qt(i):i,readAt:a[i.key]||null}));const n=new Set(e.items.map(i=>i.key));e.ownerTopics=e.ownerTopics.map(({title:i,summary:g,askedAt:y,...b})=>n.has(b.card)?b:{...b,title:i,summary:g,...y?{askedAt:y}:{}});const{generatedAt:s,...r}=e,o={...e,version:Kt.createHash("sha1").update(JSON.stringify(r)).digest("base64url").slice(0,20)};Y.delete(o.version),Y.set(o.version,o);for(const i of[...Y.keys()].slice(0,-gi))Y.delete(i);return o}u(mi,"pageBoard");const gi=8,Y=new Map,Se=rt.createContext({});rt.runInContext(await m.readFile(new URL("./public/ui-patch.js",import.meta.url),"utf8"),Se);const{boardPatch:yi}=Se.BoardUI,ki=3e3,ft=w.join(S,"since-counts.json"),Tt={since:new Date().toISOString(),patch:0,missed:0,none:0,restarts:0,...await j(ft,{})};Tt.restarts++;async function Oe(){await m.mkdir(S,{recursive:!0}),await m.writeFile(ft+".tmp",JSON.stringify(Tt)+`
5
+ `),await m.rename(ft+".tmp",ft)}u(Oe,"saveSinceCounts"),await Oe();let je=null;function bi(t){Tt[t]++,je??=setTimeout(()=>{je=null,Oe()},ki).unref()}u(bi,"countAnswer");async function C(t,e){if(new URL(t.url,"http://site").searchParams.get("board")==="0")return;const a=e&&Y.get(e);bi(a?"patch":e?"missed":"none");const n=await mi(t);return a?yi(a,n):n}u(C,"answerBoard");let $e=Promise.resolve();function vi(){const t=$e.then(async()=>{const e=z(d);if(!e)throw Object.assign(new P("This board isn\u2019t in the hub\u2019s manifest."),{status:404});const{arch:a}=await X();if(a.last?.open)return{key:a.last.key,ref:a.last.ref,existing:!0};const{key:n,data:s}=await Qe(d,e),r=await B("arch-review",s);return{key:n,id:r,existing:!1}});return $e=t.catch(()=>{}),t}u(vi,"runReview");async function Si(t){const[e,a]=t.split("/"),n=(await st(d)).find(i=>i.slug===e),s=n?await j(n.file,null):null,r=!s?.items?.some(i=>i.id===a)&&(await te(d)).find(i=>i.key===t),o=Array.isArray(s?.items)&&s.items.find(i=>i.id===a)||r?.item||null;return{item:o,questions:r?.questions||[],agents:o&&n?F(await ge(w.join(n.dir,"agents")),a)||{}:{}}}u(Si,"itemOf");async function Oi(t){const e=F(await vt(d),t),{tickets:a}=N(await D()),n=[];for(const s of await st(d)){const r=await j(s.file,null);for(const o of Array.isArray(r?.items)?r.items:[]){const i=`${s.slug}/${o.id}`;o.from_ask===t?n.unshift({key:i,ref:kt(r,o,a),became:!0}):i===e&&n.push({key:i,ref:kt(r,o,a),became:!1})}}return n}u(Oi,"askLinksOf");async function ji(t){const{item:e,agents:a,questions:n}=t.startsWith("ask:")?{item:null,agents:{},questions:[]}:await Si(t),s=await j(w.join(S,"owner.json"),{items:[]}),r={...s,items:[...s.items||[],...n]},o=new Set(r.items.filter(l=>l.topic===`item:${t}`).map(l=>l.id)),i=t.startsWith("ask:"),g=i?t.slice(4):e?.from_ask||null,y=i?[]:Object.entries(await vt(d)).filter(([,l])=>l===t).map(([l])=>l),b=[g,...y].filter(Boolean),$=new Set([t,...b.map(l=>`ask:${l}`)]),R=(await Ot()).filter(l=>$.has(l.data?.key)&&ca.includes(l.kind)||l.kind==="owner-answer"&&o.has(l.data?.id)||l.kind==="ask"&&b.includes(l.id)||l.kind==="ask-edited"),c=(await Promise.all([...$].map(l=>U(V(l))))).flat();return ya(t,{lines:c,events:await Promise.all(R.map(dt)),owner:r,working:await St(d),fromAsk:g,links:i?await Oi(g):[],agents:a,moved:y})}u(ji,"thread");const Pe=u(t=>String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e]),"esc");async function Te(t){for(const e of Yn){let a=w.resolve(e,"."+t);if(a!==e&&!a.startsWith(e+w.sep))return null;if((await m.stat(a).catch(()=>null))?.isDirectory()&&(a=w.join(a,"index.html")),await m.stat(a).then(s=>s.isFile(),()=>!1))return a}return null}u(Te,"staticFile");const Ae=w.join(L,"public","prototype.html");async function $i(t){const e=/^\/prototype-([a-z0-9-]+)\.html$/.exec(t)?.[1];return e&&(await cn(d)).find(a=>a.topic===e)||null}u($i,"recordedPrototype");const Ee=u(t=>JSON.stringify(t).replace(/</g,"\\u003c").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029"),"scriptJSON");async function Pi(t){const e=await Promise.all(t.takes.map(async r=>{const o=`prototypes/${t.topic}/${r.key}.js`,i=await m.stat(ln(d,t.topic,r.key)).catch(()=>null);return{key:r.key,label:r.label,src:i?.isFile()?`${o}?v=${lt(i)}`:null,file:o}})),a=`${t.title} \xB7 prototype`,n={lead:"back",backHref:t.card?`./#item=${encodeURIComponent(t.card)}`:"./",title:a,controls:!1},s={prototypeTitle:Pe(a),prototypeHeader:`<!--header ${Ee(n).replace(/>/g,"\\u003e")}-->`,prototypeData:Ee({topic:t.topic,title:t.title,takes:e})};return(await m.readFile(Ae,"utf8")).replace(/\{\{(prototypeTitle|prototypeHeader|prototypeData)\}\}/g,(r,o)=>s[o])}u(Pi,"prototypePage");async function Ti(t,e){try{const a=decodeURIComponent(String(t.headers["x-filename"]||""));return p(e,200,await Tn(d,t,{name:a,type:t.headers["content-type"],length:Number(t.headers["content-length"])||0}))}catch(a){if(!(a instanceof P)&&!(a instanceof URIError))throw a;return t.resume(),p(e,a.status||400,{error:a.message})}}u(Ti,"upload");const Ai=/^(text\/html|image\/svg\+xml|application\/(xhtml\+)?xml|text\/xml)$/;async function Ei(t,e,a){const n=await kn(d,a);if(!n){e.writeHead(404).end();return}if(e.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",...Ai.test(n.type)?{"content-security-policy":"sandbox"}:{}}),t.method==="HEAD"){e.end();return}Pa(n.path).pipe(e)}u(Ei,"sendUpload");const At=new Set;function Ri(t,e){e.writeHead(200,{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-store",connection:"keep-alive"}),e.write(`retry: 3000
6
6
 
7
- `),Pt.add(e),t.on("close",()=>Pt.delete(e))}u(Vn,"stream");const Se=u(t=>{for(const e of Pt)e.write(t)},"broadcast");setInterval(()=>Se(`: ping
7
+ `),At.add(e),t.on("close",()=>At.delete(e))}u(Ri,"stream");const Re=u(t=>{for(const e of At)e.write(t)},"broadcast");setInterval(()=>Re(`: ping
8
8
 
9
- `),25e3).unref();const Qn=100,Xn=500;let lt=null,ve=0;function Et(){I++;const t=Date.now();lt||(ve=t),clearTimeout(lt),lt=setTimeout(()=>{lt=null,Se(`event: change
9
+ `),25e3).unref();const xi=100,Ii=500;let ht=null,xe=0;function Et(){x++;const t=Date.now();ht||(xe=t),clearTimeout(ht),ht=setTimeout(()=>{ht=null,Re(`event: change
10
10
  data: ${JSON.stringify({at:new Date().toISOString()})}
11
11
 
12
- `),Te(),Ot(),Ct(),Bt(),dt(),Jt()},Math.max(0,Math.min(Qn,ve+Xn-t)))}u(Et,"changed");const Yn=new Set(["node_modules","dd","DerivedData"]),ut=u(t=>String(t).endsWith(".tmp")||String(t).split(/[\\/]/).some(e=>e.startsWith(".")||Yn.has(e)),"ignored"),$t=u(t=>{try{return pa(t).isDirectory()}catch{return!1}},"isFolder");function Zn(t){let e=u(()=>{},"stop"),n=!1,a=null;const r=u(()=>{a||(e(),n=!0,a=setTimeout(()=>{a=null,s()},2e3).unref())},"retry"),s=u(()=>{try{e=(process.platform==="linux"?ti:qn)(t,r)}catch{r();return}n&&Et()},"start");s()}u(Zn,"watchTree");function qn(t,e){const n=Qt(t,{recursive:!0},(a,r)=>{(!r||!ut(r))&&Et(),$t(t)||e()});return n.on("error",e),()=>n.close()}u(qn,"watchRecursive");function ti(t,e){const n=new Map,a=u(s=>{for(const[i,o]of n)(i===s||i.startsWith(s+f.sep))&&(o.close(),n.delete(i))},"unwatch"),r=u(s=>{if(n.has(s))return;const i=Qt(s,(o,g)=>{if((!g||!ut(g))&&Et(),s===t&&!$t(t))return e();if(!g||ut(g))return;const k=f.join(s,String(g));if(!$t(k))a(k);else try{r(k)}catch{a(k)}});n.set(s,i),i.on("error",()=>s===t?e():a(s));for(const o of wa(s,{withFileTypes:!0}))if(!(!o.isDirectory()||ut(o.name)))try{r(f.join(s,o.name))}catch{a(f.join(s,o.name))}},"watchFolder");try{r(t)}catch(s){throw a(t),s}return()=>a(t)}u(ti,"watchEachFolder");async function ei(){const t=(await R()).items==="waves"?[an()]:(await at(d)).map(n=>n.dir),e=[...new Set([S,...t].map(n=>f.resolve(n)))];for(const n of e.filter(a=>!e.some(r=>r!==a&&a.startsWith(r+f.sep))))Zn(n)}u(ei,"watchBoard");const ai=He.createServer(async(t,e)=>{try{if(t.method!=="GET"&&t.method!=="HEAD"&&I++,t.method!=="GET"&&t.method!=="HEAD"&&hn(t.headers)){e.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}),e.end("Requests must come from the app\u2019s own pages.");return}const n=new URL(t.url,"http://site");let a=n.pathname;if(H&&(a===H||a.startsWith(H+"/"))&&(a=a.slice(H.length)||"/"),a==="/_claude/health")return w(e,200,{ok:!0});if(a==="/api/board"){const c=await D(t,String(n.searchParams.get("since")||"")),l=`W/"${c.version}"`;if(oe(t.headers["if-none-match"],l)){e.writeHead(304,{etag:l,"cache-control":"no-store",vary:"accept-encoding"}).end();return}return w(e,200,c,{etag:l})}if(a==="/api/questions"){const c=await $(),l=new Set(c.ownerTopics.find(p=>p.id===String(n.searchParams.get("topic")??""))?.questions||[]);return w(e,200,{questions:c.owner.filter(p=>l.has(p.id)&&p.state==="resolved")})}if(a==="/api/done"){const c=String(n.searchParams.get("before")||""),l=(await Xt(d)).filter(p=>!c||Pa(p)<c);return w(e,200,{cards:await _n(l.slice(0,Gt)),left:Math.max(0,l.length-Gt)})}if(a==="/api/links")return w(e,200,{links:(await K()).links});if(a==="/api/stream"&&t.method==="GET")return Vn(t,e);if(a==="/_claude/needs")return w(e,200,Ye(await K()));if(a==="/api/working"){const c=await $(),l=new Map(c.items.filter(p=>p.kind==="work").map(p=>[p.key,p.ref]));return w(e,200,{working:c.workingOn.map(p=>({id:p.id,kind:p.kind,card:l.get(p.key)||null,since:p.since}))})}if(a==="/api/thread")return w(e,200,await Un(String(n.searchParams.get("key")||"")));if(a==="/api/notes"&&n.searchParams.has("wave"))try{return w(e,200,await za(d,String(n.searchParams.get("wave")),T(await R()).tickets))}catch(c){if(c instanceof E)return w(e,404,{error:c.message});throw c}if(a==="/api/notes"||a==="/api/plan"){const c=String(n.searchParams.get("item")||"");try{const{wave:l,item:p,markdown:O}=await Va(d,c);return w(e,200,{key:c,ref:pt(l,p,T(await R()).tickets),title:p.title,markdown:O})}catch(l){if(l instanceof E)return w(e,404,{error:l.message});throw l}}if(a==="/_claude/events"&&t.method==="POST"){const c=await B(t,524288),l=String(c.kind||"message").slice(0,64);delete c.kind;const p=String(c.since||"");if(delete c.since,l==="card-hold"&&c.hold&&c.for!==void 0){const h=te(new Date,c.for);if(!h)return w(e,400,{error:"That hold ends in the past or more than 30 days on. Pick another time."});delete c.for,h!==qt&&(c.until=h.toISOString())}if(l==="card-build"){const h=String(c.key||""),v=await Yt(d,h,P=>ft(d,h.split("/")[1],P));return w(e,200,{id:await xt(v),board:await D(t,p)})}if(l==="lineup-move"){const h=String(c.lineup||""),v=Xe(await $(),h,String(c.key||""),c.by===-1?-1:c.by===1?1:0);if(!v)return w(e,400,{error:`That card can't move that way in the wave "${h}".`});try{await $a(d,h,{phases:v.map(P=>P.join(","))})}catch(P){if(P instanceof E)return w(e,400,{error:P.message});throw P}return w(e,200,{id:null,board:await D(t,p)})}if(l==="owner-answer")return w(e,200,{id:(await ce([c]))[0],board:await D(t,p)});if(l==="owner-answers"){const h=await On(c.answers);return h?w(e,400,{error:h}):w(e,200,{ids:await ce(c.answers),board:await D(t,p)})}if(l==="card-merge"&&await ka(d,String(c.key||"")).catch(()=>null)){const h=String(c.key);try{await va(d,h)}catch(v){if(v instanceof E)return w(e,400,{error:v.message});throw v}return ri(h),w(e,200,{id:null,board:await D(t,p)})}if(l==="card-merge"){try{await Ba(d,String(c.key||""),{tapped:!0})}catch(h){if(h instanceof E)return w(e,400,{error:h.message});throw h}return w(e,200,{id:null,board:await D(t,p)})}if(l==="card-hold"||l==="card-done"){const h=String(c.key||"");let v;try{v=await Ha(d,l,c)}catch(P){return w(e,P instanceof E?400:500,{error:`Couldn\u2019t write ${h} to its wave file: ${P.message}`})}if(!v)return w(e,400,{error:`${h} is already merged or dropped, so its wave file stays as it is.`})}if(l==="reviewers"){const h=u(C=>C===void 0?[]:Array.isArray(C)&&C.every(q=>typeof q=="string"&&q.trim())?C.map(q=>q.trim()):null,"names"),v=h(c.add),P=h(c.remove);if(!v||!P||!v.length&&!P.length)return w(e,400,{error:"Send the names to add or remove, each as a list."});if(P.length)try{await Xa(d,P)}catch(C){if(C instanceof E)return w(e,400,{error:C.message});throw C}const Ae=await ot([...P.length?[["reviewers-removed",{remove:P}]]:[],...v.length?[["reviewers",{add:v}]]:[]]);return w(e,200,{id:Ae.at(-1),board:await D(t,p)})}if(l==="reask-request"){const h=await jn(String(c.question||""));if(h)return w(e,400,{error:h})}const O=await x(l,c);if((l==="grill-more"||l==="prototype-request")&&c.key){const h={...c.text?{text:String(c.text)}:{},...Array.isArray(c.files)&&c.files.length?{files:c.files}:{},...c.question?{question:String(c.question),note:c.note?String(c.note):void 0,choice:c.choice??void 0}:{}};await Oe(String(c.key),l==="grill-more"?"grill":"prototype",h)}return l==="ask-withdrawn"&&await Zt(d,String(c.id||""),!0),w(e,200,{id:O,board:await D(t,p)})}if(a==="/api/rules"&&t.method==="GET")return w(e,200,{board:await La(d),hub:rn()});if(a==="/api/rules"&&t.method==="POST"){const c=await B(t);let l;try{l=await Za(d,c.rules,c.base??[])}catch(O){if(O instanceof E)return w(e,400,{error:O.message});throw O}const p=nn(l.changes)?await x("rules",{scope:"board",...l.changes}):null;return w(e,200,{rules:l.rules,id:p})}if(a==="/api/settings"&&t.method==="GET")return w(e,200,{settings:await Wa(d)});if(a==="/api/permissions"){const c=nt(d);if(!c)return w(e,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const l=ee(c.route).repo,p=t.method==="POST"?await B(t):null;try{return p&&await gn(l,d,p.id,p.on),w(e,200,await mn(l,d))}catch(O){if(O instanceof E)return w(e,400,{error:O.message});throw O}}if(a==="/api/try/main/log"&&t.method==="GET"){let c;try{c=await m.open(f.join(d,"data","try","main.log"),"r");const{size:l}=await c.stat(),p=Math.max(0,l-64*1024),O=Buffer.alloc(l-p),{bytesRead:h}=await c.read(O,0,O.length,p),v=O.subarray(0,h).toString("utf8").split(/\r?\n/);return p&&v.shift(),v.at(-1)===""&&v.pop(),w(e,200,{lines:v.slice(-40)})}catch(l){if(l.code==="ENOENT")return w(e,200,{lines:[]});throw l}finally{await c?.close()}}if(a==="/api/log"&&t.method==="GET"){const c=n.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(c)||c.startsWith("."))return w(e,400,{error:"file takes a log file\u2019s name."});let l;try{l=await m.open(f.join(ht.homedir(),".claude","kickflip","logs",c),"r");const{size:p}=await l.stat(),O=Math.max(0,p-256*1024),h=Buffer.alloc(p-O),{bytesRead:v}=await l.read(h,0,h.length,O),P=h.subarray(0,v).toString("utf8");return w(e,200,{text:O?P.slice(P.indexOf(`
13
- `)+1):P,truncated:O>0,size:p})}catch(p){if(p.code==="ENOENT")return w(e,404,{error:"No such log."});throw p}finally{await l?.close()}}if(a==="/api/try/main/pull"&&t.method==="POST"){const c=await B(t);if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).length)return w(e,400,{error:"Send an empty object."});try{const l=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:H.slice(1)||f.basename(d)}),signal:AbortSignal.timeout(3e4)});return w(e,l.status,await l.json())}catch(l){return w(e,502,{error:`Could not pull: ${l.message}`})}}if(a==="/api/try"&&t.method==="POST"){const c=await B(t);if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).some(l=>l!=="key")||typeof c.key!="string")return w(e,400,{error:"Send only the card key."});try{const l=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:H.slice(1)||f.basename(d),key:c.key}),signal:AbortSignal.timeout(1e4)});return w(e,l.status,await l.json())}catch(l){return w(e,502,{error:`Could not start the build: ${l.message}`})}}if(a==="/api/settings"&&t.method==="POST"){const c=await B(t);let l;try{l=await qa(d,c)}catch(h){if(h instanceof E)return w(e,400,{error:h.message});throw h}const p=l.changed.filter(h=>h!=="idle_notification"),O=p.length?await x("settings",Object.fromEntries(p.map(h=>[h,l.settings[h]]))):null;return l.changed.includes("auto_merge")&&l.settings.auto_merge&&await x("check-github",{reason:"auto-merge"}),w(e,200,{settings:l.settings,id:O})}if(a==="/api/arch-review"&&t.method==="POST"){await B(t);try{return w(e,200,await Fn())}catch(c){if(c instanceof E)return w(e,c.status||400,{error:c.message});throw c}}if(a==="/api/hide"&&t.method==="POST"){const c=await B(t);try{await Zt(d,String(c.id||""),c.hidden!==!1)}catch(l){if(l instanceof E)return w(e,400,{error:l.message});throw l}return w(e,200,{ok:!0})}if(a==="/_claude/events"&&t.method==="GET")return w(e,200,await Pn(n.searchParams.get("kind"),Math.min(500,Number(n.searchParams.get("limit")||100))));if(a==="/api/upload"&&t.method==="POST")return await Gn(t,e);if(t.method!=="GET"&&t.method!=="HEAD"){e.writeHead(405).end();return}if(a.startsWith("/uploads/"))return await zn(t,e,decodeURIComponent(a.slice(9)));if(a==="/plan.html"){e.writeHead(302,{location:`notes.html${n.search}`,"cache-control":"no-store"}).end();return}if(a==="/index.html"){e.writeHead(302,{location:`./${n.search}`,"cache-control":"no-store"}).end();return}const r=dn.has(a.slice(1)),s=r?null:await Ln(a),i=r?null:s?ye:a==="/prototype.html"?null:await ge(decodeURIComponent(a));if(!r&&!i){e.writeHead(404).end();return}const o=r?".html":f.extname(i).toLowerCase(),g=r?null:await m.stat(i);let k=r?Buffer.from(fn({touchIcon:await ae()})):s?Buffer.from(await Wn(s)):await m.readFile(i);!r&&o===".html"&&(k=(await yn(k.toString("utf8"))).replaceAll("{{title}}",me((await R()).title)).replaceAll("{{touchIcon}}",await ae())),!r&&o===".html"&&(k=Buffer.from(await bn(k,a)));const y=o===".html"?`W/"${Lt.createHash("sha1").update(k).digest("base64url").slice(0,20)}"`:`W/"${st(g)}"`,j=o!==".html"&&n.searchParams.get("v")===st(g),A={"content-type":N(ie,o)||"application/octet-stream","cache-control":j?"public, max-age=31536000, immutable":"no-cache",etag:y};if(oe(t.headers["if-none-match"],y)){e.writeHead(304,{etag:y,"cache-control":A["cache-control"],vary:"accept-encoding"}).end();return}return await re(e,200,A,k)}catch(n){e.headersSent||(n.status===403?e.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}).end(n.message):e.writeHead(n.status||(n.code==="ENOENT"?404:400)).end())}});let Tt=!1;async function be(){if(!Tt){Tt=!0;try{const t=Date.now(),e=(await $()).items.filter(a=>a.heldUntil&&a.lane!=="done"&&Date.parse(a.heldUntil)<=t),n=e.length?await _(F):[];for(const{key:a,heldUntil:r}of e)await Da(d,a,r,{announce:!n.some(s=>s.kind==="card-unblocked"&&s.data?.key===a&&s.data?.until===r)})}catch(t){console.error("Pick-up check failed:",t.message)}finally{Tt=!1}}}u(be,"checkPickUps");let V=null,At=!1,U=!1;function dt(){return V?(At=!0,V):(V=(async()=>{do{At=!1;try{await ni()}catch(t){console.error("Auto check failed:",t.message)}}while(At)})().finally(()=>{V=null}),V)}u(dt,"checkAuto");async function ni(){const t=T(await R()),e=await _(F),{items:n,reviews:a}=await $(),{idle:r,pullable:s,waiting:i,starts:o,pulls:g,plannable:k}=ta(n,{cap:t.max_implementers,pulled:await It(_t)});U=it().state==="paused";const y=r&&!U;for(const h of U?[]:o)await xt(await Ca(d,h,v=>ft(d,h.split("/")[1],v)));if(U||await si(n.some(h=>h.status==="checking")),await oi(t,e,n,y))return;const j=u(async h=>xt(await Yt(d,h,v=>ft(d,h.split("/")[1],v),"hub")),"startPull");if(await $e(_t,t.auto_pull,i,U?[]:g,j))return;const A=s.length?[]:k,c=(await It(Pe)).filter(h=>A.includes(h)),l=u(async h=>Oe(h,n.find(v=>v.key===h)?.state==="prototyping"?"prototype":"grill"),"startPlan");if(await $e(Pe,t.auto_pull,c,y&&!c.length?A.slice(0,1):[],l))return;const{due:p,repo:O}=a;y&&p&&await x("pr-review",{pr:p.number,head:p.head,repo:O,title:p.title,author:p.author,url:p.url,touches:p.touches,asked:!!p.asked})}u(ni,"runAuto");async function xt(t){if(I++,!t||t.queued)return null;const e={kind:t.kind,model:t.model,relaunched:!1};try{it().state==="paused"?await Rt(t.key,{...e,pid:null}):await Rt(t.key,{...e,...await et(d,t)})}catch(n){await Ta(d,t.key,`the launch failed (${n.message}). Tap Implement to try again.`)}return null}u(xt,"handOff");async function Oe(t,e,n={}){try{const a=await _a(d,t,e);if(!a)return;try{await et(d,{...a,...n})}catch(r){await Aa(d,t,`Couldn't launch the ${e} (${r.message}).`)}}catch(a){if(!(a instanceof E))throw a;await L(d,t,{text:`Couldn't start the ${e}: ${a.message}`})}}u(Oe,"launchPlanner");async function Rt(t,e){const n=f.join(S,"headless-builders.json"),a=await b(n,{});e?a[t]=e:delete a[t],await m.mkdir(S,{recursive:!0}),await m.writeFile(n+".tmp",JSON.stringify(a)+`
14
- `),await m.rename(n+".tmp",n)}u(Rt,"trackLaunch");let Nt=!1;async function ii(){if(!Nt){Nt=!0;try{if(it().state==="paused")return;const t=f.join(S,"headless-builders.json"),e=await b(t,{});if(!Object.keys(e).length)return;const n=new Set(wt(await de(await vt())).items.filter(i=>i.lane!=="done"&&i.builder&&!i.builder.planner).map(i=>i.key)),s=await ya(e,n,{launch:u(async i=>{try{return await et(d,i)}catch(o){return await L(d,i.key,{text:`Couldn't relaunch the builder headless (${o.message}).`}),{pid:null}}},"launch"),handBack:u(async i=>{await L(d,i.key,{text:"Its builder exited twice with the build unfinished; it reads Stalled once 30 minutes pass without progress."})},"handBack")});JSON.stringify(s)!==JSON.stringify(e)&&(await m.writeFile(t+".tmp",JSON.stringify(s)+`
15
- `),await m.rename(t+".tmp",t))}catch(t){console.error("Headless builder check failed:",t.message)}finally{Nt=!1}}}u(ii,"reviveHeadless");function ri(t){const e=f.join(ht.homedir(),".claude","kickflip","logs");fa(e,{recursive:!0});const n=ha(f.join(e,`merge-${t.split("/")[1]}-${new Date().toISOString().replace(/[:.]/g,"-")}.log`),"a"),a=process.env.KICKFLIP_MERGE_CMD?JSON.parse(process.env.KICKFLIP_MERGE_CMD):[process.execPath,ma],r=Be(a[0],[...a.slice(1),"--site",M,"merge",t],{detached:!0,stdio:["ignore",n,n]});ua(n),r.on("error",s=>L(d,t,{text:`Not merged: the land couldn't start (${s.message}). Tap Merge again.`}).catch(()=>{})),r.unref()}u(ri,"runHeldMerge");async function si(t){const e=f.join(S,"landers.json"),n=Object.fromEntries(Object.entries(await b(e,{})).map(([i,o])=>[i,typeof o=="string"?{verified:o,pid:null}:o])),a=new Set;for(const[i,o]of Object.entries(n))if(!(!o.pid||ga(o.pid)))try{await Ra(d,i),a.add(i)}catch(g){if(!(g instanceof E))throw g}const r=Object.fromEntries(Object.entries(n).filter(([i])=>!a.has(i)));if(!t&&!a.size)return;const s=(await xa(d)).filter(i=>r[i.key]?.verified!==i.verified);if(s.length){for(const i of s)try{r[i.key]={verified:i.verified,pid:(await et(d,i,{lander:!0})).pid},await Rt(i.key,null)}catch(o){r[i.key]={verified:i.verified,pid:null},await L(d,i.key,{text:`Couldn't launch the lander headless (${o.message}); land it with board.mjs land ${i.key}.`})}await m.writeFile(e+".tmp",JSON.stringify(r)+`
16
- `),await m.rename(e+".tmp",e)}}u(si,"launchLanders");async function je(){const t=sa(new Date,oa(await b(f.join(ht.homedir(),".claude","kickflip","settings.json"),{})));return t&&ra(t)}u(je,"triageNight");async function oi(t,e,n,a){const r=f.join(S,"triaged.json"),s=await b(r,{}),{waiting:i,seen:o}=ia(n),g=u(async()=>{await m.mkdir(S,{recursive:!0}),await m.writeFile(r+".tmp",JSON.stringify({seen:o})+`
17
- `),await m.rename(r+".tmp",r)},"save"),k=e.filter(c=>c.kind==="overnight-triage").at(-1);if(k&&!(await ct(k)).reply)return s.seen!==o&&await g(),!0;if(!t.auto_triage||!a||i.length<2||o===s.seen)return!1;const y=await je();if(!y||k?.data?.date===y)return!1;const j=`overnight-triage-${y}`;return((await b(f.join(S,"owner.json"),{topics:[]})).topics||[]).some(c=>c.id===j)||await ba(d,{id:j,title:`Overnight triage ${y}`}),await x("overnight-triage",{date:y,topic:j,cards:i}),await g(),!0}u(oi,"checkTriage");async function ci(){if(!T(await R()).auto_triage)return!1;const t=await je();return!!t&&(await _(F)).filter(e=>e.kind==="overnight-triage").at(-1)?.data?.date!==t}u(ci,"triageDue");const _t={file:"pulled.json",line:"Pulled in: an implementer slot was free."},Pe={file:"planned.json",line:"Asked for a grill or prototype: nothing in Todo was ready to build."},Ee=u(t=>t.keys||(t.key?[t.key]:[]),"markedOf"),It=u(async({file:t})=>Ee(await b(f.join(S,t),{})),"sentOf");async function $e({file:t,line:e},n,a,r,s){const i=f.join(S,t),o=n?[...a,...r]:[];for(const g of n?r:[])await m.mkdir(f.dirname(W(g)),{recursive:!0}),await m.appendFile(W(g),JSON.stringify({ts:new Date().toISOString(),who:"claude",text:e})+`
18
- `),await s(g);return JSON.stringify(Ee(await b(i,{})))!==JSON.stringify(o)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(i+".tmp",JSON.stringify({keys:o,at:new Date().toISOString()})+`
19
- `),await m.rename(i+".tmp",i)),o.length>0}u($e,"sendPulls");let Q=null,Dt=!1;function Ct(){return Q?(Dt=!0,Q):(Q=(async()=>{do{Dt=!1;try{await li()}catch(t){console.error("Action push check failed:",t.message)}}while(Dt)})().finally(()=>{Q=null}),Q)}u(Ct,"checkActions");async function li(){const t=f.join(S,"action-pushes.json"),e=await b(t,null),n=Ze(await $()),a=Object.fromEntries(n.map(r=>[r.tag,{kind:r.kind,ids:r.questions.map(s=>s.id)}]));if(e){for(const s of n){const i=N(e,s.tag),o=s.questions.filter(k=>!i?.ids?.includes(k.id));if(i?.kind===s.kind&&!o.length)continue;const g=o.length?`questions.html#${encodeURIComponent(o[0].id)}`:s.url;try{await mt(d,{kind:"action",title:s.title,body:o[0]?.question||s.body,url:`/${M}/${g}`,tag:s.tag,now:s.now})}catch(k){if(k instanceof E)console.error(`Push ${s.tag} not sent:`,k.message.split(`
20
- `).at(-2)||k.message);else throw k}}const r=Object.keys(e).filter(s=>!N(a,s));r.length&&await la(r,M)}JSON.stringify(a)!==JSON.stringify(e)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify(a)+`
21
- `),await m.rename(t+".tmp",t))}u(li,"pushActions");let X=null,Ht=!1;function Bt(){return X?(Ht=!0,X):(X=(async()=>{do{Ht=!1;try{await ui()}catch(t){console.error("Orchestrator wait check failed:",t.message)}}while(Ht)})().finally(()=>{X=null}),X)}u(Bt,"checkWaits");async function ui(){const t=f.join(S,"orchestrator-waits.json"),e=await b(t,null),n=e?.waits||[],a=u(i=>n.find(o=>o.key===i.key&&o.kind===i.kind&&o.since===i.since),"was"),r=[],s=[];for(const i of We(await $())){let o=a(i)?.pushed===!0;if(!i.picked&&!a(i)&&r.push(i.key),!i.picked&&!o&&Date.now()-Date.parse(i.since)>=Le){o=!0;try{await mt(d,{kind:"reminder",title:`${i.ref} waits on the orchestrator`,body:`${i.title}: ${i.next}`,url:`/${M}/#item=${encodeURIComponent(i.key)}`,tag:`wait-${i.key}`,now:!0})}catch(g){if(g instanceof E)console.error(`Wait push for ${i.key} not sent:`,g.message.split(`
22
- `).at(-2)||g.message);else throw g}}s.push(o?{...i,pushed:o}:i)}(!e||JSON.stringify(s)!==JSON.stringify(n))&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify({waits:s})+`
23
- `),await m.rename(t+".tmp",t)),r.length&&await x("orchestrator-waits",{added:r})}u(ui,"stepWaits");let Y=null,Ft=!1;function Jt(){return Y?(Ft=!0,Y):(Y=(async()=>{do{Ft=!1;try{await di()}catch(t){console.error("Idle check failed:",t.message)}}while(Ft)})().finally(()=>{Y=null}),Y)}u(Jt,"checkIdle");async function di(){const t=f.join(S,"idle.json"),e=await R();if(!T(e).idle_notification)return m.rm(t,{force:!0});const n=await b(t,{}),a=await $(),r=aa(a,{pulled:await It(_t)}),{state:s,push:i}=ze(n,{idle:r,now:new Date});if(i)try{await mt(d,{kind:"reminder",title:`${e.title||M} has gone idle`,body:Ve(a.items,n.last),url:`/${M}/`,tag:"idle",now:!0})}catch(o){if(o instanceof E)console.error("Idle push not sent:",o.message.split(`
24
- `).at(-2)||o.message);else throw o}JSON.stringify(s)!==JSON.stringify(n)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify(s)+`
25
- `),await m.rename(t+".tmp",t))}u(di,"stepIdle");let Z=null,Mt=!1;function Te(){return Z?(Mt=!0,Z):(Z=(async()=>{do{Mt=!1;try{await fi()}catch(t){console.error("Done archive failed:",t.message)}}while(Mt)})().finally(()=>{Z=null}),Z)}u(Te,"checkArchive");async function fi(){if(!Wt(await $()).length)return;const t=Wt(await K()),e=t.length?await Oa(d,t.map(n=>({key:n.key,closed_at:n.sortAt}))):[];e.length&&console.log(`Done archive: ${e.length} card${e.length===1?"":"s"} moved`)}u(fi,"archiveDone");try{for(const t of await Na(d))console.log(t)}catch(t){console.error("follow-up migration:",t.message)}try{await Ia(d)}catch(t){console.error("dead-field strip:",t.message)}await Te(),await ei(),setInterval(()=>{I++,Ot(),he(),ii(),be(),U&&it().state!=="paused"?dt():ci().then(t=>{t&&dt()},t=>console.error("Triage check failed:",t.message)),Ct(),Bt(),Jt()},6e4).unref(),await Ot(),await he(),await be(),await dt(),Ct(),Bt(),Jt(),ai.listen(ne,"127.0.0.1",()=>console.log(`site on http://127.0.0.1:${ne}/ (prefix ${H||"none"})`));
12
+ `),He(),Be(),Ft(),Ut(),wt(),Lt()},Math.max(0,Math.min(xi,xe+Ii-t)))}u(Et,"changed");const Ni=new Set(["node_modules","dd","DerivedData"]),pt=u(t=>String(t).endsWith(".tmp")||String(t).split(/[\\/]/).some(e=>e.startsWith(".")||Ni.has(e)),"ignored"),Rt=u(t=>{try{return Ra(t).isDirectory()}catch{return!1}},"isFolder");function _i(t){let e=u(()=>{},"stop"),a=!1,n=null;const s=u(()=>{n||(e(),a=!0,n=setTimeout(()=>{n=null,r()},2e3).unref())},"retry"),r=u(()=>{try{e=(process.platform==="linux"?Ci:Di)(t,s)}catch{s();return}a&&Et()},"start");r()}u(_i,"watchTree");function Di(t,e){const a=Zt(t,{recursive:!0},(n,s)=>{(!s||!pt(s))&&Et(),Rt(t)||e()});return a.on("error",e),()=>a.close()}u(Di,"watchRecursive");function Ci(t,e){const a=new Map,n=u(r=>{for(const[o,i]of a)(o===r||o.startsWith(r+w.sep))&&(i.close(),a.delete(o))},"unwatch"),s=u(r=>{if(a.has(r))return;const o=Zt(r,(i,g)=>{if((!g||!pt(g))&&Et(),r===t&&!Rt(t))return e();if(!g||pt(g))return;const y=w.join(r,String(g));if(!Rt(y))n(y);else try{s(y)}catch{n(y)}});a.set(r,o),o.on("error",()=>r===t?e():n(r));for(const i of Ea(r,{withFileTypes:!0}))if(!(!i.isDirectory()||pt(i.name)))try{s(w.join(r,i.name))}catch{n(w.join(r,i.name))}},"watchFolder");try{s(t)}catch(r){throw n(t),r}return()=>n(t)}u(Ci,"watchEachFolder");async function Hi(){const t=(await D()).items==="waves"?[En()]:(await st(d)).map(a=>a.dir),e=[...new Set([S,...t].map(a=>w.resolve(a)))];for(const a of e.filter(n=>!e.some(s=>s!==n&&n.startsWith(s+w.sep))))_i(a)}u(Hi,"watchBoard");const Bi=Xe.createServer(async(t,e)=>{try{if(t.method!=="GET"&&t.method!=="HEAD"&&x++,t.method!=="GET"&&t.method!=="HEAD"&&Fn(t.headers)){e.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}),e.end("Requests must come from the app\u2019s own pages.");return}const a=new URL(t.url,"http://site");let n=a.pathname;if(M&&(n===M||n.startsWith(M+"/"))&&(n=n.slice(M.length)||"/"),n==="/_claude/health")return p(e,200,{ok:!0});if(n==="/api/board"){const c=await C(t,String(a.searchParams.get("since")||"")),l=`W/"${c.version}"`;if(we(t.headers["if-none-match"],l)){e.writeHead(304,{etag:l,"cache-control":"no-store",vary:"accept-encoding"}).end();return}return p(e,200,c,{etag:l})}if(n==="/api/reviewer-people")return p(e,200,await pi());if(n==="/api/questions"){const c=await E(),l=new Set(c.ownerTopics.find(f=>f.id===String(a.searchParams.get("topic")??""))?.questions||[]);return p(e,200,{questions:c.owner.filter(f=>l.has(f.id)&&f.state==="resolved")})}if(n==="/api/done"){const c=String(a.searchParams.get("before")||""),l=(await te(d)).filter(f=>!c||La(f)<c);return p(e,200,{cards:await di(l.slice(0,Vt)),left:Math.max(0,l.length-Vt)})}if(n==="/api/links")return p(e,200,{links:(await X()).links});if(n==="/api/stream"&&t.method==="GET")return Ri(t,e);if(n==="/_claude/needs")return p(e,200,ua(await X()));if(n==="/api/working"){const c=await E(),l=new Map(c.items.filter(f=>f.kind==="work").map(f=>[f.key,f.ref]));return p(e,200,{working:c.workingOn.map(f=>({id:f.id,kind:f.kind,card:l.get(f.key)||null,since:f.since}))})}if(n==="/api/thread")return p(e,200,await ji(String(a.searchParams.get("key")||"")));if(n==="/api/notes"&&a.searchParams.has("wave"))try{return p(e,200,await mn(d,String(a.searchParams.get("wave")),N(await D()).tickets))}catch(c){if(c instanceof P)return p(e,404,{error:c.message});throw c}if(n==="/api/notes"||n==="/api/plan"){const c=String(a.searchParams.get("item")||"");try{const{wave:l,item:f,markdown:k}=await yn(d,c);return p(e,200,{key:c,ref:kt(l,f,N(await D()).tickets),title:f.title,markdown:k})}catch(l){if(l instanceof P)return p(e,404,{error:l.message});throw l}}if(n==="/_claude/events"&&t.method==="POST"){const c=await _(t,524288),l=String(c.kind||"message").slice(0,64);delete c.kind;const f=String(c.since||"");if(delete c.since,l==="card-hold"&&c.hold&&c.for!==void 0){const h=ie(new Date,c.for);if(!h)return p(e,400,{error:"That hold ends in the past or more than 30 days on. Pick another time."});delete c.for,h!==ne&&(c.until=h.toISOString())}if(l==="card-build"){const h=String(c.key||""),v=await bt(d,h,O=>it(d,h.split("/")[1],O));return p(e,200,{id:await q(v),board:await C(t,f)})}if(l==="item-message"&&c.intent==="build"){const h=String(c.key||""),v=(await E()).items.find(I=>I.key===h&&I.kind==="work"),O=!String(c.text||"").trim()&&!(Array.isArray(c.files)&&c.files.length);if(v?.ready&&O){const I=await bt(d,h,mt=>it(d,h.split("/")[1],mt));return p(e,200,{id:await q(I),board:await C(t,f)})}v?.lane==="todo"&&(await Ca(d,h),x++)}if(l==="lineup-move"){const h=String(c.lineup||""),v=la(await E(),h,String(c.key||""),c.by===-1?-1:c.by===1?1:0);if(!v)return p(e,400,{error:`That card can't move that way in the wave "${h}".`});try{await Ga(d,h,{phases:v.map(O=>O.join(","))})}catch(O){if(O instanceof P)return p(e,400,{error:O.message});throw O}return p(e,200,{id:null,board:await C(t,f)})}if(l==="owner-answer")return p(e,200,{id:(await me([c]))[0],board:await C(t,f)});if(l==="owner-answers"){const h=await ei(c.answers);return h?p(e,400,{error:h}):p(e,200,{ids:await me(c.answers),board:await C(t,f)})}if(l==="card-merge"&&await Ha(d,String(c.key||"")).catch(()=>null)){const h=String(c.key);try{await Fa(d,h)}catch(v){if(v instanceof P)return p(e,400,{error:v.message});throw v}return Li(h),p(e,200,{id:null,board:await C(t,f)})}if(l==="card-merge"){try{await on(d,String(c.key||""),{tapped:!0})}catch(h){if(h instanceof P)return p(e,400,{error:h.message});throw h}return p(e,200,{id:null,board:await C(t,f)})}if(l==="card-hold"||l==="card-done"){const h=String(c.key||"");let v,O;try{O=l==="card-hold"&&!c.hold?[]:await _a(d,h),l==="card-done"&&await Da(d,h)&&(O=[]),v=await sn(d,l,c)}catch(I){return p(e,I instanceof P?400:500,{error:`Couldn\u2019t write ${h} to its wave file: ${I.message}`})}if(!v)return p(e,400,{error:`${h} is already merged or dropped, so its wave file stays as it is.`});O.length&&(Na(O),await H(d,h,{text:"Stopped its builder.",who:"love"}))}if(l==="reviewers"){const h=u(T=>T===void 0?[]:Array.isArray(T)?T:null,"list"),v=h(c.add),O=h(c.remove),I=u(T=>typeof T=="string"&&T.trim(),"text");if(!v||!O||!v.length&&!O.length)return p(e,400,{error:"Send the people to add and the names to remove, each as a list."});if(!O.every(I)||!v.every(T=>I(T?.name)&&I(T.slack_user)&&I(T.github)))return p(e,400,{error:"Each person to add needs a name, a Slack member and a GitHub login."});try{O.length&&await vn(d,O.map(T=>T.trim())),v.length&&await bn(d,await wi(v))}catch(T){if(T instanceof P||T instanceof oe)return p(e,400,{error:T.message});throw T}const mt=await ut([...O.length?[["reviewers-removed",{remove:O}]]:[],...v.length?[["reviewers-added",{add:v.map(T=>T.name)}]]:[]]);return p(e,200,{id:mt.at(-1),board:await C(t,f)})}if(l==="reask-request"){const h=await ai(String(c.question||""));if(h)return p(e,400,{error:h})}const k=await B(l,c);if((l==="grill-more"||l==="prototype-request")&&c.key){const h={...c.text?{text:String(c.text)}:{},...Array.isArray(c.files)&&c.files.length?{files:c.files}:{},...c.question?{question:String(c.question),note:c.note?String(c.note):void 0,choice:c.choice??void 0}:{}};await Nt(String(c.key),l==="grill-more"?"grill":"prototype",h)}return l==="ask-withdrawn"&&await ae(d,String(c.id||""),!0),p(e,200,{id:k,board:await C(t,f)})}if(n==="/api/rules"&&t.method==="GET")return p(e,200,{board:await fn(d),hub:xn()});if(n==="/api/rules"&&t.method==="POST"){const c=await _(t);let l;try{l=await jn(d,c.rules,c.base??[])}catch(k){if(k instanceof P)return p(e,400,{error:k.message});throw k}const f=Rn(l.changes)?await B("rules",{scope:"board",...l.changes}):null;return p(e,200,{rules:l.rules,id:f})}if(n==="/api/settings"&&t.method==="GET")return p(e,200,{settings:await hn(d)});if(n==="/api/permissions"){const c=z(d);if(!c)return p(e,404,{error:"This board isn\u2019t in the hub\u2019s manifest."});const l=se(c.route).repo,f=t.method==="POST"?await _(t):null;try{return f&&await Kn(l,d,f.id,f.on),p(e,200,await Gn(l,d))}catch(k){if(k instanceof P)return p(e,400,{error:k.message});throw k}}if(n==="/api/try/main/log"&&t.method==="GET"){let c;try{c=await m.open(w.join(d,"data","try","main.log"),"r");const{size:l}=await c.stat(),f=Math.max(0,l-64*1024),k=Buffer.alloc(l-f),{bytesRead:h}=await c.read(k,0,k.length,f),v=k.subarray(0,h).toString("utf8").split(/\r?\n/);return f&&v.shift(),v.at(-1)===""&&v.pop(),p(e,200,{lines:v.slice(-40)})}catch(l){if(l.code==="ENOENT")return p(e,200,{lines:[]});throw l}finally{await c?.close()}}if(n==="/api/log"&&t.method==="GET"){const c=a.searchParams.get("file")||"";if(!/^[\w.-]+\.log$/.test(c)||c.startsWith("."))return p(e,400,{error:"file takes a log file\u2019s name."});let l;try{l=await m.open(w.join(gt.homedir(),".claude","kickflip","logs",c),"r");const{size:f}=await l.stat(),k=Math.max(0,f-256*1024),h=Buffer.alloc(f-k),{bytesRead:v}=await l.read(h,0,h.length,k),O=h.subarray(0,v).toString("utf8");return p(e,200,{text:k?O.slice(O.indexOf(`
13
+ `)+1):O,truncated:k>0,size:f})}catch(f){if(f.code==="ENOENT")return p(e,404,{error:"No such log."});throw f}finally{await l?.close()}}if(n==="/api/try/main/pull"&&t.method==="POST"){const c=await _(t);if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).length)return p(e,400,{error:"Send an empty object."});try{const l=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:M.slice(1)||w.basename(d)}),signal:AbortSignal.timeout(3e4)});return p(e,l.status,await l.json())}catch(l){return p(e,502,{error:`Could not pull: ${l.message}`})}}if(n==="/api/try"&&t.method==="POST"){const c=await _(t);if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).some(l=>l!=="key")||typeof c.key!="string")return p(e,400,{error:"Send only the card key."});try{const l=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:M.slice(1)||w.basename(d),key:c.key}),signal:AbortSignal.timeout(1e4)});return p(e,l.status,await l.json())}catch(l){return p(e,502,{error:`Could not start the build: ${l.message}`})}}if(n==="/api/share"&&t.method==="POST")try{return p(e,200,await Pn(d,await _(t)))}catch(c){if(c instanceof P)return p(e,400,{error:c.message});throw c}if(n==="/api/settings"&&t.method==="POST"){const c=await _(t);let l;try{l=await $n(d,c)}catch(h){if(h instanceof P)return p(e,400,{error:h.message});throw h}const f=l.changed.filter(h=>h!=="idle_notification"),k=f.length?await B("settings",Object.fromEntries(f.map(h=>[h,l.settings[h]]))):null;return l.changed.includes("auto_merge")&&l.settings.auto_merge&&await B("check-github",{reason:"auto-merge"}),p(e,200,{settings:l.settings,id:k})}if(n==="/api/arch-review"&&t.method==="POST"){await _(t);try{return p(e,200,await vi())}catch(c){if(c instanceof P)return p(e,c.status||400,{error:c.message});throw c}}if(n==="/api/hide"&&t.method==="POST"){const c=await _(t);try{await ae(d,String(c.id||""),c.hidden!==!1)}catch(l){if(l instanceof P)return p(e,400,{error:l.message});throw l}return p(e,200,{ok:!0})}if(n==="/api/read"&&t.method==="POST"){const{marks:c}=await _(t);return c&&typeof c=="object"&&!Array.isArray(c)&&Object.values(c).every(f=>typeof f=="string"&&!Number.isNaN(Date.parse(f)))?(await ee(d,(await E()).items.map(f=>f.key),c),p(e,200,{ok:!0})):p(e,400,{error:"Send {marks: {card key: ISO time}}."})}if(n==="/_claude/events"&&t.method==="GET")return p(e,200,await ni(a.searchParams.get("kind"),Math.min(500,Number(a.searchParams.get("limit")||100))));if(n==="/api/upload"&&t.method==="POST")return await Ti(t,e);if(t.method!=="GET"&&t.method!=="HEAD"){e.writeHead(405).end();return}if(n.startsWith("/uploads/"))return await Ei(t,e,decodeURIComponent(n.slice(9)));if(n==="/plan.html"){e.writeHead(302,{location:`notes.html${a.search}`,"cache-control":"no-store"}).end();return}if(n==="/index.html"){e.writeHead(302,{location:`./${a.search}`,"cache-control":"no-store"}).end();return}const s=Hn.has(n.slice(1)),r=s?null:await $i(n),o=s?null:r?Ae:n==="/prototype.html"?null:await Te(decodeURIComponent(n));if(!s&&!o){e.writeHead(404).end();return}const i=s?".html":w.extname(o).toLowerCase(),g=s?null:await m.stat(o);let y=s?Buffer.from(Bn({touchIcon:await ue()})):r?Buffer.from(await Pi(r)):await m.readFile(o);!s&&i===".html"&&(y=(await Xn(y.toString("utf8"))).replaceAll("{{title}}",Pe((await D()).title)).replaceAll("{{touchIcon}}",await ue())),!s&&i===".html"&&(y=Buffer.from(await ti(y,n)));const b=i===".html"?`W/"${Kt.createHash("sha1").update(y).digest("base64url").slice(0,20)}"`:`W/"${lt(g)}"`,$=i!==".html"&&a.searchParams.get("v")===lt(g),R={"content-type":F(fe,i)||"application/octet-stream","cache-control":$?"public, max-age=31536000, immutable":"no-cache",etag:b};if(we(t.headers["if-none-match"],b)){e.writeHead(304,{etag:b,"cache-control":R["cache-control"],vary:"accept-encoding"}).end();return}return await he(e,200,R,y)}catch(a){e.headersSent||(a.status===403?e.writeHead(403,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}).end(a.message):e.writeHead(a.status||(a.code==="ENOENT"?404:400)).end())}});let xt=!1;async function Ie(){if(!xt){xt=!0;try{const t=Date.now(),e=(await E()).items.filter(n=>n.heldUntil&&n.lane!=="done"&&Date.parse(n.heldUntil)<=t),a=e.length?await U(W):[];for(const{key:n,heldUntil:s}of e)await en(d,n,s,{announce:!a.some(r=>r.kind==="card-unblocked"&&r.data?.key===n&&r.data?.until===s)}),A({call:"pick-up",key:n,result:"resumed",reason:"Its hold ended.",inputs:{heldUntil:s}})}catch(t){console.error("Pick-up check failed:",t.message)}finally{xt=!1}}}u(Ie,"checkPickUps");let Z=null,It=!1,J=!1;function wt(){return Z?(It=!0,Z):(Z=(async()=>{do{It=!1;try{await Fi()}catch(t){console.error("Auto check failed:",t.message)}}while(It)})().finally(()=>{Z=null}),Z)}u(wt,"checkAuto");async function Fi(){const t=N(await D()),e=await U(W),{items:a,reviews:n}=await E(),{idle:s,pullable:r,waiting:o,starts:i,pulls:g}=ha(a,{cap:t.max_implementers,pulled:await Ce(Ct)});J=ot().state==="paused";const y=s&&!J;for(const c of J?[]:i){const l=await nn(d,c,f=>it(d,c.split("/")[1],f));A({call:"released-start",key:c,result:Ne(l),reason:"Its Implement message was folded in and it was released ready.",inputs:{cap:t.max_implementers}}),await q(l)}if(J||await Wi(a.some(c=>c.status==="checking")),J||await Gi(a),t.auto_pull&&!J&&await Mi(pa(a,{cap:t.max_planners}).map(c=>a.find(l=>l.key===c))),await Ki(t,e,a,y))return;const b=u(async c=>{const l=await bt(d,c,f=>it(d,c.split("/")[1],f),"hub");return A({call:"pull",key:c,result:Ne(l),reason:"An implementer slot was free and it was the top ready card in Todo.",inputs:{cap:t.max_implementers,pullable:r,waiting:o,starts:i}}),q(l)},"startPull");if(await Vi(Ct,t.auto_pull,o,J?[]:g,b))return;const{due:$,repo:R}=n;!y||!$||(A({call:"pr-review",result:"asked",reason:`The board is idle and PR #${$.number} touches its work.`,inputs:{repo:R,pr:$.number,head:$.head,touches:$.touches,asked:!!$.asked}}),await B("pr-review",{pr:$.number,head:$.head,repo:R,title:$.title,author:$.author,url:$.url,touches:$.touches,asked:!!$.asked}))}u(Fi,"runAuto");const Ne=u(t=>t?t.bumped?"bumped":"started":"refused","startResult");async function q(t){if(x++,!t||t.bumped)return null;const e={kind:t.kind,model:t.model,relaunched:!1};try{const a=ot();re(a,t.kind)?(A({call:"launch-held",key:t.key,result:"held",reason:Un(a,t.kind),inputs:{kind:t.kind,model:t.model??null,gate:a}}),await _t(t.key,{...e,pid:null})):await _t(t.key,{...e,pid:(await K(d,t)).pid})}catch(a){await Ka(d,t.key,`the launch failed (${a.message}). Tap Implement to try again.`)}return null}u(q,"handOff");async function Mi(t){for(const{key:e,state:a}of t){const n=a==="prototyping"?"prototype":"grill";A({call:"arrival-planner",key:e,result:n,reason:a==="prototyping"?"It landed in Todo filed prototyping.":"It landed in Todo, not filed prototyping.",inputs:{state:a??null}}),await H(d,e,{text:`Landed in Todo, so the board started its ${n}.`}),await Nt(e,n),x++}}u(Mi,"launchArrivals");async function Nt(t,e,a={}){try{const n=await qa(d,t,e);if(!n)return;try{await K(d,{...n,...a})}catch(s){await Va(d,t,`Couldn't launch the ${e} (${s.message}).`)}}catch(n){if(!(n instanceof P))throw n;await H(d,t,{text:`Couldn't start the ${e}: ${n.message}`})}}u(Nt,"launchPlanner");async function _t(t,e){const a=w.join(S,"headless-builders.json"),n=await j(a,{});e?n[t]=e:delete n[t],await m.mkdir(S,{recursive:!0}),await m.writeFile(a+".tmp",JSON.stringify(n)+`
14
+ `),await m.rename(a+".tmp",a)}u(_t,"trackLaunch");async function Ui(){try{const t=await j(w.join(S,"headless-builders.json"),{});for(const e of(await za(d)).filter(a=>!t[a.key]))A({call:"launch-cut-off",key:e.key,result:"launching",reason:"A restart cut off the launch after its build started.",inputs:{kind:e.kind,model:e.model??null}}),await q(e)}catch(t){console.error("Cut-off launch check failed:",t.message)}}u(Ui,"launchCutOffBuilds");let Dt=!1;async function Ji(){if(!Dt){Dt=!0;try{const t=ot(),e=w.join(S,"headless-builders.json"),a=await j(e,{});if(!Object.keys(a).length)return;const n=new Set(yt(await ke(await $t())).items.filter(i=>i.lane!=="done"&&i.builder&&!i.builder.planner).map(i=>i.key)),o=await Ia(a,n,{launch:u(async i=>{const g=a[i.key],y={call:"relaunch",key:i.key,inputs:g,reason:g.pid===null?"Its launch was held by the usage pause, which has lifted.":"Its builder exited with its record open, for the first time."};try{const{pid:b}=await K(d,i);return A({...y,result:g.pid===null?"launched":"relaunched"}),{pid:b}}catch(b){return A({...y,result:"failed",reason:`${y.reason} The launch failed: ${b.message}`}),await H(d,i.key,{text:`Couldn't relaunch the builder headless (${b.message}).`}),{pid:null}}},"launch"),handBack:u(async i=>{A({call:"relaunch",key:i.key,result:"handed back",reason:"Its builder exited twice with its record open.",inputs:a[i.key]}),await H(d,i.key,{text:"Its builder exited twice with the build unfinished; it reads stopped until the orchestrator takes it up."})},"handBack"),held:u(i=>re(t,i),"held")});JSON.stringify(o)!==JSON.stringify(a)&&(await m.writeFile(e+".tmp",JSON.stringify(o)+`
15
+ `),await m.rename(e+".tmp",e))}catch(t){console.error("Headless builder check failed:",t.message)}finally{Dt=!1}}}u(Ji,"reviveHeadless");function Li(t){const e=w.join(gt.homedir(),".claude","kickflip","logs");Ta(e,{recursive:!0});const a=Aa(w.join(e,`merge-${t.split("/")[1]}-${new Date().toISOString().replace(/[:.]/g,"-")}.log`),"a"),n=process.env.KICKFLIP_MERGE_CMD?JSON.parse(process.env.KICKFLIP_MERGE_CMD):[process.execPath,xa],s=Ye(n[0],[...n.slice(1),"--site",G,"merge",t],{detached:!0,stdio:["ignore",a,a]});$a(a),s.on("error",r=>H(d,t,{text:`Not merged: the land couldn't start (${r.message}). Tap Merge again.`}).catch(()=>{})),s.unref()}u(Li,"runHeldMerge");async function Wi(t){const e=w.join(S,"landers.json"),a=Object.fromEntries(Object.entries(await j(e,{})).map(([o,i])=>[o,typeof i=="string"?{verified:i,pid:null}:i])),n=new Set;for(const[o,i]of Object.entries(a))if(!(!i.pid||qt(i.pid)))try{await Xa(d,o),n.add(o),A({call:"lander",key:o,result:"sent back to checking",reason:"Its lander exited with the card still building.",inputs:i})}catch(g){if(!(g instanceof P))throw g}const s=Object.fromEntries(Object.entries(a).filter(([o])=>!n.has(o)));if(!t&&!n.size)return;const r=(await Qa(d)).filter(o=>s[o.key]?.verified!==o.verified);if(r.length){for(const o of r){const i={call:"lander",key:o.key,reason:"Its build was verified and waits to land.",inputs:{verified:o.verified,before:s[o.key]??null}};try{s[o.key]={verified:o.verified,pid:(await K(d,o,{lander:!0})).pid},A({...i,result:"launched"}),await _t(o.key,null)}catch(g){A({...i,result:"failed",reason:`${i.reason} The launch failed: ${g.message}`}),s[o.key]={verified:o.verified,pid:null},await H(d,o.key,{text:`Couldn't launch the lander headless (${g.message}); land it with board.mjs land ${o.key}.`})}}await m.writeFile(e+".tmp",JSON.stringify(s)+`
16
+ `),await m.rename(e+".tmp",e)}}u(Wi,"launchLanders");async function Gi(t){const e=w.join(S,"rebases.json"),a=await j(e,{}),{base:n}=Ue(d),s=wa(t,Object.fromEntries(Object.entries(a).map(([r,o])=>[r,o.conflict])),n);if(s.length)try{for(const r of s){const o=r.key.split("/")[1],i={call:"rebase-run",key:r.key,reason:`Its PR #${r.pr} conflicts with ${n}.`,inputs:{pr:r.pr,conflict:r.conflict,before:a[r.key]??null}};a[r.key]={conflict:r.conflict,pid:null};let g;try{g=await an(d,r.key,{pr:r.pr,conflict:r.conflict,base:n},()=>Je(d,o,r.branch))}catch(y){if(!(y instanceof P))throw y;A({...i,result:"refused",reason:`${i.reason} The start refused: ${y.message}`}),await H(d,r.key,{text:`PR #${r.pr} conflicts with ${n}, but the rebase run couldn't start: ${y.message}`});continue}finally{x++}try{a[r.key].pid=(await K(d,g)).pid,A({...i,result:"launched"})}catch(y){A({...i,result:"failed",reason:`${i.reason} The launch failed: ${y.message}`}),await H(d,r.key,{text:`Couldn't launch the rebase run headless (${y.message}); it reads stopped until the orchestrator takes it up.`})}}}finally{await m.writeFile(e+".tmp",JSON.stringify(a)+`
17
+ `),await m.rename(e+".tmp",e)}}u(Gi,"launchRebases");async function _e(){const t=va(new Date,Sa(await j(w.join(gt.homedir(),".claude","kickflip","settings.json"),{})));return t&&ba(t)}u(_e,"triageNight");async function Ki(t,e,a,n){const s=w.join(S,"triaged.json"),r=await j(s,{}),{waiting:o,seen:i}=ka(a),g=u(async()=>{await m.mkdir(S,{recursive:!0}),await m.writeFile(s+".tmp",JSON.stringify({seen:i})+`
18
+ `),await m.rename(s+".tmp",s)},"save"),y=e.filter(c=>c.kind==="overnight-triage").at(-1);if(y&&!(await dt(y)).reply)return r.seen!==i&&await g(),!0;if(!t.auto_triage||!n||o.length<2||i===r.seen)return!1;const b=await _e();if(!b||y?.data?.date===b)return!1;const $=`overnight-triage-${b}`;return((await j(w.join(S,"owner.json"),{topics:[]})).topics||[]).some(c=>c.id===$)||await Ma(d,{id:$,title:`Overnight triage ${b}`}),A({call:"triage",result:"sent",reason:`Auto triage is on, ${o.length} cards wait on answers, and tonight's triage hadn't gone out.`,inputs:{date:b,cards:o}}),await B("overnight-triage",{date:b,topic:$,cards:o}),await g(),!0}u(Ki,"checkTriage");async function zi(){if(!N(await D()).auto_triage)return!1;const t=await _e();return!!t&&(await U(W)).filter(e=>e.kind==="overnight-triage").at(-1)?.data?.date!==t}u(zi,"triageDue");const Ct={file:"pulled.json",line:"Pulled in: an implementer slot was free."},De=u(t=>t.keys||(t.key?[t.key]:[]),"markedOf"),Ce=u(async({file:t})=>De(await j(w.join(S,t),{})),"sentOf");async function Vi({file:t,line:e},a,n,s,r){const o=w.join(S,t),i=a?[...n,...s]:[];for(const g of a?s:[])await m.mkdir(w.dirname(V(g)),{recursive:!0}),await m.appendFile(V(g),JSON.stringify({ts:new Date().toISOString(),who:"claude",text:e})+`
19
+ `),await r(g);return JSON.stringify(De(await j(o,{})))!==JSON.stringify(i)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(o+".tmp",JSON.stringify({keys:i,at:new Date().toISOString()})+`
20
+ `),await m.rename(o+".tmp",o)),i.length>0}u(Vi,"sendPulls");async function Ht(t,{key:e,reason:a,inputs:n}){const s={call:"push",key:e,reason:a,inputs:{tag:t.tag,title:t.title,...n}};try{await rn(d,t),A({...s,result:"sent"})}catch(r){if(!(r instanceof P))throw r;const o=r.message.split(`
21
+ `).at(-2)||r.message;A({...s,result:"not sent",reason:`${a} Not sent: ${o}`}),console.error(`Push ${t.tag} not sent:`,o)}}u(Ht,"sendOwnPush");let tt=null,Bt=!1;function Ft(){return tt?(Bt=!0,tt):(tt=(async()=>{do{Bt=!1;try{await Qi()}catch(t){console.error("Action push check failed:",t.message)}}while(Bt)})().finally(()=>{tt=null}),tt)}u(Ft,"checkActions");async function Qi(){const t=w.join(S,"action-pushes.json"),e=await j(t,null),a=da(await E()),n=Object.fromEntries(a.map(s=>[s.tag,{kind:s.kind,ids:s.questions.map(r=>r.id)}]));if(e){for(const r of a){const o=F(e,r.tag),i=r.questions.filter(b=>!o?.ids?.includes(b.id));if(o?.kind===r.kind&&!i.length)continue;const g=i.length?`questions.html#${encodeURIComponent(i[0].id)}`:r.url,y=o?o.kind!==r.kind?`What waits on the owner changed from ${o.kind} to ${r.kind}.`:"A new question waits on the owner.":"Something new waits on the owner.";await Ht({kind:"action",title:r.title,body:i[0]?.question||r.body,url:`/${G}/${g}`,tag:r.tag,now:r.now},{key:r.tag.startsWith("item-")?r.tag.slice(5):void 0,reason:y,inputs:{was:o??null,kind:r.kind,fresh:i.map(b=>b.id)}})}const s=Object.keys(e).filter(r=>!F(n,r));s.length&&await ja(s,G)}JSON.stringify(n)!==JSON.stringify(e)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify(n)+`
22
+ `),await m.rename(t+".tmp",t))}u(Qi,"pushActions");let et=null,Mt=!1;function Ut(){return et?(Mt=!0,et):(et=(async()=>{do{Mt=!1;try{await Xi()}catch(t){console.error("Orchestrator wait check failed:",t.message)}}while(Mt)})().finally(()=>{et=null}),et)}u(Ut,"checkWaits");async function Xi(){const t=w.join(S,"orchestrator-waits.json"),e=await j(t,null),a=e?.waits||[],n=u(o=>a.find(i=>i.key===o.key&&i.kind===o.kind&&i.since===o.since),"was"),s=[],r=[];for(const o of na(await E())){let i=n(o)?.pushed===!0;!o.picked&&!n(o)&&s.push(o.key),!o.picked&&!i&&Date.now()-Date.parse(o.since)>=aa&&(i=!0,await Ht({kind:"reminder",title:`${o.ref} waits on the orchestrator`,body:`${o.title}: ${o.next}`,url:`/${G}/#item=${encodeURIComponent(o.key)}`,tag:`wait-${o.key}`,now:!0},{key:o.key,reason:`It has waited on the orchestrator since ${o.since} and nobody picked it up.`,inputs:{kind:o.kind,since:o.since}})),r.push(i?{...o,pushed:i}:o)}(!e||JSON.stringify(r)!==JSON.stringify(a))&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify({waits:r})+`
23
+ `),await m.rename(t+".tmp",t)),s.length&&await B("orchestrator-waits",{added:s})}u(Xi,"stepWaits");let at=null,Jt=!1;function Lt(){return at?(Jt=!0,at):(at=(async()=>{do{Jt=!1;try{await Yi()}catch(t){console.error("Idle check failed:",t.message)}}while(Jt)})().finally(()=>{at=null}),at)}u(Lt,"checkIdle");async function Yi(){const t=w.join(S,"idle.json"),e=await D();if(!N(e).idle_notification)return m.rm(t,{force:!0});const a=await j(t,{}),n=await E(),s=ga(n,{pulled:await Ce(Ct)}),{state:r,push:o}=sa(a,{idle:s,now:new Date});o&&await Ht({kind:"reminder",title:`${e.title||G} has gone idle`,body:oa(n.items,a.last),url:`/${G}/`,tag:"idle",now:!0},{reason:"The board has been strictly idle long enough, with idle notifications on.",inputs:{previous:a}}),JSON.stringify(r)!==JSON.stringify(a)&&(await m.mkdir(S,{recursive:!0}),await m.writeFile(t+".tmp",JSON.stringify(r)+`
24
+ `),await m.rename(t+".tmp",t))}u(Yi,"stepIdle");let nt=null,Wt=!1;function He(){return nt?(Wt=!0,nt):(nt=(async()=>{do{Wt=!1;try{await Zi()}catch(t){console.error("Done archive failed:",t.message)}}while(Wt)})().finally(()=>{nt=null}),nt)}u(He,"checkArchive");async function Be(){try{await ee(d,(await E()).items.map(t=>t.key))}catch(t){console.error("Read marks failed:",t.message)}}u(Be,"markNewCards");async function Zi(){if(!zt(await E()).length)return;const t=zt(await X()),e=t.length?await Ua(d,t.map(a=>({key:a.key,closed_at:a.sortAt}))):[];e.length&&console.log(`Done archive: ${e.length} card${e.length===1?"":"s"} moved`)}u(Zi,"archiveDone");try{for(const t of await Ya(d))console.log(t)}catch(t){console.error("follow-up migration:",t.message)}try{for(const t of await Za(d))console.log(t)}catch(t){console.error("planned-status migration:",t.message)}try{await tn(d)}catch(t){console.error("dead-field strip:",t.message)}await Ui(),await He(),await Be(),await Hi(),setInterval(()=>{x++,ve(),Ji(),Ie(),J&&ot().state!=="paused"?wt():zi().then(t=>{t&&wt()},t=>console.error("Triage check failed:",t.message)),Ft(),Ut(),Lt()},6e4).unref(),await ve(),await Ie(),await wt(),Ft(),Ut(),Lt(),Bi.listen(de,"127.0.0.1",()=>console.log(`site on http://127.0.0.1:${de}/ (prefix ${M||"none"})`));