@botbuddy/cli 1.69.4 → 1.69.5

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.69.5
4
+
5
+ - The bb-status band shows the session's context use on its last row, for example `Context 62% · 124k of 200k`, the same figures as Claude Code's status line. It reads them once with the free `$.session.usage()` and then only when Claude Code pushes `session.measure` after a turn, so it never polls and makes no model calls. The row is hidden while Claude Code has no figure yet. The band's plugin version moves to 1.1.0, so `bb update` installs this and the 1.69.3 session-renewal wording into bands already installed at 1.0.0.
6
+
3
7
  ## 1.69.3
4
8
 
5
9
  - The bb-status band no longer goes dead with "BB session expired" when a Claude Code session outlives its ~8-hour BB session. Within two hours of expiry `bb status-band read` heartbeats the same session; after expiry it renews that same session in place (`renew_agent_session`), so its tickets and PRs stay on the band. `bb wait` and other CLI self-heals renew the session in place before minting a new one. A closed session is never revived: the band then names `bb doctor --fix`.
@@ -11,7 +11,7 @@
11
11
  "name": "bb-status",
12
12
  "source": "./bb-status",
13
13
  "description": "BB session status above the prompt: tickets, PRs with review counts, epic and staging, for BotBuddy and Supply Guard sessions",
14
- "version": "1.0.0"
14
+ "version": "1.1.0"
15
15
  }
16
16
  ]
17
17
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bb-status",
3
- "version": "1.0.0",
4
- "description": "BB session status above the prompt: tickets, PRs with review counts, epic and staging, live only while the session is on screen.",
3
+ "version": "1.1.0",
4
+ "description": "BB session status above the prompt: tickets, PRs with review counts, epic, staging and context use, live only while the session is on screen.",
5
5
  "author": {
6
6
  "name": "BotBuddy"
7
7
  },
@@ -31,6 +31,14 @@ The relay is parked, with no child and no server wait, while
31
31
  session. Once a surface draws the session again, the 5 s display tick does one
32
32
  read and re-arms the relay.
33
33
 
34
+ **BOT-3211 context row.** The last row shows the session's context use as
35
+ Claude Code's status line has it, for example `Context 62% · 124k of 200k`. The
36
+ mod reads it once with the free `$.session.usage()` (no `breakdown`) at
37
+ session start and resume, then only from the `session.measure` event the
38
+ engine pushes after each main-thread turn: no timer reads it. The row is hidden
39
+ while the runtime has no fill figure (a fresh or just-compacted session before
40
+ its next response), never shown as 0%.
41
+
34
42
  ## Install
35
43
 
36
44
  The bb CLI ships this mod and installs it (BOT-3152). `bb setup` and `bb update`
@@ -27,6 +27,10 @@ let parked = false;
27
27
  // BOT-3152 AC-5: a repo with no BB binding shows no band at all (the mod is
28
28
  // installed once at user scope, so it also loads in unrelated repos).
29
29
  let unbound = false;
30
+ // BOT-3211: the session's context window as the status line has it. Read once
31
+ // with the free `$.session.usage()` and then only from `session.measure`,
32
+ // which the engine pushes after each main-thread turn: never polled.
33
+ let context = null;
30
34
  const ERRORS = {
31
35
  linear_key_missing:
32
36
  "Export LINEAR_BB_API_KEY to Claude Desktop before using Live.",
@@ -692,6 +696,41 @@ function bandLine(el, spec, widths, right) {
692
696
  // Top-aligned: with a wrapped title, the id and status sit on its first line.
693
697
  return el.Box({ key: spec.key, flexDirection: "row", alignItems: "flex-start", columnGap: 1, children });
694
698
  }
699
+ // BOT-3211: 124000 → "124k", 1000000 → "1M".
700
+ function tokens(n) {
701
+ if (n >= 999500) return String(Math.round(n / 100000) / 10).replace(/\.0$/, "") + "M";
702
+ if (n >= 1000) return Math.round(n / 1000) + "k";
703
+ return String(n);
704
+ }
705
+ // The row's text, or null when the runtime has no fill figure (a fresh or
706
+ // just-compacted session): hidden, never "0%".
707
+ function contextText(c) {
708
+ if (
709
+ !c ||
710
+ !Number.isFinite(c.percent) ||
711
+ c.percent < 0 ||
712
+ c.percent > 100 ||
713
+ !Number.isSafeInteger(c.window) ||
714
+ c.window <= 0
715
+ )
716
+ return null;
717
+ const pct = "Context " + Math.round(c.percent) + "%";
718
+ return Number.isSafeInteger(c.tokens) && c.tokens >= 0
719
+ ? pct + " · " + tokens(c.tokens) + " of " + tokens(c.window)
720
+ : pct + " of " + tokens(c.window);
721
+ }
722
+ function contextLine(el, key) {
723
+ const text = contextText(context);
724
+ if (!text) return null;
725
+ return el.Box({ key, flexDirection: "row", children: [dim(el, text)] });
726
+ }
727
+ async function readContext($) {
728
+ try {
729
+ context = (await $.session.usage()).context ?? null;
730
+ } catch {
731
+ context = null;
732
+ }
733
+ }
695
734
  const UNREAD = {
696
735
  prs: "Couldn't read this session's PRs",
697
736
  epic: "Couldn't read this session's epic",
@@ -735,7 +774,8 @@ function bandLines(el, $, inPane) {
735
774
  columnGap: 1,
736
775
  children: [...tag, el.Text({ color: loading ? undefined : "warning", children: ["BB · " + text] }), details].filter(Boolean),
737
776
  }),
738
- ];
777
+ contextLine(el, k("line-context")),
778
+ ].filter(Boolean);
739
779
  }
