aiwf 0.3.21 → 0.3.23

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 (140) hide show
  1. package/package.json +3 -2
  2. package/src/cli/index.js +39 -336
  3. package/src/lib/installer.js +310 -90
  4. package/src/utils/messages.js +24 -0
  5. package/src/DEPENDENCY_MAP.md +0 -90
  6. package/src/cli/cache-cli.js +0 -459
  7. package/src/cli/checkpoint-cli.js +0 -417
  8. package/src/cli/language-cli.js +0 -286
  9. package/src/cli/sprint-cli.js +0 -287
  10. package/src/commands/ai-tool.js +0 -383
  11. package/src/commands/compress.js +0 -60
  12. package/src/commands/create-project.js +0 -160
  13. package/src/commands/evaluate.js +0 -459
  14. package/src/commands/persona.js +0 -309
  15. package/src/commands/sprint-task.js +0 -262
  16. package/src/commands/token.js +0 -312
  17. package/src/lib/ai-persona-manager.js +0 -711
  18. package/src/lib/cache-system.js +0 -332
  19. package/src/lib/context-engine.js +0 -568
  20. package/src/lib/github-integration.js +0 -402
  21. package/src/lib/memory-profiler.js +0 -471
  22. package/src/lib/metrics-collector.js +0 -885
  23. package/src/lib/offline-detector.js +0 -386
  24. package/src/lib/resource-loader-enhanced.js +0 -399
  25. package/src/lib/resource-loader.js +0 -250
  26. package/src/lib/resources/commands/ai-persona.js +0 -602
  27. package/src/lib/resources/commands/compress-context.js +0 -389
  28. package/src/lib/resources/commands/evaluate.js +0 -246
  29. package/src/lib/resources/commands/persona-context-apply.js +0 -562
  30. package/src/lib/resources/commands/token-tracking.js +0 -443
  31. package/src/lib/resources/config/commit-patterns.js +0 -103
  32. package/src/lib/resources/config/language.json +0 -6
  33. package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
  34. package/src/lib/resources/personas/analyst.json +0 -44
  35. package/src/lib/resources/personas/architect/best_practices.md +0 -136
  36. package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
  37. package/src/lib/resources/personas/architect.json +0 -44
  38. package/src/lib/resources/personas/backend/best_practices.md +0 -766
  39. package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
  40. package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
  41. package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
  42. package/src/lib/resources/personas/developer.json +0 -44
  43. package/src/lib/resources/personas/developer.md +0 -37
  44. package/src/lib/resources/personas/evaluation_criteria.json +0 -137
  45. package/src/lib/resources/personas/frontend/best_practices.md +0 -445
  46. package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
  47. package/src/lib/resources/personas/persona-index.json +0 -31
  48. package/src/lib/resources/personas/reviewer.json +0 -44
  49. package/src/lib/resources/personas/security/best_practices.md +0 -307
  50. package/src/lib/resources/personas/security/knowledge_base.md +0 -498
  51. package/src/lib/resources/personas/tester.json +0 -44
  52. package/src/lib/resources/templates/README.md +0 -78
  53. package/src/lib/resources/templates/api-server/config.json +0 -64
  54. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
  55. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  56. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  57. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  58. package/src/lib/resources/templates/api-server/template/.env.example +0 -29
  59. package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
  60. package/src/lib/resources/templates/api-server/template/README.md +0 -171
  61. package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
  62. package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
  63. package/src/lib/resources/templates/api-server/template/package.json +0 -61
  64. package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
  65. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
  66. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  67. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
  68. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
  69. package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
  70. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  71. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  72. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  73. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  74. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  75. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
  76. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
  77. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
  78. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
  79. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
  80. package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
  81. package/src/lib/resources/templates/npm-library/config.json +0 -61
  82. package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
  83. package/src/lib/resources/templates/npm-library/template/README.md +0 -201
  84. package/src/lib/resources/templates/npm-library/template/package.json +0 -78
  85. package/src/lib/resources/templates/web-app/config.json +0 -53
  86. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
  87. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  88. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  89. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  90. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
  91. package/src/lib/resources/templates/web-app/template/README.md +0 -151
  92. package/src/lib/resources/templates/web-app/template/index.html +0 -14
  93. package/src/lib/resources/templates/web-app/template/package.json +0 -44
  94. package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
  95. package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
  96. package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
  97. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
  98. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  99. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  100. package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
  101. package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
  102. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  103. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  104. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  105. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  106. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
  107. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
  108. package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
  109. package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
  110. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
  111. package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
  112. package/src/lib/resources/utils/background-monitor.js +0 -223
  113. package/src/lib/resources/utils/compression-metrics.js +0 -1031
  114. package/src/lib/resources/utils/compression-strategies.js +0 -557
  115. package/src/lib/resources/utils/content-normalizer.js +0 -455
  116. package/src/lib/resources/utils/context-compressor.js +0 -583
  117. package/src/lib/resources/utils/context-rule-parser.js +0 -180
  118. package/src/lib/resources/utils/context-token-monitor.js +0 -463
  119. package/src/lib/resources/utils/context-update-manager.js +0 -502
  120. package/src/lib/resources/utils/git-utils.js +0 -220
  121. package/src/lib/resources/utils/importance-classifier.js +0 -645
  122. package/src/lib/resources/utils/information-filter.js +0 -710
  123. package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
  124. package/src/lib/resources/utils/prompt-injector.js +0 -242
  125. package/src/lib/resources/utils/simplified-evaluator.js +0 -119
  126. package/src/lib/resources/utils/text-summarizer.js +0 -492
  127. package/src/lib/resources/utils/token-counter.js +0 -177
  128. package/src/lib/resources/utils/token-monitor.js +0 -484
  129. package/src/lib/resources/utils/token-reporter.js +0 -452
  130. package/src/lib/resources/utils/token-storage.js +0 -433
  131. package/src/lib/resources/utils/token-tracker.js +0 -283
  132. package/src/lib/state/priority-calculator.js +0 -217
  133. package/src/lib/state/state-index.js +0 -155
  134. package/src/lib/state/task-scanner.js +0 -328
  135. package/src/lib/task-analyzer.js +0 -599
  136. package/src/lib/template-cache-system.js +0 -559
  137. package/src/lib/template-downloader.js +0 -467
  138. package/src/lib/template-version-manager.js +0 -491
  139. package/src/lib/token-optimizer.js +0 -517
  140. package/src/utils/engineering-guard.js +0 -399
