aiwf 0.1.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +19 -0
- package/LICENSE +1 -1
- package/README.ko.md +90 -62
- package/README.md +109 -62
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/01_PROJECT_DOCS/ARCHITECTURE.md +1 -1
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/M01_milestone_meta.md +5 -1
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/PRD_AMEND_01_Auth_Flow_Update.md +5 -1
- package/claude-code/{simone/.simone/03_SPRINTS/CLAUDE.MD → aiwf/en/.aiwf/03_SPRINTS/CLAUDE.md} +28 -7
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/03_SPRINTS/S01_M01_Initial_API/S01_sprint_meta.md +8 -1
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/03_SPRINTS/S01_M01_Initial_API/T01_S01_Setup_Project_Structure.md +5 -1
- package/claude-code/aiwf/en/.aiwf/04_GENERAL_TASKS/CLAUDE.md +96 -0
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/04_GENERAL_TASKS/T002_API_Rate_Limiting.md +7 -2
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/04_GENERAL_TASKS/TX001_Refactor_Logging_Module.md +7 -2
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/05_ARCHITECTURAL_DECISIONS/ADR001_Chosen_Database_System.md +7 -1
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/05_ARCHITECTURAL_DECISIONS/ADR002_API_Authentication_Method.md +7 -1
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-needs-focus.md +139 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-test-alignment.md +155 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-13-00-50-solid-progress.md +122 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-14-08-30-infrastructure-challenges.md +141 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-16-21-48-critical-foundation-issues.md +177 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-17-15-08-solid-progress.md +149 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/index.json +5 -0
- package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/sync-index.js +78 -0
- package/claude-code/aiwf/en/.aiwf/98_PROMPTS/github_integration.md +130 -0
- package/claude-code/aiwf/en/.aiwf/98_PROMPTS/useful-prompts.md +31 -0
- package/claude-code/aiwf/en/.aiwf/98_PROMPTS/vibe-front-prompts.md +89 -0
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/task_template.md +1 -0
- package/claude-code/{simone/.simone/CLAUDE.MD → aiwf/en/.aiwf/CLAUDE.md} +13 -4
- package/claude-code/{simone/.simone → aiwf/en/.aiwf}/README.md +6 -6
- package/claude-code/aiwf/en/.claude/CLAUDE_BE.md +276 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_changelog.md +82 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_code_review.md +88 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_commit.md +160 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_prd.md +272 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_do_task.md +109 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_docs.md +225 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_infinite.md +202 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_initialize.md +134 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_language_manager.md +287 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_language_status.md +246 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_prime.md +9 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_project_review.md +261 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_test.md +125 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +206 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_general.md +362 -0
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_yolo.md +300 -0
- package/claude-code/aiwf/en/.claude/mcp.json +31 -0
- package/claude-code/aiwf/en/.claude/settings.json +102 -0
- package/claude-code/aiwf/ko/.aiwf/00_PROJECT_MANIFEST.md +49 -0
- package/claude-code/aiwf/ko/.aiwf/01_PROJECT_DOCS/ARCHITECTURE.md +55 -0
- package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/CLAUDE.md +78 -0
- package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/M01_milestone_meta.md +42 -0
- package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/PRD_AMEND_01_Auth_Flow_Update.md +73 -0
- package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/PRD_Backend_Setup.md +98 -0
- package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/SPECS_API_V1.md +232 -0
- package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/CLAUDE.md +83 -0
- package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/S01_M01_Initial_API/S01_sprint_meta.md +49 -0
- package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/S01_M01_Initial_API/T01_S01_Setup_Project_Structure.md +60 -0
- package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/CLAUDE.md +96 -0
- package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/T002_API_Rate_Limiting.md +54 -0
- package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/TX001_Refactor_Logging_Module.md +58 -0
- package/claude-code/aiwf/ko/.aiwf/05_ARCHITECTURAL_DECISIONS/ADR001_Chosen_Database_System.md +119 -0
- package/claude-code/aiwf/ko/.aiwf/05_ARCHITECTURAL_DECISIONS/ADR002_API_Authentication_Method.md +124 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-needs-focus.md +139 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-test-alignment.md +155 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-13-00-50-solid-progress.md +122 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-14-08-30-infrastructure-challenges.md +141 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-16-21-48-critical-foundation-issues.md +177 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-17-15-08-solid-progress.md +149 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/index.json +5 -0
- package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/sync-index.js +78 -0
- package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/github_integration.md +130 -0
- package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/useful-prompts.md +31 -0
- package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/vibe-front-prompts.md +89 -0
- package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/adr_template.md +49 -0
- package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/milestone_meta_template.md +25 -0
- package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/project_manifest_template.md +39 -0
- package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/sprint_meta_template.md +23 -0
- package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/task_template.md +36 -0
- package/claude-code/aiwf/ko/.aiwf/CLAUDE.md +76 -0
- package/claude-code/aiwf/ko/.aiwf/README.md +97 -0
- package/claude-code/aiwf/ko/.claude/CLAUDE_BE.md +276 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_changelog.md +82 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_code_review.md +88 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_commit.md +160 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_do_task.md +109 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_docs.md +225 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_infinite.md +202 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_initialize.md +134 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_language_manager.md +287 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_language_status.md +246 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_prime.md +9 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_project_review.md +261 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_test.md +124 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_yolo.md +300 -0
- package/claude-code/aiwf/ko/.claude/mcp.json +31 -0
- package/claude-code/aiwf/ko/.claude/settings.json +102 -0
- package/{AI-WORKFLOW.md → docs/AI-WORKFLOW.ko.md} +38 -24
- package/docs/AI-WORKFLOW.md +299 -0
- package/docs/COMMANDS_GUIDE.ko.md +727 -0
- package/docs/COMMANDS_GUIDE.md +726 -0
- package/docs/CONTRIBUTING.md +406 -0
- package/docs/DEVELOPMENT_GUIDE.md +727 -0
- package/docs/Enhanced_Installation_Flow_Design.md +498 -0
- package/docs/PRD.ko.md +148 -0
- package/docs/PRD.md +150 -0
- package/docs/moonklabs-metadata-system-prd.md +127 -0
- package/index.js +1493 -122
- package/jest.config.js +4 -0
- package/language-cli.js +279 -0
- package/language-utils.js +330 -0
- package/package.json +23 -10
- package/scripts/validate-commands.cjs +338 -0
- package/scripts/validate-commands.js +254 -0
- package/tests/basic.test.js +62 -0
- package/tests/commands.test.js +127 -0
- package/tests/installer.test.js +129 -0
- package/tests/language-utils.test.js +271 -0
- package/COMMANDS_GUIDE.md +0 -462
- package/PRD.ko.md +0 -96
- package/PRD.md +0 -98
- package/claude-code/simone/.simone/04_GENERAL_TASKS/CLAUDE.MD +0 -51
- package/claude-code/simone/CHANGELOG.md +0 -71
- package/claude-code/simone/LICENSE +0 -21
- package/claude-code/simone/README.md +0 -219
- package/claude-code/simone/SYNC_GUIDE.md +0 -172
- package/claude-code/simone/sync-simone.sh +0 -138
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/00_PROJECT_MANIFEST.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/CLAUDE.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/PRD_Backend_Setup.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/SPECS_API_V1.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/adr_template.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/milestone_meta_template.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/project_manifest_template.md +0 -0
- /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/sprint_meta_template.md +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIWF 명령어 파일 일관성 검증 도구
|
|
5
|
+
*
|
|
6
|
+
* 이 스크립트는 다음을 검증합니다:
|
|
7
|
+
* 1. 한국어/영어 명령어 파일 간의 일관성
|
|
8
|
+
* 2. 명령어 파일명과 실제 명령어명의 매칭
|
|
9
|
+
* 3. 누락된 명령어 파일 식별
|
|
10
|
+
* 4. GitHub URL 참조 일관성
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// 색상 출력을 위한 ANSI 코드
|
|
17
|
+
const colors = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
green: '\x1b[32m',
|
|
21
|
+
yellow: '\x1b[33m',
|
|
22
|
+
blue: '\x1b[34m',
|
|
23
|
+
cyan: '\x1b[36m'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function log(message, color = 'reset') {
|
|
27
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 명령어 파일 경로
|
|
31
|
+
const KOREAN_COMMANDS_PATH = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
|
|
32
|
+
const ENGLISH_COMMANDS_PATH = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 디렉토리에서 명령어 파일 목록을 가져옵니다
|
|
36
|
+
*/
|
|
37
|
+
function getCommandFiles(dirPath) {
|
|
38
|
+
try {
|
|
39
|
+
return fs.readdirSync(dirPath)
|
|
40
|
+
.filter(file => file.endsWith('.md'))
|
|
41
|
+
.map(file => ({
|
|
42
|
+
filename: file,
|
|
43
|
+
path: path.join(dirPath, file)
|
|
44
|
+
}));
|
|
45
|
+
} catch (error) {
|
|
46
|
+
log(`디렉토리를 읽을 수 없습니다: ${dirPath}`, 'red');
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 명령어 파일에서 메타데이터를 추출합니다
|
|
53
|
+
*/
|
|
54
|
+
function extractCommandMetadata(filePath) {
|
|
55
|
+
try {
|
|
56
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
57
|
+
const lines = content.split('\n');
|
|
58
|
+
|
|
59
|
+
// 파일명에서 명령어명 추출 (aiwf_*.md → aiwf_*)
|
|
60
|
+
const filename = path.basename(filePath, '.md');
|
|
61
|
+
|
|
62
|
+
// 첫 번째 # 제목 찾기
|
|
63
|
+
const titleLine = lines.find(line => line.startsWith('# '));
|
|
64
|
+
const title = titleLine ? titleLine.substring(2).trim() : '';
|
|
65
|
+
|
|
66
|
+
// frontmatter에서 name 필드 찾기 (있는 경우)
|
|
67
|
+
let frontmatterName = '';
|
|
68
|
+
if (content.startsWith('---')) {
|
|
69
|
+
const frontmatterEnd = content.indexOf('---', 3);
|
|
70
|
+
if (frontmatterEnd > 0) {
|
|
71
|
+
const frontmatter = content.substring(0, frontmatterEnd);
|
|
72
|
+
const nameMatch = frontmatter.match(/name:\s*["']?([^"'\n]+)["']?/);
|
|
73
|
+
if (nameMatch) {
|
|
74
|
+
frontmatterName = nameMatch[1];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
filename,
|
|
81
|
+
title,
|
|
82
|
+
frontmatterName,
|
|
83
|
+
path: filePath
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
log(`파일을 읽을 수 없습니다: ${filePath}`, 'red');
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 한국어와 영어 명령어 파일의 일관성을 검증합니다
|
|
93
|
+
*/
|
|
94
|
+
function validateConsistency() {
|
|
95
|
+
log('\n=== 명령어 파일 일관성 검증 ===', 'cyan');
|
|
96
|
+
|
|
97
|
+
const koreanFiles = getCommandFiles(KOREAN_COMMANDS_PATH);
|
|
98
|
+
const englishFiles = getCommandFiles(ENGLISH_COMMANDS_PATH);
|
|
99
|
+
|
|
100
|
+
log(`한국어 명령어 파일: ${koreanFiles.length}개`, 'blue');
|
|
101
|
+
log(`영어 명령어 파일: ${englishFiles.length}개`, 'blue');
|
|
102
|
+
|
|
103
|
+
// 파일명 기준으로 매칭 (한국어는 _kr 접미사 제거)
|
|
104
|
+
const koreanFileMap = new Map();
|
|
105
|
+
const englishFileMap = new Map();
|
|
106
|
+
|
|
107
|
+
koreanFiles.forEach(file => {
|
|
108
|
+
const normalizedName = file.filename.replace('_kr.md', '.md');
|
|
109
|
+
koreanFileMap.set(normalizedName, file);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
englishFiles.forEach(file => {
|
|
113
|
+
englishFileMap.set(file.filename, file);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 누락된 파일 확인
|
|
117
|
+
const allFileNames = new Set([...koreanFileMap.keys(), ...englishFileMap.keys()]);
|
|
118
|
+
const missingInKorean = [];
|
|
119
|
+
const missingInEnglish = [];
|
|
120
|
+
|
|
121
|
+
allFileNames.forEach(filename => {
|
|
122
|
+
if (!koreanFileMap.has(filename)) {
|
|
123
|
+
missingInKorean.push(filename);
|
|
124
|
+
}
|
|
125
|
+
if (!englishFileMap.has(filename)) {
|
|
126
|
+
missingInEnglish.push(filename);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// 결과 출력
|
|
131
|
+
if (missingInKorean.length > 0) {
|
|
132
|
+
log('\n한국어 버전에서 누락된 파일:', 'yellow');
|
|
133
|
+
missingInKorean.forEach(file => log(` - ${file}`, 'red'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (missingInEnglish.length > 0) {
|
|
137
|
+
log('\n영어 버전에서 누락된 파일:', 'yellow');
|
|
138
|
+
missingInEnglish.forEach(file => log(` - ${file}`, 'red'));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (missingInKorean.length === 0 && missingInEnglish.length === 0) {
|
|
142
|
+
log('\n✅ 모든 명령어 파일이 한국어/영어 버전으로 존재합니다', 'green');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
koreanFiles: koreanFileMap,
|
|
147
|
+
englishFiles: englishFileMap,
|
|
148
|
+
missingInKorean,
|
|
149
|
+
missingInEnglish
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 명령어 파일의 메타데이터를 검증합니다
|
|
155
|
+
*/
|
|
156
|
+
function validateMetadata() {
|
|
157
|
+
log('\n=== 명령어 메타데이터 검증 ===', 'cyan');
|
|
158
|
+
|
|
159
|
+
const koreanFiles = getCommandFiles(KOREAN_COMMANDS_PATH);
|
|
160
|
+
const englishFiles = getCommandFiles(ENGLISH_COMMANDS_PATH);
|
|
161
|
+
|
|
162
|
+
let issues = 0;
|
|
163
|
+
|
|
164
|
+
// 한국어 파일 검증
|
|
165
|
+
log('\n한국어 명령어 파일 검증:', 'blue');
|
|
166
|
+
koreanFiles.forEach(file => {
|
|
167
|
+
const metadata = extractCommandMetadata(file.path);
|
|
168
|
+
if (metadata) {
|
|
169
|
+
if (!metadata.title) {
|
|
170
|
+
log(` ❌ ${file.filename}: 제목이 없습니다`, 'red');
|
|
171
|
+
issues++;
|
|
172
|
+
} else {
|
|
173
|
+
log(` ✅ ${file.filename}: ${metadata.title}`, 'green');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// 영어 파일 검증
|
|
179
|
+
log('\n영어 명령어 파일 검증:', 'blue');
|
|
180
|
+
englishFiles.forEach(file => {
|
|
181
|
+
const metadata = extractCommandMetadata(file.path);
|
|
182
|
+
if (metadata) {
|
|
183
|
+
if (!metadata.title) {
|
|
184
|
+
log(` ❌ ${file.filename}: 제목이 없습니다`, 'red');
|
|
185
|
+
issues++;
|
|
186
|
+
} else {
|
|
187
|
+
log(` ✅ ${file.filename}: ${metadata.title}`, 'green');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (issues === 0) {
|
|
193
|
+
log('\n✅ 모든 명령어 파일의 메타데이터가 올바릅니다', 'green');
|
|
194
|
+
} else {
|
|
195
|
+
log(`\n❌ ${issues}개의 메타데이터 이슈가 발견되었습니다`, 'red');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return issues;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* GitHub URL 참조를 검증합니다
|
|
203
|
+
*/
|
|
204
|
+
function validateGitHubUrls() {
|
|
205
|
+
log('\n=== GitHub URL 참조 검증 ===', 'cyan');
|
|
206
|
+
|
|
207
|
+
const allFiles = [
|
|
208
|
+
...getCommandFiles(KOREAN_COMMANDS_PATH),
|
|
209
|
+
...getCommandFiles(ENGLISH_COMMANDS_PATH)
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
let issues = 0;
|
|
213
|
+
|
|
214
|
+
allFiles.forEach(file => {
|
|
215
|
+
const content = fs.readFileSync(file.path, 'utf8');
|
|
216
|
+
|
|
217
|
+
// moonklabs/aiwf 패턴 검색
|
|
218
|
+
if (content.includes('moonklabs/aiwf')) {
|
|
219
|
+
log(` ❌ ${file.filename}: 'moonklabs/aiwf' 참조 발견`, 'red');
|
|
220
|
+
issues++;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// aiwf/aiwf 패턴 확인
|
|
224
|
+
if (content.includes('aiwf/aiwf')) {
|
|
225
|
+
log(` ✅ ${file.filename}: 올바른 'aiwf/aiwf' 참조`, 'green');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (issues === 0) {
|
|
230
|
+
log('\n✅ 모든 GitHub URL 참조가 올바릅니다', 'green');
|
|
231
|
+
} else {
|
|
232
|
+
log(`\n❌ ${issues}개의 잘못된 GitHub URL 참조가 발견되었습니다`, 'red');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return issues;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 특정 명령어 파일이 누락되었는지 확인합니다
|
|
240
|
+
*/
|
|
241
|
+
function checkSpecificCommands() {
|
|
242
|
+
log('\n=== 특정 명령어 파일 확인 ===', 'cyan');
|
|
243
|
+
|
|
244
|
+
const requiredCommands = [
|
|
245
|
+
'aiwf_update_docs.md', // 영어 버전
|
|
246
|
+
'aiwf_update_docs_kr.md' // 한국어 버전
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
let issues = 0;
|
|
250
|
+
|
|
251
|
+
requiredCommands.forEach(commandFile => {
|
|
252
|
+
const isKorean = commandFile.includes('_kr');
|
|
253
|
+
const dirPath = isKorean ? KOREAN_COMMANDS_PATH : ENGLISH_COMMANDS_PATH;
|
|
254
|
+
const filePath = path.join(dirPath, commandFile);
|
|
255
|
+
|
|
256
|
+
if (fs.existsSync(filePath)) {
|
|
257
|
+
log(` ✅ ${commandFile}: 존재`, 'green');
|
|
258
|
+
} else {
|
|
259
|
+
log(` ❌ ${commandFile}: 누락`, 'red');
|
|
260
|
+
issues++;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// aiwf_docs.md가 있는지 확인 (기존 파일)
|
|
265
|
+
const legacyDocsKo = path.join(KOREAN_COMMANDS_PATH, 'aiwf_docs.md');
|
|
266
|
+
const legacyDocsEn = path.join(ENGLISH_COMMANDS_PATH, 'aiwf_docs.md');
|
|
267
|
+
|
|
268
|
+
if (fs.existsSync(legacyDocsKo)) {
|
|
269
|
+
log(` ℹ️ aiwf_docs.md (한국어): 기존 파일 존재`, 'yellow');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (fs.existsSync(legacyDocsEn)) {
|
|
273
|
+
log(` ℹ️ aiwf_docs.md (영어): 기존 파일 존재`, 'yellow');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return issues;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* 검증 요약을 출력합니다
|
|
281
|
+
*/
|
|
282
|
+
function printSummary(results) {
|
|
283
|
+
log('\n=== 검증 요약 ===', 'cyan');
|
|
284
|
+
|
|
285
|
+
const totalIssues = results.metadataIssues + results.githubUrlIssues + results.specificCommandIssues;
|
|
286
|
+
|
|
287
|
+
log(`총 발견된 이슈: ${totalIssues}개`, totalIssues > 0 ? 'red' : 'green');
|
|
288
|
+
log(`- 메타데이터 이슈: ${results.metadataIssues}개`, 'blue');
|
|
289
|
+
log(`- GitHub URL 이슈: ${results.githubUrlIssues}개`, 'blue');
|
|
290
|
+
log(`- 누락된 특정 명령어: ${results.specificCommandIssues}개`, 'blue');
|
|
291
|
+
log(`- 한국어 버전 누락: ${results.consistency.missingInKorean.length}개`, 'blue');
|
|
292
|
+
log(`- 영어 버전 누락: ${results.consistency.missingInEnglish.length}개`, 'blue');
|
|
293
|
+
|
|
294
|
+
if (totalIssues === 0) {
|
|
295
|
+
log('\n🎉 모든 검증이 통과되었습니다!', 'green');
|
|
296
|
+
return true;
|
|
297
|
+
} else {
|
|
298
|
+
log('\n⚠️ 일부 이슈가 발견되었습니다. 위의 내용을 확인해주세요.', 'red');
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 메인 실행 함수
|
|
305
|
+
*/
|
|
306
|
+
function main() {
|
|
307
|
+
log('AIWF 명령어 파일 일관성 검증 도구', 'cyan');
|
|
308
|
+
log('====================================', 'cyan');
|
|
309
|
+
|
|
310
|
+
const consistency = validateConsistency();
|
|
311
|
+
const metadataIssues = validateMetadata();
|
|
312
|
+
const githubUrlIssues = validateGitHubUrls();
|
|
313
|
+
const specificCommandIssues = checkSpecificCommands();
|
|
314
|
+
|
|
315
|
+
const results = {
|
|
316
|
+
consistency,
|
|
317
|
+
metadataIssues,
|
|
318
|
+
githubUrlIssues,
|
|
319
|
+
specificCommandIssues
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const success = printSummary(results);
|
|
323
|
+
|
|
324
|
+
// 종료 코드 설정
|
|
325
|
+
process.exit(success ? 0 : 1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 스크립트가 직접 실행될 때만 main 함수 호출
|
|
329
|
+
if (require.main === module) {
|
|
330
|
+
main();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
module.exports = {
|
|
334
|
+
validateConsistency,
|
|
335
|
+
validateMetadata,
|
|
336
|
+
validateGitHubUrls,
|
|
337
|
+
checkSpecificCommands
|
|
338
|
+
};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIWF 명령어 파일 일관성 검증 도구
|
|
5
|
+
*
|
|
6
|
+
* 이 스크립트는 다음을 검증합니다:
|
|
7
|
+
* 1. 한국어/영어 명령어 파일 간의 일관성
|
|
8
|
+
* 2. 명령어 파일명과 실제 명령어명의 매칭
|
|
9
|
+
* 3. 누락된 명령어 파일 식별
|
|
10
|
+
* 4. GitHub URL 참조 일관성
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs/promises';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
|
|
16
|
+
// 색상 출력을 위한 ANSI 코드
|
|
17
|
+
const colors = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
green: '\x1b[32m',
|
|
21
|
+
yellow: '\x1b[33m',
|
|
22
|
+
blue: '\x1b[34m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
gray: '\x1b[90m'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// chalk 대체 함수
|
|
28
|
+
const chalk = {
|
|
29
|
+
red: (text) => `${colors.red}${text}${colors.reset}`,
|
|
30
|
+
green: (text) => `${colors.green}${text}${colors.reset}`,
|
|
31
|
+
yellow: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
32
|
+
blue: (text) => `${colors.blue}${text}${colors.reset}`,
|
|
33
|
+
cyan: (text) => `${colors.cyan}${text}${colors.reset}`,
|
|
34
|
+
gray: (text) => `${colors.gray}${text}${colors.reset}`,
|
|
35
|
+
bold: (text) => `\x1b[1m${text}${colors.reset}`
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const KO_COMMANDS_DIR = 'claude-code/aiwf/ko/.claude/commands/aiwf';
|
|
39
|
+
const EN_COMMANDS_DIR = 'claude-code/aiwf/en/.claude/commands/aiwf';
|
|
40
|
+
|
|
41
|
+
// 명령어 파일 메타데이터 추출
|
|
42
|
+
async function extractCommandMetadata(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
45
|
+
const lines = content.split('\n');
|
|
46
|
+
|
|
47
|
+
// YAML frontmatter 찾기
|
|
48
|
+
if (lines[0] === '---') {
|
|
49
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && line === '---');
|
|
50
|
+
if (endIndex !== -1) {
|
|
51
|
+
const frontmatter = lines.slice(1, endIndex).join('\n');
|
|
52
|
+
// 간단한 YAML 파싱 (name 추출)
|
|
53
|
+
const nameMatch = frontmatter.match(/name:\s*["']?([^"'\n]+)["']?/);
|
|
54
|
+
const descMatch = frontmatter.match(/description:\s*["']?([^"'\n]+)["']?/);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
name: nameMatch ? nameMatch[1] : null,
|
|
58
|
+
description: descMatch ? descMatch[1] : null,
|
|
59
|
+
hasFrontmatter: true
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 첫 번째 헤더에서 명령어명 추출
|
|
65
|
+
const headerMatch = content.match(/^#\s+(.+)/m);
|
|
66
|
+
return {
|
|
67
|
+
name: headerMatch ? headerMatch[1] : null,
|
|
68
|
+
description: null,
|
|
69
|
+
hasFrontmatter: false
|
|
70
|
+
};
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return { error: error.message };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 디렉토리의 모든 명령어 파일 스캔
|
|
77
|
+
async function scanCommandsDirectory(dir) {
|
|
78
|
+
const commands = {};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const files = await fs.readdir(dir);
|
|
82
|
+
const mdFiles = files.filter(file => file.endsWith('.md'));
|
|
83
|
+
|
|
84
|
+
for (const file of mdFiles) {
|
|
85
|
+
const filePath = path.join(dir, file);
|
|
86
|
+
const metadata = await extractCommandMetadata(filePath);
|
|
87
|
+
commands[file] = {
|
|
88
|
+
...metadata,
|
|
89
|
+
path: filePath
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(chalk.red(`Error scanning directory ${dir}: ${error.message}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return commands;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// GitHub URL 참조 검증
|
|
100
|
+
async function validateGitHubUrls(filePath) {
|
|
101
|
+
try {
|
|
102
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
103
|
+
const moonklabsRefs = content.match(/moonklabs\/aiwf/g) || [];
|
|
104
|
+
const aiwfRefs = content.match(/aiwf\/aiwf/g) || [];
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
moonklabsCount: moonklabsRefs.length,
|
|
108
|
+
aiwfCount: aiwfRefs.length,
|
|
109
|
+
hasInconsistentUrls: moonklabsRefs.length > 0
|
|
110
|
+
};
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return { error: error.message };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 명령어 파일 검증
|
|
117
|
+
async function validateCommands() {
|
|
118
|
+
console.log(chalk.blue(chalk.bold('\n🔍 AIWF Command Files Validation\n')));
|
|
119
|
+
console.log('='.repeat(50));
|
|
120
|
+
|
|
121
|
+
// 1. 디렉토리 스캔
|
|
122
|
+
console.log(chalk.cyan('\n1. 디렉토리 스캔 중...'));
|
|
123
|
+
const koCommands = await scanCommandsDirectory(KO_COMMANDS_DIR);
|
|
124
|
+
const enCommands = await scanCommandsDirectory(EN_COMMANDS_DIR);
|
|
125
|
+
|
|
126
|
+
console.log(chalk.gray(` 한국어 명령어: ${Object.keys(koCommands).length}개`));
|
|
127
|
+
console.log(chalk.gray(` 영어 명령어: ${Object.keys(enCommands).length}개`));
|
|
128
|
+
|
|
129
|
+
// 2. 명령어 일관성 검증
|
|
130
|
+
console.log(chalk.cyan('\n2. 명령어 일관성 검증 중...'));
|
|
131
|
+
|
|
132
|
+
const allFiles = new Set([...Object.keys(koCommands), ...Object.keys(enCommands)]);
|
|
133
|
+
const issues = [];
|
|
134
|
+
|
|
135
|
+
for (const fileName of allFiles) {
|
|
136
|
+
const koExists = koCommands[fileName];
|
|
137
|
+
const enExists = enCommands[fileName];
|
|
138
|
+
|
|
139
|
+
if (!koExists) {
|
|
140
|
+
issues.push({
|
|
141
|
+
type: 'missing_ko',
|
|
142
|
+
file: fileName,
|
|
143
|
+
message: `한국어 버전 누락: ${fileName}`
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!enExists) {
|
|
148
|
+
issues.push({
|
|
149
|
+
type: 'missing_en',
|
|
150
|
+
file: fileName,
|
|
151
|
+
message: `영어 버전 누락: ${fileName}`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 명령어명 일관성 검증 (언어가 다르므로 생략)
|
|
156
|
+
if (koExists && enExists && koExists.name && enExists.name) {
|
|
157
|
+
// 언어가 다르므로 명령어명이 다른 것은 정상
|
|
158
|
+
// 이 검증은 제거
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 3. update_docs 명령어 검증
|
|
163
|
+
console.log(chalk.cyan('\n3. update_docs 명령어 검증 중...'));
|
|
164
|
+
|
|
165
|
+
const updateDocsFiles = [
|
|
166
|
+
'aiwf_update_docs.md',
|
|
167
|
+
'aiwf_docs.md'
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const updateDocsStatus = {};
|
|
171
|
+
for (const file of updateDocsFiles) {
|
|
172
|
+
updateDocsStatus[file] = {
|
|
173
|
+
ko: !!koCommands[file],
|
|
174
|
+
en: !!enCommands[file]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 4. GitHub URL 검증
|
|
179
|
+
console.log(chalk.cyan('\n4. GitHub URL 참조 검증 중...'));
|
|
180
|
+
|
|
181
|
+
const urlIssues = [];
|
|
182
|
+
for (const [fileName, command] of Object.entries({ ...koCommands, ...enCommands })) {
|
|
183
|
+
if (command.path) {
|
|
184
|
+
const urlCheck = await validateGitHubUrls(command.path);
|
|
185
|
+
if (urlCheck.hasInconsistentUrls) {
|
|
186
|
+
urlIssues.push({
|
|
187
|
+
file: fileName,
|
|
188
|
+
path: command.path,
|
|
189
|
+
moonklabsCount: urlCheck.moonklabsCount
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 결과 출력
|
|
196
|
+
console.log(chalk.blue(chalk.bold('\n📋 검증 결과\n')));
|
|
197
|
+
console.log('='.repeat(50));
|
|
198
|
+
|
|
199
|
+
// 일관성 이슈
|
|
200
|
+
if (issues.length > 0) {
|
|
201
|
+
console.log(chalk.red(`\n❌ 발견된 이슈 (${issues.length}개):`));
|
|
202
|
+
for (const issue of issues) {
|
|
203
|
+
console.log(chalk.red(` • ${issue.message}`));
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
console.log(chalk.green('\n✅ 명령어 일관성 검증 통과'));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// update_docs 상태
|
|
210
|
+
console.log(chalk.blue('\n📝 update_docs 명령어 상태:'));
|
|
211
|
+
for (const [file, status] of Object.entries(updateDocsStatus)) {
|
|
212
|
+
const koIcon = status.ko ? '✅' : '❌';
|
|
213
|
+
const enIcon = status.en ? '✅' : '❌';
|
|
214
|
+
console.log(chalk.gray(` ${file}: KO ${koIcon} EN ${enIcon}`));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// GitHub URL 이슈
|
|
218
|
+
if (urlIssues.length > 0) {
|
|
219
|
+
console.log(chalk.yellow(`\n⚠️ GitHub URL 이슈 (${urlIssues.length}개):`));
|
|
220
|
+
for (const issue of urlIssues) {
|
|
221
|
+
console.log(chalk.yellow(` • ${issue.file}: moonklabs/aiwf 참조 ${issue.moonklabsCount}개`));
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
console.log(chalk.green('\n✅ GitHub URL 참조 검증 통과'));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 요약
|
|
228
|
+
console.log(chalk.blue(chalk.bold('\n📊 요약:')));
|
|
229
|
+
console.log(chalk.gray(` 총 명령어 파일: ${allFiles.size}개`));
|
|
230
|
+
console.log(chalk.gray(` 한국어/영어 쌍: ${Math.min(Object.keys(koCommands).length, Object.keys(enCommands).length)}개`));
|
|
231
|
+
console.log(chalk.gray(` 일관성 이슈: ${issues.length}개`));
|
|
232
|
+
console.log(chalk.gray(` URL 이슈: ${urlIssues.length}개`));
|
|
233
|
+
|
|
234
|
+
const overallSuccess = issues.length === 0 && urlIssues.length === 0;
|
|
235
|
+
console.log(overallSuccess ?
|
|
236
|
+
chalk.green('\n🎉 모든 검증 통과!') :
|
|
237
|
+
chalk.red('\n⚠️ 일부 이슈가 발견되었습니다.')
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
success: overallSuccess,
|
|
242
|
+
issues,
|
|
243
|
+
urlIssues,
|
|
244
|
+
updateDocsStatus,
|
|
245
|
+
totalFiles: allFiles.size
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 메인 실행
|
|
250
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
251
|
+
validateCommands().catch(console.error);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export { validateCommands };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 기본 프로젝트 구조 테스트
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
describe('프로젝트 기본 구조', () => {
|
|
13
|
+
test('package.json 파일이 존재한다', () => {
|
|
14
|
+
const packagePath = path.join(__dirname, '../package.json');
|
|
15
|
+
expect(fs.existsSync(packagePath)).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('메인 실행 파일 index.js가 존재한다', () => {
|
|
19
|
+
const indexPath = path.join(__dirname, '../index.js');
|
|
20
|
+
expect(fs.existsSync(indexPath)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('AIWF 프레임워크 디렉토리가 존재한다', () => {
|
|
24
|
+
const aiwfPath = path.join(__dirname, '../claude-code/aiwf');
|
|
25
|
+
expect(fs.existsSync(aiwfPath)).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('한국어 버전 디렉토리가 존재한다', () => {
|
|
29
|
+
const koPath = path.join(__dirname, '../claude-code/aiwf/ko');
|
|
30
|
+
expect(fs.existsSync(koPath)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('영어 버전 디렉토리가 존재한다', () => {
|
|
34
|
+
const enPath = path.join(__dirname, '../claude-code/aiwf/en');
|
|
35
|
+
expect(fs.existsSync(enPath)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('패키지 구성 검증', () => {
|
|
40
|
+
let packageJson;
|
|
41
|
+
|
|
42
|
+
beforeAll(() => {
|
|
43
|
+
const packagePath = path.join(__dirname, '../package.json');
|
|
44
|
+
packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('패키지 이름이 올바르다', () => {
|
|
48
|
+
expect(packageJson.name).toBe('aiwf');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('필수 의존성이 설치되어 있다', () => {
|
|
52
|
+
expect(packageJson.dependencies).toHaveProperty('commander');
|
|
53
|
+
expect(packageJson.dependencies).toHaveProperty('chalk');
|
|
54
|
+
expect(packageJson.dependencies).toHaveProperty('ora');
|
|
55
|
+
expect(packageJson.dependencies).toHaveProperty('prompts');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('bin 명령어가 설정되어 있다', () => {
|
|
59
|
+
expect(packageJson.bin).toHaveProperty('aiwf');
|
|
60
|
+
expect(packageJson.bin.aiwf).toBe('./index.js');
|
|
61
|
+
});
|
|
62
|
+
});
|