Files
FoundryVTT/CLAUDE.md
2025-11-06 14:04:48 +01:00

48 KiB

Foundry VTT + PF1e Module Development Environment

Version: 1.0.0 | Last Updated: 2025-01-29 | Maintainer: Development Team

Project Overview

Purpose

This is a development environment for creating custom macros and debugging the Foundry VTT virtual tabletop with the Pathfinder 1e (PF1e) game system. The project combines the Foundry VTT core application (v11.315) with the PF1e system module (v10.8) to enable macro development, automation scripting, and troubleshooting of game mechanics.

Key Objectives

  • Develop custom macros for Pathfinder 1e character automation
  • Debug and troubleshoot Foundry VTT and PF1e system issues
  • Automate complex character abilities (e.g., Magus Arcane Pool, Haste buff management)
  • Provide AI-assisted development via Claude Code with extensive MCP server integration
  • Maintain a professional development environment with linting, formatting, and type checking

Project Type

  • Virtual Tabletop Platform (Foundry VTT)
  • Game System Module (Pathfinder 1e)
  • Macro Development (JavaScript automation scripts)
  • Electron Application (Desktop runtime)
  • Web Application
  • CLI Tool
  • Library/Package

Target Users/Audience

Game Masters (GMs) and players using Foundry VTT for Pathfinder 1e campaigns who need custom automation, particularly for complex character abilities and buff management.


Technology Stack

Core Technologies

  • Language(s): JavaScript (ES6+), TypeScript definitions (.d.ts)
  • Runtime: Node.js v16-19 (via Electron)
  • Platform: Electron (cross-platform desktop app)
  • Foundry VTT Version: v11.315 (stable)
  • Game System: Pathfinder 1e v10.8

Frontend Technologies

Rendering & UI

  • Canvas Engine: PIXI.js v7.2.4 (WebGL/Canvas2D rendering)
    • @pixi/graphics-smooth - Smooth graphics rendering
    • @pixi/particle-emitter - Particle effects
  • DOM Manipulation: jQuery v3.6.4
  • Template Engine: Handlebars v4.7.7
  • Rich Text Editor: ProseMirror v1.x (modular editor)
  • Alternative Editor: TinyMCE v6.7.1
  • Styling: LESS → CSS compilation

UI Framework

// Foundry VTT uses vanilla JavaScript with global objects
game        // Main game instance
ui          // UI managers (notifications, chat, etc.)
canvas      // Canvas rendering system
CONFIG      // Global configuration

Backend Technologies

Server Framework

  • Web Server: Express.js v4.18.2
  • Session Management: express-session with NeDB store
  • File Upload: express-fileupload
  • Compression: compression middleware
  • Static Files: serve-static

Database & Storage

  • Primary Database: NeDB v1.8.0 (embedded NoSQL)
    • World data storage
    • Compendium packs
    • Session management
  • Alternative: classic-level (LevelDB abstraction)
  • File Storage: Local filesystem (fs-extra v11.1.1)
  • Cloud Storage: AWS S3 (@aws-sdk/client-s3 v3.312.0)

Real-Time Communication

  • Protocol: Socket.io v4.6.1 (WebSocket + fallbacks)
  • Features:
    • Room-based communication (per world)
    • Real-time document synchronization
    • Audio/video signaling
    • Client-server data updates

PF1 System Technologies

Build & Development

  • Module System: ES Modules (.mjs files)
  • Build Tool: Vite v4.5.0 (with HMR support)
  • Linting: ESLint v8.53.0
  • Code Formatting: Prettier v3.1.0
  • Testing Framework: @ethaks/fvtt-quench (Foundry-specific testing)
  • Documentation Generator: TypeDoc v0.25.3
  • Git Hooks: Husky v8.0.3 with lint-staged

Development Scripts

{
  "build": "vite build",
  "build:watch": "vite build --mode development --watch",
  "packs:extract": "node ./tools/packs.mjs extract",
  "packs:compile": "node ./tools/packs.mjs compile",
  "lint": "eslint . --cache",
  "format": "prettier --write --cache .",
  "docs": "typedoc"
}

Infrastructure & DevOps

  • Version Control: Git
  • AI Development: Claude Code with 8 MCP servers
    • serena (code navigation + memory)
    • sequential-thinking (complex reasoning)
    • context7 (library documentation)
    • memory (knowledge graph)
    • fetch (web content)
    • windows-mcp (desktop automation)
    • playwright (browser automation)
    • database-server (SQL access)

Development Tools

  • IDE: Visual Studio Code (recommended)
  • Package Manager: npm
  • Node Version: 16-19 (Electron compatible)
  • Module Format: ES Modules (.mjs) for PF1 system, CommonJS for Foundry core

Project Structure

Directory Layout

