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.
- {claude_code_generator-0.5.6/src/claude_code_generator.egg-info → claude_code_generator-0.5.7}/PKG-INFO +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/pyproject.toml +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/agents.py +45 -15
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_dispatch.py +101 -78
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/generate.py +31 -2
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/env.py +27 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase1_plan.py +43 -41
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase5_closure.py +13 -18
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/state.py +10 -1
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_agents.py +139 -0
- claude_code_generator-0.5.7/tests/test_dispatch_graph_report.py +507 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase1.py +15 -60
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/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.7}/LICENSE +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/README.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/setup.cfg +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/SOURCES.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/requires.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/top_level.txt +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/checklist.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/cli.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_bench_io.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_crash_recovery.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_detect.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_resume.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/_validators.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_compare.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_export.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/init.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/optimize.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/commands/status.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/effort.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/core.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/issues.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/labels.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/gh/milestones.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/git_ops.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/logging_setup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/memory.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_cache_warmup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_client_lifecycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_comments.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_memory_writers.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_loop.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/ollama_budget.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase0_complexity.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase2_review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase3_4_implement.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase6_test.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase7_commit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/preflight.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/hashes.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-review.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/repo_info.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/requirements_structure.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/batch.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/cache_breakpoints.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/compaction_pause.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/fake_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/mcp.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/memory_tool.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/message_parsing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/options.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/phase_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/protocol.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/retry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/sdk_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/soft_reset.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/state_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/subprocess_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/types.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/runner/utils.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/state_retention.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/__init__.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/angular.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/base.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/fastapi.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/finance.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/fullstack.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/nestjs.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/src/code_generator/templates/python-cli.md +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_compare.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_export.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_fixture.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_bench_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_breakpoints.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_ttl_ordering.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cache_warmup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_changelog.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_checklist.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_claude_md.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cli_io_logging.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_client_lifecycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_comments.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_commit_message.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_compaction_pause_handler.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_crash_recovery.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_loop.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_loop_multicycle.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_ollama_model.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_cycle_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_delta_planning.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_dependencies.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_detect.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_no_default_max_turns.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_ollama_model_guide.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_docs_ollama_pro.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effective_model_routing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effort.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_effort_routing_consistency.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_env.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate_ollama.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_generate_resume.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_labels.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_milestones.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_repo_threading.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_gh_submodules.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_git_ops.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_init.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_logging_setup.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_max_turns_cli_flag.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_mcp.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory_tool.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_memory_writers.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_message_parsing.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_no_max_turns_in_call_sites.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_no_max_turns_literal.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_non_goals_grep_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_ollama_budget.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_ollama_rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_optimize.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_options.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase0.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_cache_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_multicycle_token_reduction.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase2_token_reduction.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase3_4.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase6.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase7.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_mcp_regression.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_phase_token_logging.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_preflight.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_preflight_ollama.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_drift.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_snapshots.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_stability.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_prompts.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_rate_limit.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_repo_info.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_requirements_structure.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_retry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_review.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_protocol.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_protocol_annotations.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_types.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_runner_utils.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_sdk_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_sdk_runner_shared.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_session_mode.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state_guard.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_state_retention.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_status.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_subprocess_runner.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/tests/test_telemetry.py +0 -0
- {claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/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.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" }
|
|
@@ -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.7}/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,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(
|
|
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":
|
{claude_code_generator-0.5.6 → claude_code_generator-0.5.7}/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
|
|
@@ -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(
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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(
|
|
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
|
-
"""
|
|
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:
|
|
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
|
|
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:
|
|
289
|
-
|
|
290
|
-
|
|
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
|
)
|