claude-code-generator 0.5.6__tar.gz → 0.5.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 (198) hide show
  1. {claude_code_generator-0.5.6/src/claude_code_generator.egg-info → claude_code_generator-0.5.7}/PKG-INFO +1 -1
  2. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/pyproject.toml +1 -1
  3. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
  4. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/agents.py +45 -15
  5. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_dispatch.py +101 -78
  6. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/generate.py +31 -2
  7. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/env.py +27 -0
  8. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase1_plan.py +43 -41
  9. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase5_closure.py +13 -18
  10. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/state.py +10 -1
  11. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_agents.py +139 -0
  12. claude_code_generator-0.5.7/tests/test_dispatch_graph_report.py +507 -0
  13. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase1.py +15 -60
  14. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase5.py +10 -27
  15. claude_code_generator-0.5.6/tests/test_dispatch_graph_report.py +0 -227
  16. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/LICENSE +0 -0
  17. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/README.md +0 -0
  18. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/setup.cfg +0 -0
  19. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/SOURCES.txt +0 -0
  20. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
  21. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
  22. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/requires.txt +0 -0
  23. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/top_level.txt +0 -0
  24. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/__init__.py +0 -0
  25. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/checklist.py +0 -0
  26. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/cli.py +0 -0
  27. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/__init__.py +0 -0
  28. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_bench_io.py +0 -0
  29. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_crash_recovery.py +0 -0
  30. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_detect.py +0 -0
  31. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_resume.py +0 -0
  32. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_validators.py +0 -0
  33. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench.py +0 -0
  34. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_compare.py +0 -0
  35. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_export.py +0 -0
  36. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/init.py +0 -0
  37. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/optimize.py +0 -0
  38. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/review.py +0 -0
  39. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/status.py +0 -0
  40. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/effort.py +0 -0
  41. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/__init__.py +0 -0
  42. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/core.py +0 -0
  43. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/issues.py +0 -0
  44. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/labels.py +0 -0
  45. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/milestones.py +0 -0
  46. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/git_ops.py +0 -0
  47. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/logging_setup.py +0 -0
  48. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/memory.py +0 -0
  49. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/__init__.py +0 -0
  50. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
  51. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
  52. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_comments.py +0 -0
  53. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_memory_writers.py +0 -0
  54. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
  55. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_loop.py +0 -0
  56. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
  57. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/ollama_budget.py +0 -0
  58. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
  59. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase2_review.py +0 -0
  60. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
  61. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase6_test.py +0 -0
  62. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase7_commit.py +0 -0
  63. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/preflight.py +0 -0
  64. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/__init__.py +0 -0
  65. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/hashes.py +0 -0
  66. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
  67. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
  68. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
  69. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
  70. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
  71. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
  72. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
  73. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
  74. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
  75. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-review.md +0 -0
  76. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/repo_info.py +0 -0
  77. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/requirements_structure.py +0 -0
  78. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/__init__.py +0 -0
  79. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/_telemetry.py +0 -0
  80. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/batch.py +0 -0
  81. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/cache_breakpoints.py +0 -0
  82. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/compaction_pause.py +0 -0
  83. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/fake_runner.py +0 -0
  84. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/mcp.py +0 -0
  85. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/memory_tool.py +0 -0
  86. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/message_parsing.py +0 -0
  87. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/options.py +0 -0
  88. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/phase_telemetry.py +0 -0
  89. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/protocol.py +0 -0
  90. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/rate_limit.py +0 -0
  91. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/retry.py +0 -0
  92. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/sdk_runner.py +0 -0
  93. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/soft_reset.py +0 -0
  94. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/state_guard.py +0 -0
  95. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/subprocess_runner.py +0 -0
  96. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/types.py +0 -0
  97. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/utils.py +0 -0
  98. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/state_retention.py +0 -0
  99. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/__init__.py +0 -0
  100. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/angular.md +0 -0
  101. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/base.md +0 -0
  102. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/fastapi.md +0 -0
  103. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/finance.md +0 -0
  104. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/fullstack.md +0 -0
  105. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/nestjs.md +0 -0
  106. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/python-cli.md +0 -0
  107. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench.py +0 -0
  108. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_compare.py +0 -0
  109. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_export.py +0 -0
  110. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_fixture.py +0 -0
  111. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_regression.py +0 -0
  112. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_breakpoints.py +0 -0
  113. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_ttl_ordering.py +0 -0
  114. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_warmup.py +0 -0
  115. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_changelog.py +0 -0
  116. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_checklist.py +0 -0
  117. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_claude_md.py +0 -0
  118. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cli_io_logging.py +0 -0
  119. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_client_lifecycle.py +0 -0
  120. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_comments.py +0 -0
  121. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_commit_message.py +0 -0
  122. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_compaction_pause_handler.py +0 -0
  123. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_crash_recovery.py +0 -0
  124. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_loop.py +0 -0
  125. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_loop_multicycle.py +0 -0
  126. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_ollama_model.py +0 -0
  127. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_prompts.py +0 -0
  128. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_delta_planning.py +0 -0
  129. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_dependencies.py +0 -0
  130. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_detect.py +0 -0
  131. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_no_default_max_turns.py +0 -0
  132. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_ollama_model_guide.py +0 -0
  133. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_ollama_pro.py +0 -0
  134. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effective_model_routing.py +0 -0
  135. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effort.py +0 -0
  136. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effort_routing_consistency.py +0 -0
  137. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_env.py +0 -0
  138. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate.py +0 -0
  139. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate_ollama.py +0 -0
  140. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate_resume.py +0 -0
  141. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh.py +0 -0
  142. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_labels.py +0 -0
  143. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_milestones.py +0 -0
  144. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_repo_threading.py +0 -0
  145. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_submodules.py +0 -0
  146. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_git_ops.py +0 -0
  147. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_init.py +0 -0
  148. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_logging_setup.py +0 -0
  149. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_max_turns_cli_flag.py +0 -0
  150. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_mcp.py +0 -0
  151. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory.py +0 -0
  152. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory_tool.py +0 -0
  153. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory_writers.py +0 -0
  154. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_message_parsing.py +0 -0
  155. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_no_max_turns_in_call_sites.py +0 -0
  156. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_no_max_turns_literal.py +0 -0
  157. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_non_goals_grep_guard.py +0 -0
  158. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_ollama_budget.py +0 -0
  159. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_ollama_rate_limit.py +0 -0
  160. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_optimize.py +0 -0
  161. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_options.py +0 -0
  162. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase0.py +0 -0
  163. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2.py +0 -0
  164. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_cache_regression.py +0 -0
  165. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_multicycle_token_reduction.py +0 -0
  166. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_token_reduction.py +0 -0
  167. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase3_4.py +0 -0
  168. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase5_precommit.py +0 -0
  169. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase6.py +0 -0
  170. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase7.py +0 -0
  171. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_mcp_regression.py +0 -0
  172. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_telemetry.py +0 -0
  173. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_token_logging.py +0 -0
  174. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_preflight.py +0 -0
  175. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_preflight_ollama.py +0 -0
  176. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_drift.py +0 -0
  177. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_snapshots.py +0 -0
  178. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_stability.py +0 -0
  179. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompts.py +0 -0
  180. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_rate_limit.py +0 -0
  181. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_repo_info.py +0 -0
  182. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_requirements_structure.py +0 -0
  183. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_retry.py +0 -0
  184. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_review.py +0 -0
  185. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_protocol.py +0 -0
  186. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_protocol_annotations.py +0 -0
  187. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_types.py +0 -0
  188. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_utils.py +0 -0
  189. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_sdk_runner.py +0 -0
  190. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_sdk_runner_shared.py +0 -0
  191. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_session_mode.py +0 -0
  192. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state.py +0 -0
  193. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state_guard.py +0 -0
  194. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state_retention.py +0 -0
  195. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_status.py +0 -0
  196. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_subprocess_runner.py +0 -0
  197. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_telemetry.py +0 -0
  198. {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "claude-code-generator"
7
- version = "0.5.6"
7
+ version = "0.5.7"
8
8
  description = "Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -74,6 +74,38 @@ _PYTHON_CLI = AgentSelection(
74
74
  )
