gitflow-analytics 3.13.5__tar.gz → 3.13.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/PKG-INFO +1 -1
  2. gitflow_analytics-3.13.7/docs/research/csv-generation-narrative-bug-20251222.md +412 -0
  3. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/pyproject.toml +3 -0
  4. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/_version.py +1 -1
  5. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/cli.py +135 -2
  6. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/git_auth.py +74 -0
  7. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/git_timeout_wrapper.py +8 -0
  8. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/PKG-INFO +1 -1
  9. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/SOURCES.txt +1 -0
  10. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_reports.py +0 -1
  11. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/CHANGELOG.md +0 -0
  12. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/CLAUDE.md +0 -0
  13. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/LICENSE +0 -0
  14. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/MANIFEST.in +0 -0
  15. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/README.md +0 -0
  16. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/DOCUMENTATION-STANDARDS.md +0 -0
  17. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/README.md +0 -0
  18. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/SECURITY.md +0 -0
  19. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/STRUCTURE.md +0 -0
  20. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/README.md +0 -0
  21. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/analysis-files/ewtn-critical-security-report.md +0 -0
  22. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_085239_236.md +0 -0
  23. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_114233_697.md +0 -0
  24. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_115330_511.md +0 -0
  25. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_120516_669.md +0 -0
  26. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_124906_951.md +0 -0
  27. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_175540_671.md +0 -0
  28. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250915_214023_300.md +0 -0
  29. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_engineer_20250916_130622_510.md +0 -0
  30. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_ops_20250915_123323_419.md +0 -0
  31. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_ops_20250915_134446_128.md +0 -0
  32. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_ops_20250915_184656_725.md +0 -0
  33. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_research_20250915_091233_023.md +0 -0
  34. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_research_20250915_100404_339.md +0 -0
  35. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/agent_research_20250915_110606_413.md +0 -0
  36. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/system_prompt_20250915_084833_641.md +0 -0
  37. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/system_prompt_20250915_090830_660.md +0 -0
  38. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-logs/prompts/system_prompt_20250916_130545_822.md +0 -0
  39. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/output/database_qualitative_report_20250630_20250824.md +0 -0
  40. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/output/narrative_report_20250630_20250824.md +0 -0
  41. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/reports-24week/database_qualitative_report_20250303_20250817.md +0 -0
  42. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/reports-24week/narrative_report_20250303_20250817.md +0 -0
  43. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/test-ewtn-reports/narrative_report_20250810.md +0 -0
  44. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/test-reports/database_qualitative_report_20250908_20250914.md +0 -0
  45. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/test-reports/narrative_report_20250908_20250914.md +0 -0
  46. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/test-weekly-reports/database_qualitative_report_20250623_20250817.md +0 -0
  47. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/old-reports/test-weekly-reports/narrative_report_20250623_20250817.md +0 -0
  48. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/CHANGELOG_INTERACTIVE_LAUNCHER.md +0 -0
  49. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/CLEANUP_SUMMARY-20250929.md +0 -0
  50. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/FEATURES_READY_FOR_TESTING.md +0 -0
  51. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/FIX_SUMMARY.md +0 -0
  52. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/IMPLEMENTATION_SUMMARY.md +0 -0
  53. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/PROGRESS_TRACKING_FIXES.md +0 -0
  54. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/PROJECT_CLEANUP_REPORT-20250929.md +0 -0
  55. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/README.md +0 -0
  56. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/STORY_POINTS_CONFIG_SUMMARY-archived.md +0 -0
  57. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/SYNTAX_ERROR_FIX.md +0 -0
  58. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/TEST_REPORT_GIT_CLONING.md +0 -0
  59. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/TIMEZONE_BUG_RESOLUTION.md +0 -0
  60. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/USAGE_EXAMPLES-archived.md +0 -0
  61. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/_archive/temp-files/code_analysis_report.md +0 -0
  62. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/architecture/README.md +0 -0
  63. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/architecture/branch-analysis-optimization.md +0 -0
  64. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/architecture/caching-strategy.md +0 -0
  65. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/architecture/llm-classifier-refactoring.md +0 -0
  66. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/architecture/ml-pipeline.md +0 -0
  67. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/configuration/configuration.md +0 -0
  68. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/deployment/README.md +0 -0
  69. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/README.md +0 -0
  70. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/circuit-breaker-implementation.md +0 -0
  71. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/commit-classification-design.md +0 -0
  72. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/git_pm_correlation_design.md +0 -0
  73. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/platform-agnostic-pm-framework.md +0 -0
  74. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/design/qualitative_data_extraction.md +0 -0
  75. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/README.md +0 -0
  76. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/contributing.md +0 -0
  77. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/development-setup.md +0 -0
  78. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/project-organization.md +0 -0
  79. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/refactoring-guide.md +0 -0
  80. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/developer/training-guide.md +0 -0
  81. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/examples/README.md +0 -0
  82. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/examples/interactive-launcher-examples.md +0 -0
  83. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/examples/story-points-configuration.md +0 -0
  84. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/getting-started/README.md +0 -0
  85. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/getting-started/first-analysis.md +0 -0
  86. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/getting-started/installation.md +0 -0
  87. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/getting-started/quickstart.md +0 -0
  88. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/LLM_CLASSIFICATION_GUIDE.md +0 -0
  89. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/README.md +0 -0
  90. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/chatgpt-setup.md +0 -0
  91. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/identity-resolution-enhanced.md +0 -0
  92. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/interactive-launcher.md +0 -0
  93. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/managing-aliases.md +0 -0
  94. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/ml-categorization.md +0 -0
  95. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/pm-platform-setup.md +0 -0
  96. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/guides/troubleshooting.md +0 -0
  97. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/quick-reference/launcher-and-identity.md +0 -0
  98. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/reference/README.md +0 -0
  99. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/reference/cache-system.md +0 -0
  100. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/reference/cli-commands.md +0 -0
  101. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/reference/configuration-schema.md +0 -0
  102. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/docs/reference/json-export-schema.md +0 -0
  103. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/setup.cfg +0 -0
  104. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/__init__.py +0 -0
  105. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/__init__.py +0 -0
  106. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/batch_classifier.py +0 -0
  107. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/classifier.py +0 -0
  108. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/feature_extractor.py +0 -0
  109. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/linguist_analyzer.py +0 -0
  110. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/classification/model.py +0 -0
  111. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/cli_wizards/__init__.py +0 -0
  112. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/cli_wizards/install_wizard.py +0 -0
  113. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/cli_wizards/menu.py +0 -0
  114. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/cli_wizards/run_launcher.py +0 -0
  115. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/__init__.py +0 -0
  116. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/aliases.py +0 -0
  117. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/errors.py +0 -0
  118. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/loader.py +0 -0
  119. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/profiles.py +0 -0
  120. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/repository.py +0 -0
  121. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/schema.py +0 -0
  122. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config/validator.py +0 -0
  123. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/config.py +0 -0
  124. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/constants.py +0 -0
  125. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/__init__.py +0 -0
  126. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/analyzer.py +0 -0
  127. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/branch_mapper.py +0 -0
  128. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/cache.py +0 -0
  129. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/data_fetcher.py +0 -0
  130. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/identity.py +0 -0
  131. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/metrics_storage.py +0 -0
  132. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/progress.py +0 -0
  133. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/schema_version.py +0 -0
  134. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/core/subprocess_git.py +0 -0
  135. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/extractors/__init__.py +0 -0
  136. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/extractors/base.py +0 -0
  137. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/extractors/ml_tickets.py +0 -0
  138. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/extractors/story_points.py +0 -0
  139. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/extractors/tickets.py +0 -0
  140. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/identity_llm/__init__.py +0 -0
  141. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/identity_llm/analysis_pass.py +0 -0
  142. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/identity_llm/analyzer.py +0 -0
  143. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/identity_llm/models.py +0 -0
  144. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/integrations/__init__.py +0 -0
  145. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/integrations/github_integration.py +0 -0
  146. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/integrations/jira_integration.py +0 -0
  147. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/integrations/orchestrator.py +0 -0
  148. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/metrics/__init__.py +0 -0
  149. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/metrics/activity_scoring.py +0 -0
  150. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/metrics/branch_health.py +0 -0
  151. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/metrics/dora.py +0 -0
  152. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/models/__init__.py +0 -0
  153. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/models/database.py +0 -0
  154. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/__init__.py +0 -0
  155. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/adapters/__init__.py +0 -0
  156. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/adapters/jira_adapter.py +0 -0
  157. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/base.py +0 -0
  158. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/models.py +0 -0
  159. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/orchestrator.py +0 -0
  160. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/pm_framework/registry.py +0 -0
  161. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/__init__.py +0 -0
  162. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/chatgpt_analyzer.py +0 -0
  163. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/__init__.py +0 -0
  164. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/change_type.py +0 -0
  165. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/domain_classifier.py +0 -0
  166. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/intent_analyzer.py +0 -0
  167. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/__init__.py +0 -0
  168. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/base.py +0 -0
  169. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/batch_processor.py +0 -0
  170. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/cache.py +0 -0
  171. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py +0 -0
  172. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/openai_client.py +0 -0
  173. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/prompts.py +0 -0
  174. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm/response_parser.py +0 -0
  175. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py +0 -0
  176. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/classifiers/risk_analyzer.py +0 -0
  177. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/core/__init__.py +0 -0
  178. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/core/llm_fallback.py +0 -0
  179. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/core/nlp_engine.py +0 -0
  180. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/core/pattern_cache.py +0 -0
  181. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/core/processor.py +0 -0
  182. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/enhanced_analyzer.py +0 -0
  183. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/example_enhanced_usage.py +0 -0
  184. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/models/__init__.py +0 -0
  185. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/models/schemas.py +0 -0
  186. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/utils/__init__.py +0 -0
  187. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/utils/batch_processor.py +0 -0
  188. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/utils/cost_tracker.py +0 -0
  189. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/utils/metrics.py +0 -0
  190. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/qualitative/utils/text_processing.py +0 -0
  191. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/__init__.py +0 -0
  192. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/analytics_writer.py +0 -0
  193. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/base.py +0 -0
  194. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/branch_health_writer.py +0 -0
  195. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/classification_writer.py +0 -0
  196. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/cli_integration.py +0 -0
  197. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/csv_writer.py +0 -0
  198. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/data_models.py +0 -0
  199. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/database_report_generator.py +0 -0
  200. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/example_usage.py +0 -0
  201. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/factory.py +0 -0
  202. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/formatters.py +0 -0
  203. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/html_generator.py +0 -0
  204. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/interfaces.py +0 -0
  205. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/json_exporter.py +0 -0
  206. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/narrative_writer.py +0 -0
  207. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/story_point_correlation.py +0 -0
  208. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/reports/weekly_trends_writer.py +0 -0
  209. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/__init__.py +0 -0
  210. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/config.py +0 -0
  211. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/extractors/__init__.py +0 -0
  212. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/extractors/dependency_checker.py +0 -0
  213. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/extractors/secret_detector.py +0 -0
  214. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/extractors/vulnerability_scanner.py +0 -0
  215. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/llm_analyzer.py +0 -0
  216. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/reports/__init__.py +0 -0
  217. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/reports/security_report.py +0 -0
  218. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/security/security_analyzer.py +0 -0
  219. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/training/__init__.py +0 -0
  220. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/training/model_loader.py +0 -0
  221. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/training/pipeline.py +0 -0
  222. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/types/__init__.py +0 -0
  223. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/types/commit_types.py +0 -0
  224. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/ui/__init__.py +0 -0
  225. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/ui/progress_display.py +0 -0
  226. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/utils/__init__.py +0 -0
  227. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/utils/commit_utils.py +0 -0
  228. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics/verify_activity.py +0 -0
  229. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/dependency_links.txt +0 -0
  230. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/entry_points.txt +0 -0
  231. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/requires.txt +0 -0
  232. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/src/gitflow_analytics.egg-info/top_level.txt +0 -0
  233. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/__init__.py +0 -0
  234. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/conftest.py +0 -0
  235. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/__init__.py +0 -0
  236. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_analyzer.py +0 -0
  237. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_analyzer_merge_stats.py +0 -0
  238. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_cache.py +0 -0
  239. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_data_fetcher.py +0 -0
  240. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_data_fetcher_merge_stats.py +0 -0
  241. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_identity.py +0 -0
  242. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_merge_commit_detection.py +0 -0
  243. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/core/test_progress.py +0 -0
  244. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/debug_bulk_exists.py +0 -0
  245. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/debug_commit_story_points.py +0 -0
  246. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/debug_database_storage.py +0 -0
  247. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/debug_jira_enrichment.py +0 -0
  248. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/debug_story_points.py +0 -0
  249. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/extractors/__init__.py +0 -0
  250. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/integration/__init__.py +0 -0
  251. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/integration/conftest.py +0 -0
  252. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/integration/test_merge_exclusion_workflow.py +0 -0
  253. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/integrations/__init__.py +0 -0
  254. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/metrics/__init__.py +0 -0
  255. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/models/__init__.py +0 -0
  256. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/qualitative/__init__.py +0 -0
  257. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/qualitative/test_basic_integration.py +0 -0
  258. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/reports/__init__.py +0 -0
  259. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/run_all_tests.py +0 -0
  260. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/run_security_all_repos.py +0 -0
  261. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/run_security_analysis.py +0 -0
  262. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test-qa-install/test_error_handling.py +0 -0
  263. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test-qa-install/test_wizard_automated.py +0 -0
  264. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_atomic_caching.py +0 -0
  265. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_cache_invalidation.py +0 -0
  266. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_classification_system.py +0 -0
  267. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_cli.py +0 -0
  268. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_cli_wizards/__init__.py +0 -0
  269. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_cli_wizards/test_menu.py +0 -0
  270. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_config.py +0 -0
  271. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_config_extends.py +0 -0
  272. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_config_profiles.py +0 -0
  273. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_config_story_points.py +0 -0
  274. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_jira_connection.py +0 -0
  275. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_llm_commit_classification.py +0 -0
  276. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_march_2025_comparison.py +0 -0
  277. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_metrics.py +0 -0
  278. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_ml_accuracy.py +0 -0
  279. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_ml_components.py +0 -0
  280. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_ml_comprehensive.py +0 -0
  281. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_ml_integration.py +0 -0
  282. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_pm_env_resolution.py +0 -0
  283. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_report_abstraction.py +0 -0
  284. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_story_points_analysis.py +0 -0
  285. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_training_pipeline.py +0 -0
  286. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/test_two_step_process.py +0 -0
  287. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/utils/__init__.py +0 -0
  288. {gitflow_analytics-3.13.5 → gitflow_analytics-3.13.7}/tests/utils/test_commit_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitflow-analytics
