From f9ec72d808f95205f2c7b2d9efcd958e20cb1745 Mon Sep 17 00:00:00 2001 From: Adam Jeniski Date: Thu, 8 Jan 2026 02:47:01 -0500 Subject: [PATCH] add feature toggles --- CLAUDE.md | 60 ++++++++++++++++++++++-- iamwaiting | 134 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 167 insertions(+), 27 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ccc2d1d..abf2b11 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,12 +16,14 @@ The tool integrates with Claude Code's hook system to send real-time notificatio The entire implementation is a single Babashka script (`iamwaiting`) with these key functions: -- `load-config` - Loads webhook URL from config file or environment variable +- `load-config` - Loads webhook URL and toggles from config file or environment variable - `send-discord-webhook` - Makes HTTP POST request to Discord webhook API - `format-waiting-message` - Formats the notification message with project context - `setup-config` - Interactive setup wizard for webhook configuration - `test-webhook` - Sends a test message to verify configuration -- `send-waiting-notification` - Main function that sends the notification +- `send-waiting-notification` - Main function that sends the notification (respects toggles) +- `toggle-feature` - Toggle notification features on or off via CLI +- `show-toggle-status` - Display current toggle status - `-main` - CLI argument parsing and entry point ### Configuration @@ -30,13 +32,46 @@ Configuration is stored in `~/.iamwaiting/config.edn` with the following structu ```clojure {:webhook-url "https://discord.com/api/webhooks/..." - :user-id "123456789012345678"} ; Optional: Discord user ID for @mentions + :user-id "123456789012345678" ; Optional: Discord user ID for @mentions + :toggles {:idle-prompt true ; Enable idle prompt notifications + :permission-prompt true}} ; Enable permission prompt notifications ``` Alternatively, configuration can be set via environment variables: - `IAMWAITING_WEBHOOK_URL` - Discord webhook URL (required) - `IAMWAITING_USER_ID` - Discord user ID for @mentions (optional) +### Notification Toggles + +Control which types of notifications are sent using the `:toggles` configuration key. If toggles are not specified, both notification types are enabled by default (backwards compatible). + +**Managing toggles via CLI:** +```bash +# Show current toggle status +iamwaiting toggle status + +# Disable idle prompt notifications +iamwaiting toggle idle-prompt off + +# Enable permission prompt notifications +iamwaiting toggle permission-prompt on +``` + +**Manually editing config file:** +```clojure +;; Only get notified for permission prompts +{:webhook-url "https://discord.com/api/webhooks/..." + :toggles {:idle-prompt false + :permission-prompt true}} + +;; Disable all notifications +{:webhook-url "https://discord.com/api/webhooks/..." + :toggles {:idle-prompt false + :permission-prompt false}} +``` + +**Default behavior:** If toggles are not specified in the config file, both notification types are enabled. + ## Installation Install via bbin (recommended): @@ -71,6 +106,25 @@ iamwaiting iamwaiting '{"cwd": "/path/to/project"}' ``` +### Toggle Management + +```bash +# Show current toggle status +iamwaiting toggle status + +# Disable idle prompt notifications +iamwaiting toggle idle-prompt off + +# Enable permission prompt notifications +iamwaiting toggle permission-prompt on + +# Enable idle prompt notifications +iamwaiting toggle idle-prompt on + +# Disable permission prompt notifications +iamwaiting toggle permission-prompt off +``` + ### Running via Babashka Tasks ```bash diff --git a/iamwaiting b/iamwaiting index e29c2b6..3baa7e7 100755 --- a/iamwaiting +++ b/iamwaiting @@ -15,9 +15,13 @@ webhook-url (or (:webhook-url config) (System/getenv "IAMWAITING_WEBHOOK_URL")) user-id (or (:user-id config) - (System/getenv "IAMWAITING_USER_ID"))] + (System/getenv "IAMWAITING_USER_ID")) + toggles (or (:toggles config) + {:idle-prompt true + :permission-prompt true})] {:webhook-url webhook-url - :user-id user-id})) + :user-id user-id + :toggles toggles})) (defn send-discord-webhook [webhook-url message] "Send a message to Discord via webhook" @@ -81,20 +85,37 @@ (print "> ") (flush) (let [user-id-input (str/trim (read-line)) - user-id (when-not (str/blank? user-id-input) user-id-input) - config (if user-id - {:webhook-url webhook-url :user-id user-id} - {:webhook-url webhook-url})] + user-id (when-not (str/blank? user-id-input) user-id-input)] - ;; Create config directory - (fs/create-dirs (fs/parent config-file)) + ;; NEW: Toggle prompts + (println "\nšŸ“¬ Notification Preferences") + (print "Enable notifications for idle prompts? (y/n, default: y): ") + (flush) + (let [idle-response (str/lower-case (str/trim (read-line))) + idle-enabled? (not= idle-response "n")] - ;; Write config - (spit config-file (pr-str config)) - (println "\nāœ“ Configuration saved to" config-file) - (when user-id - (println "āœ“ User ID configured - you will be @mentioned on permission prompts")) - (println "\nTest the webhook with: ./iamwaiting test")))) + (print "Enable notifications for permission prompts? (y/n, default: y): ") + (flush) + (let [perm-response (str/lower-case (str/trim (read-line))) + perm-enabled? (not= perm-response "n") + + ;; Build config with toggles + config (cond-> {:webhook-url webhook-url + :toggles {:idle-prompt idle-enabled? + :permission-prompt perm-enabled?}} + user-id (assoc :user-id user-id))] + + ;; Create config directory + (fs/create-dirs (fs/parent config-file)) + + ;; Write config + (spit config-file (pr-str config)) + (println "\nāœ“ Configuration saved to" config-file) + (when user-id + (println "āœ“ User ID configured - you will be @mentioned on permission prompts")) + (println (str "āœ“ Idle prompts: " (if idle-enabled? "enabled" "disabled"))) + (println (str "āœ“ Permission prompts: " (if perm-enabled? "enabled" "disabled"))) + (println "\nTest the webhook with: ./iamwaiting test")))))) (defn test-webhook [] "Test the webhook configuration" @@ -141,21 +162,78 @@ (json/parse-string (first event-data-args) true) (catch Exception _ {}))) {}) - message (format-waiting-message event-data (:user-id config)) - result (send-discord-webhook (:webhook-url config) message)] - (if (:success result) - (println "āœ“ Notification sent") - (do - (println "āœ— Failed to send notification:" (:error result)) - (System/exit 1))))))) + ;; Determine notification type + notification-type (:notification_type event-data) + permission-mode (:permission_mode event-data) + is-permission-prompt? (or (= notification-type "permission_prompt") + (some? permission-mode)) + ;; Determine which toggle to check + toggle-key (if is-permission-prompt? :permission-prompt :idle-prompt) + enabled? (get-in config [:toggles toggle-key] true)] + + ;; Exit early if notification type is disabled + (if-not enabled? + (when (System/getenv "DEBUG") + (println (str "Notification disabled for " toggle-key))) + + ;; Original notification logic + (let [message (format-waiting-message event-data (:user-id config)) + result (send-discord-webhook (:webhook-url config) message)] + (if (:success result) + (println "āœ“ Notification sent") + (do + (println "āœ— Failed to send notification:" (:error result)) + (System/exit 1))))))))) + +(defn toggle-feature [feature action] + "Toggle a notification feature on or off" + (let [config (load-config) + feature-key (case feature + "idle-prompt" :idle-prompt + "permission-prompt" :permission-prompt + nil) + action-bool (case action + "on" true + "off" false + nil)] + + (cond + (nil? feature-key) + (do + (println "āŒ Invalid feature. Use: idle-prompt | permission-prompt") + (System/exit 1)) + + (nil? action-bool) + (do + (println "āŒ Invalid action. Use: on | off") + (System/exit 1)) + + :else + (let [updated-config (assoc-in config [:toggles feature-key] action-bool)] + (fs/create-dirs (fs/parent config-file)) + (spit config-file (pr-str updated-config)) + (println (str "āœ… " feature " notifications: " action)))))) + +(defn show-toggle-status [] + "Display current toggle status" + (let [config (load-config) + toggles (or (:toggles config) + {:idle-prompt true + :permission-prompt true})] + (println "šŸ“Š Current Toggle Status\n") + (println (str "Idle prompts: " (if (:idle-prompt toggles) "āœ… enabled" "āŒ disabled"))) + (println (str "Permission prompts: " (if (:permission-prompt toggles) "āœ… enabled" "āŒ disabled"))))) (defn show-help [] (println "iamwaiting - Send Discord notifications when Claude is waiting") (println "") (println "Usage:") - (println " iamwaiting setup Set up Discord webhook configuration") - (println " iamwaiting test Test webhook configuration") - (println " iamwaiting [event-data-json] Send waiting notification") + (println " iamwaiting setup Set up Discord webhook configuration") + (println " iamwaiting test Test webhook configuration") + (println " iamwaiting toggle status Show current toggle status") + (println " iamwaiting toggle idle-prompt Toggle idle prompt notifications") + (println " iamwaiting toggle permission-prompt Toggle permission prompt notifications") + (println " iamwaiting [event-data-json] Send waiting notification") (println "") (println "Configuration:") (println " Config file: ~/.iamwaiting/config.edn") @@ -175,6 +253,14 @@ (case command "setup" (setup-config) "test" (test-webhook) + "toggle" (let [subcommand (second args)] + (case subcommand + "status" (show-toggle-status) + "idle-prompt" (toggle-feature "idle-prompt" (nth args 2 nil)) + "permission-prompt" (toggle-feature "permission-prompt" (nth args 2 nil)) + (do + (println "Usage: iamwaiting toggle [on|off]") + (System/exit 1)))) "help" (show-help) "--help" (show-help) "-h" (show-help)