A generic, automated utility for compiling Markdown-based D&D adventures into styled Google Docs, complete with Google Drive image syncing.
- Automated Google Docs Publishing: Combines multiple markdown files, organizes them by category, generates a title page, and publishes a single cohesive Google Document in a pageless reading format.
- Image Syncing: Automatically traverses markdown files finding local images (like maps and NPC portraits), uploads them to Google Drive (making them public), and rewrites the markdown to use the new Drive URLs.
- Missing Asset Review: Finds missing local image files, prints reusable prompts for them, and can hand off generation to a project-specific AI provider adapter.
- Stat Block Formatting: D&D 5E compliant formatting for any blockquotes matching the standard
_Size type, alignment_layout, complete with thematic parchment backgrounds and red dividers.
- Node.js: v20 or newer
- Google Cloud Console Project: You need an active Google Cloud Project with the Google Docs API and Google Drive API enabled.
Because this tool uploads images to Drive and writes documents via Docs, it must authenticate with Google APIs using one of two methods:
- OAuth desktop app (recommended for most individual users)
- Service account (recommended for automation/CI)
The tool uses these scopes:
https://www.googleapis.com/auth/documentshttps://www.googleapis.com/auth/drive
- Create/select a Google Cloud project.
- Enable Google Docs API and Google Drive API.
- Configure one auth method below.
- Run a dry run:
npx campaign-creator publish <adventure-key> --config ./campaign.json --test - Run real publish once auth is confirmed.
- Open Google Cloud Console.
- Create a new project (or select an existing one).
- Enable Google Docs API and Google Drive API.
- Configure OAuth consent screen:
- Go to Google Auth Platform > Branding / Audience.
- If app is in testing mode, add your Google account under Test users.
- Create OAuth client credentials:
- Go to Google Auth Platform > Clients.
- Click Create client.
- Choose Desktop app.
- Download the credentials JSON and save it as
credentials.jsonin your campaign repository root (the directory where you runnpx campaign-creator). - Run a publish command. On first run, a browser window opens for consent.
- After success,
token.jsonis created automatically and reused on future runs.
- In Google Cloud Console, enable Google Docs API and Google Drive API.
- Go to IAM & Admin > Service Accounts and create a service account.
- Create and download a JSON key.
- Save it as
service-account-key.jsonin your campaign repository root. - Share the destination Drive folder (and any existing target document) with the service account email (Editor role), otherwise writes will fail with 403.
- Set auth mode to service account when running commands:
macOS/Linux:
AUTH_METHOD=service-account npx campaign-creator publish my-epic-adventure --config ./campaign.jsonPowerShell:
$env:AUTH_METHOD="service-account"
npx campaign-creator publish my-epic-adventure --config ./campaign.jsonBy default, credential files are discovered in this order:
- Current working directory (
process.cwd(), typically your campaign repo) - Package directory fallback
You can override paths explicitly:
GOOGLE_OAUTH_CREDENTIALS_PATHGOOGLE_SERVICE_ACCOUNT_KEY_PATHGOOGLE_TOKEN_PATH
Auth method can be selected with either:
AUTH_METHODDRIVE_AUTH_METHOD
Accepted auth method values:
oauth(default)service-account
OAuth credentials not found: placecredentials.jsonin your campaign root or setGOOGLE_OAUTH_CREDENTIALS_PATH.Service account key not found: placeservice-account-key.jsonin your campaign root or setGOOGLE_SERVICE_ACCOUNT_KEY_PATH.Invalid service account key format: ensure the file is the original JSON key and includesclient_emailandprivate_key.access_deniedduring OAuth: your Google account may not be listed as a test user on the OAuth consent screen.redirect_uri_mismatch: recreate credentials as Desktop app (not Web app).403 insufficient permissions: for service accounts, ensure the folder/doc is explicitly shared to the service account email.- Images fail to render in Docs: this tool sets image files to public reader links. If your Workspace policy blocks public sharing, those images may need an org-allowed alternative sharing model.
If OAuth tokens become stale/corrupt, delete token.json and authenticate again.
This repository now acts as a generic, reusable engine. Campaign content itself should live in completely separate repositories and link to these tools.
If you prefer manual setup:
-
Create a new directory for your campaign:
mkdir my-campaign && cd my-campaign && npm init -y -
Link the generic tools directly from this engine repository:
npm install file:/path/to/dnd-campaign-creator
-
Copy the
campaign.example.jsonfile from this repository into your new campaign repository and rename it tocampaign.json.
The checked-in example uses null for targetDocId and folderId, so it is safe to copy before you know your final Google Doc or Drive folder IDs.
Your campaign repository must have a campaign.json at its root.
{
"campaignRoot": "./",
"assetsDir": "./assets",
"adventures": {
"my-epic-adventure": {
"title": "My Epic Adventure",
"sourceDir": "adventures/epic",
"targetDocId": null, // Optional: leave null to create a new doc
"folderId": null, // Optional: leave null to keep the doc in the default Drive location
"categories": [
{ "name": "Content", "key": "content", "pageBreakBefore": false },
{
"name": "Session Notes",
"key": "session",
"pageBreakBefore": true,
"excludeFromPublish": true
}
],
"order": {
"content": ["01-intro.md", "02-dungeon.md"],
"session": ["session-1.md"]
}
}
}
}Each adventure can include an optional artStyle object in campaign.json to define the visual identity for generated illustrations. This ensures consistency when creating images across sessions, contributors, or AI tools.
"artStyle": {
"style": "Dark fantasy, painterly, highly detailed",
"medium": "Digital painting with traditional oil painting aesthetic",
"palette": "Desaturated earth tones, warm torchlight ambers, cold dungeon blues",
"lighting": "Dramatic, directional. Deep shadows, high contrast",
"mood": "Epic, foreboding, mysterious",
"subjects": "NPCs as character portraits with environmental context. Locations as wide establishing shots with sense of scale",
"avoid": "Cartoonish styles, bright saturated colors, text or lettering in images"
}| Field | Purpose |
|---|---|
style |
Overall artistic style and level of detail |
medium |
The look and feel of the rendering technique |
palette |
Color palette, including per-section accent colors |
lighting |
Lighting direction, sources, and contrast |
mood |
Emotional tone and atmosphere |
subjects |
Composition guidelines for NPCs vs. locations |
avoid |
Explicit exclusions to maintain consistency |
When generating images (e.g. with an AI tool), prepend the artStyle fields to your prompt to maintain a cohesive visual language across the entire campaign.
Every campaign benefits from a player-facing guide that sets expectations before anyone sits down at the table. This is typically the first file in your adventure's order list (e.g., 000-session-primer.md) and gets published at the top of the Google Doc so players can read it before the first session.
Each campaign is different, but a good player guide generally covers:
- What the game is about — Tone, themes, and what kind of experience to expect (combat-heavy, roleplay-focused, survival horror, political intrigue, etc.)
- Character creation rules — Which rulebooks and sources are allowed, ability score method (Standard Array, Point Buy, rolling), starting level, and any restrictions
- What to bring — Character sheet, dice, pencils, notebooks, supplementary rulebooks
- House rules — Any deviations from the official rules (flanking, fumbles, readied spells, ammunition recovery, shooting into melee, etc.)
- XP or milestone system — How progression works, what earns XP, and what happens when a player misses a session
- Resting and resource management — If your campaign tracks light sources, rations, encumbrance, or dungeon turns
- Player expectations — Rules knowledge responsibility, engagement expectations, and the balance between combat, exploration, and social interaction
- Quick reference tables — Key rules and resource durations in a scannable format
The example campaign.json includes a 000-session-primer.md file as the first entry in the main order. Use this slot for your player guide.
Tip: Players are responsible for knowing the rules for their own characters. If they use material from supplementary sourcebooks, they should bring those rules to the table. Make this explicit in your guide — it saves enormous amounts of time during play.
Once you've linked the toolkit in your campaign repository, you can publish using npx. Run this command from within your campaign repository:
npx campaign-creator publish my-epic-adventure --config ./campaign.jsonYou can append --test to the command to run a dry-run which simulates the file fetching and category organization without hitting the Google APIs or modifying any documents.
Use sync-assets to scan an adventure for missing local image files and print
prompts for the missing assets:
npx campaign-creator sync-assets my-epic-adventure --config ./campaign.jsonAdd --generate only if you have wired a real image provider into
src/ai-service.js:
npx campaign-creator sync-assets my-epic-adventure --config ./campaign.json --generateOut of the box, the repository does not ship a built-in image provider.
If --generate is used before a provider is implemented, the command fails
fast with a clear configuration error rather than pretending success.
generate-map now builds a prompt packet for an image model rather than
trying to lay out rooms procedurally in code. You author the map brief,
attach one or more reference images, and use the generated markdown packet as
the handoff document.
npx campaign-creator generate-map ./examples/gatehouse-ruin.json --output ./examplesGenerated artifact:
<id>-packet.md(reference-image list, area schedule, final prompt, and revision checklist)
Useful options:
--output <dir>write the packet into a different directory--validate-onlyvalidate the authored brief without writing a packet
The example brief in examples/gatehouse-ruin.json
shows the current schema: metadata, reference images, deliverable/style notes,
an authored area schedule, and revision criteria. The technical reference is in
docs/map-system.md.
Generic map lessons worth encoding in briefs:
- Use
deliverable.legendItemswhen the final map needs a controlled symbol legend. - Use
areas[].exitswhen a room must show a specific edge-of-map arrow label. - Keep area labels unique. The validator now rejects duplicate room numbers before packet generation.
- Call out readability risks in
compositionNotesorrevisionChecklistwhen a map has tight clusters, alternate routes, or adjoining quarters that must stay distinct.
The repository currently ships these checks:
npm testruns the Node test suite, including prompt-packet coverage.npm run lintchecks Markdown docs.npm run security:scanscans tracked files for high-signal secrets and credential artifacts.npm run verifyruns lint, tests, the tracked-file security scan, and documentation link checks together.npm run check-linksverifies external links in docs and examples.npm run public:checkrunsverify, scans full git history for secrets, and audits production dependencies.
Public-release checks are available with:
npm run public:checkBefore making a fork or clone public, run:
npm run public:checkThis runs:
npm run verifyfor lint, tests, tracked-file secret scanning, and documentation link checksnpm run security:scan:historyto scan full git history for high-signal secret patternsnpm audit --omit=dev --audit-level=highfor production dependency vulnerabilities
Tracked-file scanning checks for:
- accidentally committed credential artifacts (
credentials.json,token.json,service-account-key.json,.env*, key files) - high-signal secret patterns (private key blocks, common API token formats)
- local-only external reference images under
docs/reference-images/
Credential files are ignored by default in .gitignore, but this scan is the
enforcement layer that prevents accidental exposure.
If security:scan:history reports legacy findings, do not make the repository
public until one of these is complete:
- Rewrite git history to remove the flagged artifacts.
- Publish from a fresh repository initialized from a clean export (no legacy history).
For vulnerability disclosure guidance, see SECURITY.md.
