Files
community-plugins/claude-companion/plugin.toml
T
d2aa9b9751 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>
2026-07-30 21:34:52 -04:00

121 lines
5.2 KiB
TOML

# Claude Companion — a Claude Code companion for Noctalia v5.
# Not a chat client: the terminal does the agentic work; this is the shell-side
# body — senses, hands, and an attention pulse. See README.md for the design.
id = "lowcache/claude-companion"
name = "Claude Companion"
version = "1.3.0"
# Plugin API level this manifest targets — mandatory as of the Noctalia 5 beta
# manifest parser (replaces the older min_noctalia gate). 3 is the oldest-supported
# level and covers every feature this plugin uses ([[panel]], ui controls, plugin IPC
# dispatch, barWidget/onIpc). The [[service]] entry additionally needs a build that
# ships the Service entry kind (Noctalia 5 beta, upstream rev da014f72 or newer).
plugin_api = 3
author = "lowcache"
license = "MIT"
icon = "robot"
description = "Claude Code companion: /claude launch + a telemetry-driven attention pulse."
# The compositor CLI (niri / hyprctl / swaymsg) is not listed: the shim detects and
# speaks whichever of the three supported compositors is running Noctalia, so exactly
# one is inherently present. See README "Requirements".
dependencies = ["claude", "python3", "playerctl", "notify-send", "nmcli", "ps", "tr", "timeout"]
tags = ["ai", "productivity", "bar", "desktop", "panel", "launcher"]
# ── User settings ─────────────────────────────────────────────────────────────
# Breath-speed multiplier for the pulse dot + orb animation (plugin-level, so both
# surfaces read the same value via noctalia.getConfig). 1.0 is the tuned default;
# higher breathes faster, lower slower. Rendered as a slider; `step` MUST be set
# explicitly on every double — the manifest parser defaults step to 1.0
# (plugin_manifest.h: `double step = 1.0`), which snaps a fractional range to a
# handful of preset stops instead of sliding.
[[setting]]
key = "breath_speed"
type = "double"
label_key = "settings.breath_speed.label"
description_key = "settings.breath_speed.description"
default = 1.0
min = 0.25
max = 3.0
step = 0.05
# Minimum brightness the bar dot dims to at the trough of its breath (0 = fully dark,
# 0.9 = barely dims). 0.45 is the tuned default. (Plugin-level so it sits with the
# other look controls; pulse.luau reads it via getConfig.)
[[setting]]
key = "pulse_glow_floor"
type = "double"
label_key = "settings.pulse_glow_floor.label"
description_key = "settings.pulse_glow_floor.description"
default = 0.45
min = 0.0
max = 0.9
step = 0.05
# How far the orb glyph magnifies as it breathes (0 = static size, 1.0 = the tuned
# default, higher = a bigger swing). orb.luau reads it via getConfig.
[[setting]]
key = "orb_swell"
type = "double"
label_key = "settings.orb_swell.label"
description_key = "settings.orb_swell.description"
default = 1.0
min = 0.0
max = 3.0
step = 0.05
# The pulse aggregator (headless) — the single source of truth for Claude session
# state. Receives hook events via onIpc (noctalia msg plugin …:pulse-svc all <event>)
# and the launcher quick-ask via state.watch("claude.state"), rolls all sessions up,
# and publishes the snapshot to noctalia.state ("claude.pulse"). Runs at shell launch
# with no surface, so capture never depends on the bar widget being placed — this
# retires the old "pulse must sit on a bar" invariant (see PROTOCOL.md). Needs the
# [[service]] entry kind (Noctalia 5 beta / post-5.0.0).
[[service]]
id = "pulse-svc"
entry = "pulse-svc.luau"
# The attention pulse (bar) — the visual centerpiece. A pure subscriber: watches
# noctalia.state ("claude.pulse") published by pulse-svc and reflects it in the bar as
# a glyph + a sine-breath brightness glow (folded in from the barpulse A/B prototype;
# barpulse.luau stays in-repo as the experiment record, unregistered).
[[widget]]
id = "pulse"
entry = "pulse.luau"
# The presence orb — the ambient desktop half of the pulse. A softly breathing disc
# that mirrors the bar dot's rollup (published to noctalia.state "claude.pulse"); pure
# view, no hooks of its own. Per-frame motion via setNeedsFrameTick/onFrameTick.
[[desktop_widget]]
id = "orb"
entry = "orb.luau"
# The answer panel — full-length scrollable surface for /claude ? quick-ask answers
# (a notification toast clips long bodies; it carries only a preview). Opens on
# a click on the bar pulse, from the "Show last answer" launcher row, or via
# `noctalia msg panel-toggle lowcache/claude-companion:answer`.
[[panel]]
id = "answer"
entry = "answer.luau"
width = 460
height = 420
# The sessions panel — the actionable form of the bar tooltip. One row per live
# session (state, model, token burn) plus a retire control for a session whose
# SessionEnd hook never fired. Opens on a RIGHT-click of the bar pulse (left-click
# stays the answer panel), or via
# `noctalia msg panel-toggle lowcache/claude-companion:sessions`.
[[panel]]
id = "sessions"
entry = "sessions.luau"
width = 440
height = 360
# /claude — launch a real Claude Code session in the terminal, or a one-shot ask.
# The single backend chokepoint (invoke/parse) is inlined here: v5 has no plugin
# module system, and this is the only entry that talks to a model.
[[launcher_provider]]
id = "claude"
entry = "claude.luau"
prefix = "claude"
glyph = "robot"