c:\DEV\Foundry/
├── src/                                    # Source code root
│   ├── FoundryVTT-11.315/                 # Foundry VTT core application
│   │   └── resources/app/                 # Electron app resources
│   │       ├── main.js                    # Electron main process entry
│   │       ├── package.json               # Core dependencies (Node 16-19)
│   │       ├── client/                    # Client-side JavaScript
│   │       │   ├── game.js                # Main game client (Game class)
│   │       │   ├── config.js              # Configuration constants
│   │       │   ├── apps/                  # Application windows (FormApplication)
│   │       │   ├── audio/                 # Audio/sound management
│   │       │   ├── av/                    # Audio/video (WebRTC)
│   │       │   ├── canvas/                # PIXI.js canvas rendering layers
│   │       │   ├── core/                  # Core utilities (hooks, settings)
│   │       │   ├── dice/                  # Dice rolling system (Roll, DiceTerm)
│   │       │   ├── pixi/                  # PIXI.js extensions
│   │       │   ├── tours/                 # User onboarding tours
│   │       │   └── ui/                    # UI components (notifications, dialogs)
│   │       ├── common/                    # Shared client/server code
│   │       │   ├── documents/             # Document base classes
│   │       │   ├── abstract/              # Abstract data models
│   │       │   ├── config.mjs             # Shared configuration
│   │       │   ├── constants.mjs          # Constants and enums
│   │       │   ├── utils/                 # Utility functions
│   │       │   └── packages/              # Package management
│   │       ├── dist/                      # Compiled/bundled code
│   │       ├── public/                    # Public web assets
│   │       │   ├── scripts/               # Bundled client scripts
│   │       │   │   ├── foundry.js         # Main bundled script (~2MB)
│   │       │   │   └── worker.js          # Web worker for background tasks
│   │       │   ├── lang/en.json           # English localization
│   │       │   ├── tours/                 # Tour definitions (JSON)
│   │       │   └── nue/                   # NUE engine assets
│   │       ├── templates/                 # Handlebars templates (.html)
│   │       ├── prosemirror/               # ProseMirror rich text editor
│   │       └── node_modules/              # Foundry core dependencies
│   │
│   ├── foundryvtt-pathfinder1-v10.8/     # Pathfinder 1e System Module
│   │   ├── pf1.js                         # System entry point (imports pf1.mjs)
│   │   ├── pf1.mjs                        # Main system module (ES module)
│   │   ├── package.json                   # PF1 dev dependencies & scripts
│   │   ├── public/                        # Public system files
│   │   │   └── system.json                # System manifest (metadata)
│   │   ├── module/                        # System source code (ES modules)
│   │   │   ├── config.mjs                 # PF1 game configuration
│   │   │   │   # Defines: abilities, skills, alignments, saves, etc.
│   │   │   ├── config.d.ts                # TypeScript definitions for config
│   │   │   ├── modules.mjs                # Module registry/loader
│   │   │   ├── migration.mjs              # Data migration system
│   │   │   ├── patch-core.mjs             # Foundry core monkey patches
│   │   │   ├── socket.mjs                 # Socket event handlers
│   │   │   ├── action-use/                # Action usage system
│   │   │   │   ├── action-use.mjs         # Main action resolution
│   │   │   │   ├── chat-attack.mjs        # Attack chat cards
│   │   │   │   └── chat-context.mjs       # Chat context menu
│   │   │   ├── applications/              # UI applications
│   │   │   │   ├── actor/                 # Actor sheets by type
│   │   │   │   │   ├── character-sheet.mjs
│   │   │   │   │   ├── npc-sheet.mjs
│   │   │   │   │   └── vehicle-sheet.mjs
│   │   │   │   ├── item/                  # Item sheets
│   │   │   │   │   └── item-sheet.mjs
│   │   │   │   ├── compendium-browser/   # Compendium browser
│   │   │   │   ├── sidebar/               # Sidebar applications
│   │   │   │   └── settings/              # Settings dialogs
│   │   │   ├── canvas/                    # Canvas layer extensions
│   │   │   ├── chat/                      # Chat message handlers
│   │   │   ├── components/                # Reusable UI components
│   │   │   ├── dice/                      # Dice rolling extensions
│   │   │   │   └── terms/                 # Custom dice terms
│   │   │   ├── documents/                 # Document classes
│   │   │   │   ├── actor/                 # ActorPF class
│   │   │   │   │   ├── entity.mjs         # Main actor class
│   │   │   │   │   ├── calculate.mjs      # Stat calculation
│   │   │   │   │   ├── rest.mjs           # Rest mechanics
│   │   │   │   │   └── spellcasting.mjs   # Spellcasting logic
│   │   │   │   ├── item/                  # ItemPF class
│   │   │   │   │   ├── entity.mjs         # Main item class
│   │   │   │   │   └── types/             # Item type handlers
│   │   │   │   ├── chat-message.mjs       # ChatMessagePF
│   │   │   │   └── combat.mjs             # CombatPF
│   │   │   ├── migration/                 # Version migration scripts
│   │   │   ├── registry/                  # Feature registries
│   │   │   ├── test/                      # Unit tests (Quench)
│   │   │   ├── utils/                     # Utility functions
│   │   │   └── _module.mjs                # Re-exports for easy import
│   │   ├── lang/                          # Localization files
│   │   │   ├── en.json                    # English (primary language)
│   │   │   ├── de.json                    # German
│   │   │   ├── fr.json                    # French
│   │   │   ├── es.json                    # Spanish
│   │   │   ├── it.json                    # Italian
│   │   │   ├── cn.json                    # Chinese
│   │   │   └── pt_BR.json                 # Portuguese (Brazil)
│   │   ├── less/                          # LESS stylesheets
│   │   │   ├── pf1.less                   # Main stylesheet
│   │   │   └── [various component styles]
│   │   ├── packs/                         # Compendium packs (.db files)
│   │   │   # Pre-packaged actors, items, spells, feats, etc.
│   │   ├── help/                          # Help documentation
│   │   ├── changelogs/                    # Version changelogs
│   │   ├── tools/                         # Build/dev tools
│   │   │   └── packs.mjs                  # Compendium pack compiler
│   │   ├── .eslintrc.js                   # ESLint configuration
│   │   ├── .prettierrc.json               # Prettier formatting config
│   │   ├── vite.config.js                 # Vite build configuration
│   │   ├── typedoc.json                   # TypeDoc documentation config
│   │   └── hooks.d.ts                     # TypeScript hook definitions
│   │
│   ├── macro.js                           # Custom Arcane Pool macro
│   │   # Magus class feature automation with dialog UI
│   ├── macro_haste.js                     # Haste buff automation macro
│   │   # Intercepts attacks to apply Haste effects
│   └── zeratal.json                       # Character data export
│       # Example Magus character for testing
│
├── .claude/                               # Claude Code configuration
│   ├── settings.json                      # Shared Claude settings
│   ├── settings.local.json                # Local user settings
│   ├── agents/                            # Specialized AI agents (8 total)
│   ├── commands/                          # Slash commands (9 total)
│   ├── output-styles/                     # Output formatting (6 styles)
│   ├── skills/                            # Custom skills
│   ├── hooks/                             # Event hooks
│   └── tools/                             # Utility scripts
│
├── .mcp.json                              # MCP server configuration
├── .git/                                  # Git repository
├── .gitignore                             # Git ignore rules
├── CLAUDE.md                              # This file - project documentation
├── README.md                              # Project overview
└── QUICKSTART.md                          # Quick start guide

