detect-objects
Run pre-trained AI models on geospatial imagery. Detect buildings, cars, ships, solar panels, agriculture fields, or use…
Search past Claude Code session logs to recover context from previous conversations. Finds past decisions, data paths, CRS info, model configurations, and unresolved work. Works across all projects or scoped to the current one.
Claude Code
$ npx -y skills add opengeos/geoai-skills --skill read-memories --agent claude-code
Codex
$ npx skills add opengeos/geoai-skills --skill read-memories -a codex -y
Cursor
$ npx skills add opengeos/geoai-skills --skill read-memories -a cursor -y
Gemini CLI
$ npx skills add opengeos/geoai-skills --skill read-memories -a gemini-cli -y
GitHub Copilot
$ npx skills add opengeos/geoai-skills --skill read-memories -a github-copilot -y
OpenCode
$ npx skills add opengeos/geoai-skills --skill read-memories -a opencode -y
Windsurf
$ npx skills add opengeos/geoai-skills --skill read-memories -a windsurf -y
name: read-memories description: > Search past Claude Code session logs to recover context from previous conversations. Finds past decisions, data paths, CRS info, model configurations, and unresolved work. Works across all projects or scoped to the current one. argument-hint: <keyword> [--here] [--geo] allowed-tools: Bash, Read
Use this to recover your own context from past sessions. Do NOT narrate the process to the user -- silently run the query, absorb the results, and continue with enriched context.
`$0` is the keyword to search for. Pass `--here` as an argument to scope the search to the current project only. Pass `--geo` as an argument to additionally extract geospatial-specific context (EPSG codes, bounding boxes, CRS info, spatial file paths, model names).
ALL_PROJECTS="$HOME/.claude/projects/*/*.jsonl" CURRENT_PROJECT="$HOME/.claude/projects/$(echo "$PWD" | sed 's|[/_]|-|g')/*.jsonl"
Use `$CURRENT_PROJECT` if any argument is `--here`, otherwise use `$ALL_PROJECTS`. Store the chosen glob in `SEARCH_PATH`.
Check whether the `--geo` flag is present.
Run the following Python script via `python3 -c "..."`, substituting `<SEARCH_PATH>` and `<KEYWORD>` with the resolved values. Escape any single quotes in `<KEYWORD>` before embedding it.
python3 -c "
import json, glob, os
SEARCH_PATH = '<SEARCH_PATH>'
KEYWORD = '<KEYWORD>'.lower()
LIMIT = 40
files = sorted(glob.glob(os.path.expanduser(SEARCH_PATH)))
results = []
for fpath in files:
parts = fpath.split('/')
try:
proj_idx = parts.index('projects') + 1
project = parts[proj_idx] if proj_idx < len(parts) else 'unknown'
except ValueError:
project = 'unknown'
with open(fpath, 'r', errors='replace') as f:
for line in f:
try:
obj = json.loads(line)
except (json.JSONDecodeError, ValueError):
continue
msg = obj.get('message')
if not isinstance(msg, dict):
continue
role = msg.get('role')
if role not in ('user', 'assistant'):
continue
content = msg.get('content', '')
if isinstance(content, list):
text = ' '.join(
c.get('text', '')
for c in content
if isinstance(c, dict) and 'text' in c
)
elif isinstance(content, str):
text = content
else:
continue
if KEYWORD not in text.lower():
continue
ts = obj.get('timestamp', '')
snippet = text[:1500]
results.append({
'project': project,
'ts': ts[:16].replace('T', ' ') if ts else '',
'role': role,
'content': snippet,
})
if len(results) >= LIMIT:
break
if len(results) >= LIMIT:
break
print(f'Found {len(results)} results (limit {LIMIT})')
print('---')
for i, r in enumerate(results):
print(f'[{i+1}] project={r[\"project\"]} ts={r[\"ts\"]} role={r[\"role\"]}')
print(r['content'][:800])
print('---')
"If Step 2 reports exactly 40 results (limit hit), the keyword is common. Run a counting pass to understand the scope:
python3 -c "
import json, glob, os
SEARCH_PATH = '<SEARCH_PATH>'
KEYWORD = '<KEYWORD>'.lower()
files = sorted(glob.glob(os.path.expanduser(SEARCH_PATH)))
total = 0
by_project = {}
for fpath in files:
parts = fpath.split('/')
try:
proj_idx = parts.index('projects') + 1
project = parts[proj_idx] if proj_idx < len(parts) else 'unknown'
except ValueError:
project = 'unknown'
with open(fpath, 'r', errors='replace') as f:
for line in f:
try:
obj = json.loads(line)
except (json.JSONDecodeError, ValueError):
continue
msg = obj.get('message')
if not isinstance(msg, dict):
continue
role = msg.get('role')
if role not in ('user', 'assistant'):
continue
content = msg.get('content', '')
if isinstance(content, list):
text = ' '.join(
c.get('text', '')
for c in content
if isinstance(c, dict) and 'text' in c
)
elif isinstance(content, str):
text = content
else:
continue
if KEYWORD in text.lower():
total += 1
by_project[project] = by_project.get(project, 0) + 1
print(f'Total matches: {total}')
for proj, cnt in sorted(by_project.items(), key=lambda x: -x[1]):
print(f' {proj}: {cnt}')
"Use this breakdown to decide whether to:
If the `--geo` flag was provided, run an additional extraction pass:
python3 -c "
import json, glob, os, re
SEARCH_PATH = '<SEARCH_PATH>'
KEYWORD = '<KEYWORD>'.lower()
patterns = {
'epsg_codes': re.compile(r'EPSG[:\s]*(\d{4,5})', re.IGNORECASE),
'bbox': re.compile(r'(?:bbox|bounding.?box|bounds)\s*[=:]\s*\[([^\]]+)\]', re.IGNORECASE),
'crs': re.compile(r'(?:CRS|SRS|projection)\s*[=:]\s*[\"\\']?([^\"\\'\\n,;]{3,60})', re.IGNORECASE),
'spatial_files': re.compile(r'[\w/.-]+\.(?:shp|gpkg|geojson|tiff?|nc|hdf[45]?|gdb|fgb|kml|las|laz|parquet)', re.IGNORECASE),
'coords': re.compile(r'(?:lat(?:itude)?|lon(?:gitude)?|lng)\s*[=:]\s*(-?\d+\.?\d*)', re.IGNORECASE),
'models': re.compile(r'(?:sam2?|segment.?anything|yolo\w*|resnet\w*|u-?net|deeA Claude Code plugin that adds GeoAI-powered skills for geospatial data exploration, satellite imagery download, AI-based object detection, and session memory. Built on the GeoAI Python library.
Run pre-trained AI models on geospatial imagery. Detect buildings, cars, ships, solar panels, agriculture fields, or use…
Download NAIP aerial imagery for a bounding box. Specify coordinates as minx,miny,maxx,maxy in WGS84 and optionally a year.
Inspect any raster or vector geospatial file. Returns CRS, bounds, bands, resolution, dtype, attribute summaries, and band…
Verify that the geoai Python package is installed and functional. If not, provide installation instructions. Optionally check…
Download Overture Maps data (buildings, places, roads, land use, water, etc.) for a bounding box. Returns a GeoDataFrame saved as…
Process raster data: clip by bounding box, stack multiple bands, mosaic GeoTIFFs, or convert between raster and vector formats.
Use this skill to extract structured Markdown/JSON from PDFs and document images—tables with cell-level precision, formulas as…
Estimates intracellular metabolic fluxes from steady-state carbon-13 isotope-tracing measurements using validated atom maps,…