Update claude-companion to v1.3.0 (what its got: a lot) (#167)

* claude-companion: v1.3.0 — headless aggregator, user settings, sessions panel

Catalog-side update for lowcache/claude-companion, from 1.0.1 to 1.3.0.

Architecture: the pulse aggregator moved out of the bar widget into a headless
[[service]] (pulse-svc.luau). Capture no longer depends on the bar dot being
placed — the service starts with the shell and listens regardless of surfaces,
retiring the plugin's old "pulse must sit on a bar" deployment invariant. The bar
widget and desktop orb are now independent subscribers of the claude.pulse
rollup, rendering only. Noctalia 5 beta also fixed the older limitation where bar
widgets did not receive state.watch callbacks, so the bar dot is event-driven
like the orb; both docs are updated accordingly.

New: a `sessions` panel on right-click of the pulse (left-click still opens the
answer panel) — one row per live session with state, model and token burn, plus a
Retire control for a session whose SessionEnd hook never fired and which would
otherwise sit at idle indefinitely. It introduces no new IPC verb: only the
trailing `session` payload field is read for routing, so `session_end` with
`,,,,,<sid>` is already a well-formed single-session retire. PROTOCOL.md now
documents that property so any adapter can use it. Session ids are allowlisted
before reaching a shell command.

New: three animation settings (breath_speed, pulse_glow_floor, orb_swell), each
declaring an explicit `step` — an omitted step defaults to 1.0 in the manifest
parser, which collapses a fractional range to a couple of preset stops instead of
a slider.

plugin_api stays at 3. Everything used here is ungated at that level, and the
contributing guidance is to raise it only when adopting a capability from a newer
level. Verified against the installed build rather than assumed.

Also ships tests/manifest_spec.py, which pins the settings contract: every
numeric setting must declare an explicit step finer than its range, defaults must
land on a step boundary, label/description must use the *_key form, and every key
must resolve in translations/en.json. Neither the linter nor a widget spec can
catch a bad step, which is how the slider bug shipped in the first place.

Validated: catalog validator 54/54 exit 0, its own 54 self-tests pass, and the
plugin's suite (5 luau + shim + manifest) is green. Live-tested on niri against
Noctalia 5 beta; the compositor shim is unchanged in this update.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* claude-companion: tint the sessions retire control, fix singular header

Follow-up on the v1.3.0 submission from live review: the retire button carried no
variant and rendered at the background colour; a single session read "1 sessions".
The panel root stays unfilled by design — noctalia panels are translucent under
the glass style, so the backdrop is the shell's, not the plugin's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: lowcache <drawpdeadredd@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
J.B. Robinson
2026-07-30 21:34:52 -04:00
committed by GitHub
co-authored by lowcache Claude Opus 5
parent d113b03439
commit d2aa9b9751
13 changed files with 807 additions and 264 deletions
+6 -6
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Pulse hook dispatcher (lowcache/claude-companion plugin).
Bridges a Claude Code lifecycle hook to the pulse bar widget, enriching the
Bridges a Claude Code lifecycle hook to the pulse aggregator service, enriching the
event with live model + token-burn telemetry parsed from the session transcript.
Invoked by the hooks in settings.snippet.json as:
@@ -10,16 +10,16 @@ Invoked by the hooks in settings.snippet.json as:
Hook JSON arrives on stdin (transcript_path, session_id). The widget is driven via
noctalia's documented plugin IPC (`noctalia msg --help`):
noctalia msg plugin lowcache/claude-companion:pulse all <event> [payload]
noctalia msg plugin lowcache/claude-companion:pulse-svc all <event> [payload]
`[payload]` is a single positional token, so the payload is a SPACE-FREE CSV the
widget (pulse.luau) parses:
aggregator service (pulse-svc.luau) parses:
model,in,out,cacheCreate,cacheRead,session
The `session` (short id) tags EVERY event, so the widget can track each concurrent
The `session` (short id) tags EVERY event, so the service can track each concurrent
session separately. The matching SessionEnd hook fires `session_end`, which retires
the session in the widget and drops its token cache here.
the session in the service and drops its token cache here.
Token accounting is incremental: a per-session cache in $XDG_RUNTIME_DIR stores the
last byte offset + running sums, so each hook reads only newly-appended transcript
@@ -36,7 +36,7 @@ import os
import subprocess
import sys
PLUGIN = "lowcache/claude-companion:pulse"
PLUGIN = "lowcache/claude-companion:pulse-svc"
TARGET = "all"