claw-pet
Catch a pet or loot item by calling a configured remote pet backend API. Use when the user asks to catch a pet, fish for loot, try their luck, or trigger a remote catch action. Supports configured API calls, result formatting, and clear handling for pet, item, empty, and error responses.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-claw-pet
Repository
Skill path: skills/aileaile/claw-pet
Catch a pet or loot item by calling a configured remote pet backend API. Use when the user asks to catch a pet, fish for loot, try their luck, or trigger a remote catch action. Supports configured API calls, result formatting, and clear handling for pet, item, empty, and error responses.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Backend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install claw-pet into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding claw-pet to shared team environments
- Use claw-pet for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: claw-pet
description: >
Catch a pet or loot item by calling a configured remote pet backend API. Use when the user asks to catch a pet,
fish for loot, try their luck, or trigger a remote catch action. Supports configured API calls, result formatting,
and clear handling for pet, item, empty, and error responses.
---
# Claw Pet
Use this skill when a user wants you to trigger a remote `catch` action against a pet backend that they control.
## Workflow
1. Load runtime config from environment variables first, then local `_meta.json`.
2. Verify `CATCH_API_URL` and `API_KEY` are present.
3. Send a `POST` request with JSON body `{"action":"catch"}`.
4. Parse the response and classify it as one of:
- `pet`
- `item`
- `empty`
- `error`
5. Return a friendly summary without exposing secrets.
## Configuration
Provide these values before use:
1. Environment variables:
- `CATCH_API_URL`
- `API_KEY`
2. Or skill-local `_meta.json` for development/testing
Keep `_meta.json` free of production secrets before sharing or publishing the skill.
## Expected Backend Behavior
The backend should:
- accept `POST` requests to `CATCH_API_URL`
- authenticate with `Authorization: Bearer <API_KEY>`
- return JSON only
- return either a pet result, item result, empty result, or structured error
Read `references/api.md` for the response contract.
## Trigger Examples
This skill should trigger for requests such as:
- "去抓一只宠物"
- "Catch me a pet"
- "Try to fish"
- "Go catch something for me"
- "帮我抽一下今天的宠物"
## Script
Use `scripts/catch_pet.py` for the actual API call. Prefer the script over re-implementing the request flow inline.
## Output Rules
- If a pet is caught, include rarity, name, level, and any short flavor text.
- If an item is caught, include item name, rarity, quantity, and description if present.
- If nothing is caught, say so plainly.
- If the backend returns an error or malformed payload, explain the failure category briefly.
## Publish Notes
This skill is only the agent-side caller. It does not bundle the backend service.
Users who install it must configure their own reachable backend endpoint and API key.
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/api.md
```markdown
# Catch Pet API Reference
Use this file to keep the backend contract aligned with the skill implementation.
## Request
- **Method:** `POST`
- **URL:** `CATCH_API_URL`
- **Headers:**
- `Authorization: Bearer <API_KEY>`
- `Content-Type: application/json`
- `Accept: application/json`
- **Body:**
```json
{
"action": "catch"
}
```
## Supported Success Shapes
### Pet result
```json
{
"message": "You caught something rare!",
"pet": {
"name": "Mochi",
"rarity": "Epic",
"level": 12,
"description": "A sleepy purple creature"
}
}
```
### Item result
```json
{
"message": "Not a pet this time, but still nice.",
"item": {
"name": "Golden Bait",
"rarity": "Rare",
"quantity": 2,
"description": "Useful for future catches"
}
}
```
### Empty result
```json
{
"message": "The water ripples, but nothing bites.",
"empty": true
}
```
## Error Shapes
### HTTP error with structured body
```json
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many catch attempts"
}
}
```
### HTTP error with top-level message
```json
{
"message": "Unauthorized"
}
```
## Notes for Backend Alignment
- Keep pet payload in `pet` and item payload in `item`.
- Return `empty: true` for no-drop cases when possible.
- Include a short `message` for user-facing flavor text.
- Return JSON for both success and error responses.
- If the backend uses a different shape, update `scripts/catch_pet.py` classification logic accordingly.
```
### scripts/catch_pet.py
```python
#!/usr/bin/env python3
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any, Dict, Optional, Tuple
SKILL_DIR = Path(__file__).resolve().parent.parent
META_PATH = SKILL_DIR / "_meta.json"
class ConfigError(RuntimeError):
pass
def load_meta_config() -> Dict[str, Any]:
if not META_PATH.exists():
return {}
with META_PATH.open("r", encoding="utf-8") as f:
return json.load(f)
def load_config() -> Dict[str, str]:
meta = load_meta_config()
config = {
"CATCH_API_URL": os.environ.get("CATCH_API_URL") or meta.get("CATCH_API_URL") or "",
"API_KEY": os.environ.get("API_KEY") or meta.get("API_KEY") or "",
}
if not config["CATCH_API_URL"]:
raise ConfigError("Missing CATCH_API_URL. Set env var CATCH_API_URL or add it to _meta.json.")
if not config["API_KEY"]:
raise ConfigError("Missing API_KEY. Set env var API_KEY or add it to _meta.json.")
return config
def build_request(url: str, api_key: str) -> urllib.request.Request:
payload = json.dumps({"action": "catch"}).encode("utf-8")
return urllib.request.Request(
url,
data=payload,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
},
method="POST",
)
def fetch_result(config: Dict[str, str]) -> Tuple[int, Dict[str, Any]]:
req = build_request(config["CATCH_API_URL"], config["API_KEY"])
try:
with urllib.request.urlopen(req, timeout=20) as resp:
status = getattr(resp, "status", 200)
body = resp.read().decode("utf-8")
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", errors="replace")
parsed = safe_json(body)
return e.code, parsed or {"error": {"message": body or str(e), "code": e.code}}
except urllib.error.URLError as e:
raise RuntimeError(f"Network error: {e.reason}") from e
parsed = safe_json(body)
if parsed is None:
raise RuntimeError("Backend returned non-JSON response")
return status, parsed
def safe_json(text: str) -> Optional[Dict[str, Any]]:
try:
data = json.loads(text)
except json.JSONDecodeError:
return None
return data if isinstance(data, dict) else {"data": data}
def classify(data: Dict[str, Any], status: int) -> Dict[str, Any]:
pet = data.get("pet")
item = data.get("item")
message = data.get("message") or data.get("detail") or data.get("status") or ""
error = data.get("error")
if status >= 400 or error:
return {
"kind": "error",
"message": message or extract_error_message(error) or f"Request failed with HTTP {status}",
"status": status,
"raw": data,
}
if isinstance(pet, dict) and pet:
return {"kind": "pet", "message": message, "pet": pet, "raw": data}
if isinstance(item, dict) and item:
return {"kind": "item", "message": message, "item": item, "raw": data}
if data.get("empty") is True or data.get("result") == "empty" or not (pet or item):
return {"kind": "empty", "message": message or "Nothing was caught this time.", "raw": data}
return {"kind": "error", "message": "Unrecognized response shape", "status": status, "raw": data}
def extract_error_message(error: Any) -> str:
if isinstance(error, dict):
return str(error.get("message") or error.get("detail") or error.get("code") or "")
return str(error or "")
def format_pet(pet: Dict[str, Any], message: str) -> str:
rarity = pet.get("rarity", "Unknown rarity")
name = pet.get("name", "Unknown pet")
level = pet.get("level", "?")
extra = pet.get("description") or pet.get("title") or ""
base = f"{message + ' ' if message else ''}Caught a {rarity} pet: {name} (Lv.{level})."
return f"{base} {extra}".strip()
def format_item(item: Dict[str, Any], message: str) -> str:
rarity = item.get("rarity", "Unknown rarity")
name = item.get("name", "Unknown item")
qty = item.get("quantity") or item.get("count") or 1
extra = item.get("description") or ""
base = f"{message + ' ' if message else ''}Caught an item: {name} x{qty} [{rarity}]."
return f"{base} {extra}".strip()
def format_result(result: Dict[str, Any]) -> str:
kind = result["kind"]
if kind == "pet":
return format_pet(result["pet"], result.get("message", ""))
if kind == "item":
return format_item(result["item"], result.get("message", ""))
if kind == "empty":
return result.get("message", "Nothing was caught this time.")
return f"Catch failed: {result.get('message', 'Unknown error')}"
def main() -> int:
debug = "--debug" in sys.argv
try:
config = load_config()
status, data = fetch_result(config)
result = classify(data, status)
if debug:
print(json.dumps({"status": status, "result": result}, ensure_ascii=False, indent=2))
else:
print(format_result(result))
return 0 if result["kind"] != "error" else 1
except ConfigError as e:
print(f"Config error: {e}", file=sys.stderr)
return 2
except Exception as e:
print(f"Runtime error: {e}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "aileaile",
"slug": "claw-pet",
"displayName": "claw-pet",
"latest": {
"version": "1.2.0",
"publishedAt": 1773245529621,
"commit": "https://github.com/openclaw/skills/commit/ccb1314e6da3096bdeb68f7952fbd43faee06ecf"
},
"history": []
}
```