claude-code-generator 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. {claude_code_generator-0.1.0/src/claude_code_generator.egg-info → claude_code_generator-0.2.0}/PKG-INFO +7 -5
  2. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/README.md +6 -4
  3. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/pyproject.toml +5 -1
  4. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0/src/claude_code_generator.egg-info}/PKG-INFO +7 -5
  5. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/claude_code_generator.egg-info/SOURCES.txt +38 -1
  6. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/__init__.py +1 -1
  7. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/cli.py +2 -0
  8. claude_code_generator-0.2.0/src/code_generator/commands/_detect.py +59 -0
  9. claude_code_generator-0.2.0/src/code_generator/commands/_dispatch.py +278 -0
  10. claude_code_generator-0.2.0/src/code_generator/commands/_resume.py +73 -0
  11. claude_code_generator-0.2.0/src/code_generator/commands/generate.py +126 -0
  12. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/commands/init.py +1 -3
  13. claude_code_generator-0.2.0/src/code_generator/commands/optimize.py +158 -0
  14. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/commands/review.py +18 -28
  15. claude_code_generator-0.2.0/src/code_generator/commands/status.py +192 -0
  16. claude_code_generator-0.2.0/src/code_generator/effort.py +164 -0
  17. claude_code_generator-0.2.0/src/code_generator/gh/__init__.py +51 -0
  18. claude_code_generator-0.2.0/src/code_generator/gh/core.py +111 -0
  19. claude_code_generator-0.2.0/src/code_generator/gh/issues.py +66 -0
  20. claude_code_generator-0.2.0/src/code_generator/gh/labels.py +102 -0
  21. claude_code_generator-0.2.0/src/code_generator/gh/milestones.py +89 -0
  22. claude_code_generator-0.2.0/src/code_generator/git_ops.py +213 -0
  23. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/logging_setup.py +61 -0
  24. claude_code_generator-0.2.0/src/code_generator/orchestrator/_comments.py +72 -0
  25. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/orchestrator/cycle_loop.py +239 -123
  26. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase0_complexity.py +315 -0
  27. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase1_plan.py +252 -0
  28. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/orchestrator/phase2_review.py +38 -34
  29. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase3_4_implement.py +306 -0
  30. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase5_closure.py +246 -0
  31. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase6_test.py +117 -0
  32. claude_code_generator-0.2.0/src/code_generator/orchestrator/phase7_commit.py +336 -0
  33. claude_code_generator-0.2.0/src/code_generator/prompts/__init__.py +146 -0
  34. claude_code_generator-0.2.0/src/code_generator/prompts/prompt-optimize-requirements.md +70 -0
  35. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-0-complexity.md +64 -4
  36. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-1-planning.md +14 -5
  37. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-2-issue-review.md +5 -1
  38. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-3-implementation.md +8 -4
  39. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-5-final-review.md +25 -1
  40. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-7-commit.md +53 -6
  41. claude_code_generator-0.2.0/src/code_generator/requirements_structure.py +78 -0
  42. claude_code_generator-0.2.0/src/code_generator/runner/__init__.py +52 -0
  43. claude_code_generator-0.2.0/src/code_generator/runner/message_parsing.py +162 -0
  44. claude_code_generator-0.2.0/src/code_generator/runner/options.py +41 -0
  45. claude_code_generator-0.2.0/src/code_generator/runner/protocol.py +60 -0
  46. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/runner/rate_limit.py +70 -6
  47. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/runner/retry.py +5 -9
  48. claude_code_generator-0.2.0/src/code_generator/runner/sdk_runner.py +211 -0
  49. claude_code_generator-0.2.0/src/code_generator/runner/subprocess_runner.py +334 -0
  50. claude_code_generator-0.2.0/src/code_generator/runner/types.py +166 -0
  51. claude_code_generator-0.2.0/src/code_generator/runner/utils.py +16 -0
  52. claude_code_generator-0.2.0/src/code_generator/state.py +364 -0
  53. claude_code_generator-0.2.0/tests/test_comments.py +342 -0
  54. claude_code_generator-0.2.0/tests/test_commit_message.py +138 -0
  55. claude_code_generator-0.2.0/tests/test_cycle_loop.py +1476 -0
  56. claude_code_generator-0.2.0/tests/test_cycle_loop_multicycle.py +903 -0
  57. claude_code_generator-0.2.0/tests/test_delta_planning.py +493 -0
  58. claude_code_generator-0.2.0/tests/test_detect.py +259 -0
  59. claude_code_generator-0.2.0/tests/test_effort.py +274 -0
  60. claude_code_generator-0.2.0/tests/test_generate.py +419 -0
  61. claude_code_generator-0.2.0/tests/test_generate_resume.py +1339 -0
  62. claude_code_generator-0.2.0/tests/test_gh.py +347 -0
  63. claude_code_generator-0.2.0/tests/test_gh_labels.py +150 -0
  64. claude_code_generator-0.2.0/tests/test_gh_milestones.py +158 -0
  65. claude_code_generator-0.2.0/tests/test_gh_submodules.py +223 -0
  66. claude_code_generator-0.2.0/tests/test_git_ops.py +506 -0
  67. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_init.py +4 -12
  68. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_logging_setup.py +1 -3
  69. claude_code_generator-0.2.0/tests/test_message_parsing.py +432 -0
  70. claude_code_generator-0.2.0/tests/test_optimize.py +621 -0
  71. claude_code_generator-0.2.0/tests/test_options.py +95 -0
  72. claude_code_generator-0.2.0/tests/test_phase0.py +401 -0
  73. claude_code_generator-0.2.0/tests/test_phase1.py +687 -0
  74. claude_code_generator-0.2.0/tests/test_phase2.py +506 -0
  75. claude_code_generator-0.2.0/tests/test_phase3_4.py +1080 -0
  76. claude_code_generator-0.2.0/tests/test_phase5.py +787 -0
  77. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_phase6.py +95 -0
  78. claude_code_generator-0.2.0/tests/test_phase7.py +1036 -0
  79. claude_code_generator-0.2.0/tests/test_phase_token_logging.py +742 -0
  80. claude_code_generator-0.2.0/tests/test_prompts.py +433 -0
  81. claude_code_generator-0.2.0/tests/test_rate_limit.py +871 -0
  82. claude_code_generator-0.2.0/tests/test_requirements_structure.py +290 -0
  83. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_retry.py +75 -0
  84. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_review.py +3 -9
  85. claude_code_generator-0.2.0/tests/test_runner_protocol.py +208 -0
  86. claude_code_generator-0.2.0/tests/test_runner_protocol_annotations.py +214 -0
  87. claude_code_generator-0.2.0/tests/test_runner_types.py +311 -0
  88. claude_code_generator-0.2.0/tests/test_runner_utils.py +104 -0
  89. claude_code_generator-0.2.0/tests/test_sdk_runner.py +902 -0
  90. claude_code_generator-0.2.0/tests/test_state.py +1154 -0
  91. claude_code_generator-0.2.0/tests/test_status.py +541 -0
  92. claude_code_generator-0.2.0/tests/test_subprocess_runner.py +1147 -0
  93. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_version.py +11 -0
  94. claude_code_generator-0.1.0/src/code_generator/commands/generate.py +0 -252
  95. claude_code_generator-0.1.0/src/code_generator/commands/status.py +0 -83
  96. claude_code_generator-0.1.0/src/code_generator/gh.py +0 -331
  97. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase0_complexity.py +0 -159
  98. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase1_plan.py +0 -170
  99. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase3_4_implement.py +0 -164
  100. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase5_closure.py +0 -154
  101. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase6_test.py +0 -98
  102. claude_code_generator-0.1.0/src/code_generator/orchestrator/phase7_commit.py +0 -167
  103. claude_code_generator-0.1.0/src/code_generator/prompts/__init__.py +0 -86
  104. claude_code_generator-0.1.0/src/code_generator/runner/__init__.py +0 -26
  105. claude_code_generator-0.1.0/src/code_generator/runner/sdk_runner.py +0 -267
  106. claude_code_generator-0.1.0/src/code_generator/runner/subprocess_runner.py +0 -200
  107. claude_code_generator-0.1.0/src/code_generator/state.py +0 -178
  108. claude_code_generator-0.1.0/tests/test_cycle_loop.py +0 -573
  109. claude_code_generator-0.1.0/tests/test_generate.py +0 -180
  110. claude_code_generator-0.1.0/tests/test_generate_resume.py +0 -436
  111. claude_code_generator-0.1.0/tests/test_gh.py +0 -365
  112. claude_code_generator-0.1.0/tests/test_phase0.py +0 -175
  113. claude_code_generator-0.1.0/tests/test_phase1.py +0 -223
  114. claude_code_generator-0.1.0/tests/test_phase2.py +0 -224
  115. claude_code_generator-0.1.0/tests/test_phase3_4.py +0 -229
  116. claude_code_generator-0.1.0/tests/test_phase5.py +0 -200
  117. claude_code_generator-0.1.0/tests/test_phase7.py +0 -257
  118. claude_code_generator-0.1.0/tests/test_prompts.py +0 -185
  119. claude_code_generator-0.1.0/tests/test_rate_limit.py +0 -260
  120. claude_code_generator-0.1.0/tests/test_sdk_runner.py +0 -285
  121. claude_code_generator-0.1.0/tests/test_state.py +0 -224
  122. claude_code_generator-0.1.0/tests/test_status.py +0 -106
  123. claude_code_generator-0.1.0/tests/test_subprocess_runner.py +0 -213
  124. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/LICENSE +0 -0
  125. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/setup.cfg +0 -0
  126. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/claude_code_generator.egg-info/dependency_links.txt +0 -0
  127. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/claude_code_generator.egg-info/entry_points.txt +0 -0
  128. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/claude_code_generator.egg-info/requires.txt +0 -0
  129. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/claude_code_generator.egg-info/top_level.txt +0 -0
  130. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/agents.py +0 -0
  131. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/commands/__init__.py +0 -0
  132. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/env.py +0 -0
  133. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/orchestrator/__init__.py +0 -0
  134. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-phase-6-test.md +0 -0
  135. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/prompts/prompt-review.md +0 -0
  136. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/__init__.py +0 -0
  137. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/angular.md +0 -0
  138. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/base.md +0 -0
  139. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/fastapi.md +0 -0
  140. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/finance.md +0 -0
  141. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/fullstack.md +0 -0
  142. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/nestjs.md +0 -0
  143. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/src/code_generator/templates/python-cli.md +0 -0
  144. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_agents.py +0 -0
  145. {claude_code_generator-0.1.0 → claude_code_generator-0.2.0}/tests/test_env.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -80,7 +80,9 @@ Read `.code-generator/requirements.md` and orchestrate Claude Code through the 0
