claude-code-generator 0.4.6__tar.gz → 0.4.8__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 (182) hide show
  1. {claude_code_generator-0.4.6/src/claude_code_generator.egg-info → claude_code_generator-0.4.8}/PKG-INFO +1 -1
  2. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/pyproject.toml +1 -1
  3. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
  4. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/__init__.py +1 -1
  5. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/generate.py +9 -8
  6. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/effort.py +10 -3
  7. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/env.py +54 -14
  8. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_effort.py +19 -0
  9. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_env.py +83 -18
  10. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/LICENSE +0 -0
  11. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/README.md +0 -0
  12. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/setup.cfg +0 -0
  13. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/claude_code_generator.egg-info/SOURCES.txt +0 -0
  14. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
  15. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
  16. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/claude_code_generator.egg-info/requires.txt +0 -0
  17. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/claude_code_generator.egg-info/top_level.txt +0 -0
  18. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/agents.py +0 -0
  19. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/cli.py +0 -0
  20. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/__init__.py +0 -0
  21. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_bench_io.py +0 -0
  22. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_crash_recovery.py +0 -0
  23. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_detect.py +0 -0
  24. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_dispatch.py +0 -0
  25. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_resume.py +0 -0
  26. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/_validators.py +0 -0
  27. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/bench.py +0 -0
  28. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/bench_compare.py +0 -0
  29. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/bench_export.py +0 -0
  30. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/init.py +0 -0
  31. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/optimize.py +0 -0
  32. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/review.py +0 -0
  33. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/commands/status.py +0 -0
  34. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/gh/__init__.py +0 -0
  35. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/gh/core.py +0 -0
  36. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/gh/issues.py +0 -0
  37. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/gh/labels.py +0 -0
  38. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/gh/milestones.py +0 -0
  39. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/git_ops.py +0 -0
  40. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/logging_setup.py +0 -0
  41. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/memory.py +0 -0
  42. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/__init__.py +0 -0
  43. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
  44. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/_comments.py +0 -0
  45. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/_memory_writers.py +0 -0
  46. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
  47. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/cycle_loop.py +0 -0
  48. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
  49. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/ollama_budget.py +0 -0
  50. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
  51. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase1_plan.py +0 -0
  52. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase2_review.py +0 -0
  53. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
  54. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase5_closure.py +0 -0
  55. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase6_test.py +0 -0
  56. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/orchestrator/phase7_commit.py +0 -0
  57. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/preflight.py +0 -0
  58. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/__init__.py +0 -0
  59. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/hashes.py +0 -0
  60. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
  61. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
  62. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
  63. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
  64. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
  65. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-2-issue-review.md +0 -0
  66. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
  67. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
  68. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
  69. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
  70. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/prompts/prompt-review.md +0 -0
  71. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/repo_info.py +0 -0
  72. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/repomap.py +0 -0
  73. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/requirements_structure.py +0 -0
  74. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/__init__.py +0 -0
  75. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/_telemetry.py +0 -0
  76. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/batch.py +0 -0
  77. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/fake_runner.py +0 -0
  78. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/mcp.py +0 -0
  79. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/message_parsing.py +0 -0
  80. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/options.py +0 -0
  81. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/protocol.py +0 -0
  82. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/rate_limit.py +0 -0
  83. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/retry.py +0 -0
  84. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/sdk_runner.py +0 -0
  85. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/soft_reset.py +0 -0
  86. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/state_guard.py +0 -0
  87. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/subprocess_runner.py +0 -0
  88. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/types.py +0 -0
  89. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/runner/utils.py +0 -0
  90. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/state.py +0 -0
  91. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/state_retention.py +0 -0
  92. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/__init__.py +0 -0
  93. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/angular.md +0 -0
  94. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/base.md +0 -0
  95. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/fastapi.md +0 -0
  96. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/finance.md +0 -0
  97. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/fullstack.md +0 -0
  98. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/nestjs.md +0 -0
  99. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/src/code_generator/templates/python-cli.md +0 -0
  100. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_agents.py +0 -0
  101. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_bench.py +0 -0
  102. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_bench_compare.py +0 -0
  103. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_bench_export.py +0 -0
  104. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_bench_fixture.py +0 -0
  105. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_bench_regression.py +0 -0
  106. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_changelog.py +0 -0
  107. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_claude_md.py +0 -0
  108. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_client_lifecycle.py +0 -0
  109. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_comments.py +0 -0
  110. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_commit_message.py +0 -0
  111. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_crash_recovery.py +0 -0
  112. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_cycle_loop.py +0 -0
  113. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_cycle_loop_multicycle.py +0 -0
  114. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_cycle_ollama_model.py +0 -0
  115. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_cycle_prompts.py +0 -0
  116. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_delta_planning.py +0 -0
  117. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_dependencies.py +0 -0
  118. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_detect.py +0 -0
  119. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_docs_no_default_max_turns.py +0 -0
  120. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_docs_ollama_model_guide.py +0 -0
  121. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_docs_ollama_pro.py +0 -0
  122. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_effective_model_routing.py +0 -0
  123. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_generate.py +0 -0
  124. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_generate_ollama.py +0 -0
  125. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_generate_resume.py +0 -0
  126. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_gh.py +0 -0
  127. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_gh_labels.py +0 -0
  128. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_gh_milestones.py +0 -0
  129. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_gh_repo_threading.py +0 -0
  130. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_gh_submodules.py +0 -0
  131. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_git_ops.py +0 -0
  132. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_init.py +0 -0
  133. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_logging_setup.py +0 -0
  134. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_max_turns_cli_flag.py +0 -0
  135. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_mcp.py +0 -0
  136. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_memory.py +0 -0
  137. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_memory_writers.py +0 -0
  138. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_message_parsing.py +0 -0
  139. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_no_max_turns_in_call_sites.py +0 -0
  140. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_no_max_turns_literal.py +0 -0
  141. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_non_goals_grep_guard.py +0 -0
  142. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_ollama_budget.py +0 -0
  143. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_ollama_rate_limit.py +0 -0
  144. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_optimize.py +0 -0
  145. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_options.py +0 -0
  146. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase0.py +0 -0
  147. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase1.py +0 -0
  148. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase2.py +0 -0
  149. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase2_batch.py +0 -0
  150. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase3_4.py +0 -0
  151. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase5.py +0 -0
  152. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase5_precommit.py +0 -0
  153. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase6.py +0 -0
  154. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase7.py +0 -0
  155. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase_mcp_regression.py +0 -0
  156. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_phase_token_logging.py +0 -0
  157. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_preflight.py +0 -0
  158. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_preflight_ollama.py +0 -0
  159. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_prompt_drift.py +0 -0
  160. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_prompt_prefix_snapshots.py +0 -0
  161. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_prompt_prefix_stability.py +0 -0
  162. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_prompts.py +0 -0
  163. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_rate_limit.py +0 -0
  164. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_repo_info.py +0 -0
  165. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_repomap.py +0 -0
  166. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_requirements_structure.py +0 -0
  167. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_retry.py +0 -0
  168. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_review.py +0 -0
  169. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_runner_protocol.py +0 -0
  170. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_runner_protocol_annotations.py +0 -0
  171. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_runner_types.py +0 -0
  172. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_runner_utils.py +0 -0
  173. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_sdk_runner.py +0 -0
  174. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_sdk_runner_shared.py +0 -0
  175. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_session_mode.py +0 -0
  176. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_state.py +0 -0
  177. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_state_guard.py +0 -0
  178. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_state_retention.py +0 -0
  179. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_status.py +0 -0
  180. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_subprocess_runner.py +0 -0
  181. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/tests/test_telemetry.py +0 -0
  182. {claude_code_generator-0.4.6 → claude_code_generator-0.4.8}/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.4.6
