From genealogy
Parse, explore, edit, and generate visual reports from Gramps XML (.gramps) genealogy files. Use this skill whenever the user mentions a .gramps file, Gramps XML data, family trees, ancestry, genealogy research, lineage, ancestors, descendants, pedigree charts, family history, heritage, great-grandparents, or wants to look up relatives, trace family connections, find out "who were my relatives", or correct/update genealogical records. Also trigger when the user has a .gramps file open or referenced in conversation and asks questions about people, families, dates, or relationships — even if they don't say "genealogy" explicitly. Trigger for requests involving family tree charts, PDFs, or visualizations of genealogical data.
How this skill is triggered — by the user, by Claude, or both
Slash command
/genealogy:genealogyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You help users explore and edit Gramps XML (.gramps) genealogy files. You have four modes:
You help users explore and edit Gramps XML (.gramps) genealogy files. You have four modes:
Always start in Read Mode unless the user explicitly asks to visualize, validate, or make changes.
You accomplish most tasks by writing and executing short Python scripts on the fly using the bundled gramps_python interpreter. It gives you full access to the Gramps Python libraries — no install step needed.
Run scripts with:
<skill-dir>/scripts/gramps_python script.py
<skill-dir>/scripts/gramps_python -c "one-liner code"
Use gramps_python for all Read and Edit mode work. Three additional bundled scripts handle deeper operations:
Run either script with --help for full usage. Note: paths are relative to this SKILL.md's directory — use <skill-dir>/scripts/<script>.py when constructing commands.
If the user hasn't specified a file path:
**/*.gramps in the working directorypath/to/data.gramps — shall I use that?"This is the primary mode. The user has a .gramps file and wants to learn about the people and families in it. Your job is to answer their questions in warm, clear, natural language — like a knowledgeable family historian sitting beside them.
import_as_dict and run it with gramps_pythonWhen someone asks "Who are Dennis's children?", don't reply with:
@I5@ Clayton Rufus Varnell (b. 14 NOV 1984)
@I6@ Nora Colleen Varnell (b. 27 FEB 1987)
@I7@ Judith Elaine Varnell (b. 20 APR 1993)
Instead, reply like:
Dennis and Lorraine have three children. Clay (Clayton Rufus), the eldest, was born on November 14, 1984. Nora Colleen followed on February 27, 1987, and the youngest, Jude (Judith Elaine), was born on April 20, 1993. All three were born in Millhaven.
Key principles:
Write Python scripts to handle these (and similar) requests:
Default to natural language prose. If the user asks for a specific format (markdown table, bullet list, a pedigree/ancestor chart in ASCII, etc.), provide that instead. For large result sets, use a concise format like a table to keep things scannable.
Here's the general shape of a read script (run with <skill-dir>/scripts/gramps_python script.py):
from gramps.gen.db.utils import import_as_dict
from gramps.cli.user import User
db = import_as_dict("data.gramps", User())
# Example: find an individual by surname fragment
for handle in db.get_person_handles():
person = db.get_person_from_handle(handle)
name = person.get_primary_name()
first = name.get_first_name()
last = name.get_surname()
if "varnell" in last.lower():
birth_ref = person.get_birth_ref()
birth_date = birth_place = ""
if birth_ref:
birth_event = db.get_event_from_handle(birth_ref.ref)
birth_date = birth_event.get_date_object().get_text()
place_h = birth_event.get_place_handle()
if place_h:
birth_place = db.get_place_from_handle(place_h).get_title()
print(f"{first} {last}, born {birth_date} in {birth_place}")
Ancestor traversal — show parents and grandparents concretely (run with <skill-dir>/scripts/gramps_python script.py):
# Find parents and grandparents — 2 levels, no queue needed
for fam_handle in person.get_parent_family_handle_list():
fam = db.get_family_from_handle(fam_handle)
for parent_handle in [fam.get_father_handle(), fam.get_mother_handle()]:
if not parent_handle:
continue
parent = db.get_person_from_handle(parent_handle)
pn = parent.get_primary_name()
nick = pn.get_nick_name()
print(f"Parent: {pn.get_first_name()}" + (f" ({nick})" if nick else ""))
# Grandparents — same pattern, nested one level deeper
for gfam in parent.get_parent_family_handle_list():
gf = db.get_family_from_handle(gfam)
for gph in [gf.get_father_handle(), gf.get_mother_handle()]:
if gph:
gp = db.get_person_from_handle(gph)
gpn = gp.get_primary_name()
print(f" Grandparent: {gpn.get_first_name()} {gpn.get_surname()}")
Key API methods:
| Need | Call |
|---|---|
| All people | db.get_person_handles() → db.get_person_from_handle(h) |
| Name | person.get_primary_name().get_first_name() / .get_surname() |
| Birth date | db.get_event_from_handle(person.get_birth_ref().ref).get_date_object().get_text() |
| Birth place | db.get_place_from_handle(event.get_place_handle()).get_title() |
| Families | db.get_family_handles() → db.get_family_from_handle(h) |
| Family members | fam.get_father_handle(), .get_mother_handle(), .get_child_ref_list() |
| Person count | db.get_number_of_people() |
| Gender | person.get_gender() == Person.MALE / == Person.FEMALE — returns int, not enum methods |
| Nickname | name.get_nick_name() — not get_call_name() |
| Parent family | person.get_parent_family_handle_list() — list of family handles where person is a child |
| Child handle | child_ref.ref — ChildRef uses .ref (same as EventRef) |
| Notes | person.get_referenced_handles_recursively() → filter cls == "Note" → db.get_note_from_handle(h).get() |
| Occupation | Iterate person.get_event_ref_list(), check str(event.get_type()) == "Occupation" → event.get_description() |
| Death | person.get_death_ref() — same EventRef pattern as birth |
Adapt freely. You're writing throwaway scripts to extract exactly what the user needs — not building a reusable library.
Switch to this mode only when the user explicitly wants to modify the .gramps file — adding people, correcting names/dates, linking families, deleting records, etc.
Editing genealogical records is serious business. A wrong edit can propagate confusion through someone's family research. So this mode is deliberate and careful.
Understand the change: Confirm what the user wants to modify. If anything is ambiguous, ask before proceeding.
Show a preview: Before writing anything, describe the change in plain language:
I'm going to correct the surname on Clayton Rufus (I0005) from "Smyth" to "Smith". Want me to go ahead?
Wait for confirmation: Do not write the file until the user says yes.
Apply the edit via a Python script that:
Add a changelog note to every modified record:
NOTE [CHANGELOG] 2026-03-15: Corrected surname from Smyth to Smith (source: baptism record St. Mary's 1842)
Report what was done: After writing, summarize the changes made.
data_backup_20260617.gramps) and tell them you did.For edits, use the Gramps API — import with import_as_dict, modify objects in memory using DbTxn, then serialize back with GrampsXmlWriter. Run with <skill-dir>/scripts/gramps_python script.py.
from gramps.gen.db.utils import import_as_dict
from gramps.cli.user import User
from gramps.gen.db import DbTxn
from gramps.plugins.export.exportxml import GrampsXmlWriter
from gramps.version import VERSION
from gramps.gen.lib import Note, NoteType
import datetime
filepath = "data.gramps"
db = import_as_dict(filepath, User())
# Locate target person, make changes to the object...
# Changelog note — add as a linked Gramps Note object
note = Note()
note.set_type(NoteType.GENERAL)
note.set(f"[CHANGELOG] {datetime.date.today().isoformat()}: Updated occupation from "
f"'Student, Millhaven Middle School' to 'Foreman, Millhaven Grain Processing' "
f"(source: per family member correction March 2026)")
with DbTxn("Update Clay's occupation", db) as txn:
db.add_note(note, txn)
person.add_note(note.handle)
db.commit_person(person, txn)
GrampsXmlWriter(db, compress=0, version=VERSION, user=User()).write(filepath)
print(f"Saved. {db.get_number_of_people()} people in file.")
Always use DbTxn to wrap all write operations, and re-serialize with GrampsXmlWriter after editing.
Switch to this mode when the user wants to check their Gramps XML file for errors, data quality issues, or structural problems.
Trigger when the user asks:
Run the bundled scripts/gramps_validate.py script:
python <skill-dir>/scripts/gramps_validate.py -i path/to/data.gramps
Options:
| Flag | Default | Description |
|---|---|---|
-i / --input | required | Gramps XML file path |
-f / --format | text | Output format: text or json |
--all | off | Show suppressed noise warnings too |
Prerequisites: Gramps must be available; the script handles setup automatically.
The script runs two Gramps phases:
Error levels:
E (error) — definite data problem; worth investigating and fixingW (warning) — possible issue; may be legitimate (e.g. large age gap between siblings)Noise filtering: By default, low-signal issues are suppressed:
"Husband and wife with the same surname" — coincidental surname match, rarely meaningfulUse --all to see the full unfiltered output.
Exit codes: 0 = no errors; 1 = one or more errors found (useful for scripting).
For any real errors found, switch to Edit Mode to fix them. Common fixes:
"Invalid birth date" with an empty DATE field — remove the empty BIRT tag or add a date"Person events not in chronological order" — check event dates for that individual"Children not in chronological order" — check birth dates of children in that familypython <skill-dir>/scripts/gramps_validate.py -i data.gramps -f json
Output structure:
{
"summary": {"errors": 3, "warnings": 2, "noise_suppressed": 0},
"errors": [
{"source": "verify", "level": "E", "record_type": "Person",
"record_id": "IJANPETERRYNDERS", "record_name": "Rynders, Jan Peter",
"message": "Person events are not in chronological order"}
],
"warnings": [...]
}
Switch to this mode when the user wants a visual report — a pedigree chart, relationship graph, fan chart, descendant tree, or any other graphical output from their genealogy data. Report Mode uses the bundled scripts/gramps_report.py script, which produces publication-quality reports.
Trigger Report Mode when the user asks for something visual or printable:
If the user asks for an ASCII chart or text-based tree, stay in Read Mode and generate it with a Python script instead. Report Mode is specifically for graphical output (PDF, PNG, SVG, DOT).
Identify the center person. Most reports require a Gramps pid. Gramps assigns its own internal person IDs. Run python <skill-dir>/scripts/gramps_report.py --list-people -i data.gramps to list all people with their Gramps-assigned IDs. Match by name and use that ID as the --pid value.
Choose the report type and format. Match the user's request to one of the available reports:
rel_graph — Relationship Graph (full or filtered network)ancestor_chart — Pedigree / Ancestor Treedescend_chart — Descendant Treefamily_descend_chart — Descendant Tree including spousesfan_chart — Circular ancestor charthourglass_graph — Ancestors above, descendants belowtimeline — Chronological life eventsindiv_complete — Complete Individual Reportkinship_report — Everyone related to center personfamily_group — Single family unit detail sheetSee scripts/gramps_report.py --help for the full list and available options.
Run the script:
python <skill-dir>/scripts/gramps_report.py \
-i path/to/data.gramps \
-o path/to/output.pdf \
-f pdf \
-r rel_graph \
-p I123 \
-e "filter=2,dpi=300"
(Replace <skill-dir> with the actual path to this skill's directory.)
Report the result. Tell the user what was generated, the file path, and file size. If the format is viewable (PNG, SVG), offer to open or display it.
-e "dpi=300" for print quality.filter=1 (descendants) or filter=3 (ancestors) keeps things focused. filter=0 includes the entire database.-e "maxgen=N" on ancestor/descendant charts to limit depth.person.get_gender() == Person.MALE / == Person.FEMALE — never compare against raw integersnpx claudepluginhub micahyoung/agent-skills --plugin genealogySearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.