aiwf 0.3.18 → 0.3.20

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/src/cli/index.js CHANGED
@@ -4,16 +4,9 @@ import { program } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import { installAIWF } from '../lib/installer.js';
6
6
  import AIToolCommand from '../commands/ai-tool.js';
7
- import PersonaCommand from '../commands/persona.js';
8
- import { CacheCLI } from './cache-cli.js';
9
7
  import { addTaskToSprint } from '../commands/sprint-task.js';
10
- import CompressCommand from '../commands/compress.js';
11
- import TokenCommand from '../commands/token.js';
12
- import EvaluateCommand from '../commands/evaluate.js';
13
- import { createProject } from '../commands/create-project.js';
14
8
  import StateCommand from '../commands/state.js';
15
9
  import { createIndependentSprint } from '../commands/sprint-independent.js';
16
- import { CheckpointManager } from '../utils/checkpoint-manager.js';
17
10
  import { createYoloConfig, createInteractiveYoloConfig, showYoloConfig } from '../commands/yolo-config.js';
18
11
 
19
12
  // Parse version from package.json
@@ -50,94 +43,7 @@ program
50
43
  });
51
44
 
52
45
  // Cache management commands
53
- const cache = program.command('cache');
54
- cache.description('Manage offline template cache');
55
-
56
- cache
57
- .command('download')
58
- .description('Download templates for offline use')
59
- .option('--all', 'Download all templates')
60
- .option('--type <type>', 'Download specific type (ai-tools, projects)')
61
- .action(async (options) => {
62
- const cacheCLI = new CacheCLI();
63
- try {
64
- await cacheCLI.init();
65
- await cacheCLI.downloadCommand(options);
66
- } catch (error) {
67
- console.error(chalk.red('Cache download failed:', error.message));
68
- process.exit(1);
69
- } finally {
70
- await cacheCLI.cleanup();
71
- }
72
- });
73
-
74
- cache
75
- .command('list')
76
- .description('List cached templates')
77
- .option('--type <type>', 'Filter by type (ai-tools, projects)')
78
- .action(async (options) => {
79
- const cacheCLI = new CacheCLI();
80
- try {
81
- await cacheCLI.init();
82
- await cacheCLI.listCommand(options);
83
- } catch (error) {
84
- console.error(chalk.red('Cache list failed:', error.message));
85
- process.exit(1);
86
- } finally {
87
- await cacheCLI.cleanup();
88
- }
89
- });
90
-
91
- cache
92
- .command('clean')
93
- .description('Clean up cached templates')
94
- .option('--all', 'Clear all cached templates')
95
- .option('--max-age <days>', 'Maximum age in days (default: 7)', '7')
96
- .action(async (options) => {
97
- const cacheCLI = new CacheCLI();
98
- try {
99
- await cacheCLI.init();
100
- await cacheCLI.cleanCommand(options);
101
- } catch (error) {
102
- console.error(chalk.red('Cache clean failed:', error.message));
103
- process.exit(1);
104
- } finally {
105
- await cacheCLI.cleanup();
106
- }
107
- });
108
-
109
- cache
110
- .command('update')
111
- .description('Check for template updates')
112
- .option('--install', 'Install available updates')
113
- .action(async (options) => {
114
- const cacheCLI = new CacheCLI();
115
- try {
116
- await cacheCLI.init();
117
- await cacheCLI.updateCommand(options);
118
- } catch (error) {
119
- console.error(chalk.red('Cache update failed:', error.message));
120
- process.exit(1);
121
- } finally {
122
- await cacheCLI.cleanup();
123
- }
124
- });
125
-
126
- cache
127
- .command('status')
128
- .description('Show cache system status')
129
- .action(async () => {
130
- const cacheCLI = new CacheCLI();
131
- try {
132
- await cacheCLI.init();
133
- await cacheCLI.statusCommand();
134
- } catch (error) {
135
- console.error(chalk.red('Cache status failed:', error.message));
136
- process.exit(1);
137
- } finally {
138
- await cacheCLI.cleanup();
139
- }
140
- });
46
+ // (removed) cache command group
141
47
 
