codeframe-ai 0.9.0__py3-none-any.whl

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 (197) hide show
  1. codeframe/__init__.py +11 -0
  2. codeframe/__main__.py +20 -0
  3. codeframe/adapters/__init__.py +5 -0
  4. codeframe/adapters/e2b/__init__.py +13 -0
  5. codeframe/adapters/e2b/adapter.py +342 -0
  6. codeframe/adapters/e2b/budget.py +71 -0
  7. codeframe/adapters/e2b/credential_scanner.py +134 -0
  8. codeframe/adapters/llm/__init__.py +92 -0
  9. codeframe/adapters/llm/anthropic.py +414 -0
  10. codeframe/adapters/llm/base.py +444 -0
  11. codeframe/adapters/llm/mock.py +281 -0
  12. codeframe/adapters/llm/openai.py +483 -0
  13. codeframe/agents/__init__.py +8 -0
  14. codeframe/agents/dependency_resolver.py +714 -0
  15. codeframe/auth/__init__.py +16 -0
  16. codeframe/auth/api_key_router.py +238 -0
  17. codeframe/auth/api_keys.py +156 -0
  18. codeframe/auth/dependencies.py +358 -0
  19. codeframe/auth/manager.py +178 -0
  20. codeframe/auth/models.py +30 -0
  21. codeframe/auth/router.py +93 -0
  22. codeframe/auth/schemas.py +15 -0
  23. codeframe/auth/scopes.py +53 -0
  24. codeframe/cli/__init__.py +12 -0
  25. codeframe/cli/__main__.py +20 -0
  26. codeframe/cli/api_client.py +275 -0
  27. codeframe/cli/app.py +5688 -0
  28. codeframe/cli/auth.py +122 -0
  29. codeframe/cli/auth_commands.py +958 -0
  30. codeframe/cli/commands/__init__.py +5 -0
  31. codeframe/cli/config_commands.py +79 -0
  32. codeframe/cli/dashboard_commands.py +67 -0
  33. codeframe/cli/engines_commands.py +205 -0
  34. codeframe/cli/env_commands.py +409 -0
  35. codeframe/cli/helpers.py +56 -0
  36. codeframe/cli/hooks_commands.py +208 -0
  37. codeframe/cli/import_commands.py +129 -0
  38. codeframe/cli/pr_commands.py +549 -0
  39. codeframe/cli/proof_commands.py +415 -0
  40. codeframe/cli/stats_commands.py +311 -0
  41. codeframe/cli/telemetry_runtime.py +153 -0
  42. codeframe/cli/validators.py +123 -0
  43. codeframe/config/rate_limits.py +165 -0
  44. codeframe/core/__init__.py +15 -0
  45. codeframe/core/adapters/__init__.py +43 -0
  46. codeframe/core/adapters/agent_adapter.py +114 -0
  47. codeframe/core/adapters/builtin.py +326 -0
  48. codeframe/core/adapters/claude_code.py +62 -0
  49. codeframe/core/adapters/codex.py +393 -0
  50. codeframe/core/adapters/git_utils.py +40 -0
  51. codeframe/core/adapters/kilocode.py +126 -0
  52. codeframe/core/adapters/opencode.py +48 -0
  53. codeframe/core/adapters/streaming_chat.py +483 -0
  54. codeframe/core/adapters/subprocess_adapter.py +213 -0
  55. codeframe/core/adapters/verification_wrapper.py +269 -0
  56. codeframe/core/agent.py +2183 -0
  57. codeframe/core/agents_config.py +569 -0
  58. codeframe/core/api_key_service.py +211 -0
  59. codeframe/core/artifacts.py +428 -0
  60. codeframe/core/blocker_detection.py +218 -0
  61. codeframe/core/blockers.py +433 -0
  62. codeframe/core/checkpoints.py +481 -0
  63. codeframe/core/conductor.py +2255 -0
  64. codeframe/core/config.py +827 -0
  65. codeframe/core/config_watcher.py +268 -0
  66. codeframe/core/context.py +542 -0
  67. codeframe/core/context_packager.py +234 -0
  68. codeframe/core/credentials.py +735 -0
  69. codeframe/core/dependency_analyzer.py +229 -0
  70. codeframe/core/dependency_graph.py +290 -0
  71. codeframe/core/diagnostic_agent.py +712 -0
  72. codeframe/core/diagnostics.py +616 -0
  73. codeframe/core/editor.py +556 -0
  74. codeframe/core/engine_registry.py +256 -0
  75. codeframe/core/engine_stats.py +231 -0
  76. codeframe/core/environment.py +697 -0
  77. codeframe/core/events.py +375 -0
  78. codeframe/core/executor.py +1005 -0
  79. codeframe/core/fix_tracker.py +480 -0
  80. codeframe/core/gates.py +1322 -0
  81. codeframe/core/git.py +477 -0
  82. codeframe/core/github_connect_service.py +178 -0
  83. codeframe/core/github_integration_config.py +118 -0
  84. codeframe/core/github_issues_service.py +449 -0
  85. codeframe/core/hooks.py +184 -0
  86. codeframe/core/importers/__init__.py +1 -0
  87. codeframe/core/importers/ralph.py +540 -0
  88. codeframe/core/installer.py +650 -0
  89. codeframe/core/models.py +1026 -0
  90. codeframe/core/notifications_config.py +183 -0
  91. codeframe/core/planner.py +437 -0
  92. codeframe/core/prd.py +670 -0
  93. codeframe/core/prd_discovery.py +1118 -0
  94. codeframe/core/prd_stress_test.py +499 -0
  95. codeframe/core/progress.py +126 -0
  96. codeframe/core/proof/__init__.py +34 -0
  97. codeframe/core/proof/capture.py +79 -0
  98. codeframe/core/proof/evidence.py +56 -0
  99. codeframe/core/proof/ledger.py +574 -0
  100. codeframe/core/proof/models.py +162 -0
  101. codeframe/core/proof/obligations.py +103 -0
  102. codeframe/core/proof/runner.py +233 -0
  103. codeframe/core/proof/scope.py +81 -0
  104. codeframe/core/proof/stubs.py +156 -0
  105. codeframe/core/quick_fixes.py +558 -0
  106. codeframe/core/react_agent.py +1650 -0
  107. codeframe/core/reconciliation.py +183 -0
  108. codeframe/core/replay.py +788 -0
  109. codeframe/core/review.py +285 -0
  110. codeframe/core/runtime.py +1134 -0
  111. codeframe/core/sandbox/__init__.py +27 -0
  112. codeframe/core/sandbox/context.py +98 -0
  113. codeframe/core/sandbox/worktree.py +20 -0
  114. codeframe/core/schedule.py +396 -0
  115. codeframe/core/stall_detector.py +71 -0
  116. codeframe/core/stall_monitor.py +134 -0
  117. codeframe/core/state_machine.py +121 -0
  118. codeframe/core/streaming.py +502 -0
  119. codeframe/core/task_tree.py +400 -0
  120. codeframe/core/tasks.py +1022 -0
  121. codeframe/core/telemetry.py +232 -0
  122. codeframe/core/templates.py +221 -0
  123. codeframe/core/tools.py +942 -0
  124. codeframe/core/workspace.py +887 -0
  125. codeframe/core/worktrees.py +276 -0
  126. codeframe/git/__init__.py +5 -0
  127. codeframe/git/github_integration.py +505 -0
  128. codeframe/lib/__init__.py +0 -0
  129. codeframe/lib/audit_logger.py +248 -0
  130. codeframe/lib/metrics_tracker.py +800 -0
  131. codeframe/lib/quality/__init__.py +7 -0
  132. codeframe/lib/quality/complexity_analyzer.py +316 -0
  133. codeframe/lib/quality/owasp_patterns.py +284 -0
  134. codeframe/lib/quality/security_scanner.py +250 -0
  135. codeframe/lib/rate_limiter.py +312 -0
  136. codeframe/notifications/__init__.py +0 -0
  137. codeframe/notifications/webhook.py +380 -0
  138. codeframe/planning/__init__.py +30 -0
  139. codeframe/planning/issue_generator.py +219 -0
  140. codeframe/planning/prd_template_functions.py +137 -0
  141. codeframe/planning/prd_templates.py +975 -0
  142. codeframe/planning/task_scheduler.py +511 -0
  143. codeframe/planning/task_templates.py +533 -0
  144. codeframe/platform_store/__init__.py +5 -0
  145. codeframe/platform_store/database.py +277 -0
  146. codeframe/platform_store/repositories/__init__.py +24 -0
  147. codeframe/platform_store/repositories/api_key_repository.py +245 -0
  148. codeframe/platform_store/repositories/audit_repository.py +67 -0
  149. codeframe/platform_store/repositories/base.py +295 -0
  150. codeframe/platform_store/repositories/interactive_sessions.py +165 -0
  151. codeframe/platform_store/repositories/token_repository.py +598 -0
  152. codeframe/platform_store/repositories/workspace_registry_repository.py +175 -0
  153. codeframe/platform_store/schema_manager.py +321 -0
  154. codeframe/templates/AGENTS.md.default +94 -0
  155. codeframe/tui/__init__.py +5 -0
  156. codeframe/tui/app.py +256 -0
  157. codeframe/tui/data_service.py +103 -0
  158. codeframe/ui/__init__.py +0 -0
  159. codeframe/ui/dependencies.py +103 -0
  160. codeframe/ui/models.py +999 -0
  161. codeframe/ui/response_models.py +201 -0
  162. codeframe/ui/routers/__init__.py +5 -0
  163. codeframe/ui/routers/_helpers.py +29 -0
  164. codeframe/ui/routers/batches_v2.py +315 -0
  165. codeframe/ui/routers/blockers_v2.py +320 -0
  166. codeframe/ui/routers/checkpoints_v2.py +310 -0
  167. codeframe/ui/routers/costs_v2.py +322 -0
  168. codeframe/ui/routers/diagnose_v2.py +225 -0
  169. codeframe/ui/routers/discovery_v2.py +417 -0
  170. codeframe/ui/routers/environment_v2.py +284 -0
  171. codeframe/ui/routers/events_v2.py +75 -0
  172. codeframe/ui/routers/gates_v2.py +166 -0
  173. codeframe/ui/routers/git_v2.py +284 -0
  174. codeframe/ui/routers/github_integrations_v2.py +532 -0
  175. codeframe/ui/routers/interactive_sessions_v2.py +238 -0
  176. codeframe/ui/routers/pr_v2.py +709 -0
  177. codeframe/ui/routers/prd_v2.py +695 -0
  178. codeframe/ui/routers/proof_v2.py +755 -0
  179. codeframe/ui/routers/review_v2.py +360 -0
  180. codeframe/ui/routers/schedule_v2.py +214 -0
  181. codeframe/ui/routers/session_chat_ws.py +354 -0
  182. codeframe/ui/routers/settings_v2.py +562 -0
  183. codeframe/ui/routers/streaming_v2.py +155 -0
  184. codeframe/ui/routers/tasks_v2.py +1098 -0
  185. codeframe/ui/routers/templates_v2.py +232 -0
  186. codeframe/ui/routers/terminal_ws.py +267 -0
  187. codeframe/ui/routers/workspace_v2.py +527 -0
  188. codeframe/ui/server.py +568 -0
  189. codeframe/ui/shared.py +241 -0
  190. codeframe/workspace/__init__.py +5 -0
  191. codeframe/workspace/manager.py +249 -0
  192. codeframe_ai-0.9.0.dist-info/METADATA +517 -0
  193. codeframe_ai-0.9.0.dist-info/RECORD +197 -0
  194. codeframe_ai-0.9.0.dist-info/WHEEL +5 -0
  195. codeframe_ai-0.9.0.dist-info/entry_points.txt +3 -0
  196. codeframe_ai-0.9.0.dist-info/licenses/LICENSE +661 -0
  197. codeframe_ai-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,975 @@