3
- Version: 3.13.5
3
+ Version: 3.13.7
4
4
  Summary: Analyze Git repositories for developer productivity insights
5
5
  Author-email: Bob Matyas <bobmatnyc@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,412 @@
1
+ # Bug Analysis: CSV Generation Disabled but Narrative Report Still Tries to Read CSV
2
+
3
+ **Date**: 2025-12-22
4
+ **Analyst**: Research Agent
5
+ **Priority**: High
6
+ **Type**: Logic Error / Missing Conditional Check
7
+
8
+ ## Executive Summary
9
+
10
+ When qualitative analysis runs with "CSV generation disabled", the code still unconditionally tries to read `qualitative_insights_20251222.csv`, causing a `FileNotFoundError`. The root cause is that CSV files are **always generated** regardless of the `generate_csv` flag, but the narrative report generation assumes these CSVs exist and attempts to read them without checking if CSV generation was enabled.
11
+
12
+ ## Bug Details
13
+
14
+ ### Symptom
15
+ ```
16
+ Error: FileNotFoundError: [Errno 2] No such file or directory: 'qualitative_insights_20251222.csv'
17
+ ```
18
+
19
+ This occurs when:
20
+ 1. User runs analysis with CSV generation disabled (`--no-csv` flag or config setting)
21
+ 2. Narrative report generation is enabled (`markdown` in output formats)
22
+ 3. The code tries to load CSV files that were never written to disk
23
+
24
+ ### Root Cause Analysis
25
+
26
+ **The Fundamental Problem**: The comment "always generate data, optionally write CSV" is misleading. The CSV files ARE written to disk unconditionally, but when `generate_csv=False`, they are deleted or not added to the reports list.
27
+
28
+ #### Code Flow
29
+
30
+ **Step 1: CSV Report Generation (Lines 3663-3725)**
31
+
32
+ All three CSV reports follow the same flawed pattern:
33
+
34
+ ```python
35
+ # Activity distribution report (always generate data, optionally write CSV)
36
+ activity_report = output / f"activity_distribution_{datetime.now().strftime('%Y%m%d')}.csv"
37
+ try:
38
+ logger.debug("Starting activity distribution report generation")
39
+ analytics_gen.generate_activity_distribution_report(
40
+ all_commits, developer_stats, activity_report
41
+ )
42
+ logger.debug("Activity distribution report completed successfully")
43
+ if generate_csv: # ⚠️ Only controls whether report is LISTED, not whether it's WRITTEN
44
+ generated_reports.append(activity_report.name)
45
+ if not display:
46
+ click.echo(f" ✅ Activity distribution: {activity_report}")
47
+ ```
48
+
49
+ **What Actually Happens**:
50
+ - `generate_activity_distribution_report()` is called unconditionally
51
+ - Inside `analytics_writer.py:316-346`, the CSV is **written to disk** (line 344: `df.to_csv(output_path, index=False)`)
52
+ - The `if generate_csv` check only controls:
53
+ - Whether the filename is added to `generated_reports` list
54
+ - Whether a success message is printed
55
+
56
+ **The Same Pattern for All Three CSVs**:
57
+ 1. **activity_report** (lines 3663-3685)
58
+ 2. **focus_report** (lines 3687-3709)
59
+ 3. **insights_report** (lines 3711-3725)
60
+
61
+ All three reports:
62
+ - Define the CSV path
63
+ - Call the generator function (which writes CSV to disk)
64
+ - Only conditionally add to `generated_reports` list if `generate_csv=True`
65
+
66
+ **Step 2: Narrative Report Generation (Lines 3911-4039)**
67
+
68
+ The narrative report generation **unconditionally reads all three CSVs**:
69
+
70
+ ```python
71
+ # Generate markdown reports if enabled
72
+ if "markdown" in cfg.output.formats:
73
+ # Calculate date range for consistent filename formatting across all markdown reports
74
+ date_range = f"{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}"
75
+
76
+ try:
77
+ logger.debug("Starting narrative report generation")
78
+ narrative_gen = NarrativeReportGenerator()
79
+
80
+ # Lazy import pandas - only needed for CSV reading in narrative generation
81
+ import pandas as pd
82
+
83
+ # Load activity distribution data
84
+ logger.debug("Loading activity distribution data")
85
+ activity_df = pd.read_csv(activity_report) # ⚠️ ASSUMES CSV EXISTS
86
+ activity_data = cast(list[dict[str, Any]], activity_df.to_dict("records"))
87
+
88
+ # Load focus data
89
+ logger.debug("Loading focus data")
90
+ focus_df = pd.read_csv(focus_report) # ⚠️ ASSUMES CSV EXISTS
91
+ focus_data = cast(list[dict[str, Any]], focus_df.to_dict("records"))
92
+
93
+ # Load insights data
94
+ logger.debug("Loading insights data")
95
+ insights_df = pd.read_csv(insights_report) # ⚠️ ASSUMES CSV EXISTS
96
+ insights_data = cast(list[dict[str, Any]], insights_df.to_dict("records"))
97
+ ```
98
+
99
+ **Problem**: The narrative report generation has NO conditional check for `generate_csv`. It assumes the CSV files exist on disk.
100
+
101
+ ### Variables Involved
102
+
103
+ | Variable | Definition Line | Purpose | Current Behavior |
104
+ |----------|----------------|---------|------------------|
105
+ | `generate_csv` | Function parameter | Controls whether CSVs should be generated | Only controls whether CSVs are listed in `generated_reports`, NOT whether they're written |
106
+ | `activity_report` | Line 3664 | Path to activity CSV | Path is defined, CSV is written, but may not be listed |
107
+ | `focus_report` | Line 3688 | Path to focus CSV | Path is defined, CSV is written, but may not be listed |
108
+ | `insights_report` | Line 3712 | Path to insights CSV | Path is defined, CSV is written, but may not be listed |
109
+ | `generated_reports` | List variable | Tracks which reports to display | Only includes CSVs if `generate_csv=True` |
110
+
111
+ ### The Contradiction
112
+
113
+ **Comment Says**: "always generate data, optionally write CSV"
114
+
115
+ **Reality Is**:
116
+ - CSV is **always written** to disk (line 344 in `analytics_writer.py`)
117
+ - The `generate_csv` flag only controls reporting/listing
118
+ - BUT: If CSV generation is disabled in the future (e.g., by wrapping the generator call in `if generate_csv:`), the narrative report will break
119
+
120
+ **Why This Works Sometimes**:
121
+ Currently, the bug doesn't manifest because CSVs ARE always written. The bug will appear if:
122
+ 1. Someone "fixes" the CSV generation to respect the `generate_csv` flag
123
+ 2. The CSV file gets deleted between generation and narrative report
124
+ 3. File permissions prevent CSV writing but don't raise an exception
125
+
126
+ ## Impact Analysis
127
+
128
+ ### Current State
129
+ - **Low Impact**: CSVs are always written, so narrative report succeeds
130
+ - **Confusion**: Users see "CSV generation disabled" but CSVs are still created
131
+ - **Technical Debt**: Misleading code comments and variable names
132
+
133
+ ### If CSV Generation is "Fixed"
134
+ - **High Impact**: Narrative report will crash with FileNotFoundError
135
+ - **User Experience**: Broken feature when CSV generation is disabled
136
+ - **Data Loss**: No way to generate narrative reports without CSV files
137
+
138
+ ## Recommended Fix Approach
139
+
140
+ ### Option 1: Skip Narrative Report When CSV Disabled (Conservative)
141
+
142
+ **Rationale**: If CSV generation is disabled, don't generate narrative report at all.
143
+
144
+ **Change Location**: `cli.py` lines 3911-3039
145
+
146
+ **Implementation**:
147
+ ```python
148
+ # Generate markdown reports if enabled
149
+ if "markdown" in cfg.output.formats and generate_csv: # ⚠️ ADD generate_csv CHECK
150
+ # Calculate date range for consistent filename formatting across all markdown reports
151
+ date_range = f"{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}"
152
+
153
+ try:
154
+ logger.debug("Starting narrative report generation")
155
+ # ... rest of the code unchanged ...
156
+ ```
157
+
158
+ **Pros**:
159
+ - Minimal code change
160
+ - Clear logic: no CSVs = no narrative report
161
+ - No risk of file not found errors
162
+
163
+ **Cons**:
164
+ - Users lose narrative reports when CSV generation is disabled
165
+ - Doesn't address the underlying architectural issue
166
+
167
+ ### Option 2: Generate In-Memory Data Without Writing CSVs (Optimal)
168
+
169
+ **Rationale**: Separate data generation from CSV writing. Generate data in memory, optionally write CSVs, always pass data to narrative report.
170
+
171
+ **Change Locations**:
172
+ 1. `analytics_writer.py` lines 316-346 (and similar methods)
173
+ 2. `cli.py` lines 3663-3725
174
+
175
+ **Implementation**:
176
+
177
+ **Step 1**: Modify report generators to return data AND optionally write CSV:
178
+
179
+ ```python
180
+ def generate_qualitative_insights_report(
181
+ self,
182
+ commits: List[Dict[str, Any]],
183
+ developer_stats: List[Dict[str, Any]],
184
+ ticket_analysis: Dict[str, Any],
185
+ output_path: Path | None = None # ⚠️ Make optional
186
+ ) -> tuple[pd.DataFrame, Path | None]: # ⚠️ Return data AND path
187
+ """Generate qualitative insights and patterns report."""
188
+ # Apply exclusion filtering in Phase 2
189
+ commits = self._filter_excluded_authors(commits)
190
+ developer_stats = self._filter_excluded_authors(developer_stats)
191
+ insights = []
192
+
193
+ # Analyze commit patterns
194
+ commit_insights = self._analyze_commit_patterns(commits)
195
+ insights.extend(commit_insights)
196
+
197
+ # ... other analysis steps ...
198
+
199
+ # Create DataFrame
200
+ df = pd.DataFrame(insights)
201
+
202
+ # Write CSV only if path provided
203
+ written_path = None
204
+ if output_path is not None:
205
+ df.to_csv(output_path, index=False)
206
+ written_path = output_path
207
+
208
+ return df, written_path # ⚠️ Return both data and path
209
+ ```
210
+
211
+ **Step 2**: Update CLI to use returned data:
212
+
213
+ ```python
214
+ # Qualitative insights report (always generate data, optionally write CSV)
215
+ insights_report_path = output / f"qualitative_insights_{datetime.now().strftime('%Y%m%d')}.csv" if generate_csv else None
216
+ try:
217
+ logger.debug("Starting qualitative insights report generation")
218
+ insights_df, insights_written_path = analytics_gen.generate_qualitative_insights_report(
219
+ all_commits, developer_stats, ticket_analysis, insights_report_path
220
+ )
221
+ insights_data = cast(list[dict[str, Any]], insights_df.to_dict("records"))
222
+ logger.debug("Qualitative insights report completed successfully")
223
+ if insights_written_path:
224
+ generated_reports.append(insights_written_path.name)
225
+ if not display:
226
+ click.echo(f" ✅ Qualitative insights: {insights_written_path}")
227
+ except Exception as e:
228
+ logger.error(f"Error in qualitative insights report generation: {e}")
229
+ raise
230
+ ```
231
+
232
+ **Step 3**: Update narrative report to use in-memory data:
233
+
234
+ ```python
235
+ # Generate markdown reports if enabled
236
+ if "markdown" in cfg.output.formats:
237
+ # ... narrative generation setup ...
238
+
239
+ # Use in-memory data instead of reading CSVs
240
+ narrative_gen.generate_narrative_report(
241
+ all_commits,
242
+ all_prs,
243
+ developer_stats,
244
+ activity_data, # ⚠️ Use in-memory data from Step 2
245
+ focus_data, # ⚠️ Use in-memory data from Step 2
246
+ insights_data, # ⚠️ Use in-memory data from Step 2
247
+ ticket_analysis,
248
+ pr_metrics,
249
+ narrative_report,
250
+ weeks,
251
+ aggregated_pm_data,
252
+ chatgpt_summary,
253
+ branch_health_metrics,
254
+ cfg.analysis.exclude_authors,
255
+ analysis_start_date=start_date,
256
+ analysis_end_date=end_date,
257
+ )
258
+ ```
259
+
260
+ **Pros**:
261
+ - Clean separation of concerns
262
+ - Narrative reports work with or without CSV generation
263
+ - No unnecessary disk I/O
264
+ - Prevents FileNotFoundError completely
265
+ - More testable (can test data generation without file I/O)
266
+
267
+ **Cons**:
268
+ - More extensive refactoring required
269
+ - Need to update all three report generators
270
+ - Need to update narrative report call
271
+
272
+ ### Option 3: Check File Existence Before Reading (Quick Fix)
273
+
274
+ **Rationale**: Add defensive checks before reading CSVs.
275
+
276
+ **Change Location**: `cli.py` lines 3920-3936
277
+
278
+ **Implementation**:
279
+ ```python
280
+ # Generate markdown reports if enabled
281
+ if "markdown" in cfg.output.formats:
282
+ # Calculate date range for consistent filename formatting across all markdown reports
283
+ date_range = f"{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}"
284
+
285
+ try:
286
+ logger.debug("Starting narrative report generation")
287
+ narrative_gen = NarrativeReportGenerator()
288
+
289
+ # Lazy import pandas - only needed for CSV reading in narrative generation
290
+ import pandas as pd
291
+
292
+ # ⚠️ CHECK FILE EXISTENCE BEFORE READING
293
+ if not (activity_report.exists() and focus_report.exists() and insights_report.exists()):
294
+ logger.warning("CSV files not found, skipping narrative report generation")
295
+ if not display:
296
+ click.echo(" ⚠️ Narrative report skipped: CSV generation was disabled")
297
+ # Skip to next section
298
+ else:
299
+ # Load activity distribution data
300
+ logger.debug("Loading activity distribution data")
301
+ activity_df = pd.read_csv(activity_report)
302
+ # ... rest of the code unchanged ...
303
+ ```
304
+
305
+ **Pros**:
306
+ - Quick fix
307
+ - Prevents crash
308
+ - Minimal code change
309
+
310
+ **Cons**:
311
+ - Doesn't solve the architectural issue
312
+ - Silent failure mode (narrative report silently skipped)
313
+ - Still relies on CSV files existing on disk
314
+
315
+ ## Recommendation
316
+
317
+ **Primary Recommendation**: **Option 2** (Generate In-Memory Data Without Writing CSVs)
318
+
319
+ This is the most robust solution because:
320
+ 1. Eliminates the file dependency completely
321
+ 2. Improves performance (no unnecessary disk I/O)
322
+ 3. Makes the code more testable
323
+ 4. Prevents future FileNotFoundError scenarios
324
+ 5. Aligns with the original intent ("always generate data, optionally write CSV")
325
+
326
+ **Fallback Recommendation**: **Option 1** (Skip Narrative Report When CSV Disabled)
327
+
328
+ If Option 2 is too complex for immediate implementation:
329
+ 1. Apply Option 1 as a quick fix
330
+ 2. File a technical debt ticket for Option 2
331
+ 3. Add a warning message to users when narrative report is skipped
332
+
333
+ **Not Recommended**: Option 3 (defensive file checks) - This is a band-aid that doesn't address the root cause.
334
+
335
+ ## Test Cases Required
336
+
337
+ After implementing the fix, verify:
338
+
339
+ 1. **CSV Generation Enabled + Narrative Enabled**
340
+ - CSVs written to disk ✓
341
+ - Narrative report generated ✓
342
+ - All data consistent ✓
343
+
344
+ 2. **CSV Generation Disabled + Narrative Enabled**
345
+ - CSVs NOT written to disk ✓
346
+ - Narrative report generated (Option 2) or skipped with warning (Option 1) ✓
347
+ - No FileNotFoundError ✓
348
+
349
+ 3. **CSV Generation Enabled + Narrative Disabled**
350
+ - CSVs written to disk ✓
351
+ - Narrative report NOT generated ✓
352
+
353
+ 4. **CSV Generation Disabled + Narrative Disabled**
354
+ - CSVs NOT written to disk ✓
355
+ - Narrative report NOT generated ✓
356
+
357
+ ## Files Requiring Changes
358
+
359
+ ### Option 1 (Conservative Fix)
360
+ - `/Users/masa/Projects/gitflow-analytics/src/gitflow_analytics/cli.py` (line 3912)
361
+
362
+ ### Option 2 (Optimal Fix)
363
+ - `/Users/masa/Projects/gitflow-analytics/src/gitflow_analytics/reports/analytics_writer.py` (lines 316-346, and similar methods)
364
+ - `/Users/masa/Projects/gitflow-analytics/src/gitflow_analytics/cli.py` (lines 3663-3725, 3911-4039)
365
+
366
+ ### Option 3 (Quick Fix)
367
+ - `/Users/masa/Projects/gitflow-analytics/src/gitflow_analytics/cli.py` (lines 3920-3936)
368
+
369
+ ## Additional Notes
370
+
371
+ ### Related Code Patterns
372
+
373
+ The same pattern exists for all three CSV reports:
374
+ 1. **Activity Distribution** (lines 3663-3685)
375
+ 2. **Developer Focus** (lines 3687-3709)
376
+ 3. **Qualitative Insights** (lines 3711-3725)
377
+
378
+ All three need the same fix applied consistently.
379
+
380
+ ### Configuration Context
381
+
382
+ The `generate_csv` flag comes from:
383
+ - CLI flag: `--no-csv` or `--csv`
384
+ - Config file: `output.formats` list (presence of "csv")
385
+
386
+ The narrative report is controlled by:
387
+ - Config file: `output.formats` list (presence of "markdown")
388
+
389
+ Currently, there's no dependency between these two settings, but they should be coupled or at least documented clearly.
390
+
391
+ ### Performance Implications
392
+
393
+ **Option 2 Benefits**:
394
+ - Reduces disk I/O when CSV generation is disabled
395
+ - Faster execution (no write → read cycle)
396
+ - Lower memory pressure (no duplicate data structures)
397
+
398
+ **Current Inefficiency**:
399
+ 1. Generate data in memory → write to CSV
400
+ 2. Read CSV back into memory → convert to dict
401
+ 3. Pass dict to narrative report
402
+
403
+ **Option 2 Flow**:
404
+ 1. Generate data in memory
405
+ 2. Optionally write to CSV (if enabled)
406
+ 3. Pass data directly to narrative report
407
+
408
+ This eliminates the write → read round-trip entirely.
409
+
410
+ ## Conclusion
411
+
412
+ The bug is a logical error where the `generate_csv` flag is not properly checked before attempting to read CSV files in the narrative report generation. The recommended fix (Option 2) involves refactoring the report generators to separate data generation from CSV writing, allowing the narrative report to use in-memory data regardless of CSV generation settings. This provides a more robust, performant, and maintainable solution.
@@ -96,6 +96,9 @@ target-version = "py39"
96
96
  select = ["E", "F", "UP", "B", "SIM", "I"]
