@botbuddy/cli 1.56.1 → 1.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.56.1",
3
+ "version": "1.57.0",
4
4
  "description": "BotBuddy \u2014 Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
package/src/commands.mjs CHANGED
@@ -17,6 +17,7 @@ import { cmdManagedAllocation } from "./managed-allocation.mjs";
17
17
  import { cmdDocker } from "./docker-hygiene.mjs";
18
18
  import { cmdRun } from "./run.mjs";
19
19
  import { cmdTest } from "./test-lane.mjs";
20
+ import { cmdEvidence } from "./evidence.mjs";
20
21
  import { runWait } from "./wait.mjs";
21
22
  import { SETUP_BLOCK } from "./setup-block.mjs";
22
23
  import { green, red, cyan, dim, bold, die } from "./utils.mjs";
@@ -112,6 +113,7 @@ export async function run(argv, {
112
113
  return code;
113
114
  }
114
115
  case "test": return cmdTest(args);
116
+ case "evidence": return cmdEvidence(args);
115
117
  case "test-ingest": return cmdTestIngest(args);
116
118
  case "wait": return runWait(args);
117
119
  case "pw": return runPw(args);
@@ -257,6 +259,7 @@ ${bold("BROWSER LANES")}
257
259
 
258
260
  ${bold("OTHER")}
259
261
  locks -m [--host name] Reserve typed local resources, including Playwright MCP lanes
262
+ evidence publish <file> Publish a local evidence statement → botbuddy/ready check-run
260
263
  update Install the latest @botbuddy/cli in the background via npm
261
264
  help Show this help
262
265
  version Show version
@@ -0,0 +1,85 @@
1
+ // BOT-2341: `bb evidence publish <statement.json>` — post a local-E2E evidence
2
+ // statement to the control plane (MCP `publish_evidence`), which stores it and
3
+ // creates/updates the advisory `botbuddy/ready` check-run on the PR head.
4
+ //
5
+ // Auth prefers this worktree's agent session (the tenant the repo is bound to),
6
+ // falling back to the owner/MCP-key ladder like every other `bb` call. Prints
7
+ // the tool's JSON result on stdout; a refusal exits non-zero.
8
+
9
+ import { readFileSync } from "node:fs";
10
+ import { CALL_EXIT, callToolJson as defaultCallToolJson, resolveCallAuth } from "./api.mjs";
11
+
12
+ export const EVIDENCE_EXIT = Object.freeze({ ...CALL_EXIT, USAGE: 2, REFUSED: 4 });
13
+
14
+ const USAGE = `Usage: bb evidence publish <statement.json>
15
+
16
+ Publish a local evidence statement for a PR head. BotBuddy stores it and posts
17
+ the advisory botbuddy/ready check-run. The statement is a JSON object with:
18
+ repo, pr, headSha, mainSha, base, fingerprint, requirement, runs[],
19
+ verdict ("pass" | "fail"), fixCommand
20
+
21
+ Exit codes: 0 published · 2 usage · 3 auth · 4 refused · 5 unreachable`;
22
+
23
+ export async function cmdEvidence(args, {
24
+ resolveAuth = resolveCallAuth,
25
+ callToolJson = defaultCallToolJson,
26
+ log = (line) => console.log(line),
27
+ errorLog = (line) => console.error(line),
28
+ } = {}) {
29
+ const [sub, file, ...rest] = args;
30
+ if (sub === undefined || sub === "help" || sub === "--help" || sub === "-h") {
31
+ log(USAGE);
32
+ return sub === undefined ? EVIDENCE_EXIT.USAGE : EVIDENCE_EXIT.OK;
33
+ }
34
+ if (sub !== "publish") {
35
+ errorLog(`Unknown evidence subcommand: ${sub}\n\n${USAGE}`);
36
+ return EVIDENCE_EXIT.USAGE;
37
+ }
38
+ if (!file || rest.length > 0) {
39
+ errorLog(USAGE);
40
+ return EVIDENCE_EXIT.USAGE;
41
+ }
42
+
43
+ let statement;
44
+ try {
45
+ statement = JSON.parse(readFileSync(file, "utf8"));
46
+ } catch (error) {
47
+ errorLog(`✗ Could not read ${file} as JSON: ${error?.message ?? error}`);
48
+ return EVIDENCE_EXIT.USAGE;
49
+ }
50
+
51
+ const resolved = await resolveAuth({ toolName: "publish_evidence", preferSession: true });
52
+ if (resolved?.error || !resolved?.auth) {
53
+ log(JSON.stringify(resolved?.error ?? { ok: false, auth: true, error: "not_authenticated" }, null, 2));
54
+ errorLog("✗ Not authenticated — run `bb doctor --fix` in this worktree.");
55
+ return EVIDENCE_EXIT.AUTH;
56
+ }
57
+
58
+ const res = await callToolJson("publish_evidence", { statement }, { auth: resolved.auth });
59
+ if (!res.ok) {
60
+ log(JSON.stringify(res, null, 2));
61
+ if (res.auth) {
62
+ errorLog(`✗ BotBuddy rejected the credential (${res.error_code ?? res.code ?? res.error}).`);
63
+ return EVIDENCE_EXIT.AUTH;
64
+ }
65
+ if (res.status === null || String(res.error ?? "").startsWith("transport")) {
66
+ errorLog(`✗ BotBuddy unreachable: ${res.error}`);
67
+ return EVIDENCE_EXIT.TRANSPORT;
68
+ }
69
+ errorLog(`✗ publish_evidence failed: ${res.error}`);
70
+ return EVIDENCE_EXIT.ERROR;
71
+ }
72
+
73
+ log(JSON.stringify(res.data, null, 2));
74
+ if (res.isError || res.data?.ok === false) {
75
+ errorLog(`✗ ${res.data?.error ?? "publish_evidence refused"}: ${res.data?.message ?? ""}`.trimEnd());
76
+ return EVIDENCE_EXIT.REFUSED;
77
+ }
78
+ // Codex #1169 R4: success means a receipt came back — an outage body that
79
+ // normalises to an empty result is never "published".
80
+ if (res.data?.ok !== true || typeof res.data?.receipt_id !== "string") {
81
+ errorLog("✗ BotBuddy returned no receipt — nothing is known to be published; retry.");
82
+ return EVIDENCE_EXIT.ERROR;
83
+ }
84
+ return EVIDENCE_EXIT.OK;
85
+ }