@lovek4d/kickflip 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,10 @@ moves every Linear board to no ticket source. If the source can't make a ticket,
26
26
  [--title <t>] answer goes in its thread and the card reads
27
27
  discussing in Todo, --title as its title. Refuses a
28
28
  card sorted another way or that isn't one an ask became
29
+ [--implement] for a reply that ends at "tap Implement": marks the card
30
+ ready to implement, so its Implement goes orange and it
31
+ reads Action needed until the owner taps Implement or
32
+ writes in its thread
29
33
  thread <file>/<id> --text <t> [--link <url>] one line in the item's thread; ask:<eventId> in place
30
34
  [--who claude|love|<name>] [--via github|slack] of <file>/<id> writes in that ask's thread
31
35
  A Claude thread line, or a reply to an ask or item-message, rewrites the body of the thread's
@@ -34,8 +38,13 @@ moves every Linear board to no ticket source. If the source can't make a ticket,
34
38
  visual add <file>/<id> --file <page.html> --title <t> a visual agent's page: copied to public/visuals/<id>/<slug>.html
35
39
  (a new name each time), linked by a visual line in the
36
40
  item's thread; prints the page's link
37
- item visual <file>/<id> --text <t> asks the board for a visual on the card, as the thread's
38
- image does: a visual agent draws what --text says
41
+ item brief <file>/<id> --kind grill|prototype|visual the orchestrator's brief for the card's next agent of that
42
+ --file <draft.md> kind: what the discussion settled, what is still open,
43
+ what the questions, takes or page should cover and the
44
+ files that matter. Appended to its notes as "Brief for
45
+ <kind> round N"; the board launches the agent from it, and
46
+ from nothing else. A grill or prototype brief moves the
47
+ Todo card to grilling or prototyping
39
48
  topic add <id> --title <t> [--summary <s>] a question topic in data/owner.json
40
49
  question add --file <json> one question or an array of them:
