aiwf 0.3.3 β†’ 0.3.4

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 (203) hide show
  1. package/.aiwf/config/language.json +1 -1
  2. package/.aiwf/sessions/session_1752877859489_1t7n2fhdb.json +7 -0
  3. package/.aiwf/sessions/session_1752877859515_cwdv211ry.json +7 -0
  4. package/.aiwf/sessions/session_1752877860664_huziadmbo.json +7 -0
  5. package/.aiwf/sessions/session_1752877860666_kyr5u1np6.json +7 -0
  6. package/.aiwf/sessions/session_1752877861848_9yinepw3t.json +7 -0
  7. package/.aiwf/sessions/session_1752877861874_y32sputuc.json +7 -0
  8. package/.aiwf/sessions/session_1752877862685_ivxbl5rhz.json +7 -0
  9. package/.aiwf/sessions/session_1752877862820_wnq4glvxi.json +7 -0
  10. package/.aiwf/sessions/session_1752877864632_3ebnpaelb.json +7 -0
  11. package/.aiwf/sessions/session_1752877864879_b9zgjcb7d.json +7 -0
  12. package/.aiwf/sessions/session_1752877865533_x14m1vodi.json +7 -0
  13. package/.aiwf/sessions/session_1752877865562_gkoh19n7d.json +7 -0
  14. package/.aiwf/sessions/session_1752877866333_wp8qpdcoh.json +7 -0
  15. package/.aiwf/sessions/session_1752877866509_dawntfpab.json +7 -0
  16. package/.aiwf/sessions/session_1752877866890_plc5vfucl.json +7 -0
  17. package/.aiwf/sessions/session_1752877866895_ga8hmq3oi.json +7 -0
  18. package/.aiwf/sessions/session_1752877867761_ayjp4v5f5.json +7 -0
  19. package/.aiwf/sessions/session_1752877867892_lfz6jy252.json +7 -0
  20. package/.aiwf/sessions/session_1752877868139_7bqd9ubli.json +7 -0
  21. package/.aiwf/sessions/session_1752877868162_zq3ofjbsh.json +7 -0
  22. package/.aiwf/sessions/session_1752877869054_7rlgm9jt6.json +7 -0
  23. package/.aiwf/sessions/session_1752877869477_ax3nhbxzr.json +7 -0
  24. package/.aiwf/sessions/session_1752877870095_6hgp4vl1e.json +7 -0
  25. package/.aiwf/sessions/session_1752877870381_qfcrdg4li.json +7 -0
  26. package/.aiwf/sessions/session_1752877871895_pgd5vllhz.json +7 -0
  27. package/.aiwf/sessions/session_1752877872199_jbhv2g2da.json +7 -0
  28. package/.aiwf/sessions/session_1752877872680_x04wpffu6.json +7 -0
  29. package/.aiwf/sessions/session_1752877872895_3y4vbeauo.json +7 -0
  30. package/.aiwf/sessions/session_1752877873178_3maolzc6b.json +7 -0
  31. package/.aiwf/sessions/session_1752877873180_ytm83j07f.json +7 -0
  32. package/.aiwf/sessions/session_1752877874217_dp56c9t42.json +7 -0
  33. package/.aiwf/sessions/session_1752877874415_epwp1g1og.json +7 -0
  34. package/.aiwf/sessions/session_1752877874454_heicyuryb.json +7 -0
  35. package/.aiwf/sessions/session_1752877874869_1c2illeu5.json +7 -0
  36. package/.aiwf/sessions/session_1752877874879_k1vo4vxyx.json +7 -0
  37. package/.aiwf/sessions/session_1752877874913_bwgyiz9bp.json +7 -0
  38. package/.aiwf/sessions/session_1752877876218_44807kztz.json +7 -0
  39. package/.aiwf/sessions/session_1752877876515_qkhjv5kpw.json +7 -0
  40. package/.aiwf/sessions/session_1752877876543_p605aud1m.json +7 -0
  41. package/.aiwf/sessions/session_1752877876819_l65w7ifck.json +7 -0
  42. package/.aiwf/sessions/session_1752877876867_xrhzss4we.json +7 -0
  43. package/.aiwf/sessions/session_1752877876872_u7aso54qb.json +7 -0
  44. package/.aiwf/sessions/session_1752877877705_telnfg4o6.json +7 -0
  45. package/.aiwf/sessions/session_1752877878285_6s45im2ib.json +7 -0
  46. package/.aiwf/sessions/session_1752877878506_v0dvguqhz.json +7 -0
  47. package/.aiwf/sessions/session_1752877878581_3ri3yc66a.json +7 -0
  48. package/.aiwf/sessions/session_1752877878756_a82x9s2pd.json +7 -0
  49. package/.aiwf/sessions/session_1752877878886_20pjsfour.json +7 -0
  50. package/.aiwf/sessions/session_1752877880266_fm1ydjw0g.json +7 -0
  51. package/.aiwf/sessions/session_1752877881149_xbat4z03d.json +7 -0
  52. package/.aiwf/sessions/session_1752877881551_qy5sic9qs.json +7 -0
  53. package/.aiwf/sessions/session_1752877881554_24ugd96wu.json +7 -0
  54. package/.aiwf/sessions/session_1752877881797_e9mm25uh0.json +7 -0
  55. package/.aiwf/sessions/session_1752877881822_bxvjj8c9g.json +7 -0
  56. package/.aiwf/sessions/session_1752877881890_jv7l6jve3.json +7 -0
  57. package/.aiwf/sessions/session_1752877881991_f0qugdtcq.json +7 -0
  58. package/.aiwf/sessions/session_1752877883387_0bvw9rvnx.json +7 -0
  59. package/.aiwf/sessions/session_1752877883820_q6g2md2up.json +7 -0
  60. package/.aiwf/sessions/session_1752877884393_sqr7trvjl.json +7 -0
  61. package/.aiwf/sessions/session_1752877884500_whtypmzlf.json +7 -0
  62. package/.aiwf/sessions/session_1752877884645_y9mvvxli1.json +7 -0
  63. package/.aiwf/sessions/session_1752877884733_hk46eenel.json +7 -0
  64. package/.aiwf/sessions/session_1752877884738_c62j6214r.json +7 -0
  65. package/.aiwf/sessions/session_1752877884771_temhxehoz.json +7 -0
  66. package/.aiwf/sessions/session_1752877885643_6v5fcktc7.json +7 -0
  67. package/.aiwf/sessions/session_1752877886426_ee8di2fqh.json +7 -0
  68. package/.aiwf/sessions/session_1752877886549_3xj6tdoew.json +7 -0
  69. package/.aiwf/sessions/session_1752877886604_aj80yyfco.json +7 -0
  70. package/.aiwf/sessions/session_1752877886702_2b8841vyg.json +7 -0
  71. package/.aiwf/sessions/session_1752877886712_ntqc8b8bz.json +7 -0
  72. package/.aiwf/sessions/session_1752877886968_946m8tbw6.json +7 -0
  73. package/.aiwf/sessions/session_1752877887008_wg6zfg8dn.json +7 -0
  74. package/.aiwf/sessions/session_1752877888479_ciiu2qiqs.json +7 -0
  75. package/.aiwf/sessions/session_1752877889330_ravg8l66k.json +7 -0
  76. package/.aiwf/sessions/session_1752877889542_7zepkhjkc.json +7 -0
  77. package/.aiwf/sessions/session_1752877889638_ev4htqiu0.json +7 -0
  78. package/.aiwf/sessions/session_1752877889956_szphy2qma.json +7 -0
  79. package/.aiwf/sessions/session_1752877890220_o6aaa57tt.json +7 -0
  80. package/.aiwf/sessions/session_1752877890527_v2loy5vu1.json +7 -0
  81. package/.aiwf/sessions/session_1752877890572_cwq8fiuma.json +7 -0
  82. package/.aiwf/sessions/session_1752877892200_sgcwq1alt.json +7 -0
  83. package/.aiwf/sessions/session_1752877892268_v8kexpbc5.json +7 -0
  84. package/.aiwf/sessions/session_1752877892384_1gbbgrp4y.json +7 -0
  85. package/.aiwf/sessions/session_1752877893568_9p2n5p31a.json +7 -0
  86. package/.aiwf/sessions/session_1752877893727_un34a5tvp.json +7 -0
  87. package/.aiwf/sessions/session_1752877893778_01j79sw9i.json +7 -0
  88. package/.aiwf/sessions/session_1752877894053_jrhba6wko.json +7 -0
  89. package/.aiwf/sessions/session_1752877894082_x3bghawox.json +7 -0
  90. package/.aiwf/sessions/session_1752877894500_iytiav2ku.json +7 -0
  91. package/.aiwf/sessions/session_1752877894667_ucn0gc2ge.json +7 -0
  92. package/.aiwf/test-results-s02.json +4 -4
  93. package/.claude/commands/aiwf/aiwf_changelog.md +82 -0
  94. package/.claude/commands/aiwf/aiwf_code_review.md +88 -0
  95. package/.claude/commands/aiwf/aiwf_commit.md +160 -0
  96. package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +53 -0
  97. package/.claude/commands/aiwf/aiwf_compress_context.md +116 -0
  98. package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +48 -0
  99. package/.claude/commands/aiwf/aiwf_compress_stats.md +79 -0
  100. package/.claude/commands/aiwf/aiwf_compress_with_persona.md +51 -0
  101. package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +54 -0
  102. package/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
  103. package/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
  104. package/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
  105. package/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
  106. package/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
  107. package/.claude/commands/aiwf/aiwf_default_mode.md +44 -0
  108. package/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
  109. package/.claude/commands/aiwf/aiwf_do_task.md +109 -0
  110. package/.claude/commands/aiwf/aiwf_docs.md +225 -0
  111. package/.claude/commands/aiwf/aiwf_evaluate.md +92 -0
  112. package/.claude/commands/aiwf/aiwf_get_feature_details.md +78 -0
  113. package/.claude/commands/aiwf/aiwf_infinite.md +202 -0
  114. package/.claude/commands/aiwf/aiwf_initialize.md +134 -0
  115. package/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
  116. package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +57 -0
  117. package/.claude/commands/aiwf/aiwf_list_features.md +54 -0
  118. package/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
  119. package/.claude/commands/aiwf/aiwf_persona_architect.md +48 -0
  120. package/.claude/commands/aiwf/aiwf_persona_backend.md +37 -0
  121. package/.claude/commands/aiwf/aiwf_persona_data_analyst.md +37 -0
  122. package/.claude/commands/aiwf/aiwf_persona_frontend.md +37 -0
  123. package/.claude/commands/aiwf/aiwf_persona_security.md +48 -0
  124. package/.claude/commands/aiwf/aiwf_persona_status.md +50 -0
  125. package/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
  126. package/.claude/commands/aiwf/aiwf_prime.md +9 -0
  127. package/.claude/commands/aiwf/aiwf_project_review.md +261 -0
  128. package/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
  129. package/.claude/commands/aiwf/aiwf_test.md +124 -0
  130. package/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
  131. package/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
  132. package/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
  133. package/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
  134. package/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
  135. package/.claude/commands/aiwf/aiwf_update_docs_kr.md +25 -0
  136. package/.claude/commands/aiwf/aiwf_update_feature_status.md +53 -0
  137. package/.claude/commands/aiwf/aiwf_yolo.md +300 -0
  138. package/.eslintrc.json +26 -0
  139. package/.prettierrc +10 -0
  140. package/CLAUDE.md +154 -54
  141. package/README.ko.md +25 -0
  142. package/README.md +25 -0
  143. package/TEST_RECOVERY_PLAN.md +68 -0
  144. package/docs/CLEANUP_RECOMMENDATIONS.md +86 -0
  145. package/docs/CLI_USAGE_GUIDE.md +479 -0
  146. package/package.json +9 -9
  147. package/scripts/update-feature-status.js +70 -0
  148. package/{lib β†’ src/cli}/cache-cli.js +4 -4
  149. package/src/cli/index.js +165 -0
  150. package/{language-cli.js β†’ src/cli/language-cli.js} +11 -4
  151. package/src/commands/cache-templates.js +209 -0
  152. package/src/commands/create-offline.js +86 -0
  153. package/src/commands/create-project.js +151 -0
  154. package/src/lib/backup-manager.js +270 -0
  155. package/src/lib/file-downloader.js +208 -0
  156. package/src/lib/installer.js +676 -0
  157. package/src/lib/rollback-manager.js +418 -0
  158. package/src/lib/validator.js +561 -0
  159. package/src/utils/git-integration.js +173 -0
  160. package/{language-utils.js β†’ src/utils/language-utils.js} +2 -1
  161. package/src/utils/messages.js +136 -0
  162. package/src/utils/paths.js +130 -0
  163. package/tests/application.test.js +2 -2
  164. package/tests/installer.test.js +2 -2
  165. package/tests/integration/ai-persona-system.test.js +4 -4
  166. package/tests/integration/test-persona-system/docs/README.md +1 -0
  167. package/tests/integration/test-persona-system/package.json +1 -0
  168. package/tests/integration/test-persona-system/src/auth.js +1 -0
  169. package/tests/integration/test-persona-system/src/index.js +1 -0
  170. package/tests/integration/test-persona-system/test/test.js +1 -0
  171. package/tests/language-utils.test.js +1 -1
  172. package/tests/temp_invalid_json/invalid.json +1 -0
  173. package/tests/unit/ai-persona-manager.test.js +7 -7
  174. package/.aiwf/05_DOCUMENTATION/performance-report.md +0 -33
  175. package/.aiwf/test-cache/86c7f9e80251abb8bedd58953ab0d1779e46b4f43ea33b63bffdb778106373b0.json +0 -6
  176. package/.aiwf/test-cache/9e252a90b80fce0585d7ed0646e7a9b4343e109f661f42a3ae678ba1ec5e22c7.json +0 -7
  177. package/.aiwf/token-data/alerts.json +0 -293
  178. package/.aiwf/token-data/monitor-config.json +0 -12
  179. package/CHANGELOG_M02.md +0 -52
  180. package/claude-code/aiwf/ko/templates/web-app/template/package-lock.json +0 -10022
  181. package/claude-code/aiwf/ko/token-data/alerts.json +0 -293
  182. package/claude-code/aiwf/ko/token-data/monitor-config.json +0 -12
  183. package/docs/m02-dod-checklist.md +0 -125
  184. package/docs/m02-gap-analysis.md +0 -214
  185. package/docs/m02-milestone-completion-report.md +0 -186
  186. package/docs/m02-milestone-executive-summary.md +0 -68
  187. package/index.js +0 -1951
  188. package/lib/cache-integration.js +0 -401
  189. package/lib/file-batch-processor.js +0 -401
  190. package/lib/performance-benchmark.js +0 -621
  191. package/lib/template-manager.js +0 -310
  192. package/scripts/validate-commands.cjs +0 -338
  193. /package/{lib β†’ src/lib}/ai-persona-manager.js +0 -0
  194. /package/{lib β†’ src/lib}/cache-system.js +0 -0
  195. /package/{lib β†’ src/lib}/context-engine.js +0 -0
  196. /package/{lib β†’ src/lib}/memory-profiler.js +0 -0
  197. /package/{lib β†’ src/lib}/metrics-collector.js +0 -0
  198. /package/{lib β†’ src/lib}/offline-detector.js +0 -0
  199. /package/{lib β†’ src/lib}/task-analyzer.js +0 -0
  200. /package/{lib β†’ src/lib}/template-cache-system.js +0 -0
  201. /package/{lib β†’ src/lib}/template-downloader.js +0 -0
  202. /package/{lib β†’ src/lib}/template-version-manager.js +0 -0
  203. /package/{lib β†’ src/lib}/token-optimizer.js +0 -0