80
80
  | 3-4 | Implementation, fresh SDK session per issue (TDD) | Sonnet 4.6 |
81
81
  | 5 | Closure + cross-module review | Opus 4.6 |
82
82
  | 6 | Test suite, max 3 retries on failure | Sonnet 4.6 |
83
- | 7 | Commit message + git add/commit/push | Haiku 4.5 |
83
+ | 7 | Commit message + git add/commit/push | Opus 4.6 |
84
+
85
+ Every phase logs a completion summary with token counts (`in`, `cache_read`, `cache_write`, `out`) and persists them to `state.json`.
84
86
 
85
87
  Flags:
86
88
 
@@ -102,7 +104,7 @@ Run a standalone Opus 4.6 review of the current codebase against the design prin
102
104
 
103
105
  ### `code-generator status`
104
106
 
105
- Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), and the last error.
107
+ Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), per-cycle token consumption columns (`in | cache_read | cache_write | out`), wall-clock elapsed time, and any `last_error` highlighted in red.
106
108
 
107
109
  ## Safety constraints
108
110
 
@@ -115,7 +117,7 @@ The tool enforces eight non-negotiables (see `CLAUDE.md` for the full list):
115
117
  5. **Wait-and-resume rate-limit handling.** Rate limits are not retried with exponential backoff — the tool sleeps until `resets_at + 60s` and resumes the same session. Backoff (10→20→40→80→120s) only applies to non-rate-limit transient errors.
