git-analytics-cli 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

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.
generate_report.py CHANGED
@@ -135,6 +135,12 @@ body {
135
135
  .rank-commits { font-size: 1em; font-weight: 700; color: #0969da; }
136
136
 
137
137
  /* 建议 */
138
+ .sug-tabs { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
139
+ .sug-tab { padding: 8px 16px; border: 1px solid #d0d7de; border-radius: 20px; background: #f6f8fa; color: #656d76; font-size: 0.85em; cursor: pointer; transition: all 0.2s; }
140
+ .sug-tab:hover { background: #eaeef2; color: #1f2328; }
141
+ .sug-tab.active { background: #0969da; color: #fff; border-color: #0969da; }
142
+ .sug-panel { display: none; }
143
+ .sug-panel.active { display: block; }
138
144
  .sug-item { display: flex; gap: 12px; margin-bottom: 10px; padding: 12px 14px; background: #f6f8fa; border: 1px solid #d0d7de; border-radius: 6px; }
139
145
  .sug-num { width: 20px; height: 20px; background: #1a7f37; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.7em; font-weight: 700; flex-shrink: 0; }
140
146
  .sug-text { font-size: 0.9em; color: #1f2328; }
@@ -754,7 +760,16 @@ def _build_eng_insight(health):
754
760
 
755
761
 
756
762
  def _build_suggestions_html(habit_score, health, data):
757
- suggestions = []
763
+ # 分类建议结构
764
+ categories = {
765
+ 'commit': {'name': '提交习惯', 'icon': '📝', 'items': []},
766
+ 'test': {'name': '测试质量', 'icon': '✅', 'items': []},
767
+ 'doc': {'name': '文档维护', 'icon': '📚', 'items': []},
768
+ 'schedule': {'name': '作息健康', 'icon': '⏰', 'items': []},
769
+ 'project': {'name': '项目管理', 'icon': '🎯', 'items': []},
770
+ 'ai': {'name': 'AI 协作', 'icon': '🤖', 'items': []},
771
+ }
772
+
758
773
  summary = data.get('summary', {})
759
774
  ai = data.get('ai_signals', {})
760
775
  avg_daily = summary.get('avg_commits_per_day', 0)
@@ -765,503 +780,109 @@ def _build_suggestions_html(habit_score, health, data):
765
780
  total_commits = summary.get('total_commits', 0)
766
781
  total_active_days = summary.get('total_active_days', 0)
767
782
  total_projects = summary.get('total_projects', 0)
783
+ projects = data.get('projects', [])
768
784
 
769
785
  # ============================================================
770
- # 1. 习惯分数维度
786
+ # 提交习惯 (最多 6 条)
771
787
  # ============================================================
772
- if habit_score['schedule'] < 10:
773
- suggestions.append("改善作息规律:作息得分仅 {}/20,夜间和周末提交较多".format(habit_score['schedule']))
774
- if habit_score['focus'] < 10:
775
- suggestions.append("提升专注度:项目聚焦得分仅 {}/15,建议减少同时维护的项目数".format(habit_score['focus']))
776
788
  if habit_score['granularity'] < 15:
777
- suggestions.append("优化提交粒度:粒度得分仅 {}/30,建议保持稳定的提交频率".format(habit_score['granularity']))
789
+ categories['commit']['items'].append("优化提交粒度:粒度得分仅 {}/40,建议保持稳定的提交频率".format(habit_score['granularity']))
790
+ if avg_daily < 1:
791
+ categories['commit']['items'].append("提高提交频率:日均仅 {:.1f} 次提交,建议小步快跑、勤提交".format(avg_daily))
792
+ elif avg_daily > 8:
793
+ categories['commit']['items'].append("优化提交粒度:日均 {:.1f} 次提交偏多,考虑合并相关改动".format(avg_daily))
794
+ if health['low_info_ratio'] > 15:
795
+ categories['commit']['items'].append("优化 Commit Message:{:.0f}% 的提交缺少描述,建议使用 Conventional Commits 规范".format(health['low_info_ratio']))
796
+ elif health['low_info_ratio'] > 5:
797
+ categories['commit']['items'].append("完善提交描述:部分 commit 信息过于简略,建议写清楚改动原因")
798
+ if total_commits > 0:
799
+ other_c = commit_types.get('other', 0)
800
+ other_pct = other_c / total_commits * 100
801
+ if other_pct > 30:
802
+ categories['commit']['items'].append("提交分类不清:{:.0f}% 归为 other,建议使用 feat/fix/refactor 等标准类型".format(other_pct))
803
+ if total_active_days > 0 and total_commits > 0:
804
+ commits_per_active_day = total_commits / total_active_days
805
+ if commits_per_active_day > 10:
806
+ categories['commit']['items'].append("控制单日提交量:活跃日均提交 {:.0f} 次,建议拆分为更小的改动".format(commits_per_active_day))
807
+
808
+ # 正面反馈
809
+ if health['low_info_ratio'] < 3:
810
+ categories['commit']['items'].append("Commit 质量高:{:.0f}% 的提交有详细描述,继续保持!".format(100 - health['low_info_ratio']))
811
+ if avg_daily >= 2 and avg_daily <= 5:
812
+ categories['commit']['items'].append("提交频率适中:日均 {:.1f} 次提交,节奏良好".format(avg_daily))
778
813
 
779
814
  # ============================================================
780
- # 2. 测试相关
815
+ # 测试质量 (最多 5 条)
781
816
  # ============================================================
782
817
  if habit_score['test_awareness'] < 10:
783
- suggestions.append("提高测试意识:尝试为每个新功能编写测试用例,目标覆盖率 10%+")
818
+ categories['test']['items'].append("提高测试意识:尝试为每个新功能编写测试用例,目标覆盖率 10%+")
784
819
  elif health['test_ratio'] < 8:
785
- suggestions.append("增加测试投入:当前测试文件占比仅 {:.0f}%,建议补充单元测试".format(health['test_ratio']))
820
+ categories['test']['items'].append("增加测试投入:当前测试文件占比仅 {:.0f}%,建议补充单元测试".format(health['test_ratio']))
821
+ if total_commits > 0:
822
+ test_count = commit_types.get('test', 0)
823
+ if test_count / total_commits < 0.05 and total_commits > 50:
824
+ categories['test']['items'].append("增加测试提交:测试相关提交仅占 {:.0f}%,建议为新功能编写测试".format(test_count / total_commits * 100))
825
+ if health['fix_ratio'] > 20:
826
+ categories['test']['items'].append("提升代码质量:Bug 修复占比 {:.0f}%,建议加强测试和 Code Review".format(health['fix_ratio']))
827
+
828
+ # 正面反馈
786
829
  if health['test_ratio'] >= 30:
787
- suggestions.append("测试做得好:测试文件占比 {:.0f}%,继续保持!".format(health['test_ratio']))
830
+ categories['test']['items'].append("测试做得好:测试文件占比 {:.0f}%,继续保持!".format(health['test_ratio']))
788
831
 
789
832
  # ============================================================
790
- # 3. 文档相关
833
+ # 文档维护 (最多 5 条)
791
834
  # ============================================================
792
835
  if habit_score['doc_awareness'] < 10:
793
- suggestions.append("增加文档投入:定期更新 README 和 API 文档")
836
+ categories['doc']['items'].append("增加文档投入:定期更新 README 和 API 文档")
794
837
  elif health['doc_ratio'] < 5:
795
- suggestions.append("完善文档:文档变更占比 {:.0f}%,重要功能应有文档说明".format(health['doc_ratio']))
838
+ categories['doc']['items'].append("完善文档:文档变更占比 {:.0f}%,重要功能应有文档说明".format(health['doc_ratio']))
839
+ if total_commits > 0:
840
+ docs_count = commit_types.get('docs', 0)
841
+ if docs_count / total_commits < 0.05 and total_commits > 50:
842
+ categories['doc']['items'].append("增加文档提交:文档相关提交仅占 {:.0f}%,建议及时更新文档".format(docs_count / total_commits * 100))
843
+
844
+ # 正面反馈
796
845
  if health['doc_ratio'] >= 15:
797
- suggestions.append("文档做得好:文档变更占比 {:.0f}%,继续保持!".format(health['doc_ratio']))
846
+ categories['doc']['items'].append("文档做得好:文档变更占比 {:.0f}%,继续保持!".format(health['doc_ratio']))
798
847
 
799
848
  # ============================================================
800
- # 4. 作息相关
849
+ # 作息健康 (最多 5 条)
801
850
  # ============================================================
851
+ if habit_score['schedule'] < 10:
852
+ categories['schedule']['items'].append("改善作息规律:作息得分仅 {}/20,夜间和周末提交较多".format(habit_score['schedule']))
802
853
  if health['night_ratio'] > 25:
803
- suggestions.append("注意作息健康:夜间提交占比 {:.0f}%,建议调整为白天工作".format(health['night_ratio']))
854
+ categories['schedule']['items'].append("注意作息健康:夜间提交占比 {:.0f}%,建议调整为白天工作".format(health['night_ratio']))
804
855
  if health['weekend_ratio'] > 25:
805
- suggestions.append("平衡工作生活:周末提交占比 {:.0f}%,注意适当休息".format(health['weekend_ratio']))
806
-
807
- # ============================================================
808
- # 5. 深夜提交
809
- # ============================================================
856
+ categories['schedule']['items'].append("平衡工作生活:周末提交占比 {:.0f}%,注意适当休息".format(health['weekend_ratio']))
810
857
  if hourly:
811
- late_night = sum(hourly[0:6]) # 凌晨 0-5 点
858
+ late_night = sum(hourly[0:6])
812
859
  total = sum(hourly)
813
860
  if total > 0 and late_night / total > 0.15:
814
- suggestions.append("减少深夜编码:凌晨 0-6 点提交占比 {:.0f}%,长期熬夜影响代码质量".format(late_night / total * 100))
815
-
816
- # ============================================================
817
- # 6. Commit 质量
818
- # ============================================================
819
- if health['low_info_ratio'] > 15:
820
- suggestions.append("优化 Commit Message:{:.0f}% 的提交缺少描述,建议使用 Conventional Commits 规范".format(health['low_info_ratio']))
821
- elif health['low_info_ratio'] > 5:
822
- suggestions.append("完善提交描述:部分 commit 信息过于简略,建议写清楚改动原因")
823
- if health['low_info_ratio'] < 3:
824
- suggestions.append("Commit 质量高:{:.0f}% 的提交有详细描述,继续保持!".format(100 - health['low_info_ratio']))
825
-
826
- # ============================================================
827
- # 7. 项目聚焦
828
- # ============================================================
829
- focus_index = data.get('focus_index', 100)
830
- if focus_index < 60:
831
- suggestions.append("提高项目聚焦度:精力较分散,建议集中精力在 1-2 个核心项目上")
832
-
833
- # ============================================================
834
- # 8. 提交粒度
835
- # ============================================================
836
- if avg_daily < 1:
837
- suggestions.append("提高提交频率:日均仅 {:.1f} 次提交,建议小步快跑、勤提交".format(avg_daily))
838
- elif avg_daily > 8:
839
- suggestions.append("优化提交粒度:日均 {:.1f} 次提交偏多,考虑合并相关改动".format(avg_daily))
840
- elif avg_daily >= 2 and avg_daily <= 5:
841
- suggestions.append("提交频率适中:日均 {:.1f} 次提交,节奏良好".format(avg_daily))
842
-
843
- # ============================================================
844
- # 9. 代码质量
845
- # ============================================================
846
- if health['fix_ratio'] > 20:
847
- suggestions.append("提升代码质量:Bug 修复占比 {:.0f}%,建议加强测试和 Code Review".format(health['fix_ratio']))
848
- elif health['fix_ratio'] < 5 and total_commits > 50:
849
- suggestions.append("Bug 很少:Bug 修复仅占 {:.0f}%,代码质量不错".format(health['fix_ratio']))
850
- if health['refactor_ratio'] < 10 and health['feat_ratio'] > 50:
851
- suggestions.append("关注技术债:功能开发占比高但重构不足,建议定期安排重构时间")
852
- if health['refactor_ratio'] >= 10:
853
- suggestions.append("重构做得好:重构占比 {:.0f}%,代码质量持续改善".format(health['refactor_ratio']))
854
-
855
- # ============================================================
856
- # 10. AI 使用
857
- # ============================================================
858
- ai_ratio = ai.get('ai_commit_ratio', 0)
859
- if ai_ratio > 40:
860
- suggestions.append("善用 AI:AI 占比超过 40%,建议仔细审查 AI 生成的代码")
861
- elif ai_ratio > 0 and ai_ratio < 15:
862
- suggestions.append("探索 AI 工具:当前 AI 使用率 {:.0f}%,可尝试 Copilot/Cursor 提升效率".format(ai_ratio))
863
- if ai_ratio >= 20 and ai_ratio <= 40:
864
- suggestions.append("AI 使用适中:AI 占比 {:.0f}%,人机协作良好".format(ai_ratio))
865
-
866
- # AI 工具多样性
867
- ai_tools = ai.get('tools', {})
868
- if len(ai_tools) >= 3:
869
- suggestions.append("AI 工具多样化:使用了 {} 种 AI 工具,建议固定 1-2 种核心工具".format(len(ai_tools)))
870
-
871
- # ============================================================
872
- # 11. 提交时间规律
873
- # ============================================================
861
+ categories['schedule']['items'].append("减少深夜编码:凌晨 0-6 点提交占比 {:.0f}%,长期熬夜影响代码质量".format(late_night / total * 100))
874
862
  if peak_hours:
875
863
  if 22 in peak_hours or 23 in peak_hours:
876
- suggestions.append("调整工作节奏:深夜是你最活跃的时段,建议将核心工作移到白天")
877
- if 6 in peak_hours or 7 in peak_hours:
878
- suggestions.append("利用晨间高效期:早上 6-7 点是你最活跃的时段,适合处理复杂任务")
879
-
880
- # ============================================================
881
- # 12. 最活跃星期
882
- # ============================================================
883
- peak_weekdays = data.get('peak_weekdays', [])
884
- if peak_weekdays:
885
- weekend_days = [d for d in peak_weekdays if d in ['周六', '周日']]
886
- if len(weekend_days) >= 2:
887
- suggestions.append("调整工作节奏:最活跃的 3 天中有 {} 天是周末,建议以工作日为主".format(len(weekend_days)))
864
+ categories['schedule']['items'].append("调整工作节奏:深夜是你最活跃的时段,建议将核心工作移到白天")
888
865
 
889
- # ============================================================
890
- # 13. 工作强度
891
- # ============================================================
892
- if avg_daily > 5:
893
- suggestions.append("注意工作强度:日均 {:.1f} 次提交,注意劳逸结合避免倦怠".format(avg_daily))
866
+ # 正面反馈
867
+ if health['night_ratio'] < 15 and health['weekend_ratio'] < 15:
868
+ categories['schedule']['items'].append("工作生活平衡:夜间和周末提交都很少,作息健康")
894
869
 
895
870
  # ============================================================
896
- # 14. 项目数量
871
+ # 项目管理 (最多 6 条)
897
872
  # ============================================================
873
+ if habit_score['focus'] < 10:
874
+ categories['project']['items'].append("提升专注度:项目聚焦得分仅 {}/15,建议减少同时维护的项目数".format(habit_score['focus']))
875
+ focus_index = data.get('focus_index', 100)
876
+ if focus_index < 60:
877
+ categories['project']['items'].append("提高项目聚焦度:精力较分散,建议集中精力在 1-2 个核心项目上")
898
878
  if total_projects > 15:
899
- suggestions.append("精简项目数量:同时维护 {} 个项目,建议聚焦核心项目提升效率".format(total_projects))
900
- elif total_projects <= 3 and total_commits > 100:
901
- suggestions.append("项目聚焦度高:仅维护 {} 个项目,精力集中".format(total_projects))
902
-
903
- # ============================================================
904
- # 15. 活跃度
905
- # ============================================================
906
- if total_active_days > 0 and avg_daily > 0:
907
- active_rate = min(total_active_days / 365 * 100, 100)
908
- if active_rate < 30:
909
- suggestions.append("保持持续性:活跃天数 {} 天,建议养成每天提交的习惯".format(total_active_days))
910
- elif active_rate > 70:
911
- suggestions.append("活跃度高:{} 天有提交, coding 习惯良好".format(total_active_days))
912
-
913
- # ============================================================
914
- # 16. Commit 类型分布
915
- # ============================================================
916
- if total_commits > 0:
917
- feat_count = commit_types.get('feat', 0)
918
- test_count = commit_types.get('test', 0)
919
- docs_count = commit_types.get('docs', 0)
920
- refactor_count = commit_types.get('refactor', 0)
921
- fix_count = commit_types.get('fix', 0)
922
- other_count = commit_types.get('other', 0)
923
- chore_count = commit_types.get('chore', 0)
924
-
925
- # 测试提交比例
926
- if test_count / total_commits < 0.05 and total_commits > 50:
927
- suggestions.append("增加测试提交:测试相关提交仅占 {:.0f}%,建议为新功能编写测试".format(test_count / total_commits * 100))
928
- elif test_count / total_commits >= 0.15:
929
- suggestions.append("测试提交充足:测试占比 {:.0f}%,质量意识强".format(test_count / total_commits * 100))
930
-
931
- # 文档提交比例
932
- if docs_count / total_commits < 0.05 and total_commits > 50:
933
- suggestions.append("增加文档提交:文档相关提交仅占 {:.0f}%,建议及时更新文档".format(docs_count / total_commits * 100))
934
- elif docs_count / total_commits >= 0.15:
935
- suggestions.append("文档提交充足:文档占比 {:.0f}%,文档维护良好".format(docs_count / total_commits * 100))
936
-
937
- # 重构提交比例
938
- if refactor_count / total_commits < 0.03 and feat_count / total_commits > 0.3:
939
- suggestions.append("定期重构:重构提交仅占 {:.0f}%,功能开发占比高,建议安排重构时间".format(refactor_count / total_commits * 100))
940
-
941
- # other 类型提交比例
942
- if other_count / total_commits > 0.25:
943
- suggestions.append("规范 Commit 类型:{:.0f}% 的提交被归为 other,建议使用 feat/fix/refactor 等标准类型".format(other_count / total_commits * 100))
944
-
945
- # chore 类型提交比例
946
- if chore_count / total_commits > 0.15:
947
- suggestions.append("自动化琐事:chore 类型提交占比 {:.0f}%,考虑用脚本或 CI 自动化".format(chore_count / total_commits * 100))
948
-
949
- # 功能开发占比
950
- if feat_count / total_commits > 0.4:
951
- suggestions.append("功能开发为主:feat 占比 {:.0f}%,注意平衡新功能与维护".format(feat_count / total_commits * 100))
952
-
953
- # Bug 修复占比
954
- if fix_count / total_commits > 0.2:
955
- suggestions.append("Bug 修复较多:fix 占比 {:.0f}%,建议加强测试预防".format(fix_count / total_commits * 100))
956
-
957
- # ============================================================
958
- # 17. 语言多样性
959
- # ============================================================
960
- projects = data.get('projects', [])
961
- if projects:
962
- lang_set = set()
963
- for p in projects:
964
- if isinstance(p, dict):
965
- lang = p.get('language', '')
966
- if lang and lang != 'Other':
967
- lang_set.add(lang)
968
- if len(lang_set) == 1:
969
- suggestions.append("拓展技术栈:当前只使用 {} 语言,建议学习其他语言拓宽视野".format(list(lang_set)[0]))
970
- elif len(lang_set) >= 5:
971
- suggestions.append("聚焦核心技术:同时使用 {} 种语言,建议深耕 1-2 种核心语言".format(len(lang_set)))
972
-
973
- # ============================================================
974
- # 18. 提交频率波动
975
- # ============================================================
976
- if hourly:
977
- max_hour = max(hourly)
978
- min_hour = min(hourly)
979
- if max_hour > 0 and min_hour / max_hour < 0.1:
980
- suggestions.append("平衡提交时间:提交集中在特定时段,建议分散到全天各时段")
981
-
982
- # ============================================================
983
- # 19. 提交连续性
984
- # ============================================================
985
- if total_active_days > 0 and total_commits > 0:
986
- commits_per_active_day = total_commits / total_active_days
987
- if commits_per_active_day > 10:
988
- suggestions.append("控制单日提交量:活跃日均提交 {:.0f} 次,建议拆分为更小的改动".format(commits_per_active_day))
989
-
990
- # ============================================================
991
- # 20. 提交间隔
992
- # ============================================================
993
- if total_active_days > 0 and total_commits > 0:
994
- avg_interval = 24 * total_active_days / total_commits
995
- if avg_interval < 1:
996
- suggestions.append("降低提交频率:平均 {:.0f} 分钟一次提交,考虑合并相关改动".format(avg_interval * 60))
997
-
998
- # ============================================================
999
- # 21. 工作节奏一致性
1000
- # ============================================================
1001
- if hourly:
1002
- total = sum(hourly)
1003
- if total > 0:
1004
- # 计算工作时间(9-18点)vs 非工作时间
1005
- work_hours = sum(hourly[9:18])
1006
- work_ratio = work_hours / total
1007
- if work_ratio > 0.6:
1008
- suggestions.append("工作节奏规律:{:.0f}% 的提交在工作时间,作息健康".format(work_ratio * 100))
1009
- elif work_ratio < 0.3:
1010
- suggestions.append("工作时间不规律:仅 {:.0f}% 的提交在工作时间,建议调整".format(work_ratio * 100))
1011
-
1012
- # ============================================================
1013
- # 22. 项目活跃度
1014
- # ============================================================
879
+ categories['project']['items'].append("精简项目数量:同时维护 {} 个项目,建议聚焦核心项目提升效率".format(total_projects))
1015
880
  if projects and total_commits > 0:
1016
881
  active_projects = sum(1 for p in projects if isinstance(p, dict) and p.get('commits', 0) > 10)
1017
882
  if active_projects <= 2 and total_projects > 5:
1018
- suggestions.append("项目活跃度不均:{} 个项目中仅 {} 个活跃,建议清理不活跃项目".format(total_projects, active_projects))
1019
-
1020
- # ============================================================
1021
- # 23. 提交消息长度
1022
- # ============================================================
1023
- if health['low_info_ratio'] < 5 and total_commits > 100:
1024
- suggestions.append("提交消息质量高:{:.0f}% 的提交有详细描述,代码可维护性强".format(100 - health['low_info_ratio']))
1025
-
1026
- # ============================================================
1027
- # 24. 代码变更规模
1028
- # ============================================================
1029
- if total_commits > 0 and total_active_days > 0:
1030
- avg_changes_per_commit = total_commits / total_active_days # 粗略估计
1031
- if avg_changes_per_commit > 5:
1032
- suggestions.append("单次提交较大:建议拆分为更小的提交,便于 Code Review")
1033
-
1034
- # ============================================================
1035
- # 25. 技术债务
1036
- # ============================================================
1037
- if health['refactor_ratio'] < 5 and health['feat_ratio'] > 40 and total_commits > 100:
1038
- suggestions.append("技术债积累:重构仅占 {:.0f}%,建议定期安排重构时间".format(health['refactor_ratio']))
1039
-
1040
- # ============================================================
1041
- # 26. 测试覆盖趋势
1042
- # ============================================================
1043
- if health['test_ratio'] >= 20:
1044
- suggestions.append("测试覆盖良好:测试文件占比 {:.0f}%,代码质量有保障".format(health['test_ratio']))
1045
-
1046
- # ============================================================
1047
- # 27. 文档覆盖趋势
1048
- # ============================================================
1049
- if health['doc_ratio'] >= 10:
1050
- suggestions.append("文档覆盖良好:文档变更占比 {:.0f}%,项目可维护性强".format(health['doc_ratio']))
1051
-
1052
- # ============================================================
1053
- # 28. AI 协作深度
1054
- # ============================================================
1055
- ai_influence = ai.get('ai_influence_score', 0)
1056
- if ai_influence > 70:
1057
- suggestions.append("AI 深度协作:AI 影响分 {},建议定期审查 AI 生成的代码".format(ai_influence))
1058
- elif ai_influence > 0 and ai_influence < 30:
1059
- suggestions.append("AI 使用较少:AI 影响分 {},可尝试更多 AI 工具提升效率".format(ai_influence))
1060
-
1061
- # ============================================================
1062
- # 29. 工作生活平衡
1063
- # ============================================================
1064
- if health['night_ratio'] < 15 and health['weekend_ratio'] < 15:
1065
- suggestions.append("工作生活平衡:夜间和周末提交都很少,作息健康")
1066
-
1067
- # ============================================================
1068
- # 30. 代码稳定性
1069
- # ============================================================
1070
- if health['fix_ratio'] < 5 and health['refactor_ratio'] < 5 and total_commits > 100:
1071
- suggestions.append("代码稳定:Bug 修复和重构都很少,代码质量稳定")
1072
-
1073
- # ============================================================
1074
- # 31. 提交频率波动(小时级别)
1075
- # ============================================================
1076
- if hourly and len(hourly) == 24:
1077
- # 计算变异系数
1078
- import statistics
1079
- mean_val = statistics.mean(hourly)
1080
- if mean_val > 0:
1081
- cv = statistics.stdev(hourly) / mean_val
1082
- if cv > 1.5:
1083
- suggestions.append("提交时间波动大:建议保持更稳定的工作节奏")
1084
-
1085
- # ============================================================
1086
- # 32. 项目集中度
1087
- # ============================================================
1088
- if projects and total_commits > 0:
1089
- # 计算 HHI 指数(赫芬达尔指数)
1090
- shares = [p.get('commits', 0) / total_commits for p in projects if isinstance(p, dict)]
1091
- hhi = sum(s ** 2 for s in shares)
1092
- if hhi < 0.1:
1093
- suggestions.append("项目分散:提交分布均匀,建议聚焦核心项目")
1094
- elif hhi > 0.5:
1095
- suggestions.append("项目集中:大部分提交集中在少数项目,精力分配合理")
1096
-
1097
- # ============================================================
1098
- # 33. 周末提交模式
1099
- # ============================================================
1100
- if weekly:
1101
- sat = weekly.get('周六', 0) if isinstance(weekly, dict) else (weekly[5] if len(weekly) > 5 else 0)
1102
- sun = weekly.get('周日', 0) if isinstance(weekly, dict) else (weekly[6] if len(weekly) > 6 else 0)
1103
- total_weekly = sum(weekly.values()) if isinstance(weekly, dict) else sum(weekly)
1104
- if total_weekly > 0:
1105
- sat_ratio = sat / total_weekly * 100
1106
- sun_ratio = sun / total_weekly * 100
1107
- if sat_ratio > 20 and sun_ratio < 5:
1108
- suggestions.append("周六活跃:周六提交占比 {:.0f}%,周日较少,节奏不错".format(sat_ratio))
1109
- elif sun_ratio > 20 and sat_ratio < 5:
1110
- suggestions.append("周日活跃:周日提交占比 {:.0f}%,建议利用周六提前完成".format(sun_ratio))
1111
-
1112
- # ============================================================
1113
- # 34. 提交分布集中度
1114
- # ============================================================
1115
- if hourly and len(hourly) == 24:
1116
- total = sum(hourly)
1117
- if total > 0:
1118
- top3_hours = sorted(range(24), key=lambda i: hourly[i], reverse=True)[:3]
1119
- top3_ratio = sum(hourly[h] for h in top3_hours) / total * 100
1120
- if top3_ratio > 50:
1121
- suggestions.append("提交高度集中:最活跃的 3 小时占 {:.0f}% 提交,建议更均匀分布".format(top3_ratio))
1122
- elif top3_ratio < 25:
1123
- suggestions.append("提交分布均匀:各时段提交较分散,时间管理良好")
1124
-
1125
- # ============================================================
1126
- # 35. 测试与功能平衡
1127
- # ============================================================
1128
- if total_commits > 0:
1129
- feat_count = commit_types.get('feat', 0)
1130
- test_count = commit_types.get('test', 0)
1131
- if feat_count > 0:
1132
- test_feat_ratio = test_count / feat_count
1133
- if test_feat_ratio < 0.3 and feat_count > 20:
1134
- suggestions.append("测试跟不上功能:每 {} 个功能提交才对应 1 个测试提交,建议提高测试比例".format(int(1 / test_feat_ratio) if test_feat_ratio > 0 else "∞"))
1135
- elif test_feat_ratio >= 0.8:
1136
- suggestions.append("测试与功能同步:测试/功能比 {:.1f},质量意识强".format(test_feat_ratio))
1137
-
1138
- # ============================================================
1139
- # 36. 提交历史跨度
1140
- # ============================================================
1141
- if total_active_days > 0:
1142
- if total_active_days > 300:
1143
- suggestions.append("开发持续性强:活跃 {} 天,长期坚持 coding".format(total_active_days))
1144
- elif total_active_days < 30 and total_commits > 50:
1145
- suggestions.append("开发集中在短期:仅 {} 天活跃但有 {} 次提交,建议保持持续性".format(total_active_days, total_commits))
1146
-
1147
- # ============================================================
1148
- # 37. 工作日偏好
1149
- # ============================================================
1150
- if weekly:
1151
- if isinstance(weekly, dict):
1152
- max_day = max(weekly, key=weekly.get)
1153
- min_day = min(weekly, key=weekly.get)
1154
- max_val = weekly[max_day]
1155
- min_val = weekly[min_day]
1156
- total_weekly = sum(weekly.values())
1157
- if total_weekly > 0 and max_val > 0:
1158
- day_ratio = max_val / total_weekly * 100
1159
- if day_ratio > 25:
1160
- suggestions.append("工作日偏好明显:{} 占 {:.0f}% 提交,是最活跃的一天".format(max_day, day_ratio))
1161
-
1162
- # ============================================================
1163
- # 38. 提交时间分布
1164
- # ============================================================
1165
- if hourly and len(hourly) == 24:
1166
- # 计算高峰时段
1167
- max_val = max(hourly)
1168
- peak_hours_list = [i for i, v in enumerate(hourly) if v > max_val * 0.8]
1169
- if len(peak_hours_list) <= 3:
1170
- suggestions.append("提交时间集中:高峰时段集中在 {} 点,建议分散到其他时段".format(', '.join([str(h) for h in peak_hours_list])))
1171
-
1172
- # ============================================================
1173
- # 39. 项目提交分布
1174
- # ============================================================
1175
- if projects and total_commits > 0:
1176
- # 计算项目提交分布的熵
1177
- import math
1178
- shares = [p.get('commits', 0) / total_commits for p in projects if isinstance(p, dict) and p.get('commits', 0) > 0]
1179
- entropy = -sum(s * math.log2(s) for s in shares if s > 0)
1180
- max_entropy = math.log2(len(shares)) if len(shares) > 1 else 1
1181
- if max_entropy > 0:
1182
- normalized_entropy = entropy / max_entropy
1183
- if normalized_entropy > 0.8:
1184
- suggestions.append("项目分布均匀:提交分散在多个项目,建议聚焦核心项目")
1185
- elif normalized_entropy < 0.3:
1186
- suggestions.append("项目分布集中:大部分提交集中在少数项目,精力分配合理")
1187
-
1188
- # ============================================================
1189
- # 40. 开发习惯总结
1190
- # ============================================================
1191
- if habit_score['total'] >= 80:
1192
- suggestions.append("开发习惯优秀:总分 {} 分,继续保持!".format(habit_score['total']))
1193
- elif habit_score['total'] >= 60:
1194
- suggestions.append("开发习惯良好:总分 {} 分,还有提升空间".format(habit_score['total']))
1195
-
1196
- # ============================================================
1197
- # 41. 项目类型多样性
1198
- # ============================================================
1199
- if projects and len(projects) > 0:
1200
- # 计算项目提交分布的基尼系数
1201
- commits_list = sorted([p.get('commits', 0) for p in projects if isinstance(p, dict)])
1202
- n = len(commits_list)
1203
- if n > 0 and sum(commits_list) > 0:
1204
- cumulative = 0
1205
- gini_sum = 0
1206
- for i, c in enumerate(commits_list):
1207
- cumulative += c
1208
- gini_sum += (2 * (i + 1) - n - 1) * c
1209
- gini = gini_sum / (n * sum(commits_list))
1210
- if gini > 0.6:
1211
- suggestions.append("项目分布不均:基尼系数 {:.2f},建议更均匀地分配精力".format(gini))
1212
- elif gini < 0.2:
1213
- suggestions.append("项目分布均匀:基尼系数 {:.2f},精力分配合理".format(gini))
1214
-
1215
- # ============================================================
1216
- # 42. 提交频率稳定性
1217
- # ============================================================
1218
- if hourly and len(hourly) == 24:
1219
- # 计算提交时间的峰度
1220
- mean_val = statistics.mean(hourly)
1221
- if mean_val > 0 and len(hourly) > 3:
1222
- variance = statistics.variance(hourly)
1223
- if variance > 0:
1224
- # 简化的峰度计算
1225
- skewness = sum((x - mean_val) ** 3 for x in hourly) / (len(hourly) * (variance ** 1.5))
1226
- if abs(skewness) > 1:
1227
- suggestions.append("提交时间分布偏斜:建议保持更规律的工作节奏")
1228
-
1229
- # ============================================================
1230
- # 43. 项目活跃周期
1231
- # ============================================================
1232
- if projects and total_commits > 0:
1233
- # 计算项目活跃周期(最近一次提交的时间)
1234
- recent_projects = 0
1235
- for p in projects:
1236
- if isinstance(p, dict):
1237
- last_commit = p.get('last_commit', '')
1238
- if last_commit and '2026' in last_commit:
1239
- recent_projects += 1
1240
- if recent_projects <= 3 and total_projects > 10:
1241
- suggestions.append("活跃项目少:{} 个项目中仅 {} 个最近有提交,建议清理不活跃项目".format(total_projects, recent_projects))
1242
-
1243
- # ============================================================
1244
- # 44. 提交频率建议
1245
- # ============================================================
1246
- if avg_daily > 0:
1247
- if avg_daily < 0.5:
1248
- suggestions.append("提交频率低:日均 {:.1f} 次提交,建议更频繁地提交代码".format(avg_daily))
1249
- elif avg_daily > 5:
1250
- suggestions.append("提交频率高:日均 {:.1f} 次提交,注意保持代码质量".format(avg_daily))
1251
-
1252
- # ============================================================
1253
- # 45. 开发效率
1254
- # ============================================================
1255
- if total_commits > 0 and total_active_days > 0:
1256
- efficiency = total_commits / total_active_days
1257
- if efficiency > 5:
1258
- suggestions.append("开发效率高:每次活跃日平均 {:.0f} 次提交,产出稳定".format(efficiency))
1259
- elif efficiency < 2:
1260
- suggestions.append("开发效率待提升:每次活跃日平均 {:.0f} 次提交,建议提高效率".format(efficiency))
1261
-
1262
- # ============================================================
1263
- # 46. 功能 vs 维护平衡
1264
- # ============================================================
883
+ categories['project']['items'].append("项目活跃度不均:{} 个项目中仅 {} 个活跃,建议清理不活跃项目".format(total_projects, active_projects))
884
+ if health['refactor_ratio'] < 10 and health['feat_ratio'] > 50:
885
+ categories['project']['items'].append("关注技术债:功能开发占比高但重构不足,建议定期安排重构时间")
1265
886
  if total_commits > 0:
1266
887
  feat_c = commit_types.get('feat', 0)
1267
888
  fix_c = commit_types.get('fix', 0)
@@ -1270,174 +891,55 @@ def _build_suggestions_html(habit_score, health, data):
1270
891
  if feat_c > 0 and maintenance > 0:
1271
892
  ratio = feat_c / maintenance
1272
893
  if ratio > 5:
1273
- suggestions.append("功能远超维护:功能/维护比 {:.1f}:1,注意技术债积累".format(ratio))
1274
- elif ratio < 1:
1275
- suggestions.append("维护为主:维护提交多于功能,代码在持续改善")
1276
-
1277
- # ============================================================
1278
- # 47. 项目状态分布
1279
- # ============================================================
1280
- if projects and total_projects > 3:
1281
- active = sum(1 for p in projects if isinstance(p, dict) and p.get('commits', 0) > 10)
1282
- inactive = total_projects - active
1283
- if inactive > total_projects * 0.6:
1284
- suggestions.append("不活跃项目多:{} 个项目中 {} 个提交不足 10 次,建议归档".format(total_projects, inactive))
1285
-
1286
- # ============================================================
1287
- # 48. 提交密度变化
1288
- # ============================================================
1289
- if hourly and len(hourly) == 24:
1290
- total = sum(hourly)
1291
- if total > 0:
1292
- # 计算高密度时段(>平均值2倍)
1293
- avg_per_hour = total / 24
1294
- high_density_hours = [h for h in range(24) if hourly[h] > avg_per_hour * 2]
1295
- if len(high_density_hours) <= 2 and len(high_density_hours) > 0:
1296
- suggestions.append("编码时段集中:高峰仅在 {} 点,建议适当分散".format(', '.join(str(h) for h in high_density_hours)))
1297
-
1298
- # ============================================================
1299
- # 49. 周内提交均匀度
1300
- # ============================================================
1301
- if weekly and isinstance(weekly, dict) and len(weekly) == 7:
1302
- values = list(weekly.values())
1303
- total = sum(values)
1304
- if total > 0:
1305
- avg_per_day = total / 7
1306
- variance = sum((v - avg_per_day) ** 2 for v in values) / 7
1307
- if avg_per_day > 0:
1308
- cv = (variance ** 0.5) / avg_per_day
1309
- if cv > 0.8:
1310
- suggestions.append("周内提交不均:各天差异大,建议保持更稳定的周节奏")
1311
-
1312
- # ============================================================
1313
- # 50. 功能开发占比
1314
- # ============================================================
1315
- if total_commits > 0:
1316
- feat_c = commit_types.get('feat', 0)
1317
- feat_pct = feat_c / total_commits * 100
1318
- if feat_pct > 60:
1319
- suggestions.append("功能开发为主:feat 占 {:.0f}%,建议平衡新功能与质量保障".format(feat_pct))
1320
- elif feat_pct < 10 and total_commits > 50:
1321
- suggestions.append("功能开发少:feat 仅占 {:.0f}%,开发以维护为主".format(feat_pct))
1322
-
1323
- # ============================================================
1324
- # 51. Chore 类型分析
1325
- # ============================================================
1326
- if total_commits > 0:
1327
- chore_c = commit_types.get('chore', 0)
1328
- chore_pct = chore_c / total_commits * 100
1329
- if chore_pct > 20:
1330
- suggestions.append("琐事较多:chore 占 {:.0f}%,建议用自动化脚本减少重复工作".format(chore_pct))
894
+ categories['project']['items'].append("功能远超维护:功能/维护比 {:.1f}:1,注意技术债积累".format(ratio))
1331
895
 
1332
- # ============================================================
1333
- # 52. 提交历史长度
1334
- # ============================================================
1335
- if total_active_days > 0:
1336
- if total_active_days > 200:
1337
- suggestions.append("长期开发者:活跃超过 {} 天,积累了丰富的开发经验".format(total_active_days))
1338
-
1339
- # ============================================================
1340
- # 53. 最热门项目贡献
1341
- # ============================================================
1342
- if projects and total_commits > 0 and len(projects) > 1:
1343
- max_commits = max(p.get('commits', 0) for p in projects if isinstance(p, dict))
1344
- top_ratio = max_commits / total_commits * 100
1345
- if top_ratio > 60:
1346
- suggestions.append("单项目主导:最热门项目占 {:.0f}% 提交,精力高度集中".format(top_ratio))
1347
- elif top_ratio < 20:
1348
- suggestions.append("精力分散:最热门项目仅占 {:.0f}% 提交,建议聚焦核心项目".format(top_ratio))
896
+ # 正面反馈
897
+ if total_projects <= 3 and total_commits > 100:
898
+ categories['project']['items'].append("项目聚焦度高:仅维护 {} 个项目,精力集中".format(total_projects))
899
+ if health['refactor_ratio'] >= 10:
900
+ categories['project']['items'].append("重构做得好:重构占比 {:.0f}%,代码质量持续改善".format(health['refactor_ratio']))
1349
901
 
1350
902
  # ============================================================
1351
- # 54. 文档与代码比例
903
+ # AI 协作 (最多 4 条)
1352
904
  # ============================================================
1353
- if total_commits > 0:
1354
- docs_c = commit_types.get('docs', 0)
1355
- code_c = total_commits - docs_c
1356
- if code_c > 0:
1357
- doc_code_ratio = docs_c / code_c
1358
- if doc_code_ratio > 0.3:
1359
- suggestions.append("文档投入高:文档/代码比 {:.1f},文档维护良好".format(doc_code_ratio))
1360
- elif doc_code_ratio < 0.05 and total_commits > 50:
1361
- suggestions.append("文档投入不足:文档/代码比 {:.2f},建议增加文档更新".format(doc_code_ratio))
905
+ ai_ratio = ai.get('ai_commit_ratio', 0)
906
+ if ai_ratio > 40:
907
+ categories['ai']['items'].append("善用 AI:AI 占比超过 40%,建议仔细审查 AI 生成的代码")
908
+ elif ai_ratio > 0 and ai_ratio < 15:
909
+ categories['ai']['items'].append("探索 AI 工具:当前 AI 使用率 {:.0f}%,可尝试 Copilot/Cursor 提升效率".format(ai_ratio))
910
+ ai_influence = ai.get('ai_influence_score', 0)
911
+ if ai_influence > 70:
912
+ categories['ai']['items'].append("AI 深度协作:AI 影响分 {},建议定期审查 AI 生成的代码".format(ai_influence))
913
+ ai_tools = ai.get('tools', {})
914
+ if len(ai_tools) >= 3:
915
+ categories['ai']['items'].append("AI 工具多样化:使用了 {} 种 AI 工具,建议固定 1-2 种核心工具".format(len(ai_tools)))
1362
916
 
1363
- # ============================================================
1364
- # 55. 早起 vs 夜猫子
1365
- # ============================================================
1366
- if hourly and len(hourly) == 24:
1367
- total = sum(hourly)
1368
- if total > 0:
1369
- morning = sum(hourly[5:9]) # 5-8点
1370
- night = sum(hourly[22:24]) + sum(hourly[0:2]) # 22-1点
1371
- if morning > night * 2 and morning > 0:
1372
- suggestions.append("早起型开发者:早晨提交多于深夜,作息健康")
1373
- elif night > morning * 2 and night > 0:
1374
- suggestions.append("夜猫子型开发者:深夜提交多于早晨,建议调整作息")
917
+ # 正面反馈
918
+ if ai_ratio >= 20 and ai_ratio <= 40:
919
+ categories['ai']['items'].append("AI 使用适中:AI 占比 {:.0f}%,人机协作良好".format(ai_ratio))
1375
920
 
1376
921
  # ============================================================
1377
- # 56. 提交稳定性
922
+ # 构建 HTML(带 tab 切换)
1378
923
  # ============================================================
1379
- if total_active_days > 10 and total_commits > 0:
1380
- commits_per_day = total_commits / total_active_days
1381
- if commits_per_day >= 2 and commits_per_day <= 6:
1382
- suggestions.append("提交节奏稳定:活跃日均 {:.1f} 次提交,节奏适中".format(commits_per_day))
924
+ # 过滤空分类
925
+ active_cats = [(k, v) for k, v in categories.items() if v['items']]
1383
926
 
1384
- # ============================================================
1385
- # 57. 多语言项目管理
1386
- # ============================================================
1387
- if projects:
1388
- lang_project_count = {}
1389
- for p in projects:
1390
- if isinstance(p, dict):
1391
- lang = p.get('language', '')
1392
- if lang and lang != 'Other':
1393
- lang_project_count[lang] = lang_project_count.get(lang, 0) + 1
1394
- if len(lang_project_count) >= 3:
1395
- top_lang = max(lang_project_count, key=lang_project_count.get)
1396
- suggestions.append("多语言开发者:使用 {} 种语言,{} 项目最多".format(len(lang_project_count), top_lang))
927
+ if not active_cats:
928
+ return '<div class="sug-item"><div class="sug-text">继续保持良好的开发习惯!各项指标都很健康</div></div>'
1397
929
 
1398
- # ============================================================
1399
- # 58. 其他类型提交分析
1400
- # ============================================================
1401
- if total_commits > 0:
1402
- other_c = commit_types.get('other', 0)
1403
- other_pct = other_c / total_commits * 100
1404
- if other_pct > 30:
1405
- suggestions.append("提交分类不清:{:.0f}% 归为 other,建议使用标准 commit 类型".format(other_pct))
1406
- elif other_pct < 10 and total_commits > 50:
1407
- suggestions.append("提交分类规范:other 仅占 {:.0f}%,类型使用清晰".format(other_pct))
930
+ html = '<div class="sug-tabs">'
931
+ for i, (cat_key, cat) in enumerate(active_cats):
932
+ active_class = ' active' if i == 0 else ''
933
+ html += f'<button class="sug-tab{active_class}" data-cat="{cat_key}">{cat["icon"]} {cat["name"]} ({len(cat["items"])})</button>'
934
+ html += '</div>'
1408
935
 
1409
- # ============================================================
1410
- # 59. 功能开发深度
1411
- # ============================================================
1412
- if total_commits > 0:
1413
- feat_c = commit_types.get('feat', 0)
1414
- fix_c = commit_types.get('fix', 0)
1415
- if feat_c > 0 and fix_c > 0:
1416
- feat_fix_ratio = feat_c / fix_c
1417
- if feat_fix_ratio > 4:
1418
- suggestions.append("功能驱动:功能/修复比 {:.1f}:1,开发节奏快".format(feat_fix_ratio))
1419
- elif feat_fix_ratio < 1:
1420
- suggestions.append("修复驱动:修复多于新功能,建议分析根本原因")
936
+ for i, (cat_key, cat) in enumerate(active_cats):
937
+ active_class = ' active' if i == 0 else ''
938
+ html += f'<div class="sug-panel{active_class}" data-cat="{cat_key}">'
939
+ for j, item in enumerate(cat['items'], 1):
940
+ html += f'<div class="sug-item"><div class="sug-num">{j}</div><div class="sug-text">{item}</div></div>'
941
+ html += '</div>'
1421
942
 
1422
- # ============================================================
1423
- # 60. 项目维护活跃度
1424
- # ============================================================
1425
- if projects and total_commits > 0:
1426
- well_maintained = sum(1 for p in projects if isinstance(p, dict) and p.get('commits', 0) > 30)
1427
- if well_maintained >= 3:
1428
- suggestions.append("多项目深耕:{} 个项目提交超过 30 次,项目管理能力强".format(well_maintained))
1429
-
1430
- # === 默认 ===
1431
- if not suggestions:
1432
- suggestions.append("继续保持良好的开发习惯!各项指标都很健康")
1433
-
1434
- html = ""
1435
- for i, s in enumerate(suggestions, 1):
1436
- html += f'''
1437
- <div class="sug-item">
1438
- <div class="sug-num">{i}</div>
1439
- <div class="sug-text">{s}</div>
1440
- </div>'''
1441
943
  return html
1442
944
 
1443
945
 
@@ -1993,10 +1495,10 @@ def _build_js(all_commits_json, project_names_json, project_meta_json,
1993
1495
  // 更新 Habit Score
1994
1496
  // ============================================================
1995
1497
  function updateHabitScore(agg) {{
1996
- const granularity = Math.round(Math.min(30, agg.avgPerDay / 4.5 * 30));
1498
+ const granularity = Math.round(Math.min(40, agg.avgPerDay / 4.5 * 40));
1997
1499
  const schedule = Math.round(Math.min(20, Math.max(0, (1 - agg.nightRatio / 0.4)) * 20));
1998
1500
  const focusScore = Math.round(Math.min(15, agg.focusIndex / 0.7 * 15));
1999
- const testAwareness = Math.round(Math.min(20, agg.testRatio / 0.15 * 20));
1501
+ const testAwareness = Math.round(Math.min(10, agg.testRatio / 0.15 * 10));
2000
1502
  const docAwareness = Math.round(Math.min(15, agg.docRatio / 0.10 * 15));
2001
1503
  const totalScore = granularity + testAwareness + docAwareness + schedule + focusScore;
2002
1504
  agg.habitScore = {{totalScore, granularity, testAwareness, docAwareness, schedule, focusScore}};
@@ -2007,8 +1509,8 @@ def _build_js(all_commits_json, project_names_json, project_meta_json,
2007
1509
  document.getElementById('scoreLabel').textContent = `/ 100 · ${{getScoreLabel(totalScore)}}`;
2008
1510
 
2009
1511
  const scoreDims = [
2010
- ['提交粒度', granularity, 30],
2011
- ['测试意识', testAwareness, 20],
1512
+ ['提交粒度', granularity, 40],
1513
+ ['测试意识', testAwareness, 10],
2012
1514
  ['文档意识', docAwareness, 15],
2013
1515
  ['作息规律', schedule, 20],
2014
1516
  ['项目聚焦', focusScore, 15],
@@ -2336,7 +1838,15 @@ def _build_js(all_commits_json, project_names_json, project_meta_json,
2336
1838
  // 更新建议
2337
1839
  // ============================================================
2338
1840
  function updateSuggestions(agg) {{
2339
- const suggestions = [];
1841
+ const categories = {{
1842
+ 'commit': {{name: '提交习惯', icon: '📝', items: []}},
1843
+ 'test': {{name: '测试质量', icon: '✅', items: []}},
1844
+ 'doc': {{name: '文档维护', icon: '📚', items: []}},
1845
+ 'schedule': {{name: '作息健康', icon: '⏰', items: []}},
1846
+ 'project': {{name: '项目管理', icon: '🎯', items: []}},
1847
+ 'ai': {{name: 'AI 协作', icon: '🤖', items: []}},
1848
+ }};
1849
+
2340
1850
  const avgDaily = agg.avgPerDay || 0;
2341
1851
  const aiRatio = agg.aiCommitRatio || 0;
2342
1852
  const hourly = agg.hourly || [];
@@ -2344,398 +1854,84 @@ def _build_js(all_commits_json, project_names_json, project_meta_json,
2344
1854
  const weekdayNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
2345
1855
 
2346
1856
  // ============================================================
2347
- // 1. 习惯分数维度
2348
- // ============================================================
2349
- if (agg.habitScore.schedule < 10) suggestions.push(`改善作息规律:作息得分仅 ${{agg.habitScore.schedule}}/20,夜间和周末提交较多`);
2350
- if (agg.habitScore.focus < 10) suggestions.push(`提升专注度:项目聚焦得分仅 ${{agg.habitScore.focus}}/15,建议减少同时维护的项目数`);
2351
- if (agg.habitScore.granularity < 15) suggestions.push(`优化提交粒度:粒度得分仅 ${{agg.habitScore.granularity}}/30,建议保持稳定的提交频率`);
2352
-
2353
- // ============================================================
2354
- // 2. 测试相关
2355
- // ============================================================
2356
- if (agg.habitScore.testAwareness < 10) suggestions.push('提高测试意识:尝试为每个新功能编写测试用例,目标覆盖率 10%+');
2357
- else if (agg.testRatio < 0.08) suggestions.push(`增加测试投入:当前测试文件占比仅 ${{(agg.testRatio*100).toFixed(0)}}%,建议补充单元测试`);
2358
- if (agg.testRatio >= 0.30) suggestions.push(`测试做得好:测试文件占比 ${{(agg.testRatio*100).toFixed(0)}}%,继续保持!`);
2359
-
2360
- // ============================================================
2361
- // 3. 文档相关
2362
- // ============================================================
2363
- if (agg.habitScore.docAwareness < 10) suggestions.push('增加文档投入:定期更新 README 和 API 文档');
2364
- else if (agg.docRatio < 0.05) suggestions.push(`完善文档:文档变更占比 ${{(agg.docRatio*100).toFixed(0)}}%,重要功能应有文档说明`);
2365
- if (agg.docRatio >= 0.15) suggestions.push(`文档做得好:文档变更占比 ${{(agg.docRatio*100).toFixed(0)}}%,继续保持!`);
2366
-
2367
- // ============================================================
2368
- // 4. 作息相关
2369
- // ============================================================
2370
- if (agg.nightRatio > 0.25) suggestions.push(`注意作息健康:夜间提交占比 ${{(agg.nightRatio*100).toFixed(0)}}%,建议调整为白天工作`);
2371
- if (agg.weekendRatio > 0.25) suggestions.push(`平衡工作生活:周末提交占比 ${{(agg.weekendRatio*100).toFixed(0)}}%,注意适当休息`);
2372
-
2373
- // ============================================================
2374
- // 5. 深夜提交
2375
- // ============================================================
2376
- if (hourly.length === 24) {{
2377
- const lateNight = hourly.slice(0, 6).reduce((a, b) => a + b, 0);
2378
- const total = hourly.reduce((a, b) => a + b, 0);
2379
- if (total > 0 && lateNight / total > 0.15) {{
2380
- suggestions.push(`减少深夜编码:凌晨 0-6 点提交占比 ${{(lateNight/total*100).toFixed(0)}}%,长期熬夜影响代码质量`);
2381
- }}
2382
- }}
2383
-
2384
- // ============================================================
2385
- // 6. Commit 质量
2386
- // ============================================================
2387
- if (agg.lowInfoRatio > 0.15) suggestions.push(`优化 Commit Message:${{(agg.lowInfoRatio*100).toFixed(0)}}% 的提交缺少描述,建议使用 Conventional Commits 规范`);
2388
- else if (agg.lowInfoRatio > 0.05) suggestions.push('完善提交描述:部分 commit 信息过于简略,建议写清楚改动原因');
2389
- if (agg.lowInfoRatio < 0.03) suggestions.push(`Commit 质量高:${{((1-agg.lowInfoRatio)*100).toFixed(0)}}% 的提交有详细描述,继续保持!`);
2390
-
2391
- // ============================================================
2392
- // 7. 项目聚焦
1857
+ // 提交习惯 (最多 6 条)
2393
1858
  // ============================================================
2394
- if (agg.focusIndex < 0.60) suggestions.push('提高项目聚焦度:精力较分散,建议集中精力在 1-2 个核心项目上');
2395
-
2396
- // ============================================================
2397
- // 8. 提交粒度
2398
- // ============================================================
2399
- if (avgDaily < 1) suggestions.push(`提高提交频率:日均仅 ${{avgDaily.toFixed(1)}} 次提交,建议小步快跑、勤提交`);
2400
- else if (avgDaily > 8) suggestions.push(`优化提交粒度:日均 ${{avgDaily.toFixed(1)}} 次提交偏多,考虑合并相关改动`);
2401
- else if (avgDaily >= 2 && avgDaily <= 5) suggestions.push(`提交频率适中:日均 ${{avgDaily.toFixed(1)}} 次提交,节奏良好`);
2402
-
2403
- // ============================================================
2404
- // 9. 代码质量
2405
- // ============================================================
2406
- if (agg.fixRatio > 0.20) suggestions.push(`提升代码质量:Bug 修复占比 ${{(agg.fixRatio*100).toFixed(0)}}%,建议加强测试和 Code Review`);
2407
- else if (agg.fixRatio < 0.05 && agg.total > 50) suggestions.push(`Bug 很少:Bug 修复仅占 ${{(agg.fixRatio*100).toFixed(0)}}%,代码质量不错`);
2408
- if (agg.refactorRatio < 0.10 && agg.featRatio > 0.50) suggestions.push('关注技术债:功能开发占比高但重构不足,建议定期安排重构时间');
2409
- if (agg.refactorRatio >= 0.10) suggestions.push(`重构做得好:重构占比 ${{(agg.refactorRatio*100).toFixed(0)}}%,代码质量持续改善`);
2410
-
2411
- // ============================================================
2412
- // 10. AI 使用
2413
- // ============================================================
2414
- if (aiRatio > 0.40) suggestions.push('善用 AI:AI 占比超过 40%,建议仔细审查 AI 生成的代码');
2415
- else if (aiRatio > 0 && aiRatio < 0.15) suggestions.push(`探索 AI 工具:当前 AI 使用率 ${{(aiRatio*100).toFixed(0)}}%,可尝试 Copilot/Cursor 提升效率`);
2416
- if (aiRatio >= 0.20 && aiRatio <= 0.40) suggestions.push(`AI 使用适中:AI 占比 ${{(aiRatio*100).toFixed(0)}}%,人机协作良好`);
2417
-
2418
- // ============================================================
2419
- // 11. 最活跃星期
2420
- // ============================================================
2421
- const peakWeekdays = agg.weekly.map((v, i) => ({{v, i}})).sort((a, b) => b.v - a.v).slice(0, 3).map(x => weekdayNames[x.i]);
2422
- const weekendDays = peakWeekdays.filter(d => d === '周六' || d === '周日');
2423
- if (weekendDays.length >= 2) suggestions.push(`调整工作节奏:最活跃的 3 天中有 ${{weekendDays.length}} 天是周末,建议以工作日为主`);
2424
-
2425
- // ============================================================
2426
- // 12. 工作强度
2427
- // ============================================================
2428
- if (avgDaily > 5) suggestions.push(`注意工作强度:日均 ${{avgDaily.toFixed(1)}} 次提交,注意劳逸结合避免倦怠`);
2429
-
2430
- // ============================================================
2431
- // 13. 项目数量
2432
- // ============================================================
2433
- if (totalProjects > 15) suggestions.push(`精简项目数量:同时维护 ${{totalProjects}} 个项目,建议聚焦核心项目提升效率`);
2434
- else if (totalProjects <= 3 && agg.total > 100) suggestions.push(`项目聚焦度高:仅维护 ${{totalProjects}} 个项目,精力集中`);
2435
-
2436
- // ============================================================
2437
- // 14. 活跃度
2438
- // ============================================================
2439
- if (agg.activeDays > 0 && avgDaily > 0) {{
2440
- const activeRate = Math.min(agg.activeDays / 365 * 100, 100);
2441
- if (activeRate < 30) suggestions.push(`保持持续性:活跃天数 ${{agg.activeDays}} 天,建议养成每天提交的习惯`);
2442
- else if (activeRate > 70) suggestions.push(`活跃度高:${{agg.activeDays}} 天有提交, coding 习惯良好`);
2443
- }}
2444
-
2445
- // ============================================================
2446
- // 15. Commit 类型分布
2447
- // ============================================================
2448
- if (agg.total > 50) {{
2449
- const testRatio = (agg.types.test || 0) / agg.total;
2450
- const docsRatio = (agg.types.docs || 0) / agg.total;
2451
- const refactorRatio = (agg.types.refactor || 0) / agg.total;
1859
+ if (agg.habitScore.granularity < 15) categories.commit.items.push(`优化提交粒度:粒度得分仅 ${{agg.habitScore.granularity}}/40,建议保持稳定的提交频率`);
1860
+ if (avgDaily < 1) categories.commit.items.push(`提高提交频率:日均仅 ${{avgDaily.toFixed(1)}} 次提交,建议小步快跑、勤提交`);
1861
+ else if (avgDaily > 8) categories.commit.items.push(`优化提交粒度:日均 ${{avgDaily.toFixed(1)}} 次提交偏多,考虑合并相关改动`);
1862
+ if (agg.lowInfoRatio > 0.15) categories.commit.items.push(`优化 Commit Message:${{(agg.lowInfoRatio*100).toFixed(0)}}% 的提交缺少描述,建议使用 Conventional Commits 规范`);
1863
+ else if (agg.lowInfoRatio > 0.05) categories.commit.items.push('完善提交描述:部分 commit 信息过于简略,建议写清楚改动原因');
1864
+ if (agg.total > 0) {{
2452
1865
  const otherRatio = (agg.types.other || 0) / agg.total;
2453
- const choreRatio = (agg.types.chore || 0) / agg.total;
2454
- const featRatio = (agg.types.feat || 0) / agg.total;
2455
- const fixRatio = (agg.types.fix || 0) / agg.total;
2456
-
2457
- if (testRatio < 0.05) suggestions.push(`增加测试提交:测试相关提交仅占 ${{(testRatio*100).toFixed(0)}}%,建议为新功能编写测试`);
2458
- else if (testRatio >= 0.15) suggestions.push(`测试提交充足:测试占比 ${{(testRatio*100).toFixed(0)}}%,质量意识强`);
2459
- if (docsRatio < 0.05) suggestions.push(`增加文档提交:文档相关提交仅占 ${{(docsRatio*100).toFixed(0)}}%,建议及时更新文档`);
2460
- else if (docsRatio >= 0.15) suggestions.push(`文档提交充足:文档占比 ${{(docsRatio*100).toFixed(0)}}%,文档维护良好`);
2461
- if (refactorRatio < 0.03 && agg.featRatio > 0.30) suggestions.push(`定期重构:重构提交仅占 ${{(refactorRatio*100).toFixed(0)}}%,功能开发占比高,建议安排重构时间`);
2462
- if (otherRatio > 0.25) suggestions.push(`规范 Commit 类型:${{(otherRatio*100).toFixed(0)}}% 的提交被归为 other,建议使用 feat/fix/refactor 等标准类型`);
2463
- if (choreRatio > 0.15) suggestions.push(`自动化琐事:chore 类型提交占比 ${{(choreRatio*100).toFixed(0)}}%,考虑用脚本或 CI 自动化`);
2464
- if (featRatio > 0.40) suggestions.push(`功能开发为主:feat 占比 ${{(featRatio*100).toFixed(0)}}%,注意平衡新功能与维护`);
2465
- if (fixRatio > 0.20) suggestions.push(`Bug 修复较多:fix 占比 ${{(fixRatio*100).toFixed(0)}}%,建议加强测试预防`);
2466
- }}
2467
-
2468
- // ============================================================
2469
- // 16. 语言多样性
2470
- // ============================================================
2471
- const langSet = new Set(Object.values(agg.languageCounts).map((_, i) => Object.keys(agg.languageCounts)[i]).filter(l => l && l !== 'Other'));
2472
- if (langSet.size === 1) suggestions.push(`拓展技术栈:当前只使用 ${{[...langSet][0]}} 语言,建议学习其他语言拓宽视野`);
2473
- else if (langSet.size >= 5) suggestions.push(`聚焦核心技术:同时使用 ${{langSet.size}} 种语言,建议深耕 1-2 种核心语言`);
2474
-
2475
- // ============================================================
2476
- // 17. 提交频率波动
2477
- // ============================================================
2478
- if (hourly.length === 24) {{
2479
- const maxHour = Math.max(...hourly);
2480
- const minHour = Math.min(...hourly);
2481
- if (maxHour > 0 && minHour / maxHour < 0.1) suggestions.push('平衡提交时间:提交集中在特定时段,建议分散到全天各时段');
1866
+ if (otherRatio > 0.25) categories.commit.items.push(`提交分类不清:${{(otherRatio*100).toFixed(0)}}% 归为 other,建议使用 feat/fix/refactor 等标准类型`);
2482
1867
  }}
2483
-
2484
- // ============================================================
2485
- // 18. 提交连续性
2486
- // ============================================================
2487
1868
  if (agg.activeDays > 0 && agg.total > 0) {{
2488
1869
  const commitsPerActiveDay = agg.total / agg.activeDays;
2489
- if (commitsPerActiveDay > 10) suggestions.push(`控制单日提交量:活跃日均提交 ${{commitsPerActiveDay.toFixed(0)}} 次,建议拆分为更小的改动`);
2490
- }}
2491
-
2492
- // ============================================================
2493
- // 19. 提交间隔
2494
- // ============================================================
2495
- if (agg.activeDays > 0 && agg.total > 0) {{
2496
- const avgInterval = 24 * agg.activeDays / agg.total;
2497
- if (avgInterval < 1) suggestions.push(`降低提交频率:平均 ${{(avgInterval * 60).toFixed(0)}} 分钟一次提交,考虑合并相关改动`);
1870
+ if (commitsPerActiveDay > 10) categories.commit.items.push(`控制单日提交量:活跃日均提交 ${{commitsPerActiveDay.toFixed(0)}} 次,建议拆分为更小的改动`);
2498
1871
  }}
2499
1872
 
2500
- // ============================================================
2501
- // 20. 工作节奏一致性
2502
- // ============================================================
2503
- if (hourly.length === 24) {{
2504
- const total = hourly.reduce((a, b) => a + b, 0);
2505
- if (total > 0) {{
2506
- const workHours = hourly.slice(9, 18).reduce((a, b) => a + b, 0);
2507
- const workRatio = workHours / total;
2508
- if (workRatio > 0.6) suggestions.push(`工作节奏规律:${{(workRatio*100).toFixed(0)}}% 的提交在工作时间,作息健康`);
2509
- else if (workRatio < 0.3) suggestions.push(`工作时间不规律:仅 ${{(workRatio*100).toFixed(0)}}% 的提交在工作时间,建议调整`);
2510
- }}
2511
- }}
1873
+ // 正面反馈
1874
+ if (agg.lowInfoRatio < 0.03) categories.commit.items.push(`Commit 质量高:${{((1-agg.lowInfoRatio)*100).toFixed(0)}}% 的提交有详细描述,继续保持!`);
1875
+ if (avgDaily >= 2 && avgDaily <= 5) categories.commit.items.push(`提交频率适中:日均 ${{avgDaily.toFixed(1)}} 次提交,节奏良好`);
2512
1876
 
2513
1877
  // ============================================================
2514
- // 21. 项目活跃度
1878
+ // 测试质量 (最多 5 条)
2515
1879
  // ============================================================
2516
- if (totalProjects > 5 && agg.total > 0) {{
2517
- const activeProjects = Object.values(agg.projectCounts).filter(c => c > 10).length;
2518
- if (activeProjects <= 2) suggestions.push(`项目活跃度不均:${{totalProjects}} 个项目中仅 ${{activeProjects}} 个活跃,建议清理不活跃项目`);
1880
+ if (agg.habitScore.testAwareness < 10) categories.test.items.push('提高测试意识:尝试为每个新功能编写测试用例,目标覆盖率 10%+');
1881
+ else if (agg.testRatio < 0.08) categories.test.items.push(`增加测试投入:当前测试文件占比仅 ${{(agg.testRatio*100).toFixed(0)}}%,建议补充单元测试`);
1882
+ if (agg.total > 50) {{
1883
+ const testRatio = (agg.types.test || 0) / agg.total;
1884
+ if (testRatio < 0.05) categories.test.items.push(`增加测试提交:测试相关提交仅占 ${{(testRatio*100).toFixed(0)}}%,建议为新功能编写测试`);
2519
1885
  }}
1886
+ if (agg.fixRatio > 0.20) categories.test.items.push(`提升代码质量:Bug 修复占比 ${{(agg.fixRatio*100).toFixed(0)}}%,建议加强测试和 Code Review`);
2520
1887
 
2521
- // ============================================================
2522
- // 22. 测试覆盖趋势
2523
- // ============================================================
2524
- if (agg.testRatio >= 0.20) suggestions.push(`测试覆盖良好:测试文件占比 ${{(agg.testRatio*100).toFixed(0)}}%,代码质量有保障`);
2525
-
2526
- // ============================================================
2527
- // 23. 文档覆盖趋势
2528
- // ============================================================
2529
- if (agg.docRatio >= 0.10) suggestions.push(`文档覆盖良好:文档变更占比 ${{(agg.docRatio*100).toFixed(0)}}%,项目可维护性强`);
1888
+ // 正面反馈
1889
+ if (agg.testRatio >= 0.30) categories.test.items.push(`测试做得好:测试文件占比 ${{(agg.testRatio*100).toFixed(0)}}%,继续保持!`);
2530
1890
 
2531
1891
  // ============================================================
2532
- // 24. 工作生活平衡
2533
- // ============================================================
2534
- if (agg.nightRatio < 0.15 && agg.weekendRatio < 0.15) suggestions.push('工作生活平衡:夜间和周末提交都很少,作息健康');
2535
-
1892
+ // 文档维护 (最多 5 条)
2536
1893
  // ============================================================
2537
- // 25. 代码稳定性
2538
- // ============================================================
2539
- if (agg.fixRatio < 0.05 && agg.refactorRatio < 0.05 && agg.total > 100) suggestions.push('代码稳定:Bug 修复和重构都很少,代码质量稳定');
2540
-
2541
- // ============================================================
2542
- // 26. AI 工具多样性
2543
- // ============================================================
2544
- // 注:JS 端暂无 ai.tools 数据,跳过
2545
-
2546
- // ============================================================
2547
- // 27. AI 协作深度
2548
- // ============================================================
2549
- // 注:JS 端暂无 ai.ai_influence_score 数据,跳过
2550
-
2551
- // ============================================================
2552
- // 28. 提交消息长度
2553
- // ============================================================
2554
- if (agg.lowInfoRatio < 0.05 && agg.total > 100) suggestions.push(`提交消息质量高:${{((1-agg.lowInfoRatio)*100).toFixed(0)}}% 的提交有详细描述,代码可维护性强`);
2555
-
2556
- // ============================================================
2557
- // 29. 代码变更规模
2558
- // ============================================================
2559
- if (agg.total > 0 && agg.activeDays > 0) {{
2560
- const avgChangesPerCommit = agg.total / agg.activeDays;
2561
- if (avgChangesPerCommit > 5) suggestions.push('单次提交较大:建议拆分为更小的提交,便于 Code Review');
1894
+ if (agg.habitScore.docAwareness < 10) categories.doc.items.push('增加文档投入:定期更新 README 和 API 文档');
1895
+ else if (agg.docRatio < 0.05) categories.doc.items.push(`完善文档:文档变更占比 ${{(agg.docRatio*100).toFixed(0)}}%,重要功能应有文档说明`);
1896
+ if (agg.total > 50) {{
1897
+ const docsRatio = (agg.types.docs || 0) / agg.total;
1898
+ if (docsRatio < 0.05) categories.doc.items.push(`增加文档提交:文档相关提交仅占 ${{(docsRatio*100).toFixed(0)}}%,建议及时更新文档`);
2562
1899
  }}
2563
1900
 
2564
- // ============================================================
2565
- // 30. 技术债务
2566
- // ============================================================
2567
- if (agg.refactorRatio < 0.05 && agg.featRatio > 0.40 && agg.total > 100) suggestions.push(`技术债积累:重构仅占 ${{(agg.refactorRatio*100).toFixed(0)}}%,建议定期安排重构时间`);
1901
+ // 正面反馈
1902
+ if (agg.docRatio >= 0.15) categories.doc.items.push(`文档做得好:文档变更占比 ${{(agg.docRatio*100).toFixed(0)}}%,继续保持!`);
2568
1903
 
2569
1904
  // ============================================================
2570
- // 31. 提交频率波动(小时级别)
2571
- // ============================================================
2572
- if (hourly.length === 24) {{
2573
- const mean = hourly.reduce((a, b) => a + b, 0) / 24;
2574
- if (mean > 0) {{
2575
- const variance = hourly.reduce((a, b) => a + (b - mean) ** 2, 0) / 24;
2576
- const cv = Math.sqrt(variance) / mean;
2577
- if (cv > 1.5) suggestions.push('提交时间波动大:建议保持更稳定的工作节奏');
2578
- }}
2579
- }}
2580
-
2581
- // ============================================================
2582
- // 32. 项目集中度
2583
- // ============================================================
2584
- if (totalProjects > 0 && agg.total > 0) {{
2585
- const shares = Object.values(agg.projectCounts).map(c => c / agg.total);
2586
- const hhi = shares.reduce((a, b) => a + b * b, 0);
2587
- if (hhi < 0.1) suggestions.push('项目分散:提交分布均匀,建议聚焦核心项目');
2588
- else if (hhi > 0.5) suggestions.push('项目集中:大部分提交集中在少数项目,精力分配合理');
2589
- }}
2590
-
2591
- // ============================================================
2592
- // 33. 周末提交模式
2593
- // ============================================================
2594
- if (agg.weekly.length === 7) {{
2595
- const sat = agg.weekly[5] || 0;
2596
- const sun = agg.weekly[6] || 0;
2597
- const totalWeekly = agg.weekly.reduce((a, b) => a + b, 0);
2598
- if (totalWeekly > 0) {{
2599
- const satRatio = sat / totalWeekly * 100;
2600
- const sunRatio = sun / totalWeekly * 100;
2601
- if (satRatio > 20 && sunRatio < 5) suggestions.push(`周六活跃:周六提交占比 ${{satRatio.toFixed(0)}}%,周日较少,节奏不错`);
2602
- else if (sunRatio > 20 && satRatio < 5) suggestions.push(`周日活跃:周日提交占比 ${{sunRatio.toFixed(0)}}%,建议利用周六提前完成`);
2603
- }}
2604
- }}
2605
-
2606
- // ============================================================
2607
- // 34. 提交分布集中度
1905
+ // 作息健康 (最多 5 条)
2608
1906
  // ============================================================
1907
+ if (agg.habitScore.schedule < 10) categories.schedule.items.push(`改善作息规律:作息得分仅 ${{agg.habitScore.schedule}}/20,夜间和周末提交较多`);
1908
+ if (agg.nightRatio > 0.25) categories.schedule.items.push(`注意作息健康:夜间提交占比 ${{(agg.nightRatio*100).toFixed(0)}}%,建议调整为白天工作`);
1909
+ if (agg.weekendRatio > 0.25) categories.schedule.items.push(`平衡工作生活:周末提交占比 ${{(agg.weekendRatio*100).toFixed(0)}}%,注意适当休息`);
2609
1910
  if (hourly.length === 24) {{
1911
+ const lateNight = hourly.slice(0, 6).reduce((a, b) => a + b, 0);
2610
1912
  const total = hourly.reduce((a, b) => a + b, 0);
2611
- if (total > 0) {{
2612
- const sorted = hourly.map((v, i) => ({{v, i}})).sort((a, b) => b.v - a.v).slice(0, 3);
2613
- const top3Ratio = sorted.reduce((a, x) => a + x.v, 0) / total * 100;
2614
- if (top3Ratio > 50) suggestions.push(`提交高度集中:最活跃的 3 小时占 ${{top3Ratio.toFixed(0)}}% 提交,建议更均匀分布`);
2615
- else if (top3Ratio < 25) suggestions.push('提交分布均匀:各时段提交较分散,时间管理良好');
2616
- }}
2617
- }}
2618
-
2619
- // ============================================================
2620
- // 35. 测试与功能平衡
2621
- // ============================================================
2622
- if (agg.total > 0) {{
2623
- const featCount = agg.types.feat || 0;
2624
- const testCount = agg.types.test || 0;
2625
- if (featCount > 0) {{
2626
- const testFeatRatio = testCount / featCount;
2627
- if (testFeatRatio < 0.3 && featCount > 20) suggestions.push(`测试跟不上功能:每 ${{Math.round(1/testFeatRatio)}} 个功能提交才对应 1 个测试提交,建议提高测试比例`);
2628
- else if (testFeatRatio >= 0.8) suggestions.push(`测试与功能同步:测试/功能比 ${{testFeatRatio.toFixed(1)}},质量意识强`);
2629
- }}
2630
- }}
2631
-
2632
- // ============================================================
2633
- // 36. 提交历史跨度
2634
- // ============================================================
2635
- if (agg.activeDays > 0) {{
2636
- if (agg.activeDays > 300) suggestions.push(`开发持续性强:活跃 ${{agg.activeDays}} 天,长期坚持 coding`);
2637
- else if (agg.activeDays < 30 && agg.total > 50) suggestions.push(`开发集中在短期:仅 ${{agg.activeDays}} 天活跃但有 ${{agg.total}} 次提交,建议保持持续性`);
1913
+ if (total > 0 && lateNight / total > 0.15) categories.schedule.items.push(`减少深夜编码:凌晨 0-6 点提交占比 ${{(lateNight/total*100).toFixed(0)}}%,长期熬夜影响代码质量`);
2638
1914
  }}
2639
-
2640
- // ============================================================
2641
- // 37. 工作日偏好
2642
- // ============================================================
2643
1915
  if (agg.weekly.length === 7) {{
2644
- const totalWeekly = agg.weekly.reduce((a, b) => a + b, 0);
2645
- if (totalWeekly > 0) {{
2646
- const maxIdx = agg.weekly.indexOf(Math.max(...agg.weekly));
2647
- const dayRatio = agg.weekly[maxIdx] / totalWeekly * 100;
2648
- if (dayRatio > 25) suggestions.push(`工作日偏好明显:${{weekdayNames[maxIdx]}} 占 ${{dayRatio.toFixed(0)}}% 提交,是最活跃的一天`);
2649
- }}
2650
- }}
2651
-
2652
- // ============================================================
2653
- // 38. 提交时间分布
2654
- // ============================================================
2655
- if (hourly.length === 24) {{
2656
- const maxVal = Math.max(...hourly);
2657
- const peakHoursList = hourly.map((v, i) => ({{v, i}})).filter(x => x.v > maxVal * 0.8).map(x => x.i);
2658
- if (peakHoursList.length <= 3) suggestions.push(`提交时间集中:高峰时段集中在 ${{peakHoursList.join(', ')}} 点,建议分散到其他时段`);
2659
- }}
2660
-
2661
- // ============================================================
2662
- // 39. 项目提交分布
2663
- // ============================================================
2664
- if (totalProjects > 0 && agg.total > 0) {{
2665
- const shares = Object.values(agg.projectCounts).map(c => c / agg.total).filter(s => s > 0);
2666
- const entropy = -shares.reduce((a, b) => a + b * Math.log2(b), 0);
2667
- const maxEntropy = Math.log2(shares.length);
2668
- if (maxEntropy > 0) {{
2669
- const normalizedEntropy = entropy / maxEntropy;
2670
- if (normalizedEntropy > 0.8) suggestions.push('项目分布均匀:提交分散在多个项目,建议聚焦核心项目');
2671
- else if (normalizedEntropy < 0.3) suggestions.push('项目分布集中:大部分提交集中在少数项目,精力分配合理');
2672
- }}
1916
+ const peakWeekdays = agg.weekly.map((v, i) => ({{v, i}})).sort((a, b) => b.v - a.v).slice(0, 3).map(x => weekdayNames[x.i]);
1917
+ const weekendDays = peakWeekdays.filter(d => d === '周六' || d === '周日');
1918
+ if (weekendDays.length >= 2) categories.schedule.items.push(`调整工作节奏:最活跃的 3 天中有 ${{weekendDays.length}} 天是周末,建议以工作日为主`);
2673
1919
  }}
2674
1920
 
2675
- // ============================================================
2676
- // 40. 开发习惯总结
2677
- // ============================================================
2678
- if (agg.habitScore.total >= 80) suggestions.push(`开发习惯优秀:总分 ${{agg.habitScore.total}} 分,继续保持!`);
2679
- else if (agg.habitScore.total >= 60) suggestions.push(`开发习惯良好:总分 ${{agg.habitScore.total}} 分,还有提升空间`);
1921
+ // 正面反馈
1922
+ if (agg.nightRatio < 0.15 && agg.weekendRatio < 0.15) categories.schedule.items.push('工作生活平衡:夜间和周末提交都很少,作息健康');
2680
1923
 
2681
1924
  // ============================================================
2682
- // 41. 项目类型多样性
1925
+ // 项目管理 (最多 6 条)
2683
1926
  // ============================================================
2684
- if (totalProjects > 0) {{
2685
- const commitsList = Object.values(agg.projectCounts).sort((a, b) => a - b);
2686
- const n = commitsList.length;
2687
- if (n > 0 && agg.total > 0) {{
2688
- let cumulative = 0;
2689
- let giniSum = 0;
2690
- for (let i = 0; i < n; i++) {{
2691
- cumulative += commitsList[i];
2692
- giniSum += (2 * (i + 1) - n - 1) * commitsList[i];
2693
- }}
2694
- const gini = giniSum / (n * agg.total);
2695
- if (gini > 0.6) suggestions.push(`项目分布不均:基尼系数 ${{gini.toFixed(2)}},建议更均匀地分配精力`);
2696
- else if (gini < 0.2) suggestions.push(`项目分布均匀:基尼系数 ${{gini.toFixed(2)}},精力分配合理`);
2697
- }}
2698
- }}
2699
-
2700
- // ============================================================
2701
- // 42. 提交频率稳定性
2702
- // ============================================================
2703
- if (hourly.length === 24) {{
2704
- const mean = hourly.reduce((a, b) => a + b, 0) / 24;
2705
- if (mean > 0) {{
2706
- const variance = hourly.reduce((a, b) => a + (b - mean) ** 2, 0) / 24;
2707
- if (variance > 0) {{
2708
- const skewness = hourly.reduce((a, b) => a + (b - mean) ** 3, 0) / (24 * (variance ** 1.5));
2709
- if (Math.abs(skewness) > 1) suggestions.push('提交时间分布偏斜:建议保持更规律的工作节奏');
2710
- }}
2711
- }}
2712
- }}
2713
-
2714
- // ============================================================
2715
- // 43. 项目活跃周期
2716
- // ============================================================
2717
- // 注:JS 端暂无 projects 数据,跳过
2718
-
2719
- // ============================================================
2720
- // 44. 提交频率建议
2721
- // ============================================================
2722
- if (avgDaily > 0) {{
2723
- if (avgDaily < 0.5) suggestions.push(`提交频率低:日均 ${{avgDaily.toFixed(1)}} 次提交,建议更频繁地提交代码`);
2724
- else if (avgDaily > 5) suggestions.push(`提交频率高:日均 ${{avgDaily.toFixed(1)}} 次提交,注意保持代码质量`);
2725
- }}
2726
-
2727
- // ============================================================
2728
- // 45. 开发效率
2729
- // ============================================================
2730
- if (agg.total > 0 && agg.activeDays > 0) {{
2731
- const efficiency = agg.total / agg.activeDays;
2732
- if (efficiency > 5) suggestions.push(`开发效率高:每次活跃日平均 ${{efficiency.toFixed(0)}} 次提交,产出稳定`);
2733
- else if (efficiency < 2) suggestions.push(`开发效率待提升:每次活跃日平均 ${{efficiency.toFixed(0)}} 次提交,建议提高效率`);
1927
+ if (agg.habitScore.focus < 10) categories.project.items.push(`提升专注度:项目聚焦得分仅 ${{agg.habitScore.focus}}/15,建议减少同时维护的项目数`);
1928
+ if (agg.focusIndex < 0.60) categories.project.items.push('提高项目聚焦度:精力较分散,建议集中精力在 1-2 个核心项目上');
1929
+ if (totalProjects > 15) categories.project.items.push(`精简项目数量:同时维护 ${{totalProjects}} 个项目,建议聚焦核心项目提升效率`);
1930
+ if (totalProjects > 5 && agg.total > 0) {{
1931
+ const activeProjects = Object.values(agg.projectCounts).filter(c => c > 10).length;
1932
+ if (activeProjects <= 2) categories.project.items.push(`项目活跃度不均:${{totalProjects}} 个项目中仅 ${{activeProjects}} 个活跃,建议清理不活跃项目`);
2734
1933
  }}
2735
-
2736
- // ============================================================
2737
- // 46. 功能 vs 维护平衡
2738
- // ============================================================
1934
+ if (agg.refactorRatio < 0.10 && agg.featRatio > 0.50) categories.project.items.push('关注技术债:功能开发占比高但重构不足,建议定期安排重构时间');
2739
1935
  if (agg.total > 0) {{
2740
1936
  const featC = agg.types.feat || 0;
2741
1937
  const fixC = agg.types.fix || 0;
@@ -2743,163 +1939,63 @@ def _build_js(all_commits_json, project_names_json, project_meta_json,
2743
1939
  const maintenance = fixC + refactorC;
2744
1940
  if (featC > 0 && maintenance > 0) {{
2745
1941
  const ratio = featC / maintenance;
2746
- if (ratio > 5) suggestions.push(`功能远超维护:功能/维护比 ${{ratio.toFixed(1)}}:1,注意技术债积累`);
2747
- else if (ratio < 1) suggestions.push('维护为主:维护提交多于功能,代码在持续改善');
1942
+ if (ratio > 5) categories.project.items.push(`功能远超维护:功能/维护比 ${{ratio.toFixed(1)}}:1,注意技术债积累`);
2748
1943
  }}
2749
1944
  }}
2750
1945
 
2751
- // ============================================================
2752
- // 47. 项目状态分布
2753
- // ============================================================
2754
- if (totalProjects > 3 && agg.total > 0) {{
2755
- const active = Object.values(agg.projectCounts).filter(c => c > 10).length;
2756
- const inactive = totalProjects - active;
2757
- if (inactive > totalProjects * 0.6) suggestions.push(`不活跃项目多:${{totalProjects}} 个项目中 ${{inactive}} 个提交不足 10 次,建议归档`);
2758
- }}
1946
+ // 正面反馈
1947
+ if (totalProjects <= 3 && agg.total > 100) categories.project.items.push(`项目聚焦度高:仅维护 ${{totalProjects}} 个项目,精力集中`);
1948
+ if (agg.refactorRatio >= 0.10) categories.project.items.push(`重构做得好:重构占比 ${{(agg.refactorRatio*100).toFixed(0)}}%,代码质量持续改善`);
2759
1949
 
2760
1950
  // ============================================================
2761
- // 48. 提交密度变化
1951
+ // AI 协作 (最多 4 条)
2762
1952
  // ============================================================
2763
- if (hourly.length === 24) {{
2764
- const total = hourly.reduce((a, b) => a + b, 0);
2765
- if (total > 0) {{
2766
- const avgPerHour = total / 24;
2767
- const highDensityHours = hourly.map((v, i) => ({{v, i}})).filter(x => x.v > avgPerHour * 2).map(x => x.i);
2768
- if (highDensityHours.length <= 2 && highDensityHours.length > 0) suggestions.push(`编码时段集中:高峰仅在 ${{highDensityHours.join(', ')}} 点,建议适当分散`);
2769
- }}
2770
- }}
1953
+ if (aiRatio > 0.40) categories.ai.items.push('善用 AI:AI 占比超过 40%,建议仔细审查 AI 生成的代码');
1954
+ else if (aiRatio > 0 && aiRatio < 0.15) categories.ai.items.push(`探索 AI 工具:当前 AI 使用率 ${{(aiRatio*100).toFixed(0)}}%,可尝试 Copilot/Cursor 提升效率`);
2771
1955
 
2772
- // ============================================================
2773
- // 49. 周内提交均匀度
2774
- // ============================================================
2775
- if (agg.weekly.length === 7) {{
2776
- const totalWeekly = agg.weekly.reduce((a, b) => a + b, 0);
2777
- if (totalWeekly > 0) {{
2778
- const avgPerDay = totalWeekly / 7;
2779
- const variance = agg.weekly.reduce((a, v) => a + (v - avgPerDay) ** 2, 0) / 7;
2780
- if (avgPerDay > 0) {{
2781
- const cv = Math.sqrt(variance) / avgPerDay;
2782
- if (cv > 0.8) suggestions.push('周内提交不均:各天差异大,建议保持更稳定的周节奏');
2783
- }}
2784
- }}
2785
- }}
2786
-
2787
- // ============================================================
2788
- // 50. 功能开发占比
2789
- // ============================================================
2790
- if (agg.total > 0) {{
2791
- const featC = agg.types.feat || 0;
2792
- const featPct = featC / agg.total * 100;
2793
- if (featPct > 60) suggestions.push(`功能开发为主:feat 占 ${{featPct.toFixed(0)}}%,建议平衡新功能与质量保障`);
2794
- else if (featPct < 10 && agg.total > 50) suggestions.push(`功能开发少:feat 仅占 ${{featPct.toFixed(0)}}%,开发以维护为主`);
2795
- }}
2796
-
2797
- // ============================================================
2798
- // 51. Chore 类型分析
2799
- // ============================================================
2800
- if (agg.total > 0) {{
2801
- const choreC = agg.types.chore || 0;
2802
- const chorePct = choreC / agg.total * 100;
2803
- if (chorePct > 20) suggestions.push(`琐事较多:chore 占 ${{chorePct.toFixed(0)}}%,建议用自动化脚本减少重复工作`);
2804
- }}
2805
-
2806
- // ============================================================
2807
- // 52. 提交历史长度
2808
- // ============================================================
2809
- if (agg.activeDays > 200) suggestions.push(`长期开发者:活跃超过 ${{agg.activeDays}} 天,积累了丰富的开发经验`);
2810
-
2811
- // ============================================================
2812
- // 53. 最热门项目贡献
2813
- // ============================================================
2814
- if (totalProjects > 1 && agg.total > 0) {{
2815
- const maxCommits = Math.max(...Object.values(agg.projectCounts));
2816
- const topRatio = maxCommits / agg.total * 100;
2817
- if (topRatio > 60) suggestions.push(`单项目主导:最热门项目占 ${{topRatio.toFixed(0)}}% 提交,精力高度集中`);
2818
- else if (topRatio < 20) suggestions.push(`精力分散:最热门项目仅占 ${{topRatio.toFixed(0)}}% 提交,建议聚焦核心项目`);
2819
- }}
2820
-
2821
- // ============================================================
2822
- // 54. 文档与代码比例
2823
- // ============================================================
2824
- if (agg.total > 0) {{
2825
- const docsC = agg.types.docs || 0;
2826
- const codeC = agg.total - docsC;
2827
- if (codeC > 0) {{
2828
- const docCodeRatio = docsC / codeC;
2829
- if (docCodeRatio > 0.3) suggestions.push(`文档投入高:文档/代码比 ${{docCodeRatio.toFixed(1)}},文档维护良好`);
2830
- else if (docCodeRatio < 0.05 && agg.total > 50) suggestions.push(`文档投入不足:文档/代码比 ${{docCodeRatio.toFixed(2)}},建议增加文档更新`);
2831
- }}
2832
- }}
2833
-
2834
- // ============================================================
2835
- // 55. 早起 vs 夜猫子
2836
- // ============================================================
2837
- if (hourly.length === 24) {{
2838
- const total = hourly.reduce((a, b) => a + b, 0);
2839
- if (total > 0) {{
2840
- const morning = hourly.slice(5, 9).reduce((a, b) => a + b, 0);
2841
- const night = hourly.slice(22, 24).reduce((a, b) => a + b, 0) + hourly.slice(0, 2).reduce((a, b) => a + b, 0);
2842
- if (morning > night * 2 && morning > 0) suggestions.push('早起型开发者:早晨提交多于深夜,作息健康');
2843
- else if (night > morning * 2 && night > 0) suggestions.push('夜猫子型开发者:深夜提交多于早晨,建议调整作息');
2844
- }}
2845
- }}
2846
-
2847
- // ============================================================
2848
- // 56. 提交稳定性
2849
- // ============================================================
2850
- if (agg.activeDays > 10 && agg.total > 0) {{
2851
- const commitsPerDay = agg.total / agg.activeDays;
2852
- if (commitsPerDay >= 2 && commitsPerDay <= 6) suggestions.push(`提交节奏稳定:活跃日均 ${{commitsPerDay.toFixed(1)}} 次提交,节奏适中`);
2853
- }}
2854
-
2855
- // ============================================================
2856
- // 57. 多语言项目管理
2857
- // ============================================================
2858
- if (langSet.size >= 3) {{
2859
- const langCounts = agg.languageCounts;
2860
- const topLang = Object.keys(langCounts).reduce((a, b) => (langCounts[a] || 0) > (langCounts[b] || 0) ? a : b, Object.keys(langCounts)[0]);
2861
- suggestions.push(`多语言开发者:使用 ${{langSet.size}} 种语言,${{topLang}} 项目最多`);
2862
- }}
1956
+ // 正面反馈
1957
+ if (aiRatio >= 0.20 && aiRatio <= 0.40) categories.ai.items.push(`AI 使用适中:AI 占比 ${{(aiRatio*100).toFixed(0)}}%,人机协作良好`);
2863
1958
 
2864
1959
  // ============================================================
2865
- // 58. 其他类型提交分析
1960
+ // 构建 HTML(带 tab 切换)
2866
1961
  // ============================================================
2867
- if (agg.total > 0) {{
2868
- const otherC = agg.types.other || 0;
2869
- const otherPct = otherC / agg.total * 100;
2870
- if (otherPct > 30) suggestions.push(`提交分类不清:${{otherPct.toFixed(0)}}% 归为 other,建议使用标准 commit 类型`);
2871
- else if (otherPct < 10 && agg.total > 50) suggestions.push(`提交分类规范:other 仅占 ${{otherPct.toFixed(0)}}%,类型使用清晰`);
2872
- }}
1962
+ const activeCats = Object.entries(categories).filter(([k, v]) => v.items.length > 0);
1963
+ let html = '';
2873
1964
 
2874
- // ============================================================
2875
- // 59. 功能开发深度
2876
- // ============================================================
2877
- if (agg.total > 0) {{
2878
- const featC = agg.types.feat || 0;
2879
- const fixC = agg.types.fix || 0;
2880
- if (featC > 0 && fixC > 0) {{
2881
- const featFixRatio = featC / fixC;
2882
- if (featFixRatio > 4) suggestions.push(`功能驱动:功能/修复比 ${{featFixRatio.toFixed(1)}}:1,开发节奏快`);
2883
- else if (featFixRatio < 1) suggestions.push('修复驱动:修复多于新功能,建议分析根本原因');
2884
- }}
2885
- }}
2886
-
2887
- // ============================================================
2888
- // 60. 项目维护活跃度
2889
- // ============================================================
2890
- if (agg.total > 0) {{
2891
- const wellMaintained = Object.values(agg.projectCounts).filter(c => c > 30).length;
2892
- if (wellMaintained >= 3) suggestions.push(`多项目深耕:${{wellMaintained}} 个项目提交超过 30 次,项目管理能力强`);
1965
+ if (activeCats.length === 0) {{
1966
+ html = '<div class="sug-item"><div class="sug-text">继续保持良好的开发习惯!各项指标都很健康</div></div>';
1967
+ }} else {{
1968
+ // Tab 按钮
1969
+ html += '<div class="sug-tabs">';
1970
+ activeCats.forEach(([key, cat], i) => {{
1971
+ const activeClass = i === 0 ? ' active' : '';
1972
+ html += `<button class="sug-tab${{activeClass}}" data-cat="${{key}}">${{cat.icon}} ${{cat.name}} (${{cat.items.length}})</button>`;
1973
+ }});
1974
+ html += '</div>';
1975
+
1976
+ // Tab 内容
1977
+ activeCats.forEach(([key, cat], i) => {{
1978
+ const activeClass = i === 0 ? ' active' : '';
1979
+ html += `<div class="sug-panel${{activeClass}}" data-cat="${{key}}">`;
1980
+ cat.items.forEach((item, j) => {{
1981
+ html += `<div class="sug-item"><div class="sug-num">${{j + 1}}</div><div class="sug-text">${{item}}</div></div>`;
1982
+ }});
1983
+ html += '</div>';
1984
+ }});
2893
1985
  }}
2894
1986
 
2895
- // 默认
2896
- if (!suggestions.length) suggestions.push('继续保持良好的开发习惯!各项指标都很健康');
1987
+ document.getElementById('suggestions').innerHTML = html;
2897
1988
 
2898
- document.getElementById('suggestions').innerHTML = suggestions.map((s, i) => `
2899
- <div class="sug-item">
2900
- <div class="sug-num">${{i + 1}}</div>
2901
- <div class="sug-text">${{s}}</div>
2902
- </div>`).join('');
1989
+ // 绑定 tab 切换事件
1990
+ document.querySelectorAll('.sug-tab').forEach(tab => {{
1991
+ tab.addEventListener('click', () => {{
1992
+ const cat = tab.dataset.cat;
1993
+ document.querySelectorAll('.sug-tab').forEach(t => t.classList.remove('active'));
1994
+ document.querySelectorAll('.sug-panel').forEach(p => p.classList.remove('active'));
1995
+ tab.classList.add('active');
1996
+ document.querySelector(`.sug-panel[data-cat="${{cat}}"]`).classList.add('active');
1997
+ }});
1998
+ }});
2903
1999
  }}
2904
2000
 
2905
2001
  // ============================================================