feat(c5): own the Kitty scrollback watcher (kitty-logging) #54

Merged
rootiest merged 6 commits from feat/kitty-watcher-ownership into main 2026-06-16 18:36:10 +00:00
Owner

Summary

Lets fish-config own the Kitty scrollback watcher the way it already owns the paru/yay wrappers — ship it, install/update it, and wire it into kitty.conf — instead of relying on a manually-placed watcher.py.

  • kitty/fish-config-watcher.py — canonical, version-marked watcher (capture + prune, sentinel-gated). The # fish-config-watcher-version: N marker lets install detect and refresh a stale copy.
  • functions/kitty-logging.fishinstall / uninstall / status / dismiss. install resolves the Kitty config dir ($KITTY_CONFIG_DIRECTORY$XDG_CONFIG_HOME/kitty~/.config/kitty), copies the watcher in, and adds a sentinel-marked managed block to the top-level kitty.conf, commenting out any conflicting watcher line to avoid double-capture. Idempotent; refreshes stale watchers; uninstall cleanly reverts.
  • conf.d/kitty-watcher-reminder.fish — non-blocking, per-session nudge shown inside Kitty until you run kitty-logging install or kitty-logging dismiss. Suppressed automatically if any watcher line already exists (so existing setups are never nagged).
  • Helpers__kitty_logging_dir, __kitty_logging_version, __kitty_logging_has_watcher.

