@lovek4d/kickflip 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTEXT.md +6 -6
- package/SKILL.md +7 -7
- package/bin/event-lines.mjs +1 -1
- package/board/bin/board.mjs +25 -20
- package/board/domain.mjs +7 -7
- package/board/public/app-cards.js +31 -8
- package/board/public/app.js +22 -2
- package/board/public/dialogs.js +2 -2
- package/board/public/new-task.js +2 -2
- package/board/public/prototype-kit.js +1 -0
- package/board/public/prototype.html +6 -0
- package/board/public/style.css +7 -2
- package/board/public/ui-cards.js +11 -3
- package/board/public/ui-enums.js +1 -1
- package/board/public/ui.js +10 -2
- package/board/public/view-questions.js +8 -4
- package/board/public/view-settings.js +9 -3
- package/board/render-check.mjs +8 -8
- package/board/server.mjs +17 -17
- package/board/store.mjs +32 -32
- package/hub/hub.mjs +11 -11
- package/hub/presence.mjs +1 -1
- package/hub/review-send.mjs +1 -1
- package/hub/slack.mjs +4 -2
- package/hub/try.mjs +12 -11
- package/hub/upgrade.mjs +1 -1
- package/hub/views.mjs +1 -1
- package/package.json +1 -1
package/board/domain.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var Vt=Object.defineProperty;var d=(e,t)=>Vt(e,"name",{value:t,configurable:!0});import{routeRecord as R}from"../hub/routes.mjs";import Xt from"node:fs";import{ticketBadge as pt,ticketIn as Yt}from"./tickets.mjs";import{prLinks as zt,reviewLinks as Jt,reviewTime as Zt,reviewersOf as en}from"./review.mjs";import{remoteFromInput as tn}from"../hub/repos.mjs";import{reviewsOf as nn,reviewNeeds as sn,reviewActions as on}from"./auto-review.mjs";import{cardPRs as rn,hasPR as et,leastReady as an,prKeyOf as ln,prURL as dn}from"../shared/card-prs.mjs";import ft from"node:vm";import{checkWords as cn,parseCheck as un}from"../shared/owner-check.mjs";import{buildScriptOf as pn}from"./build-script.mjs";import{HUB as Qe,hubLineOf as gt}from"./hub-lines.mjs";const fn="card-merged",tt=ft.createContext({});for(const e of["ui-enums.js","ui-order.js"])ft.runInContext(Xt.readFileSync(new URL(`./public/${e}`,import.meta.url),"utf8"),tt);const{BUTTON:ce,ACTION_KIND:q,BUILDER_KIND:ue,PR_STATE:V,PHASES:kt}=tt.BoardUI,{laneOrder:gn}=tt.BoardUI,kn=["item-message","mark-ready","grill-more","prototype-request","visual-request","reask-request"],mt="question-reopen",So=Object.freeze({todo:"Todo",progress:"In Progress",review:"In Review",done:"Done"}),$e="move",mn=10*6e4,hn=5*6e4,ht="build-refused",De=Object.freeze({ask:"builder-ask",answer:"builder-answer"}),wn=864e5,pe=d(e=>e.lane==="done","closedLane"),yn=d(e=>`file:${e}`,"fileFold"),wt=7*wn,yt=d((e,t)=>({id:e,url:t?.workspace?`https://linear.app/${t.workspace}/issue/${e}`:null}),"linearRef"),bn=d(e=>String(e).replace(/^S(\d+)$/,"INT-$1"),"internalRef"),nt=d((e,t,n)=>Yt(n,t.ticket)?t.ticket:e.internal===!0?bn(t.id):t.id,"refOf"),Sn=d((e,t,n)=>pt(e,t,n)||(t?{id:String(t),url:null,source:null}:null),"ticketOf");function vn(e,t,n,s,r,a,c,l,u){return!a&&(e.lane==="done"||t?.state==="MERGED"||t?.state==="CLOSED")?"done":(n||s)&&!l?"progress":r?"review":t?.state==="OPEN"?t.checks?.state==="fail"&&(!t.isDraft||c)?"progress":"review":t||u?"progress":"todo"}d(vn,"laneOf");const He="Lander",bt=d(e=>e.kind!==ue.install&&e.label!==He,"changesCode");function xn(e,t){return e?.state==="MERGED"||t?V.merged:e?.state==="CLOSED"?V.closed:e?.state==="OPEN"?e.isDraft?V.draft:V.open:null}d(xn,"prStateOf");const St=d(e=>e.pr?.status===V.closed,"unlanded"),On=d(e=>e.some(t=>t.kind==="work")&&e.every(t=>t.lane==="done"),"finishedOf");function st(e){const t=vt(e);return t?.done&&t.handed_back?t.handed_back:null}d(st,"handedBackAt");const vt=d(e=>e.filter(t=>t?.started&&Q(t)&&t.kind!==ue.install&&t.kind!==ge).sort((t,n)=>String(t.started).localeCompare(String(n.started))).at(-1),"newestBuild");function En(e,t,n){return Object.fromEntries(e.map(s=>{const r=R(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"?R(n,s.name):null,github:s.github||null}]}))}d(En,"reviewOf");function $n(e,t,n,s,r,a,{held:c,needsApproval:l}){return n?.kind===ue.install?"builder":s?"checking":n?"builder":c&&e==="review"?"merge":r&&e==="review"?"owner":e==="review"&&t?.state==="OPEN"&&t.isDraft?a?"ci":"ready":e==="review"?xt(t,l)?"merge":"reviewers":null}d($n,"waitsOnOf");const xt=d((e,t)=>e?.reviewDecision==="APPROVED"||!t&&!["REVIEW_REQUIRED","CHANGES_REQUESTED"].includes(e?.reviewDecision),"reviewed"),Ne=/(^|\/)prototype-[\w-]+\.html/,An={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"},Bn=d(e=>`Verified: tap Merge to land it on ${e}`,"heldNext"),_n="Fix the failing CI",Rn="Your reply waits on the orchestrator",Mn="Tap Done",In=["status","agent","ask-link","command",$e,De.answer],qn=d(e=>(e.who==="claude"||e.via==="github")&&!In.includes(e.kind)&&(e.kind!==Qe||gt(e).fault),"countsUnread"),Cn=["lander-handoff","land-waiting"];function Dn({slug:e,itemFile:t,it:n,order:s,prs:r,repo:a,remote:c,base:l,builder:u,reachedPhase:m,agents:f,worktree:p,eta:h,thread:v,events:_,working:x,said:P,askedAt:C,topicItems:L,prSlack:T,boardReviewers:W,now:w,bumpedAt:b,holdAsk:i,closedAt:S,mergeAt:oe,autoMerge:me,skipDraft:he,tickets:K}){const F=r.map(k=>({repo:k.repo,number:k.number,...k.gh||k.kept||{},url:(k.gh||k.kept)?.url||dn(k),status:xn(k.gh,k.merged_at)})),ee=an(r.map(k=>k.gh)),I=r[ee]?.gh||null,G=F[ee]?.status??null,ze=n.lane||null,U=!!S&&String(S)>String(n.lane_at||"");U&&(n={...n,lane:"done",closed_at:S,reopened_at:void 0});const D=[n.closed_at,...r.flatMap(k=>[k.merged_at,k.gh?.mergedAt])].filter(Boolean).map(String).sort().pop()||null,we=String(D||""),ye=F.length>0&&F.every(k=>k.status===V.closed),be=(n.lane==="done"||n.reopened_at||G===V.merged)&&!ye&&([...P.filter(k=>k.kind==="item-message").map(k=>k.ts),...C].map(String).filter(k=>k>we).sort().pop()||n.reopened_at)||void 0,Le=n.answered_at&&!n.phase&&!n.closed_at&&!be?String(n.answered_at):void 0,j=be||Le,Re=n.owner_step&&!U?String(n.owner_step):null,Je=(n.lane==="done"||G===V.merged)&&!ye,H=!!j&&!!n.next&&String(n.next_at||"")>String(n.reopened_at||D||"");j&&!H&&(n={...n,next:void 0});const X=st(f),Te=j&&P.filter(k=>String(k.ts)>we&&(k.state==="waiting"||k.state==="working")).map(k=>String(k.ts)).sort()[0]||null,Se=!!Te,ve=`${e}/${n.id}`,xe=en(t,W),Pe=Object.fromEntries(Object.entries(Jt(t,n,ve,L,zt(T,r))).map(([k,de])=>[k,Zt(de.ts)])),te=Object.values(Pe).filter(Boolean).sort()[0]||null,Ue=[...v.map(k=>k.ts),..._.map(k=>k.ts)].sort().pop()||null,je=[...v.filter(k=>qn({...k,who:k.who||"claude"})).map(k=>k.ts),..._.map(k=>k.reply?.at)].filter(Boolean).sort(),Y=!et(n)&&n.pushed?.branch?{branch:n.pushed.branch,at:n.pushed.at||null,host:c?.host||null}:null,z=!et(n)&&n.merge_ready?.branch?{branch:n.merge_ready.branch,at:n.merge_ready.at||null,base:l}:null,Me=!!n.rebase_run&&I?.state==="OPEN",Ie=(n.rebased||[]).at(-1),re=f.filter(k=>k.started&&!k.done&&Q(k)),Ze=!!X&&bt(vt(f));let A=vn(n,I,re.some(bt),Ze,Y||z,!!j,he,Me,re.length>0||!!X);j&&A==="todo"&&(A="progress");const We=A==="review"&&!!te&&w-Date.parse(te)>wt,ae=!!Re&&Je&&A==="done";ae&&(A="review");let N=$n(A,I,u,!!X&&A!=="done",Y,he,{held:z,needsApproval:xe.length>0});const Fe=N==="builder"&&I?.state==="OPEN"&&!I.isDraft,qe=A==="progress"&&!N&&I?.state==="OPEN"&&(!I.isDraft||he),Oe=ae||!!j&&A==="progress"&&!N&&!qe&&!Se&&!H;Oe&&(N="owner");const o=!!j&&f.some(k=>Q(k)&&String(k.started||"")>String(j)),g=A==="progress"&&!N&&(qe||!!j&&!o);g&&(A="review");const E=i&&String(i.ts)>String(n.held_at||""),$=A==="todo"&&(E?!!i.data.hold:!!n.held),y=$&&(E?i.data.until:n.pick_up_at)||null,M=A!=="done",B=!!b&&!(we&&String(b)<=we),Ke=v.filter(k=>(k.who||"claude")==="claude"&&!["status","command",$e].includes(k.kind)&&!Cn.includes(k.line)).map(k=>String(k.ts)).sort().pop()||"",le=[oe,z&&n.merge_ready.tapped].filter(Boolean).map(String).sort().pop(),Ce=N==="merge"&&!!le&&le>Ke,Ee=v.filter(k=>(k.kind==="status"||k.kind===Qe)&&String(k.ts)>String(le)).sort((k,de)=>String(k.ts).localeCompare(String(de.ts))).at(-1),ne=Ee?.line==="land-waiting"?`${Ve.merging} \xB7 ${Ee.text[0].toLowerCase()}${Ee.text.slice(1)}`:Ve.merging,J=v.filter(k=>!["status",$e].includes(k.kind)).sort((k,de)=>String(k.ts).localeCompare(String(de.ts))).at(-1),O=A==="todo"&&J?.line===ht?J.text:null,ie=j||f.filter(Q).map(k=>k.started).filter(Boolean).map(String).sort().pop()||null,Ge=I?.reviewDecision==="APPROVED",Kt=Y&&N==="owner"?`merge ${Y.branch} into ${l}`:z&&N==="merge"?Ce?ne:Bn(l):N==="merge"&&!me?Ce?ne:(Ge?Ve:ot).waiting:N==="merge"&&!Ge?ot.auto:An[N],Gt=G===V.draft?`Review draft PR #${F[ee].number}`:null,Qt=!Me&&Ie?.to&&Ie.to===I?.headRefOid?`Rebased onto ${Ie.base||l}, pushed${I.mergeable==="MERGEABLE"?" \xB7 PR is clean again":""}`:null,Ht=Me&&N==="checking"?f.flatMap(k=>k.calls).sort((k,de)=>String(k.at).localeCompare(String(de.at))).at(-1)?.text??null:null;return{key:ve,kind:"work",order:s,file:e,fileTitle:t.title||e,id:n.id,ref:nt(t,n,K),title:n.title,storedLane:ze,created:n.created||null,reviewSince:g&&j||n.review_since||te||n.merge_ready?.at||n.pushed?.at||n.created||null,progressSince:ie,closedAt:A==="done"?D:null,internal:t.internal===!0,repo:t.internal===!0&&!t.repo?null:a,lane:A,waitsOn:N,builder:N==="checking"?null:u,reachedPhase:m,agents:f,worktree:p??null,eta:h,working:x,reopened:!!be,answered:!!Le,awaitsDone:Oe,ownerStep:Oe?Re:null,ownerCheck:Oe&&Re&&n.owner_check?{words:cn(un(n.owner_check.check)),broken:n.owner_check.broken||null}:null,stagedAt:A==="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:$,heldUntil:y,risky:!!n.risky,bumpable:M,bumpedAt:M&&B?b:null,holdable:A!=="done",hasNotes:!!(n.plan||String(n.notes||"").trim()),finishable:A!=="done",merging:Ce,refusal:O,manualSteps:n.manual_steps||[],fromAsk:n.from_ask||null,unfiled:!!n.unfiled,filedHeld:!!n.filed_held,next:Fe||A==="done"||N==="checking"?null:g&&Se?Rn:qe?_n:Qt||Kt||n.next||Gt||null,rebaseFailed:Ht,branch:n.branch||I?.headRefName||Y?.branch||null,pushed:A==="done"||ae?null:Y,mergeReady:A==="review"?z:null,stale:A!=="done"&&(We||!!u?.stale),stalled:n.stalled?.reason||null,landStopped:A==="review"&&z&&n.merge_ready.stopped&&String(n.merge_ready.stopped)>String(n.merge_ready.tapped||"")?{words:At("land"),at:n.merge_ready.stopped}:null,ticket:K?.source==="linear"&&!n.ticket&&n.linear?.length?null:Sn(K,n.ticket,{repo:a}),ticketSource:K?.source||null,linear:(n.linear||[]).map(k=>K?.source==="linear"&&pt(K,k,{repo:a})||yt(k,K)),pr:F[ee]||null,prs:F,reviewers:xe.map(k=>k.name),review:En(xe,I||r[ee]?.kept,Pe),inReviewSince:te,threadCount:v.length+_.length,lastTs:Ue,othersAt:je,grilling:Bt("grill",_,f,C),prototyping:Bt("prototype",_,f,C),storedPhase:n.phase||null,phase:null,recheckAt:n.recheck_at||null,updatedAt:I?.mergedAt||I?.updatedAt||r[ee]?.merged_at||u?.at||Ue||null,ask:null,fold:null,action:null,install:A==="done"?null:Ln(n.install,I),waitFacts:Nn(n,{agents:f,thread:v,events:_,refusedAt:O?J.ts:null,replyAt:g?Te:null})}}d(Dn,"workItem");function Nn(e,{agents:t,thread:n,events:s,refusedAt:r,replyAt:a}){const c=new Set(n.filter(f=>f.kind===De.answer).map(f=>f.ask)),l=n.filter(f=>f.kind===De.ask&&!c.has(f.ask)).sort((f,p)=>String(f.ts).localeCompare(String(p.ts)))[0],u=d(f=>s.filter(p=>at(p,f)).pop()?.ts||null,"requestAt"),m=n.filter(f=>(f.who||"claude")==="claude"&&!["status","agent","command",$e].includes(f.kind)&&f.line!==ht).map(f=>f.ts);return{handedBackAt:st(t),next:e.next||null,ask:l?{at:l.ts,text:l.text}:null,unasked:n.filter(f=>f.line==="planner-unasked").sort((f,p)=>String(f.ts).localeCompare(String(p.ts))).map(f=>({at:f.ts,kind:f.detail})).pop()||null,grillAt:u("grill"),prototypeAt:u("prototype"),refusedAt:r,replyAt:a,unblockedAt:e.unblocked_at||null,unverified:t.some(f=>f.calls.some(p=>!f.verified||String(p.at)>String(f.verified.at))),writes:[...m,...t.flatMap(f=>[f.verified?.at,f.started])].filter(Boolean).map(String).sort()}}d(Nn,"waitFactsOf");function Ln(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))}}d(Ln,"installOf");function Tn(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",!xt(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)}d(Tn,"mergeBlockers");const Pn=d((e,t)=>!Tn(e,{needsApproval:t}).length,"mergeable");function Un(e,t,n){if(pe(e))return null;const s=`#selectedCard=${encodeURIComponent(e.key)}`;if(e.stalled)return{kind:q.stalled,url:s,label:`stalled: ${e.stalled}`};if(jn(e)){const r=t.get(e.questions.first);return Ne.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.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)||Pn(r,e.reviewers.length>0))?{kind:q.merge,url:s,label:"Merge"}:null}d(Un,"actionOf");const jn=d(e=>!!(e.questions&&e.questions.state!=="resolved"&&e.questions.open>0),"asking"),rt=new Set([q.pick,q.questions]);function Ot(e){return e.questions?ce.openQuestions:null}d(Ot,"actionButtonOf");const Wn=d(e=>e.kind==="work"&&e.lane==="todo"&&!e.action&&!e.unpicked,"buildableOf"),Fn=d((e,t)=>/^item:/.test(e.topic||"")&&Ne.test(e.url||"")&&!(e.options||[]).some(n=>n.id===t),"picksNoTake");function Kn(e,t){const n=e.filter(a=>a.topic===`item:${t}`),r=[...n.filter(a=>Ne.test(a.url||"")).flatMap(a=>a.answers.map(c=>({q:a,a:c,again:Fn(a,c.choice)}))),...n.filter(a=>a.request).map(a=>({q:a,a:a.request,again:!0}))].sort((a,c)=>String(a.a.ts).localeCompare(String(c.a.ts))).at(-1);return r?.again?{id:r.q.id,note:r.a.note.replace(/\s+/g," ").trim()}:null}d(Kn,"noTakePicked");const Gn="Prototype again",Qn=Object.freeze({[q.pick]:ce.openQuestions,[q.questions]:ce.openQuestions,[q.ready]:ce.github,[q.steps]:ce.github,[q.merge]:ce.merge,[q.stalled]:ce.reply}),Hn="Merges once unblocked",Vn=d(e=>`Blocked ${[e.blockedBy.length&&`by ${e.blockedBy.map(t=>t.ref).join(", ")}`,e.blockedUntil&&`until ${e.blockedUntil}`].filter(Boolean).join(" and ")}`,"mergeBlockedWords");function Xn(e,t,n){if(e.action=Un(e,t,n),e.mergeBlocked=e.action?.kind===q.merge&&e.blocked?Vn(e):null,e.mergeBlocked&&Object.assign(e,{action:null,next:Hn}),e.action?.kind===q.questions&&e.questions.fromAsks){Object.assign(e,{action:null,waitsOn:"owner",next:e.action.label,actionButton:Ot(e),resolvedBy:null});return}e.awaitsDone&&=!e.action,e.awaitsDone||Object.assign(e,{ownerStep:null,ownerCheck:null}),e.actionButton=Ot(e),e.resolvedBy=Qn[e.action?.kind]||null,e.action?.kind===q.merge&&!e.mergeReady&&(e.next=(e.pr?.reviewDecision==="APPROVED"?Ve:ot).ready),e.awaitsDone&&Object.assign(e,{lane:"review",next:e.ownerStep||Mn,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})}d(Xn,"placeOwnerWait");const Et=d((e,t)=>e?e!=="todo":t.some(Q),"started"),$t="none set yet";function vo(e,t){const n=t.filter(s=>s?.started);return e.next||et(e)||e.pushed||e.held||e.lane==="done"||st(n)?!1:!n.some(s=>!s.done)&&Et(e.lane,n)}d(vo,"owesNext");const fe="visual",ge="helper",Yn=new Set(["grill","prototype",fe]),Z=d(e=>Yn.has(e.kind),"plans"),Q=d(e=>!Z(e),"builds"),zn={grill:"grilling",prototype:"prototyping"},Jn=d((e,t)=>`${e} ${t} by orchestrator`,"orchestratorDid"),At=d(e=>Jn(e,"stopped"),"stoppedWords"),Zn={grill:"grill-more",prototype:"prototype-request"},at=d((e,t)=>e.kind===Zn[t]&&!e.data?.question,"isRequest");function Bt(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)))}d(Bt,"planningOf");const xo=30*6e4;function Oo(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&&at(a,"prototype")).pop();r&&n.push({key:s.key,request:r.id,since:r.ts});for(const a of e.owner.filter(c=>c.topic===`item:${s.key}`&&c.state==="requested"))n.push({key:s.key,request:a.request.id,question:a.id,since:a.request.ts})}return n}d(Oo,"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:_t(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===ge?{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))}d(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}d(it,"worktreeOf");function es(e,t=[]){const n=e.filter(Q).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}d(es,"reachedPhaseOf");function ts(e,t=[],n,s=null,r=null,a=!1){const c=e.filter(b=>b.started&&!b.done&&b.kind!==fe),l=c.every(Z),u=l?c:c.filter(Q),m=u.map(b=>String(b.started||"")).sort()[0];if(m===void 0)return null;const f=t.filter(b=>b.kind==="status"&&String(b.ts)>=m&&(l||!b.planner)).sort((b,i)=>String(b.ts).localeCompare(String(i.ts))).pop(),p=u.filter(b=>b.kind!==ge).at(-1),h=u.some(b=>b.kind===ue.install)?ue.install:p?_t(p.kind):"claude",v=l?null:u.find(b=>b.stopped&&String(b.stopped.at)>=String(b.started))?.stopped,_=f?.ts||m,x=v?v.at:s!==null&&s>Date.parse(_)?new Date(s).toISOString():_,P=f?f.phase||f.planner?f.text.replace(": "," \xB7 "):f.text||null:l?zn[h]:null,C=n-Date.parse(x),L=l?null:u.find(b=>b.pause)?.pause??null,T=!l&&!v&&(r==="held"||!!L?.paused),W=l||T?null:v?!1:r,w=T?!1:W===null?C>=mn:!W;return{progress:P,phase:f?.phase||null,at:_,kind:h,planner:l,silentSince:x,stale:w,alive:W,held:T,quiet:!!L||W===!0&&!a&&C>=hn,stopped:v?At(v.run):null,...L?{pause:{cause:L.cause,words:L.words,paused:!!L.paused}}:{}}}d(ts,"builderOf");const Eo={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"]},ns=15*6e4,ss=d(e=>(e.agents||[]).find(t=>t.name==="main"&&t.started&&!t.done&&Q(t))??null,"mainBuild"),os=d((e,t,n,{now:s,usageHeld:r})=>e.blocked||e.held||r(n??"claude")||!!(t.until&&s<Date.parse(t.until)),"stillPaused");function rs(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 c=ss(a);if(!c)continue;const{pause:l}=c;if(!l){a.builder?.phase!=="landing"&&n(c.kind??"claude")&&s.ask.push(a.key);continue}const u=os(a,l,c.kind,{now:t,usageHeld:n});if(l.paused){u||r.push({key:a.key,at:l.paused});continue}!u&&l.cause!=="bumped"?s.drop.push(a.key):t-Date.parse(l.at)>=ns&&s.stop.push(a.key)}return s.resume=r.sort((a,c)=>String(a.at).localeCompare(String(c.at))).map(a=>a.key),s}d(rs,"pauseWork");function $o(e,t,n){const s=rs(t,n).drop;return e.filter(r=>s.includes(r))}d($o,"confirmedDrops");const dt={opus:"Claude Opus",sonnet:"Claude Sonnet",codex:"Codex"},as=d(({model:e,kind:t})=>dt[e]||dt[t]||(t==="claude"?dt.opus:null),"modelName"),_t=d(e=>e==="opus"||e==="sonnet"?"claude":e,"providerOf");function Ae(e){const t=as(e),{kind:n}=e,s=e.label?.replace(/\bbuilder\b/,"implementer").replace(/\bBuilder\b/,"Implementer"),r=d(c=>c[0].toLowerCase()+c.slice(1),"lower");if(t&&Z(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"}d(Ae,"agentName");const Rt=d((e,t)=>!!e.resolved&&(!e.resolved_at||String(e.resolved_at)>=String(t)),"resolvedSince");function ls(e,t,n=[],s=[],r=[]){const a=t.filter(x=>x.data?.id===e.id),c=a[a.length-1]||null,l=r.filter(x=>x.data?.question===e.id).at(-1),u=d((x,...P)=>!!x&&P.every(C=>String(x.ts)>String(C||"")),"after"),m=!!(e.resolved&&e.resolved_at&&c&&String(c.ts)>e.resolved_at),f=n.filter(x=>x.data?.question===e.id&&u(x,c?.ts,l?.ts)).at(-1),p=u(l,c?.ts,f?.ts,e.resolved_at),h=e.resolved&&!m&&!p,v=h?s.filter(x=>x.data?.question===e.id&&String(x.ts)>String(e.resolved_at||"")).at(-1):null,_=p?"open":h?"resolved":f?"requested":c?c.reply?"replied":"answered":"open";return{...e,state:_,reopened:p,request:f?{id:f.id,ts:f.ts,note:f.data.note||"",choice:f.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,replies:x.replies,acted:Rt(e,x.ts)}))}}d(ls,"ownerQuestion");const ke=d(e=>e.round??(Ne.test(e.url||"")?1:0),"roundOf");function is(e,t){const n=d((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)}d(is,"roundSettledBy");function ds(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=is(e,n);return s?{n,state:"closed",take:s.choice}:e.some(r=>ke(r)===n&&r.state!=="resolved")?{n,state:"back"}:null}d(ds,"roundState");function cs(e){const t=e.filter(n=>Ne.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}d(cs,"prototypesOf");const Be=d((e,t,n)=>{const s=e.get(t);s?s.push(n):e.set(t,[n])},"add"),_e=d((e,t)=>{const n=new Map;for(const s of e)Be(n,t(s),s);return n},"groupBy");function us(e,t,n=new Set){const s=new Set(e.map(c=>c.id)),r=[...e];t.some(c=>!s.has(c.topic))&&r.push({id:"",title:"Other"});const a=_e(t,c=>s.has(c.topic)?c.topic:"");return r.map(c=>{const l=a.get(c.id)||[],u=d(p=>l.filter(h=>h.state===p).length,"count"),m=l.every(p=>p.state==="resolved")||n.has(c.id)&&!u("open"),f=l.length&&m?"resolved":"open";return{...c,state:f,questions:l.map(p=>p.id),open:u("open"),answered:u("answered"),replied:u("replied"),resolved:u("resolved"),round:ds(l)}}).filter(c=>c.questions.length)}d(us,"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"},Mt={"grill-more":"grill","prototype-request":"prototype","visual-request":"visual","reask-request":"reask"},It={"mark-ready":"Mark ready",...Object.fromEntries(Object.entries(Mt).map(([e,t])=>[e,Xe[t]]))},ps=["grill-more","prototype-request","visual-request"],fs="answers submitted",qt={reply:"Reply",build:"Implement"},gs=d(e=>qt[e.data.intent],"intentLabel"),ks=d((e,t)=>{if(e.kind!=="prototype-request"||!t.has(e.data.question))return e.data.text||(e.data.files?.length?"":R(It,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(`
|
|
2
|
-
\u2192 `)},"actionText"),Ct=d((e,t)=>[...e.keys()].indexOf(t)+1,"numberOf"),Dt=d((e,t)=>`Question ${Ct(e,t.id)} \xB7 ${t.question}`,"numbered"),ms="question reopened",Ye=d(e=>e.data?.shared_by?.login?{by:e.data.shared_by.name||e.data.shared_by.login}:{},"byOf"),hs=d((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"),Nt=d(e=>({kind:"command",command:e,request:!0,mark:Xe[e]}),"commandLine"),ws=d((e,t)=>{if(e.kind==="item-message")return qt[e.data.intent]?{status:gs(e)}:{};const n={request:!0,command:Mt[e.kind]};return ps.includes(e.kind)?(e.data.question?t.has(e.data.question):String(e.data.text||"").trim()||e.data.files?.length)?{status:It[e.kind]}:n:e.kind==="reask-request"?n:{}},"requestOf"),Lt=d(e=>[...e.filter(t=>t.who==="claude"&&!["status","agent","command",$e,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 Tt(e,t={}){const n=new Map,s=new Map;for(const
|
|
3
|
-
\u2192 ${
|
|
1
|
+
var Vt=Object.defineProperty;var d=(e,t)=>Vt(e,"name",{value:t,configurable:!0});import{routeRecord as R}from"../hub/routes.mjs";import Xt from"node:fs";import{ticketBadge as pt,ticketIn as Yt}from"./tickets.mjs";import{prLinks as zt,reviewLinks as Jt,reviewTime as Zt,reviewersOf as en}from"./review.mjs";import{remoteFromInput as tn}from"../hub/repos.mjs";import{reviewsOf as nn,reviewNeeds as sn,reviewActions as on}from"./auto-review.mjs";import{cardPRs as rn,hasPR as et,leastReady as an,prKeyOf as ln,prURL as dn}from"../shared/card-prs.mjs";import ft from"node:vm";import{checkWords as cn,parseCheck as un}from"../shared/owner-check.mjs";import{buildScriptOf as pn}from"./build-script.mjs";import{HUB as Qe,hubLineOf as gt}from"./hub-lines.mjs";const fn="card-merged",tt=ft.createContext({});for(const e of["ui-enums.js","ui-order.js"])ft.runInContext(Xt.readFileSync(new URL(`./public/${e}`,import.meta.url),"utf8"),tt);const{BUTTON:ce,ACTION_KIND:q,BUILDER_KIND:ue,PR_STATE:X,PHASES:kt}=tt.BoardUI,{laneOrder:gn}=tt.BoardUI,kn=["item-message","mark-ready","grill-more","prototype-request","visual-request","reask-request"],mt="question-reopen",So=Object.freeze({todo:"Todo",progress:"In Progress",review:"In Review",done:"Done"}),$e="move",mn=10*6e4,hn=5*6e4,ht="build-refused",De=Object.freeze({ask:"builder-ask",answer:"builder-answer"}),wn=864e5,pe=d(e=>e.lane==="done","closedLane"),yn=d(e=>`file:${e}`,"fileFold"),wt=7*wn,yt=d((e,t)=>({id:e,url:t?.workspace?`https://linear.app/${t.workspace}/issue/${e}`:null}),"linearRef"),bn=d(e=>String(e).replace(/^S(\d+)$/,"INT-$1"),"internalRef"),nt=d((e,t,n)=>Yt(n,t.ticket)?t.ticket:e.internal===!0?bn(t.id):t.id,"refOf"),Sn=d((e,t,n)=>pt(e,t,n)||(t?{id:String(t),url:null,source:null}:null),"ticketOf");function vn(e,t,n,s,r,a,u,i,p){return!a&&(e.lane==="done"||t?.state==="MERGED"||t?.state==="CLOSED")?"done":(n||s)&&!i?"progress":r?"review":t?.state==="OPEN"?t.checks?.state==="fail"&&(!t.isDraft||u)?"progress":"review":t||p?"progress":"todo"}d(vn,"laneOf");const He="Lander",bt=d(e=>e.kind!==ue.install&&e.label!==He,"changesCode");function xn(e,t){return e?.state==="MERGED"||t?X.merged:e?.state==="CLOSED"?X.closed:e?.state==="OPEN"?e.isDraft?X.draft:X.open:null}d(xn,"prStateOf");const St=d(e=>e.pr?.status===X.closed,"unlanded"),On=d(e=>e.some(t=>t.kind==="work")&&e.every(t=>t.lane==="done"),"finishedOf");function st(e){const t=vt(e);return t?.done&&t.handed_back?t.handed_back:null}d(st,"handedBackAt");const vt=d(e=>e.filter(t=>t?.started&&H(t)&&t.kind!==ue.install&&t.kind!==ge).sort((t,n)=>String(t.started).localeCompare(String(n.started))).at(-1),"newestBuild");function En(e,t,n){return Object.fromEntries(e.map(s=>{const r=R(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"?R(n,s.name):null,github:s.github||null}]}))}d(En,"reviewOf");function $n(e,t,n,s,r,a,{held:u,needsApproval:i}){return n?.kind===ue.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"?xt(t,i)?"merge":"reviewers":null}d($n,"waitsOnOf");const xt=d((e,t)=>e?.reviewDecision==="APPROVED"||!t&&!["REVIEW_REQUIRED","CHANGES_REQUESTED"].includes(e?.reviewDecision),"reviewed"),Ne=/(^|\/)prototype-[\w-]+\.html/,An={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"},Bn=d(e=>`Verified: tap Merge to land it on ${e}`,"heldNext"),_n="Fix the failing CI",Rn="Your reply waits on the orchestrator",Mn="Tap Done",In=["status","agent","ask-link","command",$e,De.answer],qn=d(e=>(e.who==="claude"||e.via==="github")&&!In.includes(e.kind)&&(e.kind!==Qe||gt(e).fault),"countsUnread"),Cn=["lander-handoff","land-waiting"];function Dn({slug:e,itemFile:t,it:n,order:s,prs:r,repo:a,remote:u,base:i,builder:p,reachedPhase:m,agents:f,worktree:c,eta:h,thread:O,events:A,working:S,said:P,askedAt:D,topicItems:U,prSlack:N,boardReviewers:j,now:w,bumpedAt:b,holdAsk:C,closedAt:l,mergeAt:B,autoMerge:me,skipDraft:he,tickets:G}){const K=r.map(k=>({repo:k.repo,number:k.number,...k.gh||k.kept||{},url:(k.gh||k.kept)?.url||dn(k),status:xn(k.gh,k.merged_at)})),te=an(r.map(k=>k.gh)),I=r[te]?.gh||null,Q=K[te]?.status??null,ze=n.lane||null,W=!!l&&String(l)>String(n.lane_at||"");W&&(n={...n,lane:"done",closed_at:l,reopened_at:void 0});const L=[n.closed_at,...r.flatMap(k=>[k.merged_at,k.gh?.mergedAt])].filter(Boolean).map(String).sort().pop()||null,we=String(L||""),ye=K.length>0&&K.every(k=>k.status===X.closed),be=(n.lane==="done"||n.reopened_at||Q===X.merged)&&!ye&&([...P.filter(k=>k.kind==="item-message").map(k=>k.ts),...D].map(String).filter(k=>k>we).sort().pop()||n.reopened_at)||void 0,Le=n.answered_at&&!n.phase&&!n.closed_at&&!be?String(n.answered_at):void 0,F=be||Le,Re=n.owner_step&&!W?String(n.owner_step):null,Je=(n.lane==="done"||Q===X.merged)&&!ye,V=!!F&&!!n.next&&String(n.next_at||"")>String(n.reopened_at||L||"");F&&!V&&(n={...n,next:void 0});const Y=st(f),Te=F&&P.filter(k=>String(k.ts)>we&&(k.state==="waiting"||k.state==="working")).map(k=>String(k.ts)).sort()[0]||null,Se=!!Te,ve=`${e}/${n.id}`,xe=en(t,j),Pe=Object.fromEntries(Object.entries(Jt(t,n,ve,U,zt(N,r))).map(([k,de])=>[k,Zt(de.ts)])),ne=Object.values(Pe).filter(Boolean).sort()[0]||null,Ue=[...O.map(k=>k.ts),...A.map(k=>k.ts)].sort().pop()||null,je=[...O.filter(k=>qn({...k,who:k.who||"claude"})).map(k=>k.ts),...A.map(k=>k.reply?.at)].filter(Boolean).sort(),z=!et(n)&&n.pushed?.branch?{branch:n.pushed.branch,at:n.pushed.at||null,host:u?.host||null}:null,J=!et(n)&&n.merge_ready?.branch?{branch:n.merge_ready.branch,at:n.merge_ready.at||null,base:i}:null,Me=!!n.rebase_run&&I?.state==="OPEN",Ie=(n.rebased||[]).at(-1),re=f.filter(k=>k.started&&!k.done&&H(k)),Ze=!!Y&&bt(vt(f));let E=vn(n,I,re.some(bt),Ze,z||J,!!F,he,Me,re.length>0||!!Y);F&&E==="todo"&&(E="progress");const We=E==="review"&&!!ne&&w-Date.parse(ne)>wt,ae=!!Re&&Je&&E==="done";ae&&(E="review");let T=$n(E,I,p,!!Y&&E!=="done",z,he,{held:J,needsApproval:xe.length>0});const Fe=T==="builder"&&I?.state==="OPEN"&&!I.isDraft,qe=E==="progress"&&!T&&I?.state==="OPEN"&&(!I.isDraft||he),Oe=ae||!!F&&E==="progress"&&!T&&!qe&&!Se&&!V;Oe&&(T="owner");const o=!!F&&f.some(k=>H(k)&&String(k.started||"")>String(F)),g=E==="progress"&&!T&&(qe||!!F&&!o);g&&(E="review");const x=C&&String(C.ts)>String(n.held_at||""),$=E==="todo"&&(x?!!C.data.hold:!!n.held),y=$&&(x?C.data.until:n.pick_up_at)||null,M=E!=="done",_=!!b&&!(we&&String(b)<=we),Ke=O.filter(k=>(k.who||"claude")==="claude"&&!["status","command",$e].includes(k.kind)&&!Cn.includes(k.line)).map(k=>String(k.ts)).sort().pop()||"",le=[B,J&&n.merge_ready.tapped].filter(Boolean).map(String).sort().pop(),Ce=T==="merge"&&!!le&&le>Ke,Ee=O.filter(k=>(k.kind==="status"||k.kind===Qe)&&String(k.ts)>String(le)).sort((k,de)=>String(k.ts).localeCompare(String(de.ts))).at(-1),se=Ee?.line==="land-waiting"?`${Ve.merging} \xB7 ${Ee.text[0].toLowerCase()}${Ee.text.slice(1)}`:Ve.merging,Z=O.filter(k=>!["status",$e].includes(k.kind)).sort((k,de)=>String(k.ts).localeCompare(String(de.ts))).at(-1),v=E==="todo"&&Z?.line===ht?Z.text:null,ie=F||f.filter(H).map(k=>k.started).filter(Boolean).map(String).sort().pop()||null,Ge=I?.reviewDecision==="APPROVED",Kt=z&&T==="owner"?`merge ${z.branch} into ${i}`:J&&T==="merge"?Ce?se:Bn(i):T==="merge"&&!me?Ce?se:(Ge?Ve:ot).waiting:T==="merge"&&!Ge?ot.auto:An[T],Gt=Q===X.draft?`Review draft PR #${K[te].number}`:null,Qt=!Me&&Ie?.to&&Ie.to===I?.headRefOid?`Rebased onto ${Ie.base||i}, pushed${I.mergeable==="MERGEABLE"?" \xB7 PR is clean again":""}`:null,Ht=Me&&T==="checking"?f.flatMap(k=>k.calls).sort((k,de)=>String(k.at).localeCompare(String(de.at))).at(-1)?.text??null:null;return{key:ve,kind:"work",order:s,file:e,fileTitle:t.title||e,id:n.id,ref:nt(t,n,G),title:n.title,storedLane:ze,created:n.created||null,reviewSince:g&&F||n.review_since||ne||n.merge_ready?.at||n.pushed?.at||n.created||null,progressSince:ie,closedAt:E==="done"?L:null,internal:t.internal===!0,repo:t.internal===!0&&!t.repo?null:a,lane:E,waitsOn:T,builder:T==="checking"?null:p,reachedPhase:m,agents:f,worktree:c??null,eta:h,working:S,reopened:!!be,answered:!!Le,awaitsDone:Oe,ownerStep:Oe?Re:null,ownerCheck:Oe&&Re&&n.owner_check?{words:cn(un(n.owner_check.check)),broken:n.owner_check.broken||null}:null,stagedAt:E==="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:$,heldUntil:y,risky:!!n.risky,bumpable:M,bumpedAt:M&&_?b:null,holdable:E!=="done",hasNotes:!!(n.plan||String(n.notes||"").trim()),finishable:E!=="done",merging:Ce,refusal:v,manualSteps:n.manual_steps||[],fromAsk:n.from_ask||null,unfiled:!!n.unfiled,filedHeld:!!n.filed_held,next:Fe||E==="done"||T==="checking"?null:g&&Se?Rn:qe?_n:Qt||Kt||n.next||Gt||null,rebaseFailed:Ht,branch:n.branch||I?.headRefName||z?.branch||null,pushed:E==="done"||ae?null:z,mergeReady:E==="review"?J:null,stale:E!=="done"&&(We||!!p?.stale),stalled:n.stalled?.reason||null,landStopped:E==="review"&&J&&n.merge_ready.stopped&&String(n.merge_ready.stopped)>String(n.merge_ready.tapped||"")?{words:At("land"),at:n.merge_ready.stopped}:null,ticket:G?.source==="linear"&&!n.ticket&&n.linear?.length?null:Sn(G,n.ticket,{repo:a}),ticketSource:G?.source||null,linear:(n.linear||[]).map(k=>G?.source==="linear"&&pt(G,k,{repo:a})||yt(k,G)),pr:K[te]||null,prs:K,reviewers:xe.map(k=>k.name),review:En(xe,I||r[te]?.kept,Pe),inReviewSince:ne,threadCount:O.length+A.length,lastTs:Ue,othersAt:je,grilling:Bt("grill",A,f,D),prototyping:Bt("prototype",A,f,D),storedPhase:n.phase||null,phase:null,recheckAt:n.recheck_at||null,turn:E==="todo"&&n.phase==="discussing"?P.some(k=>k.state==="waiting"||k.state==="working")?"claude":"owner":void 0,updatedAt:I?.mergedAt||I?.updatedAt||r[te]?.merged_at||p?.at||Ue||null,ask:null,fold:null,action:null,install:E==="done"?null:Ln(n.install,I),waitFacts:Nn(n,{agents:f,thread:O,events:A,refusedAt:v?Z.ts:null,replyAt:g?Te:null})}}d(Dn,"workItem");function Nn(e,{agents:t,thread:n,events:s,refusedAt:r,replyAt:a}){const u=new Set(n.filter(f=>f.kind===De.answer).map(f=>f.ask)),i=n.filter(f=>f.kind===De.ask&&!u.has(f.ask)).sort((f,c)=>String(f.ts).localeCompare(String(c.ts)))[0],p=d(f=>s.filter(c=>at(c,f)).pop()?.ts||null,"requestAt"),m=n.filter(f=>(f.who||"claude")==="claude"&&!["status","agent","command",$e].includes(f.kind)&&f.line!==ht).map(f=>f.ts);return{handedBackAt:st(t),next:e.next||null,ask:i?{at:i.ts,text:i.text}:null,unasked:n.filter(f=>f.line==="planner-unasked").sort((f,c)=>String(f.ts).localeCompare(String(c.ts))).map(f=>({at:f.ts,kind:f.detail})).pop()||null,grillAt:p("grill"),prototypeAt:p("prototype"),refusedAt:r,replyAt:a,unblockedAt:e.unblocked_at||null,unverified:t.some(f=>f.calls.some(c=>!f.verified||String(c.at)>String(f.verified.at))),writes:[...m,...t.flatMap(f=>[f.verified?.at,f.started])].filter(Boolean).map(String).sort()}}d(Nn,"waitFactsOf");function Ln(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))}}d(Ln,"installOf");function Tn(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",!xt(e,t)&&`review: ${String(e.reviewDecision||"none").toLowerCase().replace(/_/g," ")}`,n==="fail"&&`failing: ${r?.length?r.join(", "):"a check"}`,n==="pending"&&`pending: ${s?oe(s,"check"):"checks"}`,e.mergeStateStatus!=="CLEAN"&&`merge state: ${String(e.mergeStateStatus||"unknown").toLowerCase()}`].filter(Boolean)}d(Tn,"mergeBlockers");const Pn=d((e,t)=>!Tn(e,{needsApproval:t}).length,"mergeable");function Un(e,t,n){if(pe(e))return null;const s=`#selectedCard=${encodeURIComponent(e.key)}`;if(e.stalled)return{kind:q.stalled,url:s,label:`stalled: ${e.stalled}`};if(jn(e)){const r=t.get(e.questions.first);return Ne.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 ${oe(e.questions.open,"question")}`}}return e.waitsOn==="ready"?{kind:q.ready,url:e.pr.url,label:`Review draft PR #${e.pr.number}`}: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)||Pn(r,e.reviewers.length>0))?{kind:q.merge,url:s,label:"Merge"}:null}d(Un,"actionOf");const jn=d(e=>!!(e.questions&&e.questions.state!=="resolved"&&e.questions.open>0),"asking"),rt=new Set([q.pick,q.questions]);function Ot(e){return e.questions?ce.openQuestions:null}d(Ot,"actionButtonOf");const Wn=d(e=>e.kind==="work"&&e.lane==="todo"&&!e.action&&!e.unpicked,"buildableOf"),Fn=d((e,t)=>/^item:/.test(e.topic||"")&&Ne.test(e.url||"")&&!(e.options||[]).some(n=>n.id===t),"picksNoTake");function Kn(e,t){const n=e.filter(a=>a.topic===`item:${t}`),r=[...n.filter(a=>Ne.test(a.url||"")).flatMap(a=>a.answers.map(u=>({q:a,a:u,again:Fn(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}d(Kn,"noTakePicked");const Gn="Prototype again",Qn=Object.freeze({[q.pick]:ce.openQuestions,[q.questions]:ce.openQuestions,[q.ready]:ce.github,[q.steps]:ce.github,[q.merge]:ce.merge,[q.stalled]:ce.reply}),Hn="Merges once unblocked",Vn=d(e=>`Blocked ${[e.blockedBy.length&&`by ${e.blockedBy.map(t=>t.ref).join(", ")}`,e.blockedUntil&&`until ${e.blockedUntil}`].filter(Boolean).join(" and ")}`,"mergeBlockedWords");function Xn(e,t,n){if(e.action=Un(e,t,n),e.mergeBlocked=e.action?.kind===q.merge&&e.blocked?Vn(e):null,e.mergeBlocked&&Object.assign(e,{action:null,next:Hn}),e.action?.kind===q.questions&&e.questions.fromAsks){Object.assign(e,{action:null,waitsOn:"owner",next:e.action.label,actionButton:Ot(e),resolvedBy:null});return}e.awaitsDone&&=!e.action,e.awaitsDone||Object.assign(e,{ownerStep:null,ownerCheck:null}),e.actionButton=Ot(e),e.resolvedBy=Qn[e.action?.kind]||null,e.action?.kind===q.merge&&!e.mergeReady&&(e.next=(e.pr?.reviewDecision==="APPROVED"?Ve:ot).ready),e.awaitsDone&&Object.assign(e,{lane:"review",next:e.ownerStep||Mn,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})}d(Xn,"placeOwnerWait");const Et=d((e,t)=>e?e!=="todo":t.some(H),"started"),$t="none set yet";function vo(e,t){const n=t.filter(s=>s?.started);return e.next||et(e)||e.pushed||e.held||e.lane==="done"||st(n)?!1:!n.some(s=>!s.done)&&Et(e.lane,n)}d(vo,"owesNext");const fe="visual",ge="helper",Yn=new Set(["grill","prototype",fe]),ee=d(e=>Yn.has(e.kind),"plans"),H=d(e=>!ee(e),"builds"),zn={grill:"grilling",prototype:"prototyping"},Jn=d((e,t)=>`${e} ${t} by orchestrator`,"orchestratorDid"),At=d(e=>Jn(e,"stopped"),"stoppedWords"),Zn={grill:"grill-more",prototype:"prototype-request"},at=d((e,t)=>e.kind===Zn[t]&&!e.data?.question,"isRequest");function Bt(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)))}d(Bt,"planningOf");const xo=30*6e4;function Oo(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&&at(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}d(Oo,"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:_t(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===ge?{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))}d(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}d(it,"worktreeOf");function es(e,t=[]){const n=e.filter(H).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}d(es,"reachedPhaseOf");function ts(e,t=[],n,s=null,r=null,a=!1){const u=e.filter(b=>b.started&&!b.done&&b.kind!==fe),i=u.every(ee),p=i?u:u.filter(H),m=p.map(b=>String(b.started||"")).sort()[0];if(m===void 0)return null;const f=t.filter(b=>b.kind==="status"&&String(b.ts)>=m&&(i||!b.planner)).sort((b,C)=>String(b.ts).localeCompare(String(C.ts))).pop(),c=p.filter(b=>b.kind!==ge).at(-1),h=p.some(b=>b.kind===ue.install)?ue.install:c?_t(c.kind):"claude",O=i?null:p.find(b=>b.stopped&&String(b.stopped.at)>=String(b.started))?.stopped,A=f?.ts||m,S=O?O.at:s!==null&&s>Date.parse(A)?new Date(s).toISOString():A,P=f?f.phase||f.planner?f.text.replace(": "," \xB7 "):f.text||null:i?zn[h]:null,D=n-Date.parse(S),U=i?null:p.find(b=>b.pause)?.pause??null,N=!i&&!O&&(r==="held"||!!U?.paused),j=i||N?null:O?!1:r,w=N?!1:j===null?D>=mn:!j;return{progress:P,phase:f?.phase||null,at:A,kind:h,planner:i,silentSince:S,stale:w,alive:j,held:N,quiet:!!U||j===!0&&!a&&D>=hn,stopped:O?At(O.run):null,...U?{pause:{cause:U.cause,words:U.words,paused:!!U.paused}}:{}}}d(ts,"builderOf");const Eo={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"]},ns=15*6e4,ss=d(e=>(e.agents||[]).find(t=>t.name==="main"&&t.started&&!t.done&&H(t))??null,"mainBuild"),os=d((e,t,n,{now:s,usageHeld:r})=>e.blocked||e.held||r(n??"claude")||!!(t.until&&s<Date.parse(t.until)),"stillPaused");function rs(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 u=ss(a);if(!u)continue;const{pause:i}=u;if(!i){a.builder?.phase!=="landing"&&n(u.kind??"claude")&&s.ask.push(a.key);continue}const p=os(a,i,u.kind,{now:t,usageHeld:n});if(i.paused){p||r.push({key:a.key,at:i.paused});continue}!p&&i.cause!=="bumped"?s.drop.push(a.key):t-Date.parse(i.at)>=ns&&s.stop.push(a.key)}return s.resume=r.sort((a,u)=>String(a.at).localeCompare(String(u.at))).map(a=>a.key),s}d(rs,"pauseWork");function $o(e,t,n){const s=rs(t,n).drop;return e.filter(r=>s.includes(r))}d($o,"confirmedDrops");const dt={opus:"Claude Opus",sonnet:"Claude Sonnet",codex:"Codex"},as=d(({model:e,kind:t})=>dt[e]||dt[t]||(t==="claude"?dt.opus:null),"modelName"),_t=d(e=>e==="opus"||e==="sonnet"?"claude":e,"providerOf");function Ae(e){const t=as(e),{kind:n}=e,s=e.label?.replace(/\bbuilder\b/,"implementer").replace(/\bBuilder\b/,"Implementer"),r=d(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"}d(Ae,"agentName");const Rt=d((e,t)=>!!e.resolved&&(!e.resolved_at||String(e.resolved_at)>=String(t)),"resolvedSince");function ls(e,t,n=[],s=[],r=[]){const a=t.filter(S=>S.data?.id===e.id),u=a[a.length-1]||null,i=r.filter(S=>S.data?.question===e.id).at(-1),p=d((S,...P)=>!!S&&P.every(D=>String(S.ts)>String(D||"")),"after"),m=!!(e.resolved&&e.resolved_at&&u&&String(u.ts)>e.resolved_at),f=n.filter(S=>S.data?.question===e.id&&p(S,u?.ts,i?.ts)).at(-1),c=p(i,u?.ts,f?.ts,e.resolved_at),h=e.resolved&&!m&&!c,O=h?s.filter(S=>S.data?.question===e.id&&String(S.ts)>String(e.resolved_at||"")).at(-1):null,A=c?"open":h?"resolved":f?"requested":u?u.reply?"replied":"answered":"open";return{...e,state:A,reopened:c,request:f?{id:f.id,ts:f.ts,note:f.data.note||"",choice:f.data.choice||null}:null,reask:O?{id:O.id,ts:O.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,replies:S.replies,acted:Rt(e,S.ts)}))}}d(ls,"ownerQuestion");const ke=d(e=>e.round??(Ne.test(e.url||"")?1:0),"roundOf");function is(e,t){const n=d((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)}d(is,"roundSettledBy");function ds(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=is(e,n);return s?{n,state:"closed",take:s.choice}:e.some(r=>ke(r)===n&&r.state!=="resolved")?{n,state:"back"}:null}d(ds,"roundState");function cs(e){const t=e.filter(n=>Ne.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}d(cs,"prototypesOf");const Be=d((e,t,n)=>{const s=e.get(t);s?s.push(n):e.set(t,[n])},"add"),_e=d((e,t)=>{const n=new Map;for(const s of e)Be(n,t(s),s);return n},"groupBy");function us(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=_e(t,u=>s.has(u.topic)?u.topic:"");return r.map(u=>{const i=a.get(u.id)||[],p=d(c=>i.filter(h=>h.state===c).length,"count"),m=i.every(c=>c.state==="resolved")||n.has(u.id)&&!p("open"),f=i.length&&m?"resolved":"open";return{...u,state:f,questions:i.map(c=>c.id),open:p("open"),answered:p("answered"),replied:p("replied"),resolved:p("resolved"),round:ds(i)}}).filter(u=>u.questions.length)}d(us,"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",discussing:"Marked as discussing",staged:"Marked as staging",readied:"Marked as ready",related:"Related to",unrelated:"Unrelated from",followup:"Follow-up","followup-of":"Follow-up of"},Mt={"grill-more":"grill","prototype-request":"prototype","visual-request":"visual","reask-request":"reask"},It={"mark-ready":"Mark ready",...Object.fromEntries(Object.entries(Mt).map(([e,t])=>[e,Xe[t]]))},ps=["grill-more","prototype-request","visual-request"],fs="answers submitted",qt={reply:"Reply",build:"Implement"},gs=d(e=>qt[e.data.intent],"intentLabel"),ks=d((e,t)=>{if(e.kind!=="prototype-request"||!t.has(e.data.question))return e.data.text||(e.data.files?.length?"":R(It,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(`
|
|
2
|
+
\u2192 `)},"actionText"),Ct=d((e,t)=>[...e.keys()].indexOf(t)+1,"numberOf"),Dt=d((e,t)=>`Question ${Ct(e,t.id)} \xB7 ${t.question}`,"numbered"),ms="question reopened",Ye=d(e=>e.data?.shared_by?.login?{by:e.data.shared_by.name||e.data.shared_by.login}:{},"byOf"),hs=d((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"),Nt=d(e=>({kind:"command",command:e,request:!0,mark:Xe[e]}),"commandLine"),ws=d((e,t)=>{if(e.kind==="item-message")return qt[e.data.intent]?{status:gs(e)}:{};const n={request:!0,command:Mt[e.kind]};return ps.includes(e.kind)?(e.data.question?t.has(e.data.question):String(e.data.text||"").trim()||e.data.files?.length)?{status:It[e.kind]}:n:e.kind==="reask-request"?n:{}},"requestOf"),Lt=d(e=>[...e.filter(t=>t.who==="claude"&&!["status","agent","command",$e,De.answer,"visual-request"].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 Tt(e,t={}){const n=new Map,s=new Map;for(const c of t.items||[])Be(n,c.topic,c),s.set(c.id,c.topic);const r=new Map,a=new Map,u=new Map,i=new Map,p=new Map,m=new Map,f=new Map;e.forEach((c,h)=>{f.set(c,h),c.data?.key&&kn.includes(c.kind)&&Be(r,c.data.key,c),c.kind==="owner-answer"&&s.has(c.data?.id)&&Be(a,s.get(c.data.id),c),c.kind===mt&&s.has(c.data?.question)&&Be(u,s.get(c.data.question),c),c.kind==="ask"&&i.set(c.id,c)});for(const c of e.filter(h=>h.kind==="ask-edited").sort((h,O)=>String(h.ts).localeCompare(String(O.ts)))){p.set(c.data?.id,c.data?.text);for(const h of c.data?.files||[])Be(m,c.data?.id,h)}return{byKey:r,answers:a,reopens:u,asks:i,edits:p,editFiles:m,questions:n,order:f}}d(Tt,"threadIndex");const ct=d((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"),ys=/; its log is \S*\/\.claude\/kickflip\/logs\/([^/\s]+\.log)\.$/,bs=d(e=>{const t=!e.log&&typeof e.text=="string"?ys.exec(e.text):null;return t?{...e,text:`${e.text.slice(0,t.index)}.`,log:t[1]}:e},"withLog");function Ss(e,t){const n=t.run?e.find(s=>s.started===t.run):e.filter(s=>ee(s)===!!t.planner&&s.started&&String(s.started)<=String(t.ts)&&(!s.done||String(t.ts)<=String(s.done))).pop();return n?{agent:Ae(n),run:ut(e,n),running:!n.done}:{}}d(Ss,"agentOfLine");function ut(e,t){return t.label!==He?t.started:e.filter(n=>H(n)&&n.label!==He&&n.started&&String(n.started)<String(t.started)).pop()?.started??t.started}d(ut,"sectionOf");function vs(e,{lines:t=[],events:n=[],owner:s={},working:r={},fromAsk:a=null,sorted:u=!1,links:i=[],agents:p={},moved:m=[],cards:f=new Map,index:c=Tt(n,s)}){const h=e.startsWith("ask:")?[e.slice(4)]:[a,...m].filter(Boolean),O=[...new Set([e,...h.map(l=>`ask:${l}`)])],A=new Map((c.questions.get(`item:${e}`)||[]).map(l=>[l.id,l])),S=c.answers.get(`item:${e}`)||[],P=new Set([...A.values()].map(l=>l.round_of).filter(Boolean)),D=lt(p,!0).filter(H).map(l=>String(l.started||"")).filter(Boolean),U=new Set(S.filter(l=>Rt(A.get(l.data.id),l.ts)||P.has(l.data.id)||D.some(B=>B>String(l.ts))).map(l=>l.id)),N=lt(p,!0),j=[...N.flatMap(l=>[...l.started?[{ts:l.started,who:"claude",kind:"agent",text:`${Ae(l)} started`,agent:Ae(l),run:ut(N,l),finished:!1,eta:l.done?null:l.eta,...l.log?{log:l.log}:{},...ee(l)?{planner:!0}:{}}]:[],...l.done?[{ts:l.done,who:"claude",kind:"agent",text:`${Ae(l)} finished`,agent:Ae(l),run:ut(N,l),finished:!0,eta:null,...ee(l)?{planner:!0}:{}}]:[],...l.calls.length?[{ts:l.done||l.calls.at(-1).at,who:"claude",kind:"calls",text:Ae(l),calls:l.calls.map(B=>B.text),verified:l.verified}]:[]]),...t.map(bs).map(l=>l.card?hs(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}:{},...Ss(N,l)}:{},...l.kind===fe||l.kind===$e?{kind:l.kind}:{},...l.kind===Qe?{kind:Qe,line:l.line,...gt(l),...l.detail?{detail:l.detail}:{}}:{},...l.command?Nt(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}:{}})),...ct(c,O.map(l=>c.byKey.get(l))).map(l=>({ts:l.ts,who:l.data.who==="claude"?"claude":"love",via:"site",kind:l.kind,text:ks(l,A),files:l.data.files||[],eventId:l.id,reply:l.reply,replies:l.replies,...ws(l,A),...Ye(l)})),...S.map(l=>{const B=[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:`${Dt(A,A.get(l.data.id))}
|
|
3
|
+
\u2192 ${B}`,files:l.data.files||[],eventId:l.id,reply:l.reply,replies:l.replies,status:fs,...Ye(l)}}),...(c.reopens.get(`item:${e}`)||[]).map(l=>({ts:l.ts,who:"love",kind:l.kind,request:!0,mark:ms,text:`Question ${Ct(A,l.data.question)} was reopened`,...Ye(l)})),...ct(c,[...new Set(h)].map(l=>c.asks.has(l)?[c.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,replies:l.replies,...m.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",...Nt("implement"),text:Xe.implement}]:[],...i.map(B=>({ts:l.ts,who:"claude",kind:"ask-link",text:B.folded?`Moved to ${B.ref}: ${B.title}`:`${B.became?"Became":"Added to"} ${B.ref}`,link:`#selectedCard=${encodeURIComponent(B.key)}`,html:null,via:null}))])].sort((l,B)=>String(l.ts).localeCompare(String(B.ts))),w=Lt(j),{edits:b,editFiles:C}=c;for(const l of j){if(!l.eventId)continue;(l.kind==="ask"||l.kind==="item-message")&&typeof b.get(l.eventId)=="string"&&Object.assign(l,{text:b.get(l.eventId),edited:!0,files:[...l.files,...C.get(l.eventId)||[]]});const B=l.kind==="ask"&&(i.length>0||!!l.moved||u&&l.eventId===a);l.state=l.reply?"replied":B||U.has(l.eventId)||w>String(l.ts)?"answered":R(r,l.eventId)?"working":"waiting",l.canDelete=l.kind==="item-message"&&(l.state==="working"||l.state==="waiting"),l.canEdit=l.canDelete}return xs(j)}d(vs,"threadOf");const Pt=["waiting","working","answered","replied"];function xs(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(`
|
|
4
4
|
|
|
5
|
-
`),files:s.flatMap(r=>r.files),state:s.map(r=>r.state).sort((r,a)=>Pt.indexOf(r)-Pt.indexOf(a))[0],reply:void 0,answers:s.map(r=>({eventId:r.eventId,state:r.state,reply:r.reply,replies:r.replies,text:r.text}))}]})}d(xs,"foldSubmits");const Ut=d(e=>(e.answers||[e]).filter(t=>t.eventId).map(t=>[t.eventId,t.state]),"stateEntries");function Os(e,t,n,s,r,a){const
|
|
6
|
-
`)[0]||"Ask";ye.push({key:M.key,ref:M.ref,text:B,at:String(y.ts)})}}for(const o of i){const g=S.filter(E=>E.file===o.slug);if(o.finished=On(g),o.finished)for(const E of g)E.lane==="done"&&(E.fold=yn(o.slug))}for(const o of S)o.wave=null;const Se=new Set((t.topics||[]).map(o=>o.id)),ve=e.files.flatMap(({slug:o,itemFile:g})=>(g.waves||[]).map(E=>({...E,keys:$s(E).map($=>`${o}/${$}`)}))),xe=d(()=>us([...t.topics||[],...S.filter(o=>!Se.has(`item:${o.key}`)).map(o=>({id:`item:${o.key}`,card:o.key,title:`${o.ref}: ${o.title}`})),...ve.filter(o=>!Se.has(`wave:${o.slug}`)).map(o=>({id:`wave:${o.slug}`,title:o.title,summary:"Wave triage."})),...(e.fromWaves?i:[]).filter(o=>!Se.has(`file:${o.slug}`)).map(o=>({id:`file:${o.slug}`,title:o.title,summary:"Wave triage."}))],H,new Set(S.filter(pe).map(o=>`item:${o.key}`))),"topicsOf"),Pe=new Map(xe().map(o=>[o.id,o]));for(const o of S){const g=Pe.get(`item:${o.key}`);if(!g){o.questions=null,o.prototypes=null;continue}const E=g.questions.filter(M=>X.get(M)?.state!=="resolved"),$=g.questions.find(M=>X.get(M)?.state==="open")||E[0]||g.questions[0],y=g.questions.map(M=>X.get(M)).filter(M=>M?.state==="open");o.questions={topic:g.id,state:g.state,open:g.open,answered:g.answered,replied:g.replied,resolved:g.resolved,first:$,askedAt:y.map(M=>M.asked_at).filter(Boolean).sort(),fromAsks:y.length>0&&y.every(M=>M.ask)},o.prototypes=cs(g.questions.map(M=>X.get(M)))}const te=new Map(S.map(o=>[o.key,o])),Ue=new Map(ve.map(o=>[`wave:${o.slug}`,o.keys]));for(const o of S){const g=o.kind==="work"&&o.lane!=="done",E=new Set(o.deps.flatMap(y=>Ue.get(y)||[String(y).includes("/")?y:`${o.file}/${y}`]));o.blockedBy=g?[...E].map(y=>te.get(y)).filter(y=>y&&y.lane!=="done"&&!y.reopened).map(jt):[],o.blockedUntil=g?o.blockedUntil??null:null,o.blockReason=g?o.blockReason??null:null,o.blocked=o.blockedBy.length>0||!!o.blockedUntil,Xn(o,X,P),o.lane==="todo"&&o.kind==="work"&&!o.held&&Et(o.storedLane,o.agents)&&(o.lane="progress"),o.unpicked=o.kind==="work"&&o.lane==="todo"&&!!Kn(H,o.key),o.kind==="work"&&(o.phase=zs(o)),o.unpicked&&!o.action&&(o.next=Gn),o.buildable=Wn(o);const $=o.kind==="work"&&o.lane==="todo";if(o.unsorted=$&&(!!o.fromAsk||o.unfiled)&&!o.filedHeld&&o.phase==="new",o.kind==="work"){const y=o.askMsg;delete o.askMsg,o.unsorted&&y&&(o.isNew=!0,o.ask={eventId:y.eventId,state:y.state,askedAt:y.ts,edited:!!y.edited,canEdit:!0,text:y.text,files:y.files,from:y.from||null})}o.ready=$&&o.phase==="ready"&&!o.blocked&&!o.recheckAt&&!o.action,o.staging=$&&o.phase==="staging",o.kind==="work"&&(o.hostHeld=!!e.hostBusy&&o.ready&&!!o.bumpedAt&&!o.held)}_s(S,te,K);const je=new Map((t.items||[]).map(o=>[o.id,o.topic])),Y=d(o=>o.kind==="owner-answer"?je.has(o.data?.id)?/^item:(.+)$/.exec(je.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"),z=d(o=>{const g=Y(o);return I.get(g)||g},"cardOf"),Me=d(o=>[_.get(o),he.get(o)].filter(Boolean).sort().pop()||"","closedAt"),Ie=d(o=>{const g=z(o);return g===null?!o.reply:!te.has(g)||!(Me(g)<String(R(c,o.id)))?!1:oe.has(o.id)?oe.get(o.id)==="working":!o.reply&&!I.has(Y(o))&&!(String(me.get(g)||"")>String(o.ts))},"unanswered"),re=s.filter(o=>R(c,o.id)&&Ie(o)).map(o=>({id:o.id,kind:o.kind,key:z(o),data:o.data,since:R(c,o.id)}));for(const o of S)o.working=re.some(g=>g.key===o.key),o.kind==="work"&&o.lane==="progress"&&!o.builder&&!o.next&&o.waitsOn!=="checking"&&!o.working&&(o.next=$t),o.sortAt=Rs[o.lane](o)||(o.lane==="review"?null:o.updatedAt)||null;for(const o of S){if(o.kind!=="work")continue;const g=(Te.get(`item:${o.key}`)||[]).filter($=>$.state==="answered").map($=>$.answers.at(-1).ts).sort().pop(),E=re.filter($=>$.key===o.key).map($=>String($.since)).sort()[0];o.orchestrator=!o.stalled&&(["progress","todo"].includes(o.lane)||o.waitsOn==="checking"||o.lane==="review"&&(o.stale||o.waitFacts.unblockedAt||o.waitFacts.replyAt))?so(o,o.waitFacts,{answeredAt:g,workingSince:E}):null,delete o.waitFacts}Gs(S,s,c,I),Qs(S,e.screening||{}),S.sort(gn);const Ze=Os(e,i,ve,S,H,s),A=xe(),We=nn({on:e.autoReview,incoming:e.incoming,records:e.reviewRecords,events:s,now:l,refs:new Map(S.filter(o=>o.kind==="work").map(o=>[o.key,o.ref]))}),ae=[...Is(S,A,H),...sn(We)],N=new Map(ae.filter(o=>o.questions).map((o,g)=>[o.topic||`item:${o.key}`,g])),Fe=d(o=>N.get(o.id)??N.size,"rankOf");A.sort((o,g)=>Fe(o)-Fe(g));const qe=new Set(ae.filter(o=>o.questions&&o.progress).map(o=>`item:${o.key}`));for(const o of A)qe.has(o.id)&&(o.progress=!0);const Oe=S.flatMap(o=>o.answered&&me.get(o.key)?[{key:o.key,id:o.id,text:o.title,at:me.get(o.key)}]:[]).sort((o,g)=>g.at.localeCompare(o.at));return ye.sort((o,g)=>g.at.localeCompare(o.at)),As({kind:U,main:we,remote:F&&{host:F.host,name:`${F.host}/${F.path}`},itemFiles:i,waveBands:Ze,items:S,moved:Object.fromEntries(I),answers:Oe,merges:ye,unfiled:be,owner:H,ownerTopics:A,reviews:We,needs:ae,working:re.length,workingOn:re,checks:Ms(e,l)},e)}d(Ao,"buildBoard");function As(e,{listen:t,prompts:n}={}){const s=[...Cs(n),...e.needs];return{...e,needs:s,needsSummary:Ds(s),checks:{...e.checks,watcher:{listening:"running",asleep:"asleep",waking:"waking"}[t]||"stopped"}}}d(As,"withHub");const jt=d(e=>({key:e.key,ref:e.ref}),"linkOf"),Wt=d((e,t)=>String(t).includes("/")?String(t):`${e}/${t}`,"linkKey"),Bs=d((e,t)=>e.ref.localeCompare(t.ref,void 0,{numeric:!0}),"byRef");function _s(e,t,n){const s=new Map,r=new Map,a=new Map,c=d((u,m,f)=>u.set(m,[...u.get(m)||[],f]),"add");for(const[u,m]of n){m.parent&&c(s,m.parent,u);for(const f of m.related)f!==u&&(c(r,u,f),c(r,f,u))}for(const u of e)for(const m of u.blockedBy||[])c(a,m.key,u.key);const l=d((u=[])=>[...new Set(u)].map(m=>t.get(m)).filter(Boolean).map(jt).sort(Bs),"cards");for(const u of e){const m=n.get(u.key)?.parent;u.parent=m?{key:m,ref:t.get(m)?.ref||m.slice(m.indexOf("/")+1)}:null,u.children=l(s.get(u.key)),u.related=l(r.get(u.key)),u.waitsOnIt=l(a.get(u.key))}}d(_s,"linkCards");const Rs={todo:d(e=>e.created,"todo"),progress:d(e=>e.progressSince,"progress"),review:d(e=>e.reviewSince,"review"),done:d(e=>e.closedAt,"done")},se=d((e,t)=>`${e} ${t}${e===1?"":"s"}`,"plural");function Ms({checked:e,watcher:t,replyAt:n,threads:s={},working:r={},files:a=[]},c){const l=Number(e?.every)||30,m=[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(h=>Object.values(h||{})).map(h=>h?.done||h?.started))].filter(p=>typeof p=="string"&&!Number.isNaN(Date.parse(p))).reduce((p,h)=>p&&Date.parse(p)>Date.parse(h)?p:h,null),f=!!e?.idle;return{at:e?.at||null,every:f?null:l,idle:f,late:!f&&!!e?.at&&c-Date.parse(e.at)>2*l*6e4,claudeAt:m}}d(Ms,"checksOf");function Is(e,t,n){const s=new Set(e.map(l=>l.questions?.topic).filter(Boolean)),r=d(l=>l.find(u=>n.find(m=>m.id===u)?.state==="open")||l[0],"firstOpen"),a=t.filter(l=>l.state!=="resolved"&&l.open>0&&!s.has(l.id)).map(l=>{const u=`questions.html#${r(l.questions)}`,m=/^(?:wave|file):(.+)$/.exec(l.id)?.[1]||null;return{kind:"Question",title:l.title,text:`${l.title} \xB7 ${se(l.open,"question")}`,url:u,topic:l.id,band:m,questions:{open:l.open,url:u},ready:null,since:n.filter(f=>f.topic===l.id&&f.state==="open").map(f=>f.asked_at).filter(Boolean).sort()[0]||null}}),c=e.map(l=>{const u=l.questions&&l.questions.state!=="resolved"&&l.questions.open>0?{open:l.questions.open,url:`questions.html#${l.questions.first}`}:null,m=l.waitsOn==="ready"?l.pr.url:null,f=!u&&!m&&l.action?{label:l.action.label,url:l.action.url}:null;if(!u&&!m&&!f)return null;const p=`${l.ref}: ${l.title}`,h=[p,u&&se(u.open,"question"),m&&"Ready on GitHub",f?.label].filter(Boolean).join(" \xB7 ");return{kind:u?"Question":m?"Tap Ready":"Action needed",title:p,text:h,url:`#selectedCard=${encodeURIComponent(l.key)}`,key:l.key,questions:u,ready:m,action:f,since:l.created||null,progress:l.lane==="progress"}}).filter(Boolean);return[...a,...c].sort(qs)}d(Is,"needsOf");const qs=d((e,t)=>!!t.progress-!!e.progress||(e.since&&t.since?e.since.localeCompare(t.since):!!t.since-!!e.since),"oldestFirst");function Cs(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}})}d(Cs,"promptLines");function Ds(e){const t=e.filter(a=>a.prompt).length,n=e.reduce((a,c)=>a+(c.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 ")}d(Ds,"needsSummary");function Bo(e){const t=d((...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))}}d(Bo,"needsAnswer");const Ns=d(e=>e.buildable&&e.phase==="ready"&&!e.held&&!e.blocked&&!e.recheckAt,"pullableCard"),Ls=10;function Ts(e,{cap:t=Ls,pulled:n=[],hold:s=[],taken:r=0}={}){const a=e.filter(w=>!pe(w)),c=d((w,b)=>w.started&&!w.done&&Q(w)&&!(w.kind===ge&&!w.writes)&&!w.pause?.paused&&!(w.waits&&b.some(i=>i.name===w.waits&&!i.done)),"counts"),l=a.filter(w=>!w.stalled).reduce((w,b)=>w+(b.agents||[]).filter((i,S,oe)=>c(i,oe)).length,0),u=a.filter(w=>Ns(w)&&!w.working).map(w=>w.key),m=a.filter(w=>n.includes(w.key)&&(u.includes(w.key)||w.buildable&&w.working)).map(w=>w.key),f=a.flatMap(w=>(w.agents||[]).filter(b=>b.kind===ge&&!b.started&&!b.done).map(b=>({key:w.key,name:b.name,writes:b.writes}))),p=Math.max(0,t-l-m.length-r),h=f.filter(w=>w.writes),v=[...f.filter(w=>!w.writes),...h.slice(0,p)].map(({key:w,name:b})=>({key:w,name:b})),_=Math.max(0,p-h.length),x=new Set(a.filter(w=>w.bumpedAt).map(w=>w.key)),P=u.filter(w=>!m.includes(w)&&!s.includes(w)),C=P.filter(w=>x.has(w)),L=[...C,...P.filter(w=>!x.has(w)).slice(0,Math.max(0,_-C.length))],T=C.slice(_),W=Ps(a,c).slice(0,T.length).map((w,b)=>({key:w,by:T[b]}));return{idle:!l&&!a.some(w=>w.waitsOn==="checking"),pullable:u,waiting:m,pulls:L,helpers:v,bumpOuts:W,pastCap:T,free:p}}d(Ts,"pullOf");function Ps(e,t){const n=d(s=>{const r=s.agents||[],a=r.filter(u=>t(u,r)),[c]=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&&c.kind!==ge&&c.pid&&c.label!==He&&!c.pause&&!r.some(u=>u.kind===ge&&u.started&&!u.done)?String(c.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)}d(Ps,"bumpOutsOf");function _o(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(c=>c.state==="OPEN"&&c.mergeable==="CONFLICTING"&&c.repo===s.repo&&c.headRefOid),a=r&&`${r.headRefOid}@${n}`;return r&&R(t,s.key)!==a?[{key:s.key,pr:r.number,branch:r.headRefName,conflict:a}]:[]})}d(_o,"rebasesDue");const Us=3;function Ro(e,{cap:t=Us}={}){const n=e.filter(l=>!pe(l)),s=n.filter(l=>(l.agents||[]).some(u=>Z(u)&&u.started&&!u.done)).length,r=n.filter(l=>l.buildable&&!["ready","staging"].includes(l.phase)&&!l.unsorted&&!l.held&&!l.working&&!l.grilling&&!l.prototyping&&!l.questions&&!(l.agents||[]).some(u=>Z(u)&&u.kind!==fe)),a=[...r.filter(l=>!l.blocked),...r.filter(l=>l.blocked)],c=a.filter(l=>l.bumpedAt);return[...c,...a.filter(l=>!l.bumpedAt).slice(0,Math.max(0,t-s-c.length))].map(l=>l.key)}d(Ro,"arrivalsOf");function Mo(e){return e.filter(t=>t.kind==="work"&&t.lane==="todo"&&t.roundAt&&["grilling","prototyping"].includes(t.phase)&&!(t.agents||[]).some(n=>Z(n)&&n.kind!==fe&&n.started&&(String(n.started)>=String(t.roundAt)||!n.done))).map(t=>({key:t.key,kind:t.phase==="grilling"?"grill":"prototype"}))}d(Mo,"roundsDue");const js=d(e=>`${fe}-${e.id}`,"visualAgent");function Io(e,t){const n=[];for(const s of e){const r=s.agents||[];if(r.some(l=>l.kind===fe&&l.started&&!l.done))continue;const a=new Set(r.map(l=>l.name)),c=t.find(l=>l.kind==="visual-request"&&l.data?.key===s.key&&!a.has(js(l)));c&&n.push({key:s.key,request:c})}return n}d(Io,"visualsDue");const Ws=d((e,t)=>e.kind==="ask"?t.get(`ask:${e.id}`)??null:e.kind==="card-added"?e.data?.key??null:null,"intakeKeyOf");function Fs(e,t,n){const s=e.get(n);return s?t.kind==="ask"?!!s.isNew:s.unfiled:!1}d(Fs,"unsorted");const Ks={ask:d((e,t)=>`Sort the new task: item notes, then item set ${e} --phase grilling or prototyping (ready, with --size, for a Quick build); merge it into a card with item relate-ask ${t.id} <card> --summary; or answer it with reply ${e}`,"ask"),"card-added":d(()=>"Link and file the card an agent added: item notes, then item set --relate and --phase","card-added")};function Gs(e,t,n,s){const r=new Map(e.map(c=>[c.key,c])),a=new Set;for(const c of t){const l=Ws(c,s);!l||a.has(l)||!Fs(r,c,l)||(a.add(l),r.get(l).orchestrator={kind:"intake",since:String(c.ts),picked:R(n,c.id)||null,next:Ks[c.kind](l,c)})}}d(Gs,"intakeWaits");function Qs(e,t){for(const n of e){const s=R(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`})}}d(Qs,"screenWaits");function Hs(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
|
-
`)}d(Hs,"todoOf");function
|
|
5
|
+
`),files:s.flatMap(r=>r.files),state:s.map(r=>r.state).sort((r,a)=>Pt.indexOf(r)-Pt.indexOf(a))[0],reply:void 0,answers:s.map(r=>({eventId:r.eventId,state:r.state,reply:r.reply,replies:r.replies,text:r.text}))}]})}d(xs,"foldSubmits");const Ut=d(e=>(e.answers||[e]).filter(t=>t.eventId).map(t=>[t.eventId,t.state]),"stateEntries");function Os(e,t,n,s,r,a){const u=d((c,h)=>{const O=a.filter(S=>S.kind==="wave-triage"&&S.data?.[c]===h).pop(),A=r.filter(S=>S.topic===`${c}:${h}`&&(S.state==="open"||S.state==="answered")).length;return{running:!!(O&&!O.reply),questions:A}},"triageOf"),i=new Map(s.map(c=>[c.key,c])),p=d(c=>c.filter(h=>h.lane==="done"&&!St(h)).length,"merged"),m=n.map(c=>({l:c,cards:c.keys.map(h=>i.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 m)for(const O of h)O.wave={slug:c.slug,title:c.title};const f=m.filter(c=>c.open).map(({l:c,cards:h})=>({kind:"wave",slug:c.slug,title:c.title,goal:c.goal,keys:h.map(O=>O.key),next:Es(h),merged:p(h),total:h.length,triage:u("wave",c.slug)}));return e.fromWaves?[...f,...t.filter(c=>!c.finished).map(c=>({w:c,cards:s.filter(h=>h.file===c.slug&&h.kind!=="ask")})).filter(({cards:c})=>c.length).map(({w:c,cards:h})=>({kind:"file",slug:c.slug,title:c.title,goal:null,keys:h.map(O=>O.key),next:null,merged:p(h),total:h.length,triage:u("file",c.slug)}))]:f}d(Os,"waveBandsOf");function Es(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}}d(Es,"nextOf");const $s=d(e=>e.items||e.phases.flat(),"waveItems");function Ao(e){const{owner:t={},topicItems:n={},events:s=[],threads:r={},prs:a={},working:u={}}=e,i=e.now??Date.now(),p=Tt(s,t),m=new Set(s.filter(o=>o.kind==="ask-withdrawn").map(o=>o.data?.id)),f=new Map(s.filter(o=>o.kind==="ask"&&!m.has(o.id)).map(o=>[o.id,o])),c=d((o,g)=>new Map(s.filter(x=>x.kind===o).sort((x,$)=>String(x.ts).localeCompare(String($.ts))).map(x=>[x.data?.[g],x.ts])),"latest"),h=c("card-unbump","key"),O=new Map([...c("card-bump","key")].filter(([o,g])=>!(String(h.get(o)||"")>String(g)))),A=c("card-done","key"),S=c("card-merge","key"),P=e.autoMerge===!0,D=e.tickets,U=new Map(s.filter(o=>o.kind==="card-hold").sort((o,g)=>String(o.ts).localeCompare(String(g.ts))).map(o=>[o.data?.key,o])),N=new Map,j=new Map(e.files.flatMap(({slug:o,itemFile:g})=>g.items.map(x=>[`${o}/${x.id}`,{ref:nt(g,x,D),title:x.title}]))),w=e.askLinks||{},b=new Map;for(const[o,g]of Object.entries(w))f.has(o)&&b.set(g,[...b.get(g)||[],o]);const C=[],l=[],B=new Map,me=new Map,he=new Map,G=new Map,K=e.github?null:tn(e.remote);for(const{slug:o,itemFile:g,agents:x={}}of e.files){const $=e.github||g.repo||null;C.push({slug:o,title:g.title||o,status:g.status||null,internal:!!g.internal,finished:!1,reviewers:(g.reviewers||[]).map(y=>y.name)}),g.items.forEach((y,M)=>{const _=`${o}/${y.id}`,Ke=(b.get(_)||[]).filter(v=>v!==y.from_ask),le=[y.from_ask,...Ke].filter(v=>f.has(v)),Ce=new Set(le.map(v=>`ask:${v}`)),Ee=[...R(r,_)||[],...le.flatMap(v=>R(r,`ask:${v}`)||[])];y.from_ask&&N.set(y.from_ask,[...N.get(y.from_ask)||[],{key:_,ref:nt(g,y,D),became:!0}]);const se=vs(_,{lines:Ee,events:s,owner:t,working:u,fromAsk:y.from_ask||null,sorted:!!y.phase,moved:Ke,cards:j,index:p});for(const v of se)for(const[ie,Ge]of Ut(v))B.set(ie,Ge);me.set(_,Lt(se)),l.push(Dn({slug:o,itemFile:g,it:y,order:M,repo:$,remote:K,base:e.branch||"main",prs:rn(y,$).map(v=>({...v,gh:R(a,ln(v))||null})),...(v=>({agents:v,builder:ts(v,R(r,_),i,e.moved?.[it(R(x,y.id))]??null,R(e.alive,_)??null,!!e.commandRunning?.[it(R(x,y.id))]),reachedPhase:es(v,R(r,_)),eta:v.map(ie=>ie.eta).filter(Boolean).sort().pop()||null}))(lt(R(x,y.id))),worktree:it(R(x,y.id))??y.worktree?.path??null,thread:Ee,events:[...ct(p,[_,...Ce].map(v=>p.byKey.get(v))),...le.map(v=>f.get(v))],working:se.some(v=>Ut(v).some(([,ie])=>ie==="working")),said:se.filter(v=>v.who==="love"&&(v.kind==="item-message"||v.kind==="owner-answer")),askedAt:(p.questions.get(`item:${_}`)||[]).filter(v=>v.asked_at).map(v=>v.asked_at),topicItems:n,prSlack:e.prSlack||{},boardReviewers:e.reviewers||[],now:i,bumpedAt:O.get(_),holdAsk:U.get(_),closedAt:A.get(_),mergeAt:S.get(_),autoMerge:P,skipDraft:e.skipDraft===!0,tickets:D}));const Z=l[l.length-1];f.has(y.from_ask)&&(Z.created=f.get(y.from_ask).ts),Z.askMsg=se.find(v=>v.kind==="ask"&&v.eventId===y.from_ask)||null,G.set(_,{parent:y.parent?Wt(o,y.parent):null,related:(y.related||[]).map(v=>Wt(o,v))}),Z.lane==="done"&&he.set(_,Z.closedAt||String(Z.updatedAt||""))})}const te=new Map(l.map(o=>[o.key,o])),I=new Map,Q=e.try||{},ze=e.autoBuild===!0,W=["web","app"].includes(Q.kind)?Q.kind:e.files.some(({itemFile:o})=>o.items.some(g=>g.install?.url))?"app":"web";for(const o of l){if(o.kind!=="work"||o.lane==="done")continue;const g=R(Q.cards,o.key)||{},x=g.head||o.pr?.headRefOid,$=W==="web"?g.sha:o.install?.sha,y=o.builder?.kind===ue.install?"running":g.state||(W==="app"&&o.install?"ready":null);o.try={kind:W,state:y,stale:!!($&&x&&!String(x).toLowerCase().startsWith(String($).toLowerCase())),url:W==="web"?null:o.install?.url||null,port:W==="web"&&y==="ready"&&g.https||null,sha:$||null,changes:g.ahead>0,script:g.script===!0,autoBuild:ze,hostBusy:!!e.hostBusy}}const L=Q.main,we=L?.script&&L.sha?{slug:L.slug,branch:L.branch||"main",sha:L.sha.slice(0,7),at:L.at,kind:W,state:L.state||null,url:W==="web"?null:e.main?.install?.url||null,port:W==="web"&&(L.state==="ready"?L.https:L.serving?.https)||null,serving:W==="web"&&L.serving?{sha:L.serving.sha.slice(0,7),at:L.serving.at}:null,pull:Q.pull?{running:Q.pull.running===!0,error:Q.pull.error||null}:null,script:pn(L.target)}:null,ye=[],be=[],Le=_e(s.filter(o=>o.kind==="owner-answer"),o=>o.data?.id),F=_e(s.filter(o=>o.kind==="prototype-request"),o=>o.data?.question),Re=_e(s.filter(o=>o.kind==="reask-request"),o=>o.data?.question),Je=_e(s.filter(o=>o.kind===mt),o=>o.data?.question),V=(t.items||[]).map(o=>ls(o,Le.get(o.id)||[],F.get(o.id)||[],Re.get(o.id)||[],Je.get(o.id)||[])),Y=new Map(V.map(o=>[o.id,o])),Te=_e(V,o=>o.topic);for(const o of f.values()){const g=`ask:${o.id}`,x=R(r,g)||[],$=N.get(o.id)?.[0].key||R(w,o.id);if(!$){x.some(_=>_.line==="ask-moved")||be.push(o.id);continue}I.set(g,$);const y=x.filter(_=>_.line===fn).at(-1),M=te.get($);if(y&&M){const _=y.title||String(p.edits.get(o.id)??o.data?.text??"").trim().split(`
|
|
6
|
+
`)[0]||"Ask";ye.push({key:M.key,ref:M.ref,text:_,at:String(y.ts)})}}for(const o of C){const g=l.filter(x=>x.file===o.slug);if(o.finished=On(g),o.finished)for(const x of g)x.lane==="done"&&(x.fold=yn(o.slug))}for(const o of l)o.wave=null;const Se=new Set((t.topics||[]).map(o=>o.id)),ve=e.files.flatMap(({slug:o,itemFile:g})=>(g.waves||[]).map(x=>({...x,keys:$s(x).map($=>`${o}/${$}`)}))),xe=d(()=>us([...t.topics||[],...l.filter(o=>!Se.has(`item:${o.key}`)).map(o=>({id:`item:${o.key}`,card:o.key,title:`${o.ref}: ${o.title}`})),...ve.filter(o=>!Se.has(`wave:${o.slug}`)).map(o=>({id:`wave:${o.slug}`,title:o.title,summary:"Wave triage."})),...(e.fromWaves?C:[]).filter(o=>!Se.has(`file:${o.slug}`)).map(o=>({id:`file:${o.slug}`,title:o.title,summary:"Wave triage."}))],V,new Set(l.filter(pe).map(o=>`item:${o.key}`))),"topicsOf"),Pe=new Map(xe().map(o=>[o.id,o]));for(const o of l){const g=Pe.get(`item:${o.key}`);if(!g){o.questions=null,o.prototypes=null;continue}const x=g.questions.filter(M=>Y.get(M)?.state!=="resolved"),$=g.questions.find(M=>Y.get(M)?.state==="open")||x[0]||g.questions[0],y=g.questions.map(M=>Y.get(M)).filter(M=>M?.state==="open");o.questions={topic:g.id,state:g.state,open:g.open,answered:g.answered,replied:g.replied,resolved:g.resolved,first:$,askedAt:y.map(M=>M.asked_at).filter(Boolean).sort(),fromAsks:y.length>0&&y.every(M=>M.ask)},o.prototypes=cs(g.questions.map(M=>Y.get(M)))}const ne=new Map(l.map(o=>[o.key,o])),Ue=new Map(ve.map(o=>[`wave:${o.slug}`,o.keys]));for(const o of l){const g=o.kind==="work"&&o.lane!=="done",x=new Set(o.deps.flatMap(y=>Ue.get(y)||[String(y).includes("/")?y:`${o.file}/${y}`]));o.blockedBy=g?[...x].map(y=>ne.get(y)).filter(y=>y&&y.lane!=="done"&&!y.reopened).map(jt):[],o.blockedUntil=g?o.blockedUntil??null:null,o.blockReason=g?o.blockReason??null:null,o.blocked=o.blockedBy.length>0||!!o.blockedUntil,Xn(o,Y,P),o.lane==="todo"&&o.kind==="work"&&!o.held&&Et(o.storedLane,o.agents)&&(o.lane="progress"),o.unpicked=o.kind==="work"&&o.lane==="todo"&&!!Kn(V,o.key),o.kind==="work"&&(o.phase=zs(o)),o.unpicked&&!o.action&&(o.next=Gn),o.buildable=Wn(o);const $=o.kind==="work"&&o.lane==="todo";if(o.unsorted=$&&(!!o.fromAsk||o.unfiled)&&!o.filedHeld&&o.phase==="new",o.kind==="work"){const y=o.askMsg;delete o.askMsg,o.unsorted&&y&&(o.isNew=!0,o.ask={eventId:y.eventId,state:y.state,askedAt:y.ts,edited:!!y.edited,canEdit:!0,text:y.text,files:y.files,from:y.from||null})}o.ready=$&&o.phase==="ready"&&!o.blocked&&!o.recheckAt&&!o.action,o.staging=$&&o.phase==="staging",o.kind==="work"&&(o.hostHeld=!!e.hostBusy&&o.ready&&!!o.bumpedAt&&!o.held)}_s(l,ne,G);const je=new Map((t.items||[]).map(o=>[o.id,o.topic])),z=d(o=>o.kind==="owner-answer"?je.has(o.data?.id)?/^item:(.+)$/.exec(je.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"),J=d(o=>{const g=z(o);return I.get(g)||g},"cardOf"),Me=d(o=>[A.get(o),he.get(o)].filter(Boolean).sort().pop()||"","closedAt"),Ie=d(o=>{const g=J(o);return g===null?!o.reply:!ne.has(g)||!(Me(g)<String(R(u,o.id)))?!1:B.has(o.id)?B.get(o.id)==="working":!o.reply&&!I.has(z(o))&&!(String(me.get(g)||"")>String(o.ts))},"unanswered"),re=s.filter(o=>R(u,o.id)&&Ie(o)).map(o=>({id:o.id,kind:o.kind,key:J(o),data:o.data,since:R(u,o.id)}));for(const o of l)o.working=re.some(g=>g.key===o.key),o.kind==="work"&&o.lane==="progress"&&!o.builder&&!o.next&&o.waitsOn!=="checking"&&!o.working&&(o.next=$t),o.sortAt=Rs[o.lane](o)||(o.lane==="review"?null:o.updatedAt)||null;for(const o of l){if(o.kind!=="work")continue;const g=(Te.get(`item:${o.key}`)||[]).filter($=>$.state==="answered").map($=>$.answers.at(-1).ts).sort().pop(),x=re.filter($=>$.key===o.key).map($=>String($.since)).sort()[0];o.orchestrator=!o.stalled&&(["progress","todo"].includes(o.lane)||o.waitsOn==="checking"||o.lane==="review"&&(o.stale||o.waitFacts.unblockedAt||o.waitFacts.replyAt))?so(o,o.waitFacts,{answeredAt:g,workingSince:x}):null,delete o.waitFacts}Gs(l,s,u,I),Qs(l,e.screening||{}),l.sort(gn);const Ze=Os(e,C,ve,l,V,s),E=xe(),We=nn({on:e.autoReview,incoming:e.incoming,records:e.reviewRecords,events:s,now:i,refs:new Map(l.filter(o=>o.kind==="work").map(o=>[o.key,o.ref]))}),ae=[...Is(l,E,V),...sn(We)],T=new Map(ae.filter(o=>o.questions).map((o,g)=>[o.topic||`item:${o.key}`,g])),Fe=d(o=>T.get(o.id)??T.size,"rankOf");E.sort((o,g)=>Fe(o)-Fe(g));const qe=new Set(ae.filter(o=>o.questions&&o.progress).map(o=>`item:${o.key}`));for(const o of E)qe.has(o.id)&&(o.progress=!0);const Oe=l.flatMap(o=>o.answered&&me.get(o.key)?[{key:o.key,id:o.id,text:o.title,at:me.get(o.key)}]:[]).sort((o,g)=>g.at.localeCompare(o.at));return ye.sort((o,g)=>g.at.localeCompare(o.at)),As({kind:W,main:we,remote:K&&{host:K.host,name:`${K.host}/${K.path}`},itemFiles:C,waveBands:Ze,items:l,moved:Object.fromEntries(I),answers:Oe,merges:ye,unfiled:be,owner:V,ownerTopics:E,reviews:We,needs:ae,working:re.length,workingOn:re,checks:Ms(e,i)},e)}d(Ao,"buildBoard");function As(e,{listen:t,prompts:n}={}){const s=[...Cs(n),...e.needs];return{...e,needs:s,needsSummary:Ds(s),checks:{...e.checks,watcher:{listening:"running",asleep:"asleep",waking:"waking"}[t]||"stopped"}}}d(As,"withHub");const jt=d(e=>({key:e.key,ref:e.ref}),"linkOf"),Wt=d((e,t)=>String(t).includes("/")?String(t):`${e}/${t}`,"linkKey"),Bs=d((e,t)=>e.ref.localeCompare(t.ref,void 0,{numeric:!0}),"byRef");function _s(e,t,n){const s=new Map,r=new Map,a=new Map,u=d((p,m,f)=>p.set(m,[...p.get(m)||[],f]),"add");for(const[p,m]of n){m.parent&&u(s,m.parent,p);for(const f of m.related)f!==p&&(u(r,p,f),u(r,f,p))}for(const p of e)for(const m of p.blockedBy||[])u(a,m.key,p.key);const i=d((p=[])=>[...new Set(p)].map(m=>t.get(m)).filter(Boolean).map(jt).sort(Bs),"cards");for(const p of e){const m=n.get(p.key)?.parent;p.parent=m?{key:m,ref:t.get(m)?.ref||m.slice(m.indexOf("/")+1)}:null,p.children=i(s.get(p.key)),p.related=i(r.get(p.key)),p.waitsOnIt=i(a.get(p.key))}}d(_s,"linkCards");const Rs={todo:d(e=>e.created,"todo"),progress:d(e=>e.progressSince,"progress"),review:d(e=>e.reviewSince,"review"),done:d(e=>e.closedAt,"done")},oe=d((e,t)=>`${e} ${t}${e===1?"":"s"}`,"plural");function Ms({checked:e,watcher:t,replyAt:n,threads:s={},working:r={},files:a=[]},u){const i=Number(e?.every)||30,m=[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),f=!!e?.idle;return{at:e?.at||null,every:f?null:i,idle:f,late:!f&&!!e?.at&&u-Date.parse(e.at)>2*i*6e4,claudeAt:m}}d(Ms,"checksOf");function Is(e,t,n){const s=new Set(e.map(i=>i.questions?.topic).filter(Boolean)),r=d(i=>i.find(p=>n.find(m=>m.id===p)?.state==="open")||i[0],"firstOpen"),a=t.filter(i=>i.state!=="resolved"&&i.open>0&&!s.has(i.id)).map(i=>{const p=`questions.html#${r(i.questions)}`,m=/^(?:wave|file):(.+)$/.exec(i.id)?.[1]||null;return{kind:"Question",title:i.title,text:`${i.title} \xB7 ${oe(i.open,"question")}`,url:p,topic:i.id,band:m,questions:{open:i.open,url:p},ready:null,since:n.filter(f=>f.topic===i.id&&f.state==="open").map(f=>f.asked_at).filter(Boolean).sort()[0]||null}}),u=e.map(i=>{const p=i.questions&&i.questions.state!=="resolved"&&i.questions.open>0?{open:i.questions.open,url:`questions.html#${i.questions.first}`}:null,m=i.waitsOn==="ready"?i.pr.url:null,f=!p&&!m&&i.action?{label:i.action.label,url:i.action.url}:null;if(!p&&!m&&!f)return null;const c=`${i.ref}: ${i.title}`,h=[c,p&&oe(p.open,"question"),m&&"Ready on GitHub",f?.label].filter(Boolean).join(" \xB7 ");return{kind:p?"Question":m?"Tap Ready":"Action needed",title:c,text:h,url:`#selectedCard=${encodeURIComponent(i.key)}`,key:i.key,questions:p,ready:m,action:f,since:i.created||null,progress:i.lane==="progress"}}).filter(Boolean);return[...a,...u].sort(qs)}d(Is,"needsOf");const qs=d((e,t)=>!!t.progress-!!e.progress||(e.since&&t.since?e.since.localeCompare(t.since):!!t.since-!!e.since),"oldestFirst");function Cs(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}})}d(Cs,"promptLines");function Ds(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&&oe(t,"prompt"),n&&oe(n,"question"),s&&`${oe(s,"draft")} waiting on Ready`,r&&`${oe(r,"review")} held`].filter(Boolean).join(" \xB7 ")}d(Ds,"needsSummary");function Bo(e){const t=d((...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))}}d(Bo,"needsAnswer");const Ns=d(e=>e.buildable&&e.phase==="ready"&&!e.held&&!e.blocked&&!e.recheckAt,"pullableCard"),Ls=10;function Ts(e,{cap:t=Ls,pulled:n=[],hold:s=[],taken:r=0}={}){const a=e.filter(w=>!pe(w)),u=d((w,b)=>w.started&&!w.done&&H(w)&&!(w.kind===ge&&!w.writes)&&!w.pause?.paused&&!(w.waits&&b.some(C=>C.name===w.waits&&!C.done)),"counts"),i=a.filter(w=>!w.stalled).reduce((w,b)=>w+(b.agents||[]).filter((C,l,B)=>u(C,B)).length,0),p=a.filter(w=>Ns(w)&&!w.working).map(w=>w.key),m=a.filter(w=>n.includes(w.key)&&(p.includes(w.key)||w.buildable&&w.working)).map(w=>w.key),f=a.flatMap(w=>(w.agents||[]).filter(b=>b.kind===ge&&!b.started&&!b.done).map(b=>({key:w.key,name:b.name,writes:b.writes}))),c=Math.max(0,t-i-m.length-r),h=f.filter(w=>w.writes),O=[...f.filter(w=>!w.writes),...h.slice(0,c)].map(({key:w,name:b})=>({key:w,name:b})),A=Math.max(0,c-h.length),S=new Set(a.filter(w=>w.bumpedAt).map(w=>w.key)),P=p.filter(w=>!m.includes(w)&&!s.includes(w)),D=P.filter(w=>S.has(w)),U=[...D,...P.filter(w=>!S.has(w)).slice(0,Math.max(0,A-D.length))],N=D.slice(A),j=Ps(a,u).slice(0,N.length).map((w,b)=>({key:w,by:N[b]}));return{idle:!i&&!a.some(w=>w.waitsOn==="checking"),pullable:p,waiting:m,pulls:U,helpers:O,bumpOuts:j,pastCap:N,free:c}}d(Ts,"pullOf");function Ps(e,t){const n=d(s=>{const r=s.agents||[],a=r.filter(p=>t(p,r)),[u]=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&&u.kind!==ge&&u.pid&&u.label!==He&&!u.pause&&!r.some(p=>p.kind===ge&&p.started&&!p.done)?String(u.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)}d(Ps,"bumpOutsOf");function _o(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&&R(t,s.key)!==a?[{key:s.key,pr:r.number,branch:r.headRefName,conflict:a}]:[]})}d(_o,"rebasesDue");const Us=3,Ro=10;function Mo(e,{cap:t=Us}={}){const n=e.filter(i=>!pe(i)),s=n.filter(i=>(i.agents||[]).some(p=>ee(p)&&p.started&&!p.done)).length,r=n.filter(i=>i.buildable&&!["discussing","ready","staging"].includes(i.phase)&&!i.unsorted&&!i.held&&!i.working&&!i.grilling&&!i.prototyping&&!i.questions&&!(i.agents||[]).some(p=>ee(p)&&p.kind!==fe)),a=[...r.filter(i=>!i.blocked),...r.filter(i=>i.blocked)],u=a.filter(i=>i.bumpedAt);return[...u,...a.filter(i=>!i.bumpedAt).slice(0,Math.max(0,t-s-u.length))].map(i=>i.key)}d(Mo,"arrivalsOf");function Io(e){return e.filter(t=>t.kind==="work"&&t.lane==="todo"&&t.roundAt&&["grilling","prototyping"].includes(t.phase)&&!(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.phase==="grilling"?"grill":"prototype"}))}d(Io,"roundsDue");const js=d(e=>`${fe}-${e.id}`,"visualAgent");function qo(e,t){const n=[];for(const s of e){const r=s.agents||[];if(r.some(i=>i.kind===fe&&i.started&&!i.done))continue;const a=new Set(r.map(i=>i.name)),u=t.find(i=>i.kind==="visual-request"&&i.data?.key===s.key&&!a.has(js(i)));u&&n.push({key:s.key,request:u})}return n}d(qo,"visualsDue");const Ws=d((e,t)=>e.kind==="ask"?t.get(`ask:${e.id}`)??null:e.kind==="card-added"?e.data?.key??null:null,"intakeKeyOf");function Fs(e,t,n){const s=e.get(n);return s?t.kind==="ask"?!!s.isNew:s.unfiled:!1}d(Fs,"unsorted");const Ks={ask:d((e,t)=>`Sort the new task: link a ticket it names with item set ${e} --ticket <id>; discuss it with reply ${e}, which reads it discussing; item notes, then item set ${e} --phase ready, with --size, for an Implement ask, or --phase grilling or prototyping only when the ask asks for one; or merge it into a card with item relate-ask ${t.id} <card> --summary`,"ask"),"card-added":d(()=>"Link and file the card an agent added: item notes, then item set --relate and --phase","card-added")};function Gs(e,t,n,s){const r=new Map(e.map(u=>[u.key,u])),a=new Set;for(const u of t){const i=Ws(u,s);!i||a.has(i)||!Fs(r,u,i)||(a.add(i),r.get(i).orchestrator={kind:"intake",since:String(u.ts),picked:R(n,u.id)||null,next:Ks[u.kind](i,u)})}}d(Gs,"intakeWaits");function Qs(e,t){for(const n of e){const s=R(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`})}}d(Qs,"screenWaits");function Hs(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
|
+
`)}d(Hs,"todoOf");function Co(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
|
-
${Hs(e)}`}}d(qo,"triageOf");const Vs=100,Xs=["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"],Co=d(e=>Object.fromEntries(Xs.filter(t=>t in e).map(t=>[t,e[t]])),"slimDone");function Do(e,t=Vs){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"))}d(Do,"archivedOf");const Ys={ready:"draft",ci:"draft",reviewers:"reviewers",merge:"merge"};function zs(e){return e.lane==="todo"?e.unpicked?"prototyping":kt.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!==ue.install&&e.builder.phase||e.reachedPhase||"reading":e.lane==="review"?e.stale?"stale":e.waitsOn==="owner"&&e.pushed?"merge":Ys[e.waitsOn]??null:e.storedLane==="done"&&kt.done.includes(e.storedPhase)?e.storedPhase:e.prs.some(t=>t.status===V.merged)?"merged":"closed"}d(zs,"phaseOf");function No(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:Js(t,n),from:{lane:t.storedLane,phase:t.storedPhase}}]})}d(No,"movesOf");function Js(e,t){return t==="todo"?e.held?"it is held":e.blocked?"it is blocked":"nothing builds it":e.reopened&&e.storedLane==="done"?"a message or question reopened it":e.answered&&t==="review"?"answered, it waits for your Done":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"}d(Js,"moveWhy");const Zs=d(e=>e.startsWith("ask:")?`ask-${e.slice(4)}`:`item-${e}`,"pushTag");function Lo(e){const t=d(a=>e.owner.filter(c=>c.topic===a&&c.state==="open").map(({id:c,question:l})=>({id:c,question:l})),"open"),n=[q.ready,q.merge],s=e.items.filter(a=>a.action).map(a=>{const c=rt.has(a.action.kind)?t(a.questions.topic):[];return{tag:Zs(a.key),kind:a.action.kind,title:`${a.ref}: ${a.action.label}`,body:c[0]?.question||a.title,url:c.length?a.action.url:`#selectedCard=${encodeURIComponent(a.key)}`,now:!n.includes(a.action.kind),questions:c}}),r=e.needs.filter(a=>a.topic).map(a=>{const c=t(a.topic);return{tag:`topic-${a.topic}`,kind:q.questions,title:a.text,body:c[0]?.question||a.title,url:a.url,now:!0,questions:c}});return[...s,...r,...on(e.reviews)]}d(Lo,"ownerActions");function To(e,{pulled:t=[]}={}){return!e.items.filter(r=>!pe(r)).some(r=>r.waitsOn==="checking"||r.grilling||r.prototyping||(r.agents||[]).some(a=>!a.done))&&!e.working&&!Ts(e.items,{pulled:t}).waiting.length}d(To,"strictlyIdle");const eo="item stall it with the reason when it can't go on",Ft="restart it once from its worktree with a thread line; if the restarted run goes stale too, item stall it with the reason",to='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',no='Its block ended: re-read its notes and answered questions against what landed; answer a stale one again with question settle <id> --note, or send the card back with item set --phase grilling when the change is too big; then item set --phase ready --text "<what you re-checked>"';function so(e,t,{answeredAt:n,workingSince:s}){const r=d(f=>e.agents.some(p=>p.kind===f&&p.started&&!p.done),"running"),a=[e.progressSince,...e.agents.map(f=>f.done)].filter(Boolean).map(String).sort().pop()||null,c=t.unasked&&e.storedPhase==={grill:"grilling",prototype:"prototyping"}[t.unasked.kind]&&!e.agents.some(f=>f.kind===t.unasked.kind&&f.started&&(!f.done||String(f.started)>t.unasked.at))?t.unasked:null,l=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:to}:n?{kind:"answer",since:n,next:"Act on the owner's answer"}:t.replyAt?{kind:"reply",since:t.replyAt,next:"Act on the owner's reply"}:rt.has(e.action?.kind)?null:e.builder?.stopped?{kind:"stale",since:e.builder.silentSince,next:`${e.builder.stopped}: ${Ft}, 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 ${Ft}`}:e.lane==="review"&&e.stale?{kind:"stale",since:new Date(Date.parse(e.inReviewSince)+wt).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 ${eo}`}: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"}:c?{kind:"unasked",since:c.at,next:`Its ${c.kind} planner ended without its question: read its log, then post the question, start the planner again, or move the card's phase`}:e.recheckAt&&e.lane==="todo"&&e.phase==="ready"&&!e.blocked?{kind:"recheck",since:e.recheckAt,next:no}: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(!l?.since)return null;const u=String(l.since),m=[t.writes.find(f=>f>u),s].filter(Boolean).sort()[0]||null;return{kind:l.kind,since:u,picked:m,next:l.next,...l.daily?{daily:!0}:{}}}d(so,"orchestratorWaitOf");const Po=d((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 Uo(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)]}d(Uo,"orchestratorWaits");const jo=30*6e4,oo=15*6e4,ro=60*6e4,ao=1440*6e4;function Wo(e,t,n){const s=n.toISOString(),r=[],a=[];return{waits:t.map(l=>{const u=e.find(p=>p.key===l.key&&p.kind===l.kind&&p.since===l.since);if(!u)return l.picked||r.push(l.key),{...l,bumped:s};const m=l.picked?[l.picked,u.bumped].filter(Boolean).sort().pop():u.bumped||l.since,f=n-Date.parse(m)>=(l.picked?l.daily?ao:ro:oo);return f&&a.push({key:l.key,ref:l.ref,kind:l.kind,since:l.since,next:l.next}),{...l,bumped:f?s:u.bumped||l.since,...u.pushed?{pushed:!0}:{}}}),added:r,again:a}}d(Wo,"waitsStep");function Fo(e,t){const n=d(s=>{const r=Math.floor((t-Date.parse(s))/6e4),a=Math.floor(r/60),c=r%60;return a?`${a}h${c?` ${c}m`:""}`:`${c}m`},"age");return`still waiting on you: ${e.map(s=>`${s.ref} ${s.kind} for ${n(s.since)}: ${s.next}`).join("; ")}`}d(Fo,"waitsAgainLine");const lo=15;function Ko(e,{idle:t,now:n}){const s=n.toISOString(),r=e.last||s;return t?e.since?e.pushed||n-Date.parse(e.since)<lo*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}}d(Ko,"idleStep");function Go(e,t){const n=d(u=>String(u.closedAt||"")>t,"after"),s=e.filter(u=>u.kind==="work"&&u.lane==="done"&&!St(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(m=>m.lane==="done")&&u.some(n)).map(u=>u[0].wave.title),c=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: ${c}`,...a.map(u=>`${u} finished`)].filter(Boolean).join(" \xB7 ")||"Nothing closed since the last idle notification."}d(Go,"idleSummary");export{q as ACTION_KIND,It as ACTION_LABELS,fs as ANSWERS_SUBMITTED,De as BUILDER_ASK,ue as BUILDER_KIND,hn as BUILDER_QUIET_AFTER,mn as BUILDER_STALE_AFTER,ht as BUILD_REFUSED,ce as BUTTON,Xe as COMMANDS,ao as DAILY_REWAKE_AFTER,Xs as DONE_FIELDS,Vs as DONE_KEPT,ge as HELPER,lo as IDLE_MINUTES,qt as INTENT_LABELS,kn as ITEM_EVENTS,He as LANDER,So as LANES,Ls as MAX_IMPLEMENTERS,Us as MAX_PLANNERS,$e as MOVE,$t as NO_NEXT,jo as ORCHESTRATOR_PUSH_AFTER,Eo as PAUSE_CLEARS,ns as PAUSE_HARD_STOP,kt as PHASES,ro as PICKED_REWAKE_AFTER,zn as PLANNER_WORD,Gn as PROTOTYPE_AGAIN,xo as PROTOTYPE_WAIT,V as PR_STATE,mt as QUESTION_REOPEN,oo as REWAKE_AFTER,fe as VISUAL,Do as archivedOf,Ro as arrivalsOf,Ao as buildBoard,Q as builds,$o as confirmedDrops,yn as fileFold,st as handedBackAt,Ko as idleStep,Go as idleSummary,Tn as mergeBlockers,as as modelName,No as movesOf,Bo as needsAnswer,Kn as noTakePicked,Jn as orchestratorDid,Uo as orchestratorWaits,vo as owesNext,Lo as ownerActions,ls as ownerQuestion,rs as pauseWork,Fn as picksNoTake,Z as plans,Oo as prototypeWaits,_t as providerOf,Ts as pullOf,Zs as pushTag,_o as rebasesDue,nt as refOf,ke as roundOf,is as roundSettledBy,Mo as roundsDue,Co as slimDone,Ut as stateEntries,At as stoppedWords,To as strictlyIdle,Tt as threadIndex,vs as threadOf,Hs as todoOf,qo as triageOf,Po as triageWaitOf,St as unlanded,js as visualAgent,Io as visualsDue,Fo as waitsAgainLine,Wo as waitsStep,$s as waveItems,As as withHub};
|
|
10
|
+
${Hs(e)}`}}d(Co,"triageOf");const Vs=100,Xs=["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"],Do=d(e=>Object.fromEntries(Xs.filter(t=>t in e).map(t=>[t,e[t]])),"slimDone");function No(e,t=Vs){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"))}d(No,"archivedOf");const Ys={ready:"draft",ci:"draft",reviewers:"reviewers",merge:"merge"};function zs(e){return e.lane==="todo"?e.unpicked?"prototyping":kt.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!==ue.install&&e.builder.phase||e.reachedPhase||"reading":e.lane==="review"?e.stale?"stale":e.waitsOn==="owner"&&e.pushed?"merge":Ys[e.waitsOn]??null:e.storedLane==="done"&&kt.done.includes(e.storedPhase)?e.storedPhase:e.prs.some(t=>t.status===X.merged)?"merged":"closed"}d(zs,"phaseOf");function Lo(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:Js(t,n),from:{lane:t.storedLane,phase:t.storedPhase}}]})}d(Lo,"movesOf");function Js(e,t){return t==="todo"?e.held?"it is held":e.blocked?"it is blocked":"nothing builds it":e.reopened&&e.storedLane==="done"?"a message or question reopened it":e.answered&&t==="review"?"answered, it waits for your Done":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"}d(Js,"moveWhy");const Zs=d(e=>e.startsWith("ask:")?`ask-${e.slice(4)}`:`item-${e}`,"pushTag");function To(e){const t=d(a=>e.owner.filter(u=>u.topic===a&&u.state==="open").map(({id:u,question:i})=>({id:u,question:i})),"open"),n=[q.ready,q.merge],s=e.items.filter(a=>a.action).map(a=>{const u=rt.has(a.action.kind)?t(a.questions.topic):[];return{tag:Zs(a.key),kind:a.action.kind,title:`${a.ref}: ${a.action.label}`,body:u[0]?.question||a.title,url:u.length?a.action.url:`#selectedCard=${encodeURIComponent(a.key)}`,now:!n.includes(a.action.kind),questions:u}}),r=e.needs.filter(a=>a.topic).map(a=>{const u=t(a.topic);return{tag:`topic-${a.topic}`,kind:q.questions,title:a.text,body:u[0]?.question||a.title,url:a.url,now:!0,questions:u}});return[...s,...r,...on(e.reviews)]}d(To,"ownerActions");function Po(e,{pulled:t=[]}={}){return!e.items.filter(r=>!pe(r)).some(r=>r.waitsOn==="checking"||r.grilling||r.prototyping||(r.agents||[]).some(a=>!a.done))&&!e.working&&!Ts(e.items,{pulled:t}).waiting.length}d(Po,"strictlyIdle");const eo="item stall it with the reason when it can't go on",Ft="restart it once from its worktree with a thread line; if the restarted run goes stale too, item stall it with the reason",to='Judge the staged card against the whole board: for a discussed card first fold what was agreed into a Build notes section with item notes; then 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',no='Its block ended: re-read its notes and answered questions against what landed; answer a stale one again with question settle <id> --note, or send the card back with item set --phase grilling when the change is too big; then item set --phase ready --text "<what you re-checked>"';function so(e,t,{answeredAt:n,workingSince:s}){const r=d(f=>e.agents.some(c=>c.kind===f&&c.started&&!c.done),"running"),a=[e.progressSince,...e.agents.map(f=>f.done)].filter(Boolean).map(String).sort().pop()||null,u=t.unasked&&e.storedPhase==={grill:"grilling",prototype:"prototyping"}[t.unasked.kind]&&!e.agents.some(f=>f.kind===t.unasked.kind&&f.started&&(!f.done||String(f.started)>t.unasked.at))?t.unasked:null,i=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:to}:n?{kind:"answer",since:n,next:"Act on the owner's answer"}:t.replyAt?{kind:"reply",since:t.replyAt,next:"Act on the owner's reply"}:rt.has(e.action?.kind)?null:e.builder?.stopped?{kind:"stale",since:e.builder.silentSince,next:`${e.builder.stopped}: ${Ft}, 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 ${Ft}`}:e.lane==="review"&&e.stale?{kind:"stale",since:new Date(Date.parse(e.inReviewSince)+wt).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 ${eo}`}: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"}:u?{kind:"unasked",since:u.at,next:`Its ${u.kind} planner ended without its question: read its log, then post the question, start the planner again, or move the card's phase`}:e.recheckAt&&e.lane==="todo"&&e.phase==="ready"&&!e.blocked?{kind:"recheck",since:e.recheckAt,next:no}: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(!i?.since)return null;const p=String(i.since),m=[t.writes.find(f=>f>p),s].filter(Boolean).sort()[0]||null;return{kind:i.kind,since:p,picked:m,next:i.next,...i.daily?{daily:!0}:{}}}d(so,"orchestratorWaitOf");const Uo=d((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 jo(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)]}d(jo,"orchestratorWaits");const Wo=30*6e4,oo=15*6e4,ro=60*6e4,ao=1440*6e4;function Fo(e,t,n){const s=n.toISOString(),r=[],a=[];return{waits:t.map(i=>{const p=e.find(c=>c.key===i.key&&c.kind===i.kind&&c.since===i.since);if(!p)return i.picked||r.push(i.key),{...i,bumped:s};const m=i.picked?[i.picked,p.bumped].filter(Boolean).sort().pop():p.bumped||i.since,f=n-Date.parse(m)>=(i.picked?i.daily?ao:ro:oo);return f&&a.push({key:i.key,ref:i.ref,kind:i.kind,since:i.since,next:i.next}),{...i,bumped:f?s:p.bumped||i.since,...p.pushed?{pushed:!0}:{}}}),added:r,again:a}}d(Fo,"waitsStep");function Ko(e,t){const n=d(s=>{const r=Math.floor((t-Date.parse(s))/6e4),a=Math.floor(r/60),u=r%60;return a?`${a}h${u?` ${u}m`:""}`:`${u}m`},"age");return`still waiting on you: ${e.map(s=>`${s.ref} ${s.kind} for ${n(s.since)}: ${s.next}`).join("; ")}`}d(Ko,"waitsAgainLine");const lo=15;function Go(e,{idle:t,now:n}){const s=n.toISOString(),r=e.last||s;return t?e.since?e.pushed||n-Date.parse(e.since)<lo*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}}d(Go,"idleStep");function Qo(e,t){const n=d(p=>String(p.closedAt||"")>t,"after"),s=e.filter(p=>p.kind==="work"&&p.lane==="done"&&!St(p)&&n(p)),r=new Map;for(const p of e)p.wave&&r.set(p.wave.slug,[...r.get(p.wave.slug)||[],p]);const a=[...r.values()].filter(p=>p.every(m=>m.lane==="done")&&p.some(n)).map(p=>p[0].wave.title),u=s.slice(0,3).map(p=>p.ref).join(", ")+(s.length>3?` and ${s.length-3} more`:"");return[s.length&&`${s.length} ${s.length===1?"card":"cards"} landed: ${u}`,...a.map(p=>`${p} finished`)].filter(Boolean).join(" \xB7 ")||"Nothing closed since the last idle notification."}d(Qo,"idleSummary");export{q as ACTION_KIND,It as ACTION_LABELS,fs as ANSWERS_SUBMITTED,De as BUILDER_ASK,ue as BUILDER_KIND,hn as BUILDER_QUIET_AFTER,mn as BUILDER_STALE_AFTER,ht as BUILD_REFUSED,ce as BUTTON,Xe as COMMANDS,ao as DAILY_REWAKE_AFTER,Xs as DONE_FIELDS,Vs as DONE_KEPT,ge as HELPER,lo as IDLE_MINUTES,qt as INTENT_LABELS,kn as ITEM_EVENTS,He as LANDER,So as LANES,Ls as MAX_IMPLEMENTERS,Us as MAX_PLANNERS,Ro as MAX_SERVES,$e as MOVE,$t as NO_NEXT,Wo as ORCHESTRATOR_PUSH_AFTER,Eo as PAUSE_CLEARS,ns as PAUSE_HARD_STOP,kt as PHASES,ro as PICKED_REWAKE_AFTER,zn as PLANNER_WORD,Gn as PROTOTYPE_AGAIN,xo as PROTOTYPE_WAIT,X as PR_STATE,mt as QUESTION_REOPEN,oo as REWAKE_AFTER,fe as VISUAL,No as archivedOf,Mo as arrivalsOf,Ao as buildBoard,H as builds,$o as confirmedDrops,yn as fileFold,st as handedBackAt,Go as idleStep,Qo as idleSummary,Tn as mergeBlockers,as as modelName,Lo as movesOf,Bo as needsAnswer,Kn as noTakePicked,Jn as orchestratorDid,jo as orchestratorWaits,vo as owesNext,To as ownerActions,ls as ownerQuestion,rs as pauseWork,Fn as picksNoTake,ee as plans,Oo as prototypeWaits,_t as providerOf,Ts as pullOf,Zs as pushTag,_o as rebasesDue,nt as refOf,ke as roundOf,is as roundSettledBy,Io as roundsDue,Do as slimDone,Ut as stateEntries,At as stoppedWords,Po as strictlyIdle,Tt as threadIndex,vs as threadOf,Hs as todoOf,Co as triageOf,Uo as triageWaitOf,St as unlanded,js as visualAgent,qo as visualsDue,Ko as waitsAgainLine,Fo as waitsStep,$s as waveItems,As as withHub};
|
|
@@ -417,28 +417,51 @@ BoardView.part((B) => {
|
|
|
417
417
|
}
|
|
418
418
|
const off = ev.target.closest('a[data-tip]');
|
|
419
419
|
if (off) { ev.preventDefault(); toast(off.dataset.tip); return; }
|
|
420
|
-
const
|
|
420
|
+
const kill = ev.target.closest('button[data-try-kill]');
|
|
421
|
+
const button = kill || ev.target.closest('button[data-try]');
|
|
421
422
|
if (!button) return;
|
|
422
423
|
ev.preventDefault();
|
|
423
424
|
if (button.dataset.tip) { toast(button.dataset.tip); return; }
|
|
424
|
-
const key = button.dataset.try;
|
|
425
|
+
const key = kill ? kill.dataset.tryKill : button.dataset.try;
|
|
425
426
|
const it = state.board.items.find((i) => i.key === key);
|
|
426
|
-
if (!it
|
|
427
|
-
// Serve
|
|
427
|
+
if (!it) return;
|
|
428
|
+
// A web card's Serve asks the busy host's question in place of its usual confirm, and the hub's own refusal for a
|
|
429
|
+
// busy host or the board's serve cap is a dialog too
|
|
430
|
+
// (docs/adr/2026-11-12-0112-a-card-serve-stays-up-while-its-worktree-does.md).
|
|
431
|
+
const busyAsk = 'The host is busy, start this serve anyway?';
|
|
432
|
+
const web = it.try?.kind === 'web';
|
|
433
|
+
let anyway = !kill && web && it.try?.hostBusy === true;
|
|
434
|
+
const asked = kill ? `Kill ${it.ref}’s serve? Its server stops until you tap Serve.` : anyway ? busyAsk : `${tryWords(it.try?.kind).go} ${it.ref}? The hub runs its build script.`;
|
|
435
|
+
if (!(await B.confirm(asked)) || !B.alive()) return;
|
|
436
|
+
// Serve / Build and Kill spin until a board shows the card's build changed: running, done or stopped
|
|
428
437
|
// (docs/adr/2026-11-05-0200-a-tap-spins-until-the-board-shows-its-change.md).
|
|
429
438
|
const before = JSON.stringify(it.try);
|
|
430
439
|
const id = `${key}:try`;
|
|
431
440
|
BoardUI.pending.start(id, (board) => JSON.stringify(board.items.find((i) => i.key === key)?.try) !== before, B.stillWaiting);
|
|
432
441
|
B.render();
|
|
442
|
+
const send = async () => {
|
|
443
|
+
const r = await fetch(B.site.url(kill ? 'api/try/kill' : 'api/try'), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ key, ...(anyway ? { anyway } : {}) }) });
|
|
444
|
+
return { ok: r.ok, out: await r.json() };
|
|
445
|
+
};
|
|
433
446
|
try {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
447
|
+
let { ok, out } = await send();
|
|
448
|
+
if (!ok && out.ask === 'busy' && !anyway) {
|
|
449
|
+
if (!(await B.confirm(busyAsk)) || !B.alive()) { BoardUI.pending.drop(id); B.render(); return; }
|
|
450
|
+
anyway = true;
|
|
451
|
+
({ ok, out } = await send());
|
|
452
|
+
}
|
|
453
|
+
if (!ok && out.ask === 'cap') {
|
|
454
|
+
BoardUI.pending.drop(id);
|
|
455
|
+
B.render();
|
|
456
|
+
await B.notice(out.error);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (!ok) throw new Error(out.error || (kill ? 'The hub refused the Kill.' : 'The hub refused the build.'));
|
|
437
460
|
BoardUI.pending.answered(id);
|
|
438
461
|
} catch (error) {
|
|
439
462
|
BoardUI.pending.drop(id);
|
|
440
463
|
B.render();
|
|
441
|
-
toast(error.message || 'Could not start. Is the host awake?');
|
|
464
|
+
toast(error.message || (kill ? 'Could not kill it. Is the host awake?' : 'Could not start. Is the host awake?'));
|
|
442
465
|
}
|
|
443
466
|
B.reload();
|
|
444
467
|
});
|
package/board/public/app.js
CHANGED
|
@@ -360,6 +360,26 @@ window.BoardView = (function () {
|
|
|
360
360
|
$('#downLanding').hidden = false;
|
|
361
361
|
B.root.classList.add('down');
|
|
362
362
|
}
|
|
363
|
+
// A board that loaded but threw while drawing shows the landing in its place with the error; the link stays online, and
|
|
364
|
+
// the next board that draws clean clears it (land).
|
|
365
|
+
function drawFailed(err) {
|
|
366
|
+
console.error(err);
|
|
367
|
+
state.down = { state: 'draw', error: err };
|
|
368
|
+
$('#downLanding').innerHTML = BoardUI.drawLanding(err);
|
|
369
|
+
$('#downLanding').hidden = false;
|
|
370
|
+
B.root.classList.add('down');
|
|
371
|
+
}
|
|
372
|
+
B.on(B.root, 'click', (ev) => {
|
|
373
|
+
const what = state.down?.error && ev.target.closest?.('button[data-draw]')?.dataset.draw;
|
|
374
|
+
if (what === 'reload') location.reload();
|
|
375
|
+
else if (what === 'copy') {
|
|
376
|
+
navigator.clipboard?.writeText(state.down.error.stack || String(state.down.error)).then(() => B.toast('Copied'), () => B.toast('Couldn’t copy'));
|
|
377
|
+
} else if (what === 'more') {
|
|
378
|
+
const pre = $('#downLanding .down-landing-stack');
|
|
379
|
+
pre.hidden = !pre.hidden;
|
|
380
|
+
ev.target.textContent = pre.hidden ? 'Show details' : 'Hide details';
|
|
381
|
+
}
|
|
382
|
+
});
|
|
363
383
|
B.every(5000, () => { if (state.down?.retries && !document.hidden) loadBoard(); });
|
|
364
384
|
|
|
365
385
|
// A board from the server, a load's or an events POST's, or a patch from the board on screen (ui-patch.js). One built
|
|
@@ -380,7 +400,7 @@ window.BoardView = (function () {
|
|
|
380
400
|
B.setView('fresh');
|
|
381
401
|
if (!hashRead || (first && focusKey)) { readHash(); hashRead = true; }
|
|
382
402
|
B.linkUp();
|
|
383
|
-
render(load);
|
|
403
|
+
try { render(load); } catch (e) { drawFailed(e); return true; }
|
|
384
404
|
scrollToFocus();
|
|
385
405
|
returnToPlace();
|
|
386
406
|
B.site.syncBadge();
|
|
@@ -439,7 +459,7 @@ window.BoardView = (function () {
|
|
|
439
459
|
if (document.hidden) return;
|
|
440
460
|
if (signalMissed) { signalMissed = false; signalled(); } else loadBoard();
|
|
441
461
|
});
|
|
442
|
-
B.readCopy().then(paintCopy).then(() => { if (B.alive()) loadBoard(); });
|
|
462
|
+
B.readCopy().then(paintCopy).catch((e) => console.error(e)).then(() => { if (B.alive()) loadBoard(); });
|
|
443
463
|
B.signal?.(signalled);
|
|
444
464
|
B.every(30000, () => { if (!document.hidden) { loadBoard(); } });
|
|
445
465
|
}
|
package/board/public/dialogs.js
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
const team = esc(draft.team || '');
|
|
59
59
|
const note = draft.project === '__new'
|
|
60
60
|
? (isNew ? `<p class="muted small">Start creates the project in the ${team} team on Linear.</p>` : `<p class="muted small">Creates the project in the ${team} team on Linear.</p>`)
|
|
61
|
-
: `<p class="muted small">A task gets a ${team} issue in ${esc(project?.name || draft.project || '')}, in the ${esc(linear.workspace || workspace())} workspace from Hub’s key, when
|
|
61
|
+
: `<p class="muted small">A task gets a ${team} issue in ${esc(project?.name || draft.project || '')}, in the ${esc(linear.workspace || workspace())} workspace from Hub’s key, when it goes ready to build. Its badge opens it in Linear.</p>`;
|
|
62
62
|
return `<div class="tk-grid"><label class="tk-field"><span>Team</span><select data-tk="team">${(linear.teams || []).map((t) => `<option value="${esc(t.key)}"${t.key === draft.team ? ' selected' : ''}>${esc(t.key)} · ${esc(t.name)}</option>`).join('')}</select></label><label class="tk-field"><span>Project</span><select data-tk="project">${(linear.projects || []).filter((p) => p.team === draft.team).map((p) => `<option value="${esc(p.id)}"${p.id === draft.project ? ' selected' : ''}>${esc(p.name)}</option>`).join('')}<option value="__new"${draft.project === '__new' ? ' selected' : ''}>New project…</option></select></label></div>${draft.project === '__new' ? `<div class="tk-new"><input data-tk="newName" value="${esc(draft.newName ?? title())}" aria-label="New Linear project name" placeholder="Project name" maxlength="80" autocomplete="off">${isNew ? '' : `<button type="button" data-tk-create${creating ? BoardUI.BUSY : ''}>Create</button>`}</div>` : ''}${createError ? `<p class="muted small" role="alert">${esc(createError)}</p>` : ''}${note}`;
|
|
63
63
|
}
|
|
64
64
|
function render() {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
let detail;
|
|
69
69
|
if (draft.source === 'github' && off) detail = why;
|
|
70
70
|
else if (draft.source === 'none') detail = '<p class="muted small">Every card is a task, numbered T-1, T-2… on this board, with no ticket. Its badge reads Task.</p>';
|
|
71
|
-
else if (draft.source === 'github') detail = `<div class="who tk-static"><span>Issues in ${esc(repo())}<span class="muted small sub">${isNew ? 'The repo chosen above' : 'The board’s repo, set above'}. A task opens an issue there${draft.account ? ` as gh account ${esc(draft.account)}` : ''} when
|
|
71
|
+
else if (draft.source === 'github') detail = `<div class="who tk-static"><span>Issues in ${esc(repo())}<span class="muted small sub">${isNew ? 'The repo chosen above' : 'The board’s repo, set above'}. A task opens an issue there${draft.account ? ` as gh account ${esc(draft.account)}` : ''} when it goes ready to build; its badge then reads #41.</span></span></div>`;
|
|
72
72
|
else detail = linearDetail();
|
|
73
73
|
root.innerHTML = `${status()}${segment}<div class="tk-detail">${noGithub && off && draft.source !== 'github' ? why : ''}${detail}</div>`;
|
|
74
74
|
if (draft.source === 'linear' && ready()) {
|
package/board/public/new-task.js
CHANGED
|
@@ -77,8 +77,8 @@ globalThis.BoardNewTask = (function () {
|
|
|
77
77
|
B.on($('#sheetClose'), 'click', closeSheet);
|
|
78
78
|
B.on(sheet, 'click', (ev) => { if (BoardUI.onBackdrop(ev, sheet)) closeSheet(); });
|
|
79
79
|
B.on(sheet, 'cancel', (ev) => { ev.preventDefault(); closeSheet(); });
|
|
80
|
-
// Discuss has the session
|
|
81
|
-
// session marks the card ready (docs/adr/2026-11-
|
|
80
|
+
// Discuss has the session reply to the task, starting its discussion; Implement sends the ask with quick: true, and
|
|
81
|
+
// the session marks the card ready (docs/adr/2026-11-12-0500-a-new-task-is-a-conversation.md).
|
|
82
82
|
B.on($('#askForm'), 'submit', async (ev) => {
|
|
83
83
|
ev.preventDefault();
|
|
84
84
|
$('#askText').blur();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// with board.mjs prototype add (docs/adr/2026-11-01-1448-the-board-serves-every-prototype-from-one-template.md). A take file,
|
|
4
4
|
// public/prototypes/<topic>/<KEY>.js, is a module:
|
|
5
5
|
// export function render(stage, kit) { stage.innerHTML = kit.hub(); return () => {…} } // the cleanup is optional
|
|
6
|
+
// A take draws any board class at any width: prototype.html undoes the board's width-gated hides in the stage (.needs-deck, .needs-pane).
|
|
6
7
|
// render runs on every switch to that take with a fresh, empty stage; it may be async. The stage contains its layout
|
|
7
8
|
// and paint, so nothing in it, position: fixed included, can cover the viewer's bar, its ✕ or the app shell.
|
|
8
9
|
// kit.sidebar(data?, {here, switching, device, head, height}) ui-sidebar.js sidebar() as an open drawer card in the
|
|
@@ -49,6 +49,12 @@
|
|
|
49
49
|
width: min(320px, 100%); height: 640px; margin: 0 auto; padding: 0 8px; border: 1px solid var(--line); border-radius: 14px; }
|
|
50
50
|
#stage .kit-sidebar.sidebar:not(.kit-head) > header.top { display: none; }
|
|
51
51
|
@media (min-width: 900px) { #stage .kit-sidebar.sidebar { width: 272px; } }
|
|
52
|
+
/* Everything a take draws shows at every width: the board's width-gated hides are undone here (style.css hides
|
|
53
|
+
.needs-deck from 900px and .needs-pane below it, .key-hint below 600px). render-check.mjs refuses a take whose box is
|
|
54
|
+
still hidden at one of its two widths, so a gate missed here fails the round instead of drawing blank. */
|
|
55
|
+
#stage .needs-deck { display: block; }
|
|
56
|
+
#stage .needs-pane { display: block; position: static; height: auto; overflow: visible; border-left: 0; padding: 0; }
|
|
57
|
+
#stage .key-hint { display: inline; }
|
|
52
58
|
#stage .kit-sheet-strip { padding: 32px 0 0; border-radius: 12px; background: rgba(0,0,0,.35); overflow: hidden; }
|
|
53
59
|
#stage .kit-sheet { max-width: 560px; margin: 0 auto; border: 1px solid var(--line); border-bottom: 0; border-radius: 16px 16px 0 0; background: var(--panel); color: var(--text); box-shadow: 0 -8px 30px rgba(0,0,0,.18); }
|
|
54
60
|
#stage .kit-sheet .sheet-head { padding: 10px 16px 0; }
|
package/board/public/style.css
CHANGED
|
@@ -285,6 +285,9 @@ main { padding-bottom: calc(110px + env(safe-area-inset-bottom)); }
|
|
|
285
285
|
border-radius: 15px 15px 0 0; padding-bottom: calc(max(12px, calc(env(safe-area-inset-bottom) - var(--app-bottom, 0px))) + var(--kb, 0px)); }
|
|
286
286
|
.sheet.keyboard-sheet.kb form { padding-bottom: calc(10px + var(--kb, 0px)); }
|
|
287
287
|
#askText { font-size: 16px; }
|
|
288
|
+
/* The thread box is as tall as the New task box: 4 rows, its 8px top padding and the 40px under the text for the
|
|
289
|
+
attach row (the borders sit inside the box). It grows past that as it does elsewhere. */
|
|
290
|
+
form[data-reply] .dz.bare > textarea { font-size: 16px; min-height: calc(4lh + 48px); }
|
|
288
291
|
}
|
|
289
292
|
@keyframes sheet-in { from { transform: translateY(100%); } }
|
|
290
293
|
@keyframes sheet-out { to { transform: translateY(100%); } }
|
|
@@ -468,12 +471,14 @@ button.attach[aria-pressed="true"] { color: var(--accent); background: var(--chi
|
|
|
468
471
|
.view-only-strip b { color: var(--accent); }
|
|
469
472
|
/* A board out of reach (docs/adr/2026-11-06-0135-a-board-out-of-reach-keeps-its-copy-or-shows-a-landing.md): it reads as before,
|
|
470
473
|
undimmed, and only the buttons that send, ui.js SENDS, are held, the labelled ones saying why; app-header.js stops their taps. */
|
|
471
|
-
.offline :is(form button[type="submit"], .actions button, button[data-confirm], button[data-deck-pick], button[data-review-answer], button[data-prompt-send], button[data-prompt-choice], button[data-quick], button[data-card-bump], button[data-bump], button[data-withdraw], button[data-grill], button[data-proto], button[data-visual], button[data-plan], button[data-main-pull], button[data-arch-run], button[data-try], button[data-reopen]) { opacity: .55; cursor: not-allowed; }
|
|
474
|
+
.offline :is(form button[type="submit"], .actions button, button[data-confirm], button[data-deck-pick], button[data-review-answer], button[data-prompt-send], button[data-prompt-choice], button[data-quick], button[data-card-bump], button[data-bump], button[data-withdraw], button[data-grill], button[data-proto], button[data-visual], button[data-plan], button[data-main-pull], button[data-arch-run], button[data-try], button[data-try-kill], button[data-reopen]) { opacity: .55; cursor: not-allowed; }
|
|
472
475
|
/* With no copy to show, the landing takes the board's place: the kickflip mark and two lines, as hub/front-door.mjs's page has them. */
|
|
473
476
|
.down-landing { min-height: 60vh; display: grid; place-content: center; justify-items: center; gap: 10px; padding: 32px 24px; text-align: center; }
|
|
474
477
|
.down-landing-mark { display: block; margin-bottom: 8px; width: 70px; height: 60px; object-fit: contain; }
|
|
475
478
|
.down-landing-line { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.3; text-wrap: balance; }
|
|
476
479
|
.down-landing-hint { margin: 0; font-size: 14px; color: var(--muted); }
|
|
480
|
+
.down-landing-stack { margin: 0; max-width: 100%; overflow: auto; text-align: left; font-size: 12px; padding: 10px; border-radius: 8px; background: color-mix(in srgb, var(--muted) 14%, transparent); }
|
|
481
|
+
.down-landing-stack[hidden] { display: none; }
|
|
477
482
|
.view-board.down main > :not(#downLanding), .view-board.down :is(#needsPane, #compose) { display: none; }
|
|
478
483
|
.view-settings.down .settings-body > :not(.down-landing) { display: none; }
|
|
479
484
|
/* INT-28: answer banner */
|
|
@@ -866,7 +871,7 @@ button[aria-busy="true"]:is([data-card-bump]) svg { fill: currentColor; }
|
|
|
866
871
|
.tb-main-text { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; }
|
|
867
872
|
.tb-main .meta { justify-content: flex-end; margin-left: auto; }
|
|
868
873
|
/* #60: Main's refresh icon right of its timestamp; a failed pull's reason shows on hover, or on a tap on touch */
|
|
869
|
-
.tb-main .main-pull { position: relative; display: inline-flex; align-items: center; vertical-align:
|
|
874
|
+
.tb-main .main-pull { position: relative; display: inline-flex; align-items: center; vertical-align: text-bottom; margin: -7px 0 -7px 6px; }
|
|
870
875
|
.tb-main .gh-refresh { display: inline-flex; align-items: center; justify-content: center; font-size: 15px; line-height: 1; padding: 2px 4px; color: var(--muted); min-width: 28px; min-height: 28px; }
|
|
871
876
|
.tb-main .gh-refresh[aria-disabled="true"] { opacity: .45; cursor: default; }
|
|
872
877
|
.tb-main .gh-refresh.failed { color: var(--color-danger); }
|