OCaml HTML5 parser/serialiser based on Python's JustHTML

Add Zulip standard [api] section config support and daily changelog

ocaml-zulip: Support Zulip's native config format which uses [api] section
with "key" field instead of [bot] section with "api_key". Config loading
now tries [bot], then [api], then bare format.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+525 -4
+41
.changes/monopam-daily.json
··· 1 + { 2 + "repository": "monopam", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "monopam push now auto-creates missing checkout directories", 7 + "changes": [ 8 + "Added auto-clone on push: new packages no longer need manual pull first" 9 + ], 10 + "commit_range": { 11 + "from": "937635c", 12 + "to": "d0fb2e4", 13 + "count": 2 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/monopam.git" 19 + }, 20 + { 21 + "date": "2026-01-17", 22 + "summary": "New 'monopam changes' command uses AI to generate changelogs from git history", 23 + "changes": [ 24 + "Added 'monopam changes' command for AI-powered changelog generation", 25 + "Added Changes module with jsont codecs for changelog serialization", 26 + "Added Git.log function with date filtering for commit history", 27 + "Added aggregated CHANGES.md generation at monorepo root", 28 + "Added README files for jsonwt, owntracks, monopam, and srcsetter" 29 + ], 30 + "commit_range": { 31 + "from": "0231c1d", 32 + "to": "77840a3", 33 + "count": 2 34 + }, 35 + "contributors": [ 36 + "Anil Madhavapeddy" 37 + ], 38 + "repo_url": "https://tangled.org/@anil.recoil.org/monopam.git" 39 + } 40 + ] 41 + }
+21
.changes/ocaml-atp-daily.json
··· 1 + { 2 + "repository": "ocaml-atp", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Regenerated AT Protocol lexicon bindings from upstream schemas", 7 + "changes": [ 8 + "Updated atproto, bsky, standard-site, and tangled lexicon bindings" 9 + ], 10 + "commit_range": { 11 + "from": "c8ccec6", 12 + "to": "c8ccec6", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-atp.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-bushel-daily.json
··· 1 + { 2 + "repository": "ocaml-bushel", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Initial addition of ocaml-bushel library to the monorepo", 7 + "changes": [ 8 + "Added ocaml-bushel library as new subtree" 9 + ], 10 + "commit_range": { 11 + "from": "96663ea", 12 + "to": "96663ea", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-bushel.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-conpool-daily.json
··· 1 + { 2 + "repository": "ocaml-conpool", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Improved is_healthy function readability with reduced nesting", 7 + "changes": [ 8 + "Refactored is_healthy to reduce nesting and improve clarity" 9 + ], 10 + "commit_range": { 11 + "from": "77840a3", 12 + "to": "77840a3", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-conpool.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-frontmatter-daily.json
··· 1 + { 2 + "repository": "ocaml-frontmatter", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Initial import of ocaml-frontmatter library into the monorepo", 7 + "changes": [ 8 + "Added ocaml-frontmatter library for parsing frontmatter in documents" 9 + ], 10 + "commit_range": { 11 + "from": "088ec34", 12 + "to": "088ec34", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-frontmatter.git" 19 + } 20 + ] 21 + }
+45
.changes/ocaml-imap-daily.json
··· 1 + { 2 + "repository": "ocaml-imap", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Major IMAP RFC compliance update with comprehensive extension support and new mail-flag interoperability library", 7 + "changes": [ 8 + "Added mail-flag library for IMAP/JMAP interop (keywords, mailbox attrs, colors)", 9 + "Added SORT/THREAD extensions (RFC 5256) with sort(), uid_sort() functions", 10 + "Added CONDSTORE (RFC 7162) with changedsince/unchangedsince modifiers", 11 + "Fixed SEARCH response parsing and APPEND literal synchronization bugs", 12 + "Added QUOTA (RFC 9208), ESEARCH (RFC 4731), LIST-EXTENDED (RFC 5258)" 13 + ], 14 + "commit_range": { 15 + "from": "db82956", 16 + "to": "4b0e3e4", 17 + "count": 4 18 + }, 19 + "contributors": [ 20 + "Anil Madhavapeddy" 21 + ], 22 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-imap.git" 23 + }, 24 + { 25 + "date": "2026-01-17", 26 + "summary": "Major restructuring of IMAP library with critical bug fixes for UID parsing and response handling.", 27 + "changes": [ 28 + "Reorganized library into lib/imap/ (client) and lib/imapd/ (server) modules", 29 + "Fixed RECENT response parsing that was incorrectly overwriting EXISTS count", 30 + "Changed UIDs and UIDVALIDITY from int32 to int64 to handle large values (RFC 9051)", 31 + "Added Logs library integration with imap.client source for debugging", 32 + "Added AUTHENTICATE PLAIN support via --plain flag in client" 33 + ], 34 + "commit_range": { 35 + "from": "15fe09d", 36 + "to": "5eb9e22", 37 + "count": 6 38 + }, 39 + "contributors": [ 40 + "Anil Madhavapeddy" 41 + ], 42 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-imap.git" 43 + } 44 + ] 45 + }
+25
.changes/ocaml-jmap-daily.json
··· 1 + { 2 + "repository": "ocaml-jmap", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Added mail-flag library for unified email flag handling across IMAP and JMAP protocols", 7 + "changes": [ 8 + "Added Keyword module with RFC 8621 message keywords (Seen, Flagged, Draft, etc.)", 9 + "Added Mailbox_attr module for RFC 6154/5258 mailbox roles (Inbox, Sent, Trash)", 10 + "Added Flag_color module for Apple Mail 7-color flag encoding", 11 + "Added Imap_wire and Jmap_wire adapters for protocol-specific serialization", 12 + "Integrated mail-flag types into ocaml-jmap Keyword and Role modules" 13 + ], 14 + "commit_range": { 15 + "from": "db82956", 16 + "to": "db82956", 17 + "count": 1 18 + }, 19 + "contributors": [ 20 + "Anil Madhavapeddy" 21 + ], 22 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-jmap.git" 23 + } 24 + ] 25 + }
+21
.changes/ocaml-jsonwt-daily.json
··· 1 + { 2 + "repository": "ocaml-jsonwt", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Added README documentation for the jsonwt library", 7 + "changes": [ 8 + "Added README file documenting the library" 9 + ], 10 + "commit_range": { 11 + "from": "77840a3", 12 + "to": "77840a3", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-jsonwt.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-langdetect-daily.json
··· 1 + { 2 + "repository": "ocaml-langdetect", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Fixed documentation to correctly state 49 supported languages instead of 47", 7 + "changes": [ 8 + "Fixed language count in dune-project: now correctly reports 49 languages" 9 + ], 10 + "commit_range": { 11 + "from": "77840a3", 12 + "to": "77840a3", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-langdetect.git" 19 + } 20 + ] 21 + }
+25
.changes/ocaml-matrix-daily.json
··· 1 + { 2 + "repository": "ocaml-matrix", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Added pretty-printers, accessors, and interface files to improve API usability and documentation.", 7 + "changes": [ 8 + "Added pp functions to Matrix_id modules (User_id, Room_id, etc.)", 9 + "Added make constructors and accessors to event content types", 10 + "Added pp functions to Msgtype, Event_type, and message content types", 11 + "Added 13 missing .mli files for matrix_client modules", 12 + "Fixed all odoc documentation warnings for clean doc builds" 13 + ], 14 + "commit_range": { 15 + "from": "39c4ad0", 16 + "to": "209ecd3", 17 + "count": 3 18 + }, 19 + "contributors": [ 20 + "Anil Madhavapeddy" 21 + ], 22 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-matrix.git" 23 + } 24 + ] 25 + }
+21
.changes/ocaml-owntracks-daily.json
··· 1 + { 2 + "repository": "ocaml-owntracks", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Added README documentation for the library", 7 + "changes": [ 8 + "Added README documentation file" 9 + ], 10 + "commit_range": { 11 + "from": "7f68d5b", 12 + "to": "77840a3", 13 + "count": 2 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-owntracks.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-punycode-daily.json
··· 1 + { 2 + "repository": "ocaml-punycode", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Added documentation about unimplemented IDNA features", 7 + "changes": [ 8 + "Added README documenting IDNA features not yet implemented" 9 + ], 10 + "commit_range": { 11 + "from": "77840a3", 12 + "to": "77840a3", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-punycode.git" 19 + } 20 + ] 21 + }
+21
.changes/ocaml-requests-daily.json
··· 1 + { 2 + "repository": "ocaml-requests", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Fixed build error caused by missing Uri module re-export in implementation", 7 + "changes": [ 8 + "Fixed missing `Uri` module re-export that caused build errors" 9 + ], 10 + "commit_range": { 11 + "from": "8977e4b", 12 + "to": "ad8d14b", 13 + "count": 2 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-requests.git" 19 + } 20 + ] 21 + }
+22
.changes/ocaml-yamlt-daily.json
··· 1 + { 2 + "repository": "ocaml-yamlt", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Added convenience functions for decoding YAML strings and pre-parsed values", 7 + "changes": [ 8 + "Added decode_string for direct decoding from YAML strings", 9 + "Added decode_value and decode_value' for decoding from Yamlrw.value" 10 + ], 11 + "commit_range": { 12 + "from": "2887a07", 13 + "to": "2887a07", 14 + "count": 1 15 + }, 16 + "contributors": [ 17 + "Anil Madhavapeddy" 18 + ], 19 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-yamlt.git" 20 + } 21 + ] 22 + }
+38
.changes/ocaml-zulip-daily.json
··· 1 + { 2 + "repository": "ocaml-zulip", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Added xdg_app parameter for custom XDG config paths in zulip-bot", 7 + "changes": [ 8 + "Added xdg_app parameter to Config module for custom XDG directory paths", 9 + "Renamed config file from \"config\" to \"zulip.config\" for clarity" 10 + ], 11 + "commit_range": { 12 + "from": "39b79ed", 13 + "to": "39b79ed", 14 + "count": 1 15 + }, 16 + "contributors": [ 17 + "Anil Madhavapeddy" 18 + ], 19 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-zulip.git" 20 + }, 21 + { 22 + "date": "2026-01-17", 23 + "summary": "Improved documentation for retention types in channels.mli", 24 + "changes": [ 25 + "Improved retention type documentation in channels.mli" 26 + ], 27 + "commit_range": { 28 + "from": "77840a3", 29 + "to": "77840a3", 30 + "count": 1 31 + }, 32 + "contributors": [ 33 + "Anil Madhavapeddy" 34 + ], 35 + "repo_url": "https://tangled.org/@anil.recoil.org/ocaml-zulip.git" 36 + } 37 + ] 38 + }
+23
.changes/poe-daily.json
··· 1 + { 2 + "repository": "poe", 3 + "entries": [ 4 + { 5 + "date": "2026-01-18", 6 + "summary": "Configuration now stored under unified XDG path ~/.config/poe/", 7 + "changes": [ 8 + "Added xdg_app parameter to zulip-bot Config for custom XDG paths", 9 + "Moved all config files to ~/.config/poe/ directory", 10 + "Renamed zulip config file from \"config\" to \"zulip.config\"" 11 + ], 12 + "commit_range": { 13 + "from": "39b79ed", 14 + "to": "ed3535e", 15 + "count": 2 16 + }, 17 + "contributors": [ 18 + "Anil Madhavapeddy" 19 + ], 20 + "repo_url": "https://tangled.org/@anil.recoil.org/poe.git" 21 + } 22 + ] 23 + }
+21
.changes/srcsetter-daily.json
··· 1 + { 2 + "repository": "srcsetter", 3 + "entries": [ 4 + { 5 + "date": "2026-01-17", 6 + "summary": "Added README documentation for the library", 7 + "changes": [ 8 + "Added README file documenting library purpose and usage" 9 + ], 10 + "commit_range": { 11 + "from": "77840a3", 12 + "to": "77840a3", 13 + "count": 1 14 + }, 15 + "contributors": [ 16 + "Anil Madhavapeddy" 17 + ], 18 + "repo_url": "https://tangled.org/@anil.recoil.org/srcsetter.git" 19 + } 20 + ] 21 + }
+65
DAILY-CHANGES.md
··· 1 + # Daily Changelog 2 + 3 + ## 2026-01-18 4 + 5 + ### New Libraries 6 + 7 + - **[ocaml-frontmatter](https://tangled.org/@anil.recoil.org/ocaml-frontmatter.git)**: OCaml library for parsing YAML/TOML frontmatter from Markdown and other document formats — commonly used for static site generators and content management systems. — *Anil Madhavapeddy* 8 + 9 + - **[ocaml-bushel](https://tangled.org/@anil.recoil.org/ocaml-bushel.git)**: OCaml implementation of the Bushel document format — a structured approach to organizing and managing document collections. — *Anil Madhavapeddy* 10 + 11 + ### Email Protocol Libraries 12 + 13 + - **[ocaml-imap](https://tangled.org/@anil.recoil.org/ocaml-imap.git)**: Major RFC compliance update with comprehensive extension support — added SORT/THREAD (RFC 5256), CONDSTORE (RFC 7162), QUOTA (RFC 9208), ESEARCH (RFC 4731), and LIST-EXTENDED (RFC 5258). Fixed SEARCH response parsing and APPEND literal synchronization bugs. — *Mark Elvers* and *Anil Madhavapeddy* 14 + 15 + - **[ocaml-jmap](https://tangled.org/@anil.recoil.org/ocaml-jmap.git)**: Added mail-flag library for unified email flag handling across IMAP and JMAP protocols — includes RFC 8621 keywords (Seen, Flagged, Draft), RFC 6154/5258 mailbox roles (Inbox, Sent, Trash), Apple Mail color flags, and protocol-specific serialization adapters. This library bridges [ocaml-imap](https://tangled.org/@anil.recoil.org/ocaml-imap.git) and [ocaml-jmap](https://tangled.org/@anil.recoil.org/ocaml-jmap.git) for cross-protocol email applications. — *Anil Madhavapeddy* 16 + 17 + ### Configuration & XDG Improvements 18 + 19 + - **[poe](https://tangled.org/@anil.recoil.org/poe.git)**: Unified all configuration under `~/.config/poe/` directory with renamed config files for clarity. — *Anil Madhavapeddy* 20 + 21 + - **[ocaml-zulip](https://tangled.org/@anil.recoil.org/ocaml-zulip.git)**: Added `xdg_app` parameter to Config module for custom XDG directory paths; renamed config file from "config" to "zulip.config". — *Anil Madhavapeddy* 22 + 23 + ### API Improvements 24 + 25 + - **[ocaml-matrix](https://tangled.org/@anil.recoil.org/ocaml-matrix.git)**: Added pretty-printers, accessors, and 13 missing `.mli` interface files — improves API usability with `pp` functions for Matrix_id modules, `make` constructors for event types, and clean odoc builds. — *Anil Madhavapeddy* 26 + 27 + - **[ocaml-yamlt](https://tangled.org/@anil.recoil.org/ocaml-yamlt.git)**: Added convenience functions `decode_string`, `decode_value`, and `decode_value'` for more flexible YAML decoding. — *Anil Madhavapeddy* 28 + 29 + ### Tooling 30 + 31 + - **[monopam](https://tangled.org/@anil.recoil.org/monopam.git)**: `monopam push` now auto-creates missing checkout directories — new packages no longer require manual pull first. — *Anil Madhavapeddy* 32 + 33 + - **[ocaml-atp](https://tangled.org/@anil.recoil.org/ocaml-atp.git)**: Regenerated AT Protocol lexicon bindings (atproto, bsky, standard-site, tangled) from upstream schemas. — *Anil Madhavapeddy* 34 + 35 + --- 36 + 37 + ## 2026-01-17 38 + 39 + ### Major Features 40 + 41 + - **[ocaml-imap](https://tangled.org/@anil.recoil.org/ocaml-imap.git)**: Major restructuring with critical bug fixes — reorganized into `lib/imap/` (client) and `lib/imapd/` (server), fixed RECENT response parsing that was overwriting EXISTS count, changed UIDs/UIDVALIDITY from int32 to int64 for RFC 9051 compliance, added Logs integration and AUTHENTICATE PLAIN support. — *Anil Madhavapeddy* 42 + 43 + - **[monopam](https://tangled.org/@anil.recoil.org/monopam.git)**: New `monopam changes` command uses AI to generate changelogs from git history — added Changes module with jsont codecs, Git.log with date filtering, and aggregated CHANGES.md generation at monorepo root. — *Anil Madhavapeddy* 44 + 45 + ### Bug Fixes 46 + 47 + - **[ocaml-requests](https://tangled.org/@anil.recoil.org/ocaml-requests.git)**: Fixed missing `Uri` module re-export that caused build errors. — *Anil Madhavapeddy* 48 + 49 + ### Code Quality 50 + 51 + - **[ocaml-conpool](https://tangled.org/@anil.recoil.org/ocaml-conpool.git)**: Refactored `is_healthy` function to reduce nesting and improve readability. — *Anil Madhavapeddy* 52 + 53 + ### Documentation 54 + 55 + - **[ocaml-zulip](https://tangled.org/@anil.recoil.org/ocaml-zulip.git)**: Improved retention type documentation in channels.mli. — *Anil Madhavapeddy* 56 + 57 + - **[ocaml-langdetect](https://tangled.org/@anil.recoil.org/ocaml-langdetect.git)**: Fixed language count in dune-project to correctly report 49 supported languages. — *Anil Madhavapeddy* 58 + 59 + - **[ocaml-punycode](https://tangled.org/@anil.recoil.org/ocaml-punycode.git)**: Added README documenting IDNA features not yet implemented. — *Anil Madhavapeddy* 60 + 61 + - **[ocaml-jsonwt](https://tangled.org/@anil.recoil.org/ocaml-jsonwt.git)**: Added README documentation. — *Anil Madhavapeddy* 62 + 63 + - **[ocaml-owntracks](https://tangled.org/@anil.recoil.org/ocaml-owntracks.git)**: Added README documentation. — *Anil Madhavapeddy* 64 + 65 + - **[srcsetter](https://tangled.org/@anil.recoil.org/srcsetter.git)**: Added README documentation. — *Anil Madhavapeddy*
+31 -4
ocaml-zulip/lib/zulip_bot/config.ml
··· 54 54 |> opt_mem "usage" Init.string ~enc:(fun c -> c.ini_usage) 55 55 |> skip_unknown |> finish) 56 56 57 + (** Codec for Zulip's standard [api] section format (uses "key" not "api_key") *) 58 + let zulip_api_section_codec = 59 + Init.Section.( 60 + obj (fun site email key -> 61 + { 62 + ini_site = site; 63 + ini_email = email; 64 + ini_api_key = key; 65 + ini_description = None; 66 + ini_usage = None; 67 + }) 68 + |> mem "site" Init.string ~enc:(fun c -> c.ini_site) 69 + |> mem "email" Init.string ~enc:(fun c -> c.ini_email) 70 + |> mem "key" Init.string ~enc:(fun c -> c.ini_api_key) 71 + |> skip_unknown |> finish) 72 + 57 73 (** Document codec that accepts a [bot] section or bare options at top level *) 58 74 let ini_doc_codec = 59 75 Init.Document.( ··· 61 77 |> section "bot" ini_section_codec ~enc:Fun.id 62 78 |> skip_unknown |> finish) 63 79 80 + (** Document codec for Zulip's standard [api] section format *) 81 + let zulip_api_doc_codec = 82 + Init.Document.( 83 + obj (fun api -> api) 84 + |> section "api" zulip_api_section_codec ~enc:Fun.id 85 + |> skip_unknown |> finish) 86 + 64 87 (** Codec for configs without section headers (bare key=value pairs) *) 65 88 let bare_section_codec = 66 89 Init.Document.( ··· 74 97 let xdg = Xdge.create fs app in 75 98 let config_file = Eio.Path.(Xdge.config_dir xdg / "zulip.config") in 76 99 Log.debug (fun m -> m "Looking for config at: %a" Eio.Path.pp config_file); 77 - (* Try parsing with [bot] section first, fall back to bare config *) 100 + (* Try parsing with [bot] section, then Zulip [api] section, then bare config *) 78 101 let ini_config = 79 102 match Init_eio.decode_path ini_doc_codec config_file with 80 103 | Ok c -> c 81 104 | Error _ -> ( 82 - (* Try bare config format (no section headers) *) 83 - match Init_eio.decode_path bare_section_codec config_file with 105 + (* Try Zulip's standard [api] section format *) 106 + match Init_eio.decode_path zulip_api_doc_codec config_file with 84 107 | Ok c -> c 85 - | Error e -> raise (Init_eio.err e)) 108 + | Error _ -> ( 109 + (* Try bare config format (no section headers) *) 110 + match Init_eio.decode_path bare_section_codec config_file with 111 + | Ok c -> c 112 + | Error e -> raise (Init_eio.err e))) 86 113 in 87 114 { 88 115 name;