aiwf 0.3.7 → 0.3.8

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 (267) hide show
  1. package/.aiwf/00_PROJECT_MANIFEST.md +226 -2
  2. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/S03_M02_sprint_meta.md +112 -0
  3. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T01_S03_new_test_task.md +39 -0
  4. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T02_S03_first_task.md +39 -0
  5. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T03_S03_test_task.md +39 -0
  6. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T04_S03_task_with_special_characters.md +39 -0
  7. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T05_S03_test.md +39 -0
  8. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T06_S03_test_task.md +39 -0
  9. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T07_S03_next_task.md +39 -0
  10. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T08_S03_next_task.md +39 -0
  11. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T09_S03_new_test_task.md +39 -0
  12. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T100_S03_task_with_special_characters.md +39 -0
  13. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T101_S03_test.md +39 -0
  14. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T102_S03_test_task.md +39 -0
  15. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T103_S03_next_task.md +39 -0
  16. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T104_S03_next_task.md +39 -0
  17. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T105_S03_new_test_task.md +39 -0
  18. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T106_S03_first_task.md +39 -0
  19. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T107_S03_test_task.md +39 -0
  20. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T108_S03_task_with_special_characters.md +39 -0
  21. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T109_S03_test.md +39 -0
  22. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T10_S03_first_task.md +39 -0
  23. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T110_S03_test_task.md +39 -0
  24. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T111_S03_next_task.md +39 -0
  25. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T112_S03_next_task.md +39 -0
  26. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T11_S03_test_task.md +39 -0
  27. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T12_S03_task_with_special_characters.md +39 -0
  28. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T13_S03_test.md +39 -0
  29. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T14_S03_test_task.md +39 -0
  30. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T15_S03_next_task.md +39 -0
  31. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T16_S03_next_task.md +39 -0
  32. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T17_S03_new_test_task.md +39 -0
  33. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T18_S03_first_task.md +39 -0
  34. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T19_S03_test_task.md +39 -0
  35. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T20_S03_task_with_special_characters.md +39 -0
  36. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T21_S03_test.md +39 -0
  37. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T22_S03_test_task.md +39 -0
  38. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T23_S03_next_task.md +39 -0
  39. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T24_S03_next_task.md +39 -0
  40. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T25_S03_new_test_task.md +39 -0
  41. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T26_S03_first_task.md +39 -0
  42. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T27_S03_test_task.md +39 -0
  43. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T28_S03_task_with_special_characters.md +39 -0
  44. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T29_S03_test.md +39 -0
  45. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T30_S03_test_task.md +39 -0
  46. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T31_S03_next_task.md +39 -0
  47. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T32_S03_next_task.md +39 -0
  48. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T33_S03_new_test_task.md +39 -0
  49. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T34_S03_first_task.md +39 -0
  50. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T35_S03_test_task.md +39 -0
  51. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T36_S03_task_with_special_characters.md +39 -0
  52. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T37_S03_test.md +39 -0
  53. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T38_S03_test_task.md +39 -0
  54. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T39_S03_next_task.md +39 -0
  55. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T40_S03_next_task.md +39 -0
  56. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T41_S03_new_test_task.md +39 -0
  57. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T42_S03_first_task.md +39 -0
  58. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T43_S03_test_task.md +39 -0
  59. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T44_S03_task_with_special_characters.md +39 -0
  60. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T45_S03_test.md +39 -0
  61. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T46_S03_test_task.md +39 -0
  62. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T47_S03_next_task.md +39 -0
  63. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T48_S03_next_task.md +39 -0
  64. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T49_S03_new_test_task.md +39 -0
  65. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T50_S03_first_task.md +39 -0
  66. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T51_S03_test_task.md +39 -0
  67. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T52_S03_task_with_special_characters.md +39 -0
  68. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T53_S03_test.md +39 -0
  69. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T54_S03_test_task.md +39 -0
  70. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T55_S03_next_task.md +39 -0
  71. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T56_S03_next_task.md +39 -0
  72. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T57_S03_new_test_task.md +39 -0
  73. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T58_S03_first_task.md +39 -0
  74. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T59_S03_test_task.md +39 -0
  75. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T60_S03_task_with_special_characters.md +39 -0
  76. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T61_S03_test.md +39 -0
  77. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T62_S03_test_task.md +39 -0
  78. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T63_S03_next_task.md +39 -0
  79. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T64_S03_next_task.md +39 -0
  80. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T65_S03_new_test_task.md +39 -0
  81. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T66_S03_first_task.md +39 -0
  82. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T67_S03_test_task.md +39 -0
  83. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T68_S03_task_with_special_characters.md +39 -0
  84. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T69_S03_test.md +39 -0
  85. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T70_S03_test_task.md +39 -0
  86. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T71_S03_next_task.md +39 -0
  87. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T72_S03_next_task.md +39 -0
  88. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T73_S03_new_test_task.md +39 -0
  89. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T74_S03_first_task.md +39 -0
  90. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T75_S03_test_task.md +39 -0
  91. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T76_S03_task_with_special_characters.md +39 -0
  92. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T77_S03_test.md +39 -0
  93. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T78_S03_test_task.md +39 -0
  94. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T79_S03_next_task.md +39 -0
  95. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T80_S03_next_task.md +39 -0
  96. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T81_S03_new_test_task.md +39 -0
  97. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T82_S03_first_task.md +39 -0
  98. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T83_S03_test_task.md +39 -0
  99. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T84_S03_task_with_special_characters.md +39 -0
  100. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T85_S03_test.md +39 -0
  101. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T86_S03_test_task.md +39 -0
  102. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T87_S03_next_task.md +39 -0
  103. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T88_S03_next_task.md +39 -0
  104. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T89_S03_new_test_task.md +39 -0
  105. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T90_S03_first_task.md +39 -0
  106. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T91_S03_test_task.md +39 -0
  107. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T92_S03_task_with_special_characters.md +39 -0
  108. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T93_S03_test.md +39 -0
  109. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T94_S03_test_task.md +39 -0
  110. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T95_S03_next_task.md +39 -0
  111. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T96_S03_next_task.md +39 -0
  112. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T97_S03_new_test_task.md +39 -0
  113. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T98_S03_first_task.md +39 -0
  114. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T99_S03_test_task.md +39 -0
  115. package/.aiwf/test-results-s02.json +5 -5
  116. package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +3 -15
  117. package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -15
  118. package/.claude/commands/aiwf/aiwf_compress_stats.md +2 -51
  119. package/.claude/commands/aiwf/aiwf_compress_with_persona.md +5 -18
  120. package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
  121. package/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
  122. package/.claude/commands/aiwf/aiwf_evaluate.md +3 -16
  123. package/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
  124. package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
  125. package/.claude/commands/aiwf/aiwf_list_features.md +1 -1
  126. package/.claude/commands/aiwf/aiwf_switch_language.md +48 -120
  127. package/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
  128. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +2 -2
  129. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -2
  130. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_stats.md +10 -47
  131. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_with_persona.md +2 -2
  132. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
  133. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
  134. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_evaluate.md +2 -2
  135. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
  136. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
  137. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_list_features.md +1 -1
  138. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_architect.md +1 -1
  139. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_backend.md +1 -1
  140. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_data_analyst.md +1 -1
  141. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_frontend.md +1 -1
  142. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_security.md +1 -1
  143. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_status.md +1 -1
  144. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
  145. package/package.json +1 -1
  146. package/src/cli/index.js +174 -0
  147. package/src/commands/compress.js +60 -0
  148. package/src/commands/create-project.js +23 -17
  149. package/src/commands/evaluate.js +459 -0
  150. package/src/commands/feature.js +365 -0
  151. package/src/commands/persona.js +37 -16
  152. package/src/commands/token.js +312 -0
  153. package/src/lib/resource-loader.js +250 -0
  154. package/src/lib/resources/commands/ai-persona.js +602 -0
  155. package/src/lib/resources/commands/compress-context.js +389 -0
  156. package/src/lib/resources/commands/evaluate.js +246 -0
  157. package/src/lib/resources/commands/feature-ledger.js +565 -0
  158. package/src/lib/resources/commands/feature_commit_report.js +370 -0
  159. package/src/lib/resources/commands/persona-context-apply.js +562 -0
  160. package/src/lib/resources/commands/scan_git_history.js +234 -0
  161. package/src/lib/resources/commands/sync_feature_commits.js +154 -0
  162. package/src/lib/resources/commands/token-tracking.js +443 -0
  163. package/src/lib/resources/config/commit-patterns.js +103 -0
  164. package/src/lib/resources/config/language.json +6 -0
  165. package/src/lib/resources/personas/PERSONA_INDEX.md +57 -0
  166. package/src/lib/resources/personas/analyst.json +44 -0
  167. package/src/lib/resources/personas/architect/best_practices.md +136 -0
  168. package/src/lib/resources/personas/architect/knowledge_base.md +377 -0
  169. package/src/lib/resources/personas/architect.json +44 -0
  170. package/src/lib/resources/personas/backend/best_practices.md +766 -0
  171. package/src/lib/resources/personas/backend/knowledge_base.md +1070 -0
  172. package/src/lib/resources/personas/data_analyst/best_practices.md +594 -0
  173. package/src/lib/resources/personas/data_analyst/knowledge_base.md +1057 -0
  174. package/src/lib/resources/personas/developer.json +44 -0
  175. package/src/lib/resources/personas/developer.md +37 -0
  176. package/src/lib/resources/personas/evaluation_criteria.json +137 -0
  177. package/src/lib/resources/personas/frontend/best_practices.md +445 -0
  178. package/src/lib/resources/personas/frontend/knowledge_base.md +729 -0
  179. package/src/lib/resources/personas/persona-index.json +31 -0
  180. package/src/lib/resources/personas/reviewer.json +44 -0
  181. package/src/lib/resources/personas/security/best_practices.md +307 -0
  182. package/src/lib/resources/personas/security/knowledge_base.md +498 -0
  183. package/src/lib/resources/personas/tester.json +44 -0
  184. package/src/lib/resources/templates/README.md +78 -0
  185. package/src/lib/resources/templates/api-server/config.json +64 -0
  186. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +56 -0
  187. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +42 -0
  188. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +40 -0
  189. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +111 -0
  190. package/src/lib/resources/templates/api-server/template/.env.example +29 -0
  191. package/src/lib/resources/templates/api-server/template/.eslintrc.json +23 -0
  192. package/src/lib/resources/templates/api-server/template/README.md +171 -0
  193. package/src/lib/resources/templates/api-server/template/jest.config.js +26 -0
  194. package/src/lib/resources/templates/api-server/template/nodemon.json +9 -0
  195. package/src/lib/resources/templates/api-server/template/package.json +61 -0
  196. package/src/lib/resources/templates/api-server/template/src/app.ts +60 -0
  197. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +38 -0
  198. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +68 -0
  199. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +128 -0
  200. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +28 -0
  201. package/src/lib/resources/templates/api-server/template/src/index.ts +28 -0
  202. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +50 -0
  203. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +41 -0
  204. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +42 -0
  205. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +14 -0
  206. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +20 -0
  207. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +42 -0
  208. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +13 -0
  209. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +67 -0
  210. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +37 -0
  211. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +40 -0
  212. package/src/lib/resources/templates/api-server/template/tsconfig.json +43 -0
  213. package/src/lib/resources/templates/npm-library/config.json +61 -0
  214. package/src/lib/resources/templates/npm-library/template/LICENSE +21 -0
  215. package/src/lib/resources/templates/npm-library/template/README.md +201 -0
  216. package/src/lib/resources/templates/npm-library/template/package.json +78 -0
  217. package/src/lib/resources/templates/web-app/config.json +53 -0
  218. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +54 -0
  219. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +37 -0
  220. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +40 -0
  221. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +110 -0
  222. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +20 -0
  223. package/src/lib/resources/templates/web-app/template/README.md +151 -0
  224. package/src/lib/resources/templates/web-app/template/index.html +14 -0
  225. package/src/lib/resources/templates/web-app/template/package.json +44 -0
  226. package/src/lib/resources/templates/web-app/template/postcss.config.js +6 -0
  227. package/src/lib/resources/templates/web-app/template/public/vite.svg +1 -0
  228. package/src/lib/resources/templates/web-app/template/src/App.tsx +21 -0
  229. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +57 -0
  230. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +107 -0
  231. package/src/lib/resources/templates/web-app/template/src/components/aiwf/FeatureLedger.tsx +93 -0
  232. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +71 -0
  233. package/src/lib/resources/templates/web-app/template/src/index.css +60 -0
  234. package/src/lib/resources/templates/web-app/template/src/main.tsx +10 -0
  235. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +60 -0
  236. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +89 -0
  237. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +25 -0
  238. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +126 -0
  239. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +9 -0
  240. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +1 -0
  241. package/src/lib/resources/templates/web-app/template/tailwind.config.js +30 -0
  242. package/src/lib/resources/templates/web-app/template/tsconfig.json +37 -0
  243. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +10 -0
  244. package/src/lib/resources/templates/web-app/template/vite.config.ts +23 -0
  245. package/src/lib/resources/utils/background-monitor.js +223 -0
  246. package/src/lib/resources/utils/compression-metrics.js +1031 -0
  247. package/src/lib/resources/utils/compression-strategies.js +557 -0
  248. package/src/lib/resources/utils/content-normalizer.js +455 -0
  249. package/src/lib/resources/utils/context-compressor.js +583 -0
  250. package/src/lib/resources/utils/context-rule-parser.js +180 -0
  251. package/src/lib/resources/utils/context-token-monitor.js +463 -0
  252. package/src/lib/resources/utils/context-update-manager.js +502 -0
  253. package/src/lib/resources/utils/feature-updater.js +271 -0
  254. package/src/lib/resources/utils/git-utils.js +220 -0
  255. package/src/lib/resources/utils/importance-classifier.js +645 -0
  256. package/src/lib/resources/utils/information-filter.js +710 -0
  257. package/src/lib/resources/utils/persona-aware-compressor.js +598 -0
  258. package/src/lib/resources/utils/prompt-injector.js +242 -0
  259. package/src/lib/resources/utils/simplified-evaluator.js +119 -0
  260. package/src/lib/resources/utils/text-summarizer.js +492 -0
  261. package/src/lib/resources/utils/token-counter.js +177 -0
  262. package/src/lib/resources/utils/token-monitor.js +484 -0
  263. package/src/lib/resources/utils/token-reporter.js +453 -0
  264. package/src/lib/resources/utils/token-storage.js +433 -0
  265. package/src/lib/resources/utils/token-tracker.js +283 -0
  266. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_switch_language.md +0 -160
  267. /package/claude-code/aiwf/ko/.claude/commands/aiwf/{aiwf_update_docs_kr.md → aiwf_update_docs.md} +0 -0
