Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .amazonq/plan/compare_template_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""Reusable file comparison tool for template validation."""

import difflib
from pathlib import Path

import typer


def compare_files(file1: Path, file2: Path, label1: str, label2: str) -> bool:
"""Compare two files and show differences. Returns True if identical."""
typer.echo(f"\n{'='*60}")
typer.echo(f"COMPARING: {file1.name}")
typer.echo(f"{label1}: {file1}")
typer.echo(f"{label2}: {file2}")
typer.echo(f"{'='*60}")

if not file1.exists():
typer.echo(f"❌ MISSING: {file1} does not exist")
return False

if not file2.exists():
typer.echo(f"❌ MISSING: {file2} does not exist")
return False

content1 = file1.read_text().splitlines(keepends=True)
content2 = file2.read_text().splitlines(keepends=True)

diff = list(
difflib.unified_diff(
content1,
content2,
fromfile=f"{label1}/{file1.name}",
tofile=f"{label2}/{file2.name}",
lineterm="",
)
)

if not diff:
typer.echo("✅ FILES IDENTICAL")
return True
else:
typer.echo("❌ DIFFERENCES FOUND:")
for line in diff:
typer.echo(line)
return False


def main(
mode: str = typer.Argument(help="Compare template files or generated files (template|generated)"),
problem: str = typer.Option("invert_binary_tree", help="Problem name for comparison"),
):
"""Compare files for template validation."""
if mode not in ["template", "generated"]:
typer.echo(f"❌ ERROR: Invalid mode '{mode}'. Use 'template' or 'generated'")
return

base_dir = Path(__file__).parent.parent.parent

files_to_compare = ["solution.py", "tests.py", "README.md", "playground.ipynb", "__init__.py"]

if mode == "template":
# Compare reference vs template source
dir1 = base_dir / "leetcode" / ".example" / problem
dir2 = base_dir / ".templates" / "leetcode" / ".example" / "{{cookiecutter.problem_name}}"
label1, label2 = "Reference", "Template"
typer.echo("TEMPLATE SOURCE ANALYSIS")

elif mode == "generated":
# Compare reference vs currently generated
dir1 = base_dir / "leetcode" / ".example" / problem
dir2 = base_dir / "leetcode" / problem
label1, label2 = "Reference", "Generated"
typer.echo("GENERATED FILES VALIDATION")

if not dir2.exists():
typer.echo(f"\n❌ ERROR: Generated directory does not exist: {dir2}")
typer.echo(f"Run: make p-gen PROBLEM={problem}")
return

typer.echo(f"{label1}: {dir1}")
typer.echo(f"{label2}: {dir2}")

identical_count = 0
for filename in files_to_compare:
file1 = dir1 / filename
file2 = dir2 / filename
if compare_files(file1, file2, label1, label2):
identical_count += 1

typer.echo(f"\n{'='*60}")
typer.echo(f"SUMMARY: {identical_count}/{len(files_to_compare)} files identical")
typer.echo("- ✅ = Files identical")
typer.echo("- ❌ = Differences found or missing files")


if __name__ == "__main__":
typer.run(main)
224 changes: 224 additions & 0 deletions .amazonq/plan/cookiecutter-template-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Cookiecutter Template Modernization Plan

## TASK PURPOSE & CRITICAL RULES

**PURPOSE:** Update the cookiecutter template to generate files that exactly match the reference structure in `.templates/leetcode/.example/{{cookiecutter.problem_name}}/`

**REFERENCE DIRECTORIES (NEVER MODIFY - THESE ARE EXAMPLES):**

- `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` - Shows what the template SHOULD generate
- `leetcode/.example/` - Generated file examples for comparison

**ACTUAL TEMPLATE DIRECTORY (MODIFY THIS):**

- `.templates/leetcode/{{cookiecutter.problem_name}}/` - The cookiecutter template files to update

**WORKFLOW:**

1. Look at `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` to see target structure
2. Modify `.templates/leetcode/{{cookiecutter.problem_name}}/` to match the reference
3. Generate with `make p-gen`
4. Compare generated files vs reference with `make p-validate`

**ERROR PREVENTION:** The template directory does NOT have `.example` in the path!

## Analysis Summary

**Target Structure**: `leetcode/.example/` contains the reference implementation
**Key Differences Found:**

- `leetcode/.example/` has `__init__.py` files (missing in old template)
- `leetcode/.example/` uses modern Python syntax (`TreeNode | None` vs `Optional[TreeNode]`)
- `leetcode/.example/` follows project's coding standards more closely
- Template must generate files identical to `leetcode/.example/` structure

## Implementation Plan

### 0. Explicit File Content Analysis

- **Tool**: `.amazonq/plan/compare_template_files.py` (already exists - no need to implement)
- **Usage**:
- `poetry run python .amazonq/plan/compare_template_files.py generated --problem=PROBLEM_NAME` - Compare generated files vs reference
- **Analysis**: Line-by-line diff of all file types
- **Document**: Exact differences and required changes
- **Verify**: Template variables handle all variations

### 1. Incremental Template Updates (File-by-File Approach)

#### Phase 1: Add `__init__.py`

- **Add**: Empty `__init__.py` file to template
- **Validate**: `make p-gen` → `make p-validate` → `make lint`

#### Phase 2: Fix `solution.py`

- **Update**: Modern syntax (`TreeNode | None`), clean template logic
- **Validate**: `make p-gen` → `make p-validate` → `make lint`

#### Phase 3: Fix `tests.py`

- **Update**: Relative imports (`from .solution`), clean structure
- **Validate**: `make p-gen` → `make p-validate` → `make lint`

#### Phase 4: Fix `README.md`

- **Update**: Clean formatting, proper markdown
- **Validate**: `make p-gen` → `make p-validate` → `make lint`

#### Phase 5: Fix `playground.ipynb`

- **Update**: Clean cells without execution state
- **Validate**: `make p-gen` → `make p-validate` → `make lint`

**Benefits**: Isolated debugging, safer progression, easier rollback

### 2. Multi-Problem Type Testing

- **Test Cases**: Ensure template handles all problem types
- Basic array: `two_sum` (return `list[int]`)
- Tree: `invert_binary_tree` (return `TreeNode | None`)
- String: problems returning `str`
- Boolean: problems returning `bool`
- No imports vs TreeNode/ListNode imports
- **Validation**: Each type generates correctly

### 3. Modernize cookiecutter.json Schema

- **Base on**: Existing `.templates/leetcode/.example/examples/` JSON5 files
- **Ensure**: All template variables are properly defined
- **Add**: Missing fields found in real examples
- **Note**: Variable mapping handled by `gen.py` `convert_arrays_to_nested()`

### 4. Update Template Files

#### solution.py

- Use modern type hints (`TreeNode | None` not `Optional[TreeNode]`)
- Match exact import patterns from real examples
- Ensure proper TODO placeholder format

#### tests.py

- Follow `@logged_test` decorator pattern
- Use parametrized pytest structure
- Match logging format from real examples

#### README.md

- Follow exact format from real examples
- Include proper problem description formatting

#### playground.ipynb

- Ensure notebook structure matches real examples

### 5. Template Generation Logic

- **File**: `.templates/leetcode/gen.py` (already handles variable mapping)
- **Integration**: Works with `make p-gen PROBLEM=name` (verified in Makefile)
- **Update**: Handle new `__init__.py` file
- **Process**: JSON → `gen.py` → cookiecutter → `leetcode/$(PROBLEM)/`

### 6. Automated Validation System

- **Tool**: Reusable `.amazonq/plan/compare_template_files.py`
- **Usage**:
```bash
# Validate current template generates correct files
poetry run python .amazonq/plan/compare_template_files.py generated --problem=invert_binary_tree
```
- **Makefile**: `make p-validate PROBLEM=name` (implemented)
- **Test**: Template regression testing
- **Ensure**: `make p-gen` + `make lint` + `make p-test` all pass

### 7. Testing & Validation

- **Test**: Template generation with existing JSON files
- **Verify**: Generated files match `leetcode/.example/` structure exactly
- **Compare**: Automated diff against reference files
- **Ensure**: `make p-gen` works seamlessly
- **Test**: Recreation process from `.prompt/` files
- **Validate**: Multi-problem type generation

## Key Template Variables to Ensure

```json
{
"problem_name": "snake_case_name",
"class_name": "PascalCaseName",
"method_name": "snake_case_method",
"problem_number": "226",
"problem_title": "Display Title",
"difficulty": "Easy|Medium|Hard",
"topics": "Comma, Separated, Topics",
"tags": ["grind-75", "blind-75"],
"problem_description": "Full description",
"examples": [{"input": "...", "output": "..."}],
"constraints": "Formatted constraints",
"parameters": "typed_params: list[int]",
"return_type": "TreeNode | None",
"imports": "from leetcode_py.tree_node import TreeNode",
"test_cases": [{"args": [...], "expected": ...}]
}
```

## Success Criteria

### Phase-by-Phase Validation (File-by-File)

1. ✅ **Phase 1**: `__init__.py` files generated correctly
2. ✅ **Phase 2**: `solution.py` with modern syntax (`TreeNode | None`)
3. ✅ **Phase 3**: `tests.py` with relative imports and clean structure
4. ✅ **Phase 4**: `README.md` with clean formatting
5. ✅ **Phase 5**: `playground.ipynb` with clean cells

### Multi-Problem Type Validation

5. ✅ Basic problems (array/string) generate correctly
6. ✅ Tree problems generate correctly
7. ✅ Different return types handled (`bool`, `int`, `str`, `list`, etc.)

### Automated Validation

8. ✅ Automated diff shows no differences vs `leetcode/.example/`
9. ✅ `make p-validate` passes for all problem types
10. ✅ Recreation from `.prompt/` works flawlessly
11. ✅ All linting passes (`make lint`)
12. ✅ Tests run successfully (`make p-test`)

## Files to Modify

### Template Files

1. `.templates/leetcode/{{cookiecutter.problem_name}}/`
- **Add**: `__init__.py` (empty file)
- **Update**: `solution.py` (modern syntax, imports)
- **Update**: `tests.py` (match `leetcode/.example/` format)
- **Update**: `README.md` (match `leetcode/.example/` format)
- **Update**: `playground.ipynb` (match structure)

### Configuration

2. `.templates/leetcode/cookiecutter.json`
- Align with JSON5 examples
- Add missing variables

### Generation Logic

3. `.templates/leetcode/gen.py`
- Handle `__init__.py` generation
- Maintain existing variable mapping

### Validation Tools

4. **Reusable**: `.amazonq/plan/compare_template_files.py` (handles both template and generated comparisons)
5. **New**: Makefile target `make p-validate`

## Risk Mitigation

- **Incremental phases** prevent all-or-nothing failures
- **Automated validation** catches regressions immediately
- **Multi-problem testing** ensures template generalization
- **Explicit file comparison** documents exact requirements

This plan ensures the template generates files that exactly match `leetcode/.example/` while maintaining the robust generation process described in the rules.
58 changes: 16 additions & 42 deletions .amazonq/rules/development-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,27 @@

## Discussion Mode

- **Discussion Mode**: Prefix prompt with "D:" to enter read-only discussion mode
- In discussion mode: NO code updates, only read files and provide analysis/suggestions
- Always start responses with "[Discussion Mode]" header when in discussion mode
- Never exit discussion mode automatically - only when user uses "XD:" prefix
- If user seems to want code changes, remind them to use "XD:" to exit discussion mode
- **Exit Discussion**: Use "XD:" prefix to exit discussion mode and resume normal operations
- **Enter**: Prefix with "D:" for read-only analysis mode
- **Exit**: Use "XD:" to resume normal operations
- In discussion mode: NO code updates, only analysis/suggestions

## Code Standards

- Use snake_case for Python method names (following Python convention)
- Always include type hints for function parameters and return types
- Use PEP 585/604 syntax: `list[str]`, `dict[str, int]`, `Type | None`, etc.
- Add return statements to satisfy type checkers even if unreachable
- Follow the project's linting rules (black, isort, ruff, mypy)

## Template Usage

- **When user copies LeetCode problem**: Use `leetcode/_template/` to structure the question
- Copy template files to new question directory: `leetcode/{question_name}/`
- Replace template placeholders with actual problem details:
- `{method_name}` - snake_case method name (e.g., `two_sum`)
- `{ClassName}` - PascalCase class name (e.g., `TwoSum`)
- `{parameters}` - method parameters with types
- `{return_type}` - return type annotation
- Test case placeholders with actual examples
- **Template Implementation**: Do NOT implement the Solution class - only provide test cases and structure
- **Helper Functions/Classes**: If the question relies on underlying helper functions or classes (e.g., TreeNode, ListNode):
- First check if implementation already exists in `leetcode_py/common/` directory
- If found, import from common module
- If not found, create shared implementation in `leetcode_py/common/` for reusable classes
- For question-specific helpers, implement directly in the solution file
- **Update Makefile**: When adding new question, update the default `QUESTION` value in Makefile to the new question name
- Always use the template structure for consistency
- Use snake_case for Python methods
- Include type hints: `list[str]`, `dict[str, int]`, `Type | None`
- Follow linting rules (black, isort, ruff, mypy)

## File Structure
## Testing

Each LeetCode problem should have:
- Test specific: `make p-test PROBLEM=<name>`
- Test all: `make test`
- Beautiful logging with loguru

- `README.md` - Problem description and examples
- `solution.py` - Solution implementation
- `tests.py` - Parametrized pytest tests with loguru logging
- `__init__.py` - Empty file for Python package
## File Structure

## Testing
Each problem has:

- Use `make test-question QUESTION=<question_name>` to run tests
- Use `make test` to run all questions with coverage
- Default question is set to `two_sum` in Makefile
- Tests should cover all provided examples
- Use loguru for beautiful logging in tests
- `README.md` - Problem description
- `solution.py` - Implementation with TODO placeholder
- `tests.py` - Parametrized pytest tests
- `__init__.py` - Empty package file
Loading
Loading