41
50
  {id, topic, question, options: [{id, label, recommended?, detail?, diagram?, practice?}],
@@ -704,4 +713,4 @@ moves every Linear board to no ticket source. If the source can't make a ticket,
704
713
  (overnight, snoozed or the weekend). --urgent: a
705
714
  production fault only; it beats those holds. While
706
715
  notifications are off every push is dropped, urgent too
707
- KICKFLIP_PUSH_CMD='["<cmd>","<arg>"]' replaces \`node ../../bin/kickflip.mjs push\`; the flags follow it`;class d extends Error{static{i(this,"UsageError")}}const re=new Set(["new-ticket","done","clear-status","clear-next","urgent","now","clear-eta","held","unheld","idle","wait","hub","clear-install","force","worktree","dispatch","writes"]);function oe(e){const a=Object.create(null),t=[];for(let s=0;s<e.length;s++){const r=e[s];if(!r.startsWith("--")){t.push(r);continue}const l=r.slice(2);if(re.has(l)){if(Object.hasOwn(a,l))throw new d(`--${l} is given twice`);a[l]=!0}else if(s+1<e.length)(a[l]||=[]).push(e[++s]);else throw new d(`--${l} needs a value`)}return{flags:a,rest:t}}i(oe,"parse");function de(e,a,t){const s=new Set(a.filter(o=>o.startsWith("...")).map(o=>o.slice(3))),r=new Set(a.map(o=>o.replace(/^\.\.\./,""))),l=Object.keys(t).filter(o=>!r.has(o));if(l.length)throw new d(`${e} does not take ${l.map(o=>"--"+o).join(", ")}`);return Object.fromEntries(Object.entries(t).map(([o,c])=>{if(c===!0||s.has(o))return[o,c];if(c.length>1)throw new d(`--${o} is given twice`);return[o,c[0]]}))}i(de,"flagsOf");const le=["text","note","notes","heading","summary","title","body","next","progress","install-label","confirmed","asked","task","reason"],he=/^(?:\/|~\/|\.{1,2}\/)\S*\.(?:html?|md|json|txt)$/;function ce(e){const a=e.trim();if(!a||/\s/.test(a))return!1;if(he.test(a))return!0;try{return g.statSync(a).isFile()}catch{return!1}}i(ce,"looksLikePath");function ue(e,a){for(const t of le){if(typeof a[t]!="string"||!ce(a[t]))continue;const s=e.includes("file")?`; for a file use --file ${a[t].trim()}`:"";throw new d(`--${t} takes text, not a file path (${a[t].trim()})${s}`)}if(typeof a.file=="string"&&a.file!=="-"&&!g.existsSync(a.file))throw new d(`--file ${a.file}: no such file`)}i(ue,"checkInputs");const m=i(e=>g.readFileSync(e==="-"?0:e,"utf8"),"readInput");function q(e){if(!e)throw new d("--file <path> is required");try{return JSON.parse(m(e))}catch(a){throw new n.BoardError(`${e}: ${a.message}`)}}i(q,"readJSONInput");function C(e,a){const t=["notes-file","plan"].find(s=>e[s]!==void 0);if(t)throw new d(`--${t} is gone: the board names a card's notes file, <id>-notes.md, and writes it itself; ${a}`)}i(C,"refuseNotesFile");function T(e){if(e.note!==void 0&&e.file!==void 0)throw new d("pass --note or --file, not both");return e.file===void 0?e.note:m(e.file)}i(T,"noteOf");async function pe(e,a,{"owner-step":t,check:s,text:r,title:l,...o}){if(t!==void 0&&(!e||o.dir))throw new d("--owner-step goes on a board card: pass --site, not --dir");if(s!==void 0&&t===void 0)throw new d("--check goes with --owner-step: it is the code check that closes the card once the step is done");t!==void 0&&await n.setItem(e,a,{ownerStep:t,check:s});const c=e&&!o.dir,p=c&&await n.handBackCalls(e,a,o);if(p)return p;if(c&&o.next===void 0&&await n.finishesCard(e,a,o)){if(r===void 0)throw new n.BoardError(`builder done finishes ${a}: pass --text "<what you checked>", or --next "<what happens next>" to stop without finishing`);return $(e,a,{text:r,ownerStep:t,title:l})}if(c&&await n.isHelper(e,a,o.agent)){if(r===void 0)throw new d('a helper hands back its result: pass --text "<what you did or found>"');return n.stopBuilder(e,a,{...o,note:r})}if(c&&r!==void 0&&await n.isPlanner(e,a,o.agent))return n.stopPlanner(e,a,{...o,note:r,requireNext:!0});if(r!==void 0)throw new d("--text goes on a builder done that finishes the card, the main build with a worktree and no --next, or on a helper's or a planner's");return n.stopBuilder(e,a,{...o,requireNext:!0})}i(pe,"builderDone");const b={pr:[["file/id"],[],async(e,[a])=>({lines:(await n.prReport(e,a)).map(t=>JSON.stringify(t))})],"pr comment":[["pr"],["body"],(e,[a],t)=>{if(t.body===void 0)throw new d('pr comment needs --body "<what Claude says>"');return n.postPR(e,a,{body:t.body})}],"pr reply":[["pr","comment-id"],["body"],(e,[a,t],s)=>{if(s.body===void 0)throw new d('pr reply needs --body "<what Claude says>"');return n.postPR(e,a,{replyTo:t,body:s.body})}],reply:[["eventId"],["text","file","title"],(e,[a],t)=>{if(t.text===void 0==(t.file===void 0))throw new d("reply takes --text or --file");const s=t.text??m(t.file);return a.includes("/")?n.answerCard(e,a,s,t.title):n.writeReply(e,a,s,t.title)}],thread:[["file/id"],["text","link","who","via"],(e,[a],t)=>n.appendMessage(e,a,t)],"visual add":[["file/id"],["file","title"],(e,[a],t)=>n.addVisual(e,a,t)],"item visual":[["file/id"],["text"],(e,[a],t)=>n.requestVisual(e,a,t)],"topic add":[["id"],["title","summary"],(e,[a],t)=>n.addTopic(e,{id:a,...t})],"question add":[[],["file"],async(e,a,t)=>{const s=q(t.file);return{...await n.addQuestions(e,s,process.env.KICKFLIP_RENDER_CHECK==="off"?void 0:se),asked:[s].flat()}}],"question resolve":[["id"],["note"],(e,[a],t)=>n.resolveQuestion(e,a,t.note)],"question settle":[["id"],["note"],(e,[a],t)=>n.settleQuestion(e,a,t.note)],"question publish":[["key"],[],async(e,[a])=>({ids:await n.publishScreened(e,a)})],"question move":[["ids"],["to","block"],(e,[a],t)=>n.moveQuestions(e,a.split(",").filter(Boolean),t.to,(t.block||"").split(",").filter(Boolean))],"prototype add":[["topic"],["title","...take","card","served","...state"],(e,[a],t)=>n.addPrototype(e,{topic:a,title:t.title,card:t.card,takes:t.take,served:t.served,states:t.state})],"prototype base":[[],[],e=>ne(e)],"prototype list":[[],[],e=>n.listPrototypes(e)],"prototype rm":[["topic"],[],(e,[a])=>n.removePrototype(e,a)],"item set":[["file/id"],["done","split-into","status","clear-status","next","clear-next","...pr","pushed","gh-since","from-ask","phase","state","held","unheld","pick-up-at","notes-file","plan","deps","blocked-until","block-reason","parent","related","relate","unrelate","install","install-sha","install-label","clear-install","title","size","model","force","ticket","new-ticket","text","check"],async(e,[a],t,s)=>(C(t,`add to its notes with item notes ${a} --heading "<heading>" --file <draft.md>`),t.done&&e&&await n.checkNotReopened(e,a),n.setItem(e,a,{done:t.done,splitInto:t["split-into"],status:t.status,clearStatus:t["clear-status"],next:t.next,clearNext:t["clear-next"],pr:t.pr,pushed:t.pushed,ghSince:t["gh-since"],fromAsk:t["from-ask"],phase:t.phase,state:t.state,held:t.held,unheld:t.unheld,pickUpAt:t["pick-up-at"],deps:t.deps,blockedUntil:t["blocked-until"],blockReason:t["block-reason"],parent:t.parent,related:t.related,relate:t.relate,unrelate:t.unrelate,install:t.install,installSha:t["install-sha"],installLabel:t["install-label"],clearInstall:t["clear-install"],title:t.title,size:t.size,model:t.model,force:t.force,ticket:t.ticket,newTicket:t["new-ticket"],text:t.text,check:t.check},s))],"item add":[["file[/id]"],["title","notes","notes-from","deps","notes-file","plan","ticket","new-ticket","pick-up-at","held","parent","split-of","size","model","phase","state","relate","from-ask","text"],(e,[a],t,s)=>{if(C(t,"seed a new card's notes with item add --notes-from <draft.md>"),t.notes!==void 0&&t["notes-from"]!==void 0)throw new d("pass --notes or --notes-from, not both");return n.addItem(e,a,{title:t.title,notes:t.notes,notesFrom:t["notes-from"],deps:t.deps,ticket:t.ticket,newTicket:t["new-ticket"],pickUpAt:t["pick-up-at"],held:t.held,parent:t.parent,splitOf:t["split-of"],size:t.size,model:t.model,phase:t.phase,state:t.state,relate:t.relate,fromAsk:t["from-ask"],text:t.text,intake:!0},s)}],"item notes":[["file/id"],["heading","file","from"],(e,[a],t)=>{if(t.heading===void 0&&t.from===void 0)throw new d(`item notes needs --heading "<the section's title>"`);if(t.file===void 0==(t.from===void 0))throw new d("item notes takes --file <draft.md> or --from <file>/<id>");return n.addNotes(e,a,t)}],"item relate-ask":[["eventId","file/id"],["summary","title"],(e,[a,t],s)=>n.relateAsk(e,a,t,s.summary,s.title)],"ask unfold":[["eventId"],[],(e,[a])=>n.unfoldAsk(e,a)],"ask move":[["eventId"],["to"],(e,[a],t)=>n.moveAsk(e,a,t.to)],"wave create":[["slug"],["title","goal","...card","note","file"],(e,[a],t)=>n.createWave(e,a,{title:t.title,goal:t.goal,cards:t.card,note:T(t)})],"wave add":[["slug","file/id"],["after"],(e,[a,t],s)=>n.changeWave(e,a,{add:t,after:s.after})],"wave remove":[["slug","file/id"],[],(e,[a,t])=>n.changeWave(e,a,{remove:t})],"wave set":[["slug","...file/id?"],["after","title","goal","note","file"],(e,[a,...t],s)=>{if(t.length>1)throw new d("wave set takes <slug> [<file/id>]");return n.changeWave(e,a,{card:t[0],after:s.after,title:s.title,goal:s.goal,note:T(s)})}],"wave finish":[["slug"],[],(e,[a])=>n.finishWave(e,a)],slack:[[],[],()=>({lines:[JSON.stringify(M())]})],"slack set":[[],["self-dm","user","domain","timezone"],async(e,a,t)=>{const s={"self-dm":"self_dm",user:"owner_slack_user",domain:"slack_domain",timezone:"timezone"};try{return{lines:[JSON.stringify(await D(Object.fromEntries(Object.entries(t).map(([r,l])=>[s[r],l]))))]}}catch(r){throw new n.BoardError(r.message)}}],"slack thread":[["eventId"],[],(e,[a])=>n.slackThread(u(e,"slack thread"),a)],"slack answer":[["eventId"],["text"],(e,[a],t)=>n.slackAnswer(u(e,"slack answer"),a,t.text)],"slack say":[["reviewer"],["text"],(e,[a],t)=>n.slackSay(u(e,"slack say"),a,t.text)],"slack task":[["eventId","file"],["title","notes","notes-from"],(e,[a,t],s)=>{if(s.notes!==void 0&&s["notes-from"]!==void 0)throw new d("pass --notes or --notes-from, not both");return n.slackTask(u(e,"slack task"),a,t,{title:s.title,notes:s.notes,notesFrom:s["notes-from"]})}],integrations:[[],[],async()=>({lines:[JSON.stringify(await W.rows())]})],"review record":[["pr"],["state","text","file"],(e,[a],t)=>{if(t.text===void 0==(t.file===void 0))throw new d("review record takes --text or --file");return n.recordReview(e,a,{state:t.state,text:t.text??m(t.file)})}],"review post":[["pr"],["file","read-at"],(e,[a],t)=>{if(t.file===void 0)throw new d("review post needs --file <review.md>");if(t["read-at"]===void 0)throw new d("review post needs --read-at <time>, when you read the PR, so a review that landed since is caught");return n.postIncomingReview(e,a,{words:m(t.file),readAt:t["read-at"]})}],"review digest":[[],[],async(e,a,t,s)=>({lines:[JSON.stringify(await n.reviewDigest(e,s))]})],"review due":[[],[],async(e,a,t,s)=>({lines:[JSON.stringify(await n.reviewDue(e,s))]})],"reviewers set":[[],["file"],(e,a,t)=>n.setReviewers(e,q(t.file))],"reviewers add":[[],["file"],(e,a,t)=>n.addReviewers(e,q(t.file))],"reviewers remove":[["...name"],[],(e,a)=>n.removeReviewers(e,a)],"rules list":[[],[],e=>n.listRules(e)],"rules add":[[],["text","hub","by"],(e,a,t)=>n.addRuleTo(e,t)],"rules edit":[["id"],["text","hub"],(e,[a],t)=>n.editRuleIn(e,a,t)],"rules remove":[["id"],["hub"],(e,[a],t)=>n.removeRuleFrom(e,a,t)],settings:[[],[],async e=>({lines:[JSON.stringify(await n.readBoardSettings(e))]})],build:[["file/id"],["asked"],async(e,[a],t)=>{if(!e)throw new n.BoardError("build needs --site <route>");n.parseKey(a);const s=await n.askedGate(e,a,t.asked);try{const r=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/build`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:x.basename(e),key:a}),signal:AbortSignal.timeout(1e4)}),l=await r.json();if(!r.ok)throw new n.BoardError(l.error||"The hub refused the build.");return{lines:["started",...(await n.appendMessage(e,a,{text:s})).lines]}}catch(r){throw r instanceof n.BoardError?r:new n.BoardError(`Could not start the build: ${r.message}`)}}],"main set":[[],["install","install-sha"],(e,a,t)=>n.setMainInstall(e,{install:t.install,installSha:t["install-sha"]})],"settings set":[["name","value"],[],(e,[a,t])=>n.setBoardSetting(e,a,t)],"builder start":[["file/id"],["label","phase","progress","dir","agent","kind","model","eta","worktree","repo","dispatch"],(e,[a],{worktree:t,repo:s,dispatch:r,...l})=>{if(s!==void 0&&!t)throw new d("--repo goes with --worktree");const o=i(p=>z(e,a.split("/")[1],{repo:s,...p}),"make"),c=e&&l.dir===void 0&&ae(e);return n.startBuilder(e,a,{...l,dispatch:r?"session":void 0,worktree:t?o:void 0,ownWorktree:c?o:void 0})}],"builder progress":[["file/id","...text?"],["phase","text","dir","agent","eta","clear-eta"],(e,[a,...t],s)=>{if(t.length&&s.text!==void 0)throw new d("builder progress takes its text once: as words or as --text");if(!t.length&&s.text===void 0)throw new d("builder progress needs its text, as words or as --text");return n.builderProgress(e,a,s.text??t.join(" "),{...s,clearEta:s["clear-eta"]})}],"builder done":[["file/id"],["dir","agent","next","owner-step","check","text","title"],(e,[a],t)=>pe(e,a,t)],"builder pick":[["file/id"],[],(e,[a])=>n.pickBuilder(e,a)],"agent launch":[["file/id"],["name","task","writes","model"],(e,[a],t)=>n.requestHelper(e,a,t)],"agent wait":[["file/id"],["name","agent","timeout"],(e,[a],t)=>n.waitHelper(e,a,{...t,alive:Q})],"agent merge":[["file/id"],["name"],async(e,[a],t)=>{const{record:s,card:r}=await n.helperOf(e,a,t.name);if(!s.done)throw new n.BoardError(`helper ${t.name} is still open; run agent wait ${a} --name ${t.name} first`);if(!s.writes||!s.worktree)throw new n.BoardError(`helper ${t.name} writes no code, so there is nothing to merge`);if(!r)throw new n.BoardError(`${a} has no worktree to merge helper ${t.name} into`);return K(r,s.worktree,await n.cardName(e,a))}],audit:[["...file/id?"],[],(e,[a,...t])=>{if(t.length)throw new d("audit takes one <file/id> at most");return n.auditLog(e,a)}],"builder call":[["file/id","...text"],["dir","agent"],(e,[a,...t],s)=>n.recordCall(e,a,t.join(" "),s)],"builder calls":[["file/id"],["dir","agent"],(e,[a],t)=>n.listCalls(e,a,t)],"builder ask":[["file/id","...text"],["agent"],(e,[a,...t],s)=>n.askOrchestrator(e,a,t.join(" "),s)],"builder answer":[["file/id","ask"],["text"],(e,[a,t],s)=>n.answerBuilder(e,a,t,s.text)],"builder defer":[["file/id","ask"],["text"],(e,[a,t],s)=>n.answerBuilder(e,a,t,s.text,{deferred:!0})],"builder asks":[["file/id"],["wait","timeout"],(e,[a],t)=>n.listAsks(e,a,t)],"builder verified":[["file/id"],["dir","agent","note","title"],async(e,[a],t)=>{await n.verifyCalls(e,a,t);const s=e&&!t.dir?await n.verifiedFinish(e,a):null;if(!s)return{};if(s.held)return{said:`${await n.cardName(e,a)} didn't land: ${s.held}`};if(s.waits.length)return{lines:[`${await n.cardName(e,a)} finishes once the owner answers ${s.waits.join(", ")}`]};try{return await $(e,a,{text:t.note??"Judgment calls verified.",title:t.title})}catch(r){throw r instanceof n.BoardError?Object.assign(new n.BoardError(`judgment calls verified, but ${await n.cardName(e,a)} didn't finish: ${r.message}`),{exitCode:r.exitCode}):r}}],land:[["file/id"],["repo","dir","text","owner-step","check"],async(e,[a],t)=>{const s=a.split("/")[1];if(e&&t.text===void 0)throw new d(`land needs --text "<what was checked>", which closes ${a} as done once it lands`);if(!e&&t.text!==void 0)throw new d("--text closes the card, which needs --site");if(!e&&t["owner-step"]!==void 0)throw new d("--owner-step keeps a board card open, which needs --site");if(t.check!==void 0&&t["owner-step"]===void 0)throw new d("--check goes with --owner-step: it is the code check that closes the card once the step is done");if(e&&t.dir===void 0)return $(e,a,{text:t.text,ownerStep:t["owner-step"],check:t.check,repo:t.repo});const r=e?await n.cardName(e,a):void 0,l=await J(r??a,async()=>{e&&await n.checkClose(e,a,t.text,t["owner-step"],t.check),await n.checkCallsVerified(e,a,{dir:t.dir});const o=await n.recordedWorktree(e,a,{dir:t.dir}),{repo:c,base:p}=F(e,{repo:t.repo??o?.repo});return await G({path:o?.path??x.join(U(),s),branch:o?.branch??s,repo:x.resolve(c),base:p,ref:r}),e?n.setItem(e,a,{done:!0,text:t.text,ownerStep:t["owner-step"],check:t.check}):{lines:[]}});return l.joined?{lines:[`joined the land already running for ${r??a}`]}:l}],"builder stop":[["file/id"],[],(e,[a])=>ee(u(e,"builder stop"),a)],"item stall":[["file/id"],["reason"],(e,[a],t)=>Y(u(e,"item stall"),a,t.reason)],"land stop":[["file/id"],[],(e,[a])=>te(u(e,"land stop"),a)],"land queue":[[],[],()=>Z()],"item bump":[["file/id"],[],(e,[a])=>S(u(e,"item bump"),a,!0)],"item unbump":[["file/id"],[],(e,[a])=>S(u(e,"item unbump"),a,!1)],lander:[["file/id"],[],async(e,[a])=>{if(!e)throw new d("lander needs --site <route>");return await n.relaunchLander(e,a),{lines:[`${await n.cardName(e,a)} is handed back again; the server launches a lander for it within a minute`]}}],merge:[["file/id"],["after-approval","confirmed"],async(e,[a],t)=>e&&await n.heldMerge(e,a)?X(e,a):n.mergeCard(e,a,{afterApproval:t["after-approval"],confirmed:t.confirmed})],"arch file":[["file/id"],[],(e,[a])=>e?V(e,a):Promise.reject(new n.BoardError("arch file needs --site <route>"))],push:[[],["kind","title","body","url","tag","urgent","now"],(e,a,t)=>{if(t.kind!=="fault"&&t.kind!=="reminder")throw new d(`a session pushes only --kind fault or reminder${t.kind===void 0||t.kind===!0?"":`, not "${t.kind}"`}: the board server pushes Action needed, and news goes on the board`);if(t.title===void 0)throw new d("push needs --title");return n.sendPush(e,t)}]};b.install=b.build;function u(e,a){if(!e)throw new d(`${a} needs --site <route>`);return e}i(u,"needSite");const h=i((e,a)=>n.cardName(e,a),"card"),R=i((e,a)=>`${a}${e.length===1?"":"s"} ${e.join(", ")}`,"plural"),N=i(e=>e.agent===void 0||e.agent==="main"?"builder":`builder ${e.agent}`,"builderOf");function we(e){const a=e["pick-up-at"];return[e.done&&(e["split-into"]?`done, split into ${e["split-into"].split(",").join(", ")}`:"done"),e.next!==void 0&&(e.next.trim()?"next line set":"next line cleared"),e["clear-next"]&&"next line cleared",e.pr!==void 0&&`PR #${e.pr}`,e.pushed!==void 0&&`pushed ${e.pushed}`,e["gh-since"]&&`GitHub read to ${e["gh-since"]}`,e["from-ask"]&&`from task ${e["from-ask"]}`,e.phase,e.held&&"held",e.unheld&&"resumed",a!==void 0&&(a?`held until ${a}`:"resumed"),e.deps!==void 0&&(e.deps?`blocked by ${e.deps}`:"deps cleared"),e.parent!==void 0&&(e.parent?`parent ${e.parent}`:"parent cleared"),e.related!==void 0&&(e.related?`related to ${e.related.split(",").join(", ")}`:"related cleared"),e.relate!==void 0&&`related to ${e.relate.split(",").join(", ")} added`,e.unrelate!==void 0&&`unrelated from ${e.unrelate.split(",").join(", ")}`,e.install!==void 0&&"install published",e["clear-install"]&&"install cleared",e.title!==void 0&&"renamed",e.ticket!==void 0&&`ticket ${e.ticket}`,e["new-ticket"]&&"ticket made",e.check!==void 0&&(e.check.trim()?`check ${e.check.trim().split(/\s+/).join(" ")}`:"check removed")].filter(Boolean).join(", ")}i(we,"itemChanges");const me={reply:i(async(e,[a])=>a.includes("/")?`answered ${await h(e,a)}: it reads discussing, the owner's turn`:`reply written to ${await n.eventCard(e,a)}`,"reply"),thread:i(async(e,[a])=>`thread line on ${await h(e,a)}`,"thread"),"topic add":i((e,[a])=>`topic ${a} added`,"topic add"),"question add":i(async(e,a,t,{asked:s,staged:r})=>{const l=[...new Set(s.map(c=>c.topic))],o=l.length!==1?"":l[0].startsWith("item:")?` on ${await h(e,l[0].slice(5))}`:` on topic ${l[0]}`;return`${R(s.map(c=>c.id),"question")} ${r===s.length?"staged":"added"}${o}`},"question add"),"question resolve":i((e,[a])=>`question ${a} resolved`,"question resolve"),"question settle":i((e,[a])=>`question ${a} settled`,"question settle"),"question publish":i(async(e,[a],t,{ids:s})=>`${R(s,"question")} on ${await h(e,a)} passed to the owner`,"question publish"),"question move":i((e,[a],t)=>`questions ${a} merged onto ${t.to}`,"question move"),"prototype add":i(async(e,[a],t)=>`prototype ${a} recorded${t.card?` for ${await h(e,t.card)}`:""}`,"prototype add"),"prototype rm":i((e,[a])=>`prototype ${a} removed`,"prototype rm"),"item set":i(async(e,[a],t)=>`${await h(e,a)}: ${we(t)}`,"item set"),"item add":i(async(e,a,t,{key:s})=>`${await h(e,s)} added as ${s}`,"item add"),"item notes":i(async(e,[a],t,{file:s,heading:r})=>`${await h(e,a)}: notes section "${r}" added to ${s}`,"item notes"),"item relate-ask":i(async(e,[a,t])=>`task ${a} folded into ${await h(e,t)}`,"item relate-ask"),"ask unfold":i((e,[a])=>`task ${a} back on a card of its own`,"ask unfold"),"ask move":i((e,[a],t)=>`task ${a} moved to /${t.to}`,"ask move"),"wave create":i((e,[a])=>`wave ${a} created`,"wave create"),"wave add":i(async(e,[a,t])=>`${await h(e,t)} added to wave ${a}`,"wave add"),"wave remove":i(async(e,[a,t])=>`${await h(e,t)} taken out of wave ${a}`,"wave remove"),"wave set":i((e,[a])=>`wave ${a} updated`,"wave set"),"wave finish":i((e,[a])=>`wave ${a} finished`,"wave finish"),"review record":i((e,[a],t)=>`review of PR #${a} recorded: ${t.state==="needs"?"needs you":"posted"}`,"review record"),"reviewers set":i(()=>"reviewers replaced","reviewers set"),"reviewers add":i(()=>"reviewers added","reviewers add"),"reviewers remove":i((e,a)=>`${R(a,"reviewer")} removed`,"reviewers remove"),"rules add":i((e,a,t)=>`${t.hub?"hub":"board"} rule added`,"rules add"),"rules edit":i((e,[a],t)=>`${t.hub?"hub":"board"} rule ${a} reworded`,"rules edit"),"rules remove":i((e,[a],t)=>`${t.hub?"hub":"board"} rule ${a} removed`,"rules remove"),"builder start":i(async(e,[a],t,s)=>`${await h(e,a)}: ${s.alreadyOpen?"already open: the Implement tap opened its record; launch the agent, whose own builder start takes it up":`${N(t)} started`}`,"builder start"),"builder progress":i(async(e,[a])=>`progress line on ${await h(e,a)}`,"builder progress"),"builder done":i(async(e,[a],t,s)=>`${await h(e,a)}: ${s?.said??`${N(t)} done${t["owner-step"]===void 0?"":`; owner step "${t["owner-step"].trim()}"`}`}`,"builder done"),"builder call":i(async(e,[a])=>`judgment call recorded on ${await h(e,a)}`,"builder call"),"builder ask":i(async(e,[a],t,{id:s})=>`${await h(e,a)}: asked the orchestrator, ${s}`,"builder ask"),"builder answer":i(async(e,[a,t])=>`${await h(e,a)}: ${t} answered`,"builder answer"),"builder defer":i(async(e,[a,t])=>`${await h(e,a)}: ${t} deferred to the owner`,"builder defer"),"builder verified":i(async(e,[a],t,s)=>`${await h(e,a)}: judgment calls verified${s?.said?`; ${s.said}`:""}`,"builder verified"),land:i(async(e,[a],t,s)=>e?s?.said?`${await h(e,a)}: ${s.said}`:t["owner-step"]===void 0?`${await h(e,a)}: landed and done`:`${await h(e,a)}: landed; waits on the owner step "${t["owner-step"].trim()}"`:null,"land"),merge:i(async(e,[a],t,s)=>`${await h(e,a)}: ${s?.said??"merged"}`,"merge"),"arch file":i(async(e,[a])=>`findings of ${await h(e,a)} filed`,"arch file")};async function fe(e,a={}){try{const{flags:t,rest:s}=oe(e),r=[3,2,1].map(w=>s.slice(0,w).join(" ")).find(w=>y(b,w)),l=y(b,r);if(!l)throw new d(s.length?`unknown command "${s.slice(0,2).join(" ")}"`:"no command given");const[o,c,p]=l,f=s.slice(r.split(" ").length),_=o.at(-1)?.startsWith("..."),B=o.at(-1)?.endsWith("?")?o.length-1:o.length;if(_?f.length<B:f.length!==o.length)throw new d(`${r} takes ${o.map(w=>`<${w.replace(/^\.\.\.|\?$/g,"")}>`).join(" ")||"no arguments"}`);const{site:I,...E}=t;if(I?.length>1)throw new d("--site is given twice");const A=I?.[0],k=de(r,c,E);ue(c,k);const P=A===void 0?null:await n.siteDir(A),v=await p(P,f,k,a),O=y(me,r),j=O&&await O(P,f,k,v);j&&console.log(j);for(const w of typeof v=="object"&&v?.lines||[])console.log(w);return 0}catch(t){if(t instanceof d)return console.error(`${t.message} (node board.mjs --help lists the commands)`),2;if(t instanceof n.BoardError)return console.error(t.message),t.exitCode??1;throw t}}if(i(fe,"main"),process.argv[1]&&import.meta.url===L(g.realpathSync(process.argv[1])).href){const e=process.argv.slice(2);!e.length||e.includes("--help")||e[0]==="help"?console.log(ie):process.exitCode=await fe(e)}export{ie as USAGE,fe as main};
716
+ KICKFLIP_PUSH_CMD='["<cmd>","<arg>"]' replaces \`node ../../bin/kickflip.mjs push\`; the flags follow it`;class d extends Error{static{i(this,"UsageError")}}const re=new Set(["new-ticket","done","clear-status","clear-next","urgent","now","clear-eta","held","unheld","idle","wait","hub","clear-install","force","worktree","dispatch","writes","implement"]);function oe(e){const a=Object.create(null),t=[];for(let s=0;s<e.length;s++){const r=e[s];if(!r.startsWith("--")){t.push(r);continue}const l=r.slice(2);if(re.has(l)){if(Object.hasOwn(a,l))throw new d(`--${l} is given twice`);a[l]=!0}else if(s+1<e.length)(a[l]||=[]).push(e[++s]);else throw new d(`--${l} needs a value`)}return{flags:a,rest:t}}i(oe,"parse");function de(e,a,t){const s=new Set(a.filter(o=>o.startsWith("...")).map(o=>o.slice(3))),r=new Set(a.map(o=>o.replace(/^\.\.\./,""))),l=Object.keys(t).filter(o=>!r.has(o));if(l.length)throw new d(`${e} does not take ${l.map(o=>"--"+o).join(", ")}`);return Object.fromEntries(Object.entries(t).map(([o,c])=>{if(c===!0||s.has(o))return[o,c];if(c.length>1)throw new d(`--${o} is given twice`);return[o,c[0]]}))}i(de,"flagsOf");const le=["text","note","notes","heading","summary","title","body","next","progress","install-label","confirmed","asked","task","reason"],he=/^(?:\/|~\/|\.{1,2}\/)\S*\.(?:html?|md|json|txt)$/;function ce(e){const a=e.trim();if(!a||/\s/.test(a))return!1;if(he.test(a))return!0;try{return g.statSync(a).isFile()}catch{return!1}}i(ce,"looksLikePath");function ue(e,a){for(const t of le){if(typeof a[t]!="string"||!ce(a[t]))continue;const s=e.includes("file")?`; for a file use --file ${a[t].trim()}`:"";throw new d(`--${t} takes text, not a file path (${a[t].trim()})${s}`)}if(typeof a.file=="string"&&a.file!=="-"&&!g.existsSync(a.file))throw new d(`--file ${a.file}: no such file`)}i(ue,"checkInputs");const m=i(e=>g.readFileSync(e==="-"?0:e,"utf8"),"readInput");function q(e){if(!e)throw new d("--file <path> is required");try{return JSON.parse(m(e))}catch(a){throw new n.BoardError(`${e}: ${a.message}`)}}i(q,"readJSONInput");function C(e,a){const t=["notes-file","plan"].find(s=>e[s]!==void 0);if(t)throw new d(`--${t} is gone: the board names a card's notes file, <id>-notes.md, and writes it itself; ${a}`)}i(C,"refuseNotesFile");function T(e){if(e.note!==void 0&&e.file!==void 0)throw new d("pass --note or --file, not both");return e.file===void 0?e.note:m(e.file)}i(T,"noteOf");async function pe(e,a,{"owner-step":t,check:s,text:r,title:l,...o}){if(t!==void 0&&(!e||o.dir))throw new d("--owner-step goes on a board card: pass --site, not --dir");if(s!==void 0&&t===void 0)throw new d("--check goes with --owner-step: it is the code check that closes the card once the step is done");t!==void 0&&await n.setItem(e,a,{ownerStep:t,check:s});const c=e&&!o.dir,p=c&&await n.handBackCalls(e,a,o);if(p)return p;if(c&&o.next===void 0&&await n.finishesCard(e,a,o)){if(r===void 0)throw new n.BoardError(`builder done finishes ${a}: pass --text "<what you checked>", or --next "<what happens next>" to stop without finishing`);return $(e,a,{text:r,ownerStep:t,title:l})}if(c&&await n.isHelper(e,a,o.agent)){if(r===void 0)throw new d('a helper hands back its result: pass --text "<what you did or found>"');return n.stopBuilder(e,a,{...o,note:r})}if(c&&r!==void 0&&await n.isPlanner(e,a,o.agent))return n.stopPlanner(e,a,{...o,note:r,requireNext:!0});if(r!==void 0)throw new d("--text goes on a builder done that finishes the card, the main build with a worktree and no --next, or on a helper's or a planner's");return n.stopBuilder(e,a,{...o,requireNext:!0})}i(pe,"builderDone");const b={pr:[["file/id"],[],async(e,[a])=>({lines:(await n.prReport(e,a)).map(t=>JSON.stringify(t))})],"pr comment":[["pr"],["body"],(e,[a],t)=>{if(t.body===void 0)throw new d('pr comment needs --body "<what Claude says>"');return n.postPR(e,a,{body:t.body})}],"pr reply":[["pr","comment-id"],["body"],(e,[a,t],s)=>{if(s.body===void 0)throw new d('pr reply needs --body "<what Claude says>"');return n.postPR(e,a,{replyTo:t,body:s.body})}],reply:[["eventId"],["text","file","title","implement"],(e,[a],t)=>{if(t.text===void 0==(t.file===void 0))throw new d("reply takes --text or --file");if(t.implement&&!a.includes("/"))throw new d("reply --implement marks a card: name it <file>/<id>");const s=t.text??m(t.file);return a.includes("/")?n.answerCard(e,a,s,t.title,{implement:t.implement}):n.writeReply(e,a,s,t.title)}],thread:[["file/id"],["text","link","who","via"],(e,[a],t)=>n.appendMessage(e,a,t)],"visual add":[["file/id"],["file","title"],(e,[a],t)=>n.addVisual(e,a,t)],"item brief":[["file/id"],["kind","file"],(e,[a],t)=>n.writeBrief(e,a,t)],"topic add":[["id"],["title","summary"],(e,[a],t)=>n.addTopic(e,{id:a,...t})],"question add":[[],["file"],async(e,a,t)=>{const s=q(t.file);return{...await n.addQuestions(e,s,process.env.KICKFLIP_RENDER_CHECK==="off"?void 0:se),asked:[s].flat()}}],"question resolve":[["id"],["note"],(e,[a],t)=>n.resolveQuestion(e,a,t.note)],"question settle":[["id"],["note"],(e,[a],t)=>n.settleQuestion(e,a,t.note)],"question publish":[["key"],[],async(e,[a])=>({ids:await n.publishScreened(e,a)})],"question move":[["ids"],["to","block"],(e,[a],t)=>n.moveQuestions(e,a.split(",").filter(Boolean),t.to,(t.block||"").split(",").filter(Boolean))],"prototype add":[["topic"],["title","...take","card","served","...state"],(e,[a],t)=>n.addPrototype(e,{topic:a,title:t.title,card:t.card,takes:t.take,served:t.served,states:t.state})],"prototype base":[[],[],e=>ne(e)],"prototype list":[[],[],e=>n.listPrototypes(e)],"prototype rm":[["topic"],[],(e,[a])=>n.removePrototype(e,a)],"item set":[["file/id"],["done","split-into","status","clear-status","next","clear-next","...pr","pushed","gh-since","from-ask","phase","state","held","unheld","pick-up-at","notes-file","plan","deps","blocked-until","block-reason","parent","related","relate","unrelate","install","install-sha","install-label","clear-install","title","size","model","force","ticket","new-ticket","text","check"],async(e,[a],t,s)=>(C(t,`add to its notes with item notes ${a} --heading "<heading>" --file <draft.md>`),t.done&&e&&await n.checkNotReopened(e,a),n.setItem(e,a,{done:t.done,splitInto:t["split-into"],status:t.status,clearStatus:t["clear-status"],next:t.next,clearNext:t["clear-next"],pr:t.pr,pushed:t.pushed,ghSince:t["gh-since"],fromAsk:t["from-ask"],phase:t.phase,state:t.state,held:t.held,unheld:t.unheld,pickUpAt:t["pick-up-at"],deps:t.deps,blockedUntil:t["blocked-until"],blockReason:t["block-reason"],parent:t.parent,related:t.related,relate:t.relate,unrelate:t.unrelate,install:t.install,installSha:t["install-sha"],installLabel:t["install-label"],clearInstall:t["clear-install"],title:t.title,size:t.size,model:t.model,force:t.force,ticket:t.ticket,newTicket:t["new-ticket"],text:t.text,check:t.check},s))],"item add":[["file[/id]"],["title","notes","notes-from","deps","notes-file","plan","ticket","new-ticket","pick-up-at","held","parent","split-of","size","model","phase","state","relate","from-ask","text"],(e,[a],t,s)=>{if(C(t,"seed a new card's notes with item add --notes-from <draft.md>"),t.notes!==void 0&&t["notes-from"]!==void 0)throw new d("pass --notes or --notes-from, not both");return n.addItem(e,a,{title:t.title,notes:t.notes,notesFrom:t["notes-from"],deps:t.deps,ticket:t.ticket,newTicket:t["new-ticket"],pickUpAt:t["pick-up-at"],held:t.held,parent:t.parent,splitOf:t["split-of"],size:t.size,model:t.model,phase:t.phase,state:t.state,relate:t.relate,fromAsk:t["from-ask"],text:t.text,intake:!0},s)}],"item notes":[["file/id"],["heading","file","from"],(e,[a],t)=>{if(t.heading===void 0&&t.from===void 0)throw new d(`item notes needs --heading "<the section's title>"`);if(t.file===void 0==(t.from===void 0))throw new d("item notes takes --file <draft.md> or --from <file>/<id>");return n.addNotes(e,a,t)}],"item relate-ask":[["eventId","file/id"],["summary","title"],(e,[a,t],s)=>n.relateAsk(e,a,t,s.summary,s.title)],"ask unfold":[["eventId"],[],(e,[a])=>n.unfoldAsk(e,a)],"ask move":[["eventId"],["to"],(e,[a],t)=>n.moveAsk(e,a,t.to)],"wave create":[["slug"],["title","goal","...card","note","file"],(e,[a],t)=>n.createWave(e,a,{title:t.title,goal:t.goal,cards:t.card,note:T(t)})],"wave add":[["slug","file/id"],["after"],(e,[a,t],s)=>n.changeWave(e,a,{add:t,after:s.after})],"wave remove":[["slug","file/id"],[],(e,[a,t])=>n.changeWave(e,a,{remove:t})],"wave set":[["slug","...file/id?"],["after","title","goal","note","file"],(e,[a,...t],s)=>{if(t.length>1)throw new d("wave set takes <slug> [<file/id>]");return n.changeWave(e,a,{card:t[0],after:s.after,title:s.title,goal:s.goal,note:T(s)})}],"wave finish":[["slug"],[],(e,[a])=>n.finishWave(e,a)],slack:[[],[],()=>({lines:[JSON.stringify(M())]})],"slack set":[[],["self-dm","user","domain","timezone"],async(e,a,t)=>{const s={"self-dm":"self_dm",user:"owner_slack_user",domain:"slack_domain",timezone:"timezone"};try{return{lines:[JSON.stringify(await D(Object.fromEntries(Object.entries(t).map(([r,l])=>[s[r],l]))))]}}catch(r){throw new n.BoardError(r.message)}}],"slack thread":[["eventId"],[],(e,[a])=>n.slackThread(u(e,"slack thread"),a)],"slack answer":[["eventId"],["text"],(e,[a],t)=>n.slackAnswer(u(e,"slack answer"),a,t.text)],"slack say":[["reviewer"],["text"],(e,[a],t)=>n.slackSay(u(e,"slack say"),a,t.text)],"slack task":[["eventId","file"],["title","notes","notes-from"],(e,[a,t],s)=>{if(s.notes!==void 0&&s["notes-from"]!==void 0)throw new d("pass --notes or --notes-from, not both");return n.slackTask(u(e,"slack task"),a,t,{title:s.title,notes:s.notes,notesFrom:s["notes-from"]})}],integrations:[[],[],async()=>({lines:[JSON.stringify(await W.rows())]})],"review record":[["pr"],["state","text","file"],(e,[a],t)=>{if(t.text===void 0==(t.file===void 0))throw new d("review record takes --text or --file");return n.recordReview(e,a,{state:t.state,text:t.text??m(t.file)})}],"review post":[["pr"],["file","read-at"],(e,[a],t)=>{if(t.file===void 0)throw new d("review post needs --file <review.md>");if(t["read-at"]===void 0)throw new d("review post needs --read-at <time>, when you read the PR, so a review that landed since is caught");return n.postIncomingReview(e,a,{words:m(t.file),readAt:t["read-at"]})}],"review digest":[[],[],async(e,a,t,s)=>({lines:[JSON.stringify(await n.reviewDigest(e,s))]})],"review due":[[],[],async(e,a,t,s)=>({lines:[JSON.stringify(await n.reviewDue(e,s))]})],"reviewers set":[[],["file"],(e,a,t)=>n.setReviewers(e,q(t.file))],"reviewers add":[[],["file"],(e,a,t)=>n.addReviewers(e,q(t.file))],"reviewers remove":[["...name"],[],(e,a)=>n.removeReviewers(e,a)],"rules list":[[],[],e=>n.listRules(e)],"rules add":[[],["text","hub","by"],(e,a,t)=>n.addRuleTo(e,t)],"rules edit":[["id"],["text","hub"],(e,[a],t)=>n.editRuleIn(e,a,t)],"rules remove":[["id"],["hub"],(e,[a],t)=>n.removeRuleFrom(e,a,t)],settings:[[],[],async e=>({lines:[JSON.stringify(await n.readBoardSettings(e))]})],build:[["file/id"],["asked"],async(e,[a],t)=>{if(!e)throw new n.BoardError("build needs --site <route>");n.parseKey(a);const s=await n.askedGate(e,a,t.asked);try{const r=await fetch(`http://127.0.0.1:${process.env.KICKFLIP_HUB_PORT||4500}/_hub/build`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({route:x.basename(e),key:a}),signal:AbortSignal.timeout(1e4)}),l=await r.json();if(!r.ok)throw new n.BoardError(l.error||"The hub refused the build.");return{lines:["started",...(await n.appendMessage(e,a,{text:s})).lines]}}catch(r){throw r instanceof n.BoardError?r:new n.BoardError(`Could not start the build: ${r.message}`)}}],"main set":[[],["install","install-sha"],(e,a,t)=>n.setMainInstall(e,{install:t.install,installSha:t["install-sha"]})],"settings set":[["name","value"],[],(e,[a,t])=>n.setBoardSetting(e,a,t)],"builder start":[["file/id"],["label","phase","progress","dir","agent","kind","model","eta","worktree","repo","dispatch"],(e,[a],{worktree:t,repo:s,dispatch:r,...l})=>{if(s!==void 0&&!t)throw new d("--repo goes with --worktree");const o=i(p=>z(e,a.split("/")[1],{repo:s,...p}),"make"),c=e&&l.dir===void 0&&ae(e);return n.startBuilder(e,a,{...l,dispatch:r?"session":void 0,worktree:t?o:void 0,ownWorktree:c?o:void 0})}],"builder progress":[["file/id","...text?"],["phase","text","dir","agent","eta","clear-eta"],(e,[a,...t],s)=>{if(t.length&&s.text!==void 0)throw new d("builder progress takes its text once: as words or as --text");if(!t.length&&s.text===void 0)throw new d("builder progress needs its text, as words or as --text");return n.builderProgress(e,a,s.text??t.join(" "),{...s,clearEta:s["clear-eta"]})}],"builder done":[["file/id"],["dir","agent","next","owner-step","check","text","title"],(e,[a],t)=>pe(e,a,t)],"builder pick":[["file/id"],[],(e,[a])=>n.pickBuilder(e,a)],"agent launch":[["file/id"],["name","task","writes","model"],(e,[a],t)=>n.requestHelper(e,a,t)],"agent wait":[["file/id"],["name","agent","timeout"],(e,[a],t)=>n.waitHelper(e,a,{...t,alive:Q})],"agent merge":[["file/id"],["name"],async(e,[a],t)=>{const{record:s,card:r}=await n.helperOf(e,a,t.name);if(!s.done)throw new n.BoardError(`helper ${t.name} is still open; run agent wait ${a} --name ${t.name} first`);if(!s.writes||!s.worktree)throw new n.BoardError(`helper ${t.name} writes no code, so there is nothing to merge`);if(!r)throw new n.BoardError(`${a} has no worktree to merge helper ${t.name} into`);return K(r,s.worktree,await n.cardName(e,a))}],audit:[["...file/id?"],[],(e,[a,...t])=>{if(t.length)throw new d("audit takes one <file/id> at most");return n.auditLog(e,a)}],"builder call":[["file/id","...text"],["dir","agent"],(e,[a,...t],s)=>n.recordCall(e,a,t.join(" "),s)],"builder calls":[["file/id"],["dir","agent"],(e,[a],t)=>n.listCalls(e,a,t)],"builder ask":[["file/id","...text"],["agent"],(e,[a,...t],s)=>n.askOrchestrator(e,a,t.join(" "),s)],"builder answer":[["file/id","ask"],["text"],(e,[a,t],s)=>n.answerBuilder(e,a,t,s.text)],"builder defer":[["file/id","ask"],["text"],(e,[a,t],s)=>n.answerBuilder(e,a,t,s.text,{deferred:!0})],"builder asks":[["file/id"],["wait","timeout"],(e,[a],t)=>n.listAsks(e,a,t)],"builder verified":[["file/id"],["dir","agent","note","title"],async(e,[a],t)=>{await n.verifyCalls(e,a,t);const s=e&&!t.dir?await n.verifiedFinish(e,a):null;if(!s)return{};if(s.held)return{said:`${await n.cardName(e,a)} didn't land: ${s.held}`};if(s.waits.length)return{lines:[`${await n.cardName(e,a)} finishes once the owner answers ${s.waits.join(", ")}`]};try{return await $(e,a,{text:t.note??"Judgment calls verified.",title:t.title})}catch(r){throw r instanceof n.BoardError?Object.assign(new n.BoardError(`judgment calls verified, but ${await n.cardName(e,a)} didn't finish: ${r.message}`),{exitCode:r.exitCode}):r}}],land:[["file/id"],["repo","dir","text","owner-step","check"],async(e,[a],t)=>{const s=a.split("/")[1];if(e&&t.text===void 0)throw new d(`land needs --text "<what was checked>", which closes ${a} as done once it lands`);if(!e&&t.text!==void 0)throw new d("--text closes the card, which needs --site");if(!e&&t["owner-step"]!==void 0)throw new d("--owner-step keeps a board card open, which needs --site");if(t.check!==void 0&&t["owner-step"]===void 0)throw new d("--check goes with --owner-step: it is the code check that closes the card once the step is done");if(e&&t.dir===void 0)return $(e,a,{text:t.text,ownerStep:t["owner-step"],check:t.check,repo:t.repo});const r=e?await n.cardName(e,a):void 0,l=await J(r??a,async()=>{e&&await n.checkClose(e,a,t.text,t["owner-step"],t.check),await n.checkCallsVerified(e,a,{dir:t.dir});const o=await n.recordedWorktree(e,a,{dir:t.dir}),{repo:c,base:p}=F(e,{repo:t.repo??o?.repo});return await G({path:o?.path??x.join(U(),s),branch:o?.branch??s,repo:x.resolve(c),base:p,ref:r}),e?n.setItem(e,a,{done:!0,text:t.text,ownerStep:t["owner-step"],check:t.check}):{lines:[]}});return l.joined?{lines:[`joined the land already running for ${r??a}`]}:l}],"builder stop":[["file/id"],[],(e,[a])=>ee(u(e,"builder stop"),a)],"item stall":[["file/id"],["reason"],(e,[a],t)=>Y(u(e,"item stall"),a,t.reason)],"land stop":[["file/id"],[],(e,[a])=>te(u(e,"land stop"),a)],"land queue":[[],[],()=>Z()],"item bump":[["file/id"],[],(e,[a])=>S(u(e,"item bump"),a,!0)],"item unbump":[["file/id"],[],(e,[a])=>S(u(e,"item unbump"),a,!1)],lander:[["file/id"],[],async(e,[a])=>{if(!e)throw new d("lander needs --site <route>");return await n.relaunchLander(e,a),{lines:[`${await n.cardName(e,a)} is handed back again; the server launches a lander for it within a minute`]}}],merge:[["file/id"],["after-approval","confirmed"],async(e,[a],t)=>e&&await n.heldMerge(e,a)?X(e,a):n.mergeCard(e,a,{afterApproval:t["after-approval"],confirmed:t.confirmed})],"arch file":[["file/id"],[],(e,[a])=>e?V(e,a):Promise.reject(new n.BoardError("arch file needs --site <route>"))],push:[[],["kind","title","body","url","tag","urgent","now"],(e,a,t)=>{if(t.kind!=="fault"&&t.kind!=="reminder")throw new d(`a session pushes only --kind fault or reminder${t.kind===void 0||t.kind===!0?"":`, not "${t.kind}"`}: the board server pushes Action needed, and news goes on the board`);if(t.title===void 0)throw new d("push needs --title");return n.sendPush(e,t)}]};b.install=b.build;function u(e,a){if(!e)throw new d(`${a} needs --site <route>`);return e}i(u,"needSite");const h=i((e,a)=>n.cardName(e,a),"card"),R=i((e,a)=>`${a}${e.length===1?"":"s"} ${e.join(", ")}`,"plural"),N=i(e=>e.agent===void 0||e.agent==="main"?"builder":`builder ${e.agent}`,"builderOf");function we(e){const a=e["pick-up-at"];return[e.done&&(e["split-into"]?`done, split into ${e["split-into"].split(",").join(", ")}`:"done"),e.next!==void 0&&(e.next.trim()?"next line set":"next line cleared"),e["clear-next"]&&"next line cleared",e.pr!==void 0&&`PR #${e.pr}`,e.pushed!==void 0&&`pushed ${e.pushed}`,e["gh-since"]&&`GitHub read to ${e["gh-since"]}`,e["from-ask"]&&`from task ${e["from-ask"]}`,e.phase,e.held&&"held",e.unheld&&"resumed",a!==void 0&&(a?`held until ${a}`:"resumed"),e.deps!==void 0&&(e.deps?`blocked by ${e.deps}`:"deps cleared"),e.parent!==void 0&&(e.parent?`parent ${e.parent}`:"parent cleared"),e.related!==void 0&&(e.related?`related to ${e.related.split(",").join(", ")}`:"related cleared"),e.relate!==void 0&&`related to ${e.relate.split(",").join(", ")} added`,e.unrelate!==void 0&&`unrelated from ${e.unrelate.split(",").join(", ")}`,e.install!==void 0&&"install published",e["clear-install"]&&"install cleared",e.title!==void 0&&"renamed",e.ticket!==void 0&&`ticket ${e.ticket}`,e["new-ticket"]&&"ticket made",e.check!==void 0&&(e.check.trim()?`check ${e.check.trim().split(/\s+/).join(" ")}`:"check removed")].filter(Boolean).join(", ")}i(we,"itemChanges");const me={reply:i(async(e,[a])=>a.includes("/")?`answered ${await h(e,a)}: it reads discussing, the owner's turn`:`reply written to ${await n.eventCard(e,a)}`,"reply"),thread:i(async(e,[a])=>`thread line on ${await h(e,a)}`,"thread"),"topic add":i((e,[a])=>`topic ${a} added`,"topic add"),"question add":i(async(e,a,t,{asked:s,staged:r})=>{const l=[...new Set(s.map(c=>c.topic))],o=l.length!==1?"":l[0].startsWith("item:")?` on ${await h(e,l[0].slice(5))}`:` on topic ${l[0]}`;return`${R(s.map(c=>c.id),"question")} ${r===s.length?"staged":"added"}${o}`},"question add"),"question resolve":i((e,[a])=>`question ${a} resolved`,"question resolve"),"question settle":i((e,[a])=>`question ${a} settled`,"question settle"),"question publish":i(async(e,[a],t,{ids:s})=>`${R(s,"question")} on ${await h(e,a)} passed to the owner`,"question publish"),"question move":i((e,[a],t)=>`questions ${a} merged onto ${t.to}`,"question move"),"prototype add":i(async(e,[a],t)=>`prototype ${a} recorded${t.card?` for ${await h(e,t.card)}`:""}`,"prototype add"),"prototype rm":i((e,[a])=>`prototype ${a} removed`,"prototype rm"),"item set":i(async(e,[a],t)=>`${await h(e,a)}: ${we(t)}`,"item set"),"item add":i(async(e,a,t,{key:s})=>`${await h(e,s)} added as ${s}`,"item add"),"item notes":i(async(e,[a],t,{file:s,heading:r})=>`${await h(e,a)}: notes section "${r}" added to ${s}`,"item notes"),"item relate-ask":i(async(e,[a,t])=>`task ${a} folded into ${await h(e,t)}`,"item relate-ask"),"ask unfold":i((e,[a])=>`task ${a} back on a card of its own`,"ask unfold"),"ask move":i((e,[a],t)=>`task ${a} moved to /${t.to}`,"ask move"),"wave create":i((e,[a])=>`wave ${a} created`,"wave create"),"wave add":i(async(e,[a,t])=>`${await h(e,t)} added to wave ${a}`,"wave add"),"wave remove":i(async(e,[a,t])=>`${await h(e,t)} taken out of wave ${a}`,"wave remove"),"wave set":i((e,[a])=>`wave ${a} updated`,"wave set"),"wave finish":i((e,[a])=>`wave ${a} finished`,"wave finish"),"review record":i((e,[a],t)=>`review of PR #${a} recorded: ${t.state==="needs"?"needs you":"posted"}`,"review record"),"reviewers set":i(()=>"reviewers replaced","reviewers set"),"reviewers add":i(()=>"reviewers added","reviewers add"),"reviewers remove":i((e,a)=>`${R(a,"reviewer")} removed`,"reviewers remove"),"rules add":i((e,a,t)=>`${t.hub?"hub":"board"} rule added`,"rules add"),"rules edit":i((e,[a],t)=>`${t.hub?"hub":"board"} rule ${a} reworded`,"rules edit"),"rules remove":i((e,[a],t)=>`${t.hub?"hub":"board"} rule ${a} removed`,"rules remove"),"builder start":i(async(e,[a],t,s)=>`${await h(e,a)}: ${s.alreadyOpen?"already open: the Implement tap opened its record; launch the agent, whose own builder start takes it up":`${N(t)} started`}`,"builder start"),"builder progress":i(async(e,[a])=>`progress line on ${await h(e,a)}`,"builder progress"),"builder done":i(async(e,[a],t,s)=>`${await h(e,a)}: ${s?.said??`${N(t)} done${t["owner-step"]===void 0?"":`; owner step "${t["owner-step"].trim()}"`}`}`,"builder done"),"builder call":i(async(e,[a])=>`judgment call recorded on ${await h(e,a)}`,"builder call"),"builder ask":i(async(e,[a],t,{id:s})=>`${await h(e,a)}: asked the orchestrator, ${s}`,"builder ask"),"builder answer":i(async(e,[a,t])=>`${await h(e,a)}: ${t} answered`,"builder answer"),"builder defer":i(async(e,[a,t])=>`${await h(e,a)}: ${t} deferred to the owner`,"builder defer"),"builder verified":i(async(e,[a],t,s)=>`${await h(e,a)}: judgment calls verified${s?.said?`; ${s.said}`:""}`,"builder verified"),land:i(async(e,[a],t,s)=>e?s?.said?`${await h(e,a)}: ${s.said}`:t["owner-step"]===void 0?`${await h(e,a)}: landed and done`:`${await h(e,a)}: landed; waits on the owner step "${t["owner-step"].trim()}"`:null,"land"),merge:i(async(e,[a],t,s)=>`${await h(e,a)}: ${s?.said??"merged"}`,"merge"),"arch file":i(async(e,[a])=>`findings of ${await h(e,a)} filed`,"arch file")};async function fe(e,a={}){try{const{flags:t,rest:s}=oe(e),r=[3,2,1].map(w=>s.slice(0,w).join(" ")).find(w=>y(b,w)),l=y(b,r);if(!l)throw new d(s.length?`unknown command "${s.slice(0,2).join(" ")}"`:"no command given");const[o,c,p]=l,f=s.slice(r.split(" ").length),_=o.at(-1)?.startsWith("..."),B=o.at(-1)?.endsWith("?")?o.length-1:o.length;if(_?f.length<B:f.length!==o.length)throw new d(`${r} takes ${o.map(w=>`<${w.replace(/^\.\.\.|\?$/g,"")}>`).join(" ")||"no arguments"}`);const{site:I,...E}=t;if(I?.length>1)throw new d("--site is given twice");const A=I?.[0],k=de(r,c,E);ue(c,k);const P=A===void 0?null:await n.siteDir(A),v=await p(P,f,k,a),O=y(me,r),j=O&&await O(P,f,k,v);j&&console.log(j);for(const w of typeof v=="object"&&v?.lines||[])console.log(w);return 0}catch(t){if(t instanceof d)return console.error(`${t.message} (node board.mjs --help lists the commands)`),2;if(t instanceof n.BoardError)return console.error(t.message),t.exitCode??1;throw t}}if(i(fe,"main"),process.argv[1]&&import.meta.url===L(g.realpathSync(process.argv[1])).href){const e=process.argv.slice(2);!e.length||e.includes("--help")||e[0]==="help"?console.log(ie):process.exitCode=await fe(e)}export{ie as USAGE,fe as main};
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: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(`
1
+ var zt=Object.defineProperty;var d=(e,t)=>zt(e,"name",{value:t,configurable:!0});import{routeRecord as R}from"../hub/routes.mjs";import Jt from"node:fs";import{ticketBadge as ft,ticketIn as Zt}from"./tickets.mjs";import{prLinks as en,reviewLinks as tn,reviewTime as nn,reviewersOf as sn}from"./review.mjs";import{remoteFromInput as on}from"../hub/repos.mjs";import{reviewsOf as rn,reviewNeeds as an,reviewActions as ln}from"./auto-review.mjs";import{cardPRs as dn,hasPR as tt,leastReady as cn,prKeyOf as un,prURL as pn}from"../shared/card-prs.mjs";import gt from"node:vm";import{checkWords as fn,parseCheck as gn}from"../shared/owner-check.mjs";import{buildScriptOf as kn}from"./build-script.mjs";import{HUB as Qe,hubLineOf as kt}from"./hub-lines.mjs";const mn="card-merged",nt=gt.createContext({});for(const e of["ui-enums.js","ui-order.js"])gt.runInContext(Jt.readFileSync(new URL(`./public/${e}`,import.meta.url),"utf8"),nt);const{BUTTON:re,ACTION_KIND:M,BUILDER_KIND:fe,PR_STATE:z,PHASES:mt}=nt.BoardUI,{laneOrder:hn}=nt.BoardUI,wn=["item-message","mark-ready","grill-more","prototype-request","visual-request","reask-request"],ht="question-reopen",xo=Object.freeze({todo:"Todo",progress:"In Progress",review:"In Review",done:"Done"}),$e="move",yn=10*6e4,bn=5*6e4,wt="build-refused",De=Object.freeze({ask:"builder-ask",answer:"builder-answer"}),Sn=864e5,ae=d(e=>e.lane==="done","closedLane"),vn=d(e=>`file:${e}`,"fileFold"),yt=7*Sn,bt=d((e,t)=>({id:e,url:t?.workspace?`https://linear.app/${t.workspace}/issue/${e}`:null}),"linearRef"),xn=d(e=>String(e).replace(/^S(\d+)$/,"INT-$1"),"internalRef"),st=d((e,t,n)=>Zt(n,t.ticket)?t.ticket:e.internal===!0?xn(t.id):t.id,"refOf"),On=d((e,t,n)=>ft(e,t,n)||(t?{id:String(t),url:null,source:null}:null),"ticketOf");function En(e,t,n,s,r,a,c,i,u){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||c)?"progress":"review":t||u?"progress":"todo"}d(En,"laneOf");const He="Lander",St=d(e=>e.kind!==fe.install&&e.label!==He,"changesCode");function $n(e,t){return e?.state==="MERGED"||t?z.merged:e?.state==="CLOSED"?z.closed:e?.state==="OPEN"?e.isDraft?z.draft:z.open:null}d($n,"prStateOf");const vt=d(e=>e.pr?.status===z.closed,"unlanded"),An=d(e=>e.some(t=>t.kind==="work")&&e.every(t=>t.lane==="done"),"finishedOf");function ot(e){const t=xt(e);return t?.done&&t.handed_back?t.handed_back:null}d(ot,"handedBackAt");const xt=d(e=>e.filter(t=>t?.started&&V(t)&&t.kind!==fe.install&&t.kind!==ge).sort((t,n)=>String(t.started).localeCompare(String(n.started))).at(-1),"newestBuild");function Bn(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(Bn,"reviewOf");function _n(e,t,n,s,r,a,{held:c,needsApproval:i}){return n?.kind===fe.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"?Ot(t,i)?"merge":"reviewers":null}d(_n,"waitsOnOf");const Ot=d((e,t)=>e?.reviewDecision==="APPROVED"||!t&&!["REVIEW_REQUIRED","CHANGES_REQUESTED"].includes(e?.reviewDecision),"reviewed"),Ne=/(^|\/)prototype-[\w-]+\.html/,Rn={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"},rt={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"},Mn=d(e=>`Verified: tap Merge to land it on ${e}`,"heldNext"),In="Fix the failing CI",qn="Your reply waits on the orchestrator",Cn="Tap Done",Dn=["status","agent","ask-link","command",$e,De.answer],Nn=d(e=>(e.who==="claude"||e.via==="github")&&!Dn.includes(e.kind)&&(e.kind!==Qe||kt(e).fault),"countsUnread"),Tn=["lander-handoff","land-waiting"];function Ln({slug:e,itemFile:t,it:n,order:s,prs:r,repo:a,remote:c,base:i,builder:u,reachedPhase:h,agents:f,worktree:p,eta:k,thread:S,events:A,working:v,said:P,askedAt:D,topicItems:j,prSlack:N,boardReviewers:W,now:w,bumpedAt:b,holdAsk:C,closedAt:l,mergeAt:B,autoMerge:me,skipDraft:he,tickets:Q}){const G=r.map(g=>({repo:g.repo,number:g.number,...g.gh||g.kept||{},url:(g.gh||g.kept)?.url||pn(g),status:$n(g.gh,g.merged_at)})),ne=cn(r.map(g=>g.gh)),q=r[ne]?.gh||null,H=G[ne]?.status??null,ze=n.lane||null,F=!!l&&String(l)>String(n.lane_at||"");F&&(n={...n,lane:"done",closed_at:l,reopened_at:void 0});const T=[n.closed_at,...r.flatMap(g=>[g.merged_at,g.gh?.mergedAt])].filter(Boolean).map(String).sort().pop()||null,we=String(T||""),ye=G.length>0&&G.every(g=>g.status===z.closed),be=(n.lane==="done"||n.reopened_at||H===z.merged)&&!ye&&([...P.filter(g=>g.kind==="item-message").map(g=>g.ts),...D].map(String).filter(g=>g>we).sort().pop()||n.reopened_at)||void 0,Te=n.answered_at&&!n.phase&&!n.closed_at&&!be?String(n.answered_at):void 0,K=be||Te,Re=n.owner_step&&!F?String(n.owner_step):null,Je=(n.lane==="done"||H===z.merged)&&!ye,X=!!K&&!!n.next&&String(n.next_at||"")>String(n.reopened_at||T||"");K&&!X&&(n={...n,next:void 0});const J=ot(f),Le=K&&P.filter(g=>String(g.ts)>we&&(g.state==="waiting"||g.state==="working")).map(g=>String(g.ts)).sort()[0]||null,Se=!!Le,ve=`${e}/${n.id}`,xe=sn(t,W),Pe=Object.fromEntries(Object.entries(tn(t,n,ve,j,en(N,r))).map(([g,Y])=>[g,nn(Y.ts)])),se=Object.values(Pe).filter(Boolean).sort()[0]||null,Ue=[...S.map(g=>g.ts),...A.map(g=>g.ts)].sort().pop()||null,je=[...S.filter(g=>Nn({...g,who:g.who||"claude"})).map(g=>g.ts),...A.map(g=>g.reply?.at)].filter(Boolean).sort(),Z=!tt(n)&&n.pushed?.branch?{branch:n.pushed.branch,at:n.pushed.at||null,host:c?.host||null}:null,ee=!tt(n)&&n.merge_ready?.branch?{branch:n.merge_ready.branch,at:n.merge_ready.at||null,base:i}:null,Me=!!n.rebase_run&&q?.state==="OPEN",Ie=(n.rebased||[]).at(-1),de=f.filter(g=>g.started&&!g.done&&V(g)),Ze=!!J&&St(xt(f));let E=En(n,q,de.some(St),Ze,Z||ee,!!K,he,Me,de.length>0||!!J);K&&E==="todo"&&(E="progress");const We=E==="review"&&!!se&&w-Date.parse(se)>yt,ce=!!Re&&Je&&E==="done";ce&&(E="review");let L=_n(E,q,u,!!J&&E!=="done",Z,he,{held:ee,needsApproval:xe.length>0});const Fe=L==="builder"&&q?.state==="OPEN"&&!q.isDraft,qe=E==="progress"&&!L&&q?.state==="OPEN"&&(!q.isDraft||he),Oe=ce||!!K&&E==="progress"&&!L&&!qe&&!Se&&!X;Oe&&(L="owner");const o=!!K&&f.some(g=>V(g)&&String(g.started||"")>String(K)),m=E==="progress"&&!L&&(qe||!!K&&!o);m&&(E="review");const O=C&&String(C.ts)>String(n.held_at||""),$=E==="todo"&&(O?!!C.data.hold:!!n.held),y=$&&(O?C.data.until:n.pick_up_at)||null,I=E!=="done",_=!!b&&!(we&&String(b)<=we),Ke=S.filter(g=>(g.who||"claude")==="claude"&&!["status","command",$e].includes(g.kind)&&!Tn.includes(g.line)).map(g=>String(g.ts)).sort().pop()||"",ue=[B,ee&&n.merge_ready.tapped].filter(Boolean).map(String).sort().pop(),Ce=L==="merge"&&!!ue&&ue>Ke,Ee=S.filter(g=>(g.kind==="status"||g.kind===Qe)&&String(g.ts)>String(ue)).sort((g,Y)=>String(g.ts).localeCompare(String(Y.ts))).at(-1),oe=Ee?.line==="land-waiting"?`${Ve.merging} \xB7 ${Ee.text[0].toLowerCase()}${Ee.text.slice(1)}`:Ve.merging,te=S.filter(g=>!["status",$e].includes(g.kind)).sort((g,Y)=>String(g.ts).localeCompare(String(Y.ts))).at(-1),x=E==="todo"&&te?.line===wt?te.text:null,pe=K||f.filter(V).map(g=>g.started).filter(Boolean).map(String).sort().pop()||null,Ge=q?.reviewDecision==="APPROVED",Ht=Z&&L==="owner"?`merge ${Z.branch} into ${i}`:ee&&L==="merge"?Ce?oe:Mn(i):L==="merge"&&!me?Ce?oe:(Ge?Ve:rt).waiting:L==="merge"&&!Ge?rt.auto:Rn[L],Vt=H===z.draft?`Review draft PR #${G[ne].number}`:null,Xt=!Me&&Ie?.to&&Ie.to===q?.headRefOid?`Rebased onto ${Ie.base||i}, pushed${q.mergeable==="MERGEABLE"?" \xB7 PR is clean again":""}`:null,Yt=Me&&L==="checking"?f.flatMap(g=>g.calls).sort((g,Y)=>String(g.at).localeCompare(String(Y.at))).at(-1)?.text??null:null,et=E==="done"?null:_t.find(g=>(n.briefs||[]).some(Y=>Y.kind===g&&!Y.brief))??null;return{key:ve,kind:"work",order:s,file:e,fileTitle:t.title||e,id:n.id,ref:st(t,n,Q),title:n.title,storedLane:ze,created:n.created||null,reviewSince:m&&K||n.review_since||se||n.merge_ready?.at||n.pushed?.at||n.created||null,progressSince:pe,closedAt:E==="done"?T:null,internal:t.internal===!0,repo:t.internal===!0&&!t.repo?null:a,lane:E,waitsOn:L,builder:L==="checking"?null:u,reachedPhase:h,agents:f,worktree:p??null,eta:k,working:v,reopened:!!be,answered:!!Te,awaitsDone:Oe,ownerStep:Oe?Re:null,ownerCheck:Oe&&Re&&n.owner_check?{words:fn(gn(n.owner_check.check)),broken:n.owner_check.broken||null}:null,stagedAt:E==="todo"&&n.phase==="staging"&&n.staged_at||null,deps:n.deps||[],blockedUntil:n.blocked_until||null,blockReason:n.block_reason||null,held:$,heldUntil:y,risky:!!n.risky,bumpable:I,bumpedAt:I&&_?b:null,holdable:E!=="done",hasNotes:!!(n.plan||String(n.notes||"").trim()),finishable:E!=="done",merging:Ce,refusal:x,manualSteps:n.manual_steps||[],fromAsk:n.from_ask||null,unfiled:!!n.unfiled,filedHeld:!!n.filed_held,next:Fe||E==="done"||L==="checking"?null:m&&Se?qn:qe?In:Xt||Ht||n.next||Vt||null,rebaseFailed:Yt,branch:n.branch||q?.headRefName||Z?.branch||null,pushed:E==="done"||ce?null:Z,mergeReady:E==="review"?ee:null,stale:E!=="done"&&(We||!!u?.stale),stalled:n.stalled?.reason||null,implementReady:!!n.implement_ready,landStopped:E==="review"&&ee&&n.merge_ready.stopped&&String(n.merge_ready.stopped)>String(n.merge_ready.tapped||"")?{words:Rt("land"),at:n.merge_ready.stopped}:null,ticket:Q?.source==="linear"&&!n.ticket&&n.linear?.length?null:On(Q,n.ticket,{repo:a}),ticketSource:Q?.source||null,linear:(n.linear||[]).map(g=>Q?.source==="linear"&&ft(Q,g,{repo:a})||bt(g,Q)),pr:G[ne]||null,prs:G,reviewers:xe.map(g=>g.name),review:Bn(xe,q||r[ne]?.kept,Pe),inReviewSince:se,threadCount:S.length+A.length,lastTs:Ue,othersAt:je,grilling:Mt("grill",A,f,D),prototyping:Mt("prototype",A,f,D),briefs:E==="done"?[]:n.briefs||[],briefing:et,planning:et?es[et]:f.some(g=>g.kind===U&&g.started&&!g.done)?Bt[U]:null,storedPhase:n.phase||null,phase:null,recheckAt:n.recheck_at||null,turn:E==="todo"&&n.phase==="discussing"?P.some(g=>g.state==="waiting"||g.state==="working")?"claude":"owner":void 0,updatedAt:q?.mergedAt||q?.updatedAt||r[ne]?.merged_at||u?.at||Ue||null,ask:null,fold:null,action:null,install:E==="done"?null:Un(n.install,q),waitFacts:Pn(n,{agents:f,thread:S,events:A,refusedAt:x?te.ts:null,replyAt:m?Le:null})}}d(Ln,"workItem");function Pn(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)),i=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=>lt(p,f)).pop()?.ts||null,"requestAt"),h=n.filter(f=>(f.who||"claude")==="claude"&&!f.command&&!["status","agent",$e].includes(f.kind)&&f.line!==wt).map(f=>f.ts);return{handedBackAt:ot(t),next:e.next||null,ask:i?{at:i.ts,text:i.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:[...h,...t.flatMap(f=>[f.verified?.at,f.started])].filter(Boolean).map(String).sort()}}d(Pn,"waitFactsOf");function Un(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(Un,"installOf");function jn(e,{needsApproval:t=!0}={}){if(e?.state!=="OPEN")return[`PR is ${String(e?.state||"unknown").toLowerCase()}`];const{state:n,pending:s,failing:r}=e.checks||{};return[e.isDraft&&"it is a draft",!Ot(e,t)&&`review: ${String(e.reviewDecision||"none").toLowerCase().replace(/_/g," ")}`,n==="fail"&&`failing: ${r?.length?r.join(", "):"a check"}`,n==="pending"&&`pending: ${s?ie(s,"check"):"checks"}`,e.mergeStateStatus!=="CLEAN"&&`merge state: ${String(e.mergeStateStatus||"unknown").toLowerCase()}`].filter(Boolean)}d(jn,"mergeBlockers");const Wn=d((e,t)=>!jn(e,{needsApproval:t}).length,"mergeable");function Fn(e,t,n){if(ae(e))return null;const s=`#selectedCard=${encodeURIComponent(e.key)}`;if(e.stalled)return{kind:M.stalled,url:s,label:`stalled: ${e.stalled}`};if(Kn(e)){const r=t.get(e.questions.first);return Ne.test(r?.url||"")?{kind:M.pick,url:`questions.html#${r.id}`,label:"Prototypes ready: pick a take"}:{kind:M.questions,url:`questions.html#${e.questions.first}`,label:`Answer ${ie(e.questions.open,"question")}`}}return e.implementReady&&e.lane==="todo"?{kind:M.implement,url:`${s}&reply`,label:"Tap Implement"}:e.waitsOn==="ready"?{kind:M.ready,url:e.pr.url,label:`Review draft PR #${e.pr.number}`}:e.waitsOn==="merge"&&e.manualSteps.length&&!e.mergeReady?{kind:M.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)||Wn(r,e.reviewers.length>0))?{kind:M.merge,url:s,label:"Merge"}:null}d(Fn,"actionOf");const Kn=d(e=>!!(e.questions&&e.questions.state!=="resolved"&&e.questions.open>0),"asking"),at=new Set([M.pick,M.questions]);function Et(e){return e.questions?re.openQuestions:null}d(Et,"actionButtonOf");const Gn=d(e=>e.kind==="work"&&e.lane==="todo"&&!e.action&&!e.unpicked,"buildableOf"),Qn=d((e,t)=>/^item:/.test(e.topic||"")&&Ne.test(e.url||"")&&!(e.options||[]).some(n=>n.id===t),"picksNoTake");function Hn(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:Qn(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(Hn,"noTakePicked");const Vn="Prototype again",Xn=Object.freeze({[M.pick]:re.openQuestions,[M.questions]:re.openQuestions,[M.ready]:re.github,[M.steps]:re.github,[M.merge]:re.merge,[M.stalled]:re.reply,[M.implement]:re.implement}),Yn="Merges once unblocked",zn=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 Jn(e,t,n){if(e.action=Fn(e,t,n),e.mergeBlocked=e.action?.kind===M.merge&&e.blocked?zn(e):null,e.mergeBlocked&&Object.assign(e,{action:null,next:Yn}),e.action?.kind===M.questions&&e.questions.fromAsks){Object.assign(e,{action:null,waitsOn:"owner",next:e.action.label,actionButton:Et(e),resolvedBy:null});return}e.awaitsDone&&=!e.action,e.awaitsDone||Object.assign(e,{ownerStep:null,ownerCheck:null}),e.actionButton=Et(e),e.resolvedBy=Xn[e.action?.kind]||null,e.action?.kind===M.merge&&!e.mergeReady&&(e.next=(e.pr?.reviewDecision==="APPROVED"?Ve:rt).ready),e.awaitsDone&&Object.assign(e,{lane:"review",next:e.ownerStep||Cn,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"&&(at.has(e.action.kind)||e.action.kind===M.implement)?"todo":"review";Object.assign(e,{lane:r,waitsOn:e.waitsOn==="ready"?"ready":"owner",next:e.action.label})}d(Jn,"placeOwnerWait");const $t=d((e,t)=>e?e!=="todo":t.some(V),"started"),At="none set yet";function Oo(e,t){const n=t.filter(s=>s?.started);return e.next||tt(e)||e.pushed||e.held||e.lane==="done"||ot(n)?!1:!n.some(s=>!s.done)&&$t(e.lane,n)}d(Oo,"owesNext");const U="visual",ge="helper",Zn=new Set(["grill","prototype",U]),le=d(e=>Zn.has(e.kind),"plans"),V=d(e=>!le(e),"builds"),Bt={grill:"grilling",prototype:"prototyping",[U]:"generating visual"},_t=["grill","prototype",U],es={grill:"preheating",prototype:"planning prototype",[U]:"planning visual"},ts=d((e,t)=>`${e} ${t} by orchestrator`,"orchestratorDid"),Rt=d(e=>ts(e,"stopped"),"stoppedWords"),ns={grill:"grill-more",prototype:"prototype-request"},lt=d((e,t)=>e.kind===ns[t]&&!e.data?.question,"isRequest");function Mt(e,t,n,s){if(n.some(a=>a.kind===e&&a.started&&!a.done))return!0;const r=t.filter(a=>lt(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(Mt,"planningOf");const Eo=30*6e4;function $o(e,t){const n=[];for(const s of e.items){if(ae(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&&lt(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($o,"prototypeWaits");function it(e={},t=!1){return Object.entries(e).filter(([n,s])=>s&&typeof s=="object"&&(t||!n.startsWith("past-"))).map(([n,s])=>({name:n,kind:It(s.kind)||null,model:s.model||(s.kind==="opus"||s.kind==="sonnet"?s.kind:null),label:s.label||null,started:s.started||null,eta:s.done?null:s.eta||null,done:s.done||null,log:s.session?.log||null,pid:s.session?.pid??null,calls:Array.isArray(s.calls)?s.calls:[],verified:s.verified||null,stopped:s.stopped||null,handed_back:s.handed_back||null,...s.pause?{pause:s.pause}:{},...s.kind===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(it,"agentsOf");function dt(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(dt,"worktreeOf");function ss(e,t=[]){const n=e.filter(V).map(s=>String(s.started||"")).filter(Boolean).sort().pop();return n===void 0?null:t.filter(s=>s.kind==="status"&&s.phase&&!s.planner&&String(s.ts)>=n).sort((s,r)=>String(s.ts).localeCompare(String(r.ts))).pop()?.phase||null}d(ss,"reachedPhaseOf");function os(e,t=[],n,s=null,r=null,a=!1){const c=e.filter(b=>b.started&&!b.done&&b.kind!==U),i=c.every(le),u=i?c:c.filter(V),h=u.map(b=>String(b.started||"")).sort()[0];if(h===void 0)return null;const f=t.filter(b=>b.kind==="status"&&String(b.ts)>=h&&(i||!b.planner)).sort((b,C)=>String(b.ts).localeCompare(String(C.ts))).pop(),p=u.filter(b=>b.kind!==ge).at(-1),k=u.some(b=>b.kind===fe.install)?fe.install:p?It(p.kind):"claude",S=i?null:u.find(b=>b.stopped&&String(b.stopped.at)>=String(b.started))?.stopped,A=f?.ts||h,v=S?S.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?Bt[k]:null,D=n-Date.parse(v),j=i?null:u.find(b=>b.pause)?.pause??null,N=!i&&!S&&(r==="held"||!!j?.paused),W=i||N?null:S?!1:r,w=N?!1:W===null?D>=yn:!W;return{progress:P,phase:f?.phase||null,at:A,kind:k,planner:i,silentSince:v,stale:w,alive:W,held:N,quiet:!!j||W===!0&&!a&&D>=bn,stopped:S?Rt(S.run):null,...j?{pause:{cause:j.cause,words:j.words,paused:!!j.paused}}:{}}}d(os,"builderOf");const Ao={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"]},rs=15*6e4,as=d(e=>(e.agents||[]).find(t=>t.name==="main"&&t.started&&!t.done&&V(t))??null,"mainBuild"),ls=d((e,t,n,{now:s,usageHeld:r})=>e.blocked||e.held||r(n??"claude")||!!(t.until&&s<Date.parse(t.until)),"stillPaused");function is(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=as(a);if(!c)continue;const{pause:i}=c;if(!i){a.builder?.phase!=="landing"&&n(c.kind??"claude")&&s.ask.push(a.key);continue}const u=ls(a,i,c.kind,{now:t,usageHeld:n});if(i.paused){u||r.push({key:a.key,at:i.paused});continue}!u&&i.cause!=="bumped"?s.drop.push(a.key):t-Date.parse(i.at)>=rs&&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(is,"pauseWork");function Bo(e,t,n){const s=is(t,n).drop;return e.filter(r=>s.includes(r))}d(Bo,"confirmedDrops");const ct={opus:"Claude Opus",sonnet:"Claude Sonnet",codex:"Codex"},ds=d(({model:e,kind:t})=>ct[e]||ct[t]||(t==="claude"?ct.opus:null),"modelName"),It=d(e=>e==="opus"||e==="sonnet"?"claude":e,"providerOf");function Ae(e){const t=ds(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&&le(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 qt=d((e,t)=>!!e.resolved&&(!e.resolved_at||String(e.resolved_at)>=String(t)),"resolvedSince");function cs(e,t,n=[],s=[],r=[]){const a=t.filter(v=>v.data?.id===e.id),c=a[a.length-1]||null,i=r.filter(v=>v.data?.question===e.id).at(-1),u=d((v,...P)=>!!v&&P.every(D=>String(v.ts)>String(D||"")),"after"),h=!!(e.resolved&&e.resolved_at&&c&&String(c.ts)>e.resolved_at),f=n.filter(v=>v.data?.question===e.id&&u(v,c?.ts,i?.ts)).at(-1),p=u(i,c?.ts,f?.ts,e.resolved_at),k=e.resolved&&!h&&!p,S=k?s.filter(v=>v.data?.question===e.id&&String(v.ts)>String(e.resolved_at||"")).at(-1):null,A=p?"open":k?"resolved":f?"requested":c?c.reply?"replied":"answered":"open";return{...e,state:A,reopened:p,request:f?{id:f.id,ts:f.ts,note:f.data.note||"",choice:f.data.choice||null}:null,reask:S?{id:S.id,ts:S.ts}:null,answers:a.map(v=>({ts:v.ts,choice:v.data.choice||null,note:v.data.note||"",files:v.data.files||[],reply:v.reply,replies:v.replies,acted:qt(e,v.ts)}))}}d(cs,"ownerQuestion");const ke=d(e=>e.round??(Ne.test(e.url||"")?1:0),"roundOf");function us(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(us,"roundSettledBy");function ps(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=us(e,n);return s?{n,state:"closed",take:s.choice}:e.some(r=>ke(r)===n&&r.state!=="resolved")?{n,state:"back"}:null}d(ps,"roundState");function fs(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(fs,"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 gs(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 i=a.get(c.id)||[],u=d(p=>i.filter(k=>k.state===p).length,"count"),h=i.every(p=>p.state==="resolved")||n.has(c.id)&&!u("open"),f=i.length&&h?"resolved":"open";return{...c,state:f,questions:i.map(p=>p.id),open:u("open"),answered:u("answered"),replied:u("replied"),resolved:u("resolved"),round:ps(i)}}).filter(c=>c.questions.length)}d(gs,"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"},Ct={"grill-more":"grill","prototype-request":"prototype","visual-request":"visual","reask-request":"reask"},Dt={"mark-ready":"Mark ready",...Object.fromEntries(Object.entries(Ct).map(([e,t])=>[e,Xe[t]]))},ks=["grill-more","prototype-request","visual-request"],ms="answers submitted",Nt={reply:"Reply",build:"Implement"},hs=d(e=>Nt[e.data.intent],"intentLabel"),ws=d((e,t)=>{if(e.kind!=="prototype-request"||!t.has(e.data.question))return e.data.text||(e.data.files?.length?"":R(Dt,e.kind))||"";const n=t.get(e.data.question),s=(n.options||[]).find(r=>r.id===e.data.choice);return[Lt(t,n),s&&`${s.id}. ${s.label}`,e.data.note].filter(Boolean).join(`
2
+ \u2192 `)},"actionText"),Tt=d((e,t)=>[...e.keys()].indexOf(t)+1,"numberOf"),Lt=d((e,t)=>`Question ${Tt(e,t.id)} \xB7 ${t.question}`,"numbered"),ys="question reopened",Ye=d(e=>e.data?.shared_by?.login?{by:e.data.shared_by.name||e.data.shared_by.login}:{},"byOf"),bs=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"),Pt=d(e=>({kind:"command",command:e,request:!0,mark:Xe[e]}),"commandLine"),Ss=d((e,t)=>{if(e.kind==="item-message")return Nt[e.data.intent]?{status:hs(e)}:{};const n={request:!0,command:Ct[e.kind]};return ks.includes(e.kind)?(e.data.question?t.has(e.data.question):String(e.data.text||"").trim()||e.data.files?.length)?{status:Dt[e.kind]}:n:e.kind==="reask-request"?n:{}},"requestOf"),Ut=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 jt(e,t={}){const n=new Map,s=new Map;for(const p of t.items||[])Be(n,p.topic,p),s.set(p.id,p.topic);const r=new Map,a=new Map,c=new Map,i=new Map,u=new Map,h=new Map,f=new Map;e.forEach((p,k)=>{f.set(p,k),p.data?.key&&wn.includes(p.kind)&&Be(r,p.data.key,p),p.kind==="owner-answer"&&s.has(p.data?.id)&&Be(a,s.get(p.data.id),p),p.kind===ht&&s.has(p.data?.question)&&Be(c,s.get(p.data.question),p),p.kind==="ask"&&i.set(p.id,p)});for(const p of e.filter(k=>k.kind==="ask-edited").sort((k,S)=>String(k.ts).localeCompare(String(S.ts)))){u.set(p.data?.id,p.data?.text);for(const k of p.data?.files||[])Be(h,p.data?.id,k)}return{byKey:r,answers:a,reopens:c,asks:i,edits:u,editFiles:h,questions:n,order:f}}d(jt,"threadIndex");const ut=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"),vs=/; its log is \S*\/\.claude\/kickflip\/logs\/([^/\s]+\.log)\.$/,xs=d(e=>{const t=!e.log&&typeof e.text=="string"?vs.exec(e.text):null;return t?{...e,text:`${e.text.slice(0,t.index)}.`,log:t[1]}:e},"withLog");function Os(e,t){const n=t.run?e.find(s=>s.started===t.run):e.filter(s=>le(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:pt(e,n),running:!n.done}:{}}d(Os,"agentOfLine");function pt(e,t){return t.label!==He?t.started:e.filter(n=>V(n)&&n.label!==He&&n.started&&String(n.started)<String(t.started)).pop()?.started??t.started}d(pt,"sectionOf");function Es(e,{lines:t=[],events:n=[],owner:s={},working:r={},fromAsk:a=null,sorted:c=!1,links:i=[],agents:u={},moved:h=[],cards:f=new Map,index:p=jt(n,s)}){const k=e.startsWith("ask:")?[e.slice(4)]:[a,...h].filter(Boolean),S=[...new Set([e,...k.map(l=>`ask:${l}`)])],A=new Map((p.questions.get(`item:${e}`)||[]).map(l=>[l.id,l])),v=p.answers.get(`item:${e}`)||[],P=new Set([...A.values()].map(l=>l.round_of).filter(Boolean)),D=it(u,!0).filter(V).map(l=>String(l.started||"")).filter(Boolean),j=new Set(v.filter(l=>qt(A.get(l.data.id),l.ts)||P.has(l.data.id)||D.some(B=>B>String(l.ts))).map(l=>l.id)),N=it(u,!0),W=[...N.flatMap(l=>[...l.started?[{ts:l.started,who:"claude",kind:"agent",text:`${Ae(l)} started`,agent:Ae(l),run:pt(N,l),finished:!1,eta:l.done?null:l.eta,...l.log?{log:l.log}:{},...le(l)?{planner:!0}:{}}]:[],...l.done?[{ts:l.done,who:"claude",kind:"agent",text:`${Ae(l)} finished`,agent:Ae(l),run:pt(N,l),finished:!0,eta:null,...le(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(xs).map(l=>l.card?bs(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}:{},...Os(N,l)}:{},...l.kind===U||l.kind===$e?{kind:l.kind}:{},...l.kind===Qe?{kind:Qe,line:l.line,...kt(l),...l.detail?{detail:l.detail}:{}}:{},...l.command?Pt(l.command):{},...l.card?{card:l.card}:{},...Object.values(De).includes(l.kind)?{kind:l.kind,ask:l.ask}:{},text:l.text||"",html:l.html||null,link:l.link||null,via:l.via||null,...l.by?{by:l.by}:{}})),...ut(p,S.map(l=>p.byKey.get(l))).map(l=>({ts:l.ts,who:l.data.who==="claude"?"claude":"love",via:"site",kind:l.kind,text:ws(l,A),files:l.data.files||[],eventId:l.id,reply:l.reply,replies:l.replies,...Ss(l,A),...Ye(l)})),...v.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:`${Lt(A,A.get(l.data.id))}
3
+ \u2192 ${B}`,files:l.data.files||[],eventId:l.id,reply:l.reply,replies:l.replies,status:ms,...Ye(l)}}),...(p.reopens.get(`item:${e}`)||[]).map(l=>({ts:l.ts,who:"love",kind:l.kind,request:!0,mark:ys,text:`Question ${Tt(A,l.data.question)} was reopened`,...Ye(l)})),...ut(p,[...new Set(k)].map(l=>p.asks.has(l)?[p.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,...h.includes(l.id)?{moved:!0}:{},...Ye(l),...typeof l.data?.from?.title=="string"?{from:l.data.from.title}:{}},...l.data?.quick===!0?[{ts:l.ts,who:"love",...Pt("implement"),text:Xe.implement}]:[],...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=Ut(W),{edits:b,editFiles:C}=p;for(const l of W){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||c&&l.eventId===a);l.state=l.reply?"replied":B||j.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 $s(W)}d(Es,"threadOf");const Wt=["waiting","working","answered","replied"];function $s(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 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(`
5
+ `),files:s.flatMap(r=>r.files),state:s.map(r=>r.state).sort((r,a)=>Wt.indexOf(r)-Wt.indexOf(a))[0],reply:void 0,answers:s.map(r=>({eventId:r.eventId,state:r.state,reply:r.reply,replies:r.replies,text:r.text}))}]})}d($s,"foldSubmits");const Ft=d(e=>(e.answers||[e]).filter(t=>t.eventId).map(t=>[t.eventId,t.state]),"stateEntries");function As(e,t,n,s,r,a){const c=d((p,k)=>{const S=a.filter(v=>v.kind==="wave-triage"&&v.data?.[p]===k).pop(),A=r.filter(v=>v.topic===`${p}:${k}`&&(v.state==="open"||v.state==="answered")).length;return{running:!!(S&&!S.reply),questions:A}},"triageOf"),i=new Map(s.map(p=>[p.key,p])),u=d(p=>p.filter(k=>k.lane==="done"&&!vt(k)).length,"merged"),h=n.map(p=>({l:p,cards:p.keys.map(k=>i.get(k)).filter(Boolean)})).map(p=>({...p,open:!p.l.finished_at&&p.cards.some(k=>k.lane!=="done")})).sort((p,k)=>Number(p.open)-Number(k.open));for(const{l:p,cards:k}of h)for(const S of k)S.wave={slug:p.slug,title:p.title};const f=h.filter(p=>p.open).map(({l:p,cards:k})=>({kind:"wave",slug:p.slug,title:p.title,goal:p.goal,keys:k.map(S=>S.key),next:Bs(k),merged:u(k),total:k.length,triage:c("wave",p.slug)}));return e.fromWaves?[...f,...t.filter(p=>!p.finished).map(p=>({w:p,cards:s.filter(k=>k.file===p.slug&&k.kind!=="ask")})).filter(({cards:p})=>p.length).map(({w:p,cards:k})=>({kind:"file",slug:p.slug,title:p.title,goal:null,keys:k.map(S=>S.key),next:null,merged:u(k),total:k.length,triage:c("file",p.slug)}))]:f}d(As,"waveBandsOf");function Bs(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(Bs,"nextOf");const _s=d(e=>e.items||e.phases.flat(),"waveItems");function _o(e){const{owner:t={},topicItems:n={},events:s=[],threads:r={},prs:a={},working:c={}}=e,i=e.now??Date.now(),u=jt(s,t),h=new Set(s.filter(o=>o.kind==="ask-withdrawn").map(o=>o.data?.id)),f=new Map(s.filter(o=>o.kind==="ask"&&!h.has(o.id)).map(o=>[o.id,o])),p=d((o,m)=>new Map(s.filter(O=>O.kind===o).sort((O,$)=>String(O.ts).localeCompare(String($.ts))).map(O=>[O.data?.[m],O.ts])),"latest"),k=p("card-unbump","key"),S=new Map([...p("card-bump","key")].filter(([o,m])=>!(String(k.get(o)||"")>String(m)))),A=p("card-done","key"),v=p("card-merge","key"),P=e.autoMerge===!0,D=e.tickets,j=new Map(s.filter(o=>o.kind==="card-hold").sort((o,m)=>String(o.ts).localeCompare(String(m.ts))).map(o=>[o.data?.key,o])),N=new Map,W=new Map(e.files.flatMap(({slug:o,itemFile:m})=>m.items.map(O=>[`${o}/${O.id}`,{ref:st(m,O,D),title:O.title}]))),w=e.askLinks||{},b=new Map;for(const[o,m]of Object.entries(w))f.has(o)&&b.set(m,[...b.get(m)||[],o]);const C=[],l=[],B=new Map,me=new Map,he=new Map,Q=new Map,G=e.github?null:on(e.remote);for(const{slug:o,itemFile:m,agents:O={}}of e.files){const $=e.github||m.repo||null;C.push({slug:o,title:m.title||o,status:m.status||null,internal:!!m.internal,finished:!1,reviewers:(m.reviewers||[]).map(y=>y.name)}),m.items.forEach((y,I)=>{const _=`${o}/${y.id}`,Ke=(b.get(_)||[]).filter(x=>x!==y.from_ask),ue=[y.from_ask,...Ke].filter(x=>f.has(x)),Ce=new Set(ue.map(x=>`ask:${x}`)),Ee=[...R(r,_)||[],...ue.flatMap(x=>R(r,`ask:${x}`)||[])];y.from_ask&&N.set(y.from_ask,[...N.get(y.from_ask)||[],{key:_,ref:st(m,y,D),became:!0}]);const oe=Es(_,{lines:Ee,events:s,owner:t,working:c,fromAsk:y.from_ask||null,sorted:!!y.phase,moved:Ke,cards:W,index:u});for(const x of oe)for(const[pe,Ge]of Ft(x))B.set(pe,Ge);me.set(_,Ut(oe)),l.push(Ln({slug:o,itemFile:m,it:y,order:I,repo:$,remote:G,base:e.branch||"main",prs:dn(y,$).map(x=>({...x,gh:R(a,un(x))||null})),...(x=>({agents:x,builder:os(x,R(r,_),i,e.moved?.[dt(R(O,y.id))]??null,R(e.alive,_)??null,!!e.commandRunning?.[dt(R(O,y.id))]),reachedPhase:ss(x,R(r,_)),eta:x.map(pe=>pe.eta).filter(Boolean).sort().pop()||null}))(it(R(O,y.id))),worktree:dt(R(O,y.id))??y.worktree?.path??null,thread:Ee,events:[...ut(u,[_,...Ce].map(x=>u.byKey.get(x))),...ue.map(x=>f.get(x))],working:oe.some(x=>Ft(x).some(([,pe])=>pe==="working")),said:oe.filter(x=>x.who==="love"&&(x.kind==="item-message"||x.kind==="owner-answer")),askedAt:(u.questions.get(`item:${_}`)||[]).filter(x=>x.asked_at).map(x=>x.asked_at),topicItems:n,prSlack:e.prSlack||{},boardReviewers:e.reviewers||[],now:i,bumpedAt:S.get(_),holdAsk:j.get(_),closedAt:A.get(_),mergeAt:v.get(_),autoMerge:P,skipDraft:e.skipDraft===!0,tickets:D}));const te=l[l.length-1];f.has(y.from_ask)&&(te.created=f.get(y.from_ask).ts),te.askMsg=oe.find(x=>x.kind==="ask"&&x.eventId===y.from_ask)||null,Q.set(_,{parent:y.parent?Gt(o,y.parent):null,related:(y.related||[]).map(x=>Gt(o,x))}),te.lane==="done"&&he.set(_,te.closedAt||String(te.updatedAt||""))})}const ne=new Map(l.map(o=>[o.key,o])),q=new Map,H=e.try||{},ze=e.autoBuild===!0,F=["web","app"].includes(H.kind)?H.kind:e.files.some(({itemFile:o})=>o.items.some(m=>m.install?.url))?"app":"web";for(const o of l){if(o.kind!=="work"||o.lane==="done")continue;const m=R(H.cards,o.key)||{},O=m.head||o.pr?.headRefOid,$=F==="web"?m.sha:o.install?.sha,y=o.builder?.kind===fe.install?"running":m.state||(F==="app"&&o.install?"ready":null);o.try={kind:F,state:y,stale:!!($&&O&&!String(O).toLowerCase().startsWith(String($).toLowerCase())),url:F==="web"?null:o.install?.url||null,port:F==="web"&&y==="ready"&&m.https||null,sha:$||null,changes:m.ahead>0,script:m.script===!0,autoBuild:ze,hostBusy:!!e.hostBusy}}const T=H.main,we=T?.script&&T.sha?{slug:T.slug,branch:T.branch||"main",sha:T.sha.slice(0,7),at:T.at,kind:F,state:T.state||null,url:F==="web"?null:e.main?.install?.url||null,port:F==="web"&&(T.state==="ready"?T.https:T.serving?.https)||null,serving:F==="web"&&T.serving?{sha:T.serving.sha.slice(0,7),at:T.serving.at}:null,pull:H.pull?{running:H.pull.running===!0,error:H.pull.error||null}:null,script:kn(T.target)}:null,ye=[],be=[],Te=_e(s.filter(o=>o.kind==="owner-answer"),o=>o.data?.id),K=_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===ht),o=>o.data?.question),X=(t.items||[]).map(o=>cs(o,Te.get(o.id)||[],K.get(o.id)||[],Re.get(o.id)||[],Je.get(o.id)||[])),J=new Map(X.map(o=>[o.id,o])),Le=_e(X,o=>o.topic);for(const o of f.values()){const m=`ask:${o.id}`,O=R(r,m)||[],$=N.get(o.id)?.[0].key||R(w,o.id);if(!$){O.some(_=>_.line==="ask-moved")||be.push(o.id);continue}q.set(m,$);const y=O.filter(_=>_.line===mn).at(-1),I=ne.get($);if(y&&I){const _=y.title||String(u.edits.get(o.id)??o.data?.text??"").trim().split(`
6
+ `)[0]||"Ask";ye.push({key:I.key,ref:I.ref,text:_,at:String(y.ts)})}}for(const o of C){const m=l.filter(O=>O.file===o.slug);if(o.finished=An(m),o.finished)for(const O of m)O.lane==="done"&&(O.fold=vn(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:m})=>(m.waves||[]).map(O=>({...O,keys:_s(O).map($=>`${o}/${$}`)}))),xe=d(()=>gs([...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."}))],X,new Set(l.filter(ae).map(o=>`item:${o.key}`))),"topicsOf"),Pe=new Map(xe().map(o=>[o.id,o]));for(const o of l){const m=Pe.get(`item:${o.key}`);if(!m){o.questions=null,o.prototypes=null;continue}const O=m.questions.filter(I=>J.get(I)?.state!=="resolved"),$=m.questions.find(I=>J.get(I)?.state==="open")||O[0]||m.questions[0],y=m.questions.map(I=>J.get(I)).filter(I=>I?.state==="open");o.questions={topic:m.id,state:m.state,open:m.open,answered:m.answered,replied:m.replied,resolved:m.resolved,first:$,askedAt:y.map(I=>I.asked_at).filter(Boolean).sort(),fromAsks:y.length>0&&y.every(I=>I.ask)},o.prototypes=fs(m.questions.map(I=>J.get(I)))}const se=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 m=o.kind==="work"&&o.lane!=="done",O=new Set(o.deps.flatMap(y=>Ue.get(y)||[String(y).includes("/")?y:`${o.file}/${y}`]));o.blockedBy=m?[...O].map(y=>se.get(y)).filter(y=>y&&y.lane!=="done"&&!y.reopened).map(Kt):[],o.blockedUntil=m?o.blockedUntil??null:null,o.blockReason=m?o.blockReason??null:null,o.blocked=o.blockedBy.length>0||!!o.blockedUntil,Jn(o,J,P),o.lane==="todo"&&o.kind==="work"&&!o.held&&$t(o.storedLane,o.agents)&&(o.lane="progress"),o.unpicked=o.kind==="work"&&o.lane==="todo"&&!!Hn(X,o.key),o.kind==="work"&&(o.phase=Zs(o)),o.unpicked&&!o.action&&(o.next=Vn),o.buildable=Gn(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)}Is(l,se,Q);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"),ee=d(o=>{const m=Z(o);return q.get(m)||m},"cardOf"),Me=d(o=>[A.get(o),he.get(o)].filter(Boolean).sort().pop()||"","closedAt"),Ie=d(o=>{const m=ee(o);return m===null?!o.reply:!se.has(m)||!(Me(m)<String(R(c,o.id)))?!1:B.has(o.id)?B.get(o.id)==="working":!o.reply&&!q.has(Z(o))&&!(String(me.get(m)||"")>String(o.ts))},"unanswered"),de=s.filter(o=>R(c,o.id)&&Ie(o)).map(o=>({id:o.id,kind:o.kind,key:ee(o),data:o.data,since:R(c,o.id)}));for(const o of l)o.working=de.some(m=>m.key===o.key),o.kind==="work"&&o.lane==="progress"&&!o.builder&&!o.next&&o.waitsOn!=="checking"&&!o.working&&(o.next=At),o.sortAt=qs[o.lane](o)||(o.lane==="review"?null:o.updatedAt)||null;for(const o of l){if(o.kind!=="work")continue;const m=(Le.get(`item:${o.key}`)||[]).filter($=>$.state==="answered").map($=>$.answers.at(-1).ts).sort().pop(),O=de.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))?ro(o,o.waitFacts,{answeredAt:m,workingSince:O}):null,delete o.waitFacts}Hs(l,s,c,q),Vs(l,e.screening||{}),l.sort(hn);const Ze=As(e,C,ve,l,X,s),E=xe(),We=rn({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]))}),ce=[...Ds(l,E,X),...an(We)],L=new Map(ce.filter(o=>o.questions).map((o,m)=>[o.topic||`item:${o.key}`,m])),Fe=d(o=>L.get(o.id)??L.size,"rankOf");E.sort((o,m)=>Fe(o)-Fe(m));const qe=new Set(ce.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,m)=>m.at.localeCompare(o.at));return ye.sort((o,m)=>m.at.localeCompare(o.at)),Rs({kind:F,main:we,remote:G&&{host:G.host,name:`${G.host}/${G.path}`},itemFiles:C,waveBands:Ze,items:l,moved:Object.fromEntries(q),answers:Oe,merges:ye,unfiled:be,owner:X,ownerTopics:E,reviews:We,needs:ce,working:de.length,workingOn:de,checks:Cs(e,i)},e)}d(_o,"buildBoard");function Rs(e,{listen:t,prompts:n}={}){const s=[...Ts(n),...e.needs];return{...e,needs:s,needsSummary:Ls(s),checks:{...e.checks,watcher:{listening:"running",asleep:"asleep",waking:"waking"}[t]||"stopped"}}}d(Rs,"withHub");const Kt=d(e=>({key:e.key,ref:e.ref}),"linkOf"),Gt=d((e,t)=>String(t).includes("/")?String(t):`${e}/${t}`,"linkKey"),Ms=d((e,t)=>e.ref.localeCompare(t.ref,void 0,{numeric:!0}),"byRef");function Is(e,t,n){const s=new Map,r=new Map,a=new Map,c=d((u,h,f)=>u.set(h,[...u.get(h)||[],f]),"add");for(const[u,h]of n){h.parent&&c(s,h.parent,u);for(const f of h.related)f!==u&&(c(r,u,f),c(r,f,u))}for(const u of e)for(const h of u.blockedBy||[])c(a,h.key,u.key);const i=d((u=[])=>[...new Set(u)].map(h=>t.get(h)).filter(Boolean).map(Kt).sort(Ms),"cards");for(const u of e){const h=n.get(u.key)?.parent;u.parent=h?{key:h,ref:t.get(h)?.ref||h.slice(h.indexOf("/")+1)}:null,u.children=i(s.get(u.key)),u.related=i(r.get(u.key)),u.waitsOnIt=i(a.get(u.key))}}d(Is,"linkCards");const qs={todo:d(e=>e.created,"todo"),progress:d(e=>e.progressSince,"progress"),review:d(e=>e.reviewSince,"review"),done:d(e=>e.closedAt,"done")},ie=d((e,t)=>`${e} ${t}${e===1?"":"s"}`,"plural");function Cs({checked:e,watcher:t,replyAt:n,threads:s={},working:r={},files:a=[]},c){const i=Number(e?.every)||30,h=[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(k=>Object.values(k||{})).map(k=>k?.done||k?.started))].filter(p=>typeof p=="string"&&!Number.isNaN(Date.parse(p))).reduce((p,k)=>p&&Date.parse(p)>Date.parse(k)?p:k,null),f=!!e?.idle;return{at:e?.at||null,every:f?null:i,idle:f,late:!f&&!!e?.at&&c-Date.parse(e.at)>2*i*6e4,claudeAt:h}}d(Cs,"checksOf");function Ds(e,t,n){const s=new Set(e.map(i=>i.questions?.topic).filter(Boolean)),r=d(i=>i.find(u=>n.find(h=>h.id===u)?.state==="open")||i[0],"firstOpen"),a=t.filter(i=>i.state!=="resolved"&&i.open>0&&!s.has(i.id)).map(i=>{const u=`questions.html#${r(i.questions)}`,h=/^(?:wave|file):(.+)$/.exec(i.id)?.[1]||null;return{kind:"Question",title:i.title,text:`${i.title} \xB7 ${ie(i.open,"question")}`,url:u,topic:i.id,band:h,questions:{open:i.open,url:u},ready:null,since:n.filter(f=>f.topic===i.id&&f.state==="open").map(f=>f.asked_at).filter(Boolean).sort()[0]||null}}),c=e.map(i=>{const u=i.questions&&i.questions.state!=="resolved"&&i.questions.open>0?{open:i.questions.open,url:`questions.html#${i.questions.first}`}:null,h=i.waitsOn==="ready"?i.pr.url:null,f=!u&&!h&&i.action?{label:i.action.label,url:i.action.url}:null;if(!u&&!h&&!f)return null;const p=`${i.ref}: ${i.title}`,k=[p,u&&ie(u.open,"question"),h&&"Ready on GitHub",f?.label].filter(Boolean).join(" \xB7 ");return{kind:u?"Question":h?"Tap Ready":"Action needed",title:p,text:k,url:`#selectedCard=${encodeURIComponent(i.key)}`,key:i.key,questions:u,ready:h,action:f,since:i.created||null,progress:i.lane==="progress"}}).filter(Boolean);return[...a,...c].sort(Ns)}d(Ds,"needsOf");const Ns=d((e,t)=>!!t.progress-!!e.progress||(e.since&&t.since?e.since.localeCompare(t.since):!!t.since-!!e.since),"oldestFirst");function Ts(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(Ts,"promptLines");function Ls(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&&ie(t,"prompt"),n&&ie(n,"question"),s&&`${ie(s,"draft")} waiting on Ready`,r&&`${ie(r,"review")} held`].filter(Boolean).join(" \xB7 ")}d(Ls,"needsSummary");function Ro(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(Ro,"needsAnswer");const Ps=d(e=>e.buildable&&e.phase==="ready"&&!e.held&&!e.blocked&&!e.recheckAt,"pullableCard"),Us=10;function js(e,{cap:t=Us,pulled:n=[],hold:s=[],taken:r=0}={}){const a=e.filter(w=>!ae(w)),c=d((w,b)=>w.started&&!w.done&&V(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)=>c(C,B)).length,0),u=a.filter(w=>Ps(w)&&!w.working).map(w=>w.key),h=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-i-h.length-r),k=f.filter(w=>w.writes),S=[...f.filter(w=>!w.writes),...k.slice(0,p)].map(({key:w,name:b})=>({key:w,name:b})),A=Math.max(0,p-k.length),v=new Set(a.filter(w=>w.bumpedAt).map(w=>w.key)),P=u.filter(w=>!h.includes(w)&&!s.includes(w)),D=P.filter(w=>v.has(w)),j=[...D,...P.filter(w=>!v.has(w)).slice(0,Math.max(0,A-D.length))],N=D.slice(A),W=Ws(a,c).slice(0,N.length).map((w,b)=>({key:w,by:N[b]}));return{idle:!i&&!a.some(w=>w.waitsOn==="checking"),pullable:u,waiting:h,pulls:j,helpers:S,bumpOuts:W,pastCap:N,free:p}}d(js,"pullOf");function Ws(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(Ws,"bumpOutsOf");function Mo(e,t,n){return e.filter(s=>s.kind==="work"&&!ae(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(Mo,"rebasesDue");const Fs=3,Io=10;function qo(e,{cap:t=Fs}={}){const n=e.filter(i=>!ae(i)),s=n.filter(i=>(i.agents||[]).some(u=>le(u)&&u.started&&!u.done)||(i.briefs||[]).some(u=>u.kind!==U)).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(u=>le(u)&&u.kind!==U)&&!(i.briefs||[]).some(u=>u.kind!==U)),a=[...r.filter(i=>!i.blocked),...r.filter(i=>i.blocked)],c=a.filter(i=>i.bumpedAt);return[...c,...a.filter(i=>!i.bumpedAt).slice(0,Math.max(0,t-s-c.length))].map(i=>i.key)}d(qo,"arrivalsOf");const Co=d(e=>`${U}-${Date.parse(e.at)}`,"visualAgent");function Do(e){const t=[];for(const n of e.filter(s=>s.kind==="work"&&!ae(s))){const s=(n.agents||[]).filter(r=>r.started&&!r.done);for(const r of _t){if(!(n.briefs||[]).some(c=>c.kind===r&&c.brief))continue;(r===U?s.some(c=>c.kind===U):n.lane!=="todo"||s.some(c=>c.kind!==U))||t.push({key:n.key,kind:r})}}return t}d(Do,"briefsDue");const Ks=d((e,t)=>e.kind==="ask"?t.get(`ask:${e.id}`)??null:e.kind==="card-added"?e.data?.key??null:null,"intakeKeyOf");function Gs(e,t,n){const s=e.get(n);return s?t.kind==="ask"?!!s.isNew:s.unfiled:!1}d(Gs,"unsorted");const Qs={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 Hs(e,t,n,s){const r=new Map(e.map(c=>[c.key,c])),a=new Set;for(const c of t){const i=Ks(c,s);!i||a.has(i)||!Gs(r,c,i)||(a.add(i),r.get(i).orchestrator={kind:"intake",since:String(c.ts),picked:R(n,c.id)||null,next:Qs[c.kind](i,c)})}}d(Hs,"intakeWaits");function Vs(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(Vs,"screenWaits");function Xs(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(Xs,"todoOf");function No(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(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};
10
+ ${Xs(e)}`}}d(No,"triageOf");const Ys=100,zs=["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"],To=d(e=>Object.fromEntries(zs.filter(t=>t in e).map(t=>[t,e[t]])),"slimDone");function Lo(e,t=Ys){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(Lo,"archivedOf");const Js={ready:"draft",ci:"draft",reviewers:"reviewers",merge:"merge"};function Zs(e){return e.lane==="todo"?e.unpicked?"prototyping":mt.todo.includes(e.storedPhase)?e.storedPhase:e.storedLane&&e.storedLane!=="todo"?"ready":"new":e.lane==="progress"?e.waitsOn==="checking"?"checking":e.builder&&!e.builder.planner&&e.builder.kind!==fe.install&&e.builder.phase||e.reachedPhase||"reading":e.lane==="review"?e.stale?"stale":e.waitsOn==="owner"&&e.pushed?"merge":Js[e.waitsOn]??null:e.storedLane==="done"&&mt.done.includes(e.storedPhase)?e.storedPhase:e.prs.some(t=>t.status===z.merged)?"merged":"closed"}d(Zs,"phaseOf");function Po(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:eo(t,n),from:{lane:t.storedLane,phase:t.storedPhase}}]})}d(Po,"movesOf");function eo(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(eo,"moveWhy");const to=d(e=>e.startsWith("ask:")?`ask-${e.slice(4)}`:`item-${e}`,"pushTag");function Uo(e){const t=d(a=>e.owner.filter(c=>c.topic===a&&c.state==="open").map(({id:c,question:i})=>({id:c,question:i})),"open"),n=[M.ready,M.merge],s=e.items.filter(a=>a.action).map(a=>{const c=at.has(a.action.kind)?t(a.questions.topic):[];return{tag:to(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:M.questions,title:a.text,body:c[0]?.question||a.title,url:a.url,now:!0,questions:c}});return[...s,...r,...ln(e.reviews)]}d(Uo,"ownerActions");function jo(e,{pulled:t=[]}={}){return!e.items.filter(r=>!ae(r)).some(r=>r.waitsOn==="checking"||r.grilling||r.prototyping||(r.agents||[]).some(a=>!a.done))&&!e.working&&!js(e.items,{pulled:t}).waiting.length}d(jo,"strictlyIdle");const no="item stall it with the reason when it can't go on",Qt="restart it once from its worktree with a thread line; if the restarted run goes stale too, item stall it with the reason",so='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',oo='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 ro(e,t,{answeredAt:n,workingSince:s}){const r=d(k=>e.agents.some(S=>S.kind===k&&S.started&&!S.done),"running"),a=e.briefing?e.briefs.find(k=>k.kind===e.briefing&&!k.brief):null,c=d(k=>e.briefs.some(S=>S.kind===k),"briefed"),i=[e.progressSince,...e.agents.map(k=>k.done)].filter(Boolean).map(String).sort().pop()||null,u=t.unasked&&e.storedPhase==={grill:"grilling",prototype:"prototyping"}[t.unasked.kind]&&!e.agents.some(k=>k.kind===t.unasked.kind&&k.started&&(!k.done||String(k.started)>t.unasked.at))?t.unasked:null,h=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:so}: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"}:a?{kind:"brief",since:a.at,next:`Write the ${a.kind} brief: item brief ${e.key} --kind ${a.kind} --file <draft.md>, with what the discussion settled, what is still open, what the ${{grill:"questions",prototype:"takes",visual:"page"}[a.kind]} should cover and the files that matter`}:at.has(e.action?.kind)?null:e.builder?.stopped?{kind:"stale",since:e.builder.silentSince,next:`${e.builder.stopped}: ${Qt}, 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 ${Qt}`}:e.lane==="review"&&e.stale?{kind:"stale",since:new Date(Date.parse(e.inReviewSince)+yt).toISOString(),daily:!0,next:`Find out why the review went stale and fix what you can: rebase, CI, comments; if only a reviewer owes it, a thread line naming who; or ${no}`}: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")&&!c("grill")?{kind:"grill",since:t.grillAt,next:"Start the grill asked for"}:e.prototyping&&t.prototypeAt&&!r("prototype")&&!c("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:oo}:e.refusal&&t.refusedAt?{kind:"refused",since:t.refusedAt,next:e.refusal}:e.next===At&&i?{kind:"unowned",since:i,next:"Start a builder on it, or set its Next"}:t.unblockedAt&&e.lane!=="todo"&&!e.builder&&!e.blocked&&!t.writes.some(k=>k>t.unblockedAt)?{kind:"unblocked",since:t.unblockedAt,next:"Its block ended: act on what it waited for, or set its Next"}:null;if(!h?.since)return null;const f=String(h.since),p=[t.writes.find(k=>k>f),s].filter(Boolean).sort()[0]||null;return{kind:h.kind,since:f,picked:p,next:h.next,...h.daily?{daily:!0}:{}}}d(ro,"orchestratorWaitOf");const Wo=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 Fo(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(Fo,"orchestratorWaits");const Ko=30*6e4,ao=15*6e4,lo=60*6e4,io=1440*6e4;function Go(e,t,n){const s=n.toISOString(),r=[],a=[];return{waits:t.map(i=>{const u=e.find(p=>p.key===i.key&&p.kind===i.kind&&p.since===i.since);if(!u)return i.picked||r.push(i.key),{...i,bumped:s};const h=i.picked?[i.picked,u.bumped].filter(Boolean).sort().pop():u.bumped||i.since,f=n-Date.parse(h)>=(i.picked?i.daily?io:lo:ao);return f&&a.push({key:i.key,ref:i.ref,kind:i.kind,since:i.since,next:i.next}),{...i,bumped:f?s:u.bumped||i.since,...u.pushed?{pushed:!0}:{}}}),added:r,again:a}}d(Go,"waitsStep");function Qo(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(Qo,"waitsAgainLine");const co=15;function Ho(e,{idle:t,now:n}){const s=n.toISOString(),r=e.last||s;return t?e.since?e.pushed||n-Date.parse(e.since)<co*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(Ho,"idleStep");function Vo(e,t){const n=d(u=>String(u.closedAt||"")>t,"after"),s=e.filter(u=>u.kind==="work"&&u.lane==="done"&&!vt(u)&&n(u)),r=new Map;for(const u of e)u.wave&&r.set(u.wave.slug,[...r.get(u.wave.slug)||[],u]);const a=[...r.values()].filter(u=>u.every(h=>h.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(Vo,"idleSummary");export{M as ACTION_KIND,Dt as ACTION_LABELS,ms as ANSWERS_SUBMITTED,_t as BRIEF_KINDS,es as BRIEF_WORD,De as BUILDER_ASK,fe as BUILDER_KIND,bn as BUILDER_QUIET_AFTER,yn as BUILDER_STALE_AFTER,wt as BUILD_REFUSED,re as BUTTON,Xe as COMMANDS,io as DAILY_REWAKE_AFTER,zs as DONE_FIELDS,Ys as DONE_KEPT,ge as HELPER,co as IDLE_MINUTES,Nt as INTENT_LABELS,wn as ITEM_EVENTS,He as LANDER,xo as LANES,Us as MAX_IMPLEMENTERS,Fs as MAX_PLANNERS,Io as MAX_SERVES,$e as MOVE,At as NO_NEXT,Ko as ORCHESTRATOR_PUSH_AFTER,Ao as PAUSE_CLEARS,rs as PAUSE_HARD_STOP,mt as PHASES,lo as PICKED_REWAKE_AFTER,Bt as PLANNER_WORD,Vn as PROTOTYPE_AGAIN,Eo as PROTOTYPE_WAIT,z as PR_STATE,ht as QUESTION_REOPEN,ao as REWAKE_AFTER,U as VISUAL,Lo as archivedOf,qo as arrivalsOf,Do as briefsDue,_o as buildBoard,V as builds,Bo as confirmedDrops,vn as fileFold,ot as handedBackAt,Ho as idleStep,Vo as idleSummary,jn as mergeBlockers,ds as modelName,Po as movesOf,Ro as needsAnswer,Hn as noTakePicked,ts as orchestratorDid,Fo as orchestratorWaits,Oo as owesNext,Uo as ownerActions,cs as ownerQuestion,is as pauseWork,Qn as picksNoTake,le as plans,$o as prototypeWaits,It as providerOf,js as pullOf,to as pushTag,Mo as rebasesDue,st as refOf,ke as roundOf,us as roundSettledBy,To as slimDone,Ft as stateEntries,Rt as stoppedWords,jo as strictlyIdle,jt as threadIndex,Es as threadOf,Xs as todoOf,No as triageOf,Wo as triageWaitOf,vt as unlanded,Co as visualAgent,Qo as waitsAgainLine,Go as waitsStep,_s as waveItems,Rs as withHub};