-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathai-code-claude-code.el
More file actions
155 lines (138 loc) · 6.14 KB
/
ai-code-claude-code.el
File metadata and controls
155 lines (138 loc) · 6.14 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
;;; ai-code-claude-code.el --- Thin wrapper for Claude Code CLI -*- lexical-binding: t; -*-
;; Author: Kang Tu, Yoav Orot
;; SPDX-License-Identifier: Apache-2.0
;;; Commentary:
;;
;; Thin wrapper that reuses `ai-code-backends-infra' to run Claude Code CLI.
;; Provides interactive commands and aliases for the AI Code suite.
;; This is an alternative to the external claude-code.el package, using the
;; same terminal infrastructure as other backends (codex, gemini, grok, etc.).
;;
;;; Code:
(require 'ai-code-backends)
(require 'ai-code-backends-infra)
(require 'ai-code-mcp-agent)
(defgroup ai-code-claude-code nil
"Claude Code CLI integration via `ai-code-backends-infra'."
:group 'tools
:prefix "ai-code-claude-code-")
(defcustom ai-code-claude-code-program "claude"
"Path to the Claude Code CLI executable."
:type 'string
:group 'ai-code-claude-code)
(defcustom ai-code-claude-code-program-switches nil
"Command line switches to pass to Claude Code CLI on startup."
:type '(repeat string)
:group 'ai-code-claude-code)
(defcustom ai-code-claude-code-no-flicker nil
"Enable experimental flicker-free terminal renderer in Claude Code.
When non-nil, set CLAUDE_CODE_NO_FLICKER=1 which uses full-screen
redraw rendering. This can break vterm scrollback because the
screen-clearing sequences overwrite the scrollback buffer. Leave
nil unless you specifically need flicker-free rendering and do not
rely on scrolling back through terminal history."
:type 'boolean
:group 'ai-code-claude-code)
(defcustom ai-code-claude-code-multiline-input-sequence "\e\r"
"Terminal sequence used for multiline input in Claude Code sessions.
This mirrors the newline sequence Claude Code expects from `/terminal-setup'."
:type 'string
:group 'ai-code-claude-code)
(defconst ai-code-claude-code--session-prefix "claude"
"Session prefix used in Claude Code CLI buffer names.")
(defvar ai-code-claude-code--processes (make-hash-table :test 'equal)
"Hash table mapping Claude Code session keys to processes.")
;;;###autoload
(defun ai-code-claude-code (&optional arg)
"Start Claude Code (uses `ai-code-backends-infra' logic).
With prefix ARG, prompt for CLI args using
`ai-code-claude-code-program-switches' as the default input."
(interactive "P")
(let* ((working-dir (ai-code-backends-infra--session-working-directory))
(resolved (ai-code-backends-infra--resolve-start-command
ai-code-claude-code-program
ai-code-claude-code-program-switches
arg
"Claude Code"))
(command (plist-get resolved :command))
(mcp-launch (ai-code-mcp-agent-prepare-launch 'claude-code working-dir command))
(launch-command (or (plist-get mcp-launch :command) command))
(cleanup-fn (plist-get mcp-launch :cleanup-fn))
(mcp-post-start-fn (plist-get mcp-launch :post-start-fn))
;; Wrap post-start-fn to conditionally disable strip-alternate-screen.
;; strip-alternate-screen (PR #298) strips \e[?1049h/l so scrollback
;; is preserved, but it causes the Claude Code badge to repeat
;; because the Ink/React TUI redraws the full screen every frame.
;;
;; - vterm: KEEP strip-alternate-screen enabled (with throttling).
;; libvterm's alternate screen has no scrollback ring, so letting
;; \e[?1049h through truncates scrollback to vterm-max-scrollback
;; (~1000 lines).
;; - eat/ghostel: DISABLE strip-alternate-screen. eat saves the
;; entire buffer on alternate-screen entry and restores on exit,
;; so scrollback is never lost.
(post-start-fn
(lambda (buffer process instance-name)
(with-current-buffer buffer
(if (eq ai-code-backends-infra-terminal-backend 'vterm)
;; Explicitly re-enable for vterm to guard against stale
;; buffer-local nil from a previous session or backend switch.
(setq-local ai-code-backends-infra-strip-alternate-screen t)
(setq-local ai-code-backends-infra-strip-alternate-screen nil)))
(when mcp-post-start-fn
(funcall mcp-post-start-fn buffer process instance-name))))
(env-vars (append (list "TERM_PROGRAM=emacs"
"FORCE_CODE_TERMINAL=true")
(when ai-code-claude-code-no-flicker
(list "CLAUDE_CODE_NO_FLICKER=1")))))
(ai-code-backends-infra--toggle-or-create-session
working-dir
nil
ai-code-claude-code--processes
launch-command
#'ai-code-claude-code-send-escape
cleanup-fn
nil
ai-code-claude-code--session-prefix
nil
env-vars
ai-code-claude-code-multiline-input-sequence
post-start-fn)))
;;;###autoload
(defun ai-code-claude-code-switch-to-buffer (&optional force-prompt)
"Switch to the Claude Code CLI buffer.
When FORCE-PROMPT is non-nil, prompt to select a session."
(interactive "P")
(let ((working-dir (ai-code-backends-infra--session-working-directory)))
(ai-code-backends-infra--switch-to-session-buffer
nil
"No Claude Code session for this project"
ai-code-claude-code--session-prefix
working-dir
force-prompt)))
;;;###autoload
(defun ai-code-claude-code-send-command (line)
"Send LINE to Claude Code CLI."
(interactive "sClaude Code> ")
(let ((working-dir (ai-code-backends-infra--session-working-directory)))
(ai-code-backends-infra--send-line-to-session
nil
"No Claude Code session for this project"
line
ai-code-claude-code--session-prefix
working-dir)))
;;;###autoload
(defun ai-code-claude-code-send-escape ()
"Send escape key to Claude Code CLI."
(interactive)
(ai-code-backends-infra--terminal-send-escape))
;;;###autoload
(defun ai-code-claude-code-resume (&optional arg)
"Resume a previous Claude Code CLI session.
With prefix ARG, prompt for additional CLI args."
(interactive "P")
(let ((ai-code-claude-code-program-switches
(append ai-code-claude-code-program-switches '("--resume"))))
(ai-code-claude-code arg)))
(provide 'ai-code-claude-code)
;;; ai-code-claude-code.el ends here