Commit 7fc6a53
* feat(planning): add PRD template system for customizable output formats (#316)
Implements a template system for PRD generation:
- Add PrdTemplateSection and PrdTemplate dataclasses for template structure
- Add PrdTemplateManager for template loading, validation, and rendering
- Create 5 built-in templates: standard, lean, enterprise, user-story-map, technical-spec
- Implement Jinja2-based template rendering with custom filters (bullet_list, numbered_list, table)
- Add YAML serialization for template import/export
CLI commands:
- `cf prd templates list` - List available templates
- `cf prd templates show <id>` - Show template details
- `cf prd templates import <file>` - Import custom template
- `cf prd templates export <id> <output>` - Export template to YAML
- `cf prd generate --template <id>` - Generate PRD using specific template
Templates support:
- Global templates in ~/.codeframe/templates/prd/
- Project templates in .codeframe/templates/prd/
- Template validation (required fields, Jinja2 syntax)
- Custom template functions for list formatting
Tests: 44 new tests covering template system and CLI commands
* fix(planning): integrate template_id into PRD generation (#316)
The --template flag was validated but never passed to generate_prd().
Changes:
- Add template_id parameter to PrdDiscoverySession.generate_prd()
- Add _build_prd_prompt() to construct template-aware prompts
- Pass template_id from CLI to session.generate_prd()
- Store template_id in PRD metadata for traceability
- Add tests for template integration
The LLM prompt is now dynamically constructed based on the selected
template's sections, ensuring the generated PRD follows the chosen format.
* fix(planning): persist imported templates and use workspace scope (#316)
Fixes three issues with template resolution and persistence:
1. CLI prd_templates_import now persists templates to disk
- Added persist_template() method to PrdTemplateManager
- Import with persist=True saves to .codeframe/templates/prd/
- Shows save location in CLI output
2. CLI template commands now include project templates
- Pass workspace_path=Path.cwd() to PrdTemplateManager
- Project templates in .codeframe/templates/prd/ are discovered
3. prd_discovery._build_prd_prompt uses workspace scope
- Passes self.workspace.path to PrdTemplateManager
- Project templates can be used in PRD generation
Added 4 new tests for persist functionality.
* fix(planning): use repo_path instead of path for Workspace attribute
* fix(planning): track resolved vs requested template ID in PRD metadata
When template_id is missing or invalid, _build_prd_prompt falls back to
the default prompt. Previously, metadata would incorrectly record the
original requested template ID.
Changes:
- _build_prd_prompt now returns tuple of (prompt, resolved_template_id)
- Resolved ID is "default" when no template used or fallback occurred
- Metadata stores resolved template_id for accurate tracking
- If requested template differs from resolved, store requested_template_id
- Logging now reflects actual template used
* fix(planning): align docstring and use OS-agnostic test assertions
- Update generate_prd docstring to match actual fallback behavior
(uses "default" not "standard" when template not found)
- Replace string path assertion with Path object comparison for
cross-platform compatibility in test_persist_template_to_project
* style(tests): remove unused Path import
Ruff F401: Path import was redundant since tmp_path fixture
already provides a Path object for division operations.
* fix(planning): improve template validation and override handling
1. CLI template validation now includes project templates
- Pass workspace_path to PrdTemplateManager during validation
- Project templates in .codeframe/templates/prd/ are now discoverable
2. Template ID collision handling
- Log warning when a template overrides an existing one
- Helps users understand which template version is active
Added test for override warning behavior.
* security(planning): add input validation and XSS prevention
1. Jinja2 autoescape (XSS prevention)
- Enable autoescape=True in Jinja2 Environment
- Prevents HTML injection if PRDs are rendered downstream
2. Path traversal prevention
- Validate file exists and is a YAML file in load_template_from_file()
- Reject non-.yaml/.yml files
3. Improved error handling in render_template()
- Catch TemplateSyntaxError separately with specific message
- Re-raise KeyboardInterrupt/SystemExit
- Log full traceback for unexpected errors
4. Template validation on import
- Validate template before registering in import_template()
- Raise ValueError with validation errors
Added 4 security-focused tests covering all validations.
* test(planning): use resolve() for robust path comparison
Use .resolve() on both paths to handle Windows path normalization
differences (e.g., case sensitivity, symlink resolution).
* fix(planning): address PR review feedback
Code quality improvements based on CodeRabbit review:
1. Empty YAML file handling
- Add null check after yaml.safe_load() with clear error message
2. list_templates sorting robustness
- Handle name=None with fallback to empty string
3. import_template docstring accuracy
- Fix to match behavior (always saves to project directory)
4. Support both .yaml and .yml extensions
- _load_from_directory now globs both patterns
5. Cross-platform test compatibility
- Set both HOME and USERPROFILE for Path.home() tests
6. Section heading clarity
- Change "Required Sections" to "Sections" in prompt
Added test for empty YAML file rejection.
---------
Co-authored-by: Test User <test@example.com>
1 parent 3203109 commit 7fc6a53
9 files changed
Lines changed: 2401 additions & 14 deletions
File tree
- codeframe
- cli
- core
- planning
- tests
- cli
- planning
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
586 | 586 | | |
587 | 587 | | |
588 | 588 | | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
589 | 725 | | |
590 | 726 | | |
591 | 727 | | |
| |||
1104 | 1240 | | |
1105 | 1241 | | |
1106 | 1242 | | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
1107 | 1248 | | |
1108 | 1249 | | |
1109 | 1250 | | |
| |||
1121 | 1262 | | |
1122 | 1263 | | |
1123 | 1264 | | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
1124 | 1272 | | |
1125 | 1273 | | |
1126 | 1274 | | |
1127 | 1275 | | |
| 1276 | + | |
1128 | 1277 | | |
1129 | 1278 | | |
1130 | 1279 | | |
| |||
1137 | 1286 | | |
1138 | 1287 | | |
1139 | 1288 | | |
| 1289 | + | |
1140 | 1290 | | |
1141 | 1291 | | |
1142 | 1292 | | |
1143 | 1293 | | |
1144 | 1294 | | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
1145 | 1307 | | |
1146 | 1308 | | |
1147 | 1309 | | |
| |||
1272 | 1434 | | |
1273 | 1435 | | |
1274 | 1436 | | |
1275 | | - | |
| 1437 | + | |
1276 | 1438 | | |
1277 | 1439 | | |
1278 | | - | |
| 1440 | + | |
1279 | 1441 | | |
1280 | 1442 | | |
1281 | 1443 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
589 | 589 | | |
590 | 590 | | |
591 | 591 | | |
592 | | - | |
| 592 | + | |
593 | 593 | | |
594 | 594 | | |
595 | 595 | | |
596 | 596 | | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
597 | 602 | | |
598 | 603 | | |
599 | 604 | | |
| |||
606 | 611 | | |
607 | 612 | | |
608 | 613 | | |
609 | | - | |
610 | 614 | | |
611 | 615 | | |
612 | | - | |
| 616 | + | |
| 617 | + | |
613 | 618 | | |
614 | 619 | | |
615 | 620 | | |
| |||
623 | 628 | | |
624 | 629 | | |
625 | 630 | | |
626 | | - | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
627 | 644 | | |
628 | 645 | | |
629 | 646 | | |
630 | 647 | | |
631 | | - | |
632 | | - | |
633 | | - | |
634 | | - | |
635 | | - | |
636 | | - | |
637 | | - | |
| 648 | + | |
638 | 649 | | |
639 | 650 | | |
640 | 651 | | |
641 | 652 | | |
642 | 653 | | |
643 | 654 | | |
644 | | - | |
| 655 | + | |
645 | 656 | | |
646 | 657 | | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
647 | 718 | | |
648 | 719 | | |
649 | 720 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
14 | 21 | | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
18 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
19 | 30 | | |
0 commit comments