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.
Files changed (163) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/CLAUDE.md +19 -0
  3. package/LICENSE +1 -1
  4. package/README.ko.md +90 -62
  5. package/README.md +109 -62
  6. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/01_PROJECT_DOCS/ARCHITECTURE.md +1 -1
  7. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/M01_milestone_meta.md +5 -1
  8. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/PRD_AMEND_01_Auth_Flow_Update.md +5 -1
  9. package/claude-code/{simone/.simone/03_SPRINTS/CLAUDE.MD → aiwf/en/.aiwf/03_SPRINTS/CLAUDE.md} +28 -7
  10. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/03_SPRINTS/S01_M01_Initial_API/S01_sprint_meta.md +8 -1
  11. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/03_SPRINTS/S01_M01_Initial_API/T01_S01_Setup_Project_Structure.md +5 -1
  12. package/claude-code/aiwf/en/.aiwf/04_GENERAL_TASKS/CLAUDE.md +96 -0
  13. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/04_GENERAL_TASKS/T002_API_Rate_Limiting.md +7 -2
  14. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/04_GENERAL_TASKS/TX001_Refactor_Logging_Module.md +7 -2
  15. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/05_ARCHITECTURAL_DECISIONS/ADR001_Chosen_Database_System.md +7 -1
  16. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/05_ARCHITECTURAL_DECISIONS/ADR002_API_Authentication_Method.md +7 -1
  17. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-needs-focus.md +139 -0
  18. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-test-alignment.md +155 -0
  19. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-13-00-50-solid-progress.md +122 -0
  20. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-14-08-30-infrastructure-challenges.md +141 -0
  21. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-16-21-48-critical-foundation-issues.md +177 -0
  22. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/2025-06-17-15-08-solid-progress.md +149 -0
  23. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/index.json +5 -0
  24. package/claude-code/aiwf/en/.aiwf/10_STATE_OF_PROJECT/sync-index.js +78 -0
  25. package/claude-code/aiwf/en/.aiwf/98_PROMPTS/github_integration.md +130 -0
  26. package/claude-code/aiwf/en/.aiwf/98_PROMPTS/useful-prompts.md +31 -0
  27. package/claude-code/aiwf/en/.aiwf/98_PROMPTS/vibe-front-prompts.md +89 -0
  28. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/task_template.md +1 -0
  29. package/claude-code/{simone/.simone/CLAUDE.MD → aiwf/en/.aiwf/CLAUDE.md} +13 -4
  30. package/claude-code/{simone/.simone → aiwf/en/.aiwf}/README.md +6 -6
  31. package/claude-code/aiwf/en/.claude/CLAUDE_BE.md +276 -0
  32. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_changelog.md +82 -0
  33. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_code_review.md +88 -0
  34. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_commit.md +160 -0
  35. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
  36. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
  37. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_prd.md +272 -0
  38. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
  39. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
  40. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
  41. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_do_task.md +109 -0
  42. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_docs.md +225 -0
  43. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_infinite.md +202 -0
  44. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_initialize.md +134 -0
  45. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
  46. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_language_manager.md +287 -0
  47. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_language_status.md +246 -0
  48. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
  49. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
  50. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_prime.md +9 -0
  51. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_project_review.md +261 -0
  52. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
  53. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_test.md +125 -0
  54. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
  55. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +206 -0
  56. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
  57. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
  58. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_ultrathink_general.md +362 -0
  59. package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_yolo.md +300 -0
  60. package/claude-code/aiwf/en/.claude/mcp.json +31 -0
  61. package/claude-code/aiwf/en/.claude/settings.json +102 -0
  62. package/claude-code/aiwf/ko/.aiwf/00_PROJECT_MANIFEST.md +49 -0
  63. package/claude-code/aiwf/ko/.aiwf/01_PROJECT_DOCS/ARCHITECTURE.md +55 -0
  64. package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/CLAUDE.md +78 -0
  65. package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/M01_milestone_meta.md +42 -0
  66. package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/PRD_AMEND_01_Auth_Flow_Update.md +73 -0
  67. package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/PRD_Backend_Setup.md +98 -0
  68. package/claude-code/aiwf/ko/.aiwf/02_REQUIREMENTS/M01_Backend_Setup/SPECS_API_V1.md +232 -0
  69. package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/CLAUDE.md +83 -0
  70. package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/S01_M01_Initial_API/S01_sprint_meta.md +49 -0
  71. package/claude-code/aiwf/ko/.aiwf/03_SPRINTS/S01_M01_Initial_API/T01_S01_Setup_Project_Structure.md +60 -0
  72. package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/CLAUDE.md +96 -0
  73. package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/T002_API_Rate_Limiting.md +54 -0
  74. package/claude-code/aiwf/ko/.aiwf/04_GENERAL_TASKS/TX001_Refactor_Logging_Module.md +58 -0
  75. package/claude-code/aiwf/ko/.aiwf/05_ARCHITECTURAL_DECISIONS/ADR001_Chosen_Database_System.md +119 -0
  76. package/claude-code/aiwf/ko/.aiwf/05_ARCHITECTURAL_DECISIONS/ADR002_API_Authentication_Method.md +124 -0
  77. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-needs-focus.md +139 -0
  78. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-12-12-00-test-alignment.md +155 -0
  79. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-13-00-50-solid-progress.md +122 -0
  80. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-14-08-30-infrastructure-challenges.md +141 -0
  81. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-16-21-48-critical-foundation-issues.md +177 -0
  82. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/2025-06-17-15-08-solid-progress.md +149 -0
  83. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/index.json +5 -0
  84. package/claude-code/aiwf/ko/.aiwf/10_STATE_OF_PROJECT/sync-index.js +78 -0
  85. package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/github_integration.md +130 -0
  86. package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/useful-prompts.md +31 -0
  87. package/claude-code/aiwf/ko/.aiwf/98_PROMPTS/vibe-front-prompts.md +89 -0
  88. package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/adr_template.md +49 -0
  89. package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/milestone_meta_template.md +25 -0
  90. package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/project_manifest_template.md +39 -0
  91. package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/sprint_meta_template.md +23 -0
  92. package/claude-code/aiwf/ko/.aiwf/99_TEMPLATES/task_template.md +36 -0
  93. package/claude-code/aiwf/ko/.aiwf/CLAUDE.md +76 -0
  94. package/claude-code/aiwf/ko/.aiwf/README.md +97 -0
  95. package/claude-code/aiwf/ko/.claude/CLAUDE_BE.md +276 -0
  96. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_changelog.md +82 -0
  97. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_code_review.md +88 -0
  98. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_commit.md +160 -0
  99. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
  100. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
  101. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
  102. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
  103. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
  104. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
  105. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_do_task.md +109 -0
  106. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_docs.md +225 -0
  107. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_infinite.md +202 -0
  108. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_initialize.md +134 -0
  109. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
  110. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_language_manager.md +287 -0
  111. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_language_status.md +246 -0
  112. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
  113. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
  114. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_prime.md +9 -0
  115. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_project_review.md +261 -0
  116. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
  117. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_test.md +124 -0
  118. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
  119. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
  120. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
  121. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
  122. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
  123. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_yolo.md +300 -0
  124. package/claude-code/aiwf/ko/.claude/mcp.json +31 -0
  125. package/claude-code/aiwf/ko/.claude/settings.json +102 -0
  126. package/{AI-WORKFLOW.md → docs/AI-WORKFLOW.ko.md} +38 -24
  127. package/docs/AI-WORKFLOW.md +299 -0
  128. package/docs/COMMANDS_GUIDE.ko.md +727 -0
  129. package/docs/COMMANDS_GUIDE.md +726 -0
  130. package/docs/CONTRIBUTING.md +406 -0
  131. package/docs/DEVELOPMENT_GUIDE.md +727 -0
  132. package/docs/Enhanced_Installation_Flow_Design.md +498 -0
  133. package/docs/PRD.ko.md +148 -0
  134. package/docs/PRD.md +150 -0
  135. package/docs/moonklabs-metadata-system-prd.md +127 -0
  136. package/index.js +1493 -122
  137. package/jest.config.js +4 -0
  138. package/language-cli.js +279 -0
  139. package/language-utils.js +330 -0
  140. package/package.json +23 -10
  141. package/scripts/validate-commands.cjs +338 -0
  142. package/scripts/validate-commands.js +254 -0
  143. package/tests/basic.test.js +62 -0
  144. package/tests/commands.test.js +127 -0
  145. package/tests/installer.test.js +129 -0
  146. package/tests/language-utils.test.js +271 -0
  147. package/COMMANDS_GUIDE.md +0 -462
  148. package/PRD.ko.md +0 -96
  149. package/PRD.md +0 -98
  150. package/claude-code/simone/.simone/04_GENERAL_TASKS/CLAUDE.MD +0 -51
  151. package/claude-code/simone/CHANGELOG.md +0 -71
  152. package/claude-code/simone/LICENSE +0 -21
  153. package/claude-code/simone/README.md +0 -219
  154. package/claude-code/simone/SYNC_GUIDE.md +0 -172
  155. package/claude-code/simone/sync-simone.sh +0 -138
  156. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/00_PROJECT_MANIFEST.md +0 -0
  157. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/CLAUDE.md +0 -0
  158. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/PRD_Backend_Setup.md +0 -0
  159. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/02_REQUIREMENTS/M01_Backend_Setup/SPECS_API_V1.md +0 -0
  160. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/adr_template.md +0 -0
  161. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/milestone_meta_template.md +0 -0
  162. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/project_manifest_template.md +0 -0
  163. /package/claude-code/{simone/.simone → aiwf/en/.aiwf}/99_TEMPLATES/sprint_meta_template.md +0 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * AIWF 명령어 파일 테스트
