SkillAgentSearch skills...

geo-sleuth

Geolocate or chronolocate a photo with tool-verified reasoning (where was this taken / when was it taken / photo geolocation / geo-guessing).

Install / Use

npx skills add Oldcircle/geo-sleuth --skill geo-sleuth

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

95/100

Supported Platforms

Universal

Our assessment of geo-sleuth

geo-sleuth scores 95/100 on our quality scale, 375th of 4,583 Development & Engineering skills we index (top 9%).

Its SKILL.md is 27 KB long, well organised into 11 sections with 5 code examples: a thorough specification that gives an agent plenty to work with.

With 3,341 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
15/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 4 days ago, so geo-sleuth is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

geo-sleuth compared with similar skills

All 4 of these similar skills score higher than geo-sleuth; compare them before choosing.

SkillScoreStarsUpdatedFormat
geo-sleuth (this skill)by Oldcircle953.3k4d agoSKILL.md
taste-skillby Leonxlnx100101.2k2d agoCLAUDE.md
ai-job-searchby ai-job-search-dev10063.7k2d agoCLAUDE.md
claude-howtoby luongnv8910043.4k2d agoCLAUDE.md
LocalAIby mudler10049.9ktodayMCP Server

Frequently asked questions

How do I install geo-sleuth?
Run npx skills add Oldcircle/geo-sleuth --skill geo-sleuth. The install tabs above show the steps for each supported agent.
Which AI agents does geo-sleuth work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is geo-sleuth safe to use?
It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is geo-sleuth still maintained?
The repository was last updated 4 days ago, so geo-sleuth is actively maintained.

name: geo-sleuth description: Geolocate or chronolocate a photo with tool-verified reasoning (where was this taken / when was it taken / photo geolocation / geo-guessing). Given one or more photos, intake.py does metadata, OCR and reverse image search (Baidu/Yandex) in one command; clues and candidates go on a candidate board (board.py) that ranks them and gives the next step; clues.py looks up calling codes, driving side, plates and area codes from region packs; for satellite and street view the machine ranks first and you look only at the top few (CLIP in sat_scan.py, DINOv2+SIFT in match.py); plus sun and shadow math, OSM Overpass and DEM skylines. Every conclusion is checked against real data; output is coordinates + error radius, evidence images and tiered confidence. Use when the user shares a photo and asks where was this taken / geolocate this / when was this taken / 这是哪 / 在哪拍的 / 帮我定位这张照片 / 网络迷踪 / 图寻 / 几点拍的.

geo-sleuth (v2)

Goal: find a location that holds up under checking. Every conclusion must point to the clue it used, the command it ran and the file it produced.

