Files
rpg-companion-sillytavern/AGENTS.md
2026-07-03 11:22:30 +02:00

16 KiB

RPG Companion for SillyTavern — Agent Instructions

What this is

A browser extension for SillyTavern (AI roleplay frontend). It is NOT a standalone Node.js application. All JavaScript runs in the browser context inside SillyTavern. There is no build step — files are loaded directly by SillyTavern's extension loader.


Quick start

  • Entry point: index.js (loaded by SillyTavern via manifest.json)
  • CSS: style.css (single file, ~12,300 lines)
  • HTML templates: template.html (main panel), settings.html (Extensions tab settings drawer)
  • Runtime: Browser only. All code executes inside SillyTavern's page context.
  • jQuery: Available globally as $ / jQuery. Used extensively for DOM manipulation.
  • ES modules: All src/ files use import/export (browser ES module syntax).

Manifest and loading

manifest.json tells SillyTavern how to load the extension:

{
  "display_name": "RPG Companion",
  "loading_order": 100,
  "js": "index.js",
  "css": "style.css"
}

SillyTavern loads index.js and style.css relative to the extension directory. The extension name used internally is third-party/rpg-companion-sillytavern.

Extension path detection

src/core/config.js auto-detects whether the extension is installed globally (public/extensions/) or per-user (data/default-user/extensions/) by inspecting import.meta.url. This determines template loading paths.


SillyTavern integration

The extension imports from SillyTavern's global scripts using relative paths that assume the extension lives at:

  • scripts/extensions/third-party/rpg-companion-sillytavern/ (global install), or
  • data/default-user/extensions/third-party/rpg-companion-sillytavern/ (user install)

Key imports in index.js:

import { getContext, renderExtensionTemplateAsync, extension_settings as st_extension_settings }
  from '../../../extensions.js';
import { eventSource, event_types, substituteParams, chat, saveSettingsDebounced, ... }
  from '../../../../script.js';
import { selected_group, getGroupMembers } from '../../../group-chats.js';
import { power_user } from '../../../power-user.js';

The deeper relative imports in src/ files (e.g., ../../../../../../script.js) follow the same pattern adjusted for directory depth.

SillyTavern globals available at runtime

  • self.SillyTavern — global namespace object
  • $ / jQuery — jQuery
  • toastr — toast notification library
  • eventSource — SillyTavern's event emitter
  • chat — current chat message array
  • chat_metadata — per-chat metadata store
  • characters — loaded character array
  • getContext() — returns current chat context

Architecture

Source tree (src/)

src/
├── core/                    # Core infrastructure
│   ├── config.js            # Extension name, folder path, default settings
│   ├── state.js             # Centralized state variables + getters/setters
│   ├── persistence.js       # Save/load settings, chat data, migrations
│   ├── events.js            # SillyTavern event system wrapper
│   └── i18n.js              # Translation system (fetches JSON, applies data-i18n-key)
├── i18n/                    # Translation JSON files
│   ├── en.json              # English (reference, 513 lines)
│   ├── fr.json              # French
│   ├── ru.json              # Russian
│   ├── zh-cn.json           # Simplified Chinese
│   ├── zh-tw.json           # Traditional Chinese
│   └── validator.js         # Node.js script to validate translation consistency
├── systems/                 # Feature systems (organized by concern)
│   ├── generation/          # AI prompt generation, parsing, API calls
│   │   ├── promptBuilder.js # Builds tracker prompts for AI
│   │   ├── parser.js        # Parses AI responses to extract tracker data
│   │   ├── apiClient.js     # Makes API calls for separate/external generation
│   │   ├── injector.js      # Injects tracker prompts into generation context
│   │   ├── lockManager.js   # Manages locked tracker fields
│   │   ├── suppression.js   # Controls when to skip tracker injection
│   │   ├── encounterPrompts.js  # Combat encounter prompt building
│   │   ├── inventoryParser.js   # Parses inventory data from AI responses
│   │   └── jsonPromptHelpers.js # JSON formatting helpers for prompts
│   ├── rendering/           # UI rendering functions
│   │   ├── userStats.js     # Renders user stats panel
│   │   ├── infoBox.js       # Renders info box (date, weather, location, events)
│   │   ├── thoughts.js      # Renders character thoughts + chat bubbles
│   │   ├── inventory.js     # Renders inventory section
│   │   ├── equipment.js     # Renders equipment section
│   │   ├── quests.js        # Renders quests section
│   │   └── musicPlayer.js   # Renders Spotify music player
│   ├── ui/                  # UI components and helpers
│   │   ├── theme.js         # Theme system (default, sci-fi, fantasy, cyberpunk, custom)
│   │   ├── modals.js        # Settings modal, dice modal, deprecation modal
│   │   ├── layout.js        # Panel positioning, collapse/expand, section visibility
│   │   ├── mobile.js        # Mobile FAB button, mobile tabs, keyboard handling
│   │   ├── desktop.js       # Desktop strip widgets
│   │   ├── trackerEditor.js # Tracker configuration editor
│   │   ├── promptsEditor.js # Custom prompt editor
│   │   ├── checkpointUI.js  # Chapter checkpoint buttons
│   │   ├── encounterUI.js   # Combat encounter modal
│   │   ├── snowflakes.js    # Snowflake animation effect
│   │   ├── weatherEffects.js # Dynamic weather visual effects
│   │   └── alternatePresentCharacters.js  # Below-chat character panel
│   ├── integration/         # SillyTavern event integration
│   │   ├── sillytavern.js   # Event handlers: message sent/received, swipe, etc.
│   │   └── thoughtBasedExpressions.js  # Integrates with ST Character Expressions
│   ├── interaction/         # User interaction handlers
│   │   ├── inventoryActions.js  # Inventory click/edit handlers
│   │   ├── equipmentActions.js  # Equipment click/edit handlers
│   │   └── inventoryEdit.js     # Inventory inline editing
│   └── features/            # Feature modules
│       ├── plotProgression.js  # Plot progression buttons
│       ├── classicStats.js     # D&D-style attribute buttons
│       ├── dice.js             # Dice roller
│       ├── htmlCleaning.js     # Regex-based HTML tag cleaning
│       ├── jsonCleaning.js     # Regex-based JSON cleanup in messages
│       ├── musicPlayer.js      # Spotify URL parsing and embedding
│       ├── chapterCheckpoint.js # Save/restore tracker state checkpoints
│       ├── avatarGenerator.js  # Auto-generate character avatars
│       └── encounterState.js   # Combat encounter state management
├── types/                   # JSDoc type definitions
│   └── inventory.js         # InventoryV2 type definition
└── utils/                   # Utility functions
    ├── avatars.js           # Avatar URL handling
    ├── imageUrls.js         # Image URL utilities
    ├── itemParser.js        # Item string parsing
    ├── jsonMigration.js     # v2 to v3 JSON format migration
    ├── jsonRepair.js        # JSON repair for malformed AI responses
    ├── migration.js         # Settings migration helpers
    ├── presentCharacters.js # Present character data helpers
    ├── responseExtractor.js # Extract tracker data from AI responses
    ├── security.js          # Input validation and sanitization
    ├── sillyTavernExpressions.js  # ST expression mapping
    ├── thoughtBasedExpressionPortraits.js  # Expression-to-portrait mapping
    └── transformations.js   # Data transformation utilities