3
+ */
4
+
5
+ import fs from 'fs/promises';
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('AIWF 명령어 검증', () => {
13
+ test('명령어 디렉토리 구조가 올바르다', async () => {
14
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
15
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
16
+
17
+ const koFiles = await fs.readdir(koDir);
18
+ const enFiles = await fs.readdir(enDir);
19
+
20
+ expect(koFiles.length).toBeGreaterThan(20);
21
+ expect(enFiles.length).toBeGreaterThan(20);
22
+
23
+ // 한국어가 영어보다 파일이 많을 수 있음 (한국어 전용 명령어)
24
+ expect(koFiles.length).toBeGreaterThanOrEqual(enFiles.length);
25
+ });
26
+
27
+ test('한국어 명령어 디렉토리가 존재한다', async () => {
28
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
29
+ const stats = await fs.stat(koDir);
30
+ expect(stats.isDirectory()).toBe(true);
31
+ });
32
+
33
+ test('영어 명령어 디렉토리가 존재한다', async () => {
34
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
35
+ const stats = await fs.stat(enDir);
36
+ expect(stats.isDirectory()).toBe(true);
37
+ });
38
+
39
+ test('필수 명령어 파일들이 존재한다', async () => {
40
+ const requiredCommands = [
41
+ 'aiwf_initialize.md',
42
+ 'aiwf_test.md',
43
+ 'aiwf_prime.md',
44
+ 'aiwf_do_task.md'
45
+ ];
46
+
47
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
48
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
49
+
50
+ for (const command of requiredCommands) {
51
+ const koPath = path.join(koDir, command);
52
+ const enPath = path.join(enDir, command);
53
+
54
+ const koExists = await fs.access(koPath).then(() => true).catch(() => false);
55
+ const enExists = await fs.access(enPath).then(() => true).catch(() => false);
56
+
57
+ expect(koExists).toBe(true);
58
+ expect(enExists).toBe(true);
59
+ }
60
+ });
61
+
62
+ test('명령어 파일들이 유효한 마크다운 구조를 가진다', async () => {
63
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
64
+ const files = await fs.readdir(koDir);
65
+ const mdFiles = files.filter(file => file.endsWith('.md'));
66
+
67
+ expect(mdFiles.length).toBeGreaterThan(0);
68
+
69
+ // 첫 번째 파일만 샘플 테스트
70
+ const sampleFile = path.join(koDir, mdFiles[0]);
71
+ const content = await fs.readFile(sampleFile, 'utf-8');
72
+
73
+ // 마크다운 파일은 최소한 하나의 헤더를 가져야 함
74
+ expect(content).toMatch(/^#\s+.+/m);
75
+ });
76
+
77
+ test('한국어 전용 명령어 파일 존재 검증', async () => {
78
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
79
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
80
+
81
+ const koFiles = await fs.readdir(koDir);
82
+ const enFiles = await fs.readdir(enDir);
83
+
84
+ // aiwf_update_docs_kr.md는 한국어 전용이어야 함
85
+ const krSpecificFile = 'aiwf_update_docs_kr.md';
86
+ expect(koFiles).toContain(krSpecificFile);
87
+
88
+ // 영어 버전에는 없어야 함 - 이 테스트는 실패할 것임
89
+ expect(enFiles).not.toContain(krSpecificFile);
90
+ });
91
+
92
+ test('한국어 명령어가 영어 명령어보다 많거나 같아야 함', async () => {
93
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
94
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
95
+
96
+ const koFiles = await fs.readdir(koDir);
97
+ const enFiles = await fs.readdir(enDir);
98
+
99
+ // 한국어 버전이 영어보다 더 많은 명령어를 가질 수 있음 (한국어 전용 명령어)
100
+ expect(koFiles.length).toBeGreaterThanOrEqual(enFiles.length);
101
+ });
102
+
103
+ test('핵심 명령어들이 존재하는지 검증', async () => {
104
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
105
+ const coreCommands = ['aiwf_initialize.md', 'aiwf_prime.md', 'aiwf_test.md', 'aiwf_do_task.md'];
106
+
107
+ const koFiles = await fs.readdir(koDir);
108
+
109
+ coreCommands.forEach(command => {
110
+ expect(koFiles).toContain(command);
111
+ });
112
+ });
113
+
114
+ test('존재하지 않는 명령어 파일 테스트', async () => {
115
+ const nonExistentPath = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf/non_existent_command.md');
116
+
117
+ const exists = await fs.access(nonExistentPath).then(() => true).catch(() => false);
118
+ expect(exists).toBe(false);
119
+ });
120
+
121
+ test('잘못된 디렉토리 경로 테스트', async () => {
122
+ const invalidDir = path.join(__dirname, '../invalid/directory/path');
123
+
124
+ const dirExists = await fs.access(invalidDir).then(() => true).catch(() => false);
125
+ expect(dirExists).toBe(false);
126
+ });
127
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * 인스톨러 기능 테스트
3
+ */
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { execSync } from 'child_process';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ describe('AIWF 인스톨러 기능', () => {
14
+ let tempDir;
15
+
16
+ beforeEach(() => {
17
+ // 임시 테스트 디렉토리 생성
18
+ tempDir = path.join(__dirname, '../temp-test-installer');
19
+ if (fs.existsSync(tempDir)) {
20
+ fs.rmSync(tempDir, { recursive: true, force: true });
21
+ }
22
+ fs.mkdirSync(tempDir, { recursive: true });
23
+ });
24
+
25
+ afterEach(() => {
26
+ // 테스트 후 정리
27
+ if (fs.existsSync(tempDir)) {
28
+ fs.rmSync(tempDir, { recursive: true, force: true });
29
+ }
30
+ });
31
+
32
+ test('버전 명령어가 올바르게 작동한다', () => {
33
+ const result = execSync('node index.js --version', {
34
+ cwd: path.join(__dirname, '..'),
35
+ encoding: 'utf8',
36
+ timeout: 5000
37
+ });
38
+ expect(result.trim()).toMatch(/\d+\.\d+\.\d+/);
39
+ });
40
+
41
+ test('도움말 명령어가 올바르게 작동한다', () => {
42
+ const result = execSync('node index.js --help', {
43
+ cwd: path.join(__dirname, '..'),
44
+ encoding: 'utf8',
45
+ timeout: 5000
46
+ });
47
+ expect(result).toContain('AIWF Installer');
48
+ expect(result).toContain('Options:');
49
+ expect(result).toContain('--force');
50
+ });
51
+
52
+ test('명령어 파일들이 올바른 형식인지 검증한다', () => {
53
+ const koCommandsDir = path.join(__dirname, '../claude-code/aiwf/ko/.claude/commands/aiwf');
54
+ const enCommandsDir = path.join(__dirname, '../claude-code/aiwf/en/.claude/commands/aiwf');
55
+
56
+ expect(fs.existsSync(koCommandsDir)).toBe(true);
57
+ expect(fs.existsSync(enCommandsDir)).toBe(true);
58
+
59
+ // 한국어 명령어 파일 검증
60
+ const koFiles = fs.readdirSync(koCommandsDir).filter(f => f.endsWith('.md'));
61
+ expect(koFiles.length).toBeGreaterThan(0);
62
+
63
+ // 영어 명령어 파일 검증
64
+ const enFiles = fs.readdirSync(enCommandsDir).filter(f => f.endsWith('.md'));
65
+ expect(enFiles.length).toBeGreaterThan(0);
66
+
67
+ // 최소한 몇 개의 핵심 명령어가 존재하는지 확인
68
+ const coreCommands = ['aiwf_initialize', 'aiwf_prime', 'aiwf_test'];
69
+ coreCommands.forEach(cmd => {
70
+ const koFile = path.join(koCommandsDir, `${cmd}.md`);
71
+ const enFile = path.join(enCommandsDir, `${cmd}.md`);
72
+
73
+ expect(fs.existsSync(koFile) || fs.existsSync(enFile)).toBe(true);
74
+ });
75
+ });
76
+
77
+ test('AIWF 템플릿 구조가 올바르게 구성되어 있다', () => {
78
+ const koTemplatesDir = path.join(__dirname, '../claude-code/aiwf/ko/.aiwf');
79
+ const enTemplatesDir = path.join(__dirname, '../claude-code/aiwf/en/.aiwf');
80
+
81
+ expect(fs.existsSync(koTemplatesDir)).toBe(true);
82
+ expect(fs.existsSync(enTemplatesDir)).toBe(true);
83
+
84
+ // 필수 템플릿 디렉토리들 확인
85
+ const requiredDirs = ['99_TEMPLATES', '98_PROMPTS'];
86
+
87
+ requiredDirs.forEach(dir => {
88
+ const koDir = path.join(koTemplatesDir, dir);
89
+ const enDir = path.join(enTemplatesDir, dir);
90
+
91
+ expect(fs.existsSync(koDir) || fs.existsSync(enDir)).toBe(true);
92
+ });
93
+ });
94
+ });
95
+
96
+ describe('프로젝트 문서화', () => {
97
+ test('필수 문서 파일들이 존재한다', () => {
98
+ const requiredDocs = [
99
+ 'README.md',
100
+ 'README.ko.md',
101
+ 'CLAUDE.md',
102
+ 'docs/COMMANDS_GUIDE.md',
103
+ 'docs/COMMANDS_GUIDE.ko.md'
104
+ ];
105
+
106
+ requiredDocs.forEach(doc => {
107
+ const docPath = path.join(__dirname, '..', doc);
108
+ expect(fs.existsSync(docPath)).toBe(true);
109
+ });
110
+ });
111
+
112
+ test('다국어 지원이 일관되게 구현되어 있다', () => {
113
+ const koDir = path.join(__dirname, '../claude-code/aiwf/ko');
114
+ const enDir = path.join(__dirname, '../claude-code/aiwf/en');
115
+
116
+ expect(fs.existsSync(koDir)).toBe(true);
117
+ expect(fs.existsSync(enDir)).toBe(true);
118
+
119
+ // 각 언어 버전에 필수 구조가 있는지 확인
120
+ ['ko', 'en'].forEach(lang => {
121
+ const langDir = path.join(__dirname, `../claude-code/aiwf/${lang}`);
122
+ const claudeDir = path.join(langDir, '.claude/commands/aiwf');
123
+ const aiwfDir = path.join(langDir, '.aiwf');
124
+
125
+ expect(fs.existsSync(claudeDir)).toBe(true);
126
+ expect(fs.existsSync(aiwfDir)).toBe(true);
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,271 @@
1
+ /**
2
+ * 언어 관리 유틸리티 테스트
3
+ */
4
+
5
+ import { jest } from '@jest/globals';
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import {
9
+ detectLanguage,
10
+ loadUserLanguageConfig,
11
+ saveUserLanguageConfig,
12
+ getCommandPath,
13
+ resolveCommandPath,
14
+ getTemplatePath,
15
+ getLocalizedMessage,
16
+ switchLanguage,
17
+ getLanguageStatus,
18
+ SUPPORTED_LANGUAGES,
19
+ DEFAULT_LANGUAGE,
20
+ LANGUAGE_CONFIG_PATH
21
+ } from '../language-utils.js';
22
+
23
+ // 테스트용 임시 디렉토리
24
+ const TEST_DIR = '.aiwf-test';
25
+ const TEST_CONFIG_PATH = `${TEST_DIR}/config/language.json`;
26
+
27
+ describe('Language Utils', () => {
28
+ beforeEach(async () => {
29
+ // 테스트 디렉토리 생성
30
+ await fs.mkdir(`${TEST_DIR}/config`, { recursive: true });
31
+
32
+ // 환경 변수 초기화
33
+ delete process.env.LANG;
34
+ delete process.env.LC_ALL;
35
+ delete process.env.LANGUAGE;
36
+ });
37
+
38
+ afterEach(async () => {
39
+ // 테스트 디렉토리 정리
40
+ try {
41
+ await fs.rm(TEST_DIR, { recursive: true, force: true });
42
+ } catch (error) {
43
+ // 무시
44
+ }
45
+ });
46
+
47
+ describe('detectLanguage', () => {
48
+ test('should return default language when no settings exist', async () => {
49
+ const language = await detectLanguage();
50
+ expect(SUPPORTED_LANGUAGES).toContain(language);
51
+ });
52
+
53
+ test('should detect Korean from environment variables', async () => {
54
+ process.env.LANG = 'ko_KR.UTF-8';
55
+ const language = await detectLanguage();
56
+ expect(language).toBe('ko');
57
+ });
58
+
59
+ test('should detect English from environment variables', async () => {
60
+ process.env.LANG = 'en_US.UTF-8';
61
+ const language = await detectLanguage();
62
+ expect(language).toBe('en');
63
+ });
64
+
65
+ test('should return default language for unsupported locale', async () => {
66
+ process.env.LANG = 'ja_JP.UTF-8';
67
+ const language = await detectLanguage();
68
+ expect(language).toBe(DEFAULT_LANGUAGE);
69
+ });
70
+ });
71
+
72
+ describe('saveUserLanguageConfig and loadUserLanguageConfig', () => {
73
+ test('should save and load language configuration', async () => {
74
+ // 테스트용 경로로 저장
75
+ const config = {
76
+ language: 'ko',
77
+ auto_detect: true,
78
+ last_updated: new Date().toISOString(),
79
+ fallback: 'en'
80
+ };
81
+
82
+ await fs.writeFile(TEST_CONFIG_PATH, JSON.stringify(config, null, 2));
83
+
84
+ // 설정 파일 읽기 테스트는 실제 파일 시스템이 아닌 모의 구현을 사용해야 함
85
+ const savedContent = await fs.readFile(TEST_CONFIG_PATH, 'utf8');
86
+ const parsedConfig = JSON.parse(savedContent);
87
+
88
+ expect(parsedConfig.language).toBe('ko');
89
+ expect(parsedConfig.auto_detect).toBe(true);
90
+ expect(parsedConfig.fallback).toBe('en');
91
+ });
92
+
93
+ test('should handle invalid language configuration', async () => {
94
+ const invalidConfig = { language: 'invalid' };
95
+ await fs.writeFile(TEST_CONFIG_PATH, JSON.stringify(invalidConfig));
96
+
97
+ // 실제 함수가 유효성 검증을 수행한다고 가정
98
+ const savedContent = await fs.readFile(TEST_CONFIG_PATH, 'utf8');
99
+ const config = JSON.parse(savedContent);
100
+
101
+ // 유효하지 않은 설정은 처리되지 않아야 함
102
+ expect(SUPPORTED_LANGUAGES).not.toContain(config.language);
103
+ });
104
+ });
105
+
106
+ describe('getCommandPath', () => {
107
+ test('should return correct path for Korean commands', () => {
108
+ const path = getCommandPath('aiwf_do_task', 'ko');
109
+ expect(path).toBe('claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_do_task.md');
110
+ });
111
+
112
+ test('should return correct path for English commands', () => {
113
+ const path = getCommandPath('aiwf_do_task', 'en');
114
+ expect(path).toBe('claude-code/aiwf/en/.claude/commands/aiwf/aiwf_do_task.md');
115
+ });
116
+
117
+ test('should fallback to default language for invalid language', () => {
118
+ const path = getCommandPath('aiwf_do_task', 'invalid');
119
+ expect(path).toBe(`claude-code/aiwf/${DEFAULT_LANGUAGE}/.claude/commands/aiwf/aiwf_do_task.md`);
120
+ });
121
+
122
+ test('should use default language when no language specified', () => {
123
+ const path = getCommandPath('aiwf_do_task');
124
+ expect(path).toBe(`claude-code/aiwf/${DEFAULT_LANGUAGE}/.claude/commands/aiwf/aiwf_do_task.md`);
125
+ });
126
+ });
127
+
128
+ describe('getTemplatePath', () => {
129
+ test('should return correct template path for Korean', () => {
130
+ const path = getTemplatePath('task_template.md', 'ko');
131
+ expect(path).toBe('.aiwf/99_TEMPLATES/ko/task_template.md');
132
+ });
133
+
134
+ test('should return correct template path for English', () => {
135
+ const path = getTemplatePath('task_template.md', 'en');
136
+ expect(path).toBe('.aiwf/99_TEMPLATES/en/task_template.md');
137
+ });
138
+ });
139
+
140
+ describe('getLocalizedMessage', () => {
141
+ test('should return Korean message', () => {
142
+ const message = getLocalizedMessage('LANGUAGE_NOT_SUPPORTED', 'ko');
143
+ expect(message).toBe('지원되지 않는 언어입니다');
144
+ });
145
+
146
+ test('should return English message', () => {
147
+ const message = getLocalizedMessage('LANGUAGE_NOT_SUPPORTED', 'en');
148
+ expect(message).toBe('Language not supported');
149
+ });
150
+
151
+ test('should fallback to default language for invalid language', () => {
152
+ const message = getLocalizedMessage('LANGUAGE_NOT_SUPPORTED', 'invalid');
153
+ expect(message).toBe('Language not supported'); // DEFAULT_LANGUAGE가 'en'이라고 가정
154
+ });
155
+
156
+ test('should return key if message not found', () => {
157
+ const message = getLocalizedMessage('INVALID_KEY', 'ko');
158
+ expect(message).toBe('INVALID_KEY');
159
+ });
160
+ });
161
+
162
+ describe('switchLanguage', () => {
163
+ test('should validate supported language', async () => {
164
+ // 유효한 언어
165
+ const result1 = await switchLanguage('ko');
166
+ expect(['ko', 'en']).toContain('ko');
167
+
168
+ // 유효하지 않은 언어는 예외를 발생시켜야 함
169
+ try {
170
+ await switchLanguage('invalid');
171
+ // 이 라인에 도달하면 안됨
172
+ expect(true).toBe(false);
173
+ } catch (error) {
174
+ expect(error.message).toContain('지원되지 않는 언어');
175
+ }
176
+ });
177
+ });
178
+
179
+ describe('constants', () => {
180
+ test('should have valid supported languages', () => {
181
+ expect(Array.isArray(SUPPORTED_LANGUAGES)).toBe(true);
182
+ expect(SUPPORTED_LANGUAGES.length).toBeGreaterThan(0);
183
+ expect(SUPPORTED_LANGUAGES).toContain('ko');
184
+ expect(SUPPORTED_LANGUAGES).toContain('en');
185
+ });
186
+
187
+ test('should have valid default language', () => {
188
+ expect(SUPPORTED_LANGUAGES).toContain(DEFAULT_LANGUAGE);
189
+ });
190
+
191
+ test('should have valid config path', () => {
192
+ expect(typeof LANGUAGE_CONFIG_PATH).toBe('string');
193
+ expect(LANGUAGE_CONFIG_PATH).toContain('.aiwf');
194
+ expect(LANGUAGE_CONFIG_PATH).toContain('language.json');
195
+ });
196
+ });
197
+ });
198
+
199
+ describe('Language Utils Integration', () => {
200
+ test('should work with real file system operations', async () => {
201
+ // 실제 파일 시스템을 사용한 통합 테스트
202
+ const testConfigDir = '.aiwf-integration-test/config';
203
+ const testConfigFile = `${testConfigDir}/language.json`;
204
+
205
+ try {
206
+ // 테스트 디렉토리 생성
207
+ await fs.mkdir(testConfigDir, { recursive: true });
208
+
209
+ // 설정 파일 생성
210
+ const config = {
211
+ language: 'ko',
212
+ auto_detect: false,
213
+ last_updated: new Date().toISOString(),
214
+ fallback: 'en'
215
+ };
216
+
217
+ await fs.writeFile(testConfigFile, JSON.stringify(config, null, 2));
218
+
219
+ // 파일 존재 확인
220
+ const exists = await fs.access(testConfigFile).then(() => true).catch(() => false);
221
+ expect(exists).toBe(true);
222
+
223
+ // 내용 확인
224
+ const content = await fs.readFile(testConfigFile, 'utf8');
225
+ const parsedConfig = JSON.parse(content);
226
+ expect(parsedConfig.language).toBe('ko');
227
+
228
+ } finally {
229
+ // 정리
230
+ try {
231
+ await fs.rm('.aiwf-integration-test', { recursive: true, force: true });
232
+ } catch (error) {
233
+ // 무시
234
+ }
235
+ }
236
+ });
237
+ });
238
+
239
+ describe('Error Handling', () => {
240
+ test('should handle file system errors gracefully', async () => {
241
+ // 존재하지 않는 디렉토리에서 설정 로드 시도
242
+ const originalConfigPath = LANGUAGE_CONFIG_PATH;
243
+
244
+ // detectLanguage는 실패해도 기본값을 반환해야 함
245
+ const language = await detectLanguage();
246
+ expect(SUPPORTED_LANGUAGES).toContain(language);
247
+ });
248
+
249
+ test('should handle corrupt configuration files', async () => {
250
+ const testConfigDir = '.aiwf-error-test/config';
251
+ const testConfigFile = `${testConfigDir}/language.json`;
252
+
253
+ try {
254
+ await fs.mkdir(testConfigDir, { recursive: true });
255
+
256
+ // 잘못된 JSON 파일 생성
257
+ await fs.writeFile(testConfigFile, '{ invalid json }');
258
+
259
+ // 손상된 설정 파일이 있어도 언어 감지는 정상 작동해야 함
260
+ const language = await detectLanguage();
261
+ expect(SUPPORTED_LANGUAGES).toContain(language);
262
+
263
+ } finally {
264
+ try {
265
+ await fs.rm('.aiwf-error-test', { recursive: true, force: true });
266
+ } catch (error) {
267
+ // 무시
268
+ }
269
+ }
270
+ });
271
+ });