UNPKG

claude-flow-tbowman01

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

313 lines (275 loc) 9.2 kB
/** * Prompt Defaults System for Non-Interactive Mode * * This module provides a system for supplying default values * to prompts when running in non-interactive mode. */ import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { homedir } from 'node:os'; export interface PromptDefault { id: string; type: 'text' | 'confirm' | 'select' | 'multiselect' | 'number'; pattern?: RegExp | string; defaultValue: any; description?: string; } export interface PromptDefaultsConfig { global?: PromptDefault[]; command?: { [command: string]: PromptDefault[]; }; environment?: { [env: string]: PromptDefault[]; }; } export class PromptDefaultsManager { private config: PromptDefaultsConfig = {}; private configPath: string; private environmentDefaults: Map<string, any> = new Map(); constructor(configPath?: string) { this.configPath = configPath || join(homedir(), '.claude-flow', 'prompt-defaults.json'); this.loadConfig(); this.loadEnvironmentDefaults(); } /** * Load configuration from file */ private loadConfig(): void { try { if (existsSync(this.configPath)) { const content = readFileSync(this.configPath, 'utf-8'); this.config = JSON.parse(content); } } catch (error) { // Silently fail, use empty config this.config = {}; } } /** * Save configuration to file */ private saveConfig(): void { try { const dir = join(this.configPath, '..'); if (!existsSync(dir)) { require('fs').mkdirSync(dir, { recursive: true }); } writeFileSync(this.configPath, JSON.stringify(this.config, null, 2)); } catch (error) { // Silently fail } } /** * Load defaults from environment variables */ private loadEnvironmentDefaults(): void { const env = process.env; // Common defaults from environment if (env.CLAUDE_AUTO_APPROVE === '1' || env.CLAUDE_AUTO_APPROVE === 'true') { this.environmentDefaults.set('confirm:*', true); } if (env.CLAUDE_DEFAULT_MODEL) { this.environmentDefaults.set('select:model', env.CLAUDE_DEFAULT_MODEL); } if (env.CLAUDE_DEFAULT_REGION) { this.environmentDefaults.set('select:region', env.CLAUDE_DEFAULT_REGION); } // Parse CLAUDE_PROMPT_DEFAULTS if set if (env.CLAUDE_PROMPT_DEFAULTS) { try { const defaults = JSON.parse(env.CLAUDE_PROMPT_DEFAULTS); Object.entries(defaults).forEach(([key, value]) => { this.environmentDefaults.set(key, value); }); } catch (error) { // Invalid JSON, ignore } } } /** * Get default value for a prompt */ getDefault(promptId: string, command?: string, promptType?: string): any { // Check environment defaults first (highest priority) const envKey = `${promptType || 'text'}:${promptId}`; if (this.environmentDefaults.has(envKey)) { return this.environmentDefaults.get(envKey); } // Check wildcard environment defaults const wildcardKey = `${promptType || 'text'}:*`; if (this.environmentDefaults.has(wildcardKey)) { return this.environmentDefaults.get(wildcardKey); } // Check command-specific defaults if (command && this.config.command?.[command]) { const commandDefault = this.config.command[command].find( (d) => d.id === promptId || (d.pattern && this.matchPattern(promptId, d.pattern)), ); if (commandDefault) { return commandDefault.defaultValue; } } // Check environment-specific defaults const currentEnv = process.env.NODE_ENV || 'development'; if (this.config.environment?.[currentEnv]) { const envDefault = this.config.environment[currentEnv].find( (d) => d.id === promptId || (d.pattern && this.matchPattern(promptId, d.pattern)), ); if (envDefault) { return envDefault.defaultValue; } } // Check global defaults if (this.config.global) { const globalDefault = this.config.global.find( (d) => d.id === promptId || (d.pattern && this.matchPattern(promptId, d.pattern)), ); if (globalDefault) { return globalDefault.defaultValue; } } // Return undefined if no default found return undefined; } /** * Set a default value */ setDefault( promptId: string, defaultValue: any, options: { command?: string; type?: string; pattern?: string | RegExp; description?: string; scope?: 'global' | 'command' | 'environment'; } = {}, ): void { const defaultEntry: PromptDefault = { id: promptId, type: (options.type as any) || 'text', defaultValue, description: options.description, pattern: options.pattern, }; const scope = options.scope || 'global'; if (scope === 'command' && options.command) { if (!this.config.command) { this.config.command = {}; } if (!this.config.command[options.command]) { this.config.command[options.command] = []; } this.config.command[options.command].push(defaultEntry); } else if (scope === 'environment') { const currentEnv = process.env.NODE_ENV || 'development'; if (!this.config.environment) { this.config.environment = {}; } if (!this.config.environment[currentEnv]) { this.config.environment[currentEnv] = []; } this.config.environment[currentEnv].push(defaultEntry); } else { if (!this.config.global) { this.config.global = []; } this.config.global.push(defaultEntry); } this.saveConfig(); } /** * Get common defaults for non-interactive mode */ getNonInteractiveDefaults(): Record<string, any> { return { // Confirmation prompts 'confirm:continue': true, 'confirm:overwrite': true, 'confirm:delete': false, // Safety: don't auto-confirm deletes 'confirm:deploy': false, // Safety: don't auto-confirm deploys // Selection prompts 'select:model': 'claude-3-opus-20240229', 'select:region': 'us-east-1', 'select:topology': 'hierarchical', 'select:strategy': 'auto', // Text prompts 'text:projectName': 'claude-flow-project', 'text:description': 'Claude Flow AI Project', // Number prompts 'number:maxAgents': 4, 'number:timeout': 30000, 'number:port': 3000, }; } /** * Apply non-interactive defaults if needed */ applyNonInteractiveDefaults(isNonInteractive: boolean): void { if (!isNonInteractive) return; const defaults = this.getNonInteractiveDefaults(); Object.entries(defaults).forEach(([key, value]) => { if (!this.environmentDefaults.has(key)) { this.environmentDefaults.set(key, value); } }); } /** * Match a pattern against a prompt ID */ private matchPattern(promptId: string, pattern: string | RegExp): boolean { if (typeof pattern === 'string') { // Simple wildcard matching const regex = new RegExp(pattern.replace(/\*/g, '.*')); return regex.test(promptId); } else { return pattern.test(promptId); } } /** * Export current configuration */ exportConfig(): PromptDefaultsConfig { return JSON.parse(JSON.stringify(this.config)); } /** * Import configuration */ importConfig(config: PromptDefaultsConfig): void { this.config = JSON.parse(JSON.stringify(config)); this.saveConfig(); } /** * Clear all defaults */ clearDefaults(scope?: 'global' | 'command' | 'environment', target?: string): void { if (scope === 'command' && target && this.config.command) { delete this.config.command[target]; } else if (scope === 'environment' && target && this.config.environment) { delete this.config.environment[target]; } else if (scope === 'global' || !scope) { this.config.global = []; } this.saveConfig(); } } // Singleton instance let instance: PromptDefaultsManager | null = null; export function getPromptDefaultsManager(configPath?: string): PromptDefaultsManager { if (!instance) { instance = new PromptDefaultsManager(configPath); } return instance; } // Convenience function for getting defaults export function getPromptDefault(promptId: string, command?: string, promptType?: string): any { return getPromptDefaultsManager().getDefault(promptId, command, promptType); } // Apply non-interactive defaults based on environment export function applyNonInteractiveDefaults(flags: any): void { const manager = getPromptDefaultsManager(); const isNonInteractive = flags.nonInteractive || flags['non-interactive'] || flags.ci || !process.stdout.isTTY; manager.applyNonInteractiveDefaults(isNonInteractive); }