Skip to content

Commit ebf239d

Browse files
bobrowadamdnouri
andauthored
Add slash command runner API (#212)
* Add command runner API * Require a pi session for command running pi-coding-agent-run-command is a public boundary, so a missing chat buffer should be a caller-visible error rather than a quiet no-op. Keep the helper deliberately scoped to the current pi buffer session, document that scope, and cover the exact command text it sends. --------- Co-authored-by: Daniel Nouri <daniel.nouri@gmail.com>
1 parent 2ce94c0 commit ebf239d

2 files changed

Lines changed: 99 additions & 8 deletions

File tree

pi-coding-agent-menu.el

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,18 +1058,44 @@ MESSAGES is a vector of plists from get_fork_messages."
10581058

10591059
;;;; Custom Commands
10601060

1061+
(defun pi-coding-agent--command-chat-buffer-or-error ()
1062+
"Return the current pi chat buffer for running a slash command.
1063+
Signal `user-error' when no live chat buffer is linked to the current buffer."
1064+
(let ((chat-buf (pi-coding-agent--get-chat-buffer)))
1065+
(unless (and chat-buf (buffer-live-p chat-buf))
1066+
(user-error "No pi session in current buffer"))
1067+
chat-buf))
1068+
1069+
(defun pi-coding-agent-run-command (name &optional args)
1070+
"Run pi slash command NAME with optional ARGS in the current session.
1071+
NAME is the command name without the leading slash. ARGS, when
1072+
non-nil and non-empty, is appended after one space.
1073+
1074+
This command sends through the pi session associated with the current
1075+
pi chat buffer or its linked input buffer. Signal `user-error' when the
1076+
current buffer is not part of a pi session."
1077+
(interactive
1078+
(progn
1079+
(pi-coding-agent--command-chat-buffer-or-error)
1080+
(list (completing-read "Pi command: "
1081+
(mapcar (lambda (cmd) (plist-get cmd :name))
1082+
pi-coding-agent--commands)
1083+
nil t)
1084+
(read-string "Args: "))))
1085+
(let ((chat-buf (pi-coding-agent--command-chat-buffer-or-error)))
1086+
(let ((full-command (if (or (null args) (string-empty-p args))
1087+
(format "/%s" name)
1088+
(format "/%s %s" name args))))
1089+
(with-current-buffer chat-buf
1090+
(pi-coding-agent--prepare-and-send full-command)))))
1091+
10611092
(defun pi-coding-agent--run-custom-command (cmd)
10621093
"Execute custom command CMD.
10631094
Always prompts for arguments - user can press Enter if none needed.
10641095
Sends the literal /command text to pi, which handles expansion."
1065-
(when-let* ((chat-buf (pi-coding-agent--get-chat-buffer)))
1066-
(let* ((name (plist-get cmd :name))
1067-
(args-string (read-string (format "/%s: " name)))
1068-
(full-command (if (string-empty-p args-string)
1069-
(format "/%s" name)
1070-
(format "/%s %s" name args-string))))
1071-
(with-current-buffer chat-buf
1072-
(pi-coding-agent--prepare-and-send full-command)))))
1096+
(let* ((name (plist-get cmd :name))
1097+
(args-string (read-string (format "/%s: " name))))
1098+
(pi-coding-agent-run-command name args-string)))
10731099

10741100
(defun pi-coding-agent-run-custom-command ()
10751101
"Select and run a custom command.

test/pi-coding-agent-menu-test.el

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,71 @@ BINDING-SPEC is (DIR CHAT-NAME INPUT-NAME PROC). DIR is evaluated once."
810810
(should (member "fix-tests" (nth 2 completion)))
811811
(should (member "review" (nth 2 completion)))))))
812812

813+
(ert-deftest pi-coding-agent-test-run-command-formats-command-text ()
814+
"run-command builds literal slash commands from NAME and optional args."
815+
(let ((sent-messages nil)
816+
(fake-proc (start-process "test" nil "cat")))
817+
(set-process-query-on-exit-flag fake-proc nil)
818+
(unwind-protect
819+
(with-temp-buffer
820+
(pi-coding-agent-chat-mode)
821+
(let ((pi-coding-agent--process fake-proc))
822+
(cl-letf (((symbol-function 'pi-coding-agent--rpc-async)
823+
(lambda (_proc msg _cb)
824+
(push (plist-get msg :message) sent-messages))))
825+
(pi-coding-agent-run-command "greet")
826+
(pi-coding-agent-run-command "greet" "")
827+
(pi-coding-agent-run-command "greet" "world")
828+
(should (equal (nreverse sent-messages)
829+
'("/greet" "/greet" "/greet world"))))))
830+
(delete-process fake-proc))))
831+
832+
(ert-deftest pi-coding-agent-test-run-command-uses-linked-input-session ()
833+
"run-command sends through the chat buffer linked to current input."
834+
(let ((sent-message nil)
835+
(fake-proc (start-process "test" nil "cat"))
836+
(chat-buf (generate-new-buffer " *pi-command-chat*"))
837+
(input-buf (generate-new-buffer " *pi-command-input*")))
838+
(set-process-query-on-exit-flag fake-proc nil)
839+
(unwind-protect
840+
(progn
841+
(with-current-buffer chat-buf
842+
(pi-coding-agent-chat-mode)
843+
(setq pi-coding-agent--process fake-proc)
844+
(pi-coding-agent--set-input-buffer input-buf))
845+
(with-current-buffer input-buf
846+
(pi-coding-agent-input-mode)
847+
(pi-coding-agent--set-chat-buffer chat-buf)
848+
(cl-letf (((symbol-function 'pi-coding-agent--rpc-async)
849+
(lambda (_proc msg _cb)
850+
(setq sent-message (plist-get msg :message)))))
851+
(pi-coding-agent-run-command "greet" "world")))
852+
(should (equal sent-message "/greet world")))
853+
(pi-coding-agent-test--kill-live-buffers input-buf chat-buf)
854+
(delete-process fake-proc))))
855+
856+
(ert-deftest pi-coding-agent-test-run-command-requires-current-session ()
857+
"run-command reports a missing current pi session."
858+
(with-temp-buffer
859+
(should-error (pi-coding-agent-run-command "greet")
860+
:type 'user-error)))
861+
862+
(ert-deftest pi-coding-agent-test-run-command-interactive-requires-session-first ()
863+
"run-command reports a missing session before prompting interactively."
864+
(with-temp-buffer
865+
(let (prompted)
866+
(cl-letf (((symbol-function 'completing-read)
867+
(lambda (&rest _args)
868+
(setq prompted t)
869+
"greet"))
870+
((symbol-function 'read-string)
871+
(lambda (&rest _args)
872+
(setq prompted t)
873+
"")))
874+
(should-error (call-interactively #'pi-coding-agent-run-command)
875+
:type 'user-error)
876+
(should-not prompted)))))
877+
813878
(ert-deftest pi-coding-agent-test-run-custom-command-sends-literal ()
814879
"run-custom-command sends literal /command text, not expanded."
815880
(let* ((sent-message nil)

0 commit comments

Comments
 (0)