From 60b7d561cdddd3bce6c12889b103b109c339c500 Mon Sep 17 00:00:00 2001 From: thoroc Date: Sat, 31 Jan 2026 23:03:15 +0000 Subject: [PATCH] feat(warcraft): refactor plugin to use new packages - Refactor to use opencode-notification package (PR2) - Refactor to use opencode-config package (PR3) - Update notification.ts and notification-core.ts to use shared utilities - Refactor config/loader.ts and config/package.ts to use opencode-config - Update package.json with new dependencies - Update project.json with new targets - Update test utilities and schema validator - Final integration PR demonstrating new packages working together This refactor demonstrates the new opencode-notification and opencode-config packages working in production by migrating the warcraft plugin to use them. --- .../package.json | 4 +- .../pages/fix-links.js | 9 +- .../pages/generate-favicon.mjs | 5 + .../pages/test-links.js | 8 + .../pages/transform-docs.js | 10 +- .../project.json | 31 +++- .../src/bundled-sounds.ts | 5 +- .../src/config/index.test.ts | 14 +- .../src/config/loader.coverage.test.ts | 2 +- .../src/config/loader.ts | 6 +- .../src/config/package.ts | 5 +- .../src/notification-core.ts | 170 ++++++++++++++++++ .../src/notification.ts | 52 ++---- .../src/schema-validator.coverage.test.ts | 3 +- .../src/sounds/index.ts | 12 +- .../src/test-utils.test.ts | 1 + .../src/test-utils.ts | 2 +- 17 files changed, 273 insertions(+), 66 deletions(-) create mode 100644 packages/opencode-warcraft-notifications-plugin/src/notification-core.ts diff --git a/packages/opencode-warcraft-notifications-plugin/package.json b/packages/opencode-warcraft-notifications-plugin/package.json index 5869606..9d53469 100644 --- a/packages/opencode-warcraft-notifications-plugin/package.json +++ b/packages/opencode-warcraft-notifications-plugin/package.json @@ -41,13 +41,15 @@ "prepublishOnly": "bun run build && bun test && bun run verify:package" }, "dependencies": { + "@pantheon-org/opencode-config": "workspace:*", + "@pantheon-org/opencode-notification": "workspace:*", "csstype": "^3.1.3", "undici-types": "^7.16.0", "zod": "^4.1.8" }, "devDependencies": { "@nx/js": "22.1.3", - "@opencode-ai/plugin": "^1.0.133", + "@opencode-ai/plugin": "^1.1.47", "@swc/cli": "~0.6.0", "@swc/helpers": "~0.5.11", "@types/bun": "^1.3.4", diff --git a/packages/opencode-warcraft-notifications-plugin/pages/fix-links.js b/packages/opencode-warcraft-notifications-plugin/pages/fix-links.js index ea492a0..00293d7 100644 --- a/packages/opencode-warcraft-notifications-plugin/pages/fix-links.js +++ b/packages/opencode-warcraft-notifications-plugin/pages/fix-links.js @@ -88,7 +88,8 @@ async function processDirectory(dirPath, baseDir = DIST_DIR) { } else if (entry.isFile() && entry.name.endsWith('.html')) { const wasFixed = await fixLinksInFile(fullPath); if (wasFixed) { - const _relativePath = fullPath.replace(`${baseDir}/`, ''); + const relativePath = fullPath.replace(`${baseDir}/`, ''); + console.log(` āœ“ Fixed links in ${relativePath}`); fixedCount++; } } @@ -101,11 +102,17 @@ async function processDirectory(dirPath, baseDir = DIST_DIR) { * Main process */ async function main() { + console.log('šŸ”— Fixing internal links in HTML files...\n'); + console.log(`Base path: ${BASE_PATH}`); + console.log(`Dist directory: ${DIST_DIR}\n`); + try { const fixedCount = await processDirectory(DIST_DIR); if (fixedCount > 0) { + console.log(`\nāœ… Fixed links in ${fixedCount} HTML file(s)!`); } else { + console.log('\n✨ No links needed fixing!'); } process.exit(0); } catch (error) { diff --git a/packages/opencode-warcraft-notifications-plugin/pages/generate-favicon.mjs b/packages/opencode-warcraft-notifications-plugin/pages/generate-favicon.mjs index 3d736a1..9f9d082 100644 --- a/packages/opencode-warcraft-notifications-plugin/pages/generate-favicon.mjs +++ b/packages/opencode-warcraft-notifications-plugin/pages/generate-favicon.mjs @@ -33,3 +33,8 @@ const faviconSVG = blockyTextToSVG('W', { // Write to public directory const outputPath = join(publicDir, 'favicon.svg'); writeFileSync(outputPath, faviconSVG); + +console.log('āœ“ Generated: public/favicon.svg'); +console.log(' Character: W'); +console.log(' Theme: dark'); +console.log(' Block size: 8px'); diff --git a/packages/opencode-warcraft-notifications-plugin/pages/test-links.js b/packages/opencode-warcraft-notifications-plugin/pages/test-links.js index 9f1721b..2039427 100644 --- a/packages/opencode-warcraft-notifications-plugin/pages/test-links.js +++ b/packages/opencode-warcraft-notifications-plugin/pages/test-links.js @@ -143,10 +143,16 @@ async function processDirectory(dirPath) { * Main test process */ async function main() { + console.log('šŸ” Testing internal links in HTML files...\n'); + console.log(`Base path: ${BASE_PATH}`); + console.log(`Dist directory: ${DIST_DIR}\n`); + try { // Collect all links const allLinks = await processDirectory(DIST_DIR); + console.log(`Found ${allLinks.size} unique internal link(s)\n`); + // Check each link const brokenLinks = []; const missingBasePathLinks = []; @@ -195,6 +201,8 @@ async function main() { console.error('āš ļø Links are missing base path. Run: bun run fix-links\n'); process.exit(1); } + + console.log('āœ… All internal links are valid!'); process.exit(0); } catch (error) { console.error('\nāŒ Link testing failed:', error.message); diff --git a/packages/opencode-warcraft-notifications-plugin/pages/transform-docs.js b/packages/opencode-warcraft-notifications-plugin/pages/transform-docs.js index f778e4d..e3349dd 100644 --- a/packages/opencode-warcraft-notifications-plugin/pages/transform-docs.js +++ b/packages/opencode-warcraft-notifications-plugin/pages/transform-docs.js @@ -12,7 +12,7 @@ */ import { copyFile, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'; -import { basename, dirname, join } from 'node:path'; +import { basename, dirname, join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); @@ -87,12 +87,14 @@ async function copyMarkdownFiles(sourceDir, targetDir, relativePath = '') { } else if (entry.isFile() && entry.name.endsWith('.md')) { // Process markdown files (add frontmatter if needed) await processMarkdownFile(sourcePath, targetPath); + console.log(` āœ“ ${currentRelativePath}`); } else if ( entry.isFile() && (entry.name.endsWith('.json') || entry.name.endsWith('.example') || entry.name.endsWith('.schema')) ) { // Copy non-markdown files as-is await copyFile(sourcePath, targetPath); + console.log(` āœ“ ${currentRelativePath}`); } } } @@ -101,12 +103,18 @@ async function copyMarkdownFiles(sourceDir, targetDir, relativePath = '') { * Main transformation process */ async function transform() { + console.log('šŸ”„ Transforming documentation...\n'); + console.log(`Source: ${relative(process.cwd(), SOURCE_DIR)}`); + console.log(`Target: ${relative(process.cwd(), TARGET_DIR)}\n`); + try { // Ensure target directory exists await mkdir(TARGET_DIR, { recursive: true }); // Copy all markdown files await copyMarkdownFiles(SOURCE_DIR, TARGET_DIR); + + console.log('\nāœ… Documentation transformation complete!'); process.exit(0); } catch (error) { console.error('\nāŒ Transformation failed:', error.message); diff --git a/packages/opencode-warcraft-notifications-plugin/project.json b/packages/opencode-warcraft-notifications-plugin/project.json index d50feda..6fdcc87 100644 --- a/packages/opencode-warcraft-notifications-plugin/project.json +++ b/packages/opencode-warcraft-notifications-plugin/project.json @@ -32,6 +32,31 @@ "check-mirror-exists": { "executor": "@pantheon-org/tools:check-mirror-exists" }, + "lint": { + "executor": "nx:run-commands", + "options": { + "command": "biome check --write .", + "cwd": "packages/opencode-warcraft-notifications-plugin" + }, + "cache": true, + "inputs": ["default", "{workspaceRoot}/biome.json", "{projectRoot}/biome.json"] + }, + "format": { + "executor": "nx:run-commands", + "options": { + "command": "biome format --write .", + "cwd": "packages/opencode-warcraft-notifications-plugin" + }, + "cache": true, + "inputs": ["default", "{workspaceRoot}/biome.json", "{projectRoot}/biome.json"] + }, + "validate:tsdoc": { + "executor": "@pantheon-org/tools:validate-tsdoc", + "options": { + "projectRoot": "packages/opencode-warcraft-notifications-plugin" + }, + "cache": true + }, "type-check": { "executor": "nx:run-commands", "options": { @@ -46,11 +71,7 @@ }, "dev-proxy": { "executor": "@pantheon-org/tools:dev-proxy", - "options": { - "plugins": ["opencode-warcraft-notifications-plugin"], - "symlinkRoot": ".opencode/plugin", - "apply": true - } + "options": {} }, "test": { "executor": "nx:run-commands", diff --git a/packages/opencode-warcraft-notifications-plugin/src/bundled-sounds.ts b/packages/opencode-warcraft-notifications-plugin/src/bundled-sounds.ts index dacaecc..9ea83b0 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/bundled-sounds.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/bundled-sounds.ts @@ -1,4 +1,4 @@ -import { copyFile, exists, mkdir, readdir } from 'node:fs/promises'; +import { access, copyFile, mkdir, readdir } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -26,7 +26,8 @@ const ensureDirExists = async (dir: string): Promise => { const fileExists = async (path: string): Promise => { try { - return await exists(path); + await access(path); + return true; } catch { return false; } diff --git a/packages/opencode-warcraft-notifications-plugin/src/config/index.test.ts b/packages/opencode-warcraft-notifications-plugin/src/config/index.test.ts index 0e6762f..4d440db 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/config/index.test.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/config/index.test.ts @@ -125,9 +125,9 @@ it('getConfigDir handles win32 and XDG overrides', () => { } finally { // restore Object.defineProperty(process, 'platform', { value: origPlatform, configurable: true }); - if (origXdg === undefined) process.env.XDG_CONFIG_HOME = undefined; + if (origXdg === undefined) delete process.env.XDG_CONFIG_HOME; else process.env.XDG_CONFIG_HOME = origXdg; - if (origAppData === undefined) process.env.APPDATA = undefined; + if (origAppData === undefined) delete process.env.APPDATA; else process.env.APPDATA = origAppData; } }); @@ -208,7 +208,7 @@ it('getConfigDir falls back to ~/.config when XDG_CONFIG_HOME is unset', () => { const origXdg = process.env.XDG_CONFIG_HOME; try { Object.defineProperty(process, 'platform', { value: 'linux', configurable: true }); - process.env.XDG_CONFIG_HOME = undefined; + delete process.env.XDG_CONFIG_HOME; const dir = getConfigDir(); expect(typeof dir).toBe('string'); expect(dir).toContain('.config'); @@ -216,7 +216,7 @@ it('getConfigDir falls back to ~/.config when XDG_CONFIG_HOME is unset', () => { if (origPlatformDescriptor) { Object.defineProperty(process, 'platform', origPlatformDescriptor); } - if (origXdg === undefined) process.env.XDG_CONFIG_HOME = undefined; + if (origXdg === undefined) delete process.env.XDG_CONFIG_HOME; else process.env.XDG_CONFIG_HOME = origXdg; } }); @@ -226,7 +226,7 @@ it('getConfigDir falls back to AppData\\Roaming when APPDATA is unset on win32', const origAppData = process.env.APPDATA; try { Object.defineProperty(process, 'platform', { value: 'win32', configurable: true }); - process.env.APPDATA = undefined; + delete process.env.APPDATA; const dir = getConfigDir(); expect(typeof dir).toBe('string'); expect(dir).toContain('AppData'); @@ -234,7 +234,7 @@ it('getConfigDir falls back to AppData\\Roaming when APPDATA is unset on win32', if (origPlatformDescriptor) { Object.defineProperty(process, 'platform', origPlatformDescriptor); } - if (origAppData === undefined) process.env.APPDATA = undefined; + if (origAppData === undefined) delete process.env.APPDATA; else process.env.APPDATA = origAppData; } }); @@ -267,7 +267,7 @@ it('loadPluginConfig reads from XDG_CONFIG_HOME when present', async () => { } finally { // restore platform and env Object.defineProperty(process, 'platform', { value: origPlatform, configurable: true }); - if (origXdg === undefined) process.env.XDG_CONFIG_HOME = undefined; + if (origXdg === undefined) delete process.env.XDG_CONFIG_HOME; else process.env.XDG_CONFIG_HOME = origXdg; } } finally { diff --git a/packages/opencode-warcraft-notifications-plugin/src/config/loader.coverage.test.ts b/packages/opencode-warcraft-notifications-plugin/src/config/loader.coverage.test.ts index 5967700..dbb7820 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/config/loader.coverage.test.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/config/loader.coverage.test.ts @@ -75,7 +75,7 @@ describe('Plugin configuration coverage tests', () => { } } finally { if (origDebug === undefined) { - process.env.DEBUG_OPENCODE = undefined; + delete process.env.DEBUG_OPENCODE; } else { process.env.DEBUG_OPENCODE = origDebug; } diff --git a/packages/opencode-warcraft-notifications-plugin/src/config/loader.ts b/packages/opencode-warcraft-notifications-plugin/src/config/loader.ts index 8f732f8..d804177 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/config/loader.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/config/loader.ts @@ -1,4 +1,4 @@ -import { exists } from 'node:fs/promises'; +import { access } from 'node:fs/promises'; import { join } from 'node:path'; import { createLogger } from '../logger.js'; @@ -34,7 +34,9 @@ const debugInfo = (message: string, context?: Record): void => const tryLoadFromPath = async (configPath: string, pluginName: string): Promise => { debugLog('Checking configuration path', { configPath }); - if (!(await exists(configPath))) { + try { + await access(configPath); + } catch { debugLog('Configuration file not found', { configPath }); return null; } diff --git a/packages/opencode-warcraft-notifications-plugin/src/config/package.ts b/packages/opencode-warcraft-notifications-plugin/src/config/package.ts index 2bfa306..abaf71c 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/config/package.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/config/package.ts @@ -55,6 +55,7 @@ export const getPackageName = (): string | null => { ]; if (DEBUG) { + console.log('[opencode-warcraft-notifications] Looking for package.json in:', locations); } for (const pkgPath of locations) { @@ -64,11 +65,13 @@ export const getPackageName = (): string | null => { }; if (pkg && typeof pkg.name === 'string') { if (DEBUG) { + console.log('[opencode-warcraft-notifications] Found package name:', pkg.name, 'from:', pkgPath); } return pkg.name; } - } catch (_err) { + } catch (err) { if (DEBUG) { + console.log('[opencode-warcraft-notifications] Failed to read:', pkgPath, err); } } } diff --git a/packages/opencode-warcraft-notifications-plugin/src/notification-core.ts b/packages/opencode-warcraft-notifications-plugin/src/notification-core.ts new file mode 100644 index 0000000..68b7cb1 --- /dev/null +++ b/packages/opencode-warcraft-notifications-plugin/src/notification-core.ts @@ -0,0 +1,170 @@ +/** + * Notification utilities using opencode-core library + * This replaces the manual toast implementation with opencode-core + */ + +import type { PluginInput } from '@opencode-ai/plugin'; +import { createNotifier } from '@pantheon-org/opencode-notification'; + +import type { Logger } from './logger'; + +const TOAST_DURATION = { + SUCCESS: 3000, + WARNING: 5000, + INFO: 4000, + ERROR: 6000, +} as const; + +/** + * Show toast notification using opencode-core library + * + * @param ctx - Plugin context + * @param title - Toast title + * @param message - Toast message + * @param variant - Toast variant + * @param duration - Toast duration + * @param logger - Optional logger for debugging + */ +export const showToast = async ( + ctx: PluginInput, + { + title, + message, + variant, + duration, + }: { + title: string; + message: string; + variant: 'success' | 'warning' | 'info' | 'error'; + duration?: number; + }, + logger?: Logger, +): Promise => { + // Create notifier with OpenCode context and logger + const notify = createNotifier(ctx, logger); + + // Use opencode-core toast functionality with default durations + await notify.showToast({ + title, + message, + variant, + duration: duration ?? TOAST_DURATION[variant.toUpperCase() as keyof typeof TOAST_DURATION], + }); +}; + +/** + * Send message notification using opencode-core library + * + * @param ctx - Plugin context + * @param text - Message text to send + * @param options - Additional message options + * @param logger - Optional logger for debugging + */ +export const sendMessage = async ( + ctx: PluginInput, + text: string, + { + sessionID, + noReply = true, + ignored = true, + }: { + sessionID?: string; + noReply?: boolean; + ignored?: boolean; + } = {}, + logger?: Logger, +): Promise => { + // Create notifier with OpenCode context and logger + const notify = createNotifier(ctx, logger); + + // Use opencode-core message functionality + await notify.sendIgnoredMessage({ + text, + sessionID, + noReply, + ignored, + }); +}; + +/** + * Show success notification (toast + optional message) + * + * @param ctx - Plugin context + * @param title - Toast title + * @param message - Toast message + * @param useToastOnly - Whether to only show toast (default: true) + * @param logger - Optional logger for debugging + */ +export const showSuccess = async ( + ctx: PluginInput, + title: string, + message: string, + useToastOnly: boolean = true, + logger?: Logger, +): Promise => { + const notify = createNotifier(ctx, logger); + await notify.success(title, message, useToastOnly); +}; + +/** + * Show error notification (toast + optional message) + * + * @param ctx - Plugin context + * @param title - Toast title + * @param message - Toast message + * @param useToastOnly - Whether to only show toast (default: true) + * @param logger - Optional logger for debugging + */ +export const showError = async ( + ctx: PluginInput, + title: string, + message: string, + useToastOnly: boolean = true, + logger?: Logger, +): Promise => { + const notify = createNotifier(ctx, logger); + await notify.error(title, message, useToastOnly); +}; + +/** + * Show warning notification (toast + optional message) + * + * @param ctx - Plugin context + * @param title - Toast title + * @param message - Toast message + * @param useToastOnly - Whether to only show toast (default: true) + * @param logger - Optional logger for debugging + */ +export const showWarning = async ( + ctx: PluginInput, + title: string, + message: string, + useToastOnly: boolean = true, + logger?: Logger, +): Promise => { + const notify = createNotifier(ctx, logger); + await notify.warning(title, message, useToastOnly); +}; + +/** + * Show info notification (toast + optional message) + * + * @param ctx - Plugin context + * @param title - Toast title + * @param message - Toast message + * @param useToastOnly - Whether to only show toast (default: true) + * @param logger - Optional logger for debugging + */ +export const showInfo = async ( + ctx: PluginInput, + title: string, + message: string, + useToastOnly: boolean = true, + logger?: Logger, +): Promise => { + const notify = createNotifier(ctx, logger); + await notify.info(title, message, useToastOnly); +}; + +// Re-export types for convenience +export type { soundDescriptions } from './sounds'; diff --git a/packages/opencode-warcraft-notifications-plugin/src/notification.ts b/packages/opencode-warcraft-notifications-plugin/src/notification.ts index 0b3d66f..75fa11f 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/notification.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/notification.ts @@ -1,4 +1,5 @@ import type { Plugin } from '@opencode-ai/plugin'; +import { createNotifier } from '@pantheon-org/opencode-notification'; import { installBundledSoundsIfMissing } from './bundled-sounds.js'; import { loadPluginConfig } from './config/index.js'; @@ -13,13 +14,6 @@ import { const log = createLogger({ module: 'opencode-plugin-warcraft-notifications' }); -// Constants for toast durations and cache keys -const TOAST_DURATION = { - SUCCESS: 3000, - WARNING: 5000, - INFO: 4000, -} as const; - const CACHE_KEY_NOTIFIED_MISSING = '_notified_missing'; /** @@ -33,12 +27,15 @@ const CACHE_KEY_NOTIFIED_MISSING = '_notified_missing'; * to download wave files from the network. */ export const NotificationPlugin: Plugin = async (ctx) => { - const { project: _project, client, $, worktree: _worktree } = ctx; + const { project: _project, $, worktree: _worktree } = ctx; // Keep a simple cache flag to avoid repeated checks. const checkedSoundCache = new Map(); void _project; void _worktree; + // Create notifier for showing notifications + const notify = createNotifier(ctx, log); + // Load plugin configuration from plugin.json const pluginName = '@pantheon-ai/opencode-warcraft-notifications'; const pluginConfig = await loadPluginConfig(pluginName); @@ -49,14 +46,10 @@ export const NotificationPlugin: Plugin = async (ctx) => { // Only notify user if files were actually installed if (installedCount > 0) { try { - await client.tui.showToast({ - body: { - title: 'Warcraft Sounds', - message: `Installed ${installedCount} sound file${installedCount > 1 ? 's' : ''} successfully`, - variant: 'success', - duration: TOAST_DURATION.SUCCESS, - }, - }); + await notify.success( + 'Warcraft Sounds', + `Installed ${installedCount} sound file${installedCount > 1 ? 's' : ''} successfully`, + ); } catch (toastErr) { // Silently ignore toast errors - not critical if (process.env.DEBUG_OPENCODE) log.debug('Toast notification failed', { error: toastErr }); @@ -66,14 +59,7 @@ export const NotificationPlugin: Plugin = async (ctx) => { if (process.env.DEBUG_OPENCODE) log.warn('installBundledSoundsIfMissing failed', { error: err }); // Notify user of installation failure try { - await client.tui.showToast({ - body: { - title: 'Warcraft Sounds', - message: 'Failed to install sound files. Using system sounds as fallback.', - variant: 'warning', - duration: TOAST_DURATION.WARNING, - }, - }); + await notify.warning('Warcraft Sounds', 'Failed to install sound files. Using system sounds as fallback.'); } catch (toastErr) { // Silently ignore toast errors - not critical if (process.env.DEBUG_OPENCODE) log.debug('Toast notification failed', { error: toastErr }); @@ -122,14 +108,7 @@ export const NotificationPlugin: Plugin = async (ctx) => { checkedSoundCache.set(CACHE_KEY_NOTIFIED_MISSING, true); try { - await client.tui.showToast({ - body: { - title: 'Warcraft Sounds', - message: `Sound file not found: ${filename}. Using system sound as fallback.`, - variant: 'info', - duration: TOAST_DURATION.INFO, - }, - }); + await notify.info('Warcraft Sounds', `Sound file not found: ${filename}. Using system sound as fallback.`); } catch (toastErr) { // Silently ignore toast errors - not critical if (process.env.DEBUG_OPENCODE) log.debug('Toast notification failed', { error: toastErr }); @@ -202,14 +181,7 @@ export const NotificationPlugin: Plugin = async (ctx) => { if (showToast) { try { - await client.tui.showToast({ - body: { - title: toastTitle, - message: toastMessage, - variant: 'info', - duration: TOAST_DURATION.INFO, - }, - }); + await notify.info(toastTitle, toastMessage); } catch (toastErr) { // Only log toast errors in debug mode if (process.env.DEBUG_OPENCODE) { diff --git a/packages/opencode-warcraft-notifications-plugin/src/schema-validator.coverage.test.ts b/packages/opencode-warcraft-notifications-plugin/src/schema-validator.coverage.test.ts index d6f4644..82e4476 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/schema-validator.coverage.test.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/schema-validator.coverage.test.ts @@ -48,8 +48,7 @@ describe('schema-validator coverage tests', () => { describe('validatePluginConfig unexpected errors', () => { it('should handle unexpected validation errors', () => { // Pass a circular reference to cause an unexpected error - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const circular: any = {}; + const circular: Record = {}; circular.self = circular; const result = validatePluginConfig(circular); diff --git a/packages/opencode-warcraft-notifications-plugin/src/sounds/index.ts b/packages/opencode-warcraft-notifications-plugin/src/sounds/index.ts index b3422de..b0f1e6c 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/sounds/index.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/sounds/index.ts @@ -1,4 +1,4 @@ -import { exists } from 'node:fs/promises'; +import { access } from 'node:fs/promises'; import { join } from 'node:path'; import { DEFAULT_DATA_DIR, type Faction } from '../config/index.js'; @@ -43,7 +43,15 @@ export const soundExists = async ( existsFn?: (path: string) => Promise, ): Promise => { const filePath = getSoundPath(filename, faction, dataDir); - const checker = existsFn ?? exists; + const defaultChecker = async (path: string) => { + try { + await access(path); + return true; + } catch { + return false; + } + }; + const checker = existsFn ?? defaultChecker; return await checker(filePath); }; diff --git a/packages/opencode-warcraft-notifications-plugin/src/test-utils.test.ts b/packages/opencode-warcraft-notifications-plugin/src/test-utils.test.ts index b6a478c..06fe4ec 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/test-utils.test.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/test-utils.test.ts @@ -92,6 +92,7 @@ describe('test-utils helpers', () => { it('silenceConsole returns restore function', () => { const restore = silenceConsole(); try { + console.log('this should be silent'); console.error('also silent'); } finally { restore(); diff --git a/packages/opencode-warcraft-notifications-plugin/src/test-utils.ts b/packages/opencode-warcraft-notifications-plugin/src/test-utils.ts index 702ac95..47cc7e1 100644 --- a/packages/opencode-warcraft-notifications-plugin/src/test-utils.ts +++ b/packages/opencode-warcraft-notifications-plugin/src/test-utils.ts @@ -121,7 +121,7 @@ export const withPlatform = async (platform: string, fn: () => Prom return await fn(); } finally { if (desc) Object.defineProperty(process, 'platform', desc); - else (process as unknown as { platform?: string }).platform = undefined; + else delete (process as unknown as { platform?: string }).platform; } };