142
48
  // Language management commands
143
49
  const lang = program.command('lang');
@@ -171,45 +77,7 @@ lang
171
77
  await resetLanguage();
172
78
  });
173
79
 
174
- // Persona management commands
175
- const persona = program.command('persona');
176
- persona.description('Manage AI personas / AI 페르소나 관리');
177
-
178
- persona
179
- .command('status')
180
- .alias('s')
181
- .description('Show current persona status / 현재 페르소나 상태 표시')
182
- .action(async () => {
183
- const personaCmd = new PersonaCommand();
184
- await personaCmd.showStatus();
185
- });
186
-
187
- persona
188
- .command('list')
189
- .alias('ls')
190
- .description('List available personas / 사용 가능한 페르소나 목록')
191
- .action(async () => {
192
- const personaCmd = new PersonaCommand();
193
- await personaCmd.listPersonas();
194
- });
195
-
196
- persona
197
- .command('set <persona-name>')
198
- .alias('switch')
199
- .description('Switch to specific persona / 특정 페르소나로 전환')
200
- .action(async (personaName) => {
201
- const personaCmd = new PersonaCommand();
202
- await personaCmd.setPersona(personaName);
203
- });
204
-
205
- persona
206
- .command('reset')
207
- .alias('r')
208
- .description('Reset to default persona / 기본 페르소나로 리셋')
209
- .action(async () => {
210
- const personaCmd = new PersonaCommand();
211
- await personaCmd.resetPersona();
212
- });
80
+ // (removed) persona command group
213
81
 
214
82
  // Sprint task management command
215
83
  program
@@ -225,125 +93,14 @@ program
225
93
  }
226
94
  });
227
95
 
228
- // Create project command
229
- program
230
- .command('create-project')
231
- .alias('create')
232
- .description('Create a new AIWF project / 새 AIWF 프로젝트 생성')
233
- .action(async () => {
234
- try {
235
- await createProject();
236
- } catch (error) {
237
- console.error(chalk.red(`오류: ${error.message}`));
238
- process.exit(1);
239
- }
240
- });
96
+ // (removed) create-project command
241
97
 
242
- // Context compression command
243
- program
244
- .command('compress [mode] [path]')
245
- .description('Compress context for token optimization / 토큰 최적화를 위한 컨텍스트 압축')
246
- .option('--persona, -p', 'Enable persona-aware compression / 페르소나 인식 압축 활성화')
247
- .action(async (mode, path, options) => {
248
- const compressCmd = new CompressCommand();
249
- const args = [];
250
- if (mode) args.push(mode);
251
- if (path) args.push(path);
252
- if (options.persona) args.push('--persona');
253
- await compressCmd.execute(args);
254
- });
98
+ // (removed) compress command
255
99
 
256
100
 
257
- // Token tracking commands
258
- const token = program.command('token');
259
- token.description('Manage token usage tracking / 토큰 사용량 추적 관리');
101
+ // (removed) token command group
260
102
 
