aiwf 0.3.17 → 0.3.19

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 (99) hide show
  1. package/README.ko.md +84 -0
  2. package/README.md +87 -0
  3. package/ai-tools/README.md +105 -0
  4. package/ai-tools/augment/README.md +371 -0
  5. package/ai-tools/augment/config.json +30 -0
  6. package/ai-tools/augment/template/.augment/aiwf-integration.md +387 -0
  7. package/ai-tools/augment/template/.augment/augment.yaml +347 -0
  8. package/ai-tools/augment/template/augment.config.json +64 -0
  9. package/ai-tools/claude-code/README.md +151 -0
  10. package/ai-tools/claude-code/config.json +28 -0
  11. package/ai-tools/claude-code/template/CLAUDE.md +91 -0
  12. package/ai-tools/cursor/README.md +314 -0
  13. package/ai-tools/cursor/config.json +29 -0
  14. package/ai-tools/cursor/template/.cursorrules +362 -0
  15. package/ai-tools/github-copilot/README.md +195 -0
  16. package/ai-tools/github-copilot/config.json +24 -0
  17. package/ai-tools/github-copilot/template/.github/copilot-instructions.md +202 -0
  18. package/ai-tools/windsurf/README.md +355 -0
  19. package/ai-tools/windsurf/config.json +29 -0
  20. package/ai-tools/windsurf/template/.windsurf/aiwf-rules.md +260 -0
  21. package/ai-tools/windsurf/template/.windsurf/windsurf.config.js +312 -0
  22. package/ai-tools/windsurf/template/windsurf.config.json +63 -0
  23. package/docs/CODE_CLEANUP_GUIDE.ko.md +415 -0
  24. package/docs/CODE_CLEANUP_GUIDE.md +415 -0
  25. package/docs/VALIDATOR_API.ko.md +324 -0
  26. package/docs/VALIDATOR_API.md +324 -0
  27. package/package.json +3 -2
  28. package/src/cli/index.js +338 -0
  29. package/src/config/file-lists.js +5 -5
  30. package/src/lib/backup-manager.js +4 -3
  31. package/src/lib/installer.js +126 -3
  32. package/src/lib/validator.js +78 -313
  33. package/src/utils/messages.js +16 -2
  34. package/src/utils/paths.js +1 -20
  35. package/templates/README.md +0 -78
  36. package/templates/api-server/config.json +0 -64
  37. package/templates/api-server/template/.aiwf/config.json +0 -56
  38. package/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  39. package/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  40. package/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  41. package/templates/api-server/template/.env.example +0 -29
  42. package/templates/api-server/template/.eslintrc.json +0 -23
  43. package/templates/api-server/template/README.md +0 -171
  44. package/templates/api-server/template/jest.config.js +0 -26
  45. package/templates/api-server/template/nodemon.json +0 -9
  46. package/templates/api-server/template/package.json +0 -61
  47. package/templates/api-server/template/src/app.ts +0 -60
  48. package/templates/api-server/template/src/config/swagger.ts +0 -38
  49. package/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  50. package/templates/api-server/template/src/controllers/authController.ts +0 -128
  51. package/templates/api-server/template/src/controllers/statusController.ts +0 -28
  52. package/templates/api-server/template/src/index.ts +0 -28
  53. package/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  54. package/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  55. package/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  56. package/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  57. package/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  58. package/templates/api-server/template/src/routes/aiwf.ts +0 -31
  59. package/templates/api-server/template/src/routes/index.ts +0 -13
  60. package/templates/api-server/template/src/routes/v1/index.ts +0 -67
  61. package/templates/api-server/template/src/utils/logger.ts +0 -37
  62. package/templates/api-server/template/tests/app.test.ts +0 -40
  63. package/templates/api-server/template/tsconfig.json +0 -43
  64. package/templates/npm-library/config.json +0 -61
  65. package/templates/npm-library/template/LICENSE +0 -21
  66. package/templates/npm-library/template/README.md +0 -201
  67. package/templates/npm-library/template/package.json +0 -78
  68. package/templates/web-app/config.json +0 -53
  69. package/templates/web-app/template/.aiwf/config.json +0 -54
  70. package/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  71. package/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  72. package/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  73. package/templates/web-app/template/.eslintrc.cjs +0 -20
  74. package/templates/web-app/template/README.md +0 -151
  75. package/templates/web-app/template/index.html +0 -14
  76. package/templates/web-app/template/package.json +0 -44
  77. package/templates/web-app/template/postcss.config.js +0 -6
  78. package/templates/web-app/template/public/vite.svg +0 -1
  79. package/templates/web-app/template/src/App.tsx +0 -21
  80. package/templates/web-app/template/src/components/Layout.tsx +0 -57
  81. package/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  82. package/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  83. package/templates/web-app/template/src/index.css +0 -60
  84. package/templates/web-app/template/src/main.tsx +0 -10
  85. package/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  86. package/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  87. package/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  88. package/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  89. package/templates/web-app/template/src/types/global.d.ts +0 -9
  90. package/templates/web-app/template/src/vite-env.d.ts +0 -1
  91. package/templates/web-app/template/tailwind.config.js +0 -30
  92. package/templates/web-app/template/tsconfig.json +0 -37
  93. package/templates/web-app/template/tsconfig.node.json +0 -10
  94. package/templates/web-app/template/vite.config.ts +0 -23
  95. /package/rules/global/{code-style-guide.md → aiwf-code-style-guide.md} +0 -0
  96. /package/rules/global/{coding-principles.md → aiwf-coding-principles.md} +0 -0
  97. /package/rules/global/{development-process.md → aiwf-development-process.md} +0 -0
  98. /package/rules/global/{global-rules.md → aiwf-global-rules.md} +0 -0
  99. /package/rules/manual/{generate-plan-docs.md → aiwf-generate-plan-docs.md} +0 -0