97
97
  ignore = ["E501"] # Line too long - handled by formatter
98
98
 
99
+ [tool.ruff.lint.per-file-ignores]
100
+ "tests/**/*.py" = ["E402", "E722", "F401", "SIM102"] # Allow test-specific patterns
101
+
99
102
  [tool.black]
100
103
  line-length = 100
101
104
  target-version = ['py39']
@@ -1,4 +1,4 @@
1
1
  """Version information for gitflow-analytics."""
2
2
 
3
- __version__ = "3.13.5"
3
+ __version__ = "3.13.7"
4
4
  __version_info__ = tuple(int(x) for x in __version__.split("."))
@@ -1590,6 +1590,68 @@ def analyze(
1590
1590
  total_commits += result["stats"]["total_commits"]
1591
1591
  total_tickets += result["stats"]["unique_tickets"]
1592
1592
 
1593
+ # Fetch and enrich with GitHub PRs after data collection
1594
+ if repo_config.github_repo:
1595
+ try:
1596
+ if display:
1597
+ display.print_status(
1598
+ " 📥 Fetching pull requests from GitHub...",
1599
+ "info",
1600
+ )
1601
+
1602
+ # Load commits that were just fetched from cache
1603
+ with cache.get_session() as session:
1604
+ from gitflow_analytics.models.database import CachedCommit
1605
+
1606
+ cached_commits = (
1607
+ session.query(CachedCommit)
1608
+ .filter(
1609
+ CachedCommit.repo_path == str(repo_path),
1610
+ CachedCommit.timestamp >= start_date,
1611
+ CachedCommit.timestamp <= end_date,
1612
+ )
1613
+ .all()
1614
+ )
1615
+
1616
+ # Convert to dict format for enrichment
1617
+ commits_for_enrichment = []
1618
+ for cached_commit in cached_commits:
1619
+ commit_dict = {
1620
+ "hash": cached_commit.commit_hash,
1621
+ "author_name": cached_commit.author_name,
1622
+ "author_email": cached_commit.author_email,
1623
+ "date": cached_commit.timestamp,
1624
+ "message": cached_commit.message,
1625
+ }
1626
+ commits_for_enrichment.append(commit_dict)
1627
+
1628
+ # Enrich with GitHub PR data
1629
+ enrichment = orchestrator.enrich_repository_data(
1630
+ repo_config, commits_for_enrichment, start_date
1631
+ )
1632
+
1633
+ if enrichment["prs"]:
1634
+ pr_count = len(enrichment["prs"])
1635
+ if display:
1636
+ display.print_status(
1637
+ f" ✅ Found {pr_count} pull requests",
1638
+ "success",
1639
+ )
1640
+ else:
1641
+ click.echo(f" ✅ Found {pr_count} pull requests")
1642
+
1643
+ except Exception as e:
1644
+ logger.warning(
1645
+ f"Failed to fetch PRs for {repo_config.github_repo}: {e}"
1646
+ )
1647
+ if display:
1648
+ display.print_status(
1649
+ f" ⚠️ Could not fetch PRs: {e}",
1650
+ "warning",
1651
+ )
1652
+ else:
1653
+ click.echo(f" ⚠️ Could not fetch PRs: {e}")
1654
+
1593
1655
  # Collect unique developers if available
1594
1656
  if "developers" in result["stats"]:
1595
1657
  total_developers.update(result["stats"]["developers"])
@@ -2088,6 +2150,68 @@ def analyze(
2088
2150
  total_commits += result["stats"]["total_commits"]
2089
2151
  total_tickets += result["stats"]["unique_tickets"]
2090
2152
 
2153
+ # Fetch and enrich with GitHub PRs after data collection
2154
+ if repo_config.github_repo:
2155
+ try:
2156
+ if display:
2157
+ display.print_status(
2158
+ " 📥 Fetching pull requests from GitHub...",
2159
+ "info",
2160
+ )
2161
+
2162
+ # Load commits that were just fetched from cache
2163
+ with cache.get_session() as session:
2164
+ from gitflow_analytics.models.database import CachedCommit
2165
+
2166
+ cached_commits = (
2167
+ session.query(CachedCommit)
2168
+ .filter(
2169
+ CachedCommit.repo_path == str(repo_path),
2170
+ CachedCommit.timestamp >= start_date,
2171
+ CachedCommit.timestamp <= end_date,
2172
+ )
2173
+ .all()
2174
+ )
2175
+
2176
+ # Convert to dict format for enrichment
2177
+ commits_for_enrichment = []
2178
+ for cached_commit in cached_commits:
2179
+ commit_dict = {
2180
+ "hash": cached_commit.commit_hash,
2181
+ "author_name": cached_commit.author_name,
2182
+ "author_email": cached_commit.author_email,
2183
+ "date": cached_commit.timestamp,
2184
+ "message": cached_commit.message,
2185
+ }
2186
+ commits_for_enrichment.append(commit_dict)
2187
+
2188
+ # Enrich with GitHub PR data
2189
+ enrichment = orchestrator.enrich_repository_data(
2190
+ repo_config, commits_for_enrichment, start_date
2191
+ )
2192
+
2193
+ if enrichment["prs"]:
2194
+ pr_count = len(enrichment["prs"])
2195
+ if display:
2196
+ display.print_status(
2197
+ f" ✅ Found {pr_count} pull requests",
2198
+ "success",
2199
+ )
2200
+ else:
2201
+ click.echo(f" ✅ Found {pr_count} pull requests")
2202
+
2203
+ except Exception as e:
2204
+ logger.warning(
2205
+ f"Failed to fetch PRs for {repo_config.github_repo}: {e}"
2206
+ )
2207
+ if display:
2208
+ display.print_status(
2209
+ f" ⚠️ Could not fetch PRs: {e}",
2210
+ "warning",
2211
+ )
2212
+ else:
2213
+ click.echo(f" ⚠️ Could not fetch PRs: {e}")
2214
+
2091
2215
  # Collect unique developers if available
2092
2216
  if "developers" in result["stats"]:
2093
2217
  total_developers.update(result["stats"]["developers"])
@@ -3784,8 +3908,8 @@ def analyze(
3784
3908
  traceback.print_exc()
3785
3909
  raise
3786
3910
 
3787
- # Generate markdown reports if enabled
3788
- if "markdown" in cfg.output.formats:
3911
+ # Generate markdown reports if enabled (requires CSV files)
3912
+ if "markdown" in cfg.output.formats and generate_csv:
3789
3913
  # Calculate date range for consistent filename formatting across all markdown reports
3790
3914
  date_range = f"{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}"
3791
3915
 
@@ -3915,6 +4039,15 @@ def analyze(
3915
4039
 
3916
4040
  traceback.print_exc()
3917
4041
  raise
4042
+ elif "markdown" in cfg.output.formats and not generate_csv:
4043
+ # Narrative report requires CSV files, but CSV generation is disabled
4044
+ logger.info(
4045
+ "Skipping narrative report generation - CSV files required but CSV generation is disabled"
4046
+ )
4047
+ if not display:
4048
+ click.echo(
4049
+ " ℹ️ Narrative report skipped (requires CSV files - enable with --csv flag)"
4050
+ )
3918
4051
 
3919
4052
  # Generate database-backed qualitative report
3920
4053
  if "markdown" in cfg.output.formats:
@@ -112,6 +112,80 @@ def setup_git_credentials(token: str, username: str = "git") -> bool:
112
112
  return False
113
113
 
114
114
 
115
+ def ensure_remote_url_has_token(repo_path: Path, token: str) -> bool:
116
+ """Embed GitHub token in remote URL for HTTPS authentication.
117
+
118
+ This is needed because subprocess git operations may not have access
119
+ to the credential helper store due to environment variable restrictions
120
+ (GIT_CREDENTIAL_HELPER="" and GIT_ASKPASS="/bin/echo" in git_timeout_wrapper).
121
+
122
+ Args:
123
+ repo_path: Path to the git repository
124
+ token: GitHub personal access token
125
+
126
+ Returns:
127
+ True if URL was updated with token, False if already has token,
128
+ not applicable (SSH URL), or operation failed
129
+ """
130
+ if not token:
131
+ logger.debug("No token provided, skipping remote URL update")
132
+ return False
133
+
134
+ try:
135
+ # Get current origin remote URL
136
+ result = subprocess.run(
137
+ ["git", "remote", "get-url", "origin"],
138
+ cwd=repo_path,
139
+ capture_output=True,
140
+ text=True,
141
+ check=True,
142
+ )
143
+ current_url = result.stdout.strip()
144
+
145
+ if not current_url:
146
+ logger.debug(f"No origin remote found for {repo_path}")
147
+ return False
148
+
149
+ # Check if it's an HTTPS GitHub URL without embedded token
150
+ if current_url.startswith("https://github.com/"):
151
+ # URL format: https://github.com/org/repo.git
152
+ # New format: https://git:TOKEN@github.com/org/repo.git
153
+ new_url = current_url.replace("https://github.com/", f"https://git:{token}@github.com/")
154
+
155
+ # Update the remote URL
156
+ subprocess.run(
157
+ ["git", "remote", "set-url", "origin", new_url],
158
+ cwd=repo_path,
159
+ capture_output=True,
160
+ text=True,
161
+ check=True,
162
+ )
163
+ logger.debug(f"Updated remote URL with embedded token for {repo_path.name}")
164
+ return True
165
+
166
+ elif "@github.com" in current_url:
167
+ # Already has authentication embedded (either token or SSH)
168
+ logger.debug(f"Remote URL already has authentication for {repo_path.name}")
169
+ return False
170
+
171
+ elif current_url.startswith("git@github.com:"):
172
+ # SSH URL, no need to modify
173
+ logger.debug(f"Using SSH authentication for {repo_path.name}")
174
+ return False
175
+
176
+ else:
177
+ # Unknown URL format
178
+ logger.debug(f"Unknown URL format for {repo_path.name}: {current_url}")
179
+ return False
180
+
181
+ except subprocess.CalledProcessError as e:
182
+ logger.warning(f"Could not update remote URL for {repo_path.name}: {e.stderr}")
183
+ return False
184
+ except Exception as e:
185
+ logger.warning(f"Unexpected error updating remote URL for {repo_path.name}: {e}")
186
+ return False
187
+
188
+
115
189
  def preflight_git_authentication(config: dict) -> bool:
116
190
  """Run pre-flight checks for git authentication and setup credentials.
117
191