| name | rpg-ruleset-cli |
| description | Manage and query tabletop RPG rulesets using the rpg-ruleset-cli tool with category theory-inspired categorical architecture. Use when users want to create, organize, search, or validate RPG rules, or work with theories, interpretations, worlds, entities, and transport functors. |
RPG Ruleset CLI
Use this skill when users want to work with tabletop RPG rulesets, including:
- Creating new systems with categorical architecture (theories → interpretations → worlds)
- Adding and managing rules with validation
- Querying rules with provenance tracking
- Transporting entities between worlds using functors
- Managing characters, objects, events, and locations
CRITICAL: Always Use Absolute Paths
IMPORTANT: Always use absolute paths for all file and directory arguments. Relative paths may not work correctly with bazel run.
✓ CORRECT: /home/xsaw/haskell-monorepo-dnd-rules/rpg/crossed-swords
✗ WRONG: rpg/crossed-swords or ./rpg/crossed-swords
This applies to:
init <PATH>- Use absolute path for the directory to create--data-dir <DIR>- Use absolute path for the data directoryvalidate <FILE>- Use absolute path for the file to validate
Quick Command Reference
Running the CLI
# All commands use this pattern:
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- [COMMAND] [OPTIONS]
Available Commands
init - Initialize a new ruleset
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
init <PATH> --id <SYSTEM_ID> --name "System Name"
add - Add a new rule (auto-suggests ID)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add \
-d <DATA_DIR> \
--category <CATEGORY> \
--title "Rule Title" \
[--id RULE-ID] \
[--visibility public|gm-only] \
[--tag TAG1] [--tag TAG2]
query - Search for rules
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d <DATA_DIR> \
[KEYWORDS...] \
[--category CATEGORY] \
[--system SYSTEM] \
[--tag TAG] \
[--limit N] \
[--show-related]
validate - Validate rule files
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
<FILE> \
[--strict] \
[--all]
list - List systems, categories, or rules
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- list \
systems|categories|rules \
[--system SYSTEM]
info - Show detailed rule information
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- info \
<RULE_ID> \
[--changelog]
Categorical Commands (NEW)
The tool now supports a categorical architecture with three layers:
Layer 1: Theories - Abstract rule schemas Layer 2: Interpretations - Concrete realizations of theories Layer 3: Worlds - Playable game instances
Theory Commands
theory init - Create a new base theory
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
theory init <THEORY_ID> --name "Theory Name"
theory extend - Extend an existing theory
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
theory extend <BASE_THEORY> <EXT_ID> --name "Extension Name"
theory list - List all theories
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
theory list [--show-extensions]
theory info - Show theory details
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
theory info <THEORY_ID>
Interpretation Commands
interp create - Create a new interpretation
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp create <INTERP_ID> --name "Name" --theory <THEORY1> [--theory <THEORY2>...]
interp realize - Map abstract rule to concrete
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp realize <INTERP_ID> <ABSTRACT_ID> \
--concrete <CONCRETE_ID> --title "Title" [--content <FILE>]
interp list - List all interpretations
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp list [--theory <THEORY>] [--show-completeness]
interp validate - Check interpretation completeness
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp validate <INTERP_ID> [--strict]
interp info - Show interpretation details
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp info <INTERP_ID>
World Commands
world create - Create a new world
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
world create <WORLD_ID> --name "World Name" --interp <INTERP_ID>
world list - List all worlds
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
world list [--interp <INTERP_ID>]
world info - Show world details
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
world info <WORLD_ID> [--show-entities] [--show-transport]
Entity Commands
entity create - Create a new entity
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
entity create <TYPE> <ENTITY_ID> \
--world <WORLD_ID> --name "Name" [--file <YAML_FILE>]
Types: character, object, event, location
entity list - List entities in a world
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
entity list <TYPE> --world <WORLD_ID>
entity show - Show entity details
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
entity show <TYPE> <ENTITY_ID> --world <WORLD_ID>
Transport Commands
transport create-functor - Create a transport functor
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport create-functor <FUNCTOR_ID> --name "Name" \
--from <SOURCE_WORLD> --to <TARGET_WORLD> \
--type <TYPE> [--map-file <FILE>]
Functor types: FreeFunctor, ForgetfulFunctor, Projection, Embedding
transport entity - Transport an entity between worlds
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport entity <TYPE> <ENTITY_ID> \
--from <SOURCE_WORLD> --to <TARGET_WORLD> \
--functor <FUNCTOR_ID> [--validate] [--dry-run]
transport validate - Validate a transport functor
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport validate <FUNCTOR_ID> [--check-adjunction]
transport functor - List transport functors
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport functor [--from <WORLD>] [--to <WORLD>]
Global Options
--data-dir, -d DIR- Root directory containing rulesets (default: ".")--role, -r ROLE- User role: player or gm (default: player)--format, -f FORMAT- Output format: text, json, markdown (default: text)--verbose, -v- Enable verbose output
Common Workflows
Workflow 0: Complete Categorical Workflow (NEW)
This demonstrates the full categorical architecture:
# Layer 1: Create Theory (Abstract Rules)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
theory init fantasy-core --name "Fantasy Core Theory" -d /absolute/path
# Layer 2: Create Interpretation (Concrete Rules)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp create crossed-swords --name "Crossed Swords" \
--theory fantasy-core -d /absolute/path
# Realize abstract rules to concrete
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp realize crossed-swords THEORY-COMBAT-001 \
--concrete CORE-001 --title "Attack Resolution" -d /absolute/path
# Validate interpretation completeness
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
interp validate crossed-swords -d /absolute/path
# Layer 3: Create World (Playable Instance)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
world create my-campaign --name "Northern Realms" \
--interp crossed-swords -d /absolute/path
# Add Entities
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
entity create character sir-aldric \
--world my-campaign --name "Sir Aldric" -d /absolute/path
# Create second world
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
world create shadow-realm --name "Shadow Realm" \
--interp shadow-interp -d /absolute/path
# Create transport functor
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport create-functor light-to-shadow \
--name "Corruption Functor" \
--from my-campaign --to shadow-realm \
--type ForgetfulFunctor -d /absolute/path
# Transport entity (dry-run preview)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
transport entity character sir-aldric \
--from my-campaign --to shadow-realm \
--functor light-to-shadow --dry-run -d /absolute/path
Workflow 1: Create New Ruleset System
# 1. Initialize the system (MUST use absolute path)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- \
init /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
--id my-rpg --name "My Fantasy RPG"
# Creates:
# /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg/
# ├── system.yaml
# ├── README.md
# ├── character-creation/
# ├── world-building/
# └── interactions/
# 2. Add first rule (ID auto-suggested) - use absolute path for --data-dir
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
--category character-creation \
--title "Ability Scores" \
--tag core-mechanics
# 3. Edit the generated file to add content
# File created at: /home/xsaw/.../rpg/my-fantasy-rpg/character-creation/<rule-id>.md
# 4. Validate the rule (use absolute path)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
/home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg/character-creation/<rule-id>.md
Workflow 2: Query Rules
# Search for combat rules (use absolute path)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg combat
# Filter by category and tag
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
--category character-creation \
--tag combat \
--limit 10
# Get JSON output for scripting
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
magic --format json | jq
# GM-only rules
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
secret --role gm
Workflow 3: Add Rules with Specific IDs
# Add combat rule with explicit ID (use absolute path)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
--category character-creation \
--id COMBAT-1.0 \
--title "Melee Attacks" \
--tag combat --tag melee \
--visibility public
# Add GM-only secret
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add \
-d /home/xsaw/haskell-monorepo-dnd-rules/rpg/my-fantasy-rpg \
--category world-building \
--title "Campaign Secrets" \
--visibility gm-only \
--tag plot --tag secrets
File Structure
Directory Organization
my-ruleset/
├── system.yaml # System metadata
├── character-creation/ # Required category
│ ├── ability-scores.md
│ ├── classes.md
│ └── combat/ # Subcategories allowed
│ └── melee-combat.md
├── world-building/ # Required category
│ ├── geography.md
│ └── magic-system.md
└── interactions/ # Required category
└── social-rules.md
Rule File Format
Every rule file uses Markdown with YAML frontmatter:
---
category: character-creation
system: my-rpg
rules:
- id: CHAR-001
version: 1.0.0
changelog:
- version: 1.0.0
date: 2025-11-16T10:00:00Z
changes: "Initial version"
tags: [core-mechanics, attributes]
visibility: public
title: "Ability Scores"
# Optional fields:
related: [CHAR-002, COMBAT-1.0]
conditions: ["character.level >= 5"]
formulas:
attribute_modifier: "(score - 10) / 2"
crossSystemRefs:
- targetSystem: base-rpg
targetRule: CORE-1.0
refType: extends
---
## Character Creation
### [CHAR-001] Ability Scores
Every character has six core attributes:
- **Strength (STR)**: Physical power
- **Dexterity (DEX)**: Agility and reflexes
...
Rule ID Conventions
Format Rules
- Pattern:
PREFIX-X.YorPREFIX-XYZ - PREFIX: Uppercase category abbreviation
- Numbers: Version or sequence
Valid Examples
- ✓
CHAR-001- Character rule #1 - ✓
COMBAT-1.0- Combat rule v1.0 - ✓
MAGIC-100- Magic rule #100 - ✓
STEALTH-2.5- Stealth rule v2.5
Invalid Examples
- ✗
char-001- Lowercase prefix - ✗
Combat_1- Underscore separator - ✗
magic.100- Dot separator without prefix
Suggested Prefixes by Category
CHAR-- character-creationCOMBAT-- character-creation/combatCLASS-- character-creation/classesMAGIC-- world-building/magicGEO-- world-building/geographySOCIAL-- interactions/socialEXPLORE-- interactions/exploration
Validation
Common Validation Errors
Invalid Rule ID Format
ERROR: Rule ID must match pattern: UPPERCASE-NUMBER
Fix: Use format like CHAR-001 or COMBAT-1.0
Duplicate Rule ID
ERROR: Rule ID already exists in system
Fix: Use a different ID or let the tool auto-suggest one
Missing Required Fields
ERROR: Missing required field: tags
Fix: Add at least one tag to the rule
Prefix Convention Warning
WARNING: Expected prefix CHAR- for character-creation category
Fix: Use suggested prefix or ignore if intentional
Validation Workflow
# Validate single file
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
my-rpg/character-creation/abilities.md
# Strict mode (warnings = errors)
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
my-rpg/character-creation/abilities.md --strict
# Validate all files in directory
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
my-rpg/character-creation/abilities.md --all
Best Practices
1. Let the Tool Suggest IDs
When adding rules, omit --id to get auto-suggested IDs:
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add \
-d my-rpg --category character-creation --title "New Rule"
# Output: Using suggested rule ID: CHAR-002
2. Use Consistent Tags
- Use lowercase-with-dashes:
core-mechanics,magic-system - Be specific:
melee-combatnot justcombat - Tag for searchability:
dice-rolls,character-advancement
3. Organize with Subcategories
character-creation/
├── core-mechanics.md
├── classes/
│ ├── fighter.md
│ ├── wizard.md
│ └── rogue.md
└── combat/
├── melee.md
└── ranged.md
4. Use Visibility Appropriately
- public (default): Visible to all players
- gm-only: Hidden from players, visible to GMs
# Query as GM to see all rules
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
-d my-rpg --role gm
5. Version Your Rules
Use semantic versioning in rule IDs for major changes:
COMBAT-1.0- Original combat rulesCOMBAT-2.0- Major revision of combat rules
Reference old versions in changelog:
changelog:
- version: 2.0.0
date: 2025-12-01T10:00:00Z
changes: "Replaced COMBAT-1.0 with streamlined system"
6. Link Related Rules
rules:
- id: COMBAT-2.0
related: [CHAR-001, MAGIC-3.0]
# Use --show-related when querying
7. Validate Before Committing
# In git pre-commit hook:
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- validate \
changed-file.md --strict
Troubleshooting
Issue: "Error loading system"
Cause: Not in a valid ruleset directory or missing system.yaml
Solution: Use --data-dir to specify the ruleset directory
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
--data-dir /path/to/my-rpg combat
Issue: "Rule not found"
Cause: Rule ID doesn't exist or visibility mismatch Solution:
- List all rules:
list rules --system my-rpg - Check with GM role:
--role gm
Issue: "File already exists"
Cause: add command won't overwrite existing files
Solution: Edit the existing file manually or use a different rule ID
Issue: Validation fails with prefix warning
Cause: Rule ID prefix doesn't match category convention Solution: Either:
- Change ID to use suggested prefix
- Accept the warning if prefix is intentional
- Use
--strictflag to treat as error if needed
CRITICAL Format Requirements
System.yaml Format
WRONG:
system_id: my-rpg
name: My RPG
type: base # ✗ Wrong - must be "BaseSystem"
version: 1.0.0 # ✗ Wrong - must be object
CORRECT:
system_id: my-rpg
name: My RPG
type: BaseSystem # ✓ Correct
version: # ✓ Correct - object format
vMajor: 1
vMinor: 0
vPatch: 0
categories:
- character-creation
- world-building
- interactions
Rule Frontmatter Format
WRONG:
---
id: CORE-001 # ✗ Wrong - must be "rule_id"
system: my-rpg # ✗ Wrong - must be "system_id"
category: interactions
title: "My Rule"
tags: [core]
related: [] # ✗ Wrong - must be "related_rules"
---
CORRECT:
---
rule_id: CORE-001 # ✓ Correct
system_id: my-rpg # ✓ Correct
category: interactions
title: "My Rule"
visibility: public
version: 1.0.0
tags: [core]
related_rules: [] # ✓ Correct
---
Rule ID Format Restrictions
Rule IDs MUST:
- Use UPPERCASE prefix (2-6 letters)
- Use dash separator
- Use DIGITS ONLY after dash (no dots, no letters)
Valid:
- ✓
CORE-001- Simple number - ✓
COMBAT-100- Three digits - ✓
CHAR-042- Leading zeros OK - ✓
MAGIC-1- Single digit OK
Invalid:
- ✗
CORE-1.0- Dots not allowed - ✗
core-001- Lowercase not allowed - ✗
CORE_001- Underscore not allowed - ✗
CORE-1A- Letters after dash not allowed
Common Init Command Bug
The init command may create system.yaml with incorrect format. After running init, you MUST fix:
# After: bazel run //...rpg-ruleset-cli -- init /path/to/system ...
# Fix system.yaml:
# 1. Change "type: base" → "type: BaseSystem"
# 2. Change "version: 1.0.0" → version object format
# 3. Delete README.md files (they conflict with rule parsing)
README.md Files
CRITICAL: The init command creates README.md files, but the loader tries to parse ALL .md files as rules. This causes "MissingFrontmatter" errors.
Solution: Delete README.md files after init:
rm /path/to/system/README.md
rm /path/to/system/*/README.md
Output Formats
Text (default)
Human-readable output for terminal use
JSON
For scripting and integration:
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
combat --format json | jq '.results[].rule.title'
Markdown
For documentation generation:
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- query \
--category character-creation --format markdown > rules.md
When to Use This Skill
Use rpg-ruleset-cli when the user wants to:
- Create a new RPG system or ruleset
- Add rules to an existing system
- Search for specific rules by keyword, category, or tag
- Validate rule files for correct format
- List available systems, categories, or rules
- Get detailed information about a specific rule
- Organize game rules in a structured, queryable format
- Manage player vs GM visibility of rules
- Version and track changes to rules over time
Integration with Git
The tool is designed to work with version control:
cd my-rpg
git init
git add .
git commit -m "Initial ruleset"
# After adding rules
bazel run //haskell/app/rpg-ruleset-cli:rpg-ruleset-cli -- add ...
git add character-creation/new-rule.md
git commit -m "Add new character rule"
Quick Tips
- Start simple: Use
init→add→validateworkflow - Let tool suggest IDs: Omit
--idfor automatic suggestions - Use tags liberally: Makes querying easier later
- Validate often: Catch errors early with
validate --strict - Query with filters: Narrow results with
--category,--tag,--system - JSON for scripts: Use
--format jsonfor automation - GM role for secrets: Use
--role gmto see hidden rules - Related rules: Use
--show-relatedto see rule connections