feat(app): content detection overhaul, apps tab, chat UX, web search
- Overhaul content detection: expand all query/response classifiers with broader AI response patterns (news, music, books, TV, places, websites) - Add Nostr detection (isNostrQuery, isNostrLikeResponse) and App detection (isAppQuery, isAppLikeResponse) classifiers - Add bare domain extraction from AI text (e.g. "check out damus.io") - Add Apps tab with curated database of ~30 Nostr + Bitcoin ecosystem apps (clients, wallets, privacy tools, node software, dev tools) - Create AppsGrid + AppDetail components with search, filtering, how-to - Wire app extraction and Nostr detection into useContentPanel - Add PromptIndex badges for Apps and Nostr tabs - Chat UX: dedicated history button, settings modal (memory + advanced), fix collapsed chat v-if/v-else chain bug - Web search: add Brave Search API as primary backend, expand SearXNG pool - iOS HIG: comprehensive mobile UX rules in cursor rules + CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
5d4367eaff
commit
84ccdc7048
+83
-25
@@ -2,32 +2,62 @@
|
||||
# Headless loop script for overnight Claude Code automation.
|
||||
# Set CLAUDE_AUTONOMOUS=1 for Ralph Wiggum (Stop hook blocks until plan is complete).
|
||||
# Rate-limit aware: detects limits, sleeps until reset, and retries automatically.
|
||||
set -eu
|
||||
set -u
|
||||
|
||||
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
||||
PROMPT_FILE="${PROMPT_FILE:-$PROJECT_DIR/loop/prompt.md}"
|
||||
LOG_FILE="${LOG_FILE:-$PROJECT_DIR/loop/loop.log}"
|
||||
ITERATION_COUNT="${ITERATION_COUNT:-1}"
|
||||
ITERATION_DELAY="${ITERATION_DELAY:-600}"
|
||||
ITERATION_COUNT="${ITERATION_COUNT:-10}"
|
||||
ITERATION_DELAY="${ITERATION_DELAY:-30}"
|
||||
CLAUDE_BIN="${CLAUDE_BIN:-claude}"
|
||||
RATE_LIMIT_WAIT="${RATE_LIMIT_WAIT:-3600}" # Default: wait 1 hour on rate limit
|
||||
MAX_RATE_LIMIT_RETRIES="${MAX_RATE_LIMIT_RETRIES:-5}" # Max retries before giving up
|
||||
CLAUDE_EXIT=0
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Human-readable log with visual separators
|
||||
log() {
|
||||
echo "$1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
banner() {
|
||||
log ""
|
||||
log "════════════════════════════════════════════════════════════════"
|
||||
log " $1"
|
||||
log " $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
log "════════════════════════════════════════════════════════════════"
|
||||
log ""
|
||||
}
|
||||
|
||||
section() {
|
||||
log ""
|
||||
log "────────────────────────────────────────"
|
||||
log " $1"
|
||||
log "────────────────────────────────────────"
|
||||
log ""
|
||||
}
|
||||
|
||||
# Check if plan has remaining tasks
|
||||
plan_has_tasks() {
|
||||
grep -q '^\- \[ \]' "$PROJECT_DIR/loop/plan.md" 2>/dev/null
|
||||
}
|
||||
|
||||
# Detect rate limit from Claude output
|
||||
# Show remaining task count
|
||||
remaining_tasks() {
|
||||
grep -c '^\- \[ \]' "$PROJECT_DIR/loop/plan.md" 2>/dev/null || echo "0"
|
||||
}
|
||||
|
||||
# Show next task
|
||||
next_task() {
|
||||
grep -m1 '^\- \[ \]' "$PROJECT_DIR/loop/plan.md" 2>/dev/null | sed 's/^- \[ \] //' || echo "(none)"
|
||||
}
|
||||
|
||||
# Detect rate limit from Claude output (only when Claude exited non-zero)
|
||||
check_rate_limit() {
|
||||
# Check last 50 lines of log for rate limit indicators
|
||||
tail -50 "$LOG_FILE" 2>/dev/null | grep -qi \
|
||||
[ "${CLAUDE_EXIT:-0}" -eq 0 ] && return 1
|
||||
# Check last 50 lines for rate limit indicators, excluding our own log lines
|
||||
tail -50 "$LOG_FILE" 2>/dev/null | grep -v "^Rate limit detected" | grep -v "^Sleeping" | grep -v "^═" | grep -v "^─" | grep -qi \
|
||||
-e "rate.limit" \
|
||||
-e "too.many.requests" \
|
||||
-e "429" \
|
||||
@@ -36,26 +66,46 @@ check_rate_limit() {
|
||||
-e "limit.reached" 2>/dev/null
|
||||
}
|
||||
|
||||
log "=== Overnight loop started $(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || date) ==="
|
||||
log " PROMPT_FILE=$PROMPT_FILE"
|
||||
log " CLAUDE_AUTONOMOUS=${CLAUDE_AUTONOMOUS:-0}"
|
||||
log " ITERATION_COUNT=$ITERATION_COUNT"
|
||||
log " RATE_LIMIT_WAIT=${RATE_LIMIT_WAIT}s"
|
||||
banner "OVERNIGHT AUTOMATION STARTED"
|
||||
log " Project: $PROJECT_DIR"
|
||||
log " Prompt: $PROMPT_FILE"
|
||||
log " Autonomous: ${CLAUDE_AUTONOMOUS:-0}"
|
||||
log " Iterations: $ITERATION_COUNT (${ITERATION_DELAY}s between each)"
|
||||
log " Rate limit: wait ${RATE_LIMIT_WAIT}s, retry up to ${MAX_RATE_LIMIT_RETRIES}x"
|
||||
log " Tasks left: $(remaining_tasks)"
|
||||
log " Next task: $(next_task)"
|
||||
log ""
|
||||
|
||||
i=1
|
||||
rate_limit_retries=0
|
||||
while [ "$i" -le "$ITERATION_COUNT" ]; do
|
||||
log "--- Iteration $i/$ITERATION_COUNT @ $(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || date) ---"
|
||||
|
||||
# Check if there are tasks remaining before starting
|
||||
if ! plan_has_tasks; then
|
||||
banner "ALL TASKS COMPLETE"
|
||||
log " No remaining tasks in plan.md. Stopping."
|
||||
break
|
||||
fi
|
||||
|
||||
section "ITERATION $i/$ITERATION_COUNT"
|
||||
log " Tasks remaining: $(remaining_tasks)"
|
||||
log " Next task: $(next_task)"
|
||||
log ""
|
||||
|
||||
export CLAUDE_PROJECT_DIR="$PROJECT_DIR"
|
||||
export CLAUDE_AUTONOMOUS="${CLAUDE_AUTONOMOUS:-1}"
|
||||
|
||||
# Run Claude with autonomous permissions; prompt from file
|
||||
if [ -f "$PROMPT_FILE" ]; then
|
||||
"$CLAUDE_BIN" -p --dangerously-skip-permissions --output-format=stream-json \
|
||||
< "$PROMPT_FILE" 2>&1 | tee -a "$LOG_FILE" || true
|
||||
log " Starting Claude session..."
|
||||
log ""
|
||||
"$CLAUDE_BIN" -p --dangerously-skip-permissions \
|
||||
< "$PROMPT_FILE" 2>&1 | tee -a "$LOG_FILE"
|
||||
CLAUDE_EXIT=$?
|
||||
log ""
|
||||
log " Claude exited with code: $CLAUDE_EXIT"
|
||||
else
|
||||
log "Error: $PROMPT_FILE not found"
|
||||
log " ERROR: $PROMPT_FILE not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -63,8 +113,8 @@ while [ "$i" -le "$ITERATION_COUNT" ]; do
|
||||
if check_rate_limit; then
|
||||
rate_limit_retries=$((rate_limit_retries + 1))
|
||||
if [ "$rate_limit_retries" -ge "$MAX_RATE_LIMIT_RETRIES" ]; then
|
||||
log "Rate limited $rate_limit_retries times — giving up."
|
||||
log "Scheduling retry via launchd..."
|
||||
section "RATE LIMITED — SCHEDULING LAUNCHD RETRY"
|
||||
log " Hit rate limit $rate_limit_retries times. Creating launchd job to retry later."
|
||||
|
||||
# Schedule a retry using launchd for after rate limit resets
|
||||
PLIST_LABEL="com.aiui.overnight-retry"
|
||||
@@ -111,32 +161,40 @@ while [ "$i" -le "$ITERATION_COUNT" ]; do
|
||||
PLIST
|
||||
|
||||
launchctl load "$PLIST_PATH" 2>/dev/null || true
|
||||
log "Scheduled retry at ~${RETRY_TIME} via launchd ($PLIST_PATH)"
|
||||
log "The plist auto-removes after running."
|
||||
log " Scheduled retry at ~${RETRY_TIME}"
|
||||
log " Plist: $PLIST_PATH (auto-removes after running)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Rate limit detected (attempt $rate_limit_retries/$MAX_RATE_LIMIT_RETRIES)."
|
||||
log "Sleeping ${RATE_LIMIT_WAIT}s until $(date -v+${RATE_LIMIT_WAIT}S '+%H:%M:%S' 2>/dev/null || date -d "+${RATE_LIMIT_WAIT} seconds" '+%H:%M:%S')..."
|
||||
section "RATE LIMITED — WAITING"
|
||||
log " Attempt $rate_limit_retries/$MAX_RATE_LIMIT_RETRIES"
|
||||
log " Sleeping ${RATE_LIMIT_WAIT}s until $(date -v+${RATE_LIMIT_WAIT}S '+%H:%M:%S' 2>/dev/null || date -d "+${RATE_LIMIT_WAIT} seconds" '+%H:%M:%S')..."
|
||||
sleep "$RATE_LIMIT_WAIT"
|
||||
|
||||
# Check if plan still has tasks before retrying
|
||||
if ! plan_has_tasks; then
|
||||
log "All plan tasks completed during rate limit wait. Done."
|
||||
banner "ALL TASKS COMPLETE (during rate limit wait)"
|
||||
break
|
||||
fi
|
||||
log "Retrying after rate limit..."
|
||||
log " Retrying..."
|
||||
continue # Retry same iteration
|
||||
fi
|
||||
|
||||
# Reset rate limit counter on successful run
|
||||
rate_limit_retries=0
|
||||
|
||||
section "ITERATION $i COMPLETE"
|
||||
log " Tasks remaining: $(remaining_tasks)"
|
||||
log " Next task: $(next_task)"
|
||||
|
||||
i=$((i + 1))
|
||||
if [ "$i" -le "$ITERATION_COUNT" ] && [ "$ITERATION_DELAY" -gt 0 ]; then
|
||||
log "Waiting ${ITERATION_DELAY}s before next iteration..."
|
||||
log " Pausing ${ITERATION_DELAY}s before next iteration..."
|
||||
sleep "$ITERATION_DELAY"
|
||||
fi
|
||||
done
|
||||
|
||||
log "=== Loop complete $(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || date) ==="
|
||||
banner "LOOP FINISHED"
|
||||
log " Completed $((i - 1)) iterations"
|
||||
log " Tasks remaining: $(remaining_tasks)"
|
||||
log ""
|
||||
|
||||
Reference in New Issue
Block a user