aiwf
Version:
AI Workflow Framework for Claude Code with multi-language support (Korean/English)
267 lines (226 loc) • 8.88 kB
JavaScript
/**
* AIWF 파일 목록 자동 업데이트 스크립트
*
* 이 스크립트는 claude-code 디렉토리를 스캔하여
* src/config/file-lists.js 파일을 자동으로 업데이트합니다.
*
* 사용법:
* npm run update:file-lists
* npm run update:file-lists -- --dry-run # 미리보기만
*/
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import chalk from 'chalk';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT_DIR = path.resolve(__dirname, '..');
// 스캔할 디렉토리 경로들
const SCAN_PATHS = {
commands: {
en: path.join(ROOT_DIR, 'claude-code/aiwf/en/.claude/commands/aiwf'),
ko: path.join(ROOT_DIR, 'claude-code/aiwf/ko/.claude/commands/aiwf')
},
globalRules: path.join(ROOT_DIR, 'rules/global'),
manualRules: path.join(ROOT_DIR, 'rules/manual')
};
const OUTPUT_FILE = path.join(ROOT_DIR, 'src/config/file-lists.js');
/**
* 디렉토리에서 .md 파일들을 스캔
*/
async function scanDirectory(dirPath) {
try {
const files = await fs.readdir(dirPath);
return files
.filter(file => file.endsWith('.md'))
.sort();
} catch (error) {
console.warn(chalk.yellow(`Warning: Could not scan directory ${dirPath}: ${error.message}`));
return [];
}
}
/**
* 두 언어 버전에서 공통으로 존재하는 파일들만 추출
*/
async function getCommonCommandFiles() {
const [enFiles, koFiles] = await Promise.all([
scanDirectory(SCAN_PATHS.commands.en),
scanDirectory(SCAN_PATHS.commands.ko)
]);
// 두 언어에 모두 존재하는 파일들만 포함
const commonFiles = enFiles.filter(file => koFiles.includes(file));
// 언어별 특수 파일들도 포함 (선택적)
const enOnlyFiles = enFiles.filter(file => !koFiles.includes(file));
const koOnlyFiles = koFiles.filter(file => !enFiles.includes(file));
console.log(chalk.blue('📊 Command Files Analysis:'));
console.log(` Common files: ${chalk.green(commonFiles.length)}`);
console.log(` English only: ${chalk.yellow(enOnlyFiles.length)} ${enOnlyFiles.length > 0 ? `(${enOnlyFiles.join(', ')})` : ''}`);
console.log(` Korean only: ${chalk.yellow(koOnlyFiles.length)} ${koOnlyFiles.length > 0 ? `(${koOnlyFiles.join(', ')})` : ''}`);
// 공통 파일들 + 언어별 특수 파일들을 모두 포함
return [...commonFiles, ...enOnlyFiles, ...koOnlyFiles].sort();
}
/**
* file-lists.js 파일 생성
*/
async function generateFileListsContent(commandFiles, globalRulesFiles, manualRulesFiles) {
const template = `/**
* AIWF 설치 시 다운로드할 파일 목록들
* 이 파일은 GitHub에서 다운로드할 파일들의 사전 정의된 목록을 포함합니다.
*
* ⚠️ 이 파일은 자동 생성됩니다. 수동으로 편집하지 마세요!
* 업데이트하려면: npm run update:file-lists
*
* Generated at: ${new Date().toISOString()}
*/
// Claude Code 명령어 파일 목록 (한국어와 영어 공통)
export const COMMAND_FILES = [
${commandFiles.map(file => ` '${file}'`).join(',\n')}
];
// 글로벌 룰 파일 목록
export const GLOBAL_RULES_FILES = [
${globalRulesFiles.map(file => ` '${file}'`).join(',\n')}
];
// 수동 룰 파일 목록
export const MANUAL_RULES_FILES = [
${manualRulesFiles.map(file => ` '${file}'`).join(',\n')}
];
// 템플릿 파일 목록 (향후 추가 예정)
export const TEMPLATE_FILES = [
// 템플릿 파일들이 추가되면 여기에 나열
];
// 프롬프트 파일 목록 (향후 추가 예정)
export const PROMPT_FILES = [
// 프롬프트 파일들이 추가되면 여기에 나열
];
// 모든 파일 목록을 하나의 객체로 export
export const FILE_LISTS = {
COMMAND_FILES,
GLOBAL_RULES_FILES,
MANUAL_RULES_FILES,
TEMPLATE_FILES,
PROMPT_FILES
};
// 개별 카테고리별 파일 수 정보
export const FILE_COUNTS = {
commands: COMMAND_FILES.length,
globalRules: GLOBAL_RULES_FILES.length,
manualRules: MANUAL_RULES_FILES.length,
templates: TEMPLATE_FILES.length,
prompts: PROMPT_FILES.length,
total: COMMAND_FILES.length + GLOBAL_RULES_FILES.length + MANUAL_RULES_FILES.length + TEMPLATE_FILES.length + PROMPT_FILES.length
};
/**
* 특정 언어의 파일 존재 여부 확인을 위한 헬퍼 함수들
*/
/**
* 언어별로 존재하지 않을 수 있는 파일들을 필터링
* @param {Array<string>} fileList - 원본 파일 목록
* @param {string} language - 언어 코드 (ko, en)
* @returns {Array<string>} 필터링된 파일 목록
*/
export function filterFilesByLanguage(fileList, language) {
// 일부 파일들은 특정 언어에만 존재할 수 있음
const languageSpecificFiles = {
ko: [
// 한국어에만 있는 파일들 (현재는 없음)
],
en: [
// 영어에만 있는 파일들 (현재는 없음)
]
};
// 현재는 모든 파일이 두 언어 모두 존재한다고 가정
return fileList;
}
/**
* 파일 목록 업데이트를 위한 헬퍼 함수
* 실제 디렉토리를 스캔하여 파일 목록을 업데이트할 수 있는 함수
*/
export function getLatestFileList() {
// 이 함수는 향후 자동 업데이트 기능 구현 시 사용
return {
commands: COMMAND_FILES,
globalRules: GLOBAL_RULES_FILES,
manualRules: MANUAL_RULES_FILES,
templates: TEMPLATE_FILES,
prompts: PROMPT_FILES
};
}`;
return template;
}
/**
* 기존 파일과 비교하여 변경사항 확인
*/
async function compareWithExisting(newContent) {
try {
const existingContent = await fs.readFile(OUTPUT_FILE, 'utf-8');
// 생성 시간 라인 제외하고 비교 (Generated at: 부분)
const normalizeContent = (content) => {
return content.replace(/Generated at: .*$/m, 'Generated at: <timestamp>');
};
return normalizeContent(existingContent) === normalizeContent(newContent);
} catch {
return false; // 파일이 없으면 변경사항이 있다고 간주
}
}
/**
* 메인 실행 함수
*/
async function main() {
const isDryRun = process.argv.includes('--dry-run');
const isVerbose = process.argv.includes('--verbose');
console.log(chalk.blue.bold('🔄 AIWF File Lists Update Script'));
console.log(chalk.gray(`Scanning directories and updating file lists...\n`));
try {
// 각 디렉토리 스캔
const [commandFiles, globalRulesFiles, manualRulesFiles] = await Promise.all([
getCommonCommandFiles(),
scanDirectory(SCAN_PATHS.globalRules),
scanDirectory(SCAN_PATHS.manualRules)
]);
// 통계 출력
console.log(chalk.blue('📊 Scan Results:'));
console.log(` Command files: ${chalk.green(commandFiles.length)}`);
console.log(` Global rules: ${chalk.green(globalRulesFiles.length)}`);
console.log(` Manual rules: ${chalk.green(manualRulesFiles.length)}`);
console.log(` Total files: ${chalk.green(commandFiles.length + globalRulesFiles.length + manualRulesFiles.length)}`);
if (isVerbose) {
console.log(chalk.gray('\n📝 Detailed file lists:'));
console.log(chalk.gray('Commands:'), commandFiles.join(', '));
console.log(chalk.gray('Global rules:'), globalRulesFiles.join(', '));
console.log(chalk.gray('Manual rules:'), manualRulesFiles.join(', '));
}
// 새 내용 생성
const newContent = await generateFileListsContent(commandFiles, globalRulesFiles, manualRulesFiles);
// 변경사항 확인
const hasChanges = !(await compareWithExisting(newContent));
if (!hasChanges) {
console.log(chalk.green('\n✅ File lists are already up to date!'));
return;
}
if (isDryRun) {
console.log(chalk.yellow('\n🔍 Dry run mode - Changes detected but not saved:'));
console.log(chalk.gray(`Would update: ${OUTPUT_FILE}`));
console.log(chalk.gray(`Content length: ${newContent.length} characters`));
return;
}
// 파일 업데이트
await fs.writeFile(OUTPUT_FILE, newContent, 'utf-8');
console.log(chalk.green('\n✅ Successfully updated file lists!'));
console.log(chalk.gray(`Updated: ${OUTPUT_FILE}`));
console.log(chalk.blue('\n💡 Next steps:'));
console.log(chalk.gray(' 1. Review the changes: git diff src/config/file-lists.js'));
console.log(chalk.gray(' 2. Test the changes: npm test'));
console.log(chalk.gray(' 3. Commit the changes: git add . && git commit -m "update: refresh file lists"'));
} catch (error) {
console.error(chalk.red('\n❌ Error updating file lists:'));
console.error(chalk.red(error.message));
console.error(chalk.gray(error.stack));
process.exit(1);
}
}
// 스크립트가 직접 실행될 때만 main 함수 호출
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
export { main as updateFileLists };