-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpi-coding-agent.el
More file actions
189 lines (175 loc) · 8.3 KB
/
pi-coding-agent.el
File metadata and controls
189 lines (175 loc) · 8.3 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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
;;; pi-coding-agent.el --- Emacs frontend for pi coding agent -*- lexical-binding: t; -*-
;; Copyright (C) 2026 Daniel Nouri
;; Author: Daniel Nouri <daniel.nouri@gmail.com>
;; Maintainer: Daniel Nouri <daniel.nouri@gmail.com>
;; URL: https://github.com/dnouri/pi-coding-agent
;; Keywords: ai llm ai-pair-programming tools
;; Version: 2.1.0
;; Package-Requires: ((emacs "29.1") (transient "0.9.0") (md-ts-mode "0.3.0") (markdown-table-wrap "0.2.0"))
;; SPDX-License-Identifier: GPL-3.0-or-later
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Emacs frontend for the pi coding agent (https://pi.dev).
;; Provides a two-window interface for AI-assisted coding: chat history
;; with rendered markdown, and a separate prompt composition buffer.
;;
;; Requirements:
;; - Emacs 29.1 or later (tree-sitter support required)
;; - pi coding agent 0.52.9 or later, installed and in PATH
;; - tree-sitter grammars for markdown and markdown-inline
;;
;; pi-coding-agent uses `md-ts-mode` for its chat buffers only; loading it
;; does not change global Markdown file associations.
;;
;; Usage:
;; M-x pi-coding-agent Start or focus session in current project
;; C-u M-x pi-coding-agent Start a named session
;; M-x pi-coding-agent-toggle Hide/show session windows in current frame
;;
;; Many users define an alias: (defalias 'pi 'pi-coding-agent)
;;
;; Key Bindings:
;; Input buffer:
;; C-c C-c Send prompt (queues as follow-up if busy)
;; C-c C-s Queue steering (interrupts after current tool; busy only)
;; C-c C-k Abort streaming
;; C-c C-p Open menu
;; C-c C-r Resume session
;; M-p / M-n History navigation
;; C-r Incremental history search (like readline)
;; TAB Path/file completion
;; @ File reference (search project files)
;;
;; Chat buffer:
;; n / p Navigate messages
;; TAB Toggle tool output
;; RET Visit file at point (from tool blocks)
;; C-c C-p Open menu
;;
;; Editor Features:
;; - File reference (@): Type @ to search project files (respects .gitignore)
;; - Path completion (Tab): Complete relative paths, ../, ~/, etc.
;; - Message queuing: Submit messages while agent is working:
;; C-c C-c queues follow-up (delivered after agent completes)
;; C-c C-s queues steering (interrupts after current tool)
;;
;; Press C-c C-p for the full transient menu with model selection,
;; thinking level, session management, and custom commands.
;;
;; See README.org for more documentation.
;;; Code:
(require 'pi-coding-agent-menu)
(require 'pi-coding-agent-input)
;;;; Main Entry Point
(defun pi-coding-agent--setup-session (dir &optional session)
"Set up a new or existing session for DIR with optional SESSION name.
Returns the chat buffer."
(let* ((chat-buf (pi-coding-agent--get-or-create-buffer :chat dir session))
(input-buf (pi-coding-agent--get-or-create-buffer :input dir session))
(new-session nil))
;; Link buffers to each other
(with-current-buffer chat-buf
(setq default-directory dir)
(pi-coding-agent--set-input-buffer input-buf)
;; Start process if not already running
(unless (and pi-coding-agent--process (process-live-p pi-coding-agent--process))
(pi-coding-agent--set-process (pi-coding-agent--start-process dir))
(setq new-session t)
;; Associate process with chat buffer for built-in kill confirmation
(when (processp pi-coding-agent--process)
(set-process-buffer pi-coding-agent--process chat-buf)
(process-put pi-coding-agent--process 'pi-coding-agent-chat-buffer chat-buf)
;; Register event handler
(pi-coding-agent--register-display-handler pi-coding-agent--process)
;; Initialize state from server
(let ((buf chat-buf)
(proc pi-coding-agent--process)) ; Capture for closures
(pi-coding-agent--rpc-async proc '(:type "get_state")
(lambda (response)
(pi-coding-agent--apply-state-response buf response)
;; Check if no model available and warn user
(when (and (plist-get response :success)
(buffer-live-p buf))
(with-current-buffer buf
(unless (plist-get pi-coding-agent--state :model)
(pi-coding-agent--display-no-model-warning))))))
;; Fetch commands via RPC (independent of get_state)
(pi-coding-agent--fetch-commands proc
(lambda (commands)
(when (buffer-live-p buf)
(with-current-buffer buf
(pi-coding-agent--set-commands commands)
(pi-coding-agent--rebuild-commands-menu))))))))
;; Display startup header for new sessions
(when new-session
(pi-coding-agent--display-startup-header)))
(with-current-buffer input-buf
(setq default-directory dir)
(pi-coding-agent--set-chat-buffer chat-buf))
chat-buf))
;;;###autoload
(defun pi-coding-agent (&optional session)
"Start or switch to pi coding agent session in current project.
With prefix arg, prompt for SESSION name to allow multiple sessions.
If already in a pi buffer and no SESSION specified, ensures this session
is visible. When both chat and input are already shown in the current
frame, keeps layout unchanged and focuses the input window."
(interactive
(list (when current-prefix-arg
(read-string "Session name: "))))
(pi-coding-agent--check-dependencies)
(let (chat-buf input-buf)
(if (and (derived-mode-p 'pi-coding-agent-chat-mode 'pi-coding-agent-input-mode)
(not session))
;; Already in pi buffer with no new session requested - use current session
(setq chat-buf (pi-coding-agent--get-chat-buffer)
input-buf (pi-coding-agent--get-input-buffer))
;; Find or create session for current directory
(let ((dir (pi-coding-agent--session-directory)))
(setq chat-buf (pi-coding-agent--setup-session dir session))
(setq input-buf (buffer-local-value 'pi-coding-agent--input-buffer chat-buf))))
;; When both windows are already visible in current frame, just focus
;; the session input window. Otherwise restore/show the session layout.
(if (and (get-buffer-window-list chat-buf nil)
(get-buffer-window-list input-buf nil))
(pi-coding-agent--focus-input-window chat-buf input-buf)
(pi-coding-agent--display-buffers chat-buf input-buf))))
;;;###autoload
(defun pi-coding-agent-toggle ()
"Toggle pi coding agent window visibility for the current project.
If pi windows are visible in the current frame, hide them.
If hidden there but a session exists, show them.
If no session exists, signal an error."
(interactive)
(pi-coding-agent--check-dependencies)
(let* ((chat-buf (if (derived-mode-p 'pi-coding-agent-chat-mode 'pi-coding-agent-input-mode)
(pi-coding-agent--get-chat-buffer)
(car (pi-coding-agent-project-buffers))))
(input-buf (and chat-buf
(buffer-local-value 'pi-coding-agent--input-buffer chat-buf))))
(cond
;; No session at all
((null chat-buf)
(user-error "No pi session for this project"))
;; Session visible in current frame: hide it
((or (get-buffer-window-list chat-buf nil)
(and input-buf (get-buffer-window-list input-buf nil)))
(with-current-buffer chat-buf
(pi-coding-agent--hide-session-windows)))
;; Session hidden: show it
(t
(pi-coding-agent--display-buffers chat-buf input-buf)))))
(provide 'pi-coding-agent)
;;; pi-coding-agent.el ends here