75
75
 
76
76
 
77
+ # ---------------------------------------------------------------------------
78
+ # Extra-skill detection
79
+ # ---------------------------------------------------------------------------
80
+
81
+ _EXTRA_SKILL_KEYWORDS: tuple[tuple[tuple[str, ...], tuple[str, ...]], ...] = (
82
+ (("supabase",), ("supabase", "supabase-postgres-best-practices")),
83
+ (
84
+ ("skfolio", "portfolio optim", "efficient frontier", "mean-variance", "risk parity"),
85
+ ("skfolio",),
86
+ ),
87
+ (("yfinance", "yahoo finance", "ohlcv", "stock data", "ticker data"), ("yfinance",)),
88
+ (("xlsx", ".xlsx", " excel", "spreadsheet"), ("xlsx",)),
89
+ (("docx", ".docx", "word document"), ("docx",)),
90
+ )
91
+
92
+
93
+ def _detect_extra_skills(block: str) -> tuple[str, ...]:
94
+ extras: list[str] = []
95
+ for keywords, skills in _EXTRA_SKILL_KEYWORDS:
96
+ if any(kw in block for kw in keywords):
97
+ extras.extend(skills)
98
+ return tuple(extras)
99
+
100
+
101
+ def _merge(base: AgentSelection, extras: tuple[str, ...]) -> AgentSelection:
102
+ if not extras:
103
+ return base
104
+ existing = set(base.skills)
105
+ new_skills = base.skills + tuple(s for s in extras if s not in existing)
106
+ return AgentSelection(primary=base.primary, support=base.support, skills=new_skills)
107
+
108
+
77
109
  # ---------------------------------------------------------------------------
