blob: 6d512831bcb400e062e5bd1212a2f3e1df4fa3b9 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
;;; ai-term-backend-eat.el --- EAT terminal backend for ai-term -*- lexical-binding: t; -*-
;; Author: Craig Jennings <c@cjennings.net>
;;; Commentary:
;;
;; Layer: 3 (Domain Workflow).
;; Category: D.
;; Load shape: library.
;; Top-level side effects: binds M-SPC in `eat-semi-char-mode-map' after EAT
;; loads.
;; Runtime requires: ai-term-sessions.
;; Direct test load: yes.
;;
;; EAT backend of ai-term: terminal buffer creation and tmux reattach, the
;; pty send helper, and the EAT keymap integration. The name is
;; backend-specific on purpose -- a future terminal backend lands as a
;; sibling ai-term-backend-<name>.el and everything above this layer stays
;; put. The display routing the created buffer goes through lives in
;; ai-term-display (the display-buffer-alist rule); this module only calls
;; `display-buffer'.
;;; Code:
(require 'ai-term-sessions)
(declare-function eat "eat" (&optional program arg))
(declare-function cj/ai-term-next "ai-term" ())
(defvar eat-buffer-name)
(defvar eat-semi-char-mode-map)
(defun cj/--ai-term-send-string (buffer string)
"Send STRING to BUFFER's terminal process (the agent's shell).
Sends to the pty directly so the launch command reaches the shell EAT runs."
(let ((proc (get-buffer-process buffer)))
(when (process-live-p proc)
(process-send-string proc string))))
(defun cj/--ai-term-show-or-create (dir name)
"Show or create the AI-term buffer for project DIR with buffer NAME.
If a buffer named NAME exists with a live process, display it. If
the buffer exists but its process is dead, kill it and recreate. If
no such buffer exists, create a new EAT terminal in DIR and send
the project's tmux launch command (see `cj/--ai-term-launch-command') so
the same project basename reattaches across Emacs restarts.
EAT runs a plain shell with no auto-tmux hook, so the named
`tmux new-session -A' launch command is the only thing that starts the
session -- the spike confirmed EAT + tmux detach and reattach exactly
like ghostel + tmux did.
Records DIR in `cj/--ai-term-mru' (whichever branch runs) so the
project picker can list recently-opened projects first. Returns the
buffer."
(cj/--ai-term-record-mru dir)
(let ((existing (get-buffer name)))
(cond
((and existing (cj/--ai-term-process-live-p existing))
(display-buffer existing)
existing)
(t
(when existing
(kill-buffer existing))
;; `eat' switches to its buffer in the selected window before our
;; display-buffer-alist rule can route it; `save-window-excursion'
;; reverts that, and the explicit display-buffer below routes the buffer
;; through the alist into the agent slot. `eat-buffer-name' is bound to
;; NAME so the terminal is created under the agent name; EAT (unlike
;; ghostel) does not rename the buffer from the terminal's OSC title, so
;; the "agent [" prefix that buffer detection and the display rule key on
;; stays put.
(save-window-excursion
(let ((default-directory dir)
(eat-buffer-name name))
(eat)))
(let ((buf (get-buffer name)))
(with-current-buffer buf
(cj/--ai-term-send-string
buf (concat (cj/--ai-term-launch-command dir) "\n")))
(display-buffer buf)
buf)))))
;; In EAT's semi-char mode, keys not bound in `eat-semi-char-mode-map' are
;; forwarded to the pty. M-SPC (swap to the next agent) must reach Emacs from
;; inside an agent buffer, so bind it in that map -- no exception-list or rebuild
;; dance like ghostel needed. C-; is already bound there (eat-config), so the
;; C-; a family resolves through the global prefix without extra wiring.
(with-eval-after-load 'eat
(keymap-set eat-semi-char-mode-map "M-SPC" #'cj/ai-term-next))
(provide 'ai-term-backend-eat)
;;; ai-term-backend-eat.el ends here
|