claude-code-generator 0.5.6__tar.gz → 0.5.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.
- {claude_code_generator-0.5.6/src/claude_code_generator.egg-info → claude_code_generator-0.5.8}/PKG-INFO +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/pyproject.toml +2 -2
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/claude_code_generator.egg-info/SOURCES.txt +2 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/__init__.py +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/agents.py +45 -15
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_dispatch.py +99 -78
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/generate.py +34 -2
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/env.py +55 -0
- claude_code_generator-0.5.8/src/code_generator/exceptions.py +26 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/cycle_loop.py +43 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase1_plan.py +43 -41
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase5_closure.py +13 -18
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/state.py +10 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_agents.py +139 -0
- claude_code_generator-0.5.8/tests/test_dispatch_graph_report.py +507 -0
- claude_code_generator-0.5.8/tests/test_ollama_hardening.py +366 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase1.py +15 -62
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase5.py +10 -27
- claude_code_generator-0.5.6/tests/test_dispatch_graph_report.py +0 -227
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/LICENSE +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/README.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/setup.cfg +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/claude_code_generator.egg-info/requires.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/claude_code_generator.egg-info/top_level.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/checklist.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/cli.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_bench_io.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_crash_recovery.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_detect.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_resume.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_validators.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/bench.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/bench_compare.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/bench_export.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/init.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/optimize.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/status.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/effort.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/gh/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/gh/core.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/gh/issues.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/gh/labels.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/gh/milestones.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/git_ops.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/logging_setup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/memory.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/_comments.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/_memory_writers.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/ollama_budget.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase2_review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase6_test.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/orchestrator/phase7_commit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/preflight.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/hashes.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/prompts/prompt-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/repo_info.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/requirements_structure.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/batch.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/cache_breakpoints.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/compaction_pause.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/fake_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/mcp.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/memory_tool.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/message_parsing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/options.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/phase_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/protocol.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/retry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/sdk_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/soft_reset.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/state_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/subprocess_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/types.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/runner/utils.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/state_retention.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/angular.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/base.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/fastapi.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/finance.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/fullstack.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/nestjs.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/templates/python-cli.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_bench.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_bench_compare.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_bench_export.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_bench_fixture.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_bench_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cache_breakpoints.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cache_ttl_ordering.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cache_warmup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_changelog.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_checklist.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_claude_md.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cli_io_logging.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_client_lifecycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_comments.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_commit_message.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_compaction_pause_handler.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_crash_recovery.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cycle_loop.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cycle_loop_multicycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cycle_ollama_model.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_cycle_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_delta_planning.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_dependencies.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_detect.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_docs_no_default_max_turns.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_docs_ollama_model_guide.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_docs_ollama_pro.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_effective_model_routing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_effort.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_effort_routing_consistency.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_env.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_generate.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_generate_ollama.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_generate_resume.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_gh.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_gh_labels.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_gh_milestones.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_gh_repo_threading.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_gh_submodules.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_git_ops.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_init.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_logging_setup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_max_turns_cli_flag.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_mcp.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_memory.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_memory_tool.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_memory_writers.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_message_parsing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_no_max_turns_in_call_sites.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_no_max_turns_literal.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_non_goals_grep_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_ollama_budget.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_ollama_rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_optimize.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_options.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase0.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase2.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase2_cache_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase2_multicycle_token_reduction.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase2_token_reduction.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase3_4.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase6.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase7.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase_mcp_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_phase_token_logging.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_preflight.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_preflight_ollama.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_prompt_drift.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_prompt_prefix_snapshots.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_prompt_prefix_stability.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_repo_info.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_requirements_structure.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_retry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_runner_protocol.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_runner_protocol_annotations.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_runner_types.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_runner_utils.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_sdk_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_sdk_runner_shared.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_session_mode.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_state.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_state_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_state_retention.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_status.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_subprocess_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/tests/test_version.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "claude-code-generator"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.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" }
|
|
@@ -96,4 +96,4 @@ asyncio_mode = "auto"
|
|
|
96
96
|
|
|
97
97
|
[tool.pyright]
|
|
98
98
|
# Test mocks intentionally use unused parameters to match the production interface signature.
|
|
99
|
-
reportUnusedParameter =
|
|
99
|
+
reportUnusedParameter = false
|
|
@@ -13,6 +13,7 @@ src/code_generator/checklist.py
|
|
|
13
13
|
src/code_generator/cli.py
|
|
14
14
|
src/code_generator/effort.py
|
|
15
15
|
src/code_generator/env.py
|
|
16
|
+
src/code_generator/exceptions.py
|
|
16
17
|
src/code_generator/git_ops.py
|
|
17
18
|
src/code_generator/logging_setup.py
|
|
18
19
|
src/code_generator/memory.py
|
|
@@ -151,6 +152,7 @@ tests/test_no_max_turns_in_call_sites.py
|
|
|
151
152
|
tests/test_no_max_turns_literal.py
|
|
152
153
|
tests/test_non_goals_grep_guard.py
|
|
153
154
|
tests/test_ollama_budget.py
|
|
155
|
+
tests/test_ollama_hardening.py
|
|
154
156
|
tests/test_ollama_rate_limit.py
|
|
155
157
|
tests/test_optimize.py
|
|
156
158
|
tests/test_options.py
|
|
@@ -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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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))
|
{claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/_dispatch.py
RENAMED
|
@@ -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:
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
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
|
|
59
|
-
"""
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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 .`` (
|
|
75
|
-
|
|
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
|
|
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(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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,7 @@ 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(st, raw_cycles, ollama_model=effective_model)
|
|
293
314
|
|
|
294
315
|
if not new_cycles:
|
|
295
316
|
logger.info("Delta planning: no new cycles detected.")
|
|
@@ -344,7 +365,7 @@ async def dispatch_async(
|
|
|
344
365
|
# Build the codebase graph once at the top of dispatch so Phase 0
|
|
345
366
|
# (complexity analysis) sees a fresh report alongside Phases 1 and 2.
|
|
346
367
|
graph_logger = setup_phase_logger("graph-report", project_dir)
|
|
347
|
-
_compute_and_persist_graph_report(project_dir, graph_logger)
|
|
368
|
+
_compute_and_persist_graph_report(project_dir, graph_logger, ollama_model=ollama_model)
|
|
348
369
|
|
|
349
370
|
# Phase 0: determine mode when not already known.
|
|
350
371
|
if mode == "auto":
|
{claude_code_generator-0.5.6 → claude_code_generator-0.5.8}/src/code_generator/commands/generate.py
RENAMED
|
@@ -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
|
-
|
|
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
|
|
@@ -431,6 +460,9 @@ def generate_ollama_command(
|
|
|
431
460
|
)
|
|
432
461
|
raise typer.Exit(code=2)
|
|
433
462
|
|
|
463
|
+
# #268: advisory warning when the Ollama path carries a claude-* model tag.
|
|
464
|
+
env.warn_on_claude_model_for_ollama(effective_model)
|
|
465
|
+
|
|
434
466
|
# (6) Persist the resolved model on existing cycles before dispatch so
|
|
435
467
|
# --continue resume stays consistent across sessions. New cycles
|
|
436
468
|
# created by phase 0/1 inherit the tag via the orchestrator thread
|
|
@@ -286,6 +286,61 @@ def assert_safe_environment() -> None:
|
|
|
286
286
|
strip_dangerous_env()
|
|
287
287
|
|
|
288
288
|
|
|
289
|
+
def warn_on_claude_model_for_ollama(model: str | None) -> None:
|
|
290
|
+
"""Emit a WARNING when an Ollama-localhost env carries a ``claude-*`` model tag.
|
|
291
|
+
|
|
292
|
+
The Ollama codepath (#218) routes through a local daemon, so a
|
|
293
|
+
``claude-opus-4-7`` / ``claude-sonnet-4-6`` / ``claude-haiku-*`` tag
|
|
294
|
+
almost always indicates a misconfiguration — the operator intended a
|
|
295
|
+
local model but typed an Anthropic model name. The warning is purely
|
|
296
|
+
advisory; the upstream guarantee lives in the orchestrator's
|
|
297
|
+
``_run_phases`` guard, which raises
|
|
298
|
+
:class:`~code_generator.exceptions.OllamaModelRequiredError` for the
|
|
299
|
+
same condition.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
model: The resolved effective model tag, or ``None`` (no-op).
|
|
303
|
+
"""
|
|
304
|
+
if model is not None and model.startswith("claude-"):
|
|
305
|
+
_logger.warning(
|
|
306
|
+
"Ollama daemon routing is active (ANTHROPIC_BASE_URL=%s) "
|
|
307
|
+
"but the model tag %r starts with 'claude-'. "
|
|
308
|
+
"This likely indicates a misconfiguration — did you mean "
|
|
309
|
+
"to use an Ollama model tag (e.g. 'qwen3-coder:480b:cloud')? "
|
|
310
|
+
"Use `code-generator generate ollama --model <tag>` for "
|
|
311
|
+
"single-model Ollama runs.",
|
|
312
|
+
OLLAMA_BASE_URL,
|
|
313
|
+
model,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def assert_safe_environment_custom_model() -> None:
|
|
318
|
+
"""Strip Anthropic API creds but preserve routing for custom model endpoints.
|
|
319
|
+
|
|
320
|
+
When ``--model`` is passed to the default ``generate`` command (not the
|
|
321
|
+
``ollama`` subcommand), the operator's existing ``ANTHROPIC_AUTH_TOKEN``
|
|
322
|
+
and ``ANTHROPIC_BASE_URL`` are preserved so the custom endpoint routing
|
|
323
|
+
survives. Only real Anthropic credit keys are stripped.
|
|
324
|
+
"""
|
|
325
|
+
offenders = [var for var in DANGEROUS_VARS if var in os.environ]
|
|
326
|
+
if offenders:
|
|
327
|
+
listed = ", ".join(offenders)
|
|
328
|
+
print(
|
|
329
|
+
f"Using custom model (preserving routing, ignoring {listed} for this process).",
|
|
330
|
+
file=sys.stderr,
|
|
331
|
+
)
|
|
332
|
+
_strip_dangerous_env_custom()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _strip_dangerous_env_custom() -> None:
|
|
336
|
+
"""Strip DANGEROUS_VARS except ANTHROPIC_AUTH_TOKEN (preserve custom routing)."""
|
|
337
|
+
protected = {"ANTHROPIC_AUTH_TOKEN"}
|
|
338
|
+
for var in DANGEROUS_VARS:
|
|
339
|
+
if var in protected:
|
|
340
|
+
continue
|
|
341
|
+
os.environ.pop(var, None)
|
|
342
|
+
|
|
343
|
+
|
|
289
344
|
def assert_safe_environment_ollama() -> None:
|
|
290
345
|
"""Strip Anthropic env vars and install the scoped Ollama routing in place.
|
|
291
346
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Shared exception classes for the code-generator pipeline.
|
|
2
|
+
|
|
3
|
+
Single-responsibility: domain exception types only — no runner errors
|
|
4
|
+
(those live in runner/types.py), no preflight errors (preflight.py).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OllamaModelRequiredError(RuntimeError):
|
|
11
|
+
"""Raised when the Ollama codepath has no resolvable model tag.
|
|
12
|
+
|
|
13
|
+
On the Ollama single-model path (#218), every phase requires a concrete
|
|
14
|
+
model tag (e.g. ``qwen3-coder:480b:cloud``). This error fires when the
|
|
15
|
+
orchestrator detects that no model is available — either because
|
|
16
|
+
``--model`` wasn't passed on a fresh run, or because the stored
|
|
17
|
+
``CycleState.ollama_model`` is ``None`` on ``--continue``.
|
|
18
|
+
|
|
19
|
+
The ``fix_action`` string is a ready-to-paste remediation for the
|
|
20
|
+
operator; it is included in the error message automatically.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, reason: str, fix_action: str) -> None:
|
|
24
|
+
self.reason = reason
|
|
25
|
+
self.fix_action = fix_action
|
|
26
|
+
super().__init__(f"{reason} {fix_action}")
|
|
@@ -14,11 +14,14 @@ Topological iteration:
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
|
+
import os
|
|
17
18
|
import shutil
|
|
18
19
|
from typing import TYPE_CHECKING, Any, Literal
|
|
19
20
|
|
|
21
|
+
from code_generator import env as _env
|
|
20
22
|
from code_generator import gh, git_ops
|
|
21
23
|
from code_generator import state as _state
|
|
24
|
+
from code_generator.exceptions import OllamaModelRequiredError
|
|
22
25
|
from code_generator.logging_setup import setup_phase_logger
|
|
23
26
|
from code_generator.orchestrator import (
|
|
24
27
|
phase1_plan,
|
|
@@ -171,6 +174,12 @@ def _resolve_effective_model(state: State, cycle: CycleState | None) -> str | No
|
|
|
171
174
|
Priority: cycle.ollama_model (multi-cycle) > state.cycles[-1].ollama_model
|
|
172
175
|
(single-mode fallback when ``cycle`` is not yet bound). The function never
|
|
173
176
|
reads ``state.ollama_model`` because the field is cycle-level per #217.
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
OllamaModelRequiredError: When the Ollama codepath is detected
|
|
180
|
+
(``ANTHROPIC_BASE_URL`` is localhost) and no model tag can be
|
|
181
|
+
resolved from state/cycle — this prevents a silent fallback
|
|
182
|
+
to an Anthropic model name.
|
|
174
183
|
"""
|
|
175
184
|
if cycle is not None and cycle.ollama_model:
|
|
176
185
|
return cycle.ollama_model
|
|
@@ -178,6 +187,21 @@ def _resolve_effective_model(state: State, cycle: CycleState | None) -> str | No
|
|
|
178
187
|
last = state.cycles[-1]
|
|
179
188
|
if isinstance(last, CycleState) and last.ollama_model:
|
|
180
189
|
return last.ollama_model
|
|
190
|
+
|
|
191
|
+
if _env._is_localhost_base_url(os.environ.get("ANTHROPIC_BASE_URL")): # noqa: SLF001
|
|
192
|
+
raise OllamaModelRequiredError(
|
|
193
|
+
reason=(
|
|
194
|
+
"Ollama daemon routing is active but no model tag is resolvable "
|
|
195
|
+
"from state.json. This happens when --continue is used on a "
|
|
196
|
+
"legacy state file that predates the ollama_model field, or "
|
|
197
|
+
"when --model was omitted on a fresh run."
|
|
198
|
+
),
|
|
199
|
+
fix_action=(
|
|
200
|
+
"Re-run with --model <tag> to supply the model explicitly, e.g.: "
|
|
201
|
+
"code-generator generate ollama --model qwen3-coder:480b:cloud"
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
|
|
181
205
|
return None
|
|
182
206
|
|
|
183
207
|
|
|
@@ -231,6 +255,25 @@ async def _run_phases(
|
|
|
231
255
|
if effective_model is None:
|
|
232
256
|
effective_model = _resolve_effective_model(state, cycle)
|
|
233
257
|
|
|
258
|
+
# #268: belt-and-suspenders — when the Ollama codepath is active
|
|
259
|
+
# (effective_model is set) but the tag starts with "claude-", the operator
|
|
260
|
+
# almost certainly intended a local model. Refuse before any phase runs
|
|
261
|
+
# so a mistyped tag never reaches the daemon (which would emit a cryptic
|
|
262
|
+
# "model may not exist").
|
|
263
|
+
if effective_model is not None and effective_model.startswith("claude-"):
|
|
264
|
+
raise OllamaModelRequiredError(
|
|
265
|
+
reason=(
|
|
266
|
+
f"Ollama single-model routing is active but the model tag "
|
|
267
|
+
f"{effective_model!r} starts with 'claude-'. "
|
|
268
|
+
"This likely indicates a misconfiguration — did you mean "
|
|
269
|
+
"to use an Ollama model tag?"
|
|
270
|
+
),
|
|
271
|
+
fix_action=(
|
|
272
|
+
"Pass a valid Ollama model tag instead, e.g.: "
|
|
273
|
+
"code-generator generate ollama --model qwen3-coder:480b:cloud"
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
|
|
234
277
|
# #220: Ollama per-cycle budget tracker. ``effective_model is not None``
|
|
235
278
|
# uniquely identifies the Ollama codepath inside the orchestrator, so we
|
|
236
279
|
# key activation off it rather than threading an extra ``provider`` flag.
|