PlanOpticon
| 0981a08… | noreply | 1 | """Skill: Generate a product requirements document (PRD) / feature spec.""" |
| 0981a08… | noreply | 2 | |
| 0981a08… | noreply | 3 | from video_processor.agent.skills.base import ( |
| 0981a08… | noreply | 4 | AgentContext, |
| 0981a08… | noreply | 5 | Artifact, |
| 0981a08… | noreply | 6 | Skill, |
| 0981a08… | noreply | 7 | register_skill, |
| 0981a08… | noreply | 8 | ) |
| 0981a08… | noreply | 9 | |
| 0981a08… | noreply | 10 | |
| 0981a08… | noreply | 11 | class PRDSkill(Skill): |
| 0981a08… | noreply | 12 | name = "prd" |
| 0981a08… | noreply | 13 | description = "Generate a product requirements document (PRD) / feature spec" |
| 0981a08… | noreply | 14 | |
| 0981a08… | noreply | 15 | def execute(self, context: AgentContext, **kwargs) -> Artifact: |
| 0981a08… | noreply | 16 | stats = context.query_engine.stats() |
| 0981a08… | noreply | 17 | entities = context.query_engine.entities() |
| 0981a08… | noreply | 18 | relationships = context.query_engine.relationships() |
| 0981a08… | noreply | 19 | |
| 0981a08… | noreply | 20 | relevant_types = {"requirement", "feature", "constraint"} |
| 0981a08… | noreply | 21 | filtered = [ |
| 0981a08… | noreply | 22 | e for e in context.planning_entities if getattr(e, "type", "").lower() in relevant_types |
| 0981a08… | noreply | 23 | ] |
| 0981a08… | noreply | 24 | |
| 0981a08… | noreply | 25 | parts = [ |
| 0981a08… | noreply | 26 | "You are a product manager. Using the following knowledge " |
| 0981a08… | noreply | 27 | "graph context, generate a product requirements document.", |
| 0981a08… | noreply | 28 | "", |
| 0981a08… | noreply | 29 | "## Knowledge Graph Overview", |
| 0981a08… | noreply | 30 | stats.to_text(), |
| 0981a08… | noreply | 31 | "", |
| 0981a08… | noreply | 32 | "## Entities", |
| 0981a08… | noreply | 33 | entities.to_text(), |
| 0981a08… | noreply | 34 | "", |
| 0981a08… | noreply | 35 | "## Relationships", |
| 0981a08… | noreply | 36 | relationships.to_text(), |
| 0981a08… | noreply | 37 | "", |
| 0981a08… | noreply | 38 | "## Relevant Planning Entities", |
| 0981a08… | noreply | 39 | ] |
| 0981a08… | noreply | 40 | for e in filtered: |
| 0981a08… | noreply | 41 | parts.append(f"- [{getattr(e, 'type', 'unknown')}] {e}") |
| 0981a08… | noreply | 42 | |
| 0981a08… | noreply | 43 | if not filtered: |
| 0981a08… | noreply | 44 | parts.append( |
| 0981a08… | noreply | 45 | "(No pre-filtered entities; derive requirements from the full context above.)" |
| 0981a08… | noreply | 46 | ) |
| 0981a08… | noreply | 47 | |
| 0981a08… | noreply | 48 | parts.append( |
| 0981a08… | noreply | 49 | "\nGenerate a PRD with:\n" |
| 0981a08… | noreply | 50 | "1. Problem Statement\n" |
| 0981a08… | noreply | 51 | "2. User Stories\n" |
| 0981a08… | noreply | 52 | "3. Functional Requirements\n" |
| 0981a08… | noreply | 53 | "4. Non-Functional Requirements\n" |
| 0981a08… | noreply | 54 | "5. Acceptance Criteria\n" |
| 0981a08… | noreply | 55 | "6. Out of Scope\n\n" |
| 0981a08… | noreply | 56 | "Return ONLY the markdown." |
| 0981a08… | noreply | 57 | ) |
| 0981a08… | noreply | 58 | |
| 0981a08… | noreply | 59 | prompt = "\n".join(parts) |
| 0981a08… | noreply | 60 | response = context.provider_manager.chat(messages=[{"role": "user", "content": prompt}]) |
| 0981a08… | noreply | 61 | |
| 0981a08… | noreply | 62 | return Artifact( |
| 0981a08… | noreply | 63 | name="Product Requirements Document", |
| 0981a08… | noreply | 64 | content=response, |
| 0981a08… | noreply | 65 | artifact_type="prd", |
| 0981a08… | noreply | 66 | format="markdown", |
| 0981a08… | noreply | 67 | ) |
| 0981a08… | noreply | 68 | |
| 0981a08… | noreply | 69 | |
| 0981a08… | noreply | 70 | register_skill(PRDSkill()) |