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.
- package/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
package/src/commands/persona.js
DELETED
|
@@ -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;
|