From 65aed400ea10db08599b9b68bc9cc6af4114a31e Mon Sep 17 00:00:00 2001 From: Ravi Tharuma Date: Wed, 22 Jul 2026 16:45:57 +0200 Subject: [PATCH] feat: also rename Herdr panes when setting titles Keep existing tab/terminal/agent renaming, and when Herdr env is present also call pane rename + pane.report-metadata (--title/--display-agent). Official Herdr integrations only report lifecycle state, so pane task identity is missing from the default path. This keeps the Herdr sidebar aligned with outer titles. Refs: rjyo/herdr-window-title-sync#3 Upstream: ogulcancelik/herdr#1725 (discussion) --- README.md | 2 ++ sync-title.js | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/README.md b/README.md index 6dbefe3..0387531 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ herdr plugin link . ## What It Shows +When a useful task title is available, the plugin also **renames the focused Herdr pane** and publishes `pane.report-metadata` (`--title` / `--display-agent`) so the Herdr sidebar matches the outer terminal title. Tab-number fallbacks are not written back as pane names. + The plugin chooses the first available title: 1. Herdr pane metadata title, when an integration reports one. diff --git a/sync-title.js b/sync-title.js index 7985592..1c9f82b 100644 --- a/sync-title.js +++ b/sync-title.js @@ -218,11 +218,63 @@ function saveTitle(title) { writeFileSync(statePath, `${title}\n`); } +/** + * When we derive a useful task title (not a bare tab fallback), also push it + * into Herdr pane metadata + pane rename. Official integrations only report + * lifecycle state today; this closes the loop so the Herdr sidebar and other + * plugins see the same task identity we put in the outer terminal title. + * + * Fail open: never block outer title sync if metadata/rename fails. + */ +function pushPaneTitle(title) { + try { + const pane = json(["pane", "current"])?.result?.pane; + const paneId = pane?.pane_id; + if (!paneId) return; + + // Skip pure tab fallbacks like "2" / "2 / tab" / "tab 2". + const core = cleanPart(title).replace(/\s*\([^)]*\)\s*$/, ""); + if (!core || /^(tab\s*)?\d+(\s*\/.*)?$/i.test(core)) return; + if (/^tab\s+\d+/i.test(core)) return; + + // Prefer a short pane label without the workspace parenthetical. + const paneLabel = core.slice(0, 60); + + try { + run(["pane", "rename", paneId, paneLabel]); + } catch { + // ignore + } + try { + run([ + "pane", + "report-metadata", + paneId, + "--source", + "plugin:herdr-window-title-sync", + "--title", + paneLabel, + "--display-agent", + paneLabel, + "--token", + `task=${paneLabel}`, + "--ttl-ms", + "86400000", + ]); + } catch { + // ignore + } + } catch { + // ignore + } +} + try { const title = buildTitle(); if (title !== lastTitle()) { run(["terminal", "title", "set", title]); saveTitle(title); + pushPaneTitle(title); } console.log(title); } catch (error) {