116
118
  6. **Fresh SDK session per issue.** The implementation phase never reuses conversational context across issues — each issue is a `/clear`-equivalent fresh session.
117
119
  7. **Atomic state writes.** `.code-generator/state.json` is updated via `tmp → os.replace(tmp, STATE)` so a crash mid-write cannot corrupt it.
118
- 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5; Sonnet 4.6 for phases 3/4/6; Haiku 4.5 for phase 7's commit message.
120
+ 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5/7; Sonnet 4.6 for phases 3/4/6.
119
121
 
120
122
  ## Troubleshooting
121
123
 
@@ -151,7 +153,7 @@ src/code_generator/
151
153
  │ ├── phase3_4_implement.py # TDD loop, fresh session per issue
152
154
  │ ├── phase5_closure.py # closure + fix-issue feedback loop
153
155
  │ ├── phase6_test.py # test runner, max 3 retries
154
- │ ├── phase7_commit.py # Haiku commit message + push with rebase retry
156
+ │ ├── phase7_commit.py # Opus commit message + push with rebase retry
155
157
  │ └── cycle_loop.py # multi-cycle driver
156
158
  └── commands/ # init, status, generate, review (Typer commands)
157
159
  ```
@@ -49,7 +49,9 @@ Read `.code-generator/requirements.md` and orchestrate Claude Code through the 0
49
49
  | 3-4 | Implementation, fresh SDK session per issue (TDD) | Sonnet 4.6 |
50
50
  | 5 | Closure + cross-module review | Opus 4.6 |
51
51
  | 6 | Test suite, max 3 retries on failure | Sonnet 4.6 |
52
- | 7 | Commit message + git add/commit/push | Haiku 4.5 |
52
+ | 7 | Commit message + git add/commit/push | Opus 4.6 |
53
+
54
+ Every phase logs a completion summary with token counts (`in`, `cache_read`, `cache_write`, `out`) and persists them to `state.json`.
53
55
 
54
56
  Flags:
55
57
 
@@ -71,7 +73,7 @@ Run a standalone Opus 4.6 review of the current codebase against the design prin
71
73
 
72
74
  ### `code-generator status`
73
75
 
74
- Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), and the last error.
76
+ Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), per-cycle token consumption columns (`in | cache_read | cache_write | out`), wall-clock elapsed time, and any `last_error` highlighted in red.
75
77
 
76
78
  ## Safety constraints
77
79
 
@@ -84,7 +86,7 @@ The tool enforces eight non-negotiables (see `CLAUDE.md` for the full list):
84
86
  5. **Wait-and-resume rate-limit handling.** Rate limits are not retried with exponential backoff — the tool sleeps until `resets_at + 60s` and resumes the same session. Backoff (10→20→40→80→120s) only applies to non-rate-limit transient errors.
85
87
  6. **Fresh SDK session per issue.** The implementation phase never reuses conversational context across issues — each issue is a `/clear`-equivalent fresh session.
86
88
  7. **Atomic state writes.** `.code-generator/state.json` is updated via `tmp → os.replace(tmp, STATE)` so a crash mid-write cannot corrupt it.
87
- 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5; Sonnet 4.6 for phases 3/4/6; Haiku 4.5 for phase 7's commit message.
89
+ 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5/7; Sonnet 4.6 for phases 3/4/6.
88
90
 
89
91
  ## Troubleshooting
90
92
 
@@ -120,7 +122,7 @@ src/code_generator/
120
122
  │ ├── phase3_4_implement.py # TDD loop, fresh session per issue
121
123
  │ ├── phase5_closure.py # closure + fix-issue feedback loop
122
124
  │ ├── phase6_test.py # test runner, max 3 retries
123
- │ ├── phase7_commit.py # Haiku commit message + push with rebase retry
125
+ │ ├── phase7_commit.py # Opus commit message + push with rebase retry
124
126
  │ └── cycle_loop.py # multi-cycle driver
125
127
  └── commands/ # init, status, generate, review (Typer commands)
126
128
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "claude-code-generator"
7
- version = "0.1.0"
7
+ version = "0.2.0"
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" }
@@ -69,3 +69,7 @@ ignore = []
69
69
  [tool.pytest.ini_options]
70
70
  testpaths = ["tests"]
71
71
  asyncio_mode = "auto"
72
+
73
+ [tool.pyright]
74
+ # Test mocks intentionally use unused parameters to match the production interface signature.
75
+ reportUnusedParameter = "none"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-generator
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Orchestrator CLI that drives Claude Code end-to-end to generate whole projects from a requirements.md file.
5
5
  Author: Silvio Baratto
6
6
  License: MIT
@@ -80,7 +80,9 @@ Read `.code-generator/requirements.md` and orchestrate Claude Code through the 0
80
80
  | 3-4 | Implementation, fresh SDK session per issue (TDD) | Sonnet 4.6 |
81
81
  | 5 | Closure + cross-module review | Opus 4.6 |
82
82
  | 6 | Test suite, max 3 retries on failure | Sonnet 4.6 |
83
- | 7 | Commit message + git add/commit/push | Haiku 4.5 |
83
+ | 7 | Commit message + git add/commit/push | Opus 4.6 |
84
+
85
+ Every phase logs a completion summary with token counts (`in`, `cache_read`, `cache_write`, `out`) and persists them to `state.json`.
84
86
 
85
87
  Flags:
86
88
 
@@ -102,7 +104,7 @@ Run a standalone Opus 4.6 review of the current codebase against the design prin
102
104
 
103
105
  ### `code-generator status`
104
106
 
105
- Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), and the last error.
107
+ Print a Rich table with the current mode, current cycle, current phase, open/closed issue counts, the rate-limit pause state (with minutes remaining if paused), per-cycle token consumption columns (`in | cache_read | cache_write | out`), wall-clock elapsed time, and any `last_error` highlighted in red.
106
108
 
107
109
  ## Safety constraints
108
110
 
@@ -115,7 +117,7 @@ The tool enforces eight non-negotiables (see `CLAUDE.md` for the full list):
115
117
  5. **Wait-and-resume rate-limit handling.** Rate limits are not retried with exponential backoff — the tool sleeps until `resets_at + 60s` and resumes the same session. Backoff (10→20→40→80→120s) only applies to non-rate-limit transient errors.
116
118
  6. **Fresh SDK session per issue.** The implementation phase never reuses conversational context across issues — each issue is a `/clear`-equivalent fresh session.
117
119
  7. **Atomic state writes.** `.code-generator/state.json` is updated via `tmp → os.replace(tmp, STATE)` so a crash mid-write cannot corrupt it.
118
- 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5; Sonnet 4.6 for phases 3/4/6; Haiku 4.5 for phase 7's commit message.
120
+ 8. **Fixed model per phase.** Opus 4.6 for phases 0/1/2/5/7; Sonnet 4.6 for phases 3/4/6.
119
121
 
120
122
  ## Troubleshooting
121
123
 
@@ -151,7 +153,7 @@ src/code_generator/
151
153
  │ ├── phase3_4_implement.py # TDD loop, fresh session per issue
152
154
  │ ├── phase5_closure.py # closure + fix-issue feedback loop
153
155
  │ ├── phase6_test.py # test runner, max 3 retries
154
- │ ├── phase7_commit.py # Haiku commit message + push with rebase retry
156
+ │ ├── phase7_commit.py # Opus commit message + push with rebase retry
155
157
  │ └── cycle_loop.py # multi-cycle driver
156
158
  └── commands/ # init, status, generate, review (Typer commands)
157
159
  ```
