diff --git a/onchain/.claude-code-supervisor.yml b/onchain/.claude-code-supervisor.yml new file mode 100644 index 0000000..a4f7ee1 --- /dev/null +++ b/onchain/.claude-code-supervisor.yml @@ -0,0 +1,6 @@ +triage: + command: "claude -p --no-session-persistence" + model: "claude-haiku-4-20250414" + +notify: + command: "/tmp/supervisor-notify.sh" diff --git a/onchain/.claude/hooks/supervisor/lib.sh b/onchain/.claude/hooks/supervisor/lib.sh new file mode 100755 index 0000000..68d3892 --- /dev/null +++ b/onchain/.claude/hooks/supervisor/lib.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# Shared functions for claude-code-supervisor hooks and scripts. + +set -euo pipefail + +# Find config file: project .claude-code-supervisor.yml → ~/.config/ → defaults +ccs_find_config() { + local cwd="${1:-.}" + if [ -f "$cwd/.claude-code-supervisor.yml" ]; then + echo "$cwd/.claude-code-supervisor.yml" + elif [ -f "${HOME}/.config/claude-code-supervisor/config.yml" ]; then + echo "${HOME}/.config/claude-code-supervisor/config.yml" + else + echo "" + fi +} + +# Read a yaml value (simple key.subkey extraction, no yq dependency). +# Falls back to default if not found. +ccs_config_get() { + local config_file="$1" + local key="$2" + local default="${3:-}" + + if [ -z "$config_file" ] || [ ! -f "$config_file" ]; then + echo "$default" + return + fi + + # Simple grep-based yaml extraction (handles key: value on one line) + local value + case "$key" in + triage.command) value=$(grep -A0 '^\s*command:' "$config_file" | head -1 | sed 's/.*command:\s*["]*//;s/["]*$//' | xargs) ;; + triage.model) value=$(awk '/^triage:/,/^[a-z]/' "$config_file" | grep 'model:' | head -1 | sed 's/.*model:\s*["]*//;s/["]*$//' | xargs) ;; + triage.max_tokens) value=$(awk '/^triage:/,/^[a-z]/' "$config_file" | grep 'max_tokens:' | head -1 | sed 's/.*max_tokens:\s*//' | xargs) ;; + notify.command) value=$(awk '/^notify:/,/^[a-z]/' "$config_file" | grep 'command:' | head -1 | sed 's/.*command:\s*["]*//;s/["]*$//' | xargs) ;; + idle.timeout) value=$(awk '/^idle:/,/^[a-z]/' "$config_file" | grep 'timeout_seconds:' | head -1 | sed 's/.*timeout_seconds:\s*//' | xargs) ;; + idle.nudge_message) value=$(awk '/^idle:/,/^[a-z]/' "$config_file" | grep 'nudge_message:' | head -1 | sed 's/.*nudge_message:\s*["]*//;s/["]*$//' | xargs) ;; + *) value="" ;; + esac + + echo "${value:-$default}" +} + +# Send a notification via the configured notify command. +ccs_notify() { + local config_file="$1" + local message="$2" + + local notify_cmd + notify_cmd=$(ccs_config_get "$config_file" "notify.command" "openclaw gateway call wake --params") + + # Build JSON payload + local payload + payload=$(jq -n --arg text "$message" --arg mode "now" '{text: $text, mode: $mode}') + + $notify_cmd "$payload" 2>/dev/null || true +} + +# Run LLM triage via configured command. +# Accepts prompt on stdin, returns verdict on stdout. +ccs_triage() { + local config_file="$1" + local prompt="$2" + + local triage_cmd model max_tokens + triage_cmd=$(ccs_config_get "$config_file" "triage.command" "claude -p --no-session-persistence") + model=$(ccs_config_get "$config_file" "triage.model" "claude-haiku-4-20250414") + max_tokens=$(ccs_config_get "$config_file" "triage.max_tokens" "150") + + echo "$prompt" | $triage_cmd --model "$model" --max-tokens "$max_tokens" 2>/dev/null +} + +# Generate a notify wrapper script the agent can call on completion. +# Usage: ccs_generate_notify_script "$config_file" ["/tmp/supervisor-notify.sh"] +ccs_generate_notify_script() { + local config_file="$1" + local script_path="${2:-/tmp/supervisor-notify.sh}" + local notify_cmd + notify_cmd=$(ccs_config_get "$config_file" "notify.command" "openclaw gateway call wake --params") + + cat > "$script_path" <