78
110
  # Public API
79
111
  # ---------------------------------------------------------------------------
@@ -131,18 +163,16 @@ def detect(requirements_path: Path) -> AgentSelection:
131
163
  has_fastapi = "fastapi" in block or "python api" in block
132
164
 
133
165
  if has_angular and has_fastapi:
134
- return _ANGULAR_FASTAPI
135
-
136
- if has_angular:
137
- return _ANGULAR_ONLY
138
-
139
- if has_fastapi:
140
- return _FASTAPI
141
-
142
- if "baml" in block or "llm function" in block:
143
- return _BAML
144
-
145
- if "typer" in block or "click" in block or "python cli" in block:
146
- return _PYTHON_CLI
147
-
148
- return _GENERIC_FALLBACK
166
+ base = _ANGULAR_FASTAPI
167
+ elif has_angular:
168
+ base = _ANGULAR_ONLY
169
+ elif has_fastapi:
170
+ base = _FASTAPI
171
+ elif "baml" in block or "llm function" in block:
172
+ base = _BAML
173
+ elif "typer" in block or "click" in block or "python cli" in block:
174
+ base = _PYTHON_CLI
175
+ else:
176
+ base = _GENERIC_FALLBACK
177
+
178
+ return _merge(base, _detect_extra_skills(block))
@@ -37,14 +37,51 @@ __all__ = ["dispatch_async", "dispatch_orchestrator"]
37
37
 
38
38
  _GRAPHIFY_TIMEOUT = int(os.environ.get("CODE_GENERATOR_GRAPHIFY_TIMEOUT", "600"))
39
39
 
40
+ _GRAPHIFY_EXTRACT_CMD = ["graphify", "extract", "."]
41
+ _GRAPHIFY_UPDATE_CMD = ["graphify", "update", "."]
40
42
 
41
43
  _SETUP_HINT = (
42
- "graph-report: graphify-out/graph.json not found run `/graphify .` once "
43
- "inside Claude Code (or another graphify-compatible assistant) to seed the "
44
- "knowledge graph. After that, code-generator will keep it fresh "
45
- "automatically with `graphify update .`. Using fallback for now."
44
+ "graph-report: headless extraction via `graphify extract .` was attempted "
45
+ "automatically but failed ensure graphify >= 0.7.3 is installed "
46
+ "(`pipx install graphifyy`) or run `/graphify .` once inside Claude Code "
47
+ "to seed the knowledge graph manually. Using fallback for now."
46
48
  )
47
49
 
50
+ _GRAPHIFY_MIN_VERSION = (0, 7, 3)
51
+ _GRAPHIFY_UPGRADE_HINT = (
52
+ "upgrade for headless seeding and v0.5.5 workaround removal: pip install -U graphifyy"
53
+ )
54
+
55
+
56
+ def _parse_version_line(stdout: str) -> str | None:
57
+ """Return 'X.Y.Z' from pip-show stdout, or None when the line is absent."""
58
+ for line in stdout.splitlines():
59
+ if line.startswith("Version:"):
60
+ return line.split(":", 1)[1].strip()
61
+ return None
62
+
63
+
64
+ def _get_graphify_version_str() -> str | None:
65
+ """Probe installed graphifyy version via pip show; None on any failure."""
66
+ try:
67
+ result = subprocess.run(
68
+ ["pip", "show", "graphifyy"], capture_output=True, text=True, check=False
69
+ )
70
+ return _parse_version_line(result.stdout or "")
71
+ except (OSError, subprocess.SubprocessError):
72
+ return None
73
+
74
+
75
+ def _probe_graphify_version(log: logging.Logger) -> None:
76
+ """Warn when graphify is below _GRAPHIFY_MIN_VERSION or undetectable."""
77
+ version_str = _get_graphify_version_str()
78
+ if version_str is None:
79
+ log.warning("graph-report: graphify version unknown — %s", _GRAPHIFY_UPGRADE_HINT)
80
+ return
81
+ version = tuple(int(p) for p in version_str.split(".") if p.isdigit())
82
+ if version < _GRAPHIFY_MIN_VERSION:
83
+ log.warning("graph-report: graphify %s detected — %s", version_str, _GRAPHIFY_UPGRADE_HINT)
84
+
48
85
 