@@ -0,0 +1,418 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { getMessages } from '../utils/messages.js';
6
+ import { getToolDirectory as getToolDirectoryPath } from '../utils/paths.js';
7
+
8
+ // Backup file patterns for each tool
9
+ const BACKUP_PATTERNS = {
10
+ claudeCode: /^claude-commands-aiwf-.*\.bak$/,
11
+ geminiCLI: /^gemini-prompts-aiwf-.*\.bak$/,
12
+ cursor: /^cursor-rules-.*\.bak$/,
13
+ windsurf: /^windsurf-rules-.*\.bak$/,
14
+ aiwf: /^aiwf-.*\.bak$/
15
+ };
16
+
17
+ // Backup file prefixes
18
+ const BACKUP_PREFIXES = {
19
+ claudeCode: 'claude-commands-aiwf-',
20
+ geminiCLI: 'gemini-prompts-aiwf-',
21
+ cursor: 'cursor-rules-',
22
+ windsurf: 'windsurf-rules-',
23
+ aiwf: 'aiwf-'
24
+ };
25
+
26
+ // Tool names for localization
27
+ const TOOL_NAMES = {
28
+ ko: {
29
+ aiwf: 'AIWF μ½”μ–΄ ꡬ쑰',
30
+ claudeCode: 'Claude Code λͺ…λ Ήμ–΄',
31
+ 'claude-code': 'Claude Code λͺ…λ Ήμ–΄',
32
+ geminiCLI: 'Gemini-CLI ν”„λ‘¬ν”„νŠΈ',
33
+ 'gemini-cli': 'Gemini-CLI ν”„λ‘¬ν”„νŠΈ',
34
+ cursor: 'Cursor IDE κ·œμΉ™',
35
+ windsurf: 'Windsurf IDE κ·œμΉ™'
36
+ },
37
+ en: {
38
+ aiwf: 'AIWF Core Structure',
39
+ claudeCode: 'Claude Code Commands',
40
+ 'claude-code': 'Claude Code Commands',
41
+ geminiCLI: 'Gemini-CLI Prompts',
42
+ 'gemini-cli': 'Gemini-CLI Prompts',
43
+ cursor: 'Cursor IDE Rules',
44
+ windsurf: 'Windsurf IDE Rules'
45
+ }
46
+ };
47
+
48
+ /**
49
+ * Get tool directory path
50
+ * @param {string} tool - Tool name
51
+ * @returns {string|null} Directory path or null
52
+ */
53
+ export function getToolDirectory(tool) {
54
+ return getToolDirectoryPath(tool);
55
+ }
56
+
57
+ /**
58
+ * Get localized tool name
59
+ * @param {string} tool - Tool name
60
+ * @param {string} language - Language code
61
+ * @returns {string} Localized tool name
62
+ */
63
+ export function getToolName(tool, language) {
64
+ return TOOL_NAMES[language]?.[tool] || tool;
65
+ }
66
+
67
+ /**
68
+ * Restore original path from backup filename
69
+ * @param {string} backupFileName - Backup file name
70
+ * @param {string} tool - Tool name
71
+ * @returns {string|null} Original path or null
72
+ */
73
+ function restoreOriginalPath(backupFileName, tool) {
74
+ const baseDir = getToolDirectory(tool);
75
+ if (!baseDir) return null;
76
+
77
+ let originalFileName = backupFileName;
78
+
79
+ // Remove tool-specific prefix
80
+ const prefix = BACKUP_PREFIXES[tool];
81
+ if (prefix && originalFileName.startsWith(prefix)) {
82
+ originalFileName = originalFileName.substring(prefix.length);
83
+ }
84
+
85
+ // Remove .bak extension
86
+ if (originalFileName.endsWith('.bak')) {
87
+ originalFileName = originalFileName.substring(0, originalFileName.length - 4);
88
+ }
89
+
90
+ // Replace hyphens with slashes (subdirectory structure)
91
+ originalFileName = originalFileName.replace(/-/g, '/');
92
+
93
+ return path.join(baseDir, originalFileName);
94
+ }
95
+
96
+ /**
97
+ * Get backup files for a specific tool
98
+ * @param {string} tool - Tool name
99
+ * @param {string} backupDir - Backup directory path
100
+ * @returns {Promise<Array>} Array of backup file info
101
+ */
102
+ async function getBackupFilesForTool(tool, backupDir) {
103
+ const toolBackupFiles = [];
104
+
105
+ try {
106
+ const backupFiles = await fs.readdir(backupDir);
107
+ const pattern = BACKUP_PATTERNS[tool];
108
+
109
+ if (pattern) {
110
+ const matchingFiles = backupFiles.filter(file => pattern.test(file));
111
+ for (const file of matchingFiles) {
112
+ toolBackupFiles.push({
113
+ backupFile: path.join(backupDir, file),
114
+ originalPath: restoreOriginalPath(file, tool)
115
+ });
116
+ }
117
+ }
118
+ } catch (error) {
119
+ // Backup directory doesn't exist or can't be read
120
+ }
121
+
122
+ return toolBackupFiles;
123
+ }
124
+
125
+ /**
126
+ * Restore a single backup file
127
+ * @param {Object} backup - Backup file info
128
+ * @returns {Promise<boolean>} Success status
129
+ */
130
+ async function restoreBackupFile(backup) {
131
+ try {
132
+ // Ensure target directory exists
133
+ const targetDir = path.dirname(backup.originalPath);
134
+ await fs.mkdir(targetDir, { recursive: true });
135
+
136
+ // Copy backup file to original location
137
+ await fs.copyFile(backup.backupFile, backup.originalPath);
138
+ return true;
139
+ } catch (error) {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Rollback a specific tool
146
+ * @param {string} tool - Tool name
147
+ * @param {string} backupDir - Backup directory path
148
+ * @param {string} language - Language code
149
+ * @returns {Promise<Object>} Rollback result
150
+ */
151
+ export async function rollbackTool(tool, backupDir, language = 'ko') {
152
+ const messages = getMessages(language);
153
+ const spinner = ora(
154
+ language === 'ko' ? `${tool} 도ꡬλ₯Ό λ‘€λ°±ν•˜λŠ” 쀑...` : `Rolling back ${tool}...`
155
+ ).start();
156
+
157
+ try {
158
+ // 1. Remove tool directory
159
+ const toolDir = getToolDirectory(tool);
160
+ if (toolDir) {
161
+ const dirExists = await fs
162
+ .access(toolDir)
163
+ .then(() => true)
164
+ .catch(() => false);
165
+ if (dirExists) {
166
+ await fs.rm(toolDir, { recursive: true, force: true });
167
+ spinner.text =
168
+ language === 'ko' ? `${tool} 디렉토리 μ‚­μ œ μ™„λ£Œ` : `${tool} directory removed`;
169
+ }
170
+ }
171
+
172
+ // 2. Restore from backup
173
+ const backupFiles = await getBackupFilesForTool(tool, backupDir);
174
+ let restoredCount = 0;
175
+
176
+ for (const backup of backupFiles) {
177
+ const restored = await restoreBackupFile(backup);
178
+ if (restored) {
179
+ restoredCount++;
180
+ }
181
+ }
182
+
183
+ if (restoredCount > 0) {
184
+ spinner.succeed(
185
+ chalk.green(
186
+ language === 'ko'
187
+ ? `${tool} λ‘€λ°± μ™„λ£Œ (${restoredCount}개 파일 볡원)`
188
+ : `${tool} rolled back successfully (${restoredCount} files restored)`
189
+ )
190
+ );
191
+ return { success: true, restoredCount };
192
+ } else {
193
+ spinner.warn(
194
+ chalk.yellow(
195
+ language === 'ko'
196
+ ? `${tool} λ‘€λ°±: 볡원할 λ°±μ—… 파일이 μ—†μŒ`
197
+ : `${tool} rollback: No backup files to restore`
198
+ )
199
+ );
200
+ return { success: true, restoredCount: 0 };
201
+ }
202
+ } catch (error) {
203
+ spinner.fail(
204
+ chalk.red(
205
+ language === 'ko'
206
+ ? `${tool} λ‘€λ°± μ‹€νŒ¨: ${error.message}`
207
+ : `Failed to rollback ${tool}: ${error.message}`
208
+ )
209
+ );
210
+ return { success: false, error: error.message };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Enhanced rollback function with backup directory
216
+ * @param {string} tool - Tool name
217
+ * @param {string} backupDir - Backup directory path
218
+ * @param {string} language - Language code
219
+ * @returns {Promise<boolean>} Success status
220
+ */
221
+ export async function rollbackToolEnhanced(tool, backupDir, language = 'ko') {
222
+ const spinner = ora(`Rolling back ${tool}...`).start();
223
+
224
+ try {
225
+ // Get tool directory mapping
226
+ const toolDir = getToolDirectory(tool);
227
+ if (!toolDir) {
228
+ spinner.fail(`Unknown tool: ${tool}`);
229
+ return false;
230
+ }
231
+
232
+ // Remove installed directory
233
+ try {
234
+ await fs.access(toolDir);
235
+ await fs.rm(toolDir, { recursive: true, force: true });
236
+ spinner.text = `Removed ${toolDir}`;
237
+ } catch (error) {
238
+ // Directory doesn't exist, skip
239
+ }
240
+
241
+ // Restore from backup if available
242
+ if (backupDir) {
243
+ const backupFiles = await getBackupFilesForTool(tool, backupDir);
244
+ for (const backup of backupFiles) {
245
+ await restoreBackupFile(backup);
246
+ spinner.text = `Restored ${backup.originalPath}`;
247
+ }
248
+ }
249
+
250
+ spinner.succeed(`${tool} rolled back successfully`);
251
+ return true;
252
+ } catch (error) {
253
+ spinner.fail(`Failed to rollback ${tool}: ${error.message}`);
254
+ return false;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Generate installation report
260
+ * @param {Object} validationResults - Validation results
261
+ * @param {string} language - Language code
262
+ * @param {string} installationType - Installation type
263
+ */
264
+ export function generateInstallationReport(
265
+ validationResults,
266
+ language,
267
+ installationType = 'install'
268
+ ) {
269
+ const messages = getMessages(language);
270
+
271
+ // Header
272
+ console.log(chalk.blue.bold('\n' + '='.repeat(60)));
273
+ console.log(
274
+ chalk.blue.bold(
275
+ language === 'ko'
276
+ ? 'πŸ” AIWF μ„€μΉ˜ 검증 및 리포트'
277
+ : 'πŸ” AIWF Installation Validation & Report'
278
+ )
279
+ );
280
+ console.log(chalk.blue.bold('='.repeat(60)));
281
+
282
+ // Installation type
283
+ const installTypeText = {
284
+ install: language === 'ko' ? 'μƒˆ μ„€μΉ˜' : 'Fresh Installation',
285
+ update: language === 'ko' ? 'μ—…λ°μ΄νŠΈ' : 'Update',
286
+ reinstall: language === 'ko' ? 'μž¬μ„€μΉ˜' : 'Reinstallation'
287
+ };
288
+ console.log(
289
+ chalk.gray(
290
+ `μ„€μΉ˜ μœ ν˜• / Installation Type: ${installTypeText[installationType] || installTypeText.install}`
291
+ )
292
+ );
293
+ console.log(chalk.gray(`검증 μ‹œκ°„ / Validation Time: ${new Date().toLocaleString()}`));
294
+
295
+ // Overall summary
296
+ const { overall } = validationResults;
297
+ const statusIcon = overall.success ? 'βœ…' : '❌';
298
+ const statusColor = overall.success ? chalk.green : chalk.red;
299
+
300
+ console.log(
301
+ statusColor(
302
+ `\n${statusIcon} 전체 μƒνƒœ / Overall Status: ${overall.success ? 'PASSED' : 'FAILED'}`
303
+ )
304
+ );
305
+ console.log(chalk.gray(` κ²€μ¦ν•œ 도ꡬ / Tools Checked: ${overall.toolsChecked}`));
306
+ console.log(chalk.gray(` μ„±κ³΅ν•œ 도ꡬ / Tools Passed: ${overall.toolsPassed}`));
307
+
308
+ if (overall.toolsFailed > 0) {
309
+ console.log(chalk.red(` μ‹€νŒ¨ν•œ 도ꡬ / Tools Failed: ${overall.toolsFailed}`));
310
+ console.log(chalk.red(` 총 이슈 수 / Total Issues: ${overall.totalIssues}`));
311
+ }
312
+
313
+ // Individual tool results
314
+ console.log(chalk.blue('\n=== κ°œλ³„ 도ꡬ κ²°κ³Ό / Individual Tool Results ==='));
315
+
316
+ for (const [toolName, result] of Object.entries(validationResults.tools)) {
317
+ const toolIcon = result.success ? 'βœ…' : '❌';
318
+ const toolColor = result.success ? chalk.green : chalk.red;
319
+
320
+ console.log(toolColor(`\n${toolIcon} ${result.description} (${toolName})`));
321
+ console.log(chalk.gray(` 디렉토리 / Directory: ${result.baseDir}`));
322
+ console.log(chalk.gray(` 파일 수 / Files Found: ${result.checks.fileCount}`));
323
+ console.log(
324
+ chalk.gray(` 총 크기 / Total Size: ${(result.checks.totalSize / 1024).toFixed(2)} KB`)
325
+ );
326
+
327
+ if (result.checks.issues.length > 0) {
328
+ console.log(chalk.red(` 이슈 / Issues:`));
329
+ for (const issue of result.checks.issues) {
330
+ console.log(chalk.red(` β€’ ${issue}`));
331
+ }
332
+ }
333
+
334
+ if (result.checks.requiredFiles.length > 0) {
335
+ console.log(chalk.gray(` ν•„μˆ˜ 파일 / Required Files:`));
336
+ for (const fileCheck of result.checks.requiredFiles) {
337
+ const fileIcon = fileCheck.valid ? 'βœ…' : '❌';
338
+ const fileColor = fileCheck.valid ? chalk.gray : chalk.red;
339
+ console.log(
340
+ fileColor(
341
+ ` ${fileIcon} ${fileCheck.file} (${(fileCheck.size / 1024).toFixed(2)} KB)`
342
+ )
343
+ );
344
+ }
345
+ }
346
+ }
347
+
348
+ // Recommendations
349
+ console.log(chalk.blue('\n=== ꢌμž₯사항 / Recommendations ==='));
350
+ if (overall.success) {
351
+ console.log(chalk.green('πŸŽ‰ λͺ¨λ“  도ꡬ가 μ˜¬λ°”λ₯΄κ²Œ μ„€μΉ˜λ˜μ–΄ κ²€μ¦λ˜μ—ˆμŠ΅λ‹ˆλ‹€!'));
352
+ console.log(chalk.green('πŸŽ‰ All tools are properly installed and validated!'));
353
+ console.log(chalk.blue('\nλ‹€μŒ 단계 / Next Steps:'));
354
+ console.log(chalk.white(' 1. Claude Codeμ—μ„œ ν”„λ‘œμ νŠΈλ₯Ό μ—΄μ–΄λ³΄μ„Έμš”'));
355
+ console.log(chalk.white(' 2. /project:aiwf:initialize λͺ…λ Ήμ–΄λ‘œ ν”„λ‘œμ νŠΈλ₯Ό μ΄ˆκΈ°ν™”ν•˜μ„Έμš”'));
356
+ console.log(chalk.white(' 3. ν”„λ‘œμ νŠΈ 관리λ₯Ό μ‹œμž‘ν•˜μ„Έμš”!'));
357
+ } else {
358
+ console.log(chalk.yellow('⚠️ 일뢀 μ΄μŠˆκ°€ λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μœ„μ˜ 세뢀사항을 ν™•μΈν•˜μ„Έμš”.'));
359
+ console.log(chalk.yellow('⚠️ Some issues were found. See details above.'));
360
+
361
+ // Solutions for failed tools
362
+ const failedTools = Object.entries(validationResults.tools)
363
+ .filter(([_, result]) => !result.success)
364
+ .map(([tool, _]) => tool);
365
+
366
+ if (failedTools.length > 0) {
367
+ console.log(chalk.blue('\nν•΄κ²° λ°©μ•ˆ / Solutions:'));
368
+ console.log(chalk.yellow(' β€’ 전체 μž¬μ„€μΉ˜λ₯Ό μ‹œλ„ν•΄λ³΄μ„Έμš”'));
369
+ console.log(chalk.yellow(' β€’ μ‹€νŒ¨ν•œ λ„κ΅¬λ§Œ λ‘€λ°± ν›„ λ‹€μ‹œ μ„€μΉ˜ν•˜μ„Έμš”'));
370
+ console.log(chalk.yellow(' β€’ λ„€νŠΈμ›Œν¬ 연결을 ν™•μΈν•˜κ³  λ‹€μ‹œ μ‹œλ„ν•˜μ„Έμš”'));
371
+ }
372
+ }
373
+
374
+ console.log(chalk.blue.bold('\n' + '='.repeat(60)));
375
+ }
376
+
377
+ /**
378
+ * Enhanced installation report generation
379
+ * @param {Object} results - Installation results
380
+ * @param {string} language - Language code
381
+ */
382
+ export function generateInstallationReportEnhanced(results, language) {
383
+ const msg = getMessages(language);
384
+
385
+ console.log(
386
+ '\n' + chalk.bold(language === 'ko' ? 'πŸ“‹ μ„€μΉ˜ 리포트' : 'πŸ“‹ Installation Report')
387
+ );
388
+ console.log('─'.repeat(50));
389
+
390
+ if (results.success.length > 0) {
391
+ console.log(
392
+ chalk.green(
393
+ language === 'ko' ? 'βœ… μ„±κ³΅μ μœΌλ‘œ μ„€μΉ˜λœ 도ꡬ:' : 'βœ… Successfully Installed Tools:'
394
+ )
395
+ );
396
+ results.success.forEach(tool => {
397
+ console.log(chalk.green(` βœ“ ${getToolName(tool, language)}`));
398
+ });
399
+ }
400
+
401
+ if (results.failed.length > 0) {
402
+ console.log(
403
+ chalk.red(language === 'ko' ? '\n❌ μ„€μΉ˜ μ‹€νŒ¨ν•œ 도ꡬ:' : '\n❌ Failed to Install:')
404
+ );
405
+ results.failed.forEach(({ tool, reason }) => {
406
+ console.log(chalk.red(` βœ— ${getToolName(tool, language)}: ${reason}`));
407
+ });
408
+ }
409
+
410
+ if (results.warnings.length > 0) {
411
+ console.log(chalk.yellow(language === 'ko' ? '\n⚠️ 경고:' : '\n⚠️ Warnings:'));
412
+ results.warnings.forEach(warning => {
413
+ console.log(chalk.yellow(` ⚠ ${warning}`));
414
+ });
415
+ }
416
+
417
+ console.log('─'.repeat(50));
418
+ }