${CLAUDE_SKILL_DIR} in commands means the directory containing this SKILL.md (the same for commands in references). Claude Code substitutes it automatically; in other agents, first export CLAUDE_SKILL_DIR=<absolute path of this directory> before running (if the shell doesn't keep variables between commands, prefix every command with it), or replace it with that path directly.

The work is split into three layers. Remember this first, then read the flow:

| Layer | Who | Tools | |---|---|---| | Decision: which candidates exist, how evidence is scored, whether a candidate can be excluded, what to scan next | Script (candidate board) | board.py | | Perception: reading text, table lookups, finding targets in satellite imagery, street-view matching | Scripts compute and rank first; you look only at the top few | intake.py ocr.py clues.py sat_scan.py match.py geo.py bearings | | Judgment: pulling clues out of the image, proposing hypotheses when the tables have nothing, deciding among the machine's top few | You | — |

The method comes from breaking down 14 photo-geolocation (网络迷踪) creator videos and 22 puzzles, plus several rounds of blind-test comparisons; the breakdown notes are not published with the repo. The lesson of v1: rules written as prose don't get executed, and two runs of the same skill version gave very different results; so v2 puts every rule that can be written as code into board.py.

Hard rules (always in force; ★ = enforced by board.py, just follow it)

  1. If the user says the photo isn't theirs, or the image shows a private residence or minors, ask once what it's for before continuing; when the prompt already states the source or purpose (an evaluation puzzle, a puzzle setter's hint, the user says they took it), don't ask, and go to the finest level as usual.
  2. Don't fabricate verification. "Measured on the map", "matched street view", "±10 m" must correspond to commands actually run and files actually produced in this session. If you didn't run it, write "unverified".
  3. Precision needs a source: an error radius ≤100 m requires the intersection of two independent constraints, or a ground-level match on ≥3 invariant features. Before reporting a camera position, run a self-check: pose.py project --horizon <sea-horizon row> computes what the pitch should be and each feature's "expected row" for the current camera position; reconcile that with the photo (the depression angle to a target ≠ the camera's pitch; look up the camera position's elevation with terrain.py elev — if the height difference doesn't match the depression angles in the image, the camera position is wrong).
  4. Once you have a unique anchor, close the loop: all later search and geometry starts from the anchor; don't go back to generic features like "blue-green lake water" or "tropical park" and pick the most famous place of that kind.
  5. Compute bearings first, then identify structures: which block in satellite imagery is the tower or chimney in the photo is itself an interpretation. First compute its true bearing in the image with sun.py compass or a confirmed landmark, then use geo.py bearings to see which outline around the candidate camera position falls on that bearing; when nothing fits, first try both templates ("original/mirrored", "sunrise/sunset"); if not verified, only lower the tier, don't exclude.
  6. ★ List the whole category before inferring: when inferring a region from "hill city", "tropical", "IP in a municipality", use board.py children to add all subordinate admin divisions as candidates, then rank them with evidence. Don't default to the main urban district, don't pick by population.
  7. Metadata and hints are hypotheses: when EXIF, IP location, location tags or the puzzle setter's words conflict with the image, the image wins; don't invent a story to reconcile them. IP says country A but the hint or the regulation designs (plates, signs) point to another continent: intersect with clues.py lookup territories <country A> --continent <continent>.
  8. Self-consistency: redo each key judgment once with a different crop or keyword set; if the two results differ by tens of kilometers or more, lower the tier.
  9. ★ If you can't pin a point, give a range + what information is missing; when the candidates are a few discrete ones, don't take the midpoint: the main answer of board.py report is always the top-ranked candidate; the rest go into alternatives with discriminating tests.
  10. ★ Exclusion and confirmation use the same standard: board.py exclude only accepts read/computed clues + a computed file (output of geo.py frame, terrain.py, etc.); observed and inferred clues can only down-weight via evidence --against, with the likelihood ratio clamped to 1/3–3 (inferred) or 1/5–5 (observed). Generating candidates from "there's an X nearby" is also filtering, and X must be something confirmed in the image. Fine-level candidates such as campuses, residential compounds and street segments must go on the board too: add a batch at once with board.py add --from <poi.py --out / osm.py geom output> --level area/road, don't hand-pick; when you drop one, write evidence --against with a comparison image, and check lists candidates without a single piece of evidence (meaning nobody looked at them); a finer candidate goes under the one that contains it (a point under its street segment: --parent, or board.py move), so its support counts in that level's ranking, and check fails when it skips a level; labels from third-party data (tree species in a municipal inventory, an exact school-name match) can only down-weight, never justify exclusion (in two cases the ground truth was skipped by hand exactly this way). The excluded extent must be ≤ the evidence extent: for candidates with extent, such as districts, areas and roads, exclude needs --covers lat,lon[:lat,lon] stating which stretch the evidence covers; the script rejects coverage below half (excluding a whole road after looking at one point on it failed in two cases).
  11. ★ Population and fame are not evidence; scan order follows "share ÷ pages" (board.py next): finish the small districts first, put large districts last with a page cap.

Flow

You don't have to go through every step: if reverse image search in step 1 hits directly, jump to steps 6 and 7 to confirm. Every step's output goes on the candidate board.

Step 1: steps 0–3 in one command

uv run ${CLAUDE_SKILL_DIR}/scripts/intake.py photo.jpg --out-dir intake/ [--box x0,y0,x1,y1 ...]
uv run ${CLAUDE_SKILL_DIR}/scripts/board.py init --photo photo.jpg

intake.py usually takes 1–2 minutes, longer on the first run because it installs dependencies: give the command a generous timeout (10+ minutes) or run it in the background. If a command timeout interrupts it, the reverse-image-search subprocesses may still be writing into rev/, but intake.md won't be generated; don't treat it as finished.

intake.md contains: metadata, OCR text (lines read from upscaled/tiled passes are marked pass=up/tile and are hypotheses), Baidu similar images (source-site counts + numbered contact sheet), reverse-image-search labels with tiered vote counts, likely residential compound/development names, the list of edge crops, and failed items. Then you do four things:

  • Look at the image: go through every crop in edges/ (four edges, four corners); run through the checklist in references/observe.md; log each clue with board.py clue "<text>" --kind <kind> --status observed|read|inferred|computed --file <zoomed crop>. Be honest about status: text you read is read; "the building is probably 8 floors", "the road goes uphill" are inferred.
  • When a computed result actually tests an earlier clue, link it with clue "<result>" --kind <kind> --status computed --file <output> --resolves K1 (repeat --resolves for multiple clues). An unrelated computation does not resolve a pending infrastructure clue; this link records the test, not support for a location.
  • Lookup tables: calling codes, driving side, overseas territories are global; plates, area codes and other country tables come from region packs → clues.py lookup <kind> <value> [--country <cc>] (without --country, every pack is searched and matches from several countries are all returned). For those that resolve to an admin division, use board.py apply --kind plate --value <plate prefix> --file <zoomed crop> directly (adds candidates and evidence automatically; the other candidates at the same level are only down-weighted, not excluded).
  • Reverse-image-search results: first open rev/<name>_baidu_similar.jpg (the top-left tile is the query image) and look for near-duplicates of the same object or the same scene; if there are any, go by number to similar[i].from in the JSON to see the source page; compound, development or hotel names in the labels → poi.py "<name>" --city <city> --out pois.json to get coordinates; for same-name hits (several campuses, several branches) put them all on the board with board.py add --from pois.json --level area, then check; always open the screenshots, and once a post hits, look through the rest of its photo set. Choose where to search by object type (references/search.md).
  • Hints and metadata: log each as an inferred clue and state how credible it is; IP location only says where the person was when posting, and the posting time is not the capture time.
  • Country-specific material: while the country is still open, check references/clues/global.md (clues that separate countries). Country-specific clues, tables, preferred services and tips are in region packs (regions/<cc>/). Once a country is a candidate, run regions.py show <cc>: it prints the pack's lookups, services and a one-line index of its clue entries with line numbers; read only the entries you need. When the country is settled, board.py country <cc> and stop consulting global.md. regions.py list shows which packs exist; with no pack, use the global tools.

Step 2: list all candidates, score evidence, check the next step

uv run ${CLAUDE_SKILL_DIR}/scripts/board.py children <parent admin area>          # municipality → all districts; country → first-level admin divisions (gazetteer.py queries OSM, with bbox)
uv run ${CLAUDE_SKILL_DIR}/scripts/board.py evidence --clue K1 --for <candidate A>:5 --for <candidate B>:2 --against <candidate C>:0.3 --why "…" --file <comparison image>
uv run ${CLAUDE_SKILL_DIR}/scripts/board.py rank
uv run ${CLAUDE_SKILL_DIR}/scripts/board.py next

next only ever says one of two things:

  • Can't separate: run discriminating tests from cheap to expensive, each one on all candidates at once — lookup tables → terrain (plain vs. hill city, `tiles.py fetch --z

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars3.3k
CategoryDevelopment
Updated4d ago
Forks451

Languages

Python

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions