@botbuddy/cli 1.66.1 → 1.68.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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.66.1",
3
- "source_commit": "186cf2389659c39f7a7c4caa3f6fda5bdc54d670",
4
- "source_sha256": "9809bace87147ec12fee985d3b4877bbefd1b39eabc5f0f047df553f0afcd3a9",
2
+ "release_version": "1.68.0",
3
+ "source_commit": "dacd86b04baacbb89711e0a72c52ee036fd17721",
4
+ "source_sha256": "f857817d22c1d475d9b53f86b0ea73f2e6e0af18f306b1d079b21cc844413260",
5
5
  "target": "darwin-arm64",
6
- "sha256": "c3afe8d4c99efe77db044d4d7bb4aef532436e46cdc75179b0e0a847b3e8b190"
6
+ "sha256": "3a9d33c8cfd04bd4844025552464c02b55399fab1c5352fdc86799a62c8bec5e"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.66.1",
3
- "source_commit": "186cf2389659c39f7a7c4caa3f6fda5bdc54d670",
4
- "source_sha256": "9809bace87147ec12fee985d3b4877bbefd1b39eabc5f0f047df553f0afcd3a9",
2
+ "release_version": "1.68.0",
3
+ "source_commit": "dacd86b04baacbb89711e0a72c52ee036fd17721",
4
+ "source_sha256": "f857817d22c1d475d9b53f86b0ea73f2e6e0af18f306b1d079b21cc844413260",
5
5
  "target": "linux-x64",
6
- "sha256": "d8756014b7ba81cdb244425c82728e42892a5ce701fb435abd65ac8cb2743403"
6
+ "sha256": "15fde9d3dd872809e9cc0aead517e7aef9a5398af55b4543269cdd90a6ffc96b"
7
7
  }
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "schema_version": 1,
3
- "release_version": "1.66.1",
4
- "source_commit": "186cf2389659c39f7a7c4caa3f6fda5bdc54d670",
5
- "source_sha256": "9809bace87147ec12fee985d3b4877bbefd1b39eabc5f0f047df553f0afcd3a9",
3
+ "release_version": "1.68.0",
4
+ "source_commit": "dacd86b04baacbb89711e0a72c52ee036fd17721",
5
+ "source_sha256": "f857817d22c1d475d9b53f86b0ea73f2e6e0af18f306b1d079b21cc844413260",
6
6
  "targets": {
7
7
  "darwin-arm64": {
8
8
  "goos": "darwin",
9
9
  "goarch": "arm64",
10
10
  "binary": "daemon/bin/darwin-arm64/botbuddy-daemon",
11
- "sha256": "c3afe8d4c99efe77db044d4d7bb4aef532436e46cdc75179b0e0a847b3e8b190",
11
+ "sha256": "3a9d33c8cfd04bd4844025552464c02b55399fab1c5352fdc86799a62c8bec5e",
12
12
  "mode": "0755"
13
13
  },
14
14
  "linux-x64": {
15
15
  "goos": "linux",
16
16
  "goarch": "amd64",
17
17
  "binary": "daemon/bin/linux-x64/botbuddy-daemon",
18
- "sha256": "d8756014b7ba81cdb244425c82728e42892a5ce701fb435abd65ac8cb2743403",
18
+ "sha256": "15fde9d3dd872809e9cc0aead517e7aef9a5398af55b4543269cdd90a6ffc96b",
19
19
  "mode": "0755"
20
20
  }
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.66.1",
3
+ "version": "1.68.0",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,12 @@
20
20
  "daemon/",
21
21
  "daemon-release/",
22
22
  "src/",
23
- "!src/**/*.test.mjs"
23
+ "!src/**/*.test.mjs",
24
+ "claude-plugin/.claude-plugin/",
25
+ "claude-plugin/bb-status/.claude-plugin/",
26
+ "claude-plugin/bb-status/hooks/hooks.json",
27
+ "claude-plugin/bb-status/hooks/register.js",
28
+ "claude-plugin/bb-status/README.md"
24
29
  ],
