prjct-cli 0.18.2 → 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 (243) hide show
  1. package/CHANGELOG.md +40 -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/commands/done.md +57 -258
  11. package/templates/commands/now.md +72 -277
  12. package/templates/commands/ship.md +55 -261
  13. package/templates/commands/test.md +328 -21
  14. package/templates/global/CLAUDE.md +40 -205
  15. package/templates/global/docs/agents.md +88 -0
  16. package/templates/global/docs/architecture.md +103 -0
  17. package/templates/global/docs/commands.md +98 -0
  18. package/templates/global/docs/validation.md +95 -0
  19. package/templates/mcp-config.json +36 -0
  20. package/bin/dev.js +0 -216
  21. package/bin/serve.js +0 -361
  22. package/packages/web/README.md +0 -36
  23. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  24. package/packages/web/app/api/claude/status/route.ts +0 -34
  25. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  26. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  27. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  28. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  29. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  30. package/packages/web/app/api/projects/route.ts +0 -16
  31. package/packages/web/app/api/sessions/current/route.ts +0 -132
  32. package/packages/web/app/api/sessions/history/route.ts +0 -204
  33. package/packages/web/app/error.tsx +0 -34
  34. package/packages/web/app/favicon.ico +0 -0
  35. package/packages/web/app/globals.css +0 -198
  36. package/packages/web/app/layout.tsx +0 -53
  37. package/packages/web/app/loading.tsx +0 -7
  38. package/packages/web/app/not-found.tsx +0 -25
  39. package/packages/web/app/page.tsx +0 -12
  40. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  41. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  42. package/packages/web/app/project/[id]/error.tsx +0 -41
  43. package/packages/web/app/project/[id]/loading.tsx +0 -9
  44. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  45. package/packages/web/app/project/[id]/page.tsx +0 -384
  46. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  47. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  48. package/packages/web/app/sessions/page.tsx +0 -165
  49. package/packages/web/app/settings/page.tsx +0 -151
  50. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  51. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  52. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  53. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  54. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  55. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  56. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  57. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  58. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  59. package/packages/web/components/AgentsCard/index.ts +0 -2
  60. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  61. package/packages/web/components/AppSidebar/index.ts +0 -1
  62. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  63. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  64. package/packages/web/components/BackLink/index.ts +0 -2
  65. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  66. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  67. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  68. package/packages/web/components/BentoCard/index.ts +0 -2
  69. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  70. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  71. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  72. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  73. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  74. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  75. package/packages/web/components/BentoGrid/index.ts +0 -2
  76. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  77. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  78. package/packages/web/components/BlockersCard/index.ts +0 -2
  79. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  80. package/packages/web/components/CommandBar/index.ts +0 -1
  81. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  82. package/packages/web/components/CommandButton/index.ts +0 -1
  83. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  84. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  85. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  86. package/packages/web/components/DashboardContent/index.ts +0 -1
  87. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  88. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  89. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  90. package/packages/web/components/DateGroup/index.ts +0 -2
  91. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  92. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  93. package/packages/web/components/EmptyState/index.ts +0 -2
  94. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  95. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  96. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  97. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  98. package/packages/web/components/EventRow/index.ts +0 -2
  99. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  100. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  101. package/packages/web/components/ExpandButton/index.ts +0 -2
  102. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  103. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  104. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  105. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  106. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  107. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  108. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  109. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  110. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  111. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  112. package/packages/web/components/HeroSection/index.ts +0 -2
  113. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  114. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  115. package/packages/web/components/IdeasCard/index.ts +0 -2
  116. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  117. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  118. package/packages/web/components/InsightMessage/index.ts +0 -2
  119. package/packages/web/components/Logo/Logo.tsx +0 -65
  120. package/packages/web/components/Logo/index.ts +0 -1
  121. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  122. package/packages/web/components/MarkdownContent/index.ts +0 -1
  123. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  124. package/packages/web/components/MasonryGrid/index.ts +0 -1
  125. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  126. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  127. package/packages/web/components/MomentumWidget/index.ts +0 -2
  128. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  129. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  130. package/packages/web/components/NowCard/index.ts +0 -2
  131. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  132. package/packages/web/components/PageHeader/index.ts +0 -1
  133. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  134. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  135. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  136. package/packages/web/components/ProgressRing/index.ts +0 -2
  137. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  138. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  139. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  140. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  141. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  142. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  143. package/packages/web/components/Providers/Providers.tsx +0 -48
  144. package/packages/web/components/Providers/index.ts +0 -1
  145. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  146. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  147. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  148. package/packages/web/components/QueueCard/index.ts +0 -2
  149. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  150. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  151. package/packages/web/components/RecoverCard/index.ts +0 -2
  152. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  153. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  154. package/packages/web/components/RoadmapCard/index.ts +0 -2
  155. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  156. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  157. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  158. package/packages/web/components/ShipsCard/index.ts +0 -2
  159. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  160. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  161. package/packages/web/components/SparklineChart/index.ts +0 -2
  162. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  163. package/packages/web/components/StatsMasonry/index.ts +0 -1
  164. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  165. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  166. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  167. package/packages/web/components/StreakCard/index.ts +0 -2
  168. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  169. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  170. package/packages/web/components/TasksCounter/index.ts +0 -2
  171. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  172. package/packages/web/components/TechStackBadges/index.ts +0 -1
  173. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  174. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  175. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  176. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  177. package/packages/web/components/TerminalDock/index.ts +0 -2
  178. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  179. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  180. package/packages/web/components/TerminalTabs/index.ts +0 -1
  181. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  182. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  183. package/packages/web/components/VelocityBadge/index.ts +0 -2
  184. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  185. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  186. package/packages/web/components/VelocityCard/index.ts +0 -2
  187. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  188. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  189. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  190. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  191. package/packages/web/components/WeeklyReports/index.ts +0 -4
  192. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  193. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  194. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  195. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  196. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  197. package/packages/web/components/ui/badge.tsx +0 -46
  198. package/packages/web/components/ui/button.tsx +0 -60
  199. package/packages/web/components/ui/card.tsx +0 -92
  200. package/packages/web/components/ui/chart.tsx +0 -385
  201. package/packages/web/components/ui/dialog.tsx +0 -143
  202. package/packages/web/components/ui/drawer.tsx +0 -135
  203. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  204. package/packages/web/components/ui/input.tsx +0 -21
  205. package/packages/web/components/ui/scroll-area.tsx +0 -58
  206. package/packages/web/components/ui/select.tsx +0 -187
  207. package/packages/web/components/ui/sheet.tsx +0 -139
  208. package/packages/web/components/ui/tabs.tsx +0 -66
  209. package/packages/web/components/ui/tooltip.tsx +0 -61
  210. package/packages/web/components.json +0 -22
  211. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  212. package/packages/web/context/TerminalContext.tsx +0 -45
  213. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  214. package/packages/web/eslint.config.mjs +0 -18
  215. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  216. package/packages/web/hooks/useProjectStats.ts +0 -93
  217. package/packages/web/hooks/useProjects.ts +0 -73
  218. package/packages/web/lib/actions/projects.ts +0 -15
  219. package/packages/web/lib/commands.ts +0 -81
  220. package/packages/web/lib/format.ts +0 -23
  221. package/packages/web/lib/generate-week-report.ts +0 -285
  222. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  223. package/packages/web/lib/project-colors.ts +0 -58
  224. package/packages/web/lib/projects.ts +0 -506
  225. package/packages/web/lib/pty.ts +0 -101
  226. package/packages/web/lib/query-config.ts +0 -44
  227. package/packages/web/lib/services/index.ts +0 -9
  228. package/packages/web/lib/services/projects.server.ts +0 -66
  229. package/packages/web/lib/services/stats.server.ts +0 -562
  230. package/packages/web/lib/unified-loader.ts +0 -396
  231. package/packages/web/lib/utils.ts +0 -6
  232. package/packages/web/next-env.d.ts +0 -6
  233. package/packages/web/next.config.ts +0 -7
  234. package/packages/web/package.json +0 -57
  235. package/packages/web/postcss.config.mjs +0 -7
  236. package/packages/web/public/file.svg +0 -1
  237. package/packages/web/public/globe.svg +0 -1
  238. package/packages/web/public/next.svg +0 -1
  239. package/packages/web/public/vercel.svg +0 -1
  240. package/packages/web/public/window.svg +0 -1
  241. package/packages/web/server.ts +0 -312
  242. package/packages/web/tsconfig.json +0 -34
  243. package/templates/commands/serve.md +0 -121
