{"id":46203517,"url":"https://github.com/skx/org-people","last_synced_at":"2026-03-14T21:02:36.455Z","repository":{"id":340103218,"uuid":"1164520813","full_name":"skx/org-people","owner":"skx","description":"Contact management for org-mode","archived":false,"fork":false,"pushed_at":"2026-03-11T17:17:09.000Z","size":139,"stargazers_count":16,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-11T20:12:56.564Z","etag":null,"topics":["contacts","emacs-lisp","org-mode"],"latest_commit_sha":null,"homepage":"","language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-23T07:14:26.000Z","updated_at":"2026-03-11T17:17:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/skx/org-people","commit_stats":null,"previous_names":["skx/org-people"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/skx/org-people","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Forg-people","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Forg-people/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Forg-people/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Forg-people/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skx","download_url":"https://codeload.github.com/skx/org-people/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Forg-people/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30517673,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-14T19:51:21.629Z","status":"ssl_error","status_checked_at":"2026-03-14T19:51:12.959Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["contacts","emacs-lisp","org-mode"],"created_at":"2026-03-03T05:44:04.825Z","updated_at":"2026-03-14T21:02:36.450Z","avatar_url":"https://github.com/skx.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# org-people\n\nThis package allows easy contact-management via the native org-mode facilities.\n\nContacts are found from any of your org-agenda files, providing they have a tag\nof \"contacts\", for example you can see the following simple hierarchy below, or\nlook at `org-people-test.org` file within this repository which is used by the\ntest-cases:\n\n```\n* People\n  ** Alice                           :family:contact:\n  :PROPERTIES\n  :ADDRESS: 32 Something Street\n  :EMAIL: alice@example.com\n  :PHONE: +123 456 789\n  :CHILDREN: Mallory\n  :NICKNAME: Allu\n  :END\n ** Bob                           :colleague:contact:\n  :PROPERTIES\n  :ADDRESS: 32 Something Lane\n  :EMAIL: bob@example.com\n  :PHONE: +123 456 987\n  :END\n```\n\nIt is assumed that you'll have \"`ADDRESS`\", \"`EMAIL`\", \"`PHONE`\", and other similar properties, however there are no mandatory properties - you add what you prefer.  Any contacts will be recognized providing they contain at least one property, along with the necessary \"contact\" tag to identify them.  (The headline itself is used as the contact name.)\n\nAs mentioned there are no specific properties we mandate, however there are a couple of possible properties\nwhich get special handling:\n\n* If `:NICKNAME` is present it will be offered a completion-target.\n* If `:WEBSITE` is present it will be used when contact-links are exported to HTML.\n\nIt should probably be noted that the summary table, and the CSV/vCARD exportors, will default to using the `ADDRESS`, `EMAIL`, and `PHONE` properties as their main input - but this is configurable.\n\n\n\n## Installation / Configuration Example\n\nThe legacy way to install would be to clone this repository and ensure the directory is available upon your load-path, or copy your local lisp tree.\n\nThe package is now available upon MELPA, if you wish to install it from there.\n\nSuggested usage if you're using the traditional approach:\n\n```\n(require 'org-people)\n\n; insert a contact \"thing\" at the current point.\n(global-set-key (kbd \"C-c p\") 'org-people-insert)\n\n; Show a table of all known contacts.  The table is sortable, filterable, \u0026 etc.\n(global-set-key (kbd \"C-c P\") 'org-people-summary)\n```\n\nIf you prefer `use-package` then this works:\n\n```\n(use-package org-people\n  :after org\n  :bind\n    ((\"C-c p\" . org-people-insert)\n     (\"C-c P\" . org-people-summary)))\n```\n\n\n\n## Adding Entries\n\nIf you use `org-capture` you may use the following template to add a new entry:\n\n```\n(setq org-capture-templates\n  (append org-capture-templates\n       '((\"p\" \"People\" entry (file+headline \"~/Private/Org/PEOPLE.org\" \"People\")\n          \"* %^{Name}\\n:PROPERTIES:\\n:EMAIL: %^{Email}\\n:PHONE: %^{Phone}\\n:END:\\n%?\"\n         :empty-lines 1))))\n```\n\n\n\n## API / Functions\n\nThese are the main user-focused functions within the package to work with contacts:\n\n* `org-people-insert`\n  * Insert contact-data, via interactive prompts (with `completing-read`).\n* `org-people-summary`\n  * Parse all known contacts and pop to a buffer containing a summary of their details.\n  * This uses `tabulated-list-mode` and is documented further below.\n    * But in brief you can mark, filter, and adjust columns and their contents pretty flexibly.\n* `org-people-tags-to-table`\n  * Designed to create auto-updating tables inside `org-mode` documents.\n* `org-people-person-to-table`\n  * Designed to create auto-updating tables inside `org-mode` documents.\n\n\n\n## Configuration\n\nNo special configuration is required, although if you wish to use a different tag to identify the contacts you may specify that via `org-people-search-tag`.  Similarly many of the default operations may be updated via the appropriate configuration values, and these are documented within the package itself.\n\nIf you wished to limit parsing to only named file(s) you could set `org-people-search-type` to be a list containing the path(s) to process.  Otherwise all agenda-files will be read.\n\nThe configuration of the columns, within the `org-people-summary` view, has been expanded in recent releases.  Rather than only allowing a name/width to be specified you may now add optional configuration to override the column names, the width and even the function which populates the value.  This allows you to create dynamic values.  For example see the last two items here:\n\n```\n(setq org-people-summary-properties\n      '((:NAME  :width 25)\n        (:EMAIL :width 30)\n        :TAGS\n        (:PHONE :width 15 :title \"Digits\")\n        (:MEOW  :getter (lambda (plist) (concat (plist-get plist :COUNTRY) \" [\" (plist-get plist :FLAG) \"]\" )))))\n```\n\n\n\n## Limitations\n\nIf you have two contacts with the same name one will overwrite the other.  This is annoying, but not a bug.\n\n\n\n## org-mode links\n\nThis package defines a custom `org-mode` link-type for the `org-people:` protocol, which will jump to the definition of the given contact when clicked/followed.   You can add such a link via `C-c C-l`, as expected, and you'll find TAB completion works for populating the protocol-name, and the person's name.  The description will default to their name too.\n\nA link might look like this for example:\n\n    * This is a headline\n    [[org-people:Steve Kemp]] wrote this package.\n\nWhen exported to HTML the contact name will be converted to a hyperlink pointing to the user's `:WEBSITE` property, if present, otherwise it will be left unchanged.\n\nThe utility function `org-people-add-descriptions` will update all `org-people:` links within the current document to ensure the description matches the link target, which makes the display more readable.\n\n\n\n## Completion Functions\n\nThere are a pair of functions provided for the complete-at-point functionality:\n\n* `org-people-capf`\n  * Complete contact names.  (e.g. `Alice Smith`)\n* `org-people-email-capf`\n  * Complete contact emails. (e.g. `\"Mallory Jones\" \u003cfoo@bar.com\u003e\"`).\n\nThey might be enabled like so using the standard `capf`:\n\n    (add-hook 'message-mode-hook\n          (lambda ()\n            (add-hook 'completion-at-point-functions\n                      #'org-people-email-capf\n                      nil t)))\n\n    (add-hook 'text-mode-hook\n          (lambda ()\n            (add-hook 'completion-at-point-functions\n                      #'org-people-capf\n                      nil t)))\n\n\n\n## Dynamic `org-mode` tables\n\nIf you tag the contacts with more than just the `contacts` value then you may use those tags to build simple tables of matching entries.  For example the following can auto-update:\n\n    #+NAME: get-family-contacts\n    #+BEGIN_SRC elisp :results value table\n    (org-people-tags-to-table \"family\")\n    #+END_SRC\n\nIf you prefer to include different columns in your generated table you can specify them directly:\n\n    #+NAME: get-family-contacts\n    #+BEGIN_SRC elisp :results value table\n    (org-people-tags-to-table \"family\" '(:LINK :PHONE))\n    #+END_SRC\n\nYou may also create a table including all known data about a single named individual:\n\n    #+NAME: steve-kemp\n    #+BEGIN_SRC elisp :results value table :colnames '(\"Field\" \"Value\")\n    (org-people-person-to-table \"Steve Kemp\")\n    #+END_SRC\n\n    #+RESULTS: steve-kemp\n    | Field       | Value                                   |\n    |-------------+-----------------------------------------|\n    | Address     | Helsinki, Finland                       |\n    | Category    | PEOPLE                                  |\n    | Country     | Finland                                 |\n    | Email       | steve@steve.fi                          |\n    | Name        | Steve Kemp                              |\n    | Phone       | +358123456789                           |\n    | Tags        | (me)                                    |\n\nIn this case properties listed in `org-people-ignored-properties` will be ignored and excluded from the generated table.\n\n\n\n## Summary View\n\nThe `org-people-summary` function shows a table of all your known contacts.\n\nYou can customize the displayed fields, or their order, by modifying the `org-people-summary-properties` variable, as noted earlier in this documentation.  The default setting is to show the name, email, phone-number and tags associated with each entry.\n\n\u003e **NOTE**: If a given column would be 100% empty (i.e. no known contacts have a property with that name) then the column will be removed from display.\n\nSome keybindings are setup in the `org-people-summary-mode-map`, everything will be visible if you press `?`.\n\nIn brief though:\n\n* `RET` jump to the definition of the contact.\n* `c` Copy the field under the point.\n* `f` Filter the view, by property.\n  * Even properties which are not visible can be used.\n  * e.g. \":ADDRESS\" \"Finland\" will show only Finnish residents.\n* `R` reset the state of columns.\n* `s` Initiate a search forward, via `isearch-forward`.\n* `t` Toggle visibility of a named column.\n* `T` Hide the current column.\n* `v` - Export the current contact, or all marked contacts, to vCARD format.\n* `C` - Export the current contact, or all marked contacts, to CSV.\n\nPeople may be marked with `m` (the current row), or `M` (all rows), and unmarked with `u` (current row), or `U` (all rows).  As of today only the exporting functions (vCARD and CSV) use the marked-rows, but you can add extensions and perhaps additional functionality will be added in the future.\n\n\n### Coding Summary Additions\n\nThe `org-people-summary-marked-or-current` function to allow you to easily define your own custom routines that can operate either on:\n\n* The contact on the row containing the point.\n* The arbitrary number of marked people.\n  * Marks being set with `m`/`M` and cleared with `u`/`U`.\n\nThis is a brief example:\n\n    (defun show-marked-users ()\n      \"Proof of concept to show the names of marked people.\n    If no people are marked show the name of the person in the row\n    containing the point.\"\n      (interactive)\n      (org-people-summary-marked-or-current\n         (lambda (name) (message \"called with person: %s\" name))))\n\n    (define-key org-people-summary-mode-map (kbd \"x\") #'show-marked-users)\n\n\n\n## Testing\n\nYou can run `make test` via the supplied [Makefile](Makefile) to run the tests in a batch-mode, otherwise load the file [org-people-test.el](org-people-test.el) and run `M-x eval buffer`, you should see the test results in a new buffer.\n\nIf any tests fail that's a bug.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskx%2Forg-people","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskx%2Forg-people","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskx%2Forg-people/lists"}