261
- token
262
- .command('status')
263
- .alias('s')
264
- .description('Show token usage status / 토큰 사용 현황 표시')
265
- .action(async () => {
266
- const tokenCmd = new TokenCommand();
267
- await tokenCmd.execute(['status']);
268
- });
269
-
270
- token
271
- .command('report [period]')
272
- .description('Generate usage report / 사용 리포트 생성')
273
- .action(async (period) => {
274
- const tokenCmd = new TokenCommand();
275
- const args = ['report'];
276
- if (period) args.push(period);
277
- await tokenCmd.execute(args);
278
- });
279
-
280
- token
281
- .command('track <input> <output>')
282
- .description('Manually track token usage / 토큰 사용량 수동 기록')
283
- .action(async (input, output) => {
284
- const tokenCmd = new TokenCommand();
285
- await tokenCmd.execute(['track', input, output]);
286
- });
287
-
288
- token
289
- .command('limit <type> <value>')
290
- .description('Set usage limits / 사용 한도 설정')
291
- .action(async (type, value) => {
292
- const tokenCmd = new TokenCommand();
293
- await tokenCmd.execute(['limit', type, value]);
294
- });
295
-
296
- token
297
- .command('reset')
298
- .description('Reset tracking data / 추적 데이터 초기화')
299
- .action(async () => {
300
- const tokenCmd = new TokenCommand();
301
- await tokenCmd.execute(['reset']);
302
- });
303
-
304
- // Evaluation commands
305
- const evaluate = program.command('evaluate');
306
- evaluate.description('Evaluate AI responses and code quality / AI 응답 및 코드 품질 평가');
307
-
308
- evaluate
309
- .command('response <file>')
310
- .description('Evaluate AI response quality / AI 응답 품질 평가')
311
- .action(async (file) => {
312
- const evaluateCmd = new EvaluateCommand();
313
- await evaluateCmd.execute(['response', file]);
314
- });
315
-
316
- evaluate
317
- .command('code <file>')
318
- .description('Evaluate code quality / 코드 품질 평가')
319
- .action(async (file) => {
320
- const evaluateCmd = new EvaluateCommand();
321
- await evaluateCmd.execute(['code', file]);
322
- });
323
-
324
- evaluate
325
- .command('persona <file> <persona>')
326
- .description('Evaluate persona appropriateness / 페르소나 적합성 평가')
327
- .action(async (file, persona) => {
328
- const evaluateCmd = new EvaluateCommand();
329
- await evaluateCmd.execute(['persona', file, persona]);
330
- });
331
-
332
- evaluate
333
- .command('report')
334
- .description('Generate evaluation report / 평가 리포트 생성')
335
- .action(async () => {
336
- const evaluateCmd = new EvaluateCommand();
337
- await evaluateCmd.execute(['report']);
338
- });
339
-
340
- evaluate
341
- .command('criteria')
342
- .description('Show evaluation criteria / 평가 기준 표시')
343
- .action(async () => {
344
- const evaluateCmd = new EvaluateCommand();
345
- await evaluateCmd.execute(['criteria']);
346
- });
103
+ // (removed) evaluate command group
347
104
 
348
105
  // State management commands
349
106
  const state = program.command('state');
@@ -503,131 +260,173 @@ yoloConfig
503
260
  }
504
261
  });
505
262
 
506
- // Checkpoint management commands (YOLO recovery)
507
- const checkpoint = program.command('checkpoint');
508
- checkpoint.description('YOLO checkpoint management / YOLO 체크포인트 관리');
263
+ // (removed) checkpoint command group
509
264
 