@@ -1,309 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * AI 페르소나 명령어
5
- * AI 행동 페르소나 관리 및 전환
6
- */
7
-
8
- import { AIPersonaManager } from '../lib/ai-persona-manager.js';
9
- import { ResourceLoader } from '../lib/resource-loader.js';
10
- import path from 'path';
11
- import chalk from 'chalk';
12
- import { fileURLToPath } from 'url';
13
- import { dirname } from 'path';
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = dirname(__filename);
17
-
18
- class PersonaCommand {
19
- constructor() {
20
- this.personaManager = null;
21
- this.resourceLoader = new ResourceLoader();
22
- }
23
-
24
- /**
25
- * 페르소나 매니저 초기화
26
- */
27
- async initPersonaManager() {
28
- if (!this.personaManager) {
29
- // 사용자 디렉토리 초기화
30
- await this.resourceLoader.initUserDirectory();
31
-
32
- this.personaManager = new AIPersonaManager({
33
- personaConfigPath: path.join(process.cwd(), '.aiwf', 'personas'),
34
- metricsPath: path.join(process.cwd(), '.aiwf', 'metrics'),
35
- metricsEnabled: true,
36
- autoDetectionEnabled: true,
37
- resourceLoader: this.resourceLoader
38
- });
39
-
40
- await this.personaManager.init();
41
- }
42
-
43
- return this.personaManager;
44
- }
45
-
46
- /**
47
- * 페르소나 상태 표시
48
- */
49
- async showStatus() {
50
- try {
51
- // 파일에서 현재 페르소나 읽기
52
- const fs = await import('fs/promises');
53
- const path = await import('path');
54
- const personaFile = path.join(process.cwd(), '.aiwf', 'personas', 'current.json');
55
-
56
- try {
57
- const content = await fs.readFile(personaFile, 'utf-8');
58
- const currentPersona = JSON.parse(content);
59
-
60
- console.log(chalk.cyan('🎭 현재 AI 페르소나 상태'));
61
- console.log(chalk.gray('-'.repeat(50)));
62
- console.log(`현재 페르소나: ${chalk.yellow(currentPersona.name)}`);
63
- console.log(`설명: ${currentPersona.description}`);
64
- console.log(`활성화 시간: ${new Date(currentPersona.activatedAt).toLocaleString('ko-KR')}`);
65
-
66
- const behaviors = this.getKoreanBehaviors(currentPersona.name);
67
- if (behaviors.length > 0) {
68
- console.log('\n주요 동작:');
69
- behaviors.forEach(behavior => {
70
- console.log(` • ${behavior}`);
71
- });
72
- }
73
-
74
- console.log(chalk.gray('\n' + '-'.repeat(50)));
75
- console.log(chalk.green('✅ 페르소나가 활성화되어 있습니다.'));
76
-
77
- } catch (fileError) {
78
- console.log(chalk.yellow('⚠️ 현재 활성화된 페르소나가 없습니다.'));
79
- console.log(chalk.gray('사용 가능한 페르소나 목록을 보려면: aiwf persona list'));
80
- console.log(chalk.gray('페르소나를 설정하려면: aiwf persona set <페르소나명>'));
81
- }
82
-
83
- } catch (error) {
84
- console.error(chalk.red('❌ 페르소나 상태 확인 중 오류가 발생했습니다:'), error.message);
85
- console.error(error.stack);
86
- }
87
- }
88
-
89
- /**
90
- * 페르소나 목록 표시
91
- */
92
- async listPersonas() {
93
- try {
94
- console.log(chalk.cyan('🎭 사용 가능한 AI 페르소나'));
95
- console.log(chalk.gray('-'.repeat(50)));
96
-
97
- // ResourceLoader를 사용하여 페르소나 목록 가져오기
98
- try {
99
- const personas = await this.resourceLoader.listPersonas();
100
-
101
- for (const personaName of personas) {
102
- try {
103
- const persona = await this.resourceLoader.loadPersona(personaName);
104
- const displayName = chalk.yellow(personaName);
105
- const description = persona.description || '설명 없음';
106
- console.log(` ${displayName} - ${description}`);
107
- } catch (error) {
108
- // 개별 페르소나 로드 실패 시 건너뛰기
109
- console.log(` ${chalk.yellow(personaName)} - ${chalk.gray('(로드 실패)')}`);
110
- }
111
- }
112
- } catch (error) {
113
- // 페르소나 목록 가져오기 실패 시 기본 목록 표시
114
- const defaultPersonas = [
115
- { name: 'architect', description: '시스템 설계 및 아키텍처 전문가' },
116
- { name: 'developer', description: '일반 개발자 (기본값)' },
117
- { name: 'reviewer', description: '코드 리뷰 및 품질 관리 전문가' },
118
- { name: 'analyst', description: '비즈니스 분석가' },
119
- { name: 'tester', description: '테스트 엔지니어' }
120
- ];
121
-
122
- defaultPersonas.forEach(persona => {
123
- const displayName = chalk.yellow(persona.name);
124
- console.log(` ${displayName} - ${persona.description}`);
125
- });
126
- }
127
-
128
- console.log(chalk.gray('\n' + '-'.repeat(50)));
129
- console.log(chalk.gray('사용법: aiwf persona set <페르소나명>'));
130
-
131
- } catch (error) {
132
- console.error(chalk.red('❌ 페르소나 목록 조회 중 오류가 발생했습니다:'), error.message);
133
- console.error(error.stack);
134
- }
135
- }
136
-
137
- /**
138
- * 페르소나 전환
139
- */
140
- async setPersona(personaName) {
141
- if (!personaName) {
142
- console.error(chalk.red('❌ 페르소나 이름을 지정해주세요.'));
143
- console.log(chalk.gray('사용법: aiwf persona set <페르소나명>'));
144
- console.log(chalk.gray('사용 가능한 페르소나: aiwf persona list'));
145
- return;
146
- }
147
-
148
- try {
149
- // 유효한 페르소나인지 확인
150
- const validPersonas = ['architect', 'developer', 'reviewer', 'debugger', 'optimizer', 'security', 'documenter'];
151
-
152
- if (!validPersonas.includes(personaName)) {
153
- console.error(chalk.red(`❌ 페르소나 '${personaName}'를 찾을 수 없습니다.`));
154
- console.log(chalk.gray('사용 가능한 페르소나: aiwf persona list'));
155
- return;
156
- }
157
-
158
- // 간단한 페르소나 설정 (파일 기반)
159
- const fs = await import('fs/promises');
160
- const path = await import('path');
161
- const personaDir = path.join(process.cwd(), '.aiwf', 'personas');
162
-
163
- // 디렉토리 생성
164
- await fs.mkdir(personaDir, { recursive: true });
165
-
166
- // 현재 페르소나 저장
167
- const personaConfig = {
168
- name: personaName,
169
- description: this.getKoreanDescription(personaName),
170
- activatedAt: new Date().toISOString()
171
- };
172
-
173
- await fs.writeFile(
174
- path.join(personaDir, 'current.json'),
175
- JSON.stringify(personaConfig, null, 2)
176
- );
177
-
178
- console.log(chalk.green('🎭 AI 페르소나 활성화 완료'));
179
- console.log(chalk.gray('-'.repeat(50)));
180
- console.log(`현재 페르소나: ${chalk.yellow(personaName)}`);
181
- console.log(`전문 분야: ${this.getKoreanDescription(personaName)}`);
182
- console.log(chalk.gray('-'.repeat(50)));
183
- console.log(chalk.green('✅ 페르소나가 성공적으로 활성화되었습니다.'));
184
-
185
- } catch (error) {
186
- console.error(chalk.red('❌ 페르소나 전환 중 오류가 발생했습니다:'), error.message);
187
- console.error(error.stack);
188
- }
189
- }
190
-
191
- /**
192
- * 페르소나 리셋
193
- */
194
- async resetPersona() {
195
- try {
196
- // 기본 페르소나(developer)로 리셋
197
- await this.setPersona('developer');
198
- console.log(chalk.green('🔄 페르소나가 기본값(developer)으로 리셋되었습니다.'));
199
-
200
- } catch (error) {
201
- console.error(chalk.red('❌ 페르소나 리셋 중 오류가 발생했습니다:'), error.message);
202
- console.error(error.stack);
203
- }
204
- }
205
-
206
- /**
207
- * 명령어 실행
208
- */
209
- async execute(args) {
210
- const [subcommand, ...restArgs] = args;
211
-
212
- switch (subcommand) {
213
- case 'status':
214
- await this.showStatus();
215
- break;
216
- case 'list':
217
- await this.listPersonas();
218
- break;
219
- case 'set':
220
- await this.setPersona(restArgs[0]);
221
- break;
222
- case 'reset':
223
- await this.resetPersona();
224
- break;
225
- default:
226
- this.showHelp();
227
- break;
228
- }
229
- }
230
-
231
- /**
232
- * 도움말 표시
233
- */
234
- showHelp() {
235
- console.log(chalk.cyan('🎭 AIWF 페르소나 관리'));
236
- console.log(chalk.gray('-'.repeat(50)));
237
- console.log('사용법: aiwf persona <명령어> [옵션]');
238
- console.log('');
239
- console.log('명령어:');
240
- console.log(' status 현재 페르소나 상태 표시');
241
- console.log(' list 사용 가능한 페르소나 목록');
242
- console.log(' set <페르소나명> 특정 페르소나로 전환');
243
- console.log(' reset 기본 페르소나로 리셋');
244
- console.log('');
245
- console.log('예시:');
246
- console.log(' aiwf persona status');
247
- console.log(' aiwf persona list');
248
- console.log(' aiwf persona set architect');
249
- console.log(' aiwf persona reset');
250
- }
251
-
252
- /**
253
- * 한국어 설명 가져오기
254
- */
255
- getKoreanDescription(persona) {
256
- const descriptions = {
257
- architect: '시스템 설계 및 아키텍처 전문가',
258
- developer: '일반 개발자 (기본값)',
259
- reviewer: '코드 리뷰 및 품질 관리 전문가',
260
- debugger: '디버깅 및 문제 해결 전문가',
261
- optimizer: '성능 최적화 전문가',
262
- security: '보안 및 취약점 분석 전문가',
263
- devops: 'DevOps 및 인프라 관리 전문가',
264
- tester: '테스트 및 QA 전문가',
265
- documenter: '문서화 및 기술 작성 전문가',
266
- mentor: '멘토링 및 교육 전문가'
267
- };
268
-
269
- return descriptions[persona] || '설명 없음';
270
- }
271
-
272
- /**
273
- * 한국어 동작 설명 가져오기
274
- */
275
- getKoreanBehaviors(persona) {
276
- const behaviors = {
277
- architect: [
278
- '큰 그림과 전체 시스템 구조에 집중',
279
- '확장성과 유지보수성 우선시',
280
- '디자인 패턴과 아키텍처 원칙 적용',
281
- '통합 지점과 인터페이스 고려'
282
- ],
283
- developer: [
284
- '실용적이고 효율적인 코드 작성',
285
- '베스트 프랙티스 적용',
286
- '가독성과 유지보수성 중시'
287
- ],
288
- reviewer: [
289
- '코드 품질과 표준 준수 확인',
290
- '잠재적 버그와 개선점 식별',
291
- '성능과 보안 측면 검토'
292
- ],
293
- debugger: [
294
- '문제의 근본 원인 분석',
295
- '체계적인 디버깅 접근',
296
- '재현 가능한 테스트 케이스 생성'
297
- ],
298
- optimizer: [
299
- '성능 병목 지점 식별',
300
- '메모리 사용량 최적화',
301
- '알고리즘 효율성 개선'
302
- ]
303
- };
304
-
305
- return behaviors[persona] || ['기본 동작'];
306
- }
307
- }
308
-
309
- export default PersonaCommand;
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * 스프린트 태스크 관리 명령어
5
- * 스프린트에 개별 태스크를 추가하고 관리하는 기능
6
- */
7
-
8
- import fs from 'fs/promises';
9
- import path from 'path';
10
- import chalk from 'chalk';
11
- import ora from 'ora';
12
-
13
- // 프로젝트 루트에서 .aiwf 디렉토리 찾기
14
- async function findProjectRoot(startDir = process.cwd()) {
15
- let currentDir = startDir;
16
-
17
- while (currentDir !== path.parse(currentDir).root) {
18
- try {
19
- const aiwfPath = path.join(currentDir, '.aiwf');
20
- await fs.access(aiwfPath);
21
- return currentDir;
22
- } catch {
23
- currentDir = path.dirname(currentDir);
24
- }
25
- }
26
-
27
- throw new Error('.aiwf 디렉토리를 찾을 수 없습니다. AIWF 프로젝트 내에서 실행해주세요.');
28
- }
29
-
30
- // 스프린트 존재 확인
31
- async function verifySprintExists(projectRoot, sprintId) {
32
- const sprintsDir = path.join(projectRoot, '.aiwf', '03_SPRINTS');
33
-
34
- try {
35
- const entries = await fs.readdir(sprintsDir);
36
- const sprintDir = entries.find(entry => entry.startsWith(sprintId + '_'));
37
-
38
- if (!sprintDir) {
39
- throw new Error(`스프린트 ${sprintId}를 찾을 수 없습니다.`);
40
- }
41
-
42
- return path.join(sprintsDir, sprintDir);
43
- } catch (error) {
44
- throw new Error(`스프린트 디렉토리 읽기 실패: ${error.message}`);
45
- }
46
- }
47
-
48
- // 다음 태스크 번호 결정
49
- async function getNextTaskNumber(sprintPath, sprintId) {
50
- try {
51
- const files = await fs.readdir(sprintPath);
52
- const taskFiles = files.filter(f => f.match(/^T\d+_/));
53
-
54
- if (taskFiles.length === 0) {
55
- return 'T01';
56
- }
57
-
58
- const numbers = taskFiles.map(f => {
59
- const match = f.match(/^T(\d+)_/);
60
- return match ? parseInt(match[1]) : 0;
61
- });
62
-
63
- const maxNumber = Math.max(...numbers);
64
- return `T${String(maxNumber + 1).padStart(2, '0')}`;
65
- } catch {
66
- return 'T01';
67
- }
68
- }
69
-
70
- // 태스크 파일 생성
71
- async function createTaskFile(sprintPath, taskNumber, sprintId, taskTitle) {
72
- const timestamp = new Date().toISOString().slice(0, 16).replace('T', ' ');
73
- const fileName = `${taskNumber}_${sprintId}_${taskTitle.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_가-힣]/g, '')}.md`;
74
- const filePath = path.join(sprintPath, fileName);
75
-
76
- const content = `# ${taskNumber}: ${taskTitle}
77
-
78
- **스프린트**: ${sprintId}
79
- **생성일**: ${timestamp}
80
- **상태**: pending
81
- **우선순위**: medium
82
- **예상 시간**: TBD
83
-
84
- ## 개요
85
-
86
- [태스크에 대한 간단한 설명을 작성하세요]
87
-
88
- ## 요구사항
89
-
90
- - [ ] [구체적인 요구사항 1]
91
- - [ ] [구체적인 요구사항 2]
92
- - [ ] [구체적인 요구사항 3]
93
-
94
- ## 승인 기준
95
-
96
- - [ ] [완료 조건 1]
97
- - [ ] [완료 조건 2]
98
- - [ ] [완료 조건 3]
99
-
100
- ## 기술 가이드라인
101
-
102
- - [관련 아키텍처 결정 참조]
103
- - [기존 코드베이스 패턴 참조]
104
- - [사용할 기술 스택]
105
-
106
- ## 구현 노트
107
-
108
- - [구현 시 고려사항]
109
- - [잠재적 리스크]
110
- - [테스트 전략]
111
-
112
- ## 참조
113
-
114
- - [관련 문서나 코드 링크]
115
- `;
116
-
117
- await fs.writeFile(filePath, content);
118
- return { filePath, fileName, taskNumber };
119
- }
120
-
121
- // 스프린트 메타 파일 업데이트
122
- async function updateSprintMeta(sprintPath, taskNumber, taskTitle) {
123
- const files = await fs.readdir(sprintPath);
124
- const metaFile = files.find(f => f.includes('sprint_meta.md'));
125
-
126
- if (!metaFile) {
127
- console.warn(chalk.yellow('⚠️ 스프린트 메타 파일을 찾을 수 없습니다.'));
128
- return;
129
- }
130
-
131
- const metaPath = path.join(sprintPath, metaFile);
132
- let content = await fs.readFile(metaPath, 'utf-8');
133
-
134
- // 태스크 섹션 찾기 및 업데이트
135
- const taskSectionRegex = /## 태스크 목록[\s\S]*?(?=##|$)/;
136
- const taskMatch = content.match(taskSectionRegex);
137
-
138
- if (taskMatch) {
139
- const newTaskLine = `- [ ] ${taskNumber}: ${taskTitle} - 상태: pending\n`;
140
- const updatedSection = taskMatch[0] + newTaskLine;
141
- content = content.replace(taskSectionRegex, updatedSection);
142
- } else {
143
- // 태스크 섹션이 없으면 추가
144
- content += `\n## 태스크 목록\n\n- [ ] ${taskNumber}: ${taskTitle} - 상태: pending\n`;
145
- }
146
-
147
- await fs.writeFile(metaPath, content);
148
- }
149
-
150
- // 매니페스트 업데이트
151
- async function updateManifest(projectRoot, sprintId, taskNumber, taskTitle) {
152
- const manifestPath = path.join(projectRoot, '.aiwf', '00_PROJECT_MANIFEST.md');
153
-
154
- try {
155
- let content = await fs.readFile(manifestPath, 'utf-8');
156
-
157
- // 최종 업데이트 날짜 변경
158
- const dateRegex = /\*\*마지막 업데이트\*\*: \d{4}-\d{2}-\d{2}/;
159
- const today = new Date().toISOString().slice(0, 10);
160
- content = content.replace(dateRegex, `**마지막 업데이트**: ${today}`);
161
-
162
- // 활성 태스크 섹션에 추가
163
- const activeTasksRegex = /## 활성 태스크[\s\S]*?### 계획됨/;
164
- const match = content.match(activeTasksRegex);
165
-
166
- if (match) {
167
- const newTaskLine = `\n- **${taskNumber}_${sprintId}**: ${taskTitle} (pending → ${today} 생성)\n`;
168
- const updatedSection = match[0].replace('### 계획됨', newTaskLine + '### 계획됨');
169
- content = content.replace(activeTasksRegex, updatedSection);
170
- }
171
-
172
- // 태스크 통계 업데이트
173
- const statsRegex = /- \*\*총 태스크\*\*: (\d+)/;
174
- const statsMatch = content.match(statsRegex);
175
- if (statsMatch) {
176
- const total = parseInt(statsMatch[1]) + 1;
177
- content = content.replace(statsRegex, `- **총 태스크**: ${total}`);
178
- }
179
-
180
- await fs.writeFile(manifestPath, content);
181
- } catch (error) {
182
- console.warn(chalk.yellow(`⚠️ 매니페스트 업데이트 실패: ${error.message}`));
183
- }
184
- }
185
-
186
- // 메인 함수
187
- export async function addTaskToSprint(sprintId, taskTitle) {
188
- const spinner = ora('스프린트에 태스크 추가 중...').start();
189
-
190
- try {
191
- // 1. 프로젝트 루트 찾기
192
- const projectRoot = await findProjectRoot();
193
-
194
- // 2. 스프린트 존재 확인
195
- spinner.text = '스프린트 확인 중...';
196
- const sprintPath = await verifySprintExists(projectRoot, sprintId);
197
-
198
- // 3. 다음 태스크 번호 결정
199
- spinner.text = '태스크 번호 결정 중...';
200
- const taskNumber = await getNextTaskNumber(sprintPath, sprintId);
201
-
202
- // 4. 태스크 파일 생성
203
- spinner.text = '태스크 파일 생성 중...';
204
- const { filePath, fileName, taskNumber: finalTaskNumber } = await createTaskFile(
205
- sprintPath,
206
- taskNumber,
207
- sprintId,
208
- taskTitle
209
- );
210
-
211
- // 5. 스프린트 메타 업데이트
212
- spinner.text = '스프린트 메타 업데이트 중...';
213
- await updateSprintMeta(sprintPath, finalTaskNumber, taskTitle);
214
-
215
- // 6. 매니페스트 업데이트
216
- spinner.text = '프로젝트 매니페스트 업데이트 중...';
217
- await updateManifest(projectRoot, sprintId, finalTaskNumber, taskTitle);
218
-
219
- spinner.succeed('태스크 추가 완료!');
220
-
221
- // 성공 메시지
222
- console.log('\n' + chalk.green('✅ 태스크 추가 완료!'));
223
- console.log('\n📄 생성된 파일: ' + chalk.cyan(fileName));
224
- console.log('🏃 스프린트: ' + chalk.yellow(sprintId));
225
- console.log('📋 태스크: ' + chalk.yellow(`${finalTaskNumber} - ${taskTitle}`));
226
- console.log('⏱️ 상태: ' + chalk.blue('pending'));
227
-
228
- console.log('\n' + chalk.bold('📝 다음 단계:'));
229
- console.log('1. 태스크 파일을 열어 세부사항 작성');
230
- console.log(`2. ${chalk.cyan(`/aiwf:do_task ${finalTaskNumber}`)}로 작업 시작`);
231
- console.log(`3. 완료 후 ${chalk.cyan(`/aiwf:update_task_status ${finalTaskNumber} completed`)} 실행`);
232
-
233
- console.log('\n💡 팁: 태스크 목록 확인은 ' + chalk.cyan(`/aiwf:list_sprint_tasks ${sprintId}`));
234
-
235
- return { success: true, taskNumber: finalTaskNumber, filePath };
236
-
237
- } catch (error) {
238
- spinner.fail(`태스크 추가 실패: ${error.message}`);
239
- throw error;
240
- }
241
- }
242
-
243
- // CLI 실행
244
- if (import.meta.url === `file://${process.argv[1]}`) {
245
- const args = process.argv.slice(2);
246
-
247
- if (args.length < 2) {
248
- console.error(chalk.red('사용법: aiwf-sprint-task <스프린트ID> <태스크 제목>'));
249
- console.error(chalk.gray('예시: aiwf-sprint-task S03 "API 성능 최적화"'));
250
- process.exit(1);
251
- }
252
-
253
- const [sprintId, ...titleParts] = args;
254
- const taskTitle = titleParts.join(' ').replace(/^["']|["']$/g, '');
255
-
256
- addTaskToSprint(sprintId, taskTitle).catch(error => {
257
- console.error(chalk.red(`오류: ${error.message}`));
258
- process.exit(1);
259
- });
260
- }
261
-
262
- export default addTaskToSprint;