@@ -10,16 +10,28 @@ src/claude_code_generator.egg-info/top_level.txt
10
10
  src/code_generator/__init__.py
11
11
  src/code_generator/agents.py
12
12
  src/code_generator/cli.py
13
+ src/code_generator/effort.py
13
14
  src/code_generator/env.py
14
- src/code_generator/gh.py
15
+ src/code_generator/git_ops.py
15
16
  src/code_generator/logging_setup.py
17
+ src/code_generator/requirements_structure.py
16
18
  src/code_generator/state.py
17
19
  src/code_generator/commands/__init__.py
20
+ src/code_generator/commands/_detect.py
21
+ src/code_generator/commands/_dispatch.py
22
+ src/code_generator/commands/_resume.py
18
23
  src/code_generator/commands/generate.py
19
24
  src/code_generator/commands/init.py
25
+ src/code_generator/commands/optimize.py
20
26
  src/code_generator/commands/review.py
21
27
  src/code_generator/commands/status.py
28
+ src/code_generator/gh/__init__.py
29
+ src/code_generator/gh/core.py
30
+ src/code_generator/gh/issues.py
31
+ src/code_generator/gh/labels.py
32
+ src/code_generator/gh/milestones.py
22
33
  src/code_generator/orchestrator/__init__.py
34
+ src/code_generator/orchestrator/_comments.py
23
35
  src/code_generator/orchestrator/cycle_loop.py
