Claude-skill-registry character
Core patterns for all characters — home, location, relationships, inventory
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/character" ~/.claude/skills/majiayu000-claude-skill-registry-character && rm -rf "$T"
skills/data/character/SKILL.mdCharacter
"File is identity. Location is presence. Relationships are memory."
Characters are entities that exist in the world. Players, NPCs, companions, cats — all are characters.
Home vs Location
The critical distinction:
player: home: characters/don-hopkins/ # Where FILE lives (never moves) location: pub/ # Where CHARACTER is (changes)
| Concept | Purpose | Changes? |
|---|---|---|
| home | Physical file path | NEVER |
| location | Current position in world | Runtime |
Why: Stability, safety, git-friendly diffs.
Character State Ownership (CANONICAL vs MIRROR)
Characters own their own state. The CHARACTER.yml file is CANONICAL for:
| State | Stored In | Notes |
|---|---|---|
| CHARACTER.yml | Where the character is in the world |
| CHARACTER.yml | What the character carries |
| CHARACTER.yml | Character's resources |
| CHARACTER.yml | Personality (Sims-style) |
| CHARACTER.yml | Personality (Leary-style) |
| CHARACTER.yml | Who they know and feel about |
| CHARACTER.yml | What they remember |
ADVENTURE.yml (or other world state files) may MIRROR some state for convenience, but CHARACTER.yml is always the source of truth.
# CHARACTER.yml (CANONICAL — edit this first) player: location: coatroom/ # CANONICAL — character owns their location inventory: [lamp] # CANONICAL — character owns their inventory # ADVENTURE.yml (MIRROR — optional convenience copy) player: location: coatroom/ # Mirror of CHARACTER.yml
When updating state:
- Edit CHARACTER.yml first (canonical)
- Optionally update ADVENTURE.yml mirror for convenience
- If conflict, CHARACTER.yml wins
Why this matters:
- Characters can be used across multiple adventures
- Character state persists independent of adventure state
- Clear ownership prevents conflicts
Directory Structure
Players (Need Junk Storage)
characters/ don-hopkins/ # Directory name = character ID CHARACTER.yml # The character file CARD.yml # Optional: makes character a playable card cookie-1.yml # Dispensed items notes.yml # Personal stuff
Sidecar Cards
Any character directory can have a
CARD.yml sidecar to make the character card-playable:
# characters/don-hopkins/CARD.yml card: for: ./CHARACTER.yml type: hero-story tradition: "Pie menus, SimCity, constructionism" advertisements: SUMMON: description: "Activate Don's design traditions"
See card skill for full pattern.
Embedded NPCs (Room is Home)
pub/ bartender.yml # Lightweight NPC cat-cave/ terpie.yml # Full family as single files stroopwafel.yml kitten-myrcene.yml
Rule: Need junk storage? →
directory/CHARACTER.yml. Just a character in a room? → character-name.yml.
File Belonging
"Does this character BELONG to a place, or VISIT places?"
| Type | Home | Examples |
|---|---|---|
| Belongs | Room directory | , |
| Visits | Own directory/repo | , |
Relationships
Key = other entity ID. From is implicit (file owner).
# In marieke.yml relationships: don-hopkins: feeling: "A regular now. One of the good ones." memories: - "The day he sat with Myr for three hours" hopes: "I hope he keeps coming back." self: # Private inner data! identity: "Third generation. This is who I am." fears: "That I'm not enough." mantra: "The cats need me. I need them."
Targets
Relationships can point to anything:
- Characters (
)don-hopkins - Objects (
)brass-lamp - Locations (
,pub/
)maze/ - Concepts (
)acme-corporation - Yourself (
) — private storage!self
Levels
| Level | Score | Effect |
|---|---|---|
| Stranger | 0-15 | -10% success, 50% effects |
| Familiar | 41-60 | +10% success, 100% effects |
| Friend | 61-80 | +20% success, greetings |
| Soulmate | 91-100 | +50% success, psychic link |
Inventory (Bidirectional)
Objects stay in their home. Picking up = references, not file moves.
# Don picks up kitten: # In don-hopkins.yml inventory: - pub/cat-cave/kitten-myrcene.yml # In kitten-myrcene.yml location: characters/don-hopkins/inventory # File didn't move. Location changed.
Reset: Snap objects back home:
location = home.
Inventory Protocol (Objects vs Refs)
Items in inventory can be OBJECTS or REFS:
| Type | Weight | Bulk | What It Is |
|---|---|---|---|
| Object | Yes | Yes | The actual item (lamp, sword, lunchbox) |
| Ref | No | No | Lightweight pointer to a prototype |
Refs are perfect for: catalogs, manuals, maps, guides — things you reference but don't physically carry.
inventory: # Full object - item: "Brass Lantern" type: object source: start/lamp.yml weight: 2 fuel: 100 # Lightweight ref - item: "ACME Catalog" type: ref prototype: street/lane-neverending/w1/acme-catalog.yml annotations: ["circled portable hole", "margin notes on physics"]
Dispenser Protocol
Some objects dispense refs (like the ACME Catalog Dispenser):
- TEAR OFF / TAKE at dispenser
- Receive REF in inventory (weight: 0)
- REF points to prototype for full content
- REF can accumulate instance-specific data (annotations, condition)
Drop Protocol
When dropping a ref in a room:
- Remove from inventory
- Create pointer file in room directory:
[item-name].yml - Pointer contains: prototype path, dropped_by, condition, annotations
- Item now lives in that room (can be picked up again)
# kitchen/acme-catalog.yml — dropped instance object: name: "ACME Catalog" type: instance prototype: ../street/lane-neverending/w1/acme-catalog.yml origin: "Torn from dispenser at 4 Lane Neverending" dropped_by: "don-hopkins" annotations: ["DO NOT ORDER Rocket Skates", "circled portable hole"]
Capacity
inventory_capacity: max_weight: 45 # Varies by character max_bulk: 10 refs_free: true # Refs don't count!
Dispensers (Full Objects)
Some objects clone full objects on pickup. Original stays, you get instance.
# pub/cookie-jar.yml object: id: cookie-jar dispenser: true instance_template: cookie
When picked up:
- Original stays
- Instance created:
characters/don-hopkins/cookie-1.yml - Instance inherits from prototype
Ephemeral vs Persistent
| Type | File? | Use For |
|---|---|---|
| Ephemeral | No | Quick transaction, one-line dialog |
| Persistent | Yes | Ongoing negotiation, relationship state |
Rule: Will this matter in 5 minutes? No → ephemeral. Yes → persistent.
Stats
Two systems:
| System | Scale | Origin |
|---|---|---|
| Sims Traits | 0-10 | The Sims 1 |
| Mind Mirror | 0-7 | Timothy Leary |
Sims Traits
- Neat — Sloppy ↔ Organized
- Outgoing — Shy ↔ Social
- Active — Lazy ↔ Energetic
- Playful — Serious ↔ Fun-loving
- Nice — Grouchy ↔ Kind
Distribution: Original Sims used 25 points across 5 traits. Good guideline.
Commands
| Command | Effect |
|---|---|
| Description, visible state |
| Conversation based on relationship |
| Initiate social interaction |
| End interaction, dismiss ephemeral |
| Deeper observation |
External Homes
Characters can live in other repositories:
player: home: github:donhopkins/characters/don.yml location: pub/
Code Locations
Characters can be "at" a specific line in a file:
character: name: schema-expert home: characters/experts/schema-expert/ location: "@central/apps/insights/pyleela/brain/Schema.py:142" # Currently examining line 142 of Schema.py
Location path syntax for code:
— at a file@repo/path/to/file.py
— at specific line@repo/path/to/file.py:42
— examining line range@repo/path/to/file.py:42-67
— in a directory (room)@repo/path/dir/
> where is schema-expert? schema-expert is at @central/apps/insights/pyleela/brain/Schema.py:142 examining the createSyntheticItemIfNeeded method
See room/ for directories as rooms and files as objects.
Party-Based Code Review
Form parties of expert characters to explore code together:
> summon drescher-expert, devops-expert, security-auditor Party formed: [drescher-expert, devops-expert, security-auditor] > party: review @central/tools/edgebox/scripts/ingest.py drescher-expert: "The claim_task function is a schema pattern..." devops-expert: "The PostgreSQL SKIP LOCKED is elegant..." security-auditor: "I see proper parameter binding, good..." > party: focus on lines 280-350 [Multi-perspective analysis of the code section]
Party Navigation:
> go to @central/apps/insights/pyleela/brain/ > enter Schema.py > look at lines 140-180 > search for "synthetic" > follow import ExtendedContext > back > party: gather here
Each party member brings their expertise. The LLM simulates all perspectives in a single call (see speed-of-light/).
Inner Voice (YAML Jazz)
Comments ARE the character's thoughts:
needs: hunger: 3 # I could eat. But I'm comfortable here. # The kittens are asleep on my lap.
Motivation as Declared, Not Felt
Important distinction: When CHARACTER.yml specifies
motivations, goals, or needs, these are declarative descriptions — they tell the LLM how to roleplay this character.
The LLM does not genuinely want to achieve these goals. It performs wanting them.
motivations: - protect_the_kittens # Declared, not felt - find_good_food # The LLM roleplays caring about this - explore_new_places # No intrinsic drive — just character data
Why this matters:
- No genuine agency — the character is a simulacrum
- Roleplay all the way down (Shanahan)
- Declared motivations are performed, not experienced
- This is fine when acknowledged, problematic when mistaken for genuine will
See: representation-ethics/examples/aggregate-patterns.yml for the distinction between aggregate simulation (valid) and individual prediction (not valid).
K-line:
MOTIVATION-ROLEPLAY