Skip to main content

tailscale: use Fish to set tab titles, not the iTerm 2 API

ID
0c70f38
date
2026-05-28 23:01:53+00:00
author
Alex Chan <alexc@tailscale.com>
parent
a0c7966
message
tailscale: use Fish to set tab titles, not the iTerm 2 API

I can set tab titles by setting the `fish_tab_title` function, which is
built into the shell, rather than using the iTerm 2 Python API (which is
a big dependency and can sometimes change the wrong tab title).
changed files
10 files, 53 additions, 54 deletions

Changed files

fish_functions/README.md (4074) → fish_functions/README.md (4542)

diff --git a/fish_functions/README.md b/fish_functions/README.md
index 6759ea1..e17adad 100644
--- a/fish_functions/README.md
+++ b/fish_functions/README.md
@@ -78,6 +78,15 @@ cog_helpers.create_description_table(folder_name=folder_name, scripts=functions)
   </dd>
 
   <dt>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/fish_functions/devcontrol.fish">
+      <code>devcontrol.fish</code>
+    </a>
+  </dt>
+  <dd>
+    Run a local instance of the Tailscale control plane
+  </dd>
+
+  <dt>
     <a href="https://github.com/alexwlchan/scripts/blob/main/fish_functions/fish_prompt.fish">
       <code>fish_prompt.fish</code>
     </a>
@@ -132,6 +141,15 @@ cog_helpers.create_description_table(folder_name=folder_name, scripts=functions)
   </dd>
 
   <dt>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/fish_functions/start_local_tailscaled.fish">
+      <code>start_local_tailscaled.fish</code>
+    </a>
+  </dt>
+  <dd>
+    Run a local instance of the tailscaled daemon
+  </dd>
+
+  <dt>
     <a href="https://github.com/alexwlchan/scripts/blob/main/fish_functions/tmpdir.fish">
       <code>tmpdir.fish</code>
     </a>
@@ -149,4 +167,4 @@ cog_helpers.create_description_table(folder_name=folder_name, scripts=functions)
     Create and activate a new virtual environment
   </dd>
 </dl>
-<!-- [[[end]]] (sum: FW10hKks6J) -->
\ No newline at end of file
+<!-- [[[end]]] (sum: 2Jhae7vtuD) -->
\ No newline at end of file

fish_functions/devcontrol.fish (0) → fish_functions/devcontrol.fish (366)

diff --git a/fish_functions/devcontrol.fish b/fish_functions/devcontrol.fish
new file mode 100644
index 0000000..3d11905
--- /dev/null
+++ b/fish_functions/devcontrol.fish
@@ -0,0 +1,12 @@
+function devcontrol --description "Run a local instance of the Tailscale control plane"
+  # Save the arguments to a global variable, then set them as the
+  # tab title.
+  set -g devcontrol_args $argv
+  function fish_title
+    echo "devcontrol $devcontrol_args"
+  end
+
+  cd ~/repos/scripts
+  source .venv/bin/activate.fish
+  python3 tailscale/devcontrol.py $argv
+end

fish_functions/start_local_tailscaled.fish (0) → fish_functions/start_local_tailscaled.fish (323)

diff --git a/fish_functions/start_local_tailscaled.fish b/fish_functions/start_local_tailscaled.fish
new file mode 100644
index 0000000..3e9c54a
--- /dev/null
+++ b/fish_functions/start_local_tailscaled.fish
@@ -0,0 +1,10 @@
+function start_local_tailscaled --description "Run a local instance of the tailscaled daemon"
+  # Save the node name to a global variable, then set it in the tab title.
+  set -g node_name $argv[1]
+  function fish_title
+    echo $node_name
+  end
+
+  cd ~/repos/scripts
+  bash tailscale/start_local_tailscaled.sh $argv[1]
+end

macos/README.md (7691) → macos/README.md (7570)

diff --git a/macos/README.md b/macos/README.md
index 467df21..4541e1f 100644
--- a/macos/README.md
+++ b/macos/README.md
@@ -73,10 +73,6 @@ scripts = [
         "description": "Set the Finder comment of a file, which will be indexed by Spotlight for searching"
     },
     {
-        "usage": "set_iterm_tab_title.py TITLE",
-        "description": "Set the title of a tab in iTerm 2"
-    },
-    {
         "usage": "sterilise [PATH]",
         "description": "alias for <code>xattr -d com.apple.quarantine</code>"
     },

macos/set_iterm_tab_title.py (638) → macos/set_iterm_tab_title.py (0)

diff --git a/macos/set_iterm_tab_title.py b/macos/set_iterm_tab_title.py
deleted file mode 100755
index 247ad20..0000000
--- a/macos/set_iterm_tab_title.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python3
-"""
-Set the title of an iTerm 2 tab.
-"""
-
-import sys
-
-import iterm2
-
-
-def set_title(title: str):
-    async def main(connection: iterm2.connection.Connection) -> None:
-        app = await iterm2.async_get_app(connection)
-
-        window = app.current_window
-        if window is None:
-            return
-
-        tab = window.current_tab
-        if tab is None:
-            return
-
-        await tab.async_set_title(title)
-
-    iterm2.run_until_complete(main)
-
-
-if __name__ == "__main__":
-    try:
-        title = sys.argv[1]
-    except IndexError:
-        sys.exit(f"Usage: {__file__} TITLE")
-
-    set_title(title)