@@ -1,384 +0,0 @@
1
- import { notFound } from 'next/navigation'
2
- import type { Metadata } from 'next'
3
- import {
4
- getStats,
5
- getInsightMessage,
6
- calculateStreak,
7
- getVelocityChange,
8
- getWeeklyVelocityData,
9
- type StatsResult
10
- } from '@/lib/services/stats.server'
11
- import { getProject } from '@/lib/services/projects.server'
12
- import { getProjectEmoji } from '@/lib/project-colors'
13
- import type { TimelineEvent } from '@/lib/parse-prjct-files'
14
-
15
- import { HeroSection } from '@/components/HeroSection'
16
- import { StatsMasonry } from '@/components/StatsMasonry'
17
-
18
- // Dynamic metadata for browser tab title
19
- export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
20
- const { id: projectId } = await params
21
- const project = await getProject(projectId)
22
- const projectName = project?.name ?? projectId
23
- const emoji = getProjectEmoji(projectId)
24
-
25
- return {
26
- title: `${emoji} ${projectName} / p.`,
27
- }
28
- }
29
-
30
- // Types for normalized component data
31
- interface NormalizedCurrentTask {
32
- task: string
33
- startedAt?: string
34
- agent?: string
35
- estimatedDuration?: string
36
- pausedAt?: string
37
- pauseReason?: string
38
- }
39
-
40
- interface NormalizedQueueItem {
41
- task: string
42
- priority?: 'low' | 'medium' | 'high' | 'critical' | number
43
- estimatedDuration?: string
44
- }
45
-
46
- interface NormalizedShip {
47
- name: string
48
- date: string
49
- duration?: string
50
- }
51
-
52
- interface NormalizedIdea {
53
- title: string
54
- impact?: string
55
- }
56
-
57
- interface NormalizedAgent {
58
- name: string
59
- description?: string
60
- successRate?: number
61
- tasksCompleted?: number
62
- bestFor?: string[]
63
- }
64
-
65
- interface NormalizedRoadmap {
66
- phases: Array<{
67
- name: string
68
- progress: number
69
- features?: Array<{ name: string; status: string }>
70
- }>
71
- progress: number
72
- }
73
-
74
- // Data normalization functions
75
- function normalizeCurrentTask(stats: StatsResult): NormalizedCurrentTask | null {
76
- if (stats.state?.currentTask) {
77
- return {
78
- task: stats.state.currentTask.description,
79
- startedAt: stats.state.currentTask.startedAt,
80
- // Simplified - removed legacy fields not in new schema
81
- }
82
- }
83
- return stats.legacyStats?.currentTask ?? null
84
- }
85
-
86
- function normalizeQueue(stats: StatsResult): NormalizedQueueItem[] {
87
- if (stats.queue?.tasks) {
88
- return stats.queue.tasks
89
- .filter(t => !t.completed)
90
- .map(q => ({
91
- task: q.description,
92
- priority: q.priority,
93
- }))
94
- }
95
- return stats.legacyStats?.queue ?? []
96
- }
97
-
98
- function normalizeRoadmap(stats: StatsResult): NormalizedRoadmap | null {
99
- const features = stats.roadmap?.features ?? []
100
- if (features.length > 0) {
101
- const completed = features.filter(f =>
102
- f.status === 'shipped' || f.status === 'completed'
103
- ).length
104
-
105
- return {
106
- phases: features.map(f => ({
107
- name: f.name,
108
- progress: f.status === 'shipped' || f.status === 'completed' ? 100 :
109
- f.status === 'active' ? 50 : 0,
110
- features: f.tasks.map(t => ({
111
- name: t.description,
112
- status: t.completed ? 'completed' : 'pending'
113
- }))
114
- })),
115
- progress: Math.round((completed / features.length) * 100)
116
- }
117
- }
118
- return stats.legacyStats?.roadmap ?? null
119
- }
120
-
121
- function normalizeShipped(stats: StatsResult): NormalizedShip[] {
122
- // Try new format first
123
- const items = stats.shipped?.items ?? []
124
- if (items.length > 0) {
125
- return items.map(s => ({
126
- name: s.name,
127
- date: s.shippedAt || s.date || new Date().toISOString(),
128
- }))
129
- }
130
- // Fallback to legacy
131
- return (stats.legacyStats?.shipped ?? []).map(s => ({
132
- name: s.name,
133
- date: s.date,
134
- duration: s.time,
135
- }))
136
- }
137
-
138
- function normalizeIdeas(stats: StatsResult): NormalizedIdea[] {
139
- // Try new format first
140
- const ideas = stats.ideas?.ideas ?? []
141
- if (ideas.length > 0) {
142
- return ideas
143
- .filter(i => i.status === 'pending')
144
- .map(i => ({
145
- title: i.text,
146
- impact: i.priority?.toUpperCase() || 'MEDIUM'
147
- }))
148
- }
149
- // Fallback to legacy
150
- return (stats.legacyStats?.ideas?.pending ?? []).map(i => ({
151
- title: i.title,
152
- impact: i.impact?.toUpperCase() || 'MEDIUM'
153
- }))
154
- }
155
-
156
- function normalizeAgents(stats: StatsResult): NormalizedAgent[] {
157
- // Try new format first
158
- if (stats.agents.length > 0) {
159
- return stats.agents.map(a => ({
160
- name: a.name,
161
- description: a.description,
162
- successRate: a.successRate,
163
- tasksCompleted: a.tasksCompleted,
164
- bestFor: a.bestFor,
165
- }))
166
- }
167
- // Fallback to legacy
168
- return (stats.legacyStats?.agents ?? []).map(a => ({
169
- name: a.name,
170
- description: a.role,
171
- bestFor: a.whenToUse,
172
- }))
173
- }
174
-
175
- function normalizeTimeline(stats: StatsResult): TimelineEvent[] {
176
- if (stats.metrics?.recentActivity?.length) {
177
- return stats.metrics.recentActivity.map(a => ({
178
- ts: a.timestamp,
179
- type: a.action || a.type || 'task_completed',
180
- task: a.description || '',
181
- }))
182
- }
183
- return stats.legacyStats?.timeline ?? []
184
- }
185
-
186
- function getVelocity(stats: StatsResult): number {
187
- if (stats.metrics?.velocity?.tasksPerDay) {
188
- return stats.metrics.velocity.tasksPerDay
189
- }
190
- return stats.legacyStats?.metrics?.velocity?.tasksPerDay ?? 0
191
- }
192
-
193
- function getTotalShips(stats: StatsResult): number {
194
- // Use shipped.md items count (legacyStats.shipped) as source of truth
195
- return stats.shipped?.items?.length ?? stats.legacyStats?.shipped?.length ?? 0
196
- }
197
-
198
- function getCompletionRate(stats: StatsResult): number {
199
- const completed = stats.legacyStats?.shipped?.length ?? 0
200
- const pending = stats.legacyStats?.queue?.length ?? 0
201
- const total = completed + pending
202
- if (total === 0) return 0
203
- return Math.round((completed / total) * 100)
204
- }
205
-
206
- // Calculate streak from legacy timeline
207
- function calculateStreakFromTimeline(stats: StatsResult): number {
208
- const timeline = stats.legacyStats?.timeline ?? []
209
- if (timeline.length === 0) return 0
210
-
211
- const activityDates = new Set(
212
- timeline.map(e => e.ts?.split('T')[0]).filter(Boolean)
213
- )
214
-
215
- const today = new Date()
216
- today.setHours(0, 0, 0, 0)
217
-
218
- const days = Array.from({ length: 30 }, (_, i) => {
219
- const date = new Date(today)
220
- date.setDate(date.getDate() - i)
221
- return date.toISOString().split('T')[0]
222
- })
223
-
224
- const firstGapIndex = days.findIndex(date => !activityDates.has(date))
225
-
226
- if (firstGapIndex === 0) {
227
- const yesterdayHasActivity = activityDates.has(days[1])
228
- if (!yesterdayHasActivity) return 0
229
- const remainingDays = days.slice(1)
230
- const gapFromYesterday = remainingDays.findIndex(date => !activityDates.has(date))
231
- return gapFromYesterday === -1 ? remainingDays.length : gapFromYesterday
232
- }
233
-
234
- return firstGapIndex === -1 ? days.length : firstGapIndex
235
- }
236
-
237
- // Get weekly velocity data from legacy timeline
238
- function getWeeklyDataFromTimeline(stats: StatsResult): number[] {
239
- const timeline = stats.legacyStats?.timeline ?? []
240
- if (timeline.length === 0) return []
241
-
242
- const today = new Date()
243
-
244
- return Array.from({ length: 7 }, (_, i) => {
245
- const date = new Date(today)
246
- date.setDate(date.getDate() - (6 - i))
247
- const dateStr = date.toISOString().split('T')[0]
248
-
249
- return timeline.filter(e => e.ts?.startsWith(dateStr)).length
250
- })
251
- }
252
-
253
- interface PageProps {
254
- params: Promise<{ id: string }>
255
- }
256
-
257
- export default async function ProjectStatsPage({ params }: PageProps) {
258
- const { id: projectId } = await params
259
-
260
- // Fetch data directly on server - no API calls
261
- const [project, stats] = await Promise.all([
262
- getProject(projectId),
263
- getStats(projectId)
264
- ])
265
-
266
- if (!stats.hasData) {
267
- notFound()
268
- }
269
-
270
- // Compute derived values using service functions
271
- // Use legacy timeline for streak/weekly if metrics is empty
272
- const streak = stats.metrics?.recentActivity?.length
273
- ? calculateStreak(stats.metrics)
274
- : calculateStreakFromTimeline(stats)
275
- const velocity = getVelocity(stats)
276
- const velocityChange = getVelocityChange(velocity)
277
- const insightMessage = getInsightMessage(stats, streak)
278
- const weeklyVelocityData = stats.metrics?.recentActivity?.length
279
- ? getWeeklyVelocityData(stats.metrics)
280
- : getWeeklyDataFromTimeline(stats)
281
-
282
- // Normalize data for components
283
- const currentTask = normalizeCurrentTask(stats)
284
- const queue = normalizeQueue(stats)
285
- const roadmap = normalizeRoadmap(stats)
286
- const shipped = normalizeShipped(stats)
287
- const ideas = normalizeIdeas(stats)
288
- const agents = normalizeAgents(stats)
289
- const timeline = normalizeTimeline(stats)
290
-
291
- // DRY: Use counts from getProject() - same source as dashboard
292
- const totalShips = project?.shippedCount ?? 0
293
- const completionRate = project?.completionRate ?? 0
294
-
295
- // Extract insights
296
- const { estimateAccuracy, blockers } = stats.insights
297
-
298
- return (
299
- <div className="flex h-full flex-col p-4 md:p-6 overflow-auto overflow-x-hidden">
300
- {/* Mobile: Add padding for hamburger menu */}
301
- <div className="pl-10 md:pl-0">
302
- <HeroSection
303
- projectId={projectId}
304
- projectName={project?.name ?? projectId}
305
- projectVersion={project?.version}
306
- totalShips={totalShips}
307
- completionRate={completionRate}
308
- streak={streak}
309
- insightMessage={insightMessage}
310
- timeline={timeline}
311
- />
312
- </div>
313
-
314
- {/* Quick Stats Bar */}
315
- <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 sm:gap-3 mt-4 mb-6">
316
- <div className="bg-card border rounded-lg p-2 sm:p-3 flex items-center gap-2 sm:gap-3">
317
- <div className="h-8 w-8 sm:h-10 sm:w-10 rounded-lg bg-muted flex items-center justify-center shrink-0">
318
- <svg className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
319
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 3l14 9-14 9V3z" />
320
- </svg>
321
- </div>
322
- <div className="min-w-0">
323
- <div className="text-xl sm:text-2xl font-bold tabular-nums">{totalShips}</div>
324
- <div className="text-xs text-muted-foreground truncate">Shipped</div>
325
- </div>
326
- </div>
327
- <div className="bg-card border rounded-lg p-2 sm:p-3 flex items-center gap-2 sm:gap-3">
328
- <div className="h-8 w-8 sm:h-10 sm:w-10 rounded-lg bg-muted flex items-center justify-center shrink-0">
329
- <svg className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
330
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
331
- </svg>
332
- </div>
333
- <div className="min-w-0">
334
- <div className="text-xl sm:text-2xl font-bold tabular-nums">{queue.length}</div>
335
- <div className="text-xs text-muted-foreground truncate">Queue</div>
336
- </div>
337
- </div>
338
- <div className="bg-card border rounded-lg p-2 sm:p-3 flex items-center gap-2 sm:gap-3">
339
- <div className="h-8 w-8 sm:h-10 sm:w-10 rounded-lg bg-muted flex items-center justify-center shrink-0">
340
- <svg className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
341
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z" />
342
- </svg>
343
- </div>
344
- <div className="min-w-0">
345
- <div className="text-xl sm:text-2xl font-bold tabular-nums">{streak}</div>
346
- <div className="text-xs text-muted-foreground truncate">Streak</div>
347
- </div>
348
- </div>
349
- <div className="bg-card border rounded-lg p-2 sm:p-3 flex items-center gap-2 sm:gap-3">
350
- <div className="h-8 w-8 sm:h-10 sm:w-10 rounded-lg bg-muted flex items-center justify-center shrink-0">
351
- <svg className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor">
352
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
353
- </svg>
354
- </div>
355
- <div className="min-w-0">
356
- <div className="text-xl sm:text-2xl font-bold tabular-nums">{ideas.length}</div>
357
- <div className="text-xs text-muted-foreground truncate">Ideas</div>
358
- </div>
359
- </div>
360
- </div>
361
-
362
- {/* Main Content - Masonry Layout */}
363
- <StatsMasonry
364
- projectId={projectId}
365
- currentTask={currentTask}
366
- velocity={velocity}
367
- weeklyVelocityData={weeklyVelocityData}
368
- velocityChange={velocityChange}
369
- estimateAccuracy={estimateAccuracy}
370
- roadmap={roadmap}
371
- queue={queue}
372
- shipped={shipped}
373
- totalShips={totalShips}
374
- streak={streak}
375
- blockers={blockers}
376
- ideas={ideas}
377
- agents={agents}
378
- timeline={timeline}
379
- />
380
-
381
- <div className="h-4" />
382
- </div>
383
- )
384
- }
@@ -1,59 +0,0 @@
1
- import { notFound } from 'next/navigation'
2
- import Link from 'next/link'
3
- import type { Metadata } from 'next'
4
- import { ArrowLeft } from 'lucide-react'
5
- import { getStats } from '@/lib/services/stats.server'
6
- import { getProject } from '@/lib/services/projects.server'
7
- import { getProjectEmoji } from '@/lib/project-colors'
8
- import { WeeklyReports } from '@/components/WeeklyReports'
9
-
10
- export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
11
- const { id: projectId } = await params
12
- const project = await getProject(projectId)
13
- const projectName = project?.name ?? projectId
14
- const emoji = getProjectEmoji(projectId)
15
-
16
- return {
17
- title: `${emoji} ${projectName} / Reports / p.`,
18
- }
19
- }
20
-
21
- interface PageProps {
22
- params: Promise<{ id: string }>
23
- }
24
-
25
- export default async function ReportsPage({ params }: PageProps) {
26
- const { id: projectId } = await params
27
-
28
- const [project, stats] = await Promise.all([
29
- getProject(projectId),
30
- getStats(projectId)
31
- ])
32
-
33
- if (!stats.hasData) {
34
- notFound()
35
- }
36
-
37
- const projectName = project?.name ?? projectId
38
-
39
- return (
40
- <div className="flex h-full flex-col p-4 md:p-6 overflow-auto">
41
- {/* Header with back navigation */}
42
- <div className="pl-10 md:pl-0 mb-6">
43
- <Link
44
- href={`/project/${projectId}`}
45
- className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors mb-4"
46
- >
47
- <ArrowLeft className="h-4 w-4" />
48
- Back to {projectName}
49
- </Link>
50
- <h1 className="text-3xl font-bold">Weekly Reports</h1>
51
- </div>
52
-
53
- <div className="pl-10 md:pl-0">
54
- <WeeklyReports stats={stats} projectName={projectName} />
55
- </div>
56
- <div className="h-4" />
57
- </div>
58
- )
59
- }
@@ -1,58 +0,0 @@
1
- import type { Metadata } from 'next'
2
- import { getStats } from '@/lib/services/stats.server'
3
- import { getProject } from '@/lib/projects'
4
- import { getProjectEmoji } from '@/lib/project-colors'
5
- import { PrintableReport } from '@/components/WeeklyReports/PrintableReport'
6
-
7
- export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
8
- const { id: projectId } = await params
9
- const project = await getProject(projectId)
10
- const projectName = project?.name ?? projectId
11
- const emoji = getProjectEmoji(projectId)
12
-
13
- return {
14
- title: `${emoji} ${projectName} / Print / p.`,
15
- }
16
- }
17
-
18
- interface Props {
19
- params: Promise<{ id: string }>
20
- searchParams: Promise<{ weeks?: string; year?: string }>
21
- }
22
-
23
- export default async function PrintReportPage({ params, searchParams }: Props) {
24
- const { id } = await params
25
- const { weeks, year } = await searchParams
26
-
27
- const project = await getProject(id)
28
- if (!project) {
29
- return <div className="p-8 text-center">Project not found</div>
30
- }
31
-
32
- const stats = await getStats(id)
33
-
34
- // Parse weeks from query params (comma-separated)
35
- const selectedWeeks = weeks
36
- ? weeks.split(',').map(Number).filter(n => !isNaN(n))
37
- : [getCurrentWeek()]
38
-
39
- const selectedYear = year ? parseInt(year) : new Date().getFullYear()
40
-
41
- return (
42
- <PrintableReport
43
- stats={stats}
44
- projectName={project.name}
45
- selectedWeeks={selectedWeeks}
46
- year={selectedYear}
47
- />
48
- )
49
- }
50
-
51
- function getCurrentWeek(): number {
52
- const now = new Date()
53
- const d = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()))
54
- const dayNum = d.getUTCDay() || 7
55
- d.setUTCDate(d.getUTCDate() + 4 - dayNum)
56
- const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1))
57
- return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7)
58
- }
@@ -1,165 +0,0 @@
1
- 'use client'
2
-
3
- import { useQuery } from '@tanstack/react-query'
4
- import { Clock, Target, CheckCircle, PauseCircle, GitCommit, FileCode } from 'lucide-react'
5
-
6
- function formatDuration(ms: number): string {
7
- const hours = Math.floor(ms / 3600000)
8
- const minutes = Math.floor((ms % 3600000) / 60000)
9
- if (hours > 0) return `${hours}h ${minutes}m`
10
- return `${minutes}m`
11
- }
12
-
13
- function getRelativeTime(dateString: string): string {
14
- const date = new Date(dateString)
15
- const now = new Date()
16
- const diff = now.getTime() - date.getTime()
17
- const minutes = Math.floor(diff / 60000)
18
- const hours = Math.floor(diff / 3600000)
19
- const days = Math.floor(diff / 86400000)
20
-
21
- if (minutes < 1) return 'just now'
22
- if (minutes < 60) return `${minutes}m ago`
23
- if (hours < 24) return `${hours}h ago`
24
- return `${days}d ago`
25
- }
26
-
27
- export default function Sessions() {
28
- const { data: projects } = useQuery({
29
- queryKey: ['projects'],
30
- queryFn: async () => {
31
- const res = await fetch('/api/projects')
32
- const json = await res.json()
33
- return json.data || []
34
- }
35
- })
36
-
37
- // Get sessions from first project (for demo)
38
- const projectId = projects?.[0]?.id
39
-
40
- const { data: sessions, isLoading } = useQuery({
41
- queryKey: ['sessions', projectId],
42
- queryFn: async () => {
43
- if (!projectId) return []
44
- const res = await fetch(`/api/sessions?projectId=${projectId}`)
45
- const json = await res.json()
46
- return json.data || []
47
- },
48
- enabled: !!projectId
49
- })
50
-
51
- if (isLoading) {
52
- return (
53
- <div className="flex items-center justify-center h-full">
54
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
55
- </div>
56
- )
57
- }
58
-
59
- return (
60
- <div className="p-6 h-full overflow-auto">
61
- <header className="mb-6">
62
- <h1 className="text-2xl font-bold flex items-center gap-2">
63
- <Clock className="w-6 h-6" />
64
- Sessions
65
- </h1>
66
- <p className="text-muted-foreground mt-1">
67
- Your work session history
68
- </p>
69
- </header>
70
-
71
- {!sessions?.length ? (
72
- <div className="border border-dashed border-border rounded-lg p-8 text-center">
73
- <Clock className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
74
- <p className="text-muted-foreground">
75
- No sessions yet. Start one with <code className="bg-muted px-2 py-1 rounded">/p:now</code>
76
- </p>
77
- </div>
78
- ) : (
79
- <div className="space-y-4">
80
- {sessions.map((session: Session) => (
81
- <SessionCard key={session.id} session={session} />
82
- ))}
83
- </div>
84
- )}
85
- </div>
86
- )
87
- }
88
-
89
- interface Session {
90
- id: string
91
- task: string
92
- status: 'active' | 'paused' | 'completed'
93
- startedAt: string
94
- completedAt?: string
95
- duration: number
96
- metrics: {
97
- filesChanged: number
98
- linesAdded: number
99
- linesRemoved: number
100
- commits: number
101
- }
102
- }
103
-
104
- function SessionCard({ session }: { session: Session }) {
105
- const statusConfig = {
106
- active: {
107
- icon: Target,
108
- color: 'text-green-500',
109
- bg: 'bg-green-500/10',
110
- label: 'Active'
111
- },
112
- paused: {
113
- icon: PauseCircle,
114
- color: 'text-yellow-500',
115
- bg: 'bg-yellow-500/10',
116
- label: 'Paused'
117
- },
118
- completed: {
119
- icon: CheckCircle,
120
- color: 'text-blue-500',
121
- bg: 'bg-blue-500/10',
122
- label: 'Completed'
123
- }
124
- }
125
-
126
- const status = statusConfig[session.status]
127
- const StatusIcon = status.icon
128
-
129
- return (
130
- <div className="bg-card border border-border rounded-lg p-4">
131
- <div className="flex items-start justify-between mb-3">
132
- <div className="flex-1">
133
- <h3 className="font-medium mb-1">{session.task}</h3>
134
- <div className="flex items-center gap-3 text-sm text-muted-foreground">
135
- <span>{getRelativeTime(session.startedAt)}</span>
136
- <span>•</span>
137
- <span>{formatDuration(session.duration)}</span>
138
- </div>
139
- </div>
140
-
141
- <div className={`flex items-center gap-1.5 px-2 py-1 rounded-md text-xs font-medium ${status.bg} ${status.color}`}>
142
- <StatusIcon className="w-3 h-3" />
143
- {status.label}
144
- </div>
145
- </div>
146
-
147
- <div className="flex items-center gap-4 text-sm text-muted-foreground">
148
- <span className="flex items-center gap-1">
149
- <FileCode className="w-3.5 h-3.5" />
150
- {session.metrics.filesChanged} files
151
- </span>
152
- <span className="flex items-center gap-1 text-green-500">
153
- +{session.metrics.linesAdded}
154
- </span>
155
- <span className="flex items-center gap-1 text-red-500">
156
- -{session.metrics.linesRemoved}
157
- </span>
158
- <span className="flex items-center gap-1">
159
- <GitCommit className="w-3.5 h-3.5" />
160
- {session.metrics.commits} commits
161
- </span>
162
- </div>
163
- </div>
164
- )
165
- }