prjct-cli 0.18.1 → 0.19.0
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.
- package/CHANGELOG.md +53 -0
- package/CLAUDE.md +74 -211
- package/core/agentic/prompt-builder.ts +3 -7
- package/core/command-registry/optional-commands.ts +0 -20
- package/core/infrastructure/command-installer/command-installer.ts +8 -1
- package/core/infrastructure/command-installer/global-config.ts +31 -1
- package/core/infrastructure/command-installer/index.ts +1 -1
- package/core/infrastructure/setup.ts +3 -0
- package/package.json +3 -17
- package/templates/agentic/subagent-generation.md +8 -6
- package/templates/commands/done.md +57 -258
- package/templates/commands/now.md +72 -277
- package/templates/commands/ship.md +55 -261
- package/templates/commands/sync.md +7 -7
- package/templates/commands/test.md +328 -21
- package/templates/global/CLAUDE.md +40 -205
- package/templates/global/docs/agents.md +88 -0
- package/templates/global/docs/architecture.md +103 -0
- package/templates/global/docs/commands.md +98 -0
- package/templates/global/docs/validation.md +95 -0
- package/templates/mcp-config.json +36 -0
- package/bin/dev.js +0 -216
- package/bin/serve.js +0 -361
- package/packages/web/README.md +0 -36
- package/packages/web/app/api/claude/sessions/route.ts +0 -44
- package/packages/web/app/api/claude/status/route.ts +0 -34
- package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
- package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
- package/packages/web/app/api/projects/[id]/route.ts +0 -29
- package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
- package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
- package/packages/web/app/api/projects/route.ts +0 -16
- package/packages/web/app/api/sessions/current/route.ts +0 -132
- package/packages/web/app/api/sessions/history/route.ts +0 -204
- package/packages/web/app/error.tsx +0 -34
- package/packages/web/app/favicon.ico +0 -0
- package/packages/web/app/globals.css +0 -198
- package/packages/web/app/layout.tsx +0 -53
- package/packages/web/app/loading.tsx +0 -7
- package/packages/web/app/not-found.tsx +0 -25
- package/packages/web/app/page.tsx +0 -12
- package/packages/web/app/project/[id]/code/layout.tsx +0 -18
- package/packages/web/app/project/[id]/code/page.tsx +0 -408
- package/packages/web/app/project/[id]/error.tsx +0 -41
- package/packages/web/app/project/[id]/loading.tsx +0 -9
- package/packages/web/app/project/[id]/not-found.tsx +0 -27
- package/packages/web/app/project/[id]/page.tsx +0 -384
- package/packages/web/app/project/[id]/reports/page.tsx +0 -59
- package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
- package/packages/web/app/sessions/page.tsx +0 -165
- package/packages/web/app/settings/page.tsx +0 -151
- package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
- package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
- package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
- package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
- package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
- package/packages/web/components/ActivityTimeline/index.ts +0 -2
- package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
- package/packages/web/components/AgentsCard/index.ts +0 -2
- package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
- package/packages/web/components/AppSidebar/index.ts +0 -1
- package/packages/web/components/BackLink/BackLink.tsx +0 -18
- package/packages/web/components/BackLink/BackLink.types.ts +0 -5
- package/packages/web/components/BackLink/index.ts +0 -2
- package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
- package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
- package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
- package/packages/web/components/BentoCard/index.ts +0 -2
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
- package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
- package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
- package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
- package/packages/web/components/BentoGrid/index.ts +0 -2
- package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
- package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
- package/packages/web/components/BlockersCard/index.ts +0 -2
- package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
- package/packages/web/components/CommandBar/index.ts +0 -1
- package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
- package/packages/web/components/CommandButton/index.ts +0 -1
- package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
- package/packages/web/components/ConnectionStatus/index.ts +0 -1
- package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
- package/packages/web/components/DashboardContent/index.ts +0 -1
- package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
- package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
- package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
- package/packages/web/components/DateGroup/index.ts +0 -2
- package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
- package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
- package/packages/web/components/EmptyState/index.ts +0 -2
- package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
- package/packages/web/components/EventRow/EventRow.tsx +0 -49
- package/packages/web/components/EventRow/EventRow.types.ts +0 -7
- package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
- package/packages/web/components/EventRow/index.ts +0 -2
- package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
- package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
- package/packages/web/components/ExpandButton/index.ts +0 -2
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
- package/packages/web/components/HealthGradientBackground/index.ts +0 -2
- package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
- package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
- package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
- package/packages/web/components/HeroSection/hooks/index.ts +0 -2
- package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
- package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
- package/packages/web/components/HeroSection/index.ts +0 -2
- package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
- package/packages/web/components/IdeasCard/index.ts +0 -2
- package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
- package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
- package/packages/web/components/InsightMessage/index.ts +0 -2
- package/packages/web/components/Logo/Logo.tsx +0 -65
- package/packages/web/components/Logo/index.ts +0 -1
- package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
- package/packages/web/components/MarkdownContent/index.ts +0 -1
- package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
- package/packages/web/components/MasonryGrid/index.ts +0 -1
- package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
- package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
- package/packages/web/components/MomentumWidget/index.ts +0 -2
- package/packages/web/components/NowCard/NowCard.tsx +0 -118
- package/packages/web/components/NowCard/NowCard.types.ts +0 -16
- package/packages/web/components/NowCard/index.ts +0 -2
- package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
- package/packages/web/components/PageHeader/index.ts +0 -1
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
- package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
- package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
- package/packages/web/components/ProgressRing/index.ts +0 -2
- package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
- package/packages/web/components/ProjectAvatar/index.ts +0 -1
- package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
- package/packages/web/components/ProjectColorDot/index.ts +0 -1
- package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
- package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
- package/packages/web/components/Providers/Providers.tsx +0 -48
- package/packages/web/components/Providers/index.ts +0 -1
- package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
- package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
- package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
- package/packages/web/components/QueueCard/index.ts +0 -2
- package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
- package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
- package/packages/web/components/RecoverCard/index.ts +0 -2
- package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
- package/packages/web/components/RoadmapCard/index.ts +0 -2
- package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
- package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
- package/packages/web/components/ShipsCard/index.ts +0 -2
- package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
- package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
- package/packages/web/components/SparklineChart/index.ts +0 -2
- package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
- package/packages/web/components/StatsMasonry/index.ts +0 -1
- package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
- package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
- package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
- package/packages/web/components/StreakCard/index.ts +0 -2
- package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
- package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
- package/packages/web/components/TasksCounter/index.ts +0 -2
- package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
- package/packages/web/components/TechStackBadges/index.ts +0 -1
- package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
- package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
- package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
- package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
- package/packages/web/components/TerminalDock/index.ts +0 -2
- package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
- package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
- package/packages/web/components/TerminalTabs/index.ts +0 -1
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
- package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
- package/packages/web/components/VelocityBadge/index.ts +0 -2
- package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
- package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
- package/packages/web/components/VelocityCard/index.ts +0 -2
- package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
- package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
- package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
- package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
- package/packages/web/components/WeeklyReports/index.ts +0 -4
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
- package/packages/web/components/WeeklySparkline/index.ts +0 -2
- package/packages/web/components/charts/SessionsChart.tsx +0 -175
- package/packages/web/components/ui/alert-dialog.tsx +0 -157
- package/packages/web/components/ui/badge.tsx +0 -46
- package/packages/web/components/ui/button.tsx +0 -60
- package/packages/web/components/ui/card.tsx +0 -92
- package/packages/web/components/ui/chart.tsx +0 -385
- package/packages/web/components/ui/dialog.tsx +0 -143
- package/packages/web/components/ui/drawer.tsx +0 -135
- package/packages/web/components/ui/dropdown-menu.tsx +0 -257
- package/packages/web/components/ui/input.tsx +0 -21
- package/packages/web/components/ui/scroll-area.tsx +0 -58
- package/packages/web/components/ui/select.tsx +0 -187
- package/packages/web/components/ui/sheet.tsx +0 -139
- package/packages/web/components/ui/tabs.tsx +0 -66
- package/packages/web/components/ui/tooltip.tsx +0 -61
- package/packages/web/components.json +0 -22
- package/packages/web/context/GlobalTerminalContext.tsx +0 -538
- package/packages/web/context/TerminalContext.tsx +0 -45
- package/packages/web/context/TerminalTabsContext.tsx +0 -181
- package/packages/web/eslint.config.mjs +0 -18
- package/packages/web/hooks/useClaudeTerminal.ts +0 -425
- package/packages/web/hooks/useProjectStats.ts +0 -93
- package/packages/web/hooks/useProjects.ts +0 -73
- package/packages/web/lib/actions/projects.ts +0 -15
- package/packages/web/lib/commands.ts +0 -81
- package/packages/web/lib/format.ts +0 -23
- package/packages/web/lib/generate-week-report.ts +0 -285
- package/packages/web/lib/parse-prjct-files.ts +0 -1123
- package/packages/web/lib/project-colors.ts +0 -58
- package/packages/web/lib/projects.ts +0 -506
- package/packages/web/lib/pty.ts +0 -101
- package/packages/web/lib/query-config.ts +0 -44
- package/packages/web/lib/services/index.ts +0 -9
- package/packages/web/lib/services/projects.server.ts +0 -66
- package/packages/web/lib/services/stats.server.ts +0 -562
- package/packages/web/lib/unified-loader.ts +0 -396
- package/packages/web/lib/utils.ts +0 -6
- package/packages/web/next-env.d.ts +0 -6
- package/packages/web/next.config.ts +0 -7
- package/packages/web/package.json +0 -57
- package/packages/web/postcss.config.mjs +0 -7
- package/packages/web/public/file.svg +0 -1
- package/packages/web/public/globe.svg +0 -1
- package/packages/web/public/next.svg +0 -1
- package/packages/web/public/vercel.svg +0 -1
- package/packages/web/public/window.svg +0 -1
- package/packages/web/server.ts +0 -312
- package/packages/web/tsconfig.json +0 -34
- package/templates/commands/serve.md +0 -121
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function getPriorityColor(priority?: 'low' | 'medium' | 'high' | 'critical' | number): string {
|
|
2
|
-
if (typeof priority === 'string') {
|
|
3
|
-
const colors: Record<string, string> = {
|
|
4
|
-
critical: 'text-foreground font-bold',
|
|
5
|
-
high: 'text-foreground',
|
|
6
|
-
medium: 'text-muted-foreground',
|
|
7
|
-
low: 'text-muted-foreground',
|
|
8
|
-
}
|
|
9
|
-
return colors[priority] ?? 'text-muted-foreground'
|
|
10
|
-
}
|
|
11
|
-
return 'text-muted-foreground'
|
|
12
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link'
|
|
4
|
-
import { AlertCircle, ChevronRight, Clock, MessageSquare } from 'lucide-react'
|
|
5
|
-
import { Badge } from '@/components/ui/badge'
|
|
6
|
-
import { cn } from '@/lib/utils'
|
|
7
|
-
import type { RecoverCardProps } from './RecoverCard.types'
|
|
8
|
-
|
|
9
|
-
export function RecoverCard({ abandonedSessions, codeHref, className }: RecoverCardProps) {
|
|
10
|
-
if (abandonedSessions.length === 0) return null
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<div className={cn(
|
|
14
|
-
'relative overflow-hidden rounded-xl border border-yellow-500/30 bg-yellow-500/5 p-4 min-w-0 max-w-full',
|
|
15
|
-
className
|
|
16
|
-
)}>
|
|
17
|
-
<div className="flex items-center justify-between mb-3 min-w-0">
|
|
18
|
-
<div className="flex items-center gap-2 min-w-0">
|
|
19
|
-
<AlertCircle className="h-4 w-4 text-yellow-500 shrink-0" />
|
|
20
|
-
<span className="text-xs font-bold uppercase tracking-[0.15em] text-yellow-600 dark:text-yellow-500 truncate">
|
|
21
|
-
Recover
|
|
22
|
-
</span>
|
|
23
|
-
</div>
|
|
24
|
-
<Badge variant="outline" className="text-yellow-600 dark:text-yellow-500 border-yellow-500/50 shrink-0">
|
|
25
|
-
{abandonedSessions.length}
|
|
26
|
-
</Badge>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<div className="space-y-2 min-w-0 max-w-full">
|
|
30
|
-
{abandonedSessions.slice(0, 3).map((session) => (
|
|
31
|
-
<Link
|
|
32
|
-
key={session.id}
|
|
33
|
-
href={`${codeHref}?cmd=p.%20recover`}
|
|
34
|
-
className="block py-2 px-2 -mx-2 hover:bg-yellow-500/10 rounded-lg transition-colors group"
|
|
35
|
-
>
|
|
36
|
-
<div className="flex items-start justify-between gap-2">
|
|
37
|
-
<div className="flex-1 min-w-0">
|
|
38
|
-
<p className="text-sm font-medium leading-tight truncate group-hover:text-foreground transition-colors">
|
|
39
|
-
{session.task}
|
|
40
|
-
</p>
|
|
41
|
-
<div className="flex items-center gap-2 mt-1 text-xs text-muted-foreground">
|
|
42
|
-
<Clock className="h-3 w-3 shrink-0" />
|
|
43
|
-
<span>{session.hoursAgo}h ago</span>
|
|
44
|
-
{session.projectName && (
|
|
45
|
-
<>
|
|
46
|
-
<span className="text-muted-foreground/50">|</span>
|
|
47
|
-
<span className="truncate">{session.projectName}</span>
|
|
48
|
-
</>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
{session.prompt && (
|
|
52
|
-
<div className="flex items-start gap-1.5 mt-1.5">
|
|
53
|
-
<MessageSquare className="h-3 w-3 text-muted-foreground/70 shrink-0 mt-0.5" />
|
|
54
|
-
<p className="text-xs text-muted-foreground/70 italic line-clamp-2">
|
|
55
|
-
"{session.prompt.slice(0, 80)}{session.prompt.length > 80 ? '...' : ''}"
|
|
56
|
-
</p>
|
|
57
|
-
</div>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
<ChevronRight className="h-4 w-4 text-muted-foreground/50 opacity-0 group-hover:opacity-100 transition-opacity shrink-0 mt-1" />
|
|
61
|
-
</div>
|
|
62
|
-
</Link>
|
|
63
|
-
))}
|
|
64
|
-
{abandonedSessions.length > 3 && (
|
|
65
|
-
<p className="text-xs text-yellow-600/70 dark:text-yellow-500/70 text-center pt-1">
|
|
66
|
-
+{abandonedSessions.length - 3} more abandoned sessions
|
|
67
|
-
</p>
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface AbandonedSession {
|
|
2
|
-
id: string
|
|
3
|
-
task: string
|
|
4
|
-
projectId: string
|
|
5
|
-
projectName?: string
|
|
6
|
-
startedAt: string
|
|
7
|
-
lastActivity?: string
|
|
8
|
-
hoursAgo: number
|
|
9
|
-
prompt?: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface RecoverCardProps {
|
|
13
|
-
abandonedSessions: AbandonedSession[]
|
|
14
|
-
codeHref: string
|
|
15
|
-
className?: string
|
|
16
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link'
|
|
4
|
-
import { EmptyState } from '@/components/EmptyState'
|
|
5
|
-
import { Map, ChevronRight, CheckCircle2, Circle } from 'lucide-react'
|
|
6
|
-
import { cn } from '@/lib/utils'
|
|
7
|
-
import type { RoadmapCardProps } from './RoadmapCard.types'
|
|
8
|
-
|
|
9
|
-
// Traffic light colors for progress: green=100%, yellow=50-99%, red=<50%
|
|
10
|
-
function getProgressColor(progress: number): string {
|
|
11
|
-
if (progress >= 100) return 'bg-emerald-500'
|
|
12
|
-
if (progress >= 50) return 'bg-amber-500'
|
|
13
|
-
return 'bg-red-500'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function RoadmapCard({ roadmap, codeHref, className }: RoadmapCardProps) {
|
|
17
|
-
const hasPhases = roadmap?.phases && roadmap.phases.length > 0
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className={cn(
|
|
21
|
-
'relative overflow-hidden rounded-xl border bg-card p-4',
|
|
22
|
-
className
|
|
23
|
-
)}>
|
|
24
|
-
<div className="flex items-center justify-between mb-3">
|
|
25
|
-
<div className="flex items-center gap-2">
|
|
26
|
-
<Map className="h-4 w-4 text-muted-foreground" />
|
|
27
|
-
<span className="text-xs font-bold uppercase tracking-[0.15em] text-muted-foreground">
|
|
28
|
-
Roadmap
|
|
29
|
-
</span>
|
|
30
|
-
</div>
|
|
31
|
-
{hasPhases && (
|
|
32
|
-
<span className="text-xs font-bold tabular-nums">
|
|
33
|
-
{roadmap.progress}%
|
|
34
|
-
</span>
|
|
35
|
-
)}
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
{!hasPhases ? (
|
|
39
|
-
<EmptyState
|
|
40
|
-
icon={Map}
|
|
41
|
-
title="No roadmap yet"
|
|
42
|
-
description="Plan your features"
|
|
43
|
-
command="/p:feature"
|
|
44
|
-
href={codeHref}
|
|
45
|
-
/>
|
|
46
|
-
) : (
|
|
47
|
-
<div className="space-y-4">
|
|
48
|
-
{/* Overall progress */}
|
|
49
|
-
<div>
|
|
50
|
-
<div className="flex items-center justify-between mb-2">
|
|
51
|
-
<span className="text-xs text-muted-foreground">Overall progress</span>
|
|
52
|
-
<span className="text-sm font-bold tabular-nums">{roadmap.progress}%</span>
|
|
53
|
-
</div>
|
|
54
|
-
<div className="h-3 bg-muted rounded-full overflow-hidden">
|
|
55
|
-
<div
|
|
56
|
-
className={cn("h-full rounded-full transition-all duration-500", getProgressColor(roadmap.progress))}
|
|
57
|
-
style={{ width: `${roadmap.progress}%` }}
|
|
58
|
-
/>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
{/* Phases with features */}
|
|
63
|
-
<div className="space-y-4">
|
|
64
|
-
{roadmap.phases
|
|
65
|
-
.filter(p => (p.features || []).length > 0)
|
|
66
|
-
.slice(0, 6)
|
|
67
|
-
.map((phase) => (
|
|
68
|
-
<div key={phase.name} className="group">
|
|
69
|
-
<div className="flex items-center justify-between mb-2">
|
|
70
|
-
<div className="flex items-center gap-2">
|
|
71
|
-
{phase.progress === 100 ? (
|
|
72
|
-
<CheckCircle2 className="h-4 w-4 text-muted-foreground" />
|
|
73
|
-
) : (
|
|
74
|
-
<Circle className="h-4 w-4 text-muted-foreground" />
|
|
75
|
-
)}
|
|
76
|
-
<span className="text-sm font-medium">
|
|
77
|
-
{phase.name}
|
|
78
|
-
</span>
|
|
79
|
-
</div>
|
|
80
|
-
<span className="text-xs font-bold tabular-nums shrink-0 ml-2 text-muted-foreground">
|
|
81
|
-
{phase.progress}%
|
|
82
|
-
</span>
|
|
83
|
-
</div>
|
|
84
|
-
<div className="h-2 bg-muted rounded-full overflow-hidden ml-6">
|
|
85
|
-
<div
|
|
86
|
-
className={cn("h-full rounded-full transition-all duration-300", getProgressColor(phase.progress))}
|
|
87
|
-
style={{ width: `${phase.progress}%` }}
|
|
88
|
-
/>
|
|
89
|
-
</div>
|
|
90
|
-
{/* Show features under each phase */}
|
|
91
|
-
{phase.features && phase.features.length > 0 && (
|
|
92
|
-
<div className="ml-6 mt-2 space-y-1">
|
|
93
|
-
{phase.features.slice(0, 3).map((feature, i) => {
|
|
94
|
-
const isCompleted = feature.status === 'completed'
|
|
95
|
-
const cmdHref = codeHref && !isCompleted
|
|
96
|
-
? `${codeHref}?cmd=${encodeURIComponent(`p. work "${feature.name}"`)}`
|
|
97
|
-
: undefined
|
|
98
|
-
|
|
99
|
-
const content = (
|
|
100
|
-
<>
|
|
101
|
-
<ChevronRight className="h-3 w-3 shrink-0" />
|
|
102
|
-
<span className={cn(
|
|
103
|
-
'truncate',
|
|
104
|
-
isCompleted && 'line-through opacity-60'
|
|
105
|
-
)}>
|
|
106
|
-
{feature.name}
|
|
107
|
-
</span>
|
|
108
|
-
</>
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return cmdHref ? (
|
|
112
|
-
<Link
|
|
113
|
-
key={i}
|
|
114
|
-
href={cmdHref}
|
|
115
|
-
className="flex items-center gap-2 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 rounded px-1 -mx-1 py-0.5 transition-colors cursor-pointer"
|
|
116
|
-
>
|
|
117
|
-
{content}
|
|
118
|
-
</Link>
|
|
119
|
-
) : (
|
|
120
|
-
<div key={i} className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
121
|
-
{content}
|
|
122
|
-
</div>
|
|
123
|
-
)
|
|
124
|
-
})}
|
|
125
|
-
{phase.features.length > 3 && (
|
|
126
|
-
<span className="text-xs text-muted-foreground/70 ml-5">
|
|
127
|
-
+{phase.features.length - 3} more
|
|
128
|
-
</span>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
)}
|
|
132
|
-
</div>
|
|
133
|
-
))}
|
|
134
|
-
</div>
|
|
135
|
-
|
|
136
|
-
{roadmap.phases.length > 0 && (
|
|
137
|
-
<p className="text-xs text-muted-foreground border-t pt-3 mt-3">
|
|
138
|
-
{roadmap.phases.reduce((acc, p) => acc + (p.features?.length || 0), 0)} features across {roadmap.phases.length} phases
|
|
139
|
-
</p>
|
|
140
|
-
)}
|
|
141
|
-
</div>
|
|
142
|
-
)}
|
|
143
|
-
</div>
|
|
144
|
-
)
|
|
145
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface RoadmapPhase {
|
|
2
|
-
name: string
|
|
3
|
-
progress: number
|
|
4
|
-
features?: Array<{ name: string; status: string }>
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface RoadmapData {
|
|
8
|
-
phases: RoadmapPhase[]
|
|
9
|
-
progress: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface RoadmapCardProps {
|
|
13
|
-
roadmap: RoadmapData | null
|
|
14
|
-
codeHref?: string
|
|
15
|
-
className?: string
|
|
16
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import { EmptyState } from '@/components/EmptyState'
|
|
5
|
-
import { ExpandButton } from '@/components/ExpandButton'
|
|
6
|
-
import { Rocket, Clock, FileCode, Check } from 'lucide-react'
|
|
7
|
-
import { Badge } from '@/components/ui/badge'
|
|
8
|
-
import { cn } from '@/lib/utils'
|
|
9
|
-
import { formatShipDate } from './ShipsCard.utils'
|
|
10
|
-
import type { ShipsCardProps } from './ShipsCard.types'
|
|
11
|
-
|
|
12
|
-
const COLLAPSED_LIMIT = 10
|
|
13
|
-
|
|
14
|
-
export function ShipsCard({ ships, totalShips = 0, codeHref, className }: ShipsCardProps) {
|
|
15
|
-
const [expanded, setExpanded] = useState(false)
|
|
16
|
-
const displayShips = expanded ? ships : ships.slice(0, COLLAPSED_LIMIT)
|
|
17
|
-
const hasMore = ships.length > COLLAPSED_LIMIT
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className={cn(
|
|
21
|
-
'relative overflow-hidden rounded-xl border bg-card p-4',
|
|
22
|
-
className
|
|
23
|
-
)}>
|
|
24
|
-
<div className="flex items-center justify-between mb-3">
|
|
25
|
-
<div className="flex items-center gap-2">
|
|
26
|
-
<Rocket className="h-4 w-4 text-muted-foreground" />
|
|
27
|
-
<span className="text-xs font-bold uppercase tracking-[0.15em] text-muted-foreground">
|
|
28
|
-
Shipped
|
|
29
|
-
</span>
|
|
30
|
-
</div>
|
|
31
|
-
<span className="text-xs font-medium text-muted-foreground tabular-nums">
|
|
32
|
-
{totalShips} total
|
|
33
|
-
</span>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
{ships.length === 0 ? (
|
|
37
|
-
<EmptyState
|
|
38
|
-
icon={Rocket}
|
|
39
|
-
title="Nothing shipped yet"
|
|
40
|
-
description="Ship your first feature"
|
|
41
|
-
command="/p:ship"
|
|
42
|
-
href={codeHref}
|
|
43
|
-
compact
|
|
44
|
-
/>
|
|
45
|
-
) : (
|
|
46
|
-
<div className="space-y-2">
|
|
47
|
-
{displayShips.map((ship, i) => (
|
|
48
|
-
<div key={i} className="group py-1.5 hover:bg-muted/50 rounded px-2 -mx-2 border-l-2 border-transparent hover:border-foreground/20">
|
|
49
|
-
<div className="flex items-start justify-between gap-2">
|
|
50
|
-
<div className="flex-1 min-w-0">
|
|
51
|
-
<div className="flex items-center gap-2">
|
|
52
|
-
<Check className="h-3 w-3 text-muted-foreground shrink-0" />
|
|
53
|
-
<p className="text-sm font-medium truncate">
|
|
54
|
-
{ship.name}
|
|
55
|
-
</p>
|
|
56
|
-
{ship.version && (
|
|
57
|
-
<Badge variant="outline" className="text-xs px-1 py-0 font-mono shrink-0 h-4">
|
|
58
|
-
{ship.version}
|
|
59
|
-
</Badge>
|
|
60
|
-
)}
|
|
61
|
-
</div>
|
|
62
|
-
<div className="flex items-center gap-3 mt-1 ml-5">
|
|
63
|
-
<span className="text-xs text-muted-foreground">
|
|
64
|
-
{formatShipDate(ship.date)}
|
|
65
|
-
</span>
|
|
66
|
-
{ship.duration && (
|
|
67
|
-
<span className="inline-flex items-center gap-0.5 text-xs text-muted-foreground">
|
|
68
|
-
<Clock className="h-2.5 w-2.5" />
|
|
69
|
-
{ship.duration}
|
|
70
|
-
</span>
|
|
71
|
-
)}
|
|
72
|
-
{ship.filesChanged && (
|
|
73
|
-
<span className="inline-flex items-center gap-0.5 text-xs text-muted-foreground">
|
|
74
|
-
<FileCode className="h-2.5 w-2.5" />
|
|
75
|
-
{ship.filesChanged} files
|
|
76
|
-
</span>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
))}
|
|
83
|
-
{hasMore && (
|
|
84
|
-
<ExpandButton
|
|
85
|
-
expanded={expanded}
|
|
86
|
-
totalCount={ships.length}
|
|
87
|
-
collapsedLimit={COLLAPSED_LIMIT}
|
|
88
|
-
onToggle={() => setExpanded(!expanded)}
|
|
89
|
-
/>
|
|
90
|
-
)}
|
|
91
|
-
</div>
|
|
92
|
-
)}
|
|
93
|
-
</div>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Area, AreaChart, ResponsiveContainer } from 'recharts'
|
|
4
|
-
import type { SparklineChartProps } from './SparklineChart.types'
|
|
5
|
-
|
|
6
|
-
export function SparklineChart({
|
|
7
|
-
data,
|
|
8
|
-
color = 'currentColor',
|
|
9
|
-
height = 32,
|
|
10
|
-
showArea = true,
|
|
11
|
-
}: SparklineChartProps) {
|
|
12
|
-
const chartData = data.map((value, index) => ({ index, value }))
|
|
13
|
-
|
|
14
|
-
if (data.length === 0) {
|
|
15
|
-
return <div style={{ height }} className="w-full" />
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<div className="w-full min-w-0" style={{ height }}>
|
|
20
|
-
<ResponsiveContainer width="100%" height="100%">
|
|
21
|
-
<AreaChart data={chartData} margin={{ top: 0, right: 0, bottom: 0, left: 0 }}>
|
|
22
|
-
<defs>
|
|
23
|
-
<linearGradient id="sparklineGradient" x1="0" y1="0" x2="0" y2="1">
|
|
24
|
-
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
|
|
25
|
-
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
|
26
|
-
</linearGradient>
|
|
27
|
-
</defs>
|
|
28
|
-
<Area
|
|
29
|
-
type="monotone"
|
|
30
|
-
dataKey="value"
|
|
31
|
-
stroke={color}
|
|
32
|
-
strokeWidth={1.5}
|
|
33
|
-
fill={showArea ? 'url(#sparklineGradient)' : 'none'}
|
|
34
|
-
isAnimationActive={false}
|
|
35
|
-
/>
|
|
36
|
-
</AreaChart>
|
|
37
|
-
</ResponsiveContainer>
|
|
38
|
-
</div>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react'
|
|
4
|
-
import { MasonryGrid } from '@/components/MasonryGrid'
|
|
5
|
-
import { NowCard } from '@/components/NowCard'
|
|
6
|
-
import { VelocityCard } from '@/components/VelocityCard'
|
|
7
|
-
import { StreakCard } from '@/components/StreakCard'
|
|
8
|
-
import { QueueCard } from '@/components/QueueCard'
|
|
9
|
-
import { ShipsCard } from '@/components/ShipsCard'
|
|
10
|
-
import { IdeasCard } from '@/components/IdeasCard'
|
|
11
|
-
import { AgentsCard } from '@/components/AgentsCard'
|
|
12
|
-
import { RoadmapCard } from '@/components/RoadmapCard'
|
|
13
|
-
import { BlockersCard } from '@/components/BlockersCard'
|
|
14
|
-
import { RecoverCard, type AbandonedSession } from '@/components/RecoverCard'
|
|
15
|
-
import { ActivityTimeline } from '@/components/ActivityTimeline'
|
|
16
|
-
import type { TimelineEvent } from '@/lib/parse-prjct-files'
|
|
17
|
-
|
|
18
|
-
interface StatsMasonryProps {
|
|
19
|
-
projectId: string
|
|
20
|
-
currentTask: any
|
|
21
|
-
velocity: number
|
|
22
|
-
weeklyVelocityData: number[]
|
|
23
|
-
velocityChange: number
|
|
24
|
-
estimateAccuracy?: number
|
|
25
|
-
roadmap: any
|
|
26
|
-
queue: any[]
|
|
27
|
-
shipped: any[]
|
|
28
|
-
totalShips: number
|
|
29
|
-
streak: number
|
|
30
|
-
blockers: any[]
|
|
31
|
-
ideas: any[]
|
|
32
|
-
agents: any[]
|
|
33
|
-
timeline: TimelineEvent[]
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function StatsMasonry({
|
|
37
|
-
projectId,
|
|
38
|
-
currentTask,
|
|
39
|
-
velocity,
|
|
40
|
-
weeklyVelocityData,
|
|
41
|
-
velocityChange,
|
|
42
|
-
estimateAccuracy,
|
|
43
|
-
roadmap,
|
|
44
|
-
queue,
|
|
45
|
-
shipped,
|
|
46
|
-
totalShips,
|
|
47
|
-
streak,
|
|
48
|
-
blockers,
|
|
49
|
-
ideas,
|
|
50
|
-
agents,
|
|
51
|
-
timeline,
|
|
52
|
-
}: StatsMasonryProps) {
|
|
53
|
-
const codeHref = `/project/${projectId}/code`
|
|
54
|
-
const [abandonedSessions, setAbandonedSessions] = useState<AbandonedSession[]>([])
|
|
55
|
-
|
|
56
|
-
// Fetch abandoned sessions from API
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
async function fetchAbandonedSessions() {
|
|
59
|
-
try {
|
|
60
|
-
const res = await fetch(`/api/sessions/current?projectId=${projectId}`)
|
|
61
|
-
const data = await res.json()
|
|
62
|
-
if (data.success && data.data.abandonedSessions) {
|
|
63
|
-
setAbandonedSessions(data.data.abandonedSessions)
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
// Silently fail - abandoned sessions are not critical
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
fetchAbandonedSessions()
|
|
70
|
-
}, [projectId])
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<MasonryGrid>
|
|
74
|
-
{/* Show RecoverCard first if there are abandoned sessions */}
|
|
75
|
-
{abandonedSessions.length > 0 && (
|
|
76
|
-
<RecoverCard abandonedSessions={abandonedSessions} codeHref={codeHref} />
|
|
77
|
-
)}
|
|
78
|
-
<NowCard currentTask={currentTask} codeHref={codeHref} />
|
|
79
|
-
<VelocityCard
|
|
80
|
-
tasksPerDay={velocity}
|
|
81
|
-
weeklyData={weeklyVelocityData}
|
|
82
|
-
change={velocityChange}
|
|
83
|
-
estimateAccuracy={estimateAccuracy}
|
|
84
|
-
/>
|
|
85
|
-
<RoadmapCard roadmap={roadmap} codeHref={codeHref} />
|
|
86
|
-
<QueueCard queue={queue} codeHref={codeHref} />
|
|
87
|
-
<ShipsCard ships={shipped} totalShips={totalShips} codeHref={codeHref} />
|
|
88
|
-
<StreakCard streak={streak} />
|
|
89
|
-
<BlockersCard blockers={blockers} codeHref={codeHref} />
|
|
90
|
-
<IdeasCard ideas={ideas} codeHref={codeHref} />
|
|
91
|
-
<AgentsCard agents={agents} codeHref={codeHref} />
|
|
92
|
-
<ActivityTimeline timeline={timeline} />
|
|
93
|
-
</MasonryGrid>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { StatsMasonry } from './StatsMasonry'
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Flame, Calendar } from 'lucide-react'
|
|
4
|
-
import { cn } from '@/lib/utils'
|
|
5
|
-
import type { StreakCardProps } from './StreakCard.types'
|
|
6
|
-
|
|
7
|
-
function getStreakColor(streak: number): string {
|
|
8
|
-
if (streak >= 7) return 'text-emerald-600 dark:text-emerald-400'
|
|
9
|
-
if (streak >= 3) return 'text-amber-600 dark:text-amber-400'
|
|
10
|
-
return 'text-red-600 dark:text-red-400'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function StreakCard({ streak, className }: StreakCardProps) {
|
|
14
|
-
return (
|
|
15
|
-
<div className={cn(
|
|
16
|
-
'relative overflow-hidden rounded-xl border bg-card p-4',
|
|
17
|
-
className
|
|
18
|
-
)}>
|
|
19
|
-
<div className="flex items-center justify-between mb-3">
|
|
20
|
-
<div className="flex items-center gap-2">
|
|
21
|
-
<Flame className="h-4 w-4 text-muted-foreground" />
|
|
22
|
-
<span className="text-xs font-bold uppercase tracking-[0.15em] text-muted-foreground">
|
|
23
|
-
Streak
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
<div className="flex items-center gap-4">
|
|
29
|
-
<Flame className={cn("h-10 w-10", getStreakColor(streak))} />
|
|
30
|
-
<div>
|
|
31
|
-
<p className="text-3xl font-bold tabular-nums">{streak}</p>
|
|
32
|
-
<p className="text-xs text-muted-foreground">consecutive day{streak !== 1 ? 's' : ''}</p>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<div className="mt-4">
|
|
37
|
-
<div className="flex items-center gap-1.5 mb-2">
|
|
38
|
-
<Calendar className="h-3 w-3 text-muted-foreground" />
|
|
39
|
-
<span className="text-xs text-muted-foreground">Last 7 days</span>
|
|
40
|
-
</div>
|
|
41
|
-
<div className="flex gap-1">
|
|
42
|
-
{Array.from({ length: 7 }).map((_, i) => (
|
|
43
|
-
<div
|
|
44
|
-
key={i}
|
|
45
|
-
className={cn(
|
|
46
|
-
'h-2 flex-1 rounded-full transition-colors',
|
|
47
|
-
i < streak ? 'bg-foreground' : 'bg-muted'
|
|
48
|
-
)}
|
|
49
|
-
/>
|
|
50
|
-
))}
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { TasksCounterProps } from './TasksCounter.types'
|
|
2
|
-
|
|
3
|
-
export function TasksCounter({ count }: TasksCounterProps) {
|
|
4
|
-
return (
|
|
5
|
-
<div className="flex flex-col sm:flex-row sm:items-baseline gap-1 sm:gap-3">
|
|
6
|
-
<span className="text-5xl sm:text-6xl md:text-7xl font-bold tracking-tighter tabular-nums">
|
|
7
|
-
{count}
|
|
8
|
-
</span>
|
|
9
|
-
<span className="text-sm sm:text-base md:text-lg text-muted-foreground">
|
|
10
|
-
shipped
|
|
11
|
-
</span>
|
|
12
|
-
</div>
|
|
13
|
-
)
|
|
14
|
-
}
|