1
+ """PRD Template System for CodeFRAME.
2
+
3
+ This module provides a template system for customizable PRD output formats:
4
+ - PrdTemplateSection: Represents a single section with rendering template
5
+ - PrdTemplate: Contains template metadata and list of sections
6
+ - PrdTemplateManager: Manage and render templates
7
+ - BUILTIN_TEMPLATES: Predefined templates (standard, lean, enterprise, etc.)
8
+ """
9
+
10
+ import logging
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+ from typing import Any, Optional
14
+
15
+ import yaml
16
+ from jinja2 import Environment, BaseLoader, TemplateSyntaxError
17
+
18
+ from codeframe.planning.prd_template_functions import TEMPLATE_FUNCTIONS
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @dataclass
24
+ class PrdTemplateSection:
25
+ """Individual section within a PRD template.
26
+
27
+ Attributes:
28
+ id: Unique identifier for the section
29
+ title: Human-readable section title
30
+ source: Discovery data category to draw from (problem, users, features, etc.)
31
+ format_template: Jinja2 template string for rendering
32
+ required: Whether this section must be included (default: True)
33
+ """
34
+
35
+ id: str
36
+ title: str
37
+ source: str
38
+ format_template: str
39
+ required: bool = True
40
+
41
+
42
+ @dataclass
43
+ class PrdTemplate:
44
+ """Template for generating a PRD.
45
+
46
+ Attributes:
47
+ id: Unique identifier for the template
48
+ name: Human-readable name
49
+ version: Template version number
50
+ description: Description of when to use this template
51
+ sections: List of PrdTemplateSection objects
52
+ """
53
+
54
+ id: str
55
+ name: str
56
+ version: int
57
+ description: str
58
+ sections: list[PrdTemplateSection]
59
+
60
+ @property
61
+ def section_ids(self) -> list[str]:
62
+ """Get list of section IDs."""
63
+ return [s.id for s in self.sections]
64
+
65
+
66
+ # Built-in templates
67
+ BUILTIN_TEMPLATES: list[PrdTemplate] = [
68
+ PrdTemplate(
69
+ id="standard",
70
+ name="Standard PRD",
71
+ version=1,
72
+ description="Default PRD format with executive summary, problem statement, "
73
+ "user personas, features, technical architecture, success metrics, and timeline.",
74
+ sections=[
75
+ PrdTemplateSection(
76
+ id="executive_summary",
77
+ title="Executive Summary",
78
+ source="problem",
79
+ format_template="""## Executive Summary
80
+
81
+ {{ problem | default('Project overview not yet defined.') }}
82
+ """,
83
+ ),
84
+ PrdTemplateSection(
85
+ id="problem_statement",
86
+ title="Problem Statement",
87
+ source="problem",
88
+ format_template="""## Problem Statement
89
+
90
+ {{ problem | default('Problem statement not yet defined.') }}
91
+
92
+ ### Business Justification
93
+
94
+ This solution addresses a critical need in the target market by solving the core problem outlined above.
95
+ """,
96
+ ),
97
+ PrdTemplateSection(
98
+ id="user_personas",
99
+ title="User Personas",
100
+ source="users",
101
+ format_template="""## User Personas
102
+
103
+ ### Target Users
104
+
105
+ {{ users | bullet_list if users else 'User personas not yet defined.' }}
106
+ """,
107
+ ),
108
+ PrdTemplateSection(
109
+ id="features",
110
+ title="Features & Requirements",
111
+ source="features",
112
+ format_template="""## Features & Requirements
113
+
114
+ ### Core Features
115
+
116
+ {{ features | numbered_list if features else 'Features not yet defined.' }}
117
+
118
+ ### Functional Requirements
119
+
120
+ Each feature listed above represents a functional requirement that must be implemented to meet user needs.
121
+ """,
122
+ ),
123
+ PrdTemplateSection(
124
+ id="technical_architecture",
125
+ title="Technical Architecture",
126
+ source="tech_stack",
127
+ format_template="""## Technical Architecture
128
+
129
+ ### Technology Stack
130
+
131
+ {{ tech_stack | bullet_list if tech_stack else 'Technology stack not yet defined.' }}
132
+
133
+ ### Constraints
134
+
135
+ {{ constraints | format_constraints if constraints else 'No specific constraints defined.' }}
136
+ """,
137
+ ),
138
+ PrdTemplateSection(
139
+ id="success_metrics",
140
+ title="Success Metrics",
141
+ source="features",
142
+ format_template="""## Success Metrics
143
+
144
+ ### Key Performance Indicators
145
+
146
+ - User adoption rate
147
+ - Feature completion rate
148
+ - System performance metrics
149
+ - User satisfaction scores
150
+
151
+ ### Success Criteria
152
+
153
+ The project will be considered successful when all core features are implemented and user adoption targets are met.
154
+ """,
155
+ ),
156
+ PrdTemplateSection(
157
+ id="timeline",
158
+ title="Timeline & Milestones",
159
+ source="features",
160
+ format_template="""## Timeline & Milestones
161
+
162
+ ### Project Phases
163
+
164
+ 1. **Discovery & Planning** - Requirements gathering and architecture design
165
+ 2. **Development Phase 1** - Core feature implementation
166
+ 3. **Development Phase 2** - Additional features and refinements
167
+ 4. **Testing & QA** - Comprehensive testing and bug fixes
168
+ 5. **Launch** - Deployment and user onboarding
169
+
170
+ ### Milestones
171
+
172
+ Specific dates and milestones to be determined based on team capacity and priorities.
173
+ """,
174
+ ),
175
+ ],
176
+ ),
177
+ PrdTemplate(
178
+ id="lean",
179
+ name="Lean PRD",
180
+ version=1,
181
+ description="Minimal viable PRD with only problem, users, and MVP features. "
182
+ "Best for quick iterations and early-stage projects.",
183
+ sections=[
184
+ PrdTemplateSection(
185
+ id="problem",
186
+ title="Problem",
187
+ source="problem",
188
+ format_template="""## Problem
189
+
190
+ {{ problem | default('Problem not yet defined.') }}
191
+ """,
192
+ ),
193
+ PrdTemplateSection(
194
+ id="users",
195
+ title="Target Users",
196
+ source="users",
197
+ format_template="""## Target Users
198
+
199
+ {{ users | bullet_list if users else 'Users not yet defined.' }}
200
+ """,
201
+ ),
202
+ PrdTemplateSection(
203
+ id="mvp_features",
204
+ title="MVP Features",
205
+ source="features",
206
+ format_template="""## MVP Features
207
+
208
+ {{ features | numbered_list if features else 'Features not yet defined.' }}
209
+ """,
210
+ ),
211
+ ],
212
+ ),
213
+ PrdTemplate(
214
+ id="enterprise",
215
+ name="Enterprise PRD",
216
+ version=1,
217
+ description="Full formal PRD with compliance sections, traceability matrix, "
218
+ "stakeholder analysis, and risk assessment. Best for large organizations.",
219
+ sections=[
220
+ PrdTemplateSection(
221
+ id="executive_summary",
222
+ title="Executive Summary",
223
+ source="problem",
224
+ format_template="""## Executive Summary
225
+
226
+ ### Overview
227
+
228
+ {{ problem | default('Project overview not yet defined.') }}
229
+
230
+ ### Document Purpose
231
+
232
+ This Product Requirements Document (PRD) defines the requirements, scope, and specifications for the project.
233
+ """,
234
+ ),
235
+ PrdTemplateSection(
236
+ id="stakeholder_analysis",
237
+ title="Stakeholder Analysis",
238
+ source="users",
239
+ format_template="""## Stakeholder Analysis
240
+
241
+ ### Primary Stakeholders
242
+
243
+ {{ users | bullet_list if users else 'Stakeholders not yet defined.' }}
244
+
245
+ ### Stakeholder Responsibilities
246
+
247
+ | Stakeholder | Role | Responsibility |
248
+ |-------------|------|----------------|
249
+ | Product Owner | Decision Maker | Final approval on requirements |
250
+ | Development Team | Implementer | Technical implementation |
251
+ | QA Team | Validator | Quality assurance |
252
+ """,
253
+ ),
254
+ PrdTemplateSection(
255
+ id="problem_statement",
256
+ title="Problem Statement",
257
+ source="problem",
258
+ format_template="""## Problem Statement
259
+
260
+ ### Current State
261
+
262
+ {{ problem | default('Current state analysis not yet completed.') }}
263
+
264
+ ### Desired State
265
+
266
+ The solution will address the identified problems and deliver value to stakeholders.
267
+
268
+ ### Gap Analysis
269
+
270
+ Detailed gap analysis between current and desired states to be documented during discovery.
271
+ """,
272
+ ),
273
+ PrdTemplateSection(
274
+ id="user_personas",
275
+ title="User Personas",
276
+ source="users",
277
+ format_template="""## User Personas
278
+
279
+ {{ users | bullet_list if users else 'User personas not yet defined.' }}
280
+ """,
281
+ ),
282
+ PrdTemplateSection(
283
+ id="requirements",
284
+ title="Requirements",
285
+ source="features",
286
+ format_template="""## Requirements
287
+
288
+ ### Functional Requirements
289
+
290
+ {% if features %}
291
+ | ID | Requirement | Priority | Source |
292
+ |----|-------------|----------|--------|
293
+ {% for feature in features %}
294
+ | FR-{{ loop.index }} | {{ feature }} | P1 | Discovery |
295
+ {% endfor %}
296
+ {% else %}
297
+ Requirements not yet defined.
298
+ {% endif %}
299
+
300
+ ### Non-Functional Requirements
301
+
302
+ | ID | Category | Requirement |
303
+ |----|----------|-------------|
304
+ | NFR-1 | Performance | System response time < 2 seconds |
305
+ | NFR-2 | Availability | 99.9% uptime SLA |
306
+ | NFR-3 | Security | Data encryption at rest and in transit |
307
+ """,
308
+ ),
309
+ PrdTemplateSection(
310
+ id="technical_architecture",
311
+ title="Technical Architecture",
312
+ source="tech_stack",
313
+ format_template="""## Technical Architecture
314
+
315
+ ### Technology Stack
316
+
317
+ {{ tech_stack | bullet_list if tech_stack else 'Technology stack not yet defined.' }}
318
+
319
+ ### System Constraints
320
+
321
+ {{ constraints | format_constraints if constraints else 'No specific constraints defined.' }}
322
+
323
+ ### Integration Points
324
+
325
+ Integration requirements to be documented during technical design phase.
326
+ """,
327
+ ),
328
+ PrdTemplateSection(
329
+ id="risk_assessment",
330
+ title="Risk Assessment",
331
+ source="constraints",
332
+ format_template="""## Risk Assessment
333
+
334
+ ### Identified Risks
335
+
336
+ | Risk | Impact | Likelihood | Mitigation |
337
+ |------|--------|------------|------------|
338
+ | Technical complexity | High | Medium | Phased implementation |
339
+ | Resource constraints | Medium | Medium | Priority-based scheduling |
340
+ | Scope creep | High | High | Strict change control |
341
+
342
+ ### Compliance Considerations
343
+
344
+ {{ constraints | format_constraints if constraints else 'Compliance requirements to be documented.' }}
345
+ """,
346
+ required=False,
347
+ ),
348
+ PrdTemplateSection(
349
+ id="success_metrics",
350
+ title="Success Metrics",
351
+ source="features",
352
+ format_template="""## Success Metrics
353
+
354
+ ### Key Performance Indicators
355
+
356
+ | KPI | Target | Measurement Method |
357
+ |-----|--------|-------------------|
358
+ | User Adoption | 80% | Active user tracking |
359
+ | Feature Completion | 100% | Sprint tracking |
360
+ | Defect Rate | < 5% | Bug tracking |
361
+ | User Satisfaction | > 4.0/5.0 | User surveys |
362
+ """,
363
+ ),
364
+ PrdTemplateSection(
365
+ id="timeline",
366
+ title="Timeline & Milestones",
367
+ source="features",
368
+ format_template="""## Timeline & Milestones
369
+
370
+ ### Project Phases
371
+
372
+ | Phase | Description | Duration |
373
+ |-------|-------------|----------|
374
+ | Discovery | Requirements gathering | 2 weeks |
375
+ | Design | Technical architecture | 2 weeks |
376
+ | Development | Implementation | 8 weeks |
377
+ | Testing | QA and UAT | 2 weeks |
378
+ | Deployment | Production release | 1 week |
379
+
380
+ ### Approval Gates
381
+
382
+ | Gate | Criteria | Approver |
383
+ |------|----------|----------|
384
+ | Requirements Sign-off | All requirements documented | Product Owner |
385
+ | Design Approval | Architecture approved | Tech Lead |
386
+ | Release Approval | All tests passing | QA Lead |
387
+ """,
388
+ ),
389
+ PrdTemplateSection(
390
+ id="traceability",
391
+ title="Traceability Matrix",
392
+ source="features",
393
+ format_template="""## Traceability Matrix
394
+
395
+ This section maps requirements to test cases and implementation components.
396
+
397
+ | Requirement ID | Feature | Test Case | Component |
398
+ |----------------|---------|-----------|-----------|
399
+ {% if features %}
400
+ {% for feature in features %}
401
+ | FR-{{ loop.index }} | {{ feature }} | TC-{{ loop.index }} | TBD |
402
+ {% endfor %}
403
+ {% else %}
404
+ | - | Requirements not yet defined | - | - |
405
+ {% endif %}
406
+ """,
407
+ required=False,
408
+ ),
409
+ ],
410
+ ),
411
+ PrdTemplate(
412
+ id="user-story-map",
413
+ name="User Story Map PRD",
414
+ version=1,
415
+ description="Organized around user journeys with story mapping structure. "
416
+ "Best for agile teams focused on user experience.",
417
+ sections=[
418
+ PrdTemplateSection(
419
+ id="overview",
420
+ title="Overview",
421
+ source="problem",
422
+ format_template="""## Overview
423
+
424
+ {{ problem | default('Project overview not yet defined.') }}
425
+ """,
426
+ ),
427
+ PrdTemplateSection(
428
+ id="user_activities",
429
+ title="User Activities",
430
+ source="users",
431
+ format_template="""## User Activities
432
+
433
+ ### Primary Users
434
+
435
+ {{ users | bullet_list if users else 'Users not yet defined.' }}
436
+
437
+ ### User Goals
438
+
439
+ Users want to accomplish specific goals efficiently and effectively.
440
+ """,
441
+ ),
442
+ PrdTemplateSection(
443
+ id="user_stories",
444
+ title="User Stories",
445
+ source="features",
446
+ format_template="""## User Stories
447
+
448
+ {% if features %}
449
+ {% for feature in features %}
450
+ ### Story {{ loop.index }}: {{ feature }}
451
+
452
+ **As a** user
453
+ **I want to** {{ feature }}
454
+ **So that** I can accomplish my goals effectively
455
+
456
+ **Acceptance Criteria:**
457
+ - [ ] Feature is implemented as specified
458
+ - [ ] Feature is tested and working
459
+ - [ ] Feature is documented
460
+
461
+ {% endfor %}
462
+ {% else %}
463
+ User stories not yet defined.
464
+ {% endif %}
465
+ """,
466
+ ),
467
+ PrdTemplateSection(
468
+ id="release_plan",
469
+ title="Release Plan",
470
+ source="features",
471
+ format_template="""## Release Plan
472
+
473
+ ### MVP (Release 1)
474
+
475
+ Core features to be included in initial release.
476
+
477
+ {{ features | bullet_list if features else 'Features not yet prioritized.' }}
478
+
479
+ ### Future Releases
480
+
481
+ Additional features and enhancements for subsequent releases.
482
+ """,
483
+ ),
484
+ ],
485
+ ),
486
+ PrdTemplate(
487
+ id="technical-spec",
488
+ name="Technical Specification",
489
+ version=1,
490
+ description="Focused on technical requirements, architecture diagrams, "
491
+ "API specifications, and data models. Best for technical audiences.",
492
+ sections=[
493
+ PrdTemplateSection(
494
+ id="overview",
495
+ title="Technical Overview",
496
+ source="problem",
497
+ format_template="""## Technical Overview
498
+
499
+ ### Purpose
500
+
501
+ {{ problem | default('Technical purpose not yet defined.') }}
502
+
503
+ ### Scope
504
+
505
+ This document defines the technical specifications for the system.
506
+ """,
507
+ ),
508
+ PrdTemplateSection(
509
+ id="architecture",
510
+ title="System Architecture",
511
+ source="tech_stack",
512
+ format_template="""## System Architecture
513
+
514
+ ### Technology Stack
515
+
516
+ {{ tech_stack | bullet_list if tech_stack else 'Technology stack not yet defined.' }}
517
+
518
+ ### Architecture Overview
519
+
520
+ ```
521
+ +-------------------+
522
+ | Frontend UI |
523
+ +-------------------+
524
+ |
525
+ +-------------------+
526
+ | API Gateway |
527
+ +-------------------+
528
+ |
529
+ +-------------------+
530
+ | Business Logic |
531
+ +-------------------+
532
+ |
533
+ +-------------------+
534
+ | Data Layer |
535
+ +-------------------+
536
+ ```
537
+
538
+ ### Component Diagram
539
+
540
+ Components to be detailed during technical design.
541
+ """,
542
+ ),
543
+ PrdTemplateSection(
544
+ id="api_specification",
545
+ title="API Specification",
546
+ source="features",
547
+ format_template="""## API Specification
548
+
549
+ ### Endpoints
550
+
551
+ {% if features %}
552
+ | Endpoint | Method | Description |
553
+ |----------|--------|-------------|
554
+ {% for feature in features %}
555
+ | /api/{{ feature | lower | replace(' ', '-') }} | GET/POST | {{ feature }} |
556
+ {% endfor %}
557
+ {% else %}
558
+ API endpoints not yet defined.
559
+ {% endif %}
560
+
561
+ ### Authentication
562
+
563
+ API authentication method to be determined.
564
+
565
+ ### Rate Limiting
566
+
567
+ Rate limiting policies to be defined based on usage patterns.
568
+ """,
569
+ ),
570
+ PrdTemplateSection(
571
+ id="data_models",
572
+ title="Data Models",
573
+ source="features",
574
+ format_template="""## Data Models
575
+
576
+ ### Entity Relationship
577
+
578
+ Core entities and their relationships to be documented.
579
+
580
+ ### Database Schema
581
+
582
+ Schema design to be completed during technical design phase.
583
+
584
+ {{ constraints | format_constraints if constraints else 'Database constraints not yet defined.' }}
585
+ """,
586
+ ),
587
+ PrdTemplateSection(
588
+ id="security",
589
+ title="Security Considerations",
590
+ source="constraints",
591
+ format_template="""## Security Considerations
592
+
593
+ ### Authentication & Authorization
594
+
595
+ - Authentication method: TBD
596
+ - Authorization model: Role-based access control (RBAC)
597
+
598
+ ### Data Protection
599
+
600
+ - Encryption at rest
601
+ - Encryption in transit (TLS 1.3)
602
+ - Data retention policies
603
+
604
+ ### Compliance
605
+
606
+ {{ constraints | format_constraints if constraints else 'Compliance requirements to be documented.' }}
607
+ """,
608
+ ),
609
+ PrdTemplateSection(
610
+ id="performance",
611
+ title="Performance Requirements",
612
+ source="constraints",
613
+ format_template="""## Performance Requirements
614
+
615
+ ### Response Time
616
+
617
+ - API response time: < 200ms (p95)
618
+ - Page load time: < 2 seconds
619
+
620
+ ### Throughput
621
+
622
+ - Target: 1000 requests/second
623
+
624
+ ### Scalability
625
+
626
+ - Horizontal scaling capability
627
+ - Auto-scaling based on load
628
+
629
+ {{ constraints | format_constraints if constraints else 'Additional performance constraints to be defined.' }}
630
+ """,
631
+ ),
632
+ ],
633
+ ),
634
+ ]
635
+
636
+
637
+ def get_global_template_dir() -> Path:
638
+ """Get the global PRD template directory.
639
+
640
+ Returns:
641
+ Path to ~/.codeframe/templates/prd/
642
+ """
643
+ return Path.home() / ".codeframe" / "templates" / "prd"
644
+
645
+
646
+ def get_project_template_dir(workspace_path: Optional[Path] = None) -> Path:
647
+ """Get the project-level PRD template directory.
648
+
649
+ Args:
650
+ workspace_path: Optional workspace path (defaults to current directory)
651
+
652
+ Returns:
653
+ Path to .codeframe/templates/prd/
654
+ """
655
+ base = workspace_path or Path.cwd()
656
+ return base / ".codeframe" / "templates" / "prd"
657
+
658
+
659
+ def save_template_to_file(template: PrdTemplate, path: Path) -> None:
660
+ """Save a template to a YAML file.
661
+
662
+ Args:
663
+ template: Template to save
664
+ path: File path to save to
665
+ """
666
+ # Convert to dictionary for YAML serialization
667
+ data = {
668
+ "id": template.id,
669
+ "name": template.name,
670
+ "version": template.version,
671
+ "description": template.description,
672
+ "sections": [
673
+ {
674
+ "id": s.id,
675
+ "title": s.title,
676
+ "source": s.source,
677
+ "format_template": s.format_template,
678
+ "required": s.required,
679
+ }
680
+ for s in template.sections
681
+ ],
682
+ }
683
+
684
+ # Ensure parent directory exists
685
+ path.parent.mkdir(parents=True, exist_ok=True)
686
+
687
+ with open(path, "w", encoding="utf-8") as f:
688
+ yaml.safe_dump(data, f, default_flow_style=False, allow_unicode=True)
689
+
690
+ logger.debug(f"Saved template to {path}")
691
+
692
+
693
+ def load_template_from_file(path: Path) -> PrdTemplate:
694
+ """Load a template from a YAML file.
695
+
696
+ Args:
697
+ path: File path to load from
698
+
699
+ Returns:
700
+ Loaded PrdTemplate
701
+
702
+ Raises:
703
+ FileNotFoundError: If file doesn't exist
704
+ ValueError: If file is not a YAML file
705
+ yaml.YAMLError: If file is not valid YAML
706
+ """
707
+ # Resolve to absolute path and validate
708
+ path = path.resolve()
709
+ if not path.is_file():
710
+ raise FileNotFoundError(f"Template file not found: {path}")
711
+
712
+ if path.suffix.lower() not in [".yaml", ".yml"]:
713
+ raise ValueError(f"Template must be a YAML file, got: {path.suffix}")
714
+
715
+ with open(path, encoding="utf-8") as f:
716
+ data = yaml.safe_load(f)
717
+
718
+ if not data:
719
+ raise ValueError(f"Template file is empty or invalid: {path}")
720
+
721
+ sections = [
722
+ PrdTemplateSection(
723
+ id=s["id"],
724
+ title=s["title"],
725
+ source=s["source"],
726
+ format_template=s["format_template"],
727
+ required=s.get("required", True),
728
+ )
729
+ for s in data.get("sections", [])
730
+ ]
731
+
732
+ return PrdTemplate(
733
+ id=data["id"],
734
+ name=data["name"],
735
+ version=data.get("version", 1),
736
+ description=data.get("description", ""),
737
+ sections=sections,
738
+ )
739
+
740
+
741
+ class PrdTemplateManager:
742
+ """Manager for PRD templates.
743
+
744
+ Handles:
745
+ - Loading and storing templates
746
+ - Retrieving templates by ID
747
+ - Validating template structure
748
+ - Rendering templates with discovery data
749
+ - Importing/exporting templates
750
+ """
751
+
752
+ def __init__(self, workspace_path: Optional[Path] = None):
753
+ """Initialize with built-in templates.
754
+
755
+ Args:
756
+ workspace_path: Optional workspace path for project templates
757
+ """
758
+ self.templates: dict[str, PrdTemplate] = {}
759
+ self.workspace_path = workspace_path
760
+
761
+ # Load built-in templates
762
+ for template in BUILTIN_TEMPLATES:
763
+ self.templates[template.id] = template
764
+
765
+ # Load custom templates from global directory
766
+ self._load_from_directory(get_global_template_dir())
767
+
768
+ # Load project templates (override global)
769
+ if workspace_path:
770
+ self._load_from_directory(get_project_template_dir(workspace_path))
771
+
772
+ # Set up Jinja2 environment with autoescape for defense-in-depth
773
+ # PRDs are markdown but could be rendered to HTML downstream
774
+ self._env = Environment(loader=BaseLoader(), autoescape=True)
775
+ for name, func in TEMPLATE_FUNCTIONS.items():
776
+ self._env.filters[name] = func
777
+
778
+ logger.info(f"PrdTemplateManager initialized with {len(self.templates)} templates")
779
+
780
+ def _load_from_directory(self, directory: Path) -> None:
781
+ """Load templates from a directory.
782
+
783
+ Args:
784
+ directory: Directory containing YAML template files
785
+ """
786
+ if not directory.exists():
787
+ return
788
+
789
+ # Support both .yaml and .yml extensions
790
+ for pattern in ("*.yaml", "*.yml"):
791
+ for path in directory.glob(pattern):
792
+ try:
793
+ template = load_template_from_file(path)
794
+ if template.id in self.templates:
795
+ existing = self.templates[template.id]
796
+ logger.warning(
797
+ f"Template '{template.id}' from {path} overrides "
798
+ f"existing template '{existing.name}'"
799
+ )
800
+ self.templates[template.id] = template
801
+ logger.debug(f"Loaded template from {path}")
802
+ except Exception as e:
803
+ logger.warning(f"Failed to load template from {path}: {e}")
804
+
805
+ def get_template(self, template_id: str) -> Optional[PrdTemplate]:
806
+ """Get a template by ID.
807
+
808
+ Args:
809
+ template_id: Template identifier
810
+
811
+ Returns:
812
+ PrdTemplate if found, None otherwise
813
+ """
814
+ return self.templates.get(template_id)
815
+
816
+ def list_templates(self) -> list[PrdTemplate]:
817
+ """List all available templates.
818
+
819
+ Returns:
820
+ List of PrdTemplate objects sorted by name
821
+ """
822
+ return sorted(self.templates.values(), key=lambda t: t.name or "")
823
+
824
+ def validate_template(self, template: PrdTemplate) -> list[str]:
825
+ """Validate a template structure.
826
+
827
+ Args:
828
+ template: Template to validate
829
+
830
+ Returns:
831
+ List of validation error messages (empty if valid)
832
+ """
833
+ errors = []
834
+
835
+ # Check required fields
836
+ if not template.id:
837
+ errors.append("Template ID is required")
838
+
839
+ if not template.name:
840
+ errors.append("Template name is required")
841
+
842
+ if not template.sections:
843
+ errors.append("Template must have at least one section")
844
+
845
+ # Validate each section's Jinja2 syntax
846
+ for section in template.sections:
847
+ try:
848
+ self._env.from_string(section.format_template)
849
+ except TemplateSyntaxError as e:
850
+ errors.append(f"Section '{section.id}' has invalid Jinja2 syntax: {e}")
851
+
852
+ return errors
853
+
854
+ def render_template(
855
+ self, template: PrdTemplate, discovery_data: dict[str, Any]
856
+ ) -> str:
857
+ """Render a template with discovery data.
858
+
859
+ Args:
860
+ template: Template to render
861
+ discovery_data: Discovery data dictionary with keys like
862
+ problem, users, features, constraints, tech_stack
863
+
864
+ Returns:
865
+ Rendered PRD content as markdown string
866
+ """
867
+ sections = []
868
+
869
+ for section in template.sections:
870
+ try:
871
+ jinja_template = self._env.from_string(section.format_template)
872
+ rendered = jinja_template.render(**discovery_data)
873
+ sections.append(rendered.strip())
874
+ except TemplateSyntaxError as e:
875
+ logger.error(f"Template syntax error in section {section.id}: {e}")
876
+ sections.append(f"## {section.title}\n\n*Template syntax error: {e}*")
877
+ except (KeyboardInterrupt, SystemExit):
878
+ raise # Don't catch these
879
+ except Exception as e:
880
+ logger.error(f"Failed to render section {section.id}: {e}", exc_info=True)
881
+ sections.append(f"## {section.title}\n\n*Error rendering section. Check logs for details.*")
882
+
883
+ # Join with double newlines for proper markdown separation
884
+ return "\n\n".join(sections)
885
+
886
+ def import_template(self, source_path: Path, persist: bool = False) -> PrdTemplate:
887
+ """Import a template from a file.
888
+
889
+ Args:
890
+ source_path: Path to YAML template file
891
+ persist: If True, save template to the project template directory
892
+
893
+ Returns:
894
+ Imported template
895
+
896
+ Raises:
897
+ FileNotFoundError: If source file doesn't exist
898
+ ValueError: If template validation fails
899
+ """
900
+ template = load_template_from_file(source_path)
901
+
902
+ # Validate before registering
903
+ errors = self.validate_template(template)
904
+ if errors:
905
+ raise ValueError(f"Invalid template: {', '.join(errors)}")
906
+
907
+ self.templates[template.id] = template
908
+
909
+ if persist:
910
+ self.persist_template(template)
911
+
912
+ logger.info(f"Imported template: {template.id}")
913
+ return template
914
+
915
+ def persist_template(self, template: PrdTemplate, to_project: bool = True) -> Path:
916
+ """Persist a template to disk.
917
+
918
+ Saves the template to either the project template directory (.codeframe/templates/prd/)
919
+ or the global template directory (~/.codeframe/templates/prd/).
920
+
921
+ Args:
922
+ template: Template to persist
923
+ to_project: If True, save to project directory; if False, save to global
924
+
925
+ Returns:
926
+ Path where template was saved
927
+
928
+ Raises:
929
+ ValueError: If to_project=True but no workspace_path configured
930
+ """
931
+ if to_project:
932
+ if self.workspace_path is None:
933
+ raise ValueError(
934
+ "Cannot persist to project: no workspace_path configured. "
935
+ "Either set to_project=False or initialize PrdTemplateManager with workspace_path."
936
+ )
937
+ target_dir = get_project_template_dir(self.workspace_path)
938
+ else:
939
+ target_dir = get_global_template_dir()
940
+
941
+ # Ensure directory exists
942
+ target_dir.mkdir(parents=True, exist_ok=True)
943
+
944
+ # Save template
945
+ output_path = target_dir / f"{template.id}.yaml"
946
+ save_template_to_file(template, output_path)
947
+
948
+ logger.info(f"Persisted template '{template.id}' to {output_path}")
949
+ return output_path
950
+
951
+ def export_template(self, template_id: str, output_path: Path) -> None:
952
+ """Export a template to a file.
953
+
954
+ Args:
955
+ template_id: ID of template to export
956
+ output_path: Path to save the template
957
+
958
+ Raises:
959
+ ValueError: If template not found
960
+ """
961
+ template = self.get_template(template_id)
962
+ if template is None:
963
+ raise ValueError(f"Template '{template_id}' not found")
964
+
965
+ save_template_to_file(template, output_path)
966
+ logger.info(f"Exported template '{template_id}' to {output_path}")
967
+
968
+ def register_template(self, template: PrdTemplate) -> None:
969
+ """Register a custom template.
970
+
971
+ Args:
972
+ template: Template to register
973
+ """
974
+ self.templates[template.id] = template
975
+ logger.info(f"Registered template: {template.id}")