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.
Files changed (245) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/CLAUDE.md +74 -211
  3. package/core/agentic/prompt-builder.ts +3 -7
  4. package/core/command-registry/optional-commands.ts +0 -20
  5. package/core/infrastructure/command-installer/command-installer.ts +8 -1
  6. package/core/infrastructure/command-installer/global-config.ts +31 -1
  7. package/core/infrastructure/command-installer/index.ts +1 -1
  8. package/core/infrastructure/setup.ts +3 -0
  9. package/package.json +3 -17
  10. package/templates/agentic/subagent-generation.md +8 -6
  11. package/templates/commands/done.md +57 -258
  12. package/templates/commands/now.md +72 -277
  13. package/templates/commands/ship.md +55 -261
  14. package/templates/commands/sync.md +7 -7
  15. package/templates/commands/test.md +328 -21
  16. package/templates/global/CLAUDE.md +40 -205
  17. package/templates/global/docs/agents.md +88 -0
  18. package/templates/global/docs/architecture.md +103 -0
  19. package/templates/global/docs/commands.md +98 -0
  20. package/templates/global/docs/validation.md +95 -0
  21. package/templates/mcp-config.json +36 -0
  22. package/bin/dev.js +0 -216
  23. package/bin/serve.js +0 -361
  24. package/packages/web/README.md +0 -36
  25. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  26. package/packages/web/app/api/claude/status/route.ts +0 -34
  27. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  28. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  29. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  30. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  31. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  32. package/packages/web/app/api/projects/route.ts +0 -16
  33. package/packages/web/app/api/sessions/current/route.ts +0 -132
  34. package/packages/web/app/api/sessions/history/route.ts +0 -204
  35. package/packages/web/app/error.tsx +0 -34
  36. package/packages/web/app/favicon.ico +0 -0
  37. package/packages/web/app/globals.css +0 -198
  38. package/packages/web/app/layout.tsx +0 -53
  39. package/packages/web/app/loading.tsx +0 -7
  40. package/packages/web/app/not-found.tsx +0 -25
  41. package/packages/web/app/page.tsx +0 -12
  42. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  43. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  44. package/packages/web/app/project/[id]/error.tsx +0 -41
  45. package/packages/web/app/project/[id]/loading.tsx +0 -9
  46. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  47. package/packages/web/app/project/[id]/page.tsx +0 -384
  48. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  49. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  50. package/packages/web/app/sessions/page.tsx +0 -165
  51. package/packages/web/app/settings/page.tsx +0 -151
  52. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  53. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  54. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  55. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  56. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  57. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  58. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  59. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  60. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  61. package/packages/web/components/AgentsCard/index.ts +0 -2
  62. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  63. package/packages/web/components/AppSidebar/index.ts +0 -1
  64. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  65. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  66. package/packages/web/components/BackLink/index.ts +0 -2
  67. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  68. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  69. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  70. package/packages/web/components/BentoCard/index.ts +0 -2
  71. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  72. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  73. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  74. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  75. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  76. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  77. package/packages/web/components/BentoGrid/index.ts +0 -2
  78. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  79. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  80. package/packages/web/components/BlockersCard/index.ts +0 -2
  81. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  82. package/packages/web/components/CommandBar/index.ts +0 -1
  83. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  84. package/packages/web/components/CommandButton/index.ts +0 -1
  85. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  86. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  87. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  88. package/packages/web/components/DashboardContent/index.ts +0 -1
  89. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  90. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  91. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  92. package/packages/web/components/DateGroup/index.ts +0 -2
  93. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  94. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  95. package/packages/web/components/EmptyState/index.ts +0 -2
  96. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  97. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  98. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  99. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  100. package/packages/web/components/EventRow/index.ts +0 -2
  101. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  102. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  103. package/packages/web/components/ExpandButton/index.ts +0 -2
  104. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  105. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  106. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  107. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  108. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  109. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  110. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  111. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  112. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  113. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  114. package/packages/web/components/HeroSection/index.ts +0 -2
  115. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  116. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  117. package/packages/web/components/IdeasCard/index.ts +0 -2
  118. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  119. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  120. package/packages/web/components/InsightMessage/index.ts +0 -2
  121. package/packages/web/components/Logo/Logo.tsx +0 -65
  122. package/packages/web/components/Logo/index.ts +0 -1
  123. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  124. package/packages/web/components/MarkdownContent/index.ts +0 -1
  125. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  126. package/packages/web/components/MasonryGrid/index.ts +0 -1
  127. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  128. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  129. package/packages/web/components/MomentumWidget/index.ts +0 -2
  130. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  131. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  132. package/packages/web/components/NowCard/index.ts +0 -2
  133. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  134. package/packages/web/components/PageHeader/index.ts +0 -1
  135. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  136. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  137. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  138. package/packages/web/components/ProgressRing/index.ts +0 -2
  139. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  140. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  141. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  142. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  143. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  144. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  145. package/packages/web/components/Providers/Providers.tsx +0 -48
  146. package/packages/web/components/Providers/index.ts +0 -1
  147. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  148. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  149. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  150. package/packages/web/components/QueueCard/index.ts +0 -2
  151. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  152. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  153. package/packages/web/components/RecoverCard/index.ts +0 -2
  154. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  155. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  156. package/packages/web/components/RoadmapCard/index.ts +0 -2
  157. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  158. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  159. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  160. package/packages/web/components/ShipsCard/index.ts +0 -2
  161. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  162. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  163. package/packages/web/components/SparklineChart/index.ts +0 -2
  164. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  165. package/packages/web/components/StatsMasonry/index.ts +0 -1
  166. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  167. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  168. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  169. package/packages/web/components/StreakCard/index.ts +0 -2
  170. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  171. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  172. package/packages/web/components/TasksCounter/index.ts +0 -2
  173. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  174. package/packages/web/components/TechStackBadges/index.ts +0 -1
  175. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  176. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  177. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  178. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  179. package/packages/web/components/TerminalDock/index.ts +0 -2
  180. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  181. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  182. package/packages/web/components/TerminalTabs/index.ts +0 -1
  183. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  184. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  185. package/packages/web/components/VelocityBadge/index.ts +0 -2
  186. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  187. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  188. package/packages/web/components/VelocityCard/index.ts +0 -2
  189. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  190. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  191. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  192. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  193. package/packages/web/components/WeeklyReports/index.ts +0 -4
  194. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  195. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  196. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  197. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  198. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  199. package/packages/web/components/ui/badge.tsx +0 -46
  200. package/packages/web/components/ui/button.tsx +0 -60
  201. package/packages/web/components/ui/card.tsx +0 -92
  202. package/packages/web/components/ui/chart.tsx +0 -385
  203. package/packages/web/components/ui/dialog.tsx +0 -143
  204. package/packages/web/components/ui/drawer.tsx +0 -135
  205. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  206. package/packages/web/components/ui/input.tsx +0 -21
  207. package/packages/web/components/ui/scroll-area.tsx +0 -58
  208. package/packages/web/components/ui/select.tsx +0 -187
  209. package/packages/web/components/ui/sheet.tsx +0 -139
  210. package/packages/web/components/ui/tabs.tsx +0 -66
  211. package/packages/web/components/ui/tooltip.tsx +0 -61
  212. package/packages/web/components.json +0 -22
  213. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  214. package/packages/web/context/TerminalContext.tsx +0 -45
  215. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  216. package/packages/web/eslint.config.mjs +0 -18
  217. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  218. package/packages/web/hooks/useProjectStats.ts +0 -93
  219. package/packages/web/hooks/useProjects.ts +0 -73
  220. package/packages/web/lib/actions/projects.ts +0 -15
  221. package/packages/web/lib/commands.ts +0 -81
  222. package/packages/web/lib/format.ts +0 -23
  223. package/packages/web/lib/generate-week-report.ts +0 -285
  224. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  225. package/packages/web/lib/project-colors.ts +0 -58
  226. package/packages/web/lib/projects.ts +0 -506
  227. package/packages/web/lib/pty.ts +0 -101
  228. package/packages/web/lib/query-config.ts +0 -44
  229. package/packages/web/lib/services/index.ts +0 -9
  230. package/packages/web/lib/services/projects.server.ts +0 -66
  231. package/packages/web/lib/services/stats.server.ts +0 -562
  232. package/packages/web/lib/unified-loader.ts +0 -396
  233. package/packages/web/lib/utils.ts +0 -6
  234. package/packages/web/next-env.d.ts +0 -6
  235. package/packages/web/next.config.ts +0 -7
  236. package/packages/web/package.json +0 -57
  237. package/packages/web/postcss.config.mjs +0 -7
  238. package/packages/web/public/file.svg +0 -1
  239. package/packages/web/public/globe.svg +0 -1
  240. package/packages/web/public/next.svg +0 -1
  241. package/packages/web/public/vercel.svg +0 -1
  242. package/packages/web/public/window.svg +0 -1
  243. package/packages/web/server.ts +0 -312
  244. package/packages/web/tsconfig.json +0 -34
  245. package/templates/commands/serve.md +0 -121
