Skill: terrycarter1985/research-gif-enricher (version 0.1.0)

--- name: research-assistant description: Auto-enrich Bear research notes tagged 「待整理」 with topic-matched GIFs. Reads notes via grizzly, searches GIFs via gifgrep, appends media, and removes the tag. Use when the user wants to batch-process or tidy up research notes in Bear. version: 0.1.0 author: ClawdBot tags: [research, bear, gif, productivity] metadata: openclaw: requires: bins: [grizzly] skills: [bear-notes, gifgrep] install: - id: go kind: go module: github.com/tylerwince/grizzly/cmd/grizzly@latest bins: [grizzly] label: Install grizzly (go) --- # Research Assistant Batch-process Bear notes tagged 「待整理」: find a relevant GIF for each note, append it, then remove the tag. ## Prerequisites - Bear running + grizzly installed + token at `~/.config/grizzly/token` - gifgrep skill available ## Workflow 1. **Fetch tagged notes** ```bash grizzly open-tag --name "待整理" --enable-callback --json --token-file ~/.config/grizzly/token ``` Parse the JSON to get note IDs and titles/summaries. 2. **For each note:** a. **Read full content** ```bash grizzly open-note --id "$NOTE_ID" --enable-callback --json ``` b. **Derive a GIF search query** — extract 2–3 keywords from the note title or key findings. Prefer concrete nouns and verbs over abstract terms. c. **Search for a GIF** using the gifgrep skill (or `web_search` + `web_fetch` as fallback) with the derived query. d. **Append the GIF** to the note under a `## Supporting Media` heading: ```bash printf '\n## Supporting Media\n\n![%s](%s)\n' "$ALT_TEXT" "$GIF_URL" \ | grizzly add-text --id "$NOTE_ID" --mode append --token-file ~/.config/grizzly/token ``` e. **Remove the 「待整理」 tag** — replace note content with the tag removed, or use Bear's tag API: ```bash grizzly create --title "$TITLE" --tag "$OTHER_TAGS" < /dev/null # Then delete the old note if needed, or strip the tag from content ``` Simplest approach: append a line that re-tags the note (Bear removes a tag when it's deleted from the note body). Use `sed` on the note content to strip `#待整理` and re-write via `grizzly add-text --mode replace`. 3. **Report** — list processed notes and any failures. ## Script For batch runs, use `scripts/process_tagged.sh`: ```bash bash scripts/process_tagged.sh ``` The script reads all notes tagged 「待整理」, extracts keywords, and calls the gifgrep search endpoint. It appends the top GIF result and strips the tag. ## Notes - If no GIF matches, skip the note and log it — don't force irrelevant results. - Rate-limit Bear API calls (1–2 req/s) to avoid callback timeouts. - The script is a convenience wrapper; the agent can also drive the workflow step-by-step for more control.

path: scripts/process_tagged.sh
content: #!/usr/bin/env bash
# process_tagged.sh — Batch-enrich Bear notes tagged 「待整理」 with GIFs
set -euo pipefail

TOKEN_FILE="${GRIZZLY_TOKEN_FILE:-$HOME/.config/grizzly/token}"
TAG="待整理"
DELAY=1  # seconds between API calls

if ! command -v grizzly &>/dev/null; then
  echo "❌ grizzly not found. Install: go install github.com/tylerwince/grizzly/cmd/grizzly@latest"
  exit 1
fi

if [ ! -f "$TOKEN_FILE" ]; then
  echo "❌ Bear token not found at $TOKEN_FILE"
  exit 1
fi

echo "🔍 Fetching notes tagged #$TAG..."
NOTES_JSON=$(grizzly open-tag --name "$TAG" --enable-callback --json --token-file "$TOKEN_FILE" 2>/dev/null || echo '{"notes":[]}')

