claude-code-generator 0.5.5__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.5/src/claude_code_generator.egg-info → claude_code_generator-0.5.7}/PKG-INFO +1 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/pyproject.toml +1 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7/src/claude_code_generator.egg-info}/PKG-INFO +1 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/SOURCES.txt +17 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/__init__.py +1 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/agents.py +45 -15
- claude_code_generator-0.5.7/src/code_generator/checklist.py +121 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_bench_io.py +6 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_dispatch.py +109 -70
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/bench.py +40 -7
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_compare.py +52 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/generate.py +31 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/status.py +107 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/effort.py +26 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/env.py +27 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/gh/__init__.py +2 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/gh/issues.py +16 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/memory.py +1 -3
- claude_code_generator-0.5.7/src/code_generator/orchestrator/_cache_warmup.py +74 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_client_lifecycle.py +104 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_loop.py +47 -6
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/ollama_budget.py +2 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase0_complexity.py +31 -14
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase1_plan.py +108 -32
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase2_review.py +49 -15
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase3_4_implement.py +82 -17
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase5_closure.py +27 -23
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase6_test.py +49 -6
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/phase7_commit.py +39 -4
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/preflight.py +10 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-6-test.md +2 -1
- claude_code_generator-0.5.7/src/code_generator/runner/cache_breakpoints.py +356 -0
- claude_code_generator-0.5.7/src/code_generator/runner/compaction_pause.py +69 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/fake_runner.py +55 -6
- claude_code_generator-0.5.7/src/code_generator/runner/memory_tool.py +187 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/message_parsing.py +63 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/options.py +240 -20
- claude_code_generator-0.5.7/src/code_generator/runner/phase_telemetry.py +85 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/sdk_runner.py +69 -8
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/soft_reset.py +62 -4
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/subprocess_runner.py +5 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/types.py +32 -10
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/state.py +66 -4
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_agents.py +138 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_bench.py +89 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_bench_compare.py +143 -11
- claude_code_generator-0.5.7/tests/test_cache_breakpoints.py +300 -0
- claude_code_generator-0.5.7/tests/test_cache_ttl_ordering.py +230 -0
- claude_code_generator-0.5.7/tests/test_cache_warmup.py +557 -0
- claude_code_generator-0.5.7/tests/test_checklist.py +250 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_cli_io_logging.py +2 -6
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_client_lifecycle.py +182 -0
- claude_code_generator-0.5.7/tests/test_compaction_pause_handler.py +336 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_cycle_loop.py +1 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_cycle_loop_multicycle.py +1 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_cycle_prompts.py +71 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_dependencies.py +3 -3
- claude_code_generator-0.5.7/tests/test_dispatch_graph_report.py +507 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_effective_model_routing.py +2 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_effort.py +28 -3
- claude_code_generator-0.5.7/tests/test_effort_routing_consistency.py +63 -0
- claude_code_generator-0.5.7/tests/test_memory_tool.py +272 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_memory_writers.py +131 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_message_parsing.py +73 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_ollama_budget.py +2 -6
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_options.py +442 -30
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase0.py +2 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase1.py +201 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase2.py +129 -3
- claude_code_generator-0.5.7/tests/test_phase2_cache_regression.py +189 -0
- claude_code_generator-0.5.7/tests/test_phase2_multicycle_token_reduction.py +251 -0
- claude_code_generator-0.5.7/tests/test_phase2_token_reduction.py +186 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase3_4.py +87 -8
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase5.py +10 -27
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase6.py +114 -2
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase7.py +274 -0
- claude_code_generator-0.5.7/tests/test_phase_telemetry.py +185 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase_token_logging.py +3 -3
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_preflight.py +45 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_runner_types.py +133 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_sdk_runner.py +219 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_state.py +238 -12
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_state_retention.py +2 -1
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_status.py +294 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_subprocess_runner.py +1 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_telemetry.py +3 -2
- claude_code_generator-0.5.5/tests/test_dispatch_graph_report.py +0 -229
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/LICENSE +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/README.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/setup.cfg +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/requires.txt +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/claude_code_generator.egg-info/top_level.txt +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/cli.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/__init__.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_crash_recovery.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_detect.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_resume.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_validators.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/bench_export.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/init.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/optimize.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/review.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/gh/core.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/gh/labels.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/gh/milestones.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/git_ops.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/logging_setup.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/__init__.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_comments.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_memory_writers.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/orchestrator/cycle_prompts.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/__init__.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/hashes.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-cycle-specializer.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-optimize-requirements.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-0-complexity.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-1-planning.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-2-batch-review.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-3-implementation.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-5-final-review.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-phase-7-commit.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/prompts/prompt-review.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/repo_info.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/requirements_structure.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/__init__.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/_telemetry.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/batch.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/mcp.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/protocol.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/rate_limit.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/retry.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/state_guard.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/runner/utils.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/state_retention.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/__init__.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/angular.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/base.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/fastapi.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/finance.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/fullstack.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/nestjs.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/templates/python-cli.md +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_bench_export.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_bench_fixture.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_bench_regression.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_changelog.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_claude_md.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_comments.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_commit_message.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_crash_recovery.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_cycle_ollama_model.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_delta_planning.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_detect.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_docs_no_default_max_turns.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_docs_ollama_model_guide.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_docs_ollama_pro.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_env.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_generate.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_generate_ollama.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_generate_resume.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_gh.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_gh_labels.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_gh_milestones.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_gh_repo_threading.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_gh_submodules.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_git_ops.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_init.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_logging_setup.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_max_turns_cli_flag.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_mcp.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_memory.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_no_max_turns_in_call_sites.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_no_max_turns_literal.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_non_goals_grep_guard.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_ollama_rate_limit.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_optimize.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase5_precommit.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_phase_mcp_regression.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_preflight_ollama.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_prompt_drift.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_snapshots.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_prompt_prefix_stability.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_prompts.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_rate_limit.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_repo_info.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_requirements_structure.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_retry.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_review.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_runner_protocol.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_runner_protocol_annotations.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_runner_utils.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_sdk_runner_shared.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_session_mode.py +0 -0
- {claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/tests/test_state_guard.py +0 -0
- {claude_code_generator-0.5.5 → 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" }
|
|
@@ -9,6 +9,7 @@ src/claude_code_generator.egg-info/requires.txt
|
|
|
9
9
|
src/claude_code_generator.egg-info/top_level.txt
|
|
10
10
|
src/code_generator/__init__.py
|
|
11
11
|
src/code_generator/agents.py
|
|
12
|
+
src/code_generator/checklist.py
|
|
12
13
|
src/code_generator/cli.py
|
|
13
14
|
src/code_generator/effort.py
|
|
14
15
|
src/code_generator/env.py
|
|
@@ -41,6 +42,7 @@ src/code_generator/gh/issues.py
|
|
|
41
42
|
src/code_generator/gh/labels.py
|
|
42
43
|
src/code_generator/gh/milestones.py
|
|
43
44
|
src/code_generator/orchestrator/__init__.py
|
|
45
|
+
src/code_generator/orchestrator/_cache_warmup.py
|
|
44
46
|
src/code_generator/orchestrator/_client_lifecycle.py
|
|
45
47
|
src/code_generator/orchestrator/_comments.py
|
|
46
48
|
src/code_generator/orchestrator/_memory_writers.py
|
|
@@ -70,10 +72,14 @@ src/code_generator/prompts/prompt-review.md
|
|
|
70
72
|
src/code_generator/runner/__init__.py
|
|
71
73
|
src/code_generator/runner/_telemetry.py
|
|
72
74
|
src/code_generator/runner/batch.py
|
|
75
|
+
src/code_generator/runner/cache_breakpoints.py
|
|
76
|
+
src/code_generator/runner/compaction_pause.py
|
|
73
77
|
src/code_generator/runner/fake_runner.py
|
|
74
78
|
src/code_generator/runner/mcp.py
|
|
79
|
+
src/code_generator/runner/memory_tool.py
|
|
75
80
|
src/code_generator/runner/message_parsing.py
|
|
76
81
|
src/code_generator/runner/options.py
|
|
82
|
+
src/code_generator/runner/phase_telemetry.py
|
|
77
83
|
src/code_generator/runner/protocol.py
|
|
78
84
|
src/code_generator/runner/rate_limit.py
|
|
79
85
|
src/code_generator/runner/retry.py
|
|
@@ -97,12 +103,17 @@ tests/test_bench_compare.py
|
|
|
97
103
|
tests/test_bench_export.py
|
|
98
104
|
tests/test_bench_fixture.py
|
|
99
105
|
tests/test_bench_regression.py
|
|
106
|
+
tests/test_cache_breakpoints.py
|
|
107
|
+
tests/test_cache_ttl_ordering.py
|
|
108
|
+
tests/test_cache_warmup.py
|
|
100
109
|
tests/test_changelog.py
|
|
110
|
+
tests/test_checklist.py
|
|
101
111
|
tests/test_claude_md.py
|
|
102
112
|
tests/test_cli_io_logging.py
|
|
103
113
|
tests/test_client_lifecycle.py
|
|
104
114
|
tests/test_comments.py
|
|
105
115
|
tests/test_commit_message.py
|
|
116
|
+
tests/test_compaction_pause_handler.py
|
|
106
117
|
tests/test_crash_recovery.py
|
|
107
118
|
tests/test_cycle_loop.py
|
|
108
119
|
tests/test_cycle_loop_multicycle.py
|
|
@@ -117,6 +128,7 @@ tests/test_docs_ollama_model_guide.py
|
|
|
117
128
|
tests/test_docs_ollama_pro.py
|
|
118
129
|
tests/test_effective_model_routing.py
|
|
119
130
|
tests/test_effort.py
|
|
131
|
+
tests/test_effort_routing_consistency.py
|
|
120
132
|
tests/test_env.py
|
|
121
133
|
tests/test_generate.py
|
|
122
134
|
tests/test_generate_ollama.py
|
|
@@ -132,6 +144,7 @@ tests/test_logging_setup.py
|
|
|
132
144
|
tests/test_max_turns_cli_flag.py
|
|
133
145
|
tests/test_mcp.py
|
|
134
146
|
tests/test_memory.py
|
|
147
|
+
tests/test_memory_tool.py
|
|
135
148
|
tests/test_memory_writers.py
|
|
136
149
|
tests/test_message_parsing.py
|
|
137
150
|
tests/test_no_max_turns_in_call_sites.py
|
|
@@ -144,12 +157,16 @@ tests/test_options.py
|
|
|
144
157
|
tests/test_phase0.py
|
|
145
158
|
tests/test_phase1.py
|
|
146
159
|
tests/test_phase2.py
|
|
160
|
+
tests/test_phase2_cache_regression.py
|
|
161
|
+
tests/test_phase2_multicycle_token_reduction.py
|
|
162
|
+
tests/test_phase2_token_reduction.py
|
|
147
163
|
tests/test_phase3_4.py
|
|
148
164
|
tests/test_phase5.py
|
|
149
165
|
tests/test_phase5_precommit.py
|
|
150
166
|
tests/test_phase6.py
|
|
151
167
|
tests/test_phase7.py
|
|
152
168
|
tests/test_phase_mcp_regression.py
|
|
169
|
+
tests/test_phase_telemetry.py
|
|
153
170
|
tests/test_phase_token_logging.py
|
|
154
171
|
tests/test_preflight.py
|
|
155
172
|
tests/test_preflight_ollama.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))
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Feature completion checklist (issue #249).
|
|
2
|
+
|
|
3
|
+
Persists a JSON array of per-issue completion entries at
|
|
4
|
+
``.code-generator/checklists/<requirements_hash>.json``. Phase 1 seeds the
|
|
5
|
+
file; Phase 6 ticks ``tests_passed``; Phase 7 ticks ``committed``;
|
|
6
|
+
``feature_done`` is derived (``tests_passed AND committed``) so the model
|
|
7
|
+
cannot self-report completion.
|
|
8
|
+
|
|
9
|
+
All writes follow the ``tmp → os.replace(tmp, path)`` pattern so a crash
|
|
10
|
+
mid-write cannot corrupt the file. The module is orchestrator-only state —
|
|
11
|
+
the SDK runner has no Write access to ``.code-generator/checklists/``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
from dataclasses import asdict, dataclass
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import Iterable
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
from code_generator.state import IssueState
|
|
27
|
+
|
|
28
|
+
_logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class ChecklistEntry:
|
|
33
|
+
"""One row in the per-feature completion checklist."""
|
|
34
|
+
|
|
35
|
+
issue_number: int
|
|
36
|
+
title: str
|
|
37
|
+
tests_passed: bool = False
|
|
38
|
+
committed: bool = False
|
|
39
|
+
feature_done: bool = False
|
|
40
|
+
|
|
41
|
+
def recompute_feature_done(self) -> None:
|
|
42
|
+
"""Set ``feature_done`` from the two underlying flags."""
|
|
43
|
+
self.feature_done = self.tests_passed and self.committed
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def seed(path: Path, issues: Iterable[IssueState]) -> None:
|
|
47
|
+
"""Atomically write the checklist for *issues* to *path* (idempotent).
|
|
48
|
+
|
|
49
|
+
Re-seeding with the same data is a no-op (file content stays byte-identical).
|
|
50
|
+
Re-seeding with new entries appends them while preserving any existing
|
|
51
|
+
ticks on the entries already in the file.
|
|
52
|
+
"""
|
|
53
|
+
new_entries = [_entry_from_issue(issue) for issue in issues]
|
|
54
|
+
existing = {e.issue_number: e for e in load(path)}
|
|
55
|
+
merged: list[ChecklistEntry] = []
|
|
56
|
+
for fresh in new_entries:
|
|
57
|
+
prior = existing.get(fresh.issue_number)
|
|
58
|
+
merged.append(prior if prior is not None else fresh)
|
|
59
|
+
_atomic_write_json(path, [asdict(e) for e in merged])
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def load(path: Path) -> list[ChecklistEntry]:
|
|
63
|
+
"""Return the checklist entries; ``[]`` when the file is absent."""
|
|
64
|
+
if not path.exists():
|
|
65
|
+
return []
|
|
66
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
67
|
+
return [_entry_from_dict(item) for item in raw]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def tick_tests_passed(path: Path, issue_number: int) -> None:
|
|
71
|
+
"""Set ``tests_passed=True`` on *issue_number*; recompute ``feature_done``."""
|
|
72
|
+
_tick(path, issue_number, "tests_passed")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def tick_committed(path: Path, issue_number: int) -> None:
|
|
76
|
+
"""Set ``committed=True`` on *issue_number*; recompute ``feature_done``."""
|
|
77
|
+
_tick(path, issue_number, "committed")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def done_total(path: Path) -> tuple[int, int]:
|
|
81
|
+
"""Return ``(feature_done_count, total_entries)`` — used by ``status``."""
|
|
82
|
+
entries = load(path)
|
|
83
|
+
return sum(1 for e in entries if e.feature_done), len(entries)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _tick(path: Path, issue_number: int, field: str) -> None:
|
|
87
|
+
"""Mutate *field* to True for *issue_number*; warn + no-op when missing."""
|
|
88
|
+
entries = load(path)
|
|
89
|
+
target = next((e for e in entries if e.issue_number == issue_number), None)
|
|
90
|
+
if target is None:
|
|
91
|
+
_logger.warning(
|
|
92
|
+
"checklist tick: issue_number=%s not found in %s — no-op.",
|
|
93
|
+
issue_number,
|
|
94
|
+
path,
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
setattr(target, field, True)
|
|
98
|
+
target.recompute_feature_done()
|
|
99
|
+
_atomic_write_json(path, [asdict(e) for e in entries])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _entry_from_issue(issue: IssueState) -> ChecklistEntry:
|
|
103
|
+
return ChecklistEntry(issue_number=issue.number, title=issue.title)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _entry_from_dict(item: dict) -> ChecklistEntry:
|
|
107
|
+
return ChecklistEntry(
|
|
108
|
+
issue_number=int(item["issue_number"]),
|
|
109
|
+
title=str(item.get("title", "")),
|
|
110
|
+
tests_passed=bool(item.get("tests_passed", False)),
|
|
111
|
+
committed=bool(item.get("committed", False)),
|
|
112
|
+
feature_done=bool(item.get("feature_done", False)),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _atomic_write_json(path: Path, payload: list[dict]) -> None:
|
|
117
|
+
"""``tmp.write_text(...) → os.replace(tmp, path)`` atomic write."""
|
|
118
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
tmp_path = path.with_suffix(path.suffix + ".tmp")
|
|
120
|
+
tmp_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
121
|
+
os.replace(tmp_path, path)
|
{claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_bench_io.py
RENAMED
|
@@ -17,7 +17,12 @@ if TYPE_CHECKING:
|
|
|
17
17
|
|
|
18
18
|
# Bump this + add a migration note for any breaking schema change.
|
|
19
19
|
# v2 (issue #212): per-phase dicts + totals now include ``num_turns``.
|
|
20
|
-
|
|
20
|
+
# v3 (issue #248): per-phase dicts + totals add ``cache_write_5m``,
|
|
21
|
+
# ``cache_write_1h``, ``compaction_events``, ``clear_events``, and
|
|
22
|
+
# ``cache_hit_ratio`` (0.0-1.0 float). v2 readers still parse the original
|
|
23
|
+
# keys; v3 readers should fall back to ``.get(metric)`` so cross-version
|
|
24
|
+
# comparisons surface ``None`` rather than ``KeyError``.
|
|
25
|
+
SCHEMA_VERSION: int = 3
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
def write_output(path: Path | None, payload: dict[str, Any]) -> None:
|
{claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/_dispatch.py
RENAMED
|
@@ -19,6 +19,7 @@ import typer
|
|
|
19
19
|
from code_generator import memory as _memory
|
|
20
20
|
from code_generator.commands._crash_recovery import check_and_log_crash_recovery
|
|
21
21
|
from code_generator.commands._resume import next_start_phase, resolve_continue_multi_cycle
|
|
22
|
+
from code_generator.state import CycleState
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from pathlib import Path
|
|
@@ -36,14 +37,51 @@ __all__ = ["dispatch_async", "dispatch_orchestrator"]
|
|
|
36
37
|
|
|
37
38
|
_GRAPHIFY_TIMEOUT = int(os.environ.get("CODE_GENERATOR_GRAPHIFY_TIMEOUT", "600"))
|
|
38
39
|
|
|
40
|
+
_GRAPHIFY_EXTRACT_CMD = ["graphify", "extract", "."]
|
|
41
|
+
_GRAPHIFY_UPDATE_CMD = ["graphify", "update", "."]
|
|
39
42
|
|
|
40
43
|
_SETUP_HINT = (
|
|
41
|
-
"graph-report:
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
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."
|
|
45
48
|
)
|
|
46
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
|
+
|
|
47
85
|
|
|
48
86
|
def _decode_stderr(stderr: bytes | str | None) -> str:
|
|
49
87
|
"""Decode subprocess stderr to a short single-line string for logging."""
|
|
@@ -54,42 +92,65 @@ def _decode_stderr(stderr: bytes | str | None) -> str:
|
|
|
54
92
|
return text[:500]
|
|
55
93
|
|
|
56
94
|
|
|
57
|
-
def
|
|
58
|
-
"""
|
|
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)
|
|
59
100
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
132
|
+
|
|
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.
|
|
65
138
|
|
|
66
139
|
Strategy:
|
|
67
140
|
|
|
68
|
-
1. If ``graphify-out/graph.json`` does **not** exist,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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`.
|
|
72
145
|
|
|
73
|
-
2. If it exists, run ``graphify update .`` (
|
|
74
|
-
|
|
75
|
-
refreshes ``graph.json`` and ``GRAPH_REPORT.md``. Doc / paper /
|
|
76
|
-
image changes are *not* picked up — for those the user must run
|
|
77
|
-
``/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``.
|
|
78
148
|
|
|
79
149
|
Falls back to :data:`code_generator.memory.REPOMAP_FALLBACK` on any
|
|
80
150
|
failure (binary missing, timeout, non-zero exit, missing report file).
|
|
81
|
-
Stderr from graphify is captured and logged at WARN level on failure so
|
|
82
|
-
the user can diagnose without re-running by hand.
|
|
83
|
-
|
|
84
|
-
Auth: graphify inherits the parent process env. ``graphify update`` is
|
|
85
|
-
AST-only and makes no LLM calls, so OAuth context is irrelevant on this
|
|
86
|
-
codepath — but we still don't strip ``ANTHROPIC_*`` because the startup
|
|
87
|
-
``env.assert_safe_environment()`` check (CLAUDE.md non-negotiable #1)
|
|
88
|
-
already guarantees those vars are absent on the Anthropic Max path.
|
|
89
151
|
|
|
90
152
|
Args:
|
|
91
|
-
project_dir: Project root directory
|
|
92
|
-
into this directory).
|
|
153
|
+
project_dir: Project root directory.
|
|
93
154
|
log: Phase logger.
|
|
94
155
|
"""
|
|
95
156
|
memories_dir = project_dir / ".code-generator" / "memories"
|
|
@@ -105,42 +166,14 @@ def _compute_and_persist_graph_report(project_dir: Path, log: logging.Logger) ->
|
|
|
105
166
|
return
|
|
106
167
|
|
|
107
168
|
if not graph_json.exists():
|
|
108
|
-
log.info(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
cmd,
|
|
117
|
-
cwd=project_dir,
|
|
118
|
-
timeout=_GRAPHIFY_TIMEOUT,
|
|
119
|
-
capture_output=True,
|
|
120
|
-
check=True,
|
|
121
|
-
)
|
|
122
|
-
except subprocess.CalledProcessError as exc:
|
|
123
|
-
stderr = _decode_stderr(exc.stderr)
|
|
124
|
-
log.warning(
|
|
125
|
-
"graph-report: graphify exited %d; using fallback. stderr: %s",
|
|
126
|
-
exc.returncode,
|
|
127
|
-
stderr or "<empty>",
|
|
128
|
-
)
|
|
129
|
-
_memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
|
|
130
|
-
return
|
|
131
|
-
except subprocess.TimeoutExpired as exc:
|
|
132
|
-
log.warning(
|
|
133
|
-
"graph-report: graphify timed out after %ss; using fallback",
|
|
134
|
-
exc.timeout,
|
|
135
|
-
)
|
|
136
|
-
_memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
|
|
137
|
-
return
|
|
138
|
-
except (FileNotFoundError, OSError) as exc:
|
|
139
|
-
# `FileNotFoundError` here means the `graphify` binary disappeared
|
|
140
|
-
# between `shutil.which` and `subprocess.run`. Extremely unlikely.
|
|
141
|
-
log.warning("graph-report: could not invoke graphify (%s); using fallback", exc)
|
|
142
|
-
_memory.write_memory_file(memories_dir, "cycle-repo-map.md", _memory.REPOMAP_FALLBACK)
|
|
143
|
-
return
|
|
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):
|
|
176
|
+
return
|
|
144
177
|
|
|
145
178
|
try:
|
|
146
179
|
content = report_path.read_text(encoding="utf-8")
|
|
@@ -207,6 +240,8 @@ def _reset_failed_cycles_for_continue(
|
|
|
207
240
|
|
|
208
241
|
reset_ids: list[int] = []
|
|
209
242
|
for c in st.cycles:
|
|
243
|
+
if not isinstance(c, CycleState):
|
|
244
|
+
continue
|
|
210
245
|
if c.status != "failed":
|
|
211
246
|
continue
|
|
212
247
|
if target_cycle is not None and c.id != target_cycle:
|
|
@@ -275,7 +310,9 @@ async def _apply_delta_plan(
|
|
|
275
310
|
"Phase 0 failed during delta planning; aborting to preserve cycle history."
|
|
276
311
|
)
|
|
277
312
|
|
|
278
|
-
new_cycles = state_module.append_new_cycles(
|
|
313
|
+
new_cycles = state_module.append_new_cycles(
|
|
314
|
+
st, raw_cycles, ollama_model=effective_model
|
|
315
|
+
)
|
|
279
316
|
|
|
280
317
|
if not new_cycles:
|
|
281
318
|
logger.info("Delta planning: no new cycles detected.")
|
|
@@ -330,7 +367,7 @@ async def dispatch_async(
|
|
|
330
367
|
# Build the codebase graph once at the top of dispatch so Phase 0
|
|
331
368
|
# (complexity analysis) sees a fresh report alongside Phases 1 and 2.
|
|
332
369
|
graph_logger = setup_phase_logger("graph-report", project_dir)
|
|
333
|
-
_compute_and_persist_graph_report(project_dir, graph_logger)
|
|
370
|
+
_compute_and_persist_graph_report(project_dir, graph_logger, ollama_model=ollama_model)
|
|
334
371
|
|
|
335
372
|
# Phase 0: determine mode when not already known.
|
|
336
373
|
if mode == "auto":
|
|
@@ -343,7 +380,7 @@ async def dispatch_async(
|
|
|
343
380
|
# Delta planning: multi-cycle with completed cycles needs a merge, not a
|
|
344
381
|
# fresh Phase 0 run. Single-mode delta is handled upstream (mode reset to
|
|
345
382
|
# "unknown" forces Phase 0 to re-run via needs_phase0=True).
|
|
346
|
-
completed = [c for c in st.cycles if c.status == "completed"]
|
|
383
|
+
completed = [c for c in st.cycles if isinstance(c, CycleState) and c.status == "completed"]
|
|
347
384
|
if delta_hash is not None and st.mode == "multi-cycle" and completed and state_path is not None:
|
|
348
385
|
first_new_id = await _apply_delta_plan(
|
|
349
386
|
st,
|
|
@@ -504,7 +541,9 @@ def dispatch_orchestrator(
|
|
|
504
541
|
state_module.save_state(state_path, st)
|
|
505
542
|
|
|
506
543
|
elif run_mode == "delta":
|
|
507
|
-
completed = [
|
|
544
|
+
completed = [
|
|
545
|
+
c for c in st.cycles if isinstance(c, CycleState) and c.status == "completed"
|
|
546
|
+
]
|
|
508
547
|
if st.mode == "multi-cycle" and completed:
|
|
509
548
|
# Defer hash update to async path — atomicity requires Phase 0
|
|
510
549
|
# to succeed before we store the new hash.
|
{claude_code_generator-0.5.5 → claude_code_generator-0.5.7}/src/code_generator/commands/bench.py
RENAMED
|
@@ -4,10 +4,19 @@ Signature:
|
|
|
4
4
|
code-generator bench [--fake|--real] [--session-mode fresh|shared]
|
|
5
5
|
[--cycles N] [--fixture PATH] [--output FILE]
|
|
6
6
|
|
|
7
|
-
Output schema (schema_version=
|
|
7
|
+
Output schema (schema_version=3, see _bench_io.SCHEMA_VERSION):
|
|
8
8
|
{schema_version, timestamp_utc, session_mode, fixture,
|
|
9
|
-
cycles: [{cycle_name,
|
|
10
|
-
|
|
9
|
+
cycles: [{cycle_name,
|
|
10
|
+
phases: {"0"…"7": {input, output, cache_read, cache_write,
|
|
11
|
+
cache_hit_pct, wall_seconds, num_turns,
|
|
12
|
+
cache_write_5m, cache_write_1h,
|
|
13
|
+
compaction_events, clear_events,
|
|
14
|
+
cache_hit_ratio}},
|
|
15
|
+
totals}]}
|
|
16
|
+
|
|
17
|
+
v3 (issue #248): added the TTL split (cache_write_5m / cache_write_1h), the
|
|
18
|
+
SDK lifecycle counters (compaction_events / clear_events), and the 0.0-1.0
|
|
19
|
+
``cache_hit_ratio`` float used by ``bench compare`` for regression flagging.
|
|
11
20
|
|
|
12
21
|
Bump schema_version + add a migration note for any breaking change.
|
|
13
22
|
"""
|
|
@@ -49,11 +58,20 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
49
58
|
# ---------------------------------------------------------------------------
|
|
50
59
|
|
|
51
60
|
|
|
61
|
+
def _cache_hit_ratio(cache_read: int, cache_write: int, input_tokens: int) -> float:
|
|
62
|
+
"""Return the 0–1 cache hit ratio (issue #248). Zero denominator → 0.0."""
|
|
63
|
+
denominator = cache_read + cache_write + input_tokens
|
|
64
|
+
if denominator <= 0:
|
|
65
|
+
return 0.0
|
|
66
|
+
return cache_read / denominator
|
|
67
|
+
|
|
68
|
+
|
|
52
69
|
def _phase_metrics(result: RunResult) -> dict[str, int | float]:
|
|
53
|
-
"""Convert a RunResult into the
|
|
70
|
+
"""Convert a RunResult into the v3 phase-metrics dict (issue #248).
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
v3 adds the TTL split (``cache_write_5m`` / ``cache_write_1h``), the SDK
|
|
73
|
+
lifecycle counters (``compaction_events`` / ``clear_events``), and the
|
|
74
|
+
0.0-1.0 ``cache_hit_ratio`` float alongside the existing v2 keys.
|
|
57
75
|
"""
|
|
58
76
|
u = result.usage
|
|
59
77
|
denominator = u.cache_read + u.cache_write + u.input
|
|
@@ -66,14 +84,20 @@ def _phase_metrics(result: RunResult) -> dict[str, int | float]:
|
|
|
66
84
|
"cache_hit_pct": hit_pct,
|
|
67
85
|
"wall_seconds": result.wall_seconds,
|
|
68
86
|
"num_turns": u.num_turns,
|
|
87
|
+
"cache_write_5m": u.cache_write_5m,
|
|
88
|
+
"cache_write_1h": u.cache_write_1h,
|
|
89
|
+
"compaction_events": u.compaction_events,
|
|
90
|
+
"clear_events": u.clear_events,
|
|
91
|
+
"cache_hit_ratio": _cache_hit_ratio(u.cache_read, u.cache_write, u.input),
|
|
69
92
|
}
|
|
70
93
|
|
|
71
94
|
|
|
72
95
|
def _aggregate_totals(phases: dict[str, dict[str, int | float]]) -> dict[str, int | float]:
|
|
73
|
-
"""Sum per-phase metrics into cycle-level totals with recomputed
|
|
96
|
+
"""Sum per-phase metrics into cycle-level totals with recomputed ratios."""
|
|
74
97
|
total_input = total_output = total_read = total_write = 0
|
|
75
98
|
total_turns = 0
|
|
76
99
|
total_wall = 0.0
|
|
100
|
+
total_5m = total_1h = total_compaction = total_clear = 0
|
|
77
101
|
for m in phases.values():
|
|
78
102
|
total_input += int(m["input"])
|
|
79
103
|
total_output += int(m["output"])
|
|
@@ -81,6 +105,10 @@ def _aggregate_totals(phases: dict[str, dict[str, int | float]]) -> dict[str, in
|
|
|
81
105
|
total_write += int(m["cache_write"])
|
|
82
106
|
total_turns += int(m.get("num_turns", 0))
|
|
83
107
|
total_wall += float(m["wall_seconds"])
|
|
108
|
+
total_5m += int(m.get("cache_write_5m", 0))
|
|
109
|
+
total_1h += int(m.get("cache_write_1h", 0))
|
|
110
|
+
total_compaction += int(m.get("compaction_events", 0))
|
|
111
|
+
total_clear += int(m.get("clear_events", 0))
|
|
84
112
|
|
|
85
113
|
denominator = total_read + total_write + total_input
|
|
86
114
|
hit_pct = round(total_read / denominator * 100, 1) if denominator else 0.0
|
|
@@ -92,6 +120,11 @@ def _aggregate_totals(phases: dict[str, dict[str, int | float]]) -> dict[str, in
|
|
|
92
120
|
"cache_hit_pct": hit_pct,
|
|
93
121
|
"wall_seconds": total_wall,
|
|
94
122
|
"num_turns": total_turns,
|
|
123
|
+
"cache_write_5m": total_5m,
|
|
124
|
+
"cache_write_1h": total_1h,
|
|
125
|
+
"compaction_events": total_compaction,
|
|
126
|
+
"clear_events": total_clear,
|
|
127
|
+
"cache_hit_ratio": _cache_hit_ratio(total_read, total_write, total_input),
|
|
95
128
|
}
|
|
96
129
|
|
|
97
130
|
|