package/src/cli/index.js CHANGED
@@ -555,6 +555,69 @@ checkpoint
555
555
  }
556
556
  });
557
557
 
558
+ checkpoint
559
+ .command('add [type]')
560
+ .alias('create')
561
+ .description('Create a checkpoint / ģ²“ķ¬ķ¬ģøķŠø ģƒģ„±')
562
+ .option('-m, --message <text>', 'Description for the checkpoint / ģ²“ķ¬ķ¬ģøķŠø 설명')
563
+ .option('--meta <json>', 'Additional metadata as JSON / 추가 ė©”ķƒ€ė°ģ“ķ„°(JSON)')
564
+ .option('--cleanup <n>', 'Keep only last N checkpoints after creating / ģƒģ„± 후 최근 N개만 볓씓')
565
+ .action(async (type, options) => {
566
+ try {
567
+ const fs = await import('fs/promises');
568
+ const path = await import('path');
569
+ let currentDir = process.cwd();
570
+
571
+ // ķ”„ė”œģ ķŠø 루트 ķƒģƒ‰ (.aiwf 기준)
572
+ while (currentDir !== path.parse(currentDir).root) {
573
+ try {
574
+ await fs.access(path.join(currentDir, '.aiwf'));
575
+ break;
576
+ } catch {
577
+ currentDir = path.dirname(currentDir);
578
+ }
579
+ }
580
+
581
+ const manager = new CheckpointManager(currentDir);
582
+ await manager.initialize();
583
+
584
+ // ė©”ķƒ€ė°ģ“ķ„° 구성
585
+ const metadata = {};
586
+ if (options.message) metadata.message = options.message;
587
+ if (options.meta) {
588
+ try {
589
+ const parsed = JSON.parse(options.meta);
590
+ Object.assign(metadata, parsed);
591
+ } catch (e) {
592
+ console.error(chalk.red('āŒ --meta ź°’ģ“ ģ˜¬ė°”ė„ø JSON ķ˜•ģ‹ģ“ ģ•„ė‹™ė‹ˆė‹¤.'));
593
+ process.exit(1);
594
+ }
595
+ }
596
+
597
+ const cpType = type || 'manual';
598
+ const checkpointId = await manager.createCheckpoint(cpType, metadata);
599
+
600
+ console.log(chalk.green('āœ… ģ²“ķ¬ķ¬ģøķŠø ģƒģ„± ģ™„ė£Œ!'));
601
+ console.log(` ID: ${chalk.cyan(checkpointId)} ģœ ķ˜•: ${chalk.yellow(cpType)}`);
602
+ if (metadata.message) {
603
+ console.log(` ė©”ģ‹œģ§€: ${chalk.blue(metadata.message)}`);
604
+ }
605
+
606
+ if (options.cleanup) {
607
+ const keep = parseInt(options.cleanup, 10);
608
+ if (!Number.isNaN(keep) && keep > 0) {
609
+ await manager.cleanup(keep);
610
+ console.log(`🧹 최근 ${chalk.cyan(keep)}개만 ė³“ģ”“ķ•˜ė„ė” ģ •ė¦¬ķ–ˆģŠµė‹ˆė‹¤.`);
611
+ } else {
612
+ console.log(chalk.yellow('āš ļø --cleanup ź°’ģ“ ģœ ķšØķ•˜ģ§€ ģ•Šģ•„ 정리넼 ź±“ė„ˆėœė‹ˆė‹¤. ģ •ģˆ˜ė”œ ģž…ė „ķ•˜ģ„øģš”.'));
613
+ }
614
+ }
615
+ } catch (error) {
616
+ console.error(chalk.red('āŒ ģ²“ķ¬ķ¬ģøķŠø ģƒģ„± ģ‹¤ķŒØ:'), error.message);
617
+ process.exit(1);
618
+ }
619
+ });
620
+
558
621
  checkpoint
