2727(defvar ai-code--cli-resume-fn #'ai-code--unsupported-resume )
2828(defvar ai-code--cli-switch-fn #'ai-code--unsupported-switch-to-buffer )
2929(defvar ai-code--cli-send-fn #'ai-code--unsupported-send-command )
30+ (defvar ai-code-selected-backend 'claude-code
31+ " Currently selected backend key from `ai-code-backends' ." )
3032
3133(defvar ai-code--repo-backend-alist nil
3234 " Alist of (GIT-ROOT . BACKEND) to keep backend affinity per repository." )
@@ -427,6 +429,18 @@ configuration paths, upgrade commands, and skill-install commands."
427429 (expand-file-name " ai-code-backends-history.el" user-emacs-directory)
428430 " File path to store the MRU history of selected backends." )
429431
432+ (defun ai-code--delete-backends-history-file ()
433+ " Delete `ai-code-backends-history-file' when it exists."
434+ (when (file-exists-p ai-code-backends-history-file)
435+ (ignore-errors (delete-file ai-code-backends-history-file))))
436+
437+ (defun ai-code--valid-backends-history-p (history )
438+ " Return non-nil when HISTORY is a list of backend symbols."
439+ (and (listp history)
440+ (seq-every-p (lambda (item )
441+ (and item (symbolp item)))
442+ history)))
443+
430444(defun ai-code--load-backends-history ()
431445 " Load the MRU backend history from `ai-code-backends-history-file' ."
432446 (if (file-exists-p ai-code-backends-history-file)
@@ -435,9 +449,13 @@ configuration paths, upgrade commands, and skill-install commands."
435449 (insert-file-contents ai-code-backends-history-file)
436450 (let ((content (buffer-string )))
437451 (unless (string-empty-p content)
438- (read content))))
452+ (let ((history (read content)))
453+ (if (ai-code--valid-backends-history-p history)
454+ history
455+ (ai-code--delete-backends-history-file)
456+ nil )))))
439457 (error
440- (ignore-errors ( delete-file ai-code-backends-history-file) )
458+ (ai-code--delete- backends-history-file)
441459 nil ))
442460 nil ))
443461
@@ -451,9 +469,6 @@ configuration paths, upgrade commands, and skill-install commands."
451469 (prin1-to-string updated-history))))
452470 (error nil ))))
453471
454- (defvar ai-code-selected-backend 'claude-code
455- " Currently selected backend key from `ai-code-backends' ." )
456-
457472(defun ai-code-set-backend (new-backend )
458473 " Set the AI backend to NEW-BACKEND."
459474 (unless (ai-code--backend-spec new-backend)
@@ -467,6 +482,35 @@ configuration paths, upgrade commands, and skill-install commands."
467482 " Return backend plist for KEY from `ai-code-backends' ."
468483 (seq-find (lambda (it ) (eq (car it) key)) ai-code-backends))
469484
485+ (defun ai-code--ordered-backend-choices ()
486+ " Return backend choices with the effective backend first, then MRU entries."
487+ (let* ((choices (mapcar (lambda (it )
488+ (let* ((key (car it))
489+ (label (plist-get (cdr it) :label )))
490+ (cons (format " %s " label) key)))
491+ ai-code-backends))
492+ (effective-backend (ai-code--effective-backend))
493+ (history (ai-code--load-backends-history))
494+ (history-choices (seq-filter #'identity
495+ (mapcar (lambda (key )
496+ (seq-find (lambda (candidate )
497+ (eq (cdr candidate) key))
498+ choices))
499+ history)))
500+ (other-choices (seq-remove (lambda (candidate )
501+ (member candidate history-choices))
502+ choices))
503+ (sorted-choices (append history-choices other-choices))
504+ (current-choice (seq-find (lambda (candidate )
505+ (eq (cdr candidate) effective-backend))
506+ sorted-choices)))
507+ (if current-choice
508+ (cons current-choice
509+ (seq-remove (lambda (candidate )
510+ (eq (cdr candidate) effective-backend))
511+ sorted-choices))
512+ sorted-choices)))
513+
470514(defun ai-code-current-backend-label ()
471515 " Return label string of the currently selected backend.
472516Falls back to symbol name when label is unavailable."
@@ -542,25 +586,11 @@ invoke `ai-code-cli-resume'; otherwise call `ai-code-cli-start'."
542586(defun ai-code-select-backend ()
543587 " Interactively select and apply an AI backend from `ai-code-backends' ."
544588 (interactive )
545- (let* ((choices (mapcar (lambda (it )
546- (let* ((key (car it))
547- (label (plist-get (cdr it) :label )))
548- (cons (format " %s " label) key)))
549- ai-code-backends))
550- (effective-backend (ai-code--effective-backend))
551- (history (ai-code--load-backends-history))
552- (history-choices (seq-filter #'identity
553- (mapcar (lambda (key )
554- (seq-find (lambda (c ) (eq (cdr c) key)) choices))
555- history)))
556- (other-choices (seq-remove (lambda (c ) (member c history-choices)) choices))
557- (sorted-choices (append history-choices other-choices))
558- (current-choice (seq-find (lambda (it ) (eq (cdr it) effective-backend)) sorted-choices))
559- (ordered-choices (if current-choice
560- (cons current-choice
561- (seq-remove (lambda (it ) (eq (cdr it) effective-backend))
562- sorted-choices))
563- sorted-choices))
589+ (let* ((ordered-choices (ai-code--ordered-backend-choices))
590+ (current-choice (car ordered-choices))
591+ (completion-extra-properties
592+ '(:display-sort-function identity
593+ :cycle-sort-function identity))
564594 (choice (completing-read " Select backend: "
565595 (mapcar #'car ordered-choices)
566596 nil t nil nil (car current-choice)))
0 commit comments