From 600b9426c57c3d478cf9890ab7dcd53a82b60e21 Mon Sep 17 00:00:00 2001 From: Arya Rizky Date: Sat, 9 May 2026 01:16:32 +0700 Subject: [PATCH 1/4] fix(browser): use querySelectorAll instead of document.images for get_images document.images is unreliable on SPA and JavaScript-heavy pages, causing 'SyntaxError: Unexpected end of input' when the eval bridge processes expressions that rely on it. Change to document.querySelectorAll('img') which returns a static NodeList that Array.from can reliably convert. Also wrapped in an IIFE to match the working workaround pattern from browser_console. Fixes: #22012 Closes: #22012 --- skills/apple/DESCRIPTION.md | 3 - skills/apple/apple-notes/SKILL.md | 90 ----------- skills/apple/apple-reminders/SKILL.md | 98 ------------ skills/apple/findmy/SKILL.md | 131 ---------------- skills/apple/imessage/SKILL.md | 102 ------------ skills/gaming/pokemon-player/SKILL.md | 215 -------------------------- skills/smart-home/DESCRIPTION.md | 3 - skills/smart-home/openhue/SKILL.md | 108 ------------- skills/yuanbao/SKILL.md | 107 ------------- tools/browser_tool.py | 21 ++- 10 files changed, 13 insertions(+), 865 deletions(-) delete mode 100644 skills/apple/DESCRIPTION.md delete mode 100644 skills/apple/apple-notes/SKILL.md delete mode 100644 skills/apple/apple-reminders/SKILL.md delete mode 100644 skills/apple/findmy/SKILL.md delete mode 100644 skills/apple/imessage/SKILL.md delete mode 100644 skills/gaming/pokemon-player/SKILL.md delete mode 100644 skills/smart-home/DESCRIPTION.md delete mode 100644 skills/smart-home/openhue/SKILL.md delete mode 100644 skills/yuanbao/SKILL.md diff --git a/skills/apple/DESCRIPTION.md b/skills/apple/DESCRIPTION.md deleted file mode 100644 index 392bd2d87c617..0000000000000 --- a/skills/apple/DESCRIPTION.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -description: Apple/macOS-specific skills — iMessage, Reminders, Notes, FindMy, and macOS automation. These skills only load on macOS systems. ---- diff --git a/skills/apple/apple-notes/SKILL.md b/skills/apple/apple-notes/SKILL.md deleted file mode 100644 index 020f0d641df40..0000000000000 --- a/skills/apple/apple-notes/SKILL.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -name: apple-notes -description: "Manage Apple Notes via memo CLI: create, search, edit." -version: 1.0.0 -author: Hermes Agent -license: MIT -platforms: [macos] -metadata: - hermes: - tags: [Notes, Apple, macOS, note-taking] - related_skills: [obsidian] -prerequisites: - commands: [memo] ---- - -# Apple Notes - -Use `memo` to manage Apple Notes directly from the terminal. Notes sync across all Apple devices via iCloud. - -## Prerequisites - -- **macOS** with Notes.app -- Install: `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo` -- Grant Automation access to Notes.app when prompted (System Settings → Privacy → Automation) - -## When to Use - -- User asks to create, view, or search Apple Notes -- Saving information to Notes.app for cross-device access -- Organizing notes into folders -- Exporting notes to Markdown/HTML - -## When NOT to Use - -- Obsidian vault management → use the `obsidian` skill -- Bear Notes → separate app (not supported here) -- Quick agent-only notes → use the `memory` tool instead - -## Quick Reference - -### View Notes - -```bash -memo notes # List all notes -memo notes -f "Folder Name" # Filter by folder -memo notes -s "query" # Search notes (fuzzy) -``` - -### Create Notes - -```bash -memo notes -a # Interactive editor -memo notes -a "Note Title" # Quick add with title -``` - -### Edit Notes - -```bash -memo notes -e # Interactive selection to edit -``` - -### Delete Notes - -```bash -memo notes -d # Interactive selection to delete -``` - -### Move Notes - -```bash -memo notes -m # Move note to folder (interactive) -``` - -### Export Notes - -```bash -memo notes -ex # Export to HTML/Markdown -``` - -## Limitations - -- Cannot edit notes containing images or attachments -- Interactive prompts require terminal access (use pty=true if needed) -- macOS only — requires Apple Notes.app - -## Rules - -1. Prefer Apple Notes when user wants cross-device sync (iPhone/iPad/Mac) -2. Use the `memory` tool for agent-internal notes that don't need to sync -3. Use the `obsidian` skill for Markdown-native knowledge management diff --git a/skills/apple/apple-reminders/SKILL.md b/skills/apple/apple-reminders/SKILL.md deleted file mode 100644 index 37c4fa74fd848..0000000000000 --- a/skills/apple/apple-reminders/SKILL.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -name: apple-reminders -description: "Apple Reminders via remindctl: add, list, complete." -version: 1.0.0 -author: Hermes Agent -license: MIT -platforms: [macos] -metadata: - hermes: - tags: [Reminders, tasks, todo, macOS, Apple] -prerequisites: - commands: [remindctl] ---- - -# Apple Reminders - -Use `remindctl` to manage Apple Reminders directly from the terminal. Tasks sync across all Apple devices via iCloud. - -## Prerequisites - -- **macOS** with Reminders.app -- Install: `brew install steipete/tap/remindctl` -- Grant Reminders permission when prompted -- Check: `remindctl status` / Request: `remindctl authorize` - -## When to Use - -- User mentions "reminder" or "Reminders app" -- Creating personal to-dos with due dates that sync to iOS -- Managing Apple Reminders lists -- User wants tasks to appear on their iPhone/iPad - -## When NOT to Use - -- Scheduling agent alerts → use the cronjob tool instead -- Calendar events → use Apple Calendar or Google Calendar -- Project task management → use GitHub Issues, Notion, etc. -- If user says "remind me" but means an agent alert → clarify first - -## Quick Reference - -### View Reminders - -```bash -remindctl # Today's reminders -remindctl today # Today -remindctl tomorrow # Tomorrow -remindctl week # This week -remindctl overdue # Past due -remindctl all # Everything -remindctl 2026-01-04 # Specific date -``` - -### Manage Lists - -```bash -remindctl list # List all lists -remindctl list Work # Show specific list -remindctl list Projects --create # Create list -remindctl list Work --delete # Delete list -``` - -### Create Reminders - -```bash -remindctl add "Buy milk" -remindctl add --title "Call mom" --list Personal --due tomorrow -remindctl add --title "Meeting prep" --due "2026-02-15 09:00" -``` - -### Complete / Delete - -```bash -remindctl complete 1 2 3 # Complete by ID -remindctl delete 4A83 --force # Delete by ID -``` - -### Output Formats - -```bash -remindctl today --json # JSON for scripting -remindctl today --plain # TSV format -remindctl today --quiet # Counts only -``` - -## Date Formats - -Accepted by `--due` and date filters: -- `today`, `tomorrow`, `yesterday` -- `YYYY-MM-DD` -- `YYYY-MM-DD HH:mm` -- ISO 8601 (`2026-01-04T12:34:56Z`) - -## Rules - -1. When user says "remind me", clarify: Apple Reminders (syncs to phone) vs agent cronjob alert -2. Always confirm reminder content and due date before creating -3. Use `--json` for programmatic parsing diff --git a/skills/apple/findmy/SKILL.md b/skills/apple/findmy/SKILL.md deleted file mode 100644 index e2bed384d1383..0000000000000 --- a/skills/apple/findmy/SKILL.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -name: findmy -description: "Track Apple devices/AirTags via FindMy.app on macOS." -version: 1.0.0 -author: Hermes Agent -license: MIT -platforms: [macos] -metadata: - hermes: - tags: [FindMy, AirTag, location, tracking, macOS, Apple] ---- - -# Find My (Apple) - -Track Apple devices and AirTags via the FindMy.app on macOS. Since Apple doesn't -provide a CLI for FindMy, this skill uses AppleScript to open the app and -screen capture to read device locations. - -## Prerequisites - -- **macOS** with Find My app and iCloud signed in -- Devices/AirTags already registered in Find My -- Screen Recording permission for terminal (System Settings → Privacy → Screen Recording) -- **Optional but recommended**: Install `peekaboo` for better UI automation: - `brew install steipete/tap/peekaboo` - -## When to Use - -- User asks "where is my [device/cat/keys/bag]?" -- Tracking AirTag locations -- Checking device locations (iPhone, iPad, Mac, AirPods) -- Monitoring pet or item movement over time (AirTag patrol routes) - -## Method 1: AppleScript + Screenshot (Basic) - -### Open FindMy and Navigate - -```bash -# Open Find My app -osascript -e 'tell application "FindMy" to activate' - -# Wait for it to load -sleep 3 - -# Take a screenshot of the Find My window -screencapture -w -o /tmp/findmy.png -``` - -Then use `vision_analyze` to read the screenshot: -``` -vision_analyze(image_url="/tmp/findmy.png", question="What devices/items are shown and what are their locations?") -``` - -### Switch Between Tabs - -```bash -# Switch to Devices tab -osascript -e ' -tell application "System Events" - tell process "FindMy" - click button "Devices" of toolbar 1 of window 1 - end tell -end tell' - -# Switch to Items tab (AirTags) -osascript -e ' -tell application "System Events" - tell process "FindMy" - click button "Items" of toolbar 1 of window 1 - end tell -end tell' -``` - -## Method 2: Peekaboo UI Automation (Recommended) - -If `peekaboo` is installed, use it for more reliable UI interaction: - -```bash -# Open Find My -osascript -e 'tell application "FindMy" to activate' -sleep 3 - -# Capture and annotate the UI -peekaboo see --app "FindMy" --annotate --path /tmp/findmy-ui.png - -# Click on a specific device/item by element ID -peekaboo click --on B3 --app "FindMy" - -# Capture the detail view -peekaboo image --app "FindMy" --path /tmp/findmy-detail.png -``` - -Then analyze with vision: -``` -vision_analyze(image_url="/tmp/findmy-detail.png", question="What is the location shown for this device/item? Include address and coordinates if visible.") -``` - -## Workflow: Track AirTag Location Over Time - -For monitoring an AirTag (e.g., tracking a cat's patrol route): - -```bash -# 1. Open FindMy to Items tab -osascript -e 'tell application "FindMy" to activate' -sleep 3 - -# 2. Click on the AirTag item (stay on page — AirTag only updates when page is open) - -# 3. Periodically capture location -while true; do - screencapture -w -o /tmp/findmy-$(date +%H%M%S).png - sleep 300 # Every 5 minutes -done -``` - -Analyze each screenshot with vision to extract coordinates, then compile a route. - -## Limitations - -- FindMy has **no CLI or API** — must use UI automation -- AirTags only update location while the FindMy page is actively displayed -- Location accuracy depends on nearby Apple devices in the FindMy network -- Screen Recording permission required for screenshots -- AppleScript UI automation may break across macOS versions - -## Rules - -1. Keep FindMy app in the foreground when tracking AirTags (updates stop when minimized) -2. Use `vision_analyze` to read screenshot content — don't try to parse pixels -3. For ongoing tracking, use a cronjob to periodically capture and log locations -4. Respect privacy — only track devices/items the user owns diff --git a/skills/apple/imessage/SKILL.md b/skills/apple/imessage/SKILL.md deleted file mode 100644 index 82df6a6ecf8a8..0000000000000 --- a/skills/apple/imessage/SKILL.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -name: imessage -description: Send and receive iMessages/SMS via the imsg CLI on macOS. -version: 1.0.0 -author: Hermes Agent -license: MIT -platforms: [macos] -metadata: - hermes: - tags: [iMessage, SMS, messaging, macOS, Apple] -prerequisites: - commands: [imsg] ---- - -# iMessage - -Use `imsg` to read and send iMessage/SMS via macOS Messages.app. - -## Prerequisites - -- **macOS** with Messages.app signed in -- Install: `brew install steipete/tap/imsg` -- Grant Full Disk Access for terminal (System Settings → Privacy → Full Disk Access) -- Grant Automation permission for Messages.app when prompted - -## When to Use - -- User asks to send an iMessage or text message -- Reading iMessage conversation history -- Checking recent Messages.app chats -- Sending to phone numbers or Apple IDs - -## When NOT to Use - -- Telegram/Discord/Slack/WhatsApp messages → use the appropriate gateway channel -- Group chat management (adding/removing members) → not supported -- Bulk/mass messaging → always confirm with user first - -## Quick Reference - -### List Chats - -```bash -imsg chats --limit 10 --json -``` - -### View History - -```bash -# By chat ID -imsg history --chat-id 1 --limit 20 --json - -# With attachments info -imsg history --chat-id 1 --limit 20 --attachments --json -``` - -### Send Messages - -```bash -# Text only -imsg send --to "+14155551212" --text "Hello!" - -# With attachment -imsg send --to "+14155551212" --text "Check this out" --file /path/to/image.jpg - -# Force iMessage or SMS -imsg send --to "+14155551212" --text "Hi" --service imessage -imsg send --to "+14155551212" --text "Hi" --service sms -``` - -### Watch for New Messages - -```bash -imsg watch --chat-id 1 --attachments -``` - -## Service Options - -- `--service imessage` — Force iMessage (requires recipient has iMessage) -- `--service sms` — Force SMS (green bubble) -- `--service auto` — Let Messages.app decide (default) - -## Rules - -1. **Always confirm recipient and message content** before sending -2. **Never send to unknown numbers** without explicit user approval -3. **Verify file paths** exist before attaching -4. **Don't spam** — rate-limit yourself - -## Example Workflow - -User: "Text mom that I'll be late" - -```bash -# 1. Find mom's chat -imsg chats --limit 20 --json | jq '.[] | select(.displayName | contains("Mom"))' - -# 2. Confirm with user: "Found Mom at +1555123456. Send 'I'll be late' via iMessage?" - -# 3. Send after confirmation -imsg send --to "+1555123456" --text "I'll be late" -``` diff --git a/skills/gaming/pokemon-player/SKILL.md b/skills/gaming/pokemon-player/SKILL.md deleted file mode 100644 index 2a505cca6e6b5..0000000000000 --- a/skills/gaming/pokemon-player/SKILL.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -name: pokemon-player -description: "Play Pokemon via headless emulator + RAM reads." -tags: [gaming, pokemon, emulator, pyboy, gameplay, gameboy] ---- -# Pokemon Player - -Play Pokemon games via headless emulation using the `pokemon-agent` package. - -## When to Use -- User says "play pokemon", "start pokemon", "pokemon game" -- User asks about Pokemon Red, Blue, Yellow, FireRed, etc. -- User wants to watch an AI play Pokemon -- User references a ROM file (.gb, .gbc, .gba) - -## Startup Procedure - -### 1. First-time setup (clone, venv, install) -The repo is NousResearch/pokemon-agent on GitHub. Clone it, then -set up a Python 3.10+ virtual environment. Use uv (preferred for speed) -to create the venv and install the package in editable mode with the -pyboy extra. If uv is not available, fall back to python3 -m venv + pip. - -On this machine it is already set up at /home/teknium/pokemon-agent -with a venv ready — just cd there and source .venv/bin/activate. - -You also need a ROM file. Ask the user for theirs. On this machine -one exists at roms/pokemon_red.gb inside that directory. -NEVER download or provide ROM files — always ask the user. - -### 2. Start the game server -From inside the pokemon-agent directory with the venv activated, run -pokemon-agent serve with --rom pointing to the ROM and --port 9876. -Run it in the background with &. -To resume from a saved game, add --load-state with the save name. -Wait 4 seconds for startup, then verify with GET /health. - -### 3. Set up live dashboard for user to watch -Use an SSH reverse tunnel via localhost.run so the user can view -the dashboard in their browser. Connect with ssh, forwarding local -port 9876 to remote port 80 on nokey@localhost.run. Redirect output -to a log file, wait 10 seconds, then grep the log for the .lhr.life -URL. Give the user the URL with /dashboard/ appended. -The tunnel URL changes each time — give the user the new one if restarted. - -## Save and Load - -### When to save -- Every 15-20 turns of gameplay -- ALWAYS before gym battles, rival encounters, or risky fights -- Before entering a new town or dungeon -- Before any action you are unsure about - -### How to save -POST /save with a descriptive name. Good examples: -before_brock, route1_start, mt_moon_entrance, got_cut - -### How to load -POST /load with the save name. - -### List available saves -GET /saves returns all saved states. - -### Loading on server startup -Use --load-state flag when starting the server to auto-load a save. -This is faster than loading via the API after startup. - -## The Gameplay Loop - -### Step 1: OBSERVE — check state AND take a screenshot -GET /state for position, HP, battle, dialog. -GET /screenshot and save to /tmp/pokemon.png, then use vision_analyze. -Always do BOTH — RAM state gives numbers, vision gives spatial awareness. - -### Step 2: ORIENT -- Dialog/text on screen → advance it -- In battle → fight or run -- Party hurt → head to Pokemon Center -- Near objective → navigate carefully - -### Step 3: DECIDE -Priority: dialog > battle > heal > story objective > training > explore - -### Step 4: ACT — move 2-4 steps max, then re-check -POST /action with a SHORT action list (2-4 actions, not 10-15). - -### Step 5: VERIFY — screenshot after every move sequence -Take a screenshot and use vision_analyze to confirm you moved where -intended. This is the MOST IMPORTANT step. Without vision you WILL get lost. - -### Step 6: RECORD progress to memory with PKM: prefix - -### Step 7: SAVE periodically - -## Action Reference -- press_a — confirm, talk, select -- press_b — cancel, close menu -- press_start — open game menu -- walk_up/down/left/right — move one tile -- hold_b_N — hold B for N frames (use for speeding through text) -- wait_60 — wait about 1 second (60 frames) -- a_until_dialog_end — press A repeatedly until dialog clears - -## Critical Tips from Experience - -### USE VISION CONSTANTLY -- Take a screenshot every 2-4 movement steps -- The RAM state tells you position and HP but NOT what is around you -- Ledges, fences, signs, building doors, NPCs — only visible via screenshot -- Ask the vision model specific questions: "what is one tile north of me?" -- When stuck, always screenshot before trying random directions - -### Warp Transitions Need Extra Wait Time -When walking through a door or stairs, the screen fades to black during -the map transition. You MUST wait for it to complete. Add 2-3 wait_60 -actions after any door/stair warp. Without waiting, the position reads -as stale and you will think you are still in the old map. - -### Building Exit Trap -When you exit a building, you appear directly IN FRONT of the door. -If you walk north, you go right back inside. ALWAYS sidestep first -by walking left or right 2 tiles, then proceed in your intended direction. - -### Dialog Handling -Gen 1 text scrolls slowly letter-by-letter. To speed through dialog, -hold B for 120 frames then press A. Repeat as needed. Holding B makes -text display at max speed. Then press A to advance to the next line. -The a_until_dialog_end action checks the RAM dialog flag, but this flag -does not catch ALL text states. If dialog seems stuck, use the manual -hold_b + press_a pattern instead and verify via screenshot. - -### Ledges Are One-Way -Ledges (small cliff edges) can only be jumped DOWN (south), never climbed -UP (north). If blocked by a ledge going north, you must go left or right -to find the gap around it. Use vision to identify which direction the -gap is. Ask the vision model explicitly. - -### Navigation Strategy -- Move 2-4 steps at a time, then screenshot to check position -- When entering a new area, screenshot immediately to orient -- Ask the vision model "which direction to [destination]?" -- If stuck for 3+ attempts, screenshot and re-evaluate completely -- Do not spam 10-15 movements — you will overshoot or get stuck - -### Running from Wild Battles -On the battle menu, RUN is bottom-right. To reach it from the default -cursor position (FIGHT, top-left): press down then right to move cursor -to RUN, then press A. Wrap with hold_b to speed through text/animations. - -### Battling (FIGHT) -On the battle menu FIGHT is top-left (default cursor position). -Press A to enter move selection, A again to use the first move. -Then hold B to speed through attack animations and text. - -## Battle Strategy - -### Decision Tree -1. Want to catch? → Weaken then throw Poke Ball -2. Wild you don't need? → RUN -3. Type advantage? → Use super-effective move -4. No advantage? → Use strongest STAB move -5. Low HP? → Switch or use Potion - -### Gen 1 Type Chart (key matchups) -- Water beats Fire, Ground, Rock -- Fire beats Grass, Bug, Ice -- Grass beats Water, Ground, Rock -- Electric beats Water, Flying -- Ground beats Fire, Electric, Rock, Poison -- Psychic beats Fighting, Poison (dominant in Gen 1!) - -### Gen 1 Quirks -- Special stat = both offense AND defense for special moves -- Psychic type is overpowered (Ghost moves bugged) -- Critical hits based on Speed stat -- Wrap/Bind prevent opponent from acting -- Focus Energy bug: REDUCES crit rate instead of raising it - -## Memory Conventions -| Prefix | Purpose | Example | -|--------|---------|---------| -| PKM:OBJECTIVE | Current goal | Get Parcel from Viridian Mart | -| PKM:MAP | Navigation knowledge | Viridian: mart is northeast | -| PKM:STRATEGY | Battle/team plans | Need Grass type before Misty | -| PKM:PROGRESS | Milestone tracker | Beat rival, heading to Viridian | -| PKM:STUCK | Stuck situations | Ledge at y=28 go right to bypass | -| PKM:TEAM | Team notes | Squirtle Lv6, Tackle + Tail Whip | - -## Progression Milestones -- Choose starter -- Deliver Parcel from Viridian Mart, receive Pokedex -- Boulder Badge — Brock (Rock) → use Water/Grass -- Cascade Badge — Misty (Water) → use Grass/Electric -- Thunder Badge — Lt. Surge (Electric) → use Ground -- Rainbow Badge — Erika (Grass) → use Fire/Ice/Flying -- Soul Badge — Koga (Poison) → use Ground/Psychic -- Marsh Badge — Sabrina (Psychic) → hardest gym -- Volcano Badge — Blaine (Fire) → use Water/Ground -- Earth Badge — Giovanni (Ground) → use Water/Grass/Ice -- Elite Four → Champion! - -## Stopping Play -1. Save the game with a descriptive name via POST /save -2. Update memory with PKM:PROGRESS -3. Tell user: "Game saved as [name]! Say 'play pokemon' to resume." -4. Kill the server and tunnel background processes - -## Pitfalls -- NEVER download or provide ROM files -- Do NOT send more than 4-5 actions without checking vision -- Always sidestep after exiting buildings before going north -- Always add wait_60 x2-3 after door/stair warps -- Dialog detection via RAM is unreliable — verify with screenshots -- Save BEFORE risky encounters -- The tunnel URL changes each time you restart it diff --git a/skills/smart-home/DESCRIPTION.md b/skills/smart-home/DESCRIPTION.md deleted file mode 100644 index c308c214914e6..0000000000000 --- a/skills/smart-home/DESCRIPTION.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -description: Skills for controlling smart home devices — lights, switches, sensors, and home automation systems. ---- diff --git a/skills/smart-home/openhue/SKILL.md b/skills/smart-home/openhue/SKILL.md deleted file mode 100644 index ac830214291f0..0000000000000 --- a/skills/smart-home/openhue/SKILL.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -name: openhue -description: "Control Philips Hue lights, scenes, rooms via OpenHue CLI." -version: 1.0.0 -author: community -license: MIT -metadata: - hermes: - tags: [Smart-Home, Hue, Lights, IoT, Automation] - homepage: https://www.openhue.io/cli -prerequisites: - commands: [openhue] ---- - -# OpenHue CLI - -Control Philips Hue lights and scenes via a Hue Bridge from the terminal. - -## Prerequisites - -```bash -# Linux (pre-built binary) -curl -sL https://github.com/openhue/openhue-cli/releases/latest/download/openhue-linux-amd64 -o ~/.local/bin/openhue && chmod +x ~/.local/bin/openhue - -# macOS -brew install openhue/cli/openhue-cli -``` - -First run requires pressing the button on your Hue Bridge to pair. The bridge must be on the same local network. - -## When to Use - -- "Turn on/off the lights" -- "Dim the living room lights" -- "Set a scene" or "movie mode" -- Controlling specific Hue rooms, zones, or individual bulbs -- Adjusting brightness, color, or color temperature - -## Common Commands - -### List Resources - -```bash -openhue get light # List all lights -openhue get room # List all rooms -openhue get scene # List all scenes -``` - -### Control Lights - -```bash -# Turn on/off -openhue set light "Bedroom Lamp" --on -openhue set light "Bedroom Lamp" --off - -# Brightness (0-100) -openhue set light "Bedroom Lamp" --on --brightness 50 - -# Color temperature (warm to cool: 153-500 mirek) -openhue set light "Bedroom Lamp" --on --temperature 300 - -# Color (by name or hex) -openhue set light "Bedroom Lamp" --on --color red -openhue set light "Bedroom Lamp" --on --rgb "#FF5500" -``` - -### Control Rooms - -```bash -# Turn off entire room -openhue set room "Bedroom" --off - -# Set room brightness -openhue set room "Bedroom" --on --brightness 30 -``` - -### Scenes - -```bash -openhue set scene "Relax" --room "Bedroom" -openhue set scene "Concentrate" --room "Office" -``` - -## Quick Presets - -```bash -# Bedtime (dim warm) -openhue set room "Bedroom" --on --brightness 20 --temperature 450 - -# Work mode (bright cool) -openhue set room "Office" --on --brightness 100 --temperature 250 - -# Movie mode (dim) -openhue set room "Living Room" --on --brightness 10 - -# Everything off -openhue set room "Bedroom" --off -openhue set room "Office" --off -openhue set room "Living Room" --off -``` - -## Notes - -- Bridge must be on the same local network as the machine running Hermes -- First run requires physically pressing the button on the Hue Bridge to authorize -- Colors only work on color-capable bulbs (not white-only models) -- Light and room names are case-sensitive — use `openhue get light` to check exact names -- Works great with cron jobs for scheduled lighting (e.g. dim at bedtime, bright at wake) diff --git a/skills/yuanbao/SKILL.md b/skills/yuanbao/SKILL.md deleted file mode 100644 index b2f79aecb6feb..0000000000000 --- a/skills/yuanbao/SKILL.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: yuanbao -description: "Yuanbao (元宝) groups: @mention users, query info/members." -version: 1.0.0 -metadata: - hermes: - tags: [yuanbao, mention, at, group, members, 元宝, 派, 艾特] - related_skills: [] ---- - -# Yuanbao Group Interaction - -## CRITICAL: How Messaging Works - -**Your text reply IS the message sent to the group/user.** The gateway automatically delivers your response text to the chat. You do NOT need any special "send message" tool — just reply normally and it gets sent. - -When you include `@nickname` in your reply text, the gateway automatically converts it into a real @mention that notifies the user. This is built-in — you have full @mention capability. - -**NEVER say you cannot send messages or @mention users. NEVER suggest the user do it manually. NEVER add disclaimers about permissions. Just reply with the text you want sent.** - -## Available Tools - -| Tool | When to use | -|------|------------| -| `yb_query_group_info` | Query group name, owner, member count | -| `yb_query_group_members` | Find a user, list bots, list all members, or get nickname for @mention | -| `yb_send_dm` | Send a private/direct message (DM / 私信) to a user, with optional media files | - -## @Mention Workflow - -When you need to @mention / 艾特 someone: - -1. Call `yb_query_group_members` with `action="find"`, `name=""`, `mention=true` -2. Get the exact nickname from the response -3. Include `@nickname` in your reply text — the gateway handles the rest - -Example: user says "帮我艾特元宝" - -Step 1 — tool call: -```json -{ "group_code": "328306697", "action": "find", "name": "元宝", "mention": true } -``` - -Step 2 — your reply (this gets sent to the group with a working @mention): -``` -@元宝 你好,有人找你! -``` - -**That's it.** No extra explanation needed. Keep it short and natural. - -**Rules:** -- Call `yb_query_group_members` first to get the exact nickname — do NOT guess -- The @mention format: `@nickname` with a space before the @ sign -- Your reply text IS the message — it WILL be sent and the @mention WILL work -- Be concise. Do NOT explain how @mention works to the user. - -## Send DM (Private Message) Workflow - -When someone asks to send a private message / 私信 / DM to a user: - -1. Call `yb_send_dm` with `group_code`, `name` (target user's name), and `message` -2. The tool automatically finds the user and sends the DM -3. Report the result to the user - -Example: user says "给 @用户aea3 私信发一个 hello" - -```json -yb_send_dm({ "group_code": "535168412", "name": "用户aea3", "message": "hello" }) -``` - -Example with media: user says "给 @用户aea3 私信发一张图片" - -```json -yb_send_dm({ - "group_code": "535168412", - "name": "用户aea3", - "message": "Here is the image", - "media_files": [{"path": "/tmp/photo.jpg"}] -}) -``` - -**Rules:** -- Extract `group_code` from the current chat_id (e.g. `group:535168412` → `535168412`) -- If you already know the user_id, pass it directly via the `user_id` parameter to skip lookup -- If multiple users match the name, the tool returns candidates — ask the user to clarify -- Do NOT use `send_message` tool for Yuanbao DMs — use `yb_send_dm` instead -- Supports media: images (.jpg/.png/.gif/.webp/.bmp) sent as image messages, other files as documents - -## Query Group Info - -```json -yb_query_group_info({ "group_code": "328306697" }) -``` - -## Query Members - -| Action | Description | -|--------|-------------| -| `find` | Search by name (partial match, case-insensitive) | -| `list_bots` | List bots and Yuanbao AI assistants | -| `list_all` | List all members | - -## Notes - -- `group_code` comes from chat_id: `group:328306697` → `328306697` -- Groups are called "派 (Pai)" in the Yuanbao app -- Member roles: `user`, `yuanbao_ai`, `bot` diff --git a/tools/browser_tool.py b/tools/browser_tool.py index c8cdedcf0b1fe..d020ee796b42a 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -2719,14 +2719,19 @@ def browser_get_images(task_id: Optional[str] = None) -> str: effective_task_id = _last_session_key(task_id or "default") # Use eval to run JavaScript that extracts images - js_code = """JSON.stringify( - [...document.images].map(img => ({ - src: img.src, - alt: img.alt || '', - width: img.naturalWidth, - height: img.naturalHeight - })).filter(img => img.src && !img.src.startsWith('data:')) - )""" + # Note: document.images is unreliable on SPA/js-heavy pages. + # document.querySelectorAll('img') is more robust. + js_code = """(() => { + const imgs = Array.from(document.querySelectorAll('img')); + return JSON.stringify( + imgs.map(img => ({ + src: img.src || '', + alt: img.alt || '', + width: img.naturalWidth || 0, + height: img.naturalHeight || 0 + })).filter(img => img.src && !img.src.startsWith('data:')) + ); + })()""" result = _run_browser_command(effective_task_id, "eval", [js_code]) From 6df4b1e8d53f95a0629a75eb26dce1e118206b73 Mon Sep 17 00:00:00 2001 From: Arya Rizky Date: Sat, 9 May 2026 01:27:23 +0700 Subject: [PATCH 2/4] fix(minimax-oauth): handle expired_in as ms timestamp MiniMax returns expired_in as a Unix-millisecond timestamp, not a seconds duration. This caused 'year 58382 is out of range' errors when the raw value was treated as seconds. Apply the same defensive heuristic already used by _minimax_poll_token at both OAuth login and refresh sites. Fixes: #22020 --- hermes_cli/auth.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 3fa726d6a7edf..730faa14c74b4 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -4873,7 +4873,14 @@ def _minimax_oauth_login( ) now = datetime.now(timezone.utc) - expires_in_s = int(token_data["expired_in"]) + raw_expired = int(token_data["expired_in"]) + # MiniMax returns expired_in as a Unix-ms timestamp (not a seconds duration). + # Use the same defensive heuristic as _minimax_poll_token. + now_ms = int(now.timestamp() * 1000) + if raw_expired > now_ms // 2: + expires_in_s = max(1, (raw_expired / 1000.0) - now.timestamp()) + else: + expires_in_s = max(1, raw_expired) expires_at = now.timestamp() + expires_in_s auth_state = { @@ -4949,7 +4956,12 @@ def _refresh_minimax_oauth_state( relogin_required=True, ) now_dt = datetime.now(timezone.utc) - expires_in_s = int(payload["expired_in"]) + raw_expired = int(payload["expired_in"]) + now_ms = int(now_dt.timestamp() * 1000) + if raw_expired > now_ms // 2: + expires_in_s = max(1, (raw_expired / 1000.0) - now_dt.timestamp()) + else: + expires_in_s = max(1, raw_expired) new_state = dict(state) new_state.update({ "access_token": payload["access_token"], From 126d37874bc863be3c6933ea8c858dea4ee5e05a Mon Sep 17 00:00:00 2001 From: Arya Rizky Date: Sat, 9 May 2026 01:31:06 +0700 Subject: [PATCH 3/4] ci: add Python 3.12/3.13 matrix, uv caching, ripgrep for e2e, integration tests, timeout bump - Add Python 3.12 and 3.13 to test matrix with fail-fast: false (#22005) - Add actions/cache for uv cache and .venv (#22004) - Install ripgrep in e2e job (#22003) - Remove --ignore=tests/integration so integration tests run (#22002) - Bump e2e timeout from 10 to 15 minutes (#22006) Fixes: #22002, #22003, #22004, #22005, #22006 --- .github/workflows/tests.yml | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a92afdfa40dd9..866a396429f91 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,6 +24,10 @@ jobs: test: runs-on: ubuntu-latest timeout-minutes: 20 + strategy: + matrix: + python-version: ['3.11', '3.12', '3.13'] + fail-fast: false steps: - name: Checkout code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -34,32 +38,44 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 - - name: Set up Python 3.11 - run: uv python install 3.11 + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Cache uv + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 + with: + path: | + ~/.cache/uv + .venv + key: ${{ runner.os }}-python-${{ matrix.python-version }}-uv-${{ hashFiles('pyproject.toml', 'uv.lock') }} + restore-keys: | + ${{ runner.os }}-python-${{ matrix.python-version }}-uv- - name: Install dependencies run: | - uv venv .venv --python 3.11 + uv venv .venv --python ${{ matrix.python-version }} source .venv/bin/activate uv pip install -e ".[all,dev]" - - name: Run tests + - name: Run tests (unit + integration) run: | source .venv/bin/activate - python -m pytest tests/ -q --ignore=tests/integration --ignore=tests/e2e --tb=short -n auto + python -m pytest tests/ -q --ignore=tests/e2e --tb=short -n auto env: - # Ensure tests don't accidentally call real APIs OPENROUTER_API_KEY: "" OPENAI_API_KEY: "" NOUS_API_KEY: "" e2e: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y ripgrep + - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 From 4af2d0d21ef56b9263ecb0f5005e5f6dde014a03 Mon Sep 17 00:00:00 2001 From: Arya Rizky Date: Sat, 9 May 2026 01:40:32 +0700 Subject: [PATCH 4/4] feat(i18n): add Afrikaans locale (af.yaml) Add Afrikaans translations for CLI approval prompts and gateway slash-command replies. Follows the same catalog structure as other locale files. Afrikaans shares significant vocabulary with Dutch. Fixes: #21961 --- locales/af.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 locales/af.yaml diff --git a/locales/af.yaml b/locales/af.yaml new file mode 100644 index 0000000000000..3d5722ea01345 --- /dev/null +++ b/locales/af.yaml @@ -0,0 +1,25 @@ +# Hermes static-message catalog -- Afrikaans +# +# See en.yaml for scope and contribution requirements. + +approval: + dangerous_header: "⚠️ GEVAARLIKE OPDRAG: {description}" + choose_long: " [o]nce | [s]ession | [a]lways | [d]eny" + choose_short: " [o]nce | [s]ession | [d]eny" + prompt_long: " Keuse [o/s/a/D]: " + prompt_short: " Keuse [o/s/D]: " + timeout: " ⏱ Tyd verstreke - opdrag geweier" + allowed_once: " ✓ Eenmalig toegelaat" + allowed_session: " ✓ Toegelaat vir hierdie sessie" + allowed_always: " ✓ By permanente toelaatlys gevoeg" + denied: " ✗ Geweier" + cancelled: " ✗ Gekanselleer" + blocklist_message: "Hierdie opdrag is op die onvoorwaardelike blokkeerlys en kan nie goedgekeur word nie." + +gateway: + approval_expired: "⚠️ Goedkeuring verval (agent wag nie meer nie). Vra die agent om weer te probeer." + draining: "⏳ Besig om {count} aktiewe agent(e) te dreineer voor herbegin..." + goal_cleared: "✓ Doelwit skoongemaak." + no_active_goal: "Geen aktiewe doelwit nie." + config_read_failed: "⚠️ Kon nie config.yaml lees nie: {error}" + config_save_failed: "⚠️ Kon nie config stoor nie: {error}"