init commit

This commit is contained in:
2026-01-05 18:38:20 -05:00
commit 38fc49ddcc
5 changed files with 498 additions and 0 deletions
Executable
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env bb
(ns iamwaiting.core
(:require [babashka.http-client :as http]
[babashka.fs :as fs]
[cheshire.core :as json]
[clojure.string :as str]))
(def config-file (str (System/getenv "HOME") "/.iamwaiting/config.edn"))
(defn load-config []
"Load configuration from config file or environment variables"
(let [config (when (fs/exists? config-file)
(read-string (slurp config-file)))
webhook-url (or (:webhook-url config)
(System/getenv "IAMWAITING_WEBHOOK_URL"))]
{:webhook-url webhook-url}))
(defn send-discord-webhook [webhook-url message]
"Send a message to Discord via webhook"
(try
(let [payload {:content message
:username "Claude Code"
:avatar_url "https://www.anthropic.com/images/icons/apple-touch-icon.png"}
response (http/post webhook-url
{:headers {"Content-Type" "application/json"}
:body (json/generate-string payload)})]
(if (< (:status response) 300)
{:success true}
{:success false :error (str "HTTP " (:status response))}))
(catch Exception e
{:success false :error (.getMessage e)})))
(defn format-waiting-message [event-data]
"Format a message for Claude waiting event"
(let [cwd (or (:cwd event-data) (System/getProperty "user.dir"))
project-name (fs/file-name cwd)
timestamp (java.time.LocalDateTime/now)
formatted-time (.format timestamp
(java.time.format.DateTimeFormatter/ofPattern "HH:mm:ss"))
session-id (:session_id event-data)
notification-type (:notification_type event-data)
permission-mode (:permission_mode event-data)
hook-message (:message event-data)
transcript-path (:transcript_path event-data)
transcript-file (when transcript-path (fs/file-name transcript-path))]
(str "⏳ **Claude is waiting** in `" project-name "`\n"
"📁 Path: `" cwd "`\n"
"🕐 Time: " formatted-time "\n"
(when session-id (str "🔑 Session: `" session-id "`\n"))
(when notification-type (str "📢 Type: `" notification-type "`\n"))
(when permission-mode (str "🔐 Mode: `" permission-mode "`\n"))
(when transcript-file (str "📝 Transcript: `" transcript-file "`\n"))
(when hook-message (str "\n> " hook-message)))))
(defn setup-config []
"Interactive setup for webhook configuration"
(println "iamwaiting setup")
(println "================\n")
(println "Enter your Discord webhook URL:")
(println "(Get this from Discord: Server Settings > Integrations > Webhooks)\n")
(print "> ")
(flush)
(let [webhook-url (str/trim (read-line))]
(when (str/blank? webhook-url)
(println "Error: webhook URL cannot be empty")
(System/exit 1))
;; Create config directory
(fs/create-dirs (fs/parent config-file))
;; Write config
(spit config-file (pr-str {:webhook-url webhook-url}))
(println "\n✓ Configuration saved to" config-file)
(println "\nTest the webhook with: ./iamwaiting test")))
(defn test-webhook []
"Test the webhook configuration"
(let [config (load-config)]
(if-not (:webhook-url config)
(do
(println "Error: No webhook URL configured")
(println "Run: ./iamwaiting setup")
(System/exit 1))
(do
(println "Sending test message...")
(let [result (send-discord-webhook
(:webhook-url config)
"🧪 **Test message from iamwaiting**\n\nIf you see this, your webhook is configured correctly!")]
(if (:success result)
(println "✓ Test message sent successfully!")
(do
(println "✗ Failed to send message:" (:error result))
(System/exit 1))))))))
(defn read-hook-data []
"Read hook data from stdin (JSON format)"
(try
(let [stdin (slurp *in*)]
(when-not (str/blank? stdin)
(json/parse-string stdin true)))
(catch Exception e
(binding [*out* *err*]
(println "Warning: Failed to parse stdin JSON:" (.getMessage e)))
{})))
(defn send-waiting-notification [& event-data-args]
"Send a waiting notification to Discord"
(let [config (load-config)]
(if-not (:webhook-url config)
(do
(println "Error: No webhook URL configured")
(println "Run: ./iamwaiting setup")
(System/exit 1))
(let [;; Read from stdin first (hook data), fallback to command-line args
event-data (or (read-hook-data)
(when (seq event-data-args)
(try
(json/parse-string (first event-data-args) true)
(catch Exception _ {})))
{})
message (format-waiting-message event-data)
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 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 "")
(println "Configuration:")
(println " Config file: ~/.iamwaiting/config.edn")
(println " Environment: IAMWAITING_WEBHOOK_URL")
(println "")
(println "Hook Integration:")
(println " Add to ~/.claude/hooks.edn:")
(println " {:agent-waiting-for-user {:command [\"iamwaiting\"]}}")
(System/exit 0))
;; CLI entry point
(defn -main [& args]
(if (empty? args)
(send-waiting-notification)
(let [command (first args)
rest-args (rest args)]
(case command
"setup" (setup-config)
"test" (test-webhook)
"help" (show-help)
"--help" (show-help)
"-h" (show-help)
;; Default: treat first arg as event data JSON
(apply send-waiting-notification args)))))
(when (= *file* (System/getProperty "babashka.file"))
(apply -main *command-line-args*))