510
- checkpoint
511
- .command('list')
512
- .alias('ls')
513
- .description('List available checkpoints / 사용 가능한 체크포인트 목록')
514
- .option('--limit <n>', 'Limit number of checkpoints / 체크포인트 수 제한', '10')
515
- .action(async (options) => {
265
+ // Engineering Guard commands (오버엔지니어링 방지)
266
+ const guard = program.command('guard');
267
+ guard.description('Engineering guard for overengineering prevention / 오버엔지니어링 방지 가드');
268
+
269
+ guard
270
+ .command('check [path]')
271
+ .description('Check project complexity / 프로젝트 복잡도 검사')
272
+ .option('--config <path>', 'YOLO config file path / YOLO 설정 파일 경로', '.aiwf/yolo-config.yaml')
273
+ .action(async (targetPath, options) => {
516
274
  try {
517
- // 프로젝트 루트 찾기
518
- const fs = await import('fs/promises');
519
- const path = await import('path');
520
- let currentDir = process.cwd();
275
+ const { EngineeringGuard } = await import('../utils/engineering-guard.js');
276
+ const guard = new EngineeringGuard();
521
277
 
522
- while (currentDir !== path.parse(currentDir).root) {
523
- try {
524
- await fs.access(path.join(currentDir, '.aiwf'));
525
- break;
526
- } catch {
527
- currentDir = path.dirname(currentDir);
528
- }
529
- }
278
+ // 설정 파일 로드
279
+ await guard.loadConfig(options.config);
530
280
 
531
- const manager = new CheckpointManager(currentDir);
532
- const checkpoints = await manager.listCheckpoints();
281
+ const checkPath = targetPath || process.cwd();
282
+ console.log(chalk.blue(`🔍 복잡도 검사 중: ${checkPath}`));
283
+ console.log('');
284
+
285
+ const report = await guard.checkProject(checkPath);
533
286
 
534
- console.log(chalk.bold('📊 체크포인트 목록:'));
287
+ // 결과 출력
288
+ console.log(chalk.bold('📊 복잡도 분석 결과:'));
289
+ console.log(` 총 위반사항: ${chalk.yellow(report.summary.total_violations)}`);
290
+ console.log(` 높은 심각도: ${chalk.red(report.summary.high_severity)}`);
291
+ console.log(` 중간 심각도: ${chalk.yellow(report.summary.medium_severity)}`);
292
+ console.log(` 경고사항: ${chalk.blue(report.summary.warnings)}`);
535
293
  console.log('');
536
294
 
537
- if (checkpoints.length === 0) {
538
- console.log(chalk.yellow('📭 체크포인트가 없습니다.'));
539
- return;
295
+ if (report.violations && report.violations.length > 0) {
296
+ console.log(chalk.bold('🚨 위반사항:'));
297
+ for (const violation of report.violations.slice(0, 10)) {
298
+ const icon = violation.severity === 'high' ? '❌' :
299
+ violation.severity === 'medium' ? '⚠️' : '💡';
300
+ console.log(` ${icon} ${chalk.cyan(violation.type)}: ${violation.file || violation.message}`);
301
+ if (violation.details) {
302
+ console.log(` ${chalk.gray(violation.details)}`);
303
+ }
304
+ }
305
+ console.log('');
540
306
  }
541
307
 
542
- const limit = parseInt(options.limit);
543
- const limitedCheckpoints = checkpoints.slice(0, limit);
544
-
545
- for (const cp of limitedCheckpoints) {
546
- const typeIcon = cp.type === 'session_start' ? '🚀' :
547
- cp.type === 'task_complete' ? '✅' : '🔄';
548
- console.log(`${typeIcon} ${chalk.cyan(cp.id)} - ${chalk.yellow(cp.type)}`);
549
- console.log(` 태스크: ${chalk.blue(cp.tasks_completed)}개 완료`);
308
+ if (report.recommendations && report.recommendations.length > 0) {
309
+ console.log(chalk.bold('💡 권장사항:'));
310
+ for (const rec of report.recommendations) {
311
+ console.log(` • ${chalk.green(rec)}`);
312
+ }
550
313
  console.log('');
551
314
  }
315
+
316
+ // 통과/실패 판정
317
+ if (report.passed) {
318
+ console.log(chalk.green('✅ 복잡도 기준 통과'));
319
+ } else {
320
+ console.log(chalk.red('❌ 복잡도 기준 초과 - 리팩토링 필요'));
321
+ }
322
+
552
323
  } catch (error) {
553
- console.error(chalk.red('❌ 체크포인트 목록 조회 실패:'), error.message);
324
+ console.error(chalk.red('❌ 복잡도 검사 실패:'), error.message);
554
325
  process.exit(1);
555
326
  }
556
327
  });
557
328
 
558
- checkpoint
559
- .command('restore <checkpointId>')
560
- .description('Restore from checkpoint / 체크포인트에서 복구')
561
- .action(async (checkpointId) => {
329
+ guard
330
+ .command('quick [path]')
331
+ .description('Quick complexity check / 빠른 복잡도 체크')
332
+ .action(async (targetPath) => {
562
333
  try {
563
- const fs = await import('fs/promises');
564
- const path = await import('path');
565
- let currentDir = process.cwd();
566
-
567
- while (currentDir !== path.parse(currentDir).root) {
568
- try {
569
- await fs.access(path.join(currentDir, '.aiwf'));
570
- break;
571
- } catch {
572
- currentDir = path.dirname(currentDir);
573
- }
574
- }
334
+ const { quickCheck } = await import('../utils/engineering-guard.js');
575
335
 
576
- const manager = new CheckpointManager(currentDir);
577
- await manager.initialize();
336
+ const checkPath = targetPath || process.cwd();
337
+ console.log(chalk.blue(`⚡ 빠른 복잡도 체크: ${checkPath}`));
578
338
 
579
- console.log(chalk.blue(`🔄 체크포인트 ${checkpointId}에서 복구 중...`));
339
+ const report = await quickCheck(checkPath);
580
340
 
581
- const result = await manager.restoreFromCheckpoint(checkpointId);
582
-
583
- if (result.success) {
584
- console.log(chalk.green('✅ 체크포인트 복구 성공!'));
585
- console.log(` 세션 ID: ${chalk.cyan(result.checkpoint.state_snapshot.session_id)}`);
586
- console.log(` 스프린트: ${chalk.blue(result.checkpoint.state_snapshot.sprint_id || 'N/A')}`);
587
- console.log('');
588
- console.log(chalk.green('🚀 복구 완료! YOLO 모드를 다시 실행할 수 있습니다.'));
341
+ if (report.summary.high_severity > 0) {
342
+ console.log(chalk.red('⚠️ 오버엔지니어링 위험 감지!'));
343
+ console.log('권장사항:', report.recommendations.join(', '));
344
+ } else {
345
+ console.log(chalk.green('✅ 복잡도 양호'));
589
346
  }
347
+
590
348
  } catch (error) {
591
- console.error(chalk.red('❌ 체크포인트 복구 실패:'), error.message);
349
+ console.error(chalk.red('❌ 빠른 체크 실패:'), error.message);
592
350
  process.exit(1);
593
351
  }
594
352
  });
595
353
 
596
- checkpoint
597
- .command('status')
598
- .description('Show current YOLO session status / 현재 YOLO 세션 상태')
354
+ guard
355
+ .command('init')
356
+ .description('Initialize YOLO config with guard settings / YOLO 설정 초기화')
599
357
  .action(async () => {
600
358
  try {
601
359
  const fs = await import('fs/promises');
602
360
  const path = await import('path');
603
- let currentDir = process.cwd();
361
+ const yaml = await import('js-yaml');
604
362
 
605
- while (currentDir !== path.parse(currentDir).root) {
606
- try {
607
- await fs.access(path.join(currentDir, '.aiwf'));
608
- break;
609
- } catch {
610
- currentDir = path.dirname(currentDir);
363
+ const configPath = path.join(process.cwd(), '.aiwf', 'yolo-config.yaml');
364
+
365
+ // 기본 설정 생성
366
+ const defaultConfig = {
367
+ engineering_level: 'minimal',
368
+ focus_rules: ['requirement_first', 'simple_solution', 'no_gold_plating', 'stay_on_track'],
369
+ overengineering_prevention: {
370
+ max_file_lines: 300,
371
+ max_function_lines: 50,
372
+ max_nesting_depth: 4,
373
+ max_abstraction_layers: 3,
374
+ limit_design_patterns: true,
375
+ no_future_proofing: true,
376
+ enforce_yagni: true
611
377
  }
612
- }
378
+ };
379
+
380
+ // .aiwf 디렉토리 생성
381
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
382
+
383
+ // 설정 파일 쓰기
384
+ await fs.writeFile(configPath, yaml.dump(defaultConfig), 'utf-8');
385
+
386
+ console.log(chalk.green('✅ YOLO 설정 파일 생성 완료!'));
387
+ console.log(` 경로: ${chalk.cyan(configPath)}`);
388
+ console.log('');
389
+ console.log(chalk.bold('🛡️ 오버엔지니어링 방지 설정:'));
390
+ console.log(` 파일 크기 제한: ${chalk.yellow('300')}줄`);
391
+ console.log(` 함수 크기 제한: ${chalk.yellow('50')}줄`);
392
+ console.log(` 중첩 깊이 제한: ${chalk.yellow('4')}레벨`);
393
+ console.log(` YAGNI 원칙: ${chalk.green('적용')}`);
394
+
395
+ } catch (error) {
396
+ console.error(chalk.red('❌ 설정 초기화 실패:'), error.message);
397
+ process.exit(1);
398
+ }
399
+ });
400
+
401
+ guard
402
+ .command('feedback [area]')
403
+ .description('Get engineering feedback for area / 특정 영역 피드백 받기')
404
+ .action(async (area) => {
405
+ try {
406
+ const { EngineeringGuard } = await import('../utils/engineering-guard.js');
407
+ const guard = new EngineeringGuard();
613
408
 
614
- const manager = new CheckpointManager(currentDir);
615
- await manager.loadState();
409
+ // 설정 파일 로드 시도
410
+ await guard.loadConfig('.aiwf/yolo-config.yaml');
616
411
 
617
- console.log(chalk.bold('📊 YOLO 세션 상태:'));
412
+ const targetArea = area || 'current_task_area';
413
+ console.log(chalk.blue(`💡 엔지니어링 피드백 제공: ${targetArea}`));
618
414
  console.log('');
619
415
 
620
- if (!manager.currentState.session_id) {
621
- console.log(chalk.yellow('📭 활성 YOLO 세션이 없습니다.'));
622
- return;
416
+ const feedback = await guard.provideFeedback(targetArea);
417
+
418
+ if (feedback && feedback.length > 0) {
419
+ for (const item of feedback) {
420
+ const icon = item.level === 'error' ? '❌' :
421
+ item.level === 'warning' ? '⚠️' : '💡';
422
+ console.log(`${icon} ${item.message}`);
423
+ }
424
+ } else {
425
+ console.log(chalk.green('✅ 현재 영역은 복잡도 기준을 만족합니다.'));
623
426
  }
624
427
 
625
- console.log(`세션 ID: ${chalk.cyan(manager.currentState.session_id)}`);
626
- console.log(`스프린트: ${chalk.blue(manager.currentState.sprint_id || 'N/A')}`);
627
- console.log(`완료된 태스크: ${chalk.green(manager.currentState.completed_tasks.length)}개`);
628
- console.log(`체크포인트: ${chalk.blue(manager.currentState.checkpoints.length)}개`);
629
428
  } catch (error) {
630
- console.error(chalk.red('❌ 상태 확인 실패:'), error.message);
429
+ console.error(chalk.red('❌ 피드백 생성 실패:'), error.message);
631
430
  process.exit(1);
632
431
  }
633
432
  });
@@ -5,8 +5,7 @@ import chalk from 'chalk';
5
5
  import {
6
6
  TOOL_DIRS,
7
7
  AIWF_DIRS,
8
- FILES,
9
- getBackupDirName as getBackupDirNamePath
8
+ FILES
10
9
  } from '../utils/paths.js';
11
10
 
12
11
  let BACKUP_DIR = null;
@@ -16,7 +15,9 @@ let BACKUP_DIR = null;
16
15
  * @returns {string} Backup directory name
17
16
  */
18
17
  function getBackupDirName() {
19
- return getBackupDirNamePath();
18
+ const now = new Date();
19
+ const timestamp = now.toISOString().replace(/[:.]/g, '-').slice(0, -5);
20
+ return `backup-${timestamp}`;
20
21
  }
21
22
 
22
23
  /**