# Extract note IDs — grizzly JSON format varies; try common shapes
NOTE_IDS=$(echo "$NOTES_JSON" | python3 -c "
import sys, json
data = json.load(sys.stdin)
notes = data if isinstance(data, list) else data.get('notes', data.get('note', []))
if isinstance(notes, dict): notes = [notes]
for n in notes:
    nid = n.get('identifier', n.get('id', n.get('ID', '')))
    if nid: print(nid)
" 2>/dev/null || true)

if [ -z "$NOTE_IDS" ]; then
  echo "✅ No notes tagged #$TAG found. Nothing to do."
  exit 0
fi

PROCESSED=0
SKIPPED=0

while IFS= read -r NOTE_ID; do
  [ -z "$NOTE_ID" ] && continue
  echo "📝 Processing note $NOTE_ID..."

  # Read note content
  NOTE_JSON=$(grizzly open-note --id "$NOTE_ID" --enable-callback --json --token-file "$TOKEN_FILE" 2>/dev/null || echo '{}')
  TITLE=$(echo "$NOTE_JSON" | python3 -c "
import sys, json
data = json.load(sys.stdin)
note = data if isinstance(data, dict) and 'title' in data else data.get('note', data)
print(note.get('title', ''))
" 2>/dev/null || echo "")
  CONTENT=$(echo "$NOTE_JSON" | python3 -c "
import sys, json
data = json.load(sys.stdin)
note = data if isinstance(data, dict) and 'text' in data else data.get('note', data)
print(note.get('text', ''))
" 2>/dev/null || echo "")

  # Derive search keywords from title (first 3 meaningful words)
  QUERY=$(echo "$TITLE" | python3 -c "
import sys, re
title = sys.stdin.read().strip()
words = re.findall(r'[a-zA-Z\u4e00-\u9fff]+', title)
print(' '.join(words[:3]) if words else title[:30])
" 2>/dev/null || echo "${TITLE:0:30}")

  if [ -z "$QUERY" ]; then
    echo "  ⚠️  No query derived, skipping."
    SKIPPED=$((SKIPPED + 1))
    sleep "$DELAY"
    continue
  fi

  echo "  🔎 Searching GIF for: $QUERY"

  # Search GIF via gifgrep (curl to Tenor/Giphy as fallback)
  GIF_URL=""
  if command -v gifgrep &>/dev/null; then
    GIF_URL=$(gifgrep search "$QUERY" --limit 1 --json 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
results = data if isinstance(data, list) else data.get('results', [])
if results: print(results[0].get('url', results[0].get('media', [{}])[0].get('gif', {}).get('url', '')))
" 2>/dev/null || echo "")
  fi

  # Fallback: search via web
  if [ -z "$GIF_URL" ]; then
    GIF_URL=$(curl -s "https://tenor.googleapis.com/v2/search?q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")&key=AIzaSyAyimkuYQYF_FXVALexPuGQctUWRURdCYQ&limit=1" 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
results = data.get('results', [])
if results: print(results[0].get('media_formats', {}).get('gif', {}).get('url', ''))
" 2>/dev/null || echo "")
  fi

  if [ -z "$GIF_URL" ]; then
    echo "  ⚠️  No GIF found, skipping."
    SKIPPED=$((SKIPPED + 1))
    sleep "$DELAY"
    continue
  fi

  # Append GIF to note
  ALT_TEXT="${QUERY} gif"
  printf '\n## Supporting Media\n\n![%s](%s)\n' "$ALT_TEXT" "$GIF_URL" \
    | grizzly add-text --id "$NOTE_ID" --mode append --token-file "$TOKEN_FILE"

  echo "  ✅ GIF appended: $GIF_URL"

  # Strip the tag from note content by rewriting without #待整理
  if echo "$CONTENT" | grep -q '#待整理'; then
    NEW_CONTENT=$(echo "$CONTENT" | sed 's/#待整理//g' | sed '/^$/N;/^\n$/d')
    echo "$NEW_CONTENT" | grizzly add-text --id "$NOTE_ID" --mode replace --token-file "$TOKEN_FILE"
    echo "  🏷️  Tag #$TAG removed"
  fi

  PROCESSED=$((PROCESSED + 1))
  sleep "$DELAY"
done <<< "$NOTE_IDS"

echo ""
echo "📊 Done: $PROCESSED processed, $SKIPPED skipped"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)