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:
Dorian
2026-03-04 05:03:03 +00:00
co-authored by Claude Opus 4.6
parent 5d4367eaff
commit 84ccdc7048
36 changed files with 2295 additions and 321 deletions
+83 -25
View File
@@ -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 ""