3
+ Version: 0.4.8
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.4.6"
7
+ version = "0.4.8"
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.4.6
3
+ Version: 0.4.8
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
@@ -1,3 +1,3 @@
1
1
  """code-generator: orchestrator CLI for end-to-end project generation."""
2
2
 
3
- __version__ = "0.4.6"
3
+ __version__ = "0.4.8"
@@ -396,19 +396,20 @@ def generate_ollama_command(
396
396
 
397
397
  state_path = cg_dir / "state.json"
398
398
 
399
- # (1) Strip any stray ANTHROPIC_* vars from the parent process env so the
400
- # Ollama bypass runs without forcing the operator to `unset` them in their
401
- # shell. Safe here because the scoped env pins ANTHROPIC_BASE_URL to
402
- # localhost no traffic can reach Anthropic.
399
+ # (1) Strip any stray ANTHROPIC_* vars from the parent process env AND
400
+ # install the scoped Ollama routing (ANTHROPIC_AUTH_TOKEN + BASE_URL +
401
+ # empty API_KEY) into os.environ so every in-process SDK session and
402
+ # every subprocess Claude Code spawns inherits the localhost daemon.
403
+ # Without this install step Claude Code rejects non-Claude model tags
404
+ # (e.g. ``glm-5.1:cloud``) — see the 0.4.8 fix.
403
405
  env.assert_safe_environment_ollama()
404
406
 
405
407
  # (2) Ollama preflight — cheap env-var checks then live-daemon probe.
406
408
  _run_ollama_preflight_or_exit()
407
409
 
408
- # (3) Build the scoped Ollama env validates the #215 preconditions.
409
- # We don't pass the dict further; we just confirm the env builds without
410
- # raising. The orchestrator reads OLLAMA_API_KEY / ANTHROPIC_BASE_URL
411
- # when it actually spawns SDK sessions.
410
+ # (3) Re-validate the #215 preconditions defensively. The returned dict is
411
+ # intentionally discarded the SDK and subprocess runners now inherit the
412
+ # Ollama routing from os.environ (installed above in step 1).
412
413
  env.build_agent_env(provider="ollama")
413
414
 
414
415
  # (3) Resolve the effective model. --model wins; on --continue without it,
@@ -101,10 +101,14 @@ _OPUS_ONLY_EFFORTS: frozenset[str] = frozenset({"xhigh", "max"})
101
101
 
102
102
 
103
103
  def validate_effort(effort: str | None, model: str) -> None:
104
- """Raise ValueError when an Opus-only effort is paired with a non-Opus model.
104
+ """Raise ValueError when an Opus-only effort is paired with a non-Opus Claude model.
105
105
 
106
106
  ``xhigh`` and ``max`` are gated to Opus 4.7+ (introduced in 4.7); Sonnet
107
- and Haiku do not expose them.
107
+ and Haiku do not expose them. The constraint only applies to Anthropic
108
+ Claude models (IDs prefixed with ``"claude-"``); for other model families
109
+ — notably the Ollama single-model codepath (#218), which accepts tags like
110
+ ``"glm-5.1:cloud"`` or ``"llama3.2:3b"`` — the effort flag is advisory and
111
+ validation is skipped.
108
112
 
109
113
  Parameters
110
114
  ----------
@@ -112,8 +116,11 @@ def validate_effort(effort: str | None, model: str) -> None:
112
116
  Effort level string (``"low"`` / ``"medium"`` / ``"high"`` / ``"xhigh"``
113
117
  / ``"max"``), or ``None`` (no-op).
114
118
  model:
115
- Anthropic model ID (e.g. ``"claude-opus-4-7"``).
119
+ Anthropic Claude model ID (e.g. ``"claude-opus-4-7"``) or a non-Claude
120
+ tag (Ollama, etc.). Non-Claude tags are not validated.
116
121
  """
122
+ if not model.startswith("claude-"):
123
+ return
117
124
  if effort in _OPUS_ONLY_EFFORTS and model != _OPUS_MODEL:
118
125
  raise ValueError(f"effort={effort!r} is only allowed with {_OPUS_MODEL!r}, got {model!r}.")
119
126
 
@@ -39,8 +39,19 @@ OLLAMA_BASE_URL = "http://localhost:11434"
39
39
 
40
40
 
41
41
  def strip_dangerous_env() -> None:
42
- """Remove every dangerous variable from the current process environment."""
42
+ """Remove every dangerous variable from the current process environment.
43
+
44
+ When ``ANTHROPIC_BASE_URL`` points at a ``http://localhost:*`` endpoint
45
+ (the Ollama single-model codepath — see #218), ``ANTHROPIC_AUTH_TOKEN`` is
46
+ preserved so the scoped Ollama routing installed by
47
+ :func:`assert_safe_environment_ollama` survives per-SDK-run strip calls
48
+ (``sdk_runner.run``, ``run_with_shared_client``).
49
+ """
50
+ ollama_mode = _is_localhost_base_url(os.environ.get("ANTHROPIC_BASE_URL"))
51
+ protected = {"ANTHROPIC_AUTH_TOKEN"} if ollama_mode else set()
43
52
  for var in DANGEROUS_VARS:
53
+ if var in protected:
54
+ continue
44
55
  os.environ.pop(var, None)
45
56
 
46
57
 
@@ -71,8 +82,17 @@ def build_agent_env(provider: Provider = "anthropic-max") -> dict[str, str]:
71
82
 
72
83
 
73
84
  def _build_anthropic_max_env() -> dict[str, str]:
74
- """Strip DANGEROUS_VARS — the default Anthropic Max subscription path."""
75
- return {k: v for k, v in os.environ.items() if k not in DANGEROUS_VARS}
85
+ """Strip DANGEROUS_VARS — the default Anthropic Max subscription path.
86
+
87
+ When ``ANTHROPIC_BASE_URL`` in the parent env points at ``http://localhost:*``
88
+ (the Ollama single-model codepath, #218), ``ANTHROPIC_AUTH_TOKEN`` is
89
+ preserved in the returned dict so the subprocess inherits the Ollama
90
+ auth token. This keeps the default subprocess-runner codepath usable for
91
+ Ollama without threading a ``provider`` argument through every runner.
92
+ """
93
+ ollama_mode = _is_localhost_base_url(os.environ.get("ANTHROPIC_BASE_URL"))
94
+ protected = {"ANTHROPIC_AUTH_TOKEN"} if ollama_mode else set()
95
+ return {k: v for k, v in os.environ.items() if k not in DANGEROUS_VARS or k in protected}
76
96
 
77
97
 
78
98
  def _build_ollama_env() -> dict[str, str]:
@@ -247,24 +267,44 @@ def assert_safe_environment() -> None:
247
267
 
248
268
 
249
269
  def assert_safe_environment_ollama() -> None:
250
- """Strip Anthropic env vars before the Ollama bypass runs.
270
+ """Strip Anthropic env vars and install the scoped Ollama routing in place.
251
271
 
252
272
  Mirrors :func:`assert_safe_environment` for the Ollama codepath: the user
253
273
  does not have to ``unset ANTHROPIC_API_KEY`` in their shell. An Anthropic
254
274
  credential in the parent env is not a safety hazard here — the scoped
255
275
  Ollama env pins ``ANTHROPIC_BASE_URL`` to ``http://localhost:11434`` and
256
276
  re-authenticates with ``OLLAMA_API_KEY``, so no traffic reaches Anthropic.
257
- The in-place strip is defensive: any incidental subprocess that does not
258
- route through :func:`build_agent_env` still inherits a clean env.
277
+
278
+ When ``OLLAMA_API_KEY`` is already present and ``ANTHROPIC_BASE_URL`` is
279
+ unset (or already pinned to localhost), ``ANTHROPIC_AUTH_TOKEN``,
280
+ ``ANTHROPIC_BASE_URL``, and ``ANTHROPIC_API_KEY=""`` are written back
281
+ into ``os.environ`` so every in-process SDK session and every subprocess
282
+ Claude Code spawns inherits the Ollama routing. Without this install
283
+ step, the SDK rejects non-Claude model tags (e.g. ``glm-5.1:cloud``)
284
+ with *"There's an issue with the selected model"* — see the 0.4.8 fix.
285
+
286
+ Missing or mismatched preconditions are **not** raised here; the
287
+ downstream :func:`~code_generator.preflight.run_ollama_preflight` +
288
+ :func:`build_agent_env` pair produce the user-facing error messages
289
+ (with ``fix_command`` hints). Raising from this function would bypass
290
+ those surfaces.
259
291
  """
260
292
  offenders = [var for var in DANGEROUS_VARS if var in os.environ]
261
- if not offenders:
293
+ if offenders:
294
+ listed = ", ".join(offenders)
295
+ print(
296
+ f"Using Ollama daemon at {OLLAMA_BASE_URL} (ignoring {listed} for this process).",
297
+ file=sys.stderr,
298
+ )
299
+ strip_dangerous_env()
300
+
301
+ token = os.environ.get("OLLAMA_API_KEY", "")
302
+ preset_base = os.environ.get("ANTHROPIC_BASE_URL")
303
+ if not token:
304
+ return
305
+ if preset_base and preset_base != OLLAMA_BASE_URL:
262
306
  return
263
307
 
264
- listed = ", ".join(offenders)
265
- print(
266
- f"Using Ollama daemon at {OLLAMA_BASE_URL} "
267
- f"(ignoring {listed} for this process).",
268
- file=sys.stderr,
269
- )
270
- strip_dangerous_env()
308
+ os.environ["ANTHROPIC_AUTH_TOKEN"] = token
309
+ os.environ["ANTHROPIC_BASE_URL"] = OLLAMA_BASE_URL
310
+ os.environ["ANTHROPIC_API_KEY"] = ""
@@ -205,6 +205,25 @@ def test_validate_effort_none_always_passes(model: str) -> None:
205
205
  validate_effort(None, model) # must not raise
206
206
 
207
207
 
208
+ @pytest.mark.parametrize(
209
+ ("effort", "model"),
210
+ [
211
+ ("xhigh", "glm-5.1:cloud"),
212
+ ("max", "llama3.2:3b"),
213
+ ("xhigh", "qwen2.5:7b"),
214
+ ("max", "mistral:latest"),
215
+ ],
216
+ )
217
+ def test_validate_effort_non_claude_models_skip_validation(effort: str, model: str) -> None:
218
+ """Opus-only effort levels are no-ops for non-Claude models (Ollama path, #218).
219
+
220
+ The XHIGH / MAX ceiling only makes sense against Anthropic Claude models;
221
+ Ollama tags like ``glm-5.1:cloud`` do not expose the effort concept, so
222
+ ``validate_effort`` must not raise for them.
223
+ """
224
+ validate_effort(effort, model) # must not raise
225
+
226
+
208
227
  # ---------------------------------------------------------------------------
209
228
  # InvalidEffortInput guard — empty complexity in phases that depend on Phase 0
210
229
  # ---------------------------------------------------------------------------
@@ -35,12 +35,29 @@ class TestStripDangerousEnv:
35
35
  """strip_dangerous_env removes every DANGEROUS_VAR that is present."""
36
36
  for var in DANGEROUS_VARS:
37
37
  monkeypatch.setenv(var, "value")
38
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
38
39
 
39
40
  strip_dangerous_env()
40
41
 
41
42
  for var in DANGEROUS_VARS:
42
43
  assert var not in os.environ
43
44
 
45
+ def test_preserves_auth_token_on_localhost_base_url(
46
+ self, monkeypatch: pytest.MonkeyPatch
47
+ ) -> None:
48
+ """ANTHROPIC_AUTH_TOKEN survives strip when BASE_URL points at localhost (Ollama)."""
49
+ for var in DANGEROUS_VARS:
50
+ monkeypatch.delenv(var, raising=False)
51
+ monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "ollama-token")
52
+ monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-evil")
53
+ monkeypatch.setenv("ANTHROPIC_BASE_URL", "http://localhost:11434")
54
+
55
+ strip_dangerous_env()
56
+
57
+ # AUTH_TOKEN preserved; API_KEY still stripped.
58
+ assert os.environ.get("ANTHROPIC_AUTH_TOKEN") == "ollama-token"
59
+ assert "ANTHROPIC_API_KEY" not in os.environ
60
+
44
61
  def test_no_error_when_vars_absent(self, monkeypatch: pytest.MonkeyPatch) -> None:
45
62
  """strip_dangerous_env must not raise when vars are not present."""
46
63
  for var in DANGEROUS_VARS:
@@ -64,6 +81,7 @@ class TestBuildAgentEnv:
64
81
  """build_agent_env returns a copy with no dangerous vars."""
65
82
  for var in DANGEROUS_VARS:
66
83
  monkeypatch.setenv(var, "should-be-stripped")
84
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
67
85
 
68
86
  env = build_agent_env()
69
87
 
@@ -134,6 +152,7 @@ class TestAssertSafeEnvironment:
134
152
 
135
153
  def test_strips_each_dangerous_var(self, monkeypatch: pytest.MonkeyPatch) -> None:
136
154
  """assert_safe_environment strips any individual dangerous var."""
155
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
137
156
  for dangerous_var in DANGEROUS_VARS:
138
157
  for v in DANGEROUS_VARS:
139
158
  monkeypatch.delenv(v, raising=False)
@@ -144,25 +163,34 @@ class TestAssertSafeEnvironment:
144
163
 
145
164
 
146
165
  class TestAssertSafeEnvironmentOllama:
147
- """The Ollama bypass auto-strips parent ANTHROPIC_* vars instead of refusing."""
166
+ """The Ollama bypass auto-strips parent ANTHROPIC_* vars then installs scoped routing."""
148
167
 
149
- def test_passes_when_no_dangerous_vars(self, monkeypatch: pytest.MonkeyPatch) -> None:
150
- """assert_safe_environment_ollama is silent when env is already clean."""
168
+ def test_installs_auth_token_and_base_url(self, monkeypatch: pytest.MonkeyPatch) -> None:
169
+ """assert_safe_environment_ollama installs the Ollama routing into os.environ."""
151
170
  for var in DANGEROUS_VARS:
152
171
  monkeypatch.delenv(var, raising=False)
172
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
173
+ monkeypatch.setenv("OLLAMA_API_KEY", "ollama-token-xyz")
153
174
 
154
- assert_safe_environment_ollama() # must not raise
175
+ assert_safe_environment_ollama()
176
+
177
+ assert os.environ["ANTHROPIC_AUTH_TOKEN"] == "ollama-token-xyz"
178
+ assert os.environ["ANTHROPIC_BASE_URL"] == OLLAMA_BASE_URL
179
+ assert os.environ["ANTHROPIC_API_KEY"] == ""
155
180
 
156
181
  def test_strips_anthropic_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None:
157
182
  """A stray ANTHROPIC_API_KEY is stripped from os.environ instead of aborting."""
158
183
  monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-real-key")
184
+ monkeypatch.setenv("OLLAMA_API_KEY", "ollama-token")
185
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
159
186
  for var in DANGEROUS_VARS:
160
187
  if var != "ANTHROPIC_API_KEY":
161
188
  monkeypatch.delenv(var, raising=False)
162
189
 
163
190
  assert_safe_environment_ollama()
164
191
 
165
- assert "ANTHROPIC_API_KEY" not in os.environ
192
+ # API_KEY is rewritten to "", not preserved as "sk-real-key".
193
+ assert os.environ.get("ANTHROPIC_API_KEY") == ""
166
194
 
167
195
  def test_warning_mentions_localhost_daemon(
168
196
  self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
@@ -170,6 +198,8 @@ class TestAssertSafeEnvironmentOllama:
170
198
  """The printed warning names the Ollama daemon and the offending vars."""
171
199
  monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-abc")
172
200
  monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "tok")
201
+ monkeypatch.setenv("OLLAMA_API_KEY", "ollama-token")
202
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
173
203
  for var in DANGEROUS_VARS:
174
204
  if var not in ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"):
175
205
  monkeypatch.delenv(var, raising=False)
@@ -181,15 +211,32 @@ class TestAssertSafeEnvironmentOllama:
181
211
  assert "ANTHROPIC_API_KEY" in captured.err
182
212
  assert "ANTHROPIC_AUTH_TOKEN" in captured.err
183
213
 
184
- def test_strips_every_dangerous_var(self, monkeypatch: pytest.MonkeyPatch) -> None:
185
- """assert_safe_environment_ollama strips any individual dangerous var."""
186
- for dangerous_var in DANGEROUS_VARS:
187
- for v in DANGEROUS_VARS:
188
- monkeypatch.delenv(v, raising=False)
214
+ def test_skips_install_when_ollama_api_key_missing(
215
+ self, monkeypatch: pytest.MonkeyPatch
216
+ ) -> None:
217
+ """Without OLLAMA_API_KEY the installer silently skips — preflight surfaces the error."""
218
+ for var in DANGEROUS_VARS:
219
+ monkeypatch.delenv(var, raising=False)
220
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
221
+ monkeypatch.delenv("OLLAMA_API_KEY", raising=False)
189
222
 
190
- monkeypatch.setenv(dangerous_var, "present")
191
- assert_safe_environment_ollama()
192
- assert dangerous_var not in os.environ
223
+ assert_safe_environment_ollama() # must not raise
224
+
225
+ assert "ANTHROPIC_AUTH_TOKEN" not in os.environ
226
+
227
+ def test_skips_install_when_base_url_points_off_host(
228
+ self, monkeypatch: pytest.MonkeyPatch
229
+ ) -> None:
230
+ """Off-host ANTHROPIC_BASE_URL is left alone — preflight raises the user-facing error."""
231
+ for var in DANGEROUS_VARS:
232
+ monkeypatch.delenv(var, raising=False)
233
+ monkeypatch.setenv("OLLAMA_API_KEY", "ollama-token")
234
+ monkeypatch.setenv("ANTHROPIC_BASE_URL", "http://evil.example.com")
235
+
236
+ assert_safe_environment_ollama() # must not raise
237
+
238
+ assert os.environ["ANTHROPIC_BASE_URL"] == "http://evil.example.com"
239
+ assert "ANTHROPIC_AUTH_TOKEN" not in os.environ
193
240
 
194
241
 
195
242
  class TestAssertSingleWorkspace:
@@ -201,8 +248,11 @@ class TestAssertSingleWorkspace:
201
248
 
202
249
  assert_single_workspace(probe=probe) # must not raise
203
250
 
204
- def test_two_workspace_mock_raises_workspace_mismatch_error(self) -> None:
251
+ def test_two_workspace_mock_raises_workspace_mismatch_error(
252
+ self, monkeypatch: pytest.MonkeyPatch
253
+ ) -> None:
205
254
  """Two-workspace probe must raise WorkspaceMismatchError with workspace names."""
255
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
206
256
 
207
257
  def probe() -> frozenset[str]:
208
258
  return frozenset({"ws-a", "ws-b"})
@@ -215,9 +265,10 @@ class TestAssertSingleWorkspace:
215
265
  assert issubclass(WorkspaceMismatchError, RuntimeError)
216
266
 
217
267
  def test_sdk_without_probe_logs_info_exactly_once(
218
- self, caplog: pytest.LogCaptureFixture
268
+ self, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch
219
269
  ) -> None:
220
270
  """When probe returns None (SDK lacks workspace probe), logs INFO exactly once."""
271
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
221
272
 
222
273
  def probe() -> None:
223
274
  return None
@@ -261,6 +312,7 @@ class TestBuildAgentEnvAnthropicMaxProvider:
261
312
  """build_agent_env() without provider kwarg uses anthropic-max."""
262
313
  for var in DANGEROUS_VARS:
263
314
  monkeypatch.setenv(var, "value")
315
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
264
316
 
265
317
  env = build_agent_env() # no kwarg
266
318
 
@@ -271,12 +323,27 @@ class TestBuildAgentEnvAnthropicMaxProvider:
271
323
  """Explicit provider="anthropic-max" behaves identically to the default."""
272
324
  for var in DANGEROUS_VARS:
273
325
  monkeypatch.setenv(var, "value")
326
+ monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
274
327
 
275
328
  env = build_agent_env(provider="anthropic-max")
276
329
 
277
330
  for var in DANGEROUS_VARS:
278
331
  assert var not in env
279
332
 
333
+ def test_anthropic_max_preserves_auth_token_on_localhost_base_url(
334
+ self, monkeypatch: pytest.MonkeyPatch
335
+ ) -> None:
336
+ """When BASE_URL points at localhost (Ollama codepath), AUTH_TOKEN survives."""
337
+ for var in DANGEROUS_VARS:
338
+ monkeypatch.delenv(var, raising=False)
339
+ monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "ollama-token")
340
+ monkeypatch.setenv("ANTHROPIC_BASE_URL", "http://localhost:11434")
341
+
342
+ env = build_agent_env(provider="anthropic-max")
343
+
344
+ assert env.get("ANTHROPIC_AUTH_TOKEN") == "ollama-token"
345
+ assert env.get("ANTHROPIC_BASE_URL") == "http://localhost:11434"
346
+
280
347
  def test_anthropic_max_preserves_unrelated_vars(self, monkeypatch: pytest.MonkeyPatch) -> None:
281
348
  """anthropic-max path leaves unrelated env vars intact."""
282
349
  monkeypatch.setenv("PATH", "/usr/bin")
@@ -338,9 +405,7 @@ class TestBuildAgentEnvOllamaProvider:
338
405
 
339
406
  assert env["ANTHROPIC_AUTH_TOKEN"] == "ollama-wins"
340
407
 
341
- def test_ollama_ignores_parent_anthropic_api_key(
342
- self, monkeypatch: pytest.MonkeyPatch
343
- ) -> None:
408
+ def test_ollama_ignores_parent_anthropic_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None:
344
409
  """provider=ollama rewrites ANTHROPIC_API_KEY="" regardless of parent value.
345
410
 
346
411
  The builder strips every ANTHROPIC_* var from the returned dict and pins