aiwf 0.3.3 → 0.3.5

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 (208) hide show
  1. package/.aiwf/config/language.json +1 -1
  2. package/.aiwf/metrics/metrics.json +18 -0
  3. package/.aiwf/personas/current.json +5 -0
  4. package/.aiwf/sessions/session_1752877859489_1t7n2fhdb.json +7 -0
  5. package/.aiwf/sessions/session_1752877859515_cwdv211ry.json +7 -0
  6. package/.aiwf/sessions/session_1752877860664_huziadmbo.json +7 -0
  7. package/.aiwf/sessions/session_1752877860666_kyr5u1np6.json +7 -0
  8. package/.aiwf/sessions/session_1752877861848_9yinepw3t.json +7 -0
  9. package/.aiwf/sessions/session_1752877861874_y32sputuc.json +7 -0
  10. package/.aiwf/sessions/session_1752877862685_ivxbl5rhz.json +7 -0
  11. package/.aiwf/sessions/session_1752877862820_wnq4glvxi.json +7 -0
  12. package/.aiwf/sessions/session_1752877864632_3ebnpaelb.json +7 -0
  13. package/.aiwf/sessions/session_1752877864879_b9zgjcb7d.json +7 -0
  14. package/.aiwf/sessions/session_1752877865533_x14m1vodi.json +7 -0
  15. package/.aiwf/sessions/session_1752877865562_gkoh19n7d.json +7 -0
  16. package/.aiwf/sessions/session_1752877866333_wp8qpdcoh.json +7 -0
  17. package/.aiwf/sessions/session_1752877866509_dawntfpab.json +7 -0
  18. package/.aiwf/sessions/session_1752877866890_plc5vfucl.json +7 -0
  19. package/.aiwf/sessions/session_1752877866895_ga8hmq3oi.json +7 -0
  20. package/.aiwf/sessions/session_1752877867761_ayjp4v5f5.json +7 -0
  21. package/.aiwf/sessions/session_1752877867892_lfz6jy252.json +7 -0
  22. package/.aiwf/sessions/session_1752877868139_7bqd9ubli.json +7 -0
  23. package/.aiwf/sessions/session_1752877868162_zq3ofjbsh.json +7 -0
  24. package/.aiwf/sessions/session_1752877869054_7rlgm9jt6.json +7 -0
  25. package/.aiwf/sessions/session_1752877869477_ax3nhbxzr.json +7 -0
  26. package/.aiwf/sessions/session_1752877870095_6hgp4vl1e.json +7 -0
  27. package/.aiwf/sessions/session_1752877870381_qfcrdg4li.json +7 -0
  28. package/.aiwf/sessions/session_1752877871895_pgd5vllhz.json +7 -0
  29. package/.aiwf/sessions/session_1752877872199_jbhv2g2da.json +7 -0
  30. package/.aiwf/sessions/session_1752877872680_x04wpffu6.json +7 -0
  31. package/.aiwf/sessions/session_1752877872895_3y4vbeauo.json +7 -0
  32. package/.aiwf/sessions/session_1752877873178_3maolzc6b.json +7 -0
  33. package/.aiwf/sessions/session_1752877873180_ytm83j07f.json +7 -0
  34. package/.aiwf/sessions/session_1752877874217_dp56c9t42.json +7 -0
  35. package/.aiwf/sessions/session_1752877874415_epwp1g1og.json +7 -0
  36. package/.aiwf/sessions/session_1752877874454_heicyuryb.json +7 -0
  37. package/.aiwf/sessions/session_1752877874869_1c2illeu5.json +7 -0
  38. package/.aiwf/sessions/session_1752877874879_k1vo4vxyx.json +7 -0
  39. package/.aiwf/sessions/session_1752877874913_bwgyiz9bp.json +7 -0
  40. package/.aiwf/sessions/session_1752877876218_44807kztz.json +7 -0
  41. package/.aiwf/sessions/session_1752877876515_qkhjv5kpw.json +7 -0
  42. package/.aiwf/sessions/session_1752877876543_p605aud1m.json +7 -0
  43. package/.aiwf/sessions/session_1752877876819_l65w7ifck.json +7 -0
  44. package/.aiwf/sessions/session_1752877876867_xrhzss4we.json +7 -0
  45. package/.aiwf/sessions/session_1752877876872_u7aso54qb.json +7 -0
  46. package/.aiwf/sessions/session_1752877877705_telnfg4o6.json +7 -0
  47. package/.aiwf/sessions/session_1752877878285_6s45im2ib.json +7 -0
  48. package/.aiwf/sessions/session_1752877878506_v0dvguqhz.json +7 -0
  49. package/.aiwf/sessions/session_1752877878581_3ri3yc66a.json +7 -0
  50. package/.aiwf/sessions/session_1752877878756_a82x9s2pd.json +7 -0
  51. package/.aiwf/sessions/session_1752877878886_20pjsfour.json +7 -0
  52. package/.aiwf/sessions/session_1752877880266_fm1ydjw0g.json +7 -0
  53. package/.aiwf/sessions/session_1752877881149_xbat4z03d.json +7 -0
  54. package/.aiwf/sessions/session_1752877881551_qy5sic9qs.json +7 -0
  55. package/.aiwf/sessions/session_1752877881554_24ugd96wu.json +7 -0
  56. package/.aiwf/sessions/session_1752877881797_e9mm25uh0.json +7 -0
  57. package/.aiwf/sessions/session_1752877881822_bxvjj8c9g.json +7 -0
  58. package/.aiwf/sessions/session_1752877881890_jv7l6jve3.json +7 -0
  59. package/.aiwf/sessions/session_1752877881991_f0qugdtcq.json +7 -0
  60. package/.aiwf/sessions/session_1752877883387_0bvw9rvnx.json +7 -0
  61. package/.aiwf/sessions/session_1752877883820_q6g2md2up.json +7 -0
  62. package/.aiwf/sessions/session_1752877884393_sqr7trvjl.json +7 -0
  63. package/.aiwf/sessions/session_1752877884500_whtypmzlf.json +7 -0
  64. package/.aiwf/sessions/session_1752877884645_y9mvvxli1.json +7 -0
  65. package/.aiwf/sessions/session_1752877884733_hk46eenel.json +7 -0
  66. package/.aiwf/sessions/session_1752877884738_c62j6214r.json +7 -0
  67. package/.aiwf/sessions/session_1752877884771_temhxehoz.json +7 -0
  68. package/.aiwf/sessions/session_1752877885643_6v5fcktc7.json +7 -0
  69. package/.aiwf/sessions/session_1752877886426_ee8di2fqh.json +7 -0
  70. package/.aiwf/sessions/session_1752877886549_3xj6tdoew.json +7 -0
  71. package/.aiwf/sessions/session_1752877886604_aj80yyfco.json +7 -0
  72. package/.aiwf/sessions/session_1752877886702_2b8841vyg.json +7 -0
  73. package/.aiwf/sessions/session_1752877886712_ntqc8b8bz.json +7 -0
  74. package/.aiwf/sessions/session_1752877886968_946m8tbw6.json +7 -0
  75. package/.aiwf/sessions/session_1752877887008_wg6zfg8dn.json +7 -0
  76. package/.aiwf/sessions/session_1752877888479_ciiu2qiqs.json +7 -0
  77. package/.aiwf/sessions/session_1752877889330_ravg8l66k.json +7 -0
  78. package/.aiwf/sessions/session_1752877889542_7zepkhjkc.json +7 -0
  79. package/.aiwf/sessions/session_1752877889638_ev4htqiu0.json +7 -0
  80. package/.aiwf/sessions/session_1752877889956_szphy2qma.json +7 -0
  81. package/.aiwf/sessions/session_1752877890220_o6aaa57tt.json +7 -0
  82. package/.aiwf/sessions/session_1752877890527_v2loy5vu1.json +7 -0
  83. package/.aiwf/sessions/session_1752877890572_cwq8fiuma.json +7 -0
  84. package/.aiwf/sessions/session_1752877892200_sgcwq1alt.json +7 -0
  85. package/.aiwf/sessions/session_1752877892268_v8kexpbc5.json +7 -0
  86. package/.aiwf/sessions/session_1752877892384_1gbbgrp4y.json +7 -0
  87. package/.aiwf/sessions/session_1752877893568_9p2n5p31a.json +7 -0
  88. package/.aiwf/sessions/session_1752877893727_un34a5tvp.json +7 -0
  89. package/.aiwf/sessions/session_1752877893778_01j79sw9i.json +7 -0
  90. package/.aiwf/sessions/session_1752877894053_jrhba6wko.json +7 -0
  91. package/.aiwf/sessions/session_1752877894082_x3bghawox.json +7 -0
  92. package/.aiwf/sessions/session_1752877894500_iytiav2ku.json +7 -0
  93. package/.aiwf/sessions/session_1752877894667_ucn0gc2ge.json +7 -0
  94. package/.aiwf/test-results-s02.json +4 -4
  95. package/.claude/commands/aiwf/aiwf_changelog.md +82 -0
  96. package/.claude/commands/aiwf/aiwf_code_review.md +88 -0
  97. package/.claude/commands/aiwf/aiwf_commit.md +160 -0
  98. package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +53 -0
  99. package/.claude/commands/aiwf/aiwf_compress_context.md +116 -0
  100. package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +48 -0
  101. package/.claude/commands/aiwf/aiwf_compress_stats.md +79 -0
  102. package/.claude/commands/aiwf/aiwf_compress_with_persona.md +51 -0
  103. package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +54 -0
  104. package/.claude/commands/aiwf/aiwf_create_general_task.md +147 -0
  105. package/.claude/commands/aiwf/aiwf_create_milestone_plan.md +194 -0
  106. package/.claude/commands/aiwf/aiwf_create_prd.md +280 -0
  107. package/.claude/commands/aiwf/aiwf_create_sprint_tasks.md +189 -0
  108. package/.claude/commands/aiwf/aiwf_create_sprints_from_milestone.md +121 -0
  109. package/.claude/commands/aiwf/aiwf_default_mode.md +44 -0
  110. package/.claude/commands/aiwf/aiwf_discuss_review.md +29 -0
  111. package/.claude/commands/aiwf/aiwf_do_task.md +109 -0
  112. package/.claude/commands/aiwf/aiwf_docs.md +225 -0
  113. package/.claude/commands/aiwf/aiwf_evaluate.md +92 -0
  114. package/.claude/commands/aiwf/aiwf_get_feature_details.md +78 -0
  115. package/.claude/commands/aiwf/aiwf_infinite.md +202 -0
  116. package/.claude/commands/aiwf/aiwf_initialize.md +134 -0
  117. package/.claude/commands/aiwf/aiwf_issue_create.md +65 -0
  118. package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +57 -0
  119. package/.claude/commands/aiwf/aiwf_list_features.md +54 -0
  120. package/.claude/commands/aiwf/aiwf_mermaid.md +272 -0
  121. package/.claude/commands/aiwf/aiwf_persona_architect.md +48 -0
  122. package/.claude/commands/aiwf/aiwf_persona_backend.md +37 -0
  123. package/.claude/commands/aiwf/aiwf_persona_data_analyst.md +37 -0
  124. package/.claude/commands/aiwf/aiwf_persona_frontend.md +37 -0
  125. package/.claude/commands/aiwf/aiwf_persona_security.md +48 -0
  126. package/.claude/commands/aiwf/aiwf_persona_status.md +50 -0
  127. package/.claude/commands/aiwf/aiwf_pr_create.md +76 -0
  128. package/.claude/commands/aiwf/aiwf_prime.md +9 -0
  129. package/.claude/commands/aiwf/aiwf_project_review.md +261 -0
  130. package/.claude/commands/aiwf/aiwf_switch_language.md +160 -0
  131. package/.claude/commands/aiwf/aiwf_test.md +124 -0
  132. package/.claude/commands/aiwf/aiwf_testing_review.md +198 -0
  133. package/.claude/commands/aiwf/aiwf_tm-run-all-subtask.md +210 -0
  134. package/.claude/commands/aiwf/aiwf_ultrathink_code_advanced.md +460 -0
  135. package/.claude/commands/aiwf/aiwf_ultrathink_code_basic.md +165 -0
  136. package/.claude/commands/aiwf/aiwf_ultrathink_general.md +305 -0
  137. package/.claude/commands/aiwf/aiwf_update_docs_kr.md +25 -0
  138. package/.claude/commands/aiwf/aiwf_update_feature_status.md +53 -0
  139. package/.claude/commands/aiwf/aiwf_yolo.md +300 -0
  140. package/.eslintrc.json +26 -0
  141. package/.prettierrc +10 -0
  142. package/CHANGELOG.md +54 -5
  143. package/CLAUDE.md +154 -54
  144. package/README.ko.md +25 -0
  145. package/README.md +25 -0
  146. package/TEST_RECOVERY_PLAN.md +68 -0
  147. package/claude-code/aiwf/ko/commands/ai-persona.js +1 -1
  148. package/docs/CLEANUP_RECOMMENDATIONS.md +86 -0
  149. package/docs/CLI_USAGE_GUIDE.md +479 -0
  150. package/package.json +9 -9
  151. package/scripts/update-feature-status.js +70 -0
  152. package/{lib → src/cli}/cache-cli.js +4 -4
  153. package/src/cli/index.js +206 -0
  154. package/{language-cli.js → src/cli/language-cli.js} +11 -4
  155. package/src/commands/cache-templates.js +209 -0
  156. package/src/commands/create-offline.js +86 -0
  157. package/src/commands/create-project.js +151 -0
  158. package/src/commands/persona.js +288 -0
  159. package/src/lib/backup-manager.js +270 -0
  160. package/src/lib/file-downloader.js +208 -0
  161. package/src/lib/installer.js +676 -0
  162. package/src/lib/rollback-manager.js +418 -0
  163. package/src/lib/validator.js +561 -0
  164. package/src/utils/git-integration.js +173 -0
  165. package/{language-utils.js → src/utils/language-utils.js} +2 -1
  166. package/src/utils/messages.js +136 -0
  167. package/src/utils/paths.js +130 -0
  168. package/tests/application.test.js +2 -2
  169. package/tests/installer.test.js +2 -2
  170. package/tests/integration/ai-persona-system.test.js +4 -4
  171. package/tests/integration/test-persona-system/docs/README.md +1 -0
  172. package/tests/integration/test-persona-system/package.json +1 -0
  173. package/tests/integration/test-persona-system/src/auth.js +1 -0
  174. package/tests/integration/test-persona-system/src/index.js +1 -0
  175. package/tests/integration/test-persona-system/test/test.js +1 -0
  176. package/tests/language-utils.test.js +1 -1
  177. package/tests/temp_invalid_json/invalid.json +1 -0
  178. package/tests/unit/ai-persona-manager.test.js +7 -7
  179. package/.aiwf/05_DOCUMENTATION/performance-report.md +0 -33
  180. package/.aiwf/test-cache/86c7f9e80251abb8bedd58953ab0d1779e46b4f43ea33b63bffdb778106373b0.json +0 -6
  181. package/.aiwf/test-cache/9e252a90b80fce0585d7ed0646e7a9b4343e109f661f42a3ae678ba1ec5e22c7.json +0 -7
  182. package/.aiwf/token-data/alerts.json +0 -293
  183. package/.aiwf/token-data/monitor-config.json +0 -12
  184. package/CHANGELOG_M02.md +0 -52
  185. package/claude-code/aiwf/ko/templates/web-app/template/package-lock.json +0 -10022
  186. package/claude-code/aiwf/ko/token-data/alerts.json +0 -293
  187. package/claude-code/aiwf/ko/token-data/monitor-config.json +0 -12
  188. package/docs/m02-dod-checklist.md +0 -125
  189. package/docs/m02-gap-analysis.md +0 -214
  190. package/docs/m02-milestone-completion-report.md +0 -186
  191. package/docs/m02-milestone-executive-summary.md +0 -68
  192. package/index.js +0 -1951
  193. package/lib/cache-integration.js +0 -401
  194. package/lib/file-batch-processor.js +0 -401
  195. package/lib/performance-benchmark.js +0 -621
  196. package/lib/template-manager.js +0 -310
  197. package/scripts/validate-commands.cjs +0 -338
  198. /package/{lib → src/lib}/ai-persona-manager.js +0 -0
  199. /package/{lib → src/lib}/cache-system.js +0 -0
  200. /package/{lib → src/lib}/context-engine.js +0 -0
  201. /package/{lib → src/lib}/memory-profiler.js +0 -0
  202. /package/{lib → src/lib}/metrics-collector.js +0 -0
  203. /package/{lib → src/lib}/offline-detector.js +0 -0
  204. /package/{lib → src/lib}/task-analyzer.js +0 -0
  205. /package/{lib → src/lib}/template-cache-system.js +0 -0
  206. /package/{lib → src/lib}/template-downloader.js +0 -0
  207. /package/{lib → src/lib}/template-version-manager.js +0 -0
  208. /package/{lib → src/lib}/token-optimizer.js +0 -0
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AI 페르소나 명령어
5
+ * AI 행동 페르소나 관리 및 전환
6
+ */
7
+
8
+ import { AIPersonaManager } from '../lib/ai-persona-manager.js';
9
+ import path from 'path';
10
+ import chalk from 'chalk';
11
+ import { fileURLToPath } from 'url';
12
+ import { dirname } from 'path';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ class PersonaCommand {
18
+ constructor() {
19
+ this.personaManager = null;
20
+ }
21
+
22
+ /**
23
+ * 페르소나 매니저 초기화
24
+ */
25
+ async initPersonaManager() {
26
+ if (!this.personaManager) {
27
+ this.personaManager = new AIPersonaManager({
28
+ personaConfigPath: path.join(process.cwd(), '.aiwf', 'personas'),
29
+ metricsPath: path.join(process.cwd(), '.aiwf', 'metrics'),
30
+ metricsEnabled: true,
31
+ autoDetectionEnabled: true
32
+ });
33
+
34
+ await this.personaManager.init();
35
+ }
36
+
37
+ return this.personaManager;
38
+ }
39
+
40
+ /**
41
+ * 페르소나 상태 표시
42
+ */
43
+ async showStatus() {
44
+ try {
45
+ // 파일에서 현재 페르소나 읽기
46
+ const fs = await import('fs/promises');
47
+ const path = await import('path');
48
+ const personaFile = path.join(process.cwd(), '.aiwf', 'personas', 'current.json');
49
+
50
+ try {
51
+ const content = await fs.readFile(personaFile, 'utf-8');
52
+ const currentPersona = JSON.parse(content);
53
+
54
+ console.log(chalk.cyan('🎭 현재 AI 페르소나 상태'));
55
+ console.log(chalk.gray('-'.repeat(50)));
56
+ console.log(`현재 페르소나: ${chalk.yellow(currentPersona.name)}`);
57
+ console.log(`설명: ${currentPersona.description}`);
58
+ console.log(`활성화 시간: ${new Date(currentPersona.activatedAt).toLocaleString('ko-KR')}`);
59
+
60
+ const behaviors = this.getKoreanBehaviors(currentPersona.name);
61
+ if (behaviors.length > 0) {
62
+ console.log('\n주요 동작:');
63
+ behaviors.forEach(behavior => {
64
+ console.log(` • ${behavior}`);
65
+ });
66
+ }
67
+
68
+ console.log(chalk.gray('\n' + '-'.repeat(50)));
69
+ console.log(chalk.green('✅ 페르소나가 활성화되어 있습니다.'));
70
+
71
+ } catch (fileError) {
72
+ console.log(chalk.yellow('⚠️ 현재 활성화된 페르소나가 없습니다.'));
73
+ console.log(chalk.gray('사용 가능한 페르소나 목록을 보려면: aiwf persona list'));
74
+ console.log(chalk.gray('페르소나를 설정하려면: aiwf persona set <페르소나명>'));
75
+ }
76
+
77
+ } catch (error) {
78
+ console.error(chalk.red('❌ 페르소나 상태 확인 중 오류가 발생했습니다:'), error.message);
79
+ console.error(error.stack);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * 페르소나 목록 표시
85
+ */
86
+ async listPersonas() {
87
+ try {
88
+ console.log(chalk.cyan('🎭 사용 가능한 AI 페르소나'));
89
+ console.log(chalk.gray('-'.repeat(50)));
90
+
91
+ // 기본 페르소나 목록을 하드코딩으로 표시 (초기화 문제 회피)
92
+ const defaultPersonas = [
93
+ { name: 'architect', description: '시스템 설계 및 아키텍처 전문가' },
94
+ { name: 'developer', description: '일반 개발자 (기본값)' },
95
+ { name: 'reviewer', description: '코드 리뷰 및 품질 관리 전문가' },
96
+ { name: 'debugger', description: '디버깅 및 문제 해결 전문가' },
97
+ { name: 'optimizer', description: '성능 최적화 전문가' },
98
+ { name: 'security', description: '보안 및 취약점 분석 전문가' },
99
+ { name: 'documenter', description: '문서화 및 기술 작성 전문가' }
100
+ ];
101
+
102
+ defaultPersonas.forEach(persona => {
103
+ const displayName = chalk.yellow(persona.name);
104
+ console.log(` ${displayName} - ${persona.description}`);
105
+ });
106
+
107
+ console.log(chalk.gray('\n' + '-'.repeat(50)));
108
+ console.log(chalk.gray('사용법: aiwf persona set <페르소나명>'));
109
+
110
+ } catch (error) {
111
+ console.error(chalk.red('❌ 페르소나 목록 조회 중 오류가 발생했습니다:'), error.message);
112
+ console.error(error.stack);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * 페르소나 전환
118
+ */
119
+ async setPersona(personaName) {
120
+ if (!personaName) {
121
+ console.error(chalk.red('❌ 페르소나 이름을 지정해주세요.'));
122
+ console.log(chalk.gray('사용법: aiwf persona set <페르소나명>'));
123
+ console.log(chalk.gray('사용 가능한 페르소나: aiwf persona list'));
124
+ return;
125
+ }
126
+
127
+ try {
128
+ // 유효한 페르소나인지 확인
129
+ const validPersonas = ['architect', 'developer', 'reviewer', 'debugger', 'optimizer', 'security', 'documenter'];
130
+
131
+ if (!validPersonas.includes(personaName)) {
132
+ console.error(chalk.red(`❌ 페르소나 '${personaName}'를 찾을 수 없습니다.`));
133
+ console.log(chalk.gray('사용 가능한 페르소나: aiwf persona list'));
134
+ return;
135
+ }
136
+
137
+ // 간단한 페르소나 설정 (파일 기반)
138
+ const fs = await import('fs/promises');
139
+ const path = await import('path');
140
+ const personaDir = path.join(process.cwd(), '.aiwf', 'personas');
141
+
142
+ // 디렉토리 생성
143
+ await fs.mkdir(personaDir, { recursive: true });
144
+
145
+ // 현재 페르소나 저장
146
+ const personaConfig = {
147
+ name: personaName,
148
+ description: this.getKoreanDescription(personaName),
149
+ activatedAt: new Date().toISOString()
150
+ };
151
+
152
+ await fs.writeFile(
153
+ path.join(personaDir, 'current.json'),
154
+ JSON.stringify(personaConfig, null, 2)
155
+ );
156
+
157
+ console.log(chalk.green('🎭 AI 페르소나 활성화 완료'));
158
+ console.log(chalk.gray('-'.repeat(50)));
159
+ console.log(`현재 페르소나: ${chalk.yellow(personaName)}`);
160
+ console.log(`전문 분야: ${this.getKoreanDescription(personaName)}`);
161
+ console.log(chalk.gray('-'.repeat(50)));
162
+ console.log(chalk.green('✅ 페르소나가 성공적으로 활성화되었습니다.'));
163
+
164
+ } catch (error) {
165
+ console.error(chalk.red('❌ 페르소나 전환 중 오류가 발생했습니다:'), error.message);
166
+ console.error(error.stack);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * 페르소나 리셋
172
+ */
173
+ async resetPersona() {
174
+ try {
175
+ // 기본 페르소나(developer)로 리셋
176
+ await this.setPersona('developer');
177
+ console.log(chalk.green('🔄 페르소나가 기본값(developer)으로 리셋되었습니다.'));
178
+
179
+ } catch (error) {
180
+ console.error(chalk.red('❌ 페르소나 리셋 중 오류가 발생했습니다:'), error.message);
181
+ console.error(error.stack);
182
+ }
183
+ }
184
+
185
+ /**
186
+ * 명령어 실행
187
+ */
188
+ async execute(args) {
189
+ const [subcommand, ...restArgs] = args;
190
+
191
+ switch (subcommand) {
192
+ case 'status':
193
+ await this.showStatus();
194
+ break;
195
+ case 'list':
196
+ await this.listPersonas();
197
+ break;
198
+ case 'set':
199
+ await this.setPersona(restArgs[0]);
200
+ break;
201
+ case 'reset':
202
+ await this.resetPersona();
203
+ break;
204
+ default:
205
+ this.showHelp();
206
+ break;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * 도움말 표시
212
+ */
213
+ showHelp() {
214
+ console.log(chalk.cyan('🎭 AIWF 페르소나 관리'));
215
+ console.log(chalk.gray('-'.repeat(50)));
216
+ console.log('사용법: aiwf persona <명령어> [옵션]');
217
+ console.log('');
218
+ console.log('명령어:');
219
+ console.log(' status 현재 페르소나 상태 표시');
220
+ console.log(' list 사용 가능한 페르소나 목록');
221
+ console.log(' set <페르소나명> 특정 페르소나로 전환');
222
+ console.log(' reset 기본 페르소나로 리셋');
223
+ console.log('');
224
+ console.log('예시:');
225
+ console.log(' aiwf persona status');
226
+ console.log(' aiwf persona list');
227
+ console.log(' aiwf persona set architect');
228
+ console.log(' aiwf persona reset');
229
+ }
230
+
231
+ /**
232
+ * 한국어 설명 가져오기
233
+ */
234
+ getKoreanDescription(persona) {
235
+ const descriptions = {
236
+ architect: '시스템 설계 및 아키텍처 전문가',
237
+ developer: '일반 개발자 (기본값)',
238
+ reviewer: '코드 리뷰 및 품질 관리 전문가',
239
+ debugger: '디버깅 및 문제 해결 전문가',
240
+ optimizer: '성능 최적화 전문가',
241
+ security: '보안 및 취약점 분석 전문가',
242
+ devops: 'DevOps 및 인프라 관리 전문가',
243
+ tester: '테스트 및 QA 전문가',
244
+ documenter: '문서화 및 기술 작성 전문가',
245
+ mentor: '멘토링 및 교육 전문가'
246
+ };
247
+
248
+ return descriptions[persona] || '설명 없음';
249
+ }
250
+
251
+ /**
252
+ * 한국어 동작 설명 가져오기
253
+ */
254
+ getKoreanBehaviors(persona) {
255
+ const behaviors = {
256
+ architect: [
257
+ '큰 그림과 전체 시스템 구조에 집중',
258
+ '확장성과 유지보수성 우선시',
259
+ '디자인 패턴과 아키텍처 원칙 적용',
260
+ '통합 지점과 인터페이스 고려'
261
+ ],
262
+ developer: [
263
+ '실용적이고 효율적인 코드 작성',
264
+ '베스트 프랙티스 적용',
265
+ '가독성과 유지보수성 중시'
266
+ ],
267
+ reviewer: [
268
+ '코드 품질과 표준 준수 확인',
269
+ '잠재적 버그와 개선점 식별',
270
+ '성능과 보안 측면 검토'
271
+ ],
272
+ debugger: [
273
+ '문제의 근본 원인 분석',
274
+ '체계적인 디버깅 접근',
275
+ '재현 가능한 테스트 케이스 생성'
276
+ ],
277
+ optimizer: [
278
+ '성능 병목 지점 식별',
279
+ '메모리 사용량 최적화',
280
+ '알고리즘 효율성 개선'
281
+ ]
282
+ };
283
+
284
+ return behaviors[persona] || ['기본 동작'];
285
+ }
286
+ }
287
+
288
+ export default PersonaCommand;
@@ -0,0 +1,270 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import {
6
+ TOOL_DIRS,
7
+ AIWF_DIRS,
8
+ FILES,
9
+ getBackupDirName as getBackupDirNamePath
10
+ } from '../utils/paths.js';
11
+
12
+ let BACKUP_DIR = null;
13
+
14
+ /**
15
+ * Generate backup directory name with timestamp
16
+ * @returns {string} Backup directory name
17
+ */
18
+ function getBackupDirName() {
19
+ return getBackupDirNamePath();
20
+ }
21
+
22
+ /**
23
+ * Backup a single file with proper naming convention
24
+ * @param {string} filePath - File to backup
25
+ * @param {string} backupDir - Backup directory path
26
+ * @returns {Promise<string|null>} Backup file path or null
27
+ */
28
+ async function backupFile(filePath, backupDir) {
29
+ try {
30
+ const exists = await fs.access(filePath).then(() => true).catch(() => false);
31
+ if (exists) {
32
+ await fs.mkdir(backupDir, { recursive: true });
33
+ const backupPath = `${filePath}.bak`;
34
+ await fs.copyFile(filePath, backupPath);
35
+
36
+ // Tool-specific backup file naming rules
37
+ let bakFileName;
38
+ if (filePath.startsWith(TOOL_DIRS.CLAUDE_COMMANDS + '/')) {
39
+ // Claude Code command files
40
+ const relativePath = filePath.substring(TOOL_DIRS.CLAUDE_COMMANDS.length + 1);
41
+ bakFileName = `claude-commands-aiwf-${relativePath.replace(/\//g, '-')}.bak`;
42
+ } else if (filePath.startsWith(TOOL_DIRS.GEMINI_PROMPTS + '/')) {
43
+ // Gemini CLI prompt files
44
+ const relativePath = filePath.substring(TOOL_DIRS.GEMINI_PROMPTS.length + 1);
45
+ bakFileName = `gemini-prompts-aiwf-${relativePath.replace(/\//g, '-')}.bak`;
46
+ } else if (filePath.startsWith(TOOL_DIRS.CURSOR_RULES + '/')) {
47
+ // Cursor rule files
48
+ const relativePath = filePath.substring(TOOL_DIRS.CURSOR_RULES.length + 1);
49
+ bakFileName = `cursor-rules-${relativePath.replace(/\//g, '-')}.bak`;
50
+ } else if (filePath.startsWith(TOOL_DIRS.WINDSURF_RULES + '/')) {
51
+ // Windsurf rule files
52
+ const relativePath = filePath.substring(TOOL_DIRS.WINDSURF_RULES.length + 1);
53
+ bakFileName = `windsurf-rules-${relativePath.replace(/\//g, '-')}.bak`;
54
+ } else if (filePath.startsWith(AIWF_DIRS.ROOT + '/')) {
55
+ // AIWF core files
56
+ const relativePath = filePath.substring(AIWF_DIRS.ROOT.length + 1);
57
+ bakFileName = `aiwf-${relativePath.replace(/\//g, '-')}.bak`;
58
+ } else {
59
+ // Other files
60
+ bakFileName = path.basename(filePath) + '.bak';
61
+ }
62
+
63
+ const destPath = path.join(backupDir, bakFileName);
64
+ await fs.rename(backupPath, destPath);
65
+ return destPath;
66
+ }
67
+ } catch (error) {
68
+ // Backup failed, but continue
69
+ }
70
+ return null;
71
+ }
72
+
73
+ /**
74
+ * Backup commands and documentation files
75
+ * @param {Object} messages - Localized messages
76
+ * @param {string} [commandsDir] - Commands directory
77
+ * @returns {Promise<Array<string>>} Array of backed up file paths
78
+ */
79
+ async function backupCommandsAndDocs(messages, commandsDir = TOOL_DIRS.CLAUDE_COMMANDS) {
80
+ if (!BACKUP_DIR) BACKUP_DIR = getBackupDirName();
81
+ const spinner = ora(messages.backingUp).start();
82
+ const backedUpFiles = [];
83
+ await fs.mkdir(BACKUP_DIR, { recursive: true });
84
+
85
+ try {
86
+ // Files that will be updated and need backup
87
+ const filesToBackup = Object.values(FILES.CLAUDE_MD);
88
+
89
+ // Backup CLAUDE.md files
90
+ for (const file of filesToBackup) {
91
+ const backupPath = await backupFile(file, BACKUP_DIR);
92
+ if (backupPath) {
93
+ backedUpFiles.push(backupPath);
94
+ }
95
+ }
96
+
97
+ // Backup all command files
98
+ const commandsExist = await fs.access(commandsDir).then(() => true).catch(() => false);
99
+ if (commandsExist) {
100
+ try {
101
+ const commandFiles = await fs.readdir(commandsDir);
102
+ for (const file of commandFiles) {
103
+ const filePath = path.join(commandsDir, file);
104
+ const stat = await fs.stat(filePath);
105
+ if (stat.isFile() && file.endsWith('.md')) {
106
+ const backupPath = await backupFile(filePath, BACKUP_DIR);
107
+ if (backupPath) {
108
+ backedUpFiles.push(backupPath);
109
+ }
110
+ }
111
+ }
112
+ } catch (error) {
113
+ // Commands directory might be empty or have issues
114
+ }
115
+ }
116
+
117
+ // Backup IDE rule files (Cursor)
118
+ const cursorRulesExist = await fs.access(TOOL_DIRS.CURSOR_RULES).then(() => true).catch(() => false);
119
+ if (cursorRulesExist) {
120
+ try {
121
+ const cursorFiles = await fs.readdir(TOOL_DIRS.CURSOR_RULES);
122
+ for (const file of cursorFiles) {
123
+ if (file.endsWith('.mdc')) {
124
+ const filePath = path.join(TOOL_DIRS.CURSOR_RULES, file);
125
+ const backupPath = await backupFile(filePath, BACKUP_DIR);
126
+ if (backupPath) {
127
+ backedUpFiles.push(backupPath);
128
+ }
129
+ }
130
+ }
131
+ } catch (error) {
132
+ // Cursor rules directory might have issues
133
+ }
134
+ }
135
+
136
+ // Backup IDE rule files (Windsurf)
137
+ const windsurfRulesExist = await fs.access(TOOL_DIRS.WINDSURF_RULES).then(() => true).catch(() => false);
138
+ if (windsurfRulesExist) {
139
+ try {
140
+ const windsurfFiles = await fs.readdir(TOOL_DIRS.WINDSURF_RULES);
141
+ for (const file of windsurfFiles) {
142
+ if (file.endsWith('.md')) {
143
+ const filePath = path.join(TOOL_DIRS.WINDSURF_RULES, file);
144
+ const backupPath = await backupFile(filePath, BACKUP_DIR);
145
+ if (backupPath) {
146
+ backedUpFiles.push(backupPath);
147
+ }
148
+ }
149
+ }
150
+ } catch (error) {
151
+ // Windsurf rules directory might have issues
152
+ }
153
+ }
154
+
155
+ if (backedUpFiles.length > 0) {
156
+ spinner.succeed(chalk.green(messages.backupComplete.replace('{count}', backedUpFiles.length)));
157
+ } else {
158
+ spinner.succeed(chalk.gray(messages.noFilesToBackup));
159
+ }
160
+ return backedUpFiles;
161
+ } catch (error) {
162
+ spinner.fail(chalk.red(messages.backupFailed));
163
+ throw error;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Restore files from backup
169
+ * @param {Object} spinner - Ora spinner instance
170
+ * @param {Object} messages - Localized messages
171
+ * @returns {Promise<void>}
172
+ */
173
+ async function restoreFromBackup(spinner, messages) {
174
+ if (!BACKUP_DIR) return;
175
+
176
+ try {
177
+ spinner.text = messages.restoringBackup;
178
+
179
+ // Get all backup files
180
+ const backupFiles = await fs.readdir(BACKUP_DIR);
181
+ let restoredCount = 0;
182
+
183
+ for (const bakFile of backupFiles) {
184
+ if (bakFile.endsWith('.bak')) {
185
+ const backupPath = path.join(BACKUP_DIR, bakFile);
186
+ let originalPath;
187
+
188
+ // Determine original path from backup filename
189
+ if (bakFile.startsWith('claude-commands-aiwf-')) {
190
+ const relativePath = bakFile
191
+ .replace('claude-commands-aiwf-', '')
192
+ .replace('.bak', '')
193
+ .replace(/-/g, '/');
194
+ originalPath = path.join(TOOL_DIRS.CLAUDE_COMMANDS, relativePath);
195
+ } else if (bakFile.startsWith('gemini-prompts-aiwf-')) {
196
+ const relativePath = bakFile
197
+ .replace('gemini-prompts-aiwf-', '')
198
+ .replace('.bak', '')
199
+ .replace(/-/g, '/');
200
+ originalPath = path.join(TOOL_DIRS.GEMINI_PROMPTS, relativePath);
201
+ } else if (bakFile.startsWith('cursor-rules-')) {
202
+ const relativePath = bakFile
203
+ .replace('cursor-rules-', '')
204
+ .replace('.bak', '')
205
+ .replace(/-/g, '/');
206
+ originalPath = path.join(TOOL_DIRS.CURSOR_RULES, relativePath);
207
+ } else if (bakFile.startsWith('windsurf-rules-')) {
208
+ const relativePath = bakFile
209
+ .replace('windsurf-rules-', '')
210
+ .replace('.bak', '')
211
+ .replace(/-/g, '/');
212
+ originalPath = path.join(TOOL_DIRS.WINDSURF_RULES, relativePath);
213
+ } else if (bakFile.startsWith('aiwf-')) {
214
+ const relativePath = bakFile
215
+ .replace('aiwf-', '')
216
+ .replace('.bak', '')
217
+ .replace(/-/g, '/');
218
+ originalPath = path.join(AIWF_DIRS.ROOT, relativePath);
219
+ } else {
220
+ // Simple filename.ext.bak format
221
+ originalPath = bakFile.replace('.bak', '');
222
+ }
223
+
224
+ try {
225
+ // Ensure directory exists
226
+ await fs.mkdir(path.dirname(originalPath), { recursive: true });
227
+ // Copy backup to original location
228
+ await fs.copyFile(backupPath, originalPath);
229
+ restoredCount++;
230
+ } catch (error) {
231
+ // Individual file restore failed, continue with others
232
+ }
233
+ }
234
+ }
235
+
236
+ // Clean up backup directory
237
+ await fs.rm(BACKUP_DIR, { recursive: true, force: true });
238
+ BACKUP_DIR = null;
239
+
240
+ spinner.text = messages.backupRestored.replace('{count}', restoredCount);
241
+ } catch (error) {
242
+ spinner.fail(chalk.red(messages.restoreFailed));
243
+ throw error;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Get current backup directory
249
+ * @returns {string|null} Current backup directory path
250
+ */
251
+ function getCurrentBackupDir() {
252
+ return BACKUP_DIR;
253
+ }
254
+
255
+ /**
256
+ * Set backup directory
257
+ * @param {string} dir - Backup directory path
258
+ */
259
+ function setBackupDir(dir) {
260
+ BACKUP_DIR = dir;
261
+ }
262
+
263
+ export {
264
+ getBackupDirName,
265
+ backupFile,
266
+ backupCommandsAndDocs,
267
+ restoreFromBackup,
268
+ getCurrentBackupDir,
269
+ setBackupDir
270
+ };