aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/daily-prep-agenda.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-06 21:59:52 -0500
committerCraig Jennings <c@cjennings.net>2026-05-06 21:59:52 -0500
commitd81b23ad6b6e437dfe3c338a00a4be39bc555146 (patch)
tree2d4b0d7890fd1fc70d81282b81fed2808c28a106 /.ai/scripts/daily-prep-agenda.el
parent201377f57430ef28d02e703a2191434bbee55c75 (diff)
downloadrulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.tar.gz
rulesets-d81b23ad6b6e437dfe3c338a00a4be39bc555146.zip
chore(ai): initialize project notes and Claude tooling surfaces
Replace the seed notes.org with project-specific context (layout, install modes, task tracker location, recent inflection point). Bring in the synced template surfaces (protocols, workflows, scripts, references, retrospectives, someday-maybe) as tracked content for this content/documentation project.
Diffstat (limited to '.ai/scripts/daily-prep-agenda.el')
-rw-r--r--.ai/scripts/daily-prep-agenda.el142
1 files changed, 142 insertions, 0 deletions
diff --git a/.ai/scripts/daily-prep-agenda.el b/.ai/scripts/daily-prep-agenda.el
new file mode 100644
index 0000000..4c6041c
--- /dev/null
+++ b/.ai/scripts/daily-prep-agenda.el
@@ -0,0 +1,142 @@
+;;; daily-prep-agenda.el --- Standalone batch agenda extractor for daily-prep
+;;
+;; Usage:
+;; emacs --batch -q -l daily-prep-agenda.el todo.org [pcal.org ...]
+;;
+;; Filters entries to TODO/DOING/WAITING/NEXT with [#A]/[#B] priority OR
+;; DEADLINE/SCHEDULED present. Bucketizes into Overdue, Today, This Week,
+;; Priority A (no date), Priority B (no date). Emits heading + body for each.
+
+(require 'org)
+(require 'cl-lib)
+
+;; Declare the TODO keywords used across Craig's org files so org-mode parses
+;; "DOING", "WAITING", "NEXT", "CANCELLED" headings as TODO states. With `-q`,
+;; org-mode defaults to just "TODO"/"DONE" and will treat the others as plain
+;; heading text (state comes back as nil).
+(setq org-todo-keywords
+ '((sequence "TODO" "DOING" "WAITING" "NEXT" "|" "DONE" "CANCELLED")))
+
+(defvar dp-today (format-time-string "%Y-%m-%d"))
+(defvar dp-week-end
+ (format-time-string "%Y-%m-%d" (time-add (current-time) (days-to-time 7))))
+
+(defun dp-iso-date (org-ts)
+ "Extract YYYY-MM-DD from an org timestamp string like '<2026-04-25 Sat 16:00>'."
+ (when (and org-ts (string-match "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)" org-ts))
+ (match-string 1 org-ts)))
+
+(defun dp-entry-info ()
+ "Return plist of metadata + body for org entry at point."
+ (let* ((state (org-get-todo-state))
+ (el (org-element-at-point))
+ (priority (org-element-property :priority el))
+ (deadline-raw (org-entry-get (point) "DEADLINE"))
+ (scheduled-raw (org-entry-get (point) "SCHEDULED"))
+ (deadline (dp-iso-date deadline-raw))
+ (scheduled (dp-iso-date scheduled-raw))
+ (heading (org-get-heading t t t t))
+ (line (line-number-at-pos))
+ (file (or (buffer-file-name) (buffer-name)))
+ (start (save-excursion (org-end-of-meta-data t) (point)))
+ (end (save-excursion
+ (or (outline-next-heading) (goto-char (point-max)))
+ (point)))
+ (body (and (< start end)
+ (string-trim (buffer-substring-no-properties start end)))))
+ (list :state state
+ :priority priority
+ :deadline deadline
+ :deadline-raw deadline-raw
+ :scheduled scheduled
+ :scheduled-raw scheduled-raw
+ :heading heading
+ :line line
+ :file file
+ :body body)))
+
+(defun dp-active-candidate-p ()
+ "True if entry at point is an active state with [#A]/[#B] OR has DEADLINE/SCHEDULED."
+ (let* ((state (org-get-todo-state))
+ (el (org-element-at-point))
+ (pri (org-element-property :priority el))
+ (dl (org-entry-get (point) "DEADLINE"))
+ (sc (org-entry-get (point) "SCHEDULED")))
+ (and (member state '("TODO" "DOING" "WAITING" "NEXT"))
+ (or (memq pri '(?A ?B)) dl sc))))
+
+(defun dp-collect (files)
+ "Walk FILES, return list of dp-entry-info plists for matching entries."
+ (let (entries)
+ (dolist (file files)
+ (when (file-readable-p file)
+ (with-current-buffer (find-file-noselect file)
+ (org-mode)
+ (org-map-entries
+ (lambda ()
+ (when (dp-active-candidate-p)
+ (push (dp-entry-info) entries)))
+ nil 'file))))
+ (nreverse entries)))
+
+(defun dp-bucket (e)
+ "Return bucket name for entry plist E."
+ (let ((dl (plist-get e :deadline))
+ (sc (plist-get e :scheduled))
+ (pri (plist-get e :priority)))
+ (cond
+ ((and dl (string< dl dp-today)) 'overdue)
+ ((or (equal dl dp-today) (equal sc dp-today)) 'today)
+ ((and sc (string< sc dp-today)) 'overdue)
+ ((or (and dl (string< dl dp-week-end))
+ (and sc (string< sc dp-week-end))) 'this-week)
+ ((eq pri ?A) 'pri-a)
+ ((eq pri ?B) 'pri-b)
+ (t 'other))))
+
+(defun dp-format-entry (e)
+ "Format entry plist E as org-mode text."
+ (concat
+ (format "** %s%s %s\n"
+ (or (plist-get e :state) "")
+ (if-let ((p (plist-get e :priority))) (format " [#%c]" p) "")
+ (plist-get e :heading))
+ (format " :LOC: %s:%d\n"
+ (file-name-nondirectory (plist-get e :file))
+ (plist-get e :line))
+ (when-let ((d (plist-get e :deadline-raw))) (format " DEADLINE: %s\n" d))
+ (when-let ((s (plist-get e :scheduled-raw))) (format " SCHEDULED: %s\n" s))
+ (let ((b (plist-get e :body)))
+ (if (and b (not (string-empty-p b)))
+ (concat (replace-regexp-in-string "^" " " b) "\n")
+ ""))
+ "\n"))
+
+(defun dp-emit-bucket (label entries)
+ (when entries
+ (princ (format "* %s (%d)\n\n" label (length entries)))
+ (dolist (e entries)
+ (princ (dp-format-entry e)))))
+
+;; Main entrypoint
+(when noninteractive
+ (let* ((files command-line-args-left)
+ (entries (dp-collect files))
+ (groups (seq-group-by #'dp-bucket entries)))
+ (princ (format "# Daily-Prep Extract — %s\n# Files: %s\n# Total candidates: %d\n\n"
+ dp-today
+ (mapconcat #'file-name-nondirectory files ", ")
+ (length entries)))
+ (dolist (bucket '(overdue today this-week pri-a pri-b other))
+ (dp-emit-bucket
+ (pcase bucket
+ ('overdue "Overdue")
+ ('today "Today")
+ ('this-week "This Week")
+ ('pri-a "Priority A (undated)")
+ ('pri-b "Priority B (undated)")
+ ('other "Other"))
+ (alist-get bucket groups)))))
+
+(provide 'daily-prep-agenda)
+;;; daily-prep-agenda.el ends here