24
36
  src/code_generator/orchestrator/phase0_complexity.py
25
37
  src/code_generator/orchestrator/phase1_plan.py
@@ -29,6 +41,7 @@ src/code_generator/orchestrator/phase5_closure.py
29
41
  src/code_generator/orchestrator/phase6_test.py
30
42
  src/code_generator/orchestrator/phase7_commit.py
31
43
  src/code_generator/prompts/__init__.py
44
+ src/code_generator/prompts/prompt-optimize-requirements.md
32
45
  src/code_generator/prompts/prompt-phase-0-complexity.md
33
46
  src/code_generator/prompts/prompt-phase-1-planning.md
34
47
  src/code_generator/prompts/prompt-phase-2-issue-review.md
@@ -38,10 +51,15 @@ src/code_generator/prompts/prompt-phase-6-test.md
38
51
  src/code_generator/prompts/prompt-phase-7-commit.md
39
52
  src/code_generator/prompts/prompt-review.md
40
53
  src/code_generator/runner/__init__.py
54
+ src/code_generator/runner/message_parsing.py
55
+ src/code_generator/runner/options.py
56
+ src/code_generator/runner/protocol.py
41
57
  src/code_generator/runner/rate_limit.py
42
58
  src/code_generator/runner/retry.py
43
59
  src/code_generator/runner/sdk_runner.py
44
60
  src/code_generator/runner/subprocess_runner.py
61
+ src/code_generator/runner/types.py
62
+ src/code_generator/runner/utils.py
45
63
  src/code_generator/templates/__init__.py
46
64
  src/code_generator/templates/angular.md
47
65
  src/code_generator/templates/base.md
@@ -51,13 +69,26 @@ src/code_generator/templates/fullstack.md
51
69
  src/code_generator/templates/nestjs.md
52
70
  src/code_generator/templates/python-cli.md
53
71
  tests/test_agents.py
72
+ tests/test_comments.py
73
+ tests/test_commit_message.py
54
74
  tests/test_cycle_loop.py
75
+ tests/test_cycle_loop_multicycle.py
76
+ tests/test_delta_planning.py
77
+ tests/test_detect.py
78
+ tests/test_effort.py
55
79
  tests/test_env.py
56
80
  tests/test_generate.py
57
81
  tests/test_generate_resume.py
58
82
  tests/test_gh.py
83
+ tests/test_gh_labels.py
84
+ tests/test_gh_milestones.py
85
+ tests/test_gh_submodules.py
86
+ tests/test_git_ops.py
59
87
  tests/test_init.py
60
88
  tests/test_logging_setup.py
89
+ tests/test_message_parsing.py
90
+ tests/test_optimize.py
91
+ tests/test_options.py
61
92
  tests/test_phase0.py
62
93
  tests/test_phase1.py
63
94
  tests/test_phase2.py
@@ -65,10 +96,16 @@ tests/test_phase3_4.py
65
96
  tests/test_phase5.py
66
97
  tests/test_phase6.py
67
98
  tests/test_phase7.py
99
+ tests/test_phase_token_logging.py
68
100
  tests/test_prompts.py
69
101
  tests/test_rate_limit.py
102
+ tests/test_requirements_structure.py
70
103
  tests/test_retry.py
71
104
  tests/test_review.py
105
+ tests/test_runner_protocol.py
106
+ tests/test_runner_protocol_annotations.py
107
+ tests/test_runner_types.py
108
+ tests/test_runner_utils.py
72
109
  tests/test_sdk_runner.py
73
110
  tests/test_state.py
74
111
  tests/test_status.py