740
780
  const s = snapshot;
741
781
  // One row per ticket and per PR, each with its own icon, status and title.
@@ -813,6 +853,9 @@ function bandLines(el, $, inPane) {
813
853
  const lines = specs.map((spec, i) => bandLine(el, spec, widths, i === 0 ? right : null));
814
854
  // BOT-3152 AC-2: the web Staging chip's red, with its link, above the rows.
815
855
  if (s.staging?.red) lines.unshift(stagingBanner(el, k("line-staging"), s.staging.since));
856
+ // BOT-3211: the session's context use, last.
857
+ const ctx = contextLine(el, k("line-context"));
858
+ if (ctx) lines.push(ctx);
816
859
  return lines;
817
860
  }
818
861
  export function register(on) {
@@ -832,6 +875,7 @@ export function register(on) {
832
875
  immediate: true,
833
876
  });
834
877
  parked = false;
878
+ await readContext($);
835
879
  if (await onScreen($)) await refresh($);
836
880
  else parked = true;
837
881
  return next(e);
@@ -861,6 +905,14 @@ export function register(on) {
861
905
  await openPanel($);
862
906
  return {};
863
907
  });
908
+ // BOT-3211 AC-2: the context row's only trigger after the first read.
909
+ on("session.measure", async ($, e, next) => {
910
+ if (e.changed?.includes("context")) {
911
+ context = e.context ?? null;
912
+ $.ui.invalidate("ui.render");
913
+ }
914
+ return next(e);
915
+ });
864
916
  on("turn.start", async ($, e, next) => {
865
917
  if (!e.agentId) {
866
918
  activity = "Working";
@@ -886,6 +938,7 @@ export function register(on) {
886
938
  errorCode = "session_changed";
887
939
  parked = false;
888
940
  unbound = false;
941
+ context = null;
889
942
  if (timer) timer.cancel();
890
943
  return next(e);
891
944
  });
@@ -895,6 +948,8 @@ export function register(on) {
895
948
  async ($, e, next) => {
896
949
  selectedTicket = undefined;
897
950
  mode = "live";
951
+ // A resumed session's first figure comes before any turn.
952
+ await readContext($);
898
953
  await refresh($);
899
954
  return next(e);
900
955
  },
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.69.4",
3
- "source_commit": "fe786c6f3850ee404ab213075e8445a0af0ac2ec",
4
- "source_sha256": "d26c4fbae9682e5da62ba9cfd5e0be1e8f580098e9439bcc6111e0a9d4b0d22b",
2
+ "release_version": "1.69.5",
3
+ "source_commit": "53b366064d9f9db05f38f488bd51b971f73276a2",
4
+ "source_sha256": "9bc7ec78214edbb54996d8245375eb3914ec7fa20436d8d375b516a2bb5bf8eb",
5
5
  "target": "darwin-arm64",
6
- "sha256": "45eb395a0b0a1b7f4a5d69257475947a766c8875a51a97d7d523ecdb3dc95c22"
6
+ "sha256": "125e42c26d1d3116ac192a8b1138e74c61c7ea56d8fc1c1abf4392ad1d43bd5d"
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "release_version": "1.69.4",
3
- "source_commit": "fe786c6f3850ee404ab213075e8445a0af0ac2ec",
4
- "source_sha256": "d26c4fbae9682e5da62ba9cfd5e0be1e8f580098e9439bcc6111e0a9d4b0d22b",
2
+ "release_version": "1.69.5",
3
+ "source_commit": "53b366064d9f9db05f38f488bd51b971f73276a2",
4
+ "source_sha256": "9bc7ec78214edbb54996d8245375eb3914ec7fa20436d8d375b516a2bb5bf8eb",
5
5
  "target": "linux-x64",
6
- "sha256": "cb1ce9a04c0a82d89e8fbe1fa4ced2f909905b7177b9dd384942041e09056955"
6
+ "sha256": "14ebbb9064a188c5aaa7c804e20417b5611c6ee6a2a525b015064ed28f418c2e"
7
7
  }
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "schema_version": 1,
3
- "release_version": "1.69.4",
4
- "source_commit": "fe786c6f3850ee404ab213075e8445a0af0ac2ec",
5
- "source_sha256": "d26c4fbae9682e5da62ba9cfd5e0be1e8f580098e9439bcc6111e0a9d4b0d22b",
3
+ "release_version": "1.69.5",
4
+ "source_commit": "53b366064d9f9db05f38f488bd51b971f73276a2",
5
+ "source_sha256": "9bc7ec78214edbb54996d8245375eb3914ec7fa20436d8d375b516a2bb5bf8eb",
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": "45eb395a0b0a1b7f4a5d69257475947a766c8875a51a97d7d523ecdb3dc95c22",
11
+ "sha256": "125e42c26d1d3116ac192a8b1138e74c61c7ea56d8fc1c1abf4392ad1d43bd5d",
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": "cb1ce9a04c0a82d89e8fbe1fa4ced2f909905b7177b9dd384942041e09056955",
18
+ "sha256": "14ebbb9064a188c5aaa7c804e20417b5611c6ee6a2a525b015064ed28f418c2e",
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.69.4",
3
+ "version": "1.69.5",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {