Roadmap competitor analysis
This commit is contained in:
@@ -168,9 +168,10 @@ export class AgentManager extends EventEmitter {
|
||||
startRoadmapGeneration(
|
||||
projectId: string,
|
||||
projectPath: string,
|
||||
refresh: boolean = false
|
||||
refresh: boolean = false,
|
||||
enableCompetitorAnalysis: boolean = false
|
||||
): void {
|
||||
this.queueManager.startRoadmapGeneration(projectId, projectPath, refresh);
|
||||
this.queueManager.startRoadmapGeneration(projectId, projectPath, refresh, enableCompetitorAnalysis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,8 @@ export class AgentQueueManager {
|
||||
startRoadmapGeneration(
|
||||
projectId: string,
|
||||
projectPath: string,
|
||||
refresh: boolean = false
|
||||
refresh: boolean = false,
|
||||
enableCompetitorAnalysis: boolean = false
|
||||
): void {
|
||||
const autoBuildSource = this.processManager.getAutoBuildSourcePath();
|
||||
|
||||
@@ -57,6 +58,11 @@ export class AgentQueueManager {
|
||||
args.push('--refresh');
|
||||
}
|
||||
|
||||
// Add competitor analysis flag if enabled
|
||||
if (enableCompetitorAnalysis) {
|
||||
args.push('--competitor-analysis');
|
||||
}
|
||||
|
||||
// Use projectId as taskId for roadmap operations
|
||||
this.spawnRoadmapProcess(projectId, projectPath, args);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { IPC_CHANNELS, AUTO_BUILD_PATHS, getSpecsDir } from '../../shared/constants';
|
||||
import type { IPCResult, Roadmap, RoadmapFeature, RoadmapFeatureStatus, RoadmapGenerationStatus, Task, TaskMetadata } from '../../shared/types';
|
||||
import type { IPCResult, Roadmap, RoadmapFeature, RoadmapFeatureStatus, RoadmapGenerationStatus, Task, TaskMetadata, CompetitorAnalysis } from '../../shared/types';
|
||||
import path from 'path';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
|
||||
import { projectStore } from '../project-store';
|
||||
@@ -42,6 +42,65 @@ export function registerRoadmapHandlers(
|
||||
const content = readFileSync(roadmapPath, 'utf-8');
|
||||
const rawRoadmap = JSON.parse(content);
|
||||
|
||||
// Load competitor analysis if available (competitor_analysis.json)
|
||||
const competitorAnalysisPath = path.join(
|
||||
project.path,
|
||||
AUTO_BUILD_PATHS.ROADMAP_DIR,
|
||||
AUTO_BUILD_PATHS.COMPETITOR_ANALYSIS
|
||||
);
|
||||
let competitorAnalysis: CompetitorAnalysis | undefined;
|
||||
if (existsSync(competitorAnalysisPath)) {
|
||||
try {
|
||||
const competitorContent = readFileSync(competitorAnalysisPath, 'utf-8');
|
||||
const rawCompetitor = JSON.parse(competitorContent);
|
||||
// Transform snake_case to camelCase for frontend
|
||||
competitorAnalysis = {
|
||||
projectContext: {
|
||||
projectName: rawCompetitor.project_context?.project_name || '',
|
||||
projectType: rawCompetitor.project_context?.project_type || '',
|
||||
targetAudience: rawCompetitor.project_context?.target_audience || ''
|
||||
},
|
||||
competitors: (rawCompetitor.competitors || []).map((c: Record<string, unknown>) => ({
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
url: c.url,
|
||||
description: c.description,
|
||||
relevance: c.relevance || 'medium',
|
||||
painPoints: ((c.pain_points as Array<Record<string, unknown>>) || []).map((p) => ({
|
||||
id: p.id,
|
||||
description: p.description,
|
||||
source: p.source,
|
||||
severity: p.severity || 'medium',
|
||||
frequency: p.frequency || '',
|
||||
opportunity: p.opportunity || ''
|
||||
})),
|
||||
strengths: (c.strengths as string[]) || [],
|
||||
marketPosition: (c.market_position as string) || ''
|
||||
})),
|
||||
marketGaps: (rawCompetitor.market_gaps || []).map((g: Record<string, unknown>) => ({
|
||||
id: g.id,
|
||||
description: g.description,
|
||||
affectedCompetitors: (g.affected_competitors as string[]) || [],
|
||||
opportunitySize: g.opportunity_size || 'medium',
|
||||
suggestedFeature: (g.suggested_feature as string) || ''
|
||||
})),
|
||||
insightsSummary: {
|
||||
topPainPoints: rawCompetitor.insights_summary?.top_pain_points || [],
|
||||
differentiatorOpportunities: rawCompetitor.insights_summary?.differentiator_opportunities || [],
|
||||
marketTrends: rawCompetitor.insights_summary?.market_trends || []
|
||||
},
|
||||
researchMetadata: {
|
||||
searchQueriesUsed: rawCompetitor.research_metadata?.search_queries_used || [],
|
||||
sourcesConsulted: rawCompetitor.research_metadata?.sources_consulted || [],
|
||||
limitations: rawCompetitor.research_metadata?.limitations || []
|
||||
},
|
||||
createdAt: rawCompetitor.metadata?.created_at ? new Date(rawCompetitor.metadata.created_at) : new Date()
|
||||
};
|
||||
} catch {
|
||||
// Ignore competitor analysis parsing errors - it's optional
|
||||
}
|
||||
}
|
||||
|
||||
// Transform snake_case to camelCase for frontend
|
||||
const roadmap: Roadmap = {
|
||||
id: rawRoadmap.id || `roadmap-${Date.now()}`,
|
||||
@@ -82,9 +141,11 @@ export function registerRoadmapHandlers(
|
||||
status: feature.status || 'idea',
|
||||
acceptanceCriteria: feature.acceptance_criteria || [],
|
||||
userStories: feature.user_stories || [],
|
||||
linkedSpecId: feature.linked_spec_id
|
||||
linkedSpecId: feature.linked_spec_id,
|
||||
competitorInsightIds: (feature.competitor_insight_ids as string[]) || undefined
|
||||
})),
|
||||
status: rawRoadmap.status || 'draft',
|
||||
competitorAnalysis,
|
||||
createdAt: rawRoadmap.metadata?.created_at ? new Date(rawRoadmap.metadata.created_at) : new Date(),
|
||||
updatedAt: rawRoadmap.metadata?.updated_at ? new Date(rawRoadmap.metadata.updated_at) : new Date()
|
||||
};
|
||||
@@ -101,7 +162,7 @@ export function registerRoadmapHandlers(
|
||||
|
||||
ipcMain.on(
|
||||
IPC_CHANNELS.ROADMAP_GENERATE,
|
||||
(_, projectId: string) => {
|
||||
(_, projectId: string, enableCompetitorAnalysis?: boolean) => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow) return;
|
||||
|
||||
@@ -116,7 +177,7 @@ export function registerRoadmapHandlers(
|
||||
}
|
||||
|
||||
// Start roadmap generation via agent manager
|
||||
agentManager.startRoadmapGeneration(projectId, project.path, false);
|
||||
agentManager.startRoadmapGeneration(projectId, project.path, false, enableCompetitorAnalysis ?? false);
|
||||
|
||||
// Send initial progress
|
||||
mainWindow.webContents.send(
|
||||
@@ -133,7 +194,7 @@ export function registerRoadmapHandlers(
|
||||
|
||||
ipcMain.on(
|
||||
IPC_CHANNELS.ROADMAP_REFRESH,
|
||||
(_, projectId: string) => {
|
||||
(_, projectId: string, enableCompetitorAnalysis?: boolean) => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow) return;
|
||||
|
||||
@@ -148,7 +209,7 @@ export function registerRoadmapHandlers(
|
||||
}
|
||||
|
||||
// Start roadmap regeneration with refresh flag
|
||||
agentManager.startRoadmapGeneration(projectId, project.path, true);
|
||||
agentManager.startRoadmapGeneration(projectId, project.path, true, enableCompetitorAnalysis ?? false);
|
||||
|
||||
// Send initial progress
|
||||
mainWindow.webContents.send(
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { Search, Globe, AlertTriangle, TrendingUp } from 'lucide-react';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
} from './ui/alert-dialog';
|
||||
|
||||
interface CompetitorAnalysisDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onAccept: () => void;
|
||||
onDecline: () => void;
|
||||
}
|
||||
|
||||
export function CompetitorAnalysisDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onAccept,
|
||||
onDecline,
|
||||
}: CompetitorAnalysisDialogProps) {
|
||||
const handleAccept = () => {
|
||||
onAccept();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const handleDecline = () => {
|
||||
onDecline();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={onOpenChange}>
|
||||
<AlertDialogContent className="sm:max-w-[500px]">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="flex items-center gap-2 text-foreground">
|
||||
<TrendingUp className="h-5 w-5 text-primary" />
|
||||
Enable Competitor Analysis?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription className="text-muted-foreground">
|
||||
Enhance your roadmap with insights from competitor products
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<div className="py-4 space-y-4">
|
||||
{/* What it does */}
|
||||
<div className="rounded-lg bg-primary/5 border border-primary/20 p-4">
|
||||
<h4 className="text-sm font-medium text-foreground mb-2">
|
||||
What competitor analysis does:
|
||||
</h4>
|
||||
<ul className="text-sm text-muted-foreground space-y-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<Search className="h-4 w-4 mt-0.5 text-primary flex-shrink-0" />
|
||||
<span>Identifies 3-5 main competitors based on your project type</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<Globe className="h-4 w-4 mt-0.5 text-primary flex-shrink-0" />
|
||||
<span>
|
||||
Searches app stores, forums, and social media for user feedback and pain points
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<TrendingUp className="h-4 w-4 mt-0.5 text-primary flex-shrink-0" />
|
||||
<span>
|
||||
Suggests features that address gaps in competitor products
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Privacy notice */}
|
||||
<div className="rounded-lg bg-warning/10 border border-warning/30 p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className="h-5 w-5 text-warning flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-medium text-foreground">
|
||||
Web searches will be performed
|
||||
</h4>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
This feature will perform web searches to gather competitor information.
|
||||
Your project name and type will be used in search queries.
|
||||
No code or sensitive data is shared.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Optional info */}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
You can generate a roadmap without competitor analysis if you prefer.
|
||||
The roadmap will still be based on your project structure and best practices.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={handleDecline}>
|
||||
No, Skip Analysis
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleAccept}>
|
||||
Yes, Enable Analysis
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
Clock,
|
||||
AlertCircle,
|
||||
Play,
|
||||
ExternalLink
|
||||
} from 'lucide-react';
|
||||
ExternalLink,
|
||||
TrendingUp} from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Card } from './ui/card';
|
||||
@@ -41,7 +41,8 @@ import {
|
||||
ROADMAP_COMPLEXITY_COLORS,
|
||||
ROADMAP_IMPACT_COLORS
|
||||
} from '../../shared/constants';
|
||||
import type { RoadmapFeature, RoadmapPhase } from '../../shared/types';
|
||||
import { CompetitorAnalysisDialog } from './CompetitorAnalysisDialog';
|
||||
import type { RoadmapFeature, RoadmapPhase, CompetitorAnalysis, CompetitorPainPoint } from '../../shared/types';
|
||||
|
||||
interface RoadmapProps {
|
||||
projectId: string;
|
||||
@@ -50,10 +51,13 @@ interface RoadmapProps {
|
||||
|
||||
export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
const roadmap = useRoadmapStore((state) => state.roadmap);
|
||||
const competitorAnalysis = useRoadmapStore((state) => state.competitorAnalysis);
|
||||
const generationStatus = useRoadmapStore((state) => state.generationStatus);
|
||||
const updateFeatureLinkedSpec = useRoadmapStore((state) => state.updateFeatureLinkedSpec);
|
||||
const [selectedFeature, setSelectedFeature] = useState<RoadmapFeature | null>(null);
|
||||
const [activeTab, setActiveTab] = useState('phases');
|
||||
const [showCompetitorDialog, setShowCompetitorDialog] = useState(false);
|
||||
const [pendingAction, setPendingAction] = useState<'generate' | 'refresh' | null>(null);
|
||||
|
||||
// Load roadmap on mount
|
||||
useEffect(() => {
|
||||
@@ -61,11 +65,31 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
}, [projectId]);
|
||||
|
||||
const handleGenerate = () => {
|
||||
generateRoadmap(projectId);
|
||||
setPendingAction('generate');
|
||||
setShowCompetitorDialog(true);
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
refreshRoadmap(projectId);
|
||||
setPendingAction('refresh');
|
||||
setShowCompetitorDialog(true);
|
||||
};
|
||||
|
||||
const handleCompetitorDialogAccept = () => {
|
||||
if (pendingAction === 'generate') {
|
||||
generateRoadmap(projectId, true);
|
||||
} else if (pendingAction === 'refresh') {
|
||||
refreshRoadmap(projectId, true);
|
||||
}
|
||||
setPendingAction(null);
|
||||
};
|
||||
|
||||
const handleCompetitorDialogDecline = () => {
|
||||
if (pendingAction === 'generate') {
|
||||
generateRoadmap(projectId, false);
|
||||
} else if (pendingAction === 'refresh') {
|
||||
refreshRoadmap(projectId, false);
|
||||
}
|
||||
setPendingAction(null);
|
||||
};
|
||||
|
||||
const handleConvertToSpec = async (feature: RoadmapFeature) => {
|
||||
@@ -92,6 +116,23 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
|
||||
const stats = getFeatureStats(roadmap);
|
||||
|
||||
// Helper function to get competitor insights for a feature
|
||||
const getCompetitorInsightsForFeature = (feature: RoadmapFeature): CompetitorPainPoint[] => {
|
||||
if (!competitorAnalysis || !feature.competitorInsightIds || feature.competitorInsightIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const insights: CompetitorPainPoint[] = [];
|
||||
for (const competitor of competitorAnalysis.competitors) {
|
||||
for (const painPoint of competitor.painPoints) {
|
||||
if (feature.competitorInsightIds.includes(painPoint.id)) {
|
||||
insights.push(painPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
return insights;
|
||||
};
|
||||
|
||||
// Show generation progress
|
||||
if (generationStatus.phase !== 'idle' && generationStatus.phase !== 'complete') {
|
||||
return (
|
||||
@@ -116,20 +157,28 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
// Show empty state
|
||||
if (!roadmap) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Card className="w-full max-w-lg p-8 text-center">
|
||||
<Map className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||
<h2 className="text-xl font-semibold mb-2">No Roadmap Yet</h2>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
Generate an AI-powered roadmap that understands your project's target
|
||||
audience and creates a strategic feature plan.
|
||||
</p>
|
||||
<Button onClick={handleGenerate} size="lg">
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
Generate Roadmap
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
<>
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Card className="w-full max-w-lg p-8 text-center">
|
||||
<Map className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||
<h2 className="text-xl font-semibold mb-2">No Roadmap Yet</h2>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
Generate an AI-powered roadmap that understands your project's target
|
||||
audience and creates a strategic feature plan.
|
||||
</p>
|
||||
<Button onClick={handleGenerate} size="lg">
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
Generate Roadmap
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
<CompetitorAnalysisDialog
|
||||
open={showCompetitorDialog}
|
||||
onOpenChange={setShowCompetitorDialog}
|
||||
onAccept={handleCompetitorDialogAccept}
|
||||
onDecline={handleCompetitorDialogDecline}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -237,7 +286,9 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
onClick={() => setSelectedFeature(feature)}
|
||||
onConvertToSpec={handleConvertToSpec}
|
||||
onGoToTask={handleGoToTask}
|
||||
/>
|
||||
hasCompetitorInsight={
|
||||
!!feature.competitorInsightIds && feature.competitorInsightIds.length > 0
|
||||
} />
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
@@ -268,13 +319,19 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
onClick={() => setSelectedFeature(feature)}
|
||||
>
|
||||
<div className="font-medium text-sm">{feature.title}</div>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<div className="flex items-center gap-2 mt-1 flex-wrap">
|
||||
<Badge variant="outline" className={`text-xs ${ROADMAP_COMPLEXITY_COLORS[feature.complexity]}`}>
|
||||
{feature.complexity}
|
||||
</Badge>
|
||||
<Badge variant="outline" className={`text-xs ${ROADMAP_IMPACT_COLORS[feature.impact]}`}>
|
||||
{feature.impact} impact
|
||||
</Badge>
|
||||
{feature.competitorInsightIds && feature.competitorInsightIds.length > 0 && (
|
||||
<Badge variant="outline" className="text-xs text-primary border-primary/50">
|
||||
<TrendingUp className="h-3 w-3 mr-1" />
|
||||
Insight
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -294,8 +351,16 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
|
||||
onClose={() => setSelectedFeature(null)}
|
||||
onConvertToSpec={handleConvertToSpec}
|
||||
onGoToTask={handleGoToTask}
|
||||
/>
|
||||
competitorInsights={getCompetitorInsightsForFeature(selectedFeature)} />
|
||||
)}
|
||||
|
||||
{/* Competitor Analysis Permission Dialog */}
|
||||
<CompetitorAnalysisDialog
|
||||
open={showCompetitorDialog}
|
||||
onOpenChange={setShowCompetitorDialog}
|
||||
onAccept={handleCompetitorDialogAccept}
|
||||
onDecline={handleCompetitorDialogDecline}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -388,17 +453,20 @@ function PhaseCard({ phase, features, isFirst, onFeatureSelect, onConvertToSpec,
|
||||
className="flex items-center justify-between p-2 rounded-md bg-muted/50 hover:bg-muted cursor-pointer transition-colors"
|
||||
onClick={() => onFeatureSelect(feature)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`text-xs ${ROADMAP_PRIORITY_COLORS[feature.priority]}`}
|
||||
>
|
||||
{feature.priority}
|
||||
</Badge>
|
||||
<span className="text-sm">{feature.title}</span>
|
||||
<span className="text-sm truncate">{feature.title}</span>
|
||||
{feature.competitorInsightIds && feature.competitorInsightIds.length > 0 && (
|
||||
<TrendingUp className="h-3 w-3 text-primary flex-shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
{feature.status === 'done' ? (
|
||||
<CheckCircle2 className="h-4 w-4 text-success" />
|
||||
<CheckCircle2 className="h-4 w-4 text-success flex-shrink-0" />
|
||||
) : feature.linkedSpecId ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -411,12 +479,11 @@ function PhaseCard({ phase, features, isFirst, onFeatureSelect, onConvertToSpec,
|
||||
>
|
||||
<ExternalLink className="h-3 w-3 mr-1" />
|
||||
View Task
|
||||
</Button>
|
||||
) : (
|
||||
</Button> ) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-2"
|
||||
className="h-6 px-2 flex-shrink-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onConvertToSpec(feature);
|
||||
@@ -445,17 +512,17 @@ interface FeatureCardProps {
|
||||
onClick: () => void;
|
||||
onConvertToSpec: (feature: RoadmapFeature) => void;
|
||||
onGoToTask: (specId: string) => void;
|
||||
hasCompetitorInsight?: boolean;
|
||||
}
|
||||
|
||||
function FeatureCard({ feature, onClick, onConvertToSpec, onGoToTask }: FeatureCardProps) {
|
||||
return (
|
||||
function FeatureCard({ feature, onClick, onConvertToSpec, onGoToTask, hasCompetitorInsight = false }: FeatureCardProps) { return (
|
||||
<Card
|
||||
className="p-4 hover:bg-muted/50 cursor-pointer transition-colors"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<div className="flex items-center gap-2 mb-1 flex-wrap">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={ROADMAP_PRIORITY_COLORS[feature.priority]}
|
||||
@@ -468,6 +535,19 @@ function FeatureCard({ feature, onClick, onConvertToSpec, onGoToTask }: FeatureC
|
||||
<Badge variant="outline" className={`text-xs ${ROADMAP_IMPACT_COLORS[feature.impact]}`}>
|
||||
{feature.impact} impact
|
||||
</Badge>
|
||||
{hasCompetitorInsight && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="outline" className="text-xs text-primary border-primary/50">
|
||||
<TrendingUp className="h-3 w-3 mr-1" />
|
||||
Competitor Insight
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
This feature addresses competitor pain points
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="font-medium">{feature.title}</h3>
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">{feature.description}</p>
|
||||
@@ -508,10 +588,10 @@ interface FeatureDetailPanelProps {
|
||||
onClose: () => void;
|
||||
onConvertToSpec: (feature: RoadmapFeature) => void;
|
||||
onGoToTask: (specId: string) => void;
|
||||
competitorInsights?: CompetitorPainPoint[];
|
||||
}
|
||||
|
||||
function FeatureDetailPanel({ feature, onClose, onConvertToSpec, onGoToTask }: FeatureDetailPanelProps) {
|
||||
return (
|
||||
function FeatureDetailPanel({ feature, onClose, onConvertToSpec, onGoToTask, competitorInsights = [] }: FeatureDetailPanelProps) { return (
|
||||
<div className="fixed inset-y-0 right-0 w-96 bg-card border-l border-border shadow-lg flex flex-col z-50">
|
||||
{/* Header */}
|
||||
<div className="shrink-0 p-4 border-b border-border">
|
||||
@@ -624,6 +704,43 @@ function FeatureDetailPanel({ feature, onClose, onConvertToSpec, onGoToTask }: F
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Competitor Insights */}
|
||||
{competitorInsights.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-sm font-medium mb-2 flex items-center gap-2">
|
||||
<TrendingUp className="h-4 w-4 text-primary" />
|
||||
Addresses Competitor Pain Points
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{competitorInsights.map((insight) => (
|
||||
<div
|
||||
key={insight.id}
|
||||
className="p-2 bg-primary/5 border border-primary/20 rounded-md"
|
||||
>
|
||||
<p className="text-sm text-foreground">{insight.description}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{insight.source}
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`text-xs ${
|
||||
insight.severity === 'high'
|
||||
? 'text-red-500 border-red-500/50'
|
||||
: insight.severity === 'medium'
|
||||
? 'text-yellow-500 border-yellow-500/50'
|
||||
: 'text-green-500 border-green-500/50'
|
||||
}`}
|
||||
>
|
||||
{insight.severity} severity
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { create } from 'zustand';
|
||||
import type {
|
||||
CompetitorAnalysis,
|
||||
Roadmap,
|
||||
RoadmapFeature,
|
||||
RoadmapFeatureStatus,
|
||||
@@ -9,10 +10,12 @@ import type {
|
||||
interface RoadmapState {
|
||||
// Data
|
||||
roadmap: Roadmap | null;
|
||||
competitorAnalysis: CompetitorAnalysis | null;
|
||||
generationStatus: RoadmapGenerationStatus;
|
||||
|
||||
// Actions
|
||||
setRoadmap: (roadmap: Roadmap | null) => void;
|
||||
setCompetitorAnalysis: (analysis: CompetitorAnalysis | null) => void;
|
||||
setGenerationStatus: (status: RoadmapGenerationStatus) => void;
|
||||
updateFeatureStatus: (featureId: string, status: RoadmapFeatureStatus) => void;
|
||||
updateFeatureLinkedSpec: (featureId: string, specId: string) => void;
|
||||
@@ -28,11 +31,14 @@ const initialGenerationStatus: RoadmapGenerationStatus = {
|
||||
export const useRoadmapStore = create<RoadmapState>((set) => ({
|
||||
// Initial state
|
||||
roadmap: null,
|
||||
competitorAnalysis: null,
|
||||
generationStatus: initialGenerationStatus,
|
||||
|
||||
// Actions
|
||||
setRoadmap: (roadmap) => set({ roadmap }),
|
||||
|
||||
setCompetitorAnalysis: (analysis) => set({ competitorAnalysis: analysis }),
|
||||
|
||||
setGenerationStatus: (status) => set({ generationStatus: status }),
|
||||
|
||||
updateFeatureStatus: (featureId, status) =>
|
||||
@@ -74,6 +80,7 @@ export const useRoadmapStore = create<RoadmapState>((set) => ({
|
||||
clearRoadmap: () =>
|
||||
set({
|
||||
roadmap: null,
|
||||
competitorAnalysis: null,
|
||||
generationStatus: initialGenerationStatus
|
||||
})
|
||||
}));
|
||||
@@ -88,22 +95,32 @@ export async function loadRoadmap(projectId: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export function generateRoadmap(projectId: string): void {
|
||||
export function generateRoadmap(
|
||||
projectId: string,
|
||||
enableCompetitorAnalysis: boolean = false
|
||||
): void {
|
||||
useRoadmapStore.getState().setGenerationStatus({
|
||||
phase: 'analyzing',
|
||||
progress: 0,
|
||||
message: 'Starting roadmap generation...'
|
||||
message: enableCompetitorAnalysis
|
||||
? 'Starting roadmap generation with competitor analysis...'
|
||||
: 'Starting roadmap generation...'
|
||||
});
|
||||
window.electronAPI.generateRoadmap(projectId);
|
||||
window.electronAPI.generateRoadmap(projectId, enableCompetitorAnalysis);
|
||||
}
|
||||
|
||||
export function refreshRoadmap(projectId: string): void {
|
||||
export function refreshRoadmap(
|
||||
projectId: string,
|
||||
enableCompetitorAnalysis: boolean = false
|
||||
): void {
|
||||
useRoadmapStore.getState().setGenerationStatus({
|
||||
phase: 'analyzing',
|
||||
progress: 0,
|
||||
message: 'Refreshing roadmap...'
|
||||
message: enableCompetitorAnalysis
|
||||
? 'Refreshing roadmap with competitor analysis...'
|
||||
: 'Refreshing roadmap...'
|
||||
});
|
||||
window.electronAPI.refreshRoadmap(projectId);
|
||||
window.electronAPI.refreshRoadmap(projectId, enableCompetitorAnalysis);
|
||||
}
|
||||
|
||||
// Selectors
|
||||
|
||||
@@ -228,6 +228,7 @@ export const IPC_CHANNELS = {
|
||||
// Roadmap operations
|
||||
ROADMAP_GET: 'roadmap:get',
|
||||
ROADMAP_GENERATE: 'roadmap:generate',
|
||||
ROADMAP_GENERATE_WITH_COMPETITOR: 'roadmap:generateWithCompetitor',
|
||||
ROADMAP_REFRESH: 'roadmap:refresh',
|
||||
ROADMAP_UPDATE_FEATURE: 'roadmap:updateFeature',
|
||||
ROADMAP_CONVERT_TO_SPEC: 'roadmap:convertToSpec',
|
||||
@@ -366,6 +367,7 @@ export const AUTO_BUILD_PATHS = {
|
||||
REQUIREMENTS: 'requirements.json',
|
||||
ROADMAP_FILE: 'roadmap.json',
|
||||
ROADMAP_DISCOVERY: 'roadmap_discovery.json',
|
||||
COMPETITOR_ANALYSIS: 'competitor_analysis.json',
|
||||
IDEATION_FILE: 'ideation.json',
|
||||
IDEATION_CONTEXT: 'ideation_context.json',
|
||||
PROJECT_INDEX: '.auto-claude/project_index.json',
|
||||
|
||||
@@ -2,6 +2,67 @@
|
||||
* Roadmap-related types
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// Competitor Analysis Types
|
||||
// ============================================
|
||||
|
||||
export type CompetitorRelevance = 'high' | 'medium' | 'low';
|
||||
export type PainPointSeverity = 'high' | 'medium' | 'low';
|
||||
export type OpportunitySize = 'high' | 'medium' | 'low';
|
||||
|
||||
export interface CompetitorPainPoint {
|
||||
id: string;
|
||||
description: string;
|
||||
source: string;
|
||||
severity: PainPointSeverity;
|
||||
frequency: string;
|
||||
opportunity: string;
|
||||
}
|
||||
|
||||
export interface Competitor {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
description: string;
|
||||
relevance: CompetitorRelevance;
|
||||
painPoints: CompetitorPainPoint[];
|
||||
strengths: string[];
|
||||
marketPosition: string;
|
||||
}
|
||||
|
||||
export interface CompetitorMarketGap {
|
||||
id: string;
|
||||
description: string;
|
||||
affectedCompetitors: string[];
|
||||
opportunitySize: OpportunitySize;
|
||||
suggestedFeature: string;
|
||||
}
|
||||
|
||||
export interface CompetitorInsightsSummary {
|
||||
topPainPoints: string[];
|
||||
differentiatorOpportunities: string[];
|
||||
marketTrends: string[];
|
||||
}
|
||||
|
||||
export interface CompetitorResearchMetadata {
|
||||
searchQueriesUsed: string[];
|
||||
sourcesConsulted: string[];
|
||||
limitations: string[];
|
||||
}
|
||||
|
||||
export interface CompetitorAnalysis {
|
||||
projectContext: {
|
||||
projectName: string;
|
||||
projectType: string;
|
||||
targetAudience: string;
|
||||
};
|
||||
competitors: Competitor[];
|
||||
marketGaps: CompetitorMarketGap[];
|
||||
insightsSummary: CompetitorInsightsSummary;
|
||||
researchMetadata: CompetitorResearchMetadata;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Roadmap Types
|
||||
// ============================================
|
||||
@@ -52,6 +113,7 @@ export interface RoadmapFeature {
|
||||
acceptanceCriteria: string[];
|
||||
userStories: string[];
|
||||
linkedSpecId?: string;
|
||||
competitorInsightIds?: string[];
|
||||
}
|
||||
|
||||
export interface Roadmap {
|
||||
@@ -64,6 +126,7 @@ export interface Roadmap {
|
||||
phases: RoadmapPhase[];
|
||||
features: RoadmapFeature[];
|
||||
status: RoadmapStatus;
|
||||
competitorAnalysis?: CompetitorAnalysis;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
## YOUR ROLE - COMPETITOR ANALYSIS AGENT
|
||||
|
||||
You are the **Competitor Analysis Agent** in the Auto-Build framework. Your job is to research competitors of the project, analyze user feedback and pain points from competitor products, and provide insights that can inform roadmap feature prioritization.
|
||||
|
||||
**Key Principle**: Research real user feedback. Find actual pain points. Document sources.
|
||||
|
||||
---
|
||||
|
||||
## YOUR CONTRACT
|
||||
|
||||
**Inputs**:
|
||||
- `roadmap_discovery.json` - Project understanding with target audience and competitive context
|
||||
- `project_index.json` - Project structure (optional, for understanding project type)
|
||||
|
||||
**Output**: `competitor_analysis.json` - Researched competitor insights
|
||||
|
||||
You MUST create `competitor_analysis.json` with this EXACT structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"project_context": {
|
||||
"project_name": "Name from discovery",
|
||||
"project_type": "Type from discovery",
|
||||
"target_audience": "Primary persona from discovery"
|
||||
},
|
||||
"competitors": [
|
||||
{
|
||||
"id": "competitor-1",
|
||||
"name": "Competitor Name",
|
||||
"url": "https://competitor-website.com",
|
||||
"description": "Brief description of the competitor",
|
||||
"relevance": "high|medium|low",
|
||||
"pain_points": [
|
||||
{
|
||||
"id": "pain-1-1",
|
||||
"description": "Clear description of the user pain point",
|
||||
"source": "Where this was found (e.g., 'Reddit r/programming', 'App Store reviews')",
|
||||
"severity": "high|medium|low",
|
||||
"frequency": "How often this complaint appears",
|
||||
"opportunity": "How our project could address this"
|
||||
}
|
||||
],
|
||||
"strengths": ["What users like about this competitor"],
|
||||
"market_position": "How this competitor is positioned"
|
||||
}
|
||||
],
|
||||
"market_gaps": [
|
||||
{
|
||||
"id": "gap-1",
|
||||
"description": "A gap in the market identified from competitor analysis",
|
||||
"affected_competitors": ["competitor-1", "competitor-2"],
|
||||
"opportunity_size": "high|medium|low",
|
||||
"suggested_feature": "Feature idea to address this gap"
|
||||
}
|
||||
],
|
||||
"insights_summary": {
|
||||
"top_pain_points": ["Most common pain points across competitors"],
|
||||
"differentiator_opportunities": ["Ways to differentiate from competitors"],
|
||||
"market_trends": ["Trends observed in user feedback"]
|
||||
},
|
||||
"research_metadata": {
|
||||
"search_queries_used": ["list of search queries performed"],
|
||||
"sources_consulted": ["list of sources checked"],
|
||||
"limitations": ["any limitations in the research"]
|
||||
},
|
||||
"created_at": "ISO timestamp"
|
||||
}
|
||||
```
|
||||
|
||||
**DO NOT** proceed without creating this file.
|
||||
|
||||
---
|
||||
|
||||
## PHASE 0: LOAD PROJECT CONTEXT
|
||||
|
||||
First, understand what project we're analyzing competitors for:
|
||||
|
||||
```bash
|
||||
# Read discovery data for project context
|
||||
cat roadmap_discovery.json
|
||||
|
||||
# Optionally check project structure
|
||||
cat project_index.json 2>/dev/null | head -50
|
||||
```
|
||||
|
||||
Extract from roadmap_discovery.json:
|
||||
1. **Project name and type** - What kind of product is this?
|
||||
2. **Target audience** - Who are the users we're competing for?
|
||||
3. **Product vision** - What problem does this solve?
|
||||
4. **Existing competitive context** - Any competitors already mentioned?
|
||||
|
||||
---
|
||||
|
||||
## PHASE 1: IDENTIFY COMPETITORS
|
||||
|
||||
Use WebSearch to find competitors. Search for alternatives to the project type:
|
||||
|
||||
### 1.1: Search for Direct Competitors
|
||||
|
||||
Based on the project type and domain, search for competitors:
|
||||
|
||||
**Search queries to use:**
|
||||
- `"[project type] alternatives [year]"` - e.g., "task management app alternatives 2024"
|
||||
- `"best [project type] tools"` - e.g., "best code editor tools"
|
||||
- `"[project type] vs"` - e.g., "VS Code vs" to find comparisons
|
||||
- `"[specific feature] software"` - e.g., "git version control software"
|
||||
|
||||
Use the WebSearch tool:
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[project type] alternatives 2024" }
|
||||
```
|
||||
|
||||
### 1.2: Identify 3-5 Main Competitors
|
||||
|
||||
From search results, identify:
|
||||
1. **Direct competitors** - Same type of product for same audience
|
||||
2. **Indirect competitors** - Different approach to same problem
|
||||
3. **Market leaders** - Most popular options users compare against
|
||||
|
||||
For each competitor, note:
|
||||
- Name
|
||||
- Website URL
|
||||
- Brief description
|
||||
- Relevance to our project (high/medium/low)
|
||||
|
||||
---
|
||||
|
||||
## PHASE 2: RESEARCH USER FEEDBACK
|
||||
|
||||
For each identified competitor, search for user feedback and pain points:
|
||||
|
||||
### 2.1: App Store & Review Sites
|
||||
|
||||
Search for reviews and ratings:
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] reviews complaints" }
|
||||
```
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] app store reviews problems" }
|
||||
```
|
||||
|
||||
### 2.2: Community Discussions
|
||||
|
||||
Search forums and social media:
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] reddit complaints" }
|
||||
```
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] issues site:reddit.com" }
|
||||
```
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] problems site:twitter.com OR site:x.com" }
|
||||
```
|
||||
|
||||
### 2.3: Technical Forums
|
||||
|
||||
For developer tools, search technical communities:
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] issues site:stackoverflow.com" }
|
||||
```
|
||||
|
||||
```
|
||||
Tool: WebSearch
|
||||
Input: { "query": "[competitor name] problems site:github.com" }
|
||||
```
|
||||
|
||||
### 2.4: Extract Pain Points
|
||||
|
||||
From the research, identify:
|
||||
|
||||
1. **Common complaints** - Issues mentioned repeatedly
|
||||
2. **Missing features** - Things users wish existed
|
||||
3. **UX problems** - Usability issues mentioned
|
||||
4. **Performance issues** - Speed, reliability complaints
|
||||
5. **Pricing concerns** - Cost-related complaints
|
||||
6. **Support issues** - Customer service problems
|
||||
|
||||
For each pain point, document:
|
||||
- Clear description of the issue
|
||||
- Source where it was found
|
||||
- Severity (high/medium/low based on frequency and impact)
|
||||
- How often it appears
|
||||
- Opportunity for our project to address it
|
||||
|
||||
---
|
||||
|
||||
## PHASE 3: IDENTIFY MARKET GAPS
|
||||
|
||||
Analyze the collected pain points across all competitors:
|
||||
|
||||
### 3.1: Find Common Patterns
|
||||
|
||||
Look for pain points that appear across multiple competitors:
|
||||
- What problems does no one solve well?
|
||||
- What features are universally requested?
|
||||
- What frustrations are shared across the market?
|
||||
|
||||
### 3.2: Identify Differentiation Opportunities
|
||||
|
||||
Based on the analysis:
|
||||
- Where can our project excel where others fail?
|
||||
- What unique approach could solve common problems?
|
||||
- What underserved segment exists in the market?
|
||||
|
||||
---
|
||||
|
||||
## PHASE 4: CREATE COMPETITOR_ANALYSIS.JSON (MANDATORY)
|
||||
|
||||
**You MUST create this file. The orchestrator will fail if you don't.**
|
||||
|
||||
Based on all research, create the competitor analysis file:
|
||||
|
||||
```bash
|
||||
cat > competitor_analysis.json << 'EOF'
|
||||
{
|
||||
"project_context": {
|
||||
"project_name": "[from roadmap_discovery.json]",
|
||||
"project_type": "[from roadmap_discovery.json]",
|
||||
"target_audience": "[primary persona from roadmap_discovery.json]"
|
||||
},
|
||||
"competitors": [
|
||||
{
|
||||
"id": "competitor-1",
|
||||
"name": "[Competitor Name]",
|
||||
"url": "[Competitor URL]",
|
||||
"description": "[Brief description]",
|
||||
"relevance": "[high|medium|low]",
|
||||
"pain_points": [
|
||||
{
|
||||
"id": "pain-1-1",
|
||||
"description": "[Pain point description]",
|
||||
"source": "[Where found]",
|
||||
"severity": "[high|medium|low]",
|
||||
"frequency": "[How often mentioned]",
|
||||
"opportunity": "[How to address]"
|
||||
}
|
||||
],
|
||||
"strengths": ["[Strength 1]", "[Strength 2]"],
|
||||
"market_position": "[Market position description]"
|
||||
}
|
||||
],
|
||||
"market_gaps": [
|
||||
{
|
||||
"id": "gap-1",
|
||||
"description": "[Gap description]",
|
||||
"affected_competitors": ["competitor-1"],
|
||||
"opportunity_size": "[high|medium|low]",
|
||||
"suggested_feature": "[Feature suggestion]"
|
||||
}
|
||||
],
|
||||
"insights_summary": {
|
||||
"top_pain_points": ["[Pain point 1]", "[Pain point 2]"],
|
||||
"differentiator_opportunities": ["[Opportunity 1]"],
|
||||
"market_trends": ["[Trend 1]"]
|
||||
},
|
||||
"research_metadata": {
|
||||
"search_queries_used": ["[Query 1]", "[Query 2]"],
|
||||
"sources_consulted": ["[Source 1]", "[Source 2]"],
|
||||
"limitations": ["[Limitation 1]"]
|
||||
},
|
||||
"created_at": "[ISO timestamp]"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Verify the file was created:
|
||||
|
||||
```bash
|
||||
cat competitor_analysis.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PHASE 5: VALIDATION
|
||||
|
||||
After creating competitor_analysis.json, verify it:
|
||||
|
||||
1. **Is it valid JSON?** - No syntax errors
|
||||
2. **Does it have at least 1 competitor?** - Required
|
||||
3. **Does each competitor have pain_points?** - Required (at least 1)
|
||||
4. **Are sources documented?** - Each pain point needs a source
|
||||
5. **Is project_context filled?** - Required from discovery
|
||||
|
||||
If any check fails, fix the file immediately.
|
||||
|
||||
---
|
||||
|
||||
## COMPLETION
|
||||
|
||||
Signal completion:
|
||||
|
||||
```
|
||||
=== COMPETITOR ANALYSIS COMPLETE ===
|
||||
|
||||
Project: [name]
|
||||
Competitors Analyzed: [count]
|
||||
Pain Points Identified: [total count]
|
||||
Market Gaps Found: [count]
|
||||
|
||||
Top Opportunities:
|
||||
1. [Opportunity 1]
|
||||
2. [Opportunity 2]
|
||||
3. [Opportunity 3]
|
||||
|
||||
competitor_analysis.json created successfully.
|
||||
|
||||
Next phase: Discovery (will incorporate competitor insights)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CRITICAL RULES
|
||||
|
||||
1. **ALWAYS create competitor_analysis.json** - The orchestrator checks for this file
|
||||
2. **Use valid JSON** - No trailing commas, proper quotes
|
||||
3. **Include at least 1 competitor** - Even if research is limited
|
||||
4. **Document sources** - Every pain point needs a source
|
||||
5. **Use WebSearch for research** - Don't make up competitors or pain points
|
||||
6. **Focus on user feedback** - Look for actual complaints, not just feature lists
|
||||
7. **Include IDs** - Each competitor and pain point needs a unique ID for reference
|
||||
|
||||
---
|
||||
|
||||
## HANDLING EDGE CASES
|
||||
|
||||
### No Competitors Found
|
||||
|
||||
If the project is truly unique or no relevant competitors exist:
|
||||
|
||||
```json
|
||||
{
|
||||
"competitors": [],
|
||||
"market_gaps": [
|
||||
{
|
||||
"id": "gap-1",
|
||||
"description": "No direct competitors found - potential first-mover advantage",
|
||||
"affected_competitors": [],
|
||||
"opportunity_size": "high",
|
||||
"suggested_feature": "Focus on establishing category leadership"
|
||||
}
|
||||
],
|
||||
"insights_summary": {
|
||||
"top_pain_points": ["No competitor pain points found - research adjacent markets"],
|
||||
"differentiator_opportunities": ["First-mover advantage in this space"],
|
||||
"market_trends": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Internal Tools / Libraries
|
||||
|
||||
For developer libraries or internal tools where traditional competitors don't apply:
|
||||
|
||||
1. Search for alternative libraries/packages
|
||||
2. Look at GitHub issues on similar projects
|
||||
3. Search Stack Overflow for common problems in the domain
|
||||
|
||||
### Limited Search Results
|
||||
|
||||
If WebSearch returns limited results:
|
||||
|
||||
1. Document the limitation in research_metadata
|
||||
2. Include whatever competitors were found
|
||||
3. Note that additional research may be needed
|
||||
|
||||
---
|
||||
|
||||
## ERROR RECOVERY
|
||||
|
||||
If you made a mistake in competitor_analysis.json:
|
||||
|
||||
```bash
|
||||
# Read current state
|
||||
cat competitor_analysis.json
|
||||
|
||||
# Fix the issue
|
||||
cat > competitor_analysis.json << 'EOF'
|
||||
{
|
||||
[corrected JSON]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Verify
|
||||
cat competitor_analysis.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BEGIN
|
||||
|
||||
Start by reading roadmap_discovery.json to understand the project, then use WebSearch to research competitors and user feedback.
|
||||
@@ -48,7 +48,9 @@ You MUST create `roadmap_discovery.json` with this EXACT structure:
|
||||
"competitive_context": {
|
||||
"alternatives": ["Alternative 1", "Alternative 2"],
|
||||
"differentiators": ["What makes this unique?"],
|
||||
"market_position": "How does this fit in the market?"
|
||||
"market_position": "How does this fit in the market?",
|
||||
"competitor_pain_points": ["Pain points from competitor users - populated from competitor_analysis.json if available"],
|
||||
"competitor_analysis_available": false
|
||||
},
|
||||
"constraints": {
|
||||
"technical": ["Technical limitations"],
|
||||
@@ -81,12 +83,16 @@ cat package.json 2>/dev/null | head -50
|
||||
cat pyproject.toml 2>/dev/null | head -50
|
||||
cat Cargo.toml 2>/dev/null | head -30
|
||||
cat go.mod 2>/dev/null | head -30
|
||||
|
||||
# Check for competitor analysis (if enabled by user)
|
||||
cat competitor_analysis.json 2>/dev/null || echo "No competitor analysis available"
|
||||
```
|
||||
|
||||
Understand:
|
||||
- What type of project is this?
|
||||
- What tech stack is used?
|
||||
- What does the README say about the purpose?
|
||||
- Is there competitor analysis data available to incorporate?
|
||||
|
||||
---
|
||||
|
||||
@@ -157,12 +163,9 @@ Determine maturity level:
|
||||
|
||||
Based on project type and purpose, infer:
|
||||
|
||||
1. **Alternatives** - What existing tools solve similar problems?
|
||||
2. **Differentiators** - What makes this approach unique? (from README/docs)
|
||||
3. **Market position** - Is this a new category, or improving existing solutions?
|
||||
|
||||
Use domain knowledge to identify likely competitors (e.g., for AI coding tools: Cursor, Copilot, Aider, etc.)
|
||||
### 4.1: Check for Competitor Analysis Data
|
||||
|
||||
If `competitor_analysis.json` exists (created by the Competitor Analysis Agent), incorporate those insights:
|
||||
---
|
||||
|
||||
## PHASE 5: IDENTIFY CONSTRAINTS (AUTONOMOUS)
|
||||
@@ -214,10 +217,11 @@ Based on all the information gathered, create the discovery file using the Write
|
||||
"technical_debt": ["[from code smells, TODOs, FIXMEs]"]
|
||||
},
|
||||
"competitive_context": {
|
||||
"alternatives": ["[known alternatives in this space]"],
|
||||
"differentiators": ["[what makes this unique]"],
|
||||
"market_position": "[how it fits in the market]"
|
||||
},
|
||||
"alternatives": ["[alternative 1 - from competitor_analysis.json if available, or inferred from domain knowledge]"],
|
||||
"differentiators": ["[differentiator 1 - from competitor_analysis.json insights_summary.differentiator_opportunities if available, or from README/docs]"],
|
||||
"market_position": "[market positioning - incorporate market_gaps from competitor_analysis.json if available, otherwise infer from project type]",
|
||||
"competitor_pain_points": ["[from competitor_analysis.json insights_summary.top_pain_points if available, otherwise empty array]"],
|
||||
"competitor_analysis_available": true },
|
||||
"constraints": {
|
||||
"technical": ["[inferred from dependencies/architecture]"],
|
||||
"resources": ["[inferred from git contributors]"],
|
||||
@@ -278,13 +282,14 @@ Next phase: Feature Generation
|
||||
## CRITICAL RULES
|
||||
|
||||
1. **ALWAYS create roadmap_discovery.json** - The orchestrator checks for this file. CREATE IT IMMEDIATELY after analysis.
|
||||
2. **NEVER ask questions or wait for input** - This runs non-interactively. Make your best inferences.
|
||||
3. **Use valid JSON** - No trailing commas, proper quotes
|
||||
4. **Include all required fields** - project_name, target_audience, product_vision
|
||||
5. **Write to Output Directory** - Use the path provided at the end of the prompt, NOT the project root
|
||||
6. **Make educated guesses** - It's better to have reasonable inferences than empty fields
|
||||
7. **Be thorough on audience** - Infer from README, code structure, and project type
|
||||
|
||||
2. **Use valid JSON** - No trailing commas, proper quotes
|
||||
3. **Include all required fields** - project_name, target_audience, product_vision
|
||||
4. **Ask before assuming** - Don't guess what the user wants for critical information
|
||||
5. **Confirm key information** - Especially target audience and vision
|
||||
6. **Be thorough on audience** - This is the most important part for roadmap quality
|
||||
7. **Make educated guesses when appropriate** - For technical details and competitive context, reasonable inferences are acceptable
|
||||
8. **Write to Output Directory** - Use the path provided at the end of the prompt, NOT the project root
|
||||
9. **Incorporate competitor analysis** - If `competitor_analysis.json` exists, use its data to enrich `competitive_context` with real competitor insights and pain points. Set `competitor_analysis_available: true` when data is used
|
||||
---
|
||||
|
||||
## ERROR RECOVERY
|
||||
|
||||
@@ -11,6 +11,7 @@ You are the **Roadmap Feature Generator Agent** in the Auto-Build framework. You
|
||||
**Input**:
|
||||
- `roadmap_discovery.json` (project understanding)
|
||||
- `project_index.json` (codebase structure)
|
||||
- `competitor_analysis.json` (optional - competitor insights if available)
|
||||
|
||||
**Output**: `roadmap.json` (complete roadmap with prioritized features)
|
||||
|
||||
@@ -63,7 +64,8 @@ You MUST create `roadmap.json` with this EXACT structure:
|
||||
],
|
||||
"user_stories": [
|
||||
"As a [user], I want to [action] so that [benefit]"
|
||||
]
|
||||
],
|
||||
"competitor_insight_ids": ["insight-id-1"]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -90,6 +92,9 @@ cat project_index.json
|
||||
|
||||
# Check for existing features or TODOs
|
||||
grep -r "TODO\|FEATURE\|IDEA" --include="*.md" . 2>/dev/null | head -30
|
||||
|
||||
# Check for competitor analysis data (if enabled by user)
|
||||
cat competitor_analysis.json 2>/dev/null || echo "No competitor analysis available"
|
||||
```
|
||||
|
||||
Extract key information:
|
||||
@@ -97,6 +102,7 @@ Extract key information:
|
||||
- Product vision and value proposition
|
||||
- Current features and gaps
|
||||
- Constraints and dependencies
|
||||
- Competitor pain points and market gaps (if competitor_analysis.json exists)
|
||||
|
||||
---
|
||||
|
||||
@@ -129,6 +135,25 @@ Based on `current_state.technical_debt`, consider:
|
||||
- What refactoring or improvements are needed?
|
||||
- What would improve developer experience?
|
||||
|
||||
### 1.6 Competitor Pain Points (if competitor_analysis.json exists)
|
||||
|
||||
**IMPORTANT**: If `competitor_analysis.json` is available, this becomes a HIGH-PRIORITY source for feature ideas.
|
||||
|
||||
For each pain point in `competitor_analysis.json` → `insights_summary.top_pain_points`, consider:
|
||||
- What feature would directly address this pain point better than competitors?
|
||||
- Can we turn competitor weaknesses into our strengths?
|
||||
- What market gaps (from `market_gaps`) can we fill?
|
||||
|
||||
For each competitor in `competitor_analysis.json` → `competitors`:
|
||||
- Review their `pain_points` array for user frustrations
|
||||
- Use the `id` of each pain point for the `competitor_insight_ids` field when creating features
|
||||
|
||||
**Linking Features to Competitor Insights**:
|
||||
When a feature addresses a competitor pain point:
|
||||
1. Add the pain point's `id` to the feature's `competitor_insight_ids` array
|
||||
2. Reference the competitor and pain point in the feature's `rationale`
|
||||
3. Consider boosting the feature's priority if it addresses multiple competitor weaknesses
|
||||
|
||||
---
|
||||
|
||||
## PHASE 2: PRIORITIZATION (MoSCoW)
|
||||
@@ -139,11 +164,13 @@ Apply MoSCoW prioritization to each feature:
|
||||
- Critical for MVP or current phase
|
||||
- Users cannot function without this
|
||||
- Legal/compliance requirements
|
||||
- **Addresses critical competitor pain points** (if competitor_analysis.json exists)
|
||||
|
||||
**SHOULD HAVE** (priority: "should")
|
||||
- Important but not critical
|
||||
- Significant value to users
|
||||
- Can wait for next phase if needed
|
||||
- **Addresses common competitor pain points** (if competitor_analysis.json exists)
|
||||
|
||||
**COULD HAVE** (priority: "could")
|
||||
- Nice to have, enhances experience
|
||||
@@ -167,7 +194,7 @@ For each feature, assess:
|
||||
- **High**: 10+ files, architectural changes, > 3 days
|
||||
|
||||
### Impact (Low/Medium/High)
|
||||
- **High**: Core user need, differentiator, revenue driver
|
||||
- **High**: Core user need, differentiator, revenue driver, **addresses competitor pain points**
|
||||
- **Medium**: Improves experience, addresses secondary needs
|
||||
- **Low**: Edge cases, polish, nice-to-have
|
||||
|
||||
@@ -277,7 +304,7 @@ cat > roadmap.json << 'EOF'
|
||||
"id": "feature-1",
|
||||
"title": "[Feature Title]",
|
||||
"description": "[What it does]",
|
||||
"rationale": "[Why it matters]",
|
||||
"rationale": "[Why it matters - include competitor pain point reference if applicable]",
|
||||
"priority": "must|should|could|wont",
|
||||
"complexity": "low|medium|high",
|
||||
"impact": "low|medium|high",
|
||||
@@ -290,19 +317,23 @@ cat > roadmap.json << 'EOF'
|
||||
],
|
||||
"user_stories": [
|
||||
"As a [user], I want to [action] so that [benefit]"
|
||||
]
|
||||
],
|
||||
"competitor_insight_ids": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"created_at": "[ISO timestamp]",
|
||||
"updated_at": "[ISO timestamp]",
|
||||
"generated_by": "roadmap_features agent",
|
||||
"prioritization_framework": "MoSCoW"
|
||||
"prioritization_framework": "MoSCoW",
|
||||
"competitor_analysis_used": false
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Note**: Set `competitor_analysis_used: true` in metadata if competitor_analysis.json was incorporated.
|
||||
|
||||
Verify the file was created:
|
||||
|
||||
```bash
|
||||
@@ -356,6 +387,8 @@ Project: [name]
|
||||
Vision: [one_liner]
|
||||
Phases: [count]
|
||||
Features: [count]
|
||||
Competitor Analysis Used: [yes/no]
|
||||
Features Addressing Competitor Pain Points: [count]
|
||||
|
||||
Breakdown by priority:
|
||||
- Must Have: [count]
|
||||
@@ -375,6 +408,7 @@ roadmap.json created successfully.
|
||||
4. **Consider dependencies** - Don't plan impossible sequences
|
||||
5. **Include acceptance criteria** - Make features testable
|
||||
6. **Use user stories** - Connect features to user value
|
||||
7. **Leverage competitor analysis** - If `competitor_analysis.json` exists, prioritize features that address competitor pain points and include `competitor_insight_ids` to link features to specific insights
|
||||
|
||||
---
|
||||
|
||||
@@ -401,10 +435,17 @@ For each feature, ensure you capture:
|
||||
],
|
||||
"user_stories": [
|
||||
"As a [persona], I want to [action] so that [benefit]"
|
||||
]
|
||||
],
|
||||
"competitor_insight_ids": ["pain-point-id-1", "pain-point-id-2"]
|
||||
}
|
||||
```
|
||||
|
||||
**Note on `competitor_insight_ids`**:
|
||||
- This field is **optional** - only include when the feature addresses competitor pain points
|
||||
- The IDs should reference pain point IDs from `competitor_analysis.json` → `competitors[].pain_points[].id`
|
||||
- Features with `competitor_insight_ids` gain priority boost in the roadmap
|
||||
- Use empty array `[]` if the feature doesn't address any competitor insights
|
||||
|
||||
---
|
||||
|
||||
## BEGIN
|
||||
|
||||
@@ -75,6 +75,7 @@ class RoadmapConfig:
|
||||
output_dir: Path
|
||||
model: str = "claude-opus-4-5-20251101"
|
||||
refresh: bool = False # Force regeneration even if roadmap exists
|
||||
enable_competitor_analysis: bool = False # Enable competitor analysis phase
|
||||
|
||||
|
||||
class RoadmapOrchestrator:
|
||||
@@ -86,10 +87,12 @@ class RoadmapOrchestrator:
|
||||
output_dir: Path | None = None,
|
||||
model: str = "claude-opus-4-5-20251101",
|
||||
refresh: bool = False,
|
||||
enable_competitor_analysis: bool = False,
|
||||
):
|
||||
self.project_dir = Path(project_dir)
|
||||
self.model = model
|
||||
self.refresh = refresh
|
||||
self.enable_competitor_analysis = enable_competitor_analysis
|
||||
|
||||
# Default output to project's .auto-claude directory (installed instance)
|
||||
# Note: auto-claude/ is source code, .auto-claude/ is the installed instance
|
||||
@@ -320,6 +323,123 @@ class RoadmapOrchestrator:
|
||||
"graph_hints", True, [str(hints_file)], [str(e)], 0
|
||||
)
|
||||
|
||||
async def phase_competitor_analysis(self, enable_competitor_analysis: bool = False) -> RoadmapPhaseResult:
|
||||
"""Run competitor analysis to research competitors and user feedback (if enabled).
|
||||
|
||||
This is an optional phase - it gracefully degrades if disabled or if analysis fails.
|
||||
Competitor insights enhance roadmap features but are not required.
|
||||
"""
|
||||
analysis_file = self.output_dir / "competitor_analysis.json"
|
||||
|
||||
# Check if competitor analysis is enabled
|
||||
if not enable_competitor_analysis:
|
||||
print_status("Competitor analysis not enabled, skipping", "info")
|
||||
# Write a minimal file indicating analysis was skipped
|
||||
with open(analysis_file, "w") as f:
|
||||
json.dump({
|
||||
"enabled": False,
|
||||
"reason": "Competitor analysis not enabled by user",
|
||||
"competitors": [],
|
||||
"market_gaps": [],
|
||||
"insights_summary": {
|
||||
"top_pain_points": [],
|
||||
"differentiator_opportunities": [],
|
||||
"market_trends": []
|
||||
},
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}, f, indent=2)
|
||||
return RoadmapPhaseResult("competitor_analysis", True, [str(analysis_file)], [], 0)
|
||||
|
||||
# Check if already exists (skip if not refresh)
|
||||
if analysis_file.exists() and not self.refresh:
|
||||
print_status("competitor_analysis.json already exists", "success")
|
||||
return RoadmapPhaseResult("competitor_analysis", True, [str(analysis_file)], [], 0)
|
||||
|
||||
# Check if discovery file exists (required for competitor analysis)
|
||||
discovery_file = self.output_dir / "roadmap_discovery.json"
|
||||
if not discovery_file.exists():
|
||||
print_status("Discovery file not found, skipping competitor analysis", "warning")
|
||||
with open(analysis_file, "w") as f:
|
||||
json.dump({
|
||||
"enabled": True,
|
||||
"error": "Discovery file not found - cannot analyze competitors without project context",
|
||||
"competitors": [],
|
||||
"market_gaps": [],
|
||||
"insights_summary": {
|
||||
"top_pain_points": [],
|
||||
"differentiator_opportunities": [],
|
||||
"market_trends": []
|
||||
},
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}, f, indent=2)
|
||||
return RoadmapPhaseResult("competitor_analysis", True, [str(analysis_file)], ["Discovery file not found"], 0)
|
||||
|
||||
errors = []
|
||||
for attempt in range(MAX_RETRIES):
|
||||
print_status(f"Running competitor analysis agent (attempt {attempt + 1})...", "progress")
|
||||
|
||||
context = f"""
|
||||
**Discovery File**: {discovery_file}
|
||||
**Project Index**: {self.output_dir / "project_index.json"}
|
||||
**Output File**: {analysis_file}
|
||||
|
||||
Research competitors based on the project type and target audience from roadmap_discovery.json.
|
||||
Use WebSearch to find competitors and analyze user feedback (reviews, complaints, feature requests).
|
||||
Output your findings to competitor_analysis.json.
|
||||
"""
|
||||
success, output = await self._run_agent(
|
||||
"competitor_analysis.md",
|
||||
additional_context=context,
|
||||
)
|
||||
|
||||
if success and analysis_file.exists():
|
||||
# Validate JSON structure
|
||||
try:
|
||||
with open(analysis_file) as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Check for required fields (gracefully accept minimal structure)
|
||||
if "competitors" in data:
|
||||
competitor_count = len(data.get("competitors", []))
|
||||
pain_point_count = sum(
|
||||
len(c.get("pain_points", []))
|
||||
for c in data.get("competitors", [])
|
||||
)
|
||||
print_status(
|
||||
f"Analyzed {competitor_count} competitors, found {pain_point_count} pain points",
|
||||
"success"
|
||||
)
|
||||
return RoadmapPhaseResult("competitor_analysis", True, [str(analysis_file)], [], attempt)
|
||||
else:
|
||||
errors.append("Missing 'competitors' field in competitor_analysis.json")
|
||||
except json.JSONDecodeError as e:
|
||||
errors.append(f"Invalid JSON: {e}")
|
||||
else:
|
||||
errors.append(f"Attempt {attempt + 1}: Agent did not create competitor analysis file")
|
||||
|
||||
# Graceful degradation: if all retries fail, create empty analysis and continue
|
||||
print_status("Competitor analysis failed, continuing without competitor insights", "warning")
|
||||
for err in errors:
|
||||
print(f" {muted('Error:')} {err}")
|
||||
|
||||
with open(analysis_file, "w") as f:
|
||||
json.dump({
|
||||
"enabled": True,
|
||||
"error": "Analysis failed after retries",
|
||||
"errors": errors,
|
||||
"competitors": [],
|
||||
"market_gaps": [],
|
||||
"insights_summary": {
|
||||
"top_pain_points": [],
|
||||
"differentiator_opportunities": [],
|
||||
"market_trends": []
|
||||
},
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}, f, indent=2)
|
||||
|
||||
# Return success=True for graceful degradation (don't block roadmap generation)
|
||||
return RoadmapPhaseResult("competitor_analysis", True, [str(analysis_file)], errors, MAX_RETRIES)
|
||||
|
||||
async def phase_project_index(self) -> RoadmapPhaseResult:
|
||||
"""Ensure project index exists."""
|
||||
debug("roadmap_runner", "Starting phase: project_index")
|
||||
@@ -565,7 +685,7 @@ Output the complete roadmap to roadmap.json.
|
||||
return RoadmapPhaseResult("features", False, [], errors, MAX_RETRIES)
|
||||
|
||||
async def run(self) -> bool:
|
||||
"""Run the complete roadmap generation process."""
|
||||
"""Run the complete roadmap generation process with optional competitor analysis."""
|
||||
debug_section("roadmap_runner", "Starting Roadmap Generation")
|
||||
debug(
|
||||
"roadmap_runner",
|
||||
@@ -580,12 +700,12 @@ Output the complete roadmap to roadmap.json.
|
||||
box(
|
||||
f"Project: {self.project_dir}\n"
|
||||
f"Output: {self.output_dir}\n"
|
||||
f"Model: {self.model}",
|
||||
f"Model: {self.model}\n"
|
||||
f"Competitor Analysis: {'enabled' if self.enable_competitor_analysis else 'disabled'}",
|
||||
title="ROADMAP GENERATOR",
|
||||
style="heavy",
|
||||
)
|
||||
)
|
||||
|
||||
results = []
|
||||
|
||||
# Phase 1: Project Index & Graph Hints (in parallel)
|
||||
@@ -638,6 +758,14 @@ Output the complete roadmap to roadmap.json.
|
||||
return False
|
||||
debug_success("roadmap_runner", "Phase 2 complete")
|
||||
|
||||
# Phase 2.5: Competitor Analysis (optional, runs after discovery)
|
||||
print_section("PHASE 2.5: COMPETITOR ANALYSIS", Icons.SEARCH)
|
||||
competitor_result = await self.phase_competitor_analysis(
|
||||
enable_competitor_analysis=self.enable_competitor_analysis
|
||||
)
|
||||
results.append(competitor_result)
|
||||
# Note: competitor_result.success is always True (graceful degradation)
|
||||
|
||||
# Phase 3: Feature Generation
|
||||
debug("roadmap_runner", "Starting Phase 3: Feature Generation")
|
||||
print_section("PHASE 3: FEATURE GENERATION", Icons.SUBTASK)
|
||||
@@ -727,6 +855,12 @@ def main():
|
||||
action="store_true",
|
||||
help="Force regeneration even if roadmap exists",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--competitor-analysis",
|
||||
action="store_true",
|
||||
dest="enable_competitor_analysis",
|
||||
help="Enable competitor analysis phase",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -759,6 +893,7 @@ def main():
|
||||
output_dir=args.output,
|
||||
model=args.model,
|
||||
refresh=args.refresh,
|
||||
enable_competitor_analysis=args.enable_competitor_analysis,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user