Key Files

Module Organization

The project follows a clear separation between:

  1. Foundry Core - Generic VTT platform code
  2. PF1 System - Pathfinder 1e game rules and mechanics
  3. Custom Macros - User-specific automation scripts

Each layer builds on the previous:

Custom Macros
    ↓ (uses)
PF1 System API (ActorPF, ItemPF, CONFIG.PF1)
    ↓ (extends)
Foundry Core API (Actor, Item, game, ui, canvas)
    ↓ (runs on)
Electron + Node.js

Code Style & Standards

Naming Conventions

Variables & Functions

  • Use camelCase for local variables and function names
  • Use _camelCase for private/internal variables (PF1 system uses this convention)
  • Use descriptive names that reveal intent
  • Prefix boolean variables with is, has, can, or should
// Good
const arcanePool = actor.system.resources.classFeat_arcanePool;
let selectedBuffs = [];
const isActive = buff.system.active;
const hasHasteBuff = actor.items.find(i => i.name === "Haste");

// Avoid
const ap = actor.system.resources.classFeat_arcanePool;
let x = [];
const flag = buff.system.active;

Classes & Interfaces

  • Use PascalCase for classes and constructors
  • Suffix with descriptive type (e.g., Sheet, Application, Service)
  • Foundry documents often use a suffix (e.g., ActorPF, ItemPF)
// Good
class ActorPF extends Actor { }
class CharacterSheetPF extends ActorSheet { }
class DicePoolBuilder { }

// Avoid
class actor { }
class sheet1 { }

Constants & Configuration

  • Use UPPER_SNAKE_CASE for true constants
  • Use camelCase for configuration objects
  • Global CONFIG uses UPPER_CASE keys with nested objects
// Good
const MAX_ARCANE_POOL = 10;
const config = { abilities: { str: "Strength" } };
CONFIG.PF1.abilities = { str: "STR", dex: "DEX" };

// Avoid
const maxarcanepool = 10;
const CONFIG = { abilities: { str: "Strength" } };

Files & Folders

  • Use kebab-case.mjs for ES module files (PF1 system)
  • Use kebab-case.js for CommonJS files (Foundry core)
  • Use descriptive_name.js or camelCase.js for macro files
  • Use _module.mjs for re-export aggregation files
actor-sheet.mjs          # PF1 system module
game.js                  # Foundry core
macro_arcane_pool.js     # Custom macro
_module.mjs              # Re-export file

Code Organization

File Size

  • Keep files under 500 lines when possible
  • Split large classes using mixins or composition
  • Use _module.mjs files to aggregate related exports

Function Complexity

  • Functions should do one thing well
  • Keep functions under 50 lines when possible
  • Maximum 5-6 parameters; use options objects for more
  • Use async/await for all asynchronous operations
// Good - focused function
async function applyArcanePoolBuff(actor, buffName, duration) {
  const buff = actor.items.find(i => i.name === buffName);
  if (!buff) return false;

  await buff.update({
    "system.active": true,
    "system.duration.value": duration
  });

  return true;
}

// Avoid - doing too much
function processArcanePool(actor, buffName, duration, bonusType,
                          bonusAmount, target, options, callback) {
  // 200+ lines of mixed concerns
}

Module Import Organization

Order imports in this sequence:

  1. Foundry core imports
  2. PF1 system imports
  3. Utility imports
  4. Local module imports
// Good
import { ActorSheet } from "../../common/documents/actor.mjs";
import { CONFIG } from "../../common/config.mjs";
import { ActorPF } from "../documents/actor/entity.mjs";
import { applyBuff } from "../utils/buffs.mjs";
import { CharacterSheetHelper } from "./_helper.mjs";

