+33
stack/sortal/CLAUDE.md
+33
stack/sortal/CLAUDE.md
···
···+1. **XDG Storage**: All contact data is stored in XDG-compliant locations using the xdge library+2. **JSON Format**: Contact metadata is serialized using jsont for type-safe JSON encoding/decoding+3. **Nested Modules**: The Contact module is nested within the main Sortal module following the canonical `type t` pattern+4. **One File Per Contact**: Each contact is stored as a separate JSON file named "{handle}.json"+The Contact type includes the following metadata fields (all optional except handle and names):
+162
stack/sortal/README.md
+162
stack/sortal/README.md
···
···+Sortal is an OCaml library that provides a system for mapping usernames to various metadata including URLs, emails, ORCID identifiers, and social media handles. It uses the XDG Base Directory Specification for storage locations and jsont for JSON encoding/decoding.+- **Rich metadata**: Support for multiple names, email, social media handles (GitHub, Twitter, Bluesky, Mastodon), ORCID, URLs, and Atom feeds+The contact metadata structure is inspired by the Contact module from [Bushel](https://github.com/avsm/bushel), adapted to use JSON instead of YAML and stored in XDG-compliant locations.
+4
stack/sortal/bin/dune
+4
stack/sortal/bin/dune
+43
stack/sortal/bin/sortal_cli.ml
+43
stack/sortal/bin/sortal_cli.ml
···
···+let show_cmd_term = Term.(const (fun handle -> Sortal.Cmd.show_cmd handle) $ Sortal.Cmd.handle_arg) in+let search_cmd_term = Term.(const (fun query -> Sortal.Cmd.search_cmd query) $ Sortal.Cmd.query_arg) in
+16
stack/sortal/dune-project
+16
stack/sortal/dune-project
···
···+"Sortal provides a system for mapping usernames to various metadata including URLs, emails, ORCID identifiers, and social media handles. It uses XDG Base Directory Specification for storage locations and jsont for JSON encoding/decoding.")
+4
stack/sortal/lib/dune
+4
stack/sortal/lib/dune
+311
stack/sortal/lib/sortal.ml
+311
stack/sortal/lib/sortal.ml
···
···+let with_feeds = List.filter (fun c -> Contact.atom_feeds c <> None) contacts |> List.length in+Logs.app (fun m -> m " With email: %d (%.1f%%)" with_email (float_of_int with_email /. float_of_int total *. 100.));+Logs.app (fun m -> m " With GitHub: %d (%.1f%%)" with_github (float_of_int with_github /. float_of_int total *. 100.));+Logs.app (fun m -> m " With ORCID: %d (%.1f%%)" with_orcid (float_of_int with_orcid /. float_of_int total *. 100.));+Logs.app (fun m -> m " With URL: %d (%.1f%%)" with_url (float_of_int with_url /. float_of_int total *. 100.));+Logs.app (fun m -> m " With Atom feeds: %d (%.1f%%)" with_feeds (float_of_int with_feeds /. float_of_int total *. 100.));
+323
stack/sortal/lib/sortal.mli
+323
stack/sortal/lib/sortal.mli
···
···
+119
stack/sortal/scripts/import_yaml_contacts.py
+119
stack/sortal/scripts/import_yaml_contacts.py
···
···
+3
stack/sortal/test/dune
+3
stack/sortal/test/dune
+167
stack/sortal/test/test_sortal.ml
+167
stack/sortal/test/test_sortal.ml
···
···