49
86
  def _decode_stderr(stderr: bytes | str | None) -> str:
50
87
  """Decode subprocess stderr to a short single-line string for logging."""
@@ -55,42 +92,65 @@ def _decode_stderr(stderr: bytes | str | None) -> str:
55
92
  return text[:500]
56
93
 
57
94
 
58
- def _compute_and_persist_graph_report(project_dir: Path, log: logging.Logger) -> None:
59
- """Refresh the codebase graph via graphify and persist the report.
95
+ def _extract_argv(ollama_model: str | None) -> list[str]:
96
+ """Build graphify extract argv, appending --backend ollama on the Ollama path."""
97
+ if ollama_model is not None:
98
+ return [*_GRAPHIFY_EXTRACT_CMD, "--backend", "ollama"]
99
+ return list(_GRAPHIFY_EXTRACT_CMD)
100
+
101
+
102
+ def _run_graphify(
103
+ cmd: list[str],
104
+ project_dir: Path,
105
+ log: logging.Logger,
106
+ memories_dir: Path,
107
+ ) -> bool:
108
+ """Run a graphify subprocess. Write fallback and return False on any error."""
109
+ try:
110
+ log.info("graph-report: running %s", " ".join(cmd))
111
+ subprocess.run( # noqa: S603
112
+ cmd,
113
+ cwd=project_dir,
114
+ timeout=_GRAPHIFY_TIMEOUT,
115
+ capture_output=True,
116
+ check=True,
117
+ )
118
+ return True
119
+ except subprocess.CalledProcessError as exc:
120
+ stderr = _decode_stderr(exc.stderr)
121
+ log.warning(
122
+ "graph-report: graphify exited %d; using fallback. stderr: %s",
123
+ exc.returncode,
124
+ stderr or "<empty>",
125
+ )
126
+ except subprocess.TimeoutExpired as exc:
127
+ log.warning("graph-report: graphify timed out after %ss; using fallback", exc.timeout)
128
+ except (FileNotFoundError, OSError) as exc:
129
+ log.warning("graph-report: could not invoke graphify (%s); using fallback", exc)
130
+ _memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
131
+ return False
60
132
 
61
- Graphify's shell CLI does **not** support a "build" subcommand — the full
62
- LLM-driven build only happens through the ``/graphify`` slash-command
63
- inside an AI assistant (Claude Code, Codex, Cursor, etc.). The shell
64
- binary only supports maintenance subcommands like ``update``, ``watch``,
65
- ``query``.
133
+
134
+ def _compute_and_persist_graph_report(
135
+ project_dir: Path, log: logging.Logger, *, ollama_model: str | None = None
136
+ ) -> None:
137
+ """Refresh the codebase graph via graphify and persist the report.
66
138
 
67
139
  Strategy:
68
140
 
69
- 1. If ``graphify-out/graph.json`` does **not** exist, the user has never
70
- seeded the graph. Log a one-time setup hint, write the fallback
71
- sentinel, and return do not call subprocess. The user must run
72
- ``/graphify .`` in their assistant once to seed it.
141
+ 1. If ``graphify-out/graph.json`` does **not** exist, run
142
+ ``graphify extract .`` (headless LLM-driven seeding, requires >= 0.7.3).
143
+ On success, read ``GRAPH_REPORT.md`` and persist. On failure, write the
144
+ fallback sentinel and emit :data:`_SETUP_HINT`.
73
145
 
74
- 2. If it exists, run ``graphify update .`` (cwd=project_dir). This is
75
- AST-only re-extraction, no LLM cost, fast on cached files. It
76
- refreshes ``graph.json`` and ``GRAPH_REPORT.md``. Doc / paper /
77
- image changes are *not* picked up — for those the user must run
78
- ``/graphify .`` again in their assistant.
146
+ 2. If it exists, run ``graphify update .`` (AST-only refresh, no LLM cost).
147
+ Refreshes ``graph.json`` and ``GRAPH_REPORT.md``.
79
148
 
80
149
  Falls back to :data:`code_generator.memory.REPOMAP_FALLBACK` on any
81
150
  failure (binary missing, timeout, non-zero exit, missing report file).
