From 1e6b8d9070b2d2043ec03debbe18dd7312810f82 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 10:15:52 +0200 Subject: [PATCH 1/8] refactor: migrate prompt utilities to genai-lite v0.1.1 - Updated genai-lite from v0.1.0 to v0.1.1 to use new utils export - Removed local PromptUtils.ts implementation - Updated RelevanceEngineService to import from genai-lite/utils - Added webpack alias to resolve genai-lite/utils path - Successfully tested with npm run package This completes Phase 2 of the prompt utilities extraction project, reducing code duplication and leveraging the shared genai-lite library. Signed-off-by: Luigi Acerbi Signed-off-by: lacerbi --- electron/services/PromptUtils.ts | 71 --------------------- electron/services/RelevanceEngineService.ts | 7 +- package-lock.json | 9 +-- package.json | 2 +- webpack.main.config.js | 3 + 5 files changed, 13 insertions(+), 79 deletions(-) delete mode 100644 electron/services/PromptUtils.ts diff --git a/electron/services/PromptUtils.ts b/electron/services/PromptUtils.ts deleted file mode 100644 index e90b2fe..0000000 --- a/electron/services/PromptUtils.ts +++ /dev/null @@ -1,71 +0,0 @@ -// AI Summary: Provides utility functions for prompt construction, specifically token counting -// and smart content previewing, for use within the main process. - -import { Tiktoken, encodingForModel } from 'js-tiktoken'; - -let tokenizer: Tiktoken | null = null; - -// Initialize tokenizer with GPT-4 encoding (cl100k_base) -function getTokenizer(): Tiktoken { - if (!tokenizer) { - try { - tokenizer = encodingForModel('gpt-4'); - } catch (error) { - console.error('Failed to initialize tokenizer:', error); - throw error; // Re-throw to be handled by the calling function - } - } - return tokenizer; -} - -export function countTokens(text: string): number { - if (!text) { - return 0; - } - - try { - const enc = getTokenizer(); - if (!enc) { - console.error('Failed to get tokenizer'); - return 0; - } - const tokens = enc.encode(text); - return tokens.length; - } catch (error) { - console.error('Error counting tokens:', error); - return 0; - } -} - -// Smart content preview for non-selected files -export function getSmartPreview(content: string, config: { minLines: number; maxLines: number }): string { - const lines = content.split('\n'); - - // If the file is not longer than maxLines, return it in full - if (lines.length <= config.maxLines) { - return content; - } - - // Always show at least minLines - let endLine = config.minLines; - let emptyLinesCount = lines - .slice(0, config.minLines) - .filter((line) => line.trim() === '').length; - - // If we haven't found at least two empty lines, keep looking up to maxLines - if (emptyLinesCount < 2 && lines.length > config.minLines) { - for ( - let i = config.minLines; - i < Math.min(lines.length, config.maxLines); - i++ - ) { - if (lines[i].trim() === '') { - endLine = i + 1; // Include the empty line - break; - } - endLine = i + 1; - } - } - - return lines.slice(0, endLine).join('\n') + '\n... (content truncated)'; -} diff --git a/electron/services/RelevanceEngineService.ts b/electron/services/RelevanceEngineService.ts index d23ad75..e797670 100644 --- a/electron/services/RelevanceEngineService.ts +++ b/electron/services/RelevanceEngineService.ts @@ -6,7 +6,8 @@ import type { IGitService } from '../../common/types/git-service'; import { DependencyScanner } from './DependencyScanner'; import { PathUtils } from './PathUtils'; import { CONTEXT_BUILDER, SETTINGS } from '../../src/utils/constants'; -import * as PromptUtils from './PromptUtils'; +// @ts-ignore - webpack module resolution issue +import { countTokens, getSmartPreview } from 'genai-lite/utils'; import { ProjectGraphService } from './ProjectGraphService'; import { analyzeTaskDescription } from './TaskAnalysisUtils'; import { UserActivityService } from './UserActivityService'; @@ -354,11 +355,11 @@ export class RelevanceEngineService { const content = (await this.fileService.read(filePath, { encoding: 'utf-8', })) as string; - const preview = PromptUtils.getSmartPreview( + const preview = getSmartPreview( content, smartPreviewConfig ); - const tokenCount = PromptUtils.countTokens(preview); + const tokenCount = countTokens(preview); if (currentTokens + tokenCount <= options.maxNeighborTokens) { promptNeighbors.push(filePath); diff --git a/package-lock.json b/package-lock.json index 01482cf..a960d7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.0", + "genai-lite": "^0.1.1", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", @@ -9450,13 +9450,14 @@ } }, "node_modules/genai-lite": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.0.tgz", - "integrity": "sha512-o8axryP9nIA+UIJYT7QD4wW1sFEAudFpTjQIKc+xpH63CRA4A7sc8Yuivy/vDkYbpZa4f7D25zIvZCd6VrqWRw==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.1.tgz", + "integrity": "sha512-jTyUIIxFhSA1VnK6Ozy1XFEuUNXehq75x4a7aNeg57KFpf97OqWJ/w5iPpT8hXdssWNsHtwaBE5cuz66CS3PXA==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.52.0", "@google/genai": "^1.0.1", + "js-tiktoken": "^1.0.20", "openai": "^4.103.0" }, "funding": { diff --git a/package.json b/package.json index 26b8aef..8eafdfe 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.0", + "genai-lite": "^0.1.1", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", diff --git a/webpack.main.config.js b/webpack.main.config.js index c83f16c..c774f51 100644 --- a/webpack.main.config.js +++ b/webpack.main.config.js @@ -22,6 +22,9 @@ module.exports = { }, resolve: { extensions: ['.ts', '.js'], + alias: { + 'genai-lite/utils': path.resolve(__dirname, 'node_modules/genai-lite/dist/utils/index.js'), + }, }, output: { path: path.resolve(__dirname, '.webpack'), From ad672c6b9c6754a74d835dfebdb7b2ee7f4ecd14 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 10:20:42 +0200 Subject: [PATCH 2/8] docs: update summaries after PromptUtils migration - Removed PromptUtils.ts references from summary files - Added note about genai-lite/utils usage in RelevanceEngineService - Updated both short and long summaries in electron/services/ Signed-off-by: Luigi Acerbi Signed-off-by: lacerbi --- electron/services/.summary_long.md | 14 +------------- electron/services/.summary_short.md | 3 +-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/electron/services/.summary_long.md b/electron/services/.summary_long.md index 58b463b..180b65d 100644 --- a/electron/services/.summary_long.md +++ b/electron/services/.summary_long.md @@ -131,6 +131,7 @@ Sophisticated context selection engine: - Incorporates UserActivityService signals - Respects GitService history - Applies TaskAnalysisUtils keywords +- Uses genai-lite/utils for token counting and smart preview ### TaskAnalysisUtils.ts @@ -230,19 +231,6 @@ Import path resolution: ## Utility Services -### PromptUtils.ts - -Token management utilities: - -**Core Functions:** -- `countTokens()`: GPT-4 tokenizer usage -- `getSmartPreview()`: Intelligent truncation - -**Smart Preview Algorithm:** -- Respects code boundaries -- Looks for natural breaks -- Configurable line limits - ### wordFilters.ts Keyword filtering data: diff --git a/electron/services/.summary_short.md b/electron/services/.summary_short.md index 056088a..e69c3d3 100644 --- a/electron/services/.summary_short.md +++ b/electron/services/.summary_short.md @@ -15,8 +15,7 @@ This directory contains core services that power Athanor's file system operation - **PathUtils.test.ts** - Unit tests for PathUtils validating cross-platform path manipulation functions. - **PathUtils.ts** - Stateless utilities for consistent cross-platform path manipulation with Unix-style normalization. - **ProjectGraphService.ts** - Analyzes project structure to build dependency graphs, file mentions, and commit relationships for relevance scoring. -- **PromptUtils.ts** - Utilities for prompt construction including token counting and smart content preview generation. -- **RelevanceEngineService.ts** - Two-phase scoring engine that identifies relevant context files using multiple heuristics and token budgeting. +- **RelevanceEngineService.ts** - Two-phase scoring engine that identifies relevant context files using multiple heuristics and token budgeting (uses genai-lite/utils for prompt utilities). - **SettingsService.ts** - Manages reading/writing of project and application settings JSON files with proper error handling. - **ShellService.ts** - Manages persistent pseudo-terminal sessions for integrated CLI functionality with buffered output. - **TaskAnalysisUtils.test.ts** - Unit tests for task analysis utilities validating keyword extraction and path mention detection. From 275e792d95020f28f7a65d0e63ee28d166c731f3 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 15:57:42 +0200 Subject: [PATCH 3/8] refactor: migrate to genai-lite v0.1.3 preset system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated genai-lite dependency from v0.1.1 to v0.1.3 - Configured LLMService to use Athanor's presets with 'replace' mode - Removed redundant local preset service and types: - Deleted src/types/athanorPresets.ts - Deleted src/services/athanorPresetService.ts - Added IPC support for fetching presets from LLM service: - Added GET_PRESETS channel to common/types/llm.ts - Implemented handler in electron/handlers/llmIpc.ts - Updated preload.ts and global types - Updated UI components to use genai-lite's ModelPreset type: - SendViaApiControls.tsx now fetches presets via IPC - ApiKeyManagementPane.tsx uses LLM service for presets - Fixed webpack and TypeScript config to support JSON imports - Updated AI summaries to reflect architectural changes This migration eliminates code duplication while maintaining full control over Athanor's model presets through genai-lite's configurable system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- common/types/llm.ts | 1 + electron/handlers/llmIpc.ts | 12 +- electron/main.ts | 9 +- electron/preload.ts | 1 + package-lock.json | 42 ++- package.json | 2 +- src/components/ApiKeyManagementPane.tsx | 4 +- .../action-panel/SendViaApiControls.tsx | 10 +- src/services/athanorPresetService.ts | 239 ------------------ src/types/athanorPresets.ts | 26 -- src/types/global.d.ts | 5 + tsconfig.json | 1 + webpack.main.config.js | 9 +- 13 files changed, 78 insertions(+), 283 deletions(-) delete mode 100644 src/services/athanorPresetService.ts delete mode 100644 src/types/athanorPresets.ts diff --git a/common/types/llm.ts b/common/types/llm.ts index 8f7028b..3d9d791 100644 --- a/common/types/llm.ts +++ b/common/types/llm.ts @@ -7,6 +7,7 @@ export const LLM_IPC_CHANNELS = { GET_MODELS: 'llm:get-models', SEND_MESSAGE: 'llm:send-message', IS_KEY_AVAILABLE: 'llm:is-key-available', + GET_PRESETS: 'llm:get-presets', } as const; /** diff --git a/electron/handlers/llmIpc.ts b/electron/handlers/llmIpc.ts index f477d2b..6f41325 100644 --- a/electron/handlers/llmIpc.ts +++ b/electron/handlers/llmIpc.ts @@ -1,5 +1,5 @@ // AI Summary: IPC handlers for LLM operations, routing requests between renderer and main process. -// Registers handlers for getting providers/models and sending messages to LLMs. +// Registers handlers for getting providers/models, sending messages to LLMs, and fetching presets. import { ipcMain } from 'electron'; import type { LLMService, LLMChatRequest, ApiProviderId } from 'genai-lite'; @@ -73,5 +73,15 @@ export function registerLlmIpc(llmService: LLMService, apiKeyService: ApiKeyServ } ); + // Handler for getting configured presets + ipcMain.handle(LLM_IPC_CHANNELS.GET_PRESETS, async () => { + try { + return llmService.getPresets(); + } catch (error) { + console.error('Error in GET_PRESETS handler:', error); + throw error; + } + }); + console.log('LLM IPC handlers registered successfully'); } diff --git a/electron/main.ts b/electron/main.ts index 2a4cf10..1421ca1 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1,6 +1,7 @@ // AI Summary: Main electron process that coordinates window management, file system operations, // and IPC communication between processes. Handles application lifecycle events, path resolution, // and uncaught exception handling with proper cleanup of file watchers. +// Configures genai-lite LLM service with Athanor's model presets using replace mode. import { app, BrowserWindow, Menu, nativeTheme, ipcMain } from 'electron'; import fixPath from 'fix-path'; import { Worker } from 'worker_threads'; @@ -14,7 +15,7 @@ import { ApiKeyServiceMain, registerSecureApiKeyIpc, } from 'genai-key-storage-lite'; -import { LLMService, type ApiKeyProvider } from 'genai-lite'; +import { LLMService, type ApiKeyProvider, type ModelPreset } from 'genai-lite'; import { RelevanceEngineService } from './services/RelevanceEngineService'; import { GitService } from './services/GitService'; import { UserActivityService } from './services/UserActivityService'; @@ -25,6 +26,7 @@ import { } from './services/ProjectGraphService'; import { PROJECT_ANALYSIS } from '../src/utils/constants'; import type { ApplicationSettings } from '../src/types/global'; +import athanorPresets from '../src/config/athanorModelPresets.json'; // --- WSL Graphics Fix Start --- // This addresses a specific rendering issue on WSL where Electron may default @@ -332,7 +334,10 @@ app.whenReady().then(async () => { return envKey || null; } }; - llmService = new LLMService(electronKeyProvider); + llmService = new LLMService(electronKeyProvider, { + presets: athanorPresets as ModelPreset[], + presetMode: 'replace' + }); // Register IPC handlers from the external package registerSecureApiKeyIpc(apiKeyService); diff --git a/electron/preload.ts b/electron/preload.ts index 131c4de..95ffebe 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -86,6 +86,7 @@ contextBridge.exposeInMainWorld('electronBridge', { getModels: (providerId: string) => ipcRenderer.invoke(LLM_IPC_CHANNELS.GET_MODELS, providerId), sendMessage: (request: any) => ipcRenderer.invoke(LLM_IPC_CHANNELS.SEND_MESSAGE, request), isKeyAvailable: (providerId: string) => ipcRenderer.invoke(LLM_IPC_CHANNELS.IS_KEY_AVAILABLE, providerId), + getPresets: () => ipcRenderer.invoke(LLM_IPC_CHANNELS.GET_PRESETS), }, userActivity: () => ipcRenderer.send('user-activity'), context: { diff --git a/package-lock.json b/package-lock.json index a960d7d..ff0eb2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.1", + "genai-lite": "^0.1.3", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", @@ -9450,21 +9450,51 @@ } }, "node_modules/genai-lite": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.1.tgz", - "integrity": "sha512-jTyUIIxFhSA1VnK6Ozy1XFEuUNXehq75x4a7aNeg57KFpf97OqWJ/w5iPpT8hXdssWNsHtwaBE5cuz66CS3PXA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.3.tgz", + "integrity": "sha512-0qxuXb1QtF+ANoYDinjRAkxDlHJqwiVBC9Wu1QE2DqTSyGoCeEO3P3GD+4zItaX85Xlvly05YNXia6oLGXOo6w==", "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "^0.52.0", + "@anthropic-ai/sdk": "^0.56.0", "@google/genai": "^1.0.1", "js-tiktoken": "^1.0.20", - "openai": "^4.103.0" + "openai": "^5.8.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/lacerbi" } }, + "node_modules/genai-lite/node_modules/@anthropic-ai/sdk": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.56.0.tgz", + "integrity": "sha512-SLCB8M8+VMg1cpCucnA1XWHGWqVSZtIWzmOdDOEu3eTFZMB+A0sGZ1ESO5MHDnqrNTXz3safMrWx9x4rMZSOqA==", + "license": "MIT", + "bin": { + "anthropic-ai-sdk": "bin/cli" + } + }, + "node_modules/genai-lite/node_modules/openai": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.8.2.tgz", + "integrity": "sha512-8C+nzoHYgyYOXhHGN6r0fcb4SznuEn1R7YZMvlqDbnCuE0FM2mm3T1HiYW6WIcMS/F1Of2up/cSPjLPaWt0X9Q==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", diff --git a/package.json b/package.json index 8eafdfe..b980792 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.1", + "genai-lite": "^0.1.3", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", diff --git a/src/components/ApiKeyManagementPane.tsx b/src/components/ApiKeyManagementPane.tsx index ac00eee..a236671 100644 --- a/src/components/ApiKeyManagementPane.tsx +++ b/src/components/ApiKeyManagementPane.tsx @@ -1,6 +1,7 @@ // AI Summary: Dedicated component for secure API key management with provider selection, // key storage/deletion, validation, and display functionality. Uses secure storage via IPC bridge. // Updated for enhanced security - no longer has access to plaintext keys in renderer process. +// Fetches model presets from genai-lite LLM service to determine available providers. import React, { useEffect, useState, useCallback } from 'react'; import { HelpCircle, Eye, EyeOff, Save, Trash2, Check } from 'lucide-react'; @@ -9,7 +10,6 @@ import { ApiKeyServiceRenderer } from 'genai-key-storage-lite/renderer'; // Once common export is added to the package, use these imports: import type { ApiProvider } from 'genai-key-storage-lite/common'; import { ApiKeyStorageError } from 'genai-key-storage-lite/common'; -import { getAllAthanorPresets } from '../services/athanorPresetService'; const ApiKeyManagementPane: React.FC = () => { // API Key Management state @@ -96,7 +96,7 @@ const ApiKeyManagementPane: React.FC = () => { setKeyOpError(null); // Fetch Athanor presets and filter providers - getAllAthanorPresets() + window.electronBridge.llmService.getPresets() .then((presets) => { // Extract unique provider IDs from presets const presetProviderIdSet = new Set( diff --git a/src/components/action-panel/SendViaApiControls.tsx b/src/components/action-panel/SendViaApiControls.tsx index b46c79b..07e92c1 100644 --- a/src/components/action-panel/SendViaApiControls.tsx +++ b/src/components/action-panel/SendViaApiControls.tsx @@ -1,10 +1,10 @@ // AI Summary: Component for managing "Send via API" functionality including LLM preset selection, // API key validation, and sending prompts to LLM services with response processing. +// Uses genai-lite for model presets via the IPC bridge. import React, { useState, useEffect, useRef } from 'react'; import { Info } from 'lucide-react'; -import type { AthanorModelPreset } from '../../types/athanorPresets'; +import type { ModelPreset } from 'genai-lite'; import type { ApplicationSettings, LogEntry } from '../../types/global'; -import { getAllAthanorPresets } from '../../services/athanorPresetService'; import { processAiResponseContent } from '../../actions/ApplyAiOutputAction'; import { useApplyChangesStore } from '../../stores/applyChangesStore'; @@ -33,7 +33,7 @@ const SendViaApiControls: React.FC = ({ }) => { // State for LLM preset selection const [availablePresets, setAvailablePresets] = useState< - AthanorModelPreset[] + ModelPreset[] >([]); const [isLoadingPresets, setIsLoadingPresets] = useState(false); @@ -51,8 +51,8 @@ const SendViaApiControls: React.FC = ({ setIsLoadingPresets(true); try { - const allPresets = await getAllAthanorPresets(); - const filtered: AthanorModelPreset[] = []; + const allPresets = await window.electronBridge.llmService.getPresets(); + const filtered: ModelPreset[] = []; for (const preset of allPresets) { // Check if API key is available from any source for the provider diff --git a/src/services/athanorPresetService.ts b/src/services/athanorPresetService.ts deleted file mode 100644 index 9031563..0000000 --- a/src/services/athanorPresetService.ts +++ /dev/null @@ -1,239 +0,0 @@ -// AI Summary: Service for loading and managing Athanor model presets, filtering against available providers/models, -// with caching and validation. Integrates with LLM service via Electron bridge and provides logging. -import type { AthanorModelPreset } from '../types/athanorPresets'; -import { useLogStore } from '../stores/logStore'; - -// Import JSON data using require to avoid TypeScript module resolution issues -const athanorModelPresetsData = require('../config/athanorModelPresets.json'); -const rawPresets: AthanorModelPreset[] = athanorModelPresetsData as AthanorModelPreset[]; - -// Module-level cache and initialization tracking -let filteredPresetsCache: AthanorModelPreset[] | null = null; -let initializationPromise: Promise | null = null; - -/** - * Performs the actual initialization of presets by validating against available providers and models - */ -async function performInitialization(): Promise { - const { addLog } = useLogStore.getState(); - - // Check if the LLM service bridge is available - if (!window.electronBridge || !window.electronBridge.llmService) { - addLog("Error: LLM Service bridge not available. Cannot initialize Athanor model presets."); - filteredPresetsCache = []; - return; - } - - try { - addLog("Initializing Athanor model presets..."); - - // Fetch available providers and their models - const availableProviders = await window.electronBridge.llmService.getProviders(); - const validProviderIds = new Set(availableProviders.map(p => p.id)); - const validModelMap = new Map>(); - - // Build a map of provider -> valid models - for (const provider of availableProviders) { - try { - const models = await window.electronBridge.llmService.getModels(provider.id); - validModelMap.set(provider.id, new Set(models.map(m => m.id))); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - addLog(`Warning: Failed to fetch models for provider "${provider.id}": ${message}`); - validModelMap.set(provider.id, new Set()); // Set empty set for this provider - } - } - - // Filter presets against available providers and models - filteredPresetsCache = rawPresets.filter(preset => { - // Validate basic structure - if (!preset.id || !preset.displayName || !preset.providerId || !preset.modelId) { - addLog(`Warning: Athanor preset with incomplete data structure will be excluded. ID: ${preset.id || 'unknown'}`); - return false; - } - - // Check if provider is available - if (!validProviderIds.has(preset.providerId)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) references an unavailable provider "${preset.providerId}". It will be excluded.`); - return false; - } - - // Check if model is available for this provider - const providerModels = validModelMap.get(preset.providerId); - if (!providerModels || !providerModels.has(preset.modelId)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) references an unavailable model "${preset.modelId}" for provider "${preset.providerId}". It will be excluded.`); - return false; - } - - // Validate settings structure - if (preset.settings && typeof preset.settings !== 'object') { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) has invalid 'settings' format. It will be excluded.`); - return false; - } - - // Additional validation for specific settings - if (preset.settings) { - // Validate temperature - if (preset.settings.temperature !== undefined && - (typeof preset.settings.temperature !== 'number' || - preset.settings.temperature < 0 || - preset.settings.temperature > 2)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) has invalid temperature value. It will be excluded.`); - return false; - } - - // Validate maxTokens - if (preset.settings.maxTokens !== undefined && - (!Number.isInteger(preset.settings.maxTokens) || - preset.settings.maxTokens < 1 || - preset.settings.maxTokens > 100000)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) has invalid maxTokens value. It will be excluded.`); - return false; - } - - // Validate topP - if (preset.settings.topP !== undefined && - (typeof preset.settings.topP !== 'number' || - preset.settings.topP < 0 || - preset.settings.topP > 1)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) has invalid topP value. It will be excluded.`); - return false; - } - - // Validate Gemini safety settings if present - if (preset.settings.geminiSafetySettings && - !Array.isArray(preset.settings.geminiSafetySettings)) { - addLog(`Warning: Athanor preset "${preset.displayName}" (ID: ${preset.id}) has invalid geminiSafetySettings format. It will be excluded.`); - return false; - } - } - - return true; - }); - - const totalCount = rawPresets.length; - const validCount = filteredPresetsCache.length; - const excludedCount = totalCount - validCount; - - addLog(`Athanor model presets initialized. ${validCount} presets available${excludedCount > 0 ? ` (${excludedCount} excluded due to validation issues)` : ''}.`); - - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - addLog(`Error initializing Athanor model presets: ${message}`); - filteredPresetsCache = []; // Set to empty on error - } -} - -/** - * Ensures that the preset service is initialized before use - */ -function ensureInitialized(): Promise { - if (!initializationPromise) { - initializationPromise = performInitialization(); - } - return initializationPromise; -} - -/** - * Gets all available Athanor model presets - * @returns Promise resolving to array of valid presets - */ -export async function getAllAthanorPresets(): Promise { - await ensureInitialized(); - return [...(filteredPresetsCache || [])]; // Return a copy to prevent external modification -} - -/** - * Gets a specific Athanor model preset by ID - * @param id - The preset ID to look up - * @returns Promise resolving to the preset or undefined if not found - */ -export async function getAthanorPresetById(id: string): Promise { - await ensureInitialized(); - return filteredPresetsCache?.find(p => p.id === id); -} - -/** - * Gets all presets for a specific provider - * @param providerId - The provider ID to filter by - * @returns Promise resolving to array of presets for the provider - */ -export async function getAthanorPresetsByProvider(providerId: string): Promise { - await ensureInitialized(); - return (filteredPresetsCache || []).filter(p => p.providerId === providerId); -} - -/** - * Gets all presets for a specific model - * @param providerId - The provider ID - * @param modelId - The model ID to filter by - * @returns Promise resolving to array of presets for the model - */ -export async function getAthanorPresetsByModel(providerId: string, modelId: string): Promise { - await ensureInitialized(); - return (filteredPresetsCache || []).filter(p => p.providerId === providerId && p.modelId === modelId); -} - -/** - * Checks if presets are currently being initialized - * @returns True if initialization is in progress - */ -export function isInitializing(): boolean { - return initializationPromise !== null && filteredPresetsCache === null; -} - -/** - * Checks if presets have been initialized - * @returns True if initialization has completed (successfully or with errors) - */ -export function isInitialized(): boolean { - return filteredPresetsCache !== null; -} - -/** - * Forces re-initialization of the preset service - * This is useful when the available providers/models might have changed - * @returns Promise that resolves when re-initialization is complete - */ -export async function reinitializeAthanorModelPresetService(): Promise { - // Reset the cache and initialization state - filteredPresetsCache = null; - initializationPromise = null; - - // Perform fresh initialization - await ensureInitialized(); -} - -/** - * Explicitly initializes the Athanor model preset service - * This can be called during application startup to trigger loading - * @returns Promise that resolves when initialization is complete - */ -export async function initializeAthanorModelPresetService(): Promise { - await ensureInitialized(); -} - -/** - * Gets statistics about the preset service - * @returns Object containing initialization status and preset counts - */ -export async function getAthanorPresetServiceStats(): Promise<{ - isInitialized: boolean; - isInitializing: boolean; - totalRawPresets: number; - validPresets: number; - excludedPresets: number; -}> { - await ensureInitialized(); - - const validCount = filteredPresetsCache?.length || 0; - const totalCount = rawPresets.length; - - return { - isInitialized: isInitialized(), - isInitializing: isInitializing(), - totalRawPresets: totalCount, - validPresets: validCount, - excludedPresets: totalCount - validCount - }; -} diff --git a/src/types/athanorPresets.ts b/src/types/athanorPresets.ts deleted file mode 100644 index 8b6ba0e..0000000 --- a/src/types/athanorPresets.ts +++ /dev/null @@ -1,26 +0,0 @@ -// AI Summary: Defines TypeScript interface for Athanor model presets, combining provider/model references with custom LLM settings. -import type { ApiProviderId, LLMSettings } from 'genai-lite'; - -/** - * Represents an Athanor-specific model preset with pre-configured LLM settings - * optimized for common use cases within the application. - */ -export interface AthanorModelPreset { - /** Unique preset identifier, e.g., "gemini-pro-creative-writing" */ - id: string; - - /** User-friendly display name, e.g., "Google Gemini - gemini-1.5-pro-002 (Creative)" */ - displayName: string; - - /** Optional description of the preset's intended use case */ - description?: string; - - /** Provider ID that matches an entry in SUPPORTED_PROVIDERS from llm/main/config.ts */ - providerId: ApiProviderId; - - /** Model ID that matches a supported model for the given providerId */ - modelId: string; - - /** Preset-specific LLM settings, can include provider-specific configurations */ - settings: Partial; -} diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 4455966..ba92b03 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -258,6 +258,11 @@ declare global { * Checks if an API key is available from any source (secure storage or ENV). */ isKeyAvailable: (providerId: string) => Promise; + + /** + * Gets the configured model presets + */ + getPresets: () => Promise; }; userActivity: () => void; context: { diff --git a/tsconfig.json b/tsconfig.json index 7c0ac5a..5cf54b1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "strict": true, "skipLibCheck": true, "jsx": "react", + "resolveJsonModule": true, "baseUrl": "./", "paths": { "@components/*": ["src/components/*"], diff --git a/webpack.main.config.js b/webpack.main.config.js index c774f51..771ecb1 100644 --- a/webpack.main.config.js +++ b/webpack.main.config.js @@ -18,10 +18,17 @@ module.exports = { ], use: [{ loader: 'ts-loader' }], }, + { + test: /\.json$/, + include: [ + path.resolve(__dirname, 'src/config'), + ], + type: 'json', + }, ], }, resolve: { - extensions: ['.ts', '.js'], + extensions: ['.ts', '.js', '.json'], alias: { 'genai-lite/utils': path.resolve(__dirname, 'node_modules/genai-lite/dist/utils/index.js'), }, From eb7b9cda99da9db656ffc04c108d3486fb715487 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 15:58:53 +0200 Subject: [PATCH 4/8] chore: bump version to 0.7.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b980792..5928c14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "athanor", - "version": "0.7.11", + "version": "0.7.12", "bugs": { "url": "https://github.com/lacerbi/athanor/issues" }, From ac7d9861f588609941ded1337d198a0157ff3bed Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 20:23:47 +0200 Subject: [PATCH 5/8] refactor: migrate template rendering to genai-lite v0.1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace local substituteVariables with genai-lite's renderTemplate - Update package.json to use genai-lite v0.1.4 - Remove substituteVariables function from promptTemplates.ts - Update imports in buildPrompt.ts to use renderTemplate - Update summary documentation to reflect changes The template rendering functionality is now provided by the genai-lite library, reducing code duplication and providing a well-tested utility to the broader community. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- CHANGELOG.md | 18 ++++++++ package-lock.json | 12 +++--- package.json | 2 +- src/utils/.summary_long.md | 9 ++-- src/utils/.summary_short.md | 4 +- src/utils/buildPrompt.ts | 4 +- src/utils/promptTemplates.ts | 80 +----------------------------------- 7 files changed, 35 insertions(+), 94 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bad841..22e66c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,24 @@ and this project aims to adhere to [Semantic Versioning](https://semver.org/spec _(Future changes will go here)_ +## [0.7.12] - 2025-07-05 + +### Changed + +- **Migrated to genai-lite v0.1.3 Preset System**: Refactored model preset management to use genai-lite's configurable preset system with 'replace' mode. This eliminates code duplication while maintaining full control over Athanor's model configurations. ([275e792](https://github.com/lacerbi/athanor/commit/275e792)) + - Removed local `AthanorModelPreset` type and `athanorPresetService` + - Added IPC channel for fetching presets from the LLM service + - Updated UI components to use genai-lite's `ModelPreset` type + - Configuration now centralized through `src/config/athanorModelPresets.json` + +### Fixed + +- **Build Configuration**: Added JSON module resolution support to TypeScript and webpack configurations to properly handle preset configuration imports. + +### Dependencies + +- Updated `genai-lite` from v0.1.1 to v0.1.3 + ## [0.7.11] - 2025-07-04 ### Added diff --git a/package-lock.json b/package-lock.json index ff0eb2d..e362462 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "athanor", - "version": "0.7.11", + "version": "0.7.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "athanor", - "version": "0.7.11", + "version": "0.7.12", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/sdk": "^0.52.0", @@ -20,7 +20,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.3", + "genai-lite": "^0.1.4", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", @@ -9450,9 +9450,9 @@ } }, "node_modules/genai-lite": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.3.tgz", - "integrity": "sha512-0qxuXb1QtF+ANoYDinjRAkxDlHJqwiVBC9Wu1QE2DqTSyGoCeEO3P3GD+4zItaX85Xlvly05YNXia6oLGXOo6w==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/genai-lite/-/genai-lite-0.1.4.tgz", + "integrity": "sha512-gsgwBxhzsmgxOFa/nGmBBwZMV70THC//fvj4OEKaeUDHhf5kxkIdl73hA8jUqjg8LqRZAhvZnixNqT6m4Eo3gw==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.56.0", diff --git a/package.json b/package.json index 5928c14..bdd15f6 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "diff-match-patch": "^1.0.5", "fix-path": "^4.0.0", "genai-key-storage-lite": "^0.1.4", - "genai-lite": "^0.1.3", + "genai-lite": "^0.1.4", "ignore": "^7.0.0", "js-tiktoken": "^1.0.16", "lucide-react": "^0.469.0", diff --git a/src/utils/.summary_long.md b/src/utils/.summary_long.md index ecc69e1..45a6fd5 100644 --- a/src/utils/.summary_long.md +++ b/src/utils/.summary_long.md @@ -12,19 +12,18 @@ The utils directory contains the foundational utility functions that power Athan The main orchestrator for AI prompt generation: - **Dynamic Prompt Building**: Core function `buildDynamicPrompt` assembles complete prompts from templates - **Variable Management**: Handles 20+ template variables including project info, file contents, and task descriptions +- **Template Rendering**: Uses `renderTemplate` from genai-lite for variable substitution with conditional logic - **Configuration Integration**: Respects user settings for file tree inclusion, format type, and smart previews - **Supplementary Materials**: Separates regular files from materials using "materials:" prefix - **Format Flexibility**: Supports both XML and Markdown documentation formats - **Smart Preview Config**: Configurable preview lines (min/max) with fallback to constants #### promptTemplates.ts -Template engine with advanced variable substitution: +Template loading and task extraction utilities: - **Template Loading**: Async loading from resources directory via Electron IPC -- **Variable Substitution**: `{{variable}}` syntax with conditional ternary support -- **Conditional Logic**: `{{condition ? `true_value` : `false_value`}}` with backtick delimiters -- **Backward Compatibility**: Maintains support for legacy quote-based syntax - **Task Extraction**: Extracts task descriptions from XML tags in templates -- **Empty Value Handling**: Special handling for task_context to avoid empty sections +- **Error Handling**: Proper error propagation with descriptive messages +- **Path Resolution**: Uses main process for secure template path resolution #### codebaseDocumentation.ts Generates structured documentation for AI consumption: diff --git a/src/utils/.summary_short.md b/src/utils/.summary_short.md index 8aea1f8..2122785 100644 --- a/src/utils/.summary_short.md +++ b/src/utils/.summary_short.md @@ -4,7 +4,7 @@ Core utility functions for prompt building, file operations, and UI configuratio ## Files -- **buildPrompt.ts** - Builds AI prompts dynamically by loading templates, substituting variables, and applying user settings +- **buildPrompt.ts** - Builds AI prompts dynamically by loading templates, substituting variables via genai-lite, and applying user settings - **codebaseDocumentation.ts** - Generates formatted codebase documentation including file trees and code content with smart previews - **configUtils.ts** - Handles reading and resolving Athanor configuration with proper precedence for project settings - **constants.ts** - Stores configuration constants for file handling, UI animations, and application settings @@ -17,5 +17,5 @@ Core utility functions for prompt building, file operations, and UI configuratio - **fileTree.ts** - Core data structures and traversal utilities for the hierarchical file tree UI - **languageMapping.ts** - Maps file extensions to programming languages for syntax highlighting - **projectInfoUtils.ts** - Finds and reads project information files with case-insensitive search and content normalization -- **promptTemplates.ts** - Loads prompt templates and performs variable substitution with conditional logic support +- **promptTemplates.ts** - Loads prompt templates and extracts task descriptions from XML tags - **tokenCount.ts** - Counts tokens using js-tiktoken with efficient encoder caching and formatting utilities \ No newline at end of file diff --git a/src/utils/buildPrompt.ts b/src/utils/buildPrompt.ts index 48a36da..b7f6545 100644 --- a/src/utils/buildPrompt.ts +++ b/src/utils/buildPrompt.ts @@ -6,9 +6,9 @@ import { generateCodebaseDocumentation } from './codebaseDocumentation'; import { DOC_FORMAT, FILE_SYSTEM, SETTINGS } from './constants'; import { loadTemplateContent, - substituteVariables, extractTaskDescription, } from './promptTemplates'; +import { renderTemplate } from 'genai-lite'; import { PromptData, PromptVariant } from '../types/promptTypes'; import { useFileSystemStore } from '../stores/fileSystemStore'; import { AthanorConfig } from '../types/global'; @@ -230,5 +230,5 @@ export async function buildDynamicPrompt( }; // Use the variant content directly - return substituteVariables(variant.content, variables); + return renderTemplate(variant.content, variables); } diff --git a/src/utils/promptTemplates.ts b/src/utils/promptTemplates.ts index b67b558..a1f1127 100644 --- a/src/utils/promptTemplates.ts +++ b/src/utils/promptTemplates.ts @@ -1,7 +1,6 @@ -// AI Summary: Handles loading prompt templates and substituting variables. +// AI Summary: Handles loading prompt templates. // Uses template paths resolved by the main process and properly handles empty values. -// Provides functions for template loading, variable substitution, and task description extraction. -import { PromptVariables } from './buildPrompt'; +// Provides functions for template loading and task description extraction. import { extractTagContent } from './extractTagContent'; // Load a template from the prompts folder export async function loadTemplateContent( @@ -23,78 +22,3 @@ export async function loadTemplateContent( export function extractTaskDescription(templateContent: string): string { return extractTagContent(templateContent, 'task_description'); } -// Substitute variables in template content -export function substituteVariables( - template: string, - variables: PromptVariables -): string { - return template.replace(/(\n?)\{\{([\s\S]+?)\}\}(\n?)/g, (match, leadingNewline: string, expression: string, trailingNewline: string) => { - const trimmedExpression = expression.trim(); - const conditionalMarkerIndex = trimmedExpression.indexOf('?'); - - let result: string; - - if (conditionalMarkerIndex === -1) { - // --- Simple variable substitution (backward compatible) --- - const key = trimmedExpression as keyof PromptVariables; - const value = variables[key]; - // Handle task context specially - only include if it exists - if (key === 'task_context' && (!value || (typeof value === 'string' && value.trim() === ''))) { - result = ''; - } else { - result = value !== undefined ? String(value) : ''; - } - } else { - // --- Conditional 'ternary' substitution --- - const conditionKey = trimmedExpression.substring(0, conditionalMarkerIndex).trim() as keyof PromptVariables; - const rest = trimmedExpression.substring(conditionalMarkerIndex + 1).trim(); - - // Parse ternary expression with backtick-delimited strings - // Expected format: condition ? `true_value` : `false_value` or condition ? `true_value` - const ternaryMatch = rest.match(/^\s*`([\s\S]*?)`(?:\s*:\s*`([\s\S]*?)`)?\s*$/); - - let trueText: string; - let falseText: string = ''; // Default to empty string if no 'else' part - - if (ternaryMatch) { - // Backtick format matched - unescape any escaped backticks - trueText = ternaryMatch[1].replace(/\\`/g, '`'); - if (ternaryMatch[2] !== undefined) { - falseText = ternaryMatch[2].replace(/\\`/g, '`'); - } - } else { - // Fallback to old quote-based parsing for backward compatibility - const elseMarkerIndex = rest.indexOf(':'); - - if (elseMarkerIndex === -1) { - trueText = rest; - } else { - trueText = rest.substring(0, elseMarkerIndex).trim(); - falseText = rest.substring(elseMarkerIndex + 1).trim(); - } - - // Remove quotes from the start and end of the text parts - const unquote = (text: string) => { - if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) { - return text.slice(1, -1); - } - return text; - }; - - trueText = unquote(trueText); - falseText = unquote(falseText); - } - - const conditionValue = !!variables[conditionKey]; // Evaluate truthiness - result = conditionValue ? trueText : falseText; - } - - // If result is empty, don't include the captured newlines - if (result === '') { - return ''; - } - - // If result is not empty, preserve the captured newlines - return leadingNewline + result + trailingNewline; - }); -} From b1a53c6d8c9a0f1956a9fb407f06da721f64a954 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 20:25:12 +0200 Subject: [PATCH 6/8] docs: update CHANGELOG for template rendering migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add entry for the migration to genai-lite's renderTemplate function in v0.7.12 release notes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e66c1..5f90c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,13 +19,18 @@ _(Future changes will go here)_ - Updated UI components to use genai-lite's `ModelPreset` type - Configuration now centralized through `src/config/athanorModelPresets.json` +- **Migrated Template Rendering to genai-lite**: Refactored to use genai-lite's `renderTemplate` function for variable substitution in prompts. ([ac7d986](https://github.com/lacerbi/athanor/commit/ac7d986)) + - Removed local `substituteVariables` function from `promptTemplates.ts` + - Updated `buildPrompt.ts` to use `renderTemplate` from genai-lite + - Template rendering now provided by the shared library + ### Fixed - **Build Configuration**: Added JSON module resolution support to TypeScript and webpack configurations to properly handle preset configuration imports. ### Dependencies -- Updated `genai-lite` from v0.1.1 to v0.1.3 +- Updated `genai-lite` from v0.1.1 to v0.1.4 ## [0.7.11] - 2025-07-04 From 66386c95de3f84035d0f20ae62c0906c680c7d3f Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 20:39:22 +0200 Subject: [PATCH 7/8] fix: correct genai-lite import path for webpack compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the standard genai-lite/utils import path with @ts-ignore to match the pattern already used in main process files. This resolves the blank screen issue caused by webpack module resolution errors. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- src/utils/buildPrompt.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/buildPrompt.ts b/src/utils/buildPrompt.ts index b7f6545..1887c46 100644 --- a/src/utils/buildPrompt.ts +++ b/src/utils/buildPrompt.ts @@ -8,7 +8,8 @@ import { loadTemplateContent, extractTaskDescription, } from './promptTemplates'; -import { renderTemplate } from 'genai-lite'; +// @ts-ignore - webpack module resolution issue +import { renderTemplate } from 'genai-lite/utils'; import { PromptData, PromptVariant } from '../types/promptTypes'; import { useFileSystemStore } from '../stores/fileSystemStore'; import { AthanorConfig } from '../types/global'; From c1eb07dc2fd4f1bfc94d83cb7920baac8dbdd438 Mon Sep 17 00:00:00 2001 From: lacerbi Date: Sat, 5 Jul 2025 20:41:11 +0200 Subject: [PATCH 8/8] docs: add guidance for external package imports in renderer process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add important notes about handling genai-lite and similar package imports in the renderer process to prevent webpack module resolution errors. This documents the solution to the blank screen issue and will help prevent similar issues in the future. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Signed-off-by: lacerbi --- CLAUDE.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 596c2af..4ef771f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -182,6 +182,15 @@ Athanor is an Electron desktop application for AI-assisted development workflows - Conventional Commits for commit messages - **DCO Sign-off Required**: All commits must be signed with DCO. Always use `git commit -s` when committing. If git config is not set (check with `git config user.name` and `git config user.email`), inform the user that these need to be configured before committing. For fixing unsigned commits, use `git rebase --signoff` or `git commit --amend -s` +**Important Notes on External Package Imports:** + +When importing from `genai-lite` or similar external packages in the renderer process (`src/` directory): +- Use the package's exported paths as defined in their `package.json` exports field (e.g., `genai-lite/utils`) +- Add `// @ts-ignore - webpack module resolution issue` if TypeScript complains +- Do NOT use deep imports like `genai-lite/dist/utils/templateEngine` as webpack will reject these +- Check existing imports in the codebase for the correct pattern (e.g., see `RelevanceEngineService.ts`) +- Main process imports (`electron/` directory) typically work without issues + ## Key Dependencies - **Electron 33+** - Desktop app framework