|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This is **codeplain**, a Python CLI tool that renders code from `***plain` specification files using the Codeplain API. The tool acts as a client that: |
| 8 | +1. Parses `***plain` spec files (a domain-specific language for software specifications) |
| 9 | +2. Sends specs to the Codeplain API (LLM-based code generation service) |
| 10 | +3. Manages an iterative state-machine-driven render process with testing and refactoring cycles |
| 11 | +4. Outputs generated code to a build folder |
| 12 | + |
| 13 | +The codebase serves two audiences: |
| 14 | +- **End users**: developers who write `***plain` specs and run `plain2code.py` to generate code |
| 15 | +- **Internal devs**: maintaining the renderer itself (this codebase) |
| 16 | + |
| 17 | +## Development Setup |
| 18 | + |
| 19 | +### Prerequisites |
| 20 | +- Python 3.11+ |
| 21 | +- `CODEPLAIN_API_KEY` environment variable set |
| 22 | + |
| 23 | +### Installation |
| 24 | +```bash |
| 25 | +python -m venv .venv |
| 26 | +source .venv/bin/activate # On Windows: .venv\Scripts\activate |
| 27 | +pip install -r requirements.txt |
| 28 | +``` |
| 29 | + |
| 30 | +### Git Hooks |
| 31 | +A pre-push hook runs automatically on `git push` and executes: |
| 32 | +- Black formatting check |
| 33 | +- isort import sorting check |
| 34 | +- Flake8 linting |
| 35 | +- Mypy type checking |
| 36 | +- Full test suite with coverage |
| 37 | + |
| 38 | +Hook is located at `.git/hooks/pre-push`. |
| 39 | + |
| 40 | +## Common Commands |
| 41 | + |
| 42 | +### Running the Tool |
| 43 | +```bash |
| 44 | +# Basic usage - render a .plain file |
| 45 | +python plain2code.py path/to/file.plain |
| 46 | + |
| 47 | +# Dry run (parse and validate .plain files without rendering code) |
| 48 | +# This parses the spec files, resolves imports/requires, but doesn't generate code |
| 49 | +python plain2code.py path/to/file.plain --dry-run |
| 50 | + |
| 51 | +# Enable file logging (writes to codeplain.log in same dir as .plain file) |
| 52 | +python plain2code.py path/to/file.plain --log-to-file |
| 53 | + |
| 54 | +# Headless mode (no TUI, logs to file only) |
| 55 | +python plain2code.py path/to/file.plain --headless |
| 56 | + |
| 57 | +# Render specific functionality range |
| 58 | +python plain2code.py file.plain --render-range 1,3 # Render functionalities 1-3 |
| 59 | +python plain2code.py file.plain --render-from 2 # Resume from functionality 2 |
| 60 | + |
| 61 | +# Run with examples |
| 62 | +cd examples/example_hello_world_python |
| 63 | +python ../../plain2code.py hello_world_python.plain |
| 64 | +cd build && python hello_world.py |
| 65 | +``` |
| 66 | + |
| 67 | +### Testing |
| 68 | +```bash |
| 69 | +# Run all tests |
| 70 | +pytest tests/ -v |
| 71 | + |
| 72 | +# Run specific test file |
| 73 | +pytest tests/test_plain_modules.py -v |
| 74 | + |
| 75 | +# Run specific test |
| 76 | +pytest tests/test_plain_modules.py::test_get_next_frid_within_same_module -v |
| 77 | + |
| 78 | +# Run with coverage |
| 79 | +coverage run -m pytest tests/ -v |
| 80 | +coverage report |
| 81 | +coverage html # Generates htmlcov/index.html |
| 82 | +``` |
| 83 | + |
| 84 | +### Code Quality |
| 85 | +```bash |
| 86 | +# Format code |
| 87 | +black . |
| 88 | + |
| 89 | +# Sort imports |
| 90 | +isort . |
| 91 | + |
| 92 | +# Lint |
| 93 | +flake8 . |
| 94 | + |
| 95 | +# Type check |
| 96 | +mypy . --check-untyped-defs |
| 97 | + |
| 98 | +# Run all checks (same as pre-push hook) |
| 99 | +black . --check && isort . --check-only && flake8 . && mypy . --check-untyped-defs && coverage run -m pytest tests/ -v && coverage report |
| 100 | +``` |
| 101 | + |
| 102 | +## Architecture |
| 103 | + |
| 104 | +### High-Level Flow |
| 105 | +1. **Entry Point** (`plain2code.py`): CLI argument parsing, logging setup, TUI initialization |
| 106 | +2. **Module Loading** (`plain_modules.py`, `plain_file.py`): Parse `.plain` files into `PlainModule` objects with dependency trees |
| 107 | +3. **Rendering Orchestration** (`module_renderer.py`): Coordinates render process across modules |
| 108 | +4. **State Machine** (`render_machine/`): Hierarchical state machine drives the render lifecycle |
| 109 | +5. **API Communication** (`codeplain_REST_api.py`): HTTP client for Codeplain API |
| 110 | +6. **Code Generation** (`render_machine/code_renderer.py`): Template-based code synthesis using Liquid2 |
| 111 | +7. **TUI** (`tui/`): Textual-based terminal UI showing render progress |
| 112 | + |
| 113 | +### Key Concepts |
| 114 | + |
| 115 | +**Plain Modules**: A `.plain` file can `import` other `.plain` files, creating a module dependency tree. The renderer processes required modules first (depth-first), then the top module. |
| 116 | + |
| 117 | +**Functionalities (FRIDs)**: Each functional requirement in a `.plain` file has a unique ID (FRID). The renderer processes them sequentially. State is checkpointed after each FRID. |
| 118 | + |
| 119 | +**Render State Machine** (`render_machine/states.py`): Hierarchical FSM with states like: |
| 120 | +- `IMPLEMENTING_FRID` → `PROCESSING_UNIT_TESTS` → `REFACTORING_CODE` → `PROCESSING_CONFORMANCE_TESTS` |
| 121 | +- Transitions handled by triggers in `render_machine/triggers.py` |
| 122 | +- Config in `render_machine/state_machine_config.py` |
| 123 | + |
| 124 | +**Memory Management** (`memory_management.py`): Tracks previously rendered code to provide context to the LLM for incremental changes. Uses git commits to persist checkpoints. |
| 125 | + |
| 126 | +**Partial Rendering** (`partial_rendering.py`): Detects when specs or code changed and determines the optimal starting point to resume rendering (avoids full re-renders). |
| 127 | + |
| 128 | +**Event Bus** (`event_bus.py`): Pub/sub for UI updates. Events defined in `plain2code_events.py` (e.g., `LogMessageEmitted`, `RenderCompleted`). |
| 129 | + |
| 130 | +### Directory Structure |
| 131 | + |
| 132 | +- `plain2code.py` - Main CLI entry point |
| 133 | +- `plain_modules.py` - Module dependency resolution |
| 134 | +- `plain_file.py` - `.plain` file parser |
| 135 | +- `plain_spec.py` - Spec extraction and FRID utilities |
| 136 | +- `module_renderer.py` - Orchestrates render for a module |
| 137 | +- `render_machine/` - State machine implementation |
| 138 | + - `code_renderer.py` - Generates code via templates |
| 139 | + - `states.py` - State name constants |
| 140 | + - `triggers.py` - State transition logic |
| 141 | + - `render_context.py` - Shared context across states |
| 142 | + - `conformance_tests.py` - Test generation and execution |
| 143 | +- `tui/` - Terminal UI (Textual framework) |
| 144 | + - `plain2code_tui.py` - Main TUI app |
| 145 | + - `components.py` - Custom UI widgets |
| 146 | +- `codeplain_REST_api.py` - API client |
| 147 | +- `memory_management.py` - Context tracking |
| 148 | +- `git_utils.py` - Git operations for checkpointing |
| 149 | +- `file_utils.py` - File I/O utilities |
| 150 | +- `concept_utils.py` - Concept validation (***plain language feature) |
| 151 | +- `plain2code_logger.py` - Custom logging with elapsed time timestamps |
| 152 | +- `plain2code_state.py` - Runtime state (render ID, counters, timing) |
| 153 | +- `standard_template_library/` - Built-in code templates |
| 154 | +- `examples/` - Sample `.plain` projects |
| 155 | + |
| 156 | +### Testing Architecture |
| 157 | + |
| 158 | +Tests are in `tests/` and cover: |
| 159 | +- `test_plain_modules.py` - Module loading, dependency resolution, metadata |
| 160 | +- `test_plainfileparser.py` - `.plain` syntax parsing |
| 161 | +- `test_partial_rendering.py` - Render resumption logic |
| 162 | +- `test_git_utils.py` - Git checkpoint/restore operations |
| 163 | +- `test_imports.py`, `test_requires.py` - Module dependency resolution |
| 164 | + |
| 165 | +No integration tests hit the actual Codeplain API (would require API key + network). Testing focuses on parser, state management, and file operations. |
| 166 | + |
| 167 | +### Logging |
| 168 | + |
| 169 | +Two logging modes: |
| 170 | +1. **Console (TUI)**: Logs displayed in TUI with relative timestamps `[HH:MM:SS]` (elapsed since render start, accounts for pauses) |
| 171 | +2. **File**: Same format as TUI, written to `codeplain.log` in the `.plain` file's directory |
| 172 | + |
| 173 | +`ElapsedTimeFormatter` (in `plain2code_logger.py`) uses `RunState` to calculate elapsed time. Format: `[HH:MM:SS] LEVEL logger_name: message` |
| 174 | + |
| 175 | +## Important Notes |
| 176 | + |
| 177 | +### Plain Specification Language |
| 178 | +- `***plain` is a Markdown-like DSL for software specs |
| 179 | +- Docs at https://www.plainlang.org/docs/ |
| 180 | +- Parser handles sections: definitions, functional specs, implementation reqs, test reqs, acceptance tests |
| 181 | +- Concepts (domain terms) validated across modules via `concept_utils.py` |
| 182 | + |
| 183 | +### Code Generation Process |
| 184 | +- Templates in `standard_template_library/` use Liquid2 syntax |
| 185 | +- Template search order: (1) `.plain` file dir, (2) `--template-dir`, (3) built-in standard lib |
| 186 | +- Generated code written to `build/` (configurable via `--build-folder`) |
| 187 | +- Each FRID render produces a git commit in build folder for rollback capability |
| 188 | + |
| 189 | +### Configuration |
| 190 | +- `config.yaml` can be placed in `.plain` file dir or CWD |
| 191 | +- Specifies test scripts, template dirs, build paths |
| 192 | +- CLI args override config file values |
| 193 | + |
| 194 | +### Running Plain2Code from Another Directory |
| 195 | +The tool can be run from any directory by providing an absolute or relative path to the `.plain` file. All paths (build folder, log file, templates) are resolved relative to the `.plain` file's directory, not the current working directory, unless explicitly overridden via CLI arguments. |
| 196 | + |
| 197 | +### Windows Support |
| 198 | +Windows users must use WSL (Windows Subsystem for Linux). The codebase has some platform-specific script handling (`.ps1` for Windows, `.sh` for Unix). |
| 199 | + |
| 200 | +### CRITICAL: No User-Specific Paths in Version Control |
| 201 | +**Never commit files containing user-specific absolute paths** (e.g., `/Users/username/...`, `/home/username/...`, `C:\Users\...`) to version-controlled files like: |
| 202 | +- `.claude/settings.json` - Use relative paths (`.venv/bin/python`) instead of absolute paths |
| 203 | +- `config.yaml` or any config files |
| 204 | +- Any documentation or scripts |
| 205 | + |
| 206 | +User-specific paths should only exist in: |
| 207 | +- `.claude/settings.local.json` (gitignored) |
| 208 | +- Personal `.env` files (gitignored) |
| 209 | +- User's global `~/.claude/settings.json` |
0 commit comments