Data flow

  1. User sends messageMESSAGE_SENT event fires
  2. Extension hooks onMessageSent in sillytavern.js
  3. Together mode: Tracker prompt is injected into the generation context via injector.js
  4. Separate mode: After main response, apiClient.js makes a separate API call
  5. External API mode: Uses user-configured external API endpoint
  6. AI respondsMESSAGE_RECEIVED event fires
  7. Extension hooks onMessageReceivedparser.js extracts tracker data
  8. Rendering: renderUserStats(), renderInfoBox(), renderThoughts(), etc. update the UI
  9. Persistence: Tracker data is saved to chat_metadata per chat, and per-swipe

State management

All extension state lives in src/core/state.js as module-level let variables with getter/setter functions. Key state:

  • extensionSettings — persisted settings object (merged with defaults on load)
  • lastGeneratedData — tracker data from the last AI generation
  • committedTrackerData — tracker data committed after a message
  • lastActionWasSwipe — flag to handle swipe-specific logic
  • isGenerating — flag to prevent re-entrant generation
  • pendingDiceRoll — dice roll result to inject into next generation
  • DOM element caches: $panelContainer, $userStatsContainer, etc.

Persistence

src/core/persistence.js handles:

  • Extension settings: saved via SillyTavern's saveSettingsDebounced()
  • Chat-specific data: saved in chat_metadata.rpg_companion per chat
  • Per-swipe data: stored on each chat message object
  • Settings versioning: automatic migration via CURRENT_SETTINGS_VERSION (currently 7)
  • Deferred saves: batches chat data saves to avoid excessive writes

Development commands

# Validate translation files (check missing keys, type mismatches, empty values)
npm run validate_locale_once

# Watch mode: re-validate on file changes
npm run validate_locale

No build, lint, test, or typecheck commands exist. The code runs directly in the browser.


i18n system

How it works

  1. Translation files are flat JSON in src/i18n/{locale}.json
  2. Keys use dot notation: "template.settingsModal.display.showUserStats"
  3. HTML elements use data-i18n-key, data-i18n-title, or data-i18n-aria-label attributes
  4. src/core/i18n.js fetches translations via fetch() from the extension path
  5. applyTranslations() walks the DOM and updates textContent, title, and aria-label

Adding translations

  1. Add keys to src/i18n/en.json (reference locale)
  2. Add corresponding keys to all other locale files
  3. Add data-i18n-key="your.key.here" to HTML elements in template.html or settings.html
  4. For dynamically generated text, use i18n.getTranslation('your.key.here') in JS

Validator

