toolroll 0.9.99 → 0.9.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/accent-colors.js +1 -1
- package/dist/acp-check.js +4 -2
- package/dist/activity-line.d.ts +1 -1
- package/dist/activity-line.js +1 -1
- package/dist/agent-state.js +2 -1
- package/dist/agents-board.js +3 -2
- package/dist/agents-live.js +3 -2
- package/dist/approval-authority.d.ts +2 -1
- package/dist/approval-authority.js +2 -1
- package/dist/browser/terminal.css +1 -1
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-workspace.d.ts +31 -2
- package/dist/chat-ask.js +4 -2
- package/dist/chat-core.js +4 -2
- package/dist/chat-decide.js +1 -2
- package/dist/chat-delivery-state.d.ts +29 -6
- package/dist/chat-delivery-state.js +43 -13
- package/dist/chat-delivery.js +12 -5
- package/dist/chat-flow.js +15 -13
- package/dist/chat-live.js +5 -2
- package/dist/chat-message-states.d.ts +75 -0
- package/dist/chat-message-states.js +74 -0
- package/dist/chat-messages.d.ts +10 -4
- package/dist/chat-messages.js +12 -7
- package/dist/check-failure.js +2 -1
- package/dist/check-report.js +1 -1
- package/dist/claim.d.ts +40 -2
- package/dist/claim.js +92 -19
- package/dist/contracts/chat-tables.js +3 -0
- package/dist/coordinator.js +3 -3
- package/dist/demo.js +7 -2
- package/dist/design/kit.css +94 -2
- package/dist/design/kit.d.ts +12 -4
- package/dist/design/kit.js +8 -2
- package/dist/design/tokens.css +5 -5
- package/dist/desktop-update.d.ts +1 -1
- package/dist/discord-chat.js +2 -3
- package/dist/dispatch.js +2 -1
- package/dist/dispose.d.ts +4 -1
- package/dist/dispose.js +2 -1
- package/dist/fallback.js +3 -2
- package/dist/fast-start.js +2 -2
- package/dist/first-run.d.ts +10 -6
- package/dist/first-run.js +19 -18
- package/dist/guides.js +1 -1
- package/dist/monitoring.js +2 -1
- package/dist/operate.js +77 -36
- package/dist/plane-review.js +2 -1
- package/dist/project-knowledge.d.ts +3 -3
- package/dist/release-authority.d.ts +9 -0
- package/dist/release-authority.js +13 -0
- package/dist/release-auto.js +2 -3
- package/dist/release-gate.js +1 -1
- package/dist/release-view.js +2 -1
- package/dist/release.js +18 -30
- package/dist/result-shots.js +1 -1
- package/dist/route-results.js +2 -1
- package/dist/run-ending.d.ts +21 -0
- package/dist/run-ending.js +19 -0
- package/dist/scope.d.ts +5 -0
- package/dist/scope.js +10 -3
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +1 -1
- package/dist/server/console.css +46 -17
- package/dist/server/kit.d.ts +36 -3
- package/dist/server/kit.js +33 -4
- package/dist/server/render-chat.d.ts +2 -0
- package/dist/server/render-chat.js +1 -0
- package/dist/server/render-pages.js +1 -1
- package/dist/server/render-results.js +6 -5
- package/dist/server/render-tasks.d.ts +10 -0
- package/dist/server/render-tasks.js +234 -64
- package/dist/server/task-views.js +5 -1
- package/dist/settings-groups-ui.js +1 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.js +2 -3
- package/dist/store.d.ts +61 -2
- package/dist/store.js +141 -80
- package/dist/subagent-question.js +8 -6
- package/dist/surface.js +2 -2
- package/dist/task-archive.d.ts +3 -0
- package/dist/task-archive.js +5 -0
- package/dist/task-moves.d.ts +90 -0
- package/dist/task-moves.js +68 -0
- package/dist/task-status.d.ts +2 -2
- package/dist/task-status.js +3 -3
- package/dist/teams-chat.js +2 -2
- package/dist/telegram-flow.d.ts +1 -1
- package/dist/telegram-flow.js +1 -1
- package/dist/telegram-lead.d.ts +1 -2
- package/dist/telegram-lead.js +18 -23
- package/dist/telegram-progress.js +2 -1
- package/dist/telegram.d.ts +0 -1
- package/dist/telegram.js +6 -7
- package/dist/tui.js +1 -1
- package/dist/vanished-retry.js +3 -2
- package/dist/workspace-ui.d.ts +69 -0
- package/dist/workspace-ui.js +105 -1
- package/dist/worktree.js +3 -2
- package/package.json +1 -1
package/dist/tui.js
CHANGED
|
@@ -67,7 +67,7 @@ Read about how to prevent this error on https://github.com/vadimdemedes/ink/#isr
|
|
|
67
67
|
`))}patchConsole(){this.options.debug||(this.restoreConsole=_C((u,a)=>{u==="stdout"&&this.writeToStdout(a),u==="stderr"&&(a.startsWith("The above error occurred")||this.writeToStderr(a))}))}initKittyKeyboard(){if(!this.options.kittyKeyboard)return;let u=this.options.kittyKeyboard,a=u.mode??"auto";if(a==="disabled"||!this.options.stdin.isTTY||!this.options.stdout.isTTY)return;let l=u.flags??["disambiguateEscapeCodes"];if(a==="enabled"){this.enableKittyProtocol(l);return}let r=xi.env.TERM??"",E=xi.env.TERM_PROGRAM??"",B="KITTY_WINDOW_ID"in xi.env||r==="xterm-kitty"||E==="WezTerm"||E==="ghostty";!qe&&B&&this.confirmKittySupport(l)}confirmKittySupport(u){let{stdin:a,stdout:l}=this.options,r=[],E=()=>{this.cancelKittyDetection=void 0,clearTimeout(c),a.removeListener("data",B);let Q=Cy(r);r=[],Q.length>0&&a.unshift(Buffer.from(Q))},B=Q=>{let y=typeof Q=="string"?Buffer.from(Q):Q;for(let N of y)r.push(N);sy(r)&&(E(),this.isUnmounted||this.enableKittyProtocol(u))};a.on("data",B);let c=setTimeout(E,200);this.cancelKittyDetection=E,l.write("\x1B[?u")}enableKittyProtocol(u){this.options.stdout.write(`\x1B[>${_Q(u)}u`),this.kittyProtocolEnabled=!0}};var cy=(e,u)=>{let a={stdout:xo.stdout,stdin:xo.stdin,stderr:xo.stderr,debug:!1,exitOnCtrlC:!0,patchConsole:!0,maxFps:30,incrementalRendering:!1,concurrent:!1,...Qy(u)},l=fy(a.stdout,()=>new za(a),a.concurrent??!1);return l.render(e),{rerender:l.render,unmount(){l.unmount()},waitUntilExit:l.waitUntilExit,cleanup:()=>Ui.delete(a.stdout),clear:l.clear}},TE=cy,Qy=(e={})=>e instanceof Iy?{stdout:e,stdin:xo.stdin}:e,fy=(e,u,a)=>{let l=Ui.get(e);return l?l.isConcurrent!==a&&console.warn(`Warning: render() was called with concurrent: ${a}, but the existing instance for this stdout uses concurrent: ${l.isConcurrent}. The concurrent option only takes effect on the first render. Call unmount() first if you need to change the rendering mode.`):(l=u(),Ui.set(e,l)),l};var Dy=aA(Zl(),1);var To=aA(SA(),1);var XQ=aA(SA(),1);var dy=aA(SA(),1);var py=aA(SA(),1);var LE=aA(SA(),1);import{Buffer as my}from"node:buffer";var hy=/^(?:\x1b)([a-zA-Z0-9])$/,yy=/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/,jQ={OP:"f1",OQ:"f2",OR:"f3",OS:"f4","[11~":"f1","[12~":"f2","[13~":"f3","[14~":"f4","[[A":"f1","[[B":"f2","[[C":"f3","[[D":"f4","[[E":"f5","[15~":"f5","[17~":"f6","[18~":"f7","[19~":"f8","[20~":"f9","[21~":"f10","[23~":"f11","[24~":"f12","[A":"up","[B":"down","[C":"right","[D":"left","[E":"clear","[F":"end","[H":"home",OA:"up",OB:"down",OC:"right",OD:"left",OE:"clear",OF:"end",OH:"home","[1~":"home","[2~":"insert","[3~":"delete","[4~":"end","[5~":"pageup","[6~":"pagedown","[[5~":"pageup","[[6~":"pagedown","[7~":"home","[8~":"end","[a":"up","[b":"down","[c":"right","[d":"left","[e":"clear","[2$":"insert","[3$":"delete","[5$":"pageup","[6$":"pagedown","[7$":"home","[8$":"end",Oa:"up",Ob:"down",Oc:"right",Od:"left",Oe:"clear","[2^":"insert","[3^":"delete","[5^":"pageup","[6^":"pagedown","[7^":"home","[8^":"end","[Z":"tab"},VQ=[...Object.values(jQ),"backspace"],Sy=e=>["[a","[b","[c","[d","[e","[2$","[3$","[5$","[6$","[7$","[8$","[Z"].includes(e),Fy=e=>["Oa","Ob","Oc","Od","Oe","[2^","[3^","[5^","[6^","[7^","[8^"].includes(e),WQ=/^\x1b\[(\d+)(?:;(\d+)(?::(\d+))?(?:;([\d:]+))?)?u$/,Ny=/^\x1b\[(\d+);(\d+):(\d+)([A-Za-z~])$/,Ry={A:"up",B:"down",C:"right",D:"left",E:"clear",F:"end",H:"home",P:"f1",Q:"f2",R:"f3",S:"f4"},vy={2:"insert",3:"delete",5:"pageup",6:"pagedown",7:"home",8:"end",11:"f1",12:"f2",13:"f3",14:"f4",15:"f5",17:"f6",18:"f7",19:"f8",20:"f9",21:"f10",23:"f11",24:"f12"},ZQ={27:"escape",9:"tab",127:"delete",8:"backspace",57358:"capslock",57359:"scrolllock",57360:"numlock",57361:"printscreen",57362:"pause",57363:"menu",57376:"f13",57377:"f14",57378:"f15",57379:"f16",57380:"f17",57381:"f18",57382:"f19",57383:"f20",57384:"f21",57385:"f22",57386:"f23",57387:"f24",57388:"f25",57389:"f26",57390:"f27",57391:"f28",57392:"f29",57393:"f30",57394:"f31",57395:"f32",57396:"f33",57397:"f34",57398:"f35",57399:"kp0",57400:"kp1",57401:"kp2",57402:"kp3",57403:"kp4",57404:"kp5",57405:"kp6",57406:"kp7",57407:"kp8",57408:"kp9",57409:"kpdecimal",57410:"kpdivide",57411:"kpmultiply",57412:"kpsubtract",57413:"kpadd",57414:"kpenter",57415:"kpequal",57416:"kpseparator",57417:"kpleft",57418:"kpright",57419:"kpup",57420:"kpdown",57421:"kppageup",57422:"kppagedown",57423:"kphome",57424:"kpend",57425:"kpinsert",57426:"kpdelete",57427:"kpbegin",57428:"mediaplay",57429:"mediapause",57430:"mediaplaypause",57431:"mediareverse",57432:"mediastop",57433:"mediafastforward",57434:"mediarewind",57435:"mediatracknext",57436:"mediatrackprevious",57437:"mediarecord",57438:"lowervolume",57439:"raisevolume",57440:"mutevolume",57441:"leftshift",57442:"leftcontrol",57443:"leftalt",57444:"leftsuper",57445:"lefthyper",57446:"leftmeta",57447:"rightshift",57448:"rightcontrol",57449:"rightalt",57450:"rightsuper",57451:"righthyper",57452:"rightmeta",57453:"isoLevel3Shift",57454:"isoLevel5Shift"},PQ=e=>e>=0&&e<=1114111&&!(e>=55296&&e<=57343),bE=e=>PQ(e)?String.fromCodePoint(e):"?";function $Q(e){return e===3?"release":e===2?"repeat":"press"}function Af(e){return{ctrl:!!(e&We.ctrl),shift:!!(e&We.shift),meta:!!(e&We.meta),option:!!(e&We.alt),super:!!(e&We.super),hyper:!!(e&We.hyper),capsLock:!!(e&We.capsLock),numLock:!!(e&We.numLock)}}var wy=e=>{let u=WQ.exec(e);if(!u)return null;let a=parseInt(u[1],10),l=u[2]?Math.max(0,parseInt(u[2],10)-1):0,r=u[3]?parseInt(u[3],10):1,E=u[4];if(!PQ(a))return null;let B;E&&(B=E.split(":").map(y=>bE(parseInt(y,10))).join(""));let c,Q;return a===32?(c="space",Q=!0):a===13?(c="return",Q=!0):ZQ[a]?(c=ZQ[a],Q=!1):a>=1&&a<=26?(c=String.fromCodePoint(a+96),Q=!1):(c=bE(a).toLowerCase(),Q=!0),Q&&!B&&(B=bE(a)),{name:c,...Af(l),eventType:$Q(r),sequence:e,raw:e,isKittyProtocol:!0,isPrintable:Q,text:B}},Gy=e=>{let u=Ny.exec(e);if(!u)return null;let a=parseInt(u[1],10),l=Math.max(0,parseInt(u[2],10)-1),r=parseInt(u[3],10),E=u[4],B=E==="~"?vy[a]:Ry[E];return B?{name:B,...Af(l),eventType:$Q(r),sequence:e,raw:e,isKittyProtocol:!0,isPrintable:!1}:null},My=(e="")=>{let u;my.isBuffer(e)?e[0]>127&&e[1]===void 0?(e[0]-=128,e="\x1B"+String(e)):e=String(e):e!==void 0&&typeof e!="string"?e=String(e):e||(e="");let a=wy(e);if(a)return a;let l=Gy(e);if(l)return l;if(WQ.test(e))return{name:"",ctrl:!1,meta:!1,shift:!1,option:!1,sequence:e,raw:e,isKittyProtocol:!0,isPrintable:!1};let r={name:"",ctrl:!1,meta:!1,shift:!1,option:!1,sequence:e,raw:e};if(r.sequence=r.sequence||e||r.name,e==="\r"||e==="\x1B\r")r.raw=void 0,r.name="return",r.option=e.length===2;else if(e===`
|
|
68
68
|
`)r.name="enter";else if(e===" ")r.name="tab";else if(e==="\b"||e==="\x1B\b")r.name="backspace",r.meta=e.charAt(0)==="\x1B";else if(e===""||e==="\x1B")r.name="delete",r.meta=e.charAt(0)==="\x1B";else if(e==="\x1B"||e==="\x1B\x1B")r.name="escape",r.meta=e.length===2;else if(e===" "||e==="\x1B ")r.name="space",r.meta=e.length===2;else if(e.length===1&&e<="")r.name=String.fromCharCode(e.charCodeAt(0)+97-1),r.ctrl=!0;else if(e.length===1&&e>="0"&&e<="9")r.name="number";else if(e.length===1&&e>="a"&&e<="z")r.name=e;else if(e.length===1&&e>="A"&&e<="Z")r.name=e.toLowerCase(),r.shift=!0;else if(u=hy.exec(e))r.meta=!0,r.shift=/^[A-Z]$/.test(u[1]);else if(u=yy.exec(e)){let E=[...e];E[0]==="\x1B"&&E[1]==="\x1B"&&(r.option=!0);let B=[u[1],u[2],u[4],u[6]].filter(Boolean).join(""),c=(u[3]||u[5]||1)-1;r.ctrl=!!(c&4),r.meta=!!(c&10),r.shift=!!(c&1),r.code=B,r.name=jQ[B],r.shift=Sy(B)||r.shift,r.ctrl=Fy(B)||r.ctrl}return r},tf=My;var ef=aA(SA(),1);var Hy=()=>(0,ef.useContext)(Ro),bo=Hy;var Uy=(e,u={})=>{let{stdin:a,setRawMode:l,internal_exitOnCtrlC:r,internal_eventEmitter:E}=bo();(0,LE.useEffect)(()=>{if(u.isActive!==!1)return l(!0),()=>{l(!1)}},[u.isActive,l]),(0,LE.useEffect)(()=>{if(u.isActive===!1)return;let B=c=>{let Q=tf(c),y={upArrow:Q.name==="up",downArrow:Q.name==="down",leftArrow:Q.name==="left",rightArrow:Q.name==="right",pageDown:Q.name==="pagedown",pageUp:Q.name==="pageup",home:Q.name==="home",end:Q.name==="end",return:Q.name==="return",escape:Q.name==="escape",ctrl:Q.ctrl,shift:Q.shift,tab:Q.name==="tab",backspace:Q.name==="backspace",delete:Q.name==="delete",meta:Q.meta||Q.name==="escape"||Q.option,super:Q.super??!1,hyper:Q.hyper??!1,capsLock:Q.capsLock??!1,numLock:Q.numLock??!1,eventType:Q.eventType},N;Q.isKittyProtocol?Q.isPrintable?N=Q.text??Q.name:Q.ctrl&&Q.name.length===1?N=Q.name:N="":Q.ctrl?N=Q.name:N=Q.sequence,!Q.isKittyProtocol&&VQ.includes(Q.name)&&(N=""),N.startsWith("\x1B")&&(N=N.slice(1)),N.length===1&&typeof N[0]=="string"&&/[A-Z]/.test(N[0])&&(y.shift=!0),(!(N==="c"&&y.ctrl)||!r)&&fe.batchedUpdates(()=>{e(N,y)})};return E?.on("input",B),()=>{E?.removeListener("input",B)}},[u.isActive,a,r,e])},OE=Uy;var nf=aA(SA(),1);var xy=()=>(0,nf.useContext)(No),kE=xy;var Ty=aA(SA(),1);var by=aA(SA(),1);var JE=aA(SA(),1);var Ly=aA(SA(),1);var Oy=aA(SA(),1);var Lo=aA(SA(),1);import{pathToFileURL as Py}from"node:url";var ky=["[data-ink-approve]",'[data-ink-act="accept"]',"[data-accept-result][data-primary-action]"],Jy=['[data-act="request-changes"]','[data-act="revise"]','a.result-feedback-link[href="#request-changes"]'],Ky=['[data-act="retry"]','form[data-retry] button[type="submit"]','form[data-rebuild] button[type="submit"]'],KE=[{keys:"n",says:"new task",web:{kind:"go",href:"/tasks/new"},tui:"new"},{keys:"a",says:"Approve or Accept (presses the button on the page)",web:{kind:"press",selectors:ky},tui:"ink"},{keys:"b",says:"Send back (opens its note)",web:{kind:"press",selectors:Jy},tui:"send-back"},{keys:"r",says:"Retry (presses the button on the page)",web:{kind:"press",selectors:Ky},tui:"retry"},{keys:"/",says:"search",web:{kind:"search"},tui:{none:"The terminal has no search; press w to open the console."}},{keys:"g h",says:"go Home",web:{kind:"go",href:"/"},tui:"list"},{keys:"g t",says:"go to Tasks",web:{kind:"go",href:"/tasks"},tui:"list"},{keys:"?",says:"these shortcuts",web:{kind:"help"},tui:"help"}],uf=e=>e.split(" ").join(" then ");var PA=aA(SA(),1);var Yy=/[--]/g;function _y(e){return e.replace(/(?:\x1b\[|\u009b)[0-?]*[ -/]*[@-~]/g,"").replace(/(?:\x1b\]|\u009d)[^\x07\x1b\u009c]*(?:\x07|\x1b\\|\u009c)?/g,"").replace(/\x1b[ -/]*[0-~]/g,"").replace(/\t/g," ").replace(/[\x00-\x1f\x7f-\x9f]/g,"").replace(new RegExp("\\p{Cf}","gu"),"").replace(Yy,"")}function af(e){return e.split(`
|
|
69
69
|
`).map(_y).join(`
|
|
70
|
-
`)}var YE=["you","working","done"],lf={you:"Waiting on you",working:"Working",done:"Done"};var X=aA(qa(),1);function at({children:e,...u}){return(0,X.jsx)(EA,{...u,children:af(e)})}var Xy={new:"new task",ink:"approve the plan (shows it in full first), or accept the result","send-back":"send the result back",retry:"retry",list:"back to the task list",help:"these keys"},Zy=["j / k or ↑ / ↓ move","Enter open the task",...KE.filter(e=>typeof e.tui=="string").map(e=>`${uf(e.keys).padEnd(17)}${Xy[e.tui]}`),"c accept the result","s stop the running attempt","1–9 answer the task's question","w open in the browser","Esc back","q quit"];function jy(e){let u=a=>a.group===null?YE.length:YE.indexOf(a.group);return e.map((a,l)=>({row:a,index:l})).sort((a,l)=>u(a.row)-u(l.row)||a.index-l.index).map(a=>a.row)}var Vy=e=>e.group===null?"Other":lf[e.group],Wy=e=>[...e].filter(u=>u>=" "&&u!=="").join("");function Ef({source:e,session:u,onExit:a,pollMs:l=1e3}){let{exit:r}=kE(),[E,B]=(0,PA.useState)(null),[c,Q]=(0,PA.useState)(null),[y,N]=(0,PA.useState)(0),[w,h]=(0,PA.useState)({kind:"main"}),[v,H]=(0,PA.useState)(null),[L,M]=(0,PA.useState)(null),[W,FA]=(0,PA.useState)([]),NA=(0,PA.useRef)(null),gA=(0,PA.useRef)(0),HA=(0,PA.useRef)(!1),vA=jy(E?.rows??[]),QA=vA[Math.min(y,Math.max(vA.length-1,0))],XA=(0,PA.useCallback)(async()=>{let T=await e.snapshot();B(T),T.rows.length===0&&Q(await e.setup())},[e]);(0,PA.useEffect)(()=>{XA()},[XA]),(0,PA.useEffect)(()=>{let T=setInterval(()=>{gA.current+=1;let _=e.changed();(_!==NA.current||gA.current%5===0)&&(NA.current=_,XA())},l);return()=>clearInterval(T)},[e,XA,l]);let St=QA?.id??null;(0,PA.useEffect)(()=>{if(St===null){FA([]);return}FA(e.live(St));let T=setInterval(()=>FA(e.live(St)),l);return()=>clearInterval(T)},[e,St,l]);let Ut=T=>{HA.current||(HA.current=!0,r(),a?.(T))},zt=(0,PA.useRef)(!1),TA=async T=>{M("Working…");try{let _=await e.act(T);return _.ok?(H(_.message),await XA(),T.kind==="new"&&u.shouldAskBackground()&&h({kind:"background"}),!0):(_.needsPassword?h({kind:"password",message:_.message}):H(_.message),!1)}finally{M(null)}},Ft=async(T,_)=>{let P=await e.task(T);P===null?H("That task can't be read now."):_(P)},xt=T=>Ft(T,_=>{_.plan===null?H("No plan is waiting for your approval on this task."):h({kind:"approve",task:_})}),Ot=T=>Ft(T,_=>{_.plan!==null?h({kind:"approve",task:_}):_.hasResult?TA({kind:"complete",id:T}):H("No plan or result is waiting for you on this task.")}),Tt=(T,_)=>{if(_===null){H("Nothing is running on this task.");return}TA({kind:"stop",id:T,runId:_})},qt=(T,_)=>{let P=T.decisions[0],V=P?.options[_];P===void 0||V===void 0||TA({kind:"answer",decision:P.id,option:V.id}).then(At=>{At&&h({kind:"main"})})},GA=()=>{M("Opening the browser…"),u.openBrowser().then(T=>{H(T),M(null)})},It=()=>{u.owned()?h({kind:"quit"}):Ut("leave")},de=(T,_,P)=>{if(zt.current&&(zt.current=!1,T==="h"||T==="t"))return h({kind:"main"}),!0;if(T==="g")return zt.current=!0,!0;if(T==="/"){let V=KE.find(At=>At.keys==="/").tui;return H(typeof V=="string"?null:V.none),!0}return T==="n"?(h({kind:"compose",purpose:"new",id:null,text:"",project:0}),!0):T==="w"?(GA(),!0):T==="?"?(h({kind:"help"}),!0):T==="q"?(It(),!0):_===null?!1:T==="a"?(Ot(_),!0):T==="s"?(Tt(_,P),!0):T==="b"?(h({kind:"compose",purpose:"revise",id:_,text:"",project:0}),!0):T==="r"?(TA({kind:"retry",id:_}),!0):T==="c"?(TA({kind:"complete",id:_}),!0):!1},pe=(T,_)=>{if(L===null){if(_.ctrl&&T==="c"){It();return}switch(w.kind){case"main":{if(T==="j"||_.downArrow){N(P=>Math.min(P+1,Math.max(vA.length-1,0)));return}if(T==="k"||_.upArrow){N(P=>Math.max(P-1,0));return}if(_.return&&QA!==void 0){Ft(QA.id,P=>h({kind:"task",task:P}));return}if(_.escape){H(null);return}de(T,QA?.id??null,QA?.liveRunId??null);return}case"task":{if(_.escape||_.leftArrow){h({kind:"main"});return}if(/^[1-9]$/.test(T)){qt(w.task,Number(T)-1);return}de(T,w.task.id,w.task.liveRunId);return}case"approve":{if(_.escape||T==="n"){h({kind:"main"}),H("Nothing approved.");return}if(T==="y"){let{task:P}=w;TA({kind:"approve",id:P.id,digest:P.plan.digest}).then(V=>{V&&h({kind:"main"})})}return}case"build":{if(_.escape){h({kind:"main"}),H("Nothing started.");return}if(T==="p"){let{text:P,repo:V}=w;h({kind:"main"}),TA({kind:"new",text:P,repo:V});return}if(_.return){let{preview:P,digest:V}=w.terms;TA({kind:"build",preview:P,digest:V}).then(At=>{At&&h({kind:"main"})})}return}case"compose":{if(_.escape){h({kind:"main"});return}let P=E?.projects??[];if(_.tab&&w.purpose==="new"&&P.length>1){h({...w,project:(w.project+1)%P.length});return}if(_.backspace||_.delete){h({...w,text:w.text.slice(0,-1)});return}if(_.return){let At=w.text.trim();if(At==="")return;let Z=w.purpose==="new"?{kind:"new",text:At,repo:P[w.project]??null}:{kind:"revise",id:w.id,text:At};if(h({kind:"main"}),Z.kind==="new"){M("Reading it…"),e.fastTerms(At,Z.repo).catch(()=>null).then(uA=>{M(null),uA===null?TA(Z):h({kind:"build",terms:uA,text:At,repo:Z.repo})});return}TA(Z);return}let V=Wy(T);V!==""&&h({...w,text:w.text+V});return}case"quit":{T==="s"?Ut("stop"):T==="b"?Ut("keep"):_.escape&&h({kind:"main"});return}case"background":{let P=T==="y"||T==="Y"||_.return;if(!P&&T!=="n"&&T!=="N")return;h({kind:"main"}),M(P?"Moving Toolroll to the background…":"Saving…"),u.answerBackground(P).then(V=>{H(V),M(null)});return}default:(_.escape||_.return||T==="q")&&h({kind:"main"})}}},Xt=(0,PA.useRef)(pe);Xt.current=pe,OE((0,PA.useCallback)((T,_)=>Xt.current(T,_),[]));let me=L??v;return(0,X.jsxs)(RA,{flexDirection:"column",children:[(0,X.jsxs)(RA,{justifyContent:"space-between",children:[(0,X.jsx)(EA,{bold:!0,children:"Toolroll"}),E?.needsYou?(0,X.jsx)(at,{color:"yellow",children:E.needsYou}):(0,X.jsx)(EA,{dimColor:!0,children:"Nothing needs you"}),E?.learning?(0,X.jsx)(at,{dimColor:!0,children:E.learning}):null]}),$A(),me===null?null:(0,X.jsx)(at,{children:me}),(0,X.jsx)(EA,{dimColor:!0,children:Zt()})]});function Zt(){switch(w.kind){case"main":return vA.length===0?"n new task · w browser · ? help · q quit":"j/k move · Enter open · n new · a approve/accept · b send back · r retry · s stop · w browser · ? help · q quit";case"task":return"a approve/accept · b send back · r retry · s stop · 1–9 answer · w browser · Esc back";case"approve":return"y approve · Esc cancel";case"build":return"Enter Build · p plan it first · Esc cancel";case"compose":return w.purpose==="new"&&(E?.projects.length??0)>1?"Enter send · Tab project · Esc cancel":"Enter send · Esc cancel";case"quit":return"s stop · b background · Esc cancel";case"background":return"y yes · n no";default:return"Esc back"}}function $A(){switch(w.kind){case"help":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Keys"}),Zy.map(T=>(0,X.jsx)(EA,{children:T},T))]});case"password":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"This needs your password"}),(0,X.jsx)(at,{children:w.message}),E?.passwordSet?(0,X.jsx)(EA,{children:"Sign in in the browser, then do it there."}):(0,X.jsx)(EA,{children:"Set a password: run `toolroll password set`"})]});case"quit":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Quit Toolroll?"}),(0,X.jsx)(EA,{children:"s Stop agents and quit (running work is saved)"}),(0,X.jsx)(EA,{children:"b Keep running in the background"})]});case"background":return(0,X.jsx)(RA,{borderStyle:"single",paddingX:1,children:(0,X.jsx)(EA,{bold:!0,children:"Keep Toolroll running in the background? (Y/n)"})});case"build":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Build approves exactly this:"}),w.terms.terms.map((T,_)=>(0,X.jsx)(at,{children:`${T.label}: ${T.words}`},_))]});case"compose":{let T=E?.projects[w.project]??null;return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"round",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:w.purpose==="new"?"New task":"What should change?"}),w.purpose==="new"&&T!==null?(0,X.jsx)(at,{dimColor:!0,children:`In ${T}`}):null,(0,X.jsxs)(EA,{children:[(0,X.jsx)(at,{children:w.text}),(0,X.jsx)(EA,{inverse:!0,children:" "})]})]})}case"approve":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(at,{bold:!0,children:`Approve this plan? ${w.task.title}`}),w.task.plan.lines.map((T,_)=>(0,X.jsx)(at,{children:T},_))]});case"task":{let{task:T}=w,_=T.decisions[0];return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(at,{bold:!0,children:T.title}),(0,X.jsx)(at,{children:`${T.status}${T.detail===""?"":` · ${T.detail}`}`}),T.plan===null?null:(0,X.jsx)(EA,{children:"A plan waits for your approval (a)."}),(T.ready?.length??0)===0?null:(0,X.jsx)(RA,{flexDirection:"column",marginTop:1,children:T.ready.map((P,V)=>(0,X.jsx)(at,{...V===0?{bold:!0}:{},children:P},V))}),_===void 0?null:(0,X.jsxs)(RA,{flexDirection:"column",marginTop:1,children:[(0,X.jsx)(at,{bold:!0,children:_.question}),_.options.map((P,V)=>(0,X.jsx)(at,{children:`${V+1} ${P.label}`},P.id))]}),W.length===0?null:(0,X.jsx)(RA,{flexDirection:"column",marginTop:1,children:W.slice(-12).map((P,V)=>(0,X.jsx)(at,{dimColor:!0,children:P},V))})]})}default:return E===null?(0,X.jsx)(EA,{dimColor:!0,children:"Loading…"}):vA.length===0?(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Get to your first result"}),(c?.lines??["No tasks yet. Press n to write one."]).map((T,_)=>(0,X.jsx)(at,{children:T},_))]}):(0,X.jsxs)(RA,{children:[(0,X.jsx)(RA,{flexDirection:"column",width:"45%",borderStyle:"single",paddingX:1,children:vA.map((T,_)=>(0,X.jsxs)(PA.Fragment,{children:[_===0||vA[_-1].group!==T.group?(0,X.jsx)(EA,{bold:!0,children:Vy(T)}):null,(0,X.jsxs)(EA,{inverse:T===QA,wrap:"truncate-end",children:[T===QA?"> ":" ",(0,X.jsx)(at,{children:T.title})," ",(0,X.jsx)(at,{...T.needsYou?{color:"yellow"}:{dimColor:!0},children:T.status})]})]},T.id))}),(0,X.jsx)(RA,{flexDirection:"column",flexGrow:1,borderStyle:"single",paddingX:1,children:QA===void 0?(0,X.jsx)(EA,{dimColor:!0,children:"Nothing selected"}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(at,{bold:!0,wrap:"truncate-end",children:QA.title}),(0,X.jsx)(at,{children:`${QA.status}${QA.detail===""?"":` · ${QA.detail}`}`}),W.length===0?(0,X.jsx)(EA,{dimColor:!0,children:QA.liveRunId===null?"No agent is working on it now.":"Waiting for output…"}):W.slice(-12).map((T,_)=>(0,X.jsx)(at,{dimColor:!0,wrap:"truncate-end",children:T},_))]})})]})}}}function Bf(){let e=[{id:"t3",title:"Sample: upgrade the test runner",status:"Needs you",detail:"Plan ready to approve",needsYou:!0,liveRunId:null,repo:null,group:"you"},{id:"t1",title:"Sample: fix the login redirect",status:"Running",detail:"Sample task (not real)",needsYou:!1,liveRunId:1,repo:null,group:"working"},{id:"t2",title:"Sample: add CSV export",status:"Complete",detail:"Sample task (not real)",needsYou:!1,liveRunId:null,repo:null,group:"done"}];return{fastTerms:async()=>null,snapshot:async()=>({rows:e,needsYou:"1 task needs you: Sample: upgrade the test runner",projects:[],passwordSet:!1}),task:async u=>{let a=e.find(r=>r.id===u);return a===void 0?null:{id:u,title:a.title,status:a.status,detail:a.detail,liveRunId:a.liveRunId,hasResult:a.status==="Complete",decisions:[],plan:a.needsYou?{digest:"sample",lines:["Goal: upgrade the test runner (sample)"]}:null}},live:u=>u==="t1"?["Editing src/auth.ts (sample)"]:[],changed:()=>0,act:async()=>({ok:!1,message:"This is a sample: nothing changes.",needsPassword:!1}),setup:async()=>null}}function sf(){return{owned:()=>!1,openBrowser:async()=>"This is a sample.",shouldAskBackground:()=>!1,answerBackground:async()=>"This is a sample."}}var If=aA(qa(),1);async function $y(e=Bf(),u=sf()){if(!process.stdin.isTTY||!process.stdout.isTTY)throw new Error("The terminal UI needs an interactive terminal.");let a="leave";return await TE((0,If.jsx)(Ef,{source:e,session:u,onExit:r=>{a=r}}),{exitOnCtrlC:!1}).waitUntilExit(),a}var Cf=process.argv[1];Cf!==void 0&&import.meta.url===Py(Cf).href&&$y().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
70
|
+
`)}var YE=["you","working","done"],lf={you:"Waiting on you",working:"Working",done:"Done"};var X=aA(qa(),1);function at({children:e,...u}){return(0,X.jsx)(EA,{...u,children:af(e)})}var Xy={new:"new task",ink:"approve the plan (shows it in full first), or accept the result","send-back":"send the result back",retry:"retry",list:"back to the task list",help:"these keys"},Zy=["j / k or ↑ / ↓ move","Enter open the task",...KE.filter(e=>typeof e.tui=="string").map(e=>`${uf(e.keys).padEnd(17)}${Xy[e.tui]}`),"c accept the result","s stop the running attempt","1–9 answer the task's question","w open in the browser","Esc back","q quit"];function jy(e){let u=a=>a.group===null?YE.length:YE.indexOf(a.group);return e.map((a,l)=>({row:a,index:l})).sort((a,l)=>u(a.row)-u(l.row)||a.index-l.index).map(a=>a.row)}var Vy=e=>e.group===null?"Other":lf[e.group],Wy=e=>[...e].filter(u=>u>=" "&&u!=="").join("");function Ef({source:e,session:u,onExit:a,pollMs:l=1e3}){let{exit:r}=kE(),[E,B]=(0,PA.useState)(null),[c,Q]=(0,PA.useState)(null),[y,N]=(0,PA.useState)(0),[w,h]=(0,PA.useState)({kind:"main"}),[v,H]=(0,PA.useState)(null),[L,M]=(0,PA.useState)(null),[W,FA]=(0,PA.useState)([]),NA=(0,PA.useRef)(null),gA=(0,PA.useRef)(0),HA=(0,PA.useRef)(!1),vA=jy(E?.rows??[]),QA=vA[Math.min(y,Math.max(vA.length-1,0))],XA=(0,PA.useCallback)(async()=>{let T=await e.snapshot();B(T),T.rows.length===0&&Q(await e.setup())},[e]);(0,PA.useEffect)(()=>{XA()},[XA]),(0,PA.useEffect)(()=>{let T=setInterval(()=>{gA.current+=1;let _=e.changed();(_!==NA.current||gA.current%5===0)&&(NA.current=_,XA())},l);return()=>clearInterval(T)},[e,XA,l]);let St=QA?.id??null;(0,PA.useEffect)(()=>{if(St===null){FA([]);return}FA(e.live(St));let T=setInterval(()=>FA(e.live(St)),l);return()=>clearInterval(T)},[e,St,l]);let Ut=T=>{HA.current||(HA.current=!0,r(),a?.(T))},zt=(0,PA.useRef)(!1),TA=async T=>{M("Working…");try{let _=await e.act(T);return _.ok?(H(_.message),await XA(),T.kind==="new"&&u.shouldAskBackground()&&h({kind:"background"}),!0):(_.needsPassword?h({kind:"password",message:_.message}):H(_.message),!1)}finally{M(null)}},Ft=async(T,_)=>{let P=await e.task(T);P===null?H("That task can't be read now."):_(P)},xt=T=>Ft(T,_=>{_.plan===null?H("No plan is waiting for your approval on this task."):h({kind:"approve",task:_})}),Ot=T=>Ft(T,_=>{_.plan!==null?h({kind:"approve",task:_}):_.hasResult?TA({kind:"complete",id:T}):H("No plan or result is waiting for you on this task.")}),Tt=(T,_)=>{if(_===null){H("Nothing is running on this task.");return}TA({kind:"stop",id:T,runId:_})},qt=(T,_)=>{let P=T.decisions[0],V=P?.options[_];P===void 0||V===void 0||TA({kind:"answer",decision:P.id,option:V.id}).then(At=>{At&&h({kind:"main"})})},GA=()=>{M("Opening the browser…"),u.openBrowser().then(T=>{H(T),M(null)})},It=()=>{u.owned()?h({kind:"quit"}):Ut("leave")},de=(T,_,P)=>{if(zt.current&&(zt.current=!1,T==="h"||T==="t"))return h({kind:"main"}),!0;if(T==="g")return zt.current=!0,!0;if(T==="/"){let V=KE.find(At=>At.keys==="/").tui;return H(typeof V=="string"?null:V.none),!0}return T==="n"?(h({kind:"compose",purpose:"new",id:null,text:"",project:0}),!0):T==="w"?(GA(),!0):T==="?"?(h({kind:"help"}),!0):T==="q"?(It(),!0):_===null?!1:T==="a"?(Ot(_),!0):T==="s"?(Tt(_,P),!0):T==="b"?(h({kind:"compose",purpose:"revise",id:_,text:"",project:0}),!0):T==="r"?(TA({kind:"retry",id:_}),!0):T==="c"?(TA({kind:"complete",id:_}),!0):!1},pe=(T,_)=>{if(L===null){if(_.ctrl&&T==="c"){It();return}switch(w.kind){case"main":{if(T==="j"||_.downArrow){N(P=>Math.min(P+1,Math.max(vA.length-1,0)));return}if(T==="k"||_.upArrow){N(P=>Math.max(P-1,0));return}if(_.return&&QA!==void 0){Ft(QA.id,P=>h({kind:"task",task:P}));return}if(_.escape){H(null);return}de(T,QA?.id??null,QA?.liveRunId??null);return}case"task":{if(_.escape||_.leftArrow){h({kind:"main"});return}if(/^[1-9]$/.test(T)){qt(w.task,Number(T)-1);return}de(T,w.task.id,w.task.liveRunId);return}case"approve":{if(_.escape||T==="n"){h({kind:"main"}),H("Nothing approved.");return}if(T==="y"){let{task:P}=w;TA({kind:"approve",id:P.id,digest:P.plan.digest}).then(V=>{V&&h({kind:"main"})})}return}case"build":{if(_.escape){h({kind:"main"}),H("Nothing started.");return}if(T==="p"){let{text:P,repo:V}=w;h({kind:"main"}),TA({kind:"new",text:P,repo:V});return}if(_.return){let{preview:P,digest:V}=w.terms;TA({kind:"build",preview:P,digest:V}).then(At=>{At&&h({kind:"main"})})}return}case"compose":{if(_.escape){h({kind:"main"});return}let P=E?.projects??[];if(_.tab&&w.purpose==="new"&&P.length>1){h({...w,project:(w.project+1)%P.length});return}if(_.backspace||_.delete){h({...w,text:w.text.slice(0,-1)});return}if(_.return){let At=w.text.trim();if(At==="")return;let Z=w.purpose==="new"?{kind:"new",text:At,repo:P[w.project]??null}:{kind:"revise",id:w.id,text:At};if(h({kind:"main"}),Z.kind==="new"){M("Reading it…"),e.fastTerms(At,Z.repo).catch(()=>null).then(uA=>{M(null),uA===null?TA(Z):h({kind:"build",terms:uA,text:At,repo:Z.repo})});return}TA(Z);return}let V=Wy(T);V!==""&&h({...w,text:w.text+V});return}case"quit":{T==="s"?Ut("stop"):T==="b"?Ut("keep"):_.escape&&h({kind:"main"});return}case"background":{let P=T==="y"||T==="Y"||_.return;if(!P&&T!=="n"&&T!=="N")return;h({kind:"main"}),M(P?"Moving Toolroll to the background…":"Saving…"),u.answerBackground(P).then(V=>{H(V),M(null)});return}default:(_.escape||_.return||T==="q")&&h({kind:"main"})}}},Xt=(0,PA.useRef)(pe);Xt.current=pe,OE((0,PA.useCallback)((T,_)=>Xt.current(T,_),[]));let me=L??v;return(0,X.jsxs)(RA,{flexDirection:"column",children:[(0,X.jsxs)(RA,{justifyContent:"space-between",children:[(0,X.jsx)(EA,{bold:!0,children:"Toolroll"}),E?.needsYou?(0,X.jsx)(at,{bold:!0,children:E.needsYou}):(0,X.jsx)(EA,{dimColor:!0,children:"Nothing needs you"}),E?.learning?(0,X.jsx)(at,{dimColor:!0,children:E.learning}):null]}),$A(),me===null?null:(0,X.jsx)(at,{children:me}),(0,X.jsx)(EA,{dimColor:!0,children:Zt()})]});function Zt(){switch(w.kind){case"main":return vA.length===0?"n new task · w browser · ? help · q quit":"j/k move · Enter open · n new · a approve/accept · b send back · r retry · s stop · w browser · ? help · q quit";case"task":return"a approve/accept · b send back · r retry · s stop · 1–9 answer · w browser · Esc back";case"approve":return"y approve · Esc cancel";case"build":return"Enter Build · p plan it first · Esc cancel";case"compose":return w.purpose==="new"&&(E?.projects.length??0)>1?"Enter send · Tab project · Esc cancel":"Enter send · Esc cancel";case"quit":return"s stop · b background · Esc cancel";case"background":return"y yes · n no";default:return"Esc back"}}function $A(){switch(w.kind){case"help":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Keys"}),Zy.map(T=>(0,X.jsx)(EA,{children:T},T))]});case"password":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"This needs your password"}),(0,X.jsx)(at,{children:w.message}),E?.passwordSet?(0,X.jsx)(EA,{children:"Sign in in the browser, then do it there."}):(0,X.jsx)(EA,{children:"Set a password: run `toolroll password set`"})]});case"quit":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Quit Toolroll?"}),(0,X.jsx)(EA,{children:"s Stop agents and quit (running work is saved)"}),(0,X.jsx)(EA,{children:"b Keep running in the background"})]});case"background":return(0,X.jsx)(RA,{borderStyle:"single",paddingX:1,children:(0,X.jsx)(EA,{bold:!0,children:"Keep Toolroll running in the background? (Y/n)"})});case"build":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Build approves exactly this:"}),w.terms.terms.map((T,_)=>(0,X.jsx)(at,{children:`${T.label}: ${T.words}`},_))]});case"compose":{let T=E?.projects[w.project]??null;return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"round",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:w.purpose==="new"?"New task":"What should change?"}),w.purpose==="new"&&T!==null?(0,X.jsx)(at,{dimColor:!0,children:`In ${T}`}):null,(0,X.jsxs)(EA,{children:[(0,X.jsx)(at,{children:w.text}),(0,X.jsx)(EA,{inverse:!0,children:" "})]})]})}case"approve":return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(at,{bold:!0,children:`Approve this plan? ${w.task.title}`}),w.task.plan.lines.map((T,_)=>(0,X.jsx)(at,{children:T},_))]});case"task":{let{task:T}=w,_=T.decisions[0];return(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(at,{bold:!0,children:T.title}),(0,X.jsx)(at,{children:`${T.status}${T.detail===""?"":` · ${T.detail}`}`}),T.plan===null?null:(0,X.jsx)(EA,{children:"A plan waits for your approval (a)."}),(T.ready?.length??0)===0?null:(0,X.jsx)(RA,{flexDirection:"column",marginTop:1,children:T.ready.map((P,V)=>(0,X.jsx)(at,{...V===0?{bold:!0}:{},children:P},V))}),_===void 0?null:(0,X.jsxs)(RA,{flexDirection:"column",marginTop:1,children:[(0,X.jsx)(at,{bold:!0,children:_.question}),_.options.map((P,V)=>(0,X.jsx)(at,{children:`${V+1} ${P.label}`},P.id))]}),W.length===0?null:(0,X.jsx)(RA,{flexDirection:"column",marginTop:1,children:W.slice(-12).map((P,V)=>(0,X.jsx)(at,{dimColor:!0,children:P},V))})]})}default:return E===null?(0,X.jsx)(EA,{dimColor:!0,children:"Loading…"}):vA.length===0?(0,X.jsxs)(RA,{flexDirection:"column",borderStyle:"single",paddingX:1,children:[(0,X.jsx)(EA,{bold:!0,children:"Get to your first result"}),(c?.lines??["No tasks yet. Press n to write one."]).map((T,_)=>(0,X.jsx)(at,{children:T},_))]}):(0,X.jsxs)(RA,{children:[(0,X.jsx)(RA,{flexDirection:"column",width:"45%",borderStyle:"single",paddingX:1,children:vA.map((T,_)=>(0,X.jsxs)(PA.Fragment,{children:[_===0||vA[_-1].group!==T.group?(0,X.jsx)(EA,{bold:!0,children:Vy(T)}):null,(0,X.jsxs)(EA,{inverse:T===QA,wrap:"truncate-end",children:[T===QA?"> ":" ",(0,X.jsx)(at,{children:T.title})," ",(0,X.jsx)(at,{...T.needsYou?{bold:!0}:{dimColor:!0},children:T.status})]})]},T.id))}),(0,X.jsx)(RA,{flexDirection:"column",flexGrow:1,borderStyle:"single",paddingX:1,children:QA===void 0?(0,X.jsx)(EA,{dimColor:!0,children:"Nothing selected"}):(0,X.jsxs)(X.Fragment,{children:[(0,X.jsx)(at,{bold:!0,wrap:"truncate-end",children:QA.title}),(0,X.jsx)(at,{children:`${QA.status}${QA.detail===""?"":` · ${QA.detail}`}`}),W.length===0?(0,X.jsx)(EA,{dimColor:!0,children:QA.liveRunId===null?"No agent is working on it now.":"Waiting for output…"}):W.slice(-12).map((T,_)=>(0,X.jsx)(at,{dimColor:!0,wrap:"truncate-end",children:T},_))]})})]})}}}function Bf(){let e=[{id:"t3",title:"Sample: upgrade the test runner",status:"Needs you",detail:"Plan ready to approve",needsYou:!0,liveRunId:null,repo:null,group:"you"},{id:"t1",title:"Sample: fix the login redirect",status:"Running",detail:"Sample task (not real)",needsYou:!1,liveRunId:1,repo:null,group:"working"},{id:"t2",title:"Sample: add CSV export",status:"Complete",detail:"Sample task (not real)",needsYou:!1,liveRunId:null,repo:null,group:"done"}];return{fastTerms:async()=>null,snapshot:async()=>({rows:e,needsYou:"1 task needs you: Sample: upgrade the test runner",projects:[],passwordSet:!1}),task:async u=>{let a=e.find(r=>r.id===u);return a===void 0?null:{id:u,title:a.title,status:a.status,detail:a.detail,liveRunId:a.liveRunId,hasResult:a.status==="Complete",decisions:[],plan:a.needsYou?{digest:"sample",lines:["Goal: upgrade the test runner (sample)"]}:null}},live:u=>u==="t1"?["Editing src/auth.ts (sample)"]:[],changed:()=>0,act:async()=>({ok:!1,message:"This is a sample: nothing changes.",needsPassword:!1}),setup:async()=>null}}function sf(){return{owned:()=>!1,openBrowser:async()=>"This is a sample.",shouldAskBackground:()=>!1,answerBackground:async()=>"This is a sample."}}var If=aA(qa(),1);async function $y(e=Bf(),u=sf()){if(!process.stdin.isTTY||!process.stdout.isTTY)throw new Error("The terminal UI needs an interactive terminal.");let a="leave";return await TE((0,If.jsx)(Ef,{source:e,session:u,onExit:r=>{a=r}}),{exitOnCtrlC:!1}).waitUntilExit(),a}var Cf=process.argv[1];Cf!==void 0&&import.meta.url===Py(Cf).href&&$y().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
71
71
|
`),process.exitCode=1});export{$y as startTui};
|
|
72
72
|
/*! Bundled license information:
|
|
73
73
|
|
package/dist/vanished-retry.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { authorityStandsSql } from "./approval-authority.js";
|
|
20
20
|
import { stopFactOf } from "./store.js";
|
|
21
21
|
import { refArchived } from "./task-archive.js";
|
|
22
|
+
import { moveTask } from "./task-moves.js";
|
|
22
23
|
/** The automatic-retry limits, in one place. */
|
|
23
24
|
export const RETRY_LIMIT = 3;
|
|
24
25
|
export const RETRY_SPEND_TIMES = 2;
|
|
@@ -48,7 +49,7 @@ function approvalStands(store, taskRef, now) {
|
|
|
48
49
|
WHERE task_scope.approved_digest = task_scope.digest AND task_scope.approved_at IS NOT NULL AND ${authorityStandsSql("task_scope", "task_ref.repo", "?")}`).get(taskRef, stamp, stamp, stamp, stamp) !== undefined;
|
|
49
50
|
}
|
|
50
51
|
function requeue(store, taskId, now) {
|
|
51
|
-
return
|
|
52
|
+
return moveTask(store.handle, taskId, "requeue", now);
|
|
52
53
|
}
|
|
53
54
|
function episodeOf(store, taskRef) {
|
|
54
55
|
const row = store.handle.prepare("SELECT retries, reserved_microusd FROM retry_episode WHERE task_ref = ?").get(taskRef);
|
|
@@ -59,7 +60,7 @@ function stopFor(store, input) {
|
|
|
59
60
|
store.handle.prepare(`INSERT INTO retry_episode (task_ref, retries, reserved_microusd, stopped, stopped_run, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
60
61
|
ON CONFLICT (task_ref) DO UPDATE SET stopped = excluded.stopped, stopped_run = excluded.stopped_run, reserved_microusd = excluded.reserved_microusd, updated_at = excluded.updated_at`)
|
|
61
62
|
.run(input.taskRef, input.retries, input.reservedMicrousd, input.why, input.run, stamp, stamp);
|
|
62
|
-
store.handle
|
|
63
|
+
moveTask(store.handle, input.taskId, "giveUp", input.now);
|
|
63
64
|
store.enqueueNotification({
|
|
64
65
|
source: { run: input.run },
|
|
65
66
|
dedupeKey: `vanished:${input.run}`,
|
package/dist/workspace-ui.d.ts
CHANGED
|
@@ -314,3 +314,72 @@ export type BuildProgress = {
|
|
|
314
314
|
export declare function buildProgressOf(steps: readonly BuildStep[] | null | undefined): BuildProgress | null;
|
|
315
315
|
/** Earlier attempts that stopped before the one now running, in one quiet line. */
|
|
316
316
|
export declare function earlierAttemptsWords(count: number): string;
|
|
317
|
+
/** One row of a task page's steps (docs/plans/task-page-steps.md): the step's label, how its marker reads, one plain
|
|
318
|
+
* sentence and a time, whether it has something to open, and whether it starts open. `mark`: done (an ink disc with
|
|
319
|
+
* a check), needs (waits on this person: ink), live (something runs now: blue), wait (current, waiting on nothing
|
|
320
|
+
* this person does), stuck (stopped there: vermilion), skipped (a dashed ring), todo (not reached: grey). */
|
|
321
|
+
export type StepRow = {
|
|
322
|
+
key: "plan" | "approve" | "build" | "checks" | "ready";
|
|
323
|
+
label: string;
|
|
324
|
+
state: "done" | "current" | "stuck" | "skipped" | "todo";
|
|
325
|
+
mark: "done" | "needs" | "live" | "wait" | "stuck" | "skipped" | "todo";
|
|
326
|
+
sentence: string;
|
|
327
|
+
/** When the step happened (ISO), shown as a clock time; null when nothing recorded says. */
|
|
328
|
+
at: string | null;
|
|
329
|
+
needsYou: boolean;
|
|
330
|
+
/** It holds something to read or do: a finished row opens on press, the current one is open. */
|
|
331
|
+
opens: boolean;
|
|
332
|
+
open: boolean;
|
|
333
|
+
};
|
|
334
|
+
/** What `stepRowsOf` reads: facts the task page's view already carries. */
|
|
335
|
+
export type StepRowFacts = {
|
|
336
|
+
journey?: readonly {
|
|
337
|
+
key: StepRow["key"];
|
|
338
|
+
label: string;
|
|
339
|
+
state: StepRow["state"];
|
|
340
|
+
needsYou?: true;
|
|
341
|
+
say?: string;
|
|
342
|
+
}[] | null;
|
|
343
|
+
scout: boolean;
|
|
344
|
+
status: {
|
|
345
|
+
status: {
|
|
346
|
+
headline: string;
|
|
347
|
+
};
|
|
348
|
+
attempts: readonly unknown[];
|
|
349
|
+
} | null;
|
|
350
|
+
/** Builds that stopped before this one: with more than one version, the Build row's history. */
|
|
351
|
+
earlier?: {
|
|
352
|
+
attempts: readonly unknown[];
|
|
353
|
+
} | null;
|
|
354
|
+
approved?: {
|
|
355
|
+
by: string;
|
|
356
|
+
at: string;
|
|
357
|
+
automatic: boolean;
|
|
358
|
+
rule: string | null;
|
|
359
|
+
} | null;
|
|
360
|
+
approve?: {
|
|
361
|
+
heading: string;
|
|
362
|
+
} | null;
|
|
363
|
+
approval?: string;
|
|
364
|
+
progress?: {
|
|
365
|
+
line: string;
|
|
366
|
+
} | null;
|
|
367
|
+
plan?: {
|
|
368
|
+
lead: string | null;
|
|
369
|
+
by: string | null;
|
|
370
|
+
at: string | null;
|
|
371
|
+
planning: boolean;
|
|
372
|
+
updating?: boolean;
|
|
373
|
+
} | null;
|
|
374
|
+
result?: {
|
|
375
|
+
selected: unknown;
|
|
376
|
+
} | null;
|
|
377
|
+
thread?: readonly {
|
|
378
|
+
kind: string;
|
|
379
|
+
at: string;
|
|
380
|
+
}[];
|
|
381
|
+
release?: unknown;
|
|
382
|
+
};
|
|
383
|
+
/** The task page's rows, from its road (first-run.ts taskRoad) and the facts beside it. Null when the task has no road
|
|
384
|
+
* (cancelled, or no status): the page keeps its status block then. Pure: no clock, no store. */
|
|
385
|
+
export declare function stepRowsOf(view: StepRowFacts): StepRow[] | null;
|
package/dist/workspace-ui.js
CHANGED
|
@@ -23,6 +23,7 @@ import { manualReviewOnly, plainReasonWords } from "./proof.js";
|
|
|
23
23
|
import { CHECK_COMMAND_CHANGED, CHECK_UNREADABLE, OPEN_RESULT, approvedCheckOf, approvedCheckStateOf, plainReasonOf, stageOfCode, taskStatusOf, workToneOf } from "./task-status.js";
|
|
24
24
|
import { whenUtc } from "./when-html.js";
|
|
25
25
|
import { NEEDS } from "./needs-you.js";
|
|
26
|
+
import { wasInterrupted } from "./run-ending.js";
|
|
26
27
|
import { html, postForm } from "./html.js";
|
|
27
28
|
export const WORK_VIEWS = [
|
|
28
29
|
{ key: "all", label: "All", hint: "Every task in view, most urgent first.", empty: "Nothing is in progress. Describe work in chat or add a task, and it appears here." },
|
|
@@ -50,7 +51,7 @@ export function reviewFactsOf(retry, reviewerAlive, automaticPending = false) {
|
|
|
50
51
|
attempts: retry.attempts.length,
|
|
51
52
|
retriesRemaining: retry.retriesRemaining,
|
|
52
53
|
latestReason: retry.latest?.reason ?? null,
|
|
53
|
-
interrupted: retry.latest !== null && (retry.latest
|
|
54
|
+
interrupted: retry.latest !== null && wasInterrupted(retry.latest),
|
|
54
55
|
queuedBy: retry.openRequest?.requestedBy ?? null,
|
|
55
56
|
queuedOrigin: retry.openRequest?.origin ?? null,
|
|
56
57
|
reviewerAlive: retry.live !== null && reviewerAlive(retry.live.runner),
|
|
@@ -623,3 +624,106 @@ export function buildProgressOf(steps) {
|
|
|
623
624
|
export function earlierAttemptsWords(count) {
|
|
624
625
|
return `${count} earlier attempt${count === 1 ? "" : "s"} stopped`;
|
|
625
626
|
}
|
|
627
|
+
/** The task page's rows, from its road (first-run.ts taskRoad) and the facts beside it. Null when the task has no road
|
|
628
|
+
* (cancelled, or no status): the page keeps its status block then. Pure: no clock, no store. */
|
|
629
|
+
export function stepRowsOf(view) {
|
|
630
|
+
const road = view.journey ?? null;
|
|
631
|
+
if (road === null || road.length === 0)
|
|
632
|
+
return null;
|
|
633
|
+
const headline = view.status?.status.headline ?? null;
|
|
634
|
+
const runs = headline === "Planning" || headline === "Building";
|
|
635
|
+
const approvalOpen = view.approve != null || road.some(step => step.key === "approve" && step.state === "current");
|
|
636
|
+
const hasResult = view.result != null;
|
|
637
|
+
// Earlier versions or stopped builds: the Build row holds them, and opens for them before the next build starts.
|
|
638
|
+
const hasHistory = (view.status?.attempts.length ?? 0) > 1 || (view.earlier?.attempts.length ?? 0) > 0;
|
|
639
|
+
const lastResult = [...(view.thread ?? [])].filter(one => one.kind === "result").map(one => one.at).sort().at(-1) ?? null;
|
|
640
|
+
const checksStuck = road.some(step => step.key === "checks" && step.state === "stuck");
|
|
641
|
+
// A step stopped there, in a word (its box under it says why).
|
|
642
|
+
const stoppedWord = headline === "Failed" ? "Failed" : headline === "Stopped" ? "Stopped" : headline === "Waiting" || headline === "Queued" ? "Waiting" : "Stopped here";
|
|
643
|
+
const words = (step) => {
|
|
644
|
+
const here = step.state === "current" || step.state === "stuck";
|
|
645
|
+
switch (step.key) {
|
|
646
|
+
case "plan":
|
|
647
|
+
if (step.state === "done" || step.state === "skipped") {
|
|
648
|
+
const by = view.plan?.by ?? null;
|
|
649
|
+
if (approvalOpen)
|
|
650
|
+
return { sentence: by === null ? "Shown below" : `Written by ${by} · shown below`, at: view.plan?.at ?? null };
|
|
651
|
+
return { sentence: view.plan?.lead ?? (by === null ? "Plan written" : `Written by ${by}`), at: view.plan?.at ?? null };
|
|
652
|
+
}
|
|
653
|
+
// Stopped at Plan with a plan already written (a held task whose plan waits for approval): it says it stopped, never "No plan yet".
|
|
654
|
+
if (here)
|
|
655
|
+
return { sentence: view.plan?.updating === true ? "Updating the plan" : view.plan?.planning === true || headline === "Planning" ? "Writing the plan" : step.needsYou ? ""
|
|
656
|
+
: step.state === "stuck" && view.approve != null ? stoppedWord : "No plan yet", at: null };
|
|
657
|
+
return { sentence: "A plan for you to approve", at: null };
|
|
658
|
+
case "approve":
|
|
659
|
+
if (step.state === "done") {
|
|
660
|
+
const by = view.approved ?? null;
|
|
661
|
+
return { sentence: by === null ? "Approved" : by.automatic ? "Approved automatically" : `Approved by ${by.by}`, at: by?.at ?? null };
|
|
662
|
+
}
|
|
663
|
+
if (step.state === "skipped")
|
|
664
|
+
return { sentence: "No approval was asked for", at: null };
|
|
665
|
+
if (here)
|
|
666
|
+
return { sentence: step.needsYou ? "" : "Waiting for approval", at: null };
|
|
667
|
+
return { sentence: view.scout ? "You approve before it starts" : "You approve before anything is built", at: null };
|
|
668
|
+
case "build":
|
|
669
|
+
if (step.state === "done")
|
|
670
|
+
return { sentence: view.scout ? "The investigation finished" : "The build finished", at: lastResult };
|
|
671
|
+
if (step.state === "skipped")
|
|
672
|
+
return { sentence: "Didn't run", at: null };
|
|
673
|
+
// The open row's box says the rest: its head says only how the step stands.
|
|
674
|
+
if (here)
|
|
675
|
+
return { sentence: step.needsYou ? "" : runs ? "Working now" : step.state === "stuck" ? stoppedWord : "Waiting to start", at: null };
|
|
676
|
+
return { sentence: `${approvalOpen ? "Starts when you approve" : "Starts after approval"}${hasHistory ? ". Earlier attempts are here." : ""}`, at: null };
|
|
677
|
+
case "checks": {
|
|
678
|
+
const said = step.say ?? null;
|
|
679
|
+
if (step.state === "done")
|
|
680
|
+
return { sentence: "Project checks passed", at: null };
|
|
681
|
+
if (step.state === "skipped")
|
|
682
|
+
return { sentence: said === "Checks not run" ? "The checks didn't run" : said === "Checks at release" ? "Checked at release" : "No checks were recorded", at: null };
|
|
683
|
+
if (step.state === "stuck")
|
|
684
|
+
return { sentence: step.needsYou && said === null ? "" : said === "Checks unreadable" ? "The checks couldn't be read" : said === "Checks failed" ? "The checks failed" : stoppedWord, at: null };
|
|
685
|
+
if (step.state === "current")
|
|
686
|
+
return { sentence: said === "Checks running" ? "The checks are running" : step.needsYou ? "" : "Waiting for the checks", at: null };
|
|
687
|
+
return { sentence: "The project's checks run on the result", at: null };
|
|
688
|
+
}
|
|
689
|
+
case "ready":
|
|
690
|
+
if (step.state === "done")
|
|
691
|
+
return { sentence: headline === "Complete" ? "Finished" : view.scout ? "Report ready" : "Ready", at: lastResult };
|
|
692
|
+
if (here)
|
|
693
|
+
return { sentence: step.needsYou ? "" : step.state === "stuck" ? stoppedWord : "Waiting", at: null };
|
|
694
|
+
if (hasResult)
|
|
695
|
+
return { sentence: checksStuck ? "Not ready: the checks didn't pass. The result is here." : "Not ready yet. The result so far is here.", at: null };
|
|
696
|
+
return { sentence: view.scout ? "A report for you to read" : "A result for you to review", at: null };
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
const rows = road.map((step) => {
|
|
700
|
+
const said = words(step);
|
|
701
|
+
const here = step.state === "current" || step.state === "stuck";
|
|
702
|
+
const needsYou = step.needsYou === true && here;
|
|
703
|
+
const mark = step.state === "done" ? "done" : step.state === "skipped" ? "skipped" : step.state === "todo" ? "todo"
|
|
704
|
+
// Vermilion only for a failure: a stopped or held step waits, in grey.
|
|
705
|
+
: needsYou ? "needs" : step.state === "stuck" ? (headline === "Failed" || step.say === "Checks failed" || step.say === "Checks unreadable" ? "stuck" : "wait")
|
|
706
|
+
: runs || step.say === "Checks running" ? "live" : "wait";
|
|
707
|
+
// A step not reached opens only when it holds something already there: a result before Ready was reached, or
|
|
708
|
+
// earlier attempts before the next build starts.
|
|
709
|
+
// An approval this person can give is always on the page, open, wherever the road stands (a held task stops at
|
|
710
|
+
// Plan with its plan still waiting for a yes): the Approval row holds its form.
|
|
711
|
+
const approvalHere = step.key === "approve" && view.approve != null;
|
|
712
|
+
const opens = step.state !== "todo" || (step.key === "ready" && hasResult) || (step.key === "build" && hasHistory) || approvalHere;
|
|
713
|
+
return { key: step.key, label: step.label, state: step.state, mark, sentence: said.sentence, at: said.at, needsYou, opens, open: here || approvalHere };
|
|
714
|
+
});
|
|
715
|
+
// A finished road whose task still waits on this person (a result to review): the last step it reached is theirs.
|
|
716
|
+
const waitsOnViewer = headline === "Ready for review" || headline === "Needs you";
|
|
717
|
+
if (waitsOnViewer && !rows.some(row => row.state === "current" || row.state === "stuck")) {
|
|
718
|
+
const last = [...rows].reverse().find(row => row.state === "done");
|
|
719
|
+
if (last !== undefined)
|
|
720
|
+
Object.assign(last, { state: "current", mark: "needs", needsYou: true, sentence: "", at: null, open: true, opens: true });
|
|
721
|
+
}
|
|
722
|
+
// Nothing happening (a finished task): the last step it reached is the one open.
|
|
723
|
+
if (!rows.some(row => row.open)) {
|
|
724
|
+
const last = [...rows].reverse().find(row => row.opens && row.state !== "todo");
|
|
725
|
+
if (last !== undefined)
|
|
726
|
+
last.open = true;
|
|
727
|
+
}
|
|
728
|
+
return rows;
|
|
729
|
+
}
|
package/dist/worktree.js
CHANGED
|
@@ -34,6 +34,7 @@ import { HANDOFF_PREFIX, MAILBOX_SUFFIX, looksLikeProtocolFile, readMailbox } fr
|
|
|
34
34
|
import { parseHandoff } from "./decision.js";
|
|
35
35
|
import { diskBytes } from "./storage.js";
|
|
36
36
|
import { markNeverIndex } from "./never-index.js";
|
|
37
|
+
import { endedInterrupted } from "./run-ending.js";
|
|
37
38
|
import { checkoutGit, gitIncidentRecord, teardownGit } from "./teardown-git.js";
|
|
38
39
|
/**
|
|
39
40
|
* The note a lease leaves in the checkout, naming the process holding it.
|
|
@@ -598,7 +599,7 @@ export class WorktreePool {
|
|
|
598
599
|
// failed/interrupted source required here.
|
|
599
600
|
if (prior.parentRun !== null) {
|
|
600
601
|
const source = this.store.getRun(prior.parentRun);
|
|
601
|
-
if (source
|
|
602
|
+
if (source !== null && endedInterrupted(source)) {
|
|
602
603
|
return { runId: source.id, kind: "partial" };
|
|
603
604
|
}
|
|
604
605
|
}
|
|
@@ -619,7 +620,7 @@ export class WorktreePool {
|
|
|
619
620
|
if (unhanded !== null && unhanded.outcome === "failed" && unhanded.reason === "no-handoff") {
|
|
620
621
|
return brokeTree(attempt) || brokeTree(later) ? null : { runId: unhanded.id, kind: "partial" };
|
|
621
622
|
}
|
|
622
|
-
if (prior.outcome !== null && (prior
|
|
623
|
+
if (prior.outcome !== null && !endedInterrupted(prior))
|
|
623
624
|
return null;
|
|
624
625
|
let names;
|
|
625
626
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.101",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|