@@ -1,284 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useCallback, useTransition } from 'react'
4
- import Link from 'next/link'
5
- import { useRouter } from 'next/navigation'
6
- import { SessionsChart } from '@/components/charts/SessionsChart'
7
- import { Button } from '@/components/ui/button'
8
- import { ProgressRing } from '@/components/ProgressRing'
9
- import { TechStackBadges } from '@/components/TechStackBadges'
10
- import { formatRelativeTime, formatPath } from '@/lib/format'
11
- import {
12
- MoreHorizontal,
13
- Trash2,
14
- AlertTriangle,
15
- Target,
16
- Lightbulb,
17
- ListTodo,
18
- Zap,
19
- FolderGit2,
20
- Play,
21
- FileText
22
- } from 'lucide-react'
23
- import {
24
- DropdownMenu,
25
- DropdownMenuContent,
26
- DropdownMenuItem,
27
- DropdownMenuTrigger,
28
- } from '@/components/ui/dropdown-menu'
29
- import {
30
- AlertDialog,
31
- AlertDialogAction,
32
- AlertDialogCancel,
33
- AlertDialogContent,
34
- AlertDialogDescription,
35
- AlertDialogFooter,
36
- AlertDialogHeader,
37
- AlertDialogTitle,
38
- } from '@/components/ui/alert-dialog'
39
- import { deleteProject } from '@/lib/actions/projects'
40
-
41
- // Traffic light colors based on completion rate
42
- type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
43
- function getCompletionColor(rate: number): AccentColor {
44
- if (rate >= 75) return 'success'
45
- if (rate >= 25) return 'warning'
46
- return 'destructive'
47
- }
48
-
49
- export interface Project {
50
- id: string
51
- name: string
52
- path: string
53
- repoPath?: string | null
54
- currentTask?: string | null
55
- hasActiveSession?: boolean
56
- lastActivity?: string | null
57
- ideasCount?: number
58
- nextTasksCount?: number
59
- shippedCount?: number
60
- techStack?: string[]
61
- iconPath?: string | null
62
- version?: string
63
- stack?: string
64
- filesCount?: number
65
- commitsCount?: number
66
- }
67
-
68
- export interface GlobalStats {
69
- userName: string
70
- totalProjects: number
71
- }
72
-
73
- interface DashboardContentProps {
74
- projects: Project[]
75
- stats: GlobalStats
76
- }
77
-
78
- export function DashboardContent({ projects, stats }: DashboardContentProps) {
79
- const [projectToDelete, setProjectToDelete] = useState<Project | null>(null)
80
- const [isPending, startTransition] = useTransition()
81
- const router = useRouter()
82
-
83
- const handleDeleteClick = useCallback((project: Project, e: React.MouseEvent) => {
84
- e.preventDefault()
85
- e.stopPropagation()
86
- setProjectToDelete(project)
87
- }, [])
88
-
89
- const handleConfirmDelete = useCallback(() => {
90
- if (projectToDelete) {
91
- startTransition(async () => {
92
- await deleteProject(projectToDelete.id)
93
- setProjectToDelete(null)
94
- router.refresh()
95
- })
96
- }
97
- }, [projectToDelete, router])
98
-
99
- const projectCount = projects?.length || 0
100
-
101
- return (
102
- <div className="p-4 sm:p-6 md:p-8 h-full overflow-auto">
103
- {/* Mobile: Add padding for hamburger menu */}
104
- <div className="pl-10 md:pl-0">
105
- <p className="text-sm sm:text-base text-muted-foreground">Hola! {stats?.userName || 'Developer'}</p>
106
-
107
- <h1 className="text-6xl sm:text-7xl md:text-8xl font-bold tracking-tighter tabular-nums mt-2">{projectCount}</h1>
108
- <p className="text-sm sm:text-base text-muted-foreground mt-1">{projectCount === 1 ? 'project' : 'projects'}</p>
109
- </div>
110
-
111
- <section className="mt-6 sm:mt-8">
112
- <SessionsChart />
113
- </section>
114
-
115
- <section className="mt-8 sm:mt-10 md:mt-12">
116
- <h2 className="font-bold uppercase tracking-wide text-muted-foreground mb-4 sm:mb-6 text-sm">Active Projects</h2>
117
-
118
- {projects?.length === 0 ? (
119
- <div className="border-2 border-dashed border-border rounded-xl p-6 sm:p-8 text-center">
120
- <p className="text-muted-foreground text-sm sm:text-base">
121
- No projects yet. Initialize with <code className="bg-muted px-2 py-1 rounded font-mono text-xs sm:text-sm">/p:init</code>
122
- </p>
123
- </div>
124
- ) : (
125
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
126
- {projects?.map((project: Project) => (
127
- <ProjectCard key={project.id} project={project} onDeleteClick={handleDeleteClick} />
128
- ))}
129
- </div>
130
- )}
131
- </section>
132
-
133
- <AlertDialog open={!!projectToDelete} onOpenChange={(open: boolean) => !open && setProjectToDelete(null)}>
134
- <AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
135
- <AlertDialogHeader>
136
- <AlertDialogTitle className="flex items-center gap-2">
137
- <AlertTriangle className="w-5 h-5 text-destructive shrink-0" />
138
- Delete Project?
139
- </AlertDialogTitle>
140
- <AlertDialogDescription>
141
- <strong>&quot;{projectToDelete?.name}&quot;</strong> will be moved to trash.
142
- <br />
143
- <span className="text-muted-foreground text-xs sm:text-sm break-all">
144
- Location: ~/.prjct-cli/.trash/{projectToDelete?.id}
145
- </span>
146
- </AlertDialogDescription>
147
- </AlertDialogHeader>
148
- <AlertDialogFooter className="flex-col sm:flex-row gap-2">
149
- <AlertDialogCancel disabled={isPending} className="w-full sm:w-auto">Cancel</AlertDialogCancel>
150
- <AlertDialogAction
151
- onClick={handleConfirmDelete}
152
- disabled={isPending}
153
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
154
- >
155
- {isPending ? 'Deleting...' : 'Delete'}
156
- </AlertDialogAction>
157
- </AlertDialogFooter>
158
- </AlertDialogContent>
159
- </AlertDialog>
160
-
161
- {/* Bottom safe area padding */}
162
- <div className="h-6 sm:h-8" />
163
- </div>
164
- )
165
- }
166
-
167
- interface ProjectCardProps {
168
- project: Project
169
- onDeleteClick: (project: Project, e: React.MouseEvent) => void
170
- }
171
-
172
- function ProjectCard({ project, onDeleteClick }: ProjectCardProps) {
173
- const hasStats = project.currentTask || project.nextTasksCount || project.ideasCount || project.lastActivity
174
-
175
- // Calculate completion rate
176
- const shipped = project.shippedCount ?? 0
177
- const pending = project.nextTasksCount ?? 0
178
- const total = shipped + pending
179
- const completionRate = total > 0 ? Math.round((shipped / total) * 100) : 0
180
- const completionColor = getCompletionColor(completionRate)
181
-
182
- return (
183
- <div className="group relative bg-card border border-border rounded-lg overflow-hidden hover:border-primary/50 active:scale-[0.99] transition-all">
184
- {project.hasActiveSession && <div className="absolute top-0 left-0 right-0 h-0.5 bg-green-500" />}
185
-
186
- <Link href={`/project/${project.id}`} className="block p-3 sm:p-4">
187
- <div className="flex items-start gap-3">
188
- <ProgressRing value={completionRate} size="md" accentColor={completionColor} className="shrink-0" />
189
-
190
- <div className="flex-1 min-w-0">
191
- <div className="flex items-center gap-2">
192
- <h3 className="font-bold truncate text-sm sm:text-base">{project.name}</h3>
193
- {project.hasActiveSession && (
194
- <span className="flex h-2 w-2 shrink-0">
195
- <span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-green-400 opacity-75" />
196
- <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
197
- </span>
198
- )}
199
- </div>
200
- {project.repoPath && (
201
- <p className="text-xs text-muted-foreground truncate mt-0.5 flex items-center gap-1">
202
- <FolderGit2 className="w-3 h-3 shrink-0" />
203
- {formatPath(project.repoPath)}
204
- </p>
205
- )}
206
- </div>
207
-
208
- {/* Mobile: Always show menu button */}
209
- <DropdownMenu>
210
- <DropdownMenuTrigger asChild>
211
- <Button
212
- variant="ghost"
213
- size="icon-sm"
214
- className="shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
215
- onClick={(e) => e.preventDefault()}
216
- >
217
- <MoreHorizontal className="w-4 h-4" />
218
- </Button>
219
- </DropdownMenuTrigger>
220
- <DropdownMenuContent align="end">
221
- <DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
222
- <Link href={`/project/${project.id}/code`}>
223
- <Play className="w-4 h-4 mr-2" />
224
- Start working
225
- </Link>
226
- </DropdownMenuItem>
227
- <DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
228
- <Link href={`/project/${project.id}/reports`}>
229
- <FileText className="w-4 h-4 mr-2" />
230
- Reports
231
- </Link>
232
- </DropdownMenuItem>
233
- <DropdownMenuItem
234
- className="text-destructive focus:text-destructive min-h-[44px] sm:min-h-0"
235
- onClick={(e: React.MouseEvent) => onDeleteClick(project, e)}
236
- >
237
- <Trash2 className="w-4 h-4 mr-2" />
238
- Delete
239
- </DropdownMenuItem>
240
- </DropdownMenuContent>
241
- </DropdownMenu>
242
- </div>
243
-
244
- {project.currentTask && (
245
- <div className="mt-3 p-2 bg-primary/5 rounded border border-primary/10">
246
- <div className="flex items-start gap-2">
247
- <Target className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
248
- <span className="text-xs sm:text-sm text-foreground line-clamp-2">{project.currentTask}</span>
249
- </div>
250
- </div>
251
- )}
252
-
253
- {hasStats && (
254
- <div className="mt-3 flex flex-wrap items-center gap-2 sm:gap-3 text-xs text-muted-foreground">
255
- {(project.nextTasksCount ?? 0) > 0 && (
256
- <div className="flex items-center gap-1">
257
- <ListTodo className="w-3.5 h-3.5" />
258
- <span>{project.nextTasksCount} queued</span>
259
- </div>
260
- )}
261
- {(project.ideasCount ?? 0) > 0 && (
262
- <div className="flex items-center gap-1">
263
- <Lightbulb className="w-3.5 h-3.5" />
264
- <span>{project.ideasCount} ideas</span>
265
- </div>
266
- )}
267
- {project.lastActivity && (
268
- <div className="flex items-center gap-1 ml-auto">
269
- <Zap className="w-3.5 h-3.5" />
270
- <span>{formatRelativeTime(project.lastActivity)}</span>
271
- </div>
272
- )}
273
- </div>
274
- )}
275
-
276
- {project.techStack && project.techStack.length > 0 && (
277
- <div className="mt-3">
278
- <TechStackBadges techStack={project.techStack} />
279
- </div>
280
- )}
281
- </Link>
282
- </div>
283
- )
284
- }
@@ -1 +0,0 @@
1
- export { DashboardContent } from './DashboardContent'
@@ -1,18 +0,0 @@
1
- import { EventRow } from '@/components/EventRow'
2
- import { formatTimelineDate } from './DateGroup.utils'
3
- import type { DateGroupProps } from './DateGroup.types'
4
-
5
- export function DateGroup({ dateKey, events }: DateGroupProps) {
6
- return (
7
- <div>
8
- <p className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-2">
9
- {formatTimelineDate(dateKey + 'T00:00:00')}
10
- </p>
11
- <div className="space-y-1">
12
- {events.map((event, i) => (
13
- <EventRow key={i} event={event} />
14
- ))}
15
- </div>
16
- </div>
17
- )
18
- }
@@ -1,6 +0,0 @@
1
- import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
-
3
- export interface DateGroupProps {
4
- dateKey: string
5
- events: TimelineEvent[]
6
- }
@@ -1,11 +0,0 @@
1
- export function formatTimelineDate(dateString: string): string {
2
- const date = new Date(dateString)
3
- const today = new Date()
4
- const yesterday = new Date(today)
5
- yesterday.setDate(yesterday.getDate() - 1)
6
-
7
- if (date.toDateString() === today.toDateString()) return 'Today'
8
- if (date.toDateString() === yesterday.toDateString()) return 'Yesterday'
9
-
10
- return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })
11
- }
@@ -1,2 +0,0 @@
1
- export { DateGroup } from './DateGroup'
2
- export type { DateGroupProps } from './DateGroup.types'
@@ -1,76 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { cn } from '@/lib/utils'
5
- import { ArrowRight } from 'lucide-react'
6
- import type { EmptyStateProps } from './EmptyState.types'
7
-
8
- export function EmptyState({
9
- icon: Icon,
10
- title,
11
- description,
12
- command,
13
- href,
14
- className,
15
- compact = false,
16
- }: EmptyStateProps) {
17
- // Command button content
18
- const CommandButton = href ? (
19
- <Link
20
- href={href}
21
- className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
22
- >
23
- {command}
24
- <ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
25
- </Link>
26
- ) : command ? (
27
- <button
28
- onClick={() => navigator.clipboard.writeText(command)}
29
- className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs font-mono hover:bg-muted/80"
30
- >
31
- {command}
32
- </button>
33
- ) : null
34
-
35
- if (compact) {
36
- return (
37
- <div className={cn('flex items-center gap-2 text-muted-foreground', className)}>
38
- <Icon className="h-4 w-4" />
39
- <span className="text-sm">{title}</span>
40
- {CommandButton}
41
- </div>
42
- )
43
- }
44
-
45
- return (
46
- <div className={cn('flex flex-col items-center justify-center text-center py-4', className)}>
47
- <div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3">
48
- <Icon className="h-5 w-5 text-muted-foreground" />
49
- </div>
50
- <p className="text-sm font-medium text-muted-foreground">{title}</p>
51
- {description && (
52
- <p className="text-xs text-muted-foreground/70 mt-1">{description}</p>
53
- )}
54
- {command && (
55
- <div className="mt-2">
56
- {href ? (
57
- <Link
58
- href={href}
59
- className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
60
- >
61
- {command}
62
- <ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
63
- </Link>
64
- ) : (
65
- <button
66
- onClick={() => navigator.clipboard.writeText(command)}
67
- className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-muted text-xs font-mono text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors"
68
- >
69
- {command}
70
- </button>
71
- )}
72
- </div>
73
- )}
74
- </div>
75
- )
76
- }
@@ -1,11 +0,0 @@
1
- import type { LucideIcon } from 'lucide-react'
2
-
3
- export interface EmptyStateProps {
4
- icon: LucideIcon
5
- title: string
6
- description?: string
7
- command?: string
8
- href?: string
9
- className?: string
10
- compact?: boolean
11
- }
@@ -1,2 +0,0 @@
1
- export { EmptyState } from './EmptyState'
2
- export type { EmptyStateProps } from './EmptyState.types'
@@ -1,10 +0,0 @@
1
- import { Activity, CheckCircle2, Rocket, Target, RefreshCw } from 'lucide-react'
2
- import type { EventIconName } from './EventRow.types'
3
-
4
- export const EVENT_ICON_MAP: Record<EventIconName, typeof Activity> = {
5
- check: CheckCircle2,
6
- target: Target,
7
- rocket: Rocket,
8
- refresh: RefreshCw,
9
- activity: Activity,
10
- }
@@ -1,49 +0,0 @@
1
- import { cn } from '@/lib/utils'
2
- import { EVENT_ICON_MAP } from './EventRow.constants'
3
- import {
4
- formatTime,
5
- getEventIconName,
6
- getEventColor,
7
- getEventBadge,
8
- getEventLabel,
9
- } from './EventRow.utils'
10
- import type { EventRowProps } from './EventRow.types'
11
-
12
- export function EventRow({ event }: EventRowProps) {
13
- const iconName = getEventIconName(event.type)
14
- const Icon = EVENT_ICON_MAP[iconName]
15
- const duration = 'duration' in event && typeof event.duration === 'string' ? event.duration : null
16
-
17
- return (
18
- <div className="flex items-center gap-2 sm:gap-3 py-2 sm:py-1.5 px-2 -mx-2 rounded-md hover:bg-muted/50 active:bg-muted/70 transition-colors group">
19
- {event.ts && (
20
- <span className="hidden sm:block text-xs text-muted-foreground w-14 shrink-0 tabular-nums">
21
- {formatTime(event.ts)}
22
- </span>
23
- )}
24
-
25
- <Icon className={cn('h-4 w-4 sm:h-3.5 sm:w-3.5 shrink-0', getEventColor(event.type))} />
26
-
27
- <div className="flex-1 min-w-0">
28
- <span className="text-sm truncate block group-hover:text-foreground transition-colors">
29
- {getEventLabel(event)}
30
- </span>
31
- {event.ts && (
32
- <span className="sm:hidden text-xs text-muted-foreground">
33
- {formatTime(event.ts)}
34
- </span>
35
- )}
36
- </div>
37
-
38
- <span className="text-xs font-bold tracking-wider text-muted-foreground shrink-0">
39
- {getEventBadge(event.type)}
40
- </span>
41
-
42
- {duration && (
43
- <span className="text-xs text-muted-foreground shrink-0">
44
- {duration}
45
- </span>
46
- )}
47
- </div>
48
- )
49
- }
@@ -1,7 +0,0 @@
1
- import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
-
3
- export type EventIconName = 'check' | 'target' | 'rocket' | 'refresh' | 'activity'
4
-
5
- export interface EventRowProps {
6
- event: TimelineEvent
7
- }
@@ -1,49 +0,0 @@
1
- import type { EventIconName } from './EventRow.types'
2
-
3
- export function formatTime(dateString: string): string {
4
- const date = new Date(dateString)
5
- return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })
6
- }
7
-
8
- export function getEventIconName(type: string): EventIconName {
9
- const iconMap: Record<string, EventIconName> = {
10
- task_complete: 'check',
11
- task_start: 'target',
12
- feature_ship: 'rocket',
13
- sync: 'refresh',
14
- }
15
- return iconMap[type] ?? 'activity'
16
- }
17
-
18
- export function getEventColor(type: string): string {
19
- const colorMap: Record<string, string> = {
20
- task_complete: 'text-muted-foreground',
21
- task_start: 'text-muted-foreground',
22
- feature_ship: 'text-muted-foreground',
23
- sync: 'text-muted-foreground',
24
- }
25
- return colorMap[type] ?? 'text-muted-foreground'
26
- }
27
-
28
- export function getEventBadge(type: string): string {
29
- const badgeMap: Record<string, string> = {
30
- task_complete: 'DONE',
31
- task_start: 'START',
32
- feature_ship: 'SHIP',
33
- sync: 'SYNC',
34
- }
35
- return badgeMap[type] ?? type.toUpperCase()
36
- }
37
-
38
- export function getEventLabel(event: unknown): string {
39
- const e = event as { type?: string; task?: string; name?: string }
40
- const type = e.type ?? ''
41
-
42
- const labelMap: Record<string, string> = {
43
- task_complete: e.task ?? 'Task completed',
44
- task_start: e.task ?? 'Task started',
45
- feature_ship: e.name ?? 'Feature shipped',
46
- sync: 'Project synced',
47
- }
48
- return labelMap[type] ?? type
49
- }
@@ -1,2 +0,0 @@
1
- export { EventRow } from './EventRow'
2
- export type { EventRowProps } from './EventRow.types'
@@ -1,18 +0,0 @@
1
- import { ChevronDown } from 'lucide-react'
2
- import { cn } from '@/lib/utils'
3
- import { Button } from '@/components/ui/button'
4
- import type { ExpandButtonProps } from './ExpandButton.types'
5
-
6
- export function ExpandButton({ expanded, totalCount, collapsedLimit, onToggle }: ExpandButtonProps) {
7
- return (
8
- <Button
9
- variant="ghost"
10
- size="sm"
11
- onClick={onToggle}
12
- className="w-full text-muted-foreground min-h-[44px]"
13
- >
14
- <ChevronDown className={cn('h-4 w-4 mr-1 transition-transform', expanded && 'rotate-180')} />
15
- {expanded ? 'Show less' : `Show ${totalCount - collapsedLimit} more`}
16
- </Button>
17
- )
18
- }
@@ -1,6 +0,0 @@
1
- export interface ExpandButtonProps {
2
- expanded: boolean
3
- totalCount: number
4
- collapsedLimit: number
5
- onToggle: () => void
6
- }
@@ -1,2 +0,0 @@
1
- export { ExpandButton } from './ExpandButton'
2
- export type { ExpandButtonProps } from './ExpandButton.types'
@@ -1,14 +0,0 @@
1
- import { cn } from '@/lib/utils'
2
- import { getHealthGradient } from './HealthGradientBackground.utils'
3
- import type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'
4
-
5
- export function HealthGradientBackground({ score }: HealthGradientBackgroundProps) {
6
- return (
7
- <div
8
- className={cn(
9
- 'absolute inset-0 -m-4 md:-m-8 rounded-2xl opacity-30 blur-3xl transition-colors duration-1000',
10
- getHealthGradient(score)
11
- )}
12
- />
13
- )
14
- }
@@ -1,5 +0,0 @@
1
- export type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
2
-
3
- export interface HealthGradientBackgroundProps {
4
- score: number
5
- }
@@ -1,13 +0,0 @@
1
- import type { AccentColor } from './HealthGradientBackground.types'
2
-
3
- export function getHealthColor(score: number): AccentColor {
4
- if (score >= 70) return 'success'
5
- if (score >= 40) return 'warning'
6
- return 'destructive'
7
- }
8
-
9
- export function getHealthGradient(score: number): string {
10
- if (score >= 70) return 'bg-gradient-to-br from-emerald-500/20 to-transparent'
11
- if (score >= 40) return 'bg-gradient-to-br from-amber-500/20 to-transparent'
12
- return 'bg-gradient-to-br from-destructive/20 to-transparent'
13
- }
@@ -1,2 +0,0 @@
1
- export { HealthGradientBackground } from './HealthGradientBackground'
2
- export type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'