aiwf
Version:
AI Workflow Framework for Claude Code with multi-language support (Korean/English)
1,020 lines (890 loc) • 34.8 kB
JavaScript
import { program } from 'commander';
import chalk from 'chalk';
import { installAIWF } from '../lib/installer.js';
import AIToolCommand from '../commands/ai-tool.js';
import PersonaCommand from '../commands/persona.js';
import { CacheCLI } from './cache-cli.js';
import { addTaskToSprint } from '../commands/sprint-task.js';
import CompressCommand from '../commands/compress.js';
import TokenCommand from '../commands/token.js';
import EvaluateCommand from '../commands/evaluate.js';
import { createProject } from '../commands/create-project.js';
import StateCommand from '../commands/state.js';
import { createIndependentSprint } from '../commands/sprint-independent.js';
import { CheckpointManager } from '../utils/checkpoint-manager.js';
import { createYoloConfig, createInteractiveYoloConfig, showYoloConfig } from '../commands/yolo-config.js';
// Parse version from package.json
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'));
// Main program setup
program
.name('aiwf')
.version(packageJson.version)
.description('AIWF - AI Workflow Framework');
// Install command
program
.command('install')
.description('Install AIWF framework')
.option('-f, --force', 'Force complete reinstall without prompts')
.action((options) => installAIWF({ ...options, debugLog: true }));
// AI Tool command
program
.command('ai-tool <subcommand> [tool-name]')
.description('Manage AI tool integrations')
.action(async (subcommand, toolName) => {
const aiToolCmd = new AIToolCommand();
const args = [subcommand];
if (toolName) args.push(toolName);
await aiToolCmd.execute(args);
});
// Cache management commands
const cache = program.command('cache');
cache.description('Manage offline template cache');
cache
.command('download')
.description('Download templates for offline use')
.option('--all', 'Download all templates')
.option('--type <type>', 'Download specific type (ai-tools, projects)')
.action(async (options) => {
const cacheCLI = new CacheCLI();
try {
await cacheCLI.init();
await cacheCLI.downloadCommand(options);
} catch (error) {
console.error(chalk.red('Cache download failed:', error.message));
process.exit(1);
} finally {
await cacheCLI.cleanup();
}
});
cache
.command('list')
.description('List cached templates')
.option('--type <type>', 'Filter by type (ai-tools, projects)')
.action(async (options) => {
const cacheCLI = new CacheCLI();
try {
await cacheCLI.init();
await cacheCLI.listCommand(options);
} catch (error) {
console.error(chalk.red('Cache list failed:', error.message));
process.exit(1);
} finally {
await cacheCLI.cleanup();
}
});
cache
.command('clean')
.description('Clean up cached templates')
.option('--all', 'Clear all cached templates')
.option('--max-age <days>', 'Maximum age in days (default: 7)', '7')
.action(async (options) => {
const cacheCLI = new CacheCLI();
try {
await cacheCLI.init();
await cacheCLI.cleanCommand(options);
} catch (error) {
console.error(chalk.red('Cache clean failed:', error.message));
process.exit(1);
} finally {
await cacheCLI.cleanup();
}
});
cache
.command('update')
.description('Check for template updates')
.option('--install', 'Install available updates')
.action(async (options) => {
const cacheCLI = new CacheCLI();
try {
await cacheCLI.init();
await cacheCLI.updateCommand(options);
} catch (error) {
console.error(chalk.red('Cache update failed:', error.message));
process.exit(1);
} finally {
await cacheCLI.cleanup();
}
});
cache
.command('status')
.description('Show cache system status')
.action(async () => {
const cacheCLI = new CacheCLI();
try {
await cacheCLI.init();
await cacheCLI.statusCommand();
} catch (error) {
console.error(chalk.red('Cache status failed:', error.message));
process.exit(1);
} finally {
await cacheCLI.cleanup();
}
});
// Language management commands
const lang = program.command('lang');
lang.description('Manage language settings / 언어 설정 관리');
lang
.command('status')
.alias('s')
.description('Show current language configuration / 현재 언어 설정 표시')
.action(async () => {
const { showLanguageStatus } = await import('./language-cli.js');
await showLanguageStatus();
});
lang
.command('set [language]')
.alias('switch')
.description('Set or change language / 언어 설정 또는 변경')
.option('-a, --auto-detect [boolean]', 'Enable/disable auto detection / 자동 감지 활성화/비활성화')
.action(async (language, options) => {
const { setLanguage } = await import('./language-cli.js');
await setLanguage(language, options);
});
lang
.command('reset')
.alias('r')
.description('Reset language configuration to auto-detection / 언어 설정을 자동 감지로 초기화')
.action(async () => {
const { resetLanguage } = await import('./language-cli.js');
await resetLanguage();
});
// Persona management commands
const persona = program.command('persona');
persona.description('Manage AI personas / AI 페르소나 관리');
persona
.command('status')
.alias('s')
.description('Show current persona status / 현재 페르소나 상태 표시')
.action(async () => {
const personaCmd = new PersonaCommand();
await personaCmd.showStatus();
});
persona
.command('list')
.alias('ls')
.description('List available personas / 사용 가능한 페르소나 목록')
.action(async () => {
const personaCmd = new PersonaCommand();
await personaCmd.listPersonas();
});
persona
.command('set <persona-name>')
.alias('switch')
.description('Switch to specific persona / 특정 페르소나로 전환')
.action(async (personaName) => {
const personaCmd = new PersonaCommand();
await personaCmd.setPersona(personaName);
});
persona
.command('reset')
.alias('r')
.description('Reset to default persona / 기본 페르소나로 리셋')
.action(async () => {
const personaCmd = new PersonaCommand();
await personaCmd.resetPersona();
});
// Sprint task management command
program
.command('sprint-task <sprintId> <taskTitle>')
.alias('st')
.description('Add a task to an existing sprint / 스프린트에 태스크 추가')
.action(async (sprintId, taskTitle) => {
try {
await addTaskToSprint(sprintId, taskTitle);
} catch (error) {
console.error(chalk.red(`오류: ${error.message}`));
process.exit(1);
}
});
// Create project command
program
.command('create-project')
.alias('create')
.description('Create a new AIWF project / 새 AIWF 프로젝트 생성')
.action(async () => {
try {
await createProject();
} catch (error) {
console.error(chalk.red(`오류: ${error.message}`));
process.exit(1);
}
});
// Context compression command
program
.command('compress [mode] [path]')
.description('Compress context for token optimization / 토큰 최적화를 위한 컨텍스트 압축')
.option('--persona, -p', 'Enable persona-aware compression / 페르소나 인식 압축 활성화')
.action(async (mode, path, options) => {
const compressCmd = new CompressCommand();
const args = [];
if (mode) args.push(mode);
if (path) args.push(path);
if (options.persona) args.push('--persona');
await compressCmd.execute(args);
});
// Token tracking commands
const token = program.command('token');
token.description('Manage token usage tracking / 토큰 사용량 추적 관리');
token
.command('status')
.alias('s')
.description('Show token usage status / 토큰 사용 현황 표시')
.action(async () => {
const tokenCmd = new TokenCommand();
await tokenCmd.execute(['status']);
});
token
.command('report [period]')
.description('Generate usage report / 사용 리포트 생성')
.action(async (period) => {
const tokenCmd = new TokenCommand();
const args = ['report'];
if (period) args.push(period);
await tokenCmd.execute(args);
});
token
.command('track <input> <output>')
.description('Manually track token usage / 토큰 사용량 수동 기록')
.action(async (input, output) => {
const tokenCmd = new TokenCommand();
await tokenCmd.execute(['track', input, output]);
});
token
.command('limit <type> <value>')
.description('Set usage limits / 사용 한도 설정')
.action(async (type, value) => {
const tokenCmd = new TokenCommand();
await tokenCmd.execute(['limit', type, value]);
});
token
.command('reset')
.description('Reset tracking data / 추적 데이터 초기화')
.action(async () => {
const tokenCmd = new TokenCommand();
await tokenCmd.execute(['reset']);
});
// Evaluation commands
const evaluate = program.command('evaluate');
evaluate.description('Evaluate AI responses and code quality / AI 응답 및 코드 품질 평가');
evaluate
.command('response <file>')
.description('Evaluate AI response quality / AI 응답 품질 평가')
.action(async (file) => {
const evaluateCmd = new EvaluateCommand();
await evaluateCmd.execute(['response', file]);
});
evaluate
.command('code <file>')
.description('Evaluate code quality / 코드 품질 평가')
.action(async (file) => {
const evaluateCmd = new EvaluateCommand();
await evaluateCmd.execute(['code', file]);
});
evaluate
.command('persona <file> <persona>')
.description('Evaluate persona appropriateness / 페르소나 적합성 평가')
.action(async (file, persona) => {
const evaluateCmd = new EvaluateCommand();
await evaluateCmd.execute(['persona', file, persona]);
});
evaluate
.command('report')
.description('Generate evaluation report / 평가 리포트 생성')
.action(async () => {
const evaluateCmd = new EvaluateCommand();
await evaluateCmd.execute(['report']);
});
evaluate
.command('criteria')
.description('Show evaluation criteria / 평가 기준 표시')
.action(async () => {
const evaluateCmd = new EvaluateCommand();
await evaluateCmd.execute(['criteria']);
});
// State management commands
const state = program.command('state');
state.description('Manage task state index / 태스크 상태 인덱스 관리');
state
.command('init')
.description('Initialize state index / 상태 인덱스 초기화')
.action(async () => {
const stateCmd = new StateCommand();
await stateCmd.execute(['init']);
});
state
.command('update')
.alias('u')
.description('Update state index / 상태 인덱스 업데이트')
.action(async () => {
const stateCmd = new StateCommand();
await stateCmd.execute(['update']);
});
state
.command('show')
.alias('s')
.description('Show current state / 현재 상태 표시')
.action(async () => {
const stateCmd = new StateCommand();
await stateCmd.execute(['show']);
});
state
.command('focus <task-id>')
.alias('f')
.description('Focus on a task / 태스크에 포커스')
.action(async (taskId) => {
const stateCmd = new StateCommand();
await stateCmd.execute(['focus', taskId]);
});
state
.command('complete <task-id>')
.alias('c')
.description('Mark task as completed / 태스크 완료 처리')
.action(async (taskId) => {
const stateCmd = new StateCommand();
await stateCmd.execute(['complete', taskId]);
});
state
.command('next')
.alias('n')
.description('Suggest next actions / 다음 작업 제안')
.action(async () => {
const stateCmd = new StateCommand();
await stateCmd.execute(['next']);
});
// Sprint management commands (YOLO focused)
const sprint = program.command('sprint');
sprint.description('Independent sprint management / 독립 스프린트 관리');
sprint
.command('independent [name]')
.alias('ind')
.description('Create independent sprint / 독립 스프린트 생성')
.option('--from-readme', 'Extract from README TODOs / README TODO에서 추출')
.option('--from-issue <number>', 'Create from GitHub issue / GitHub 이슈에서 생성')
.option('--minimal', 'Minimal engineering level / 최소 엔지니어링 레벨')
.option('--balanced', 'Balanced engineering level / 균형 엔지니어링 레벨')
.option('--complete', 'Complete engineering level / 완전 엔지니어링 레벨')
.option('--description <desc>', 'Sprint description / 스프린트 설명')
.action(async (name, options) => {
try {
console.log(chalk.blue('🚀 독립 스프린트 생성 중...'));
// 엔지니어링 레벨 결정
let engineeringLevel = 'minimal';
if (options.balanced) engineeringLevel = 'balanced';
else if (options.complete) engineeringLevel = 'complete';
const sprintOptions = {
name,
description: options.description,
engineeringLevel,
fromReadme: options.fromReadme,
fromIssue: options.fromIssue,
minimal: engineeringLevel === 'minimal'
};
const result = await createIndependentSprint(sprintOptions);
if (result.success) {
console.log('');
console.log(chalk.green('✅ 독립 스프린트 생성 완료!'));
console.log(` 스프린트 ID: ${chalk.cyan(result.sprintId)}`);
console.log(` 태스크 수: ${chalk.blue(result.tasks)}개`);
console.log('');
console.log(chalk.bold('🚀 다음 단계:'));
console.log(` Claude Code에서 ${chalk.cyan(`/project:aiwf:yolo ${result.sprintId}`)} 실행`);
}
} catch (error) {
console.error(chalk.red('❌ 독립 스프린트 생성 실패:'), error.message);
process.exit(1);
}
});
// YOLO configuration commands
const yoloConfig = program.command('yolo-config');
yoloConfig.description('YOLO configuration management / YOLO 설정 관리');
yoloConfig
.command('init')
.description('Initialize YOLO configuration / YOLO 설정 초기화')
.option('-f, --force', 'Force overwrite existing config / 기존 설정 덮어쓰기')
.action(async (options) => {
try {
const result = await createYoloConfig(options);
if (result.success) {
console.log(chalk.green('✅ YOLO 설정이 초기화되었습니다!'));
console.log(`📁 위치: ${chalk.cyan(result.configPath)}`);
} else if (result.skipped) {
console.log(chalk.yellow('⏭️ 설정 파일이 이미 존재합니다.'));
}
} catch (error) {
console.error(chalk.red('❌ YOLO 설정 초기화 실패:'), error.message);
process.exit(1);
}
});
yoloConfig
.command('wizard')
.alias('interactive')
.description('Interactive YOLO configuration wizard / 대화형 YOLO 설정 마법사')
.action(async () => {
try {
const result = await createInteractiveYoloConfig();
if (!result.success && result.cancelled) {
process.exit(0);
}
} catch (error) {
console.error(chalk.red('❌ YOLO 설정 마법사 실패:'), error.message);
process.exit(1);
}
});
yoloConfig
.command('show')
.alias('status')
.description('Show current YOLO configuration / 현재 YOLO 설정 확인')
.action(async () => {
try {
await showYoloConfig();
} catch (error) {
console.error(chalk.red('❌ YOLO 설정 확인 실패:'), error.message);
process.exit(1);
}
});
// Checkpoint management commands (YOLO recovery)
const checkpoint = program.command('checkpoint');
checkpoint.description('YOLO checkpoint management / YOLO 체크포인트 관리');
checkpoint
.command('list')
.alias('ls')
.description('List available checkpoints / 사용 가능한 체크포인트 목록')
.option('--limit <n>', 'Limit number of checkpoints / 체크포인트 수 제한', '10')
.action(async (options) => {
try {
// 프로젝트 루트 찾기
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
const checkpoints = await manager.listCheckpoints();
console.log(chalk.bold('📊 체크포인트 목록:'));
console.log('');
if (checkpoints.length === 0) {
console.log(chalk.yellow('📭 체크포인트가 없습니다.'));
return;
}
const limit = parseInt(options.limit);
const limitedCheckpoints = checkpoints.slice(0, limit);
for (const cp of limitedCheckpoints) {
const typeIcon = cp.type === 'session_start' ? '🚀' :
cp.type === 'task_complete' ? '✅' : '🔄';
console.log(`${typeIcon} ${chalk.cyan(cp.id)} - ${chalk.yellow(cp.type)}`);
console.log(` 태스크: ${chalk.blue(cp.tasks_completed)}개 완료`);
console.log('');
}
} catch (error) {
console.error(chalk.red('❌ 체크포인트 목록 조회 실패:'), error.message);
process.exit(1);
}
});
checkpoint
.command('add [type]')
.alias('create')
.description('Create a checkpoint / 체크포인트 생성')
.option('-m, --message <text>', 'Description for the checkpoint / 체크포인트 설명')
.option('--meta <json>', 'Additional metadata as JSON / 추가 메타데이터(JSON)')
.option('--cleanup <n>', 'Keep only last N checkpoints after creating / 생성 후 최근 N개만 보존')
.action(async (type, options) => {
try {
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
// 프로젝트 루트 탐색 (.aiwf 기준)
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
await manager.initialize();
// 메타데이터 구성
const metadata = {};
if (options.message) metadata.message = options.message;
if (options.meta) {
try {
const parsed = JSON.parse(options.meta);
Object.assign(metadata, parsed);
} catch (e) {
console.error(chalk.red('❌ --meta 값이 올바른 JSON 형식이 아닙니다.'));
process.exit(1);
}
}
const cpType = type || 'manual';
const checkpointId = await manager.createCheckpoint(cpType, metadata);
console.log(chalk.green('✅ 체크포인트 생성 완료!'));
console.log(` ID: ${chalk.cyan(checkpointId)} 유형: ${chalk.yellow(cpType)}`);
if (metadata.message) {
console.log(` 메시지: ${chalk.blue(metadata.message)}`);
}
if (options.cleanup) {
const keep = parseInt(options.cleanup, 10);
if (!Number.isNaN(keep) && keep > 0) {
await manager.cleanup(keep);
console.log(`🧹 최근 ${chalk.cyan(keep)}개만 보존하도록 정리했습니다.`);
} else {
console.log(chalk.yellow('⚠️ --cleanup 값이 유효하지 않아 정리를 건너뜁니다. 정수로 입력하세요.'));
}
}
} catch (error) {
console.error(chalk.red('❌ 체크포인트 생성 실패:'), error.message);
process.exit(1);
}
});
checkpoint
.command('restore <checkpointId>')
.description('Restore from checkpoint / 체크포인트에서 복구')
.action(async (checkpointId) => {
try {
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
await manager.initialize();
console.log(chalk.blue(`🔄 체크포인트 ${checkpointId}에서 복구 중...`));
const result = await manager.restoreFromCheckpoint(checkpointId);
if (result.success) {
console.log(chalk.green('✅ 체크포인트 복구 성공!'));
console.log(` 세션 ID: ${chalk.cyan(result.checkpoint.state_snapshot.session_id)}`);
console.log(` 스프린트: ${chalk.blue(result.checkpoint.state_snapshot.sprint_id || 'N/A')}`);
console.log('');
console.log(chalk.green('🚀 복구 완료! YOLO 모드를 다시 실행할 수 있습니다.'));
}
} catch (error) {
console.error(chalk.red('❌ 체크포인트 복구 실패:'), error.message);
process.exit(1);
}
});
checkpoint
.command('status')
.description('Show current YOLO session status / 현재 YOLO 세션 상태')
.action(async () => {
try {
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
await manager.loadState();
console.log(chalk.bold('📊 YOLO 세션 상태:'));
console.log('');
if (!manager.currentState.session_id) {
console.log(chalk.yellow('📭 활성 YOLO 세션이 없습니다.'));
return;
}
console.log(`세션 ID: ${chalk.cyan(manager.currentState.session_id)}`);
console.log(`스프린트: ${chalk.blue(manager.currentState.sprint_id || 'N/A')}`);
console.log(`완료된 태스크: ${chalk.green(manager.currentState.completed_tasks.length)}개`);
console.log(`체크포인트: ${chalk.blue(manager.currentState.checkpoints.length)}개`);
} catch (error) {
console.error(chalk.red('❌ 상태 확인 실패:'), error.message);
process.exit(1);
}
});
// 체크포인트 리포트 생성
checkpoint
.command('report')
.description('Generate progress report / 진행 상황 리포트 생성')
.action(async () => {
try {
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
await manager.loadState();
console.log(chalk.blue('📊 진행 상황 리포트 생성 중...'));
console.log('');
const report = await manager.generateProgressReport();
console.log(chalk.bold('🎯 YOLO 세션 리포트'));
console.log(''.padEnd(50, '='));
console.log('');
// 세션 정보
console.log(chalk.bold('📋 세션 정보:'));
console.log(` ID: ${chalk.cyan(report.session.id)}`);
console.log(` 시작: ${chalk.gray(new Date(report.session.started).toLocaleString())}`);
console.log(` 스프린트: ${chalk.blue(report.session.sprint || 'N/A')}`);
console.log('');
// 진행 상황
console.log(chalk.bold('📈 진행 상황:'));
console.log(` 완료: ${chalk.green(report.progress.completed)}개`);
console.log(` 실패: ${chalk.red(report.progress.failed)}개`);
console.log(` 건너뜀: ${chalk.yellow(report.progress.skipped)}개`);
console.log('');
// 성능 지표
console.log(chalk.bold('⏱️ 성능 지표:'));
console.log(` 총 시간: ${chalk.blue(report.performance.total_time)}`);
console.log(` 평균 태스크 시간: ${chalk.blue(report.performance.avg_task_time)}`);
console.log(` 성공률: ${chalk.green(report.performance.success_rate)}`);
} catch (error) {
console.error(chalk.red('❌ 리포트 생성 실패:'), error.message);
process.exit(1);
}
});
// 체크포인트 정리
checkpoint
.command('clean')
.description('Clean old checkpoints / 오래된 체크포인트 정리')
.option('--keep <n>', 'Number of checkpoints to keep / 유지할 체크포인트 수', '10')
.option('--dry-run', 'Preview without deletion / 실제 삭제 없이 미리보기')
.action(async (options) => {
try {
const fs = await import('fs/promises');
const path = await import('path');
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
try {
await fs.access(path.join(currentDir, '.aiwf'));
break;
} catch {
currentDir = path.dirname(currentDir);
}
}
const manager = new CheckpointManager(currentDir);
const keepLast = parseInt(options.keep);
console.log(chalk.blue(`🧹 체크포인트 정리 중... (최근 ${keepLast}개 유지)`));
if (options.dryRun) {
console.log(chalk.yellow('🔍 드라이런 모드: 실제 삭제는 수행하지 않습니다.'));
const checkpoints = await manager.listCheckpoints();
if (checkpoints.length <= keepLast) {
console.log(chalk.green('✅ 정리할 체크포인트가 없습니다.'));
} else {
const toDelete = checkpoints.slice(keepLast);
console.log(chalk.yellow(`삭제 예정 체크포인트 (${toDelete.length}개):`));
for (const cp of toDelete) {
console.log(` ${chalk.red('🗑️')} ${chalk.cyan(cp.id)}`);
}
}
} else {
await manager.cleanup(keepLast);
console.log(chalk.green('✅ 체크포인트 정리 완료!'));
}
} catch (error) {
console.error(chalk.red('❌ 체크포인트 정리 실패:'), error.message);
process.exit(1);
}
});
// Engineering Guard commands (오버엔지니어링 방지)
const guard = program.command('guard');
guard.description('Engineering guard for overengineering prevention / 오버엔지니어링 방지 가드');
guard
.command('check [path]')
.description('Check project complexity / 프로젝트 복잡도 검사')
.option('--config <path>', 'YOLO config file path / YOLO 설정 파일 경로', '.aiwf/yolo-config.yaml')
.action(async (targetPath, options) => {
try {
const { EngineeringGuard } = await import('../utils/engineering-guard.js');
const guard = new EngineeringGuard();
// 설정 파일 로드
await guard.loadConfig(options.config);
const checkPath = targetPath || process.cwd();
console.log(chalk.blue(`🔍 복잡도 검사 중: ${checkPath}`));
console.log('');
const report = await guard.checkProject(checkPath);
// 결과 출력
console.log(chalk.bold('📊 복잡도 분석 결과:'));
console.log(` 총 위반사항: ${chalk.yellow(report.summary.total_violations)}`);
console.log(` 높은 심각도: ${chalk.red(report.summary.high_severity)}`);
console.log(` 중간 심각도: ${chalk.yellow(report.summary.medium_severity)}`);
console.log(` 경고사항: ${chalk.blue(report.summary.warnings)}`);
console.log('');
if (report.violations && report.violations.length > 0) {
console.log(chalk.bold('🚨 위반사항:'));
for (const violation of report.violations.slice(0, 10)) {
const icon = violation.severity === 'high' ? '❌' :
violation.severity === 'medium' ? '⚠️' : '💡';
console.log(` ${icon} ${chalk.cyan(violation.type)}: ${violation.file || violation.message}`);
if (violation.details) {
console.log(` ${chalk.gray(violation.details)}`);
}
}
console.log('');
}
if (report.recommendations && report.recommendations.length > 0) {
console.log(chalk.bold('💡 권장사항:'));
for (const rec of report.recommendations) {
console.log(` • ${chalk.green(rec)}`);
}
console.log('');
}
// 통과/실패 판정
if (report.passed) {
console.log(chalk.green('✅ 복잡도 기준 통과'));
} else {
console.log(chalk.red('❌ 복잡도 기준 초과 - 리팩토링 필요'));
}
} catch (error) {
console.error(chalk.red('❌ 복잡도 검사 실패:'), error.message);
process.exit(1);
}
});
guard
.command('quick [path]')
.description('Quick complexity check / 빠른 복잡도 체크')
.action(async (targetPath) => {
try {
const { quickCheck } = await import('../utils/engineering-guard.js');
const checkPath = targetPath || process.cwd();
console.log(chalk.blue(`⚡ 빠른 복잡도 체크: ${checkPath}`));
const report = await quickCheck(checkPath);
if (report.summary.high_severity > 0) {
console.log(chalk.red('⚠️ 오버엔지니어링 위험 감지!'));
console.log('권장사항:', report.recommendations.join(', '));
} else {
console.log(chalk.green('✅ 복잡도 양호'));
}
} catch (error) {
console.error(chalk.red('❌ 빠른 체크 실패:'), error.message);
process.exit(1);
}
});
guard
.command('init')
.description('Initialize YOLO config with guard settings / YOLO 설정 초기화')
.action(async () => {
try {
const fs = await import('fs/promises');
const path = await import('path');
const yaml = await import('js-yaml');
const configPath = path.join(process.cwd(), '.aiwf', 'yolo-config.yaml');
// 기본 설정 생성
const defaultConfig = {
engineering_level: 'minimal',
focus_rules: ['requirement_first', 'simple_solution', 'no_gold_plating', 'stay_on_track'],
overengineering_prevention: {
max_file_lines: 300,
max_function_lines: 50,
max_nesting_depth: 4,
max_abstraction_layers: 3,
limit_design_patterns: true,
no_future_proofing: true,
enforce_yagni: true
}
};
// .aiwf 디렉토리 생성
await fs.mkdir(path.dirname(configPath), { recursive: true });
// 설정 파일 쓰기
await fs.writeFile(configPath, yaml.dump(defaultConfig), 'utf-8');
console.log(chalk.green('✅ YOLO 설정 파일 생성 완료!'));
console.log(` 경로: ${chalk.cyan(configPath)}`);
console.log('');
console.log(chalk.bold('🛡️ 오버엔지니어링 방지 설정:'));
console.log(` 파일 크기 제한: ${chalk.yellow('300')}줄`);
console.log(` 함수 크기 제한: ${chalk.yellow('50')}줄`);
console.log(` 중첩 깊이 제한: ${chalk.yellow('4')}레벨`);
console.log(` YAGNI 원칙: ${chalk.green('적용')}`);
} catch (error) {
console.error(chalk.red('❌ 설정 초기화 실패:'), error.message);
process.exit(1);
}
});
guard
.command('feedback [area]')
.description('Get engineering feedback for area / 특정 영역 피드백 받기')
.action(async (area) => {
try {
const { EngineeringGuard } = await import('../utils/engineering-guard.js');
const guard = new EngineeringGuard();
// 설정 파일 로드 시도
await guard.loadConfig('.aiwf/yolo-config.yaml');
const targetArea = area || 'current_task_area';
console.log(chalk.blue(`💡 엔지니어링 피드백 제공: ${targetArea}`));
console.log('');
const feedback = await guard.provideFeedback(targetArea);
if (feedback && feedback.length > 0) {
for (const item of feedback) {
const icon = item.level === 'error' ? '❌' :
item.level === 'warning' ? '⚠️' : '💡';
console.log(`${icon} ${item.message}`);
}
} else {
console.log(chalk.green('✅ 현재 영역은 복잡도 기준을 만족합니다.'));
}
} catch (error) {
console.error(chalk.red('❌ 피드백 생성 실패:'), error.message);
process.exit(1);
}
});
// GitHub integration commands
const github = program.command('github');
github.description('GitHub integration / GitHub 통합');
github
.command('issue <task-id>')
.description('Create GitHub issue from task / 태스크에서 GitHub 이슈 생성')
.action(async (taskId) => {
try {
const { GitHubIntegration } = await import('../lib/github-integration.js');
const ghIntegration = new GitHubIntegration();
await ghIntegration.createIssue(taskId);
} catch (error) {
console.error(chalk.red(`GitHub issue creation failed: ${error.message}`));
process.exit(1);
}
});
github
.command('pr [task-id]')
.description('Create pull request for completed task / 완료된 태스크로 PR 생성')
.action(async (taskId) => {
try {
const { GitHubIntegration } = await import('../lib/github-integration.js');
const ghIntegration = new GitHubIntegration();
await ghIntegration.createPullRequest(taskId);
} catch (error) {
console.error(chalk.red(`Pull request creation failed: ${error.message}`));
process.exit(1);
}
});
github
.command('sync')
.description('Sync GitHub issues with AIWF tasks / GitHub 이슈와 AIWF 태스크 동기화')
.action(async () => {
try {
const { GitHubIntegration } = await import('../lib/github-integration.js');
const ghIntegration = new GitHubIntegration();
await ghIntegration.syncIssues();
} catch (error) {
console.error(chalk.red(`GitHub sync failed: ${error.message}`));
process.exit(1);
}
});
// Parse command line arguments
program.parse(process.argv);