[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6GDYPBDP9PZRRWRK2HJTQV7":3,"skill-same:01M6GDYPBDP9PZRRWRK2HJTQV7":88},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":52,"linkOnly":53,"markdownContent":54,"markdownBody":55,"frontMatter":56,"refName":66,"commitSha":67,"skillDirectory":68,"forksCount":69,"repositoryHtmlUrl":70,"repositoryHomepageUrl":6,"ownerHtmlUrl":71,"ownerBlogUrl":6,"ownerDescription":72,"updatedAt":73,"install":74,"resources":87},"01M6GDYPBDP9PZRRWRK2HJTQV7","rest-graphql-debug",null,"Debug REST\u002FGraphQL APIs: status codes, auth, schemas, repro.","zapabob\u002FSkills","zapabob","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F130830376?v=4","01M6DBMYZAGRTFQDSQYW3W764P","峯岸 亮","01M6DBMZ3GKDSJ01NZ9XTZD7JD",2,"MIT","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Farchive\u002Fskill-md-reviewed\u002Fsha256\u002Fc0\u002Fc05365959c9352a98bb6758d2c73e34d0cafad4440db0f7c2c4fd6a2843fbd09\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51],"debug","rest graphql debug","REST","GraphQL","software-development","testing","test-driven-development","codes","software","systematic","repro","curl","api","integration","development","debugging","http","status","auth","test","2026-11-10 10:12:30",false,"---\r\nname: rest-graphql-debug\r\ndescription: \"Debug REST\u002FGraphQL APIs: status codes, auth, schemas, repro.\"\r\nversion: 1.2.0\r\nauthor: eren-karakus0\r\nlicense: MIT\r\nmetadata:\r\n  hermes:\r\n    tags: [api, rest, graphql, http, debugging, testing, curl, integration]\r\n    category: software-development\r\n    related_skills: [systematic-debugging, test-driven-development]\r\n---\r\n\r\n# API Testing & Debugging\r\n\r\nDrive REST and GraphQL diagnosis through Hermes tools — `terminal` for `curl`, `execute_code` for Python `requests`, `web_extract` for vendor docs. Isolate the failing layer before guessing at the fix.\r\n\r\n## When to Use\r\n\r\n- API returns unexpected status or body\r\n- Auth fails (401\u002F403 after token refresh, OAuth, API key)\r\n- Works in Postman but fails in code\r\n- Webhook \u002F callback integration debugging\r\n- Building or reviewing API integration tests\r\n- Rate limiting or pagination issues\r\n\r\nSkip for UI rendering, DB query tuning, or DNS\u002Ffirewall infra (escalate).\r\n\r\n## Core Principle\r\n\r\n**Isolate the layer, then fix.** A 200 OK can hide broken data. A 500 can mask a one-character auth typo. Walk the chain in order; never skip a step.\r\n\r\n```\r\n1. Connectivity   → can we reach the host at all?\r\n1.5 Timeouts      → connect-slow vs read-slow?\r\n2. TLS\u002FSSL        → cert valid and trusted?\r\n3. Auth           → credentials correct and unexpired?\r\n4. Request format → payload shape match server expectations?\r\n5. Response parse → does our code accept what came back?\r\n6. Semantics      → does the data mean what we assume?\r\n```\r\n\r\n## 5-Minute Quickstart\r\n\r\n### REST via terminal\r\n\r\n```python\r\n# Verbose request\u002Fresponse exchange\r\nterminal('curl -v https:\u002F\u002Fapi.example.com\u002Fusers\u002F1')\r\n\r\n# POST with JSON\r\nterminal(\"\"\"curl -X POST https:\u002F\u002Fapi.example.com\u002Fusers \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -H \"Authorization: Bearer $TOKEN\" \\\\\r\n  -d '{\"name\":\"test\",\"email\":\"test@example.com\"}'\"\"\")\r\n\r\n# Headers only\r\nterminal('curl -sI https:\u002F\u002Fapi.example.com\u002Fhealth')\r\n\r\n# Pretty-print JSON\r\nterminal('curl -s https:\u002F\u002Fapi.example.com\u002Fusers | python3 -m json.tool')\r\n```\r\n\r\n### GraphQL via terminal\r\n\r\n```python\r\nterminal(\"\"\"curl -X POST https:\u002F\u002Fapi.example.com\u002Fgraphql \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -H \"Authorization: Bearer $TOKEN\" \\\\\r\n  -d '{\"query\":\"{ user(id: 1) { name email } }\"}'\"\"\")\r\n```\r\n\r\n**GraphQL gotcha:** servers often return HTTP 200 even when the query failed. Always inspect the `errors` field regardless of status code:\r\n\r\n```python\r\nexecute_code('''\r\nimport os, requests\r\nresp = requests.post(\r\n    \"https:\u002F\u002Fapi.example.com\u002Fgraphql\",\r\n    json={\"query\": \"{ user(id: 1) { name email } }\"},\r\n    headers={\"Authorization\": f\"Bearer {os.environ['TOKEN']}\"},\r\n    timeout=10,\r\n)\r\ndata = resp.json()\r\nif data.get(\"errors\"):\r\n    for err in data[\"errors\"]:\r\n        print(f\"GraphQL error: {err['message']} (path: {err.get('path')})\")\r\nprint(data.get(\"data\"))\r\n''')\r\n```\r\n\r\n### Python (requests) via execute_code\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.get(\r\n    \"https:\u002F\u002Fapi.example.com\u002Fusers\u002F1\",\r\n    headers={\"Authorization\": \"Bearer \u003CTOKEN>\"},\r\n    timeout=(3.05, 30),  # (connect, read)\r\n)\r\nprint(resp.status_code, dict(resp.headers))\r\nprint(resp.text[:500])\r\n''')\r\n```\r\n\r\n## Layered Debug Flow\r\n\r\n### Step 1 — Connectivity\r\n\r\n```python\r\nterminal('nslookup api.example.com')\r\nterminal('curl -v --connect-timeout 5 https:\u002F\u002Fapi.example.com\u002Fhealth')\r\n```\r\n\r\nFailures: DNS not resolving, firewall, VPN required, proxy missing.\r\n\r\n### Step 1.5 — Timeouts\r\n\r\nDistinguish *can't reach* from *reaches but slow*:\r\n\r\n```python\r\nterminal('''curl -w \"dns:%{time_namelookup}s connect:%{time_connect}s tls:%{time_appconnect}s ttfb:%{time_starttransfer}s total:%{time_total}s\\\\n\" \\\\\r\n  -o \u002Fdev\u002Fnull -s https:\u002F\u002Fapi.example.com\u002Fendpoint''')\r\n```\r\n\r\nIn Python, always pass a tuple timeout — `requests` has no default and will hang forever:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nfrom requests.exceptions import ConnectTimeout, ReadTimeout\r\ntry:\r\n    requests.get(url, timeout=(3.05, 30))\r\nexcept ConnectTimeout:\r\n    print(\"Cannot reach host — DNS, firewall, VPN\")\r\nexcept ReadTimeout:\r\n    print(\"Connected but server is slow\")\r\n''')\r\n```\r\n\r\nDiagnosis: high `time_connect` is network\u002Ffirewall; high `time_starttransfer` with low `time_connect` is a slow server.\r\n\r\n### Step 2 — TLS\u002FSSL\r\n\r\n```python\r\nterminal('curl -vI https:\u002F\u002Fapi.example.com 2>&1 | grep -E \"SSL|subject|expire|issuer\"')\r\n```\r\n\r\nFailures: expired cert, self-signed, hostname mismatch, missing CA bundle. Use `-k` only for ad-hoc debug, never in code.\r\n\r\n### Step 3 — Authentication\r\n\r\n```python\r\n# Token validity check\r\nterminal('curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\\\\n\" -H \"Authorization: Bearer $TOKEN\" https:\u002F\u002Fapi.example.com\u002Fme')\r\n\r\n# Decode JWT exp claim — handles base64url padding correctly\r\nexecute_code('''\r\nimport json, base64, os\r\ntok = os.environ[\"TOKEN\"]\r\npayload = tok.split(\".\")[1]\r\npayload += \"=\" * (-len(payload) % 4)\r\nprint(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))\r\n''')\r\n```\r\n\r\nChecklist:\r\n- Token expired? (`exp` claim in JWT)\r\n- Right scheme? Bearer vs Basic vs Token vs `X-Api-Key`\r\n- Right environment? Staging key on prod is a classic\r\n- API key in header vs query param (`?api_key=…`)?\r\n\r\n### Step 4 — Request Format\r\n\r\n```python\r\nterminal(\"\"\"curl -v -X POST https:\u002F\u002Fapi.example.com\u002Fendpoint \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -d '{\"key\":\"value\"}' 2>&1\"\"\")\r\n```\r\n\r\n**Content-Type \u002F body mismatch — the silent 415\u002F400:**\r\n\r\n```python\r\n# WRONG — data= sends form-encoded, header lies\r\nrequests.post(url, data='{\"k\":\"v\"}', headers={\"Content-Type\": \"application\u002Fjson\"})\r\n\r\n# RIGHT — json= auto-sets header AND serializes\r\nrequests.post(url, json={\"k\": \"v\"})\r\n\r\n# WRONG — Accept says XML, code calls .json()\r\nrequests.get(url, headers={\"Accept\": \"text\u002Fxml\"})\r\n\r\n# RIGHT — let requests build multipart with boundary\r\nrequests.post(url, files={\"file\": open(\"doc.pdf\", \"rb\")})\r\n```\r\n\r\nCommon: form-encoded vs JSON, missing required fields, wrong HTTP method, unencoded query params.\r\n\r\n### Step 5 — Response Parsing\r\n\r\nAlways inspect content-type before calling `.json()`:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.post(url, json=payload, timeout=10)\r\nprint(f\"status={resp.status_code}\")\r\nprint(f\"headers={dict(resp.headers)}\")\r\nct = resp.headers.get(\"Content-Type\", \"\")\r\nif \"application\u002Fjson\" in ct:\r\n    print(resp.json())\r\nelse:\r\n    print(f\"unexpected content-type {ct!r}, body={resp.text[:500]!r}\")\r\n''')\r\n```\r\n\r\nFailures: HTML error page where JSON expected, empty body, wrong charset.\r\n\r\n### Step 6 — Semantic Validation\r\n\r\nParsed cleanly — but is the data *correct*?\r\n\r\n- Does `\"status\": \"active\"` mean what your code thinks?\r\n- ID in response matches the one requested?\r\n- Timestamps in expected timezone?\r\n- Pagination returning all results, or just page 1?\r\n\r\n## HTTP Status Playbook\r\n\r\n### 401 Unauthorized — credentials missing or invalid\r\n\r\n1. `Authorization` header actually present? (`curl -v` to confirm)\r\n2. Token correct and unexpired?\r\n3. Right auth scheme? (`Bearer` vs `Basic` vs `Token`)\r\n4. Some APIs use query param (`?api_key=…`) instead of header.\r\n\r\n### 403 Forbidden — authenticated but not authorized\r\n\r\n1. Token has the required scopes\u002Fpermissions?\r\n2. Resource owned by a different account?\r\n3. IP allowlist blocking you?\r\n4. CORS in browser? (check `Access-Control-Allow-Origin`)\r\n\r\n### 404 Not Found — resource doesn't exist or URL is wrong\r\n\r\n1. Path correct? (trailing slash, typo, version prefix)\r\n2. Resource ID exists?\r\n3. Right API version (`\u002Fv1\u002F` vs `\u002Fv2\u002F`)?\r\n4. Right base URL (staging vs prod)?\r\n\r\n### 409 Conflict — state collision\r\n\r\n1. Resource already exists (duplicate create)?\r\n2. Stale `ETag` \u002F `If-Match`?\r\n3. Concurrent modification by another process?\r\n\r\n### 422 Unprocessable Entity — valid JSON, invalid data\r\n\r\nThe error body usually names the bad fields. Check:\r\n- Field types (string vs int, date format)\r\n- Required vs optional\r\n- Enum values inside the allowed set\r\n\r\n### 429 Too Many Requests — rate limited\r\n\r\nCheck `Retry-After` and `X-RateLimit-*` headers. Exponential backoff:\r\n\r\n```python\r\nexecute_code('''\r\nimport time, requests\r\n\r\ndef with_backoff(method, url, **kwargs):\r\n    for attempt in range(5):\r\n        resp = requests.request(method, url, **kwargs)\r\n        if resp.status_code != 429:\r\n            return resp\r\n        wait = int(resp.headers.get(\"Retry-After\", 2 ** attempt))\r\n        time.sleep(wait)\r\n    return resp\r\n''')\r\n```\r\n\r\n### 5xx — server-side, usually not your fault\r\n\r\n- **500** — server bug. Capture correlation ID, file with provider.\r\n- **502** — upstream down. Backoff + retry.\r\n- **503** — overloaded \u002F maintenance. Check status page.\r\n- **504** — upstream timeout. Reduce payload or raise timeout.\r\n\r\nFor all 5xx: backoff with jitter, alert on persistence.\r\n\r\n## Pagination & Idempotency\r\n\r\n**Pagination.** Verify you're getting *all* results. Look for `next_cursor`, `next_page`, `total_count`. Two patterns:\r\n- Offset (`?limit=100&offset=200`) — simple, can skip items if data shifts.\r\n- Cursor (`?cursor=abc123`) — preferred for live or large datasets.\r\n\r\n**Idempotency.** For non-idempotent operations (POST), send `Idempotency-Key: \u003Cuuid>` so retries don't double-charge \u002F double-create. Mandatory for payments and orders.\r\n\r\n## Contract Validation\r\n\r\nCatch schema drift before it hits production:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\n\r\ndef validate_user(data: dict) -> list[str]:\r\n    errors = []\r\n    required = {\"id\": int, \"email\": str, \"created_at\": str}\r\n    for field, expected in required.items():\r\n        if field not in data:\r\n            errors.append(f\"missing field: {field}\")\r\n        elif not isinstance(data[field], expected):\r\n            errors.append(f\"{field}: want {expected.__name__}, got {type(data[field]).__name__}\")\r\n    return errors\r\n\r\nresp = requests.get(f\"{BASE}\u002Fusers\u002F1\", headers=HEADERS, timeout=10)\r\nissues = validate_user(resp.json())\r\nif issues:\r\n    print(f\"contract violations: {issues}\")\r\n''')\r\n```\r\n\r\nRun after API upgrades, when integrating new third parties, or in CI smoke tests.\r\n\r\n## Correlation IDs\r\n\r\nAlways capture the provider's request ID — fastest path to vendor support:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.post(url, json=payload, headers=headers, timeout=10)\r\nrequest_id = (\r\n    resp.headers.get(\"X-Request-Id\")\r\n    or resp.headers.get(\"X-Trace-Id\")\r\n    or resp.headers.get(\"CF-Ray\")  # Cloudflare\r\n)\r\nif resp.status_code >= 400:\r\n    print(f\"failed status={resp.status_code} req_id={request_id} ts={resp.headers.get('Date')}\")\r\n''')\r\n```\r\n\r\n**Vendor bug-report template:**\r\n\r\n```\r\nEndpoint:    POST \u002Fapi\u002Fv1\u002Forders\r\nRequest ID:  req_abc123xyz\r\nTimestamp:   2026-03-17T14:30:00Z\r\nStatus:      500\r\nExpected:    201 with order object\r\nActual:      500 {\"error\":\"internal server error\"}\r\nRepro:       curl -X POST … (auth: \u003CREDACTED>)\r\n```\r\n\r\n## Regression Test Template\r\n\r\nDrop this into `tests\u002F` and run via `terminal('pytest tests\u002Ftest_api_smoke.py -v')`:\r\n\r\n```python\r\nimport os, requests, pytest\r\n\r\nBASE_URL = os.environ.get(\"API_BASE_URL\", \"https:\u002F\u002Fapi.example.com\")\r\nTOKEN    = os.environ.get(\"API_TOKEN\", \"\")\r\nHEADERS  = {\"Authorization\": f\"Bearer {TOKEN}\"}\r\n\r\nclass TestAPISmoke:\r\n    def test_health(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fhealth\", timeout=5)\r\n        assert resp.status_code == 200\r\n\r\n    def test_list_users_returns_array(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fusers\", headers=HEADERS, timeout=10)\r\n        assert resp.status_code == 200\r\n        data = resp.json()\r\n        assert isinstance(data.get(\"data\", data), list)\r\n\r\n    def test_get_user_required_fields(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fusers\u002F1\", headers=HEADERS, timeout=10)\r\n        assert resp.status_code in (200, 404)\r\n        if resp.status_code == 200:\r\n            user = resp.json()\r\n            assert \"id\" in user and \"email\" in user\r\n\r\n    def test_invalid_auth_returns_401(self):\r\n        resp = requests.get(\r\n            f\"{BASE_URL}\u002Fusers\",\r\n            headers={\"Authorization\": \"Bearer invalid-token\"},\r\n            timeout=10,\r\n        )\r\n        assert resp.status_code == 401\r\n```\r\n\r\n## Security\r\n\r\n### Token handling\r\n- Never log full tokens. Redact: `Bearer \u003CREDACTED>`.\r\n- Never hardcode tokens in scripts. Read from env (`os.environ[\"API_TOKEN\"]`) or `${HERMES_HOME:-~\u002F.hermes}\u002F.env`.\r\n- Rotate immediately if a token surfaces in logs, error messages, or git history.\r\n\r\n### Safe logging\r\n\r\n```python\r\ndef redact_auth(headers: dict) -> dict:\r\n    sensitive = {\"authorization\", \"x-api-key\", \"cookie\", \"set-cookie\"}\r\n    return {k: (\"\u003CREDACTED>\" if k.lower() in sensitive else v) for k, v in headers.items()}\r\n```\r\n\r\n### Leak checklist\r\n\r\n- [ ] **Credentials in URLs.** API keys in query strings end up in server logs, browser history, referrer headers — use headers.\r\n- [ ] **PII in error responses.** `404 on \u002Fusers\u002F123` shouldn't reveal whether the user exists (enumeration).\r\n- [ ] **Stack traces in prod.** 500s shouldn't leak file paths, framework versions.\r\n- [ ] **Internal hostnames\u002FIPs.** `10.x.x.x`, `internal-api.corp.local` in error bodies.\r\n- [ ] **Tokens echoed back.** Some APIs include the auth token in error details. Verify they don't.\r\n- [ ] **Verbose `Server` \u002F `X-Powered-By`.** Stack-info leaks. Note for security review.\r\n\r\n## Hermes Tool Patterns\r\n\r\n### terminal — for curl, dig, openssl\r\n\r\n```python\r\nterminal('curl -sI https:\u002F\u002Fapi.example.com')\r\nterminal('openssl s_client -connect api.example.com:443 -servername api.example.com \u003C\u002Fdev\u002Fnull 2>\u002Fdev\u002Fnull | openssl x509 -noout -dates')\r\n```\r\n\r\n### execute_code — for multi-step Python flows\r\n\r\nWhen debugging spans auth → fetch → paginate → validate, use `execute_code`. Variables persist for the script, results print to stdout, no risk of token spam in your context:\r\n\r\n```python\r\nexecute_code('''\r\nimport os, requests\r\n\r\ntoken = os.environ[\"API_TOKEN\"]\r\nbase  = \"https:\u002F\u002Fapi.example.com\"\r\nH     = {\"Authorization\": f\"Bearer {token}\"}\r\n\r\n# 1. auth\r\nme = requests.get(f\"{base}\u002Fme\", headers=H, timeout=10)\r\nprint(f\"auth {me.status_code}\")\r\n\r\n# 2. paginate\r\nall_users, cursor = [], None\r\nwhile True:\r\n    params = {\"cursor\": cursor} if cursor else {}\r\n    r = requests.get(f\"{base}\u002Fusers\", headers=H, params=params, timeout=10)\r\n    body = r.json()\r\n    all_users.extend(body[\"data\"])\r\n    cursor = body.get(\"next_cursor\")\r\n    if not cursor:\r\n        break\r\nprint(f\"users={len(all_users)}\")\r\n''')\r\n```\r\n\r\n### web_extract — for vendor API docs\r\n\r\nPull the spec for the endpoint you're debugging instead of guessing:\r\n\r\n```python\r\nweb_extract(urls=[\"https:\u002F\u002Fdocs.example.com\u002Fapi\u002Fv1\u002Fusers\"])\r\n```\r\n\r\n### delegate_task — for full CRUD test sweeps\r\n\r\n```python\r\ndelegate_task(\r\n    goal=\"Test all CRUD endpoints for \u002Fapi\u002Fv1\u002Fusers\",\r\n    context=\"\"\"\r\nFollow the rest-graphql-debug skill (optional-skills\u002Fsoftware-development\u002Frest-graphql-debug).\r\nBase URL: https:\u002F\u002Fapi.example.com\r\nAuth: Bearer token from API_TOKEN env var.\r\n\r\nFor each verb (POST, GET, PATCH, DELETE):\r\n  - happy path: assert status + response schema\r\n  - error cases: 400, 404, 422\r\n  - log a repro curl for any failure (redact tokens)\r\n\r\nOutput: pass\u002Ffail per endpoint + correlation IDs for failures.\r\n\"\"\",\r\n    toolsets=[\"terminal\", \"file\"],\r\n)\r\n```\r\n\r\n## Output Format\r\n\r\nWhen reporting findings:\r\n\r\n```\r\n## Finding\r\nEndpoint: POST \u002Fapi\u002Fv1\u002Fusers\r\nStatus:   422 Unprocessable Entity\r\nReq ID:   req_abc123xyz\r\n\r\n## Repro\r\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Fapi\u002Fv1\u002Fusers \\\r\n  -H 'Content-Type: application\u002Fjson' \\\r\n  -H 'Authorization: Bearer \u003CREDACTED>' \\\r\n  -d '{\"name\":\"test\"}'\r\n\r\n## Root Cause\r\nMissing required field `email`. Server validation rejects before processing.\r\n\r\n## Fix\r\n-d '{\"name\":\"test\",\"email\":\"test@example.com\"}'\r\n```\r\n\r\n## Related\r\n\r\n- `systematic-debugging` — once the failing API layer is isolated, root-cause your code\r\n- `test-driven-development` — write the regression test before shipping the fix\r\n","# API Testing & Debugging\r\n\r\nDrive REST and GraphQL diagnosis through Hermes tools — `terminal` for `curl`, `execute_code` for Python `requests`, `web_extract` for vendor docs. Isolate the failing layer before guessing at the fix.\r\n\r\n## When to Use\r\n\r\n- API returns unexpected status or body\r\n- Auth fails (401\u002F403 after token refresh, OAuth, API key)\r\n- Works in Postman but fails in code\r\n- Webhook \u002F callback integration debugging\r\n- Building or reviewing API integration tests\r\n- Rate limiting or pagination issues\r\n\r\nSkip for UI rendering, DB query tuning, or DNS\u002Ffirewall infra (escalate).\r\n\r\n## Core Principle\r\n\r\n**Isolate the layer, then fix.** A 200 OK can hide broken data. A 500 can mask a one-character auth typo. Walk the chain in order; never skip a step.\r\n\r\n```\r\n1. Connectivity   → can we reach the host at all?\r\n1.5 Timeouts      → connect-slow vs read-slow?\r\n2. TLS\u002FSSL        → cert valid and trusted?\r\n3. Auth           → credentials correct and unexpired?\r\n4. Request format → payload shape match server expectations?\r\n5. Response parse → does our code accept what came back?\r\n6. Semantics      → does the data mean what we assume?\r\n```\r\n\r\n## 5-Minute Quickstart\r\n\r\n### REST via terminal\r\n\r\n```python\r\n# Verbose request\u002Fresponse exchange\r\nterminal('curl -v https:\u002F\u002Fapi.example.com\u002Fusers\u002F1')\r\n\r\n# POST with JSON\r\nterminal(\"\"\"curl -X POST https:\u002F\u002Fapi.example.com\u002Fusers \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -H \"Authorization: Bearer $TOKEN\" \\\\\r\n  -d '{\"name\":\"test\",\"email\":\"test@example.com\"}'\"\"\")\r\n\r\n# Headers only\r\nterminal('curl -sI https:\u002F\u002Fapi.example.com\u002Fhealth')\r\n\r\n# Pretty-print JSON\r\nterminal('curl -s https:\u002F\u002Fapi.example.com\u002Fusers | python3 -m json.tool')\r\n```\r\n\r\n### GraphQL via terminal\r\n\r\n```python\r\nterminal(\"\"\"curl -X POST https:\u002F\u002Fapi.example.com\u002Fgraphql \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -H \"Authorization: Bearer $TOKEN\" \\\\\r\n  -d '{\"query\":\"{ user(id: 1) { name email } }\"}'\"\"\")\r\n```\r\n\r\n**GraphQL gotcha:** servers often return HTTP 200 even when the query failed. Always inspect the `errors` field regardless of status code:\r\n\r\n```python\r\nexecute_code('''\r\nimport os, requests\r\nresp = requests.post(\r\n    \"https:\u002F\u002Fapi.example.com\u002Fgraphql\",\r\n    json={\"query\": \"{ user(id: 1) { name email } }\"},\r\n    headers={\"Authorization\": f\"Bearer {os.environ['TOKEN']}\"},\r\n    timeout=10,\r\n)\r\ndata = resp.json()\r\nif data.get(\"errors\"):\r\n    for err in data[\"errors\"]:\r\n        print(f\"GraphQL error: {err['message']} (path: {err.get('path')})\")\r\nprint(data.get(\"data\"))\r\n''')\r\n```\r\n\r\n### Python (requests) via execute_code\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.get(\r\n    \"https:\u002F\u002Fapi.example.com\u002Fusers\u002F1\",\r\n    headers={\"Authorization\": \"Bearer \u003CTOKEN>\"},\r\n    timeout=(3.05, 30),  # (connect, read)\r\n)\r\nprint(resp.status_code, dict(resp.headers))\r\nprint(resp.text[:500])\r\n''')\r\n```\r\n\r\n## Layered Debug Flow\r\n\r\n### Step 1 — Connectivity\r\n\r\n```python\r\nterminal('nslookup api.example.com')\r\nterminal('curl -v --connect-timeout 5 https:\u002F\u002Fapi.example.com\u002Fhealth')\r\n```\r\n\r\nFailures: DNS not resolving, firewall, VPN required, proxy missing.\r\n\r\n### Step 1.5 — Timeouts\r\n\r\nDistinguish *can't reach* from *reaches but slow*:\r\n\r\n```python\r\nterminal('''curl -w \"dns:%{time_namelookup}s connect:%{time_connect}s tls:%{time_appconnect}s ttfb:%{time_starttransfer}s total:%{time_total}s\\\\n\" \\\\\r\n  -o \u002Fdev\u002Fnull -s https:\u002F\u002Fapi.example.com\u002Fendpoint''')\r\n```\r\n\r\nIn Python, always pass a tuple timeout — `requests` has no default and will hang forever:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nfrom requests.exceptions import ConnectTimeout, ReadTimeout\r\ntry:\r\n    requests.get(url, timeout=(3.05, 30))\r\nexcept ConnectTimeout:\r\n    print(\"Cannot reach host — DNS, firewall, VPN\")\r\nexcept ReadTimeout:\r\n    print(\"Connected but server is slow\")\r\n''')\r\n```\r\n\r\nDiagnosis: high `time_connect` is network\u002Ffirewall; high `time_starttransfer` with low `time_connect` is a slow server.\r\n\r\n### Step 2 — TLS\u002FSSL\r\n\r\n```python\r\nterminal('curl -vI https:\u002F\u002Fapi.example.com 2>&1 | grep -E \"SSL|subject|expire|issuer\"')\r\n```\r\n\r\nFailures: expired cert, self-signed, hostname mismatch, missing CA bundle. Use `-k` only for ad-hoc debug, never in code.\r\n\r\n### Step 3 — Authentication\r\n\r\n```python\r\n# Token validity check\r\nterminal('curl -s -o \u002Fdev\u002Fnull -w \"%{http_code}\\\\n\" -H \"Authorization: Bearer $TOKEN\" https:\u002F\u002Fapi.example.com\u002Fme')\r\n\r\n# Decode JWT exp claim — handles base64url padding correctly\r\nexecute_code('''\r\nimport json, base64, os\r\ntok = os.environ[\"TOKEN\"]\r\npayload = tok.split(\".\")[1]\r\npayload += \"=\" * (-len(payload) % 4)\r\nprint(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))\r\n''')\r\n```\r\n\r\nChecklist:\r\n- Token expired? (`exp` claim in JWT)\r\n- Right scheme? Bearer vs Basic vs Token vs `X-Api-Key`\r\n- Right environment? Staging key on prod is a classic\r\n- API key in header vs query param (`?api_key=…`)?\r\n\r\n### Step 4 — Request Format\r\n\r\n```python\r\nterminal(\"\"\"curl -v -X POST https:\u002F\u002Fapi.example.com\u002Fendpoint \\\\\r\n  -H 'Content-Type: application\u002Fjson' \\\\\r\n  -d '{\"key\":\"value\"}' 2>&1\"\"\")\r\n```\r\n\r\n**Content-Type \u002F body mismatch — the silent 415\u002F400:**\r\n\r\n```python\r\n# WRONG — data= sends form-encoded, header lies\r\nrequests.post(url, data='{\"k\":\"v\"}', headers={\"Content-Type\": \"application\u002Fjson\"})\r\n\r\n# RIGHT — json= auto-sets header AND serializes\r\nrequests.post(url, json={\"k\": \"v\"})\r\n\r\n# WRONG — Accept says XML, code calls .json()\r\nrequests.get(url, headers={\"Accept\": \"text\u002Fxml\"})\r\n\r\n# RIGHT — let requests build multipart with boundary\r\nrequests.post(url, files={\"file\": open(\"doc.pdf\", \"rb\")})\r\n```\r\n\r\nCommon: form-encoded vs JSON, missing required fields, wrong HTTP method, unencoded query params.\r\n\r\n### Step 5 — Response Parsing\r\n\r\nAlways inspect content-type before calling `.json()`:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.post(url, json=payload, timeout=10)\r\nprint(f\"status={resp.status_code}\")\r\nprint(f\"headers={dict(resp.headers)}\")\r\nct = resp.headers.get(\"Content-Type\", \"\")\r\nif \"application\u002Fjson\" in ct:\r\n    print(resp.json())\r\nelse:\r\n    print(f\"unexpected content-type {ct!r}, body={resp.text[:500]!r}\")\r\n''')\r\n```\r\n\r\nFailures: HTML error page where JSON expected, empty body, wrong charset.\r\n\r\n### Step 6 — Semantic Validation\r\n\r\nParsed cleanly — but is the data *correct*?\r\n\r\n- Does `\"status\": \"active\"` mean what your code thinks?\r\n- ID in response matches the one requested?\r\n- Timestamps in expected timezone?\r\n- Pagination returning all results, or just page 1?\r\n\r\n## HTTP Status Playbook\r\n\r\n### 401 Unauthorized — credentials missing or invalid\r\n\r\n1. `Authorization` header actually present? (`curl -v` to confirm)\r\n2. Token correct and unexpired?\r\n3. Right auth scheme? (`Bearer` vs `Basic` vs `Token`)\r\n4. Some APIs use query param (`?api_key=…`) instead of header.\r\n\r\n### 403 Forbidden — authenticated but not authorized\r\n\r\n1. Token has the required scopes\u002Fpermissions?\r\n2. Resource owned by a different account?\r\n3. IP allowlist blocking you?\r\n4. CORS in browser? (check `Access-Control-Allow-Origin`)\r\n\r\n### 404 Not Found — resource doesn't exist or URL is wrong\r\n\r\n1. Path correct? (trailing slash, typo, version prefix)\r\n2. Resource ID exists?\r\n3. Right API version (`\u002Fv1\u002F` vs `\u002Fv2\u002F`)?\r\n4. Right base URL (staging vs prod)?\r\n\r\n### 409 Conflict — state collision\r\n\r\n1. Resource already exists (duplicate create)?\r\n2. Stale `ETag` \u002F `If-Match`?\r\n3. Concurrent modification by another process?\r\n\r\n### 422 Unprocessable Entity — valid JSON, invalid data\r\n\r\nThe error body usually names the bad fields. Check:\r\n- Field types (string vs int, date format)\r\n- Required vs optional\r\n- Enum values inside the allowed set\r\n\r\n### 429 Too Many Requests — rate limited\r\n\r\nCheck `Retry-After` and `X-RateLimit-*` headers. Exponential backoff:\r\n\r\n```python\r\nexecute_code('''\r\nimport time, requests\r\n\r\ndef with_backoff(method, url, **kwargs):\r\n    for attempt in range(5):\r\n        resp = requests.request(method, url, **kwargs)\r\n        if resp.status_code != 429:\r\n            return resp\r\n        wait = int(resp.headers.get(\"Retry-After\", 2 ** attempt))\r\n        time.sleep(wait)\r\n    return resp\r\n''')\r\n```\r\n\r\n### 5xx — server-side, usually not your fault\r\n\r\n- **500** — server bug. Capture correlation ID, file with provider.\r\n- **502** — upstream down. Backoff + retry.\r\n- **503** — overloaded \u002F maintenance. Check status page.\r\n- **504** — upstream timeout. Reduce payload or raise timeout.\r\n\r\nFor all 5xx: backoff with jitter, alert on persistence.\r\n\r\n## Pagination & Idempotency\r\n\r\n**Pagination.** Verify you're getting *all* results. Look for `next_cursor`, `next_page`, `total_count`. Two patterns:\r\n- Offset (`?limit=100&offset=200`) — simple, can skip items if data shifts.\r\n- Cursor (`?cursor=abc123`) — preferred for live or large datasets.\r\n\r\n**Idempotency.** For non-idempotent operations (POST), send `Idempotency-Key: \u003Cuuid>` so retries don't double-charge \u002F double-create. Mandatory for payments and orders.\r\n\r\n## Contract Validation\r\n\r\nCatch schema drift before it hits production:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\n\r\ndef validate_user(data: dict) -> list[str]:\r\n    errors = []\r\n    required = {\"id\": int, \"email\": str, \"created_at\": str}\r\n    for field, expected in required.items():\r\n        if field not in data:\r\n            errors.append(f\"missing field: {field}\")\r\n        elif not isinstance(data[field], expected):\r\n            errors.append(f\"{field}: want {expected.__name__}, got {type(data[field]).__name__}\")\r\n    return errors\r\n\r\nresp = requests.get(f\"{BASE}\u002Fusers\u002F1\", headers=HEADERS, timeout=10)\r\nissues = validate_user(resp.json())\r\nif issues:\r\n    print(f\"contract violations: {issues}\")\r\n''')\r\n```\r\n\r\nRun after API upgrades, when integrating new third parties, or in CI smoke tests.\r\n\r\n## Correlation IDs\r\n\r\nAlways capture the provider's request ID — fastest path to vendor support:\r\n\r\n```python\r\nexecute_code('''\r\nimport requests\r\nresp = requests.post(url, json=payload, headers=headers, timeout=10)\r\nrequest_id = (\r\n    resp.headers.get(\"X-Request-Id\")\r\n    or resp.headers.get(\"X-Trace-Id\")\r\n    or resp.headers.get(\"CF-Ray\")  # Cloudflare\r\n)\r\nif resp.status_code >= 400:\r\n    print(f\"failed status={resp.status_code} req_id={request_id} ts={resp.headers.get('Date')}\")\r\n''')\r\n```\r\n\r\n**Vendor bug-report template:**\r\n\r\n```\r\nEndpoint:    POST \u002Fapi\u002Fv1\u002Forders\r\nRequest ID:  req_abc123xyz\r\nTimestamp:   2026-03-17T14:30:00Z\r\nStatus:      500\r\nExpected:    201 with order object\r\nActual:      500 {\"error\":\"internal server error\"}\r\nRepro:       curl -X POST … (auth: \u003CREDACTED>)\r\n```\r\n\r\n## Regression Test Template\r\n\r\nDrop this into `tests\u002F` and run via `terminal('pytest tests\u002Ftest_api_smoke.py -v')`:\r\n\r\n```python\r\nimport os, requests, pytest\r\n\r\nBASE_URL = os.environ.get(\"API_BASE_URL\", \"https:\u002F\u002Fapi.example.com\")\r\nTOKEN    = os.environ.get(\"API_TOKEN\", \"\")\r\nHEADERS  = {\"Authorization\": f\"Bearer {TOKEN}\"}\r\n\r\nclass TestAPISmoke:\r\n    def test_health(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fhealth\", timeout=5)\r\n        assert resp.status_code == 200\r\n\r\n    def test_list_users_returns_array(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fusers\", headers=HEADERS, timeout=10)\r\n        assert resp.status_code == 200\r\n        data = resp.json()\r\n        assert isinstance(data.get(\"data\", data), list)\r\n\r\n    def test_get_user_required_fields(self):\r\n        resp = requests.get(f\"{BASE_URL}\u002Fusers\u002F1\", headers=HEADERS, timeout=10)\r\n        assert resp.status_code in (200, 404)\r\n        if resp.status_code == 200:\r\n            user = resp.json()\r\n            assert \"id\" in user and \"email\" in user\r\n\r\n    def test_invalid_auth_returns_401(self):\r\n        resp = requests.get(\r\n            f\"{BASE_URL}\u002Fusers\",\r\n            headers={\"Authorization\": \"Bearer invalid-token\"},\r\n            timeout=10,\r\n        )\r\n        assert resp.status_code == 401\r\n```\r\n\r\n## Security\r\n\r\n### Token handling\r\n- Never log full tokens. Redact: `Bearer \u003CREDACTED>`.\r\n- Never hardcode tokens in scripts. Read from env (`os.environ[\"API_TOKEN\"]`) or `${HERMES_HOME:-~\u002F.hermes}\u002F.env`.\r\n- Rotate immediately if a token surfaces in logs, error messages, or git history.\r\n\r\n### Safe logging\r\n\r\n```python\r\ndef redact_auth(headers: dict) -> dict:\r\n    sensitive = {\"authorization\", \"x-api-key\", \"cookie\", \"set-cookie\"}\r\n    return {k: (\"\u003CREDACTED>\" if k.lower() in sensitive else v) for k, v in headers.items()}\r\n```\r\n\r\n### Leak checklist\r\n\r\n- [ ] **Credentials in URLs.** API keys in query strings end up in server logs, browser history, referrer headers — use headers.\r\n- [ ] **PII in error responses.** `404 on \u002Fusers\u002F123` shouldn't reveal whether the user exists (enumeration).\r\n- [ ] **Stack traces in prod.** 500s shouldn't leak file paths, framework versions.\r\n- [ ] **Internal hostnames\u002FIPs.** `10.x.x.x`, `internal-api.corp.local` in error bodies.\r\n- [ ] **Tokens echoed back.** Some APIs include the auth token in error details. Verify they don't.\r\n- [ ] **Verbose `Server` \u002F `X-Powered-By`.** Stack-info leaks. Note for security review.\r\n\r\n## Hermes Tool Patterns\r\n\r\n### terminal — for curl, dig, openssl\r\n\r\n```python\r\nterminal('curl -sI https:\u002F\u002Fapi.example.com')\r\nterminal('openssl s_client -connect api.example.com:443 -servername api.example.com \u003C\u002Fdev\u002Fnull 2>\u002Fdev\u002Fnull | openssl x509 -noout -dates')\r\n```\r\n\r\n### execute_code — for multi-step Python flows\r\n\r\nWhen debugging spans auth → fetch → paginate → validate, use `execute_code`. Variables persist for the script, results print to stdout, no risk of token spam in your context:\r\n\r\n```python\r\nexecute_code('''\r\nimport os, requests\r\n\r\ntoken = os.environ[\"API_TOKEN\"]\r\nbase  = \"https:\u002F\u002Fapi.example.com\"\r\nH     = {\"Authorization\": f\"Bearer {token}\"}\r\n\r\n# 1. auth\r\nme = requests.get(f\"{base}\u002Fme\", headers=H, timeout=10)\r\nprint(f\"auth {me.status_code}\")\r\n\r\n# 2. paginate\r\nall_users, cursor = [], None\r\nwhile True:\r\n    params = {\"cursor\": cursor} if cursor else {}\r\n    r = requests.get(f\"{base}\u002Fusers\", headers=H, params=params, timeout=10)\r\n    body = r.json()\r\n    all_users.extend(body[\"data\"])\r\n    cursor = body.get(\"next_cursor\")\r\n    if not cursor:\r\n        break\r\nprint(f\"users={len(all_users)}\")\r\n''')\r\n```\r\n\r\n### web_extract — for vendor API docs\r\n\r\nPull the spec for the endpoint you're debugging instead of guessing:\r\n\r\n```python\r\nweb_extract(urls=[\"https:\u002F\u002Fdocs.example.com\u002Fapi\u002Fv1\u002Fusers\"])\r\n```\r\n\r\n### delegate_task — for full CRUD test sweeps\r\n\r\n```python\r\ndelegate_task(\r\n    goal=\"Test all CRUD endpoints for \u002Fapi\u002Fv1\u002Fusers\",\r\n    context=\"\"\"\r\nFollow the rest-graphql-debug skill (optional-skills\u002Fsoftware-development\u002Frest-graphql-debug).\r\nBase URL: https:\u002F\u002Fapi.example.com\r\nAuth: Bearer token from API_TOKEN env var.\r\n\r\nFor each verb (POST, GET, PATCH, DELETE):\r\n  - happy path: assert status + response schema\r\n  - error cases: 400, 404, 422\r\n  - log a repro curl for any failure (redact tokens)\r\n\r\nOutput: pass\u002Ffail per endpoint + correlation IDs for failures.\r\n\"\"\",\r\n    toolsets=[\"terminal\", \"file\"],\r\n)\r\n```\r\n\r\n## Output Format\r\n\r\nWhen reporting findings:\r\n\r\n```\r\n## Finding\r\nEndpoint: POST \u002Fapi\u002Fv1\u002Fusers\r\nStatus:   422 Unprocessable Entity\r\nReq ID:   req_abc123xyz\r\n\r\n## Repro\r\ncurl -X POST https:\u002F\u002Fapi.example.com\u002Fapi\u002Fv1\u002Fusers \\\r\n  -H 'Content-Type: application\u002Fjson' \\\r\n  -H 'Authorization: Bearer \u003CREDACTED>' \\\r\n  -d '{\"name\":\"test\"}'\r\n\r\n## Root Cause\r\nMissing required field `email`. Server validation rejects before processing.\r\n\r\n## Fix\r\n-d '{\"name\":\"test\",\"email\":\"test@example.com\"}'\r\n```\r\n\r\n## Related\r\n\r\n- `systematic-debugging` — once the failing API layer is isolated, root-cause your code\r\n- `test-driven-development` — write the regression test before shipping the fix\r\n",{"name":5,"author":57,"license":15,"version":58,"metadata":59,"description":7},"eren-karakus0","1.2.0",{"hermes":60},{"tags":61,"category":36,"related_skills":64},[44,62,63,48,47,37,43,45],"rest","graphql",[65,38],"systematic-debugging","main","0d1434f89f4bc6b07e9d272accd8c1013768820f","archive\u002Fskill-md-reviewed\u002Fsha256\u002Fc0\u002Fc05365959c9352a98bb6758d2c73e34d0cafad4440db0f7c2c4fd6a2843fbd09",0,"https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills","https:\u002F\u002Fgithub.com\u002Fzapabob","The Open University of JAPAN　2019-now\r\n\r\nTokyo University of Science　2007-2010","2026-10-26 16:29:01",{"source":8,"skillName":5,"commands":75,"prompt":85,"zipAvailable":86},[76,79,82],{"tool":77,"command":78},"npx","npx skills add zapabob\u002FSkills --skill rest-graphql-debug",{"tool":80,"command":81},"bunx","bunx skills add zapabob\u002FSkills --skill rest-graphql-debug",{"tool":83,"command":84},"pnpm","pnpm dlx skills add zapabob\u002FSkills --skill rest-graphql-debug","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Farchive\u002Fskill-md-reviewed\u002Fsha256\u002Fc0\u002Fc05365959c9352a98bb6758d2c73e34d0cafad4440db0f7c2c4fd6a2843fbd09\u002FSKILL.md",true,[],[89,118,151,178,205,221],{"skillNo":90,"name":91,"nameZh":6,"description":92,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":100,"publishedAt":52},"01M6GJTE1GESGGF9K028Y7753E","xurl","X\u002FTwitter via xurl CLI: post, search, DM, media, v2 API.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fxurl\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[95,96,99],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},[91,101,102,103,104,105,106,107,108,109,110,44,111,112,113,114,66,115,116,117],"openclaw","macos","cli","github","linux","windows","official","search","https","social-media","Media","hermes","hermes agent","twitter","social","via","post",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":135,"publishedAt":52},"01M6GJT8S1PXQ8WPXT1HSNBWH2","writing-plans","Write implementation plans: bite-sized tasks, paths, code.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwriting-plans\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[124,127,130,131,132],{"categoryNo":125,"categoryName":126,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":128,"categoryName":129,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},{"categoryNo":133,"categoryName":134,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000036","内容创作者",[136,137,138,139,102,140,105,141,106,142,143,46,144,145,146,147,148,149,150,38],"writing","writing plans","plans","design","review","workflow","documentation","tasks","code","planning","code-review","subagent","write","superpowers","paths",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":160,"publishedAt":52},"01M6GJST0H5X7WHBJ3BZK0DVKH","whisper","OpenAI's general-purpose speech recognition model. Supports 99 languages, transcription, translation to English, and language identification. Six model sizes from tiny (39M params) to large (1550M params). Use for speech-to-text, podcast transcription, or multilingual audio processing. Best for robust, multilingual ASR.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwhisper\u002Fvariants\u002Fhermes-agent-optional\u002FSKILL.md",[157],{"categoryNo":158,"categoryName":159,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[153,102,105,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177],"openai","speech","multimodal","Transformers","ASR","Audio","text","model","speech recognition","use","general","multilingual","translation","english","identification","processing","Audio Processing",{"skillNo":179,"name":180,"nameZh":6,"description":181,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":182,"categories":183,"keywords":187,"publishedAt":52},"01M6GJSMRQ8EETZTYMG72P70EA","weights-and-biases","W&B: log ML experiments, sweeps, model registry, dashboards.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fweights-and-biases\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[184],{"categoryNo":185,"categoryName":186,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000013","分析与可视化",[188,102,189,190,106,191,192,193,194,195,196,168,197,198,199,200,201,202,203,204],"weights and biases","tensorflow","experiments","pytorch","visualization","registry","real-time","tuning","dashboards","experiment","log","time","Experiment Tracking","Model Registry","Hyperparameter Tuning","WandB","Real-Time Visualization",{"skillNo":206,"name":207,"nameZh":6,"description":208,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":209,"categories":210,"keywords":212,"publishedAt":52},"01M6GJS19KDPRY112JTR326S06","watchers","Poll RSS, JSON APIs, and GitHub with watermark dedup.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fwatchers\u002Fvariants\u002Fhermes-agent-optional\u002FSKILL.md",[211],{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},[207,102,213,104,105,214,48,215,216,217,218,112,113,219,220],"terminal","automation","monitoring","cron","json","rss","devops","with",{"skillNo":222,"name":223,"nameZh":6,"description":224,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":225,"categories":226,"keywords":229,"publishedAt":52},"01M6GJRHJD2A6Q830SWF9W48BC","serving-llms-vllm","vLLM: high-throughput LLM serving, OpenAI API, quantization.","https:\u002F\u002Fgithub.com\u002Fzapabob\u002FSkills\u002Fblob\u002Fmain\u002Fregistry\u002Fskills\u002Fvllm\u002Fvariants\u002Fhermes-agent\u002FSKILL.md",[227,228],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},[230,231,232,105,233,161,234,235,236,164,237,44,238,239,240,241,242,243,244,245],"llms","vLLM","serving llms vllm","research","openai api","tensor","Inference Serving","Production","llm serving","High Throughput","PagedAttention","Continuous Batching","Quantization","Tensor Parallelism","high-throughput","throughput"]