PlanOpticon
| 0981a08… | noreply | 1 | """Skill: Generate a product/project roadmap.""" |
| 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 RoadmapSkill(Skill): |
| 0981a08… | noreply | 12 | name = "roadmap" |
| 0981a08… | noreply | 13 | description = "Generate a product/project roadmap" |
| 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 | roadmap_types = {"milestone", "feature", "dependency"} |
| 0981a08… | noreply | 21 | relevant = [ |
| 0981a08… | noreply | 22 | e for e in context.planning_entities if getattr(e, "type", "").lower() in roadmap_types |
| 0981a08… | noreply | 23 | ] |
| 0981a08… | noreply | 24 | |
| 0981a08… | noreply | 25 | parts = [ |
| 0981a08… | noreply | 26 | "You are a product strategist. Using the following " |
| 0981a08… | noreply | 27 | "knowledge graph context, generate a product roadmap.", |
| 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 | "## Milestones, Features & Dependencies", |
| 0981a08… | noreply | 39 | ] |
| 0981a08… | noreply | 40 | for e in relevant: |
| 0981a08… | noreply | 41 | parts.append(f"- [{getattr(e, 'type', 'unknown')}] {e}") |
| 0981a08… | noreply | 42 | |
| 0981a08… | noreply | 43 | if not relevant: |
| 0981a08… | noreply | 44 | parts.append( |
| 0981a08… | noreply | 45 | "(No pre-filtered entities; derive roadmap items from the full context above.)" |
| 0981a08… | noreply | 46 | ) |
| 0981a08… | noreply | 47 | |
| 0981a08… | noreply | 48 | parts.append( |
| 0981a08… | noreply | 49 | "\nGenerate a markdown roadmap with:\n" |
| 0981a08… | noreply | 50 | "1. Vision & Strategy\n" |
| 0981a08… | noreply | 51 | "2. Phases (with timeline estimates)\n" |
| 0981a08… | noreply | 52 | "3. Key Dependencies\n" |
| 0981a08… | noreply | 53 | "4. A Mermaid Gantt chart summarizing the timeline\n\n" |
| 0981a08… | noreply | 54 | "Return ONLY the markdown." |
| 0981a08… | noreply | 55 | ) |
| 0981a08… | noreply | 56 | |
| 0981a08… | noreply | 57 | prompt = "\n".join(parts) |
| 0981a08… | noreply | 58 | response = context.provider_manager.chat(messages=[{"role": "user", "content": prompt}]) |
| 0981a08… | noreply | 59 | |
| 0981a08… | noreply | 60 | return Artifact( |
| 0981a08… | noreply | 61 | name="Roadmap", |
| 0981a08… | noreply | 62 | content=response, |
| 0981a08… | noreply | 63 | artifact_type="roadmap", |
| 0981a08… | noreply | 64 | format="markdown", |
| 0981a08… | noreply | 65 | ) |
| 0981a08… | noreply | 66 | |
| 0981a08… | noreply | 67 | |
| 0981a08… | noreply | 68 | register_skill(RoadmapSkill()) |