summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzymon Szukalski <szymon@szymonszukalski.com>2026-04-09 09:30:06 +1000
committerSzymon Szukalski <szymon@szymonszukalski.com>2026-04-09 09:30:06 +1000
commita9cb41ef7ae6fbd46dc6ff446c86b5df08f324b3 (patch)
treeb54f9306f95f272269d8dbea31d09d5e6af7921d
parent018c70ae0ba8a8c3a7ece3a10fb0be2199e3af96 (diff)
Rename people commands to crm
-rw-r--r--README.md26
-rw-r--r--config.org393
2 files changed, 217 insertions, 202 deletions
diff --git a/README.md b/README.md
index 6d70f82..ed1edae 100644
--- a/README.md
+++ b/README.md
@@ -92,13 +92,16 @@ The minibuffer stack is intentionally small:
The people workflow is a CRM rooted at `~/org/areas/people/people.org`.
- Each top-level heading represents one person.
-- Entries are strictly structured around heading text and properties.
+- Entries are strictly structured around heading text and flat properties.
- The system generates abbrevs from the people file.
- The system provides canonical-name completion through a CAPF.
- The system provides reporting views grouped by person properties.
- The system rebuilds on any people-file change, whether the change comes from manual editing or from a command.
- The system must remain up to date after changes.
- Failures must surface visibly. The system must not silently fall back to stale state.
+- The CRM is individual-focused: `TEAM` captures the current working team and `MANAGER` captures the formal organisational manager.
+- Team structure, org charts, and matrix relationships belong in separate notes, not in the CRM.
+- Person cards use `ROLE`, `TEAM`, `MANAGER`, `ENGAGEMENT`, `LOCATION`, and `CURRENT_FOCUS` in that order.
- `CURRENT_FOCUS` must stay short and phrase-like so summaries and completion annotations remain readable.
Name entry uses fixed abbrevs plus the people CRM:
@@ -106,14 +109,14 @@ Name entry uses fixed abbrevs plus the people CRM:
- `abbrev` provides deterministic one-shot shortcuts for fixed name expansions.
- people-specific abbrevs are generated from top-level cards in `~/org/areas/people/people.org`.
- a CAPF feeds Corfu canonical names from `~/org/areas/people/people.org`, while alias matching stays available for lookup and completion.
-- Marginalia annotates person candidates with `role | location | engagement | current focus`.
-- `M-x ss/people-open` opens `~/org/areas/people/people.org` in overview mode through `ss/people-overview`.
-- `M-x ss/people-find` opens a person card narrowed to that subtree.
-- `M-x ss/people-overview` opens the people file in overview mode, resetting the file by widening and restoring the overview when leaving card view.
-- `M-x ss/people-insert-name` inserts the canonical name at point.
-- `M-x ss/people-insert-summary` inserts a compact single-line summary at point.
-- `M-x ss/people-add` adds a new person card directly to `~/org/areas/people/people.org`.
-- `M-x ss/people-report-by-relationship`, `M-x ss/people-report-by-engagement`, `M-x ss/people-report-by-role`, and `M-x ss/people-report-by-location` render grouped Org reports in a read-only buffer.
+- Marginalia annotates person candidates with `role | team | engagement | current focus`.
+- `M-x ss/crm-open` opens `~/org/areas/people/people.org` in overview mode through `ss/crm-overview`.
+- `M-x ss/crm-find` opens a person card narrowed to that subtree.
+- `M-x ss/crm-overview` opens the people file in overview mode, resetting the file by widening and restoring the overview when leaving card view.
+- `M-x ss/crm-insert-name` inserts the canonical name at point.
+- `M-x ss/crm-insert-summary` inserts a compact single-line summary at point.
+- `M-x ss/crm-add` adds a new person card directly to `~/org/areas/people/people.org`.
+- `M-x ss/crm-report-by-role`, `M-x ss/crm-report-by-team`, `M-x ss/crm-report-by-manager`, `M-x ss/crm-report-by-engagement`, and `M-x ss/crm-report-by-location` render grouped Org reports in a read-only buffer.
### Persistent abbrevs
@@ -199,7 +202,7 @@ Journal task capture writes under the current day's `Tasks` heading. Journal not
Denote captures still prompt for title, keywords, and subdirectory placement where appropriate, but folder placement does most of the classification work. The project capture template prepopulates the `project` keyword. Area and resource captures do not inject structural keywords automatically, and there is no Denote-backed meeting capture template.
-The people CRM is intentionally outside `org-capture`: `M-x ss/people-add` writes a compact card directly into `~/org/areas/people/people.org`, keeping the people file as a fast reference file instead of another capture sink.
+The people CRM is intentionally outside `org-capture`: `M-x ss/crm-add` writes a compact card directly into `~/org/areas/people/people.org`, keeping the people file as a fast reference file instead of another capture sink.
### Note creation and linking
@@ -218,7 +221,8 @@ Denote handles long-lived notes. The main bindings are:
- `C-c n O` to show people grouped by role.
- `C-c n p` to open `~/org/areas/people/people.org`.
- `C-c n P` to add a new person card.
-- `C-c n R` to show people grouped by relationship.
+- `C-c n R` to show people grouped by manager.
+- `C-c n T` to show people grouped by team.
Keyword prompts and directory placement are part of the workflow, not an afterthought. The config is set up so structure is created first, then capture writes into it, with folder placement carrying most of the durable type information.
diff --git a/config.org b/config.org
index d31c6e8..cd804b0 100644
--- a/config.org
+++ b/config.org
@@ -287,73 +287,75 @@ The people workflow is a CRM backed by the canonical
with properties for lookup, completion, reports, and abbrevs. Abbrev remains
the fast path for names you type all the time, while CAPF plus Corfu remains
the discovery path. The machine-facing layer only reads heading text and
-properties; the =Context= and =TODOs= sections stay human-facing notes.
+properties; the =Context= and =TODOs= sections stay human-facing notes. The
+CRM stays individual-focused: =TEAM= captures the current working team and
+=MANAGER= captures the formal organisational manager, but organisational
+structure lives in separate notes rather than in the CRM itself.
-The CRM is designed around =ss/people-overview=: opening the file starts in
+The CRM is designed around =ss/crm-overview=: opening the file starts in
overview mode, direct visits reset back to overview mode, and exiting card view
means widening the buffer and restoring that overview. Keep =CURRENT_FOCUS=
short and phrase-like so summaries and completion annotations stay readable.
+Person cards use a flat property model in this order: =ROLE=, =TEAM=,
+=MANAGER=, =ENGAGEMENT=, =LOCATION=, and =CURRENT_FOCUS=. =ABBREV= and
+=ALIASES= remain optional helpers for lookup and insertion.
#+begin_src emacs-lisp
(require 'seq)
(require 'subr-x)
(require 'marginalia nil t)
- (defconst ss/people-file
+ (defconst ss/crm-file
(expand-file-name "areas/people/people.org" "~/org/")
"Single source of truth for the people CRM.")
- (defconst ss/people-engagement-values
+ (defconst ss/crm-engagement-values
'("permanent" "contractor" "other")
"Allowed engagement values for people cards.")
- (defconst ss/people-relationship-values
- '("direct-report" "stakeholder" "peer" "skip" "other")
- "Allowed relationship values for people cards.")
+ (defvar ss/crm--cache nil
+ "Cached CRM entries loaded from `ss/crm-file'.")
- (defvar ss/people--cache nil
- "Cached CRM entries loaded from `ss/people-file'.")
-
- (defvar ss/people--cache-mtime nil
+ (defvar ss/crm--cache-mtime nil
"Modification time of the cached CRM entries.")
- (defun ss/people--entry-name (entry)
+ (defun ss/crm--entry-name (entry)
"Return the canonical name in ENTRY."
(plist-get entry :name))
- (defun ss/people--entry-abbrev (entry)
+ (defun ss/crm--entry-abbrev (entry)
"Return the abbrev trigger in ENTRY."
(plist-get entry :abbrev))
- (defun ss/people--entry-aliases (entry)
+ (defun ss/crm--entry-aliases (entry)
"Return alias variants in ENTRY."
(plist-get entry :aliases))
- (defun ss/people--entry-role (entry)
+ (defun ss/crm--entry-role (entry)
"Return the role in ENTRY."
(plist-get entry :role))
- (defun ss/people--entry-location (entry)
+ (defun ss/crm--entry-location (entry)
"Return the location in ENTRY."
(plist-get entry :location))
- (defun ss/people--entry-engagement (entry)
+ (defun ss/crm--entry-engagement (entry)
"Return the engagement in ENTRY."
(plist-get entry :engagement))
- (defun ss/people--entry-relationship (entry)
- "Return the relationship in ENTRY."
- (plist-get entry :relationship))
+ (defun ss/crm--entry-manager (entry)
+ "Return the manager in ENTRY."
+ (plist-get entry :manager))
- (defun ss/people--entry-current-focus (entry)
+ (defun ss/crm--entry-current-focus (entry)
"Return the current focus in ENTRY."
(plist-get entry :current-focus))
- (defun ss/people--entry-team (entry)
+ (defun ss/crm--entry-team (entry)
"Return the team in ENTRY."
(plist-get entry :team))
- (defun ss/people-default-abbrev (name)
+ (defun ss/crm-default-abbrev (name)
"Suggest a short abbrev trigger for NAME."
(let* ((parts (split-string (string-trim name) "[[:space:]]+" t))
(first (downcase (substring (car parts) 0 (min 2 (length (car parts))))))
@@ -362,50 +364,50 @@ short and phrase-like so summaries and completion annotations stay readable.
(concat ";" first last)
(concat ";" first))))
- (defun ss/people--split-values (value)
+ (defun ss/crm--split-values (value)
"Split VALUE on commas and trim each item."
(when (and value (not (string-empty-p value)))
(seq-filter
(lambda (string) (not (string-empty-p string)))
(mapcar #'string-trim (split-string value "," t)))))
- (defun ss/people--summary (entry)
+ (defun ss/crm--summary (entry)
"Return the compact one-line summary for ENTRY."
(string-join
(seq-filter
(lambda (string) (and string (not (string-empty-p string))))
- (list (ss/people--entry-role entry)
- (ss/people--entry-location entry)
- (ss/people--entry-engagement entry)
- (ss/people--entry-current-focus entry)))
+ (list (ss/crm--entry-role entry)
+ (ss/crm--entry-team entry)
+ (ss/crm--entry-engagement entry)
+ (ss/crm--entry-current-focus entry)))
" | "))
- (defun ss/people--display (entry)
+ (defun ss/crm--display (entry)
"Return the compact display string for ENTRY."
- (let ((summary (ss/people--summary entry)))
+ (let ((summary (ss/crm--summary entry)))
(if (string-empty-p summary)
- (ss/people--entry-name entry)
- (format "%s %s" (ss/people--entry-name entry) summary))))
+ (ss/crm--entry-name entry)
+ (format "%s %s" (ss/crm--entry-name entry) summary))))
- (defun ss/people--property-line (key value)
+ (defun ss/crm--property-line (key value)
"Return an Org property line for KEY and VALUE."
(if (and value (not (string-empty-p value)))
(format ":%s: %s\n" key value)
""))
- (defun ss/people--require-file ()
- "Return `ss/people-file', signaling when it is unavailable."
- (unless (file-exists-p ss/people-file)
- (user-error "People file does not exist: %s" ss/people-file))
- ss/people-file)
+ (defun ss/crm--require-file ()
+ "Return `ss/crm-file', signaling when it is unavailable."
+ (unless (file-exists-p ss/crm-file)
+ (user-error "People file does not exist: %s" ss/crm-file))
+ ss/crm-file)
- (defun ss/people-entries ()
- "Return top-level people cards from `ss/people-file'."
- (let* ((file (ss/people--require-file))
+ (defun ss/crm-entries ()
+ "Return top-level people cards from `ss/crm-file'."
+ (let* ((file (ss/crm--require-file))
(attributes (file-attributes file))
(mtime (file-attribute-modification-time attributes)))
- (unless (and ss/people--cache
- (equal mtime ss/people--cache-mtime))
+ (unless (and ss/crm--cache
+ (equal mtime ss/crm--cache-mtime))
(let ((entries
(with-temp-buffer
(insert-file-contents file)
@@ -421,47 +423,48 @@ short and phrase-like so summaries and completion annotations stay readable.
(goto-char (org-element-property :begin headline))
(push (list :name (org-element-property :raw-value headline)
:abbrev (org-entry-get nil "ABBREV")
- :aliases (ss/people--split-values
+ :aliases (ss/crm--split-values
(org-entry-get nil "ALIASES"))
:role (org-entry-get nil "ROLE")
- :location (org-entry-get nil "LOCATION")
+ :team (org-entry-get nil "TEAM")
+ :manager (org-entry-get nil "MANAGER")
:engagement (org-entry-get nil "ENGAGEMENT")
- :relationship (org-entry-get nil "RELATIONSHIP")
+ :location (org-entry-get nil "LOCATION")
:current-focus (org-entry-get nil "CURRENT_FOCUS")
- :team (org-entry-get nil "TEAM"))
+ )
cards))))
(sort cards
(lambda (left right)
- (string< (ss/people--entry-name left)
- (ss/people--entry-name right))))))))
- (setq ss/people--cache entries
- ss/people--cache-mtime mtime)))
- ss/people--cache))
+ (string< (ss/crm--entry-name left)
+ (ss/crm--entry-name right))))))))
+ (setq ss/crm--cache entries
+ ss/crm--cache-mtime mtime)))
+ ss/crm--cache))
- (defun ss/people-reload ()
+ (defun ss/crm-reload ()
"Reload the people cache and refresh prose buffers."
(interactive)
- (setq ss/people--cache nil
- ss/people--cache-mtime nil)
- (ss/people-refresh-buffers)
+ (setq ss/crm--cache nil
+ ss/crm--cache-mtime nil)
+ (ss/crm-refresh-buffers)
(message "Reloaded people CRM"))
- (defun ss/people--entry-by-name (name)
+ (defun ss/crm--entry-by-name (name)
"Return the people entry matching canonical NAME."
(seq-find
(lambda (entry)
- (string= name (ss/people--entry-name entry)))
- (ss/people-entries)))
+ (string= name (ss/crm--entry-name entry)))
+ (ss/crm-entries)))
- (defun ss/people--search-keys (entry)
+ (defun ss/crm--search-keys (entry)
"Return canonical and alias search keys for ENTRY."
- (cons (ss/people--entry-name entry)
- (ss/people--entry-aliases entry)))
+ (cons (ss/crm--entry-name entry)
+ (ss/crm--entry-aliases entry)))
- (defun ss/people--match-p (query entry)
+ (defun ss/crm--match-p (query entry)
"Return non-nil when QUERY matches ENTRY name or aliases."
(let* ((parts (split-string (downcase (string-trim query)) "[[:space:]]+" t))
- (keys (mapcar #'downcase (ss/people--search-keys entry))))
+ (keys (mapcar #'downcase (ss/crm--search-keys entry))))
(seq-every-p
(lambda (part)
(seq-some (lambda (key)
@@ -469,105 +472,105 @@ short and phrase-like so summaries and completion annotations stay readable.
keys))
parts)))
- (defun ss/people--matching-entries (query)
+ (defun ss/crm--matching-entries (query)
"Return entries whose canonical name or aliases match QUERY."
- (let ((entries (ss/people-entries)))
+ (let ((entries (ss/crm-entries)))
(if (string-empty-p (string-trim query))
entries
(seq-filter (lambda (entry)
- (ss/people--match-p query entry))
+ (ss/crm--match-p query entry))
entries))))
- (defun ss/people--completion-table (string pred action)
+ (defun ss/crm--completion-table (string pred action)
"Complete canonical people names while matching aliases via STRING."
(if (eq action 'metadata)
'(metadata (category . ss-person))
(complete-with-action
action
- (mapcar #'ss/people--entry-name (ss/people--matching-entries string))
+ (mapcar #'ss/crm--entry-name (ss/crm--matching-entries string))
string
pred)))
- (defun ss/people-marginalia-annotator (candidate)
+ (defun ss/crm-marginalia-annotator (candidate)
"Return a Marginalia annotation for person CANDIDATE."
- (when-let ((entry (ss/people--entry-by-name candidate)))
- (concat " " (ss/people--summary entry))))
+ (when-let ((entry (ss/crm--entry-by-name candidate)))
+ (concat " " (ss/crm--summary entry))))
- (defun ss/people-select-entry (&optional prompt)
+ (defun ss/crm-select-entry (&optional prompt)
"Select a person entry using PROMPT."
- (let ((completion-extra-properties '(:annotation-function ss/people-marginalia-annotator)))
- (ss/people--entry-by-name
+ (let ((completion-extra-properties '(:annotation-function ss/crm-marginalia-annotator)))
+ (ss/crm--entry-by-name
(completing-read (or prompt "Person: ")
- #'ss/people--completion-table
+ #'ss/crm--completion-table
nil
t))))
- (defun ss/people-overview ()
- "Open `ss/people-file' in overview mode, widening first when needed."
+ (defun ss/crm-overview ()
+ "Open `ss/crm-file' in overview mode, widening first when needed."
(interactive)
(unless (and buffer-file-name
(string= (file-truename buffer-file-name)
- (file-truename ss/people-file)))
- (find-file (ss/people--require-file)))
+ (file-truename ss/crm-file)))
+ (find-file (ss/crm--require-file)))
(widen)
(goto-char (point-min))
(org-overview)
(org-cycle-hide-drawers 'all))
- (defun ss/people-open ()
- "Open the people CRM by delegating to `ss/people-overview'."
+ (defun ss/crm-open ()
+ "Open the people CRM by delegating to `ss/crm-overview'."
(interactive)
- (ss/people-overview))
+ (ss/crm-overview))
- (defun ss/people--track-buffer ()
- "Refresh CRM caches when `ss/people-file' is saved."
+ (defun ss/crm--track-buffer ()
+ "Refresh CRM caches when `ss/crm-file' is saved."
(when (and buffer-file-name
(string= (file-truename buffer-file-name)
- (file-truename ss/people-file)))
- (add-hook 'after-save-hook #'ss/people-reload nil t)))
+ (file-truename ss/crm-file)))
+ (add-hook 'after-save-hook #'ss/crm-reload nil t)))
- (defun ss/people--source-buffer-p ()
- "Return non-nil when the current buffer visits `ss/people-file'."
+ (defun ss/crm--source-buffer-p ()
+ "Return non-nil when the current buffer visits `ss/crm-file'."
(and buffer-file-name
(string= (file-truename buffer-file-name)
- (file-truename ss/people-file))))
+ (file-truename ss/crm-file))))
- (defun ss/people--open-entry (entry)
+ (defun ss/crm--open-entry (entry)
"Open the people CRM file, then narrow to ENTRY for card view."
- (find-file (ss/people--require-file))
+ (find-file (ss/crm--require-file))
(widen)
(let ((position (org-find-exact-headline-in-buffer
- (ss/people--entry-name entry))))
+ (ss/crm--entry-name entry))))
(unless position
- (user-error "No people card for %s" (ss/people--entry-name entry)))
+ (user-error "No people card for %s" (ss/crm--entry-name entry)))
(goto-char position))
(org-narrow-to-subtree)
(org-fold-show-subtree)
(org-show-entry)
(goto-char (point-min)))
- (defun ss/people-find ()
+ (defun ss/crm-find ()
"Find a person and open that card."
(interactive)
- (ss/people--open-entry
- (or (ss/people-select-entry "Find person: ")
+ (ss/crm--open-entry
+ (or (ss/crm-select-entry "Find person: ")
(user-error "No person selected"))))
- (defun ss/people-insert-name ()
+ (defun ss/crm-insert-name ()
"Insert a canonical person name at point."
(interactive)
- (let ((entry (or (ss/people-select-entry "Insert person name: ")
+ (let ((entry (or (ss/crm-select-entry "Insert person name: ")
(user-error "No person selected"))))
- (insert (ss/people--entry-name entry))))
+ (insert (ss/crm--entry-name entry))))
- (defun ss/people-insert-summary ()
+ (defun ss/crm-insert-summary ()
"Insert a compact person summary at point."
(interactive)
- (let ((entry (or (ss/people-select-entry "Insert person summary: ")
+ (let ((entry (or (ss/crm-select-entry "Insert person summary: ")
(user-error "No person selected"))))
- (insert (ss/people--display entry))))
+ (insert (ss/crm--display entry))))
- (defun ss/people--report-buffer (title group-fn)
+ (defun ss/crm--report-buffer (title group-fn)
"Render a grouped CRM report titled TITLE using GROUP-FN."
(let ((groups
(sort (seq-group-by
@@ -576,7 +579,7 @@ short and phrase-like so summaries and completion annotations stay readable.
(if (string-empty-p (or value ""))
"(none)"
value)))
- (ss/people-entries))
+ (ss/crm-entries))
(lambda (left right)
(string< (car left) (car right))))))
(with-current-buffer (get-buffer-create "*People Report*")
@@ -588,79 +591,86 @@ short and phrase-like so summaries and completion annotations stay readable.
(insert "* " (car group) "\n")
(dolist (entry (sort (copy-sequence (cdr group))
(lambda (left right)
- (string< (ss/people--entry-name left)
- (ss/people--entry-name right)))))
- (insert "- " (ss/people--display entry) "\n")))
+ (string< (ss/crm--entry-name left)
+ (ss/crm--entry-name right)))))
+ (insert "- " (ss/crm--display entry) "\n")))
(goto-char (point-min))
(read-only-mode 1)
(view-mode 1))
(pop-to-buffer (current-buffer)))))
- (defun ss/people-report-by-relationship ()
- "Show people grouped by relationship."
+ (defun ss/crm-report-by-team ()
+ "Show people grouped by team."
+ (interactive)
+ (ss/crm--report-buffer
+ "People by team"
+ #'ss/crm--entry-team))
+
+ (defun ss/crm-report-by-manager ()
+ "Show people grouped by manager."
(interactive)
- (ss/people--report-buffer
- "People by relationship"
- #'ss/people--entry-relationship))
+ (ss/crm--report-buffer
+ "People by manager"
+ #'ss/crm--entry-manager))
- (defun ss/people-report-by-engagement ()
+ (defun ss/crm-report-by-engagement ()
"Show people grouped by engagement."
(interactive)
- (ss/people--report-buffer
+ (ss/crm--report-buffer
"People by engagement"
- #'ss/people--entry-engagement))
+ #'ss/crm--entry-engagement))
- (defun ss/people-report-by-role ()
+ (defun ss/crm-report-by-role ()
"Show people grouped by role."
(interactive)
- (ss/people--report-buffer
+ (ss/crm--report-buffer
"People by role"
- #'ss/people--entry-role))
+ #'ss/crm--entry-role))
- (defun ss/people-report-by-location ()
+ (defun ss/crm-report-by-location ()
"Show people grouped by location."
(interactive)
- (ss/people--report-buffer
+ (ss/crm--report-buffer
"People by location"
- #'ss/people--entry-location))
+ #'ss/crm--entry-location))
- (defun ss/people-read-string (prompt &optional default)
+ (defun ss/crm-read-string (prompt &optional default)
"Read PROMPT and trim the result."
(string-trim (read-string prompt nil nil default)))
- (defun ss/people-read-required-string (prompt &optional default)
+ (defun ss/crm-read-required-string (prompt &optional default)
"Read PROMPT and require a non-empty result."
- (let ((value (ss/people-read-string prompt default)))
+ (let ((value (ss/crm-read-string prompt default)))
(if (string-empty-p value)
(user-error "%s is required" (string-remove-suffix ": " prompt))
value)))
- (defun ss/people-read-optional-string (prompt)
+ (defun ss/crm-read-optional-string (prompt)
"Read PROMPT and return nil when the answer is empty."
- (let ((value (ss/people-read-string prompt)))
+ (let ((value (ss/crm-read-string prompt)))
(unless (string-empty-p value)
value)))
- (defun ss/people-add ()
- "Add a new compact person card to `ss/people-file'."
+ (defun ss/crm-add ()
+ "Add a new compact person card to `ss/crm-file'."
(interactive)
- (let* ((name (ss/people-read-required-string "Full name: "))
- (abbrev (ss/people-read-string "Abbrev: " (ss/people-default-abbrev name)))
- (aliases (ss/people-read-string "Aliases (comma-separated, optional): "))
- (role (ss/people-read-required-string "Role: "))
- (location (ss/people-read-required-string "Location: "))
+ (let* ((name (ss/crm-read-required-string "Full name: "))
+ (abbrev (ss/crm-read-string "Abbrev: " (ss/crm-default-abbrev name)))
+ (aliases (ss/crm-read-string "Aliases (comma-separated, optional): "))
+ (role (ss/crm-read-required-string "Role: "))
+ (team (ss/crm-read-required-string "Team: "))
+ (manager (ss/crm-read-required-string "Manager: "))
(engagement (completing-read "Engagement: "
- ss/people-engagement-values nil t nil nil
+ ss/crm-engagement-values nil t nil nil
"permanent"))
- (relationship (completing-read "Relationship: "
- ss/people-relationship-values nil t))
- (current-focus (ss/people-read-required-string "Current focus: "))
- (team (ss/people-read-optional-string "Team (optional): ")))
- (when (ss/people--entry-by-name name)
+ (location (ss/crm-read-required-string "Location: "))
+ (current-focus (ss/crm-read-required-string "Current focus: "))
+ )
+ (when (ss/crm--entry-by-name name)
(user-error "A person card for %s already exists" name))
(when (string-empty-p abbrev)
- (setq abbrev (ss/people-default-abbrev name)))
- (find-file (ss/people--require-file))
+ (setq abbrev (ss/crm-default-abbrev name)))
+ (find-file (ss/crm--require-file))
(widen)
(goto-char (point-max))
(unless (bolp)
@@ -669,54 +679,54 @@ short and phrase-like so summaries and completion annotations stay readable.
(insert "\n"))
(insert "* " name "\n"
":PROPERTIES:\n"
- (ss/people--property-line "ABBREV" abbrev)
- (ss/people--property-line "ALIASES" aliases)
- (ss/people--property-line "ROLE" role)
- (ss/people--property-line "LOCATION" location)
- (ss/people--property-line "ENGAGEMENT" engagement)
- (ss/people--property-line "RELATIONSHIP" relationship)
- (ss/people--property-line "CURRENT_FOCUS" current-focus)
- (ss/people--property-line "TEAM" team)
+ (ss/crm--property-line "ABBREV" abbrev)
+ (ss/crm--property-line "ALIASES" aliases)
+ (ss/crm--property-line "ROLE" role)
+ (ss/crm--property-line "TEAM" team)
+ (ss/crm--property-line "MANAGER" manager)
+ (ss/crm--property-line "ENGAGEMENT" engagement)
+ (ss/crm--property-line "LOCATION" location)
+ (ss/crm--property-line "CURRENT_FOCUS" current-focus)
":END:\n\n"
"** Context\n\n"
"** TODOs\n")
(save-buffer)
- (ss/people-reload)
- (ss/people--open-entry (ss/people--entry-by-name name))))
+ (ss/crm-reload)
+ (ss/crm--open-entry (ss/crm--entry-by-name name))))
- (defun ss/people--clear-installed-abbrevs ()
+ (defun ss/crm--clear-installed-abbrevs ()
"Remove people-specific abbrevs from the current local table."
(mapatoms
(lambda (symbol)
- (when (abbrev-get symbol :ss/people)
+ (when (abbrev-get symbol :ss/crm)
(define-abbrev local-abbrev-table (symbol-name symbol) nil)))
local-abbrev-table))
- (defun ss/people-install-abbrevs ()
+ (defun ss/crm-install-abbrevs ()
"Install people abbrevs into the current buffer."
- (unless (ss/people--source-buffer-p)
+ (unless (ss/crm--source-buffer-p)
(setq-local local-abbrev-table (copy-abbrev-table local-abbrev-table))
- (ss/people--clear-installed-abbrevs)
- (dolist (entry (ss/people-entries))
- (let* ((name (ss/people--entry-name entry))
- (abbrev (ss/people--entry-abbrev entry))
+ (ss/crm--clear-installed-abbrevs)
+ (dolist (entry (ss/crm-entries))
+ (let* ((name (ss/crm--entry-name entry))
+ (abbrev (ss/crm--entry-abbrev entry))
(abbrev-name
(if (or (null abbrev) (string-empty-p abbrev))
- (ss/people-default-abbrev name)
+ (ss/crm-default-abbrev name)
abbrev)))
(define-abbrev local-abbrev-table abbrev-name name)
(when-let ((abbrev-symbol (abbrev-symbol abbrev-name local-abbrev-table)))
- (abbrev-put abbrev-symbol :ss/people t))))))
+ (abbrev-put abbrev-symbol :ss/crm t))))))
- (defun ss/people-refresh-buffers ()
+ (defun ss/crm-refresh-buffers ()
"Refresh people abbrevs in every prose buffer."
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (and (bound-and-true-p abbrev-mode)
(derived-mode-p 'text-mode 'org-mode))
- (ss/people-install-abbrevs)))))
+ (ss/crm-install-abbrevs)))))
- (defun ss/people-capf ()
+ (defun ss/crm-capf ()
"Return canonical people completions at a word boundary."
(let ((end (point)))
(save-excursion
@@ -726,28 +736,28 @@ short and phrase-like so summaries and completion annotations stay readable.
;; Verify this in real Org/text writing buffers, not just by inspection.
(let ((annotation
(lambda (candidate)
- (when-let ((entry (ss/people--entry-by-name candidate)))
- (concat " " (ss/people--summary entry)))))
+ (when-let ((entry (ss/crm--entry-by-name candidate)))
+ (concat " " (ss/crm--summary entry)))))
(docsig
(lambda (candidate)
- (when-let ((entry (ss/people--entry-by-name candidate)))
- (ss/people--summary entry)))))
- (list beg end #'ss/people--completion-table
+ (when-let ((entry (ss/crm--entry-by-name candidate)))
+ (ss/crm--summary entry)))))
+ (list beg end #'ss/crm--completion-table
:exclusive 'no
:annotation-function annotation
:company-docsig docsig)))))))
(defun ss/enable-people-capf ()
- "Add `ss/people-capf' once in prose buffers."
- (unless (or (ss/people--source-buffer-p)
- (memq #'ss/people-capf completion-at-point-functions))
- (add-hook 'completion-at-point-functions #'ss/people-capf nil t)))
+ "Add `ss/crm-capf' once in prose buffers."
+ (unless (or (ss/crm--source-buffer-p)
+ (memq #'ss/crm-capf completion-at-point-functions))
+ (add-hook 'completion-at-point-functions #'ss/crm-capf nil t)))
- (defun ss/people--maybe-overview-buffer ()
+ (defun ss/crm--maybe-overview-buffer ()
"Reset the people CRM buffer to overview when visiting it directly."
(when (and buffer-file-name
(string= (file-truename buffer-file-name)
- (file-truename ss/people-file)))
+ (file-truename ss/crm-file)))
(widen)
(goto-char (point-min))
(org-overview)
@@ -755,9 +765,9 @@ short and phrase-like so summaries and completion annotations stay readable.
(dolist (hook '(text-mode-hook org-mode-hook))
(add-hook hook #'ss/enable-people-capf)
- (add-hook hook #'ss/people-install-abbrevs))
- (add-hook 'find-file-hook #'ss/people--track-buffer)
- (add-hook 'find-file-hook #'ss/people--maybe-overview-buffer)
+ (add-hook hook #'ss/crm-install-abbrevs))
+ (add-hook 'find-file-hook #'ss/crm--track-buffer)
+ (add-hook 'find-file-hook #'ss/crm--maybe-overview-buffer)
#+end_src
* Notes workflow
@@ -984,17 +994,18 @@ When CREATE is non-nil, create the datetree entry when missing."
:bind (("C-c a" . ss/open-agenda)
("C-c c" . org-capture)
("C-c n M" . ss/open-moc)
- ("C-c n E" . ss/people-report-by-engagement)
- ("C-c n f" . ss/people-find)
- ("C-c n i" . ss/people-insert-name)
- ("C-c n I" . ss/people-insert-summary)
- ("C-c n L" . ss/people-report-by-location)
+ ("C-c n E" . ss/crm-report-by-engagement)
+ ("C-c n f" . ss/crm-find)
+ ("C-c n i" . ss/crm-insert-name)
+ ("C-c n I" . ss/crm-insert-summary)
+ ("C-c n L" . ss/crm-report-by-location)
("C-c n d" . ss/open-journal)
- ("C-c n o" . ss/people-overview)
- ("C-c n O" . ss/people-report-by-role)
- ("C-c n p" . ss/people-open)
- ("C-c n P" . ss/people-add)
- ("C-c n R" . ss/people-report-by-relationship))
+ ("C-c n o" . ss/crm-overview)
+ ("C-c n O" . ss/crm-report-by-role)
+ ("C-c n p" . ss/crm-open)
+ ("C-c n P" . ss/crm-add)
+ ("C-c n R" . ss/crm-report-by-manager)
+ ("C-c n T" . ss/crm-report-by-team))
:config
(setq org-directory ss/org-directory
org-hide-emphasis-markers t
@@ -1015,7 +1026,7 @@ own Org integration so note identity, metadata, and directories stay under
Denote's control rather than custom code. The convenience templates keep the
familiar entry points, but only project capture injects a structural keyword by
default. The people CRM lives outside =org-capture=: adding a person uses the
-dedicated =ss/people-add= command so =~/org/areas/people/people.org= stays a
+dedicated =ss/crm-add= command so =~/org/areas/people/people.org= stays a
compact, structured card file rather than turning into another capture target.
#+begin_src emacs-lisp