aiwf 0.3.6 → 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.
- package/.aiwf/00_PROJECT_MANIFEST.md +226 -2
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/S03_M02_sprint_meta.md +112 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T01_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T02_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T03_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T04_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T05_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T06_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T07_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T08_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T09_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T100_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T101_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T102_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T103_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T104_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T105_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T106_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T107_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T108_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T109_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T10_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T110_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T111_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T112_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T11_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T12_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T13_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T14_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T15_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T16_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T17_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T18_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T19_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T20_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T21_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T22_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T23_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T24_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T25_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T26_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T27_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T28_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T29_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T30_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T31_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T32_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T33_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T34_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T35_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T36_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T37_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T38_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T39_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T40_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T41_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T42_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T43_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T44_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T45_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T46_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T47_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T48_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T49_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T50_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T51_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T52_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T53_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T54_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T55_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T56_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T57_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T58_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T59_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T60_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T61_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T62_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T63_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T64_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T65_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T66_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T67_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T68_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T69_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T70_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T71_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T72_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T73_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T74_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T75_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T76_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T77_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T78_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T79_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T80_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T81_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T82_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T83_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T84_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T85_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T86_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T87_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T88_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T89_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T90_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T91_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T92_S03_task_with_special_characters.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T93_S03_test.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T94_S03_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T95_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T96_S03_next_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T97_S03_new_test_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T98_S03_first_task.md +39 -0
- package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T99_S03_test_task.md +39 -0
- package/.aiwf/test-results-s02.json +5 -5
- package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +3 -15
- package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -15
- package/.claude/commands/aiwf/aiwf_compress_stats.md +2 -51
- package/.claude/commands/aiwf/aiwf_compress_with_persona.md +5 -18
- package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
- package/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
- package/.claude/commands/aiwf/aiwf_evaluate.md +3 -16
- package/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
- package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
- package/.claude/commands/aiwf/aiwf_list_features.md +1 -1
- package/.claude/commands/aiwf/aiwf_switch_language.md +48 -120
- package/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
- package/claude-code/aiwf/en/.claude/commands/aiwf/aiwf_create_testcase.md +561 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +2 -2
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -2
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_stats.md +10 -47
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_with_persona.md +2 -2
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_testcase.md +561 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_evaluate.md +2 -2
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_list_features.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_architect.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_backend.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_data_analyst.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_frontend.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_security.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_status.md +1 -1
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
- package/package.json +1 -1
- package/src/cli/index.js +174 -0
- package/src/commands/compress.js +60 -0
- package/src/commands/create-project.js +23 -17
- package/src/commands/evaluate.js +459 -0
- package/src/commands/feature.js +365 -0
- package/src/commands/persona.js +37 -16
- package/src/commands/token.js +312 -0
- package/src/lib/file-downloader.js +189 -33
- package/src/lib/installer.js +38 -11
- package/src/lib/resource-loader.js +250 -0
- package/src/lib/resources/commands/ai-persona.js +602 -0
- package/src/lib/resources/commands/compress-context.js +389 -0
- package/src/lib/resources/commands/evaluate.js +246 -0
- package/src/lib/resources/commands/feature-ledger.js +565 -0
- package/src/lib/resources/commands/feature_commit_report.js +370 -0
- package/src/lib/resources/commands/persona-context-apply.js +562 -0
- package/src/lib/resources/commands/scan_git_history.js +234 -0
- package/src/lib/resources/commands/sync_feature_commits.js +154 -0
- package/src/lib/resources/commands/token-tracking.js +443 -0
- package/src/lib/resources/config/commit-patterns.js +103 -0
- package/src/lib/resources/config/language.json +6 -0
- package/src/lib/resources/personas/PERSONA_INDEX.md +57 -0
- package/src/lib/resources/personas/analyst.json +44 -0
- package/src/lib/resources/personas/architect/best_practices.md +136 -0
- package/src/lib/resources/personas/architect/knowledge_base.md +377 -0
- package/src/lib/resources/personas/architect.json +44 -0
- package/src/lib/resources/personas/backend/best_practices.md +766 -0
- package/src/lib/resources/personas/backend/knowledge_base.md +1070 -0
- package/src/lib/resources/personas/data_analyst/best_practices.md +594 -0
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +1057 -0
- package/src/lib/resources/personas/developer.json +44 -0
- package/src/lib/resources/personas/developer.md +37 -0
- package/src/lib/resources/personas/evaluation_criteria.json +137 -0
- package/src/lib/resources/personas/frontend/best_practices.md +445 -0
- package/src/lib/resources/personas/frontend/knowledge_base.md +729 -0
- package/src/lib/resources/personas/persona-index.json +31 -0
- package/src/lib/resources/personas/reviewer.json +44 -0
- package/src/lib/resources/personas/security/best_practices.md +307 -0
- package/src/lib/resources/personas/security/knowledge_base.md +498 -0
- package/src/lib/resources/personas/tester.json +44 -0
- package/src/lib/resources/templates/README.md +78 -0
- package/src/lib/resources/templates/api-server/config.json +64 -0
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +56 -0
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +42 -0
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +40 -0
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +111 -0
- package/src/lib/resources/templates/api-server/template/.env.example +29 -0
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +23 -0
- package/src/lib/resources/templates/api-server/template/README.md +171 -0
- package/src/lib/resources/templates/api-server/template/jest.config.js +26 -0
- package/src/lib/resources/templates/api-server/template/nodemon.json +9 -0
- package/src/lib/resources/templates/api-server/template/package.json +61 -0
- package/src/lib/resources/templates/api-server/template/src/app.ts +60 -0
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +38 -0
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +68 -0
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +128 -0
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +28 -0
- package/src/lib/resources/templates/api-server/template/src/index.ts +28 -0
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +50 -0
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +41 -0
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +42 -0
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +14 -0
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +20 -0
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +42 -0
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +13 -0
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +67 -0
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +37 -0
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +40 -0
- package/src/lib/resources/templates/api-server/template/tsconfig.json +43 -0
- package/src/lib/resources/templates/npm-library/config.json +61 -0
- package/src/lib/resources/templates/npm-library/template/LICENSE +21 -0
- package/src/lib/resources/templates/npm-library/template/README.md +201 -0
- package/src/lib/resources/templates/npm-library/template/package.json +78 -0
- package/src/lib/resources/templates/web-app/config.json +53 -0
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +54 -0
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +37 -0
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +40 -0
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +110 -0
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +20 -0
- package/src/lib/resources/templates/web-app/template/README.md +151 -0
- package/src/lib/resources/templates/web-app/template/index.html +14 -0
- package/src/lib/resources/templates/web-app/template/package.json +44 -0
- package/src/lib/resources/templates/web-app/template/postcss.config.js +6 -0
- package/src/lib/resources/templates/web-app/template/public/vite.svg +1 -0
- package/src/lib/resources/templates/web-app/template/src/App.tsx +21 -0
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +57 -0
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +107 -0
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/FeatureLedger.tsx +93 -0
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +71 -0
- package/src/lib/resources/templates/web-app/template/src/index.css +60 -0
- package/src/lib/resources/templates/web-app/template/src/main.tsx +10 -0
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +60 -0
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +89 -0
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +25 -0
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +126 -0
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +9 -0
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +1 -0
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +30 -0
- package/src/lib/resources/templates/web-app/template/tsconfig.json +37 -0
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +10 -0
- package/src/lib/resources/templates/web-app/template/vite.config.ts +23 -0
- package/src/lib/resources/utils/background-monitor.js +223 -0
- package/src/lib/resources/utils/compression-metrics.js +1031 -0
- package/src/lib/resources/utils/compression-strategies.js +557 -0
- package/src/lib/resources/utils/content-normalizer.js +455 -0
- package/src/lib/resources/utils/context-compressor.js +583 -0
- package/src/lib/resources/utils/context-rule-parser.js +180 -0
- package/src/lib/resources/utils/context-token-monitor.js +463 -0
- package/src/lib/resources/utils/context-update-manager.js +502 -0
- package/src/lib/resources/utils/feature-updater.js +271 -0
- package/src/lib/resources/utils/git-utils.js +220 -0
- package/src/lib/resources/utils/importance-classifier.js +645 -0
- package/src/lib/resources/utils/information-filter.js +710 -0
- package/src/lib/resources/utils/persona-aware-compressor.js +598 -0
- package/src/lib/resources/utils/prompt-injector.js +242 -0
- package/src/lib/resources/utils/simplified-evaluator.js +119 -0
- package/src/lib/resources/utils/text-summarizer.js +492 -0
- package/src/lib/resources/utils/token-counter.js +177 -0
- package/src/lib/resources/utils/token-monitor.js +484 -0
- package/src/lib/resources/utils/token-reporter.js +453 -0
- package/src/lib/resources/utils/token-storage.js +433 -0
- package/src/lib/resources/utils/token-tracker.js +283 -0
- package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_switch_language.md +0 -160
- /package/claude-code/aiwf/ko/.claude/commands/aiwf/{aiwf_update_docs_kr.md → aiwf_update_docs.md} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIWF 토큰 추적 명령어
|
|
5
|
+
* AI 토큰 사용량 모니터링 및 분석
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ResourceLoader } from '../lib/resource-loader.js';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
|
|
13
|
+
class TokenCommand {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.resourceLoader = new ResourceLoader();
|
|
16
|
+
this.tokenDataPath = path.join(process.cwd(), '.aiwf', 'token-usage');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Token 명령어 실행
|
|
21
|
+
*/
|
|
22
|
+
async execute(args) {
|
|
23
|
+
const [subcommand, ...restArgs] = args;
|
|
24
|
+
|
|
25
|
+
switch (subcommand) {
|
|
26
|
+
case 'status':
|
|
27
|
+
await this.showStatus();
|
|
28
|
+
break;
|
|
29
|
+
case 'report':
|
|
30
|
+
await this.generateReport(restArgs);
|
|
31
|
+
break;
|
|
32
|
+
case 'track':
|
|
33
|
+
await this.trackUsage(restArgs);
|
|
34
|
+
break;
|
|
35
|
+
case 'reset':
|
|
36
|
+
await this.resetTracking();
|
|
37
|
+
break;
|
|
38
|
+
case 'limit':
|
|
39
|
+
await this.setLimit(restArgs);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
this.showHelp();
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 토큰 사용 상태 표시
|
|
49
|
+
*/
|
|
50
|
+
async showStatus() {
|
|
51
|
+
try {
|
|
52
|
+
// 토큰 추적 모듈 로드
|
|
53
|
+
const tokenTrackerModule = await this.resourceLoader.loadUtil('token-tracker.js');
|
|
54
|
+
const TokenTracker = tokenTrackerModule.TokenTracker || tokenTrackerModule.default;
|
|
55
|
+
|
|
56
|
+
if (!TokenTracker) {
|
|
57
|
+
throw new Error('TokenTracker 모듈을 로드할 수 없습니다.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const tracker = new TokenTracker({
|
|
61
|
+
storageDir: this.tokenDataPath
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await tracker.init();
|
|
65
|
+
const stats = await tracker.getStatistics();
|
|
66
|
+
const limits = await this.getLimits();
|
|
67
|
+
|
|
68
|
+
console.log(chalk.cyan('💰 토큰 사용 현황'));
|
|
69
|
+
console.log(chalk.gray('-'.repeat(50)));
|
|
70
|
+
|
|
71
|
+
// 오늘 사용량
|
|
72
|
+
console.log(chalk.bold('오늘 사용량:'));
|
|
73
|
+
console.log(` 입력 토큰: ${chalk.yellow(stats.today.input.toLocaleString())}`);
|
|
74
|
+
console.log(` 출력 토큰: ${chalk.yellow(stats.today.output.toLocaleString())}`);
|
|
75
|
+
console.log(` 총 토큰: ${chalk.yellow(stats.today.total.toLocaleString())}`);
|
|
76
|
+
|
|
77
|
+
if (limits.daily > 0) {
|
|
78
|
+
const dailyUsagePercent = (stats.today.total / limits.daily * 100).toFixed(1);
|
|
79
|
+
const dailyColor = dailyUsagePercent > 90 ? 'red' : dailyUsagePercent > 70 ? 'yellow' : 'green';
|
|
80
|
+
console.log(` 일일 한도: ${chalk[dailyColor](`${dailyUsagePercent}% 사용`)}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 이번 주 사용량
|
|
84
|
+
console.log(chalk.bold('\n이번 주 사용량:'));
|
|
85
|
+
console.log(` 입력 토큰: ${chalk.yellow(stats.week.input.toLocaleString())}`);
|
|
86
|
+
console.log(` 출력 토큰: ${chalk.yellow(stats.week.output.toLocaleString())}`);
|
|
87
|
+
console.log(` 총 토큰: ${chalk.yellow(stats.week.total.toLocaleString())}`);
|
|
88
|
+
|
|
89
|
+
// 이번 달 사용량
|
|
90
|
+
console.log(chalk.bold('\n이번 달 사용량:'));
|
|
91
|
+
console.log(` 입력 토큰: ${chalk.yellow(stats.month.input.toLocaleString())}`);
|
|
92
|
+
console.log(` 출력 토큰: ${chalk.yellow(stats.month.output.toLocaleString())}`);
|
|
93
|
+
console.log(` 총 토큰: ${chalk.yellow(stats.month.total.toLocaleString())}`);
|
|
94
|
+
|
|
95
|
+
if (limits.monthly > 0) {
|
|
96
|
+
const monthlyUsagePercent = (stats.month.total / limits.monthly * 100).toFixed(1);
|
|
97
|
+
const monthlyColor = monthlyUsagePercent > 90 ? 'red' : monthlyUsagePercent > 70 ? 'yellow' : 'green';
|
|
98
|
+
console.log(` 월간 한도: ${chalk[monthlyColor](`${monthlyUsagePercent}% 사용`)}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 전체 통계
|
|
102
|
+
console.log(chalk.bold('\n전체 통계:'));
|
|
103
|
+
console.log(` 총 세션: ${stats.overall.sessions}`);
|
|
104
|
+
console.log(` 평균 세션당 토큰: ${Math.round(stats.overall.total / stats.overall.sessions).toLocaleString()}`);
|
|
105
|
+
console.log(` 최대 단일 세션: ${stats.overall.maxSession.toLocaleString()} 토큰`);
|
|
106
|
+
|
|
107
|
+
console.log(chalk.gray('\n' + '-'.repeat(50)));
|
|
108
|
+
console.log(chalk.gray('상세 리포트: aiwf token report'));
|
|
109
|
+
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('❌ 토큰 상태 조회 중 오류가 발생했습니다:'), error.message);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 토큰 사용 리포트 생성
|
|
117
|
+
*/
|
|
118
|
+
async generateReport(args) {
|
|
119
|
+
const [period = 'week'] = args;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// 토큰 리포터 모듈 로드
|
|
123
|
+
const reporterModule = await this.resourceLoader.loadUtil('token-reporter.js');
|
|
124
|
+
const TokenReporter = reporterModule.TokenReporter || reporterModule.default;
|
|
125
|
+
|
|
126
|
+
if (!TokenReporter) {
|
|
127
|
+
throw new Error('TokenReporter 모듈을 로드할 수 없습니다.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const reporter = new TokenReporter({
|
|
131
|
+
storageDir: this.tokenDataPath
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
console.log(chalk.cyan(`📊 토큰 사용 리포트 (${period})`));
|
|
135
|
+
console.log(chalk.gray('생성 중...'));
|
|
136
|
+
|
|
137
|
+
const report = await reporter.generateReport(period);
|
|
138
|
+
const reportPath = path.join(this.tokenDataPath, 'reports', `token-report-${period}-${Date.now()}.md`);
|
|
139
|
+
|
|
140
|
+
await fs.ensureDir(path.dirname(reportPath));
|
|
141
|
+
await fs.writeFile(reportPath, report);
|
|
142
|
+
|
|
143
|
+
console.log(chalk.green(`✅ 리포트가 생성되었습니다: ${reportPath}`));
|
|
144
|
+
|
|
145
|
+
// 리포트 요약 표시
|
|
146
|
+
console.log(chalk.cyan('\n📄 리포트 요약:'));
|
|
147
|
+
const lines = report.split('\n').slice(0, 20);
|
|
148
|
+
console.log(lines.join('\n'));
|
|
149
|
+
console.log(chalk.gray('\n... (전체 리포트는 파일을 확인하세요)'));
|
|
150
|
+
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(chalk.red('❌ 리포트 생성 중 오류가 발생했습니다:'), error.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 토큰 사용량 추적
|
|
158
|
+
*/
|
|
159
|
+
async trackUsage(args) {
|
|
160
|
+
const [inputTokens, outputTokens] = args.map(Number);
|
|
161
|
+
|
|
162
|
+
if (!inputTokens || !outputTokens) {
|
|
163
|
+
console.error(chalk.red('❌ 입력 토큰과 출력 토큰 수를 지정해주세요.'));
|
|
164
|
+
console.log(chalk.gray('사용법: aiwf token track <입력토큰> <출력토큰>'));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const trackerModule = await this.resourceLoader.loadUtil('token-tracker.js');
|
|
170
|
+
const TokenTracker = trackerModule.TokenTracker || trackerModule.default;
|
|
171
|
+
|
|
172
|
+
const tracker = new TokenTracker({
|
|
173
|
+
storageDir: this.tokenDataPath
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await tracker.init();
|
|
177
|
+
await tracker.trackUsage({
|
|
178
|
+
input: inputTokens,
|
|
179
|
+
output: outputTokens,
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
source: 'manual'
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log(chalk.green('✅ 토큰 사용량이 기록되었습니다.'));
|
|
185
|
+
console.log(`입력: ${chalk.yellow(inputTokens.toLocaleString())} 토큰`);
|
|
186
|
+
console.log(`출력: ${chalk.yellow(outputTokens.toLocaleString())} 토큰`);
|
|
187
|
+
console.log(`총합: ${chalk.yellow((inputTokens + outputTokens).toLocaleString())} 토큰`);
|
|
188
|
+
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(chalk.red('❌ 토큰 추적 중 오류가 발생했습니다:'), error.message);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 토큰 추적 초기화
|
|
196
|
+
*/
|
|
197
|
+
async resetTracking() {
|
|
198
|
+
try {
|
|
199
|
+
const confirm = await this.confirmReset();
|
|
200
|
+
if (!confirm) {
|
|
201
|
+
console.log(chalk.yellow('초기화가 취소되었습니다.'));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
await fs.remove(this.tokenDataPath);
|
|
206
|
+
console.log(chalk.green('✅ 토큰 추적 데이터가 초기화되었습니다.'));
|
|
207
|
+
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(chalk.red('❌ 초기화 중 오류가 발생했습니다:'), error.message);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 토큰 사용 한도 설정
|
|
215
|
+
*/
|
|
216
|
+
async setLimit(args) {
|
|
217
|
+
const [limitType, limitValue] = args;
|
|
218
|
+
|
|
219
|
+
if (!limitType || !limitValue) {
|
|
220
|
+
console.error(chalk.red('❌ 한도 유형과 값을 지정해주세요.'));
|
|
221
|
+
console.log(chalk.gray('사용법: aiwf token limit <daily|monthly> <값>'));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const validTypes = ['daily', 'monthly'];
|
|
226
|
+
if (!validTypes.includes(limitType)) {
|
|
227
|
+
console.error(chalk.red(`❌ 유효하지 않은 한도 유형: ${limitType}`));
|
|
228
|
+
console.log(chalk.gray('유효한 유형: daily, monthly'));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const limit = parseInt(limitValue);
|
|
233
|
+
if (isNaN(limit) || limit < 0) {
|
|
234
|
+
console.error(chalk.red('❌ 한도 값은 0 이상의 숫자여야 합니다.'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
const configPath = path.join(this.tokenDataPath, 'limits.json');
|
|
240
|
+
await fs.ensureDir(this.tokenDataPath);
|
|
241
|
+
|
|
242
|
+
let limits = {};
|
|
243
|
+
if (fs.existsSync(configPath)) {
|
|
244
|
+
limits = await fs.readJson(configPath);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
limits[limitType] = limit;
|
|
248
|
+
await fs.writeJson(configPath, limits, { spaces: 2 });
|
|
249
|
+
|
|
250
|
+
console.log(chalk.green(`✅ ${limitType === 'daily' ? '일일' : '월간'} 한도가 ${limit.toLocaleString()} 토큰으로 설정되었습니다.`));
|
|
251
|
+
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error(chalk.red('❌ 한도 설정 중 오류가 발생했습니다:'), error.message);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 한도 설정 가져오기
|
|
259
|
+
*/
|
|
260
|
+
async getLimits() {
|
|
261
|
+
try {
|
|
262
|
+
const configPath = path.join(this.tokenDataPath, 'limits.json');
|
|
263
|
+
if (fs.existsSync(configPath)) {
|
|
264
|
+
return await fs.readJson(configPath);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
// 에러 무시
|
|
268
|
+
}
|
|
269
|
+
return { daily: 0, monthly: 0 };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 초기화 확인
|
|
274
|
+
*/
|
|
275
|
+
async confirmReset() {
|
|
276
|
+
// 간단한 확인 (실제로는 prompts 패키지 사용 권장)
|
|
277
|
+
console.log(chalk.yellow('⚠️ 모든 토큰 추적 데이터가 삭제됩니다.'));
|
|
278
|
+
console.log(chalk.gray('계속하려면 5초 내에 Ctrl+C로 취소하세요...'));
|
|
279
|
+
|
|
280
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 도움말 표시
|
|
286
|
+
*/
|
|
287
|
+
showHelp() {
|
|
288
|
+
console.log(chalk.cyan('💰 AIWF 토큰 추적'));
|
|
289
|
+
console.log(chalk.gray('-'.repeat(50)));
|
|
290
|
+
console.log('사용법: aiwf token <명령어> [옵션]');
|
|
291
|
+
console.log('');
|
|
292
|
+
console.log('명령어:');
|
|
293
|
+
console.log(' status 토큰 사용 현황 표시');
|
|
294
|
+
console.log(' report [기간] 사용 리포트 생성');
|
|
295
|
+
console.log(' track <입력> <출력> 토큰 사용량 수동 기록');
|
|
296
|
+
console.log(' limit <유형> <값> 사용 한도 설정');
|
|
297
|
+
console.log(' reset 추적 데이터 초기화');
|
|
298
|
+
console.log('');
|
|
299
|
+
console.log('리포트 기간:');
|
|
300
|
+
console.log(' day 일간 리포트');
|
|
301
|
+
console.log(' week 주간 리포트 (기본값)');
|
|
302
|
+
console.log(' month 월간 리포트');
|
|
303
|
+
console.log('');
|
|
304
|
+
console.log('예시:');
|
|
305
|
+
console.log(' aiwf token status');
|
|
306
|
+
console.log(' aiwf token report week');
|
|
307
|
+
console.log(' aiwf token track 1500 500');
|
|
308
|
+
console.log(' aiwf token limit daily 100000');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export default TokenCommand;
|
|
@@ -6,6 +6,10 @@ import path from 'path';
|
|
|
6
6
|
|
|
7
7
|
const GITHUB_RAW_URL = 'https://raw.githubusercontent.com/moonklabs/aiwf/main';
|
|
8
8
|
const GITHUB_CONTENT_PREFIX = 'claude-code/aiwf';
|
|
9
|
+
const GITHUB_API_URL = 'https://api.github.com/repos/moonklabs/aiwf/contents';
|
|
10
|
+
const GITHUB_OWNER = 'moonklabs';
|
|
11
|
+
const GITHUB_REPO = 'aiwf';
|
|
12
|
+
const GITHUB_BRANCH = 'main';
|
|
9
13
|
|
|
10
14
|
// Pre-defined list of command files
|
|
11
15
|
const COMMAND_FILES = [
|
|
@@ -82,16 +86,22 @@ const PROMPT_FILES = [
|
|
|
82
86
|
/**
|
|
83
87
|
* Fetch content from URL
|
|
84
88
|
* @param {string} url - The URL to fetch from
|
|
89
|
+
* @param {Object} headers - Optional headers
|
|
85
90
|
* @returns {Promise<string>} The response content
|
|
86
91
|
*/
|
|
87
|
-
async function fetchContent(url) {
|
|
92
|
+
async function fetchContent(url, headers = {}) {
|
|
88
93
|
return new Promise((resolve, reject) => {
|
|
89
|
-
|
|
94
|
+
const defaultHeaders = { 'User-Agent': 'aiwf' };
|
|
95
|
+
const requestHeaders = { ...defaultHeaders, ...headers };
|
|
96
|
+
|
|
97
|
+
https.get(url, { headers: requestHeaders }, (res) => {
|
|
90
98
|
let data = '';
|
|
91
99
|
res.on('data', chunk => data += chunk);
|
|
92
100
|
res.on('end', () => {
|
|
93
101
|
if (res.statusCode === 200) {
|
|
94
102
|
resolve(data);
|
|
103
|
+
} else if (res.statusCode === 403 && res.headers['x-ratelimit-remaining'] === '0') {
|
|
104
|
+
reject(new Error('GitHub API rate limit exceeded. Please try again later.'));
|
|
95
105
|
} else {
|
|
96
106
|
reject(new Error(`Failed to fetch ${url}: ${res.statusCode}`));
|
|
97
107
|
}
|
|
@@ -104,35 +114,164 @@ async function fetchContent(url) {
|
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
/**
|
|
107
|
-
*
|
|
117
|
+
* Sleep for specified milliseconds
|
|
118
|
+
* @param {number} ms - Milliseconds to sleep
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
function sleep(ms) {
|
|
122
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Fetch directory contents from GitHub API with retry logic
|
|
127
|
+
* @param {string} path - Repository path
|
|
128
|
+
* @param {number} retries - Number of retries for rate limit
|
|
129
|
+
* @returns {Promise<Array>} Array of file/directory objects
|
|
130
|
+
*/
|
|
131
|
+
async function fetchGitHubDirectory(path, retries = 3) {
|
|
132
|
+
const apiUrl = `${GITHUB_API_URL}/${path}?ref=${GITHUB_BRANCH}`;
|
|
133
|
+
|
|
134
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetchContent(apiUrl, {
|
|
137
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
138
|
+
});
|
|
139
|
+
return JSON.parse(response);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (error.message.includes('rate limit exceeded') && attempt < retries) {
|
|
142
|
+
console.warn(`GitHub API rate limit hit. Retrying in ${(attempt + 1) * 10} seconds...`);
|
|
143
|
+
await sleep((attempt + 1) * 10000); // 10s, 20s, 30s
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// GitHub API가 실패하면 빈 배열 반환
|
|
148
|
+
console.warn(`Failed to fetch directory listing for ${path}: ${error.message}`);
|
|
149
|
+
|
|
150
|
+
// 404 에러의 경우 정상적으로 빈 배열 반환 (디렉토리가 없는 경우)
|
|
151
|
+
if (error.message.includes('404')) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Rate limit이나 다른 에러의 경우 재시도 후 실패
|
|
156
|
+
if (attempt === retries) {
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Recursively download directory contents
|
|
167
|
+
* @param {string} githubPath - GitHub repository path
|
|
168
|
+
* @param {string} localPath - Local destination path
|
|
169
|
+
* @param {Object} spinner - Ora spinner instance
|
|
170
|
+
* @param {boolean} debugLog - Debug logging flag
|
|
171
|
+
* @returns {Promise<void>}
|
|
172
|
+
*/
|
|
173
|
+
async function downloadDirectoryRecursive(githubPath, localPath, spinner, debugLog = false) {
|
|
174
|
+
try {
|
|
175
|
+
// GitHub API로 디렉토리 내용 가져오기
|
|
176
|
+
const contents = await fetchGitHubDirectory(githubPath);
|
|
177
|
+
|
|
178
|
+
if (contents.length === 0) {
|
|
179
|
+
if (debugLog) console.log(`No contents found in ${githubPath}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 로컬 디렉토리 생성
|
|
184
|
+
await fs.mkdir(localPath, { recursive: true });
|
|
185
|
+
|
|
186
|
+
for (const item of contents) {
|
|
187
|
+
if (item.type === 'file') {
|
|
188
|
+
// 파일 다운로드
|
|
189
|
+
const destPath = path.join(localPath, item.name);
|
|
190
|
+
const downloadUrl = item.download_url;
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
await downloadFile(downloadUrl, destPath);
|
|
194
|
+
if (spinner) {
|
|
195
|
+
spinner.text = `Downloaded: ${item.name}`;
|
|
196
|
+
}
|
|
197
|
+
if (debugLog) console.log(`Downloaded: ${githubPath}/${item.name}`);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (debugLog) console.error(`Failed to download ${item.name}: ${error.message}`);
|
|
200
|
+
}
|
|
201
|
+
} else if (item.type === 'dir') {
|
|
202
|
+
// 하위 디렉토리 재귀적으로 처리
|
|
203
|
+
const subGithubPath = `${githubPath}/${item.name}`;
|
|
204
|
+
const subLocalPath = path.join(localPath, item.name);
|
|
205
|
+
|
|
206
|
+
if (spinner) {
|
|
207
|
+
spinner.text = `Processing directory: ${item.name}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await downloadDirectoryRecursive(subGithubPath, subLocalPath, spinner, debugLog);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (debugLog) console.error(`Error processing directory ${githubPath}: ${error.message}`);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Download a file from URL to destination path with retry logic
|
|
108
221
|
* @param {string} url - The URL to download from
|
|
109
222
|
* @param {string} destPath - The destination file path
|
|
223
|
+
* @param {number} retries - Number of retries
|
|
110
224
|
* @returns {Promise<void>}
|
|
111
225
|
*/
|
|
112
|
-
async function downloadFile(url, destPath) {
|
|
226
|
+
async function downloadFile(url, destPath, retries = 3) {
|
|
113
227
|
// Ensure destination directory exists
|
|
114
228
|
const destDir = path.dirname(destPath);
|
|
115
229
|
await fs.mkdir(destDir, { recursive: true });
|
|
116
230
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
231
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
232
|
+
try {
|
|
233
|
+
await new Promise((resolve, reject) => {
|
|
234
|
+
https.get(url, { headers: { 'User-Agent': 'aiwf' } }, (response) => {
|
|
235
|
+
if (response.statusCode === 403 && response.headers['x-ratelimit-remaining'] === '0') {
|
|
236
|
+
response.destroy();
|
|
237
|
+
reject(new Error('GitHub API rate limit exceeded'));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (response.statusCode !== 200) {
|
|
242
|
+
response.destroy();
|
|
243
|
+
reject(new Error(`Failed to download ${url}: ${response.statusCode}`));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const file = createWriteStream(destPath);
|
|
248
|
+
pipeline(response, file)
|
|
249
|
+
.then(() => {
|
|
250
|
+
resolve();
|
|
251
|
+
})
|
|
252
|
+
.catch((error) => {
|
|
253
|
+
reject(error);
|
|
254
|
+
});
|
|
255
|
+
}).on('error', (err) => {
|
|
256
|
+
reject(err);
|
|
131
257
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// 성공하면 반환
|
|
261
|
+
return;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error.message.includes('rate limit exceeded') && attempt < retries) {
|
|
264
|
+
console.warn(`Rate limit hit while downloading ${path.basename(destPath)}. Retrying in ${(attempt + 1) * 10} seconds...`);
|
|
265
|
+
await sleep((attempt + 1) * 10000);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 마지막 시도에서도 실패하면 에러 throw
|
|
270
|
+
if (attempt === retries) {
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
136
275
|
}
|
|
137
276
|
|
|
138
277
|
/**
|
|
@@ -164,17 +303,33 @@ async function downloadFileList(githubPath, localPath, fileList, spinner, messag
|
|
|
164
303
|
}
|
|
165
304
|
|
|
166
305
|
/**
|
|
167
|
-
* Download directory contents using predefined file lists
|
|
306
|
+
* Download directory contents using predefined file lists or dynamic fetching
|
|
168
307
|
* @param {string} githubPath - GitHub repository path
|
|
169
308
|
* @param {string} localPath - Local destination path
|
|
170
309
|
* @param {Object} spinner - Ora spinner instance
|
|
171
310
|
* @param {Object} messages - Localized messages
|
|
172
311
|
* @param {string} [selectedLanguage='en'] - Selected language
|
|
312
|
+
* @param {boolean} [useDynamicFetch=false] - Use GitHub API for dynamic fetching
|
|
173
313
|
* @returns {Promise<void>}
|
|
174
314
|
*/
|
|
175
|
-
async function downloadDirectory(githubPath, localPath, spinner, messages, selectedLanguage = 'en') {
|
|
315
|
+
async function downloadDirectory(githubPath, localPath, spinner, messages, selectedLanguage = 'en', useDynamicFetch = false) {
|
|
176
316
|
try {
|
|
177
|
-
//
|
|
317
|
+
// .aiwf 디렉토리나 알 수 없는 디렉토리의 경우 동적 다운로드 사용
|
|
318
|
+
if (useDynamicFetch || githubPath.includes('.aiwf') ||
|
|
319
|
+
(!githubPath.includes('.claude/commands/aiwf') &&
|
|
320
|
+
!githubPath.includes('rules/global') &&
|
|
321
|
+
!githubPath.includes('rules/manual'))) {
|
|
322
|
+
|
|
323
|
+
if (spinner) {
|
|
324
|
+
spinner.text = `Fetching directory structure: ${githubPath}`;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// GitHub API를 사용한 동적 디렉토리 다운로드
|
|
328
|
+
await downloadDirectoryRecursive(githubPath, localPath, spinner, messages && messages.debugLog);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 기존 로직: 사전 정의된 파일 리스트 사용
|
|
178
333
|
let fileList = [];
|
|
179
334
|
|
|
180
335
|
if (githubPath.includes('.claude/commands/aiwf')) {
|
|
@@ -183,16 +338,11 @@ async function downloadDirectory(githubPath, localPath, spinner, messages, selec
|
|
|
183
338
|
fileList = GLOBAL_RULES_FILES;
|
|
184
339
|
} else if (githubPath.includes('rules/manual')) {
|
|
185
340
|
fileList = MANUAL_RULES_FILES;
|
|
186
|
-
} else if (githubPath.includes('98_PROMPTS')) {
|
|
187
|
-
fileList = PROMPT_FILES;
|
|
188
|
-
} else if (githubPath.includes('99_TEMPLATES')) {
|
|
189
|
-
fileList = TEMPLATE_FILES;
|
|
190
|
-
} else {
|
|
191
|
-
// For unknown directories, try to download common files
|
|
192
|
-
fileList = ['README.md', 'index.md'];
|
|
193
341
|
}
|
|
194
342
|
|
|
195
|
-
|
|
343
|
+
if (fileList.length > 0) {
|
|
344
|
+
await downloadFileList(githubPath, localPath, fileList, spinner, messages);
|
|
345
|
+
}
|
|
196
346
|
} catch (error) {
|
|
197
347
|
throw new Error(`Failed to download directory ${githubPath}: ${error.message}`);
|
|
198
348
|
}
|
|
@@ -203,8 +353,14 @@ export {
|
|
|
203
353
|
downloadFile,
|
|
204
354
|
downloadFileList,
|
|
205
355
|
downloadDirectory,
|
|
356
|
+
downloadDirectoryRecursive,
|
|
357
|
+
fetchGitHubDirectory,
|
|
206
358
|
GITHUB_RAW_URL,
|
|
207
359
|
GITHUB_CONTENT_PREFIX,
|
|
360
|
+
GITHUB_API_URL,
|
|
361
|
+
GITHUB_OWNER,
|
|
362
|
+
GITHUB_REPO,
|
|
363
|
+
GITHUB_BRANCH,
|
|
208
364
|
COMMAND_FILES,
|
|
209
365
|
GLOBAL_RULES_FILES,
|
|
210
366
|
MANUAL_RULES_FILES,
|
package/src/lib/installer.js
CHANGED
|
@@ -219,20 +219,46 @@ async function downloadManifest(languagePath, spinner, msg, debugLog) {
|
|
|
219
219
|
async function downloadTemplates(languagePath, spinner, msg, debugLog) {
|
|
220
220
|
try {
|
|
221
221
|
logWithSpinner(spinner, msg.downloadingTemplates, debugLog);
|
|
222
|
+
|
|
223
|
+
// .aiwf 전체 디렉토리를 동적으로 다운로드
|
|
224
|
+
logWithSpinner(spinner, `Downloading .aiwf directory structure...`, debugLog);
|
|
222
225
|
await downloadDirectory(
|
|
223
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf
|
|
224
|
-
AIWF_DIRS.
|
|
226
|
+
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf`,
|
|
227
|
+
AIWF_DIRS.ROOT,
|
|
225
228
|
spinner,
|
|
226
|
-
msg
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/99_TEMPLATES`,
|
|
230
|
-
AIWF_DIRS.TEMPLATES,
|
|
231
|
-
spinner,
|
|
232
|
-
msg
|
|
229
|
+
msg,
|
|
230
|
+
languagePath,
|
|
231
|
+
true // useDynamicFetch = true
|
|
233
232
|
);
|
|
233
|
+
|
|
234
|
+
logWithSpinner(spinner, `Successfully downloaded .aiwf directory`, debugLog);
|
|
234
235
|
} catch (error) {
|
|
235
|
-
logWithSpinner(spinner,
|
|
236
|
+
logWithSpinner(spinner, `Error downloading .aiwf: ${error.message}`, debugLog);
|
|
237
|
+
|
|
238
|
+
// Fallback: 개별 디렉토리 다운로드 시도
|
|
239
|
+
try {
|
|
240
|
+
logWithSpinner(spinner, `Trying individual directory downloads...`, debugLog);
|
|
241
|
+
|
|
242
|
+
await downloadDirectory(
|
|
243
|
+
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/98_PROMPTS`,
|
|
244
|
+
AIWF_DIRS.PROMPTS,
|
|
245
|
+
spinner,
|
|
246
|
+
msg,
|
|
247
|
+
languagePath,
|
|
248
|
+
true
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
await downloadDirectory(
|
|
252
|
+
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/99_TEMPLATES`,
|
|
253
|
+
AIWF_DIRS.TEMPLATES,
|
|
254
|
+
spinner,
|
|
255
|
+
msg,
|
|
256
|
+
languagePath,
|
|
257
|
+
true
|
|
258
|
+
);
|
|
259
|
+
} catch (fallbackError) {
|
|
260
|
+
logWithSpinner(spinner, msg.templatesNotFound, debugLog);
|
|
261
|
+
}
|
|
236
262
|
}
|
|
237
263
|
}
|
|
238
264
|
|
|
@@ -282,7 +308,8 @@ async function updateCommands(languagePath, spinner, msg, debugLog) {
|
|
|
282
308
|
TOOL_DIRS.CLAUDE_COMMANDS,
|
|
283
309
|
spinner,
|
|
284
310
|
msg,
|
|
285
|
-
languagePath
|
|
311
|
+
languagePath,
|
|
312
|
+
false // commands는 기존 방식 유지 (파일 리스트 사용)
|
|
286
313
|
);
|
|
287
314
|
} catch (error) {
|
|
288
315
|
logWithSpinner(spinner, msg.commandsNotFound, debugLog);
|