+689
stack/zulip/CLAUDE.md
+689
stack/zulip/CLAUDE.md
···+The library follows OCaml best practices with abstract types (`type t`) per module, comprehensive constructors/accessors, and proper pretty printers. Each core concept gets its own module with a clean interface.+val edit : Client.t -> message_id:int -> ?content:string -> ?topic:string -> unit -> (unit, Error.t) result+val get_events : t -> Client.t -> ?last_event_id:int -> unit -> (Event.t list, Error.t) result+- https://github.com/zulip/python-zulip-api/blob/main/zulip_botserver/zulip_botserver/server.py+Bot handlers require direct access to the EIO environment for legitimate I/O operations beyond HTTP requests to Zulip:+Based on analysis, the current EIO environment plumbing is **essential** and should be cleaned up:+val handle_message : t -> #Eio.Env.t -> Message_context.t -> (Response.t, Zulip.Error.t) result+This design maintains flexibility for real-world bot functionality while providing clean, type-safe interfaces.+- Bot framework: https://github.com/zulip/python-zulip-api/blob/main/zulip_bots/zulip_bots/lib.py+- Bot server: https://github.com/zulip/python-zulip-api/blob/main/zulip_botserver/zulip_botserver/server.py+The design adapts these Python patterns to idiomatic OCaml with abstract types, proper error handling, and EIO's structured concurrency for robust, type-safe Zulip integration.
+143
stack/zulip/README.md
+143
stack/zulip/README.md
···
+42
stack/zulip/dune-project
+42
stack/zulip/dune-project
···+(description "High-quality OCaml bindings to the Zulip REST API using EIO for async operations")
+167
stack/zulip/examples/README_ECHO_BOT.md
+167
stack/zulip/examples/README_ECHO_BOT.md
···+A simple echo bot that demonstrates the basic functionality of the Zulip bot framework. The bot responds to direct messages and mentions by echoing back the message content.+- **Bot Handler Module**: Implements the `Bot_handler.S` signature with message processing logic
+383
stack/zulip/examples/atom_feed_bot.ml
+383
stack/zulip/examples/atom_feed_bot.ml
···
+20
stack/zulip/examples/bot_config.toml
+20
stack/zulip/examples/bot_config.toml
···
+44
stack/zulip/examples/bot_example.ml
+44
stack/zulip/examples/bot_example.ml
···+Printf.printf "✅ Message topic: %s\n" (match Zulip.Message.topic message with Some t -> t | None -> "none");+Printf.printf "Note: This uses mock responses since we're not connected to a real Zulip server.\n"
+1
stack/zulip/examples/bot_example.mli
+1
stack/zulip/examples/bot_example.mli
···
+17
stack/zulip/examples/dune
+17
stack/zulip/examples/dune
···
+142
stack/zulip/examples/echo_bot.ml
+142
stack/zulip/examples/echo_bot.ml
···
+47
stack/zulip/examples/example.ml
+47
stack/zulip/examples/example.ml
···
+1
stack/zulip/examples/example.mli
+1
stack/zulip/examples/example.mli
···
+16
stack/zulip/examples/example_bot_config.toml
+16
stack/zulip/examples/example_bot_config.toml
···
+9
stack/zulip/examples/example_zuliprc.toml
+9
stack/zulip/examples/example_zuliprc.toml
+75
stack/zulip/examples/test_client.ml
+75
stack/zulip/examples/test_client.ml
···
+100
stack/zulip/examples/toml_example.ml
+100
stack/zulip/examples/toml_example.ml
···
+1
stack/zulip/examples/toml_example.mli
+1
stack/zulip/examples/toml_example.mli
···
+1
stack/zulip/lib/dune
+1
stack/zulip/lib/dune
···
+83
stack/zulip/lib/zulip/lib/auth.ml
+83
stack/zulip/lib/zulip/lib/auth.ml
···+Error (Error.create ~code:(Other "parse_error") ~msg:("Error parsing zuliprc: " ^ Printexc.to_string exn) ())
+8
stack/zulip/lib/zulip/lib/auth.mli
+8
stack/zulip/lib/zulip/lib/auth.mli
···
+50
stack/zulip/lib/zulip/lib/channel.ml
+50
stack/zulip/lib/zulip/lib/channel.ml
···+let create ~name ~description ?(invite_only = false) ?(history_public_to_subscribers = true) () =+Error (Error.create ~code:(Other "json_parse_error") ~msg:("Channel JSON parsing failed: " ^ Printexc.to_string exn) ())
+16
stack/zulip/lib/zulip/lib/channel.mli
+16
stack/zulip/lib/zulip/lib/channel.mli
···
+58
stack/zulip/lib/zulip/lib/channels.ml
+58
stack/zulip/lib/zulip/lib/channels.ml
···+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Invalid streams response format" ()))+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Streams response must be an object" ()))
+5
stack/zulip/lib/zulip/lib/channels.mli
+5
stack/zulip/lib/zulip/lib/channels.mli
···
+137
stack/zulip/lib/zulip/lib/client.ml
+137
stack/zulip/lib/zulip/lib/client.ml
···
+31
stack/zulip/lib/zulip/lib/client.mli
+31
stack/zulip/lib/zulip/lib/client.mli
···
+4
stack/zulip/lib/zulip/lib/dune
+4
stack/zulip/lib/zulip/lib/dune
+62
stack/zulip/lib/zulip/lib/error.ml
+62
stack/zulip/lib/zulip/lib/error.ml
···+type json = [`Null | `Bool of bool | `Float of float | `String of string | `A of json list | `O of (string * json) list]
+20
stack/zulip/lib/zulip/lib/error.mli
+20
stack/zulip/lib/zulip/lib/error.mli
···+type json = [`Null | `Bool of bool | `Float of float | `String of string | `A of json list | `O of (string * json) list]
+40
stack/zulip/lib/zulip/lib/event.ml
+40
stack/zulip/lib/zulip/lib/event.ml
···+Error (Error.create ~code:(Other "json_parse_error") ~msg:("Event JSON parsing failed: " ^ Printexc.to_string exn) ())
+7
stack/zulip/lib/zulip/lib/event.mli
+7
stack/zulip/lib/zulip/lib/event.mli
+51
stack/zulip/lib/zulip/lib/event_queue.ml
+51
stack/zulip/lib/zulip/lib/event_queue.ml
···+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Invalid register response: missing queue_id" ()))+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Register response must be an object" ()))+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Invalid events response format" ()))+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Events response must be an object" ()))
+12
stack/zulip/lib/zulip/lib/event_queue.mli
+12
stack/zulip/lib/zulip/lib/event_queue.mli
···
+19
stack/zulip/lib/zulip/lib/event_type.ml
+19
stack/zulip/lib/zulip/lib/event_type.ml
···
+9
stack/zulip/lib/zulip/lib/event_type.mli
+9
stack/zulip/lib/zulip/lib/event_type.mli
+43
stack/zulip/lib/zulip/lib/message.ml
+43
stack/zulip/lib/zulip/lib/message.ml
···
+21
stack/zulip/lib/zulip/lib/message.mli
+21
stack/zulip/lib/zulip/lib/message.mli
···
+33
stack/zulip/lib/zulip/lib/message_response.ml
+33
stack/zulip/lib/zulip/lib/message_response.ml
···+Error (Error.create ~code:(Other "parse_error") ~msg:("Failed to parse message response: " ^ msg) ())+Error (Error.create ~code:(Other "parse_error") ~msg:"Failed to parse message response: missing field" ())+Error (Error.create ~code:(Other "parse_error") ~msg:"Expected JSON object for message response" ())
+6
stack/zulip/lib/zulip/lib/message_response.mli
+6
stack/zulip/lib/zulip/lib/message_response.mli
+12
stack/zulip/lib/zulip/lib/message_type.ml
+12
stack/zulip/lib/zulip/lib/message_type.ml
···
+5
stack/zulip/lib/zulip/lib/message_type.mli
+5
stack/zulip/lib/zulip/lib/message_type.mli
+46
stack/zulip/lib/zulip/lib/messages.ml
+46
stack/zulip/lib/zulip/lib/messages.ml
···+match Client.request client ~method_:`PATCH ~path:("/messages/" ^ string_of_int message_id) ~params () with+match Client.request client ~method_:`DELETE ~path:("/messages/" ^ string_of_int message_id) () with+(match narrow with Some n -> List.mapi (fun i s -> ("narrow[" ^ string_of_int i ^ "]", s)) n | None -> []) in
+12
stack/zulip/lib/zulip/lib/messages.mli
+12
stack/zulip/lib/zulip/lib/messages.mli
···+val edit : Client.t -> message_id:int -> ?content:string -> ?topic:string -> unit -> (unit, Error.t) result
+54
stack/zulip/lib/zulip/lib/user.ml
+54
stack/zulip/lib/zulip/lib/user.ml
···+Error (Error.create ~code:(Other "json_parse_error") ~msg:("User JSON parsing failed: " ^ Printexc.to_string exn) ())
+18
stack/zulip/lib/zulip/lib/user.mli
+18
stack/zulip/lib/zulip/lib/user.mli
···
+46
stack/zulip/lib/zulip/lib/users.ml
+46
stack/zulip/lib/zulip/lib/users.ml
···+| _ -> Error (Error.create ~code:(Other "api_error") ~msg:"Users response must be an object" ()))
+4
stack/zulip/lib/zulip/lib/users.mli
+4
stack/zulip/lib/zulip/lib/users.mli
+93
stack/zulip/lib/zulip_bot/lib/bot_config.ml
+93
stack/zulip/lib/zulip_bot/lib/bot_config.ml
···+Error (Zulip.Error.create ~code:(Other "file_error") ~msg:("Cannot read config file: " ^ msg) ())+Error (Zulip.Error.create ~code:(Other "parse_error") ~msg:("Error parsing config: " ^ Printexc.to_string exn) ())+let config_key = String.sub key (String.length prefix) (String.length key - String.length prefix) in+Error (Zulip.Error.create ~code:(Other "env_error") ~msg:("Error reading environment: " ^ Printexc.to_string exn) ())+| None -> Error (Zulip.Error.create ~code:(Other "config_missing") ~msg:("Required config key missing: " ^ key) ())
+24
stack/zulip/lib/zulip_bot/lib/bot_config.mli
+24
stack/zulip/lib/zulip_bot/lib/bot_config.mli
···
+110
stack/zulip/lib/zulip_bot/lib/bot_handler.ml
+110
stack/zulip/lib/zulip_bot/lib/bot_handler.ml
···
+85
stack/zulip/lib/zulip_bot/lib/bot_handler.mli
+85
stack/zulip/lib/zulip_bot/lib/bot_handler.mli
···+| ChannelMessage of { channel: string; topic: string; content: string } (** Send to a channel *)+val handle_message_with_env : t -> _ -> Message_context.t -> (Response.t, Zulip.Error.t) result
+42
stack/zulip/lib/zulip_bot/lib/bot_runner.ml
+42
stack/zulip/lib/zulip_bot/lib/bot_runner.ml
···
+25
stack/zulip/lib/zulip_bot/lib/bot_runner.mli
+25
stack/zulip/lib/zulip_bot/lib/bot_runner.mli
···
+28
stack/zulip/lib/zulip_bot/lib/bot_storage.ml
+28
stack/zulip/lib/zulip_bot/lib/bot_storage.ml
···
+21
stack/zulip/lib/zulip_bot/lib/bot_storage.mli
+21
stack/zulip/lib/zulip_bot/lib/bot_storage.mli
···
+4
stack/zulip/lib/zulip_bot/lib/dune
+4
stack/zulip/lib/zulip_bot/lib/dune
+35
stack/zulip/lib/zulip_botserver/lib/bot_registry.mli
+35
stack/zulip/lib/zulip_botserver/lib/bot_registry.mli
···+create_config:(Server_config.Bot_config.t -> (Zulip_bot.Bot_config.t, Zulip.Error.t) result) ->+val create_handler : t -> Server_config.Bot_config.t -> Zulip.Client.t -> (Zulip_bot.Bot_handler.t, Zulip.Error.t) result
+22
stack/zulip/lib/zulip_botserver/lib/bot_server.mli
+22
stack/zulip/lib/zulip_botserver/lib/bot_server.mli
···
+5
stack/zulip/lib/zulip_botserver/lib/dune
+5
stack/zulip/lib/zulip_botserver/lib/dune
+40
stack/zulip/lib/zulip_botserver/lib/server_config.mli
+40
stack/zulip/lib/zulip_botserver/lib/server_config.mli
···
+33
stack/zulip/lib/zulip_botserver/lib/webhook_handler.mli
+33
stack/zulip/lib/zulip_botserver/lib/webhook_handler.mli
···