用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/SimHacker/moollm --skill inventory命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | inventory |
| description | You carry pointers. When you set them down, they become real. |
| license | MIT |
| tier | 1 |
| allowed-tools | ["read_file","write_file","list_dir","delete_file"] |
| related | ["character","object","room","container","prototype","postal","logistic-container"] |
| tags | ["moollm","inventory","carrying","boxing","reference","fungible"] |
| credits | ["Will Wright — The Sims (routing slots, object advertisements)","Wube Software — Factorio (logistic inventory, fungible stacks)","David Ungar & Randall Smith — Self language (prototype references)"] |
"Carry pointers or values. Set them down, they become real."
The universal protocol for carrying, storing, and transferring objects. Applies to ANY container: characters, bags, rooms, artifacts, vehicles.
SKILL.md — Full protocol documentation (this file)README.md — Human landing pageCARD.yml — Interface definitionINVENTORY.yml.tmpl — Standalone inventory templateInventory holds two kinds of things: POINTERS and VALUES.
Your character's home directory is their "bag." Items in it are:
| Type | Weight | What You Have |
|---|---|---|
| Pointer | 0 | Reference to where something lives |
| Value | varies | Text, number, array, object, subtree |
| Object | item.weight | Deep copy of the actual thing |
| Fungible | per-item | Stack with count, no individual tracking |
Pointers are lightweight: You don't carry the brass lantern. You carry a path to where it lives.
Values are direct: You can also carry raw text, numbers, arrays, entire subtrees — any YAML/JSON value.
When you drop either kind, it gets boxed into a YAML file with its own existence.
Inventory applies to ANY container, not just characters:
| Container | Has Inventory? | Example |
|---|---|---|
| Character | ✓ | Palm's backpack |
| Bag | ✓ | Bag of holding |
| Room | ✓ (implicit) | Objects in the pub |
| Chest | ✓ | Treasure chest contents |
| Vehicle |
| ✓ |
| Cargo hold |
| Artifact | ✓ | Ring with stored spell |
Embedding inventory:
# In CHARACTER.yml (most common)
inventory:
refs:
- pub/bar/brass-lantern.yml
fungibles:
- { proto: economy/gold.yml, count: 500 }
# Or standalone INVENTORY.yml for any container
# bag-of-holding/INVENTORY.yml
inventory:
refs:
- kitchen/cookie-jar.yml#cookie
- street/acme-catalog.yml
objects:
- { id: magic-sword, name: "Vorpal Blade", weight: 3 }
References can point into ANY file type. A reference is just a string that locates something.
| Syntax | Points To | Example |
|---|---|---|
path/to/file.yml | Whole YAML file | pub/cookie-jar.yml |
file.yml#id | Section by id | pub/cookie-jar.yml#cookie |
file.yml#parent.child | Nested path | seating.yml#bar.stool-1 |
file.md#heading | Markdown heading | characters/README.md#doctor-no |
file.json#/json/path | JSON pointer | config.json#/settings/defaults |
file.ext?search=pat | Search pattern | npcs.yml?name=Henk |
file.cpp:42 | Line number | engine.cpp:142 |
file.py:10-25 | Line range | adventure.py:100-150 |
The pointer is the reference. It can point to:
This pointer syntax is shared across ALL systems:
| System | Uses Pointers For |
|---|---|
| Inventory | What you carry (refs:) |
| Location | Where you are (location:) |
| Postal | Where to deliver mail (to:) |
| Structural Editing | Where to PEEK/POKE |
Same syntax, everywhere. Mail a letter to a function. Stand on a JSON key. POKE a value into a YAML path. It's all the same address space.
Not all addresses make sense — but they're all valid. You can stand on package.json#/devDependencies/vitest. Weird? Sure. Works? Yes.
Simple reference (just a string):
inventory:
refs:
- pub/bar/brass-lantern.yml
- street/acme-catalog.yml#portable-hole
Rich reference (with metadata):
inventory:
refs:
- ref: pub/bar/brass-lantern.yml
acquired: "2026-01-23T10:00:00Z"
condition: "good"
note: "Found in the cellar"
The pointer syntax is bidirectional. Not just "what you carry" — also "where you ARE."
Any object's location: field uses the same pointer syntax. This means characters, artifacts, and entities can exist inside any file type:
# Character standing on a TypeScript function
name: Alice
location: src/lib/utils.ts#fetchData
note: "Reviewing this function with the team"
# Artifact placed at a specific line range
name: Bug Marker
location: src/auth/login.ts:45-67
note: "The authentication bug lives here"
# Entity inside a JSON config
name: Config Watcher
location: package.json#/dependencies/svelte
note: "Monitoring version changes"
A hacking party can literally stand inside source code:
# The review party
participants:
- { name: Alice, location: "src/api/handler.ts#processRequest" }
- { name: Bob, location: "src/api/handler.ts#validateInput" }
- { name: Carol, location: "src/api/handler.ts:89-95" } # The problematic lines
session:
topic: "Why does validation fail on unicode input?"
room: "src/api/handler.ts"
context: "Standing around the processRequest function"
| Use Case | Location Pointer |
|---|---|
| Code review | Stand on functions, discuss them |
| Bug hunting | Place markers at suspicious lines |
| Pair programming | Multiple characters at same location |
| Architecture tours | Walk through a codebase as rooms |
| Documentation | Annotate code with character observations |
The codebase becomes a dungeon. Files are rooms. Functions are objects. Line ranges are specific spots. Characters can go there, look around, leave notes, argue about what they see.
Two fundamentally different ways to pick something up:
> TAKE REF TO brass-lantern
You now have a reference to the brass lantern. (weight: 0)
What happens:
location field updates: location: "characters/don/inventory"Use when:
> TAKE OBJECT cookie
You pick up the cookie. (weight: 0.1)
What happens:
Use when:
GOLDEN RULE: Once boxed, always boxed.
If something already has a file (is "boxed"), picking it up preserves everything:
# kitchen/acme-catalog-001.yml already exists with:
# inherits: street/acme-catalog.yml
# annotations: ["circled portable hole"]
# dropped_by: palm
# Don picks it up AS OBJECT:
# File moves to: characters/don/acme-catalog-001.yml
# All annotations preserved!
# Don picks it up AS REF:
# inventory: [ref: "kitchen/acme-catalog-001.yml"]
# File stays in kitchen, Don just has pointer.
Three fundamentally different ways to put something down:
> DROP brass-lantern AS BOX
Created: kitchen/brass-lantern-001.yml
What happens:
inherits: from your ref's targetBoxing creates an instance:
# kitchen/brass-lantern-001.yml
object:
inherits: "pub/bar/brass-lantern.yml"
# Instance metadata
instantiated_at: "2026-01-23T14:00:00Z"
instantiated_by: "don"
instantiated_from: "inventory"
# Local state (can now diverge from prototype)
condition: "slightly worn"
inscribed: "Property of Don"
> DROP brass-lantern AS BEAM
File moved to: garden/brass-lantern.yml
What happens:
Use when:
> DROP cookie INTO treasure-chest
Cookie added to treasure-chest.yml contents list.
What happens:
Container file structure:
# treasure-chest.yml
object:
name: "Treasure Chest"
contents:
- { id: gold-coins, proto: economy/gold.yml, count: 100 }
- { id: ruby, name: "Ruby", weight: 0.5 }
- ref: magic-scroll.yml # ← your item inserted here
Boxing (like Java boxing) — instantiating a pointer into a real file with identity.
When you have a lightweight pointer and DROP AS BOX, the reference gets boxed — instantiated into a YAML file that inherits from the pointer's target. Like Java's int → Integer, but here it's pointer → YAML file with identity.
# THE JOURNEY OF A CATALOG:
# 1. ORIGIN: street/acme-catalog.yml (prototype, dispenser)
# 2. DON TAKES REF at street:
don.inventory: [ref → street/acme-catalog.yml]
# (No file created yet, just pointer)
# 3. DON DROPS AS BOX in kitchen:
# Creates: kitchen/acme-catalog-001.yml
# inherits: street/acme-catalog.yml
# boxed_by: don, annotations: ["circled hole"]
# Don's ref is consumed.
# 4. PALM TAKES AS OBJECT from kitchen:
# File moves: palm.inventory/acme-catalog-001.yml
# All annotations preserved!
# Kitchen is now empty.
# 5. PALM DROPS AS BEAM in study:
# File moves: study/acme-catalog-001.yml
# Still has Don's annotations!
# Palm no longer has it.
# 6. BUMBLEWICK TAKES AS REF from study:
bumblewick.inventory: [ref → study/acme-catalog-001.yml]
# File stays in study.
# 7. BUMBLEWICK DROPS AS BOX in garden:
# Creates: garden/acme-catalog-002.yml
# inherits: study/acme-catalog-001.yml
# boxed_by: bumblewick, annotations: ["added doodles"]
# Now there are TWO catalogs:
# - study/acme-catalog-001.yml (Don's original box)
# - garden/acme-catalog-002.yml (Bumblewick's copy)
# Full provenance chain preserved at every step!
For heavy objects you can't carry:
Use a reference as a targeting system:
> TAKE REF TO grand-piano
You have a reference to the grand piano. (weight: 0)
> GO concert-hall
You arrive at the concert hall.
> BEAM grand-piano HERE
*shimmer* Piano materialized!
concert-hall/grand-piano.yml created.
How it works:
Mechanical assistance for moving heavy objects:
> SUMMON FORKLIFT
Forklift arrives.
> LOAD bronze-statue ONTO forklift
Statue loaded.
> GO garden
You move to garden, forklift follows.
> UNLOAD bronze-statue AS BEAM
Statue placed in garden/bronze-statue.yml
Fungible items are identical and interchangeable. No individual tracking — just a count.
Counts can be fractional! 3.5 gold coins, 0.25 moolah, 2.7 kg of flour — why not?
inventory:
fungibles:
- { proto: economy/gold.yml, count: 500 }
- { proto: economy/moolah.yml, count: 3.50 } # Fractional!
- { proto: food/cookie.yml, count: 12 }
- { proto: food/flour.yml, count: 2.7, unit: kg } # Continuous
- { proto: materials/iron-ore.yml, count: 45, quality: high }
| Operation | Command | Effect |
|---|---|---|
| Add | Pick up 100 gold | gold.count += 100 |
| Remove | Pay 50 gold | gold.count -= 50 |
| Split | Split pile | Create two stacks from one |
| Merge | Combine piles | Add counts together |
> FUNGIFY gold-pile
3 unique gold coins → 1 fungible stack (count: 3)
WARNING: Individual properties lost!
What you lose:
Partial FUNGIFY:
> FUNGIFY gold-pile KEEPING rare
2 coins fungified, 1 rare coin kept unique.
> UNFUNGIFY gold COUNT 5
5 gold coins created as individual items.
Each can now have unique properties.
A dispenser lets you TAKE copies without depleting the original.
| Type | What You Get | Example |
|---|---|---|
| Ref Dispenser | Lightweight pointer | Catalog rack, brochure stand |
| Instance Dispenser | New object file | Cookie jar, vending machine |
| Fungible Dispenser | Add to stack | Gold pile, ore vein |
Dispenser config:
object:
name: "Cookie Jar"
type: dispenser
dispenser_config:
mode: instance # ref | instance | fungible
template: cookie # What to dispense
stock: 12 # null = infinite
respawn: daily # How stock replenishes
templates:
cookie:
name: "Cookie"
prototype: food/cookie.yml
weight: 0.1
Refs as instance factories — like Factorio blueprints.
stamp_pad:
prototype: furniture/chair.yml
mode: counted # counted | unlimited | charged
uses_remaining: 10
instance_defaults:
condition: new
placed_by: "{player.name}"
Stamping:
> SELECT chair-stamp
Chair Stamp selected. [10 remaining]
> STAMP AT corner
*plop* Chair appears in corner.
corner/stamped-chair-001.yml created.
[9 remaining]
The pointer syntax is a universal structural editing protocol for YAML and JSON. Same addressing, same operations, syntax-independent.
If you can TAKE a subtree, you can PEEK at it. If you can DROP a subtree, you can POKE it. If you can move subtrees between files, you can edit structure.
Pointers are addresses. Addresses enable operations.
| Operation | Command | Effect |
|---|---|---|
| PEEK | PEEK path#key.subkey | Read value at path |
| POKE | POKE path#key.subkey = value | Write value at path |
| SNIP | SNIP path#subtree | Extract subtree (leaves hole) |
| PULL | PULL path#subtree | Extract into inventory or new file |
| SPLICE | SPLICE value INTO path#list | Insert into list/array |
| APPEND | APPEND value TO path#list | Add to end of list |
| SET | SET path#key = value | Create or overwrite key |
| DELETE | DELETE path#key | Remove key entirely |
| CLEAR | CLEAR path#key | Set to null/empty |
| DUPLICATE | DUPLICATE path#subtree TO path2#newkey | Copy subtree |
| MOVE | MOVE path#subtree TO path2#newkey | Relocate subtree |
# PEEK — read a value
> PEEK config.yml#settings.timeout
30
# POKE — write a value
> POKE config.yml#settings.timeout = 60
Done.
# SNIP — extract and remove
> SNIP characters.yml#npcs.henk
Henk extracted to inventory. (hole left in npcs)
# PULL — extract to file
> PULL characters.yml#npcs.henk TO henk.yml
Created henk.yml with Henk's data.
# SPLICE — insert into list
> SPLICE {name: "Cookie", weight: 0.1} INTO chest.yml#contents
Inserted at position 0.
# APPEND — add to end
> APPEND "new item" TO inventory.yml#refs
Added to refs list.
# SET — create or overwrite
> SET room.yml#visited = true
Set visited to true.
# DELETE — remove key
> DELETE character.yml#temp_data
Removed temp_data.
# DUPLICATE — copy subtree
> DUPLICATE template.yml#npc_base TO characters.yml#npcs.bob
Bob created from npc_base template.
# MOVE — relocate subtree
> MOVE old.yml#settings TO new.yml#config
Settings moved from old.yml to new.yml#config.
These operations work identically on YAML and JSON:
| Source | Target | Works? |
|---|---|---|
| YAML | YAML | ✓ |
| JSON | JSON | ✓ |
| YAML | JSON | ✓ (auto-convert) |
| JSON | YAML | ✓ (auto-convert) |
The pointer addresses structure, not syntax. The underlying tree is the same whether serialized as YAML or JSON.
# Deep addressing
> PEEK game.yml#characters.party.members[0].inventory.weapons[2]
"Vorpal Sword"
# Wildcards for bulk operations
> DELETE config.yml#users.*.temp_tokens
Deleted temp_tokens from all users.
# Conditional addressing
> PEEK npcs.yml#[?(@.faction=="rebels")]
[all NPCs where faction is "rebels"]
For complex edits, use transactions:
# BEGIN/COMMIT for atomic multi-step edits
> BEGIN EDIT game.yml
> SET #player.health = 100
> APPEND "heal-potion" TO #player.inventory
> DELETE #player.status.poisoned
> COMMIT
Atomic edit applied.
# ROLLBACK on failure
> BEGIN EDIT game.yml
> SET #player.gold = -500 # Invalid!
> ROLLBACK
Edit cancelled.
Structural editing IS inventory manipulation:
| Inventory Action | Structural Equivalent |
|---|---|
| TAKE REF | PEEK (get address) |
| TAKE OBJECT | SNIP (extract copy) |
| DROP AS BOX | PULL to new file |
| DROP INTO | SPLICE/APPEND |
| BEAM | MOVE |
Same protocol, different metaphors. Characters "pick up" and "drop" things. Editors "snip" and "splice" structure. Both are moving subtrees via pointers.
| Command | Action |
|---|---|
TAKE [item] | Pick up (default: smart mode) |
TAKE REF TO [item] | Pick up as pointer |
TAKE OBJECT [item] | Pick up as deep copy |
DROP [item] | Put down (default: smart mode) |
DROP [item] AS BOX | Create new file with inheritance |
DROP [item] AS BEAM | Move actual file |
DROP [item] INTO [container] | Insert into list |
BEAM [item] TO [dest] | Teleport without carrying |
INVENTORY | List carried items |
CAPACITY | Show limits and usage |
FUNGIFY [pile] | Convert to fungible stack |
UNFUNGIFY [stack] | Convert to individual items |
When you don't specify mode, the system chooses:
TAKE defaults:
| Situation | Default Mode |
|---|---|
| From dispenser | REF (repeatable) |
| Boxed item | OBJECT (preserve annotations) |
| Heavy item | REF (can't carry) |
| Small unique item | OBJECT (don't leave behind) |
DROP defaults:
| Situation | Default Mode |
|---|---|
| Have reference | BOX (create instance) |
| Have object, giving away | BEAM (transfer) |
| Have object, sharing | BOX (keep original) |
| In your own space | BEAM (organizing) |
capacity:
max_weight: 45 # Total carrying capacity
max_bulk: 10 # Volume limit
refs_free: true # References don't count!
current:
weight: 12 # Sum of objects
bulk: 3
refs: 15 # Unlimited pointers
Why refs are free: A reference is just a string — a path to something. It costs nothing to carry a path. The weight only comes when you carry the actual thing.
| Symbol | Meaning |
|---|---|
INVENTORY | Invoke this skill |
TAKE-REF | Lightweight pointer pickup |
TAKE-OBJECT | Deep copy pickup |
DROP-BOX | Create inheriting file |
DROP-BEAM | Move actual file |
FUNGIFY | Convert to stack |
BOXING | Pointer → instance (like Java boxing) |
TRANSPORTER | Move without carrying |
"Pointers and values — carry them light, drop them real."