// Avoid mixing import sources

Comments & Documentation

When to Comment

  • DO: Explain why something is done, especially workarounds
  • DO: Document complex Pathfinder 1e rules implementation
  • DO: Add TODOs with context: // TODO: Support metamagic feats
  • DO: Explain Foundry API quirks or version-specific behavior
  • DON'T: State the obvious
  • DON'T: Leave commented-out code (use git history)
// Good - explains why
// Using setTimeout to ensure Foundry's hook cycle completes
// before updating the actor. See Foundry issue #7234
setTimeout(() => actor.update(data), 0);

// Avoid - states the obvious
// Set arcane pool to 5
arcanePool.value = 5;

JSDoc Documentation

Document all public APIs, classes, and exported functions:

/**
 * Applies an Arcane Pool enhancement to a weapon
 * @param {ActorPF} actor - The character using Arcane Pool
 * @param {ItemPF} weapon - The weapon to enhance
 * @param {number} bonus - Enhancement bonus (1-5)
 * @param {string[]} abilities - Special abilities (e.g., "keen", "flaming")
 * @returns {Promise<boolean>} True if successfully applied
 * @example
 * await applyArcanePoolEnhancement(actor, weapon, 2, ["keen"]);
 */
async function applyArcanePoolEnhancement(actor, weapon, bonus, abilities) {
  // Implementation
}

Error Handling

Strategy

  • Use try-catch blocks for Foundry API calls that may fail
  • Use ui.notifications for user-facing errors
  • Log errors to console for debugging
  • Never swallow errors silently
  • Validate inputs early (fail fast)
// Good
try {
  if (!actor) {
    ui.notifications.warn("You must select a token!");
    return;
  }

  const result = await actor.update({"system.hp.value": newHP});
  ui.notifications.info(`HP updated to ${newHP}`);
} catch (error) {
  console.error("Failed to update HP:", error);
  ui.notifications.error("Failed to update HP. Check console for details.");
}

// Avoid
try {
  actor.update({"system.hp.value": newHP});
} catch (e) {
  // Silent failure
}

Performance Considerations

  • Use fromUuidSync() instead of fromUuid() when possible (synchronous is faster)
  • Cache actor/item lookups instead of repeated searches
  • Batch document updates when updating multiple fields
  • Use actor.updateSource() for preparation phase (before rendering)
  • Avoid unnecessary re-renders in dialogs
// Good - batch update
await actor.update({
  "system.hp.value": newHP,
  "system.resources.arcanePool.value": newPool,
  "system.attributes.ac.total": newAC
});

// Avoid - multiple updates
await actor.update({"system.hp.value": newHP});
await actor.update({"system.resources.arcanePool.value": newPool});
await actor.update({"system.attributes.ac.total": newAC});

Foundry VTT API Reference

Official Documentation

Primary API Documentation: Foundry VTT API v11

Key API Modules:

Global Objects

Foundry VTT provides several global objects available in all scripts:

// Core game instance
game                        // Main Game object
game.actors                 // Actor collection
game.items                  // Item collection
game.scenes                 // Scene collection
game.users                  // User collection
game.macros                 // Macro collection
game.settings               // Settings registry
game.i18n                   // Localization

// UI managers
ui                          // UI manager
ui.notifications            // Toast notifications
ui.chat                     // Chat sidebar
ui.combat                   // Combat tracker
ui.actors                   // Actor directory
ui.items                    // Item directory

// Canvas rendering
canvas                      // Canvas manager
canvas.tokens               // Token layer
canvas.lighting             // Lighting layer
canvas.walls                // Walls layer

// Configuration
CONFIG                      // Global configuration object
CONFIG.Actor                // Actor configuration
CONFIG.Item                 // Item configuration
CONFIG.PF1                  // PF1 system configuration

// Utilities
Hooks                       // Hook system
fromUuid()                  // Get document by UUID
fromUuidSync()              // Synchronous UUID lookup

Common API Patterns

Document Access

// Get by ID
const actor = game.actors.get(actorId);
const item = game.items.get(itemId);

// Get by name
const actor = game.actors.getName("Character Name");
const macro = game.macros.getName("Macro Name");

// Get by UUID
const doc = await fromUuid("Actor.abc123.Item.def456");
const doc = fromUuidSync("Actor.abc123.Item.def456"); // Faster, synchronous

// Search collection
const magus = game.actors.find(a => a.name === "Zeratal");
const hastes = game.items.filter(i => i.name.includes("Haste"));

Document Updates

// Update single field
await actor.update({"system.hp.value": 50});

// Update multiple fields (preferred - single transaction)
await actor.update({
  "system.hp.value": 50,
  "system.attributes.ac.total": 25
});

// Update embedded documents (items)
await actor.updateEmbeddedDocuments("Item", [
  {_id: itemId, "system.active": true}
]);

// Create embedded documents
await actor.createEmbeddedDocuments("Item", [itemData]);

// Delete embedded documents
await actor.deleteEmbeddedDocuments("Item", [itemId]);

Hooks System

// One-time hooks (initialization)
Hooks.once("init", () => {
  console.log("Foundry initializing...");
});

Hooks.once("ready", () => {
  console.log("Foundry ready!");
});

// Recurring hooks
Hooks.on("updateActor", (actor, change, options, userId) => {
  console.log(`${actor.name} was updated`);
});

