bostrat-node 1.17.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bostrat-replay +13 -1
- package/dist/cli.js +37 -37
- package/dist/index.js +312 -308
- package/package.json +1 -1
package/bin/bostrat-replay
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
2
|
+
var k=process.env.BOSTRAT_AGENT_URL||`http://127.0.0.1:${process.env.BOSTRAT_AGENT_PORT||3362}`,p=["usage: bostrat-replay census [--rows] [--json] [--budget <ms>] [--once]"," bostrat-replay spec frame <source> [--json]",' bostrat-replay spec draft <source> (--statement "<text>" | --statement-file <path>) [--suite "<cmd>"] [--note "<why>"]',' bostrat-replay spec revise <spec> (--statement "<text>" | --statement-file <path>) [--suite "<cmd>"] [--note "<what changed>"]'," bostrat-replay spec ls [--status drafted|approved|dismissed] [--json]"," bostrat-replay spec show <spec> [--json]"].join(`
|
|
3
|
+
`);function c(e){console.error(`[bostrat-replay] ${e}`),process.exit(1)}async function m(e,o,s){let t=await fetch(`${k}${o}`,{method:e,headers:s?{"content-type":"application/json"}:void 0,body:s?JSON.stringify(s):void 0}),n=await t.json().catch(()=>({}));if(!t.ok){let r=new Error(n.error||`HTTP ${t.status}`);throw r.status=t.status,r.data=n,r}return n}var $=e=>m("GET",e),j=new Set(["rows","json","once"]),O=new Set(["budget","statement","statement-file","suite","note","status"]);function w(e){let o={_:[]};for(let s=0;s<e.length;s++){let t=e[s];if(t.startsWith("--")){let n=t.slice(2);j.has(n)?o[n]=!0:O.has(n)?(s+1>=e.length&&c(`${t} needs a value
|
|
4
|
+
${p}`),o[n]=e[++s]):c(`unknown flag ${t}
|
|
5
|
+
${p}`)}else o._.push(t)}return o}var g=e=>{let o=Math.round((Number(e)||0)/6e4);return o>=60?`${(o/60).toFixed(1)}h`:`${o}m`},N=(e,o)=>o?`${Math.round(100*e/o)}%`:"\u2013",h=e=>String(e||"").slice(0,8)||"\u2013",R=e=>e?new Date(e).toISOString().slice(0,10):"\u2013",A=(e,o=96)=>{let s=String(e||"").replace(/\s+/g," ").trim();return s.length>o?`${s.slice(0,o-1)}\u2026`:s};async function E(e){let o=Number.isFinite(Number(e.budget))&&Number(e.budget)>0?Number(e.budget):8e3,s=e.rows?"1":"0",t;for(let a=0;a<120&&(t=await $(`/api/usage/replay-census?budgetMs=${o}&rows=${s}`),!(!t.partial||e.once));a++)process.stderr.write(`[bostrat-replay] census in progress \u2014 ${t.pending} still pending (${t.judged} judged)\r`);if(t||c("no answer from the agent"),e.json){console.log(JSON.stringify(t,null,2));return}let n=t.tracks,r=t.criteria,l=t.excluded;console.log(`[bostrat-replay] ${t.label}${t.partial?` \u2014 PARTIAL, ${t.pending} pending (run again)`:""}`),console.log(` eligible: ${t.eligible} of ${t.merged} merged archived Tracks (${N(t.eligible,t.merged)}) \u2014 ${n.archived} archived on this node, ${n.open} open, ${n.loops} loop runs excluded, ${n.unreadable} unreadable`),console.log(` funnel (each criterion counted on its own, over the ${t.judged} judged):`),console.log(` merged PR ${r.mergedPr}`),console.log(` recoverable base ${r.recoverableBase} (not: ${l.baseNotRecoverable}${Object.keys(t.baseReasons).length?" \u2014 "+Object.entries(t.baseReasons).map(([a,i])=>`${a} ${i}`).join(", "):""})`),console.log(` tests touched ${r.testsTouched} (not: ${l.noTestsTouched} of those with a base)`),console.log(` no ops/deploy step ${r.noOpsStep} (not: ${l.opsStep}${Object.keys(t.opsRules).length?" \u2014 "+Object.entries(t.opsRules).map(([a,i])=>`${a} ${i}`).join(", "):""})`),console.log(` not merged (archived without a merged PR): ${l.notMerged}`);let d=Object.entries(t.byRepo);if(d.length){console.log(" by repo:");for(let[a,i]of d)console.log(` ${a.padEnd(32)} merged ${String(i.merged).padStart(4)} eligible ${String(i.eligible).padStart(4)} (base ${i.baseNotRecoverable} \xB7 tests ${i.noTestsTouched} \xB7 ops ${i.opsStep})`)}let v=Object.entries(t.byCategory);v.length&&console.log(` eligible by category: ${v.map(([a,i])=>`${a} ${i}`).join(" \xB7 ")}`);let y=Object.entries(t.byModel);if(y.length&&console.log(` eligible by model: ${y.map(([a,i])=>`${a} ${i}`).join(" \xB7 ")}`),e.rows&&Array.isArray(t.rows)){console.log(` eligible Tracks (${t.rows.length}):`);for(let a of t.rows)console.log(` ${a.repo}#${a.pr} ${String(a.name||a.trackId).slice(0,48).padEnd(48)} ${a.category||"\u2013"} ${a.model||a.provider||"\u2013"} active ${g(a.activeMs)} tests ${a.tests}/${a.files} base ${h(a.base)} ${a.trackId}`)}console.log(` basis: ${t.basis.label}; ${t.basis.scope}`),console.log(` no ops/deploy step: ${t.basis.noOpsStep}`)}async function I(e){let o;for(let s=0;s<40&&(o=await $(`/api/replay/frame?source=${encodeURIComponent(e)}&budgetMs=15000`),!!o.partial);s++)process.stderr.write(`[bostrat-replay] reading the archived logs \u2014 ${o.pending} still pending\r`);return(!o||o.partial)&&c("the archived logs are still being read \u2014 run again"),o}function f(e,o=" "){console.log(`${o}source: ${e.repo}#${e.pr} \u2014 Track \u201C${e.trackName||e.trackId}\u201D (${e.trackId})`),console.log(`${o} merged ${R(e.mergedAt)} at ${h(e.mergeCommit)} \xB7 base ${h(e.base)} (the commit a sealed clone is cut at)`),console.log(`${o} ${e.category||"uncategorized"} \xB7 ${e.model||e.provider||"\u2013"} \xB7 agent-active ${g(e.activeMs)} \xB7 wall ${g(e.wallMs)} \xB7 ${e.prompts} operator prompt${e.prompts===1?"":"s"} \xB7 $${(Number(e.usd)||0).toFixed(2)} notional`)}async function x(e){let o=e._[0];o||c(`spec frame needs a <source>
|
|
6
|
+
${p}`);let s=await I(o);if(e.json){console.log(JSON.stringify(s,null,2));return}if(console.log(`[bostrat-replay] ${s.label||"replay task spec"} \u2014 frame for ${o}`),!s.eligible){console.log(` NOT replay-eligible: ${s.reasons.join(", ")}`),s.source&&f(s.source),console.log(" a spec cannot be drafted from this Track (R11: no acceptance tests, or an ops step a sealed clone cannot replay)");return}f(s.source),console.log(` acceptance: ${s.acceptance.tests.length} test path${s.acceptance.tests.length===1?"":"s"} the PR touched (of ${s.acceptance.files} files):`);for(let n of s.acceptance.tests)console.log(` ${n}`);let t=s.promptNotes||{};console.log(` operator prompts, verbatim and in order (${s.prompts.length}${t.echoesDropped?`; ${t.echoesDropped} transcript echo${t.echoesDropped===1?"":"es"} dropped`:""}${t.truncated?`; ${t.truncated} cut at 16 KB`:""}${t.omitted?`; ${t.omitted} beyond the first 200 omitted`:""}):`),s.prompts.forEach((n,r)=>{console.log(` \u2500\u2500 ${r+1}${r===0?" (the opening ask)":" (follow-up \u2014 NOT replayed)"} \xB7 ${new Date(n.at).toISOString()} \xB7 ${n.box||"\u2013"}${n.truncated?" \xB7 truncated":""}`),console.log(n.text.split(`
|
|
7
|
+
`).map(l=>` \u2502 ${l}`).join(`
|
|
8
|
+
`))}),console.log(" next: write the STATEMENT \u2014 the standalone task a replay box gets instead of this conversation (fold what the follow-ups settled into it; name the outcome the merged PR delivered, never the conversation) \u2014 and hand it in:"),console.log(` bostrat-replay spec draft ${o} --statement-file <path> [--suite "<the repo's suite command>"]`)}async function T(e){if(e.statement&&e["statement-file"]&&c("give --statement OR --statement-file, not both"),e["statement-file"]){let{readFile:o}=await import("node:fs/promises");try{return await o(e["statement-file"],"utf8")}catch(s){c(`cannot read ${e["statement-file"]}: ${s?.message||s}`)}}return e.statement||c(`a statement is required: --statement "<text>" or --statement-file <path>
|
|
9
|
+
${p}`),e.statement}function b(e){let o=e.versions[e.versions.length-1],s=e.approvedVersion!=null?`approved v${e.approvedVersion}`:"not approved";return console.log(`[bostrat-replay] spec ${e.id} v${e.version} \u2014 ${e.status} (${s}) \u2014 ${e.source.repo}#${e.source.pr} \u201C${e.source.trackName||e.source.trackId}\u201D`),o}async function M(e){let o=e._[0];o||c(`spec draft needs a <source>
|
|
10
|
+
${p}`);let s=await T(e),t;try{t=await m("POST","/api/replay/specs",{cwd:process.cwd(),source:o,statement:s,suite:e.suite??null,note:e.note??null})}catch(r){r.status===503&&r.data?.partial&&c(`${r.message}`),c(r.message)}let n=t.spec;b(n),console.log(` v1 drafted \u2014 the approval card is in this Track's chat (${n.acceptance.tests.length} acceptance test path${n.acceptance.tests.length===1?"":"s"}, ${n.prompts.length} original prompt${n.prompts.length===1?"":"s"} stored verbatim).`),console.log(" This is a BLOCKING prompt: stop and end your turn. The spec enters the bench only when the operator approves this version; to change it, `bostrat-replay spec revise "+n.id+" \u2026` (a new version, a fresh card).")}async function _(e){let o=e._[0];o||c(`spec revise needs a <spec>
|
|
11
|
+
${p}`);let s=await $(`/api/replay/specs/${encodeURIComponent(o)}`).catch(d=>c(d.message)),t=await T(e),n={cwd:process.cwd(),statement:t,note:e.note??null};e.suite!==void 0&&(n.suite=e.suite);let r;try{r=await m("POST",`/api/replay/specs/${encodeURIComponent(s.spec.id)}/revise`,n)}catch(d){c(d.message)}let l=r.spec;b(l),console.log(` v${l.version} drafted \u2014 a fresh approval card is in this Track's chat${l.approvedVersion!=null?`; v${l.approvedVersion} stays the bench entry until this one is approved`:""}.`),console.log(" This is a BLOCKING prompt: stop and end your turn.")}async function P(e){let{specs:o}=await $(`/api/replay/specs${e.status?`?status=${encodeURIComponent(e.status)}`:""}`);if(e.json){console.log(JSON.stringify(o,null,2));return}if(!o.length){console.log("[bostrat-replay] no task specs on this node yet \u2014 `bostrat-replay census --rows` lists the eligible sources");return}let s={drafted:0,approved:0,dismissed:0};for(let t of o)s[t.status]=(s[t.status]||0)+1;console.log(`[bostrat-replay] ${o.length} task spec${o.length===1?"":"s"} on this node \u2014 ${s.approved} approved (the bench) \xB7 ${s.drafted} awaiting approval \xB7 ${s.dismissed} dismissed`);for(let t of o){let n=t.versions[t.versions.length-1],r=t.status==="approved"?"+":t.status==="drafted"?"?":"-";console.log(`${r} ${t.id} v${t.version}${t.approvedVersion!=null&&t.approvedVersion!==t.version?` (approved v${t.approvedVersion})`:""} ${`${t.source.repo}#${t.source.pr}`.padEnd(28)} ${(t.source.category||"\u2013").padEnd(14)} ${(t.source.model||t.source.provider||"\u2013").padEnd(20)} active ${g(t.source.activeMs).padStart(5)} tests ${String(t.acceptance.tests.length).padStart(2)} ${A(n.statement,60)}`)}}async function C(e){let o=e._[0];o||c(`spec show needs a <spec>
|
|
12
|
+
${p}`);let{spec:s}=await $(`/api/replay/specs/${encodeURIComponent(o)}`).catch(t=>c(t.message));if(e.json){console.log(JSON.stringify(s,null,2));return}b(s),f(s.source),console.log(` acceptance: ${s.acceptance.tests.length} test path${s.acceptance.tests.length===1?"":"s"} (of ${s.acceptance.files} files the PR changed)${s.versions[s.versions.length-1].suite?` \xB7 suite: ${s.versions[s.versions.length-1].suite}`:""}`);for(let t of s.acceptance.tests)console.log(` ${t}`);console.log(` versions (${s.versions.length}):`);for(let t of s.versions)console.log(` \u2500\u2500 v${t.v} \xB7 ${t.outcome}${t.v===s.approvedVersion?" \xB7 THE BENCH ENTRY":""} \xB7 ${new Date(t.createdAt).toISOString()} \xB7 by ${t.by?.agent||"the lead"} on Track \u201C${t.by?.trackName||t.by?.trackId||"\u2013"}\u201D${t.note?` \xB7 ${t.note}`:""}${t.suite?` \xB7 suite: ${t.suite}`:""}`),console.log(t.statement.split(`
|
|
13
|
+
`).map(n=>` \u2502 ${n}`).join(`
|
|
14
|
+
`));console.log(` original prompts stored verbatim: ${s.prompts.length} (the opening ask is what the statement replaces; follow-ups are not replayed) \u2014 \`spec show ${s.id} --json\` prints them`)}var u=process.argv.slice(2),S=u.shift();try{if(S==="census")await E(w(u));else if(S==="spec"){let e=u.shift(),o=w(u);e==="frame"?await x(o):e==="draft"?await M(o):e==="revise"?await _(o):e==="ls"?await P(o):e==="show"?await C(o):c(p)}else c(p)}catch(e){c(e?.message||String(e))}
|