@quilltap/plugin-utils 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,66 +27,9 @@ __export(roleplay_templates_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(roleplay_templates_exports);
29
29
 
30
- // src/logging/plugin-logger.ts
31
- function getCoreLoggerFactory() {
32
- return globalThis.__quilltap_logger_factory ?? null;
33
- }
34
- function createConsoleLoggerWithChild(prefix, minLevel = "debug", baseContext = {}) {
35
- const levels = ["debug", "info", "warn", "error"];
36
- const shouldLog = (level) => levels.indexOf(level) >= levels.indexOf(minLevel);
37
- const formatContext = (context) => {
38
- const merged = { ...baseContext, ...context };
39
- const entries = Object.entries(merged).filter(([key]) => key !== "context").map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
40
- return entries ? ` ${entries}` : "";
41
- };
42
- const logger = {
43
- debug: (message, context) => {
44
- if (shouldLog("debug")) {
45
- console.debug(`[${prefix}] ${message}${formatContext(context)}`);
46
- }
47
- },
48
- info: (message, context) => {
49
- if (shouldLog("info")) {
50
- console.info(`[${prefix}] ${message}${formatContext(context)}`);
51
- }
52
- },
53
- warn: (message, context) => {
54
- if (shouldLog("warn")) {
55
- console.warn(`[${prefix}] ${message}${formatContext(context)}`);
56
- }
57
- },
58
- error: (message, context, error) => {
59
- if (shouldLog("error")) {
60
- console.error(
61
- `[${prefix}] ${message}${formatContext(context)}`,
62
- error ? `
63
- ${error.stack || error.message}` : ""
64
- );
65
- }
66
- },
67
- child: (additionalContext) => {
68
- return createConsoleLoggerWithChild(prefix, minLevel, {
69
- ...baseContext,
70
- ...additionalContext
71
- });
72
- }
73
- };
74
- return logger;
75
- }
76
- function createPluginLogger(pluginName, minLevel = "debug") {
77
- const coreFactory = getCoreLoggerFactory();
78
- if (coreFactory) {
79
- return coreFactory(pluginName);
80
- }
81
- return createConsoleLoggerWithChild(pluginName, minLevel);
82
- }
83
-
84
- // src/logging/index.ts
85
- var import_plugin_types = require("@quilltap/plugin-types");
86
-
87
30
  // src/roleplay-templates/builder.ts
88
31
  function createRoleplayTemplatePlugin(options) {
89
- const { metadata, templates, initialize, enableLogging = false } = options;
32
+ const { metadata, templates, initialize, enableLogging: _enableLogging = false } = options;
90
33
  const templateArray = Array.isArray(templates) ? templates : [templates];
91
34
  if (templateArray.length === 0) {
92
35
  throw new Error("At least one template is required");
@@ -109,21 +52,9 @@ function createRoleplayTemplatePlugin(options) {
109
52
  },
110
53
  templates: templateArray
111
54
  };
112
- if (initialize || enableLogging) {
55
+ if (initialize) {
113
56
  plugin.initialize = async () => {
114
- if (enableLogging) {
115
- const logger = createPluginLogger(metadata.templateId);
116
- logger.debug("Roleplay template plugin loaded", {
117
- context: "init",
118
- templateId: metadata.templateId,
119
- displayName: metadata.displayName,
120
- templateCount: templateArray.length,
121
- templateNames: templateArray.map((t) => t.name)
122
- });
123
- }
124
- if (initialize) {
125
- await initialize();
126
- }
57
+ await initialize();
127
58
  };
128
59
  }
129
60
  return plugin;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/roleplay-templates/index.ts","../../src/logging/plugin-logger.ts","../../src/logging/index.ts","../../src/roleplay-templates/builder.ts"],"sourcesContent":["/**\n * Roleplay Template Plugin utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nexport {\n // Builder functions\n createRoleplayTemplatePlugin,\n createSingleTemplatePlugin,\n\n // Validation utilities\n validateTemplateConfig,\n validateRoleplayTemplatePlugin,\n} from './builder';\n\nexport type {\n // Builder option types\n CreateRoleplayTemplatePluginOptions,\n CreateSingleTemplatePluginOptions,\n} from './builder';\n\n// Re-export types from plugin-types for convenience\nexport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n RoleplayTemplatePluginExport,\n} from '@quilltap/plugin-types';\n","/**\n * Plugin Logger Bridge\n *\n * Provides a logger factory for plugins that automatically bridges\n * to Quilltap's core logging when running inside the host application,\n * or falls back to console logging when running standalone.\n *\n * @module @quilltap/plugin-utils/logging/plugin-logger\n */\n\nimport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n/**\n * Extended logger interface with child logger support\n */\nexport interface PluginLoggerWithChild extends PluginLogger {\n /**\n * Create a child logger with additional context\n * @param additionalContext Context to merge with parent context\n * @returns A new logger with combined context\n */\n child(additionalContext: LogContext): PluginLoggerWithChild;\n}\n\n/**\n * Type for the global Quilltap logger bridge\n * Stored on globalThis to work across different npm package copies\n */\ndeclare global {\n \n var __quilltap_logger_factory:\n | ((pluginName: string) => PluginLoggerWithChild)\n | undefined;\n}\n\n/**\n * Get the core logger factory from global namespace\n *\n * @returns The injected factory or null if not in Quilltap environment\n */\nfunction getCoreLoggerFactory(): ((pluginName: string) => PluginLoggerWithChild) | null {\n return globalThis.__quilltap_logger_factory ?? null;\n}\n\n/**\n * Inject the core logger factory from Quilltap host\n *\n * This is called by Quilltap core when loading plugins to bridge\n * plugin logging into the host's logging system. Uses globalThis\n * to ensure it works even when plugins have their own copy of\n * plugin-utils in their node_modules.\n *\n * **Internal API - not for plugin use**\n *\n * @param factory A function that creates a child logger for a plugin\n */\nexport function __injectCoreLoggerFactory(\n factory: (pluginName: string) => PluginLoggerWithChild\n): void {\n globalThis.__quilltap_logger_factory = factory;\n}\n\n/**\n * Clear the injected core logger factory\n *\n * Useful for testing or when unloading the plugin system.\n *\n * **Internal API - not for plugin use**\n */\nexport function __clearCoreLoggerFactory(): void {\n globalThis.__quilltap_logger_factory = undefined;\n}\n\n/**\n * Check if a core logger has been injected\n *\n * @returns True if running inside Quilltap with core logging available\n */\nexport function hasCoreLogger(): boolean {\n return getCoreLoggerFactory() !== null;\n}\n\n/**\n * Create a console logger with child support\n *\n * @param prefix Logger prefix\n * @param minLevel Minimum log level\n * @param baseContext Base context to include in all logs\n */\nfunction createConsoleLoggerWithChild(\n prefix: string,\n minLevel: LogLevel = 'debug',\n baseContext: LogContext = {}\n): PluginLoggerWithChild {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const shouldLog = (level: LogLevel): boolean =>\n levels.indexOf(level) >= levels.indexOf(minLevel);\n\n const formatContext = (context?: LogContext): string => {\n const merged = { ...baseContext, ...context };\n const entries = Object.entries(merged)\n .filter(([key]) => key !== 'context')\n .map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n .join(' ');\n return entries ? ` ${entries}` : '';\n };\n\n const logger: PluginLoggerWithChild = {\n debug: (message: string, context?: LogContext): void => {\n if (shouldLog('debug')) {\n console.debug(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n info: (message: string, context?: LogContext): void => {\n if (shouldLog('info')) {\n console.info(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n warn: (message: string, context?: LogContext): void => {\n if (shouldLog('warn')) {\n console.warn(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n error: (message: string, context?: LogContext, error?: Error): void => {\n if (shouldLog('error')) {\n console.error(\n `[${prefix}] ${message}${formatContext(context)}`,\n error ? `\\n${error.stack || error.message}` : ''\n );\n }\n },\n\n child: (additionalContext: LogContext): PluginLoggerWithChild => {\n return createConsoleLoggerWithChild(prefix, minLevel, {\n ...baseContext,\n ...additionalContext,\n });\n },\n };\n\n return logger;\n}\n\n/**\n * Create a plugin logger that bridges to Quilltap core logging\n *\n * When running inside Quilltap:\n * - Routes all logs to the core logger\n * - Tags logs with `{ plugin: pluginName, module: 'plugin' }`\n * - Logs appear in Quilltap's combined.log and console\n *\n * When running standalone:\n * - Falls back to console logging with `[pluginName]` prefix\n * - Respects the specified minimum log level\n *\n * @param pluginName - The plugin identifier (e.g., 'qtap-plugin-openai')\n * @param minLevel - Minimum log level when running standalone (default: 'debug')\n * @returns A logger instance\n *\n * @example\n * ```typescript\n * // In your plugin's provider.ts\n * import { createPluginLogger } from '@quilltap/plugin-utils';\n *\n * const logger = createPluginLogger('qtap-plugin-my-provider');\n *\n * export class MyProvider implements LLMProvider {\n * async sendMessage(params: LLMParams, apiKey: string): Promise<LLMResponse> {\n * logger.debug('Sending message', { model: params.model });\n *\n * try {\n * const response = await this.client.chat({...});\n * logger.info('Received response', { tokens: response.usage?.total_tokens });\n * return response;\n * } catch (error) {\n * logger.error('Failed to send message', { model: params.model }, error as Error);\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport function createPluginLogger(\n pluginName: string,\n minLevel: LogLevel = 'debug'\n): PluginLoggerWithChild {\n // Check for core logger factory from global namespace\n const coreFactory = getCoreLoggerFactory();\n if (coreFactory) {\n return coreFactory(pluginName);\n }\n\n // Standalone mode: use enhanced console logger\n return createConsoleLoggerWithChild(pluginName, minLevel);\n}\n\n/**\n * Get the minimum log level from environment\n *\n * Checks for LOG_LEVEL or QUILTTAP_LOG_LEVEL environment variables.\n * Useful for configuring standalone plugin logging.\n *\n * @returns The configured log level, or 'info' as default\n */\nexport function getLogLevelFromEnv(): LogLevel {\n if (typeof process !== 'undefined' && process.env) {\n const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;\n if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {\n return envLevel as LogLevel;\n }\n }\n return 'info';\n}\n","/**\n * Logging Utilities\n *\n * Exports the plugin logger bridge and related utilities.\n *\n * @module @quilltap/plugin-utils/logging\n */\n\nexport {\n createPluginLogger,\n hasCoreLogger,\n getLogLevelFromEnv,\n __injectCoreLoggerFactory,\n __clearCoreLoggerFactory,\n} from './plugin-logger';\n\nexport type { PluginLoggerWithChild } from './plugin-logger';\n\n// Re-export logger types from plugin-types\nexport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n// Re-export logger utilities from plugin-types for convenience\nexport { createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';\n","/**\n * Roleplay Template Plugin Builder utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nimport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n} from '@quilltap/plugin-types';\nimport { createPluginLogger } from '../logging';\n\n// ============================================================================\n// BUILDER OPTIONS\n// ============================================================================\n\n/**\n * Options for creating a roleplay template plugin\n */\nexport interface CreateRoleplayTemplatePluginOptions {\n /** Plugin metadata */\n metadata: RoleplayTemplateMetadata;\n\n /**\n * One or more roleplay templates.\n * Pass a single template object or an array of templates.\n */\n templates: RoleplayTemplateConfig | RoleplayTemplateConfig[];\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n/**\n * Simplified options for plugins that provide a single template\n */\nexport interface CreateSingleTemplatePluginOptions {\n /** Unique template identifier (lowercase, hyphens allowed) */\n templateId: string;\n\n /** Human-readable display name */\n displayName: string;\n\n /** Template description */\n description?: string;\n\n /**\n * The system prompt that defines the formatting rules.\n * This is prepended to character system prompts when the template is active.\n */\n systemPrompt: string;\n\n /** Template author */\n author?: string | {\n name: string;\n email?: string;\n url?: string;\n };\n\n /** Tags for categorization and searchability */\n tags?: string[];\n\n /** Template version */\n version?: string;\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n// ============================================================================\n// BUILDER FUNCTIONS\n// ============================================================================\n\n/**\n * Creates a roleplay template plugin with full control over metadata and templates.\n *\n * Use this when you want to provide multiple templates or have fine-grained\n * control over the plugin structure.\n *\n * @param options - Plugin configuration options\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createRoleplayTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createRoleplayTemplatePlugin({\n * metadata: {\n * templateId: 'my-rp-format',\n * displayName: 'My RP Format',\n * description: 'A custom roleplay formatting style',\n * },\n * templates: [\n * {\n * name: 'My RP Format',\n * description: 'Custom formatting with specific syntax',\n * systemPrompt: '[FORMATTING INSTRUCTIONS]...',\n * tags: ['custom'],\n * },\n * ],\n * });\n * ```\n */\nexport function createRoleplayTemplatePlugin(\n options: CreateRoleplayTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const { metadata, templates, initialize, enableLogging = false } = options;\n\n // Normalize templates to array\n const templateArray = Array.isArray(templates) ? templates : [templates];\n\n // Validate templates\n if (templateArray.length === 0) {\n throw new Error('At least one template is required');\n }\n\n for (const template of templateArray) {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error(`Template \"${template.name}\" requires a systemPrompt`);\n }\n }\n\n // Create the plugin\n const plugin: RoleplayTemplatePlugin = {\n metadata: {\n ...metadata,\n // Ensure tags from templates are included in metadata if not already set\n tags: metadata.tags ?? Array.from(\n new Set(templateArray.flatMap(t => t.tags ?? []))\n ),\n },\n templates: templateArray,\n };\n\n // Add initialize function with optional logging\n if (initialize || enableLogging) {\n plugin.initialize = async () => {\n if (enableLogging) {\n const logger = createPluginLogger(metadata.templateId);\n logger.debug('Roleplay template plugin loaded', {\n context: 'init',\n templateId: metadata.templateId,\n displayName: metadata.displayName,\n templateCount: templateArray.length,\n templateNames: templateArray.map(t => t.name),\n });\n }\n\n if (initialize) {\n await initialize();\n }\n };\n }\n\n return plugin;\n}\n\n/**\n * Creates a simple roleplay template plugin with a single template.\n *\n * This is a convenience function for the common case of a plugin\n * that provides just one roleplay template.\n *\n * @param options - Simplified plugin configuration\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createSingleTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createSingleTemplatePlugin({\n * templateId: 'quilltap-rp',\n * displayName: 'Quilltap RP',\n * description: 'Custom formatting with [actions], {thoughts}, and // OOC',\n * systemPrompt: `[FORMATTING INSTRUCTIONS]\n * 1. DIALOGUE: Write as bare text without quotes\n * 2. ACTIONS: Use [square brackets]\n * 3. THOUGHTS: Use {curly braces}\n * 4. OOC: Use // prefix`,\n * tags: ['quilltap', 'custom'],\n * });\n * ```\n */\nexport function createSingleTemplatePlugin(\n options: CreateSingleTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const {\n templateId,\n displayName,\n description,\n systemPrompt,\n author,\n tags,\n version,\n initialize,\n enableLogging,\n } = options;\n\n return createRoleplayTemplatePlugin({\n metadata: {\n templateId,\n displayName,\n description,\n author,\n tags,\n version,\n },\n templates: {\n name: displayName,\n description,\n systemPrompt,\n tags,\n },\n initialize,\n enableLogging,\n });\n}\n\n// ============================================================================\n// VALIDATION UTILITIES\n// ============================================================================\n\n/**\n * Validates a roleplay template configuration\n *\n * @param template - The template configuration to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateTemplateConfig(template: RoleplayTemplateConfig): boolean {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n\n if (template.name.length > 100) {\n throw new Error('Template name must be 100 characters or less');\n }\n\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error('Template systemPrompt is required');\n }\n\n if (template.description && template.description.length > 500) {\n throw new Error('Template description must be 500 characters or less');\n }\n\n if (template.tags) {\n if (!Array.isArray(template.tags)) {\n throw new Error('Template tags must be an array');\n }\n for (const tag of template.tags) {\n if (typeof tag !== 'string') {\n throw new Error('All tags must be strings');\n }\n }\n }\n\n return true;\n}\n\n/**\n * Validates a complete roleplay template plugin\n *\n * @param plugin - The plugin to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateRoleplayTemplatePlugin(plugin: RoleplayTemplatePlugin): boolean {\n // Validate metadata\n if (!plugin.metadata) {\n throw new Error('Plugin metadata is required');\n }\n\n if (!plugin.metadata.templateId || plugin.metadata.templateId.trim() === '') {\n throw new Error('Plugin metadata.templateId is required');\n }\n\n if (!/^[a-z0-9-]+$/.test(plugin.metadata.templateId)) {\n throw new Error('Plugin templateId must be lowercase alphanumeric with hyphens only');\n }\n\n if (!plugin.metadata.displayName || plugin.metadata.displayName.trim() === '') {\n throw new Error('Plugin metadata.displayName is required');\n }\n\n // Validate templates\n if (!plugin.templates || !Array.isArray(plugin.templates) || plugin.templates.length === 0) {\n throw new Error('Plugin must have at least one template');\n }\n\n for (const template of plugin.templates) {\n validateTemplateConfig(template);\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/roleplay-templates/index.ts","../../src/roleplay-templates/builder.ts"],"sourcesContent":["/**\n * Roleplay Template Plugin utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nexport {\n // Builder functions\n createRoleplayTemplatePlugin,\n createSingleTemplatePlugin,\n\n // Validation utilities\n validateTemplateConfig,\n validateRoleplayTemplatePlugin,\n} from './builder';\n\nexport type {\n // Builder option types\n CreateRoleplayTemplatePluginOptions,\n CreateSingleTemplatePluginOptions,\n} from './builder';\n\n// Re-export types from plugin-types for convenience\nexport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n RoleplayTemplatePluginExport,\n} from '@quilltap/plugin-types';\n","/**\n * Roleplay Template Plugin Builder utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nimport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n} from '@quilltap/plugin-types';\n\n// ============================================================================\n// BUILDER OPTIONS\n// ============================================================================\n\n/**\n * Options for creating a roleplay template plugin\n */\nexport interface CreateRoleplayTemplatePluginOptions {\n /** Plugin metadata */\n metadata: RoleplayTemplateMetadata;\n\n /**\n * One or more roleplay templates.\n * Pass a single template object or an array of templates.\n */\n templates: RoleplayTemplateConfig | RoleplayTemplateConfig[];\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n/**\n * Simplified options for plugins that provide a single template\n */\nexport interface CreateSingleTemplatePluginOptions {\n /** Unique template identifier (lowercase, hyphens allowed) */\n templateId: string;\n\n /** Human-readable display name */\n displayName: string;\n\n /** Template description */\n description?: string;\n\n /**\n * The system prompt that defines the formatting rules.\n * This is prepended to character system prompts when the template is active.\n */\n systemPrompt: string;\n\n /** Template author */\n author?: string | {\n name: string;\n email?: string;\n url?: string;\n };\n\n /** Tags for categorization and searchability */\n tags?: string[];\n\n /** Template version */\n version?: string;\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n// ============================================================================\n// BUILDER FUNCTIONS\n// ============================================================================\n\n/**\n * Creates a roleplay template plugin with full control over metadata and templates.\n *\n * Use this when you want to provide multiple templates or have fine-grained\n * control over the plugin structure.\n *\n * @param options - Plugin configuration options\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createRoleplayTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createRoleplayTemplatePlugin({\n * metadata: {\n * templateId: 'my-rp-format',\n * displayName: 'My RP Format',\n * description: 'A custom roleplay formatting style',\n * },\n * templates: [\n * {\n * name: 'My RP Format',\n * description: 'Custom formatting with specific syntax',\n * systemPrompt: '[FORMATTING INSTRUCTIONS]...',\n * tags: ['custom'],\n * },\n * ],\n * });\n * ```\n */\nexport function createRoleplayTemplatePlugin(\n options: CreateRoleplayTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const { metadata, templates, initialize, enableLogging: _enableLogging = false } = options;\n\n // Normalize templates to array\n const templateArray = Array.isArray(templates) ? templates : [templates];\n\n // Validate templates\n if (templateArray.length === 0) {\n throw new Error('At least one template is required');\n }\n\n for (const template of templateArray) {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error(`Template \"${template.name}\" requires a systemPrompt`);\n }\n }\n\n // Create the plugin\n const plugin: RoleplayTemplatePlugin = {\n metadata: {\n ...metadata,\n // Ensure tags from templates are included in metadata if not already set\n tags: metadata.tags ?? Array.from(\n new Set(templateArray.flatMap(t => t.tags ?? []))\n ),\n },\n templates: templateArray,\n };\n\n // Add initialize function if provided\n if (initialize) {\n plugin.initialize = async () => {\n await initialize();\n };\n }\n\n return plugin;\n}\n\n/**\n * Creates a simple roleplay template plugin with a single template.\n *\n * This is a convenience function for the common case of a plugin\n * that provides just one roleplay template.\n *\n * @param options - Simplified plugin configuration\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createSingleTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createSingleTemplatePlugin({\n * templateId: 'quilltap-rp',\n * displayName: 'Quilltap RP',\n * description: 'Custom formatting with [actions], {thoughts}, and // OOC',\n * systemPrompt: `[FORMATTING INSTRUCTIONS]\n * 1. DIALOGUE: Write as bare text without quotes\n * 2. ACTIONS: Use [square brackets]\n * 3. THOUGHTS: Use {curly braces}\n * 4. OOC: Use // prefix`,\n * tags: ['quilltap', 'custom'],\n * });\n * ```\n */\nexport function createSingleTemplatePlugin(\n options: CreateSingleTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const {\n templateId,\n displayName,\n description,\n systemPrompt,\n author,\n tags,\n version,\n initialize,\n enableLogging,\n } = options;\n\n return createRoleplayTemplatePlugin({\n metadata: {\n templateId,\n displayName,\n description,\n author,\n tags,\n version,\n },\n templates: {\n name: displayName,\n description,\n systemPrompt,\n tags,\n },\n initialize,\n enableLogging,\n });\n}\n\n// ============================================================================\n// VALIDATION UTILITIES\n// ============================================================================\n\n/**\n * Validates a roleplay template configuration\n *\n * @param template - The template configuration to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateTemplateConfig(template: RoleplayTemplateConfig): boolean {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n\n if (template.name.length > 100) {\n throw new Error('Template name must be 100 characters or less');\n }\n\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error('Template systemPrompt is required');\n }\n\n if (template.description && template.description.length > 500) {\n throw new Error('Template description must be 500 characters or less');\n }\n\n if (template.tags) {\n if (!Array.isArray(template.tags)) {\n throw new Error('Template tags must be an array');\n }\n for (const tag of template.tags) {\n if (typeof tag !== 'string') {\n throw new Error('All tags must be strings');\n }\n }\n }\n\n return true;\n}\n\n/**\n * Validates a complete roleplay template plugin\n *\n * @param plugin - The plugin to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateRoleplayTemplatePlugin(plugin: RoleplayTemplatePlugin): boolean {\n // Validate metadata\n if (!plugin.metadata) {\n throw new Error('Plugin metadata is required');\n }\n\n if (!plugin.metadata.templateId || plugin.metadata.templateId.trim() === '') {\n throw new Error('Plugin metadata.templateId is required');\n }\n\n if (!/^[a-z0-9-]+$/.test(plugin.metadata.templateId)) {\n throw new Error('Plugin templateId must be lowercase alphanumeric with hyphens only');\n }\n\n if (!plugin.metadata.displayName || plugin.metadata.displayName.trim() === '') {\n throw new Error('Plugin metadata.displayName is required');\n }\n\n // Validate templates\n if (!plugin.templates || !Array.isArray(plugin.templates) || plugin.templates.length === 0) {\n throw new Error('Plugin must have at least one template');\n }\n\n for (const template of plugin.templates) {\n validateTemplateConfig(template);\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
@@ -1,63 +1,6 @@
1
- // src/logging/plugin-logger.ts
2
- function getCoreLoggerFactory() {
3
- return globalThis.__quilltap_logger_factory ?? null;
4
- }
5
- function createConsoleLoggerWithChild(prefix, minLevel = "debug", baseContext = {}) {
6
- const levels = ["debug", "info", "warn", "error"];
7
- const shouldLog = (level) => levels.indexOf(level) >= levels.indexOf(minLevel);
8
- const formatContext = (context) => {
9
- const merged = { ...baseContext, ...context };
10
- const entries = Object.entries(merged).filter(([key]) => key !== "context").map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
11
- return entries ? ` ${entries}` : "";
12
- };
13
- const logger = {
14
- debug: (message, context) => {
15
- if (shouldLog("debug")) {
16
- console.debug(`[${prefix}] ${message}${formatContext(context)}`);
17
- }
18
- },
19
- info: (message, context) => {
20
- if (shouldLog("info")) {
21
- console.info(`[${prefix}] ${message}${formatContext(context)}`);
22
- }
23
- },
24
- warn: (message, context) => {
25
- if (shouldLog("warn")) {
26
- console.warn(`[${prefix}] ${message}${formatContext(context)}`);
27
- }
28
- },
29
- error: (message, context, error) => {
30
- if (shouldLog("error")) {
31
- console.error(
32
- `[${prefix}] ${message}${formatContext(context)}`,
33
- error ? `
34
- ${error.stack || error.message}` : ""
35
- );
36
- }
37
- },
38
- child: (additionalContext) => {
39
- return createConsoleLoggerWithChild(prefix, minLevel, {
40
- ...baseContext,
41
- ...additionalContext
42
- });
43
- }
44
- };
45
- return logger;
46
- }
47
- function createPluginLogger(pluginName, minLevel = "debug") {
48
- const coreFactory = getCoreLoggerFactory();
49
- if (coreFactory) {
50
- return coreFactory(pluginName);
51
- }
52
- return createConsoleLoggerWithChild(pluginName, minLevel);
53
- }
54
-
55
- // src/logging/index.ts
56
- import { createConsoleLogger, createNoopLogger } from "@quilltap/plugin-types";
57
-
58
1
  // src/roleplay-templates/builder.ts
59
2
  function createRoleplayTemplatePlugin(options) {
60
- const { metadata, templates, initialize, enableLogging = false } = options;
3
+ const { metadata, templates, initialize, enableLogging: _enableLogging = false } = options;
61
4
  const templateArray = Array.isArray(templates) ? templates : [templates];
62
5
  if (templateArray.length === 0) {
63
6
  throw new Error("At least one template is required");
@@ -80,21 +23,9 @@ function createRoleplayTemplatePlugin(options) {
80
23
  },
81
24
  templates: templateArray
82
25
  };
83
- if (initialize || enableLogging) {
26
+ if (initialize) {
84
27
  plugin.initialize = async () => {
85
- if (enableLogging) {
86
- const logger = createPluginLogger(metadata.templateId);
87
- logger.debug("Roleplay template plugin loaded", {
88
- context: "init",
89
- templateId: metadata.templateId,
90
- displayName: metadata.displayName,
91
- templateCount: templateArray.length,
92
- templateNames: templateArray.map((t) => t.name)
93
- });
94
- }
95
- if (initialize) {
96
- await initialize();
97
- }
28
+ await initialize();
98
29
  };
99
30
  }
100
31
  return plugin;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/logging/plugin-logger.ts","../../src/logging/index.ts","../../src/roleplay-templates/builder.ts"],"sourcesContent":["/**\n * Plugin Logger Bridge\n *\n * Provides a logger factory for plugins that automatically bridges\n * to Quilltap's core logging when running inside the host application,\n * or falls back to console logging when running standalone.\n *\n * @module @quilltap/plugin-utils/logging/plugin-logger\n */\n\nimport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n/**\n * Extended logger interface with child logger support\n */\nexport interface PluginLoggerWithChild extends PluginLogger {\n /**\n * Create a child logger with additional context\n * @param additionalContext Context to merge with parent context\n * @returns A new logger with combined context\n */\n child(additionalContext: LogContext): PluginLoggerWithChild;\n}\n\n/**\n * Type for the global Quilltap logger bridge\n * Stored on globalThis to work across different npm package copies\n */\ndeclare global {\n \n var __quilltap_logger_factory:\n | ((pluginName: string) => PluginLoggerWithChild)\n | undefined;\n}\n\n/**\n * Get the core logger factory from global namespace\n *\n * @returns The injected factory or null if not in Quilltap environment\n */\nfunction getCoreLoggerFactory(): ((pluginName: string) => PluginLoggerWithChild) | null {\n return globalThis.__quilltap_logger_factory ?? null;\n}\n\n/**\n * Inject the core logger factory from Quilltap host\n *\n * This is called by Quilltap core when loading plugins to bridge\n * plugin logging into the host's logging system. Uses globalThis\n * to ensure it works even when plugins have their own copy of\n * plugin-utils in their node_modules.\n *\n * **Internal API - not for plugin use**\n *\n * @param factory A function that creates a child logger for a plugin\n */\nexport function __injectCoreLoggerFactory(\n factory: (pluginName: string) => PluginLoggerWithChild\n): void {\n globalThis.__quilltap_logger_factory = factory;\n}\n\n/**\n * Clear the injected core logger factory\n *\n * Useful for testing or when unloading the plugin system.\n *\n * **Internal API - not for plugin use**\n */\nexport function __clearCoreLoggerFactory(): void {\n globalThis.__quilltap_logger_factory = undefined;\n}\n\n/**\n * Check if a core logger has been injected\n *\n * @returns True if running inside Quilltap with core logging available\n */\nexport function hasCoreLogger(): boolean {\n return getCoreLoggerFactory() !== null;\n}\n\n/**\n * Create a console logger with child support\n *\n * @param prefix Logger prefix\n * @param minLevel Minimum log level\n * @param baseContext Base context to include in all logs\n */\nfunction createConsoleLoggerWithChild(\n prefix: string,\n minLevel: LogLevel = 'debug',\n baseContext: LogContext = {}\n): PluginLoggerWithChild {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const shouldLog = (level: LogLevel): boolean =>\n levels.indexOf(level) >= levels.indexOf(minLevel);\n\n const formatContext = (context?: LogContext): string => {\n const merged = { ...baseContext, ...context };\n const entries = Object.entries(merged)\n .filter(([key]) => key !== 'context')\n .map(([key, value]) => `${key}=${JSON.stringify(value)}`)\n .join(' ');\n return entries ? ` ${entries}` : '';\n };\n\n const logger: PluginLoggerWithChild = {\n debug: (message: string, context?: LogContext): void => {\n if (shouldLog('debug')) {\n console.debug(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n info: (message: string, context?: LogContext): void => {\n if (shouldLog('info')) {\n console.info(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n warn: (message: string, context?: LogContext): void => {\n if (shouldLog('warn')) {\n console.warn(`[${prefix}] ${message}${formatContext(context)}`);\n }\n },\n\n error: (message: string, context?: LogContext, error?: Error): void => {\n if (shouldLog('error')) {\n console.error(\n `[${prefix}] ${message}${formatContext(context)}`,\n error ? `\\n${error.stack || error.message}` : ''\n );\n }\n },\n\n child: (additionalContext: LogContext): PluginLoggerWithChild => {\n return createConsoleLoggerWithChild(prefix, minLevel, {\n ...baseContext,\n ...additionalContext,\n });\n },\n };\n\n return logger;\n}\n\n/**\n * Create a plugin logger that bridges to Quilltap core logging\n *\n * When running inside Quilltap:\n * - Routes all logs to the core logger\n * - Tags logs with `{ plugin: pluginName, module: 'plugin' }`\n * - Logs appear in Quilltap's combined.log and console\n *\n * When running standalone:\n * - Falls back to console logging with `[pluginName]` prefix\n * - Respects the specified minimum log level\n *\n * @param pluginName - The plugin identifier (e.g., 'qtap-plugin-openai')\n * @param minLevel - Minimum log level when running standalone (default: 'debug')\n * @returns A logger instance\n *\n * @example\n * ```typescript\n * // In your plugin's provider.ts\n * import { createPluginLogger } from '@quilltap/plugin-utils';\n *\n * const logger = createPluginLogger('qtap-plugin-my-provider');\n *\n * export class MyProvider implements LLMProvider {\n * async sendMessage(params: LLMParams, apiKey: string): Promise<LLMResponse> {\n * logger.debug('Sending message', { model: params.model });\n *\n * try {\n * const response = await this.client.chat({...});\n * logger.info('Received response', { tokens: response.usage?.total_tokens });\n * return response;\n * } catch (error) {\n * logger.error('Failed to send message', { model: params.model }, error as Error);\n * throw error;\n * }\n * }\n * }\n * ```\n */\nexport function createPluginLogger(\n pluginName: string,\n minLevel: LogLevel = 'debug'\n): PluginLoggerWithChild {\n // Check for core logger factory from global namespace\n const coreFactory = getCoreLoggerFactory();\n if (coreFactory) {\n return coreFactory(pluginName);\n }\n\n // Standalone mode: use enhanced console logger\n return createConsoleLoggerWithChild(pluginName, minLevel);\n}\n\n/**\n * Get the minimum log level from environment\n *\n * Checks for LOG_LEVEL or QUILTTAP_LOG_LEVEL environment variables.\n * Useful for configuring standalone plugin logging.\n *\n * @returns The configured log level, or 'info' as default\n */\nexport function getLogLevelFromEnv(): LogLevel {\n if (typeof process !== 'undefined' && process.env) {\n const envLevel = process.env.LOG_LEVEL || process.env.QUILTTAP_LOG_LEVEL;\n if (envLevel && ['debug', 'info', 'warn', 'error'].includes(envLevel)) {\n return envLevel as LogLevel;\n }\n }\n return 'info';\n}\n","/**\n * Logging Utilities\n *\n * Exports the plugin logger bridge and related utilities.\n *\n * @module @quilltap/plugin-utils/logging\n */\n\nexport {\n createPluginLogger,\n hasCoreLogger,\n getLogLevelFromEnv,\n __injectCoreLoggerFactory,\n __clearCoreLoggerFactory,\n} from './plugin-logger';\n\nexport type { PluginLoggerWithChild } from './plugin-logger';\n\n// Re-export logger types from plugin-types\nexport type { PluginLogger, LogContext, LogLevel } from '@quilltap/plugin-types';\n\n// Re-export logger utilities from plugin-types for convenience\nexport { createConsoleLogger, createNoopLogger } from '@quilltap/plugin-types';\n","/**\n * Roleplay Template Plugin Builder utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nimport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n} from '@quilltap/plugin-types';\nimport { createPluginLogger } from '../logging';\n\n// ============================================================================\n// BUILDER OPTIONS\n// ============================================================================\n\n/**\n * Options for creating a roleplay template plugin\n */\nexport interface CreateRoleplayTemplatePluginOptions {\n /** Plugin metadata */\n metadata: RoleplayTemplateMetadata;\n\n /**\n * One or more roleplay templates.\n * Pass a single template object or an array of templates.\n */\n templates: RoleplayTemplateConfig | RoleplayTemplateConfig[];\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n/**\n * Simplified options for plugins that provide a single template\n */\nexport interface CreateSingleTemplatePluginOptions {\n /** Unique template identifier (lowercase, hyphens allowed) */\n templateId: string;\n\n /** Human-readable display name */\n displayName: string;\n\n /** Template description */\n description?: string;\n\n /**\n * The system prompt that defines the formatting rules.\n * This is prepended to character system prompts when the template is active.\n */\n systemPrompt: string;\n\n /** Template author */\n author?: string | {\n name: string;\n email?: string;\n url?: string;\n };\n\n /** Tags for categorization and searchability */\n tags?: string[];\n\n /** Template version */\n version?: string;\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n// ============================================================================\n// BUILDER FUNCTIONS\n// ============================================================================\n\n/**\n * Creates a roleplay template plugin with full control over metadata and templates.\n *\n * Use this when you want to provide multiple templates or have fine-grained\n * control over the plugin structure.\n *\n * @param options - Plugin configuration options\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createRoleplayTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createRoleplayTemplatePlugin({\n * metadata: {\n * templateId: 'my-rp-format',\n * displayName: 'My RP Format',\n * description: 'A custom roleplay formatting style',\n * },\n * templates: [\n * {\n * name: 'My RP Format',\n * description: 'Custom formatting with specific syntax',\n * systemPrompt: '[FORMATTING INSTRUCTIONS]...',\n * tags: ['custom'],\n * },\n * ],\n * });\n * ```\n */\nexport function createRoleplayTemplatePlugin(\n options: CreateRoleplayTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const { metadata, templates, initialize, enableLogging = false } = options;\n\n // Normalize templates to array\n const templateArray = Array.isArray(templates) ? templates : [templates];\n\n // Validate templates\n if (templateArray.length === 0) {\n throw new Error('At least one template is required');\n }\n\n for (const template of templateArray) {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error(`Template \"${template.name}\" requires a systemPrompt`);\n }\n }\n\n // Create the plugin\n const plugin: RoleplayTemplatePlugin = {\n metadata: {\n ...metadata,\n // Ensure tags from templates are included in metadata if not already set\n tags: metadata.tags ?? Array.from(\n new Set(templateArray.flatMap(t => t.tags ?? []))\n ),\n },\n templates: templateArray,\n };\n\n // Add initialize function with optional logging\n if (initialize || enableLogging) {\n plugin.initialize = async () => {\n if (enableLogging) {\n const logger = createPluginLogger(metadata.templateId);\n logger.debug('Roleplay template plugin loaded', {\n context: 'init',\n templateId: metadata.templateId,\n displayName: metadata.displayName,\n templateCount: templateArray.length,\n templateNames: templateArray.map(t => t.name),\n });\n }\n\n if (initialize) {\n await initialize();\n }\n };\n }\n\n return plugin;\n}\n\n/**\n * Creates a simple roleplay template plugin with a single template.\n *\n * This is a convenience function for the common case of a plugin\n * that provides just one roleplay template.\n *\n * @param options - Simplified plugin configuration\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createSingleTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createSingleTemplatePlugin({\n * templateId: 'quilltap-rp',\n * displayName: 'Quilltap RP',\n * description: 'Custom formatting with [actions], {thoughts}, and // OOC',\n * systemPrompt: `[FORMATTING INSTRUCTIONS]\n * 1. DIALOGUE: Write as bare text without quotes\n * 2. ACTIONS: Use [square brackets]\n * 3. THOUGHTS: Use {curly braces}\n * 4. OOC: Use // prefix`,\n * tags: ['quilltap', 'custom'],\n * });\n * ```\n */\nexport function createSingleTemplatePlugin(\n options: CreateSingleTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const {\n templateId,\n displayName,\n description,\n systemPrompt,\n author,\n tags,\n version,\n initialize,\n enableLogging,\n } = options;\n\n return createRoleplayTemplatePlugin({\n metadata: {\n templateId,\n displayName,\n description,\n author,\n tags,\n version,\n },\n templates: {\n name: displayName,\n description,\n systemPrompt,\n tags,\n },\n initialize,\n enableLogging,\n });\n}\n\n// ============================================================================\n// VALIDATION UTILITIES\n// ============================================================================\n\n/**\n * Validates a roleplay template configuration\n *\n * @param template - The template configuration to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateTemplateConfig(template: RoleplayTemplateConfig): boolean {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n\n if (template.name.length > 100) {\n throw new Error('Template name must be 100 characters or less');\n }\n\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error('Template systemPrompt is required');\n }\n\n if (template.description && template.description.length > 500) {\n throw new Error('Template description must be 500 characters or less');\n }\n\n if (template.tags) {\n if (!Array.isArray(template.tags)) {\n throw new Error('Template tags must be an array');\n }\n for (const tag of template.tags) {\n if (typeof tag !== 'string') {\n throw new Error('All tags must be strings');\n }\n }\n }\n\n return true;\n}\n\n/**\n * Validates a complete roleplay template plugin\n *\n * @param plugin - The plugin to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateRoleplayTemplatePlugin(plugin: RoleplayTemplatePlugin): boolean {\n // Validate metadata\n if (!plugin.metadata) {\n throw new Error('Plugin metadata is required');\n }\n\n if (!plugin.metadata.templateId || plugin.metadata.templateId.trim() === '') {\n throw new Error('Plugin metadata.templateId is required');\n }\n\n if (!/^[a-z0-9-]+$/.test(plugin.metadata.templateId)) {\n throw new Error('Plugin templateId must be lowercase alphanumeric with hyphens only');\n }\n\n if (!plugin.metadata.displayName || plugin.metadata.displayName.trim() === '') {\n throw new Error('Plugin metadata.displayName is required');\n }\n\n // Validate templates\n if (!plugin.templates || !Array.isArray(plugin.templates) || plugin.templates.length === 0) {\n throw new Error('Plugin must have at least one template');\n }\n\n for (const template of plugin.templates) {\n validateTemplateConfig(template);\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/roleplay-templates/builder.ts"],"sourcesContent":["/**\n * Roleplay Template Plugin Builder utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nimport type {\n RoleplayTemplateConfig,\n RoleplayTemplateMetadata,\n RoleplayTemplatePlugin,\n} from '@quilltap/plugin-types';\n\n// ============================================================================\n// BUILDER OPTIONS\n// ============================================================================\n\n/**\n * Options for creating a roleplay template plugin\n */\nexport interface CreateRoleplayTemplatePluginOptions {\n /** Plugin metadata */\n metadata: RoleplayTemplateMetadata;\n\n /**\n * One or more roleplay templates.\n * Pass a single template object or an array of templates.\n */\n templates: RoleplayTemplateConfig | RoleplayTemplateConfig[];\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n/**\n * Simplified options for plugins that provide a single template\n */\nexport interface CreateSingleTemplatePluginOptions {\n /** Unique template identifier (lowercase, hyphens allowed) */\n templateId: string;\n\n /** Human-readable display name */\n displayName: string;\n\n /** Template description */\n description?: string;\n\n /**\n * The system prompt that defines the formatting rules.\n * This is prepended to character system prompts when the template is active.\n */\n systemPrompt: string;\n\n /** Template author */\n author?: string | {\n name: string;\n email?: string;\n url?: string;\n };\n\n /** Tags for categorization and searchability */\n tags?: string[];\n\n /** Template version */\n version?: string;\n\n /**\n * Optional initialization function.\n * Called when the plugin is loaded.\n */\n initialize?: () => void | Promise<void>;\n\n /**\n * Whether to enable debug logging.\n * Defaults to false.\n */\n enableLogging?: boolean;\n}\n\n// ============================================================================\n// BUILDER FUNCTIONS\n// ============================================================================\n\n/**\n * Creates a roleplay template plugin with full control over metadata and templates.\n *\n * Use this when you want to provide multiple templates or have fine-grained\n * control over the plugin structure.\n *\n * @param options - Plugin configuration options\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createRoleplayTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createRoleplayTemplatePlugin({\n * metadata: {\n * templateId: 'my-rp-format',\n * displayName: 'My RP Format',\n * description: 'A custom roleplay formatting style',\n * },\n * templates: [\n * {\n * name: 'My RP Format',\n * description: 'Custom formatting with specific syntax',\n * systemPrompt: '[FORMATTING INSTRUCTIONS]...',\n * tags: ['custom'],\n * },\n * ],\n * });\n * ```\n */\nexport function createRoleplayTemplatePlugin(\n options: CreateRoleplayTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const { metadata, templates, initialize, enableLogging: _enableLogging = false } = options;\n\n // Normalize templates to array\n const templateArray = Array.isArray(templates) ? templates : [templates];\n\n // Validate templates\n if (templateArray.length === 0) {\n throw new Error('At least one template is required');\n }\n\n for (const template of templateArray) {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error(`Template \"${template.name}\" requires a systemPrompt`);\n }\n }\n\n // Create the plugin\n const plugin: RoleplayTemplatePlugin = {\n metadata: {\n ...metadata,\n // Ensure tags from templates are included in metadata if not already set\n tags: metadata.tags ?? Array.from(\n new Set(templateArray.flatMap(t => t.tags ?? []))\n ),\n },\n templates: templateArray,\n };\n\n // Add initialize function if provided\n if (initialize) {\n plugin.initialize = async () => {\n await initialize();\n };\n }\n\n return plugin;\n}\n\n/**\n * Creates a simple roleplay template plugin with a single template.\n *\n * This is a convenience function for the common case of a plugin\n * that provides just one roleplay template.\n *\n * @param options - Simplified plugin configuration\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createSingleTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createSingleTemplatePlugin({\n * templateId: 'quilltap-rp',\n * displayName: 'Quilltap RP',\n * description: 'Custom formatting with [actions], {thoughts}, and // OOC',\n * systemPrompt: `[FORMATTING INSTRUCTIONS]\n * 1. DIALOGUE: Write as bare text without quotes\n * 2. ACTIONS: Use [square brackets]\n * 3. THOUGHTS: Use {curly braces}\n * 4. OOC: Use // prefix`,\n * tags: ['quilltap', 'custom'],\n * });\n * ```\n */\nexport function createSingleTemplatePlugin(\n options: CreateSingleTemplatePluginOptions\n): RoleplayTemplatePlugin {\n const {\n templateId,\n displayName,\n description,\n systemPrompt,\n author,\n tags,\n version,\n initialize,\n enableLogging,\n } = options;\n\n return createRoleplayTemplatePlugin({\n metadata: {\n templateId,\n displayName,\n description,\n author,\n tags,\n version,\n },\n templates: {\n name: displayName,\n description,\n systemPrompt,\n tags,\n },\n initialize,\n enableLogging,\n });\n}\n\n// ============================================================================\n// VALIDATION UTILITIES\n// ============================================================================\n\n/**\n * Validates a roleplay template configuration\n *\n * @param template - The template configuration to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateTemplateConfig(template: RoleplayTemplateConfig): boolean {\n if (!template.name || template.name.trim() === '') {\n throw new Error('Template name is required');\n }\n\n if (template.name.length > 100) {\n throw new Error('Template name must be 100 characters or less');\n }\n\n if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n throw new Error('Template systemPrompt is required');\n }\n\n if (template.description && template.description.length > 500) {\n throw new Error('Template description must be 500 characters or less');\n }\n\n if (template.tags) {\n if (!Array.isArray(template.tags)) {\n throw new Error('Template tags must be an array');\n }\n for (const tag of template.tags) {\n if (typeof tag !== 'string') {\n throw new Error('All tags must be strings');\n }\n }\n }\n\n return true;\n}\n\n/**\n * Validates a complete roleplay template plugin\n *\n * @param plugin - The plugin to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateRoleplayTemplatePlugin(plugin: RoleplayTemplatePlugin): boolean {\n // Validate metadata\n if (!plugin.metadata) {\n throw new Error('Plugin metadata is required');\n }\n\n if (!plugin.metadata.templateId || plugin.metadata.templateId.trim() === '') {\n throw new Error('Plugin metadata.templateId is required');\n }\n\n if (!/^[a-z0-9-]+$/.test(plugin.metadata.templateId)) {\n throw new Error('Plugin templateId must be lowercase alphanumeric with hyphens only');\n }\n\n if (!plugin.metadata.displayName || plugin.metadata.displayName.trim() === '') {\n throw new Error('Plugin metadata.displayName is required');\n }\n\n // Validate templates\n if (!plugin.templates || !Array.isArray(plugin.templates) || plugin.templates.length === 0) {\n throw new Error('Plugin must have at least one template');\n }\n\n for (const template of plugin.templates) {\n validateTemplateConfig(template);\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quilltap/plugin-utils",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "Utility functions for Quilltap plugin development",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",