82
- Stderr from graphify is captured and logged at WARN level on failure so
83
- the user can diagnose without re-running by hand.
84
-
85
- Auth: graphify inherits the parent process env. ``graphify update`` is
86
- AST-only and makes no LLM calls, so OAuth context is irrelevant on this
87
- codepath — but we still don't strip ``ANTHROPIC_*`` because the startup
88
- ``env.assert_safe_environment()`` check (CLAUDE.md non-negotiable #1)
89
- already guarantees those vars are absent on the Anthropic Max path.
90
151
 
91
152
  Args:
92
- project_dir: Project root directory (graphify writes ``graphify-out/``
93
- into this directory).
153
+ project_dir: Project root directory.
94
154
  log: Phase logger.
95
155
  """
96
156
  memories_dir = project_dir / ".code-generator" / "memories"
@@ -106,53 +166,14 @@ def _compute_and_persist_graph_report(project_dir: Path, log: logging.Logger) ->
106
166
  return
107
167
 
108
168
  if not graph_json.exists():
109
- log.info(_SETUP_HINT)
110
- _memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
111
- return
112
-
113
- cmd = ["graphify", "update", "."]
114
- # GRAPHIFY_NO_TIPS=1 suppresses the tip-printer that has a NameError in
115
- # graphify v0.5.5 (_os not in scope outside the install block).
116
- os.environ.setdefault("GRAPHIFY_NO_TIPS", "1")
117
- try:
118
- log.info("graph-report: refreshing (cmd=%s)", " ".join(cmd))
119
- subprocess.run( # noqa: S603
120
- cmd,
121
- cwd=project_dir,
122
- timeout=_GRAPHIFY_TIMEOUT,
123
- capture_output=True,
124
- check=True,
125
- )
126
- except subprocess.CalledProcessError as exc:
127
- stderr = _decode_stderr(exc.stderr)
128
- # graphify v0.5.5 has a NameError in its tip-printer (_os not in scope)
129
- # that fires after a successful update — treat it as success so the
130
- # real GRAPH_REPORT.md is used instead of the fallback sentinel.
131
- if "NameError: name '_os' is not defined" in (stderr or ""):
132
- log.info(
133
- "graph-report: graphify tip-printer bug (v0.5.5) — graph updated; ignoring exit 1"
134
- )
135
- else:
136
- log.warning(
137
- "graph-report: graphify exited %d; using fallback. stderr: %s",
138
- exc.returncode,
139
- stderr or "<empty>",
140
- )
141
- _memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
169
+ log.info("graph-report: graph.json absent — attempting headless extraction")
170
+ if not _run_graphify(_extract_argv(ollama_model), project_dir, log, memories_dir):
171
+ log.info(_SETUP_HINT)
172
+ return
173
+ else:
174
+ _probe_graphify_version(log)
175
+ if not _run_graphify(_GRAPHIFY_UPDATE_CMD, project_dir, log, memories_dir):
142
176
  return
143
- except subprocess.TimeoutExpired as exc:
144
- log.warning(
145
- "graph-report: graphify timed out after %ss; using fallback",
146
- exc.timeout,
147
- )
148
- _memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
149
- return
150
- except (FileNotFoundError, OSError) as exc:
151
- # `FileNotFoundError` here means the `graphify` binary disappeared
152
- # between `shutil.which` and `subprocess.run`. Extremely unlikely.
153
- log.warning("graph-report: could not invoke graphify (%s); using fallback", exc)
154
- _memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
155
- return
156
177
 
157
178
  try:
158
179
  content = report_path.read_text(encoding="utf-8")
@@ -289,7 +310,9 @@ async def _apply_delta_plan(
289
310
  "Phase 0 failed during delta planning; aborting to preserve cycle history."
290
311
  )
291
312
 
292
- new_cycles = state_module.append_new_cycles(st, raw_cycles)
313
+ new_cycles = state_module.append_new_cycles(
314
+ st, raw_cycles, ollama_model=effective_model
315
+ )
293
316
 
294
317
  if not new_cycles:
295
318
  logger.info("Delta planning: no new cycles detected.")
@@ -344,7 +367,7 @@ async def dispatch_async(
344
367
  # Build the codebase graph once at the top of dispatch so Phase 0
345
368
  # (complexity analysis) sees a fresh report alongside Phases 1 and 2.
346
369
  graph_logger = setup_phase_logger("graph-report", project_dir)
347
- _compute_and_persist_graph_report(project_dir, graph_logger)
370
+ _compute_and_persist_graph_report(project_dir, graph_logger, ollama_model=ollama_model)
348
371
 
349
372
  # Phase 0: determine mode when not already known.
350
373
  if mode == "auto":
@@ -147,6 +147,20 @@ def _persist_repo_info(
147
147
  @generate_app.callback(invoke_without_command=True)
148
148
  def generate_command(
149
149
  ctx: typer.Context,
150
+ model: Annotated[
151
+ str | None,
152
+ typer.Option(
153
+ "--model",
154
+ help=(
155
+ "Model tag to use for ALL phases (single-model routing). "
156
+ "When set, overrides the per-phase Opus/Sonnet/Haiku defaults "
157
+ "and routes every phase through the operator's configured endpoint. "
158
+ "Preserves existing ANTHROPIC_AUTH_TOKEN + ANTHROPIC_BASE_URL from "
159
+ "the parent environment."
160
+ ),
161
+ show_default=False,
162
+ ),
163
+ ] = None,
150
164
  dry_run: Annotated[
151
165
  bool,
152
166
  typer.Option("--dry-run/--no-dry-run", help="Print what would happen without doing it."),
@@ -192,12 +206,21 @@ def generate_command(
192
206
  ),
193
207
  ] = None,
194
208
  ) -> None:
195
- """Run the full code-generation pipeline (default: Anthropic Max)."""
209
+ """Run the full code-generation pipeline (default: Anthropic Max).
210
+
211
+ Pass --model to route all phases through a custom endpoint (e.g. a local
212
+ Ollama daemon or a cloud proxy) instead of Anthropic Max. When --model is
213
+ set, the existing ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL from the
214
+ parent environment are preserved.
215
+ """
196
216
  # A subcommand like `generate ollama` has its own body; skip the default flow.
197
217
  if ctx.invoked_subcommand is not None:
198
218
  return
199
219
 
200
- env.assert_safe_environment()
220
+ if model is not None:
221
+ env.assert_safe_environment_custom_model()
222
+ else:
223
+ env.assert_safe_environment()
201
224
  env.assert_single_workspace()
202
225
 
203
226
  if phase is not None and continue_:
@@ -235,6 +258,11 @@ def generate_command(
235
258
  # Persist the fresh RepoInfo atomically; warns when it differs from stored value.
236
259
  _persist_repo_info(st, repo_info, state_path)
237
260
 
261
+ # When --model is set, stamp the tag on existing cycles so --continue
262
+ # resolves the correct model without the operator re-typing --model.
263
+ if model is not None:
264
+ _stamp_model_on_existing_cycles(st, model, state_path)
265
+
238
266
  # Rate-limit pause guard: inform user and exit cleanly.
239
267
  if _check_paused(st):
240
268
  raise typer.Exit(code=0)
@@ -262,6 +290,7 @@ def generate_command(
262
290
  session_mode=effective_session_mode,
263
291
  session_mode_explicit=session_mode_explicit,
264
292
  max_turns=max_turns,
293
+ ollama_model=model,
265
294
  )
266
295
  except typer.Exit:
267
296
  raise
@@ -286,6 +286,33 @@ def assert_safe_environment() -> None:
286
286
  strip_dangerous_env()
287
287
 
288
288
 
289
+ def assert_safe_environment_custom_model() -> None:
290
+ """Strip Anthropic API creds but preserve routing for custom model endpoints.
291
+
292
+ When ``--model`` is passed to the default ``generate`` command (not the
293
+ ``ollama`` subcommand), the operator's existing ``ANTHROPIC_AUTH_TOKEN``
294
+ and ``ANTHROPIC_BASE_URL`` are preserved so the custom endpoint routing
295
+ survives. Only real Anthropic credit keys are stripped.
296
+ """
297
+ offenders = [var for var in DANGEROUS_VARS if var in os.environ]
298
+ if offenders:
299
+ listed = ", ".join(offenders)
300
+ print(
301
+ f"Using custom model (preserving routing, ignoring {listed} for this process).",
302
+ file=sys.stderr,
303
+ )
304
+ _strip_dangerous_env_custom()
305
+
306
+
307
+ def _strip_dangerous_env_custom() -> None:
308
+ """Strip DANGEROUS_VARS except ANTHROPIC_AUTH_TOKEN (preserve custom routing)."""
309
+ protected = {"ANTHROPIC_AUTH_TOKEN"}
310
+ for var in DANGEROUS_VARS:
311
+ if var in protected:
312
+ continue
313
+ os.environ.pop(var, None)
314
+
315
+
289
316
  def assert_safe_environment_ollama() -> None:
290
317
  """Strip Anthropic env vars and install the scoped Ollama routing in place.
291
318
 
@@ -38,19 +38,6 @@ class Phase1NoIssuesError(RuntimeError):
38
38
  """Phase 1 completed but created zero GitHub issues — abort the pipeline."""
39
39
 
40
40
 
41
- class MissingSpecializedPromptError(RuntimeError):
42
- """Multi-cycle: per-cycle specialized prompt file is missing or hash unset.
43
-
44
- Raised from :func:`_load_specialized_prompt` when ``state.mode == "multi-cycle"``
45
- and either ``state.requirements_hash`` is ``None`` or the expected
46
- ``cycle_<id>.md`` file does not exist under
47
- ``.code-generator/cycles_prompts/<requirements_hash>/``.
48
-
49
- Operator fix: re-run ``code-generator generate --phase 0 --cycle <K>`` to
50
- regenerate the per-cycle briefings.
51
- """
52
-
53
-
54
41
  def _deduplicate_by_title(issues: list[dict[str, Any]]) -> list[dict[str, Any]]: # type: ignore[type-arg]
55
42
  """Return only the lowest-numbered issue per case-insensitive stripped title.
56
43
 
@@ -174,29 +161,42 @@ def _accumulate_usage(
174
161
  )
175
162
 
176
163
 
177
- def _multi_cycle_briefing(project_dir: Path, state: State, cycle: CycleState) -> str:
178
- """Load the per-cycle briefing or raise if the hash/file is missing."""
164
+ def _multi_cycle_briefing(
165
+ project_dir: Path,
166
+ state: State,
167
+ cycle: CycleState,
168
+ logger: logging.Logger | None = None,
169
+ ) -> str:
170
+ """Load the per-cycle briefing or fall back to raw requirements.
171
+
172
+ When the cycle prompt file is missing (e.g. because
173
+ :func:`~code_generator.orchestrator.cycle_prompts.generate_cycle_prompts`
174
+ failed during Phase 0 — common with non-Anthropic models that struggle with
175
+ the specializer JSON format), a WARNING is emitted and the raw
176
+ ``requirements.md`` is returned as a safe fallback. The operator can re-run
177
+ ``--phase 0 --cycle N`` later to regenerate the specialized briefings.
178
+ """
179
179
  if state.requirements_hash is None:
180
- raise MissingSpecializedPromptError(
181
- "multi-cycle Phase 1 requires state.requirements_hash to be set; "
182
- f"got None for cycle {cycle.id}. Re-run "
183
- f"`code-generator generate --phase 0 --cycle {cycle.id}` to regenerate."
184
- )
180
+ if logger:
181
+ logger.warning(
182
+ "cycle-prompts: requirements_hash is None for cycle %d "
183
+ "falling back to raw requirements.",
184
+ cycle.id,
185
+ )
186
+ return _single_cycle_fallback(project_dir)
185
187
  try:
186
188
  return _cycle_prompts.load_cycle_prompt(project_dir, state.requirements_hash, cycle.id)
187
- except FileNotFoundError as exc:
188
- expected = (
189
- project_dir
190
- / ".code-generator"
191
- / "cycles_prompts"
192
- / state.requirements_hash
193
- / f"cycle_{cycle.id}.md"
194
- )
195
- raise MissingSpecializedPromptError(
196
- f"multi-cycle Phase 1 expected specialized briefing at {expected} "
197
- f"but it is missing. Re-run "
198
- f"`code-generator generate --phase 0 --cycle {cycle.id}` to regenerate."
199
- ) from exc
189
+ except FileNotFoundError:
190
+ if logger:
191
+ logger.warning(
192
+ "cycle-prompts: specialized briefing missing for cycle %d (hash=%s) — "
193
+ "falling back to raw requirements. Re-run "
194
+ "`code-generator generate --phase 0 --cycle %d` to regenerate.",
195
+ cycle.id,
196
+ state.requirements_hash,
197
+ cycle.id,
198
+ )
199
+ return _single_cycle_fallback(project_dir)
200
200
 
201
201
 
202
202
  def _single_cycle_fallback(project_dir: Path) -> str:
@@ -213,21 +213,21 @@ def _load_specialized_prompt(
213
213
  project_dir: Path,
214
214
  state: State,
215
215
  cycle: CycleState | None,
216
+ logger: logging.Logger | None = None,
216
217
  ) -> str:
217
218
  """Return the cycle-specialized briefing or the single-cycle fallback.
218
219
 
219
- Multi-cycle mode (``state.mode == "multi-cycle"`` with ``cycle is not None``)
220
- must read the verbatim per-cycle briefing from
221
- ``.code-generator/cycles_prompts/<requirements_hash>/cycle_<id>.md``. A
222
- missing hash or missing file raises :class:`MissingSpecializedPromptError`
223
- so the operator can repair the cycles_prompts/ folder before tokens burn
224
- on a malformed Phase 1 prompt (issue #232).
220
+ Multi-cycle mode reads the verbatim per-cycle briefing from
221
+ ``.code-generator/cycles_prompts/<requirements_hash>/cycle_<id>.md``.
222
+ When the file is missing (common when the cycle-prompts specializer call
223
+ fails with non-Anthropic models), falls back to raw ``requirements.md``
224
+ with a warning instead of crashing the pipeline.
225
225
 
226
226
  Single-cycle mode (``cycle is None``) keeps the historic raw-requirements
227
227
  fallback because there is no per-cycle briefing to load.
228
228
  """
229
229
  if state.mode == "multi-cycle" and cycle is not None:
230
- return _multi_cycle_briefing(project_dir, state, cycle)
230
+ return _multi_cycle_briefing(project_dir, state, cycle, logger=logger)
231
231
  return _single_cycle_fallback(project_dir)
232
232
 
233
233
 
@@ -296,7 +296,9 @@ async def run(
296
296
  repo_map = _memory.read_cycle_repomap(memories_dir)
297
297
  prompt = load_prompt(
298
298
  "prompt-phase-1-planning.md",
299
- CYCLE_SPECIALIZED_PROMPT=_load_specialized_prompt(project_dir, state, cycle),
299
+ CYCLE_SPECIALIZED_PROMPT=_load_specialized_prompt(
300
+ project_dir, state, cycle, logger=logger
301
+ ),
300
302
  CYCLE_SCOPE=(
301
303
  cycle.scope
302
304
  if cycle is not None
@@ -26,7 +26,6 @@ from code_generator.runner.cache_breakpoints import resolve_active_ttl
26
26
  from code_generator.runner.mcp import build_mcp_servers, mcp_tool_wildcards
27
27
  from code_generator.runner.options import make_agent_options, max_turns_kwargs
28
28
  from code_generator.runner.phase_telemetry import accumulate_phase_telemetry
29
- from code_generator.runner.sdk_runner import run_with_shared_client
30
29
  from code_generator.runner.types import TokenUsage
31
30
 
32
31
  if TYPE_CHECKING:
@@ -183,27 +182,24 @@ async def _run_closure(
183
182
  options: Any,
184
183
  *,
185
184
  runner_module: RunnerProtocol,
186
- shared_client: Any | None,
187
185
  state_path: Path,
188
186
  logger: logging.Logger,
189
187
  ) -> Any:
190
- """Dispatch the closure session to shared-client or fresh-runner path.
188
+ """Run the closure session via one-shot subprocess.
189
+
190
+ Phase 5 always uses the fresh runner so the reviewer sees only artefacts,
191
+ not the Phase 3/4 implementation conversation.
191
192
 
192
193
  Args:
193
194
  prompt: The prompt text to send.
194
195
  options: Agent options (model, tools, etc.).
195
- runner_module: Fresh-mode runner (used only when shared_client is None).
196
- shared_client: Open ClaudeSDKClient for shared-session mode, or None.
196
+ runner_module: One-shot subprocess runner.
197
197
  state_path: Path to state.json for rate-limit persistence.
198
198
  logger: Phase logger.
199
199
 
200
200
  Returns:
201
- RunResult from whichever path was taken.
201
+ RunResult from rate_limit.main_loop.
202
202
  """
203
- if shared_client is not None:
204
- return await run_with_shared_client(
205
- shared_client, prompt, options, logger=logger, state_path=state_path
206
- )
207
203
  return await rate_limit.main_loop(
208
204
  runner_module, prompt, options, state_path=state_path, logger=logger
209
205
  )
@@ -273,24 +269,24 @@ async def run(
273
269
  *,
274
270
  runner_module: RunnerProtocol,
275
271
  logger: logging.Logger | None = None,
276
- shared_client: Any = None,
272
+ shared_client: Any = None, # accepted for call-site compat; Phase 5 uses one-shot subprocess
277
273
  max_turns: int | None = None,
278
274
  effective_model: str | None = None,
279
275
  ) -> FixResult:
280
276
  """Execute phase 5: overall review and closure.
281
277
 
278
+ Phase 5 always runs in one-shot subprocess mode so the reviewer sees only
279
+ the final artefacts, not the Phase 3/4 implementation conversation.
280
+
282
281
  Args:
283
282
  state: Root state object (mutated in place).
284
283
  cycle: Active cycle (multi-cycle) or ``None`` (single mode).
285
284
  project_dir: Project root directory.
286
285
  runner_module: Runner module from ``get_runner()``.
287
286
  logger: Optional phase logger.
288
- shared_client: Open ``ClaudeSDKClient`` injected by the orchestrator
289
- in shared-session mode; ``None`` in fresh mode. When set, the
290
- closure session is dispatched via ``run_with_shared_client()`` so
291
- the Phase 2 + Phase 3/4 cache prefix is preserved. No
292
- ``soft_reset_context()`` call is made — Phase 5 runs once per
293
- cycle (not per issue).
287
+ shared_client: Accepted but ignored Phase 5 deliberately runs one-shot.
288
+ The shared client lifecycle is managed by ``managed_shared_client``
289
+ in ``cycle_loop.py`` and remains open after Phase 5 returns.
294
290
 
295
291
  Returns:
296
292
  :class:`FixResult` listing any new fix issues discovered after the
@@ -355,7 +351,6 @@ async def run(
355
351
  prompt,
356
352
  options,
357
353
  runner_module=runner_module,
358
- shared_client=shared_client,
359
354
  state_path=state_path,
360
355
  logger=logger,
361
356
  )