bostrat-node 1.16.5 → 1.16.6
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/bin/bostrat-estimate +4 -3
- package/dist/cli.js +87 -77
- package/dist/index.js +318 -293
- package/package.json +1 -1
package/bin/bostrat-estimate
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
${
|
|
4
|
-
${
|
|
2
|
+
var O=process.env.BOSTRAT_AGENT_URL||`http://127.0.0.1:${process.env.BOSTRAT_AGENT_PORT||3362}`;async function $(t,a,o){let l=await fetch(`${O}${a}`,{method:t,headers:o?{"content-type":"application/json"}:void 0,body:o?JSON.stringify(o):void 0}),s=await l.json().catch(()=>({}));if(!l.ok)throw new Error(s.error||`HTTP ${l.status}`);return s}function r(t){console.error(`[bostrat-estimate] ${t}`),process.exit(1)}function f(t){let a=Number(t)||0;if(a<60)return`${a}m`;let o=a/60;if(o<8)return`${Number.isInteger(o)?o:o.toFixed(1)}h`;let l=o/8;return`${Number.isInteger(l)?l:l.toFixed(1)}d`}var y=t=>t?new Date(t).toISOString().slice(0,10):"?",n=process.argv.slice(2),h=n.shift(),T='usage: bostrat-estimate record --human <dur> --agent <dur> --scope "<one line, \u2264160 chars>" [--bp BP-XX-I3 | --ticket KEY-123]',b='usage: bostrat-estimate close <estimate-id> [--note "<what landed>"] (or: close --latest)',E="usage: bostrat-estimate prior [--category <c>] [--model <m>] [--provider <p>] [--window all|24h|7d|30d] [--json]",m=t=>f(Math.round((Number(t)||0)/6e4));function S(t,a){if(!t.n)return"n=0 \u2014 no merged task matched";if(t.belowFloor)return`n=${t.n} merged task${t.n===1?"":"s"} \u2014 under the sample floor of ${a}, no figure`;let o=t.activeMs;return`n=${t.n} merged tasks \xB7 agent-active median ${m(o.p50)} \xB7 range ${m(o.min)}\u2013${m(o.max)} (p25 ${m(o.p25)} \xB7 p75 ${m(o.p75)}) \xB7 wall-clock median ${m(t.wallMs.p50)} \xB7 prompts median ${t.prompts.p50}`}if(h==="record"){let t=null,a=null,o=null,l=null,s=null,i=[];for(let e=0;e<n.length;e++)n[e]==="--human"?t=n[++e]:n[e]==="--agent"?a=n[++e]:n[e]==="--scope"?o=n[++e]:n[e]==="--bp"?l=n[++e]:n[e]==="--ticket"?s=n[++e]:i.push(n[e]);!o&&i.length&&(o=i.join(" ").trim()),(!t||!a||!o)&&r(T);try{let e=await $("POST","/api/estimates/record",{cwd:process.cwd(),human:t,agent:a,scope:o,bp:l,ticket:s}),c=e.bp?` for ${e.bp}`:e.ticket?` for ${e.ticket}`:"",d=[e.provider,e.model].filter(Boolean).join(" \xB7 ");console.log(`[bostrat-estimate] recorded ${e.estimateId}${c}: human ${f(e.human.minutes)} \xB7 agent-active ${f(e.agent.minutes)}${d?` (${d})`:""}`),console.log(` \u201C${e.scope}\u201D \u2014 close it when the work lands: bostrat-estimate close ${e.estimateId}`)}catch(e){r(`${e.message}
|
|
3
|
+
${T}`)}}else if(h==="close"){let t=null,a=!1,o="";for(let l=0;l<n.length;l++)n[l]==="--latest"?a=!0:n[l]==="--note"?o=n[++l]:!t&&!n[l].startsWith("--")?t=n[l]:r(b);!t&&!a&&r(b);try{let l=await $("POST","/api/estimates/close",{cwd:process.cwd(),estimateId:t,latest:a,note:o});console.log(`[bostrat-estimate] closed ${l.estimateId} \u2014 \u201C${l.scope}\u201D`),console.log(" Its actuals are folded from this Track's own log and held against the estimate.")}catch(l){r(`${l.message}
|
|
4
|
+
${b}`)}}else if(h==="ls"){let t=n.includes("--open");try{let{estimates:a,counts:o}=await $("GET",`/api/estimates?cwd=${encodeURIComponent(process.cwd())}`),l=t?a.filter(s=>s.status==="open"):a;l.length||console.log(`[bostrat-estimate] ${t?"no open estimates":"nothing recorded"} on this Track yet`);for(let s of l){let i=s.status==="open"?"o":s.status==="expired"?"!":"x",e=s.bp||s.ticket||"",c=[s.provider,s.model].filter(Boolean).join(" \xB7 ");console.log(`${i} ${s.estimateId.padEnd(13)} ${y(s.recordedAt)} human ${f(s.human?.minutes).padEnd(6)} agent ${f(s.agent?.minutes).padEnd(6)} ${e.padEnd(12)} ${s.scope}`);let d=s.status==="closed"?`closed ${y(s.closedAt)} (${s.closeReason}${s.closeRef?` ${s.closeRef}`:""})`:s.status==="expired"?`expired ${y(s.closedAt)} \u2014 unclosed`:null,p=[c,d?`${d}${s.closeNote?` \u2014 ${s.closeNote}`:""}`:null].filter(Boolean).join(" \xB7 ");p&&console.log(` ${p}`)}if(o&&l.length){let s=o.unclosed??o.open+(o.expired||0);console.log(` (${o.open} open \xB7 ${o.closed} closed${o.expired?` \xB7 ${o.expired} expired`:""} \u2014 ${s} unclosed)`)}}catch(a){r(a.message)}}else if(h==="prior"){let t=null,a=null,o=null,l=null,s=!1;for(let e=0;e<n.length;e++)n[e]==="--category"?t=n[++e]:n[e]==="--model"?a=n[++e]:n[e]==="--provider"?o=n[++e]:n[e]==="--window"?l=n[++e]:n[e]==="--json"?s=!0:!t&&!n[e].startsWith("--")?t=n[e]:r(E);let i=new URLSearchParams({cwd:process.cwd()});for(let[e,c]of Object.entries({category:t,model:a,provider:o,window:l}))c&&i.set(e,c);try{let e=await $("GET",`/api/estimates/prior?${i}`);if(s)console.log(JSON.stringify(e,null,2));else{let c=e.model||(e.provider?`${e.provider} (no billed turn yet \u2014 matched on the provider)`:"no calling box"),d=e.category?`${e.category} tasks`:"every category";console.log(`[bostrat-estimate] prior for ${d} on ${c} \u2014 ${e.label}, ${e.tier}; ${e.tracks.merged} merged tasks${e.window==="all"?" all-time":` in ${e.window}`}`);let p={task:e.category?`${e.category} on this model`:"this model, every category",model:"this model, every category",category:e.category?`${e.category} on every model`:null},k=new Set;for(let u of["task","model","category"]){let g=p[u],w=e.cells[u];if(!g||!w)continue;let v=JSON.stringify(w.match);k.has(v)||(k.add(v),console.log(` ${g}${u==="task"?" (estimate from this one)":""}`),console.log(` ${S(w,e.floor)}`))}!e.cells.task.n&&e.category&&Object.keys(e.categories).length&&console.log(` categories this model's merged tasks fell in: ${Object.entries(e.categories).map(([u,g])=>`${u} ${g}`).join(" \xB7 ")}`),e.partial&&console.log(` (partial: ${e.pending} Track log(s) still unfolded \u2014 run again for the full corpus)`)}}catch(e){r(`${e.message}
|
|
5
|
+
${E}`)}}else r("usage: bostrat-estimate <record|close|ls|prior> \u2026");
|