Skip to content
Data
Skill

/read-memories

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.

From plugin
geoai-skills
308 skills
Install
$ npx -y skills add opengeos/geoai-skills --skill read-memories --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/read-memories

Context preview

The summary Claude sees to decide when to auto-load this skill.

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.

SKILL.md

read-memories.SKILL.md
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).

Step 1 -- Set the search path

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.

Step 2 -- Query with Python

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('---')
"

Step 3 -- Handle large result sets

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:

  • Narrow the keyword (combine with a second term)
  • Scope to `--here` if not already scoped
  • Retrieve only the most recent results (sort by timestamp descending)

Step 4 -- Extract geospatial context (when --geo is set)

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|dee
Read more
Ships withgeoai-skills

A 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.

Get the whole plugin
Stats
30
Stars
4
Forks
Maintained
Maintenance
MIT
License
1mo ago
Last commit
6mo ago
Created

Repo: opengeos/geoai-skills

Other skills on geoai-skills.