claude-flow-tbowman01
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
313 lines (275 loc) • 9.2 kB
text/typescript
/**
* 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);
}