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,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
- }