@@ -1,3 +1,3 @@
1
1
  """code-generator: orchestrator CLI for end-to-end project generation."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
@@ -11,6 +11,7 @@ import typer
11
11
  import code_generator
12
12
  from code_generator.commands.generate import generate_command
13
13
  from code_generator.commands.init import init_command
14
+ from code_generator.commands.optimize import optimize_command
14
15
  from code_generator.commands.review import review_command
15
16
  from code_generator.commands.status import status_command
16
17
 
@@ -46,4 +47,5 @@ def root(
46
47
  app.command(name="init")(init_command)
47
48
  app.command(name="status")(status_command)
48
49
  app.command(name="generate")(generate_command)
50
+ app.command(name="optimize")(optimize_command)
49
51
  app.command(name="review")(review_command)
@@ -0,0 +1,59 @@
1
+ """Re-run detection for the generate command.
2
+
3
+ Compares the current requirements.md hash against the stored hash in state to
4
+ decide the correct execution path on startup.
5
+
6
+ The sole exported function is a pure function (no I/O) so it is trivially
7
+ unit-testable.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING, Literal
13
+
14
+ if TYPE_CHECKING:
15
+ from code_generator import state as state_module
16
+
17
+ RunMode = Literal["first-run", "resume", "no-op", "delta"]
18
+
19
+ __all__ = ["RunMode", "detect_run_mode"]
20
+
21
+
22
+ def _run_is_complete(state: state_module.State) -> bool:
23
+ """Return True when the generation run is fully complete.
24
+
25
+ Single mode: complete when phase == 7 (final commit phase).
26
+ Multi-cycle mode: complete when every cycle has status "completed"
27
+ and at least one cycle exists.
28
+ """
29
+ if state.mode == "multi-cycle":
30
+ return len(state.cycles) > 0 and all(c.status == "completed" for c in state.cycles)
31
+ return state.phase == 7
32
+
33
+
34
+ def detect_run_mode(state: state_module.State, current_hash: str) -> RunMode:
35
+ """Determine how the generate command should proceed given existing state.
36
+
37
+ This is a pure function: it reads ``state`` and ``current_hash`` and
38
+ returns a decision literal. It never performs I/O.
39
+
40
+ Decision rules (evaluated in order):
41
+ 1. No stored hash → ``"first-run"`` (treat as a brand-new run)
42
+ 2. Hashes differ → ``"delta"`` (requirements changed)
43
+ 3. Run complete → ``"no-op"`` (nothing left to do)
44
+ 4. Otherwise → ``"resume"`` (same requirements, incomplete run)
45
+
46
+ Args:
47
+ state: Loaded root state (may have requirements_hash=None for old runs).
48
+ current_hash: SHA-256 hex digest of the current requirements.md.
49
+
50
+ Returns:
51
+ One of ``"first-run"``, ``"resume"``, ``"no-op"``, or ``"delta"``.
52
+ """
53
+ if state.requirements_hash is None:
54
+ return "first-run"
55
+ if state.requirements_hash != current_hash:
56
+ return "delta"
57
+ if _run_is_complete(state):
58
+ return "no-op"
59
+ return "resume"
@@ -0,0 +1,278 @@
1
+ """Dispatch helpers for the generate command.
2
+
3
+ Handles resolving resume parameters from CLI flags and delegating to the
4
+ async orchestration pipeline. Separated from the Typer command definition
5
+ so both layers can be tested in isolation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import logging
12
+ from typing import TYPE_CHECKING
13
+
14
+ import typer
15
+
16
+ from code_generator.commands._resume import next_start_phase, resolve_continue_multi_cycle
17
+
18
+ if TYPE_CHECKING:
19
+ from pathlib import Path
20
+
21
+ from code_generator import state as state_module
22
+
23
+ _logger = logging.getLogger(__name__)
24
+
25
+ __all__ = ["dispatch_async", "dispatch_orchestrator"]
26
+
27
+
28
+ async def _apply_delta_plan(
29
+ st: state_module.State,
30
+ project_dir: Path,
31
+ *,
32
+ runner_module: object,
33
+ delta_hash: str,
34
+ state_path: Path,
35
+ logger: logging.Logger,
36
+ ) -> int | None:
37
+ """Run Phase 0 and merge new cycles into state for multi-cycle delta planning.
38
+
39
+ Atomicity guarantee: state is only mutated after a successful Phase 0
40
+ response. If Phase 0 fails, a ``RuntimeError`` is raised and neither
41
+ ``state.cycles`` nor ``state.requirements_hash`` is changed.
42
+
43
+ Args:
44
+ st: Root state (mutated in place on success).
45
+ project_dir: Project root directory.
46
+ runner_module: Runner module for Phase 0 execution.
47
+ delta_hash: The new requirements hash to store on success.
48
+ state_path: Path to state.json for atomic persistence.
49
+ logger: Phase logger.
50
+
51
+ Returns:
52
+ ID of the first newly appended cycle, or ``None`` when the new plan
53
+ contains no scopes that are not already in ``state.cycles``.
54
+
55
+ Raises:
56
+ RuntimeError: When Phase 0 returns ``None`` (all retries exhausted).
57
+ """
58
+ from code_generator import state as state_module
59
+ from code_generator.orchestrator import phase0_complexity
60
+
61
+ raw_cycles = await phase0_complexity.get_raw_cycle_plan(
62
+ project_dir,
63
+ runner_module=runner_module, # type: ignore[arg-type]
64
+ logger=logger,
65
+ )
66
+
67
+ if raw_cycles is None:
68
+ raise RuntimeError(
69
+ "Phase 0 failed during delta planning; aborting to preserve cycle history."
70
+ )
71
+
72
+ new_cycles = state_module.append_new_cycles(st, raw_cycles)
73
+
74
+ if not new_cycles:
75
+ logger.info("Delta planning: no new cycles detected.")
76
+ st.requirements_hash = delta_hash
77
+ state_module.save_state(state_path, st)
78
+ return None
79
+
80
+ # Extend state atomically: build new list, then assign all at once.
81
+ st.cycles = list(st.cycles) + new_cycles
82
+ st.current_cycle = new_cycles[0].id
83
+ st.requirements_hash = delta_hash
84
+ state_module.save_state(state_path, st)
85
+ return new_cycles[0].id
86
+
87
+
88
+ async def dispatch_async(
89
+ st: state_module.State,
90
+ project_dir: Path,
91
+ *,
92
+ dry_run: bool,
93
+ start_phase: int,
94
+ start_cycle: int | None,
95
+ mode: str,
96
+ delta_hash: str | None = None,
97
+ state_path: Path | None = None,
98
+ ) -> None:
99
+ """Async entry point for the orchestration pipeline.
100
+
101
+ Args:
102
+ st: Root state object.
103
+ project_dir: Project root directory.
104
+ dry_run: When True, only phases 0 and 1 run.
105
+ start_phase: Resume from this phase (1 = full run).
106
+ start_cycle: Resume from this cycle (multi-cycle only).
107
+ mode: ``"auto"``, ``"single"``, or ``"multi-cycle"``.
108
+ delta_hash: When set, triggers delta planning for multi-cycle mode
109
+ with completed cycles (the new requirements hash to persist on
110
+ success). ``None`` disables delta planning.
111
+ state_path: Path to state.json; required when ``delta_hash`` is set.
112
+ """
113
+ from code_generator.logging_setup import setup_phase_logger
114
+ from code_generator.orchestrator import cycle_loop, phase0_complexity
115
+ from code_generator.runner import get_runner
116
+
117
+ runner_module = get_runner()
118
+ logger = setup_phase_logger("generate", project_dir)
119
+
120
+ # Phase 0: determine mode when not already known.
121
+ if mode == "auto":
122
+ needs_phase0 = st.mode not in ("single", "multi-cycle")
123
+ else:
124
+ needs_phase0 = False
125
+ # Force the mode from the flag.
126
+ st.mode = mode # type: ignore[assignment]
127
+
128
+ # Delta planning: multi-cycle with completed cycles needs a merge, not a
129
+ # fresh Phase 0 run. Single-mode delta is handled upstream (mode reset to
130
+ # "unknown" forces Phase 0 to re-run via needs_phase0=True).
131
+ completed = [c for c in st.cycles if c.status == "completed"]
132
+ if delta_hash is not None and st.mode == "multi-cycle" and completed and state_path is not None:
133
+ first_new_id = await _apply_delta_plan(
134
+ st,
135
+ project_dir,
136
+ runner_module=runner_module,
137
+ delta_hash=delta_hash,
138
+ state_path=state_path,
139
+ logger=logger,
140
+ )
141
+ if first_new_id is None:
142
+ return # No new cycles — nothing to run.
143
+ start_cycle = first_new_id
144
+ start_phase = 1
145
+ needs_phase0 = False
146
+
147
+ if needs_phase0:
148
+ await phase0_complexity.run(st, project_dir, runner_module=runner_module, logger=logger)
149
+
150
+ # Dry-run: planning only (phase 0 + phase 1).
151
+ if dry_run:
152
+ from code_generator.orchestrator import phase1_plan
153
+
154
+ logger.info("--dry-run: running phase 1 (planning only).")
155
+ await phase1_plan.run(st, None, project_dir, runner_module=runner_module, logger=logger)
156
+ return
157
+
158
+ # Dispatch to cycle loop.
159
+ if st.mode == "multi-cycle":
160
+ await cycle_loop.run_multi_cycle(
161
+ st,
162
+ project_dir,
163
+ runner_module=runner_module,
164
+ logger=logger,
165
+ start_cycle=start_cycle,
166
+ start_phase=start_phase,
167
+ )
168
+ else:
169
+ await cycle_loop.run_single_mode(
170
+ st,
171
+ project_dir,
172
+ runner_module=runner_module,
173
+ logger=logger,
174
+ start_phase=start_phase,
175
+ )
176
+
177
+
178
+ def dispatch_orchestrator(
179
+ st: state_module.State,
180
+ project_dir: Path,
181
+ *,
182
+ dry_run: bool,
183
+ phase: int | None,
184
+ continue_: bool,
185
+ mode: str,
186
+ cycle: int | None,
187
+ state_path: Path,
188
+ requirements_hash: str | None = None,
189
+ ) -> None:
190
+ """Resolve resume parameters and dispatch the async orchestrator.
191
+
192
+ Args:
193
+ st: Root state.
194
+ project_dir: Project root directory.
195
+ dry_run: When True, only phase 0/1 planning runs.
196
+ phase: Resume from this explicit phase number.
197
+ continue_: Resume from the last completed phase/cycle in state.
198
+ mode: ``"auto"``, ``"single"``, or ``"multi-cycle"``.
199
+ cycle: Resume from this specific cycle (multi-cycle only).
200
+ state_path: Path to state.json, used for atomic hash updates.
201
+ requirements_hash: SHA-256 digest of the current requirements.md, or
202
+ None when the file is absent (detection is skipped).
203
+ """
204
+ from code_generator import state as state_module
205
+
206
+ # Re-run detection: only when no explicit resume flags override intent.
207
+ # --continue and --phase N express explicit user intent, so they bypass
208
+ # detection entirely to avoid surprising "nothing to do" responses.
209
+ delta_hash_for_async: str | None = None
210
+ if not continue_ and phase is None and requirements_hash is not None:
211
+ from code_generator.commands._detect import detect_run_mode
212
+
213
+ run_mode = detect_run_mode(st, requirements_hash)
214
+
215
+ if run_mode == "no-op":
216
+ _logger.info("nothing to do: all cycles complete, requirements unchanged")
217
+ typer.echo("All cycles complete. Nothing to do.")
218
+ return
219
+
220
+ if run_mode == "first-run":
221
+ st.requirements_hash = requirements_hash
222
+ state_module.save_state(state_path, st)
223
+
224
+ elif run_mode == "delta":
225
+ completed = [c for c in st.cycles if c.status == "completed"]
226
+ if st.mode == "multi-cycle" and completed:
227
+ # Defer hash update to async path — atomicity requires Phase 0
228
+ # to succeed before we store the new hash.
229
+ delta_hash_for_async = requirements_hash
230
+ else:
231
+ # Single mode or no completed cycles: treat as fresh run from Phase 0.
232
+ # Reset mode to "unknown" so Phase 0 re-runs and re-evaluates complexity.
233
+ st.mode = "unknown" # type: ignore[assignment]
234
+ st.requirements_hash = requirements_hash
235
+ state_module.save_state(state_path, st)
236
+
237
+ # Resolve effective mode (honor existing state when --mode auto).
238
+ effective_mode = mode
239
+ if mode == "auto" and st.mode in ("single", "multi-cycle"):
240
+ effective_mode = st.mode
241
+
242
+ # Resolve start_phase and start_cycle.
243
+ # Flag precedence (highest → lowest):
244
+ # 1. --continue → derive start_cycle + start_phase from state (last completed)
245
+ # 2. --phase N → explicit phase; start_cycle stays None (single-mode default)
246
+ # 3. --cycle N → overrides any continue-derived start_cycle; start_phase
247
+ # defaults to 1 unless --phase N is also given
248
+ # 4. (none) → fresh run from phase 1, cycle None
249
+ start_phase = 1
250
+ start_cycle: int | None = None
251
+
252
+ if continue_:
253
+ # Resume from the *next* phase/cycle after the last completed one.
254
+ if effective_mode == "multi-cycle":
255
+ start_cycle, start_phase = resolve_continue_multi_cycle(st)
256
+ else:
257
+ start_phase = next_start_phase(st)
258
+
259
+ elif phase is not None:
260
+ start_phase = phase
261
+
262
+ if cycle is not None:
263
+ # --cycle N overrides any continue-derived start_cycle.
264
+ start_cycle = cycle
265
+ start_phase = phase or 1
266
+
267
+ asyncio.run(
268
+ dispatch_async(
269
+ st,
270
+ project_dir,
271
+ dry_run=dry_run,
272
+ start_phase=start_phase,
273
+ start_cycle=start_cycle,
274
+ mode=effective_mode,
275
+ delta_hash=delta_hash_for_async,
276
+ state_path=state_path,
277
+ )
278
+ )
@@ -0,0 +1,73 @@
1
+ """Resume helpers for the generate command.
2
+
3
+ Derives ``start_phase`` and ``start_cycle`` from persisted state when the user
4
+ passes ``--continue``. These functions are pure (no I/O) so they are easy to
5
+ unit-test in isolation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from typing import TYPE_CHECKING
12
+
13
+ if TYPE_CHECKING:
14
+ from code_generator import state as state_module
15
+
16
+ _logger = logging.getLogger(__name__)
17
+
18
+ __all__ = ["next_start_phase", "resolve_continue_multi_cycle"]
19
+
20
+
21
+ def next_start_phase(st: state_module.State) -> int:
22
+ """Return the phase to resume from after ``--continue``.
23
+
24
+ ``state.phase`` records the last *completed* phase, so the next phase to
25
+ run is always ``phase + 1``. When no prior phase is recorded (fresh run),
26
+ start from phase 1.
27
+
28
+ Args:
29
+ st: Root state.
30
+
31
+ Returns:
32
+ Phase number to pass as ``start_phase`` to the cycle loop.
33
+ """
34
+ return (st.phase or 0) + 1
35
+
36
+
37
+ def resolve_continue_multi_cycle(
38
+ st: state_module.State,
39
+ ) -> tuple[int | None, int]:
40
+ """Return ``(start_cycle, start_phase)`` for a ``--continue`` in multi-cycle mode.
41
+
42
+ Rules:
43
+ - When ``current_cycle`` is *completed* (all phases done), advance to the
44
+ next cycle and restart from phase 1 — the completed cycle must not be
45
+ re-entered.
46
+ - When ``current_cycle`` is still *open* (interrupted mid-cycle), resume
47
+ within that cycle at the next phase.
48
+ - When no ``current_cycle`` is recorded, return ``(None, 1)`` for a full run.
49
+
50
+ Args:
51
+ st: Root state.
52
+
53
+ Returns:
54
+ Tuple of ``(start_cycle, start_phase)`` to pass to ``run_multi_cycle``.
55
+ """
56
+ if st.current_cycle is None:
57
+ return None, 1
58
+
59
+ current = next((c for c in st.cycles if c.id == st.current_cycle), None)
60
+ if current is not None and current.status == "completed":
61
+ return st.current_cycle + 1, 1
62
+
63
+ if current is not None:
64
+ cycle_phase = current.phase or 0
65
+ # Phase 7 with non-completed status → retry commit (don't advance beyond 7).
66
+ start_phase = cycle_phase + 1 if cycle_phase < 7 else 7
67
+ return st.current_cycle, start_phase
68
+
69
+ _logger.warning(
70
+ "No cycle record found for current_cycle=%d; defaulting to phase 1.",
71
+ st.current_cycle,
72
+ )
73
+ return st.current_cycle, 1