Hooks.on("preCreateItem", (item, data, options, userId) => {
  // Can modify data or return false to prevent
  return true;
});

// Call custom hooks
Hooks.call("myCustomHook", arg1, arg2);
Hooks.callAll("myCustomHook", arg1, arg2);

Dialog API

// Simple dialog
new Dialog({
  title: "Dialog Title",
  content: "<p>Dialog content HTML</p>",
  buttons: {
    yes: {
      icon: '<i class="fas fa-check"></i>',
      label: "Yes",
      callback: (html) => {
        console.log("Yes clicked");
      }
    },
    no: {
      icon: '<i class="fas fa-times"></i>',
      label: "No"
    }
  },
  default: "yes",
  render: (html) => {
    // Setup event listeners
    html.find('.my-input').focus();
  },
  close: (html) => {
    console.log("Dialog closed");
  }
}).render(true);

Notifications

// Toast notifications
ui.notifications.info("Information message");
ui.notifications.warn("Warning message");
ui.notifications.error("Error message");

// Persistent notification
ui.notifications.notify("Message", "info", {permanent: true});

Templates

// Render template with data
const html = await renderTemplate(
  "systems/pf1/templates/actors/character-sheet.hbs",
  { actor: actor, data: actor.system }
);

// Load template
const template = await getTemplate("path/to/template.hbs");

PF1 System API

Actor API (ActorPF)

// Access actor data
actor.system.attributes.hp.value           // Current HP
actor.system.attributes.hp.max             // Max HP
actor.system.attributes.ac.normal.total    // AC
actor.system.resources.classFeat_arcanePool.value  // Class resource

// Class information
actor.classes                              // Object of class items
actor.classes["magus"]                     // Specific class
actor.classes["magus"].level               // Class level

// Buff management
const buffs = actor.items.filter(i => i.type === "buff");
const activeBuff = actor.items.find(i =>
  i.type === "buff" &&
  i.system.active &&
  i.name === "Haste"
);

// Spellcasting
const spellbook = actor.system.spells.spellbooks.primary;
spellbook.spells.level1                    // 1st level spells

Item API (ItemPF)

// Access item data
item.system.active                         // Buff active state
item.system.duration.value                 // Duration remaining
item.system.enhancement                    // Enhancement bonus

// Item usage
await item.use(options);                   // Use item (attack, spell, etc.)
await item.roll();                         // Roll item

// Item types
item.type === "weapon"                     // Weapon
item.type === "buff"                       // Buff/effect
item.type === "spell"                      // Spell
item.type === "feat"                       // Feat
item.type === "class"                      // Class

Configuration (pf1.config)

// Access PF1 configuration
pf1.config.abilities                       // Ability scores
pf1.config.skills                          // Skills
pf1.config.alignments                      // Alignments
pf1.config.saves                           // Saving throws
pf1.config.buffTargets                     // Buff target types
pf1.config.bonusTypes                      // Bonus types

// Via CONFIG global
CONFIG.PF1.abilities                       // Same as pf1.config.abilities

Macro Development Patterns

Basic Macro Structure

All macros should follow this IIFE (Immediately Invoked Function Expression) pattern:

(async () => {
  // 1. Validation - check prerequisites
  if (!token) {
    ui.notifications.warn("You must select a token!");
    return;
  }

  if (!actor) {
    ui.notifications.warn("Selected token has no actor!");
    return;
  }

  // 2. Data access - get required data
  const arcanePool = actor.system.resources.classFeat_arcanePool;
  const magusLevel = actor.classes["magus"]?.level || 0;

  // 3. Business logic - main macro functionality
  async function doSomething() {
    // Implementation
  }

  // 4. Execution
  await doSomething();

  // 5. User feedback
  ui.notifications.info("Macro completed successfully!");
})();

Dialog-Based Macros

For complex user interactions, use the Dialog API:

(async () => {
  // Validation
  if (!token) {
    ui.notifications.warn("You must select a token!");
    return;
  }

  const actor = token.actor;

  // Define dialog function
  function showDialog() {
    // Build HTML content with inline styles
    const dialogContent = `
      <html>
        <head>
          <style>
            .macro-dialog { padding: 10px; }
            .macro-option { margin: 5px 0; }
            .macro-button {
              padding: 5px 10px;
              margin: 2px;
              cursor: pointer;
            }
          </style>
        </head>
        <body class="macro-dialog">
          <h3>Select Options</h3>
          <div class="macro-option">
            <label>
              <input type="checkbox" name="option1" value="keen">
              Keen
            </label>
          </div>
          <div class="macro-option">
            <label>
              <input type="checkbox" name="option2" value="flaming">
              Flaming
            </label>
          </div>
          <div class="macro-option">
            <label>Bonus:
              <select name="bonus">
                <option value="1">+1</option>
                <option value="2">+2</option>
                <option value="3">+3</option>
              </select>
            </label>
          </div>
        </body>
      </html>
    `;

    // Create dialog
    new Dialog({
      title: "Macro Dialog",
      content: dialogContent,
      buttons: {
        confirm: {
          icon: '<i class="fas fa-check"></i>',
          label: "Confirm",
          callback: async (html) => {
            // Extract user selections
            const selectedOptions = [];
            html.find('input[type="checkbox"]:checked').each(function() {
              selectedOptions.push($(this).val());
            });

            const bonus = parseInt(html.find('select[name="bonus"]').val());

            // Process selections
            await processSelection(selectedOptions, bonus);

            ui.notifications.info("Macro applied successfully!");
          }
        },
        cancel: {
          icon: '<i class="fas fa-times"></i>',
          label: "Cancel"
        }
      },
      render: (html) => {
        // Setup dynamic event handlers
        html.find('input[type="checkbox"]').on('change', function() {
          updatePreview(html);
        });
      }
    }).render(true);
  }

  // Helper functions
  async function processSelection(options, bonus) {
    // Implementation
  }

  function updatePreview(html) {
    // Update UI based on selections
  }

  // Execute
  showDialog();
})();