559
622
  .command('restore <checkpointId>')
560
623
  .description('Restore from checkpoint / ģ²“ķ¬ķ¬ģøķŠøģ—ģ„œ 복구')
@@ -632,6 +695,281 @@ checkpoint
632
695
  }
633
696
  });
634
697
 
698
+ // ģ²“ķ¬ķ¬ģøķŠø ė¦¬ķ¬ķŠø ģƒģ„±
699
+ checkpoint
700
+ .command('report')
701
+ .description('Generate progress report / ģ§„ķ–‰ ģƒķ™© ė¦¬ķ¬ķŠø ģƒģ„±')
702
+ .action(async () => {
703
+ try {
704
+ const fs = await import('fs/promises');
705
+ const path = await import('path');
706
+ let currentDir = process.cwd();
707
+
708
+ while (currentDir !== path.parse(currentDir).root) {
709
+ try {
710
+ await fs.access(path.join(currentDir, '.aiwf'));
711
+ break;
712
+ } catch {
713
+ currentDir = path.dirname(currentDir);
714
+ }
715
+ }
716
+
717
+ const manager = new CheckpointManager(currentDir);
718
+ await manager.loadState();
719
+
720
+ console.log(chalk.blue('šŸ“Š ģ§„ķ–‰ ģƒķ™© ė¦¬ķ¬ķŠø ģƒģ„± 중...'));
721
+ console.log('');
722
+
723
+ const report = await manager.generateProgressReport();
724
+
725
+ console.log(chalk.bold('šŸŽÆ YOLO ģ„øģ…˜ ė¦¬ķ¬ķŠø'));
726
+ console.log(''.padEnd(50, '='));
727
+ console.log('');
728
+
729
+ // ģ„øģ…˜ 정볓
730
+ console.log(chalk.bold('šŸ“‹ ģ„øģ…˜ 정볓:'));
731
+ console.log(` ID: ${chalk.cyan(report.session.id)}`);
732
+ console.log(` ģ‹œģž‘: ${chalk.gray(new Date(report.session.started).toLocaleString())}`);
733
+ console.log(` ģŠ¤ķ”„ė¦°ķŠø: ${chalk.blue(report.session.sprint || 'N/A')}`);
734
+ console.log('');
735
+
736
+ // ģ§„ķ–‰ ģƒķ™©
737
+ console.log(chalk.bold('šŸ“ˆ ģ§„ķ–‰ ģƒķ™©:'));
738
+ console.log(` ģ™„ė£Œ: ${chalk.green(report.progress.completed)}개`);
739
+ console.log(` ģ‹¤ķŒØ: ${chalk.red(report.progress.failed)}개`);
740
+ console.log(` ź±“ė„ˆėœ€: ${chalk.yellow(report.progress.skipped)}개`);
741
+ console.log('');
742
+
743
+ // ģ„±ėŠ„ ģ§€ķ‘œ
744
+ console.log(chalk.bold('ā±ļø ģ„±ėŠ„ ģ§€ķ‘œ:'));
745
+ console.log(` ģ“ ģ‹œź°„: ${chalk.blue(report.performance.total_time)}`);
746
+ console.log(` ķ‰ź·  태스크 ģ‹œź°„: ${chalk.blue(report.performance.avg_task_time)}`);
747
+ console.log(` 성공넠: ${chalk.green(report.performance.success_rate)}`);
748
+
749
+ } catch (error) {
750
+ console.error(chalk.red('āŒ ė¦¬ķ¬ķŠø ģƒģ„± ģ‹¤ķŒØ:'), error.message);
751
+ process.exit(1);
752
+ }
753
+ });
754
+
755
+ // ģ²“ķ¬ķ¬ģøķŠø 정리
756
+ checkpoint
757
+ .command('clean')
758
+ .description('Clean old checkpoints / ģ˜¤ėž˜ėœ ģ²“ķ¬ķ¬ģøķŠø 정리')
759
+ .option('--keep <n>', 'Number of checkpoints to keep / ģœ ģ§€ķ•  ģ²“ķ¬ķ¬ģøķŠø 수', '10')
760
+ .option('--dry-run', 'Preview without deletion / ģ‹¤ģ œ ģ‚­ģ œ ģ—†ģ“ 미리볓기')
761
+ .action(async (options) => {
762
+ try {
763
+ const fs = await import('fs/promises');
764
+ const path = await import('path');
765
+ let currentDir = process.cwd();
766
+
767
+ while (currentDir !== path.parse(currentDir).root) {
768
+ try {
769
+ await fs.access(path.join(currentDir, '.aiwf'));
770
+ break;
771
+ } catch {
772
+ currentDir = path.dirname(currentDir);
773
+ }
774
+ }
775
+
776
+ const manager = new CheckpointManager(currentDir);
777
+ const keepLast = parseInt(options.keep);
778
+
779
+ console.log(chalk.blue(`🧹 ģ²“ķ¬ķ¬ģøķŠø 정리 중... (최근 ${keepLast}개 ģœ ģ§€)`));
780
+
781
+ if (options.dryRun) {
782
+ console.log(chalk.yellow('šŸ” ė“œė¼ģ“ėŸ° ėŖØė“œ: ģ‹¤ģ œ ģ‚­ģ œėŠ” ģˆ˜ķ–‰ķ•˜ģ§€ ģ•ŠģŠµė‹ˆė‹¤.'));
783
+ const checkpoints = await manager.listCheckpoints();
784
+ if (checkpoints.length <= keepLast) {
785
+ console.log(chalk.green('āœ… 정리할 ģ²“ķ¬ķ¬ģøķŠøź°€ ģ—†ģŠµė‹ˆė‹¤.'));
786
+ } else {
787
+ const toDelete = checkpoints.slice(keepLast);
788
+ console.log(chalk.yellow(`ģ‚­ģ œ ģ˜ˆģ • ģ²“ķ¬ķ¬ģøķŠø (${toDelete.length}개):`));
789
+ for (const cp of toDelete) {
790
+ console.log(` ${chalk.red('šŸ—‘ļø')} ${chalk.cyan(cp.id)}`);
791
+ }
792
+ }
793
+ } else {
794
+ await manager.cleanup(keepLast);
795
+ console.log(chalk.green('āœ… ģ²“ķ¬ķ¬ģøķŠø 정리 ģ™„ė£Œ!'));
796
+ }
797
+
798
+ } catch (error) {
799
+ console.error(chalk.red('āŒ ģ²“ķ¬ķ¬ģøķŠø 정리 ģ‹¤ķŒØ:'), error.message);
800
+ process.exit(1);
801
+ }
802
+ });
803
+
804
+ // Engineering Guard commands (ģ˜¤ė²„ģ—”ģ§€ė‹ˆģ–“ė§ ė°©ģ§€)
805
+ const guard = program.command('guard');
806
+ guard.description('Engineering guard for overengineering prevention / ģ˜¤ė²„ģ—”ģ§€ė‹ˆģ–“ė§ ė°©ģ§€ ź°€ė“œ');
807
+
808
+ guard
809
+ .command('check [path]')
810
+ .description('Check project complexity / ķ”„ė”œģ ķŠø ė³µģž”ė„ 검사')
811
+ .option('--config <path>', 'YOLO config file path / YOLO 설정 ķŒŒģ¼ 경딜', '.aiwf/yolo-config.yaml')
812
+ .action(async (targetPath, options) => {
813
+ try {
814
+ const { EngineeringGuard } = await import('../utils/engineering-guard.js');
815
+ const guard = new EngineeringGuard();
816
+
817
+ // 설정 ķŒŒģ¼ ė”œė“œ
818
+ await guard.loadConfig(options.config);
819
+
820
+ const checkPath = targetPath || process.cwd();
821
+ console.log(chalk.blue(`šŸ” ė³µģž”ė„ 검사 중: ${checkPath}`));
822
+ console.log('');
823
+
824
+ const report = await guard.checkProject(checkPath);
825
+
826
+ // 결과 출렄
827
+ console.log(chalk.bold('šŸ“Š ė³µģž”ė„ ė¶„ģ„ ź²°ź³¼:'));
828
+ console.log(` ģ“ ģœ„ė°˜ģ‚¬ķ•­: ${chalk.yellow(report.summary.total_violations)}`);
829
+ console.log(` ė†’ģ€ ģ‹¬ź°ė„: ${chalk.red(report.summary.high_severity)}`);
830
+ console.log(` 중간 ģ‹¬ź°ė„: ${chalk.yellow(report.summary.medium_severity)}`);
831
+ console.log(` 경고사항: ${chalk.blue(report.summary.warnings)}`);
832
+ console.log('');
833
+
834
+ if (report.violations && report.violations.length > 0) {
835
+ console.log(chalk.bold('🚨 ģœ„ė°˜ģ‚¬ķ•­:'));
836
+ for (const violation of report.violations.slice(0, 10)) {
837
+ const icon = violation.severity === 'high' ? 'āŒ' :
838
+ violation.severity === 'medium' ? 'āš ļø' : 'šŸ’”';
839
+ console.log(` ${icon} ${chalk.cyan(violation.type)}: ${violation.file || violation.message}`);
840
+ if (violation.details) {
841
+ console.log(` ${chalk.gray(violation.details)}`);
842
+ }
843
+ }
844
+ console.log('');
845
+ }
846
+
847
+ if (report.recommendations && report.recommendations.length > 0) {
848
+ console.log(chalk.bold('šŸ’” ź¶Œģž„ģ‚¬ķ•­:'));
849
+ for (const rec of report.recommendations) {
850
+ console.log(` • ${chalk.green(rec)}`);
851
+ }
852
+ console.log('');
853
+ }
854
+
855
+ // 통과/ģ‹¤ķŒØ ķŒģ •
856
+ if (report.passed) {
857
+ console.log(chalk.green('āœ… ė³µģž”ė„ 기준 통과'));
858
+ } else {
859
+ console.log(chalk.red('āŒ ė³µģž”ė„ 기준 쓈과 - ė¦¬ķŒ©ķ† ė§ ķ•„ģš”'));
860
+ }
861
+
862
+ } catch (error) {
863
+ console.error(chalk.red('āŒ ė³µģž”ė„ 검사 ģ‹¤ķŒØ:'), error.message);
864
+ process.exit(1);
865
+ }
866
+ });
867
+
868
+ guard
869
+ .command('quick [path]')
870
+ .description('Quick complexity check / 빠넸 ė³µģž”ė„ 첓크')
871
+ .action(async (targetPath) => {
872
+ try {
873
+ const { quickCheck } = await import('../utils/engineering-guard.js');
874
+
875
+ const checkPath = targetPath || process.cwd();
876
+ console.log(chalk.blue(`⚔ 빠넸 ė³µģž”ė„ 첓크: ${checkPath}`));
877
+
878
+ const report = await quickCheck(checkPath);
879
+
880
+ if (report.summary.high_severity > 0) {
881
+ console.log(chalk.red('āš ļø ģ˜¤ė²„ģ—”ģ§€ė‹ˆģ–“ė§ ģœ„ķ—˜ 감지!'));
882
+ console.log('ź¶Œģž„ģ‚¬ķ•­:', report.recommendations.join(', '));
883
+ } else {
884
+ console.log(chalk.green('āœ… ė³µģž”ė„ ģ–‘ķ˜ø'));
885
+ }
886
+
887
+ } catch (error) {
888
+ console.error(chalk.red('āŒ 빠넸 첓크 ģ‹¤ķŒØ:'), error.message);
889
+ process.exit(1);
890
+ }
891
+ });
892
+
893
+ guard
894
+ .command('init')
895
+ .description('Initialize YOLO config with guard settings / YOLO 설정 ģ“ˆźø°ķ™”')
896
+ .action(async () => {
897
+ try {
898
+ const fs = await import('fs/promises');
899
+ const path = await import('path');
900
+ const yaml = await import('js-yaml');
901
+
902
+ const configPath = path.join(process.cwd(), '.aiwf', 'yolo-config.yaml');
903
+
904
+ // źø°ė³ø 설정 ģƒģ„±
905
+ const defaultConfig = {
906
+ engineering_level: 'minimal',
907
+ focus_rules: ['requirement_first', 'simple_solution', 'no_gold_plating', 'stay_on_track'],
908
+ overengineering_prevention: {
909
+ max_file_lines: 300,
910
+ max_function_lines: 50,
911
+ max_nesting_depth: 4,
912
+ max_abstraction_layers: 3,
913
+ limit_design_patterns: true,
914
+ no_future_proofing: true,
915
+ enforce_yagni: true
916
+ }
917
+ };
918
+
919
+ // .aiwf 디렉토리 ģƒģ„±
920
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
921
+
922
+ // 설정 ķŒŒģ¼ ģ“°źø°
923
+ await fs.writeFile(configPath, yaml.dump(defaultConfig), 'utf-8');
924
+
925
+ console.log(chalk.green('āœ… YOLO 설정 ķŒŒģ¼ ģƒģ„± ģ™„ė£Œ!'));
926
+ console.log(` 경딜: ${chalk.cyan(configPath)}`);
927
+ console.log('');
928
+ console.log(chalk.bold('šŸ›”ļø ģ˜¤ė²„ģ—”ģ§€ė‹ˆģ–“ė§ ė°©ģ§€ 설정:'));
929
+ console.log(` ķŒŒģ¼ 크기 ģ œķ•œ: ${chalk.yellow('300')}줄`);
930
+ console.log(` ķ•Øģˆ˜ 크기 ģ œķ•œ: ${chalk.yellow('50')}줄`);
931
+ console.log(` 중첩 ź¹Šģ“ ģ œķ•œ: ${chalk.yellow('4')}레벨`);
932
+ console.log(` YAGNI 원칙: ${chalk.green('적용')}`);
933
+
934
+ } catch (error) {
935
+ console.error(chalk.red('āŒ 설정 ģ“ˆźø°ķ™” ģ‹¤ķŒØ:'), error.message);
936
+ process.exit(1);
937
+ }
938
+ });
939
+
940
+ guard
941
+ .command('feedback [area]')
942
+ .description('Get engineering feedback for area / ķŠ¹ģ • ģ˜ģ—­ ķ”¼ė“œė°± 받기')
943
+ .action(async (area) => {
944
+ try {
945
+ const { EngineeringGuard } = await import('../utils/engineering-guard.js');
946
+ const guard = new EngineeringGuard();
947
+
948
+ // 설정 ķŒŒģ¼ ė”œė“œ ģ‹œė„
949
+ await guard.loadConfig('.aiwf/yolo-config.yaml');
950
+
951
+ const targetArea = area || 'current_task_area';
952
+ console.log(chalk.blue(`šŸ’” ģ—”ģ§€ė‹ˆģ–“ė§ ķ”¼ė“œė°± 제공: ${targetArea}`));
953
+ console.log('');
954
+
955
+ const feedback = await guard.provideFeedback(targetArea);
956
+
957
+ if (feedback && feedback.length > 0) {
958
+ for (const item of feedback) {
959
+ const icon = item.level === 'error' ? 'āŒ' :
960
+ item.level === 'warning' ? 'āš ļø' : 'šŸ’”';
961
+ console.log(`${icon} ${item.message}`);
962
+ }
963
+ } else {
964
+ console.log(chalk.green('āœ… ķ˜„ģž¬ ģ˜ģ—­ģ€ ė³µģž”ė„ źø°ģ¤€ģ„ ė§Œģ”±ķ•©ė‹ˆė‹¤.'));
965
+ }
966
+
967
+ } catch (error) {
968
+ console.error(chalk.red('āŒ ķ”¼ė“œė°± ģƒģ„± ģ‹¤ķŒØ:'), error.message);
969
+ process.exit(1);
970
+ }
971
+ });
972
+
635
973
  // GitHub integration commands
