From 59b0854464ef29511a0d09f1e76fd1140e675833 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 11 May 2026 07:18:20 -0500 Subject: refactor(ai-vterm): rename Claude-specific names to a generic "agent" I may add other terminal agents to this launcher (aider, an open-source LLM TUI), so the buffer prefix, the user knob, and the internal helpers shouldn't say "Claude". The module name (ai-vterm) and the `cj/ai-vterm-*` customs were already generic. This finishes the job: - buffer prefix `claude []` -> `agent []` (the `defconst` and the matching display-buffer-alist regex move together) - `cj/ai-vterm-claude-command` -> `cj/ai-vterm-agent-command` (the default still runs the `claude` CLI, with a docstring note on swapping it) - `cj/--ai-vterm-claude-buffers` / `-displayed-claude-window` / `-reuse-existing-claude` -> `-agent-*`, and their test files renamed to match - prose in the module commentary and docstrings, plus the matching test docstrings and buffer-name literals `vterm-config.el` hardcodes the same buffer prefix in `cj/--vterm-toggle-buffer-p` (F12 excludes agent buffers from its candidate set), so that literal moved too. Collapsing it into the shared `cj/--ai-vterm-name-prefix` is a cleanup for another day. After a reload, a project's buffer opens as `agent [foo]` instead of `claude [foo]`. Old buffers keep their names until killed. I also corrected two stale `eshell-vterm-config.el` references in ai-vterm.el docstrings (that module was split into `vterm-config.el`). Two things keep saying "Claude": the `cj/ai-vterm-agent-command` default value (the actual CLI), and the "Claude Code" example in `vterm-config.el`'s cursor-restore docstring (a concrete TUI example, not branding). 90 tests pass. `make validate-modules` clean. --- tests/test-ai-vterm--agent-buffers.el | 59 +++++++ tests/test-ai-vterm--buffer-name.el | 14 +- tests/test-ai-vterm--candidates.el | 2 +- tests/test-ai-vterm--claude-buffers.el | 59 ------- tests/test-ai-vterm--dispatch.el | 42 ++--- tests/test-ai-vterm--display-rule.el | 14 +- tests/test-ai-vterm--display-saved.el | 194 ++++++++++++------------ tests/test-ai-vterm--displayed-agent-window.el | 60 ++++++++ tests/test-ai-vterm--displayed-claude-window.el | 60 -------- tests/test-ai-vterm--launch-command.el | 36 ++--- tests/test-ai-vterm--live-tmux-sessions.el | 2 +- tests/test-ai-vterm--pick-buffer-candidates.el | 40 ++--- tests/test-ai-vterm--pick-project.el | 4 +- tests/test-ai-vterm--reuse-existing-agent.el | 99 ++++++++++++ tests/test-ai-vterm--reuse-existing-claude.el | 99 ------------ tests/test-ai-vterm--show-or-create.el | 24 +-- tests/test-ai-vterm--sort-candidates.el | 2 +- tests/test-ai-vterm--tmux-session-name.el | 2 +- tests/test-vterm-tmux-history.el | 10 +- tests/test-vterm-toggle--buffer-filter.el | 36 ++--- tests/testutil-vterm-buffers.el | 8 +- 21 files changed, 433 insertions(+), 433 deletions(-) create mode 100644 tests/test-ai-vterm--agent-buffers.el delete mode 100644 tests/test-ai-vterm--claude-buffers.el create mode 100644 tests/test-ai-vterm--displayed-agent-window.el delete mode 100644 tests/test-ai-vterm--displayed-claude-window.el create mode 100644 tests/test-ai-vterm--reuse-existing-agent.el delete mode 100644 tests/test-ai-vterm--reuse-existing-claude.el (limited to 'tests') diff --git a/tests/test-ai-vterm--agent-buffers.el b/tests/test-ai-vterm--agent-buffers.el new file mode 100644 index 00000000..57d01730 --- /dev/null +++ b/tests/test-ai-vterm--agent-buffers.el @@ -0,0 +1,59 @@ +;;; test-ai-vterm--agent-buffers.el --- Tests for cj/--ai-vterm-agent-buffers -*- lexical-binding: t; -*- + +;;; Commentary: +;; The helper returns the list of buffers whose names start with the +;; literal prefix "agent [". Order is the same order `buffer-list' +;; gives them (most-recently-selected first). Non-agent buffers and +;; buffers whose names merely contain the prefix as a substring are +;; excluded. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) +(require 'ai-vterm) +(require 'testutil-vterm-buffers) + +(ert-deftest test-ai-vterm--agent-buffers-empty-when-none-exist () + "Boundary: no agent-prefixed buffers anywhere -> empty list." + (cj/test--kill-agent-buffers) + (unwind-protect + (should (null (cj/--ai-vterm-agent-buffers))) + (cj/test--kill-agent-buffers))) + +(ert-deftest test-ai-vterm--agent-buffers-returns-only-agent-buffers () + "Normal: filters to only agent-prefixed buffers, leaves others alone." + (cj/test--kill-agent-buffers) + (let ((c1 (get-buffer-create "agent [a]")) + (c2 (get-buffer-create "agent [b]")) + (other (get-buffer-create "regular-buffer"))) + (unwind-protect + (let ((result (cj/--ai-vterm-agent-buffers))) + (should (memq c1 result)) + (should (memq c2 result)) + (should-not (memq other result)) + (should (= (length result) 2))) + (kill-buffer c1) + (kill-buffer c2) + (kill-buffer other)))) + +(ert-deftest test-ai-vterm--agent-buffers-anchors-prefix-not-substring () + "Boundary: 'foo agent [bar]' is not an agent buffer -- prefix anchored." + (cj/test--kill-agent-buffers) + (let ((not-agent (get-buffer-create "foo agent [bar]"))) + (unwind-protect + (should-not (memq not-agent (cj/--ai-vterm-agent-buffers))) + (kill-buffer not-agent)))) + +(ert-deftest test-ai-vterm--agent-buffers-bare-agent-not-included () + "Boundary: 'agent' alone (no bracket) doesn't match the 'agent [' prefix." + (cj/test--kill-agent-buffers) + (let ((bare (get-buffer-create "agent"))) + (unwind-protect + (should-not (memq bare (cj/--ai-vterm-agent-buffers))) + (kill-buffer bare)))) + +(provide 'test-ai-vterm--agent-buffers) +;;; test-ai-vterm--agent-buffers.el ends here diff --git a/tests/test-ai-vterm--buffer-name.el b/tests/test-ai-vterm--buffer-name.el index 95c673ba..2ebe91ee 100644 --- a/tests/test-ai-vterm--buffer-name.el +++ b/tests/test-ai-vterm--buffer-name.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; Tests for the buffer-name transform. Given an absolute project -;; directory, the helper returns "claude []". The naming pattern +;; directory, the helper returns "agent []". The naming pattern ;; is what the display-buffer-alist rule keys on, so a regression here ;; silently breaks routing to the right side-window. @@ -14,29 +14,29 @@ (require 'ai-vterm) (ert-deftest test-ai-vterm--buffer-name-normal-project () - "Normal: a typical project path yields claude []." + "Normal: a typical project path yields agent []." (should (equal (cj/--ai-vterm-buffer-name "/home/cjennings/projects/foo") - "claude [foo]"))) + "agent [foo]"))) (ert-deftest test-ai-vterm--buffer-name-trailing-slash () "Boundary: trailing slash collapses before basename extraction." (should (equal (cj/--ai-vterm-buffer-name "/home/cjennings/projects/foo/") - "claude [foo]"))) + "agent [foo]"))) (ert-deftest test-ai-vterm--buffer-name-dot-prefix-dir () "Boundary: dot-prefix dirs (.emacs.d) preserve the dot in the basename." (should (equal (cj/--ai-vterm-buffer-name "/home/cjennings/.emacs.d") - "claude [.emacs.d]"))) + "agent [.emacs.d]"))) (ert-deftest test-ai-vterm--buffer-name-space-in-basename () "Boundary: a space in the basename round-trips into the buffer name." (should (equal (cj/--ai-vterm-buffer-name "/tmp/my work") - "claude [my work]"))) + "agent [my work]"))) (ert-deftest test-ai-vterm--buffer-name-deeply-nested () "Normal: only the last path component is used." (should (equal (cj/--ai-vterm-buffer-name "/a/b/c/d/e/leaf") - "claude [leaf]"))) + "agent [leaf]"))) (provide 'test-ai-vterm--buffer-name) ;;; test-ai-vterm--buffer-name.el ends here diff --git a/tests/test-ai-vterm--candidates.el b/tests/test-ai-vterm--candidates.el index b45888cc..be9041ce 100644 --- a/tests/test-ai-vterm--candidates.el +++ b/tests/test-ai-vterm--candidates.el @@ -19,7 +19,7 @@ (require 'ai-vterm) (defun test-ai-vterm--make-marker (dir) - "Create DIR/.ai/protocols.org so DIR registers as a Claude project." + "Create DIR/.ai/protocols.org so DIR registers as an AI-agent project." (let ((ai-dir (expand-file-name ".ai" dir))) (make-directory ai-dir t) (write-region "" nil (expand-file-name "protocols.org" ai-dir)))) diff --git a/tests/test-ai-vterm--claude-buffers.el b/tests/test-ai-vterm--claude-buffers.el deleted file mode 100644 index f975b64e..00000000 --- a/tests/test-ai-vterm--claude-buffers.el +++ /dev/null @@ -1,59 +0,0 @@ -;;; test-ai-vterm--claude-buffers.el --- Tests for cj/--ai-vterm-claude-buffers -*- lexical-binding: t; -*- - -;;; Commentary: -;; The helper returns the list of buffers whose names start with the -;; literal prefix "claude [". Order is the same order `buffer-list' -;; gives them (most-recently-selected first). Non-claude buffers and -;; buffers whose names merely contain the prefix as a substring are -;; excluded. - -;;; Code: - -(require 'ert) - -(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) -(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) -(require 'ai-vterm) -(require 'testutil-vterm-buffers) - -(ert-deftest test-ai-vterm--claude-buffers-empty-when-none-exist () - "Boundary: no claude-prefixed buffers anywhere -> empty list." - (cj/test--kill-claude-buffers) - (unwind-protect - (should (null (cj/--ai-vterm-claude-buffers))) - (cj/test--kill-claude-buffers))) - -(ert-deftest test-ai-vterm--claude-buffers-returns-only-claude-buffers () - "Normal: filters to only claude-prefixed buffers, leaves others alone." - (cj/test--kill-claude-buffers) - (let ((c1 (get-buffer-create "claude [a]")) - (c2 (get-buffer-create "claude [b]")) - (other (get-buffer-create "regular-buffer"))) - (unwind-protect - (let ((result (cj/--ai-vterm-claude-buffers))) - (should (memq c1 result)) - (should (memq c2 result)) - (should-not (memq other result)) - (should (= (length result) 2))) - (kill-buffer c1) - (kill-buffer c2) - (kill-buffer other)))) - -(ert-deftest test-ai-vterm--claude-buffers-anchors-prefix-not-substring () - "Boundary: 'foo claude [bar]' is not a claude buffer -- prefix anchored." - (cj/test--kill-claude-buffers) - (let ((not-claude (get-buffer-create "foo claude [bar]"))) - (unwind-protect - (should-not (memq not-claude (cj/--ai-vterm-claude-buffers))) - (kill-buffer not-claude)))) - -(ert-deftest test-ai-vterm--claude-buffers-bare-claude-not-included () - "Boundary: 'claude' alone (no bracket) doesn't match the 'claude [' prefix." - (cj/test--kill-claude-buffers) - (let ((bare (get-buffer-create "claude"))) - (unwind-protect - (should-not (memq bare (cj/--ai-vterm-claude-buffers))) - (kill-buffer bare)))) - -(provide 'test-ai-vterm--claude-buffers) -;;; test-ai-vterm--claude-buffers.el ends here diff --git a/tests/test-ai-vterm--dispatch.el b/tests/test-ai-vterm--dispatch.el index 8871af9a..94b02123 100644 --- a/tests/test-ai-vterm--dispatch.el +++ b/tests/test-ai-vterm--dispatch.el @@ -3,8 +3,8 @@ ;;; Commentary: ;; The dispatch helper is a pure decision function used by F9. ;; Returns one of (toggle-off . WIN), (redisplay-recent . BUF), -;; or (pick-project) based on whether a claude buffer is currently -;; displayed and whether any alive claude buffers exist. Tests mock +;; or (pick-project) based on whether an agent buffer is currently +;; displayed and whether any alive agent buffers exist. Tests mock ;; the two underlying helpers so the dispatch logic can be exercised ;; without touching real windows. @@ -19,38 +19,38 @@ (require 'testutil-vterm-buffers) (ert-deftest test-ai-vterm--dispatch-window-displayed-returns-toggle-off () - "Normal: displayed claude window -> (toggle-off . WIN)." + "Normal: displayed agent window -> (toggle-off . WIN)." (let ((sentinel-win 'fake-window)) - (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-claude-window) + (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-agent-window) (lambda (&optional _frame) sentinel-win))) (should (equal (cj/--ai-vterm-dispatch) (cons 'toggle-off sentinel-win)))))) (ert-deftest test-ai-vterm--dispatch-no-window-single-buffer-returns-redisplay-recent () - "Normal: no displayed claude, one alive buffer -> redisplay-recent + buffer." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [single]"))) + "Normal: no displayed agent, one alive buffer -> redisplay-recent + buffer." + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [single]"))) (unwind-protect - (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-claude-window) + (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-agent-window) (lambda (&optional _frame) nil)) - ((symbol-function 'cj/--ai-vterm-claude-buffers) + ((symbol-function 'cj/--ai-vterm-agent-buffers) (lambda () (list b1)))) (should (equal (cj/--ai-vterm-dispatch) (cons 'redisplay-recent b1)))) (kill-buffer b1)))) (ert-deftest test-ai-vterm--dispatch-no-window-multiple-buffers-returns-redisplay-recent () - "Normal: no displayed claude, 2+ alive buffers -> redisplay-recent + MRU. -F9 redisplays the most-recently-selected claude (head of buffer-list + "Normal: no displayed agent, 2+ alive buffers -> redisplay-recent + MRU. +F9 redisplays the most-recently-selected agent (head of buffer-list order) rather than opening the project picker, so the user toggles -THE claude they were last using. Other claudes are reachable via M-F9." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [a]")) - (b2 (get-buffer-create "claude [b]"))) +THE agent they were last using. Other agents are reachable via M-F9." + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [a]")) + (b2 (get-buffer-create "agent [b]"))) (unwind-protect - (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-claude-window) + (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-agent-window) (lambda (&optional _frame) nil)) - ((symbol-function 'cj/--ai-vterm-claude-buffers) + ((symbol-function 'cj/--ai-vterm-agent-buffers) (lambda () (list b1 b2)))) (should (equal (cj/--ai-vterm-dispatch) (cons 'redisplay-recent b1)))) @@ -58,11 +58,11 @@ THE claude they were last using. Other claudes are reachable via M-F9." (kill-buffer b2)))) (ert-deftest test-ai-vterm--dispatch-no-window-zero-buffers-returns-pick-project () - "Boundary: no displayed claude, zero alive buffers -> pick-project." - (cj/test--kill-claude-buffers) - (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-claude-window) + "Boundary: no displayed agent, zero alive buffers -> pick-project." + (cj/test--kill-agent-buffers) + (cl-letf (((symbol-function 'cj/--ai-vterm-displayed-agent-window) (lambda (&optional _frame) nil)) - ((symbol-function 'cj/--ai-vterm-claude-buffers) + ((symbol-function 'cj/--ai-vterm-agent-buffers) (lambda () nil))) (should (equal (cj/--ai-vterm-dispatch) '(pick-project))))) diff --git a/tests/test-ai-vterm--display-rule.el b/tests/test-ai-vterm--display-rule.el index af481eb3..15d270e2 100644 --- a/tests/test-ai-vterm--display-rule.el +++ b/tests/test-ai-vterm--display-rule.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; The module installs a `display-buffer-alist' entry routing buffers -;; whose names match "\\`claude \\[" to a right-side window. These +;; whose names match "\\`agent \\[" to a right-side window. These ;; tests verify the rule reaches the right side and ignores buffers ;; that don't match the prefix. @@ -26,13 +26,13 @@ (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) ,@body))) -(ert-deftest test-ai-vterm--display-rule-routes-claude-buffer-to-right () - "Normal: a buffer named \"claude [foo]\" lands in a window to the right. +(ert-deftest test-ai-vterm--display-rule-routes-agent-buffer-to-right () + "Normal: a buffer named \"agent [foo]\" lands in a window to the right. The rule uses `display-buffer-in-direction' with `(direction . right)', which splits the current window so the new window's left edge sits at a positive column. The buffer winds up in that new window." - (let ((name "claude [display-rule-test]")) + (let ((name "agent [display-rule-test]")) (test-ai-vterm--cleanup name) (unwind-protect (test-ai-vterm--with-clean-frame @@ -43,7 +43,7 @@ a positive column. The buffer winds up in that new window." (test-ai-vterm--cleanup name)))) (ert-deftest test-ai-vterm--display-rule-skips-non-matching-buffer () - "Boundary: a buffer not named \"claude [...]\" does not match the rule. + "Boundary: a buffer not named \"agent [...]\" does not match the rule. The rule's regex doesn't fire, so `display-buffer' falls back to the default action -- reuse the current window -- and no rightward split @@ -59,8 +59,8 @@ occurs." (test-ai-vterm--cleanup name)))) (ert-deftest test-ai-vterm--display-rule-prefix-not-substring () - "Boundary: \"foo claude [bar]\" does not match -- the rule anchors at start." - (let ((name "foo claude [substring-test]")) + "Boundary: \"foo agent [bar]\" does not match -- the rule anchors at start." + (let ((name "foo agent [substring-test]")) (test-ai-vterm--cleanup name) (unwind-protect (test-ai-vterm--with-clean-frame diff --git a/tests/test-ai-vterm--display-saved.el b/tests/test-ai-vterm--display-saved.el index 2b2bad09..a17df0aa 100644 --- a/tests/test-ai-vterm--display-saved.el +++ b/tests/test-ai-vterm--display-saved.el @@ -23,7 +23,7 @@ (ert-deftest test-ai-vterm--display-saved-uses-defaults-when-state-nil () "Normal: nil state -> direction=rightmost, size=cj/ai-vterm-window-width. The cardinal `right' default maps to the frame-edge variant -`rightmost' so claude lands at the frame's right edge regardless of +`rightmost' so agent lands at the frame's right edge regardless of which window is selected." (let (received-buf received-alist (cj/--ai-vterm-last-direction nil) @@ -100,13 +100,13 @@ which window is selected." "Regression: capture+delete+display in a 3-window layout preserves body-width. Reproduces Craig's `peeking ~1 col' report from 2026-05-09: when -the new claude lands at a different position than the captured one +the new agent lands at a different position than the captured one (rightmost vs middle), `window-total-width' differs by 1 because of the right divider. `window-body-width' is divider-independent and is what the user actually sees, so the assertion locks down the body match." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [3win-roundtrip]") + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [3win-roundtrip]") (left-name "*test-3win-left*") (right-name "*test-3win-right*")) (unwind-protect @@ -114,25 +114,25 @@ the body match." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (right-buf (get-buffer-create right-name)) - (claude-buf (get-buffer-create claude-name))) - ;; Build: left | claude | right. Selected window starts as + (agent-buf (get-buffer-create agent-name))) + ;; Build: left | agent | right. Selected window starts as ;; the only window. Split right twice to get three windows. (set-window-buffer (selected-window) left-buf) (let* ((right-win (split-window (selected-window) nil 'right)) (_ (set-window-buffer right-win right-buf)) - (claude-win (split-window (selected-window) nil 'right))) - (set-window-buffer claude-win claude-buf) - ;; Capture claude's state. - (cj/--ai-vterm-capture-state claude-win) + (agent-win (split-window (selected-window) nil 'right))) + (set-window-buffer agent-win agent-buf) + ;; Capture agent's state. + (cj/--ai-vterm-capture-state agent-win) (let ((captured-size cj/--ai-vterm-last-size) (captured-direction cj/--ai-vterm-last-direction)) - ;; Simulate quit-window on claude. - (delete-window claude-win) + ;; Simulate quit-window on agent. + (delete-window agent-win) ;; Now route a fresh display through the actual rule. (let* ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) - (new-win (display-buffer claude-buf))) + (new-win (display-buffer agent-buf))) (should (windowp new-win)) - (should (eq (window-buffer new-win) claude-buf)) + (should (eq (window-buffer new-win) agent-buf)) ;; The captured size should be replayed exactly. (should (= (window-body-width new-win) captured-size)) @@ -140,12 +140,12 @@ the body match." (should (eq captured-direction 'right))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer right-name) (kill-buffer right-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) -(ert-deftest test-ai-vterm--display-saved-3window-claude-rightmost-roundtrip () - "Round-trip when claude is the rightmost window (no right divider)." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [rightmost]") +(ert-deftest test-ai-vterm--display-saved-3window-agent-rightmost-roundtrip () + "Round-trip when agent is the rightmost window (no right divider)." + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [rightmost]") (left-name "*test-rm-left*") (mid-name "*test-rm-mid*")) (unwind-protect @@ -153,28 +153,28 @@ the body match." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (mid-buf (get-buffer-create mid-name)) - (claude-buf (get-buffer-create claude-name))) - ;; Build: left | mid | claude (claude rightmost) + (agent-buf (get-buffer-create agent-name))) + ;; Build: left | mid | agent (agent rightmost) (set-window-buffer (selected-window) left-buf) (let* ((mid-win (split-window (selected-window) nil 'right)) - (claude-win (split-window mid-win nil 'right))) + (agent-win (split-window mid-win nil 'right))) (set-window-buffer mid-win mid-buf) - (set-window-buffer claude-win claude-buf) - (cj/--ai-vterm-capture-state claude-win) + (set-window-buffer agent-win agent-buf) + (cj/--ai-vterm-capture-state agent-win) (let ((captured-size cj/--ai-vterm-last-size)) - (delete-window claude-win) + (delete-window agent-win) (let* ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) - (new-win (display-buffer claude-buf))) + (new-win (display-buffer agent-buf))) (should (windowp new-win)) (should (= (window-body-width new-win) captured-size))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer mid-name) (kill-buffer mid-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (ert-deftest test-ai-vterm--display-saved-3window-after-mouse-resize () "Round-trip after a deliberate mid-window resize (mimics mouse-drag)." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [mouse-resize]") + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [mouse-resize]") (left-name "*test-mr-left*") (right-name "*test-mr-right*")) (unwind-protect @@ -182,32 +182,32 @@ the body match." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (right-buf (get-buffer-create right-name)) - (claude-buf (get-buffer-create claude-name))) + (agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) left-buf) (let* ((right-win (split-window (selected-window) nil 'right)) - (claude-win (split-window (selected-window) nil 'right))) + (agent-win (split-window (selected-window) nil 'right))) (set-window-buffer right-win right-buf) - (set-window-buffer claude-win claude-buf) - ;; Resize claude smaller to mimic the user dragging the - ;; divider. Shrink claude by 5 cols, give to left. + (set-window-buffer agent-win agent-buf) + ;; Resize agent smaller to mimic the user dragging the + ;; divider. Shrink agent by 5 cols, give to left. (let ((delta -5)) - (when (window--resizable-p claude-win delta t) - (window-resize claude-win delta t))) - (cj/--ai-vterm-capture-state claude-win) + (when (window--resizable-p agent-win delta t) + (window-resize agent-win delta t))) + (cj/--ai-vterm-capture-state agent-win) (let ((captured-size cj/--ai-vterm-last-size)) - (delete-window claude-win) + (delete-window agent-win) (let* ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) - (new-win (display-buffer claude-buf))) + (new-win (display-buffer agent-buf))) (should (windowp new-win)) (should (= (window-body-width new-win) captured-size))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer right-name) (kill-buffer right-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (ert-deftest test-ai-vterm--display-saved-roundtrip-via-cj/ai-vterm-toggle () "End-to-end: toggle-off via dispatch then redisplay -- preserves size." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [toggle-roundtrip]") + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [toggle-roundtrip]") (left-name "*test-tr-left*") (right-name "*test-tr-right*")) (unwind-protect @@ -215,33 +215,33 @@ the body match." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (right-buf (get-buffer-create right-name)) - (claude-buf (get-buffer-create claude-name))) + (agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) left-buf) (let* ((right-win (split-window (selected-window) nil 'right)) - (claude-win (split-window (selected-window) nil 'right))) + (agent-win (split-window (selected-window) nil 'right))) (set-window-buffer right-win right-buf) - (set-window-buffer claude-win claude-buf) + (set-window-buffer agent-win agent-buf) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) - ;; Focus claude (mimics `M-x cj/ai-vterm' from inside claude). - (select-window claude-win) - (let ((before-size (window-body-width claude-win))) + ;; Focus agent (mimics `M-x cj/ai-vterm' from inside agent). + (select-window agent-win) + (let ((before-size (window-body-width agent-win))) ;; Toggle off via the actual command -- captures + quit-window. (cj/ai-vterm) - (should-not (cj/--ai-vterm-displayed-claude-window)) + (should-not (cj/--ai-vterm-displayed-agent-window)) ;; Toggle on -- single-buffer DWIM redisplay path. (cj/ai-vterm) - (let* ((new-win (cj/--ai-vterm-displayed-claude-window)) + (let* ((new-win (cj/--ai-vterm-displayed-agent-window)) (new-size (window-body-width new-win))) (should (windowp new-win)) (should (= new-size before-size)))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer right-name) (kill-buffer right-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (ert-deftest test-ai-vterm--display-saved-two-toggle-cycles-stable () "Two consecutive toggle-off+toggle-on cycles -- no compounding error." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [two-cycle]") + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [two-cycle]") (left-name "*test-2c-left*") (right-name "*test-2c-right*")) (unwind-protect @@ -249,86 +249,86 @@ the body match." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (right-buf (get-buffer-create right-name)) - (claude-buf (get-buffer-create claude-name))) + (agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) left-buf) (let* ((right-win (split-window (selected-window) nil 'right)) - (claude-win (split-window (selected-window) nil 'right))) + (agent-win (split-window (selected-window) nil 'right))) (set-window-buffer right-win right-buf) - (set-window-buffer claude-win claude-buf) + (set-window-buffer agent-win agent-buf) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) - (initial-size (window-body-width claude-win))) - (select-window claude-win) + (initial-size (window-body-width agent-win))) + (select-window agent-win) ;; Cycle 1 (cj/ai-vterm) ; off (cj/ai-vterm) ; on (let ((cycle1-size (window-body-width - (cj/--ai-vterm-displayed-claude-window)))) + (cj/--ai-vterm-displayed-agent-window)))) (should (= cycle1-size initial-size)) - (select-window (cj/--ai-vterm-displayed-claude-window)) + (select-window (cj/--ai-vterm-displayed-agent-window)) ;; Cycle 2 (cj/ai-vterm) ; off (cj/ai-vterm) ; on (let ((cycle2-size (window-body-width - (cj/--ai-vterm-displayed-claude-window)))) + (cj/--ai-vterm-displayed-agent-window)))) (should (= cycle2-size initial-size)))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer right-name) (kill-buffer right-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (ert-deftest test-ai-vterm--display-saved-craig-c-x-3-roundtrip () "Reproduces Craig's repro from 2026-05-09: launch -> F9 -> dashboard splits via C-x 3 -> toggle off -> toggle on. -Expected: new claude lands at the same total-width it had before." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [c-x-3-repro]") +Expected: new agent lands at the same total-width it had before." + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [c-x-3-repro]") (dash-name "*test-cx3-dashboard*")) (unwind-protect (save-window-excursion (delete-other-windows) (let ((dash-buf (get-buffer-create dash-name)) - (claude-buf (get-buffer-create claude-name))) + (agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) dash-buf) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) - ;; Step 1: F9 displays claude. Layout: dashboard | claude. - (let ((claude-win-1 (display-buffer claude-buf))) - (should (windowp claude-win-1))) + ;; Step 1: F9 displays agent. Layout: dashboard | agent. + (let ((agent-win-1 (display-buffer agent-buf))) + (should (windowp agent-win-1))) ;; Step 2: focus dashboard, C-x 3 (split-window-right). (let ((dash-win (get-buffer-window dash-buf))) (select-window dash-win) (split-window-right)) - ;; Layout now: dashboard1 | dashboard2 | claude - ;; Capture claude's pre-toggle body width for later assertion. - (let* ((claude-win-2 (cj/--ai-vterm-displayed-claude-window)) - (size-before (window-body-width claude-win-2))) - ;; Step 3: F9 toggles claude off (selected is dashboard). + ;; Layout now: dashboard1 | dashboard2 | agent + ;; Capture agent's pre-toggle body width for later assertion. + (let* ((agent-win-2 (cj/--ai-vterm-displayed-agent-window)) + (size-before (window-body-width agent-win-2))) + ;; Step 3: F9 toggles agent off (selected is dashboard). (cj/ai-vterm) - (should-not (cj/--ai-vterm-displayed-claude-window)) - ;; Step 4: F9 toggles claude on -- redisplay-single path. + (should-not (cj/--ai-vterm-displayed-agent-window)) + ;; Step 4: F9 toggles agent on -- redisplay-single path. (cj/ai-vterm) - (let* ((claude-win-3 (cj/--ai-vterm-displayed-claude-window)) - (size-after (window-body-width claude-win-3))) - (should (windowp claude-win-3)) + (let* ((agent-win-3 (cj/--ai-vterm-displayed-agent-window)) + (size-after (window-body-width agent-win-3))) + (should (windowp agent-win-3)) (should (= size-after size-before))))))) (when (get-buffer dash-name) (kill-buffer dash-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (ert-deftest test-ai-vterm--toggle-after-buffer-move-no-extra-window () - "Regression: toggle-off must remove claude's window even when buffer-move + "Regression: toggle-off must remove agent's window even when buffer-move has cleared its `quit-restore' parameter. Reproduces Craig's repro from 2026-05-09: 3 windows, user uses -buffer-move (C-M-arrows) to relocate claude. buffer-move swaps +buffer-move (C-M-arrows) to relocate agent. buffer-move swaps buffers between windows and leaves the receiving window with no -record that it was created for the claude buffer. `quit-window' +record that it was created for the agent buffer. `quit-window' respects that history and only buries -- the window stays with some other buffer in it. The next toggle-on then doesn't recognize -that window as a claude home and creates a fresh one alongside, +that window as an agent home and creates a fresh one alongside, landing the user at N+1 windows instead of N. Assertion: after toggle-off+toggle-on, the window count is back to its pre-cycle value, regardless of `quit-restore' state." - (cj/test--kill-claude-buffers) - (let ((claude-name "claude [buffer-move-toggle]") + (cj/test--kill-agent-buffers) + (let ((agent-name "agent [buffer-move-toggle]") (left-name "*test-bm-left*") (right-name "*test-bm-right*")) (unwind-protect @@ -336,31 +336,31 @@ its pre-cycle value, regardless of `quit-restore' state." (delete-other-windows) (let ((left-buf (get-buffer-create left-name)) (right-buf (get-buffer-create right-name)) - (claude-buf (get-buffer-create claude-name))) + (agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) left-buf) (let* ((right-win (split-window (selected-window) nil 'right)) - (claude-win (split-window (selected-window) nil 'right))) + (agent-win (split-window (selected-window) nil 'right))) (set-window-buffer right-win right-buf) - (set-window-buffer claude-win claude-buf) - ;; Mimic buffer-move's effect: claude lives in this + (set-window-buffer agent-win agent-buf) + ;; Mimic buffer-move's effect: agent lives in this ;; window but quit-restore says nothing about it. - (set-window-parameter claude-win 'quit-restore nil) + (set-window-parameter agent-win 'quit-restore nil) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) (window-count-before (count-windows))) - (select-window claude-win) + (select-window agent-win) (cj/ai-vterm) ; off (cj/ai-vterm) ; on (should (= (count-windows) window-count-before)) - ;; Claude must be displayed exactly once. - (let ((claude-windows + ;; Agent must be displayed exactly once. + (let ((agent-windows (seq-filter (lambda (w) - (eq (window-buffer w) claude-buf)) + (eq (window-buffer w) agent-buf)) (window-list)))) - (should (= (length claude-windows) 1))))))) + (should (= (length agent-windows) 1))))))) (when (get-buffer left-name) (kill-buffer left-name)) (when (get-buffer right-name) (kill-buffer right-name)) - (cj/test--kill-claude-buffers)))) + (cj/test--kill-agent-buffers)))) (provide 'test-ai-vterm--display-saved) ;;; test-ai-vterm--display-saved.el ends here diff --git a/tests/test-ai-vterm--displayed-agent-window.el b/tests/test-ai-vterm--displayed-agent-window.el new file mode 100644 index 00000000..f36ca9f5 --- /dev/null +++ b/tests/test-ai-vterm--displayed-agent-window.el @@ -0,0 +1,60 @@ +;;; test-ai-vterm--displayed-agent-window.el --- Tests for the displayed-window helper -*- lexical-binding: t; -*- + +;;; Commentary: +;; The helper returns a window in the selected frame whose buffer +;; satisfies `cj/--ai-vterm-buffer-p', or nil when no such window +;; exists. Used by F9 dispatch and M-F9 in-place replacement. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) +(require 'ai-vterm) +(require 'testutil-vterm-buffers) + +(ert-deftest test-ai-vterm--displayed-agent-window-no-buffers-returns-nil () + "Boundary: no agent buffers anywhere -> nil." + (cj/test--kill-agent-buffers) + (save-window-excursion + (delete-other-windows) + (should-not (cj/--ai-vterm-displayed-agent-window)))) + +(ert-deftest test-ai-vterm--displayed-agent-window-not-displayed-returns-nil () + "Boundary: agent buffer exists but not in any window -> nil." + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [hidden]"))) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (should-not (cj/--ai-vterm-displayed-agent-window))) + (kill-buffer b1)))) + +(ert-deftest test-ai-vterm--displayed-agent-window-returns-window-when-displayed () + "Normal: agent buffer in a window -> returns that window." + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [shown]"))) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (let ((win (split-window-right))) + (set-window-buffer win b1) + (let ((result (cj/--ai-vterm-displayed-agent-window))) + (should (windowp result)) + (should (eq (window-buffer result) b1))))) + (kill-buffer b1)))) + +(ert-deftest test-ai-vterm--displayed-agent-window-ignores-non-agent-windows () + "Boundary: only a non-agent buffer is displayed -> nil." + (cj/test--kill-agent-buffers) + (let ((other (get-buffer-create "regular-displayed-buffer"))) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (set-window-buffer (selected-window) other) + (should-not (cj/--ai-vterm-displayed-agent-window))) + (kill-buffer other)))) + +(provide 'test-ai-vterm--displayed-agent-window) +;;; test-ai-vterm--displayed-agent-window.el ends here diff --git a/tests/test-ai-vterm--displayed-claude-window.el b/tests/test-ai-vterm--displayed-claude-window.el deleted file mode 100644 index 9f84f87b..00000000 --- a/tests/test-ai-vterm--displayed-claude-window.el +++ /dev/null @@ -1,60 +0,0 @@ -;;; test-ai-vterm--displayed-claude-window.el --- Tests for the displayed-window helper -*- lexical-binding: t; -*- - -;;; Commentary: -;; The helper returns a window in the selected frame whose buffer -;; satisfies `cj/--ai-vterm-buffer-p', or nil when no such window -;; exists. Used by F9 dispatch and M-F9 in-place replacement. - -;;; Code: - -(require 'ert) - -(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) -(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) -(require 'ai-vterm) -(require 'testutil-vterm-buffers) - -(ert-deftest test-ai-vterm--displayed-claude-window-no-buffers-returns-nil () - "Boundary: no claude buffers anywhere -> nil." - (cj/test--kill-claude-buffers) - (save-window-excursion - (delete-other-windows) - (should-not (cj/--ai-vterm-displayed-claude-window)))) - -(ert-deftest test-ai-vterm--displayed-claude-window-not-displayed-returns-nil () - "Boundary: claude buffer exists but not in any window -> nil." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [hidden]"))) - (unwind-protect - (save-window-excursion - (delete-other-windows) - (should-not (cj/--ai-vterm-displayed-claude-window))) - (kill-buffer b1)))) - -(ert-deftest test-ai-vterm--displayed-claude-window-returns-window-when-displayed () - "Normal: claude buffer in a window -> returns that window." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [shown]"))) - (unwind-protect - (save-window-excursion - (delete-other-windows) - (let ((win (split-window-right))) - (set-window-buffer win b1) - (let ((result (cj/--ai-vterm-displayed-claude-window))) - (should (windowp result)) - (should (eq (window-buffer result) b1))))) - (kill-buffer b1)))) - -(ert-deftest test-ai-vterm--displayed-claude-window-ignores-non-claude-windows () - "Boundary: only a non-claude buffer is displayed -> nil." - (cj/test--kill-claude-buffers) - (let ((other (get-buffer-create "regular-displayed-buffer"))) - (unwind-protect - (save-window-excursion - (delete-other-windows) - (set-window-buffer (selected-window) other) - (should-not (cj/--ai-vterm-displayed-claude-window))) - (kill-buffer other)))) - -(provide 'test-ai-vterm--displayed-claude-window) -;;; test-ai-vterm--displayed-claude-window.el ends here diff --git a/tests/test-ai-vterm--launch-command.el b/tests/test-ai-vterm--launch-command.el index 17f02f02..7e455a8b 100644 --- a/tests/test-ai-vterm--launch-command.el +++ b/tests/test-ai-vterm--launch-command.el @@ -2,12 +2,12 @@ ;;; Commentary: ;; The launch command is what gets typed into a fresh vterm shell to bring -;; up Claude inside a per-project tmux session. The session is named +;; up the agent inside a per-project tmux session. The session is named ;; `cj/ai-vterm-tmux-session-prefix' + the project basename, so a second -;; F9 on the same project reattaches to the running Claude rather than +;; F9 on the same project reattaches to the running agent rather than ;; spawning a new one, and `tmux ls' output can be filtered to AI-vterm's ;; own sessions. The trailing `exec bash' keeps the tmux window alive if -;; Claude exits, leaving the session intact for recovery. +;; the agent exits, leaving the session intact for recovery. ;;; Code: @@ -18,22 +18,22 @@ (ert-deftest test-ai-vterm--launch-command-uses-new-session-attach () "Normal: starts with `tmux new-session -A' so existing sessions reattach." - (let ((cj/ai-vterm-claude-command "claude")) + (let ((cj/ai-vterm-agent-command "agent")) (should (string-prefix-p "tmux new-session -A " (cj/--ai-vterm-launch-command "/code/foo"))))) (ert-deftest test-ai-vterm--launch-command-includes-prefixed-session-name () "Normal: the session name is the prefixed form from the name helper." - (let ((cj/ai-vterm-claude-command "claude") + (let ((cj/ai-vterm-agent-command "agent") (cj/ai-vterm-tmux-session-prefix "aiv-")) (should (string-match-p " -s aiv-foo " (cj/--ai-vterm-launch-command "/code/foo"))))) (ert-deftest test-ai-vterm--launch-command-names-window () - "Normal: `-n ' so the claude window is named distinctly." - (let ((cj/ai-vterm-claude-command "claude") + "Normal: `-n ' so the agent window is named distinctly." + (let ((cj/ai-vterm-agent-command "agent") (cj/ai-vterm-tmux-window-name "ai")) (should (string-match-p " -n ai " @@ -41,36 +41,36 @@ (ert-deftest test-ai-vterm--launch-command-honors-custom-window-name () "Boundary: a non-default window name is what `-n' gets." - (let ((cj/ai-vterm-claude-command "claude") - (cj/ai-vterm-tmux-window-name "claude")) + (let ((cj/ai-vterm-agent-command "agent") + (cj/ai-vterm-tmux-window-name "agent")) (should (string-match-p - " -n claude " + " -n agent " (cj/--ai-vterm-launch-command "/code/foo"))))) (ert-deftest test-ai-vterm--launch-command-includes-start-directory () "Normal: `-c ' so the new session's first window starts in DIR." - (let ((cj/ai-vterm-claude-command "claude")) + (let ((cj/ai-vterm-agent-command "agent")) (should (string-match-p " -c /code/foo " (cj/--ai-vterm-launch-command "/code/foo"))))) -(ert-deftest test-ai-vterm--launch-command-includes-claude-command () - "Normal: the configured claude command is in the launched shell command." - (let ((cj/ai-vterm-claude-command "claude --some-flag")) +(ert-deftest test-ai-vterm--launch-command-includes-agent-command () + "Normal: the configured agent command is in the launched shell command." + (let ((cj/ai-vterm-agent-command "agent --some-flag")) (should (string-match-p - "claude --some-flag" + "agent --some-flag" (cj/--ai-vterm-launch-command "/code/foo"))))) (ert-deftest test-ai-vterm--launch-command-tails-with-exec-bash () - "Boundary: `exec bash' tails so the tmux window survives Claude exiting." - (let ((cj/ai-vterm-claude-command "claude")) + "Boundary: `exec bash' tails so the tmux window survives the agent exiting." + (let ((cj/ai-vterm-agent-command "agent")) (should (string-match-p "exec bash" (cj/--ai-vterm-launch-command "/code/foo"))))) (ert-deftest test-ai-vterm--launch-command-handles-spaces-in-basename () "Boundary: a basename with whitespace becomes hyphenated before quoting." - (let ((cj/ai-vterm-claude-command "claude") + (let ((cj/ai-vterm-agent-command "agent") (cj/ai-vterm-tmux-session-prefix "aiv-")) (should (string-match-p " -s aiv-my-work " diff --git a/tests/test-ai-vterm--live-tmux-sessions.el b/tests/test-ai-vterm--live-tmux-sessions.el index 38a0488d..e00b0018 100644 --- a/tests/test-ai-vterm--live-tmux-sessions.el +++ b/tests/test-ai-vterm--live-tmux-sessions.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; Lists the live tmux sessions that carry the AI-vterm prefix so the -;; project picker can surface projects whose Claude session survived an +;; project picker can surface projects whose agent session survived an ;; Emacs crash. tmux being absent or no server running is a normal ;; "nothing to match" outcome, not an error -- the lister returns nil. diff --git a/tests/test-ai-vterm--pick-buffer-candidates.el b/tests/test-ai-vterm--pick-buffer-candidates.el index ddfd7529..c32039de 100644 --- a/tests/test-ai-vterm--pick-buffer-candidates.el +++ b/tests/test-ai-vterm--pick-buffer-candidates.el @@ -22,57 +22,57 @@ (ert-deftest test-ai-vterm--pick-buffer-candidates-empty-buffers () "Boundary: empty buffer list -> empty alist regardless of shown." - (cj/test--kill-claude-buffers) + (cj/test--kill-agent-buffers) (should (null (cj/--ai-vterm-pick-buffer-candidates nil nil))) (should (null (cj/--ai-vterm-pick-buffer-candidates nil 'sentinel)))) (ert-deftest test-ai-vterm--pick-buffer-candidates-shown-nil () "Normal: shown is nil -> straight alist in input order, no marker." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [a]")) - (b2 (get-buffer-create "claude [b]"))) + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [a]")) + (b2 (get-buffer-create "agent [b]"))) (unwind-protect (let ((result (cj/--ai-vterm-pick-buffer-candidates (list b1 b2) nil))) - (should (equal result `(("claude [a]" . ,b1) - ("claude [b]" . ,b2))))) + (should (equal result `(("agent [a]" . ,b1) + ("agent [b]" . ,b2))))) (kill-buffer b1) (kill-buffer b2)))) (ert-deftest test-ai-vterm--pick-buffer-candidates-shown-promotes-non-shown () "Normal: shown buffer sorts last with [shown] suffix; others first." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [a]")) - (b2 (get-buffer-create "claude [b]")) - (b3 (get-buffer-create "claude [c]"))) + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [a]")) + (b2 (get-buffer-create "agent [b]")) + (b3 (get-buffer-create "agent [c]"))) (unwind-protect (let ((result (cj/--ai-vterm-pick-buffer-candidates (list b1 b2 b3) b1))) (should (equal result - `(("claude [b]" . ,b2) - ("claude [c]" . ,b3) - ("claude [a] [shown]" . ,b1))))) + `(("agent [b]" . ,b2) + ("agent [c]" . ,b3) + ("agent [a] [shown]" . ,b1))))) (kill-buffer b1) (kill-buffer b2) (kill-buffer b3)))) (ert-deftest test-ai-vterm--pick-buffer-candidates-shown-only-buffer () "Boundary: shown is the only entry -> single cell with [shown] marker." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [a]"))) + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [a]"))) (unwind-protect (let ((result (cj/--ai-vterm-pick-buffer-candidates (list b1) b1))) - (should (equal result `(("claude [a] [shown]" . ,b1))))) + (should (equal result `(("agent [a] [shown]" . ,b1))))) (kill-buffer b1)))) (ert-deftest test-ai-vterm--pick-buffer-candidates-shown-not-in-buffers () "Boundary: stale shown buffer not in list -> all cells are non-shown." - (cj/test--kill-claude-buffers) - (let ((b1 (get-buffer-create "claude [a]")) - (b-stale (get-buffer-create "claude [stale]"))) + (cj/test--kill-agent-buffers) + (let ((b1 (get-buffer-create "agent [a]")) + (b-stale (get-buffer-create "agent [stale]"))) (unwind-protect (let ((result (cj/--ai-vterm-pick-buffer-candidates (list b1) b-stale))) - (should (equal result `(("claude [a]" . ,b1))))) + (should (equal result `(("agent [a]" . ,b1))))) (kill-buffer b1) (kill-buffer b-stale)))) diff --git a/tests/test-ai-vterm--pick-project.el b/tests/test-ai-vterm--pick-project.el index a90fe822..f332589a 100644 --- a/tests/test-ai-vterm--pick-project.el +++ b/tests/test-ai-vterm--pick-project.el @@ -69,7 +69,7 @@ Works whether COLLECTION is an alist or a completion-table function." '("/c/baz [detached]" "/c/bar" "/c/foo")))))) (ert-deftest test-ai-vterm--format-candidate-flags-running-project () - "Normal: a path whose claude buffer has a live process gets a [running] suffix." + "Normal: a path whose agent buffer has a live process gets a [running] suffix." (let* ((path (expand-file-name "~/code/already-running")) (buffer-name (cj/--ai-vterm-buffer-name path)) (buf (get-buffer-create buffer-name))) @@ -107,7 +107,7 @@ Works whether COLLECTION is an alist or a completion-table function." (ert-deftest test-ai-vterm--format-candidate-omits-flag-when-not-running () "Boundary: a path with no buffer or no live process -> plain abbreviated path." (let ((path (expand-file-name "~/code/not-running"))) - ;; Make sure no claude buffer exists for this path. + ;; Make sure no agent buffer exists for this path. (let ((bn (cj/--ai-vterm-buffer-name path))) (when (get-buffer bn) (kill-buffer bn))) (should (equal (cj/--ai-vterm-format-candidate path) diff --git a/tests/test-ai-vterm--reuse-existing-agent.el b/tests/test-ai-vterm--reuse-existing-agent.el new file mode 100644 index 00000000..e6848014 --- /dev/null +++ b/tests/test-ai-vterm--reuse-existing-agent.el @@ -0,0 +1,99 @@ +;;; test-ai-vterm--reuse-existing-agent.el --- Tests for reuse-existing-agent action -*- lexical-binding: t; -*- + +;;; Commentary: +;; The action looks for any window in the selected frame whose buffer +;; satisfies `cj/--ai-vterm-buffer-p'. When found, swaps that +;; window's buffer for the one being displayed and returns the +;; window. When not found, returns nil so the next action in the +;; chain runs. +;; +;; This is the action that keeps C-F9 (project-switch) from stealing +;; a non-agent window when the user is focused inside agent. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) +(require 'ai-vterm) +(require 'testutil-vterm-buffers) + +(ert-deftest test-ai-vterm--reuse-existing-agent-swaps-buffer-when-window-exists () + "Normal: an agent window exists -> swap its buffer, return the window." + (cj/test--kill-agent-buffers) + (save-window-excursion + (delete-other-windows) + (let ((existing (get-buffer-create "agent [existing]")) + (new-buf (get-buffer-create "agent [new]")) + (split (split-window (selected-window) nil 'right))) + (unwind-protect + (progn + (set-window-buffer split existing) + (let ((result (cj/--ai-vterm-reuse-existing-agent new-buf nil))) + (should (eq result split)) + (should (eq (window-buffer split) new-buf)))) + (kill-buffer existing) + (kill-buffer new-buf))))) + +(ert-deftest test-ai-vterm--reuse-existing-agent-returns-nil-when-no-agent-window () + "Boundary: no agent window in frame -> nil (chain continues to next action)." + (cj/test--kill-agent-buffers) + (save-window-excursion + (delete-other-windows) + (let ((new-buf (get-buffer-create "agent [no-existing]"))) + (unwind-protect + (should (null (cj/--ai-vterm-reuse-existing-agent new-buf nil))) + (kill-buffer new-buf))))) + +(ert-deftest test-ai-vterm--reuse-existing-agent-leaves-non-agent-windows-alone () + "Boundary: only non-agent windows in frame -> nil; other windows untouched." + (cj/test--kill-agent-buffers) + (save-window-excursion + (delete-other-windows) + (let ((code-buf (get-buffer-create "*test-code-buffer*")) + (new-agent (get-buffer-create "agent [new-here]")) + (other-win (split-window (selected-window) nil 'right))) + (unwind-protect + (progn + (set-window-buffer (selected-window) code-buf) + (set-window-buffer other-win code-buf) + (let ((result (cj/--ai-vterm-reuse-existing-agent + new-agent nil))) + (should (null result)) + (should (eq (window-buffer (selected-window)) code-buf)) + (should (eq (window-buffer other-win) code-buf)))) + (kill-buffer code-buf) + (kill-buffer new-agent))))) + +(ert-deftest test-ai-vterm--reuse-existing-agent-preserves-non-agent-window-when-swapping () + "Normal: swap agent window only; the other window keeps its buffer. + +This is the C-F9-from-agent regression: with agent at the bottom +and code on top, switching projects must replace the bottom window's +buffer, not the top window's." + (cj/test--kill-agent-buffers) + (save-window-excursion + (delete-other-windows) + (let* ((code-buf (get-buffer-create "*test-code-top*")) + (agent-a (get-buffer-create "agent [a]")) + (agent-b (get-buffer-create "agent [b]")) + (top-win (selected-window)) + (bottom-win (split-window top-win nil 'below))) + (unwind-protect + (progn + (set-window-buffer top-win code-buf) + (set-window-buffer bottom-win agent-a) + ;; Focus the agent window -- this is the regression scenario. + (select-window bottom-win) + (let ((result (cj/--ai-vterm-reuse-existing-agent + agent-b nil))) + (should (eq result bottom-win)) + (should (eq (window-buffer bottom-win) agent-b)) + (should (eq (window-buffer top-win) code-buf)))) + (kill-buffer code-buf) + (kill-buffer agent-a) + (kill-buffer agent-b))))) + +(provide 'test-ai-vterm--reuse-existing-agent) +;;; test-ai-vterm--reuse-existing-agent.el ends here diff --git a/tests/test-ai-vterm--reuse-existing-claude.el b/tests/test-ai-vterm--reuse-existing-claude.el deleted file mode 100644 index 195e50a2..00000000 --- a/tests/test-ai-vterm--reuse-existing-claude.el +++ /dev/null @@ -1,99 +0,0 @@ -;;; test-ai-vterm--reuse-existing-claude.el --- Tests for reuse-existing-claude action -*- lexical-binding: t; -*- - -;;; Commentary: -;; The action looks for any window in the selected frame whose buffer -;; satisfies `cj/--ai-vterm-buffer-p'. When found, swaps that -;; window's buffer for the one being displayed and returns the -;; window. When not found, returns nil so the next action in the -;; chain runs. -;; -;; This is the action that keeps C-F9 (project-switch) from stealing -;; a non-claude window when the user is focused inside claude. - -;;; Code: - -(require 'ert) - -(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) -(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) -(require 'ai-vterm) -(require 'testutil-vterm-buffers) - -(ert-deftest test-ai-vterm--reuse-existing-claude-swaps-buffer-when-window-exists () - "Normal: a claude window exists -> swap its buffer, return the window." - (cj/test--kill-claude-buffers) - (save-window-excursion - (delete-other-windows) - (let ((existing (get-buffer-create "claude [existing]")) - (new-buf (get-buffer-create "claude [new]")) - (split (split-window (selected-window) nil 'right))) - (unwind-protect - (progn - (set-window-buffer split existing) - (let ((result (cj/--ai-vterm-reuse-existing-claude new-buf nil))) - (should (eq result split)) - (should (eq (window-buffer split) new-buf)))) - (kill-buffer existing) - (kill-buffer new-buf))))) - -(ert-deftest test-ai-vterm--reuse-existing-claude-returns-nil-when-no-claude-window () - "Boundary: no claude window in frame -> nil (chain continues to next action)." - (cj/test--kill-claude-buffers) - (save-window-excursion - (delete-other-windows) - (let ((new-buf (get-buffer-create "claude [no-existing]"))) - (unwind-protect - (should (null (cj/--ai-vterm-reuse-existing-claude new-buf nil))) - (kill-buffer new-buf))))) - -(ert-deftest test-ai-vterm--reuse-existing-claude-leaves-non-claude-windows-alone () - "Boundary: only non-claude windows in frame -> nil; other windows untouched." - (cj/test--kill-claude-buffers) - (save-window-excursion - (delete-other-windows) - (let ((code-buf (get-buffer-create "*test-code-buffer*")) - (new-claude (get-buffer-create "claude [new-here]")) - (other-win (split-window (selected-window) nil 'right))) - (unwind-protect - (progn - (set-window-buffer (selected-window) code-buf) - (set-window-buffer other-win code-buf) - (let ((result (cj/--ai-vterm-reuse-existing-claude - new-claude nil))) - (should (null result)) - (should (eq (window-buffer (selected-window)) code-buf)) - (should (eq (window-buffer other-win) code-buf)))) - (kill-buffer code-buf) - (kill-buffer new-claude))))) - -(ert-deftest test-ai-vterm--reuse-existing-claude-preserves-non-claude-window-when-swapping () - "Normal: swap claude window only; the other window keeps its buffer. - -This is the C-F9-from-claude regression: with claude at the bottom -and code on top, switching projects must replace the bottom window's -buffer, not the top window's." - (cj/test--kill-claude-buffers) - (save-window-excursion - (delete-other-windows) - (let* ((code-buf (get-buffer-create "*test-code-top*")) - (claude-a (get-buffer-create "claude [a]")) - (claude-b (get-buffer-create "claude [b]")) - (top-win (selected-window)) - (bottom-win (split-window top-win nil 'below))) - (unwind-protect - (progn - (set-window-buffer top-win code-buf) - (set-window-buffer bottom-win claude-a) - ;; Focus the claude window -- this is the regression scenario. - (select-window bottom-win) - (let ((result (cj/--ai-vterm-reuse-existing-claude - claude-b nil))) - (should (eq result bottom-win)) - (should (eq (window-buffer bottom-win) claude-b)) - (should (eq (window-buffer top-win) code-buf)))) - (kill-buffer code-buf) - (kill-buffer claude-a) - (kill-buffer claude-b))))) - -(provide 'test-ai-vterm--reuse-existing-claude) -;;; test-ai-vterm--reuse-existing-claude.el ends here diff --git a/tests/test-ai-vterm--show-or-create.el b/tests/test-ai-vterm--show-or-create.el index 3fee4883..0a3dbde5 100644 --- a/tests/test-ai-vterm--show-or-create.el +++ b/tests/test-ai-vterm--show-or-create.el @@ -3,7 +3,7 @@ ;;; Commentary: ;; Tests the show-or-create branching: ;; -;; - buffer absent -> vterm called, claude command sent +;; - buffer absent -> vterm called, agent command sent ;; - buffer present, live -> vterm not called, buffer displayed ;; - buffer present, dead -> old buffer killed, vterm recreates ;; @@ -58,7 +58,7 @@ VARS is a plist of capture variable names: :calls, :strings, :returns, (ert-deftest test-ai-vterm--show-or-create-creates-when-buffer-missing () "Normal: no existing buffer -> vterm called once, launch cmd sent." - (let ((name "claude [normal-create-test]")) + (let ((name "agent [normal-create-test]")) (test-ai-vterm--cleanup name) (unwind-protect (test-ai-vterm--with-mock-vterm (:calls calls :strings strings @@ -73,7 +73,7 @@ VARS is a plist of capture variable names: :calls, :strings, :returns, (ert-deftest test-ai-vterm--show-or-create-displays-existing-when-process-live () "Normal: buffer exists with live process -> vterm not called." - (let ((name "claude [reuse-test]")) + (let ((name "agent [reuse-test]")) (test-ai-vterm--cleanup name) (unwind-protect (let ((buf (get-buffer-create name))) @@ -89,7 +89,7 @@ VARS is a plist of capture variable names: :calls, :strings, :returns, (ert-deftest test-ai-vterm--show-or-create-recreates-when-process-dead () "Boundary: buffer exists with dead process -> killed and recreated." - (let ((name "claude [dead-test]")) + (let ((name "agent [dead-test]")) (test-ai-vterm--cleanup name) (unwind-protect (let ((stale (get-buffer-create name))) @@ -111,17 +111,17 @@ VARS is a plist of capture variable names: :calls, :strings, :returns, Real `vterm' replaces the selected window's buffer as a side-effect of construction. On a fresh-boot frame (one window showing the dashboard), that side-effect previously left the original window pointing at the new -claude buffer; the dashboard was buried, the alist-routed split then -created a second window also showing claude. The wrapper must restore +agent buffer; the dashboard was buried, the alist-routed split then +created a second window also showing agent. The wrapper must restore the original window state before `display-buffer' fires so dashboard -stays put and the alist places claude into a fresh right-side split. +stays put and the alist places agent into a fresh right-side split. This test stubs `vterm' to mimic the pop-to-buffer-same-window side-effect and asserts the originally-selected window still shows its original buffer after `cj/--ai-vterm-show-or-create' returns." - (let ((claude-name "claude [preserve-window-test]") + (let ((agent-name "agent [preserve-window-test]") (orig-name "*test-original-buffer*")) - (test-ai-vterm--cleanup claude-name) + (test-ai-vterm--cleanup agent-name) (when (get-buffer orig-name) (kill-buffer orig-name)) (unwind-protect (save-window-excursion @@ -139,14 +139,14 @@ after `cj/--ai-vterm-show-or-create' returns." (lambda (_s &optional _) nil)) ((symbol-function 'vterm-send-return) (lambda () nil))) - (cj/--ai-vterm-show-or-create "/tmp/preserve" claude-name) + (cj/--ai-vterm-show-or-create "/tmp/preserve" agent-name) (should (eq (window-buffer orig-win) orig-buf))))) - (test-ai-vterm--cleanup claude-name) + (test-ai-vterm--cleanup agent-name) (when (get-buffer orig-name) (kill-buffer orig-name))))) (ert-deftest test-ai-vterm--show-or-create-returns-buffer () "Normal: return value is the vterm buffer." - (let ((name "claude [return-test]")) + (let ((name "agent [return-test]")) (test-ai-vterm--cleanup name) (unwind-protect (test-ai-vterm--with-mock-vterm (:calls _c :strings _s diff --git a/tests/test-ai-vterm--sort-candidates.el b/tests/test-ai-vterm--sort-candidates.el index 0b602083..5e3d760a 100644 --- a/tests/test-ai-vterm--sort-candidates.el +++ b/tests/test-ai-vterm--sort-candidates.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; The project picker lists candidates with a live tmux session first -;; (so a Claude that survived an Emacs crash is easy to get back to), +;; (so an agent that survived an Emacs crash is easy to get back to), ;; then everything else. Within each group the order is alphabetical ;; by abbreviated path. diff --git a/tests/test-ai-vterm--tmux-session-name.el b/tests/test-ai-vterm--tmux-session-name.el index 44c20a8b..8d9220eb 100644 --- a/tests/test-ai-vterm--tmux-session-name.el +++ b/tests/test-ai-vterm--tmux-session-name.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; The tmux session name is `cj/ai-vterm-tmux-session-prefix' followed by -;; the project's basename, so reopening Claude on the same project (e.g. +;; the project's basename, so reopening the agent on the same project (e.g. ;; after an Emacs crash) reattaches to the same tmux session rather than ;; spawning a new one -- and the prefix lets `tmux ls' output be filtered ;; down to AI-vterm's own sessions. Whitespace in the basename becomes diff --git a/tests/test-vterm-tmux-history.el b/tests/test-vterm-tmux-history.el index db82176f..901d96c9 100644 --- a/tests/test-vterm-tmux-history.el +++ b/tests/test-vterm-tmux-history.el @@ -164,13 +164,13 @@ modern keyboards and was redundant." "Normal: an AI-vterm-named buffer still resolves by process TTY. The copy path belongs to `vterm-mode', not to `*vterm*'-named buffers. -A buffer named like `claude [repo]' (ai-vterm.el's naming) is a +A buffer named like `agent [repo]' (ai-vterm.el's naming) is a `vterm-mode' buffer and must inherit tmux history copy. The pane lookup keys off the live process TTY, never the buffer name -- so the AI-vterm name neither helps nor blocks resolution." - (let ((claude (cj/test--make-fake-vterm-buffer "claude [emacs.d]"))) + (let ((agent (cj/test--make-fake-vterm-buffer "agent [emacs.d]"))) (unwind-protect - (with-current-buffer claude + (with-current-buffer agent (cl-letf (((symbol-function 'get-buffer-process) (lambda (_buffer) 'fake-process)) ((symbol-function 'process-tty-name) @@ -179,8 +179,8 @@ AI-vterm name neither helps nor blocks resolution." '((("list-clients" "-F" "#{client_tty}\t#{pane_id}") 0 "/dev/pts/1\t%1\n/dev/pts/8\t%8\n")) (should (equal (cj/vterm--current-tmux-pane-id) "%8"))))) - (when (buffer-live-p claude) - (kill-buffer claude))))) + (when (buffer-live-p agent) + (kill-buffer agent))))) (provide 'test-vterm-tmux-history) ;;; test-vterm-tmux-history.el ends here diff --git a/tests/test-vterm-toggle--buffer-filter.el b/tests/test-vterm-toggle--buffer-filter.el index 82afe756..d6fd2c8c 100644 --- a/tests/test-vterm-toggle--buffer-filter.el +++ b/tests/test-vterm-toggle--buffer-filter.el @@ -4,8 +4,8 @@ ;; Three closely-related helpers determine which vterm buffers F12 ;; manages: the predicate `cj/--vterm-toggle-buffer-p', the MRU list ;; `cj/--vterm-toggle-buffers', and the per-frame window finder -;; `cj/--vterm-toggle-displayed-window'. All three exclude claude- -;; prefixed buffers so claude has its own F9 surface. +;; `cj/--vterm-toggle-displayed-window'. All three exclude agent- +;; prefixed buffers so agent has its own F9 surface. ;;; Code: @@ -17,22 +17,22 @@ (require 'testutil-vterm-buffers) (defun test-vterm-toggle--cleanup () - "Kill leftover claude- and *test-vterm- prefixed buffers." - (cj/test--kill-claude-buffers) + "Kill leftover agent- and *test-vterm- prefixed buffers." + (cj/test--kill-agent-buffers) (cj/test--kill-test-vterm-buffers)) (ert-deftest test-vterm-toggle--buffer-p-accepts-vterm-mode () - "Normal: a vterm-mode buffer with non-claude name qualifies." + "Normal: a vterm-mode buffer with non-agent name qualifies." (test-vterm-toggle--cleanup) (let ((buf (cj/test--make-fake-vterm-buffer "*test-vterm-1*"))) (unwind-protect (should (cj/--vterm-toggle-buffer-p buf)) (kill-buffer buf)))) -(ert-deftest test-vterm-toggle--buffer-p-rejects-claude () - "Boundary: claude-prefixed vterm buffers are excluded from F12's set." +(ert-deftest test-vterm-toggle--buffer-p-rejects-agent () + "Boundary: agent-prefixed vterm buffers are excluded from F12's set." (test-vterm-toggle--cleanup) - (let ((buf (cj/test--make-fake-vterm-buffer "claude [project-a]"))) + (let ((buf (cj/test--make-fake-vterm-buffer "agent [project-a]"))) (unwind-protect (should-not (cj/--vterm-toggle-buffer-p buf)) (kill-buffer buf)))) @@ -52,17 +52,17 @@ (kill-buffer buf) (should-not (cj/--vterm-toggle-buffer-p buf)))) -(ert-deftest test-vterm-toggle--buffers-filters-claude () - "Normal: returns vterm buffers but excludes claude-prefixed ones." +(ert-deftest test-vterm-toggle--buffers-filters-agent () + "Normal: returns vterm buffers but excludes agent-prefixed ones." (test-vterm-toggle--cleanup) (let ((normal (cj/test--make-fake-vterm-buffer "*test-vterm-normal*")) - (claude (cj/test--make-fake-vterm-buffer "claude [for-test]"))) + (agent (cj/test--make-fake-vterm-buffer "agent [for-test]"))) (unwind-protect (let ((result (cj/--vterm-toggle-buffers))) (should (memq normal result)) - (should-not (memq claude result))) + (should-not (memq agent result))) (kill-buffer normal) - (kill-buffer claude)))) + (kill-buffer agent)))) (ert-deftest test-vterm-toggle--displayed-window-finds-vterm () "Normal: vterm in a window -> returns that window." @@ -78,17 +78,17 @@ (should (eq (window-buffer result) vt))))) (kill-buffer vt)))) -(ert-deftest test-vterm-toggle--displayed-window-skips-claude () - "Boundary: only a claude vterm is displayed -> nil (claude not F12-managed)." +(ert-deftest test-vterm-toggle--displayed-window-skips-agent () + "Boundary: only an agent vterm is displayed -> nil (agent not F12-managed)." (test-vterm-toggle--cleanup) - (let ((claude (cj/test--make-fake-vterm-buffer "claude [skip-test]"))) + (let ((agent (cj/test--make-fake-vterm-buffer "agent [skip-test]"))) (unwind-protect (save-window-excursion (delete-other-windows) (let ((win (split-window-right))) - (set-window-buffer win claude) + (set-window-buffer win agent) (should-not (cj/--vterm-toggle-displayed-window)))) - (kill-buffer claude)))) + (kill-buffer agent)))) (provide 'test-vterm-toggle--buffer-filter) ;;; test-vterm-toggle--buffer-filter.el ends here diff --git a/tests/testutil-vterm-buffers.el b/tests/testutil-vterm-buffers.el index 864dd8f3..01a65d90 100644 --- a/tests/testutil-vterm-buffers.el +++ b/tests/testutil-vterm-buffers.el @@ -1,4 +1,4 @@ -;;; testutil-vterm-buffers.el --- Shared helpers for vterm/claude buffer tests -*- lexical-binding: t; -*- +;;; testutil-vterm-buffers.el --- Shared helpers for vterm/agent buffer tests -*- lexical-binding: t; -*- ;;; Commentary: ;; Cleanup helpers and a fake-vterm constructor used across the @@ -15,9 +15,9 @@ (when (string-prefix-p prefix (buffer-name b)) (kill-buffer b)))) -(defun cj/test--kill-claude-buffers () - "Kill all live buffers whose name matches the AI-vterm prefix \"claude [\"." - (cj/test--kill-buffers-matching-prefix "claude [")) +(defun cj/test--kill-agent-buffers () + "Kill all live buffers whose name matches the AI-vterm prefix \"agent [\"." + (cj/test--kill-buffers-matching-prefix "agent [")) (defun cj/test--kill-test-vterm-buffers () "Kill all live buffers whose name starts with \"*test-vterm\"." -- cgit v1.2.3