Macro Chaining

Call other macros from within a macro:

(async () => {
  // Get macro by name
  const buffMacro = game.macros.getName("_applyBuff");

  if (!buffMacro) {
    ui.notifications.error("Required macro '_applyBuff' not found!");
    return;
  }

  // Execute macro with arguments
  await buffMacro.execute({
    actorId: actor.id,
    buffName: "Haste",
    duration: 5,
    active: true
  });

  // Continue with other logic
  ui.notifications.info("Buff applied!");
})();

Buff Management Pattern

(async () => {
  const actor = token.actor;
  const buffName = "Haste";

  // Find buff on actor
  const buff = actor.items.find(i =>
    i.type === "buff" &&
    i.name === buffName
  );

  if (!buff) {
    ui.notifications.warn(`Buff "${buffName}" not found on ${actor.name}!`);
    return;
  }

  // Toggle buff
  const newState = !buff.system.active;
  await buff.update({
    "system.active": newState,
    "system.duration.value": newState ? 5 : 0
  });

  ui.notifications.info(
    `${buffName} ${newState ? "activated" : "deactivated"} on ${actor.name}`
  );
})();

Hook Interception Pattern

For advanced macros that intercept game events:

(async () => {
  // Register a temporary hook
  const hookId = Hooks.on("pf1.itemRoll", (item, options) => {
    // Check if this is the right item
    if (item.type !== "weapon") return;

    // Check if actor has Haste buff
    const hasHaste = item.actor.items.find(i =>
      i.type === "buff" &&
      i.name === "Haste" &&
      i.system.active
    );

    if (!hasHaste) return;

    // Modify roll options
    options.extraAttacks = (options.extraAttacks || 0) + 1;
    ui.notifications.info("Haste: Added extra attack!");
  });

  // Clean up hook after some time or condition
  setTimeout(() => {
    Hooks.off("pf1.itemRoll", hookId);
  }, 60000); // Remove after 1 minute
})();

Configuration

Foundry VTT Configuration

System Manifest (system.json)

Located at: system.json

{
  "id": "pf1",
  "title": "Pathfinder 1e",
  "description": "The Pathfinder First Edition game system for Foundry VTT",
  "version": "10.8",
  "compatibility": {
    "minimum": "11",
    "verified": "11",
    "maximum": "11"
  },
  "esmodules": ["pf1.js"],
  "styles": ["pf1.css"],
  "packs": [
    {
      "name": "classes",
      "label": "Classes",
      "type": "Item",
      "system": "pf1"
    }
    // ... more compendium packs
  ],
  "languages": [
    {
      "lang": "en",
      "name": "English",
      "path": "lang/en.json"
    }
    // ... more languages
  ],
  "socket": true,
  "initiative": "1d20 + @attributes.init.total + (@attributes.init.total / 100)"
}

Key Fields:

  • id: System identifier (must be unique)
  • esmodules: JavaScript entry points (loaded as ES modules)
  • styles: CSS stylesheets to load
  • packs: Compendium packs (pre-packaged content)
  • languages: Localization files
  • socket: Enable socket communication for multiplayer
  • initiative: Formula for initiative rolls

PF1 System Configuration

Development Scripts

# Build for production
npm run build

# Development build with watch mode
npm run build:watch

# Extract compendium packs to JSON
npm run packs:extract

# Compile JSON to compendium packs
npm run packs:compile

# Lint code
npm run lint

# Format code
npm run format

# Generate documentation
npm run docs

ESLint Configuration (.eslintrc.js)

module.exports = {
  env: {
    browser: true,
    es2021: true,
    jquery: true
  },
  extends: [
    "eslint:recommended",
    "plugin:prettier/recommended"
  ],
  parserOptions: {
    ecmaVersion: 2022,
    sourceType: "module"
  },
  globals: {
    game: "readonly",
    ui: "readonly",
    canvas: "readonly",
    CONFIG: "readonly",
    Hooks: "readonly",
    foundry: "readonly",
    Actor: "readonly",
    Item: "readonly"
  },
  rules: {
    "no-unused-vars": ["warn", {
      argsIgnorePattern: "^_",
      varsIgnorePattern: "^_"
    }]
  }
};

Prettier Configuration (.prettierrc.json)

{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": false,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "arrowParens": "always"
}

Claude Code Configuration

MCP Servers (.mcp.json)

The project uses 8 MCP servers for AI-assisted development:

{
  "mcpServers": {
    "serena": {
      "description": "Code navigation, symbol search, and project memory",
      "capabilities": ["semantic search", "symbol manipulation", "memory"]
    },
    "sequential-thinking": {
      "description": "Complex problem decomposition and reasoning"
    },
    "context7": {
      "description": "Up-to-date library documentation retrieval"
    },
    "memory": {
      "description": "Knowledge graph for persistent context"
    },
    "fetch": {
      "description": "Web content retrieval and scraping"
    },
    "windows-mcp": {
      "description": "Windows desktop automation and interaction"
    },
    "playwright": {
      "description": "Browser automation and testing"
    },
    "database-server": {
      "description": "SQL Server database access"
    }
  }
}

Claude Settings


Running the Application

Prerequisites

  • Foundry VTT installed (v11.315 recommended)
  • Node.js v16-19 (for PF1 system development)
  • npm (Node Package Manager)
  • Visual Studio Code (recommended IDE)

Initial Setup

# Navigate to project root
cd "C:\DEV\Foundry"

# Install PF1 system dependencies (if developing system)
cd src/foundryvtt-pathfinder1-v10.8
npm install

# Build PF1 system
npm run build

# Or use watch mode for development
npm run build:watch

Running Foundry VTT

Option 1: Run Electron Application

# Navigate to Foundry directory
cd "C:\DEV\Foundry\src\FoundryVTT-11.315"

# Run Foundry (Windows)
.\foundryvtt.exe

# Or run via Node.js
node resources/app/main.js

Option 2: Launch from Start Menu/Desktop

  • Double-click Foundry VTT shortcut
  • Or launch from Windows Start Menu

Accessing the Application

Once Foundry VTT is running:

  1. Setup Screen: First launch shows setup page
  2. Select World: Choose or create a world
  3. Launch World: Click "Launch World"
  4. Login: Enter game master password (if set)

Creating/Using Macros

Import Macro from File

  1. Open Foundry VTT
  2. Click "Macro Directory" in sidebar
  3. Click "Create Macro" button
  4. Set macro type to "Script"
  5. Copy content from macro.js or macro_haste.js
  6. Paste into macro editor
  7. Save macro
  8. Drag to hotbar for quick access

Testing Macros

  1. Select a token on the canvas
  2. Click macro from hotbar
  3. Follow dialog prompts
  4. Check console (F12) for errors/logs

Development Workflow

# 1. Make changes to PF1 system or macros
# Edit files in src/foundryvtt-pathfinder1-v10.8/module/

# 2. Build system (if modified system code)
cd src/foundryvtt-pathfinder1-v10.8
npm run build:watch  # Auto-rebuilds on changes

# 3. Reload Foundry
# Press F5 in Foundry VTT to reload

# 4. Test changes
# Execute macros or test system features

# 5. Check for issues
# Open browser console (F12) for errors

# 6. Format code before committing
npm run format
npm run lint

# 7. Commit changes
git add .
git commit -m "feat: add new macro feature"

Testing Requirements

Manual Testing

For macro development:

  1. Token Selection: Ensure macro validates token selection
  2. Actor Data: Verify correct actor data access
  3. Dialog UI: Test all dialog buttons and inputs
  4. Error Handling: Test with invalid inputs
  5. User Feedback: Verify notifications display correctly
  6. Edge Cases: Test with missing items, zero resources, etc.

Console Debugging

// Add debug logging to macros
(async () => {
  console.log("=== Macro Start ===");
  console.log("Token:", token);
  console.log("Actor:", actor);
  console.log("Actor Data:", actor.system);

  try {
    // Macro logic
  } catch (error) {
    console.error("Macro Error:", error);
    ui.notifications.error(`Error: ${error.message}`);
  }

  console.log("=== Macro End ===");
})();

PF1 System Testing

The PF1 system uses Quench for unit testing:

# Run tests (in Foundry VTT console)
quench.runAll();

# Or access test UI
# Game Settings > System Settings > Test Runner

Browser Console Access

  • Press F12 to open browser developer tools
  • Use Console tab for logs and errors
  • Use Network tab for API call debugging
  • Use Elements tab for DOM inspection

Git & Version Control

Commit Message Format

Follow the Conventional Commits specification:

<type>(<scope>): <subject>

<body>

<footer>

Types

  • feat: New feature (macro, system enhancement)
  • fix: Bug fix
  • docs: Documentation changes
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Maintenance tasks
  • style: Code style changes (formatting)

Scopes

  • macro: Macro development
  • system: PF1 system changes
  • core: Foundry core modifications
  • config: Configuration changes
  • docs: Documentation

Examples

# New macro
feat(macro): add arcane pool enhancement UI

# Bug fix
fix(macro): correct haste buff duration calculation

# Documentation
docs(readme): update macro installation instructions

# Refactoring
refactor(macro): extract dialog HTML to separate function

# System enhancement
feat(system): add support for mythic rules

Branching Strategy

# Main branch for stable code
main

# Feature branches
feature/arcane-pool-macro
feature/haste-automation

# Bug fix branches
fix/dialog-validation

# Create feature branch
git checkout -b feature/new-macro

# Commit changes
git add src/macro_new.js
git commit -m "feat(macro): add new macro feature"

# Merge to main
git checkout main
git merge feature/new-macro

Ignoring Files

The .gitignore should exclude:

# Foundry user data
src/FoundryVTT-11.315/Data/

# Node modules
node_modules/

# Build artifacts
dist/
*.log

# OS files
.DS_Store
Thumbs.db