@@ -0,0 +1,1031 @@
1
+ import { TokenCounter } from './token-counter.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * 압축률 측정 및 성능 분석 도구
7
+ */
8
+ export class CompressionMetrics {
9
+ constructor() {
10
+ this.tokenCounter = new TokenCounter();
11
+ this.measurements = new Map();
12
+ this.benchmarks = new Map();
13
+ this.initializeMetrics();
14
+ }
15
+
16
+ /**
17
+ * 메트릭 시스템을 초기화합니다
18
+ */
19
+ initializeMetrics() {
20
+ // 메트릭 타입 정의
21
+ this.metricTypes = {
22
+ compression: {
23
+ name: 'Compression Metrics',
24
+ description: '압축률 관련 메트릭',
25
+ measurements: [
26
+ 'compression_ratio',
27
+ 'token_reduction',
28
+ 'size_reduction',
29
+ 'information_preservation'
30
+ ]
31
+ },
32
+ performance: {
33
+ name: 'Performance Metrics',
34
+ description: '성능 관련 메트릭',
35
+ measurements: [
36
+ 'compression_time',
37
+ 'decompression_time',
38
+ 'memory_usage',
39
+ 'throughput'
40
+ ]
41
+ },
42
+ quality: {
43
+ name: 'Quality Metrics',
44
+ description: '품질 관련 메트릭',
45
+ measurements: [
46
+ 'readability_score',
47
+ 'structure_preservation',
48
+ 'semantic_similarity',
49
+ 'information_loss'
50
+ ]
51
+ },
52
+ efficiency: {
53
+ name: 'Efficiency Metrics',
54
+ description: '효율성 관련 메트릭',
55
+ measurements: [
56
+ 'tokens_per_second',
57
+ 'compression_efficiency',
58
+ 'resource_utilization',
59
+ 'cost_effectiveness'
60
+ ]
61
+ }
62
+ };
63
+
64
+ // 벤치마크 기준점
65
+ this.benchmarkThresholds = {
66
+ compression_ratio: {
67
+ excellent: 70,
68
+ good: 50,
69
+ acceptable: 30,
70
+ poor: 10
71
+ },
72
+ compression_time: {
73
+ excellent: 1000, // 1초
74
+ good: 3000, // 3초
75
+ acceptable: 5000, // 5초
76
+ poor: 10000 // 10초
77
+ },
78
+ information_preservation: {
79
+ excellent: 90,
80
+ good: 80,
81
+ acceptable: 70,
82
+ poor: 50
83
+ },
84
+ readability_score: {
85
+ excellent: 90,
86
+ good: 80,
87
+ acceptable: 70,
88
+ poor: 50
89
+ }
90
+ };
91
+
92
+ // 측정 히스토리
93
+ this.measurementHistory = [];
94
+ }
95
+
96
+ /**
97
+ * 압축 결과를 측정합니다
98
+ * @param {string} original - 원본 콘텐츠
99
+ * @param {string} compressed - 압축된 콘텐츠
100
+ * @param {Object} metadata - 압축 메타데이터
101
+ * @returns {Object} 측정 결과
102
+ */
103
+ measureCompression(original, compressed, metadata = {}) {
104
+ const measurementId = this.generateMeasurementId();
105
+ const timestamp = Date.now();
106
+
107
+ const measurement = {
108
+ id: measurementId,
109
+ timestamp,
110
+ originalContent: original,
111
+ compressedContent: compressed,
112
+ metadata,
113
+ metrics: {},
114
+ analysis: {},
115
+ benchmark: {}
116
+ };
117
+
118
+ // 1. 압축 메트릭 계산
119
+ measurement.metrics.compression = this.calculateCompressionMetrics(original, compressed);
120
+
121
+ // 2. 성능 메트릭 계산
122
+ measurement.metrics.performance = this.calculatePerformanceMetrics(metadata);
123
+
124
+ // 3. 품질 메트릭 계산
125
+ measurement.metrics.quality = this.calculateQualityMetrics(original, compressed, metadata);
126
+
127
+ // 4. 효율성 메트릭 계산
128
+ measurement.metrics.efficiency = this.calculateEfficiencyMetrics(original, compressed, metadata);
129
+
130
+ // 5. 벤치마크 분석
131
+ measurement.benchmark = this.analyzeBenchmark(measurement.metrics);
132
+
133
+ // 6. 종합 분석
134
+ measurement.analysis = this.generateAnalysis(measurement.metrics, measurement.benchmark);
135
+
136
+ // 측정 결과 저장
137
+ this.measurements.set(measurementId, measurement);
138
+ this.measurementHistory.push(measurement);
139
+
140
+ return measurement;
141
+ }
142
+
143
+ /**
144
+ * 압축 메트릭을 계산합니다
145
+ * @param {string} original - 원본 콘텐츠
146
+ * @param {string} compressed - 압축된 콘텐츠
147
+ * @returns {Object} 압축 메트릭
148
+ */
149
+ calculateCompressionMetrics(original, compressed) {
150
+ const originalTokens = this.tokenCounter.countTokens(original);
151
+ const compressedTokens = this.tokenCounter.countTokens(compressed);
152
+ const originalSize = Buffer.byteLength(original, 'utf8');
153
+ const compressedSize = Buffer.byteLength(compressed, 'utf8');
154
+
155
+ const tokenReduction = originalTokens - compressedTokens;
156
+ const sizeReduction = originalSize - compressedSize;
157
+ const compressionRatio = originalTokens > 0 ? (tokenReduction / originalTokens) * 100 : 0;
158
+ const sizeCompressionRatio = originalSize > 0 ? (sizeReduction / originalSize) * 100 : 0;
159
+
160
+ // 정보 보존률 계산
161
+ const informationPreservation = this.calculateInformationPreservation(original, compressed);
162
+
163
+ return {
164
+ original_tokens: originalTokens,
165
+ compressed_tokens: compressedTokens,
166
+ token_reduction: tokenReduction,
167
+ token_reduction_percentage: compressionRatio,
168
+ original_size: originalSize,
169
+ compressed_size: compressedSize,
170
+ size_reduction: sizeReduction,
171
+ size_reduction_percentage: sizeCompressionRatio,
172
+ information_preservation: informationPreservation,
173
+ compression_efficiency: this.calculateCompressionEfficiency(compressionRatio, informationPreservation)
174
+ };
175
+ }
176
+
177
+ /**
178
+ * 성능 메트릭을 계산합니다
179
+ * @param {Object} metadata - 메타데이터
180
+ * @returns {Object} 성능 메트릭
181
+ */
182
+ calculatePerformanceMetrics(metadata) {
183
+ const performance = {
184
+ compression_time: metadata.processingTime || 0,
185
+ decompression_time: metadata.decompressionTime || 0,
186
+ memory_usage: metadata.memoryUsage || 0,
187
+ throughput: 0,
188
+ tokens_per_second: 0
189
+ };
190
+
191
+ // 처리량 계산
192
+ if (performance.compression_time > 0 && metadata.originalTokens) {
193
+ performance.tokens_per_second = (metadata.originalTokens / performance.compression_time) * 1000;
194
+ performance.throughput = (metadata.originalTokens / performance.compression_time) * 1000;
195
+ }
196
+
197
+ // 메모리 효율성 계산
198
+ if (performance.memory_usage > 0 && metadata.originalTokens) {
199
+ performance.memory_efficiency = metadata.originalTokens / performance.memory_usage;
200
+ }
201
+
202
+ // 파이프라인 성능 분석
203
+ if (metadata.pipelineStages) {
204
+ performance.pipeline_analysis = this.analyzePipelinePerformance(metadata.pipelineStages);
205
+ }
206
+
207
+ return performance;
208
+ }
209
+
210
+ /**
211
+ * 품질 메트릭을 계산합니다
212
+ * @param {string} original - 원본 콘텐츠
213
+ * @param {string} compressed - 압축된 콘텐츠
214
+ * @param {Object} metadata - 메타데이터
215
+ * @returns {Object} 품질 메트릭
216
+ */
217
+ calculateQualityMetrics(original, compressed, metadata) {
218
+ const quality = {
219
+ readability_score: this.calculateReadabilityScore(compressed),
220
+ structure_preservation: this.calculateStructurePreservation(original, compressed),
221
+ semantic_similarity: this.calculateSemanticSimilarity(original, compressed),
222
+ information_loss: this.calculateInformationLoss(original, compressed),
223
+ coherence_score: this.calculateCoherenceScore(compressed),
224
+ completeness_score: this.calculateCompletenessScore(original, compressed)
225
+ };
226
+
227
+ // 전체 품질 점수 계산
228
+ quality.overall_quality = this.calculateOverallQuality(quality);
229
+
230
+ return quality;
231
+ }
232
+
233
+ /**
234
+ * 효율성 메트릭을 계산합니다
235
+ * @param {string} original - 원본 콘텐츠
236
+ * @param {string} compressed - 압축된 콘텐츠
237
+ * @param {Object} metadata - 메타데이터
238
+ * @returns {Object} 효율성 메트릭
239
+ */
240
+ calculateEfficiencyMetrics(original, compressed, metadata) {
241
+ const originalTokens = this.tokenCounter.countTokens(original);
242
+ const compressedTokens = this.tokenCounter.countTokens(compressed);
243
+ const processingTime = metadata.processingTime || 1;
244
+
245
+ const efficiency = {
246
+ compression_efficiency: this.calculateCompressionEfficiency(
247
+ ((originalTokens - compressedTokens) / originalTokens) * 100,
248
+ this.calculateInformationPreservation(original, compressed)
249
+ ),
250
+ resource_utilization: this.calculateResourceUtilization(metadata),
251
+ cost_effectiveness: this.calculateCostEffectiveness(originalTokens, compressedTokens, processingTime),
252
+ energy_efficiency: this.calculateEnergyEfficiency(originalTokens, processingTime)
253
+ };
254
+
255
+ return efficiency;
256
+ }
257
+
258
+ /**
259
+ * 정보 보존률을 계산합니다
260
+ * @param {string} original - 원본 콘텐츠
261
+ * @param {string} compressed - 압축된 콘텐츠
262
+ * @returns {number} 정보 보존률 (0-100)
263
+ */
264
+ calculateInformationPreservation(original, compressed) {
265
+ // 키워드 보존률 계산
266
+ const originalKeywords = this.extractKeywords(original);
267
+ const compressedKeywords = this.extractKeywords(compressed);
268
+
269
+ const preservedKeywords = originalKeywords.filter(keyword =>
270
+ compressedKeywords.includes(keyword)
271
+ );
272
+
273
+ const keywordPreservation = originalKeywords.length > 0
274
+ ? (preservedKeywords.length / originalKeywords.length) * 100
275
+ : 100;
276
+
277
+ // 구조 보존률 계산
278
+ const structurePreservation = this.calculateStructurePreservation(original, compressed);
279
+
280
+ // 의미 보존률 계산
281
+ const semanticPreservation = this.calculateSemanticSimilarity(original, compressed);
282
+
283
+ // 가중 평균 계산
284
+ return (keywordPreservation * 0.4 + structurePreservation * 0.3 + semanticPreservation * 0.3);
285
+ }
286
+
287
+ /**
288
+ * 키워드를 추출합니다
289
+ * @param {string} text - 텍스트
290
+ * @returns {Array<string>} 키워드 배열
291
+ */
292
+ extractKeywords(text) {
293
+ const keywords = [];
294
+ const words = text.toLowerCase().split(/\s+/);
295
+
296
+ // 중요한 키워드 패턴
297
+ const keywordPatterns = [
298
+ /\b(goal|objective|task|requirement|critical|important|key|main|primary|essential)\b/g,
299
+ /\b(목표|목적|태스크|요구사항|중요|핵심|주요|필수|기본|우선)\b/g
300
+ ];
301
+
302
+ for (const pattern of keywordPatterns) {
303
+ const matches = text.match(pattern);
304
+ if (matches) {
305
+ keywords.push(...matches);
306
+ }
307
+ }
308
+
309
+ // 고유 키워드만 반환
310
+ return [...new Set(keywords)];
311
+ }
312
+
313
+ /**
314
+ * 구조 보존률을 계산합니다
315
+ * @param {string} original - 원본 콘텐츠
316
+ * @param {string} compressed - 압축된 콘텐츠
317
+ * @returns {number} 구조 보존률 (0-100)
318
+ */
319
+ calculateStructurePreservation(original, compressed) {
320
+ const originalHeaders = (original.match(/^#{1,6}\s+.+$/gm) || []).length;
321
+ const compressedHeaders = (compressed.match(/^#{1,6}\s+.+$/gm) || []).length;
322
+
323
+ const originalLists = (original.match(/^\s*[-*+]\s+/gm) || []).length;
324
+ const compressedLists = (compressed.match(/^\s*[-*+]\s+/gm) || []).length;
325
+
326
+ const originalCodeBlocks = (original.match(/```[\s\S]*?```/g) || []).length;
327
+ const compressedCodeBlocks = (compressed.match(/```[\s\S]*?```/g) || []).length;
328
+
329
+ const headerPreservation = originalHeaders > 0 ? (compressedHeaders / originalHeaders) * 100 : 100;
330
+ const listPreservation = originalLists > 0 ? (compressedLists / originalLists) * 100 : 100;
331
+ const codePreservation = originalCodeBlocks > 0 ? (compressedCodeBlocks / originalCodeBlocks) * 100 : 100;
332
+
333
+ return (headerPreservation + listPreservation + codePreservation) / 3;
334
+ }
335
+
336
+ /**
337
+ * 의미 유사도를 계산합니다
338
+ * @param {string} original - 원본 콘텐츠
339
+ * @param {string} compressed - 압축된 콘텐츠
340
+ * @returns {number} 의미 유사도 (0-100)
341
+ */
342
+ calculateSemanticSimilarity(original, compressed) {
343
+ // 단순한 어휘 기반 유사도 계산
344
+ const originalWords = new Set(original.toLowerCase().split(/\s+/));
345
+ const compressedWords = new Set(compressed.toLowerCase().split(/\s+/));
346
+
347
+ const intersection = new Set([...originalWords].filter(word => compressedWords.has(word)));
348
+ const union = new Set([...originalWords, ...compressedWords]);
349
+
350
+ return union.size > 0 ? (intersection.size / union.size) * 100 : 0;
351
+ }
352
+
353
+ /**
354
+ * 정보 손실률을 계산합니다
355
+ * @param {string} original - 원본 콘텐츠
356
+ * @param {string} compressed - 압축된 콘텐츠
357
+ * @returns {number} 정보 손실률 (0-100)
358
+ */
359
+ calculateInformationLoss(original, compressed) {
360
+ const preservation = this.calculateInformationPreservation(original, compressed);
361
+ return 100 - preservation;
362
+ }
363
+
364
+ /**
365
+ * 가독성 점수를 계산합니다
366
+ * @param {string} text - 텍스트
367
+ * @returns {number} 가독성 점수 (0-100)
368
+ */
369
+ calculateReadabilityScore(text) {
370
+ const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
371
+ const words = text.split(/\s+/).filter(w => w.trim().length > 0);
372
+
373
+ if (sentences.length === 0 || words.length === 0) return 0;
374
+
375
+ const avgWordsPerSentence = words.length / sentences.length;
376
+ const avgCharsPerWord = words.reduce((sum, word) => sum + word.length, 0) / words.length;
377
+
378
+ // 단순한 가독성 점수 계산
379
+ let score = 100;
380
+
381
+ // 문장 길이 페널티
382
+ if (avgWordsPerSentence > 20) score -= 20;
383
+ else if (avgWordsPerSentence > 15) score -= 10;
384
+
385
+ // 단어 길이 페널티
386
+ if (avgCharsPerWord > 7) score -= 20;
387
+ else if (avgCharsPerWord > 5) score -= 10;
388
+
389
+ return Math.max(0, Math.min(100, score));
390
+ }
391
+
392
+ /**
393
+ * 일관성 점수를 계산합니다
394
+ * @param {string} text - 텍스트
395
+ * @returns {number} 일관성 점수 (0-100)
396
+ */
397
+ calculateCoherenceScore(text) {
398
+ // 단순한 일관성 점수 계산
399
+ const lines = text.split('\n');
400
+ let coherenceScore = 100;
401
+
402
+ // 빈 줄 패턴 확인
403
+ const emptyLinePattern = /^\s*$/;
404
+ const consecutiveEmptyLines = lines.reduce((count, line, index) => {
405
+ if (emptyLinePattern.test(line) && index > 0 && emptyLinePattern.test(lines[index - 1])) {
406
+ return count + 1;
407
+ }
408
+ return count;
409
+ }, 0);
410
+
411
+ coherenceScore -= consecutiveEmptyLines * 2;
412
+
413
+ // 헤더 계층 구조 확인
414
+ const headers = lines.filter(line => line.match(/^#{1,6}\s+/));
415
+ const headerLevels = headers.map(header => header.match(/^(#{1,6})/)[1].length);
416
+
417
+ let previousLevel = 0;
418
+ for (const level of headerLevels) {
419
+ if (level - previousLevel > 1) {
420
+ coherenceScore -= 5; // 헤더 레벨 건너뛰기 페널티
421
+ }
422
+ previousLevel = level;
423
+ }
424
+
425
+ return Math.max(0, Math.min(100, coherenceScore));
426
+ }
427
+
428
+ /**
429
+ * 완성도 점수를 계산합니다
430
+ * @param {string} original - 원본 콘텐츠
431
+ * @param {string} compressed - 압축된 콘텐츠
432
+ * @returns {number} 완성도 점수 (0-100)
433
+ */
434
+ calculateCompletenessScore(original, compressed) {
435
+ // 중요한 섹션이 유지되었는지 확인
436
+ const importantSections = [
437
+ 'goal', 'objective', 'requirement', 'task', 'subtask', 'acceptance criteria',
438
+ '목표', '목적', '요구사항', '태스크', '하위', '승인 기준'
439
+ ];
440
+
441
+ let foundSections = 0;
442
+ const lowerCompressed = compressed.toLowerCase();
443
+
444
+ for (const section of importantSections) {
445
+ if (lowerCompressed.includes(section.toLowerCase())) {
446
+ foundSections++;
447
+ }
448
+ }
449
+
450
+ const completenessRatio = foundSections / importantSections.length;
451
+ return completenessRatio * 100;
452
+ }
453
+
454
+ /**
455
+ * 압축 효율성을 계산합니다
456
+ * @param {number} compressionRatio - 압축률
457
+ * @param {number} informationPreservation - 정보 보존률
458
+ * @returns {number} 압축 효율성 (0-100)
459
+ */
460
+ calculateCompressionEfficiency(compressionRatio, informationPreservation) {
461
+ // 압축률과 정보 보존률의 균형을 고려한 효율성 계산
462
+ const alpha = 0.6; // 압축률 가중치
463
+ const beta = 0.4; // 정보 보존률 가중치
464
+
465
+ return (alpha * compressionRatio) + (beta * informationPreservation);
466
+ }
467
+
468
+ /**
469
+ * 자원 사용률을 계산합니다
470
+ * @param {Object} metadata - 메타데이터
471
+ * @returns {number} 자원 사용률 (0-100)
472
+ */
473
+ calculateResourceUtilization(metadata) {
474
+ // 메모리 사용량, 처리 시간 등을 고려한 자원 사용률
475
+ const memoryScore = metadata.memoryUsage ? Math.min(100, 100 - (metadata.memoryUsage / 1024 / 1024)) : 100;
476
+ const timeScore = metadata.processingTime ? Math.min(100, 100 - (metadata.processingTime / 1000)) : 100;
477
+
478
+ return (memoryScore + timeScore) / 2;
479
+ }
480
+
481
+ /**
482
+ * 비용 효율성을 계산합니다
483
+ * @param {number} originalTokens - 원본 토큰 수
484
+ * @param {number} compressedTokens - 압축된 토큰 수
485
+ * @param {number} processingTime - 처리 시간
486
+ * @returns {number} 비용 효율성 (0-100)
487
+ */
488
+ calculateCostEffectiveness(originalTokens, compressedTokens, processingTime) {
489
+ const tokenSaved = originalTokens - compressedTokens;
490
+ const processingTimeInSeconds = processingTime / 1000;
491
+
492
+ if (processingTimeInSeconds === 0) return 0;
493
+
494
+ // 초당 절약된 토큰 수
495
+ const tokensPerSecond = tokenSaved / processingTimeInSeconds;
496
+
497
+ // 정규화된 비용 효율성 (최대 100)
498
+ return Math.min(100, tokensPerSecond);
499
+ }
500
+
501
+ /**
502
+ * 에너지 효율성을 계산합니다
503
+ * @param {number} originalTokens - 원본 토큰 수
504
+ * @param {number} processingTime - 처리 시간
505
+ * @returns {number} 에너지 효율성 (0-100)
506
+ */
507
+ calculateEnergyEfficiency(originalTokens, processingTime) {
508
+ const processingTimeInSeconds = processingTime / 1000;
509
+
510
+ if (processingTimeInSeconds === 0) return 0;
511
+
512
+ // 초당 처리된 토큰 수
513
+ const tokensPerSecond = originalTokens / processingTimeInSeconds;
514
+
515
+ // 정규화된 에너지 효율성 (최대 100)
516
+ return Math.min(100, tokensPerSecond / 10);
517
+ }
518
+
519
+ /**
520
+ * 전체 품질 점수를 계산합니다
521
+ * @param {Object} quality - 품질 메트릭
522
+ * @returns {number} 전체 품질 점수 (0-100)
523
+ */
524
+ calculateOverallQuality(quality) {
525
+ const weights = {
526
+ readability_score: 0.2,
527
+ structure_preservation: 0.25,
528
+ semantic_similarity: 0.25,
529
+ coherence_score: 0.15,
530
+ completeness_score: 0.15
531
+ };
532
+
533
+ let totalScore = 0;
534
+ let totalWeight = 0;
535
+
536
+ for (const [metric, weight] of Object.entries(weights)) {
537
+ if (quality[metric] !== undefined) {
538
+ totalScore += quality[metric] * weight;
539
+ totalWeight += weight;
540
+ }
541
+ }
542
+
543
+ return totalWeight > 0 ? totalScore / totalWeight : 0;
544
+ }
545
+
546
+ /**
547
+ * 파이프라인 성능을 분석합니다
548
+ * @param {Array<Object>} stages - 파이프라인 스테이지
549
+ * @returns {Object} 파이프라인 성능 분석
550
+ */
551
+ analyzePipelinePerformance(stages) {
552
+ const analysis = {
553
+ total_stages: stages.length,
554
+ total_duration: stages.reduce((sum, stage) => sum + stage.duration, 0),
555
+ average_duration: 0,
556
+ slowest_stage: null,
557
+ fastest_stage: null,
558
+ stage_breakdown: []
559
+ };
560
+
561
+ if (stages.length > 0) {
562
+ analysis.average_duration = analysis.total_duration / stages.length;
563
+
564
+ // 가장 느린/빠른 스테이지 찾기
565
+ let slowest = stages[0];
566
+ let fastest = stages[0];
567
+
568
+ for (const stage of stages) {
569
+ if (stage.duration > slowest.duration) slowest = stage;
570
+ if (stage.duration < fastest.duration) fastest = stage;
571
+ }
572
+
573
+ analysis.slowest_stage = { name: slowest.name, duration: slowest.duration };
574
+ analysis.fastest_stage = { name: fastest.name, duration: fastest.duration };
575
+
576
+ // 스테이지별 세부 분석
577
+ analysis.stage_breakdown = stages.map(stage => ({
578
+ name: stage.name,
579
+ duration: stage.duration,
580
+ percentage: (stage.duration / analysis.total_duration) * 100,
581
+ success: stage.success
582
+ }));
583
+ }
584
+
585
+ return analysis;
586
+ }
587
+
588
+ /**
589
+ * 벤치마크 분석을 수행합니다
590
+ * @param {Object} metrics - 메트릭 데이터
591
+ * @returns {Object} 벤치마크 분석
592
+ */
593
+ analyzeBenchmark(metrics) {
594
+ const benchmark = {
595
+ overall_grade: 'C',
596
+ overall_score: 0,
597
+ category_grades: {},
598
+ recommendations: []
599
+ };
600
+
601
+ let totalScore = 0;
602
+ let categoryCount = 0;
603
+
604
+ // 각 카테고리별 벤치마크 분석
605
+ for (const [category, categoryMetrics] of Object.entries(metrics)) {
606
+ const categoryGrade = this.gradeCategoryMetrics(categoryMetrics);
607
+ benchmark.category_grades[category] = categoryGrade;
608
+ totalScore += categoryGrade.score;
609
+ categoryCount++;
610
+ }
611
+
612
+ // 전체 점수 계산
613
+ benchmark.overall_score = categoryCount > 0 ? totalScore / categoryCount : 0;
614
+ benchmark.overall_grade = this.scoreToGrade(benchmark.overall_score);
615
+
616
+ // 권장사항 생성
617
+ benchmark.recommendations = this.generateRecommendations(metrics, benchmark);
618
+
619
+ return benchmark;
620
+ }
621
+
622
+ /**
623
+ * 카테고리 메트릭을 평가합니다
624
+ * @param {Object} categoryMetrics - 카테고리 메트릭
625
+ * @returns {Object} 카테고리 평가
626
+ */
627
+ gradeCategoryMetrics(categoryMetrics) {
628
+ const grade = {
629
+ score: 0,
630
+ grade: 'F',
631
+ details: {}
632
+ };
633
+
634
+ let totalScore = 0;
635
+ let metricCount = 0;
636
+
637
+ for (const [metric, value] of Object.entries(categoryMetrics)) {
638
+ if (typeof value === 'number' && this.benchmarkThresholds[metric]) {
639
+ const metricGrade = this.gradeMetric(metric, value);
640
+ grade.details[metric] = metricGrade;
641
+ totalScore += metricGrade.score;
642
+ metricCount++;
643
+ }
644
+ }
645
+
646
+ if (metricCount > 0) {
647
+ grade.score = totalScore / metricCount;
648
+ grade.grade = this.scoreToGrade(grade.score);
649
+ }
650
+
651
+ return grade;
652
+ }
653
+
654
+ /**
655
+ * 개별 메트릭을 평가합니다
656
+ * @param {string} metric - 메트릭 이름
657
+ * @param {number} value - 메트릭 값
658
+ * @returns {Object} 메트릭 평가
659
+ */
660
+ gradeMetric(metric, value) {
661
+ const thresholds = this.benchmarkThresholds[metric];
662
+ if (!thresholds) return { score: 50, grade: 'C' };
663
+
664
+ let score = 0;
665
+
666
+ if (value >= thresholds.excellent) {
667
+ score = 90;
668
+ } else if (value >= thresholds.good) {
669
+ score = 80;
670
+ } else if (value >= thresholds.acceptable) {
671
+ score = 70;
672
+ } else if (value >= thresholds.poor) {
673
+ score = 60;
674
+ } else {
675
+ score = 40;
676
+ }
677
+
678
+ return {
679
+ score,
680
+ grade: this.scoreToGrade(score),
681
+ value,
682
+ threshold: thresholds
683
+ };
684
+ }
685
+
686
+ /**
687
+ * 점수를 등급으로 변환합니다
688
+ * @param {number} score - 점수 (0-100)
689
+ * @returns {string} 등급 (A-F)
690
+ */
691
+ scoreToGrade(score) {
692
+ if (score >= 90) return 'A';
693
+ if (score >= 80) return 'B';
694
+ if (score >= 70) return 'C';
695
+ if (score >= 60) return 'D';
696
+ return 'F';
697
+ }
698
+
699
+ /**
700
+ * 분석 결과를 생성합니다
701
+ * @param {Object} metrics - 메트릭 데이터
702
+ * @param {Object} benchmark - 벤치마크 분석
703
+ * @returns {Object} 분석 결과
704
+ */
705
+ generateAnalysis(metrics, benchmark) {
706
+ return {
707
+ summary: this.generateSummary(metrics),
708
+ strengths: this.identifyStrengths(metrics, benchmark),
709
+ weaknesses: this.identifyWeaknesses(metrics, benchmark),
710
+ recommendations: this.generateRecommendations(metrics, benchmark),
711
+ trend_analysis: this.analyzeTrends(),
712
+ comparative_analysis: this.generateComparativeAnalysis(metrics)
713
+ };
714
+ }
715
+
716
+ /**
717
+ * 요약을 생성합니다
718
+ * @param {Object} metrics - 메트릭 데이터
719
+ * @returns {string} 요약
720
+ */
721
+ generateSummary(metrics) {
722
+ const compressionRatio = metrics.compression?.token_reduction_percentage || 0;
723
+ const informationPreservation = metrics.compression?.information_preservation || 0;
724
+ const processingTime = metrics.performance?.compression_time || 0;
725
+
726
+ return `압축률 ${compressionRatio.toFixed(1)}%로 ${informationPreservation.toFixed(1)}%의 정보를 보존하며 ${processingTime}ms 내에 처리되었습니다.`;
727
+ }
728
+
729
+ /**
730
+ * 강점을 식별합니다
731
+ * @param {Object} metrics - 메트릭 데이터
732
+ * @param {Object} benchmark - 벤치마크 분석
733
+ * @returns {Array<string>} 강점 목록
734
+ */
735
+ identifyStrengths(metrics, benchmark) {
736
+ const strengths = [];
737
+
738
+ for (const [category, grade] of Object.entries(benchmark.category_grades)) {
739
+ if (grade.score >= 80) {
740
+ strengths.push(`${category} 카테고리에서 우수한 성능을 보였습니다 (${grade.grade}등급)`);
741
+ }
742
+ }
743
+
744
+ return strengths;
745
+ }
746
+
747
+ /**
748
+ * 약점을 식별합니다
749
+ * @param {Object} metrics - 메트릭 데이터
750
+ * @param {Object} benchmark - 벤치마크 분석
751
+ * @returns {Array<string>} 약점 목록
752
+ */
753
+ identifyWeaknesses(metrics, benchmark) {
754
+ const weaknesses = [];
755
+
756
+ for (const [category, grade] of Object.entries(benchmark.category_grades)) {
757
+ if (grade.score < 60) {
758
+ weaknesses.push(`${category} 카테고리에서 개선이 필요합니다 (${grade.grade}등급)`);
759
+ }
760
+ }
761
+
762
+ return weaknesses;
763
+ }
764
+
765
+ /**
766
+ * 권장사항을 생성합니다
767
+ * @param {Object} metrics - 메트릭 데이터
768
+ * @param {Object} benchmark - 벤치마크 분석
769
+ * @returns {Array<string>} 권장사항 목록
770
+ */
771
+ generateRecommendations(metrics, benchmark) {
772
+ const recommendations = [];
773
+
774
+ // 압축률 기반 권장사항
775
+ const compressionRatio = metrics.compression?.token_reduction_percentage || 0;
776
+ if (compressionRatio < 30) {
777
+ recommendations.push('압축률을 높이기 위해 더 적극적인 압축 전략을 고려하세요.');
778
+ } else if (compressionRatio > 80) {
779
+ recommendations.push('압축률이 너무 높습니다. 정보 손실을 방지하기 위해 덜 적극적인 압축을 고려하세요.');
780
+ }
781
+
782
+ // 성능 기반 권장사항
783
+ const processingTime = metrics.performance?.compression_time || 0;
784
+ if (processingTime > 5000) {
785
+ recommendations.push('처리 시간이 오래 걸립니다. 성능 최적화를 고려하세요.');
786
+ }
787
+
788
+ // 품질 기반 권장사항
789
+ const informationPreservation = metrics.compression?.information_preservation || 0;
790
+ if (informationPreservation < 70) {
791
+ recommendations.push('정보 보존률이 낮습니다. 중요한 정보를 더 잘 보존하는 방법을 고려하세요.');
792
+ }
793
+
794
+ return recommendations;
795
+ }
796
+
797
+ /**
798
+ * 트렌드를 분석합니다
799
+ * @returns {Object} 트렌드 분석
800
+ */
801
+ analyzeTrends() {
802
+ if (this.measurementHistory.length < 2) {
803
+ return { message: '트렌드 분석을 위한 충분한 데이터가 없습니다.' };
804
+ }
805
+
806
+ const recent = this.measurementHistory.slice(-5);
807
+ const trends = {};
808
+
809
+ // 압축률 트렌드
810
+ const compressionRatios = recent.map(m => m.metrics.compression?.token_reduction_percentage || 0);
811
+ trends.compression_ratio = this.calculateTrend(compressionRatios);
812
+
813
+ // 처리 시간 트렌드
814
+ const processingTimes = recent.map(m => m.metrics.performance?.compression_time || 0);
815
+ trends.processing_time = this.calculateTrend(processingTimes);
816
+
817
+ // 품질 트렌드
818
+ const qualityScores = recent.map(m => m.metrics.quality?.overall_quality || 0);
819
+ trends.quality = this.calculateTrend(qualityScores);
820
+
821
+ return trends;
822
+ }
823
+
824
+ /**
825
+ * 트렌드를 계산합니다
826
+ * @param {Array<number>} values - 값 배열
827
+ * @returns {Object} 트렌드 정보
828
+ */
829
+ calculateTrend(values) {
830
+ if (values.length < 2) return { trend: 'stable', change: 0 };
831
+
832
+ const first = values[0];
833
+ const last = values[values.length - 1];
834
+ const change = ((last - first) / first) * 100;
835
+
836
+ let trend = 'stable';
837
+ if (change > 5) trend = 'increasing';
838
+ else if (change < -5) trend = 'decreasing';
839
+
840
+ return { trend, change: change.toFixed(1) };
841
+ }
842
+
843
+ /**
844
+ * 비교 분석을 생성합니다
845
+ * @param {Object} metrics - 메트릭 데이터
846
+ * @returns {Object} 비교 분석
847
+ */
848
+ generateComparativeAnalysis(metrics) {
849
+ const analysis = {
850
+ vs_previous: null,
851
+ vs_average: null,
852
+ vs_best: null
853
+ };
854
+
855
+ if (this.measurementHistory.length > 1) {
856
+ const current = metrics;
857
+ const previous = this.measurementHistory[this.measurementHistory.length - 2].metrics;
858
+
859
+ analysis.vs_previous = {
860
+ compression_ratio_change: this.calculateChange(
861
+ previous.compression?.token_reduction_percentage || 0,
862
+ current.compression?.token_reduction_percentage || 0
863
+ ),
864
+ quality_change: this.calculateChange(
865
+ previous.quality?.overall_quality || 0,
866
+ current.quality?.overall_quality || 0
867
+ ),
868
+ performance_change: this.calculateChange(
869
+ previous.performance?.compression_time || 0,
870
+ current.performance?.compression_time || 0
871
+ )
872
+ };
873
+ }
874
+
875
+ return analysis;
876
+ }
877
+
878
+ /**
879
+ * 변화량을 계산합니다
880
+ * @param {number} previous - 이전 값
881
+ * @param {number} current - 현재 값
882
+ * @returns {Object} 변화량 정보
883
+ */
884
+ calculateChange(previous, current) {
885
+ const change = current - previous;
886
+ const changePercentage = previous > 0 ? (change / previous) * 100 : 0;
887
+
888
+ return {
889
+ absolute: change,
890
+ percentage: changePercentage.toFixed(1),
891
+ direction: change > 0 ? 'increase' : change < 0 ? 'decrease' : 'stable'
892
+ };
893
+ }
894
+
895
+ /**
896
+ * 측정 ID를 생성합니다
897
+ * @returns {string} 측정 ID
898
+ */
899
+ generateMeasurementId() {
900
+ const timestamp = Date.now();
901
+ const random = Math.random().toString(36).substr(2, 9);
902
+ return `metric_${timestamp}_${random}`;
903
+ }
904
+
905
+ /**
906
+ * 측정 결과를 내보냅니다
907
+ * @param {string} measurementId - 측정 ID
908
+ * @param {string} format - 내보내기 형식 ('json', 'csv', 'report')
909
+ * @returns {string} 내보내기 결과
910
+ */
911
+ exportMeasurement(measurementId, format = 'json') {
912
+ const measurement = this.measurements.get(measurementId);
913
+ if (!measurement) {
914
+ throw new Error(`측정 결과를 찾을 수 없습니다: ${measurementId}`);
915
+ }
916
+
917
+ switch (format) {
918
+ case 'json':
919
+ return JSON.stringify(measurement, null, 2);
920
+ case 'csv':
921
+ return this.convertToCSV(measurement);
922
+ case 'report':
923
+ return this.generateReport(measurement);
924
+ default:
925
+ throw new Error(`지원되지 않는 형식: ${format}`);
926
+ }
927
+ }
928
+
929
+ /**
930
+ * CSV 형식으로 변환합니다
931
+ * @param {Object} measurement - 측정 결과
932
+ * @returns {string} CSV 문자열
933
+ */
934
+ convertToCSV(measurement) {
935
+ // 플랫한 구조로 변환
936
+ const flatData = this.flattenObject(measurement.metrics);
937
+
938
+ const headers = Object.keys(flatData);
939
+ const values = Object.values(flatData);
940
+
941
+ return [headers.join(','), values.join(',')].join('\n');
942
+ }
943
+
944
+ /**
945
+ * 객체를 평면화합니다
946
+ * @param {Object} obj - 객체
947
+ * @param {string} prefix - 접두사
948
+ * @returns {Object} 평면화된 객체
949
+ */
950
+ flattenObject(obj, prefix = '') {
951
+ const flattened = {};
952
+
953
+ for (const [key, value] of Object.entries(obj)) {
954
+ const newKey = prefix ? `${prefix}.${key}` : key;
955
+
956
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
957
+ Object.assign(flattened, this.flattenObject(value, newKey));
958
+ } else {
959
+ flattened[newKey] = value;
960
+ }
961
+ }
962
+
963
+ return flattened;
964
+ }
965
+
966
+ /**
967
+ * 보고서를 생성합니다
968
+ * @param {Object} measurement - 측정 결과
969
+ * @returns {string} 보고서
970
+ */
971
+ generateReport(measurement) {
972
+ const report = [];
973
+
974
+ report.push('# 압축 성능 보고서');
975
+ report.push(`측정 ID: ${measurement.id}`);
976
+ report.push(`측정 시간: ${new Date(measurement.timestamp).toLocaleString()}`);
977
+ report.push('');
978
+
979
+ // 요약
980
+ report.push('## 요약');
981
+ report.push(measurement.analysis.summary);
982
+ report.push('');
983
+
984
+ // 벤치마크 결과
985
+ report.push('## 벤치마크 결과');
986
+ report.push(`전체 등급: ${measurement.benchmark.overall_grade} (${measurement.benchmark.overall_score.toFixed(1)}점)`);
987
+ report.push('');
988
+
989
+ // 강점과 약점
990
+ if (measurement.analysis.strengths.length > 0) {
991
+ report.push('## 강점');
992
+ measurement.analysis.strengths.forEach(strength => {
993
+ report.push(`- ${strength}`);
994
+ });
995
+ report.push('');
996
+ }
997
+
998
+ if (measurement.analysis.weaknesses.length > 0) {
999
+ report.push('## 약점');
1000
+ measurement.analysis.weaknesses.forEach(weakness => {
1001
+ report.push(`- ${weakness}`);
1002
+ });
1003
+ report.push('');
1004
+ }
1005
+
1006
+ // 권장사항
1007
+ if (measurement.analysis.recommendations.length > 0) {
1008
+ report.push('## 권장사항');
1009
+ measurement.analysis.recommendations.forEach(rec => {
1010
+ report.push(`- ${rec}`);
1011
+ });
1012
+ report.push('');
1013
+ }
1014
+
1015
+ return report.join('\n');
1016
+ }
1017
+
1018
+ /**
1019
+ * 리소스를 정리합니다
1020
+ */
1021
+ cleanup() {
1022
+ if (this.tokenCounter) {
1023
+ this.tokenCounter.cleanup();
1024
+ }
1025
+ this.measurements.clear();
1026
+ this.benchmarks.clear();
1027
+ this.measurementHistory.length = 0;
1028
+ }
1029
+ }
1030
+
1031
+ export default CompressionMetrics;