@lovek4d/kickflip 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTEXT.md +60 -48
- package/SKILL.md +20 -14
- package/bin/event-lines.mjs +1 -0
- package/bin/kickflip.mjs +8 -8
- package/bin/wait-events.mjs +2 -2
- package/board/agents.mjs +2 -2
- package/board/arch-review.mjs +4 -4
- package/board/bin/board.mjs +118 -104
- package/board/domain.mjs +7 -7
- package/board/finish.mjs +4 -4
- package/board/headless.mjs +8 -9
- package/board/hub-lines.mjs +1 -0
- package/board/onboarding.mjs +2 -2
- package/board/prototype-page.mjs +1 -1
- package/board/public/app-arch.js +3 -3
- package/board/public/app-banner.js +1 -1
- package/board/public/app-cards.js +17 -9
- package/board/public/app-header.js +1 -1
- package/board/public/app-lanes.js +39 -61
- package/board/public/app-needs.js +116 -30
- package/board/public/app-reader.js +54 -44
- package/board/public/app-threads.js +2 -2
- package/board/public/app.html +1 -1
- package/board/public/app.js +23 -13
- package/board/public/dialogs.js +1 -1
- package/board/public/hub-kit.js +1 -1
- package/board/public/hub.css +2 -0
- package/board/public/linear-key.js +1 -1
- package/board/public/new-task.js +1 -1
- package/board/public/progress-review.js +4 -4
- package/board/public/prototype-kit.js +27 -16
- package/board/public/prototype-switcher.js +47 -32
- package/board/public/prototype.html +7 -4
- package/board/public/router.js +35 -7
- package/board/public/shell.js +19 -13
- package/board/public/sidebar.css +4 -4
- package/board/public/sidebar.js +2 -2
- package/board/public/style.css +74 -36
- package/board/public/ui-cards.js +56 -37
- package/board/public/ui-enums.js +10 -1
- package/board/public/ui-hub.js +1 -5
- package/board/public/ui-links.js +6 -6
- package/board/public/ui-order.js +8 -6
- package/board/public/ui-progress-bar.js +6 -7
- package/board/public/ui-questions.js +30 -13
- package/board/public/ui-sidebar.js +3 -2
- package/board/public/ui-threads.js +20 -19
- package/board/public/ui.js +15 -4
- package/board/public/view-hub-settings.js +28 -31
- package/board/public/view-hub.js +7 -9
- package/board/public/view-notes.js +2 -2
- package/board/public/view-questions.js +35 -6
- package/board/public/view-settings.js +63 -31
- package/board/render-check.mjs +10 -10
- package/board/review.mjs +1 -1
- package/board/server.mjs +18 -20
- package/board/stop.mjs +1 -1
- package/board/store.mjs +26 -25
- package/board/tickets.mjs +1 -1
- package/board/transcript.mjs +4 -2
- package/hooks-plugin/hooks/board-data-gate.mjs +6 -6
- package/hub/awake.mjs +1 -1
- package/hub/boards.mjs +4 -4
- package/hub/github.mjs +3 -3
- package/hub/home.mjs +1 -1
- package/hub/hub.mjs +11 -11
- package/hub/incoming.mjs +2 -2
- package/hub/locks.mjs +1 -1
- package/hub/new-board.mjs +2 -2
- package/hub/owner-checks.mjs +1 -1
- package/hub/try.mjs +6 -7
- package/hub/upgrade.mjs +1 -1
- package/hub/views.mjs +1 -1
- package/package.json +1 -1
- package/shared/tailscale.mjs +1 -1
- package/shared/usage-gate.mjs +1 -1
package/board/domain.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var Gt=Object.defineProperty;var l=(e,t)=>Gt(e,"name",{value:t,configurable:!0});import{routeRecord as A}from"../hub/routes.mjs";import Qt from"node:fs";import{ticketBadge as ct,ticketIn as Vt}from"./tickets.mjs";import{prLinks as Ht,reviewLinks as Xt,reviewTime as Yt,reviewersOf as zt}from"./review.mjs";import{remoteFromInput as Jt}from"../hub/repos.mjs";import{reviewsOf as Zt,reviewNeeds as en,reviewActions as tn}from"./auto-review.mjs";import{cardPRs as nn,hasPR as et,leastReady as on,prKeyOf as sn,prURL as rn}from"../shared/card-prs.mjs";import ut from"node:vm";import{checkWords as an,parseCheck as ln}from"../shared/owner-check.mjs";import{buildScriptOf as dn}from"./build-script.mjs";const tt=ut.createContext({});for(const e of["ui-enums.js","ui-order.js"])ut.runInContext(Qt.readFileSync(new URL(`./public/${e}`,import.meta.url),"utf8"),tt);const{BUTTON:oe,ACTION_KIND:q,BUILDER_KIND:be,PR_STATE:Z}=tt.BoardUI,{laneOrder:cn}=tt.BoardUI,un=["item-message","mark-ready","grill-more","prototype-request","visual-request","reask-request"],pt="question-reopen",ts=Object.freeze({todo:"Todo",progress:"In Progress",review:"In Review",done:"Done"}),Se="move",ft=10*6e4,pn=5*6e4,gt="build-refused",Ie=Object.freeze({ask:"builder-ask",answer:"builder-answer"}),fn=864e5,pe=l(e=>e.lane==="done"||e.lane==="asks","closedLane"),gn=l(e=>`wave:${e}`,"waveFold"),kt=7*fn,mt=l((e,t)=>({id:e,url:t?.workspace?`https://linear.app/${t.workspace}/issue/${e}`:null}),"linearRef"),kn=l((e,t)=>String(e).replace(/^S(\d+)$/,`${t||"INT"}-$1`),"internalRef"),Ke=l((e,t,n)=>Vt(n,t.ticket)?t.ticket:e.internal===!0?kn(t.id,n?.prefix):t.id,"refOf"),mn=l((e,t,n,s)=>ct(e||(t?{source:"internal",prefix:"INT"}:null),n,s)||(t?{id:String(t),url:null,source:null}:null),"ticketOf");function wn(e,t,n,s,r,a,u,d,p){return!a&&(e.lane==="done"||t?.state==="MERGED"||t?.state==="CLOSED")?"done":(n||s)&&!d?"progress":r?"review":t?.state==="OPEN"?t.checks?.state==="fail"&&(!t.isDraft||u)?"progress":"review":t||p?"progress":"todo"}l(wn,"laneOf");const hn="Lander",wt=l(e=>e.kind!==be.install&&e.label!==hn,"changesCode");function yn(e,t){return e?.state==="MERGED"||t?Z.merged:e?.state==="CLOSED"?Z.closed:e?.state==="OPEN"?e.isDraft?Z.draft:Z.open:null}l(yn,"prStateOf");const ht=l(e=>e.pr?.status===Z.closed,"unlanded"),bn=l(e=>e.some(t=>t.kind==="work")&&e.every(t=>t.lane==="done"),"finishedOf");function nt(e){const t=yt(e);return t?.done&&t.handed_back?t.handed_back:null}l(nt,"handedBackAt");const yt=l(e=>e.filter(t=>t?.started&&te(t)&&t.kind!==be.install).sort((t,n)=>String(t.started).localeCompare(String(n.started))).at(-1),"newestBuild");function Sn(e,t,n){return Object.fromEntries(e.map(s=>{const r=A(t?.reviews,s.github),a=r==="APPROVED"?"approved":r==="CHANGES_REQUESTED"?"changes":Object.hasOwn(n,s.name)?"sent":"unsent";return[s.name,{state:a,since:a==="sent"?A(n,s.name):null,github:s.github||null}]}))}l(Sn,"reviewOf");function vn(e,t,n,s,r,a,{held:u,needsApproval:d}){return n?.kind===be.install?"builder":s?"checking":n?"builder":u&&e==="review"?"merge":r&&e==="review"?"owner":e==="review"&&t?.state==="OPEN"&&t.isDraft?a?"ci":"ready":e==="review"?bt(t,d)?"merge":"reviewers":null}l(vn,"waitsOnOf");const bt=l((e,t)=>e?.reviewDecision==="APPROVED"||!t&&!["REVIEW_REQUIRED","CHANGES_REQUESTED"].includes(e?.reviewDecision),"reviewed"),qe=/(^|\/)prototype-[\w-]+\.html/,xn={reviewers:"Waiting for PR reviewers",merge:"Approved, merges once CI and the merge state allow",ci:"Marked ready for review once CI passes"},Ge={waiting:"Approved, Merge shows once CI and the merge state allow",ready:"Approved and green: tap Merge",merging:"Merging"},ot={auto:"Ready, merges once CI and the merge state allow",waiting:"Ready, Merge shows once CI and the merge state allow",ready:"Ready and green: tap Merge"},On=l(e=>`Verified: tap Merge to land it on ${e}`,"heldNext"),$n="Fix the failing CI",St="Tap Done once it is settled",En=["status","agent","ask-link","command",Se,Ie.answer],vt=l(e=>(e.who==="claude"||e.via==="github")&&!En.includes(e.kind),"countsUnread"),An=/^(?:Not merged yet: |Lander launched headless )/;function Bn({slug:e,wave:t,it:n,order:s,prs:r,repo:a,remote:u,base:d,builder:p,reachedPhase:f,agents:g,worktree:c,eta:h,thread:m,events:x,working:S,said:L,askedAt:_,topicItems:P,prSlack:T,boardReviewers:K,now:E,bumpedAt:C,holdAsk:i,closedAt:y,mergeAt:B,autoMerge:ke,skipDraft:re,tickets:U}){const V=r.map(w=>({repo:w.repo,number:w.number,url:rn(w),...w.gh||w.kept||{},status:yn(w.gh,w.merged_at)})),W=on(r.map(w=>w.gh)),N=r[W]?.gh||null,H=V[W]?.status??null,X=n.lane||null,Ne=!!y&&String(y)>String(n.lane_at||"");Ne&&(n={...n,lane:"done",closed_at:y,reopened_at:void 0});const F=[n.closed_at,...r.flatMap(w=>[w.merged_at,w.gh?.mergedAt])].filter(Boolean).map(String).sort().pop()||null,j=String(F||""),Ce=V.length>0&&V.every(w=>w.status===Z.closed),G=(n.lane==="done"||n.reopened_at||H===Z.merged)&&!Ce&&([...L.filter(w=>w.kind==="item-message").map(w=>w.ts),..._].map(String).filter(w=>w>j).sort().pop()||n.reopened_at)||void 0,me=n.owner_step&&!Ne?String(n.owner_step):null,Ye=(n.lane==="done"||H===Z.merged)&&!Ce;G&&(n={...n,next:void 0});const $e=nt(g),ze=!!G&&L.some(w=>String(w.ts)>j&&(w.state==="waiting"||w.state==="working")),we=`${e}/${n.id}`,Q=zt(t,K),ne=Object.fromEntries(Object.entries(Xt(t,n,we,P,Ht(T,r))).map(([w,J])=>[w,Yt(J.ts)])),ae=Object.values(ne).filter(Boolean).sort()[0]||null,Te=[...m.map(w=>w.ts),...x.map(w=>w.ts)].sort().pop()||null,Ee=[...m.filter(w=>vt({...w,who:w.who||"claude"})).map(w=>w.ts),...x.map(w=>w.reply?.at)].filter(Boolean).sort(),Y=!et(n)&&n.pushed?.branch?{branch:n.pushed.branch,at:n.pushed.at||null,host:u?.host||null}:null,z=!et(n)&&n.merge_ready?.branch?{branch:n.merge_ready.branch,at:n.merge_ready.at||null,base:d}:null,Ae=!!n.rebase_run&&N?.state==="OPEN",le=(n.rebased||[]).at(-1),Be=g.filter(w=>w.started&&!w.done&&te(w)),Le=!!$e&&wt(yt(g));let R=wn(n,N,Be.some(wt),Le,Y||z,!!G,re,Ae,Be.length>0||!!$e);G&&R==="todo"&&(R="progress");const Je=R==="review"&&!!ae&&E-Date.parse(ae)>kt,Re=!!me&&Ye&&R==="done";Re&&(R="review");let D=vn(R,N,p,!!$e&&R!=="done",Y,re,{held:z,needsApproval:Q.length>0});const Ze=D==="builder"&&N?.state==="OPEN"&&!N.isDraft,ie=R==="progress"&&!D&&N?.state==="OPEN"&&(!N.isDraft||re),de=Re||!!G&&R==="progress"&&!D&&!ie&&!ze;de&&(D="owner");const he=i&&String(i.ts)>String(n.held_at||""),_e=R==="todo"&&(he?!!i.data.hold:!!n.held),Pe=_e&&(he?i.data.until:n.pick_up_at)||null,je=R==="todo"||R==="progress",Ue=g.filter(te).map(w=>String(w.started||"")).filter(Boolean).sort()[0]||"",o=C&&!(Ue&&String(C)<Ue),k=m.filter(w=>(w.who||"claude")==="claude"&&!["status","command",Se].includes(w.kind)&&!An.test(w.text||"")).map(w=>String(w.ts)).sort().pop()||"",b=[B,z&&n.merge_ready.tapped].filter(Boolean).map(String).sort().pop(),v=D==="merge"&&!!b&&b>k,O=m.filter(w=>w.kind==="status"&&String(w.ts)>String(b)).sort((w,J)=>String(w.ts).localeCompare(String(J.ts))).at(-1),M=O?.text.startsWith("Waiting for ")?`${Ge.merging} \xB7 ${O.text[0].toLowerCase()}${O.text.slice(1)}`:Ge.merging,I=m.filter(w=>!["status",Se].includes(w.kind)).sort((w,J)=>String(w.ts).localeCompare(String(J.ts))).at(-1),ce=R==="todo"&&I?.kind===gt?I.text:null,ue=G||g.filter(te).map(w=>w.started).filter(Boolean).map(String).sort().pop()||null,We=N?.reviewDecision==="APPROVED",Fe=Y&&D==="owner"?`merge ${Y.branch} into ${d}`:z&&D==="merge"?v?M:On(d):D==="merge"&&!ke?v?M:(We?Ge:ot).waiting:D==="merge"&&!We?ot.auto:xn[D],ye=H===Z.draft?`Review draft PR #${V[W].number}`:null,Me=!Ae&&le?.to&&le.to===N?.headRefOid?`Rebased onto ${le.base||d}, pushed${N.mergeable==="MERGEABLE"?" \xB7 PR is clean again":""}`:null,$=Ae&&D==="checking"?g.flatMap(w=>w.calls).sort((w,J)=>String(w.at).localeCompare(String(J.at))).at(-1)?.text??null:null;return{key:we,kind:"work",order:s,wave:e,waveTitle:t.title||e,id:n.id,ref:Ke(t,n,U),title:n.title,storedLane:X,created:n.created||null,reviewSince:n.review_since||ae||n.merge_ready?.at||n.pushed?.at||n.created||null,progressSince:ue,closedAt:R==="done"?F:null,internal:t.internal===!0,repo:t.internal===!0&&!t.repo?null:a,lane:R,waitsOn:D,builder:D==="checking"?null:p,reachedPhase:f,agents:g,worktree:c??null,eta:h,working:S,reopened:!!G,awaitsDone:de,ownerStep:de?me:null,ownerCheck:de&&me&&n.owner_check?{words:an(ln(n.owner_check.check)),broken:n.owner_check.broken||null}:null,stagedAt:R==="todo"&&n.state==="staging"&&n.staged_at||null,roundAt:n.round_at||null,deps:n.deps||[],blockedUntil:n.blocked_until||null,blockReason:n.block_reason||null,held:_e,heldUntil:Pe,risky:!!n.risky,bumpable:je,bumpedAt:je&&o?C:null,holdable:R!=="done",hasNotes:!!(n.plan||String(n.notes||"").trim()),finishable:R!=="done",merging:v,refusal:ce,manualSteps:n.manual_steps||[],fromAsk:n.from_ask||null,unfiled:!!n.unfiled,next:Ze||R==="done"||D==="checking"?null:ie?$n:Me||Fe||n.next||ye||null,rebaseFailed:$,branch:n.branch||N?.headRefName||Y?.branch||null,pushed:R==="done"||Re?null:Y,mergeReady:R==="review"?z:null,stale:R!=="done"&&(Je||!!p?.stale),stalled:n.stalled?.reason||null,landStopped:R==="review"&&z&&n.merge_ready.stopped&&String(n.merge_ready.stopped)>String(n.merge_ready.tapped||"")?{words:Et("land"),at:n.merge_ready.stopped}:null,ticket:U?.source==="linear"&&!n.ticket&&n.linear?.length?null:mn(U,n.ticket,n.ticket||(t.internal?Ke(t,n,U):n.linear?.[0]),{repo:a,key:we}),ticketSource:U?.source||null,linear:(n.linear||[]).map(w=>U?.source==="linear"&&ct(U,w,{key:we,repo:a})||mt(w,U)),pr:V[W]||null,prs:V,reviewers:Q.map(w=>w.name),review:Sn(Q,N||r[W]?.kept,ne),inReviewSince:ae,threadCount:m.length+x.length,lastTs:Te,othersAt:Ee,grilling:At("grill",x,g,_),prototyping:At("prototype",x,g,_),state:n.state||null,updatedAt:N?.mergedAt||N?.updatedAt||r[W]?.merged_at||p?.at||Te||null,ask:null,fold:null,action:null,install:R==="done"?null:_n(n.install,N),waitFacts:Rn(n,{agents:g,thread:m,events:x,refusedAt:ce?I.ts:null})}}l(Bn,"workItem");function Rn(e,{agents:t,thread:n,events:s,refusedAt:r}){const a=new Set(n.filter(f=>f.kind===Ie.answer).map(f=>f.ask)),u=n.filter(f=>f.kind===Ie.ask&&!a.has(f.ask)).sort((f,g)=>String(f.ts).localeCompare(String(g.ts)))[0],d=l(f=>s.filter(g=>rt(g,f)).pop()?.ts||null,"requestAt"),p=n.filter(f=>(f.who||"claude")==="claude"&&!["status","agent","command",Se,gt].includes(f.kind)).map(f=>f.ts);return{handedBackAt:nt(t),next:e.next||null,ask:u?{at:u.ts,text:u.text}:null,grillAt:d("grill"),prototypeAt:d("prototype"),refusedAt:r,unverified:t.some(f=>f.calls.some(g=>!f.verified||String(g.at)>String(f.verified.at))),writes:[...p,...t.flatMap(f=>[f.verified?.at,f.started])].filter(Boolean).map(String).sort()}}l(Rn,"waitFactsOf");function _n(e,t){if(!e?.url)return null;const n=e.sha?String(e.sha).toLowerCase():null,s=t?.headRefOid?String(t.headRefOid).toLowerCase():null;return{url:e.url,label:e.label||null,builtAt:e.built_at||null,sha:n,older:!!(n&&s&&!s.startsWith(n))}}l(_n,"installOf");function Mn(e,{needsApproval:t=!0}={}){if(e?.state!=="OPEN")return[`PR is ${String(e?.state||"unknown").toLowerCase()}`];const{state:n,pending:s,failing:r}=e.checks||{};return[e.isDraft&&"it is a draft",!bt(e,t)&&`review: ${String(e.reviewDecision||"none").toLowerCase().replace(/_/g," ")}`,n==="fail"&&`failing: ${r?.length?r.join(", "):"a check"}`,n==="pending"&&`pending: ${s?se(s,"check"):"checks"}`,e.mergeStateStatus!=="CLEAN"&&`merge state: ${String(e.mergeStateStatus||"unknown").toLowerCase()}`].filter(Boolean)}l(Mn,"mergeBlockers");const In=l((e,t)=>!Mn(e,{needsApproval:t}).length,"mergeable");function qn(e,t,n){if(pe(e))return null;const s=`#item=${encodeURIComponent(e.key)}`;if(e.stalled)return{kind:q.stalled,url:s,label:`stalled: ${e.stalled}`};if(Dn(e)){const r=t.get(e.questions.first);return qe.test(r?.url||"")?{kind:q.pick,url:`questions.html#${r.id}`,label:"Prototypes ready: pick a take"}:{kind:q.questions,url:`questions.html#${e.questions.first}`,label:`Answer ${se(e.questions.open,"question")}`}}return e.waitsOn==="ready"?{kind:q.ready,url:e.pr.url,label:`Review draft PR #${e.pr.number}`}:e.kind==="ask"&&e.waitsOn==="owner"?{kind:q.done,url:s,label:e.next||St}:e.waitsOn==="merge"&&e.manualSteps.length&&!e.mergeReady?{kind:q.steps,url:s,label:"Run the manual steps, then merge"}:e.waitsOn==="merge"&&(!n||e.mergeReady)&&!e.merging&&e.prs.every(r=>["MERGED","CLOSED"].includes(r.state)||In(r,e.reviewers.length>0))?{kind:q.merge,url:s,label:"Merge"}:null}l(qn,"actionOf");const Dn=l(e=>!!(e.questions&&e.questions.state!=="resolved"&&e.questions.open>0),"asking"),st=new Set([q.pick,q.questions]);function xt(e){return e.questions?oe.openQuestions:null}l(xt,"actionButtonOf");const Nn=l(e=>e.kind==="work"&&e.lane==="todo"&&!e.action&&!e.unpicked,"buildableOf"),Cn=l((e,t)=>/^item:/.test(e.topic||"")&&qe.test(e.url||"")&&!(e.options||[]).some(n=>n.id===t),"picksNoTake");function Tn(e,t){const n=e.filter(a=>a.topic===`item:${t}`),r=[...n.filter(a=>qe.test(a.url||"")).flatMap(a=>a.answers.map(u=>({q:a,a:u,again:Cn(a,u.choice)}))),...n.filter(a=>a.request).map(a=>({q:a,a:a.request,again:!0}))].sort((a,u)=>String(a.a.ts).localeCompare(String(u.a.ts))).at(-1);return r?.again?{id:r.q.id,note:r.a.note.replace(/\s+/g," ").trim()}:null}l(Tn,"noTakePicked");const Ln="Prototype again",Pn=Object.freeze({[q.pick]:oe.openQuestions,[q.questions]:oe.openQuestions,[q.ready]:oe.github,[q.steps]:oe.github,[q.merge]:oe.merge,[q.done]:oe.done,[q.stalled]:oe.reply}),jn="Merges once unblocked",Un=l(e=>`Blocked ${[e.blockedBy.length&&`by ${e.blockedBy.map(t=>t.ref).join(", ")}`,e.blockedUntil&&`until ${e.blockedUntil}`].filter(Boolean).join(" and ")}`,"mergeBlockedWords");function Wn(e,t,n){if(e.action=qn(e,t,n),e.mergeBlocked=e.action?.kind===q.merge&&e.blocked?Un(e):null,e.mergeBlocked&&Object.assign(e,{action:null,next:jn}),e.action?.kind===q.questions&&e.questions.fromAsks){Object.assign(e,{action:null,waitsOn:"owner",next:e.action.label,actionButton:xt(e),resolvedBy:null});return}e.awaitsDone&&=!e.action,e.awaitsDone||Object.assign(e,{ownerStep:null,ownerCheck:null}),e.actionButton=xt(e),e.resolvedBy=Pn[e.action?.kind]||null,e.action?.kind===q.merge&&!e.mergeReady&&(e.next=(e.pr?.reviewDecision==="APPROVED"?Ge:ot).ready),e.awaitsDone&&Object.assign(e,{lane:"review",next:e.ownerStep||St,bumpable:!1,bumpedAt:null});const s=e.lane==="todo"||e.waitsOn==="ready"||e.lane==="progress"&&e.waitsOn==="owner";if(!e.action||!s||e.stalled)return;const r=e.lane==="todo"&&st.has(e.action.kind)?"todo":"review";Object.assign(e,{lane:r,waitsOn:e.waitsOn==="ready"?"ready":"owner",next:e.action.label}),r==="review"&&Object.assign(e,{bumpable:!1,bumpedAt:null})}l(Wn,"placeOwnerWait");const Ot=l((e,t)=>e?e!=="todo":t.some(te),"started"),$t="none set yet";function ns(e,t){const n=t.filter(s=>s?.started);return e.next||et(e)||e.pushed||e.held||e.lane==="done"||nt(n)?!1:!n.some(s=>!s.done)&&Ot(e.lane,n)}l(ns,"owesNext");function Fn(e,t=new Date){const n=new Date(e),s=n.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"});return n.toDateString()===t.toDateString()?s:`${n.getMonth()+1}/${n.getDate()} ${s}`}l(Fn,"askedLabel");function Kn(e,t,n,s,r,a,u){const d=t.find(y=>y.kind==="ask"&&y.eventId===e.id),p=d.files,f=(d.text||"").trim().split(`
|
|
2
|
-
`)[0]||p.map(y=>y.name).join(", ")||"Ask",g=d.state==="replied"||d.state==="answered",c=r?.state==="progress"&&!(String(a||"")>String(r.at||"")),h=!c&&(!!r||!!a),m=g&&!h&&!c&&u.open>0,x=g&&!h&&!c&&!m&&u.answered>0,S=h||g&&!c&&!m&&!x,L=g&&!s&&!r?.moved&&[...t.filter(y=>y.who==="claude"&&y.kind!=="ask-link"&&y.kind!=="status"&&y.kind!=="command").map(y=>y.ts),...t.map(y=>y.reply?.at)].filter(Boolean).sort().pop()||null,_=S||c||m||x,P=t.some(y=>y.who==="love"&&y.kind!=="ask"&&(y.state==="waiting"||y.state==="working")),T=t.filter(y=>y.who==="love"&&y.kind==="item-message").flatMap(De).filter(([,y])=>y==="waiting"||y==="working").map(([y])=>y),K=_?"answered":d.state,E=_?null:n||null,C=[...t.filter(vt).map(y=>y.ts),...t.map(y=>y.reply?.at)].filter(Boolean).sort(),i=[...t.map(y=>y.ts),...C,...c?[r.at]:[]].filter(Boolean).sort().pop();return{key:`ask:${e.id}`,kind:"ask",order:0,wave:null,waveTitle:null,id:e.id,ref:_?Fn(e.ts):"New",isNew:!_,title:f,storedLane:null,internal:!1,repo:null,lane:c?"progress":S?"asks":"todo",waitsOn:c&&!P?"owner":null,builder:null,agents:[],eta:null,working:t.some(y=>De(y).some(([,B])=>B==="working")),deps:[],held:!1,risky:!1,bumpable:!_,bumpedAt:E,manualSteps:[],fromAsk:null,from:null,next:c&&r.next||null,hasNotes:!1,branch:null,finishable:_&&!S,linear:[],pr:null,prs:[],reviewers:[],review:{},inReviewSince:null,reviewSince:null,progressSince:c&&r.at||null,closedAt:null,threadCount:t.length,lastTs:i,othersAt:_?C:[],grilling:!1,prototyping:!1,created:e.ts,updatedAt:_?i:e.ts,ask:{eventId:e.id,state:K,askedAt:e.ts,bumpedAt:E,edited:!!d.edited,canEdit:!_,text:d.text,files:p,answerAt:L,from:d.from||null,owed:T},fold:null,action:null}}l(Kn,"askItem");const fe="visual",Qe="helper",Gn=new Set(["grill","prototype",fe]),ee=l(e=>Gn.has(e.kind),"plans"),te=l(e=>!ee(e),"builds"),Qn={grill:"grilling",prototype:"prototyping"},Vn=l((e,t)=>`${e} ${t} by orchestrator`,"orchestratorDid"),Et=l(e=>Vn(e,"stopped"),"stoppedWords"),Hn={grill:"grill-more",prototype:"prototype-request"},rt=l((e,t)=>e.kind===Hn[t]&&!e.data?.question,"isRequest");function At(e,t,n,s){if(n.some(a=>a.kind===e&&a.started&&!a.done))return!0;const r=t.filter(a=>rt(a,e)).pop();return!!(r&&!r.reply&&!s.some(a=>String(a)>String(r.ts))&&!n.some(a=>a.kind===e&&a.done&&String(a.started)>=String(r.ts)))}l(At,"planningOf");const os=30*6e4;function ss(e,t){const n=[];for(const s of e.items){if(pe(s)||(s.agents||[]).some(a=>a.kind==="prototype"&&a.started&&!a.done))continue;const r=s.prototyping&&t.filter(a=>a.data?.key===s.key&&rt(a,"prototype")).pop();r&&n.push({key:s.key,request:r.id,since:r.ts});for(const a of e.owner.filter(u=>u.topic===`item:${s.key}`&&u.state==="requested"))n.push({key:s.key,request:a.request.id,question:a.id,since:a.request.ts})}return n}l(ss,"prototypeWaits");function Ve(e={}){return Object.entries(e).filter(([,t])=>t&&typeof t=="object").map(([t,n])=>({name:t,kind:Bt(n.kind)||null,model:n.model||(n.kind==="opus"||n.kind==="sonnet"?n.kind:null),label:n.label||null,started:n.started||null,eta:n.done?null:n.eta||null,done:n.done||null,log:n.session?.log||null,calls:Array.isArray(n.calls)?n.calls:[],verified:n.verified||null,stopped:n.stopped||null,handed_back:n.handed_back||null,...n.kind===Qe?{writes:!!n.writes,requested:n.requested||null}:{},...n.waits?{waits:n.waits}:{}})).sort((t,n)=>String(t.started||"").localeCompare(String(n.started||""))||t.name.localeCompare(n.name))}l(Ve,"agentsOf");function at(e={}){return Object.values(e).filter(t=>t?.worktree?.path).sort((t,n)=>String(t.started||"").localeCompare(String(n.started||""))).at(-1)?.worktree.path??null}l(at,"worktreeOf");function Xn(e,t=[]){const n=e.filter(te).map(s=>String(s.started||"")).filter(Boolean).sort().pop();return n===void 0?null:t.filter(s=>s.kind==="status"&&s.phase&&!s.planner&&String(s.ts)>=n).sort((s,r)=>String(s.ts).localeCompare(String(r.ts))).pop()?.phase||null}l(Xn,"reachedPhaseOf");function Yn(e,t=[],n,s=null,r=null,a=!1){const u=e.filter(E=>E.started&&!E.done&&E.kind!==fe),d=u.every(ee),p=d?u:u.filter(te),f=p.map(E=>String(E.started||"")).sort()[0];if(f===void 0)return null;const g=t.filter(E=>E.kind==="status"&&String(E.ts)>=f&&(d||!E.planner)).sort((E,C)=>String(E.ts).localeCompare(String(C.ts))).pop(),c=p.filter(E=>E.kind!==Qe).at(-1),h=p.some(E=>E.kind===be.install)?be.install:c?Bt(c.kind):"claude",m=d?null:p.find(E=>E.stopped&&String(E.stopped.at)>=String(E.started))?.stopped,x=g?.ts||f,S=m?m.at:s!==null&&s>Date.parse(x)?new Date(s).toISOString():x,L=g?g.phase||g.planner?g.text.replace(": "," \xB7 "):g.text||null:d?Qn[h]:null,_=n-Date.parse(S),P=!d&&!m&&r==="held",T=d||P?null:m?!1:r,K=P?!1:T===null?_>=ft:!T;return{progress:L,phase:g?.phase||null,at:x,kind:h,planner:d,silentSince:S,stale:K,alive:T,held:P,quiet:T===!0&&!a&&_>=pn,stopped:m?Et(m.run):null}}l(Yn,"builderOf");const lt={opus:"Claude Opus",sonnet:"Claude Sonnet",codex:"Codex"},zn=l(({model:e,kind:t})=>lt[e]||lt[t]||(t==="claude"?lt.opus:null),"modelName"),Bt=l(e=>e==="opus"||e==="sonnet"?"claude":e,"providerOf");function ve(e){const t=zn(e),{kind:n}=e,s=e.label?.replace(/\bbuilder\b/,"implementer").replace(/\bBuilder\b/,"Implementer"),r=l(u=>u[0].toLowerCase()+u.slice(1),"lower");if(t&&ee(e))return`${t} ${n}`;if(t)return`${t} ${r(s?.replace(/^(?:claude\s+)?(?:opus|sonnet|codex)\b\s*/i,"")||"implementer")}`;const a=n?n[0].toUpperCase()+n.slice(1):"";return a?s?s.toLowerCase().includes(n.toLowerCase())?s:`${a} ${r(s)}`:a:s||"Implementer"}l(ve,"agentName");const Rt=l((e,t)=>!!e.resolved&&(!e.resolved_at||String(e.resolved_at)>=String(t)),"resolvedSince");function Jn(e,t,n=[],s=[],r=[]){const a=t.filter(S=>S.data?.id===e.id),u=a[a.length-1]||null,d=r.filter(S=>S.data?.question===e.id).at(-1),p=l((S,...L)=>!!S&&L.every(_=>String(S.ts)>String(_||"")),"after"),f=!!(e.resolved&&e.resolved_at&&u&&String(u.ts)>e.resolved_at),g=n.filter(S=>S.data?.question===e.id&&p(S,u?.ts,d?.ts)).at(-1),c=p(d,u?.ts,g?.ts,e.resolved_at),h=e.resolved&&!f&&!c,m=h?s.filter(S=>S.data?.question===e.id&&String(S.ts)>String(e.resolved_at||"")).at(-1):null,x=c?"open":h?"resolved":g?"requested":u?u.reply?"replied":"answered":"open";return{...e,state:x,reopened:c,request:g?{id:g.id,ts:g.ts,note:g.data.note||"",choice:g.data.choice||null}:null,reask:m?{id:m.id,ts:m.ts}:null,answers:a.map(S=>({ts:S.ts,choice:S.data.choice||null,note:S.data.note||"",files:S.data.files||[],reply:S.reply,acted:Rt(e,S.ts)}))}}l(Jn,"ownerQuestion");const ge=l(e=>e.round??(qe.test(e.url||"")?1:0),"roundOf");function Zn(e,t){const n=l((s,r)=>s.confirm_of||(s.options||[]).some(a=>a.id===r.choice),"settles");return e.filter(s=>ge(s)===t&&s.answers.length).map(s=>[s,s.answers.at(-1)]).filter(([s,r])=>n(s,r)).map(([,s])=>s).sort((s,r)=>String(s.ts).localeCompare(String(r.ts))).at(-1)}l(Zn,"roundSettledBy");function eo(e){const t=e.find(r=>r.request&&!e.some(a=>ge(a)>ge(r)));if(t)return{n:ge(t)+1,state:"making"};const n=Math.max(0,...e.map(ge));if(!n)return null;const s=Zn(e,n);return s?{n,state:"closed",take:s.choice}:e.some(r=>ge(r)===n&&r.state!=="resolved")?{n,state:"back"}:null}l(eo,"roundState");function to(e){const t=e.filter(n=>qe.test(n.url||"")).map(n=>({n:ge(n),q:n.id,url:n.url,confirm:!!n.confirm_of,state:n.state})).sort((n,s)=>n.n-s.n);return t.length?{open:t.some(n=>n.state==="open"),rounds:t}:null}l(to,"prototypesOf");const xe=l((e,t,n)=>{const s=e.get(t);s?s.push(n):e.set(t,[n])},"add"),Oe=l((e,t)=>{const n=new Map;for(const s of e)xe(n,t(s),s);return n},"groupBy");function no(e,t,n=new Set){const s=new Set(e.map(u=>u.id)),r=[...e];t.some(u=>!s.has(u.topic))&&r.push({id:"",title:"Other"});const a=Oe(t,u=>s.has(u.topic)?u.topic:"");return r.map(u=>{const d=a.get(u.id)||[],p=l(c=>d.filter(h=>h.state===c).length,"count"),f=d.every(c=>c.state==="resolved")||n.has(u.id)&&!p("open"),g=d.length&&f?"resolved":"open";return{...u,state:g,questions:d.map(c=>c.id),open:p("open"),answered:p("answered"),replied:p("replied"),resolved:p("resolved"),round:eo(d)}}).filter(u=>u.questions.length)}l(no,"ownerTopics");const He={implement:"Implementation requested",prototype:"Prototype requested",grill:"Grill requested",visual:"Visual requested",reask:"Re-ask requested",hold:"Held",resume:"Resumed",done:"Marked done",related:"Related to",unrelated:"Unrelated from",followup:"Follow-up","followup-of":"Follow-up of"},_t={"grill-more":"grill","prototype-request":"prototype","visual-request":"visual","reask-request":"reask"},Mt={"mark-ready":"Mark ready",...Object.fromEntries(Object.entries(_t).map(([e,t])=>[e,He[t]]))},oo=["grill-more","prototype-request","visual-request"],so="answers submitted",It={reply:"Reply",build:"Implement"},ro=l(e=>It[e.data.intent],"intentLabel"),ao=l((e,t)=>{if(e.kind!=="prototype-request"||!t.has(e.data.question))return e.data.text||(e.data.files?.length?"":A(Mt,e.kind))||"";const n=t.get(e.data.question),s=(n.options||[]).find(r=>r.id===e.data.choice);return[Dt(t,n),s&&`${s.id}. ${s.label}`,e.data.note].filter(Boolean).join(`
|
|
3
|
-
\u2192 `)},"actionText"),
|
|
4
|
-
\u2192 ${
|
|
1
|
+
var Qt=Object.defineProperty;var i=(e,t)=>Qt(e,"name",{value:t,configurable:!0});import{routeRecord as B}from"../hub/routes.mjs";import Ht from"node:fs";import{ticketBadge as ft,ticketIn as Vt}from"./tickets.mjs";import{prLinks as Xt,reviewLinks as Yt,reviewTime as zt,reviewersOf as Jt}from"./review.mjs";import{remoteFromInput as Zt}from"../hub/repos.mjs";import{reviewsOf as en,reviewNeeds as tn,reviewActions as nn}from"./auto-review.mjs";import{cardPRs as sn,hasPR as tt,leastReady as on,prKeyOf as rn,prURL as an}from"../shared/card-prs.mjs";import gt from"node:vm";import{checkWords as ln,parseCheck as dn}from"../shared/owner-check.mjs";import{buildScriptOf as cn}from"./build-script.mjs";import{HUB as Ge,hubLineOf as kt}from"./hub-lines.mjs";const nt=gt.createContext({});for(const e of["ui-enums.js","ui-order.js"])gt.runInContext(Ht.readFileSync(new URL(`./public/${e}`,import.meta.url),"utf8"),nt);const{BUTTON:ae,ACTION_KIND:D,BUILDER_KIND:pe,PR_STATE:H,PHASES:mt}=nt.BoardUI,{laneOrder:un}=nt.BoardUI,pn=["item-message","mark-ready","grill-more","prototype-request","visual-request","reask-request"],wt="question-reopen",go=Object.freeze({todo:"Todo",progress:"In Progress",review:"In Review",done:"Done"}),xe="move",fn=10*6e4,gn=5*6e4,ht="build-refused",De=Object.freeze({ask:"builder-ask",answer:"builder-answer"}),kn=864e5,fe=i(e=>e.lane==="done"||e.lane==="asks","closedLane"),mn=i(e=>`file:${e}`,"fileFold"),yt=7*kn,bt=i((e,t)=>({id:e,url:t?.workspace?`https://linear.app/${t.workspace}/issue/${e}`:null}),"linearRef"),wn=i((e,t)=>String(e).replace(/^S(\d+)$/,`${t||"INT"}-$1`),"internalRef"),Qe=i((e,t,n)=>Vt(n,t.ticket)?t.ticket:e.internal===!0?wn(t.id,n?.prefix):t.id,"refOf"),hn=i((e,t,n,s)=>ft(e||(t?{source:"internal",prefix:"INT"}:null),n,s)||(t?{id:String(t),url:null,source:null}:null),"ticketOf");function yn(e,t,n,s,r,a,d,c,u){return!a&&(e.lane==="done"||t?.state==="MERGED"||t?.state==="CLOSED")?"done":(n||s)&&!c?"progress":r?"review":t?.state==="OPEN"?t.checks?.state==="fail"&&(!t.isDraft||d)?"progress":"review":t||u?"progress":"todo"}i(yn,"laneOf");const He="Lander",St=i(e=>e.kind!==pe.install&&e.label!==He,"changesCode");function bn(e,t){return e?.state==="MERGED"||t?H.merged:e?.state==="CLOSED"?H.closed:e?.state==="OPEN"?e.isDraft?H.draft:H.open:null}i(bn,"prStateOf");const vt=i(e=>e.pr?.status===H.closed,"unlanded"),Sn=i(e=>e.some(t=>t.kind==="work")&&e.every(t=>t.lane==="done"),"finishedOf");function st(e){const t=xt(e);return t?.done&&t.handed_back?t.handed_back:null}i(st,"handedBackAt");const xt=i(e=>e.filter(t=>t?.started&&V(t)&&t.kind!==pe.install).sort((t,n)=>String(t.started).localeCompare(String(n.started))).at(-1),"newestBuild");function vn(e,t,n){return Object.fromEntries(e.map(s=>{const r=B(t?.reviews,s.github),a=r==="APPROVED"?"approved":r==="CHANGES_REQUESTED"?"changes":Object.hasOwn(n,s.name)?"sent":"unsent";return[s.name,{state:a,since:a==="sent"?B(n,s.name):null,github:s.github||null}]}))}i(vn,"reviewOf");function xn(e,t,n,s,r,a,{held:d,needsApproval:c}){return n?.kind===pe.install?"builder":s?"checking":n?"builder":d&&e==="review"?"merge":r&&e==="review"?"owner":e==="review"&&t?.state==="OPEN"&&t.isDraft?a?"ci":"ready":e==="review"?Ot(t,c)?"merge":"reviewers":null}i(xn,"waitsOnOf");const Ot=i((e,t)=>e?.reviewDecision==="APPROVED"||!t&&!["REVIEW_REQUIRED","CHANGES_REQUESTED"].includes(e?.reviewDecision),"reviewed"),Ce=/(^|\/)prototype-[\w-]+\.html/,On={reviewers:"Waiting for PR reviewers",merge:"Approved, merges once CI and the merge state allow",ci:"Marked ready for review once CI passes"},Ve={waiting:"Approved, Merge shows once CI and the merge state allow",ready:"Approved and green: tap Merge",merging:"Merging"},ot={auto:"Ready, merges once CI and the merge state allow",waiting:"Ready, Merge shows once CI and the merge state allow",ready:"Ready and green: tap Merge"},$n=i(e=>`Verified: tap Merge to land it on ${e}`,"heldNext"),An="Fix the failing CI",$t="Tap Done once it is settled",En=["status","agent","ask-link","command",xe,De.answer],At=i(e=>(e.who==="claude"||e.via==="github")&&!En.includes(e.kind)&&(e.kind!==Ge||kt(e).fault),"countsUnread"),Bn=["lander-handoff","land-waiting"];function _n({slug:e,itemFile:t,it:n,order:s,prs:r,repo:a,remote:d,base:c,builder:u,reachedPhase:f,agents:g,worktree:p,eta:w,thread:v,events:R,working:x,said:N,askedAt:C,topicItems:_,prSlack:T,boardReviewers:P,now:y,bumpedAt:b,holdAsk:l,closedAt:E,mergeAt:h,autoMerge:ne,skipDraft:ie,tickets:F}){const X=r.map(m=>({repo:m.repo,number:m.number,url:an(m),...m.gh||m.kept||{},status:bn(m.gh,m.merged_at)})),K=on(r.map(m=>m.gh)),L=r[K]?.gh||null,Y=X[K]?.status??null,z=n.lane||null,Te=!!E&&String(E)>String(n.lane_at||"");Te&&(n={...n,lane:"done",closed_at:E,reopened_at:void 0});const W=[n.closed_at,...r.flatMap(m=>[m.merged_at,m.gh?.mergedAt])].filter(Boolean).map(String).sort().pop()||null,U=String(W||""),Le=X.length>0&&X.every(m=>m.status===H.closed),G=(n.lane==="done"||n.reopened_at||Y===H.merged)&&!Le&&([...N.filter(m=>m.kind==="item-message").map(m=>m.ts),...C].map(String).filter(m=>m>U).sort().pop()||n.reopened_at)||void 0,me=n.owner_step&&!Te?String(n.owner_step):null,ze=(n.lane==="done"||Y===H.merged)&&!Le,Pe=!!G&&!!n.next&&String(n.next_at||"")>String(n.reopened_at||W||"");G&&!Pe&&(n={...n,next:void 0});const Be=st(g),Je=!!G&&N.some(m=>String(m.ts)>U&&(m.state==="waiting"||m.state==="working")),Q=`${e}/${n.id}`,J=Jt(t,P),_e=Object.fromEntries(Object.entries(Yt(t,n,Q,_,Xt(T,r))).map(([m,ee])=>[m,zt(ee.ts)])),we=Object.values(_e).filter(Boolean).sort()[0]||null,he=[...v.map(m=>m.ts),...R.map(m=>m.ts)].sort().pop()||null,je=[...v.filter(m=>At({...m,who:m.who||"claude"})).map(m=>m.ts),...R.map(m=>m.reply?.at)].filter(Boolean).sort(),Z=!tt(n)&&n.pushed?.branch?{branch:n.pushed.branch,at:n.pushed.at||null,host:d?.host||null}:null,se=!tt(n)&&n.merge_ready?.branch?{branch:n.merge_ready.branch,at:n.merge_ready.at||null,base:c}:null,de=!!n.rebase_run&&L?.state==="OPEN",ye=(n.rebased||[]).at(-1),Re=g.filter(m=>m.started&&!m.done&&V(m)),Ue=!!Be&&St(xt(g));let M=yn(n,L,Re.some(St),Ue,Z||se,!!G,ie,de,Re.length>0||!!Be);G&&M==="todo"&&(M="progress");const Ze=M==="review"&&!!we&&y-Date.parse(we)>yt,oe=!!me&&ze&&M==="done";oe&&(M="review");let j=xn(M,L,u,!!Be&&M!=="done",Z,ie,{held:se,needsApproval:J.length>0});const be=j==="builder"&&L?.state==="OPEN"&&!L.isDraft,Me=M==="progress"&&!j&&L?.state==="OPEN"&&(!L.isDraft||ie),re=oe||!!G&&M==="progress"&&!j&&!Me&&!Je&&!Pe;re&&(j="owner");const Ie=l&&String(l.ts)>String(n.held_at||""),qe=M==="todo"&&(Ie?!!l.data.hold:!!n.held),et=qe&&(Ie?l.data.until:n.pick_up_at)||null,We=M!=="done",o=!!b&&!(U&&String(b)<=U),k=v.filter(m=>(m.who||"claude")==="claude"&&!["status","command",xe].includes(m.kind)&&!Bn.includes(m.line)).map(m=>String(m.ts)).sort().pop()||"",S=[h,se&&n.merge_ready.tapped].filter(Boolean).map(String).sort().pop(),O=j==="merge"&&!!S&&S>k,$=v.filter(m=>(m.kind==="status"||m.kind===Ge)&&String(m.ts)>String(S)).sort((m,ee)=>String(m.ts).localeCompare(String(ee.ts))).at(-1),q=$?.line==="land-waiting"?`${Ve.merging} \xB7 ${$.text[0].toLowerCase()}${$.text.slice(1)}`:Ve.merging,I=v.filter(m=>!["status",xe].includes(m.kind)).sort((m,ee)=>String(m.ts).localeCompare(String(ee.ts))).at(-1),ce=M==="todo"&&I?.line===ht?I.text:null,ue=G||g.filter(V).map(m=>m.started).filter(Boolean).map(String).sort().pop()||null,Fe=L?.reviewDecision==="APPROVED",Ke=Z&&j==="owner"?`merge ${Z.branch} into ${c}`:se&&j==="merge"?O?q:$n(c):j==="merge"&&!ne?O?q:(Fe?Ve:ot).waiting:j==="merge"&&!Fe?ot.auto:On[j],Se=Y===H.draft?`Review draft PR #${X[K].number}`:null,ve=!de&&ye?.to&&ye.to===L?.headRefOid?`Rebased onto ${ye.base||c}, pushed${L.mergeable==="MERGEABLE"?" \xB7 PR is clean again":""}`:null,A=de&&j==="checking"?g.flatMap(m=>m.calls).sort((m,ee)=>String(m.at).localeCompare(String(ee.at))).at(-1)?.text??null:null;return{key:Q,kind:"work",order:s,file:e,fileTitle:t.title||e,id:n.id,ref:Qe(t,n,F),title:n.title,storedLane:z,created:n.created||null,reviewSince:n.review_since||we||n.merge_ready?.at||n.pushed?.at||n.created||null,progressSince:ue,closedAt:M==="done"?W:null,internal:t.internal===!0,repo:t.internal===!0&&!t.repo?null:a,lane:M,waitsOn:j,builder:j==="checking"?null:u,reachedPhase:f,agents:g,worktree:p??null,eta:w,working:x,reopened:!!G,awaitsDone:re,ownerStep:re?me:null,ownerCheck:re&&me&&n.owner_check?{words:ln(dn(n.owner_check.check)),broken:n.owner_check.broken||null}:null,stagedAt:M==="todo"&&n.phase==="staging"&&n.staged_at||null,roundAt:n.round_at||null,deps:n.deps||[],blockedUntil:n.blocked_until||null,blockReason:n.block_reason||null,held:qe,heldUntil:et,risky:!!n.risky,bumpable:We,bumpedAt:We&&o?b:null,holdable:M!=="done",hasNotes:!!(n.plan||String(n.notes||"").trim()),finishable:M!=="done",merging:O,refusal:ce,manualSteps:n.manual_steps||[],fromAsk:n.from_ask||null,unfiled:!!n.unfiled,filedHeld:!!n.filed_held,next:be||M==="done"||j==="checking"?null:Me?An:ve||Ke||n.next||Se||null,rebaseFailed:A,branch:n.branch||L?.headRefName||Z?.branch||null,pushed:M==="done"||oe?null:Z,mergeReady:M==="review"?se:null,stale:M!=="done"&&(Ze||!!u?.stale),stalled:n.stalled?.reason||null,landStopped:M==="review"&&se&&n.merge_ready.stopped&&String(n.merge_ready.stopped)>String(n.merge_ready.tapped||"")?{words:Rt("land"),at:n.merge_ready.stopped}:null,ticket:F?.source==="linear"&&!n.ticket&&n.linear?.length?null:hn(F,n.ticket,n.ticket||(t.internal?Qe(t,n,F):n.linear?.[0]),{repo:a,key:Q}),ticketSource:F?.source||null,linear:(n.linear||[]).map(m=>F?.source==="linear"&&ft(F,m,{key:Q,repo:a})||bt(m,F)),pr:X[K]||null,prs:X,reviewers:J.map(m=>m.name),review:vn(J,L||r[K]?.kept,_e),inReviewSince:we,threadCount:v.length+R.length,lastTs:he,othersAt:je,grilling:Mt("grill",R,g,C),prototyping:Mt("prototype",R,g,C),storedPhase:n.phase||null,phase:null,updatedAt:L?.mergedAt||L?.updatedAt||r[K]?.merged_at||u?.at||he||null,ask:null,fold:null,action:null,install:M==="done"?null:Mn(n.install,L),waitFacts:Rn(n,{agents:g,thread:v,events:R,refusedAt:ce?I.ts:null})}}i(_n,"workItem");function Rn(e,{agents:t,thread:n,events:s,refusedAt:r}){const a=new Set(n.filter(f=>f.kind===De.answer).map(f=>f.ask)),d=n.filter(f=>f.kind===De.ask&&!a.has(f.ask)).sort((f,g)=>String(f.ts).localeCompare(String(g.ts)))[0],c=i(f=>s.filter(g=>at(g,f)).pop()?.ts||null,"requestAt"),u=n.filter(f=>(f.who||"claude")==="claude"&&!["status","agent","command",xe].includes(f.kind)&&f.line!==ht).map(f=>f.ts);return{handedBackAt:st(t),next:e.next||null,ask:d?{at:d.ts,text:d.text}:null,grillAt:c("grill"),prototypeAt:c("prototype"),refusedAt:r,unblockedAt:e.unblocked_at||null,unverified:t.some(f=>f.calls.some(g=>!f.verified||String(g.at)>String(f.verified.at))),writes:[...u,...t.flatMap(f=>[f.verified?.at,f.started])].filter(Boolean).map(String).sort()}}i(Rn,"waitFactsOf");function Mn(e,t){if(!e?.url)return null;const n=e.sha?String(e.sha).toLowerCase():null,s=t?.headRefOid?String(t.headRefOid).toLowerCase():null;return{url:e.url,label:e.label||null,builtAt:e.built_at||null,sha:n,older:!!(n&&s&&!s.startsWith(n))}}i(Mn,"installOf");function In(e,{needsApproval:t=!0}={}){if(e?.state!=="OPEN")return[`PR is ${String(e?.state||"unknown").toLowerCase()}`];const{state:n,pending:s,failing:r}=e.checks||{};return[e.isDraft&&"it is a draft",!Ot(e,t)&&`review: ${String(e.reviewDecision||"none").toLowerCase().replace(/_/g," ")}`,n==="fail"&&`failing: ${r?.length?r.join(", "):"a check"}`,n==="pending"&&`pending: ${s?le(s,"check"):"checks"}`,e.mergeStateStatus!=="CLEAN"&&`merge state: ${String(e.mergeStateStatus||"unknown").toLowerCase()}`].filter(Boolean)}i(In,"mergeBlockers");const qn=i((e,t)=>!In(e,{needsApproval:t}).length,"mergeable");function Dn(e,t,n){if(fe(e))return null;const s=`#selectedCard=${encodeURIComponent(e.key)}`;if(e.stalled)return{kind:D.stalled,url:s,label:`stalled: ${e.stalled}`};if(Cn(e)){const r=t.get(e.questions.first);return Ce.test(r?.url||"")?{kind:D.pick,url:`questions.html#${r.id}`,label:"Prototypes ready: pick a take"}:{kind:D.questions,url:`questions.html#${e.questions.first}`,label:`Answer ${le(e.questions.open,"question")}`}}return e.waitsOn==="ready"?{kind:D.ready,url:e.pr.url,label:`Review draft PR #${e.pr.number}`}:e.kind==="ask"&&e.waitsOn==="owner"?{kind:D.done,url:s,label:e.next||$t}:e.waitsOn==="merge"&&e.manualSteps.length&&!e.mergeReady?{kind:D.steps,url:s,label:"Run the manual steps, then merge"}:e.waitsOn==="merge"&&(!n||e.mergeReady)&&!e.merging&&e.prs.every(r=>["MERGED","CLOSED"].includes(r.state)||qn(r,e.reviewers.length>0))?{kind:D.merge,url:s,label:"Merge"}:null}i(Dn,"actionOf");const Cn=i(e=>!!(e.questions&&e.questions.state!=="resolved"&&e.questions.open>0),"asking"),rt=new Set([D.pick,D.questions]);function Et(e){return e.questions?ae.openQuestions:null}i(Et,"actionButtonOf");const Nn=i(e=>e.kind==="work"&&e.lane==="todo"&&!e.action&&!e.unpicked,"buildableOf"),Tn=i((e,t)=>/^item:/.test(e.topic||"")&&Ce.test(e.url||"")&&!(e.options||[]).some(n=>n.id===t),"picksNoTake");function Ln(e,t){const n=e.filter(a=>a.topic===`item:${t}`),r=[...n.filter(a=>Ce.test(a.url||"")).flatMap(a=>a.answers.map(d=>({q:a,a:d,again:Tn(a,d.choice)}))),...n.filter(a=>a.request).map(a=>({q:a,a:a.request,again:!0}))].sort((a,d)=>String(a.a.ts).localeCompare(String(d.a.ts))).at(-1);return r?.again?{id:r.q.id,note:r.a.note.replace(/\s+/g," ").trim()}:null}i(Ln,"noTakePicked");const Pn="Prototype again",jn=Object.freeze({[D.pick]:ae.openQuestions,[D.questions]:ae.openQuestions,[D.ready]:ae.github,[D.steps]:ae.github,[D.merge]:ae.merge,[D.done]:ae.done,[D.stalled]:ae.reply}),Un="Merges once unblocked",Wn=i(e=>`Blocked ${[e.blockedBy.length&&`by ${e.blockedBy.map(t=>t.ref).join(", ")}`,e.blockedUntil&&`until ${e.blockedUntil}`].filter(Boolean).join(" and ")}`,"mergeBlockedWords");function Fn(e,t,n){if(e.action=Dn(e,t,n),e.mergeBlocked=e.action?.kind===D.merge&&e.blocked?Wn(e):null,e.mergeBlocked&&Object.assign(e,{action:null,next:Un}),e.action?.kind===D.questions&&e.questions.fromAsks){Object.assign(e,{action:null,waitsOn:"owner",next:e.action.label,actionButton:Et(e),resolvedBy:null});return}e.awaitsDone&&=!e.action,e.awaitsDone||Object.assign(e,{ownerStep:null,ownerCheck:null}),e.actionButton=Et(e),e.resolvedBy=jn[e.action?.kind]||null,e.action?.kind===D.merge&&!e.mergeReady&&(e.next=(e.pr?.reviewDecision==="APPROVED"?Ve:ot).ready),e.awaitsDone&&Object.assign(e,{lane:"review",next:e.ownerStep||$t,bumpable:!1,bumpedAt:null});const s=e.lane==="todo"||e.waitsOn==="ready"||e.lane==="progress"&&e.waitsOn==="owner";if(!e.action||!s||e.stalled)return;const r=e.lane==="todo"&&rt.has(e.action.kind)?"todo":"review";Object.assign(e,{lane:r,waitsOn:e.waitsOn==="ready"?"ready":"owner",next:e.action.label})}i(Fn,"placeOwnerWait");const Bt=i((e,t)=>e?e!=="todo":t.some(V),"started"),_t="none set yet";function ko(e,t){const n=t.filter(s=>s?.started);return e.next||tt(e)||e.pushed||e.held||e.lane==="done"||st(n)?!1:!n.some(s=>!s.done)&&Bt(e.lane,n)}i(ko,"owesNext");function Kn(e,t=new Date){const n=new Date(e),s=n.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"});return n.toDateString()===t.toDateString()?s:`${n.getMonth()+1}/${n.getDate()} ${s}`}i(Kn,"askedLabel");function Gn(e,t,n,s,r,a,d,c){const u=t.find(h=>h.kind==="ask"&&h.eventId===e.id),f=u.files,g=c||(u.text||"").trim().split(`
|
|
2
|
+
`)[0]||f.map(h=>h.name).join(", ")||"Ask",p=u.state==="replied"||u.state==="answered",w=r?.state==="progress"&&!(String(a||"")>String(r.at||"")),v=!w&&(!!r||!!a),R=p&&!v&&!w&&d.open>0,x=p&&!v&&!w&&!R&&d.answered>0,N=v||p&&!w&&!R&&!x,C=p&&!s&&!r?.moved&&[...t.filter(h=>h.who==="claude"&&h.kind!=="ask-link"&&h.kind!=="status"&&h.kind!=="command").map(h=>h.ts),...t.map(h=>h.reply?.at)].filter(Boolean).sort().pop()||null,_=N||w||R||x,T=t.some(h=>h.who==="love"&&h.kind!=="ask"&&(h.state==="waiting"||h.state==="working")),P=t.filter(h=>h.who==="love"&&h.kind==="item-message").flatMap(Ne).filter(([,h])=>h==="waiting"||h==="working").map(([h])=>h),y=_?"answered":u.state,b=_?null:n||null,l=[...t.filter(At).map(h=>h.ts),...t.map(h=>h.reply?.at)].filter(Boolean).sort(),E=[...t.map(h=>h.ts),...l,...w?[r.at]:[]].filter(Boolean).sort().pop();return{key:`ask:${e.id}`,kind:"ask",order:0,file:null,fileTitle:null,id:e.id,ref:_?Kn(e.ts):"New",isNew:!_,title:g,storedLane:null,internal:!1,repo:null,lane:w?"progress":N?"asks":"todo",waitsOn:w&&!T?"owner":null,builder:null,agents:[],eta:null,working:t.some(h=>Ne(h).some(([,ne])=>ne==="working")),deps:[],held:!1,risky:!1,bumpable:!_,bumpedAt:b,manualSteps:[],fromAsk:null,from:null,next:w&&r.next||null,hasNotes:!1,branch:null,finishable:_&&!N,linear:[],pr:null,prs:[],reviewers:[],review:{},inReviewSince:null,reviewSince:null,progressSince:w&&r.at||null,closedAt:null,threadCount:t.length,lastTs:E,othersAt:_?l:[],grilling:!1,prototyping:!1,created:e.ts,updatedAt:_?E:e.ts,ask:{eventId:e.id,state:y,askedAt:e.ts,bumpedAt:b,edited:!!u.edited,canEdit:!_,text:u.text,files:f,answerAt:C,from:u.from||null,owed:P},fold:null,action:null}}i(Gn,"askItem");const ge="visual",Oe="helper",Qn=new Set(["grill","prototype",ge]),te=i(e=>Qn.has(e.kind),"plans"),V=i(e=>!te(e),"builds"),Hn={grill:"grilling",prototype:"prototyping"},Vn=i((e,t)=>`${e} ${t} by orchestrator`,"orchestratorDid"),Rt=i(e=>Vn(e,"stopped"),"stoppedWords"),Xn={grill:"grill-more",prototype:"prototype-request"},at=i((e,t)=>e.kind===Xn[t]&&!e.data?.question,"isRequest");function Mt(e,t,n,s){if(n.some(a=>a.kind===e&&a.started&&!a.done))return!0;const r=t.filter(a=>at(a,e)).pop();return!!(r&&!r.reply&&!s.some(a=>String(a)>String(r.ts))&&!n.some(a=>a.kind===e&&a.done&&String(a.started)>=String(r.ts)))}i(Mt,"planningOf");const mo=30*6e4;function wo(e,t){const n=[];for(const s of e.items){if(fe(s)||(s.agents||[]).some(a=>a.kind==="prototype"&&a.started&&!a.done))continue;const r=s.prototyping&&t.filter(a=>a.data?.key===s.key&&at(a,"prototype")).pop();r&&n.push({key:s.key,request:r.id,since:r.ts});for(const a of e.owner.filter(d=>d.topic===`item:${s.key}`&&d.state==="requested"))n.push({key:s.key,request:a.request.id,question:a.id,since:a.request.ts})}return n}i(wo,"prototypeWaits");function lt(e={},t=!1){return Object.entries(e).filter(([n,s])=>s&&typeof s=="object"&&(t||!n.startsWith("past-"))).map(([n,s])=>({name:n,kind:It(s.kind)||null,model:s.model||(s.kind==="opus"||s.kind==="sonnet"?s.kind:null),label:s.label||null,started:s.started||null,eta:s.done?null:s.eta||null,done:s.done||null,log:s.session?.log||null,pid:s.session?.pid??null,calls:Array.isArray(s.calls)?s.calls:[],verified:s.verified||null,stopped:s.stopped||null,handed_back:s.handed_back||null,...s.pause?{pause:s.pause}:{},...s.kind===Oe?{writes:!!s.writes,requested:s.requested||null}:{},...s.waits?{waits:s.waits}:{}})).sort((n,s)=>String(n.started||"").localeCompare(String(s.started||""))||n.name.localeCompare(s.name))}i(lt,"agentsOf");function it(e={}){return Object.values(e).filter(t=>t?.worktree?.path).sort((t,n)=>String(t.started||"").localeCompare(String(n.started||""))).at(-1)?.worktree.path??null}i(it,"worktreeOf");function Yn(e,t=[]){const n=e.filter(V).map(s=>String(s.started||"")).filter(Boolean).sort().pop();return n===void 0?null:t.filter(s=>s.kind==="status"&&s.phase&&!s.planner&&String(s.ts)>=n).sort((s,r)=>String(s.ts).localeCompare(String(r.ts))).pop()?.phase||null}i(Yn,"reachedPhaseOf");function zn(e,t=[],n,s=null,r=null,a=!1){const d=e.filter(b=>b.started&&!b.done&&b.kind!==ge),c=d.every(te),u=c?d:d.filter(V),f=u.map(b=>String(b.started||"")).sort()[0];if(f===void 0)return null;const g=t.filter(b=>b.kind==="status"&&String(b.ts)>=f&&(c||!b.planner)).sort((b,l)=>String(b.ts).localeCompare(String(l.ts))).pop(),p=u.filter(b=>b.kind!==Oe).at(-1),w=u.some(b=>b.kind===pe.install)?pe.install:p?It(p.kind):"claude",v=c?null:u.find(b=>b.stopped&&String(b.stopped.at)>=String(b.started))?.stopped,R=g?.ts||f,x=v?v.at:s!==null&&s>Date.parse(R)?new Date(s).toISOString():R,N=g?g.phase||g.planner?g.text.replace(": "," \xB7 "):g.text||null:c?Hn[w]:null,C=n-Date.parse(x),_=c?null:u.find(b=>b.pause)?.pause??null,T=!c&&!v&&(r==="held"||!!_?.paused),P=c||T?null:v?!1:r,y=T?!1:P===null?C>=fn:!P;return{progress:N,phase:g?.phase||null,at:R,kind:w,planner:c,silentSince:x,stale:y,alive:P,held:T,quiet:!!_||P===!0&&!a&&C>=gn,stopped:v?Rt(v.run):null,..._?{pause:{cause:_.cause,words:_.words,paused:!!_.paused}}:{}}}i(zn,"builderOf");const ho={blocked:["nothing blocks it","nothing blocks it"],held:["you unhold it","unheld"],usage:["the usage pause lifts","the usage pause lifted"],bumped:["a slot frees","a slot freed"]},Jn=15*6e4,Zn=i(e=>(e.agents||[]).find(t=>t.name==="main"&&t.started&&!t.done&&V(t))??null,"mainBuild"),es=i((e,t,n,{now:s,usageHeld:r})=>e.blocked||e.held||r(n??"claude")||!!(t.until&&s<Date.parse(t.until)),"stillPaused");function yo(e,{now:t=Date.now(),usageHeld:n}){const s={ask:[],drop:[],stop:[],resume:[]},r=[];for(const a of e){if(a.kind!=="work"||a.lane!=="progress")continue;const d=Zn(a);if(!d)continue;const{pause:c}=d;if(!c){a.builder?.phase!=="landing"&&n(d.kind??"claude")&&s.ask.push(a.key);continue}const u=es(a,c,d.kind,{now:t,usageHeld:n});if(c.paused){u||r.push({key:a.key,at:c.paused});continue}!u&&c.cause!=="bumped"?s.drop.push(a.key):t-Date.parse(c.at)>=Jn&&s.stop.push(a.key)}return s.resume=r.sort((a,d)=>String(a.at).localeCompare(String(d.at))).map(a=>a.key),s}i(yo,"pauseWork");const dt={opus:"Claude Opus",sonnet:"Claude Sonnet",codex:"Codex"},ts=i(({model:e,kind:t})=>dt[e]||dt[t]||(t==="claude"?dt.opus:null),"modelName"),It=i(e=>e==="opus"||e==="sonnet"?"claude":e,"providerOf");function $e(e){const t=ts(e),{kind:n}=e,s=e.label?.replace(/\bbuilder\b/,"implementer").replace(/\bBuilder\b/,"Implementer"),r=i(d=>d[0].toLowerCase()+d.slice(1),"lower");if(t&&te(e))return`${t} ${n}`;if(t)return`${t} ${r(s?.replace(/^(?:claude\s+)?(?:opus|sonnet|codex)\b\s*/i,"")||"implementer")}`;const a=n?n[0].toUpperCase()+n.slice(1):"";return a?s?s.toLowerCase().includes(n.toLowerCase())?s:`${a} ${r(s)}`:a:s||"Implementer"}i($e,"agentName");const qt=i((e,t)=>!!e.resolved&&(!e.resolved_at||String(e.resolved_at)>=String(t)),"resolvedSince");function ns(e,t,n=[],s=[],r=[]){const a=t.filter(x=>x.data?.id===e.id),d=a[a.length-1]||null,c=r.filter(x=>x.data?.question===e.id).at(-1),u=i((x,...N)=>!!x&&N.every(C=>String(x.ts)>String(C||"")),"after"),f=!!(e.resolved&&e.resolved_at&&d&&String(d.ts)>e.resolved_at),g=n.filter(x=>x.data?.question===e.id&&u(x,d?.ts,c?.ts)).at(-1),p=u(c,d?.ts,g?.ts,e.resolved_at),w=e.resolved&&!f&&!p,v=w?s.filter(x=>x.data?.question===e.id&&String(x.ts)>String(e.resolved_at||"")).at(-1):null,R=p?"open":w?"resolved":g?"requested":d?d.reply?"replied":"answered":"open";return{...e,state:R,reopened:p,request:g?{id:g.id,ts:g.ts,note:g.data.note||"",choice:g.data.choice||null}:null,reask:v?{id:v.id,ts:v.ts}:null,answers:a.map(x=>({ts:x.ts,choice:x.data.choice||null,note:x.data.note||"",files:x.data.files||[],reply:x.reply,acted:qt(e,x.ts)}))}}i(ns,"ownerQuestion");const ke=i(e=>e.round??(Ce.test(e.url||"")?1:0),"roundOf");function ss(e,t){const n=i((s,r)=>s.confirm_of||(s.options||[]).some(a=>a.id===r.choice),"settles");return e.filter(s=>ke(s)===t&&s.answers.length).map(s=>[s,s.answers.at(-1)]).filter(([s,r])=>n(s,r)).map(([,s])=>s).sort((s,r)=>String(s.ts).localeCompare(String(r.ts))).at(-1)}i(ss,"roundSettledBy");function os(e){const t=e.find(r=>r.request&&!e.some(a=>ke(a)>ke(r)));if(t)return{n:ke(t)+1,state:"making"};const n=Math.max(0,...e.map(ke));if(!n)return null;const s=ss(e,n);return s?{n,state:"closed",take:s.choice}:e.some(r=>ke(r)===n&&r.state!=="resolved")?{n,state:"back"}:null}i(os,"roundState");function rs(e){const t=e.filter(n=>Ce.test(n.url||"")).map(n=>({n:ke(n),q:n.id,url:n.url,confirm:!!n.confirm_of,state:n.state})).sort((n,s)=>n.n-s.n);return t.length?{open:t.some(n=>n.state==="open"),rounds:t}:null}i(rs,"prototypesOf");const Ae=i((e,t,n)=>{const s=e.get(t);s?s.push(n):e.set(t,[n])},"add"),Ee=i((e,t)=>{const n=new Map;for(const s of e)Ae(n,t(s),s);return n},"groupBy");function as(e,t,n=new Set){const s=new Set(e.map(d=>d.id)),r=[...e];t.some(d=>!s.has(d.topic))&&r.push({id:"",title:"Other"});const a=Ee(t,d=>s.has(d.topic)?d.topic:"");return r.map(d=>{const c=a.get(d.id)||[],u=i(p=>c.filter(w=>w.state===p).length,"count"),f=c.every(p=>p.state==="resolved")||n.has(d.id)&&!u("open"),g=c.length&&f?"resolved":"open";return{...d,state:g,questions:c.map(p=>p.id),open:u("open"),answered:u("answered"),replied:u("replied"),resolved:u("resolved"),round:os(c)}}).filter(d=>d.questions.length)}i(as,"ownerTopics");const Xe={implement:"Implementation requested",prototype:"Prototype requested",grill:"Grill requested",visual:"Visual requested",reask:"Re-ask requested",hold:"Held",resume:"Resumed",done:"Marked done",staged:"Marked as staging",readied:"Marked as ready",related:"Related to",unrelated:"Unrelated from",followup:"Follow-up","followup-of":"Follow-up of"},Dt={"grill-more":"grill","prototype-request":"prototype","visual-request":"visual","reask-request":"reask"},Ct={"mark-ready":"Mark ready",...Object.fromEntries(Object.entries(Dt).map(([e,t])=>[e,Xe[t]]))},ls=["grill-more","prototype-request","visual-request"],is="answers submitted",Nt={reply:"Reply",build:"Implement"},ds=i(e=>Nt[e.data.intent],"intentLabel"),cs=i((e,t)=>{if(e.kind!=="prototype-request"||!t.has(e.data.question))return e.data.text||(e.data.files?.length?"":B(Ct,e.kind))||"";const n=t.get(e.data.question),s=(n.options||[]).find(r=>r.id===e.data.choice);return[Lt(t,n),s&&`${s.id}. ${s.label}`,e.data.note].filter(Boolean).join(`
|
|
3
|
+
\u2192 `)},"actionText"),Tt=i((e,t)=>[...e.keys()].indexOf(t)+1,"numberOf"),Lt=i((e,t)=>`Question ${Tt(e,t.id)} \xB7 ${t.question}`,"numbered"),us="question reopened",Ye=i(e=>e.data?.shared_by?.login?{by:e.data.shared_by.name||e.data.shared_by.login}:{},"byOf"),ps=i((e,t)=>{const n=t.get(e.card);return{...e,text:`${Xe[e.command]} ${n?`${n.ref}: ${n.title}`:e.card}`,link:`#selectedCard=${encodeURIComponent(e.card)}`}},"linkLine"),Pt=i(e=>({kind:"command",command:e,request:!0,mark:Xe[e]}),"commandLine"),fs=i((e,t)=>{if(e.kind==="item-message")return Nt[e.data.intent]?{status:ds(e)}:{};const n={request:!0,command:Dt[e.kind]};return ls.includes(e.kind)?(e.data.question?t.has(e.data.question):String(e.data.text||"").trim()||e.data.files?.length)?{status:Ct[e.kind]}:n:e.kind==="reask-request"?n:{}},"requestOf"),ct=i(e=>[...e.filter(t=>t.who==="claude"&&!["status","agent","command",xe,De.answer].includes(t.kind)).map(t=>t.ts),...e.filter(t=>t.kind==="ask").map(t=>t.reply?.at)].filter(Boolean).map(String).sort().pop()||"","claudeAtOf");function jt(e,t={}){const n=new Map,s=new Map;for(const p of t.items||[])Ae(n,p.topic,p),s.set(p.id,p.topic);const r=new Map,a=new Map,d=new Map,c=new Map,u=new Map,f=new Map,g=new Map;e.forEach((p,w)=>{g.set(p,w),p.data?.key&&pn.includes(p.kind)&&Ae(r,p.data.key,p),p.kind==="owner-answer"&&s.has(p.data?.id)&&Ae(a,s.get(p.data.id),p),p.kind===wt&&s.has(p.data?.question)&&Ae(d,s.get(p.data.question),p),p.kind==="ask"&&c.set(p.id,p)});for(const p of e.filter(w=>w.kind==="ask-edited").sort((w,v)=>String(w.ts).localeCompare(String(v.ts)))){u.set(p.data?.id,p.data?.text);for(const w of p.data?.files||[])Ae(f,p.data?.id,w)}return{byKey:r,answers:a,reopens:d,asks:c,edits:u,editFiles:f,questions:n,order:g}}i(jt,"threadIndex");const ut=i((e,t)=>{const n=t.filter(s=>s?.length);return n.length>1?n.flat().sort((s,r)=>e.order.get(s)-e.order.get(r)):[...n[0]||[]]},"inLogOrder"),gs=/; its log is \S*\/\.claude\/kickflip\/logs\/([^/\s]+\.log)\.$/,ks=i(e=>{const t=!e.log&&typeof e.text=="string"?gs.exec(e.text):null;return t?{...e,text:`${e.text.slice(0,t.index)}.`,log:t[1]}:e},"withLog");function ms(e,t){const n=t.run?e.find(s=>s.started===t.run):e.filter(s=>te(s)===!!t.planner&&s.started&&String(s.started)<=String(t.ts)&&(!s.done||String(t.ts)<=String(s.done))).pop();return n?{agent:$e(n),run:pt(e,n),running:!n.done}:{}}i(ms,"agentOfLine");function pt(e,t){return t.label!==He?t.started:e.filter(n=>V(n)&&n.label!==He&&n.started&&String(n.started)<String(t.started)).pop()?.started??t.started}i(pt,"sectionOf");function Ut(e,{lines:t=[],events:n=[],owner:s={},working:r={},fromAsk:a=null,links:d=[],agents:c={},moved:u=[],cards:f=new Map,index:g=jt(n,s)}){const p=e.startsWith("ask:")?[e.slice(4)]:[a,...u].filter(Boolean),w=[...new Set([e,...p.map(l=>`ask:${l}`)])],v=new Map((g.questions.get(`item:${e}`)||[]).map(l=>[l.id,l])),R=g.answers.get(`item:${e}`)||[],x=new Set([...v.values()].map(l=>l.round_of).filter(Boolean)),N=lt(c,!0).filter(V).map(l=>String(l.started||"")).filter(Boolean),C=new Set(R.filter(l=>qt(v.get(l.data.id),l.ts)||x.has(l.data.id)||N.some(E=>E>String(l.ts))).map(l=>l.id)),_=lt(c,!0),T=[..._.flatMap(l=>[...l.started?[{ts:l.started,who:"claude",kind:"agent",text:`${$e(l)} started`,agent:$e(l),run:pt(_,l),finished:!1,eta:l.done?null:l.eta,...l.log?{log:l.log}:{},...te(l)?{planner:!0}:{}}]:[],...l.done?[{ts:l.done,who:"claude",kind:"agent",text:`${$e(l)} finished`,agent:$e(l),run:pt(_,l),finished:!0,eta:null,...te(l)?{planner:!0}:{}}]:[],...l.calls.length?[{ts:l.done||l.calls.at(-1).at,who:"claude",kind:"calls",text:$e(l),calls:l.calls.map(E=>E.text),verified:l.verified}]:[]]),...t.map(ks).map(l=>l.card?ps(l,f):l).map(l=>({ts:l.ts,who:l.who||"claude",...l.log?{log:l.log}:{},...l.kind==="status"?{kind:"status",...l.planner?{planner:!0}:{},...ms(_,l)}:{},...l.kind===ge||l.kind===xe?{kind:l.kind}:{},...l.kind===Ge?{kind:Ge,line:l.line,...kt(l)}:{},...l.command?Pt(l.command):{},...l.card?{card:l.card}:{},...Object.values(De).includes(l.kind)?{kind:l.kind,ask:l.ask}:{},text:l.text||"",html:l.html||null,link:l.link||null,via:l.via||null,...l.by?{by:l.by}:{}})),...ut(g,w.map(l=>g.byKey.get(l))).map(l=>({ts:l.ts,who:"love",via:"site",kind:l.kind,text:cs(l,v),files:l.data.files||[],eventId:l.id,reply:l.reply,...fs(l,v),...Ye(l)})),...R.map(l=>{const E=[l.data.label||l.data.choice,l.data.note].filter(Boolean).join(" \xB7 ");return{ts:l.ts,who:"love",via:"questions",kind:l.kind,text:`${Lt(v,v.get(l.data.id))}
|
|
4
|
+
\u2192 ${E}`,files:l.data.files||[],eventId:l.id,reply:l.reply,status:is,...Ye(l)}}),...(g.reopens.get(`item:${e}`)||[]).map(l=>({ts:l.ts,who:"love",kind:l.kind,request:!0,mark:us,text:`Question ${Tt(v,l.data.question)} was reopened`,...Ye(l)})),...ut(g,[...new Set(p)].map(l=>g.asks.has(l)?[g.asks.get(l)]:[])).flatMap(l=>[{ts:l.ts,who:"love",via:"ask",kind:l.kind,text:l.data?.text||"",files:l.data?.files||[],eventId:l.id,reply:l.reply,...u.includes(l.id)?{moved:!0}:{},...Ye(l),...typeof l.data?.from?.title=="string"?{from:l.data.from.title}:{}},...l.data?.quick===!0?[{ts:l.ts,who:"love",...Pt("implement"),text:Xe.implement}]:[],...d.map(E=>({ts:l.ts,who:"claude",kind:"ask-link",text:E.folded?`Moved to ${E.ref}: ${E.title}`:`${E.became?"Became":"Added to"} ${E.ref}`,link:`#selectedCard=${encodeURIComponent(E.key)}`,html:null,via:null}))])].sort((l,E)=>String(l.ts).localeCompare(String(E.ts))),P=ct(T),{edits:y,editFiles:b}=g;for(const l of T){if(!l.eventId)continue;(l.kind==="ask"||l.kind==="item-message")&&typeof y.get(l.eventId)=="string"&&Object.assign(l,{text:y.get(l.eventId),edited:!0,files:[...l.files,...b.get(l.eventId)||[]]});const E=l.kind==="ask"&&(d.length>0||!!l.moved);l.state=l.reply?"replied":E||C.has(l.eventId)||P>String(l.ts)?"answered":B(r,l.eventId)?"working":"waiting",l.canDelete=l.kind==="item-message"&&(l.state==="working"||l.state==="waiting"),l.canEdit=l.canDelete}return ws(T)}i(Ut,"threadOf");const Wt=["waiting","working","answered","replied"];function ws(e){const t=new Map;for(const n of e)n.kind==="owner-answer"&&t.set(n.ts,[...t.get(n.ts)||[],n]);return e.flatMap(n=>{if(n.kind!=="owner-answer")return[n];const s=t.get(n.ts);return s.length===1?[n]:s[0]!==n?[]:[{...n,text:s.map(r=>r.text).join(`
|
|
5
5
|
|
|
6
|
-
`),files:s.flatMap(r=>r.files),state:s.map(r=>r.state).sort((r,a)=>Lt.indexOf(r)-Lt.indexOf(a))[0],reply:void 0,answers:s.map(r=>({eventId:r.eventId,state:r.state,reply:r.reply,text:r.text}))}]})}l(go,"foldSubmits");const De=l(e=>(e.answers||[e]).filter(t=>t.eventId).map(t=>[t.eventId,t.state]),"stateEntries");function ko(e,t,n,s,r,a){const u=l((c,h)=>{const m=a.filter(S=>S.kind==="wave-triage"&&S.data?.[c]===h).pop(),x=r.filter(S=>S.topic===`${c}:${h}`&&(S.state==="open"||S.state==="answered")).length;return{running:!!(m&&!m.reply),questions:x}},"triageOf"),d=new Map(s.map(c=>[c.key,c])),p=l(c=>c.filter(h=>h.lane==="done"&&!ht(h)).length,"merged"),f=n.map(c=>({l:c,cards:c.keys.map(h=>d.get(h)).filter(Boolean)})).map(c=>({...c,open:!c.l.finished_at&&c.cards.some(h=>h.lane!=="done")})).sort((c,h)=>Number(c.open)-Number(h.open));for(const{l:c,cards:h}of f)for(const m of h)m.lineup={slug:c.slug,title:c.title};const g=f.filter(c=>c.open).map(({l:c,cards:h})=>({kind:"lineup",slug:c.slug,title:c.title,goal:c.goal,keys:h.map(m=>m.key),phases:c.phases.map(m=>m.filter(x=>d.has(x))).filter(m=>m.length),next:mo(h),merged:p(h),total:h.length,triage:u("lineup",c.slug)}));return e.fromWaves?[...g,...t.filter(c=>!c.finished).map(c=>({w:c,cards:s.filter(h=>h.wave===c.slug&&h.kind!=="ask")})).filter(({cards:c})=>c.length).map(({w:c,cards:h})=>({kind:"wave",slug:c.slug,title:c.title,goal:null,keys:h.map(m=>m.key),phases:null,next:null,merged:p(h),total:h.length,triage:u("wave",c.slug)}))]:g}l(ko,"waveBandsOf");function mo(e){const t=e.filter(a=>a.lane==="todo"),n=t.find(a=>a.ready&&a.buildable)||t[0];if(!n)return null;const s=n.ready&&n.buildable,r=s?"ready":n.held?"held":n.blockedBy.length?`waits on ${n.blockedBy.map(a=>a.ref).join(", ")}`:n.blockedUntil?`blocked until ${n.blockedUntil}`:n.action?"needs you":n.state||"not ready";return{key:n.key,ref:n.ref,ready:s,word:r}}l(mo,"nextOf");function rs(e,t,n,s){const r=e.waveBands.find(c=>c.kind==="lineup"&&c.slug===t);if(!r)return null;const a=new Map(e.items.map(c=>[c.key,c.lane])),u=r.keys.filter(c=>a.get(c)==="todo"),d=u.indexOf(n),p=d+s;if(d<0||p<0||p>=u.length)return null;[u[d],u[p]]=[u[p],u[d]];let f=0;const g=r.keys.map(c=>a.get(c)==="todo"?u[f++]:c);return f=0,r.phases.map(c=>g.slice(f,f+=c.length))}l(rs,"lineupMoved");const wo=l(e=>e.phases||e.items.map(t=>[t]),"lineupPhases");function as(e){const{owner:t={},topicItems:n={},events:s=[],threads:r={},prs:a={},working:u={}}=e,d=e.now??Date.now(),p=Ct(s,t),f=new Set(s.filter(o=>o.kind==="ask-withdrawn"&&!o.data?.key).map(o=>o.data?.id)),g=new Map(s.filter(o=>o.kind==="ask"&&!f.has(o.id)).map(o=>[o.id,o])),c=l((o,k)=>new Map(s.filter(b=>b.kind===o).sort((b,v)=>String(b.ts).localeCompare(String(v.ts))).map(b=>[b.data?.[k],b.ts])),"latest"),h=c("ask-bump","id"),m=c("card-unbump","key"),x=new Map([...c("card-bump","key")].filter(([o,k])=>!(String(m.get(o)||"")>String(k)))),S=c("card-done","key"),L=c("card-merge","key"),_=e.autoMerge===!0,P=e.tickets,T=new Map(s.filter(o=>o.kind==="card-hold").sort((o,k)=>String(o.ts).localeCompare(String(k.ts))).map(o=>[o.data?.key,o])),K=new Map,E=new Map(e.waves.flatMap(({slug:o,wave:k})=>k.items.map(b=>[`${o}/${b.id}`,{ref:Ke(k,b,P),title:b.title}]))),C=e.askLinks||{},i=new Map;for(const[o,k]of Object.entries(C))g.has(o)&&i.set(k,[...i.get(k)||[],o]);const y=[],B=[],ke=new Map,re=new Map,U=new Map,V=new Map,W=e.github?null:Jt(e.remote);for(const{slug:o,wave:k,agents:b={}}of e.waves){const v=e.github||k.repo||null;y.push({slug:o,title:k.title||o,status:k.status||null,internal:!!k.internal,finished:!1,reviewers:(k.reviewers||[]).map(O=>O.name)}),k.items.forEach((O,M)=>{const I=`${o}/${O.id}`,ce=(i.get(I)||[]).filter($=>$!==O.from_ask),ue=[O.from_ask,...ce].filter($=>g.has($)),We=new Set(ue.map($=>`ask:${$}`)),Fe=[...A(r,I)||[],...ue.flatMap($=>A(r,`ask:${$}`)||[])];O.from_ask&&K.set(O.from_ask,[...K.get(O.from_ask)||[],{key:I,ref:Ke(k,O,P),became:!0}]);const ye=Tt(I,{lines:Fe,events:s,owner:t,working:u,fromAsk:O.from_ask||null,moved:ce,cards:E,index:p});for(const $ of ye)for(const[w,J]of De($))ke.set(w,J);re.set(I,it(ye)),B.push(Bn({slug:o,wave:k,it:O,order:M,repo:v,remote:W,base:e.branch||"main",prs:nn(O,v).map($=>({...$,gh:A(a,sn($))||null})),...($=>({agents:$,builder:Yn($,A(r,I),d,e.moved?.[at(A(b,O.id))]??null,A(e.alive,I)??null,!!e.commandRunning?.[at(A(b,O.id))]),reachedPhase:Xn($,A(r,I)),eta:$.map(w=>w.eta).filter(Boolean).sort().pop()||null}))(Ve(A(b,O.id))),worktree:at(A(b,O.id))??O.worktree?.path??null,thread:Fe,events:[...dt(p,[I,...We].map($=>p.byKey.get($))),...ue.map($=>g.get($))],working:ye.some($=>De($).some(([,w])=>w==="working")),said:ye.filter($=>$.who==="love"&&($.kind==="item-message"||$.kind==="owner-answer")),askedAt:(p.questions.get(`item:${I}`)||[]).filter($=>$.asked_at).map($=>$.asked_at),topicItems:n,prSlack:e.prSlack||{},boardReviewers:e.reviewers||[],now:d,bumpedAt:x.get(I),holdAsk:T.get(I),closedAt:S.get(I),mergeAt:L.get(I),autoMerge:_,skipDraft:e.skipDraft===!0,tickets:P}));const Me=B[B.length-1];V.set(I,{parent:O.parent?jt(o,O.parent):null,related:(O.related||[]).map($=>jt(o,$))}),Me.lane==="done"&&U.set(I,Me.closedAt||String(Me.updatedAt||""))})}const N=new Map(B.map(o=>[o.key,o])),H=new Map,X=e.try||{},Ne=e.autoBuild===!0,F=["web","app"].includes(X.kind)?X.kind:e.waves.some(({wave:o})=>o.items.some(k=>k.install?.url))?"app":"web";for(const o of B){if(o.kind!=="work"||o.lane==="done")continue;const k=A(X.cards,o.key)||{},b=k.head||o.pr?.headRefOid,v=F==="web"?k.sha:o.install?.sha,O=o.builder?.kind===be.install?"running":k.state||(F==="app"&&o.install?"ready":null);o.try={kind:F,state:O,stale:!!(v&&b&&!String(b).toLowerCase().startsWith(String(v).toLowerCase())),url:F==="web"?null:o.install?.url||null,port:F==="web"&&O==="ready"&&k.https||null,sha:v||null,changes:k.ahead>0,script:k.script===!0,autoBuild:Ne}}const j=X.main,Ce=j?.script&&j.sha?{slug:j.slug,branch:j.branch||"main",sha:j.sha.slice(0,7),at:j.at,kind:F,state:j.state||null,url:F==="web"?null:e.main?.install?.url||null,port:F==="web"&&j.state==="ready"&&j.https||null,pull:X.pull?{running:X.pull.running===!0,error:X.pull.error||null}:null,script:dn(j.target)}:null,G=e.askProgress||{},me=c("ask-done","id"),Ye=Oe(s.filter(o=>o.kind==="owner-answer"),o=>o.data?.id),$e=Oe(s.filter(o=>o.kind==="prototype-request"),o=>o.data?.question),ze=Oe(s.filter(o=>o.kind==="reask-request"),o=>o.data?.question),we=Oe(s.filter(o=>o.kind===pt),o=>o.data?.question),Q=(t.items||[]).map(o=>Jn(o,Ye.get(o.id)||[],$e.get(o.id)||[],ze.get(o.id)||[],we.get(o.id)||[])),ne=new Map(Q.map(o=>[o.id,o])),ae=Oe(Q,o=>o.topic),Te=l(o=>{const k=ae.get(o)||[];return{open:k.filter(b=>b.state==="open").length,answered:k.filter(b=>b.state==="answered").length}},"pendingOn");for(const o of g.values()){const k=`ask:${o.id}`,b=N.get(A(C,o.id)),v=[...K.get(o.id)||[]],O=b&&!v.some(I=>I.key===b.key);if(O&&v.push({key:b.key,ref:b.ref,title:b.title,became:!1,folded:!0}),v.length&&!O){H.set(k,v[0].key);continue}if(!O&&A(C,o.id)){H.set(k,A(C,o.id));continue}const M=Tt(k,{lines:A(r,k)||[],events:s,working:u,links:v,index:p});for(const I of M)for(const[ce,ue]of De(I))ke.set(ce,ue);re.set(k,it(M)),B.push(Kn(o,M,h.get(o.id),v.length>0||!!A(C,o.id),A(G,o.id),me.get(o.id),Te(`item:${k}`)))}for(const o of y){const k=B.filter(b=>b.wave===o.slug);if(o.finished=bn(k),o.finished)for(const b of k)b.lane==="done"&&(b.fold=gn(o.slug))}for(const o of B)o.lineup=null;const Ee=new Set((t.topics||[]).map(o=>o.id)),Y=e.waves.flatMap(({slug:o,wave:k})=>(k.lineups||[]).map(b=>{const v=wo(b).map(O=>O.map(M=>`${o}/${M}`));return{...b,phases:v,keys:v.flat()}})),z=l(()=>no([...t.topics||[],...B.filter(o=>!Ee.has(`item:${o.key}`)).map(o=>({id:`item:${o.key}`,card:o.key,title:`${o.ref}: ${o.title}`,...o.kind==="ask"?{askedAt:o.ask.askedAt}:{}})),...Y.filter(o=>!Ee.has(`lineup:${o.slug}`)).map(o=>({id:`lineup:${o.slug}`,title:o.title,summary:"Wave triage."})),...(e.fromWaves?y:[]).filter(o=>!Ee.has(`wave:${o.slug}`)).map(o=>({id:`wave:${o.slug}`,title:o.title,summary:"Wave triage."}))],Q,new Set(B.filter(pe).map(o=>`item:${o.key}`))),"topicsOf"),Ae=new Map(z().map(o=>[o.id,o]));for(const o of B){const k=Ae.get(`item:${o.key}`);if(!k){o.questions=null,o.prototypes=null;continue}const b=k.questions.filter(M=>ne.get(M)?.state!=="resolved"),v=k.questions.find(M=>ne.get(M)?.state==="open")||b[0]||k.questions[0],O=k.questions.map(M=>ne.get(M)).filter(M=>M?.state==="open");o.questions={topic:k.id,state:k.state,open:k.open,answered:k.answered,replied:k.replied,resolved:k.resolved,first:v,askedAt:O.map(M=>M.asked_at).filter(Boolean).sort(),fromAsks:O.length>0&&O.every(M=>M.ask)},o.prototypes=to(k.questions.map(M=>ne.get(M)))}const le=new Map(B.map(o=>[o.key,o]));for(const o of B){const k=o.kind==="work"&&o.lane!=="done";o.blockedBy=k?o.deps.map(v=>le.get(String(v).includes("/")?v:`${o.wave}/${v}`)).filter(v=>v&&v.lane!=="done"&&!v.reopened).map(Pt):[],o.blockedUntil=k?o.blockedUntil??null:null,o.blockReason=k?o.blockReason??null:null,o.blocked=o.blockedBy.length>0||!!o.blockedUntil,Wn(o,ne,_),o.lane==="todo"&&o.kind==="work"&&!o.held&&Ot(o.storedLane,o.agents)&&(o.lane="progress"),o.unpicked=o.kind==="work"&&o.lane==="todo"&&!!Tn(Q,o.key),o.unpicked&&(o.state="prototyping"),o.unpicked&&!o.action&&(o.next=Ln),o.buildable=Nn(o);const b=o.kind==="work"&&o.lane==="todo";o.unsorted=b&&(!!o.fromAsk||o.unfiled)&&!o.state,o.ready=b&&o.state==="ready"&&!o.blocked&&!o.action,o.staging=b&&o.state==="staging"}bo(B,le,V);const Be=new Map((t.items||[]).map(o=>[o.id,o.topic])),Le=l(o=>o.kind==="owner-answer"?Be.has(o.data?.id)?/^item:(.+)$/.exec(Be.get(o.data.id))?.[1]||null:"":o.kind==="ask"?`ask:${o.id}`:o.data?.key||(o.kind.startsWith("ask-")&&o.data?.id?`ask:${o.data.id}`:null),"ownCardOf"),R=l(o=>{const k=Le(o);return H.get(k)||k},"cardOf"),Je=l(o=>{const k=o.startsWith("ask:")?o.slice(4):null,b=k&&A(G,k)?.state==="done"?A(G,k).at:null;return[S.get(o),k&&me.get(k),b,U.get(o)].filter(Boolean).sort().pop()||""},"closedAt"),Re=l(o=>{const k=R(o);return k===null?!o.reply:!le.has(k)||!(Je(k)<String(A(u,o.id)))?!1:ke.has(o.id)?ke.get(o.id)==="working":!o.reply&&!H.has(Le(o))&&!(String(re.get(k)||"")>String(o.ts))},"unanswered"),D=s.filter(o=>A(u,o.id)&&Re(o)).map(o=>({id:o.id,kind:o.kind,key:R(o),data:o.data,since:A(u,o.id)}));for(const o of B)o.working=D.some(k=>k.key===o.key),o.kind==="work"&&o.lane==="progress"&&!o.builder&&!o.next&&o.waitsOn!=="checking"&&!o.working&&(o.next=$t),o.sortAt=So[o.lane](o)||(o.lane==="review"?null:o.updatedAt)||null;for(const o of B){if(o.kind!=="work")continue;const k=(ae.get(`item:${o.key}`)||[]).filter(v=>v.state==="answered").map(v=>v.answers.at(-1).ts).sort().pop(),b=D.filter(v=>v.key===o.key).map(v=>String(v.since)).sort()[0];o.orchestrator=!o.stalled&&(["progress","todo"].includes(o.lane)||o.waitsOn==="checking"||o.lane==="review"&&o.stale)?jo(o,o.waitFacts,{answeredAt:k,workingSince:b}):null,delete o.waitFacts}qo(B,s),B.sort(cn);const Ze=ko(e,y,Y,B,Q,s),ie=z(),de=Zt({on:e.autoReview,incoming:e.incoming,records:e.reviewRecords,events:s,now:d,refs:new Map(B.filter(o=>o.kind==="work").map(o=>[o.key,o.ref]))}),he=[...xo(B,ie,Q),...en(de)],_e=new Map(he.filter(o=>o.questions).map((o,k)=>[o.topic||`item:${o.key}`,k])),Pe=l(o=>_e.get(o.id)??_e.size,"rankOf");ie.sort((o,k)=>Pe(o)-Pe(k));const je=new Set(he.filter(o=>o.questions&&o.progress).map(o=>`item:${o.key}`));for(const o of ie)je.has(o.id)&&(o.progress=!0);const Ue=B.filter(o=>o.ask?.answerAt).sort((o,k)=>k.ask.answerAt.localeCompare(o.ask.answerAt)).map(o=>({key:o.key,id:o.id,text:o.title,at:o.ask.answerAt}));return ho({kind:F,main:Ce,remote:W&&{host:W.host,name:`${W.host}/${W.path}`},waves:y,waveBands:Ze,items:B,moved:Object.fromEntries(H),answers:Ue,owner:Q,ownerTopics:ie,reviews:de,needs:he,working:D.length,workingOn:D,checks:vo(e,d)},e)}l(as,"buildBoard");function ho(e,{listen:t,prompts:n}={}){const s=[...$o(n),...e.needs];return{...e,needs:s,needsSummary:Eo(s),checks:{...e.checks,watcher:{listening:"running",asleep:"asleep",waking:"waking"}[t]||"stopped"}}}l(ho,"withHub");const Pt=l(e=>({key:e.key,ref:e.ref}),"linkOf"),jt=l((e,t)=>String(t).includes("/")?String(t):`${e}/${t}`,"linkKey"),yo=l((e,t)=>e.ref.localeCompare(t.ref,void 0,{numeric:!0}),"byRef");function bo(e,t,n){const s=new Map,r=new Map,a=new Map,u=l((p,f,g)=>p.set(f,[...p.get(f)||[],g]),"add");for(const[p,f]of n){f.parent&&u(s,f.parent,p);for(const g of f.related)g!==p&&(u(r,p,g),u(r,g,p))}for(const p of e)for(const f of p.blockedBy||[])u(a,f.key,p.key);const d=l((p=[])=>[...new Set(p)].map(f=>t.get(f)).filter(Boolean).map(Pt).sort(yo),"cards");for(const p of e){const f=n.get(p.key)?.parent;p.parent=f?{key:f,ref:t.get(f)?.ref||f.slice(f.indexOf("/")+1)}:null,p.children=d(s.get(p.key)),p.related=d(r.get(p.key)),p.waitsOnIt=d(a.get(p.key))}}l(bo,"linkCards");const So={todo:l(e=>e.created,"todo"),progress:l(e=>e.progressSince,"progress"),review:l(e=>e.reviewSince,"review"),done:l(e=>e.closedAt,"done"),asks:l(e=>e.ask.answerAt,"asks")},se=l((e,t)=>`${e} ${t}${e===1?"":"s"}`,"plural");function vo({checked:e,watcher:t,replyAt:n,threads:s={},working:r={},waves:a=[]},u){const d=Number(e?.every)||30,f=[n,t?.since,...Object.values(r),...Object.values(s).flat().filter(c=>(c.who||"claude")==="claude").map(c=>c.ts),...a.flatMap(c=>Object.values(c.agents||{}).flatMap(h=>Object.values(h||{})).map(h=>h?.done||h?.started))].filter(c=>typeof c=="string"&&!Number.isNaN(Date.parse(c))).reduce((c,h)=>c&&Date.parse(c)>Date.parse(h)?c:h,null),g=!!e?.idle;return{at:e?.at||null,every:g?null:d,idle:g,late:!g&&!!e?.at&&u-Date.parse(e.at)>2*d*6e4,claudeAt:f}}l(vo,"checksOf");function xo(e,t,n){const s=new Set(e.map(d=>d.questions?.topic).filter(Boolean)),r=l(d=>d.find(p=>n.find(f=>f.id===p)?.state==="open")||d[0],"firstOpen"),a=t.filter(d=>d.state!=="resolved"&&d.open>0&&!s.has(d.id)).map(d=>{const p=`questions.html#${r(d.questions)}`,f=/^(?:lineup|wave):(.+)$/.exec(d.id)?.[1]||null;return{kind:"Question",title:d.title,text:`${d.title} \xB7 ${se(d.open,"question")}`,url:p,topic:d.id,band:f,questions:{open:d.open,url:p},ready:null,since:n.filter(g=>g.topic===d.id&&g.state==="open").map(g=>g.asked_at).filter(Boolean).sort()[0]||null}}),u=e.map(d=>{const p=d.questions&&d.questions.state!=="resolved"&&d.questions.open>0?{open:d.questions.open,url:`questions.html#${d.questions.first}`}:null,f=d.waitsOn==="ready"?d.pr.url:null,g=!p&&!f&&d.action?{label:d.action.label,url:d.action.url}:null;if(!p&&!f&&!g)return null;const c=`${d.ref}: ${d.title}`,h=[c,p&&se(p.open,"question"),f&&"Ready on GitHub",g?.label].filter(Boolean).join(" \xB7 ");return{kind:p?"Question":f?"Tap Ready":"Action needed",title:c,text:h,url:`#item=${encodeURIComponent(d.key)}`,key:d.key,questions:p,ready:f,action:g,since:d.created||null,progress:d.lane==="progress"}}).filter(Boolean);return[...a,...u].sort(Oo)}l(xo,"needsOf");const Oo=l((e,t)=>!!t.progress-!!e.progress||(e.since&&t.since?e.since.localeCompare(t.since):!!t.since-!!e.since),"oldestFirst");function $o(e=[]){return e.map(t=>{const n=t.questions?.length?`Question: ${t.questions.map(s=>s.question).join(" \xB7 ")}`:`${t.tool}: ${t.detail||t.action}`;return{kind:"Prompt",title:n,text:n,url:"",prompt:t}})}l($o,"promptLines");function Eo(e){const t=e.filter(a=>a.prompt).length,n=e.reduce((a,u)=>a+(u.questions?.open||0),0),s=e.filter(a=>a.ready).length,r=e.filter(a=>a.review).length;return[t&&se(t,"prompt"),n&&se(n,"question"),s&&`${se(s,"draft")} waiting on Ready`,r&&`${se(r,"review")} held`].filter(Boolean).join(" \xB7 ")}l(Eo,"needsSummary");function ls(e){const t=l((...n)=>e.items.filter(s=>n.includes(s.lane)).length,"count");return{items:e.needs.map(({kind:n,text:s,url:r})=>({kind:n,text:s,url:r})),working:e.working,summary:e.needsSummary,lanes:{review:t("review"),progress:t("progress"),todo:t("todo")},building:e.items.some(n=>(n.agents||[]).some(s=>!s.done))}}l(ls,"needsAnswer");const Ao=l(e=>e.buildable&&e.state==="ready"&&!e.held&&!e.blocked,"pullableCard"),Bo=10;function Ro(e,{cap:t=Bo,pulled:n=[]}={}){const s=e.filter(m=>!pe(m)),r=l((m,x)=>m.started&&!m.done&&te(m)&&!(m.kind===Qe&&!m.writes)&&!(m.waits&&x.some(S=>S.name===m.waits&&!S.done)),"counts"),a=s.filter(m=>!m.stalled).reduce((m,x)=>m+(x.agents||[]).filter((S,L,_)=>r(S,_)).length,0),u=s.filter(m=>Ao(m)&&!m.working).map(m=>m.key),d=s.filter(m=>n.includes(m.key)&&(u.includes(m.key)||m.buildable&&m.working)).map(m=>m.key),p=s.flatMap(m=>(m.agents||[]).filter(x=>x.kind===Qe&&!x.started&&!x.done).map(x=>({key:m.key,name:x.name,writes:x.writes}))),f=Math.max(0,t-a-d.length),g=p.filter(m=>m.writes),c=[...p.filter(m=>!m.writes),...g.slice(0,f)].map(({key:m,name:x})=>({key:m,name:x})),h=u.filter(m=>!d.includes(m)).slice(0,Math.max(0,f-g.length));return{idle:!a&&!s.some(m=>m.waitsOn==="checking"),pullable:u,waiting:d,pulls:h,helpers:c}}l(Ro,"pullOf");function is(e,t,n){return e.filter(s=>s.kind==="work"&&!pe(s)&&s.waitsOn!=="checking"&&!(s.agents||[]).some(r=>r.started&&!r.done)).flatMap(s=>{const r=(s.prs||[]).find(u=>u.state==="OPEN"&&u.mergeable==="CONFLICTING"&&u.repo===s.repo&&u.headRefOid),a=r&&`${r.headRefOid}@${n}`;return r&&A(t,s.key)!==a?[{key:s.key,pr:r.number,branch:r.headRefName,conflict:a}]:[]})}l(is,"rebasesDue");const _o=3;function ds(e,{cap:t=_o}={}){const n=e.filter(a=>!pe(a)),s=n.filter(a=>(a.agents||[]).some(u=>ee(u)&&u.started&&!u.done)).length;return n.filter(a=>a.buildable&&!["ready","staging"].includes(a.state)&&!a.unsorted&&!a.blocked&&!a.held&&!a.working&&!a.grilling&&!a.prototyping&&!a.questions&&!(a.agents||[]).some(u=>ee(u)&&u.kind!==fe)).slice(0,Math.max(0,t-s)).map(a=>a.key)}l(ds,"arrivalsOf");function cs(e){return e.filter(t=>t.kind==="work"&&t.lane==="todo"&&t.roundAt&&["grilling","prototyping"].includes(t.state)&&!(t.agents||[]).some(n=>ee(n)&&n.kind!==fe&&n.started&&(String(n.started)>=String(t.roundAt)||!n.done))).map(t=>({key:t.key,kind:t.state==="grilling"?"grill":"prototype"}))}l(cs,"roundsDue");const Mo=l(e=>`${fe}-${e.id}`,"visualAgent");function us(e,t){const n=[];for(const s of e){const r=s.agents||[];if(r.some(d=>d.kind===fe&&d.started&&!d.done))continue;const a=new Set(r.map(d=>d.name)),u=t.find(d=>d.kind==="visual-request"&&d.data?.key===s.key&&!a.has(Mo(d)));u&&n.push({key:s.key,request:u})}return n}l(us,"visualsDue");const Ut=l(e=>e.kind==="ask"?`ask:${e.id}`:e.kind==="card-added"?e.data?.key??null:e.kind==="item-message"&&String(e.data?.key||"").startsWith("ask:")?e.data.key:null,"intakeKeyOf");function Wt(e,t,n){const s=e.get(n);return s?t.kind==="ask"?s.isNew:t.kind==="card-added"?s.kind==="work"&&s.unfiled:(s.ask?.owed||[]).includes(t.id):!1}l(Wt,"unsorted");function Ft(e,t){const n=new Map(e.map(r=>[r.key,r])),s=new Set(t.filter(r=>r.kind==="intake-handoff").map(r=>r.data?.event));return t.flatMap(r=>{const a=Ut(r);return a&&!s.has(r.id)&&Wt(n,r,a)?[{event:r.id,key:a,kind:r.kind}]:[]})}l(Ft,"intakeOpen");function ps(e,t,n){return Object.values(n).some(s=>!s.ended)?null:Ft(e,t).find(s=>!A(n,s.event))??null}l(ps,"intakeDue");const Io=ft;function fs(e,t){return Object.entries(e).filter(([,n])=>!n.ended&&t-Date.parse(n.at)>=Io).map(([n])=>n)}l(fs,"intakeHung");function gs(e,t,n){return Ft(e,t).filter(s=>A(n,s.event)?.ended)}l(gs,"intakeStranded");function qo(e,t){const n=new Map(e.map(r=>[r.key,r])),s=new Map(t.map(r=>[r.id,r]));for(const r of t.filter(a=>a.kind==="intake-handoff")){const a=s.get(r.data?.event),u=a&&Ut(a);!u||!Wt(n,a,u)||(n.get(u).orchestrator={kind:"intake",since:String(r.ts),picked:null,next:`Sort what intake triage handed you: ${r.data.text}`})}}l(qo,"intakeWaits");function Do(e){return e.filter(t=>t.lane==="todo"||t.lane==="review"&&t.stale).map(t=>JSON.stringify([t.key,t.title,t.deps||[]])).sort().join(`
|
|
7
|
-
`)}
|
|
6
|
+
`),files:s.flatMap(r=>r.files),state:s.map(r=>r.state).sort((r,a)=>Wt.indexOf(r)-Wt.indexOf(a))[0],reply:void 0,answers:s.map(r=>({eventId:r.eventId,state:r.state,reply:r.reply,text:r.text}))}]})}i(ws,"foldSubmits");const Ne=i(e=>(e.answers||[e]).filter(t=>t.eventId).map(t=>[t.eventId,t.state]),"stateEntries");function hs(e,t,n,s,r,a){const d=i((p,w)=>{const v=a.filter(x=>x.kind==="wave-triage"&&x.data?.[p]===w).pop(),R=r.filter(x=>x.topic===`${p}:${w}`&&(x.state==="open"||x.state==="answered")).length;return{running:!!(v&&!v.reply),questions:R}},"triageOf"),c=new Map(s.map(p=>[p.key,p])),u=i(p=>p.filter(w=>w.lane==="done"&&!vt(w)).length,"merged"),f=n.map(p=>({l:p,cards:p.keys.map(w=>c.get(w)).filter(Boolean)})).map(p=>({...p,open:!p.l.finished_at&&p.cards.some(w=>w.lane!=="done")})).sort((p,w)=>Number(p.open)-Number(w.open));for(const{l:p,cards:w}of f)for(const v of w)v.wave={slug:p.slug,title:p.title};const g=f.filter(p=>p.open).map(({l:p,cards:w})=>({kind:"wave",slug:p.slug,title:p.title,goal:p.goal,keys:w.map(v=>v.key),next:ys(w),merged:u(w),total:w.length,triage:d("wave",p.slug)}));return e.fromWaves?[...g,...t.filter(p=>!p.finished).map(p=>({w:p,cards:s.filter(w=>w.file===p.slug&&w.kind!=="ask")})).filter(({cards:p})=>p.length).map(({w:p,cards:w})=>({kind:"file",slug:p.slug,title:p.title,goal:null,keys:w.map(v=>v.key),next:null,merged:u(w),total:w.length,triage:d("file",p.slug)}))]:g}i(hs,"waveBandsOf");function ys(e){const t=e.filter(a=>a.lane==="todo"),n=t.find(a=>a.ready&&a.buildable)||t[0];if(!n)return null;const s=n.ready&&n.buildable,r=s?"ready":n.held?"held":n.blockedBy.length?`waits on ${n.blockedBy.map(a=>a.ref).join(", ")}`:n.blockedUntil?`blocked until ${n.blockedUntil}`:n.action?"needs you":n.phase==="new"?"not ready":n.phase;return{key:n.key,ref:n.ref,ready:s,word:r}}i(ys,"nextOf");const bs=i(e=>e.items||e.phases.flat(),"waveItems");function bo(e){const{owner:t={},topicItems:n={},events:s=[],threads:r={},prs:a={},working:d={}}=e,c=e.now??Date.now(),u=jt(s,t),f=new Set(s.filter(o=>o.kind==="ask-withdrawn"&&!o.data?.key).map(o=>o.data?.id)),g=new Map(s.filter(o=>o.kind==="ask"&&!f.has(o.id)).map(o=>[o.id,o])),p=i((o,k)=>new Map(s.filter(S=>S.kind===o).sort((S,O)=>String(S.ts).localeCompare(String(O.ts))).map(S=>[S.data?.[k],S.ts])),"latest"),w=p("ask-bump","id"),v=p("card-unbump","key"),R=new Map([...p("card-bump","key")].filter(([o,k])=>!(String(v.get(o)||"")>String(k)))),x=p("card-done","key"),N=p("card-merge","key"),C=e.autoMerge===!0,_=e.tickets,T=new Map(s.filter(o=>o.kind==="card-hold").sort((o,k)=>String(o.ts).localeCompare(String(k.ts))).map(o=>[o.data?.key,o])),P=new Map,y=new Map(e.files.flatMap(({slug:o,itemFile:k})=>k.items.map(S=>[`${o}/${S.id}`,{ref:Qe(k,S,_),title:S.title}]))),b=e.askLinks||{},l=new Map;for(const[o,k]of Object.entries(b))g.has(o)&&l.set(k,[...l.get(k)||[],o]);const E=[],h=[],ne=new Map,ie=new Map,F=new Map,X=new Map,K=e.github?null:Zt(e.remote);for(const{slug:o,itemFile:k,agents:S={}}of e.files){const O=e.github||k.repo||null;E.push({slug:o,title:k.title||o,status:k.status||null,internal:!!k.internal,finished:!1,reviewers:(k.reviewers||[]).map($=>$.name)}),k.items.forEach(($,q)=>{const I=`${o}/${$.id}`,ce=(l.get(I)||[]).filter(A=>A!==$.from_ask),ue=[$.from_ask,...ce].filter(A=>g.has(A)),Fe=new Set(ue.map(A=>`ask:${A}`)),Ke=[...B(r,I)||[],...ue.flatMap(A=>B(r,`ask:${A}`)||[])];$.from_ask&&P.set($.from_ask,[...P.get($.from_ask)||[],{key:I,ref:Qe(k,$,_),became:!0}]);const Se=Ut(I,{lines:Ke,events:s,owner:t,working:d,fromAsk:$.from_ask||null,moved:ce,cards:y,index:u});for(const A of Se)for(const[m,ee]of Ne(A))ne.set(m,ee);ie.set(I,ct(Se)),h.push(_n({slug:o,itemFile:k,it:$,order:q,repo:O,remote:K,base:e.branch||"main",prs:sn($,O).map(A=>({...A,gh:B(a,rn(A))||null})),...(A=>({agents:A,builder:zn(A,B(r,I),c,e.moved?.[it(B(S,$.id))]??null,B(e.alive,I)??null,!!e.commandRunning?.[it(B(S,$.id))]),reachedPhase:Yn(A,B(r,I)),eta:A.map(m=>m.eta).filter(Boolean).sort().pop()||null}))(lt(B(S,$.id))),worktree:it(B(S,$.id))??$.worktree?.path??null,thread:Ke,events:[...ut(u,[I,...Fe].map(A=>u.byKey.get(A))),...ue.map(A=>g.get(A))],working:Se.some(A=>Ne(A).some(([,m])=>m==="working")),said:Se.filter(A=>A.who==="love"&&(A.kind==="item-message"||A.kind==="owner-answer")),askedAt:(u.questions.get(`item:${I}`)||[]).filter(A=>A.asked_at).map(A=>A.asked_at),topicItems:n,prSlack:e.prSlack||{},boardReviewers:e.reviewers||[],now:c,bumpedAt:R.get(I),holdAsk:T.get(I),closedAt:x.get(I),mergeAt:N.get(I),autoMerge:C,skipDraft:e.skipDraft===!0,tickets:_}));const ve=h[h.length-1];g.has($.from_ask)&&(ve.created=g.get($.from_ask).ts),X.set(I,{parent:$.parent?Kt(o,$.parent):null,related:($.related||[]).map(A=>Kt(o,A))}),ve.lane==="done"&&F.set(I,ve.closedAt||String(ve.updatedAt||""))})}const L=new Map(h.map(o=>[o.key,o])),Y=new Map,z=e.try||{},Te=e.autoBuild===!0,W=["web","app"].includes(z.kind)?z.kind:e.files.some(({itemFile:o})=>o.items.some(k=>k.install?.url))?"app":"web";for(const o of h){if(o.kind!=="work"||o.lane==="done")continue;const k=B(z.cards,o.key)||{},S=k.head||o.pr?.headRefOid,O=W==="web"?k.sha:o.install?.sha,$=o.builder?.kind===pe.install?"running":k.state||(W==="app"&&o.install?"ready":null);o.try={kind:W,state:$,stale:!!(O&&S&&!String(S).toLowerCase().startsWith(String(O).toLowerCase())),url:W==="web"?null:o.install?.url||null,port:W==="web"&&$==="ready"&&k.https||null,sha:O||null,changes:k.ahead>0,script:k.script===!0,autoBuild:Te}}const U=z.main,Le=U?.script&&U.sha?{slug:U.slug,branch:U.branch||"main",sha:U.sha.slice(0,7),at:U.at,kind:W,state:U.state||null,url:W==="web"?null:e.main?.install?.url||null,port:W==="web"&&U.state==="ready"&&U.https||null,pull:z.pull?{running:z.pull.running===!0,error:z.pull.error||null}:null,script:cn(U.target)}:null,G=e.askProgress||{},me=p("ask-done","id"),ze=Ee(s.filter(o=>o.kind==="owner-answer"),o=>o.data?.id),Pe=Ee(s.filter(o=>o.kind==="prototype-request"),o=>o.data?.question),Be=Ee(s.filter(o=>o.kind==="reask-request"),o=>o.data?.question),Je=Ee(s.filter(o=>o.kind===wt),o=>o.data?.question),Q=(t.items||[]).map(o=>ns(o,ze.get(o.id)||[],Pe.get(o.id)||[],Be.get(o.id)||[],Je.get(o.id)||[])),J=new Map(Q.map(o=>[o.id,o])),_e=Ee(Q,o=>o.topic),we=i(o=>{const k=_e.get(o)||[];return{open:k.filter(S=>S.state==="open").length,answered:k.filter(S=>S.state==="answered").length}},"pendingOn");for(const o of g.values()){const k=`ask:${o.id}`,S=L.get(B(b,o.id)),O=[...P.get(o.id)||[]],$=S&&!O.some(I=>I.key===S.key);if($&&O.push({key:S.key,ref:S.ref,title:S.title,became:!1,folded:!0}),O.length&&!$){Y.set(k,O[0].key);continue}if(!$&&B(b,o.id)){Y.set(k,B(b,o.id));continue}const q=Ut(k,{lines:B(r,k)||[],events:s,working:d,links:O,index:u});for(const I of q)for(const[ce,ue]of Ne(I))ne.set(ce,ue);ie.set(k,ct(q)),h.push(Gn(o,q,w.get(o.id),O.length>0||!!B(b,o.id),B(G,o.id),me.get(o.id),we(`item:${k}`),B(e.askTitles||{},o.id)))}for(const o of E){const k=h.filter(S=>S.file===o.slug);if(o.finished=Sn(k),o.finished)for(const S of k)S.lane==="done"&&(S.fold=mn(o.slug))}for(const o of h)o.wave=null;const he=new Set((t.topics||[]).map(o=>o.id)),je=e.files.flatMap(({slug:o,itemFile:k})=>(k.waves||[]).map(S=>({...S,keys:bs(S).map(O=>`${o}/${O}`)}))),Z=i(()=>as([...t.topics||[],...h.filter(o=>!he.has(`item:${o.key}`)).map(o=>({id:`item:${o.key}`,card:o.key,title:`${o.ref}: ${o.title}`,...o.kind==="ask"?{askedAt:o.ask.askedAt}:{}})),...je.filter(o=>!he.has(`wave:${o.slug}`)).map(o=>({id:`wave:${o.slug}`,title:o.title,summary:"Wave triage."})),...(e.fromWaves?E:[]).filter(o=>!he.has(`file:${o.slug}`)).map(o=>({id:`file:${o.slug}`,title:o.title,summary:"Wave triage."}))],Q,new Set(h.filter(fe).map(o=>`item:${o.key}`))),"topicsOf"),se=new Map(Z().map(o=>[o.id,o]));for(const o of h){const k=se.get(`item:${o.key}`);if(!k){o.questions=null,o.prototypes=null;continue}const S=k.questions.filter(q=>J.get(q)?.state!=="resolved"),O=k.questions.find(q=>J.get(q)?.state==="open")||S[0]||k.questions[0],$=k.questions.map(q=>J.get(q)).filter(q=>q?.state==="open");o.questions={topic:k.id,state:k.state,open:k.open,answered:k.answered,replied:k.replied,resolved:k.resolved,first:O,askedAt:$.map(q=>q.asked_at).filter(Boolean).sort(),fromAsks:$.length>0&&$.every(q=>q.ask)},o.prototypes=rs(k.questions.map(q=>J.get(q)))}const de=new Map(h.map(o=>[o.key,o]));for(const o of h){const k=o.kind==="work"&&o.lane!=="done";o.blockedBy=k?o.deps.map(O=>de.get(String(O).includes("/")?O:`${o.file}/${O}`)).filter(O=>O&&O.lane!=="done"&&!O.reopened).map(Ft):[],o.blockedUntil=k?o.blockedUntil??null:null,o.blockReason=k?o.blockReason??null:null,o.blocked=o.blockedBy.length>0||!!o.blockedUntil,Fn(o,J,C),o.lane==="todo"&&o.kind==="work"&&!o.held&&Bt(o.storedLane,o.agents)&&(o.lane="progress"),o.unpicked=o.kind==="work"&&o.lane==="todo"&&!!Ln(Q,o.key),o.kind==="work"&&(o.phase=Gs(o)),o.unpicked&&!o.action&&(o.next=Pn),o.buildable=Nn(o);const S=o.kind==="work"&&o.lane==="todo";o.unsorted=S&&(!!o.fromAsk||o.unfiled)&&!o.filedHeld&&o.phase==="new",o.ready=S&&o.phase==="ready"&&!o.blocked&&!o.action,o.staging=S&&o.phase==="staging",o.kind==="work"&&(o.hostHeld=!!e.hostBusy&&o.ready&&!!o.bumpedAt&&!o.held)}xs(h,de,X);const ye=new Map((t.items||[]).map(o=>[o.id,o.topic])),Re=i(o=>o.kind==="owner-answer"?ye.has(o.data?.id)?/^item:(.+)$/.exec(ye.get(o.data.id))?.[1]||null:"":o.kind==="ask"?`ask:${o.id}`:o.data?.key||(o.kind.startsWith("ask-")&&o.data?.id?`ask:${o.data.id}`:null),"ownCardOf"),Ue=i(o=>{const k=Re(o);return Y.get(k)||k},"cardOf"),M=i(o=>{const k=o.startsWith("ask:")?o.slice(4):null,S=k&&B(G,k)?.state==="done"?B(G,k).at:null;return[x.get(o),k&&me.get(k),S,F.get(o)].filter(Boolean).sort().pop()||""},"closedAt"),Ze=i(o=>{const k=Ue(o);return k===null?!o.reply:!de.has(k)||!(M(k)<String(B(d,o.id)))?!1:ne.has(o.id)?ne.get(o.id)==="working":!o.reply&&!Y.has(Re(o))&&!(String(ie.get(k)||"")>String(o.ts))},"unanswered"),oe=s.filter(o=>B(d,o.id)&&Ze(o)).map(o=>({id:o.id,kind:o.kind,key:Ue(o),data:o.data,since:B(d,o.id)}));for(const o of h)o.working=oe.some(k=>k.key===o.key),o.kind==="work"&&o.lane==="progress"&&!o.builder&&!o.next&&o.waitsOn!=="checking"&&!o.working&&(o.next=_t),o.sortAt=Os[o.lane](o)||(o.lane==="review"?null:o.updatedAt)||null;for(const o of h){if(o.kind!=="work")continue;const k=(_e.get(`item:${o.key}`)||[]).filter(O=>O.state==="answered").map(O=>O.answers.at(-1).ts).sort().pop(),S=oe.filter(O=>O.key===o.key).map(O=>String(O.since)).sort()[0];o.orchestrator=!o.stalled&&(["progress","todo"].includes(o.lane)||o.waitsOn==="checking"||o.lane==="review"&&(o.stale||o.waitFacts.unblockedAt))?Ys(o,o.waitFacts,{answeredAt:k,workingSince:S}):null,delete o.waitFacts}Ps(h,s,d),js(h,e.screening||{}),h.sort(un);const j=hs(e,E,je,h,Q,s),be=Z(),Me=en({on:e.autoReview,incoming:e.incoming,records:e.reviewRecords,events:s,now:c,refs:new Map(h.filter(o=>o.kind==="work").map(o=>[o.key,o.ref]))}),re=[...As(h,be,Q),...tn(Me)],Ie=new Map(re.filter(o=>o.questions).map((o,k)=>[o.topic||`item:${o.key}`,k])),qe=i(o=>Ie.get(o.id)??Ie.size,"rankOf");be.sort((o,k)=>qe(o)-qe(k));const et=new Set(re.filter(o=>o.questions&&o.progress).map(o=>`item:${o.key}`));for(const o of be)et.has(o.id)&&(o.progress=!0);const We=h.filter(o=>o.ask?.answerAt).sort((o,k)=>k.ask.answerAt.localeCompare(o.ask.answerAt)).map(o=>({key:o.key,id:o.id,text:o.title,at:o.ask.answerAt}));return Ss({kind:W,main:Le,remote:K&&{host:K.host,name:`${K.host}/${K.path}`},itemFiles:E,waveBands:j,items:h,moved:Object.fromEntries(Y),answers:We,owner:Q,ownerTopics:be,reviews:Me,needs:re,working:oe.length,workingOn:oe,checks:$s(e,c)},e)}i(bo,"buildBoard");function Ss(e,{listen:t,prompts:n}={}){const s=[...Bs(n),...e.needs];return{...e,needs:s,needsSummary:_s(s),checks:{...e.checks,watcher:{listening:"running",asleep:"asleep",waking:"waking"}[t]||"stopped"}}}i(Ss,"withHub");const Ft=i(e=>({key:e.key,ref:e.ref}),"linkOf"),Kt=i((e,t)=>String(t).includes("/")?String(t):`${e}/${t}`,"linkKey"),vs=i((e,t)=>e.ref.localeCompare(t.ref,void 0,{numeric:!0}),"byRef");function xs(e,t,n){const s=new Map,r=new Map,a=new Map,d=i((u,f,g)=>u.set(f,[...u.get(f)||[],g]),"add");for(const[u,f]of n){f.parent&&d(s,f.parent,u);for(const g of f.related)g!==u&&(d(r,u,g),d(r,g,u))}for(const u of e)for(const f of u.blockedBy||[])d(a,f.key,u.key);const c=i((u=[])=>[...new Set(u)].map(f=>t.get(f)).filter(Boolean).map(Ft).sort(vs),"cards");for(const u of e){const f=n.get(u.key)?.parent;u.parent=f?{key:f,ref:t.get(f)?.ref||f.slice(f.indexOf("/")+1)}:null,u.children=c(s.get(u.key)),u.related=c(r.get(u.key)),u.waitsOnIt=c(a.get(u.key))}}i(xs,"linkCards");const Os={todo:i(e=>e.created,"todo"),progress:i(e=>e.progressSince,"progress"),review:i(e=>e.reviewSince,"review"),done:i(e=>e.closedAt,"done"),asks:i(e=>e.ask.answerAt,"asks")},le=i((e,t)=>`${e} ${t}${e===1?"":"s"}`,"plural");function $s({checked:e,watcher:t,replyAt:n,threads:s={},working:r={},files:a=[]},d){const c=Number(e?.every)||30,f=[n,t?.since,...Object.values(r),...Object.values(s).flat().filter(p=>(p.who||"claude")==="claude").map(p=>p.ts),...a.flatMap(p=>Object.values(p.agents||{}).flatMap(w=>Object.values(w||{})).map(w=>w?.done||w?.started))].filter(p=>typeof p=="string"&&!Number.isNaN(Date.parse(p))).reduce((p,w)=>p&&Date.parse(p)>Date.parse(w)?p:w,null),g=!!e?.idle;return{at:e?.at||null,every:g?null:c,idle:g,late:!g&&!!e?.at&&d-Date.parse(e.at)>2*c*6e4,claudeAt:f}}i($s,"checksOf");function As(e,t,n){const s=new Set(e.map(c=>c.questions?.topic).filter(Boolean)),r=i(c=>c.find(u=>n.find(f=>f.id===u)?.state==="open")||c[0],"firstOpen"),a=t.filter(c=>c.state!=="resolved"&&c.open>0&&!s.has(c.id)).map(c=>{const u=`questions.html#${r(c.questions)}`,f=/^(?:wave|file):(.+)$/.exec(c.id)?.[1]||null;return{kind:"Question",title:c.title,text:`${c.title} \xB7 ${le(c.open,"question")}`,url:u,topic:c.id,band:f,questions:{open:c.open,url:u},ready:null,since:n.filter(g=>g.topic===c.id&&g.state==="open").map(g=>g.asked_at).filter(Boolean).sort()[0]||null}}),d=e.map(c=>{const u=c.questions&&c.questions.state!=="resolved"&&c.questions.open>0?{open:c.questions.open,url:`questions.html#${c.questions.first}`}:null,f=c.waitsOn==="ready"?c.pr.url:null,g=!u&&!f&&c.action?{label:c.action.label,url:c.action.url}:null;if(!u&&!f&&!g)return null;const p=`${c.ref}: ${c.title}`,w=[p,u&&le(u.open,"question"),f&&"Ready on GitHub",g?.label].filter(Boolean).join(" \xB7 ");return{kind:u?"Question":f?"Tap Ready":"Action needed",title:p,text:w,url:`#selectedCard=${encodeURIComponent(c.key)}`,key:c.key,questions:u,ready:f,action:g,since:c.created||null,progress:c.lane==="progress"}}).filter(Boolean);return[...a,...d].sort(Es)}i(As,"needsOf");const Es=i((e,t)=>!!t.progress-!!e.progress||(e.since&&t.since?e.since.localeCompare(t.since):!!t.since-!!e.since),"oldestFirst");function Bs(e=[]){return e.map(t=>{const n=t.questions?.length?`Question: ${t.questions.map(s=>s.question).join(" \xB7 ")}`:`${t.tool}: ${t.detail||t.action}`;return{kind:"Prompt",title:n,text:n,url:"",prompt:t}})}i(Bs,"promptLines");function _s(e){const t=e.filter(a=>a.prompt).length,n=e.reduce((a,d)=>a+(d.questions?.open||0),0),s=e.filter(a=>a.ready).length,r=e.filter(a=>a.review).length;return[t&&le(t,"prompt"),n&&le(n,"question"),s&&`${le(s,"draft")} waiting on Ready`,r&&`${le(r,"review")} held`].filter(Boolean).join(" \xB7 ")}i(_s,"needsSummary");function So(e){const t=i((...n)=>e.items.filter(s=>n.includes(s.lane)).length,"count");return{items:e.needs.map(({kind:n,text:s,url:r})=>({kind:n,text:s,url:r})),working:e.working,summary:e.needsSummary,lanes:{review:t("review"),progress:t("progress"),todo:t("todo")},building:e.items.some(n=>(n.agents||[]).some(s=>!s.done))}}i(So,"needsAnswer");const Rs=i(e=>e.buildable&&e.phase==="ready"&&!e.held&&!e.blocked,"pullableCard"),Ms=10;function Is(e,{cap:t=Ms,pulled:n=[],hold:s=[],taken:r=0}={}){const a=e.filter(y=>!fe(y)),d=i((y,b)=>y.started&&!y.done&&V(y)&&!(y.kind===Oe&&!y.writes)&&!y.pause?.paused&&!(y.waits&&b.some(l=>l.name===y.waits&&!l.done)),"counts"),c=a.filter(y=>!y.stalled).reduce((y,b)=>y+(b.agents||[]).filter((l,E,h)=>d(l,h)).length,0),u=a.filter(y=>Rs(y)&&!y.working).map(y=>y.key),f=a.filter(y=>n.includes(y.key)&&(u.includes(y.key)||y.buildable&&y.working)).map(y=>y.key),g=a.flatMap(y=>(y.agents||[]).filter(b=>b.kind===Oe&&!b.started&&!b.done).map(b=>({key:y.key,name:b.name,writes:b.writes}))),p=Math.max(0,t-c-f.length-r),w=g.filter(y=>y.writes),v=[...g.filter(y=>!y.writes),...w.slice(0,p)].map(({key:y,name:b})=>({key:y,name:b})),R=Math.max(0,p-w.length),x=new Set(a.filter(y=>y.bumpedAt).map(y=>y.key)),N=u.filter(y=>!f.includes(y)&&!s.includes(y)),C=N.filter(y=>x.has(y)),_=[...C,...N.filter(y=>!x.has(y)).slice(0,Math.max(0,R-C.length))],T=C.slice(R),P=qs(a,d).slice(0,T.length).map((y,b)=>({key:y,by:T[b]}));return{idle:!c&&!a.some(y=>y.waitsOn==="checking"),pullable:u,waiting:f,pulls:_,helpers:v,bumpOuts:P,pastCap:T,free:p}}i(Is,"pullOf");function qs(e,t){const n=i(s=>{const r=s.agents||[],a=r.filter(u=>t(u,r)),[d]=a;return s.lane==="progress"&&!s.bumpedAt&&!s.stalled&&s.waitsOn!=="checking"&&s.builder?.phase!=="landing"&&!(s.prs||[]).length&&!s.pushed&&!s.mergeReady&&a.length===1&&d.kind!==Oe&&d.pid&&d.label!==He&&!d.pause&&!r.some(u=>u.kind===Oe&&u.started&&!u.done)?String(d.started):null},"newest");return e.map(s=>({key:s.key,at:n(s)})).filter(s=>s.at).sort((s,r)=>r.at.localeCompare(s.at)).map(s=>s.key)}i(qs,"bumpOutsOf");function vo(e,t,n){return e.filter(s=>s.kind==="work"&&!fe(s)&&s.waitsOn!=="checking"&&!(s.agents||[]).some(r=>r.started&&!r.done)).flatMap(s=>{const r=(s.prs||[]).find(d=>d.state==="OPEN"&&d.mergeable==="CONFLICTING"&&d.repo===s.repo&&d.headRefOid),a=r&&`${r.headRefOid}@${n}`;return r&&B(t,s.key)!==a?[{key:s.key,pr:r.number,branch:r.headRefName,conflict:a}]:[]})}i(vo,"rebasesDue");const Ds=3;function xo(e,{cap:t=Ds}={}){const n=e.filter(d=>!fe(d)),s=n.filter(d=>(d.agents||[]).some(c=>te(c)&&c.started&&!c.done)).length,r=n.filter(d=>d.buildable&&!["ready","staging"].includes(d.phase)&&!d.unsorted&&!d.blocked&&!d.held&&!d.working&&!d.grilling&&!d.prototyping&&!d.questions&&!(d.agents||[]).some(c=>te(c)&&c.kind!==ge)),a=r.filter(d=>d.bumpedAt);return[...a,...r.filter(d=>!d.bumpedAt).slice(0,Math.max(0,t-s-a.length))].map(d=>d.key)}i(xo,"arrivalsOf");function Oo(e){return e.filter(t=>t.kind==="work"&&t.lane==="todo"&&t.roundAt&&["grilling","prototyping"].includes(t.phase)&&!(t.agents||[]).some(n=>te(n)&&n.kind!==ge&&n.started&&(String(n.started)>=String(t.roundAt)||!n.done))).map(t=>({key:t.key,kind:t.phase==="grilling"?"grill":"prototype"}))}i(Oo,"roundsDue");const Cs=i(e=>`${ge}-${e.id}`,"visualAgent");function $o(e,t){const n=[];for(const s of e){const r=s.agents||[];if(r.some(c=>c.kind===ge&&c.started&&!c.done))continue;const a=new Set(r.map(c=>c.name)),d=t.find(c=>c.kind==="visual-request"&&c.data?.key===s.key&&!a.has(Cs(c)));d&&n.push({key:s.key,request:d})}return n}i($o,"visualsDue");const Ns=i(e=>e.kind==="ask"?`ask:${e.id}`:e.kind==="card-added"?e.data?.key??null:e.kind==="item-message"&&String(e.data?.key||"").startsWith("ask:")?e.data.key:null,"intakeKeyOf");function Ts(e,t,n){const s=e.get(n);return s?t.kind==="ask"?s.isNew:t.kind==="card-added"?s.kind==="work"&&s.unfiled:(s.ask?.owed||[]).includes(t.id):!1}i(Ts,"unsorted");const Ls={ask:"Sort the new task: file it with item add --from-ask, fold it with item relate-ask, or answer it with reply","item-message":"Answer the owner's message in the ask's thread: reply to it","card-added":"Link and file the card an agent added: item notes, then item set --relate and --phase"};function Ps(e,t,n){const s=new Map(e.map(a=>[a.key,a])),r=new Set;for(const a of t){const d=Ns(a);!d||r.has(d)||!Ts(s,a,d)||(r.add(d),s.get(d).orchestrator={kind:"intake",since:String(a.ts),picked:B(n,a.id)||null,next:Ls[a.kind]})}}i(Ps,"intakeWaits");function js(e,t){for(const n of e){const s=B(t,n.key);s&&(n.orchestrator={kind:"screen",since:s.since,picked:null,next:`Screen the grill's questions ${s.ids.join(", ")}: question settle <id> --note for each the board already answers, then question publish ${n.key}; left 15 minutes unscreened, they reach the owner as they are`})}}i(js,"screenWaits");function Us(e){return e.filter(t=>t.lane==="todo"||t.lane==="review"&&t.stale).map(t=>JSON.stringify([t.key,t.title,t.deps||[]])).sort().join(`
|
|
7
|
+
`)}i(Us,"todoOf");function Ao(e){const t=e.filter(s=>s.kind==="work"&&s.lane!=="done"&&s.questions?.open>0),n=t.map(s=>JSON.stringify([s.key,s.questions.open,s.questions.askedAt||[]])).sort().join(`
|
|
8
8
|
`);return{waiting:t.map(s=>s.key),seen:`${n}
|
|
9
9
|
--
|
|
10
|
-
${
|
|
10
|
+
${Us(e)}`}}i(Ao,"triageOf");const Ws=100,Fs=["key","kind","id","ref","title","lane","phase","file","fileTitle","fold","internal","ticket","ticketSource","linear","pr","prs","review","reviewers","risky","sortAt","threadCount","othersAt","hasNotes","questions","prototypes","next","ask","action","actionButton","builder","working","waitsOn","reachedPhase","grilling","refusal","install","parent","children","related","waitsOnIt","blockedBy","ready","finishable","reopened","manualSteps","branch","worktree"],Eo=i(e=>Object.fromEntries(Fs.filter(t=>t in e).map(t=>[t,e[t]])),"slimDone");function Bo(e,t=Ws){const n=new Set((e.waveBands||[]).flatMap(r=>r.keys)),s=new Set(e.items.filter(r=>r.lane!=="done").flatMap(r=>[...(r.deps||[]).map(a=>String(a).includes("/")?String(a):`${r.file}/${a}`),r.parent?.key,...(r.children||[]).map(a=>a.key),...(r.related||[]).map(a=>a.key)]));return e.items.filter(r=>r.lane==="done"&&r.kind==="work").sort((r,a)=>String(a.closedAt||"").localeCompare(String(r.closedAt||""))).slice(t).filter(r=>r.storedLane==="done"&&!r.reopened&&!n.has(r.key)&&!s.has(r.key)&&(!r.questions||r.questions.state==="resolved"))}i(Bo,"archivedOf");const Ks={ready:"draft",ci:"draft",reviewers:"reviewers",merge:"merge"};function Gs(e){return e.lane==="todo"?e.unpicked?"prototyping":mt.todo.includes(e.storedPhase)?e.storedPhase:e.storedLane&&e.storedLane!=="todo"?"ready":"new":e.lane==="progress"?e.waitsOn==="checking"?"checking":e.builder&&!e.builder.planner&&e.builder.kind!==pe.install&&e.builder.phase||e.reachedPhase||"reading":e.lane==="review"?e.stale?"stale":e.waitsOn==="owner"&&e.pushed?"merge":Ks[e.waitsOn]??null:e.storedLane==="done"&&mt.done.includes(e.storedPhase)?e.storedPhase:e.prs.some(t=>t.status===H.merged)?"merged":"closed"}i(Gs,"phaseOf");function _o(e){return e.items.filter(t=>t.kind==="work").flatMap(t=>{const n=t.lane;return n==="done"||t.storedLane==="done"&&!t.reopened?[]:n===t.storedLane&&(n==="todo"||t.phase===t.storedPhase)?[]:[{key:t.key,to:n,phase:t.phase,why:Qs(t,n),from:{lane:t.storedLane,phase:t.storedPhase}}]})}i(_o,"movesOf");function Qs(e,t){return t==="todo"?e.held?"it is held":e.blocked?"it is blocked":"nothing builds it":e.reopened&&t==="progress"?"a message or question reopened it":t==="progress"?e.waitsOn==="checking"?"its build handed back for a check":e.builder?"a build started":"Claude acts next":e.next||"it waits on review"}i(Qs,"moveWhy");const Hs=i(e=>e.startsWith("ask:")?`ask-${e.slice(4)}`:`item-${e}`,"pushTag");function Ro(e){const t=i(a=>e.owner.filter(d=>d.topic===a&&d.state==="open").map(({id:d,question:c})=>({id:d,question:c})),"open"),n=[D.ready,D.merge],s=e.items.filter(a=>a.action).map(a=>{const d=rt.has(a.action.kind)?t(a.questions.topic):[];return{tag:Hs(a.key),kind:a.action.kind,title:`${a.ref}: ${a.action.label}`,body:d[0]?.question||a.title,url:d.length?a.action.url:`#selectedCard=${encodeURIComponent(a.key)}`,now:!n.includes(a.action.kind),questions:d}}),r=e.needs.filter(a=>a.topic).map(a=>{const d=t(a.topic);return{tag:`topic-${a.topic}`,kind:D.questions,title:a.text,body:d[0]?.question||a.title,url:a.url,now:!0,questions:d}});return[...s,...r,...nn(e.reviews)]}i(Ro,"ownerActions");function Mo(e,{pulled:t=[]}={}){return!e.items.filter(r=>!fe(r)).some(r=>r.waitsOn==="checking"||r.grilling||r.prototyping||(r.agents||[]).some(a=>!a.done))&&!e.working&&!Is(e.items,{pulled:t}).waiting.length}i(Mo,"strictlyIdle");const Vs="item stall it with the reason when it can't go on",Gt="restart it once from its worktree with a thread line; if the restarted run goes stale too, item stall it with the reason",Xs='Judge the staged card against the whole board: item set --phase ready --text "<what you checked>", or --phase grilling or prototyping for another round; where its notes answer a split question Split as proposed, file the split instead';function Ys(e,t,{answeredAt:n,workingSince:s}){const r=i(f=>e.agents.some(g=>g.kind===f&&g.started&&!g.done),"running"),a=[e.progressSince,...e.agents.map(f=>f.done)].filter(Boolean).map(String).sort().pop()||null,d=t.ask?{kind:"ask",since:t.ask.at,next:`Answer the builder's ask: ${t.ask.text}`}:e.staging?{kind:"staging",since:e.stagedAt,next:Xs}:n?{kind:"answer",since:n,next:"Act on the owner's answer"}:rt.has(e.action?.kind)?null:e.builder?.stopped?{kind:"stale",since:e.builder.silentSince,next:`${e.builder.stopped}: ${Gt}, or drop the card`}:e.builder?.stale?{kind:"stale",since:e.builder.silentSince,next:`Find out why the builder went silent, then finish its run or ${Gt}`}:e.lane==="review"&&e.stale?{kind:"stale",since:new Date(Date.parse(e.inReviewSince)+yt).toISOString(),daily:!0,next:`Find out why the review went stale and fix what you can: rebase, CI, comments; if only a reviewer owes it, a thread line naming who; or ${Vs}`}:e.waitsOn==="checking"?t.unverified?{kind:"calls",since:t.handedBackAt,next:t.next||"Verify the builder's judgment calls"}:{kind:"checking",since:t.handedBackAt,next:t.next||"Land the checked build"}:e.grilling&&t.grillAt&&!r("grill")?{kind:"grill",since:t.grillAt,next:"Start the grill asked for"}:e.prototyping&&t.prototypeAt&&!r("prototype")?{kind:"prototype",since:t.prototypeAt,next:"Draw the prototype asked for"}:e.refusal&&t.refusedAt?{kind:"refused",since:t.refusedAt,next:e.refusal}:e.next===_t&&a?{kind:"unowned",since:a,next:"Start a builder on it, or set its Next"}:t.unblockedAt&&e.lane!=="todo"&&!e.builder&&!e.blocked&&!t.writes.some(f=>f>t.unblockedAt)?{kind:"unblocked",since:t.unblockedAt,next:"Its block ended: act on what it waited for, or set its Next"}:null;if(!d?.since)return null;const c=String(d.since),u=[t.writes.find(f=>f>c),s].filter(Boolean).sort()[0]||null;return{kind:d.kind,since:c,picked:u,next:d.next,...d.daily?{daily:!0}:{}}}i(Ys,"orchestratorWaitOf");const Io=i((e,t=null)=>({key:e.id,ref:e.data.date,title:`Overnight triage ${e.data.date}`,kind:"triage",since:e.ts,picked:t,next:`Run the overnight triage and reply: board.mjs reply ${e.id}`}),"triageWaitOf");function qo(e){const t=e.items.filter(n=>n.orchestrator).map(n=>({key:n.key,ref:n.ref,title:n.title,...n.orchestrator,bumpedAt:n.bumpedAt||null}));return[...t.filter(n=>n.bumpedAt).sort((n,s)=>n.bumpedAt.localeCompare(s.bumpedAt)),...t.filter(n=>!n.bumpedAt)]}i(qo,"orchestratorWaits");const Do=30*6e4,zs=15*6e4,Js=60*6e4,Zs=1440*6e4;function Co(e,t,n){const s=n.toISOString(),r=[],a=[];return{waits:t.map(c=>{const u=e.find(p=>p.key===c.key&&p.kind===c.kind&&p.since===c.since);if(!u)return c.picked||r.push(c.key),{...c,bumped:s};const f=c.picked?[c.picked,u.bumped].filter(Boolean).sort().pop():u.bumped||c.since,g=n-Date.parse(f)>=(c.picked?c.daily?Zs:Js:zs);return g&&a.push({key:c.key,ref:c.ref,kind:c.kind,since:c.since,next:c.next}),{...c,bumped:g?s:u.bumped||c.since,...u.pushed?{pushed:!0}:{}}}),added:r,again:a}}i(Co,"waitsStep");function No(e,t){const n=i(s=>{const r=Math.floor((t-Date.parse(s))/6e4),a=Math.floor(r/60),d=r%60;return a?`${a}h${d?` ${d}m`:""}`:`${d}m`},"age");return`still waiting on you: ${e.map(s=>`${s.ref} ${s.kind} for ${n(s.since)}: ${s.next}`).join("; ")}`}i(No,"waitsAgainLine");const eo=15;function To(e,{idle:t,now:n}){const s=n.toISOString(),r=e.last||s;return t?e.since?e.pushed||n-Date.parse(e.since)<eo*6e4?{state:{...e,last:r},push:!1}:{state:{last:s,since:e.since,pushed:!0},push:!0}:{state:{last:r,since:s},push:!1}:{state:{last:r},push:!1}}i(To,"idleStep");function Lo(e,t){const n=i(u=>String(u.closedAt||"")>t,"after"),s=e.filter(u=>u.kind==="work"&&u.lane==="done"&&!vt(u)&&n(u)),r=new Map;for(const u of e)u.wave&&r.set(u.wave.slug,[...r.get(u.wave.slug)||[],u]);const a=[...r.values()].filter(u=>u.every(f=>f.lane==="done")&&u.some(n)).map(u=>u[0].wave.title),d=s.slice(0,3).map(u=>u.ref).join(", ")+(s.length>3?` and ${s.length-3} more`:"");return[s.length&&`${s.length} ${s.length===1?"card":"cards"} landed: ${d}`,...a.map(u=>`${u} finished`)].filter(Boolean).join(" \xB7 ")||"Nothing closed since the last idle notification."}i(Lo,"idleSummary");export{D as ACTION_KIND,Ct as ACTION_LABELS,is as ANSWERS_SUBMITTED,De as BUILDER_ASK,pe as BUILDER_KIND,gn as BUILDER_QUIET_AFTER,fn as BUILDER_STALE_AFTER,ht as BUILD_REFUSED,ae as BUTTON,Xe as COMMANDS,Zs as DAILY_REWAKE_AFTER,Fs as DONE_FIELDS,Ws as DONE_KEPT,Oe as HELPER,eo as IDLE_MINUTES,Nt as INTENT_LABELS,pn as ITEM_EVENTS,He as LANDER,go as LANES,Ms as MAX_IMPLEMENTERS,Ds as MAX_PLANNERS,xe as MOVE,_t as NO_NEXT,Do as ORCHESTRATOR_PUSH_AFTER,ho as PAUSE_CLEARS,Jn as PAUSE_HARD_STOP,mt as PHASES,Js as PICKED_REWAKE_AFTER,Hn as PLANNER_WORD,Pn as PROTOTYPE_AGAIN,mo as PROTOTYPE_WAIT,H as PR_STATE,wt as QUESTION_REOPEN,zs as REWAKE_AFTER,ge as VISUAL,Bo as archivedOf,xo as arrivalsOf,bo as buildBoard,V as builds,mn as fileFold,st as handedBackAt,To as idleStep,Lo as idleSummary,In as mergeBlockers,ts as modelName,_o as movesOf,So as needsAnswer,Ln as noTakePicked,Vn as orchestratorDid,qo as orchestratorWaits,ko as owesNext,Ro as ownerActions,ns as ownerQuestion,yo as pauseWork,Tn as picksNoTake,te as plans,wo as prototypeWaits,It as providerOf,Is as pullOf,Hs as pushTag,vo as rebasesDue,Qe as refOf,ke as roundOf,ss as roundSettledBy,Oo as roundsDue,Eo as slimDone,Ne as stateEntries,Rt as stoppedWords,Mo as strictlyIdle,jt as threadIndex,Ut as threadOf,Us as todoOf,Ao as triageOf,Io as triageWaitOf,vt as unlanded,Cs as visualAgent,$o as visualsDue,No as waitsAgainLine,Co as waitsStep,bs as waveItems,Ss as withHub};
|
package/board/finish.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)[0];if(/^rebasing .+ conflicts in |^tests failed in /.test(
|
|
3
|
-
${
|
|
4
|
-
`)[0]}`);const
|
|
1
|
+
var I=Object.defineProperty;var c=(t,a)=>I(t,"name",{value:a,configurable:!0});import P from"node:fs";import v from"node:path";import{spawnSync as q}from"node:child_process";import*as r from"./store.mjs";import{builderRepo as E,git as w,joiningLand as F,landInTurn as N,passTests as j,syncBase as A,worktreesDir as L}from"./worktree.mjs";import{readPermissions as S}from"./permissions.mjs";import{landByBuilder as T}from"./headless.mjs";import{boardAt as C}from"../hub/boards.mjs";import{boardRepo as D}from"../hub/board-repo.mjs";const b=c(t=>{throw new r.BoardError(t)},"fail"),W=c(t=>typeof t=="string"&&t.trim()?t.trim():b("--text must be non-empty text"),"said");async function H(t,a,{text:o,closing:n,ownerStep:e,check:s,repo:i,onWait:g,merge:d=!1}){r.messageText(o);const f=a.split("/")[1],u=await r.recordedWorktree(t,a),{repo:$,base:p,remote:l}=E(t,{repo:i??u?.repo}),m={path:u?.path??v.join(L(),f),branch:u?.branch??f,repo:v.resolve($),base:p},M=await r.cardName(t,a),h=await r.readBoardSettings(t),R=l?D(C(t).route).github:null;if(l&&(!R||h.prs))return _(t,a,{...m,ref:M,text:W(o)});const B=await F(M,()=>V(t,a,{tree:m,ref:M,base:p,remote:l,repo:$,settings:h,text:o,closing:n,ownerStep:e,check:s,onWait:g}),{merge:d});return B.joined?await r.isFinished(t,a)?{said:"landed and done",lines:[]}:H(t,a,{text:o,closing:n,ownerStep:e,check:s,repo:i,onWait:g,merge:d}):B}c(H,"finishCard");async function V(t,a,{tree:o,ref:n,base:e,remote:s,repo:i,settings:g,text:d,closing:f,ownerStep:u,check:$,onWait:p}){const l=g.auto_merge||!!(await r.heldMerge(t,a))?.tapped,m=l&&await r.blockWords(t,a);m&&b(`${n} is blocked ${m}, so it can't land; it lands once nothing blocks it`),await r.checkClose(t,a,d,u,$),await r.checkCallsVerified(t,a),s&&(A(o.repo,e),w(o.repo,"branch","--unset-upstream",o.branch)),l&&await r.landingLine(t,a);const M={site:t,key:a,first:await r.landFirst(t,a)};if(await N({...o,ref:n},{hold:!l,onWait:p,queue:M,sync:c(()=>s&&A(o.repo,e),"sync")}),!l){const R=await r.setItem(t,a,{mergeReady:o.branch,ownerStep:u,check:$});return await r.appendMessage(t,a,{text:d}),{said:"verified; waits for your Merge",lines:R.lines}}const h=await r.setItem(t,a,{done:!0,landed:!0,...f?{closing:f}:{text:d},ownerStep:u,check:$});return{said:u===void 0?"landed and done":`landed; waits on the owner step "${u.trim()}"`,lines:[...h.lines,...await x(t,a,i,e)]}}c(V,"landCard");async function Z(t,a,{launch:o=T}={}){(await r.heldMerge(t,a))?.tapped||b(`${a} isn't waiting on a Merge tap`);try{const{base:e}=E(t,{repo:(await r.recordedWorktree(t,a))?.repo}),s=c(i=>r.appendStatus(t,a,"land-waiting",{card:i}),"onWait");return await H(t,a,{text:`Landed on ${e} on your Merge.`,closing:["landed-on-merge",{base:e}],onWait:s,merge:!0})}catch(e){if(!(e instanceof r.BoardError))throw e;const s=e.message.split(`
|
|
2
|
+
`)[0];if(/^rebasing .+ conflicts in |^tests failed in /.test(s)){await r.appendStatus(t,a,"lander-handoff",{why:s});try{await o(t,{key:a,model:await r.builderModel(t,a)},{why:s})}catch(i){await r.appendStatus(t,a,"lander-failed",{error:i.message})}}else await r.appendStatus(t,a,"land-failed",{why:s});throw e}}c(Z,"landHeld");async function x(t,a,o,n){if(!(await S(o)).pushMain)return[];const e=w(o,"push","--quiet","origin",n);if(e.ok)return[`pushed ${n} to origin`];const s=`${n} didn't reach origin: ${e.err}; push it with git -C ${o} push origin ${n}`;return await r.appendStatus(t,a,"push-failed",{base:n,error:e.err}),[`landed, but ${s}`]}c(x,"pushBase");async function _(t,a,{path:o,branch:n,base:e,ref:s,text:i}){await r.checkCallsVerified(t,a),P.existsSync(o)||b(`no worktree at ${o}`),w(o,"status","--porcelain").out&&b(`${o} has uncommitted changes; commit them there first`),Number(w(o,"rev-list","--count",`refs/remotes/origin/${e}..HEAD`).out)===0&&b(`${n} has no commits beyond ${e}; nothing to send for review`),await j(o,{outcome:"nothing was pushed"});const{github:g}=D(C(t).route),d=g?await r.cardPR(t,a):null,f=d?w(o,"rev-parse",`refs/remotes/origin/${n}`).out:null,u=d?w(o,"push","--quiet",`--force-with-lease=refs/heads/${n}:${f}`,"origin",`HEAD:refs/heads/${n}`):w(o,"push","--quiet","origin",`HEAD:refs/heads/${n}`);if(u.ok||b(`could not push ${n} to origin: ${u.err}`),d){const h=c(B=>Number(w(o,"rev-list","--count",`refs/remotes/origin/${e}..${B}`).out),"ahead");await r.recordRebase(t,a,{from:f,to:w(o,"rev-parse","HEAD").out,replayed:h("HEAD")<=h(f)});const R=await r.setItem(t,a,{pr:d});return await r.appendMessage(t,a,{text:i}),{said:`pushed ${n} to PR #${d}`,lines:R.lines}}if(!g){const h=await r.setItem(t,a,{pushed:n});return await r.appendMessage(t,a,{text:i}),{said:`pushed ${n} for its merge`,lines:h.lines}}const{title:$}=await r.briefFacts(t,a),p=q("gh",["pr","create","--draft","--repo",g,"--base",e,"--head",n,"--title",`${s}: ${$}`,"--body",i],{cwd:o,encoding:"utf8"}),l=`${p.stdout||""}
|
|
3
|
+
${p.stderr||""}`,m=(p.status===0||/already exists/.test(l))&&/\/pull\/(\d+)/.exec(l)?.[1];m||b(`could not open a draft PR for ${n}: ${(p.stderr||p.error?.message||`gh exited ${p.status}`).trim().split(`
|
|
4
|
+
`)[0]}`);const M=await r.setItem(t,a,{pr:Number(m)});return await r.appendMessage(t,a,{text:i}),{said:`draft PR #${m} opened`,lines:M.lines}}c(_,"sendForReview");export{H as finishCard,Z as landHeld};
|