Runtime enable/disable stays governed by the existing C5 .logging_disabled sentinel; install affects new Kitty windows only (a watcher binds at window creation and can't be retrofitted — acceptable, and you confirmed the sentinel covers mid-session disable).

Why not parse the resolved kitty config / per-toggle regeneration?

Kitty 0.47 has no stable resolved-config dump that includes watchers, and modular configs make scanning includes unreliable. Instead we own a marked block in the entry-point kitty.conf only. And because the sentinel already handles runtime disable, this is an install-once concern, not paru/yay-style per-toggle churn.

Hardening from review

  • install aborts if the watcher copy fails (was wiring a block pointing at a missing file and reporting success).
  • dismiss works even without kitty installed (handled before the kitty guard).
  • uninstall requires both block markers before its sed range-delete, so a malformed block can't delete to EOF.

Tests

TDD throughout — self-contained fish assert scripts cover: helper resolution/version/has-watcher (5), install/idempotency/legacy-migration/version-refresh/uninstall/fresh-create/bad-subcommand (10), and the reminder's four guard cases. Also validated end-to-end against a copy of the real kitty.conf (migrates watcher watcher.py → managed block correctly).

Spec & plan: docs/superpowers/specs/2026-06-16-kitty-watcher-ownership-design.md, docs/superpowers/plans/2026-06-16-kitty-watcher-ownership.md.

Manual Verification

  • Open a fresh fish shell inside Kitty (with no watcher line in kitty.conf) → the per-session reminder appears
  • Run kitty-logging install → managed block added to kitty.conf, fish-config-watcher.py copied into the Kitty config dir, any prior watcher line commented out
  • kitty-logging status → reports managed block present, watcher version, C5 enabled
  • Open a new Kitty window, run commands, close it → a scrollback_*.log appears in ~/.terminal_history
  • Start a new shell in Kitty after install → reminder no longer appears
  • kitty-logging dismiss (in a config without the watcher) → reminder silenced; works even on a machine without kitty
  • kitty-logging uninstall → managed block + watcher file removed, rest of kitty.conf intact
  • set -U __fish_config_op_logging off → watcher goes inert (no new logs) without uninstalling; re-enable resumes
## Summary Lets fish-config **own** the Kitty scrollback watcher the way it already owns the paru/yay wrappers — ship it, install/update it, and wire it into `kitty.conf` — instead of relying on a manually-placed `watcher.py`. - **`kitty/fish-config-watcher.py`** — canonical, version-marked watcher (capture + prune, sentinel-gated). The `# fish-config-watcher-version: N` marker lets `install` detect and refresh a stale copy. - **`functions/kitty-logging.fish`** — `install` / `uninstall` / `status` / `dismiss`. `install` resolves the Kitty config dir (`$KITTY_CONFIG_DIRECTORY` → `$XDG_CONFIG_HOME/kitty` → `~/.config/kitty`), copies the watcher in, and adds a sentinel-marked managed block to the top-level `kitty.conf`, commenting out any conflicting `watcher` line to avoid double-capture. Idempotent; refreshes stale watchers; `uninstall` cleanly reverts. - **`conf.d/kitty-watcher-reminder.fish`** — non-blocking, per-session nudge shown inside Kitty until you run `kitty-logging install` or `kitty-logging dismiss`. Suppressed automatically if any `watcher` line already exists (so existing setups are never nagged). - **Helpers** — `__kitty_logging_dir`, `__kitty_logging_version`, `__kitty_logging_has_watcher`. Runtime enable/disable stays governed by the existing C5 `.logging_disabled` sentinel; install affects **new** Kitty windows only (a watcher binds at window creation and can't be retrofitted — acceptable, and you confirmed the sentinel covers mid-session disable). ## Why not parse the resolved kitty config / per-toggle regeneration? Kitty 0.47 has no stable resolved-config dump that includes watchers, and modular configs make scanning includes unreliable. Instead we own a marked block in the entry-point `kitty.conf` only. And because the sentinel already handles runtime disable, this is an install-once concern, not paru/yay-style per-toggle churn. ## Hardening from review - `install` aborts if the watcher copy fails (was wiring a block pointing at a missing file and reporting success). - `dismiss` works even without kitty installed (handled before the kitty guard). - `uninstall` requires **both** block markers before its `sed` range-delete, so a malformed block can't delete to EOF. ## Tests TDD throughout — self-contained `fish` assert scripts cover: helper resolution/version/has-watcher (5), install/idempotency/legacy-migration/version-refresh/uninstall/fresh-create/bad-subcommand (10), and the reminder's four guard cases. Also validated end-to-end against a copy of the real `kitty.conf` (migrates `watcher watcher.py` → managed block correctly). Spec & plan: `docs/superpowers/specs/2026-06-16-kitty-watcher-ownership-design.md`, `docs/superpowers/plans/2026-06-16-kitty-watcher-ownership.md`. ## Manual Verification - [x] Open a fresh fish shell inside Kitty (with no `watcher` line in `kitty.conf`) → the per-session reminder appears - [x] Run `kitty-logging install` → managed block added to `kitty.conf`, `fish-config-watcher.py` copied into the Kitty config dir, any prior `watcher` line commented out - [x] `kitty-logging status` → reports managed block present, watcher version, C5 enabled - [x] Open a **new** Kitty window, run commands, close it → a `scrollback_*.log` appears in `~/.terminal_history` - [x] Start a new shell in Kitty after install → reminder no longer appears - [x] `kitty-logging dismiss` (in a config without the watcher) → reminder silenced; works even on a machine without kitty - [x] `kitty-logging uninstall` → managed block + watcher file removed, rest of `kitty.conf` intact - [x] `set -U __fish_config_op_logging off` → watcher goes inert (no new logs) without uninstalling; re-enable resumes
rootiest added 6 commits 2026-06-16 14:07:55 +00:00
- abort install if the watcher copy fails (was wiring a block pointing at
  a missing file and reporting success)
- handle dismiss before the kitty guard so it works without kitty installed
- require both managed-block markers before the uninstall range-delete to
  prevent deleting to EOF on a malformed block
rootiest merged commit b4bac6a562 into main 2026-06-16 18:36:10 +00:00
rootiest deleted branch feat/kitty-watcher-ownership 2026-06-16 18:36:14 +00:00
Sign in to join this conversation.