fvtt-compendiums
This skill should be used when creating compendium packs, registering packs in manifests, importing/exporting documents, querying pack contents, or using the CLI for pack management and version control workflows.
What this skill does
# Foundry VTT Compendium Packs
**Domain:** Foundry VTT Module/System Development
**Status:** Production-Ready
**Last Updated:** 2026-01-05
## Overview
Compendium packs store pre-built content (actors, items, journal entries, etc.) for distribution with modules and systems. Understanding pack management is essential for content creation.
### When to Use This Skill
- Creating content packs for distribution
- Registering packs in module/system manifests
- Importing/exporting documents programmatically
- Setting up version control workflows with CLI
- Querying and searching pack contents
## Pack Registration
### Manifest Configuration
```json
{
"id": "my-module",
"packs": [
{
"name": "monsters",
"label": "Monsters",
"type": "Actor",
"path": "./packs/monsters",
"system": "dnd5e"
},
{
"name": "items",
"label": "Magic Items",
"type": "Item",
"path": "./packs/items"
}
]
}
```
### Valid Document Types
- `Actor` - Characters, NPCs, creatures
- `Item` - Equipment, spells, features
- `JournalEntry` - Lore, handouts
- `RollTable` - Random tables
- `Scene` - Maps and encounters
- `Macro` - Executable scripts
- `Playlist` - Audio collections
- `Cards` - Card decks
- `Adventure` - Mixed content bundles
### Directory Structure
```
my-module/
├── module.json
├── packs/
│ ├── monsters/ # LevelDB folder (V11+)
│ └── items/
└── src/
└── packs/ # JSON/YAML source (for version control)
```
## Accessing Packs
### Get Pack Reference
```javascript
const pack = game.packs.get("my-module.monsters");
// Check properties
console.log(pack.locked); // Edit lock status
console.log(pack.visible); // User visibility
console.log(pack.metadata); // Pack configuration
```
### Load Index (Lightweight)
```javascript
// Get minimal cached data
const index = await pack.getIndex();
for (const entry of index) {
console.log(entry._id, entry.name);
}
```
### Get Single Document
```javascript
const actor = await pack.getDocument(documentId);
console.log(actor.name, actor.system);
```
### Get Multiple Documents
```javascript
// All documents (expensive for large packs)
const allDocs = await pack.getDocuments();
// Filtered query
const npcs = await pack.getDocuments({ type: "npc" });
```
### Search Contents
```javascript
const results = await pack.search({
query: "dragon",
fields: ["name", "system.description"]
});
```
## Import/Export
### Import Document to World
```javascript
// From pack to world
const pack = game.packs.get("my-module.monsters");
const doc = await pack.getDocument(docId);
const imported = await Actor.create(doc.toObject());
```
### Export Document to Pack
```javascript
// Requires unlocked pack
const pack = game.packs.get("my-module.monsters");
if (!pack.locked) {
await pack.importDocument(existingActor);
}
```
### Bulk Import
```javascript
await pack.importAll({
folderName: "Imported Monsters",
keepId: true // Preserve document IDs
});
```
## CLI Workflow
### Installation
```bash
npm install @foundryvtt/foundryvtt-cli --save-dev
```
### Extract for Version Control
```bash
# Unpack to JSON/YAML
fvtt package unpack -n "monsters" \
--outputDirectory "./src/packs/monsters" \
--yaml \
--folders \
--omitVolatile
```
### Repack for Distribution
```bash
# Pack back to LevelDB
fvtt package pack -n "monsters" \
--inputDirectory "./src/packs/monsters" \
--outputDirectory "./packs"
```
### Programmatic API
```javascript
import { extractPack, compilePack } from "@foundryvtt/foundryvtt-cli";
// Extract
await extractPack({
packName: "monsters",
outputDir: "./src/packs",
yaml: true,
omitVolatile: true
});
// Compile
await compilePack({
packName: "monsters",
inputDir: "./src/packs",
outputDir: "./packs"
});
```
## Version Control Setup
### Git Attributes
```gitattributes
# Treat LevelDB as binary
packs/** binary
```
### Recommended Workflow
1. **Development**: Edit JSON/YAML in `src/packs/`
2. **Build**: Run `fvtt package pack` before commit
3. **Commit**: Include both source and compiled packs
4. **Release**: LevelDB packs ready for distribution
### Volatile Fields
These fields change on access and should be omitted:
```javascript
// Use --omitVolatile flag
_stats.createdTime
_stats.modifiedTime
_stats.lastModifiedBy
_stats.systemVersion
_stats.coreVersion
```
## Common Patterns
### Safe Pack Modification
```javascript
async function addToPack(packId, documentData) {
const pack = game.packs.get(packId);
if (pack.locked) {
ui.notifications.warn("Pack is locked");
return null;
}
return await pack.importDocument(
new Actor(documentData)
);
}
```
### Filter Index by Name
```javascript
async function findByName(packId, searchName) {
const pack = game.packs.get(packId);
const index = await pack.getIndex();
return index.filter(entry =>
entry.name.toLowerCase().includes(searchName.toLowerCase())
);
}
```
### Import with Folder
```javascript
async function importWithFolder(packId, folderName) {
const pack = game.packs.get(packId);
// Create folder if needed
let folder = game.folders.find(f =>
f.name === folderName && f.type === pack.metadata.type
);
if (!folder) {
folder = await Folder.create({
name: folderName,
type: pack.metadata.type
});
}
// Import all to folder
const docs = await pack.getDocuments();
for (const doc of docs) {
const data = doc.toObject();
data.folder = folder.id;
await doc.constructor.create(data);
}
}
```
## Common Pitfalls
### 1. Forgetting Async/Await
```javascript
// WRONG - returns promise, not document
const doc = pack.getDocument(id);
console.log(doc.name); // undefined!
// CORRECT
const doc = await pack.getDocument(id);
console.log(doc.name); // "Dragon"
```
### 2. Modifying Locked Packs
```javascript
// WRONG - will fail silently or error
await pack.importDocument(actor);
// CORRECT - check lock first
if (!pack.locked) {
await pack.importDocument(actor);
} else {
ui.notifications.warn("Unlock the pack first");
}
```
### 3. Loading All Documents
```javascript
// BAD - memory issues with large packs
const all = await pack.getDocuments();
// BETTER - use index for listings
const index = await pack.getIndex();
// Only load specific documents when needed
```
### 4. User Data Overwrite
```javascript
// WARNING: Module updates overwrite pack contents
// Never store user-created content in module packs
// Use world compendiums for user content
```
### 5. Missing System Field
```javascript
// WRONG - pack won't work with system
{
"name": "items",
"type": "Item",
"path": "./packs/items"
}
// CORRECT - include system for typed content
{
"name": "items",
"type": "Item",
"path": "./packs/items",
"system": "dnd5e"
}
```
### 6. Wrong Pack Path
```javascript
// V11+ uses folders, not .db files
// WRONG
"path": "./packs/monsters.db"
// CORRECT
"path": "./packs/monsters"
```
## Implementation Checklist
- [ ] Register packs in manifest with correct type
- [ ] Include `system` field for system-specific content
- [ ] Use folders (not .db files) for V11+ packs
- [ ] Set up CLI for version control workflow
- [ ] Use `--omitVolatile` when extracting for git
- [ ] Check `pack.locked` before modifications
- [ ] Use index for listings, documents for details
- [ ] Test import/export in fresh world
- [ ] Add `.gitattributes` for binary packs
## References
- [Compendium Packs Article](https://foundryvtt.com/article/compendium/)
- [CompendiumCollection API](https://foundryvtt.com/api/classes/client.CompendiumCollection.html)
- [Content Packaging Guide](https://foundryvtt.com/article/packaging-guide/)
- [V11 LevelDB Changes](https://foundryvtt.com/article/v11-leveldb-packs/)
- [CLI Package](https://www.npmjs.com/package/@foundryvtt/foundryvtt-cli)
---
**Last Updated:** 2026-01-05
**Status:** Production-Ready
**Maintainer:** ImproperSubset
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.