This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm install # install dev deps (jsdom for tests, marked + dompurify for vendor regeneration)
npm test # run all tests via Node built-in test runner
npm run vendor # regenerate vendor/ ESM bundles from installed npm packagesRun a single test file:
node --test --import ./tests/setup.js tests/convert.test.jsA FoundryVTT module (v13/v14) that adds a Paste Markdown button to every ProseMirror editor in Foundry. module.json declares the entry point and compatibility; scripts/main.js is the sole ESModule loaded by Foundry at runtime.
The data flow is: Toolbar hook → Dialog → Convert → Insert
scripts/main.js Foundry init hook — registerSettings() + registerColorMenu() +
registerMenuHook() + a "Global Settings" heading hook; on ready,
applies the callout palette to every client
scripts/settings.js MODULE_ID constant + 7 world-scoped boolean settings (GM-controlled)
plus the non-config calloutColors object setting
scripts/callout-colors.js Pure: DEFAULT_CALLOUT_COLORS, isValidHexColor, buildCalloutColorCss,
applyCalloutColors (injects a <style> overriding :root vars). No Foundry import.
scripts/callout-color-menu.js CalloutColorMenu (ApplicationV2 + Handlebars) + registerColorMenu();
the 13-picker sub-form (registerMenu). Foundry-only — imported only by main.js
scripts/menu-button.js getProseMirrorMenuItems hook — detects surface (Journal/Item/Actor),
gates on setting, adds the toolbar button
scripts/dialog.js DialogV2.wait() — builds textarea UI, reads settings, calls convert(),
checks view.dom is still in the document, then calls insertHtml()
scripts/convert.js Pure function: MD string → sanitized HTML. Receives marked + DOMPurify
as injected deps (no Foundry import) so it is unit-testable in Node+jsdom
scripts/obsidian.js Obsidian-layer transforms: extractFrontmatter, frontmatterToHtml,
stripWikiLinks, transformCallouts. No Foundry imports — fully testable.
scripts/insert.js Uses ProseMirror.DOMParser + view.state.tr.replaceSelection to insert
the HTML slice at the current cursor / selection
vendor/ contains committed, generated ESM bundles — never edit them by hand:
vendor/marked.esm.js←markedvendor/purify.es.mjs←dompurify
After bumping versions in package.json, run npm install && npm run vendor && npm test. CI will fail if vendor/ diverges from the pinned package versions.
All UI strings are keys like markdown-paste.dialog.title. English in lang/en.json, French in lang/fr.json. The Obsidian layer receives localized labels injected by dialog.js via game.i18n.localize(), so obsidian.js itself has no Foundry dependency.
Tests live in tests/ and run under Node's built-in node:test runner. There is no Jest/Vitest. jsdom is configured per-suite (not globally). convert.js and obsidian.js are deliberately free of Foundry globals so they can be unit-tested in Node.
- The default ongoing development branch is
develop.mainonly receives released code. - Unless explicitly instructed otherwise, every new feature or fix gets its own branch off
develop, namedfeature-xxxfor features orbugfix-yyyfor fixes. Use branches, not git worktrees. - A change ships in two PRs: feature/bugfix branch →
develop, thendevelop→main. The release workflow then runs from the tag pushed onmain(see Releasing).
Releases are automated by .github/workflows/release.yml, which fires on any tag matching v*.
From the main branch, run:
./release.sh
The script handles version selection, module.json edits, the release commit, tag creation, push, and the merge-back to develop. It validates that CHANGELOG.md already has a ## [X.Y.Z] - YYYY-MM-DD entry for the target version and aborts cleanly if not.
End state: you are on develop, fully synced with main. The GitHub Actions release workflow runs from the pushed tag and submits to FoundryVTT.
Pre-release tags (vX.Y.Z-beta.N, vX.Y.Z-rc.N, etc.) create a GitHub pre-release and skip the FoundryVTT publish step.
The workflow builds markdown-paste.zip containing the runtime files referenced by module.json:
module.json
scripts/
styles/
lang/
templates/
vendor/
images/, docs/, temp/, CHANGELOG.md, README.md, LICENSE, and .github/ are repo-only and excluded.
If the workflow is broken or unavailable, the release can be produced by hand:
zip -r /tmp/markdown-paste.zip module.json scripts/ styles/ lang/ templates/ vendor/
gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes /tmp/markdown-paste.zip module.jsonThen submit the release to FoundryVTT by editing the package on foundryvtt.com (no automated submission in this path).
If installed and available, use codebase-memory-mcp tools first for any structural code exploration:
search_graph(name_pattern/label/qn_pattern)— find functions, classes, modules by nametrace_path(function_name, mode=calls|data_flow)— follow call chainsget_code_snippet(qualified_name)— read source for a specific symbolget_architecture(aspects)— understand project structuresearch_code(pattern)— graph-augmented text search
If the project is not yet indexed, run index_repository first using the mode full. Fall back to Grep/Glob/Read only for config values, non-code files, or plain text content.
If the Context7 MCP server is available, when working with FoundryVTT APIs, use it to fetch up-to-date documentation rather than relying on training data:
mcp__plugin_context7_context7__resolve-library-id({ libraryName: "foundryvtt" })
mcp__plugin_context7_context7__query-docs({ context7CompatibleLibraryID: "...", query: "..." })
Use this for: ApplicationV2, DocumentSheet, Hooks API, canvas/scene APIs, compendium packs, data models, and any other FoundryVTT or CoC7 system APIs.
- FoundryVTT API docs: https://foundryvtt.com/api/
- Design specs:
docs/superpowers/specs/ - Implementation plans:
docs/superpowers/plans/ - FoundryVTT v13 Source :
../Foundry Virtual Tabletop/v13(workspace sibling) - FoundryVTT v14 Source :
../Foundry Virtual Tabletop/v14(workspace sibling)