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-sleuthInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
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.
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.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| geo-sleuth (this skill)by Oldcircle | 95 | 3.3k | 4d ago | SKILL.md |
| taste-skillby Leonxlnx | 100 | 101.2k | 2d ago | CLAUDE.md |
| ai-job-searchby ai-job-search-dev | 100 | 63.7k | 2d ago | CLAUDE.md |
| claude-howtoby luongnv89 | 100 | 43.4k | 2d ago | CLAUDE.md |
| LocalAIby mudler | 100 | 49.9k | today | MCP 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.
Skill content
View source on GitHubname: 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, firstexport 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)
- 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.
- 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".
- 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 withterrain.py elev— if the height difference doesn't match the depression angles in the image, the camera position is wrong). - 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.
- 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 compassor a confirmed landmark, then usegeo.py bearingsto 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. - ★ List the whole category before inferring: when inferring a region from "hill city", "tropical", "IP in a municipality", use
board.py childrento 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. - 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>. - 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.
- ★ 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 reportis always the top-ranked candidate; the rest go into alternatives with discriminating tests. - ★ Exclusion and confirmation use the same standard:
board.py excludeonly accepts read/computed clues + a computed file (output ofgeo.py frame,terrain.py, etc.); observed and inferred clues can only down-weight viaevidence --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 withboard.py add --from <poi.py --out / osm.py geom output> --level area/road, don't hand-pick; when you drop one, writeevidence --againstwith a comparison image, andchecklists 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, orboard.py move), so its support counts in that level's ranking, andcheckfails 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,excludeneeds--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). - ★ 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 inreferences/observe.md; log each clue withboard.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--resolvesfor 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, useboard.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 tosimilar[i].fromin the JSON to see the source page; compound, development or hotel names in the labels →poi.py "<name>" --city <city> --out pois.jsonto get coordinates; for same-name hits (several campuses, several branches) put them all on the board withboard.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, runregions.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 consultingglobal.md.regions.py listshows 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
taste-skill
101.2kAgent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
ai-job-search
63.7kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
claude-howto
43.4kA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.
LocalAI
49.9kLocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
