From 770b163d25d45c3aa2c09496d5f7c5f7738308b9 Mon Sep 17 00:00:00 2001
From: Paul Buetow
-
Technical references
I didn't read them from the beginning to the end, but I am using them to look up things. The books are in random order:
+
Self-development and soft-skills books
In random order:
-
Here are notes of mine for some of the books
@@ -164,31 +164,31 @@
Some of these were in-person with exams; others were online learning lectures only. In random order:
-
Technical guides
These are not whole books, but guides (smaller or larger) which I found very useful. in random order:
+
Podcasts
@@ -197,51 +197,51 @@
In random order:
-
Podcasts I liked
I liked them but am not listening to them anymore. The podcasts have either "finished" (no more episodes) or I stopped listening to them due to time constraints or a shift in my interests.
-
Newsletters I like
This is a mix of tech and non-tech newsletters I am subscribed to. In random order:
-
Magazines I like(d)
@@ -249,9 +249,9 @@
Formal education
diff --git a/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html b/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html
new file mode 100644
index 00000000..112691fd
--- /dev/null
+++ b/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html
@@ -0,0 +1,317 @@
+
+
+
+
+A tmux popup editor for Cursor Agent prompts
+
+Published at 2026-02-01T20:24:16+02:00
+
+...and any other TUI based application
+
+Table of Contents
+
+
+
+Why I built this
+
+I spend some time in Cursor Agent (the CLI version of the Cursor IDE, I don't like really the IDE), and I also jump between Claude Code CLI, Ampcode, Gemini CLI, OpenAI Codex CLI, OpenCode, and Aider just to see how things are evolving. But for the next month I'll be with Cursor Agent.
+
+Short prompts are fine in the inline input, but for longer prompts I want a real editor: spellcheck, search/replace, multiple cursors, and all the Helix muscle memory I already have.
+
+Cursor Agent has a Vim editing mode, but not Helix. And even in Vim mode I can't use my full editor setup. I want the real thing, not a partial emulation.
+
+https://helix-editor.com
+https://www.vim.org
+https://neovim.io
+
+So I built a tiny tmux popup editor. It opens $EDITOR (Helix for me), and when I close it, the buffer is sent back into the prompt. It sounds simple, but it feels surprisingly native.
+
+This is how it looks like:
+
+
+
+What it is
+
+The idea is straightforward:
+
+
+
+It also pre-fills the temp file with whatever is already typed after Cursor Agent's → prompt, so I can continue where I left off.
+
+How it works (overview)
+
+This is the tmux binding I use (trimmed to the essentials):
+
+
+bind-key e run-shell -b "tmux display-message -p '#{pane_id}'
+ > /tmp/tmux-edit-target-#{client_pid} \;
+ tmux popup -E -w 90% -h 35% -x 5% -y 65% -d '#{pane_current_path}'
+ \"~/scripts/tmux-edit-send /tmp/tmux-edit-target-#{client_pid}\""
+
+
+And this is how it looks like after sending back the text to the Cursor Agent's input:
+
+
+
+And here is the full script. It is a bit ugly since it's shell (written with Cursor Agent with GPT-5.2-Codex), and I might (let) rewrite it in Go and release it once I have time. But it works well enough for now.
+
+
+#!/usr/bin/env bash
+set -u -o pipefail
+
+declare -i LOG_ENABLED=0
+
+log_file="${TMPDIR:-/tmp}/tmux-edit-send.log"
+
+log() {
+ if [ "$LOG_ENABLED" -eq 1 ]; then
+ printf '%s\n' "$*" >> "$log_file"
+ fi
+}
+
+# Read the target pane id from a temp file created by tmux binding.
+read_target_from_file() {
+ local file_path="$1"
+ if [ -n "$file_path" ] && [ -f "$file_path" ]; then
+ sed -n '1p' "$file_path" | tr -d '[:space:]'
+ fi
+}
+
+# Read the target pane id from tmux environment if present.
+read_target_from_env() {
+ local env_line
+ env_line="$(tmux show-environment -g TMUX_EDIT_TARGET 2>/dev/null || true)"
+ case "$env_line" in
+ TMUX_EDIT_TARGET=*) printf '%s' "${env_line#TMUX_EDIT_TARGET=}" ;;
+ esac
+}
+
+# Resolve the target pane id, falling back to the last pane.
+resolve_target_pane() {
+ local candidate="$1"
+ local current_pane last_pane
+
+ current_pane="$(tmux display-message -p "#{pane_id}" 2>/dev/null || true)"
+ log "current pane=${current_pane:-<empty>}"
+ if [ -n "$candidate" ] && [[ "$candidate" == *"#{"* ]]; then
+ log "format target detected, clearing"
+ candidate=""
+ fi
+ if [ -z "$candidate" ]; then
+ candidate="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
+ elif [ "$candidate" = "$current_pane" ]; then
+ last_pane="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
+ if [ -n "$last_pane" ]; then
+ candidate="$last_pane"
+ fi
+ fi
+ printf '%s' "$candidate"
+}
+
+# Capture the latest multi-line prompt content from the pane.
+capture_prompt_text() {
+ local target="$1"
+ tmux capture-pane -p -t "$target" -S -2000 2>/dev/null | awk '
+ function trim_box(line) {
+ sub(/^ *│ ?/, "", line)
+ sub(/ *│ *$/, "", line)
+ sub(/[[:space:]]+$/, "", line)
+ return line
+ }
+ /^ *│ *→/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0 {
+ if (text != "") last = text
+ text = ""
+ capture = 1
+ line = $0
+ sub(/^.*→ ?/, "", line)
+ line = trim_box(line)
+ if (line != "") text = line
+ next
+ }
+ capture {
+ if ($0 ~ /^ *└/) {
+ capture = 0
+ if (text != "") last = text
+ next
+ }
+ if ($0 ~ /^ *│/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0) {
+ line = trim_box($0)
+ if (line != "") {
+ if (text != "") text = text " " line
+ else text = line
+ }
+ }
+ }
+ END {
+ if (text != "") last = text
+ if (last != "") print last
+ }
+ '
+}
+
+# Write captured prompt text into the temp file if available.
+prefill_tmpfile() {
+ local tmpfile="$1"
+ local prompt_text="$2"
+ if [ -n "$prompt_text" ]; then
+ printf '%s\n' "$prompt_text" > "$tmpfile"
+ fi
+}
+
+# Ensure the target pane exists before sending keys.
+validate_target_pane() {
+ local target="$1"
+ local pane target_found
+ if [ -z "$target" ]; then
+ log "error: no target pane determined"
+ echo "Could not determine target pane." >&2
+ return 1
+ fi
+ target_found=0
+ for pane in $(tmux list-panes -a -F "#{pane_id}" 2>/dev/null || true); do
+ if [ "$pane" = "$target" ]; then
+ target_found=1
+ break
+ fi
+ done
+ if [ "$target_found" -ne 1 ]; then
+ log "error: target pane not found: $target"
+ echo "Target pane not found: $target" >&2
+ return 1
+ fi
+}
+
+# Send temp file contents to the target pane line by line.
+send_content() {
+ local target="$1"
+ local tmpfile="$2"
+ local prompt_text="$3"
+ local first_line=1
+ local line
+ while IFS= read -r line || [ -n "$line" ]; do
+ if [ "$first_line" -eq 1 ] && [ -n "$prompt_text" ]; then
+ if [[ "$line" == "$prompt_text"* ]]; then
+ line="${line#"$prompt_text"}"
+ fi
+ fi
+ first_line=0
+ tmux send-keys -t "$target" -l "$line"
+ tmux send-keys -t "$target" Enter
+ done < "$tmpfile"
+ log "sent content to $target"
+}
+
+# Main entry point.
+main() {
+ local target_file="${1:-}"
+ local target
+ local editor="${EDITOR:-vi}"
+ local tmpfile
+ local prompt_text
+
+ target="$(read_target_from_file "$target_file" || true)"
+ if [ -n "$target" ]; then
+ log "file target=${target:-<empty>}"
+ rm -f "$target_file"
+ fi
+ if [ -z "$target" ]; then
+ target="${TMUX_EDIT_TARGET:-}"
+ fi
+ log "env target=${target:-<empty>}"
+ if [ -z "$target" ]; then
+ target="$(read_target_from_env || true)"
+ fi
+ log "tmux env target=${target:-<empty>}"
+ target="$(resolve_target_pane "$target")"
+ log "fallback target=${target:-<empty>}"
+
+ tmpfile="$(mktemp "./.tmux-edit-send.XXXXXX.md")"
+ trap 'rm -f "$tmpfile"' EXIT
+
+ prompt_text="$(capture_prompt_text "$target")"
+ prefill_tmpfile "$tmpfile" "$prompt_text"
+
+ "$editor" "$tmpfile"
+ log "editor exited with status $?"
+
+ if [ ! -s "$tmpfile" ]; then
+ log "empty file, nothing sent"
+ exit 0
+ fi
+
+ validate_target_pane "$target"
+ send_content "$target" "$tmpfile" "$prompt_text"
+}
+
+main "$@"
+
+
+Challenges and small discoveries
+
+The problems were mostly small but annoying:
+
+
+
+Test cases (for a future rewrite)
+
+These are the cases I test whenever I touch the script:
+
+
+
+(Almost) works with any editor (or any TUI)
+
+Although I use Helix, this is just $EDITOR. If you prefer Vim, Neovim, or something more exotic, it should work. The same mechanism can be used to feed text into any TUI that reads from a terminal pane, not just Cursor Agent.
+
+One caveat: different agents draw different prompt UIs, so the capture logic depends on the prompt shape. A future version of this script should be more modular in that respect; for now this is just a PoC tailored to Cursor Agent.
+
+If I get a chance, I'll clean it up and rewrite it in Go (and release it properly). For now, I am happy with this little hack. It already feels like a native editing workflow for Cursor Agent prompts.
+
+E-Mail your comments to paul@nospam.buetow.org :-)
+
+Other related posts are:
+
+2026-02-02 A tmux popup editor for Cursor Agent prompts (You are currently reading this)
+2025-08-05 Local LLM for Coding with Ollama on macOS
+2025-05-02 Terminal multiplexing with tmux - Fish edition
+2024-06-23 Terminal multiplexing with tmux - Z-Shell edition
+
+Back to the main site
+
+
+
diff --git a/gemfeed/atom.xml b/gemfeed/atom.xml
index d8063be1..86db1b25 100644
--- a/gemfeed/atom.xml
+++ b/gemfeed/atom.xml
@@ -1,11 +1,319 @@
A tmux popup editor for Cursor Agent prompts
+
+...and any other TUI based application
+
+Table of Contents
+
+
+
+Why I built this
+
+I spend some time in Cursor Agent (the CLI version of the Cursor IDE, I don't like really the IDE), and I also jump between Claude Code CLI, Ampcode, Gemini CLI, OpenAI Codex CLI, OpenCode, and Aider just to see how things are evolving. But for the next month I'll be with Cursor Agent.
+
+Short prompts are fine in the inline input, but for longer prompts I want a real editor: spellcheck, search/replace, multiple cursors, and all the Helix muscle memory I already have.
+
+Cursor Agent has a Vim editing mode, but not Helix. And even in Vim mode I can't use my full editor setup. I want the real thing, not a partial emulation.
+
+https://helix-editor.com
+https://www.vim.org
+https://neovim.io
+
+So I built a tiny tmux popup editor. It opens $EDITOR (Helix for me), and when I close it, the buffer is sent back into the prompt. It sounds simple, but it feels surprisingly native.
+
+This is how it looks like:
+
+
+
+What it is
+
+The idea is straightforward:
+
+
+
+It also pre-fills the temp file with whatever is already typed after Cursor Agent's → prompt, so I can continue where I left off.
+
+How it works (overview)
+
+This is the tmux binding I use (trimmed to the essentials):
+
+
+bind-key e run-shell -b "tmux display-message -p '#{pane_id}'
+ > /tmp/tmux-edit-target-#{client_pid} \;
+ tmux popup -E -w 90% -h 35% -x 5% -y 65% -d '#{pane_current_path}'
+ \"~/scripts/tmux-edit-send /tmp/tmux-edit-target-#{client_pid}\""
+
+
+And this is how it looks like after sending back the text to the Cursor Agent's input:
+
+
+
+And here is the full script. It is a bit ugly since it's shell (written with Cursor Agent with GPT-5.2-Codex), and I might (let) rewrite it in Go and release it once I have time. But it works well enough for now.
+
+
+#!/usr/bin/env bash
+set -u -o pipefail
+
+declare -i LOG_ENABLED=0
+
+log_file="${TMPDIR:-/tmp}/tmux-edit-send.log"
+
+log() {
+ if [ "$LOG_ENABLED" -eq 1 ]; then
+ printf '%s\n' "$*" >> "$log_file"
+ fi
+}
+
+# Read the target pane id from a temp file created by tmux binding.
+read_target_from_file() {
+ local file_path="$1"
+ if [ -n "$file_path" ] && [ -f "$file_path" ]; then
+ sed -n '1p' "$file_path" | tr -d '[:space:]'
+ fi
+}
+
+# Read the target pane id from tmux environment if present.
+read_target_from_env() {
+ local env_line
+ env_line="$(tmux show-environment -g TMUX_EDIT_TARGET 2>/dev/null || true)"
+ case "$env_line" in
+ TMUX_EDIT_TARGET=*) printf '%s' "${env_line#TMUX_EDIT_TARGET=}" ;;
+ esac
+}
+
+# Resolve the target pane id, falling back to the last pane.
+resolve_target_pane() {
+ local candidate="$1"
+ local current_pane last_pane
+
+ current_pane="$(tmux display-message -p "#{pane_id}" 2>/dev/null || true)"
+ log "current pane=${current_pane:-<empty>}"
+ if [ -n "$candidate" ] && [[ "$candidate" == *"#{"* ]]; then
+ log "format target detected, clearing"
+ candidate=""
+ fi
+ if [ -z "$candidate" ]; then
+ candidate="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
+ elif [ "$candidate" = "$current_pane" ]; then
+ last_pane="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
+ if [ -n "$last_pane" ]; then
+ candidate="$last_pane"
+ fi
+ fi
+ printf '%s' "$candidate"
+}
+
+# Capture the latest multi-line prompt content from the pane.
+capture_prompt_text() {
+ local target="$1"
+ tmux capture-pane -p -t "$target" -S -2000 2>/dev/null | awk '
+ function trim_box(line) {
+ sub(/^ *│ ?/, "", line)
+ sub(/ *│ *$/, "", line)
+ sub(/[[:space:]]+$/, "", line)
+ return line
+ }
+ /^ *│ *→/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0 {
+ if (text != "") last = text
+ text = ""
+ capture = 1
+ line = $0
+ sub(/^.*→ ?/, "", line)
+ line = trim_box(line)
+ if (line != "") text = line
+ next
+ }
+ capture {
+ if ($0 ~ /^ *└/) {
+ capture = 0
+ if (text != "") last = text
+ next
+ }
+ if ($0 ~ /^ *│/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0) {
+ line = trim_box($0)
+ if (line != "") {
+ if (text != "") text = text " " line
+ else text = line
+ }
+ }
+ }
+ END {
+ if (text != "") last = text
+ if (last != "") print last
+ }
+ '
+}
+
+# Write captured prompt text into the temp file if available.
+prefill_tmpfile() {
+ local tmpfile="$1"
+ local prompt_text="$2"
+ if [ -n "$prompt_text" ]; then
+ printf '%s\n' "$prompt_text" > "$tmpfile"
+ fi
+}
+
+# Ensure the target pane exists before sending keys.
+validate_target_pane() {
+ local target="$1"
+ local pane target_found
+ if [ -z "$target" ]; then
+ log "error: no target pane determined"
+ echo "Could not determine target pane." >&2
+ return 1
+ fi
+ target_found=0
+ for pane in $(tmux list-panes -a -F "#{pane_id}" 2>/dev/null || true); do
+ if [ "$pane" = "$target" ]; then
+ target_found=1
+ break
+ fi
+ done
+ if [ "$target_found" -ne 1 ]; then
+ log "error: target pane not found: $target"
+ echo "Target pane not found: $target" >&2
+ return 1
+ fi
+}
+
+# Send temp file contents to the target pane line by line.
+send_content() {
+ local target="$1"
+ local tmpfile="$2"
+ local prompt_text="$3"
+ local first_line=1
+ local line
+ while IFS= read -r line || [ -n "$line" ]; do
+ if [ "$first_line" -eq 1 ] && [ -n "$prompt_text" ]; then
+ if [[ "$line" == "$prompt_text"* ]]; then
+ line="${line#"$prompt_text"}"
+ fi
+ fi
+ first_line=0
+ tmux send-keys -t "$target" -l "$line"
+ tmux send-keys -t "$target" Enter
+ done < "$tmpfile"
+ log "sent content to $target"
+}
+
+# Main entry point.
+main() {
+ local target_file="${1:-}"
+ local target
+ local editor="${EDITOR:-vi}"
+ local tmpfile
+ local prompt_text
+
+ target="$(read_target_from_file "$target_file" || true)"
+ if [ -n "$target" ]; then
+ log "file target=${target:-<empty>}"
+ rm -f "$target_file"
+ fi
+ if [ -z "$target" ]; then
+ target="${TMUX_EDIT_TARGET:-}"
+ fi
+ log "env target=${target:-<empty>}"
+ if [ -z "$target" ]; then
+ target="$(read_target_from_env || true)"
+ fi
+ log "tmux env target=${target:-<empty>}"
+ target="$(resolve_target_pane "$target")"
+ log "fallback target=${target:-<empty>}"
+
+ tmpfile="$(mktemp "./.tmux-edit-send.XXXXXX.md")"
+ trap 'rm -f "$tmpfile"' EXIT
+
+ prompt_text="$(capture_prompt_text "$target")"
+ prefill_tmpfile "$tmpfile" "$prompt_text"
+
+ "$editor" "$tmpfile"
+ log "editor exited with status $?"
+
+ if [ ! -s "$tmpfile" ]; then
+ log "empty file, nothing sent"
+ exit 0
+ fi
+
+ validate_target_pane "$target"
+ send_content "$target" "$tmpfile" "$prompt_text"
+}
+
+main "$@"
+
+
+Challenges and small discoveries
+
+The problems were mostly small but annoying:
+
+
+
+Test cases (for a future rewrite)
+
+These are the cases I test whenever I touch the script:
+
+
+
+(Almost) works with any editor (or any TUI)
+
+Although I use Helix, this is just $EDITOR. If you prefer Vim, Neovim, or something more exotic, it should work. The same mechanism can be used to feed text into any TUI that reads from a terminal pane, not just Cursor Agent.
+
+One caveat: different agents draw different prompt UIs, so the capture logic depends on the prompt shape. A future version of this script should be more modular in that respect; for now this is just a PoC tailored to Cursor Agent.
+
+If I get a chance, I'll clean it up and rewrite it in Go (and release it properly). For now, I am happy with this little hack. It already feels like a native editing workflow for Cursor Agent prompts.
+
+E-Mail your comments to paul@nospam.buetow.org :-)
+
+Other related posts are:
+
+2026-02-02 A tmux popup editor for Cursor Agent prompts (You are currently reading this)
+2025-08-05 Local LLM for Coding with Ollama on macOS
+2025-05-02 Terminal multiplexing with tmux - Fish edition
+2024-06-23 Terminal multiplexing with tmux - Z-Shell edition
+
+Back to the main site
+
E-Mail your comments to paul@nospam.buetow.org :-)
-Back to the main site
-
-
- One reason why I love OpenBSD
-
-Published at 2024-01-13T22:55:33+02:00
-
-
- FISHKISSFISHKIS
- SFISHKISSFISHKISSFISH F
- ISHK ISSFISHKISSFISHKISS FI
- SHKISS FISHKISSFISHKISSFISS FIS
-HKISSFISHKISSFISHKISSFISHKISSFISH KISS
- FISHKISSFISHKISSFISHKISSFISHKISS FISHK
- SSFISHKISSFISHKISSFISHKISSFISHKISSF
- ISHKISSFISHKISSFISHKISSFISHKISSF ISHKI
-SSFISHKISSFISHKISSFISHKISSFISHKIS SFIS
- HKISSFISHKISSFISHKISSFISHKISS FIS
- HKISSFISHKISSFISHKISSFISHK IS
- SFISHKISSFISHKISSFISH K
- ISSFISHKISSFISHK
-
-
-I just upgraded my OpenBSD's from 7.3 to 7.4 by following the unattended upgrade guide:
-
-https://www.openbsd.org/faq/upgrade74.html
-
-
-$ doas installboot sd0 # Update the bootloader (not for every upgrade required)
-$ doas sysupgrade # Update all binaries (including Kernel)
-
-
-sysupgrade downloaded and upgraded to the next release and rebooted the system. After the reboot, I run:
-
-Note to myself: I have to undo the /var/www symlink before upgrading, and re-establishing the symlink afterwards again. This is due to disk space constraings on my setup!
-
-
-$ doas sysmerge # Update system configuration files
-$ doas pkg_add -u # Update all packages
-$ doas reboot # Just in case, reboot one more time
-
-
-That's it! Took me around 5 minutes in total! No issues, only these few comands, only 5 minutes! It just works! No problems, no conflicts, no tons (actually none) config file merge conflicts.
-
-I followed the same procedure the previous times and never encountered any difficulties with any OpenBSD upgrades.
-
-I have seen upgrades of other Operating Systems either take a long time or break the system (which takes manual steps to repair). That's just one of many reasons why I love OpenBSD! There appear never to be any problems. It just gets its job done!
-
-The OpenBSD Project
-
-BTW: are you looking for an opinionated OpenBSD VM hoster? OpenBSD Amsterdam may be for you. They rock (I am having a VM there, too)!
-
-https://openbsd.amsterdam
-
-E-Mail your comments to paul@nospam.buetow.org :-)
-
-Other *BSD related posts are:
-
-2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability
-2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments
-2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage
-2025-05-11 f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network
-2025-04-05 f3s: Kubernetes with FreeBSD - Part 4: Rocky Linux Bhyve VMs
-2025-02-01 f3s: Kubernetes with FreeBSD - Part 3: Protecting from power cuts
-2024-12-03 f3s: Kubernetes with FreeBSD - Part 2: Hardware and base installation
-2024-11-17 f3s: Kubernetes with FreeBSD - Part 1: Setting the stage
-2024-04-01 KISS high-availability with OpenBSD
-2024-01-13 One reason why I love OpenBSD (You are currently reading this)
-2022-10-30 Installing DTail on OpenBSD
-2022-07-30 Let's Encrypt with OpenBSD and Rex
-2016-04-09 Jails and ZFS with Puppet on FreeBSD
-
Back to the main site
To be in the .zone!
+2026-02-02 - A tmux popup editor for Cursor Agent prompts
2026-01-01 - Using Supernote Nomad offline
2026-01-01 - Posts from July to December 2025
2026-01-01 - Cloudless Kobo Forma with KOReader
diff --git a/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo1.png b/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo1.png
new file mode 100644
index 00000000..5985d55f
Binary files /dev/null and b/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo1.png differ
diff --git a/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo2.png b/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo2.png
new file mode 100644
index 00000000..1d7f26d7
Binary files /dev/null and b/gemfeed/tmux-popup-editor-for-cursor-agent-prompts/demo2.png differ
diff --git a/index.html b/index.html
index 5f3fee80..42c87227 100644
--- a/index.html
+++ b/index.html
@@ -13,7 +13,7 @@