src/i18n/validator.js is a Node.js script (uses require(), not ES modules). It:

  • Compares all locales against the reference locale for missing/extra keys
  • Checks for type mismatches
  • Checks for empty strings and null values
  • Scans .html/.js/.jsx files for unlocalized text (text in tags without data-i18n-key)

Key conventions

jQuery usage

DOM manipulation uses jQuery throughout. Element caches are stored as jQuery objects with $ prefix:

$panelContainer = $('#rpg-companion-panel')
$userStatsContainer = $('#rpg-user-stats')

Event system

All SillyTavern event registration goes through src/core/events.js:

registerAllEvents({
    [event_types.MESSAGE_SENT]: onMessageSent,
    [event_types.CHAT_CHANGED]: [onCharacterChanged, updatePersonaAvatar],
});

CSS naming

All CSS classes use rpg- prefix to avoid conflicts with SillyTavern and other extensions:

  • .rpg-panel, .rpg-section, .rpg-stat-bar, .rpg-btn-primary, etc.
  • CSS custom properties: --rpg-bg, --rpg-accent, --rpg-text, --rpg-highlight, --rpg-border, --rpg-shadow

Panel structure

The main panel (template.html) has these sections in order:

  1. Collapse toggle button
  2. Strip widget container (shown when panel is collapsed)
  3. Game container with header
  4. Dice roll display
  5. Content box with sections: User Stats → Info Box → Thoughts → Inventory → Equipment → Quests → Music Player
  6. Feature toggles row (HTML, Dialogue Coloring, Deception, Omniscience, CYOA, Spotify, Weather, Narrator, Auto Avatars)
  7. Manual update button
  8. Settings and Edit Trackers buttons

Generation modes

Three modes control how tracker data is generated:

  • together: Tracker prompt injected into main generation. Single API call, faster, but tracker JSON mixed in response.
  • separate: Two API calls — one for roleplay, one for tracker data. Cleaner roleplay, slower.
  • external: Uses user-configured external API (OpenAI-compatible endpoint) for tracker generation.

Guided generation compatibility

The skipInjectionsForGuided setting controls tracker injection behavior during guided generations:

  • 'none' — always inject (default)
  • 'impersonation' — skip only for impersonation-style guided generations
  • 'guided' — skip for any guided/instruct or quiet_prompt generation

Important gotchas

  1. No bundler: All imports use relative paths that depend on the extension's directory structure within SillyTavern. Changing file locations requires updating import paths.

  2. Relative import depth matters: Files in src/ use deeper relative paths (../../../../../../script.js) than index.js (../../../../script.js). When moving files between directories, adjust accordingly.

  3. Settings merge: On load, saved settings are deep-merged with defaults from src/core/state.js. New settings fields should be added to both the state module (runtime defaults) AND src/core/config.js (documentation defaults).

  4. Chat metadata: Per-chat tracker data lives in chat_metadata.rpg_companion. Per-swipe data lives on individual chat message objects in the swipe_store.

  5. i18n fetch path: Translations are fetched at /scripts/extensions/third-party/rpg-companion-sillytavern/src/i18n/{lang}.json. The hardcoded path in i18n.js must match the extension's actual URL path.

  6. Extension enable/disable: The extension can be toggled from SillyTavern's Extensions tab. When disabled, all UI elements are removed from the DOM. When re-enabled, initUI() rebuilds everything.

  7. Mobile vs desktop: Viewport breakpoint is 1000px. Below: FAB button + mobile tabs. Above: fixed panel + desktop tabs.

  8. External API key: Stored in localStorage as rpg_companion_external_api_key, NOT in extension settings (for security).

  9. The package.json has no runtime dependencies. The devDependencies (chokidar, fs-extra, glob) are only for the i18n validator.

  10. Deprecation status: The README states this extension is deprecated in favor of Marinara Engine. The code shows a deprecation modal that displays on first load.


File reference

File Purpose
index.js Main entry point. All imports, UI init, event registration, startup logic.
manifest.json SillyTavern extension manifest (name, loading order, JS/CSS files).
style.css All CSS (~12,300 lines). Themes, animations, responsive breakpoints.
template.html Main panel HTML template. Rendered by SillyTavern's renderExtensionTemplateAsync.
settings.html Extensions tab settings drawer. Minimal — most settings are in the panel modal.
src/core/state.js Central state module. All extension-wide variables live here.
src/core/persistence.js Settings/chat data save/load, version migrations.
src/core/config.js Extension name, path detection, default settings documentation.
src/core/events.js SillyTavern event system wrapper with bulk register/unregister.
src/core/i18n.js Translation loader and DOM applier.
src/systems/integration/sillytavern.js All SillyTavern event handlers (message lifecycle, swipes, chat changes).
src/systems/generation/promptBuilder.js Builds AI prompts for tracker generation.
src/systems/generation/parser.js Parses AI responses to extract tracker JSON.
src/systems/generation/apiClient.js API client for separate/external generation modes.
src/systems/generation/injector.js Injects tracker prompts into SillyTavern's generation context.
src/i18n/validator.js Node.js script for validating translation consistency.