 
         (cons (lambda ()
                 (looking-at "^\\(bump\\|revert\\|release\\|merge\\|module\\)!?([^)]+):"))
-              "This commit type's scope goes after the colon, not before")
+              "This type's scope goes after the colon, not before")
 
         (cons (lambda ()
                 (when (looking-at "[^ :!(]+!?(\\([^)]+\\)): ")
                   (not
-                   (string-match
-                    (string-join
-                     (cons (concat
-                            "^" (regexp-opt
-                                 (cl-loop for path
-                                          in (cdr (doom-module-load-path (list doom-modules-dir)))
-                                          for (_category . module)
-                                          = (doom-module-from-path path)
-                                          collect (symbol-name module))) "$")
-                           '("^&" "^cli$"))
-                     "\\|")
-                    (match-string 1)))))
+                   (cl-loop with scopes =
+                            (cl-loop for path
+                                     in (cdr (doom-module-load-path (list doom-modules-dir)))
+                                     for (_category . module)
+                                     = (doom-module-from-path path)
+                                     collect (symbol-name module))
+                            with scopes-re =
+                            (string-join (cons (concat "^" (regexp-opt scopes) "$")
+                                               '("^&" "^cli$"))
+                                         "\\|")
+                            for scope in (split-string (match-string 1) ",")
+                            if (string-match scopes-re scope)
+                            return t))))
               "Invalid scope")
 
         (cons (lambda ()

;;; Git hooks

(defvar doom-cli-commit-rules
  (list (cons "^[^\n]\\{10,\\}\n"
              "Commit summary is too short (<10) and should be more descriptive")

        (cons "^\\(revert!?:[^\n]\\{,72\\}\\|[^\n]\\{,80\\}\\)\n"
              "Commit summary too long; <=72 is ideal, 80 is max")

        (cons (concat
               "^\\("

;;;

(defun doom-cli--ci-lint-commits (from &optional to)
  (let ((errors? 0)
        commits
        case-fold-search)
    (with-temp-buffer
      (insert
       (cdr (doom-call-process
             "git" "log"
             (format "%s...%s" from (or to "HEAD")))))
      (while (re-search-backward "^commit \\([a-z0-9]\\{40\\}\\)" nil t)
        (push (cons (match-string 1)
                    (replace-regexp-in-string
                     "^    " ""
                     (save-excursion
                       (buffer-substring-no-properties
                        (search-forward "\n\n")
                        (if (re-search-forward "\ncommit \\([a-z0-9]\\{40\\}\\)" nil t)
                            (match-beginning 0)
                          (point-max))))))
              commits)))
    (dolist (commit commits)
      (let (errors)
        (with-temp-buffer
          (save-excursion (insert (cdr commit)))
          (dolist (rule doom-cli-commit-rules)
            (save-excursion
              (save-match-data
                (cl-destructuring-bind (pred . msg) rule
                  (and (cond ((functionp pred)
                              (funcall pred))
                             ((stringp pred)
                              (not (re-search-forward pred nil t)))
                             ((error "Invalid predicate: %S" pred)))
                       (push msg errors))))))
          (if (not errors)
              (print! (success "Commit %s") (car commit))
            (cl-incf errors?)
            (print! (error "Commit %s") (car commit))
            (print-group!
             (print! "%S" (cdr commit))
             (dolist (e (reverse errors))
               (print! (error "%s" e))))))))
    (when (> errors? 0)
      (terpri)
      (print! "%d commit(s) failed the linter" errors?)
      (terpri)
      (print! "See https://doomemacs.org/project.org#commit-message-formatting for details")
      (throw 'exit 1)))
  t)
