Add modeline indicators to Prodigy in Emacs
I recently started using prodigy.el at work since I had to run a few services locally for development. I used to run them in my terminal in different tabs, but I thought of giving Prodigy a try and it worked great! I was able to not just run the services without having to look at the logs (unless I needed to) but also configure the status of each service based on certain text patterns in the logs. This way Prodigy would show if the service is Ready/Running/Stopped.
One thing missing in Prodigy is that it doesn’t show any modeline indicators. What I wanted was to be able to quickly find which services were running and which ones were stopped. This was going to be too much information to be displayed on the modeline, so I instead wanted just the number of services running vs stopped, and I wanted them to be color coded (green for running, blue for ready, yellow for stopped, etc.) And on hover, I wanted the respective service names to be displayed in the echo area.
I asked Gemini 3 Pro to generate functions and hooks with these exact requirements and it did a great job.
Screenshots
Here’s the screenshot of mouse hover on services stopped

And here’s the screenshot of mouse hover on services running

Code
Like I mentioned above, the entire code was generated by an LLM (Gemini 3 Pro) and I didn’t have to tweak anything at all.
;; 1. Define a variable to hold the modeline string
(defvar my/prodigy-mode-line-string ""
"String to display in the modeline for Prodigy services.")
(put 'my/prodigy-mode-line-string 'risky-local-variable t)
;; 2. Add to mode-line-misc-info (instead of global-mode-string)
;; We use `assq-delete-all` first to ensure we don't add duplicates if you re-eval.
(setq mode-line-misc-info
(append (assq-delete-all 'my/prodigy-mode-line-string mode-line-misc-info)
'(my/prodigy-mode-line-string)))
;; 3. Define the function that builds the status string
(defun my/prodigy-get-services-string (status)
(let ((names '()))
(dolist (service prodigy-services)
(let ((s-status (or (plist-get service :status) 'stopped)))
(when (if (eq status 'stopped)
(or (eq s-status 'stopped) (null (plist-get service :status)))
(eq s-status status))
(push (plist-get service :name) names))))
(mapconcat #'identity (nreverse names) ", ")))
(defvar my/prodigy-mode-line-map
(let ((map (make-sparse-keymap)))
(define-key map [mode-line mouse-1] #'prodigy)
(define-key map [mode-line mouse-2] #'prodigy)
map))
;; 4. Define the update function (same as before)
(defun my/prodigy-update-mode-line (&rest _)
(let ((failed 0) (stopped 0) (running 0) (ready 0))
(dolist (service prodigy-services)
(let ((status (plist-get service :status)))
(cond
((eq status 'failed) (cl-incf failed))
((eq status 'ready) (cl-incf ready))
((eq status 'running) (cl-incf running))
((or (null status) (eq status 'stopped)) (cl-incf stopped)))))
(let ((items '()))
(cl-flet ((make-item (count color status-label status-sym)
(propertize (number-to-string count)
'face `(:foreground ,color :weight bold)
'help-echo (format "%s: %s" status-label (my/prodigy-get-services-string status-sym))
'mouse-face 'mode-line-highlight
'local-map my/prodigy-mode-line-map)))
(when (> failed 0) (push (make-item failed "red" "Failed" 'failed) items))
(when (> stopped 0) (push (make-item stopped "yellow" "Stopped" 'stopped) items))
(when (> running 0) (push (make-item running "green" "Running" 'running) items))
(when (> ready 0) (push (make-item ready "#44aaff" "Ready" 'ready) items))
(setq my/prodigy-mode-line-string
(if items
(concat " " (mapconcat #'identity (nreverse items) " | ") " ")
""))))
(force-mode-line-update t)))
;; 5. "Hook" into Prodigy's status changes
;; Prodigy doesn't have a standard hook, so we advise the status setter function.
(advice-add 'prodigy-set-status :after #'my/prodigy-update-mode-line)
;; 6. Initial update (in case you eval this while services are already up)
(my/prodigy-update-mode-line)
The entire process took less than 20 minutes. I’m not super familiar with elisp. I’ve done some minor tweaking and I didn’t have the time to learn enough elisp to be able to pull this off.