636
974
  const github = program.command('github');
637
975
  github.description('GitHub integration / GitHub 통합');
@@ -66,15 +66,15 @@ export const COMMAND_FILES = [
66
66
 
67
67
  // źø€ė”œė²Œ 룰 ķŒŒģ¼ ėŖ©ė”
68
68
  export const GLOBAL_RULES_FILES = [
69
- 'code-style-guide.md',
70
- 'coding-principles.md',
71
- 'development-process.md',
72
- 'global-rules.md'
69
+ 'aiwf-code-style-guide.md',
70
+ 'aiwf-coding-principles.md',
71
+ 'aiwf-development-process.md',
72
+ 'aiwf-global-rules.md'
73
73
  ];
74
74
 
75
75
  // ģˆ˜ė™ 룰 ķŒŒģ¼ ėŖ©ė”
76
76
  export const MANUAL_RULES_FILES = [
77
- 'generate-plan-docs.md'
77
+ 'aiwf-generate-plan-docs.md'
78
78
  ];
79
79
 
80
80
  // ķ…œķ”Œė¦æ ķŒŒģ¼ ėŖ©ė” (햄후 추가 ģ˜ˆģ •)
@@ -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
  /**
@@ -34,7 +34,6 @@ import {
34
34
  import {
35
35
  FILES,
36
36
  TOOL_DIRS,
37
- TEMP_DIRS,
38
37
  AIWF_DIRS,
39
38
  BASE_DIRS,
40
39
  getAllAiwfDirectories,
@@ -507,14 +506,112 @@ async function updateCommands(languagePath, spinner, msg, debugLog) {
507
506
  }
508
507
  }
509
508
 
509
+ /**
510
+ * Check if tool directories exist and handle them
511
+ * @param {Object} spinner - Ora spinner instance
512
+ * @param {Object} msg - Localized messages
513
+ * @param {boolean} debugLog - Debug mode flag
514
+ * @returns {Promise<Object>} Directory status info
515
+ */
516
+ async function checkAndHandleExistingToolDirs(spinner, msg, debugLog) {
517
+ const toolDirStatus = {
518
+ cursor: {
519
+ exists: false,
520
+ hadFiles: false,
521
+ backedUp: false
522
+ },
523
+ windsurf: {
524
+ exists: false,
525
+ hadFiles: false,
526
+ backedUp: false
527
+ }
528
+ };
529
+
530
+ // Check Cursor directory
531
+ try {
532
+ await fs.access(TOOL_DIRS.CURSOR_RULES);
533
+ toolDirStatus.cursor.exists = true;
534
+
535
+ const cursorFiles = await fs.readdir(TOOL_DIRS.CURSOR_RULES);
536
+ const mdcFiles = cursorFiles.filter(file => file.endsWith('.mdc'));
537
+
538
+ if (mdcFiles.length > 0) {
539
+ toolDirStatus.cursor.hadFiles = true;
540
+ logWithSpinner(spinner, `${msg.foundExistingCursor} (${mdcFiles.length} files)`, debugLog);
541
+
542
+ // Backup existing files
543
+ logWithSpinner(spinner, msg.backingUpToolFiles, debugLog);
544
+ const backupDir = path.join(TOOL_DIRS.CURSOR_RULES, `backup_${Date.now()}`);
545
+ await fs.mkdir(backupDir, { recursive: true });
546
+
547
+ for (const file of mdcFiles) {
548
+ const srcPath = path.join(TOOL_DIRS.CURSOR_RULES, file);
549
+ const backupPath = path.join(backupDir, file);
550
+ await fs.copyFile(srcPath, backupPath);
551
+ }
552
+
553
+ toolDirStatus.cursor.backedUp = true;
554
+ logWithSpinner(spinner, `${msg.toolBackupCreated}: ${backupDir}`, debugLog);
555
+
556
+ // Remove existing files
557
+ for (const file of mdcFiles) {
558
+ await fs.unlink(path.join(TOOL_DIRS.CURSOR_RULES, file));
559
+ }
560
+ }
561
+ } catch (error) {
562
+ // Directory doesn't exist, that's fine
563
+ logWithSpinner(spinner, `Cursor rules directory not found, will create new`, debugLog);
564
+ }
565
+
566
+ // Check Windsurf directory
567
+ try {
568
+ await fs.access(TOOL_DIRS.WINDSURF_RULES);
569
+ toolDirStatus.windsurf.exists = true;
570
+
571
+ const windsurfFiles = await fs.readdir(TOOL_DIRS.WINDSURF_RULES);
572
+ const mdFiles = windsurfFiles.filter(file => file.endsWith('.md'));
573
+
574
+ if (mdFiles.length > 0) {
575
+ toolDirStatus.windsurf.hadFiles = true;
576
+ logWithSpinner(spinner, `${msg.foundExistingWindsurf} (${mdFiles.length} files)`, debugLog);
577
+
578
+ // Backup existing files
579
+ logWithSpinner(spinner, msg.backingUpToolFiles, debugLog);
580
+ const backupDir = path.join(TOOL_DIRS.WINDSURF_RULES, `backup_${Date.now()}`);
581
+ await fs.mkdir(backupDir, { recursive: true });
582
+
583
+ for (const file of mdFiles) {
584
+ const srcPath = path.join(TOOL_DIRS.WINDSURF_RULES, file);
585
+ const backupPath = path.join(backupDir, file);
586
+ await fs.copyFile(srcPath, backupPath);
587
+ }
588
+
589
+ toolDirStatus.windsurf.backedUp = true;
590
+ logWithSpinner(spinner, `${msg.toolBackupCreated}: ${backupDir}`, debugLog);
591
+
592
+ // Remove existing files
593
+ for (const file of mdFiles) {
594
+ await fs.unlink(path.join(TOOL_DIRS.WINDSURF_RULES, file));
595
+ }
596
+ }
597
+ } catch (error) {
598
+ // Directory doesn't exist, that's fine
599
+ logWithSpinner(spinner, `Windsurf rules directory not found, will create new`, debugLog);
600
+ }
601
+
602
+ return toolDirStatus;
603
+ }
604
+
510
605
  /**
511
606
  * Download and process rules
512
607
  * @param {Object} spinner - Ora spinner instance
513
- {{ ... }}
514
608
  * @param {Object} msg - Localized messages
515
609
  * @param {boolean} debugLog - Debug mode flag
516
610
  */
517
611
  async function downloadAndProcessRules(spinner, msg, debugLog) {
612
+ // Check and handle existing tool directories first
613
+ logWithSpinner(spinner, msg.checkingExistingTools, debugLog);
614
+ const toolDirStatus = await checkAndHandleExistingToolDirs(spinner, msg, debugLog);
518
615
  // Try to download from GitHub first, fallback to local files
519
616
  const tmpRulesGlobal = path.join(os.tmpdir(), 'aiwf-rules-global');
520
617
  const tmpRulesManual = path.join(os.tmpdir(), 'aiwf-rules-manual');
@@ -670,6 +767,32 @@ async function downloadAndProcessRules(spinner, msg, debugLog) {
670
767
  } catch (error) {
671
768
  logWithSpinner(spinner, `No rules/manual directory found`, debugLog);
672
769
  }
770
+
771
+ // Success message for tool rules installation
772
+ logWithSpinner(spinner, msg.toolRulesInstalled, debugLog);
773
+
774
+ // Display installation results for tool directories
775
+ if (toolDirStatus.cursor.backedUp || toolDirStatus.windsurf.backedUp) {
776
+ console.log(chalk.cyan(`\nšŸ“‚ Tool Directory Installation Results:`));
777
+
778
+ if (toolDirStatus.cursor.backedUp) {
779
+ console.log(chalk.yellow(` • Cursor: Existing rules backed up and replaced`));
780
+ } else if (toolDirStatus.cursor.exists) {
781
+ console.log(chalk.green(` • Cursor: Rules installed (directory was empty)`));
782
+ } else {
783
+ console.log(chalk.green(` • Cursor: Rules installed (new directory)`));
784
+ }
785
+
786
+ if (toolDirStatus.windsurf.backedUp) {
787
+ console.log(chalk.yellow(` • Windsurf: Existing rules backed up and replaced`));
788
+ } else if (toolDirStatus.windsurf.exists) {
789
+ console.log(chalk.green(` • Windsurf: Rules installed (directory was empty)`));
790
+ } else {
791
+ console.log(chalk.green(` • Windsurf: Rules installed (new directory)`));
792
+ }
793
+ }
794
+
795
+ return toolDirStatus;
673
796
  }
674
797
 
675
798
  /**
@@ -819,7 +942,7 @@ export async function installAIWF(options = {}) {
819
942
  await updateCommands(languagePath, spinner, msg, debugLog);
820
943
 
821
944
  // Download and process rules
822
- await downloadAndProcessRules(spinner, msg, debugLog);
945
+ const toolDirStatus = await downloadAndProcessRules(spinner, msg, debugLog);
823
946
 
824
947
  // Restore project files if needed
825
948
  if (projectBackupDir) {