UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

143 lines 5.29 kB
import { existsSync, readFileSync } from 'fs'; import { homedir, platform, release } from 'os'; import { join } from 'path'; import { promptPath } from '../config/index.js'; import { PlaceholderType } from '../types/hooks.js'; import { getLogger } from './logging/index.js'; /** * Get the default shell for the current platform */ function getDefaultShell() { const shellEnv = process.env.SHELL; if (shellEnv) { return shellEnv; } switch (platform()) { case 'win32': return process.env.COMSPEC || 'cmd.exe'; case 'darwin': return '/bin/zsh'; default: return '/bin/bash'; } } /** * Get a human-readable OS name */ function getOSName() { const plat = platform(); switch (plat) { case 'darwin': return 'macOS'; case 'win32': return 'Windows'; case 'linux': return 'Linux'; default: return plat; } } /** * Generate system information string */ function generateSystemInfo() { const now = new Date(); const dateStr = now.toISOString().split('T')[0]; // YYYY-MM-DD return `Operating System: ${getOSName()} OS Version: ${release()} Platform: ${platform()} Default Shell: ${getDefaultShell()} Home Directory: ${homedir()} Current Working Directory: ${process.cwd()} Current Date: ${dateStr}`; } /** * Inject system information into the prompt template */ function injectSystemInfo(prompt) { const systemInfo = generateSystemInfo(); return prompt.replace(/<!-- DYNAMIC_SYSTEM_INFO_START -->[\s\S]*?<!-- DYNAMIC_SYSTEM_INFO_END -->/, systemInfo); } /** * Process the main prompt template by injecting system info */ export function processPromptTemplate() { let systemPrompt = 'You are a helpful AI assistant.'; // fallback // Load base prompt if (existsSync(promptPath)) { try { systemPrompt = readFileSync(promptPath, 'utf-8'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const logger = getLogger(); logger.warn(`Failed to load system prompt from ${promptPath}: ${errorMessage}`); } } // Inject system information systemPrompt = injectSystemInfo(systemPrompt); // Check for AGENTS.md in current working directory and append it const agentsPath = join(process.cwd(), 'AGENTS.md'); if (existsSync(agentsPath)) { try { const agentsContent = readFileSync(agentsPath, 'utf-8'); systemPrompt += `\n\nAdditional Context...\n\n${agentsContent}`; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const logger = getLogger(); logger.warn(`Failed to load AGENTS.md from ${agentsPath}: ${errorMessage}`); } } return systemPrompt; } /** * Assemble the final prompt by replacing all placeholders with their full content * This function is called before sending the prompt to the AI */ export function assemblePrompt(inputState) { let assembledPrompt = inputState.displayValue; // Replace each placeholder with its full content Object.entries(inputState.placeholderContent).forEach(([placeholderId, placeholderContent]) => { // Each placeholder type can have its own replacement logic let replacementContent = placeholderContent.content || ''; // Type-specific content assembly (extensible for future types) switch (placeholderContent.type) { case PlaceholderType.PASTE: { // For paste, use content directly replacementContent = placeholderContent.content; break; } case PlaceholderType.FILE: { // Format file content with header for LLM context const fileName = placeholderContent.filePath.split('/').pop() || placeholderContent.filePath; const header = `=== File: ${fileName} ===`; const footer = '='.repeat(header.length); replacementContent = `${header}\n${placeholderContent.content}\n${footer}`; break; } default: { // TypeScript will ensure this is unreachable with proper enum usage // Exhaustiveness check to ensure all enum cases are handled placeholderContent; // Fallback for safety, though this should never be reached replacementContent = ''; break; } } // Use the displayText to find and replace the placeholder const displayText = placeholderContent.displayText; if (displayText) { assembledPrompt = assembledPrompt.replace(displayText, replacementContent); } else { // Fallback for legacy paste format const placeholderPattern = `\\[Paste #${placeholderId}: \\d+ chars\\]`; const regex = new RegExp(placeholderPattern, 'g'); assembledPrompt = assembledPrompt.replace(regex, replacementContent); } }); return assembledPrompt; } //# sourceMappingURL=prompt-processor.js.map