Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions ai-code-backends-infra.el
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

(require 'cl-lib)
(require 'project)
(require 'subr-x)
(require 'ai-code-session-link)
;; Terminal-specific implementations live in dedicated modules so this
;; file can stay focused on shared session orchestration.
Expand Down Expand Up @@ -145,6 +146,22 @@ being sent for the response completion.")
(defvar ai-code-cli-args-history nil
"History list for CLI args prompts.")

(defconst ai-code-backends-infra--uuid-regexp
"[[:xdigit:]]\\{8\\}-[[:xdigit:]]\\{4\\}-[[:xdigit:]]\\{4\\}-[[:xdigit:]]\\{4\\}-[[:xdigit:]]\\{12\\}"
"Regexp matching the general UUID 8-4-4-4-12 text structure.
This validates the textual shape, but not UUID version or variant bits.")

(defun ai-code-backends-infra--selected-session-id ()
"Return the active region text when it contains a UUID session id."
(when (use-region-p)
(let ((candidate
(string-trim
(buffer-substring-no-properties (region-beginning) (region-end)))))
(when (string-match-p
(concat "\\`" ai-code-backends-infra--uuid-regexp "\\'")
candidate)
candidate))))

(defcustom ai-code-backends-infra-idle-delay 5.0
"Delay in seconds of inactivity before considering response complete.
After this period of terminal inactivity, a notification may be sent
Expand Down Expand Up @@ -854,12 +871,27 @@ If FORCE-PROMPT is nil and there are no existing instances, return \"default\"."
(defun ai-code-backends-infra--resolve-start-command (program switches arg &optional prompt-label)
"Build command string for PROGRAM and SWITCHES.
When ARG is non-nil, prompt for CLI args using SWITCHES as default input.
PROMPT-LABEL is used in the minibuffer prompt."
(let* ((default-args (mapconcat #'identity switches " "))
PROMPT-LABEL is used in the minibuffer prompt.
When resuming and the active region contains a UUID, prompt as though ARG
were non-nil and append that UUID to the default CLI args."
(let* ((found-resume-switch
(cl-some (lambda (switch)
(member switch '("resume" "--resume")))
switches))
(selected-session-id
(and (null arg)
found-resume-switch
(ai-code-backends-infra--selected-session-id)))
(prompt-p (or arg selected-session-id))
(default-args (mapconcat #'identity
(append switches
(and selected-session-id
(list selected-session-id)))
" "))
(prompt (format "%s args: " (or prompt-label "CLI")))
(prompt-args (when arg
(read-string prompt default-args 'ai-code-cli-args-history)))
(resolved-args (if arg
(prompt-args (when prompt-p
(read-string prompt default-args 'ai-code-cli-args-history)))
(resolved-args (if prompt-p
(split-string-shell-command prompt-args)
switches))
(command (mapconcat #'identity
Expand Down
75 changes: 75 additions & 0 deletions test/test_ai-code-backends-infra.el
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
(defvar ghostel--copy-mode-active)
(defvar ghostel--process)

(defconst test-ai-code-backends-infra-valid-uuid
"123e4567-e89b-12d3-a456-426614174000"
"UUID fixture used by resume command resolution tests.")

(ert-deftest test-ai-code-backends-infra-output-meaningful-p-noise ()
"Ensure terminal noise is not considered meaningful output."
(should-not (ai-code-backends-infra--output-meaningful-p nil))
Expand Down Expand Up @@ -63,6 +67,77 @@
(should (re-search-forward
"^(defcustom ai-code-backends-infra-eat-preserve-position\\_>" nil t))))

(ert-deftest test-ai-code-backends-infra--resume-double-dash-prefills-uuid ()
"A selected UUID should make `--resume' prompt with that id appended."
(let ((uuid test-ai-code-backends-infra-valid-uuid)
seen-prompt
seen-initial
seen-history
result)
(with-temp-buffer
(transient-mark-mode 1)
(insert uuid)
(goto-char (point-min))
(set-mark (point))
(goto-char (point-max))
(activate-mark)
(cl-letf (((symbol-function 'read-string)
(lambda (prompt &optional initial-input history &rest _args)
(setq seen-prompt prompt
seen-initial initial-input
seen-history history)
initial-input)))
(setq result
(ai-code-backends-infra--resolve-start-command
"claude" '("--resume") nil "Claude"))))
(should (equal seen-prompt "Claude args: "))
(should (equal seen-initial (format "--resume %s" uuid)))
(should (eq seen-history 'ai-code-cli-args-history))
(should (equal (plist-get result :args) `("--resume" ,uuid)))
(should (equal (plist-get result :command)
(format "claude --resume %s" uuid)))))

(ert-deftest test-ai-code-backends-infra--resume-subcommand-prefills-uuid ()
"A selected UUID should make `resume' prompt with that id appended."
(let ((uuid test-ai-code-backends-infra-valid-uuid)
seen-initial
result)
(with-temp-buffer
(transient-mark-mode 1)
(insert uuid)
(goto-char (point-min))
(set-mark (point))
(goto-char (point-max))
(activate-mark)
(cl-letf (((symbol-function 'read-string)
(lambda (_prompt &optional initial-input _history &rest _args)
(setq seen-initial initial-input)
initial-input)))
(setq result
(ai-code-backends-infra--resolve-start-command
"codex" '("resume") nil "Codex"))))
(should (equal seen-initial (format "resume %s" uuid)))
(should (equal (plist-get result :args) `("resume" ,uuid)))
(should (equal (plist-get result :command)
(format "codex resume %s" uuid)))))

(ert-deftest test-ai-code-backends-infra--resume-ignores-non-uuid ()
"A non-UUID region should not trigger resume prompting."
(with-temp-buffer
(transient-mark-mode 1)
(insert "not-a-session-id")
(goto-char (point-min))
(set-mark (point))
(goto-char (point-max))
(activate-mark)
(cl-letf (((symbol-function 'read-string)
(lambda (&rest _args)
(ert-fail "non-UUID selection should not prompt"))))
(let ((result (ai-code-backends-infra--resolve-start-command
"claude" '("--resume") nil "Claude")))
(should (equal (plist-get result :args) '("--resume")))
(should (equal (plist-get result :command) "claude --resume"))))))

(ert-deftest test-ai-code-backends-infra-buffer-user-visible-p ()
"Return non-nil only when buffer has a visible window."
(with-temp-buffer
Expand Down