25
30
  "keywords": [
26
31
  "mcp",
package/src/commands.mjs CHANGED
@@ -19,6 +19,8 @@ import { cmdRun } from "./run.mjs";
19
19
  import { cmdTest } from "./test-lane.mjs";
20
20
  import { cmdEvidence } from "./evidence.mjs";
21
21
  import { cmdFeedback } from "./feedback.mjs";
22
+ import { cmdReleaseNotes } from "./release-notes.mjs";
23
+ import { cmdStatusBand, installStatusBand, statusBandLine } from "./status-band.mjs";
22
24
  import { runWait } from "./wait.mjs";
23
25
  import { SETUP_BLOCK } from "./setup-block.mjs";
24
26
  import { green, red, cyan, dim, bold, die } from "./utils.mjs";
@@ -63,6 +65,7 @@ export function shouldCheckForUpdates(argv) {
63
65
  export const LOCAL_HELP_COMMANDS = Object.freeze([
64
66
  "wait", "stack", "pw", "docker", "run", "mcp", "login", "setup", "doctor", "host",
65
67
  "staging-pool", "daemon", "evidence", "feedback", "usage", "telemetry", "allocation",
68
+ "release-notes", "status-band",
66
69
  ]);
67
70
  const HELP_WORDS = new Set(["help", "--help", "-h"]);
68
71
 
@@ -143,6 +146,10 @@ export async function run(argv, {
143
146
  case "test": return cmdTest(args);
144
147
  case "evidence": return cmdEvidence(args);
145
148
  case "feedback": return cmdFeedback(args);
149
+ // BOT-3156: governed release notes (submit one note through submit_release_note).
150
+ case "release-notes": return cmdReleaseNotes(args);
151
+ // BOT-3152: the bb-status band's host side (read/relay) and its Claude Code install.
152
+ case "status-band": return cmdStatusBand(args);
146
153
  case "test-ingest": return cmdTestIngest(args);
147
154
  case "wait": return runWait(args);
148
155
  case "pw": return runPw(args);
@@ -329,6 +336,10 @@ ${bold("DURABLE WORKLOADS")} (${cyan("bb help run")})
329
336
  ${bold("EVIDENCE")} (${cyan("bb help evidence")})
330
337
  feedback init|submit --file <report.json>
331
338
  Prepare or send a BotBuddy issue report
339
+ release-notes submit <note.json>
340
+ Submit one release note for your tenant (stored as draft)
341
+ status-band install Install the bb-status band into Claude Code (bb setup does it)
342
+ status-band read|relay The band's own reads and live wake (run by the mod)
332
343
  evidence require <file> Record an exact-head evidence disposition → botbuddy/ready
333
344
  evidence publish <file> Publish a local evidence statement → botbuddy/ready check-run
334
345
 
@@ -839,6 +850,8 @@ export async function cmdSetup(args, {
839
850
  selfHeal = selfHealAgentSession,
840
851
  callerResolver = resolveCaller,
841
852
  usageAgent = installUsageAgent,
853
+ // BOT-3152: setup also installs the bb-status band into Claude Code, once.
854
+ statusBand = installStatusBand,
842
855
  // BOT-2599: setup also gives this clone's SSH push transport a keepalive.
843
856
  gitTransport = async (opts) => checkGitTransport(opts),
844
857
  cwd = process.cwd(),
@@ -953,6 +966,7 @@ export async function cmdSetup(args, {
953
966
  }
954
967
  log(`bb setup: ${tenant} is ready (${session.source} worktree session). Run bb wait '<condition>'.`);
955
968
  await reportGitTransport(gitTransport, { cwd, env, log });
969
+ await reportStatusBand(statusBand, { log });
956
970
  await reportUsageAgent(usageAgent, { cwd, log });
957
971
  return 0;
958
972
  } catch (error) {
@@ -961,6 +975,13 @@ export async function cmdSetup(args, {
961
975
  }
962
976
  }
963
977
 
978
+ // BOT-3152: never fails setup — the worktree session is ready either way.
979
+ async function reportStatusBand(statusBand, { log }) {
980
+ let result;
981
+ try { result = await statusBand(); } catch (error) { result = { status: "failed", reason: error?.code ?? "install_failed" }; }
982
+ log(`bb setup: ${statusBandLine(result)}`);
983
+ }
984
+
964
985
  // BOT-1021: setup also (re)installs the daily usage sweep. Never fails setup —
965
986
  // the worktree session is ready either way; the line says what happened.
966
987
  async function reportUsageAgent(usageAgent, { cwd, log }) {
@@ -0,0 +1,102 @@
1
+ // BOT-3156: `bb release-notes submit <note.json>` — submit one governed release
2
+ // note through the MCP tool `submit_release_note`. The server is the only
3
+ // validator: the CLI sends the file's JSON unchanged (no rewriting, no filling
4
+ // in), records the route as `cli`, and prints the stored source key, state and
5
+ // revision, or the named refusal. The tenant is the authenticated agent's.
6
+ import { readFile } from "node:fs/promises";
7
+ import { CALL_EXIT, callToolJson, resolveCallAuth } from "./api.mjs";
8
+
9
+ export const RELEASE_NOTES_USAGE = `Usage: bb release-notes submit <note.json> [--json]
10
+
11
+ Submits one release note for your tenant. The file is a schema-1 payload:
12
+
13
+ { "schema_version": 1,
14
+ "source_key": "BOT-1234",
15
+ "type": "Feature",
16
+ "internal": { "title": "…", "body": "…" },
17
+ "external": { "title": "…", "body": "…" },
18
+ "internal_links": [ { "name": "PR #12", "url": "https://github.com/…", "kind": "pr" } ],
19
+ "external_links": [ { "name": "Open the report", "url": "/reports?tab=x", "kind": "deep_link" } ] }
20
+
21
+ type is one of Feature, Improvement, Fix, Security, Deprecation, Internal.
22
+ external and both link lists are optional. A body uses bbrn-md-1: paragraphs,
23
+ "- " lists, **bold**, *italic* and \`code\`; a backslash escapes only \\ * \` and <.
24
+ No HTML and no links inside a body. A link url is https://… or an app path
25
+ starting with one /. Write the text yourself: BotBuddy stores exactly what you
26
+ submit.
27
+
28
+ Submitting the same source_key again updates the note and adds a revision.
29
+ An unchanged resubmit (a retry) adds none. A refusal names its code
30
+ (schema_invalid, type_unknown, markup_refused, link_refused,
31
+ entitlement_missing) and stores nothing.
32
+
33
+ Exit codes: 0 stored · 1 refused · 2 usage · 3 authentication · 5 BotBuddy unreachable.`;
34
+
35
+ const TOOL = "submit_release_note";
36
+
37
+ export async function cmdReleaseNotes(args, {
38
+ read = readFile,
39
+ resolveAuth = resolveCallAuth,
40
+ call = callToolJson,
41
+ log = console.log,
42
+ errorLog = console.error,
43
+ } = {}) {
44
+ if (args.length === 0 || ["help", "--help", "-h"].includes(args[0])) {
45
+ (args.length === 0 ? errorLog : log)(RELEASE_NOTES_USAGE);
46
+ return args.length === 0 ? 2 : CALL_EXIT.OK;
47
+ }
48
+ const json = args.includes("--json");
49
+ const rest = args.filter((a) => a !== "--json");
50
+ if (rest[0] !== "submit" || rest.length !== 2 || !rest[1] || rest[1].startsWith("-")) {
51
+ errorLog(RELEASE_NOTES_USAGE);
52
+ return 2;
53
+ }
54
+ const file = rest[1];
55
+ let note;
56
+ try {
57
+ note = JSON.parse(await read(file, "utf8"));
58
+ } catch {
59
+ errorLog(`Could not read ${file} as JSON; nothing was submitted.`);
60
+ return 2;
61
+ }
62
+
63
+ let result;
64
+ try {
65
+ const resolved = await resolveAuth({ toolName: TOOL, preferSession: true });
66
+ result = resolved?.error || !resolved?.auth
67
+ ? { ok: false, auth: true }
68
+ : await call(TOOL, { note, route: "cli" }, { auth: resolved.auth, signal: AbortSignal.timeout(30_000) });
69
+ } catch {
70
+ result = { ok: false, status: null };
71
+ }
72
+
73
+ const data = result?.data;
74
+ if (result?.ok && data?.ok === true && typeof data.source_key === "string" && Number.isInteger(data.revision)) {
75
+ const receipt = { ok: true, source_key: data.source_key, state: data.state, revision: data.revision, created: data.created === true, changed: data.changed === true, tenant: data.tenant ?? null };
76
+ if (json) log(JSON.stringify(receipt));
77
+ else {
78
+ const what = receipt.created ? "stored" : receipt.changed ? "updated" : "unchanged (no new revision)";
79
+ log(`Release note ${receipt.source_key} ${what}: state ${receipt.state}, revision ${receipt.revision}${receipt.tenant ? `, tenant ${receipt.tenant}` : ""}.`);
80
+ }
81
+ return CALL_EXIT.OK;
82
+ }
83
+ if (result?.ok && data?.ok === false && typeof data.error === "string") {
84
+ const refusal = { ok: false, error: data.error, ...(data.field ? { field: data.field } : {}), message: data.message ?? null };
85
+ if (json) log(JSON.stringify(refusal));
86
+ errorLog(`✗ ${refusal.error}${refusal.field ? ` (${refusal.field})` : ""}: ${refusal.message ?? "Nothing was stored."}`);
87
+ return CALL_EXIT.ERROR;
88
+ }
89
+ if (result?.auth) {
90
+ if (json) log(JSON.stringify({ ok: false, error: "authentication_failed" }));
91
+ errorLog("✗ BotBuddy could not authenticate this command. Run bb doctor --fix in this worktree, then retry. Nothing was stored.");
92
+ return CALL_EXIT.AUTH;
93
+ }
94
+ if (!result?.ok && result?.status == null) {
95
+ if (json) log(JSON.stringify({ ok: false, error: "transport_unavailable" }));
96
+ errorLog("✗ BotBuddy is unreachable. Retry the same command; an unchanged resubmit adds no revision.");
97
+ return CALL_EXIT.TRANSPORT;
98
+ }
99
+ if (json) log(JSON.stringify({ ok: false, error: "receipt_unavailable" }));
100
+ errorLog("✗ BotBuddy did not confirm the submit. Retry the same command; an unchanged resubmit adds no revision.");
101
+ return CALL_EXIT.ERROR;
102
+ }