# IDE files
.vscode/
.idea/

# Temporary files
*.tmp
*.bak

Claude Code Specific Instructions

Mandatory Tooling Usage Policy

CRITICAL: Claude Code must maximize the use of available advanced features for efficiency and quality:

🎯 Agent Usage (REQUIRED)

  • ALWAYS consider using specialized agents for their intended tasks:

    • Explore agent: For codebase exploration, file pattern searches, keyword searches
    • test-engineer: For generating test suites
    • code-reviewer: For code quality reviews
    • refactoring-specialist: For code cleanup
    • debugger: For systematic bug diagnosis
    • architect: For system design planning
    • documentation-writer: For comprehensive documentation
    • security-analyst: For security reviews
  • When NOT to use agents:

    • Single file reads with known path
    • Simple edits to existing macros
    • Tasks completable in 1-2 tool calls

IF YOU DECIDE NOT TO USE AN AGENT: State explicitly at the beginning:

🔧 Agent Decision: Not using agents because [reason: e.g., "single file edit", "straightforward macro modification"]

Slash Command Usage (REQUIRED)

  • ALWAYS check available slash commands before starting tasks
  • Use custom commands when applicable:
    • /test [file]: Generate tests
    • /review [file]: Code review
    • /explain [file]: Explain code
    • /analyze [path]: Code analysis
    • /scaffold [type]: Generate boilerplate

IF YOU DECIDE NOT TO USE SLASH COMMANDS: State explicitly:

🔧 Slash Command Decision: Not using slash commands because [reason]

🔌 MCP Server Usage (REQUIRED)

  • ALWAYS leverage MCP servers:
    • serena: Semantic code search, symbol manipulation
    • context7: Library documentation (Foundry VTT, PIXI.js)
    • fetch: Web content retrieval
    • sequential-thinking: Complex reasoning
    • memory: Knowledge graph
    • playwright: Browser automation
    • windows-mcp: Desktop interaction

IF YOU DECIDE NOT TO USE MCP SERVERS: State explicitly:

🔧 MCP Server Decision: Not using MCP servers because [reason]

When Working with This Project

Claude should:

  • ALWAYS provide tooling decision statements at the start of each task
  • Follow Foundry VTT API best practices
  • Use async/await for all asynchronous operations
  • Validate user input in macros (token selection, actor data)
  • Use ui.notifications for user feedback
  • Add console logging for debugging
  • Use jQuery for DOM manipulation in dialogs
  • Follow the IIFE pattern for macros
  • Test macros with actual Foundry VTT instance
  • Document complex Pathfinder 1e rules implementation
  • Handle API errors gracefully
  • Use parallel tool calls for efficiency
  • Leverage specialized agents and MCP servers

Important Project Context

Critical Files to Understand:

API Documentation:

Common Tasks:

  1. Creating a new macro: Follow IIFE pattern, validate inputs, use Dialog API
  2. Debugging macros: Add console.log statements, check browser console (F12)
  3. Modifying PF1 system: Edit files in module/, run npm run build:watch
  4. Testing changes: Reload Foundry (F5), execute macro, check console

Project-Specific Patterns:

  • All macros use IIFE: (async () => { /* code */ })();
  • Dialog HTML includes inline CSS for styling
  • Buff management via actor.items.find(i => i.type === "buff")
  • Resource access via actor.system.resources.*
  • Macro chaining via game.macros.getName("MacroName")
  • Notifications via ui.notifications.[info|warn|error]()

Task Initiation Requirements

MANDATORY: At the START of EVERY task response, provide:

🎯 Tooling Strategy Decision

Task Analysis: [Brief description of the task]

Tooling Decisions:

  • Agents: Using [agent-name] / Not using - Reason: [specific justification]
  • Slash Commands: Using [/command] / Not using - Reason: [specific justification]
  • MCP Servers: Using [server: tool] / Not using - Reason: [specific justification]
  • Approach: [Overall strategy for completing the task]

Task Completion Status Messages

IMPORTANT: At the end of EVERY response where you performed work, provide:

📊 Task Completion Summary

What Was Done: [Brief description of tasks completed]

Features Involved:

  • Agents: [Agent1, Agent2, or None - with usage justification]
  • Slash Commands: [/command1, /command2, or None - with usage justification]
  • MCP Servers: [Server1: tool1/tool2, Server2: tool3, or None - with usage justification]
  • Core Tools: [Read, Write, Edit, Glob, Grep, Bash, WebFetch, WebSearch, Task]
  • Files Modified: [file1.js, file2.mjs, or None]
  • Performance: [Parallel ops, extended thinking, or N/A]

Efficiency Notes: [Any observations about tooling choices and their impact]


Additional Resources

Documentation

Community

Troubleshooting

  • Browser Console: Press F12 to access developer tools
  • Foundry Logs: Check logs in Data/Logs/ directory
  • Module Conflicts: Disable modules to isolate issues
  • Cache Issues: Clear browser cache or Foundry cache

Contact & Support

Maintainers:

  • Development Team

Getting Help:

  1. Check browser console (F12) for errors
  2. Review Foundry VTT API documentation
  3. Consult PF1 system GitHub issues
  4. Ask in Foundry VTT Discord community

Last Updated: 2025-01-29 Project Version: 1.0.0 Foundry VTT Version: v11.315 PF1 System Version: v10.8