[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CPEJNZEX0Y2WQF77YXKTJD":3,"skill-same:01M6CPEJNZEX0Y2WQF77YXKTJD":87},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":25,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":63,"commitSha":64,"skillDirectory":65,"forksCount":66,"repositoryHtmlUrl":67,"repositoryHomepageUrl":68,"ownerHtmlUrl":69,"ownerBlogUrl":70,"ownerDescription":71,"updatedAt":72,"install":73,"resources":86},"01M6CPEJNZEX0Y2WQF77YXKTJD","location",null,"Get the user's current GPS location. Auto-detects the best backend\nfor the host OS:\n\n  macOS  →  corelocationcli  (CoreLocation — cell \u002F WiFi \u002F GPS)\n  Linux  →  gdbus + geoclue2  (kernel-driver \u002F WiFi \u002F Mozilla MLS)\n  fallback → IP geolocation   (ipapi.co — city-level only)\n\nReturns coordinates \u002F address \u002F city \u002F full details — request what\nyou need with the `format` arg.\n\nWhen to use this vs the {{location}} system prompt context:\n  - {{location}} is set from $KINCLAW_LOCATION env var — \"where the\n    user generally is\" (home \u002F office). Static, free, always available.\n  - This skill reads the OS location service in real time — \"where the\n    user is right NOW\". Use when user might be traveling \u002F driving \u002F\n    out, or when meter-level precision matters.\n\nFirst invocation prompts for permission (macOS Location Services \u002F\nLinux Geoclue per-app authorization through xdg-desktop-portal).\nGrant it for whichever process kinclaw runs in.\n\nFirst-time setup:\n  macOS:  brew install corelocationcli\n  Linux:  apt install geoclue-2.0 jq curl    # or pacman -S \u002F dnf install\n          (gdbus ships with glib2 — already present on every desktop Linux)","LocalKinAI\u002Fkinclaw","LocalKinAI","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F228881863?v=4","01M6CPDM368JVRW78EPV2NDABD","01M6CPDM7CKTKCXE8JD7K3658Q",9,"Apache-2.0","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Flocation\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[5,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"fix","done","query","back","default","Echo","up","only","auto","valid","command","dialog","branch","option","wifi","SET","ci","create","cleanup","2026-11-10 10:12:30",false,"---\nname: location\ndescription: |\n  Get the user's current GPS location. Auto-detects the best backend\n  for the host OS:\n\n    macOS  →  corelocationcli  (CoreLocation — cell \u002F WiFi \u002F GPS)\n    Linux  →  gdbus + geoclue2  (kernel-driver \u002F WiFi \u002F Mozilla MLS)\n    fallback → IP geolocation   (ipapi.co — city-level only)\n\n  Returns coordinates \u002F address \u002F city \u002F full details — request what\n  you need with the `format` arg.\n\n  When to use this vs the {{location}} system prompt context:\n    - {{location}} is set from $KINCLAW_LOCATION env var — \"where the\n      user generally is\" (home \u002F office). Static, free, always available.\n    - This skill reads the OS location service in real time — \"where the\n      user is right NOW\". Use when user might be traveling \u002F driving \u002F\n      out, or when meter-level precision matters.\n\n  First invocation prompts for permission (macOS Location Services \u002F\n  Linux Geoclue per-app authorization through xdg-desktop-portal).\n  Grant it for whichever process kinclaw runs in.\n\n  First-time setup:\n    macOS:  brew install corelocationcli\n    Linux:  apt install geoclue-2.0 jq curl    # or pacman -S \u002F dnf install\n            (gdbus ships with glib2 — already present on every desktop Linux)\ncommand:\n  - sh\n  - -c\n  - |\n    FORMAT=\"$1\"\n    [ \"$FORMAT\" = \"\" ] && FORMAT=\"coords\"\n\n    # ──── Backend detection ────────────────────────────────────────\n    # Prefer corelocationcli on macOS (CoreLocation is the most accurate\n    # and lowest-latency option there). Linux uses geoclue2 via gdbus\n    # (universal — ships with glib2). IP fallback is last resort for\n    # both, useful for servers \u002F CI \u002F Pi without GPS hardware.\n\n    OS=$(uname -s)\n    BACKEND=\"\"\n    if [ \"$OS\" = \"Darwin\" ] && command -v corelocationcli >\u002Fdev\u002Fnull 2>&1; then\n      BACKEND=\"corelocationcli\"\n    elif [ \"$OS\" = \"Linux\" ] && command -v gdbus >\u002Fdev\u002Fnull 2>&1; then\n      BACKEND=\"geoclue\"\n    elif command -v curl >\u002Fdev\u002Fnull 2>&1; then\n      BACKEND=\"ip\"\n    else\n      echo \"no location backend available\" >&2\n      case \"$OS\" in\n        Darwin) echo \"  install: brew install corelocationcli\" >&2 ;;\n        Linux)  echo \"  install: apt install geoclue-2.0 jq curl\" >&2 ;;\n      esac\n      exit 1\n    fi\n\n    # ──── Backend: corelocationcli (macOS) ─────────────────────────\n    # NOTE: do NOT pass `-once`. It is not a valid flag in v4.x, and the\n    # tool silently swallows unknown args — including the `-format` that\n    # follows — then exits 0 printing the default \"lat lon\". Every branch\n    # below would return bare coords for every requested format. It prints\n    # once anyway unless `--watch` is given. (Verified against v4.0.7.)\n    if [ \"$BACKEND\" = \"corelocationcli\" ]; then\n      case \"$FORMAT\" in\n        coords)\n          LL=$(corelocationcli --format \"%latitude,%longitude\")\n          printf 'GPS coordinates (lat,lon): %s\\n' \"$LL\"\n          ;;\n        address)\n          ADDR=$(corelocationcli --format \"%address\")\n          printf 'GPS address: %s\\n' \"$ADDR\"\n          ;;\n        city)\n          CITY=$(corelocationcli --format \"%locality\")\n          printf 'GPS city: %s\\n' \"$CITY\"\n          ;;\n        full)\n          printf 'GPS (full reading from CoreLocation):\\n'\n          # One line with '|' between fields, made lines here: written as lines inside the\n          # format, the fields fell outside this YAML block and the whole skill stopped loading.\n          corelocationcli --format \"latitude:    %latitude|longitude:   %longitude|altitude:    %altitude|accuracy_m:  %h_accuracy|address:     %address|time:        %time\" \\\n            | tr '|' '\\n' | sed 's\u002F^\u002F  \u002F'\n          ;;\n        *)\n          echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n          exit 1\n          ;;\n      esac\n      exit 0\n    fi\n\n    # ──── Backend: geoclue2 via gdbus (Linux) ──────────────────────\n    # Geoclue2 D-Bus dance:\n    #   1) Manager.GetClient → client object path\n    #   2) Set DesktopId + RequestedAccuracyLevel on the client\n    #   3) Client.Start → triggers actual fix\n    #   4) Read Location property → returns location object path\n    #   5) Read Latitude \u002F Longitude \u002F Description from location object\n    #   6) Client.Stop → release the resource\n    if [ \"$BACKEND\" = \"geoclue\" ]; then\n      gdbus_call() {\n        gdbus call --system --dest org.freedesktop.GeoClue2 \"$@\" 2>\u002Fdev\u002Fnull\n      }\n\n      # 1) Create a Client\n      CLIENT_OUT=$(gdbus_call --object-path \u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FManager \\\n                              --method org.freedesktop.GeoClue2.Manager.GetClient)\n      if [ -z \"$CLIENT_OUT\" ]; then\n        echo \"Geoclue2 not running. Start with: systemctl --user start geoclue.service\" >&2\n        echo \"Falling back to IP geolocation...\" >&2\n        BACKEND=\"ip\"\n      else\n        CLIENT_PATH=$(printf '%s' \"$CLIENT_OUT\" | sed -n \"s|.*'\\\\(\u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FClient\u002F[^']*\\\\)'.*|\\\\1|p\")\n        if [ -z \"$CLIENT_PATH\" ]; then\n          echo \"Geoclue2: couldn't parse client path from: $CLIENT_OUT\" >&2\n          BACKEND=\"ip\"\n        fi\n      fi\n\n      if [ \"$BACKEND\" = \"geoclue\" ]; then\n        # 2) Set DesktopId (required for newer geoclue) + accuracy 8 = exact\n        gdbus_call --object-path \"$CLIENT_PATH\" \\\n          --method org.freedesktop.DBus.Properties.Set \\\n          org.freedesktop.GeoClue2.Client DesktopId '\u003C\"kinclaw\">' >\u002Fdev\u002Fnull\n\n        gdbus_call --object-path \"$CLIENT_PATH\" \\\n          --method org.freedesktop.DBus.Properties.Set \\\n          org.freedesktop.GeoClue2.Client RequestedAccuracyLevel '\u003Cuint32 8>' >\u002Fdev\u002Fnull\n\n        # 3) Start\n        gdbus_call --object-path \"$CLIENT_PATH\" \\\n          --method org.freedesktop.GeoClue2.Client.Start >\u002Fdev\u002Fnull\n\n        # 4) Poll for Location property (up to ~10s)\n        LOC_PATH=\"\"\n        for i in 1 2 3 4 5 6 7 8 9 10; do\n          LP_OUT=$(gdbus_call --object-path \"$CLIENT_PATH\" \\\n            --method org.freedesktop.DBus.Properties.Get \\\n            org.freedesktop.GeoClue2.Client Location)\n          LOC_PATH=$(printf '%s' \"$LP_OUT\" | sed -n \"s|.*'\\\\(\u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FClient\u002F[^']*Location[^']*\\\\)'.*|\\\\1|p\")\n          [ -n \"$LOC_PATH\" ] && [ \"$LOC_PATH\" != \"\u002F\" ] && break\n          sleep 1\n        done\n\n        if [ -z \"$LOC_PATH\" ] || [ \"$LOC_PATH\" = \"\u002F\" ]; then\n          # 6) Cleanup\n          gdbus_call --object-path \"$CLIENT_PATH\" \\\n            --method org.freedesktop.GeoClue2.Client.Stop >\u002Fdev\u002Fnull\n          echo \"Geoclue2 didn't return a fix within 10s — agent may need to grant permission via xdg-desktop-portal dialog.\" >&2\n          echo \"Falling back to IP geolocation...\" >&2\n          BACKEND=\"ip\"\n        else\n          # 5) Read Latitude \u002F Longitude \u002F Description\n          read_prop() {\n            local prop=\"$1\"\n            local out\n            out=$(gdbus_call --object-path \"$LOC_PATH\" \\\n              --method org.freedesktop.DBus.Properties.Get \\\n              org.freedesktop.GeoClue2.Location \"$prop\")\n            # Strip the variant wrapper: \"(\u003Cvalue>,)\" → \"value\"\n            printf '%s' \"$out\" | sed -E \"s\u002F^\\\\(\u003C(.*)>,?\\\\)\\$\u002F\\\\1\u002F\" | sed -E \"s\u002F^'(.*)'\\$\u002F\\\\1\u002F\"\n          }\n          LAT=$(read_prop Latitude)\n          LON=$(read_prop Longitude)\n          ALT=$(read_prop Altitude)\n          ACC=$(read_prop Accuracy)\n          DESCR=$(read_prop Description)\n\n          # Cleanup\n          gdbus_call --object-path \"$CLIENT_PATH\" \\\n            --method org.freedesktop.GeoClue2.Client.Stop >\u002Fdev\u002Fnull\n\n          case \"$FORMAT\" in\n            coords)\n              printf 'GPS coordinates (lat,lon): %s,%s\\n' \"$LAT\" \"$LON\"\n              ;;\n            address|city)\n              # Geoclue doesn't reverse-geocode; query Nominatim (OpenStreetMap).\n              if command -v curl >\u002Fdev\u002Fnull 2>&1; then\n                NOM=$(curl -sS --max-time 5 \\\n                  -H 'User-Agent: KinClaw\u002F1.0 (https:\u002F\u002Flocalkin.dev)' \\\n                  \"https:\u002F\u002Fnominatim.openstreetmap.org\u002Freverse?lat=$LAT&lon=$LON&format=json\")\n                if [ \"$FORMAT\" = \"address\" ]; then\n                  ADDR=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"display_name\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n                  printf 'GPS address: %s\\n' \"${ADDR:-(reverse geocode failed)}\"\n                else\n                  CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"city\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n                  [ -z \"$CITY\" ] && CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"town\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n                  [ -z \"$CITY\" ] && CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"village\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n                  printf 'GPS city: %s\\n' \"${CITY:-(reverse geocode failed)}\"\n                fi\n              else\n                echo \"curl not installed — can't reverse-geocode. Coords: $LAT,$LON\" >&2\n                exit 1\n              fi\n              ;;\n            full)\n              printf 'GPS (Geoclue2):\\n'\n              printf '  latitude:    %s\\n' \"$LAT\"\n              printf '  longitude:   %s\\n' \"$LON\"\n              printf '  altitude:    %s\\n' \"$ALT\"\n              printf '  accuracy_m:  %s\\n' \"$ACC\"\n              printf '  description: %s\\n' \"$DESCR\"\n              ;;\n            *)\n              echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n              exit 1\n              ;;\n          esac\n          exit 0\n        fi\n      fi\n    fi\n\n    # ──── Backend: IP geolocation (last resort) ────────────────────\n    # Useful for servers \u002F CI \u002F Pi without GPS. Accuracy: city-level only.\n    if [ \"$BACKEND\" = \"ip\" ]; then\n      JSON=$(curl -sS --max-time 5 https:\u002F\u002Fipapi.co\u002Fjson)\n      if [ -z \"$JSON\" ]; then\n        echo \"IP geolocation failed (no network?)\" >&2\n        exit 1\n      fi\n      LAT=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"latitude\": *([^,]+).*\u002F\\1\u002Fp')\n      LON=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"longitude\": *([^,]+).*\u002F\\1\u002Fp')\n      CITY=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"city\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n      REGION=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"region\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n      COUNTRY=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"country_name\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n\n      case \"$FORMAT\" in\n        coords)\n          printf 'GPS coordinates (lat,lon, via IP geolocation — city-level accuracy): %s,%s\\n' \"$LAT\" \"$LON\"\n          ;;\n        address)\n          printf 'GPS address (via IP — city-level only): %s, %s, %s\\n' \"$CITY\" \"$REGION\" \"$COUNTRY\"\n          ;;\n        city)\n          printf 'GPS city (via IP): %s\\n' \"$CITY\"\n          ;;\n        full)\n          printf 'GPS (full reading via IP geolocation — city-level accuracy):\\n'\n          printf '  latitude:   %s\\n' \"$LAT\"\n          printf '  longitude:  %s\\n' \"$LON\"\n          printf '  city:       %s\\n' \"$CITY\"\n          printf '  region:     %s\\n' \"$REGION\"\n          printf '  country:    %s\\n' \"$COUNTRY\"\n          ;;\n        *)\n          echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n          exit 1\n          ;;\n      esac\n      exit 0\n    fi\n  - \"_\"\nargs:\n  - \"{{format}}\"\nschema:\n  format:\n    type: string\n    description: |\n      Output shape:\n        coords   - \"37.7749,-122.4194\" (default; lat,lon)\n        address  - reverse-geocoded street address\n        city     - locality name only\n        full     - all available fields (lat \u002F lon \u002F altitude \u002F accuracy \u002F address \u002F time)\ntimeout: 20\n---\n\n# location — real-time GPS, cross-platform\n\nAuto-detected backend per OS:\n\n| OS | Backend | Accuracy | Permission |\n|---|---|---|---|\n| macOS | `corelocationcli` (CoreLocation) | cell \u002F WiFi \u002F GPS (meter-level) | System Settings → Privacy & Security → Location |\n| Linux | `gdbus` + Geoclue2 | WiFi \u002F Mozilla MLS \u002F kernel-DRM | xdg-desktop-portal dialog (per-app) |\n| any | `curl ipapi.co` fallback | city-level only | none (uses your IP) |\n\nTwo-tier location story for KinClaw:\n\n| Layer | Where | When to use |\n|---|---|---|\n| `{{location}}` substitution | Soul prompt, from `$KINCLAW_LOCATION` env | Persistent context — \"user lives in Beijing\" |\n| `location` skill (this) | Tool call to OS location service | Real-time GPS — \"user is at $current_lat,$lon\" |\n\nThe skill is opt-in: not installed → clear error message + install hint.\nAlready installed → uses the OS's actual location services.\n\n## Privacy\n\n- **macOS**: triggers Apple's standard Location Services permission flow.\n  macOS asks ONCE per process; subsequent calls reuse the grant. Revoke\n  via System Settings → Privacy & Security → Location Services.\n- **Linux**: Geoclue2 uses `xdg-desktop-portal` for permission. On GNOME \u002F\n  KDE it prompts via the portal dialog; on Sway\u002Fi3 the dialog may not\n  appear (in which case grant via `gnome-control-center privacy` once).\n- **IP fallback**: no permission needed because no GPS hardware is queried —\n  ipapi.co geolocates your public IP. City-level only.\n\n## Usage\n\n```\nlocation                       → \"37.7749,-122.4194\"\nlocation format=address        → \"1 Apple Park Way, Cupertino, CA\"\nlocation format=city           → \"Cupertino\"\nlocation format=full           → multi-line: lat\u002Flon\u002Falt\u002Faccuracy\u002F...\n```\n\nThe backend is auto-selected at invocation time; the agent doesn't need\nto know whether it's running on macOS or Linux.\n\n## Linux setup (one-time)\n\n```bash\n# Debian \u002F Ubuntu\nsudo apt install geoclue-2.0 curl\n\n# Fedora\nsudo dnf install geoclue2 curl\n\n# Arch\nsudo pacman -S geoclue curl\n\n# Verify geoclue is running\nsystemctl --user status geoclue.service  # may be system-wide, try without --user too\n```\n\nIf Geoclue2 doesn't return a fix within 10 seconds (often the case on\nservers \u002F Pi without WiFi positioning), the skill auto-falls-back to IP\ngeolocation. Forge a derivative skill if you want different fallback\nbehavior (e.g. cache last good fix, exponential retry).\n","# location — real-time GPS, cross-platform\n\nAuto-detected backend per OS:\n\n| OS | Backend | Accuracy | Permission |\n|---|---|---|---|\n| macOS | `corelocationcli` (CoreLocation) | cell \u002F WiFi \u002F GPS (meter-level) | System Settings → Privacy & Security → Location |\n| Linux | `gdbus` + Geoclue2 | WiFi \u002F Mozilla MLS \u002F kernel-DRM | xdg-desktop-portal dialog (per-app) |\n| any | `curl ipapi.co` fallback | city-level only | none (uses your IP) |\n\nTwo-tier location story for KinClaw:\n\n| Layer | Where | When to use |\n|---|---|---|\n| `{{location}}` substitution | Soul prompt, from `$KINCLAW_LOCATION` env | Persistent context — \"user lives in Beijing\" |\n| `location` skill (this) | Tool call to OS location service | Real-time GPS — \"user is at $current_lat,$lon\" |\n\nThe skill is opt-in: not installed → clear error message + install hint.\nAlready installed → uses the OS's actual location services.\n\n## Privacy\n\n- **macOS**: triggers Apple's standard Location Services permission flow.\n  macOS asks ONCE per process; subsequent calls reuse the grant. Revoke\n  via System Settings → Privacy & Security → Location Services.\n- **Linux**: Geoclue2 uses `xdg-desktop-portal` for permission. On GNOME \u002F\n  KDE it prompts via the portal dialog; on Sway\u002Fi3 the dialog may not\n  appear (in which case grant via `gnome-control-center privacy` once).\n- **IP fallback**: no permission needed because no GPS hardware is queried —\n  ipapi.co geolocates your public IP. City-level only.\n\n## Usage\n\n```\nlocation                       → \"37.7749,-122.4194\"\nlocation format=address        → \"1 Apple Park Way, Cupertino, CA\"\nlocation format=city           → \"Cupertino\"\nlocation format=full           → multi-line: lat\u002Flon\u002Falt\u002Faccuracy\u002F...\n```\n\nThe backend is auto-selected at invocation time; the agent doesn't need\nto know whether it's running on macOS or Linux.\n\n## Linux setup (one-time)\n\n```bash\n# Debian \u002F Ubuntu\nsudo apt install geoclue-2.0 curl\n\n# Fedora\nsudo dnf install geoclue2 curl\n\n# Arch\nsudo pacman -S geoclue curl\n\n# Verify geoclue is running\nsystemctl --user status geoclue.service  # may be system-wide, try without --user too\n```\n\nIf Geoclue2 doesn't return a fix within 10 seconds (often the case on\nservers \u002F Pi without WiFi positioning), the skill auto-falls-back to IP\ngeolocation. Forge a derivative skill if you want different fallback\nbehavior (e.g. cache last good fix, exponential retry).\n",{"args":50,"name":5,"schema":52,"command":56,"timeout":61,"description":62},[51],"{{format}}",{"format":53},{"type":54,"description":55},"string","Output shape:\n  coords   - \"37.7749,-122.4194\" (default; lat,lon)\n  address  - reverse-geocoded street address\n  city     - locality name only\n  full     - all available fields (lat \u002F lon \u002F altitude \u002F accuracy \u002F address \u002F time)\n",[57,58,59,60],"sh","-c","FORMAT=\"$1\"\n[ \"$FORMAT\" = \"\" ] && FORMAT=\"coords\"\n\n# ──── Backend detection ────────────────────────────────────────\n# Prefer corelocationcli on macOS (CoreLocation is the most accurate\n# and lowest-latency option there). Linux uses geoclue2 via gdbus\n# (universal — ships with glib2). IP fallback is last resort for\n# both, useful for servers \u002F CI \u002F Pi without GPS hardware.\n\nOS=$(uname -s)\nBACKEND=\"\"\nif [ \"$OS\" = \"Darwin\" ] && command -v corelocationcli >\u002Fdev\u002Fnull 2>&1; then\n  BACKEND=\"corelocationcli\"\nelif [ \"$OS\" = \"Linux\" ] && command -v gdbus >\u002Fdev\u002Fnull 2>&1; then\n  BACKEND=\"geoclue\"\nelif command -v curl >\u002Fdev\u002Fnull 2>&1; then\n  BACKEND=\"ip\"\nelse\n  echo \"no location backend available\" >&2\n  case \"$OS\" in\n    Darwin) echo \"  install: brew install corelocationcli\" >&2 ;;\n    Linux)  echo \"  install: apt install geoclue-2.0 jq curl\" >&2 ;;\n  esac\n  exit 1\nfi\n\n# ──── Backend: corelocationcli (macOS) ─────────────────────────\n# NOTE: do NOT pass `-once`. It is not a valid flag in v4.x, and the\n# tool silently swallows unknown args — including the `-format` that\n# follows — then exits 0 printing the default \"lat lon\". Every branch\n# below would return bare coords for every requested format. It prints\n# once anyway unless `--watch` is given. (Verified against v4.0.7.)\nif [ \"$BACKEND\" = \"corelocationcli\" ]; then\n  case \"$FORMAT\" in\n    coords)\n      LL=$(corelocationcli --format \"%latitude,%longitude\")\n      printf 'GPS coordinates (lat,lon): %s\\n' \"$LL\"\n      ;;\n    address)\n      ADDR=$(corelocationcli --format \"%address\")\n      printf 'GPS address: %s\\n' \"$ADDR\"\n      ;;\n    city)\n      CITY=$(corelocationcli --format \"%locality\")\n      printf 'GPS city: %s\\n' \"$CITY\"\n      ;;\n    full)\n      printf 'GPS (full reading from CoreLocation):\\n'\n      # One line with '|' between fields, made lines here: written as lines inside the\n      # format, the fields fell outside this YAML block and the whole skill stopped loading.\n      corelocationcli --format \"latitude:    %latitude|longitude:   %longitude|altitude:    %altitude|accuracy_m:  %h_accuracy|address:     %address|time:        %time\" \\\n        | tr '|' '\\n' | sed 's\u002F^\u002F  \u002F'\n      ;;\n    *)\n      echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n      exit 1\n      ;;\n  esac\n  exit 0\nfi\n\n# ──── Backend: geoclue2 via gdbus (Linux) ──────────────────────\n# Geoclue2 D-Bus dance:\n#   1) Manager.GetClient → client object path\n#   2) Set DesktopId + RequestedAccuracyLevel on the client\n#   3) Client.Start → triggers actual fix\n#   4) Read Location property → returns location object path\n#   5) Read Latitude \u002F Longitude \u002F Description from location object\n#   6) Client.Stop → release the resource\nif [ \"$BACKEND\" = \"geoclue\" ]; then\n  gdbus_call() {\n    gdbus call --system --dest org.freedesktop.GeoClue2 \"$@\" 2>\u002Fdev\u002Fnull\n  }\n\n  # 1) Create a Client\n  CLIENT_OUT=$(gdbus_call --object-path \u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FManager \\\n                          --method org.freedesktop.GeoClue2.Manager.GetClient)\n  if [ -z \"$CLIENT_OUT\" ]; then\n    echo \"Geoclue2 not running. Start with: systemctl --user start geoclue.service\" >&2\n    echo \"Falling back to IP geolocation...\" >&2\n    BACKEND=\"ip\"\n  else\n    CLIENT_PATH=$(printf '%s' \"$CLIENT_OUT\" | sed -n \"s|.*'\\\\(\u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FClient\u002F[^']*\\\\)'.*|\\\\1|p\")\n    if [ -z \"$CLIENT_PATH\" ]; then\n      echo \"Geoclue2: couldn't parse client path from: $CLIENT_OUT\" >&2\n      BACKEND=\"ip\"\n    fi\n  fi\n\n  if [ \"$BACKEND\" = \"geoclue\" ]; then\n    # 2) Set DesktopId (required for newer geoclue) + accuracy 8 = exact\n    gdbus_call --object-path \"$CLIENT_PATH\" \\\n      --method org.freedesktop.DBus.Properties.Set \\\n      org.freedesktop.GeoClue2.Client DesktopId '\u003C\"kinclaw\">' >\u002Fdev\u002Fnull\n\n    gdbus_call --object-path \"$CLIENT_PATH\" \\\n      --method org.freedesktop.DBus.Properties.Set \\\n      org.freedesktop.GeoClue2.Client RequestedAccuracyLevel '\u003Cuint32 8>' >\u002Fdev\u002Fnull\n\n    # 3) Start\n    gdbus_call --object-path \"$CLIENT_PATH\" \\\n      --method org.freedesktop.GeoClue2.Client.Start >\u002Fdev\u002Fnull\n\n    # 4) Poll for Location property (up to ~10s)\n    LOC_PATH=\"\"\n    for i in 1 2 3 4 5 6 7 8 9 10; do\n      LP_OUT=$(gdbus_call --object-path \"$CLIENT_PATH\" \\\n        --method org.freedesktop.DBus.Properties.Get \\\n        org.freedesktop.GeoClue2.Client Location)\n      LOC_PATH=$(printf '%s' \"$LP_OUT\" | sed -n \"s|.*'\\\\(\u002Forg\u002Ffreedesktop\u002FGeoClue2\u002FClient\u002F[^']*Location[^']*\\\\)'.*|\\\\1|p\")\n      [ -n \"$LOC_PATH\" ] && [ \"$LOC_PATH\" != \"\u002F\" ] && break\n      sleep 1\n    done\n\n    if [ -z \"$LOC_PATH\" ] || [ \"$LOC_PATH\" = \"\u002F\" ]; then\n      # 6) Cleanup\n      gdbus_call --object-path \"$CLIENT_PATH\" \\\n        --method org.freedesktop.GeoClue2.Client.Stop >\u002Fdev\u002Fnull\n      echo \"Geoclue2 didn't return a fix within 10s — agent may need to grant permission via xdg-desktop-portal dialog.\" >&2\n      echo \"Falling back to IP geolocation...\" >&2\n      BACKEND=\"ip\"\n    else\n      # 5) Read Latitude \u002F Longitude \u002F Description\n      read_prop() {\n        local prop=\"$1\"\n        local out\n        out=$(gdbus_call --object-path \"$LOC_PATH\" \\\n          --method org.freedesktop.DBus.Properties.Get \\\n          org.freedesktop.GeoClue2.Location \"$prop\")\n        # Strip the variant wrapper: \"(\u003Cvalue>,)\" → \"value\"\n        printf '%s' \"$out\" | sed -E \"s\u002F^\\\\(\u003C(.*)>,?\\\\)\\$\u002F\\\\1\u002F\" | sed -E \"s\u002F^'(.*)'\\$\u002F\\\\1\u002F\"\n      }\n      LAT=$(read_prop Latitude)\n      LON=$(read_prop Longitude)\n      ALT=$(read_prop Altitude)\n      ACC=$(read_prop Accuracy)\n      DESCR=$(read_prop Description)\n\n      # Cleanup\n      gdbus_call --object-path \"$CLIENT_PATH\" \\\n        --method org.freedesktop.GeoClue2.Client.Stop >\u002Fdev\u002Fnull\n\n      case \"$FORMAT\" in\n        coords)\n          printf 'GPS coordinates (lat,lon): %s,%s\\n' \"$LAT\" \"$LON\"\n          ;;\n        address|city)\n          # Geoclue doesn't reverse-geocode; query Nominatim (OpenStreetMap).\n          if command -v curl >\u002Fdev\u002Fnull 2>&1; then\n            NOM=$(curl -sS --max-time 5 \\\n              -H 'User-Agent: KinClaw\u002F1.0 (https:\u002F\u002Flocalkin.dev)' \\\n              \"https:\u002F\u002Fnominatim.openstreetmap.org\u002Freverse?lat=$LAT&lon=$LON&format=json\")\n            if [ \"$FORMAT\" = \"address\" ]; then\n              ADDR=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"display_name\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n              printf 'GPS address: %s\\n' \"${ADDR:-(reverse geocode failed)}\"\n            else\n              CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"city\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n              [ -z \"$CITY\" ] && CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"town\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n              [ -z \"$CITY\" ] && CITY=$(printf '%s' \"$NOM\" | sed -nE 's\u002F.*\"village\":\"([^\"]+)\".*\u002F\\1\u002Fp')\n              printf 'GPS city: %s\\n' \"${CITY:-(reverse geocode failed)}\"\n            fi\n          else\n            echo \"curl not installed — can't reverse-geocode. Coords: $LAT,$LON\" >&2\n            exit 1\n          fi\n          ;;\n        full)\n          printf 'GPS (Geoclue2):\\n'\n          printf '  latitude:    %s\\n' \"$LAT\"\n          printf '  longitude:   %s\\n' \"$LON\"\n          printf '  altitude:    %s\\n' \"$ALT\"\n          printf '  accuracy_m:  %s\\n' \"$ACC\"\n          printf '  description: %s\\n' \"$DESCR\"\n          ;;\n        *)\n          echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n          exit 1\n          ;;\n      esac\n      exit 0\n    fi\n  fi\nfi\n\n# ──── Backend: IP geolocation (last resort) ────────────────────\n# Useful for servers \u002F CI \u002F Pi without GPS. Accuracy: city-level only.\nif [ \"$BACKEND\" = \"ip\" ]; then\n  JSON=$(curl -sS --max-time 5 https:\u002F\u002Fipapi.co\u002Fjson)\n  if [ -z \"$JSON\" ]; then\n    echo \"IP geolocation failed (no network?)\" >&2\n    exit 1\n  fi\n  LAT=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"latitude\": *([^,]+).*\u002F\\1\u002Fp')\n  LON=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"longitude\": *([^,]+).*\u002F\\1\u002Fp')\n  CITY=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"city\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n  REGION=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"region\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n  COUNTRY=$(printf '%s' \"$JSON\" | sed -nE 's\u002F.*\"country_name\": *\"([^\"]+)\".*\u002F\\1\u002Fp')\n\n  case \"$FORMAT\" in\n    coords)\n      printf 'GPS coordinates (lat,lon, via IP geolocation — city-level accuracy): %s,%s\\n' \"$LAT\" \"$LON\"\n      ;;\n    address)\n      printf 'GPS address (via IP — city-level only): %s, %s, %s\\n' \"$CITY\" \"$REGION\" \"$COUNTRY\"\n      ;;\n    city)\n      printf 'GPS city (via IP): %s\\n' \"$CITY\"\n      ;;\n    full)\n      printf 'GPS (full reading via IP geolocation — city-level accuracy):\\n'\n      printf '  latitude:   %s\\n' \"$LAT\"\n      printf '  longitude:  %s\\n' \"$LON\"\n      printf '  city:       %s\\n' \"$CITY\"\n      printf '  region:     %s\\n' \"$REGION\"\n      printf '  country:    %s\\n' \"$COUNTRY\"\n      ;;\n    *)\n      echo \"unknown format: $FORMAT (expected: coords | address | city | full)\" >&2\n      exit 1\n      ;;\n  esac\n  exit 0\nfi\n","_",20,"Get the user's current GPS location. Auto-detects the best backend\nfor the host OS:\n\n  macOS  →  corelocationcli  (CoreLocation — cell \u002F WiFi \u002F GPS)\n  Linux  →  gdbus + geoclue2  (kernel-driver \u002F WiFi \u002F Mozilla MLS)\n  fallback → IP geolocation   (ipapi.co — city-level only)\n\nReturns coordinates \u002F address \u002F city \u002F full details — request what\nyou need with the `format` arg.\n\nWhen to use this vs the {{location}} system prompt context:\n  - {{location}} is set from $KINCLAW_LOCATION env var — \"where the\n    user generally is\" (home \u002F office). Static, free, always available.\n  - This skill reads the OS location service in real time — \"where the\n    user is right NOW\". Use when user might be traveling \u002F driving \u002F\n    out, or when meter-level precision matters.\n\nFirst invocation prompts for permission (macOS Location Services \u002F\nLinux Geoclue per-app authorization through xdg-desktop-portal).\nGrant it for whichever process kinclaw runs in.\n\nFirst-time setup:\n  macOS:  brew install corelocationcli\n  Linux:  apt install geoclue-2.0 jq curl    # or pacman -S \u002F dnf install\n          (gdbus ships with glib2 — already present on every desktop Linux)\n","main","d1b92d10cddb779854af342bea74d0b6c46788e2","skills\u002Flocation",3,"https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw","https:\u002F\u002Fwww.localkin.app\u002Fkinclaw","https:\u002F\u002Fgithub.com\u002FLocalKinAI","localkin.ai","Faith and Chinese medicine at localkin.ai · the LocalKin agent platform at localkin.dev · open-source tools (kinfer, KinClaw, …) at localkin.app","2026-11-01 12:25:06",{"source":8,"skillName":5,"commands":74,"prompt":84,"zipAvailable":85},[75,78,81],{"tool":76,"command":77},"npx","npx skills add LocalKinAI\u002Fkinclaw --skill location",{"tool":79,"command":80},"bunx","bunx skills add LocalKinAI\u002Fkinclaw --skill location",{"tool":82,"command":83},"pnpm","pnpm dlx skills add LocalKinAI\u002Fkinclaw --skill location","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Flocation\u002FSKILL.md",true,[],[88,123,137,160,170,181],{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":103,"publishedAt":45},"01M6CPGNC9S31FTGMV90NZ0EN9","web","Universal web tool — fetches and interacts with web pages through\nreal Chromium (Playwright). Renders JavaScript, follows redirects,\nhandles cookies, bypasses many anti-scrape stubs that block plain\n`web_fetch`. Each call launches a fresh browser, executes the\nrequested flow, closes. ~2-3s cold start; no persistent state.\n\nUse INSTEAD of `web_fetch` when:\n  - Target is a JS-heavy SPA (prices via XHR, lazy content)\n  - `web_fetch` returned an anti-scrape placeholder (Zhihu's\n    \"click here\" stub, JD's chrome-only HTML)\n  - You need to interact (click, fill input) before extracting\n\nCommon patterns:\n  fetch rendered text:           url=X\n  wait for content then read:    url=X wait_for=\".price\"\n  extract specific element:      url=X selector=\".product-list\"\n  click and read result:         url=X click=\".search-button\"\n  fill form, then read:          url=X click=\"input[name=q]\" type_text=\"kinclaw\"\n  fill + Enter (React forms):    url=X click=\"input\" type_text=\"hi\" press_enter=true wait_for=\".reply\"\n  screenshot the page:           url=X screenshot=true (returns image:\u002F\u002F marker)\n  run JS and get result:         url=X js=\"document.title\"\n\nFirst-time setup (one-time per machine, ~500MB Chromium download):\n  pip install playwright\n  playwright install chromium","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Fweb\u002FSKILL.md",[94,97,100],{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000002","文档处理",{"categoryNo":98,"categoryName":99,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":101,"categoryName":102,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",[90,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122],"image","search","forms","files","log","read","text","wait for","how","scrape","start","PDF","run","generate","placeholder","change","react","css","directory",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":129,"publishedAt":45},"01M6CPGJTGF7P8QS3HXPCZE3KV","weather","Current conditions plus a three-day forecast (today \u002F tomorrow \u002F day\nafter) for a place, in metric, from wttr.in. No API key. Use this for\nany weather question — including \"明天\" and \"后天\" — before reaching\nfor web search.","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Fweather\u002FSKILL.md",[],[125,105,130,5,131,132,133,134,135,136,90],"three","use","question","python3","web search","api","forecast",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":147,"publishedAt":45},"01M6CPGDR0W8FR0WMBBJ2282GM","tts","Synthesize speech from text and play it through the macOS speakers.\nTalks to a local LocalKin Service Audio Server (default :8001 \u002F Kokoro).\nWhen `record` is running with audio=true, the spoken audio is\ncaptured into the recording — use this in place of `shell say` for\nhigh-quality multilingual narration in demo videos. Set the\nTTS_ENDPOINT env var to point at a different server.","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Ftts\u002FSKILL.md",[143,146],{"categoryNo":144,"categoryName":145,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},[139,148,29,149,150,30,31,151,152,153,34,154,110,155,156,41,157,158,33,159],"next","chinese","multilingual","payload","pick","code","model","Audio","Video","data","server","macos",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":166,"publishedAt":45},"01M6CPGAM9K2VN0MVYD4CMC92E","translate","Translate text between languages using the system's translate command","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Ftranslate\u002FSKILL.md",[],[162,149,167,168,31,36,110,169],"spanish","language","system",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":179,"publishedAt":45},"01M6CPG8DWEY0GWCFDGTTBWA2Q","summarize","Summarize a file's contents into key bullet points","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Fsummarize\u002FSKILL.md",[176],{"categoryNo":177,"categoryName":178,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000021","资料检索与研究",[172,180],"file",{"skillNo":182,"name":183,"nameZh":6,"description":184,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":185,"categories":186,"keywords":188,"publishedAt":45},"01M6CPG48P3AYKRREAGYSGT944","stt","Transcribe an audio file (wav\u002Fmp3\u002Fmp4\u002Fm4a) into text via a local\nLocalKin Service Audio Server (default :8000 \u002F SenseVoice). Pair\nwith `record action=start mic=true` to turn a recorded mic track\ninto text. Set STT_ENDPOINT env var to point at a different server.","https:\u002F\u002Fgithub.com\u002FLocalKinAI\u002Fkinclaw\u002Fblob\u002Fmain\u002Fskills\u002Fstt\u002FSKILL.md",[187],{"categoryNo":144,"categoryName":145,"taxonomyType":20,"taxonomyTypeDesc":21},[183,110,29,189,30,31,190,34,191,192,193,194,155,195,41,114,158,196,153,197],"do","en","pair","just","returns","mp4","endpoint","transcribe","go"]