feat: project-aware Hermes session integration
- SPC p h: new session (prompts for title, captures ID, opens vterm) - SPC p H: resume menu (newest-first, completing-read) - SPC p l: link external session to project (browse or by ID) - hermes: org link type opens in vterm, not shell-mode - One-shot init message: "Init session for PROJECT. Reply ONLY with OK." - Regex fix: [0-9a-f_]+ for hex session IDs - .hermes-sessions dotfile at project root (git-tracked) - gortium/ensure-node-structure creates .hermes-sessions + gitignore whitelist - Refactored: gortium/hermes--cmd, gortium/hermes--project-info
This commit is contained in:
@@ -42,6 +42,24 @@ they are implemented.
|
||||
#+begin_src emacs-lisp
|
||||
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
|
||||
#+end_src
|
||||
** Banner
|
||||
#+begin_src emacs-lisp
|
||||
;; 1. Set the image path
|
||||
(setq fancy-splash-image (expand-file-name "hermes_logo_ascii.png" doom-user-dir))
|
||||
|
||||
;; 2. Define the quote function
|
||||
(defun my-dashboard-quote ()
|
||||
(insert "\n" (+dashboard--center +dashboard--width "Simplicity is the ultimate sophistication.") "\n"))
|
||||
|
||||
;; 3. Override the functions list using the correct variable from the docs
|
||||
(after! doom-dashboard
|
||||
(setq +dashboard-functions
|
||||
'(+dashboard-widget-banner
|
||||
my-dashboard-quote
|
||||
+dashboard-widget-loaded
|
||||
+dashboard-widget-shortmenu
|
||||
+dashboard-widget-footer)))
|
||||
#+end_src
|
||||
|
||||
** User Info
|
||||
|
||||
@@ -110,7 +128,7 @@ No more auto-discovery of random =.git= repos.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(setq bookmark-save-flag 1)
|
||||
(setq bookmark-default-file (expand-file-name "bookmarks" (expand-file-name "~/ExoKortex/2-Areas/Home-Infra/2-Assets/bookmark")))
|
||||
(setq bookmark-default-file (expand-file-name "~/ExoKortex/2-Areas/Home-Infra/2-Assets/bookmark"))
|
||||
#+end_src
|
||||
|
||||
** Buffer auto reload
|
||||
@@ -127,17 +145,7 @@ My current workflow consist in having the 3-5 files I work on open in vertical s
|
||||
#+begin_src emacs-lisp
|
||||
(map! :leader
|
||||
(:prefix ("w" . "window")
|
||||
:desc "Minimize window" "O" #'minimize-window)
|
||||
(:prefix ("z" . "Personal")
|
||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||
#+end_src
|
||||
|
||||
** Hermes remote shortcut
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(map! :leader
|
||||
(:prefix ("z" . "Personal")
|
||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||
:desc "Minimize window" "O" #'minimize-window))
|
||||
#+end_src
|
||||
|
||||
** Ultra-scroll
|
||||
@@ -333,9 +341,11 @@ org-agenda-files (seq-uniq org-agenda-files)
|
||||
(setq org-agenda-exporter-settings '((ps-print-color-p 'nil)))
|
||||
(setq
|
||||
org-agenda-follow-mode nil
|
||||
org-agenda-skip-deadline-if-done nil
|
||||
org-agenda-skip-scheduled-if-done nil
|
||||
org-agenda-skip-timestamp-if-done nil
|
||||
org-agenda-skip-deadline-if-done t
|
||||
org-agenda-skip-scheduled-if-done t
|
||||
org-agenda-skip-timestamp-if-done t
|
||||
org-agenda-skip-deadline-prewarning-if-scheduled t
|
||||
org-agenda-skip-scheduled-if-deadline-is-shown t
|
||||
org-agenda-sticky t
|
||||
org-journal-enable-agenda-integration t
|
||||
org-log-done 'time
|
||||
@@ -726,6 +736,27 @@ org-agenda-files (seq-uniq org-agenda-files)
|
||||
(append '(("\\.x?html?\\'" . default))
|
||||
(assq-delete-all "\\.x?html?\\'" org-file-apps))))
|
||||
#+end_src
|
||||
** Calendar
|
||||
#+begin_src emacs-lisp
|
||||
(after! calendar
|
||||
;; Define a function to open org-roam daily note for selected date in calendar
|
||||
(defun gortium/org-roam-dailies-open-at-point ()
|
||||
"Open org-roam daily note for date at point in calendar."
|
||||
(interactive)
|
||||
(let ((date (calendar-cursor-to-date)))
|
||||
(org-roam-dailies-goto-date date)))
|
||||
)
|
||||
|
||||
;; Bind RET in calendar mode to open daily note instead of default exit
|
||||
(evil-define-key 'normal calendar-mode-map
|
||||
(kbd "RET") #'gortium/org-roam-dailies-open-at-point)
|
||||
|
||||
(after! org
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-h") (kbd "S-<left>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-j") (kbd "S-<down>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-k") (kbd "S-<up>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-l") (kbd "S-<right>")))
|
||||
#+end_src
|
||||
** +Org Superstar mode+
|
||||
org-modern instead
|
||||
|
||||
@@ -1205,19 +1236,6 @@ Handles org-clock and context link capture for tasks."
|
||||
(gortium/connecteam-clock-out gortium/connecteam-clock-id))))
|
||||
|
||||
(add-hook 'org-clock-out-hook #'gortium/org-clock-out-from-connecteam)
|
||||
|
||||
(after! calendar
|
||||
;; Define a function to open org-roam daily note for selected date in calendar
|
||||
(defun gortium/org-roam-dailies-open-at-point ()
|
||||
"Open org-roam daily note for date at point in calendar."
|
||||
(interactive)
|
||||
(let ((date (calendar-cursor-to-date)))
|
||||
(org-roam-dailies-goto-date date)))
|
||||
)
|
||||
|
||||
;; Bind RET in calendar mode to open daily note instead of default exit
|
||||
(evil-define-key 'normal calendar-mode-map
|
||||
(kbd "RET") #'gortium/org-roam-dailies-open-at-point)
|
||||
#+end_src
|
||||
|
||||
*** Old config
|
||||
@@ -1949,23 +1967,260 @@ Org-AI
|
||||
(org-ai-install-yasnippets)) ; if you are using yasnippet and want `ai` snippets
|
||||
#+end_src
|
||||
|
||||
** +Hermes Agent+
|
||||
Hermes Agent runs on lazyworkhorse server. Launched via SSH in a vterm buffer.
|
||||
** +Hermes Agent — Project Session Tracking+
|
||||
|
||||
Hermes chat sessions are automatically linked to ExoKortex projects.
|
||||
All logic runs inside Emacs — no bash aliases, no wrapper scripts.
|
||||
|
||||
| Binding | Function |
|
||||
|-------------+---------------------------------------|
|
||||
| ~SPC p h~ | New session (prompts for title) |
|
||||
| ~C-u SPC p h~ | New remote session (SSH + Docker) |
|
||||
| ~SPC p H~ | Browse/resume past sessions (menu) |
|
||||
| ~hermes:~ | Org link type — click to resume |
|
||||
|
||||
Session ID is captured at session *start* (before vterm opens), so
|
||||
even if the terminal window is closed immediately, the session is
|
||||
already saved to ~.hermes-sessions~ at the project root.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(after! vterm
|
||||
(defun gortium/hermes-vterm ()
|
||||
"Launch Hermes Agent in vterm via SSH to lazyworkhorse (CLI mode)."
|
||||
(interactive)
|
||||
(let ((buf (get-buffer-create "*hermes*")))
|
||||
(switch-to-buffer buf)
|
||||
(unless (and (eq major-mode 'vterm-mode)
|
||||
(get-buffer-process buf))
|
||||
(vterm-mode)
|
||||
(let ((proc (get-buffer-process buf)))
|
||||
(when proc
|
||||
(process-send-string
|
||||
proc "ssh -t lazyworkhorse 'docker exec -it hermes /opt/hermes/.venv/bin/hermes'\n")))))))
|
||||
;;; ====================================================================
|
||||
;;; Hermes Session ↔ Project Integration
|
||||
;;; ====================================================================
|
||||
|
||||
;; Configuration (customize per-host)
|
||||
(defvar gortium/hermes-remote-host "lazyworkhorse"
|
||||
"SSH host for remote Hermes. nil = use local Hermes.")
|
||||
(defvar gortium/hermes-remote-docker "hermes"
|
||||
"Docker container name for remote Hermes.")
|
||||
(defvar gortium/hermes-remote-path
|
||||
"/opt/hermes/.venv/bin/hermes"
|
||||
"Path to the hermes executable inside the Docker container.")
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Org link type: [[hermes:20260718_162500_a1b2c3][Resume — Title]]
|
||||
;; -------------------------------------------------------------------
|
||||
(org-link-set-parameters "hermes"
|
||||
:follow (lambda (id)
|
||||
(gortium/hermes--open-vterm
|
||||
(format "hermes chat --resume %s" id)
|
||||
"*hermes*"))
|
||||
:help-echo "Resume this Hermes chat session in vterm")
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Helpers — build commands, detect project
|
||||
;; -------------------------------------------------------------------
|
||||
|
||||
(defun gortium/hermes--remote-prefix (&optional host)
|
||||
"Build SSH/Docker prefix for running Hermes commands remotely."
|
||||
(let ((h (or host gortium/hermes-remote-host)))
|
||||
(format "ssh %s docker exec %s %s" h
|
||||
gortium/hermes-remote-docker
|
||||
gortium/hermes-remote-path)))
|
||||
|
||||
(defun gortium/hermes--cmd (subcmd &optional remote-host)
|
||||
"Prefix SUBCMD with local 'hermes' or remote SSH/Docker."
|
||||
(if remote-host
|
||||
(format "%s %s" (gortium/hermes--remote-prefix remote-host) subcmd)
|
||||
(format "hermes %s" subcmd)))
|
||||
|
||||
(defun gortium/hermes--project-info ()
|
||||
"Return (ROOT SESSIONS-FILE NAME) or signal error if not in project."
|
||||
(let ((root (projectile-project-root)))
|
||||
(if root
|
||||
(list root
|
||||
(expand-file-name ".hermes-sessions" root)
|
||||
(file-name-nondirectory (directory-file-name root)))
|
||||
(user-error "Not in a Projectile project"))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Capture session ID at session start (one-shot, before vterm opens)
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--capture-session-id (&optional remote-host)
|
||||
"Create a quick Hermes session and return its ID.
|
||||
Sends a meaningful init message so history starts with project context.
|
||||
session_id: is on stderr; 2>&1 merges it so shell-command-to-string catches it."
|
||||
(let* ((proj (nth 2 (gortium/hermes--project-info)))
|
||||
(cmd (gortium/hermes--cmd
|
||||
(format "chat -q \"Init session for %s. Reply ONLY with OK.\" --quiet 2>&1" proj)
|
||||
remote-host))
|
||||
(output (shell-command-to-string cmd)))
|
||||
(if (string-match "session_id: \\([0-9a-f_]+\\)" output)
|
||||
(match-string 1 output)
|
||||
(user-error "Could not capture Hermes session ID"))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Append session entry to .hermes-sessions
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes-append-session (sessions-file id title)
|
||||
"Append session ID and TITLE to SESSIONS-FILE (org format)."
|
||||
(let ((proj (file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory sessions-file)))))
|
||||
(unless (file-exists-p sessions-file)
|
||||
(with-temp-buffer
|
||||
(insert "# -*- mode: org -*-\n"
|
||||
"#+TITLE: Sessions — " proj "\n\n* Sessions\n\n")
|
||||
(write-region (point-min) (point-max) sessions-file nil 'silent)))
|
||||
(with-temp-buffer
|
||||
(insert (format "\n** %s — %s\n:PROPERTIES:\n:HERMES_SESSION: %s\n:END:\n[[hermes:%s][Resume — %s]]\n\n"
|
||||
(format-time-string "%Y-%m-%d %H:%M") title
|
||||
id id title))
|
||||
(write-region (point-min) (point-max) sessions-file t 'silent))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Rename session globally
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--rename-session (id fancy-title &optional remote-host)
|
||||
"Rename a Hermes session globally (adds project prefix)."
|
||||
(shell-command-to-string
|
||||
(gortium/hermes--cmd
|
||||
(format "sessions rename %s \"%s\"" id fancy-title) remote-host)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Build launch command for interactive vterm
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--launch-cmd (id &optional remote-host)
|
||||
"Build shell command to resume session ID in a terminal.
|
||||
Remote needs ssh -t + docker exec -it (PTY required)."
|
||||
(if remote-host
|
||||
(format "ssh -t %s docker exec -it %s %s chat --resume %s"
|
||||
remote-host gortium/hermes-remote-docker
|
||||
gortium/hermes-remote-path id)
|
||||
(format "hermes chat --resume %s" id)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Open vterm buffer and run command
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--open-vterm (command &optional name)
|
||||
"Open a vterm buffer and run COMMAND inside it."
|
||||
(let ((buf (get-buffer-create (or name "*hermes*"))))
|
||||
(switch-to-buffer buf)
|
||||
(unless (and (eq major-mode 'vterm-mode)
|
||||
(get-buffer-process buf))
|
||||
(vterm-mode)
|
||||
(let ((proc (get-buffer-process buf)))
|
||||
(when proc (process-send-string proc (concat command "\n")))))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Start a new Hermes session (with title prompt)
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-start (title &optional remote-host)
|
||||
"Start a new Hermes session for the current project.
|
||||
With prefix arg, prompt for remote SSH host (default: lazyworkhorse)."
|
||||
(interactive
|
||||
(let* ((remote (when current-prefix-arg
|
||||
(let ((h (read-string
|
||||
"Remote host [lazyworkhorse]: " nil nil "lazyworkhorse")))
|
||||
(unless (string-empty-p h) h))))
|
||||
(title (read-string "Session title: ")))
|
||||
(list title remote)))
|
||||
(pcase-let ((`(,root ,sfile ,name) (gortium/hermes--project-info)))
|
||||
(let ((id (gortium/hermes--capture-session-id remote-host)))
|
||||
(gortium/hermes--rename-session id (format "[%s] %s" name title) remote-host)
|
||||
(gortium/hermes-append-session sfile id title)
|
||||
(gortium/hermes--open-vterm
|
||||
(gortium/hermes--launch-cmd id remote-host)
|
||||
(format "*hermes-%s*" name)))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Browse / resume past sessions (completing-read menu)
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-resume ()
|
||||
"Browse and resume a past Hermes session from the current project."
|
||||
(interactive)
|
||||
(pcase-let ((`(,root ,sfile ,_name) (gortium/hermes--project-info)))
|
||||
(if (not (file-exists-p sfile))
|
||||
(user-error (format "No .hermes-sessions in %s" root))
|
||||
(let (sessions current)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents sfile)
|
||||
(while (not (eobp))
|
||||
(forward-line 1)
|
||||
(cond
|
||||
((looking-at "^\\*\\* \\([^—]+\\) — \\(.+\\)$")
|
||||
(setq current (list :date (string-trim (match-string 1))
|
||||
:title (match-string 2)
|
||||
:id nil)))
|
||||
((and current (looking-at "^:HERMES_SESSION: \\(.+\\)$"))
|
||||
(plist-put current :id (match-string 1))
|
||||
(push current sessions)
|
||||
(setq current nil)))))
|
||||
;; sessions is in push-order = newest first
|
||||
(if (null sessions)
|
||||
(message "No Hermes sessions recorded for this project.")
|
||||
(let* ((items (mapcar (lambda (s)
|
||||
(format "%s — %s"
|
||||
(plist-get s :date)
|
||||
(plist-get s :title)))
|
||||
sessions))
|
||||
(choice (completing-read "Resume Hermes session: " items nil t))
|
||||
(idx (cl-position choice items :test #'string=))
|
||||
(session (nth idx sessions))
|
||||
(id (plist-get session :id)))
|
||||
(gortium/hermes--open-vterm
|
||||
(format "hermes chat --resume %s" id))))))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Link external session to current project
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-link-session (session-id title)
|
||||
"Link an existing Hermes session to the current project.
|
||||
If SESSION-ID is empty, show a menu of recent sessions to browse."
|
||||
(interactive
|
||||
(let ((id (read-string "Session ID (empty to browse recent): ")))
|
||||
(if (string-empty-p id)
|
||||
(let* ((output (shell-command-to-string "hermes sessions list --limit 50 2>&1"))
|
||||
(lines (split-string output "\n" t))
|
||||
(data-lines (nthcdr 2 lines)) ; skip header + separator
|
||||
(id-re "[0-9]\\{8\\}_[0-9]\\{6\\}_[a-f0-9]\\{6\\}")
|
||||
(items (delq nil
|
||||
(mapcar (lambda (line)
|
||||
(when (string-match (concat id-re "$") line)
|
||||
(let* ((id (match-string 0 line))
|
||||
(before-id (substring line 0 (match-beginning 0)))
|
||||
(full-preview (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+[a-z][a-z0-9]*\\s-*$" "" before-id))))
|
||||
(cons full-preview id)))
|
||||
data-lines))
|
||||
(titles (delq nil
|
||||
(mapcar (lambda (line)
|
||||
(when (string-match (concat id-re "$") line)
|
||||
(let* ((id (match-string 0 line))
|
||||
(before-id (substring line 0 (match-beginning 0)))
|
||||
(full (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+[a-z][a-z0-9]*\\s-*$" "" before-id)))
|
||||
(clean (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+\\(just now\\|[0-9]+[mhd] ago\\|[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)$"
|
||||
"" full))))
|
||||
(cons id clean)))
|
||||
data-lines))))
|
||||
(if (null items)
|
||||
(user-error "No recent sessions found")
|
||||
(let* ((vertico-sort-function 'identity) ; keep vertico, no re-sort
|
||||
(choice (completing-read "Session: " items nil t))
|
||||
(sid (cdr (assoc choice items)))
|
||||
(default-title (cdr (assoc sid titles))))
|
||||
(list sid (read-string "Title: " default-title)))))
|
||||
(list id (read-string "Title: ")))))
|
||||
(pcase-let ((`(,root ,sfile ,name) (gortium/hermes--project-info)))
|
||||
(gortium/hermes--rename-session id (format "[%s] %s" name title))
|
||||
(gortium/hermes-append-session sfile id title)
|
||||
(message "Session %s linked to project %s as \"%s\"" id name title)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Keybindings (under SPC p)
|
||||
;; -------------------------------------------------------------------
|
||||
(map! :leader
|
||||
:desc "New Hermes session" "p h" #'gortium/hermes-start
|
||||
:desc "Resume Hermes session" "p H" #'gortium/hermes-resume
|
||||
:desc "Link session to project" "p l" #'gortium/hermes-link-session)
|
||||
#+end_src
|
||||
|
||||
* Tramp
|
||||
@@ -2043,7 +2298,7 @@ Recently added the quick access entries. Game changer.
|
||||
;; Use `p` to open the yank menu
|
||||
(map! :after dirvish-yank
|
||||
:map dirvish-mode-map
|
||||
:n "p" #'dirvish-yank-menu)
|
||||
:n "P" #'dirvish-yank-menu)
|
||||
|
||||
(map! :after dirvish
|
||||
:leader
|
||||
@@ -2466,6 +2721,27 @@ only injects when the project's context-tag matches current mu4e context."
|
||||
(emms-player-set emms-player-mpv 'regex
|
||||
(rx (or (: "http" (? "s") "://" (* nonl) "youtube.com" (* nonl))
|
||||
(: "http" (? "s") "://" (* nonl) "youtu.be" (* nonl))))))
|
||||
|
||||
(defun my-elfeed-tube-add-to-emms ()
|
||||
"Add the currently selected Elfeed video(s) to the EMMS playlist."
|
||||
(interactive)
|
||||
(let ((entries (if (eq major-mode 'elfeed-show-mode)
|
||||
(list elfeed-show-entry)
|
||||
(elfeed-search-selected))))
|
||||
(dolist (entry entries)
|
||||
(let ((link (elfeed-entry-link entry)))
|
||||
(if link
|
||||
(progn
|
||||
(emms-add-url link)
|
||||
(message "Queued in EMMS: %s" link))
|
||||
(message "No link found for this entry."))))))
|
||||
|
||||
;; Bind the function to 'e' in both elfeed views
|
||||
(map! :map elfeed-search-mode-map
|
||||
:n "e" #'my-elfeed-tube-add-to-emms)
|
||||
|
||||
(map! :map elfeed-show-mode-map
|
||||
:n "e" #'my-elfeed-tube-add-to-emms)
|
||||
#+end_src
|
||||
* EAF
|
||||
#+begin_src emacs-lisp
|
||||
@@ -3291,7 +3567,7 @@ Suppresses all logging, notes, and custom odometer hooks."
|
||||
(delete-window old-win)))))))
|
||||
#+end_src
|
||||
|
||||
** ExoKortex System
|
||||
** ExoKortex File System
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
;;; ====================================================================
|
||||
@@ -3324,7 +3600,7 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
;; 2. Enforce text-only gitignore rules if absent
|
||||
(unless (file-exists-p gitignore-file)
|
||||
(with-temp-file gitignore-file
|
||||
(insert "# Local Node Rule: Track text layers only\n*\n!.gitignore\n!.projectile\n!.dir-locals.el\n!.bookmarks\n!1-Plan/\n!1-Plan/**\n!3-Notes/\n!3-Notes/**\n")))
|
||||
(insert "# Local Node Rule: Track text layers only\n*\n!.gitignore\n!.projectile\n!.dir-locals.el\n!.bookmarks\n!.hermes-sessions\n!1-Plan/\n!1-Plan/**\n!3-Notes/\n!3-Notes/**\n")))
|
||||
|
||||
;; 3. Build index Org block if absent
|
||||
(unless (file-exists-p org-file)
|
||||
@@ -3369,6 +3645,12 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
(prin1 vars (current-buffer))
|
||||
(princ "))" (current-buffer)))))))
|
||||
|
||||
;; Create .hermes-sessions skeleton
|
||||
(let ((hs-file (expand-file-name ".hermes-sessions" node-dir)))
|
||||
(unless (file-exists-p hs-file)
|
||||
(with-temp-file hs-file
|
||||
(insert (format "# -*- mode: org -*-\n#+TITLE: Sessions — %s\n\n* Sessions\n\n" node-name)))))
|
||||
|
||||
;; Mark these directory-local variables as safe
|
||||
(put 'gortium/project-tag 'safe-local-variable #'stringp)
|
||||
(put 'gortium/context-tag 'safe-local-variable #'stringp)
|
||||
@@ -3381,14 +3663,14 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
(shell-command "git init")
|
||||
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||
(when project-tag
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el .hermes-sessions")))
|
||||
(shell-command (concat "git add -f " add-files)))
|
||||
(shell-command "git commit -m 'System: Initialize node repository'"))
|
||||
|
||||
;; existing repo: add new marker files if they exist
|
||||
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||
(when project-tag
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el .hermes-sessions")))
|
||||
(shell-command (concat "git add -f " add-files)))))
|
||||
|
||||
;; Pass the target file location back up to the caller
|
||||
@@ -3432,14 +3714,14 @@ Optional PROJECT-TAG and CONTEXT-TAG can be given to create .projectile and .dir
|
||||
dired-elements))))
|
||||
(dirs (or marked-dirs
|
||||
(let* ((cursor-path (when (derived-mode-p 'dired-mode)
|
||||
(dired-get-filename nil t)))
|
||||
(dired-get-filename nil t)))
|
||||
(initial-path (cond
|
||||
((and cursor-path (file-directory-p cursor-path))
|
||||
cursor-path)
|
||||
(cursor-path
|
||||
(file-name-directory cursor-path))
|
||||
(t default-directory))))
|
||||
(list (read-directory-name "Directory to convert: " nil initial-path t initial-path)))))
|
||||
(list (read-directory-name "Directory to convert: " initial-path initial-path t)))))
|
||||
(tag (read-string "Project tag (blank to skip): "))
|
||||
(ctx (read-string "Context tag (or blank): ")))
|
||||
(list dirs tag ctx)))
|
||||
@@ -3449,9 +3731,9 @@ Optional PROJECT-TAG and CONTEXT-TAG can be given to create .projectile and .dir
|
||||
(dolist (dir node-dirs)
|
||||
(let* ((node-dir (expand-file-name dir))
|
||||
(org-file (gortium/ensure-node-structure node-dir
|
||||
"Audit existing assets and reconcile tracking nodes"
|
||||
(unless (string-empty-p project-tag) project-tag)
|
||||
(unless (string-empty-p context-tag) context-tag))))
|
||||
"Audit existing assets and reconcile tracking nodes"
|
||||
(unless (string-empty-p project-tag) project-tag)
|
||||
(unless (string-empty-p context-tag) context-tag))))
|
||||
(setq last-org-file org-file)
|
||||
(cl-incf count)))
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; 1. Set the image path
|
||||
(setq fancy-splash-image (expand-file-name "hermes_logo_ascii.png" doom-user-dir))
|
||||
|
||||
;; 2. Define the quote function
|
||||
(defun my-dashboard-quote ()
|
||||
(insert "\n" (+dashboard--center +dashboard--width "Simplicity is the ultimate sophistication.") "\n"))
|
||||
|
||||
;; 3. Override the functions list using the correct variable from the docs
|
||||
(after! doom-dashboard
|
||||
(setq +dashboard-functions
|
||||
'(+dashboard-widget-banner
|
||||
my-dashboard-quote
|
||||
+dashboard-widget-loaded
|
||||
+dashboard-widget-shortmenu
|
||||
+dashboard-widget-footer)))
|
||||
|
||||
(setq user-full-name "Thierry Pouplier"
|
||||
user-mail-address "tpouplier@tdnde.com"
|
||||
user-mail-address "tpouplier@tdnde.com")
|
||||
@@ -20,19 +36,13 @@
|
||||
(setq projectile-auto-discover nil))
|
||||
|
||||
(setq bookmark-save-flag 1)
|
||||
(setq bookmark-default-file (expand-file-name "bookmarks" (expand-file-name "~/ExoKortex/2-Areas/Home-Infra/2-Assets/bookmark")))
|
||||
(setq bookmark-default-file (expand-file-name "~/ExoKortex/2-Areas/Home-Infra/2-Assets/bookmark"))
|
||||
|
||||
(setq global-auto-revert-mode 1)
|
||||
|
||||
(map! :leader
|
||||
(:prefix ("w" . "window")
|
||||
:desc "Minimize window" "O" #'minimize-window)
|
||||
(:prefix ("z" . "Personal")
|
||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||
|
||||
(map! :leader
|
||||
(:prefix ("z" . "Personal")
|
||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||
:desc "Minimize window" "O" #'minimize-window))
|
||||
|
||||
(use-package! ultra-scroll
|
||||
:init
|
||||
@@ -190,9 +200,11 @@ org-agenda-files (seq-uniq org-agenda-files)
|
||||
(setq org-agenda-exporter-settings '((ps-print-color-p 'nil)))
|
||||
(setq
|
||||
org-agenda-follow-mode nil
|
||||
org-agenda-skip-deadline-if-done nil
|
||||
org-agenda-skip-scheduled-if-done nil
|
||||
org-agenda-skip-timestamp-if-done nil
|
||||
org-agenda-skip-deadline-if-done t
|
||||
org-agenda-skip-scheduled-if-done t
|
||||
org-agenda-skip-timestamp-if-done t
|
||||
org-agenda-skip-deadline-prewarning-if-scheduled t
|
||||
org-agenda-skip-scheduled-if-deadline-is-shown t
|
||||
org-agenda-sticky t
|
||||
org-journal-enable-agenda-integration t
|
||||
org-log-done 'time
|
||||
@@ -567,6 +579,25 @@ org-agenda-files (seq-uniq org-agenda-files)
|
||||
(append '(("\\.x?html?\\'" . default))
|
||||
(assq-delete-all "\\.x?html?\\'" org-file-apps))))
|
||||
|
||||
(after! calendar
|
||||
;; Define a function to open org-roam daily note for selected date in calendar
|
||||
(defun gortium/org-roam-dailies-open-at-point ()
|
||||
"Open org-roam daily note for date at point in calendar."
|
||||
(interactive)
|
||||
(let ((date (calendar-cursor-to-date)))
|
||||
(org-roam-dailies-goto-date date)))
|
||||
)
|
||||
|
||||
;; Bind RET in calendar mode to open daily note instead of default exit
|
||||
(evil-define-key 'normal calendar-mode-map
|
||||
(kbd "RET") #'gortium/org-roam-dailies-open-at-point)
|
||||
|
||||
(after! org
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-h") (kbd "S-<left>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-j") (kbd "S-<down>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-k") (kbd "S-<up>"))
|
||||
(define-key org-read-date-minibuffer-local-map (kbd "M-l") (kbd "S-<right>")))
|
||||
|
||||
(after! grip-mode
|
||||
(setq grip-binary-path "/home/tpouplier/.local/bin/grip")
|
||||
)
|
||||
@@ -780,19 +811,6 @@ Handles org-clock and context link capture for tasks."
|
||||
|
||||
(add-hook 'org-clock-out-hook #'gortium/org-clock-out-from-connecteam)
|
||||
|
||||
(after! calendar
|
||||
;; Define a function to open org-roam daily note for selected date in calendar
|
||||
(defun gortium/org-roam-dailies-open-at-point ()
|
||||
"Open org-roam daily note for date at point in calendar."
|
||||
(interactive)
|
||||
(let ((date (calendar-cursor-to-date)))
|
||||
(org-roam-dailies-goto-date date)))
|
||||
)
|
||||
|
||||
;; Bind RET in calendar mode to open daily note instead of default exit
|
||||
(evil-define-key 'normal calendar-mode-map
|
||||
(kbd "RET") #'gortium/org-roam-dailies-open-at-point)
|
||||
|
||||
(after! dap-mode
|
||||
(require 'dap-python)
|
||||
(setq dap-python-debugger 'debugpy)
|
||||
@@ -1162,19 +1180,243 @@ DIFF:
|
||||
:desc "GPT generate commit"
|
||||
"g" #'gortium/magit-gptel-generate-commit)
|
||||
|
||||
(after! vterm
|
||||
(defun gortium/hermes-vterm ()
|
||||
"Launch Hermes Agent in vterm via SSH to lazyworkhorse (CLI mode)."
|
||||
(interactive)
|
||||
(let ((buf (get-buffer-create "*hermes*")))
|
||||
(switch-to-buffer buf)
|
||||
(unless (and (eq major-mode 'vterm-mode)
|
||||
(get-buffer-process buf))
|
||||
(vterm-mode)
|
||||
(let ((proc (get-buffer-process buf)))
|
||||
(when proc
|
||||
(process-send-string
|
||||
proc "ssh -t lazyworkhorse 'docker exec -it hermes /opt/hermes/.venv/bin/hermes'\n")))))))
|
||||
;;; ====================================================================
|
||||
;;; Hermes Session ↔ Project Integration
|
||||
;;; ====================================================================
|
||||
|
||||
;; Configuration (customize per-host)
|
||||
(defvar gortium/hermes-remote-host "lazyworkhorse"
|
||||
"SSH host for remote Hermes. nil = use local Hermes.")
|
||||
(defvar gortium/hermes-remote-docker "hermes"
|
||||
"Docker container name for remote Hermes.")
|
||||
(defvar gortium/hermes-remote-path
|
||||
"/opt/hermes/.venv/bin/hermes"
|
||||
"Path to the hermes executable inside the Docker container.")
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Org link type: [[hermes:20260718_162500_a1b2c3][Resume — Title]]
|
||||
;; -------------------------------------------------------------------
|
||||
(org-link-set-parameters "hermes"
|
||||
:follow (lambda (id)
|
||||
(gortium/hermes--open-vterm
|
||||
(format "hermes chat --resume %s" id)
|
||||
"*hermes*"))
|
||||
:help-echo "Resume this Hermes chat session in vterm")
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Helpers — build commands, detect project
|
||||
;; -------------------------------------------------------------------
|
||||
|
||||
(defun gortium/hermes--remote-prefix (&optional host)
|
||||
"Build SSH/Docker prefix for running Hermes commands remotely."
|
||||
(let ((h (or host gortium/hermes-remote-host)))
|
||||
(format "ssh %s docker exec %s %s" h
|
||||
gortium/hermes-remote-docker
|
||||
gortium/hermes-remote-path)))
|
||||
|
||||
(defun gortium/hermes--cmd (subcmd &optional remote-host)
|
||||
"Prefix SUBCMD with local 'hermes' or remote SSH/Docker."
|
||||
(if remote-host
|
||||
(format "%s %s" (gortium/hermes--remote-prefix remote-host) subcmd)
|
||||
(format "hermes %s" subcmd)))
|
||||
|
||||
(defun gortium/hermes--project-info ()
|
||||
"Return (ROOT SESSIONS-FILE NAME) or signal error if not in project."
|
||||
(let ((root (projectile-project-root)))
|
||||
(if root
|
||||
(list root
|
||||
(expand-file-name ".hermes-sessions" root)
|
||||
(file-name-nondirectory (directory-file-name root)))
|
||||
(user-error "Not in a Projectile project"))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Capture session ID at session start (one-shot, before vterm opens)
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--capture-session-id (&optional remote-host)
|
||||
"Create a quick Hermes session and return its ID.
|
||||
Sends a meaningful init message so history starts with project context.
|
||||
session_id: is on stderr; 2>&1 merges it so shell-command-to-string catches it."
|
||||
(let* ((proj (nth 2 (gortium/hermes--project-info)))
|
||||
(cmd (gortium/hermes--cmd
|
||||
(format "chat -q \"Init session for %s. Reply ONLY with OK.\" --quiet 2>&1" proj)
|
||||
remote-host))
|
||||
(output (shell-command-to-string cmd)))
|
||||
(if (string-match "session_id: \\([0-9a-f_]+\\)" output)
|
||||
(match-string 1 output)
|
||||
(user-error "Could not capture Hermes session ID"))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Append session entry to .hermes-sessions
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes-append-session (sessions-file id title)
|
||||
"Append session ID and TITLE to SESSIONS-FILE (org format)."
|
||||
(let ((proj (file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory sessions-file)))))
|
||||
(unless (file-exists-p sessions-file)
|
||||
(with-temp-buffer
|
||||
(insert "# -*- mode: org -*-\n"
|
||||
"#+TITLE: Sessions — " proj "\n\n* Sessions\n\n")
|
||||
(write-region (point-min) (point-max) sessions-file nil 'silent)))
|
||||
(with-temp-buffer
|
||||
(insert (format "\n** %s — %s\n:PROPERTIES:\n:HERMES_SESSION: %s\n:END:\n[[hermes:%s][Resume — %s]]\n\n"
|
||||
(format-time-string "%Y-%m-%d %H:%M") title
|
||||
id id title))
|
||||
(write-region (point-min) (point-max) sessions-file t 'silent))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Rename session globally
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--rename-session (id fancy-title &optional remote-host)
|
||||
"Rename a Hermes session globally (adds project prefix)."
|
||||
(shell-command-to-string
|
||||
(gortium/hermes--cmd
|
||||
(format "sessions rename %s \"%s\"" id fancy-title) remote-host)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Build launch command for interactive vterm
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--launch-cmd (id &optional remote-host)
|
||||
"Build shell command to resume session ID in a terminal.
|
||||
Remote needs ssh -t + docker exec -it (PTY required)."
|
||||
(if remote-host
|
||||
(format "ssh -t %s docker exec -it %s %s chat --resume %s"
|
||||
remote-host gortium/hermes-remote-docker
|
||||
gortium/hermes-remote-path id)
|
||||
(format "hermes chat --resume %s" id)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Open vterm buffer and run command
|
||||
;; -------------------------------------------------------------------
|
||||
(defun gortium/hermes--open-vterm (command &optional name)
|
||||
"Open a vterm buffer and run COMMAND inside it."
|
||||
(let ((buf (get-buffer-create (or name "*hermes*"))))
|
||||
(switch-to-buffer buf)
|
||||
(unless (and (eq major-mode 'vterm-mode)
|
||||
(get-buffer-process buf))
|
||||
(vterm-mode)
|
||||
(let ((proc (get-buffer-process buf)))
|
||||
(when proc (process-send-string proc (concat command "\n")))))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Start a new Hermes session (with title prompt)
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-start (title &optional remote-host)
|
||||
"Start a new Hermes session for the current project.
|
||||
With prefix arg, prompt for remote SSH host (default: lazyworkhorse)."
|
||||
(interactive
|
||||
(let* ((remote (when current-prefix-arg
|
||||
(let ((h (read-string
|
||||
"Remote host [lazyworkhorse]: " nil nil "lazyworkhorse")))
|
||||
(unless (string-empty-p h) h))))
|
||||
(title (read-string "Session title: ")))
|
||||
(list title remote)))
|
||||
(pcase-let ((`(,root ,sfile ,name) (gortium/hermes--project-info)))
|
||||
(let ((id (gortium/hermes--capture-session-id remote-host)))
|
||||
(gortium/hermes--rename-session id (format "[%s] %s" name title) remote-host)
|
||||
(gortium/hermes-append-session sfile id title)
|
||||
(gortium/hermes--open-vterm
|
||||
(gortium/hermes--launch-cmd id remote-host)
|
||||
(format "*hermes-%s*" name)))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Browse / resume past sessions (completing-read menu)
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-resume ()
|
||||
"Browse and resume a past Hermes session from the current project."
|
||||
(interactive)
|
||||
(pcase-let ((`(,root ,sfile ,_name) (gortium/hermes--project-info)))
|
||||
(if (not (file-exists-p sfile))
|
||||
(user-error (format "No .hermes-sessions in %s" root))
|
||||
(let (sessions current)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents sfile)
|
||||
(while (not (eobp))
|
||||
(forward-line 1)
|
||||
(cond
|
||||
((looking-at "^\\*\\* \\([^—]+\\) — \\(.+\\)$")
|
||||
(setq current (list :date (string-trim (match-string 1))
|
||||
:title (match-string 2)
|
||||
:id nil)))
|
||||
((and current (looking-at "^:HERMES_SESSION: \\(.+\\)$"))
|
||||
(plist-put current :id (match-string 1))
|
||||
(push current sessions)
|
||||
(setq current nil)))))
|
||||
;; sessions is in push-order = newest first
|
||||
(if (null sessions)
|
||||
(message "No Hermes sessions recorded for this project.")
|
||||
(let* ((items (mapcar (lambda (s)
|
||||
(format "%s — %s"
|
||||
(plist-get s :date)
|
||||
(plist-get s :title)))
|
||||
sessions))
|
||||
(choice (completing-read "Resume Hermes session: " items nil t))
|
||||
(idx (cl-position choice items :test #'string=))
|
||||
(session (nth idx sessions))
|
||||
(id (plist-get session :id)))
|
||||
(gortium/hermes--open-vterm
|
||||
(format "hermes chat --resume %s" id))))))))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Link external session to current project
|
||||
;; -------------------------------------------------------------------
|
||||
;;;###autoload
|
||||
(defun gortium/hermes-link-session (session-id title)
|
||||
"Link an existing Hermes session to the current project.
|
||||
If SESSION-ID is empty, show a menu of recent sessions to browse."
|
||||
(interactive
|
||||
(let ((id (read-string "Session ID (empty to browse recent): ")))
|
||||
(if (string-empty-p id)
|
||||
(let* ((output (shell-command-to-string "hermes sessions list --limit 50 2>&1"))
|
||||
(lines (split-string output "\n" t))
|
||||
(data-lines (nthcdr 2 lines)) ; skip header + separator
|
||||
(id-re "[0-9]\\{8\\}_[0-9]\\{6\\}_[a-f0-9]\\{6\\}")
|
||||
(items (delq nil
|
||||
(mapcar (lambda (line)
|
||||
(when (string-match (concat id-re "$") line)
|
||||
(let* ((id (match-string 0 line))
|
||||
(before-id (substring line 0 (match-beginning 0)))
|
||||
(full-preview (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+[a-z][a-z0-9]*\\s-*$" "" before-id))))
|
||||
(cons full-preview id)))
|
||||
data-lines))
|
||||
(titles (delq nil
|
||||
(mapcar (lambda (line)
|
||||
(when (string-match (concat id-re "$") line)
|
||||
(let* ((id (match-string 0 line))
|
||||
(before-id (substring line 0 (match-beginning 0)))
|
||||
(full (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+[a-z][a-z0-9]*\\s-*$" "" before-id)))
|
||||
(clean (string-trim
|
||||
(replace-regexp-in-string
|
||||
"\\s-+\\(just now\\|[0-9]+[mhd] ago\\|[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)$"
|
||||
"" full))))
|
||||
(cons id clean)))
|
||||
data-lines))))
|
||||
(if (null items)
|
||||
(user-error "No recent sessions found")
|
||||
(let* ((vertico-sort-function 'identity) ; keep vertico, no re-sort
|
||||
(choice (completing-read "Session: " items nil t))
|
||||
(sid (cdr (assoc choice items)))
|
||||
(default-title (cdr (assoc sid titles))))
|
||||
(list sid (read-string "Title: " default-title)))))
|
||||
(list id (read-string "Title: ")))))
|
||||
(pcase-let ((`(,root ,sfile ,name) (gortium/hermes--project-info)))
|
||||
(gortium/hermes--rename-session id (format "[%s] %s" name title))
|
||||
(gortium/hermes-append-session sfile id title)
|
||||
(message "Session %s linked to project %s as \"%s\"" id name title)))
|
||||
|
||||
;; -------------------------------------------------------------------
|
||||
;; Keybindings (under SPC p)
|
||||
;; -------------------------------------------------------------------
|
||||
(map! :leader
|
||||
:desc "New Hermes session" "p h" #'gortium/hermes-start
|
||||
:desc "Resume Hermes session" "p H" #'gortium/hermes-resume
|
||||
:desc "Link session to project" "p l" #'gortium/hermes-link-session)
|
||||
|
||||
;; set `tramp-direct-async-process' locally in all ssh connections
|
||||
(connection-local-set-profile-variables
|
||||
@@ -1240,7 +1482,7 @@ DIFF:
|
||||
;; Use `p` to open the yank menu
|
||||
(map! :after dirvish-yank
|
||||
:map dirvish-mode-map
|
||||
:n "p" #'dirvish-yank-menu)
|
||||
:n "P" #'dirvish-yank-menu)
|
||||
|
||||
(map! :after dirvish
|
||||
:leader
|
||||
@@ -1620,6 +1862,27 @@ only injects when the project's context-tag matches current mu4e context."
|
||||
(rx (or (: "http" (? "s") "://" (* nonl) "youtube.com" (* nonl))
|
||||
(: "http" (? "s") "://" (* nonl) "youtu.be" (* nonl))))))
|
||||
|
||||
(defun my-elfeed-tube-add-to-emms ()
|
||||
"Add the currently selected Elfeed video(s) to the EMMS playlist."
|
||||
(interactive)
|
||||
(let ((entries (if (eq major-mode 'elfeed-show-mode)
|
||||
(list elfeed-show-entry)
|
||||
(elfeed-search-selected))))
|
||||
(dolist (entry entries)
|
||||
(let ((link (elfeed-entry-link entry)))
|
||||
(if link
|
||||
(progn
|
||||
(emms-add-url link)
|
||||
(message "Queued in EMMS: %s" link))
|
||||
(message "No link found for this entry."))))))
|
||||
|
||||
;; Bind the function to 'e' in both elfeed views
|
||||
(map! :map elfeed-search-mode-map
|
||||
:n "e" #'my-elfeed-tube-add-to-emms)
|
||||
|
||||
(map! :map elfeed-show-mode-map
|
||||
:n "e" #'my-elfeed-tube-add-to-emms)
|
||||
|
||||
(use-package! eaf
|
||||
:load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework"
|
||||
:config
|
||||
@@ -2408,7 +2671,7 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
;; 2. Enforce text-only gitignore rules if absent
|
||||
(unless (file-exists-p gitignore-file)
|
||||
(with-temp-file gitignore-file
|
||||
(insert "# Local Node Rule: Track text layers only\n*\n!.gitignore\n!.projectile\n!.dir-locals.el\n!.bookmarks\n!1-Plan/\n!1-Plan/**\n!3-Notes/\n!3-Notes/**\n")))
|
||||
(insert "# Local Node Rule: Track text layers only\n*\n!.gitignore\n!.projectile\n!.dir-locals.el\n!.bookmarks\n!.hermes-sessions\n!1-Plan/\n!1-Plan/**\n!3-Notes/\n!3-Notes/**\n")))
|
||||
|
||||
;; 3. Build index Org block if absent
|
||||
(unless (file-exists-p org-file)
|
||||
@@ -2453,6 +2716,12 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
(prin1 vars (current-buffer))
|
||||
(princ "))" (current-buffer)))))))
|
||||
|
||||
;; Create .hermes-sessions skeleton
|
||||
(let ((hs-file (expand-file-name ".hermes-sessions" node-dir)))
|
||||
(unless (file-exists-p hs-file)
|
||||
(with-temp-file hs-file
|
||||
(insert (format "# -*- mode: org -*-\n#+TITLE: Sessions — %s\n\n* Sessions\n\n" node-name)))))
|
||||
|
||||
;; Mark these directory-local variables as safe
|
||||
(put 'gortium/project-tag 'safe-local-variable #'stringp)
|
||||
(put 'gortium/context-tag 'safe-local-variable #'stringp)
|
||||
@@ -2465,14 +2734,14 @@ with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)
|
||||
(shell-command "git init")
|
||||
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||
(when project-tag
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el .hermes-sessions")))
|
||||
(shell-command (concat "git add -f " add-files)))
|
||||
(shell-command "git commit -m 'System: Initialize node repository'"))
|
||||
|
||||
;; existing repo: add new marker files if they exist
|
||||
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||
(when project-tag
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||
(setq add-files (concat add-files " .projectile .dir-locals.el .hermes-sessions")))
|
||||
(shell-command (concat "git add -f " add-files)))))
|
||||
|
||||
;; Pass the target file location back up to the caller
|
||||
@@ -2516,14 +2785,14 @@ Optional PROJECT-TAG and CONTEXT-TAG can be given to create .projectile and .dir
|
||||
dired-elements))))
|
||||
(dirs (or marked-dirs
|
||||
(let* ((cursor-path (when (derived-mode-p 'dired-mode)
|
||||
(dired-get-filename nil t)))
|
||||
(dired-get-filename nil t)))
|
||||
(initial-path (cond
|
||||
((and cursor-path (file-directory-p cursor-path))
|
||||
cursor-path)
|
||||
(cursor-path
|
||||
(file-name-directory cursor-path))
|
||||
(t default-directory))))
|
||||
(list (read-directory-name "Directory to convert: " nil initial-path t initial-path)))))
|
||||
(list (read-directory-name "Directory to convert: " initial-path initial-path t)))))
|
||||
(tag (read-string "Project tag (blank to skip): "))
|
||||
(ctx (read-string "Context tag (or blank): ")))
|
||||
(list dirs tag ctx)))
|
||||
@@ -2533,9 +2802,9 @@ Optional PROJECT-TAG and CONTEXT-TAG can be given to create .projectile and .dir
|
||||
(dolist (dir node-dirs)
|
||||
(let* ((node-dir (expand-file-name dir))
|
||||
(org-file (gortium/ensure-node-structure node-dir
|
||||
"Audit existing assets and reconcile tracking nodes"
|
||||
(unless (string-empty-p project-tag) project-tag)
|
||||
(unless (string-empty-p context-tag) context-tag))))
|
||||
"Audit existing assets and reconcile tracking nodes"
|
||||
(unless (string-empty-p project-tag) project-tag)
|
||||
(unless (string-empty-p context-tag) context-tag))))
|
||||
(setq last-org-file org-file)
|
||||
(cl-incf count)))
|
||||
|
||||
|
||||
BIN
doom/.config/doom/hermes_logo_ascii.png
Normal file
BIN
doom/.config/doom/hermes_logo_ascii.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,33 +0,0 @@
|
||||
(setq doom-theme 'doom-gruvbox)
|
||||
;; Enable flashing mode-line on errors
|
||||
(doom-themes-visual-bell-config)
|
||||
;; Corrects (and improves) org-mode's native fontification.
|
||||
(doom-themes-org-config)
|
||||
|
||||
(after! org
|
||||
;; 1. Update existing faces (Org defines 1-8 by default)
|
||||
(set-face-attribute 'org-document-title nil :height 2.0 :weight 'bold)
|
||||
(set-face-attribute 'org-level-1 nil :weight 'semi-bold :height 1.45)
|
||||
(set-face-attribute 'org-level-2 nil :weight 'semi-bold :height 1.35)
|
||||
(set-face-attribute 'org-level-3 nil :weight 'semi-bold :height 1.25)
|
||||
(set-face-attribute 'org-level-4 nil :weight 'semi-bold :height 1.15)
|
||||
(set-face-attribute 'org-level-5 nil :weight 'normal :height 1.10)
|
||||
(set-face-attribute 'org-level-6 nil :weight 'normal :height 1.05)
|
||||
(set-face-attribute 'org-level-7 nil :weight 'normal :height 1.02)
|
||||
(set-face-attribute 'org-level-8 nil :weight 'normal :height 1.00)
|
||||
|
||||
;; 2. Create missing faces (9-20) and inherit them directly from org-level-8
|
||||
(cl-loop for lvl from 9 to 20
|
||||
do (let ((face-name (intern (format "org-level-%d" lvl))))
|
||||
(custom-declare-face face-name
|
||||
'((t :inherit org-level-8))
|
||||
(format "Face for org heading level %d." lvl)
|
||||
:group 'org-faces)))
|
||||
|
||||
;; 3. Add the newly created faces to Org's level list
|
||||
(setq org-level-faces
|
||||
(append org-level-faces
|
||||
(mapcar (lambda (lvl) (intern (format "org-level-%d" lvl)))
|
||||
(number-sequence 9 20))))
|
||||
|
||||
(setq org-n-level-faces (length org-level-faces)))
|
||||
Reference in New Issue
Block a user