requirements.in (172) → requirements.in (165)

diff --git a/requirements.in b/requirements.in
index e46545b..55a98d1 100644
--- a/requirements.in
+++ b/requirements.in
@@ -2,7 +2,6 @@ alexwlchan-chives[fetch]>=41
 beautifulsoup4
 cogapp
 humanize
-iterm2
 Pillow
 pygments  # Used as 'pygmentize' in 'pp_xml.sh'
 pypdf

requirements.txt (1434) → requirements.txt (1335)

diff --git a/requirements.txt b/requirements.txt
index 8aaf2c7..d7ebcf6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,8 +21,6 @@ idna==3.11
     # via requests
 iniconfig==2.3.0
     # via pytest
-iterm2==2.14
-    # via -r requirements.in
 mutagen==1.47.0
     # via yt-dlp
 packaging==26.0
@@ -31,8 +29,6 @@ pillow==12.2.0
     # via -r requirements.in
 pluggy==1.6.0
     # via pytest
-protobuf==7.34.1
-    # via iterm2
 pycryptodomex==3.23.0
     # via yt-dlp
 pygments==2.20.0
@@ -58,9 +54,7 @@ urllib3==2.6.3
     #   requests
     #   yt-dlp
 websockets==16.0
-    # via
-    #   iterm2
-    #   yt-dlp
+    # via yt-dlp
 yt-dlp==2026.3.17
     # via -r requirements.in
 yt-dlp-ejs==0.8.0

tailscale/README.md (3355) → tailscale/README.md (3364)

diff --git a/tailscale/README.md b/tailscale/README.md
index 5d28b70..b3a34d2 100644
--- a/tailscale/README.md
+++ b/tailscale/README.md
@@ -18,7 +18,7 @@ folder_name = "tailscale"
 
 scripts = [
     {
-        "usage": "devcontrol",
+        "usage": "devcontrol.py",
         "description": "run a local instance of the Tailscale control plane"
     },
     {
@@ -56,8 +56,8 @@ cog_helpers.create_description_table(folder_name=folder_name, scripts=scripts)
 ]]]-->
 <dl>
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/tailscale/devcontrol">
-      <code>devcontrol</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/tailscale/devcontrol.py">
+      <code>devcontrol.py</code>
     </a>
   </dt>
   <dd>
@@ -127,4 +127,4 @@ cog_helpers.create_description_table(folder_name=folder_name, scripts=scripts)
     run the Yarn formatting/linting commands for the admin console
   </dd>
 </dl>
-<!-- [[[end]]] (sum: BEjnN4Zchx) -->
\ No newline at end of file
+<!-- [[[end]]] (sum: 8W27QzHggD) -->
\ No newline at end of file

tailscale/devcontrol (3049) → tailscale/devcontrol.py (3100)

diff --git a/tailscale/devcontrol b/tailscale/devcontrol.py
similarity index 98%
rename from tailscale/devcontrol
rename to tailscale/devcontrol.py
index 1ee6366..c6fcb6d 100755
--- a/tailscale/devcontrol
+++ b/tailscale/devcontrol.py
@@ -15,6 +15,7 @@ import subprocess
 import sys
 from typing import TypedDict
 
+from chives.text import coloured
 
 
 Args = TypedDict(
@@ -101,7 +102,7 @@ if __name__ == "__main__":
         debug_string.append(f"{name}={value}")
     debug_string.extend(tailcontrol_cmd)
 
-    print(" ".join(debug_string))
+    print(coloured(" ".join(debug_string), "blue"))
     try:
         subprocess.check_call(tailcontrol_cmd, env=tailcontrol_env, cwd=corp_dir)
     except KeyboardInterrupt:

tailscale/start_local_tailscaled.sh (531) → tailscale/start_local_tailscaled.sh (524)

diff --git a/tailscale/start_local_tailscaled.sh b/tailscale/start_local_tailscaled.sh
index 6ca2e34..85be0f1 100755
--- a/tailscale/start_local_tailscaled.sh
+++ b/tailscale/start_local_tailscaled.sh
@@ -3,14 +3,17 @@
 set -o errexit
 set -o nounset
 
+if (( $# == 0 )); then
+  echo "Usage: $0 HOSTNAME" >&2
+  exit 1
+fi
+
 NAME="$1"
 
 print_info "socket: /tmp/ts/$NAME/ts.sock"
 
 source ~/repos/scripts/.venv/bin/activate
 
-python3 ~/repos/scripts/macos/set_iterm_tab_title.py "$NAME (tailscaled)"
-
 cd ~/repos/oss
 
 if [[ "${2:-}" != "--persist" ]]