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,93 +0,0 @@
1
- 'use client'
2
-
3
- import { useQuery } from '@tanstack/react-query'
4
- import type { ProjectStats, RawProjectFiles } from '@/lib/parse-prjct-files'
5
- import type {
6
- UnifiedApiResponse,
7
- ProjectState,
8
- OutcomeSummary,
9
- AgentPerformance,
10
- ProjectInsights,
11
- } from '@prjct/shared'
12
-
13
- interface ProjectStatsResponse {
14
- success: boolean
15
- data?: ProjectStats
16
- raw?: RawProjectFiles
17
- error?: string
18
- }
19
-
20
- interface ProjectStatsData {
21
- stats: ProjectStats
22
- raw: RawProjectFiles
23
- }
24
-
25
- // Legacy fetch
26
- async function fetchProjectStats(projectId: string): Promise<ProjectStatsData> {
27
- const res = await fetch(`/api/projects/${projectId}/stats`, {
28
- cache: 'no-store',
29
- })
30
- if (!res.ok) throw new Error('Failed to fetch project stats')
31
- const json: ProjectStatsResponse = await res.json()
32
- if (!json.success || !json.data || !json.raw) {
33
- throw new Error(json.error || 'Failed to fetch project stats')
34
- }
35
- return { stats: json.data, raw: json.raw }
36
- }
37
-
38
- export function useProjectStats(projectId: string) {
39
- return useQuery({
40
- queryKey: ['project-stats', projectId],
41
- queryFn: () => fetchProjectStats(projectId),
42
- staleTime: 30_000, // Cache 30s
43
- refetchOnWindowFocus: true,
44
- enabled: !!projectId,
45
- })
46
- }
47
-
48
- // ============== Unified API ==============
49
-
50
- interface UnifiedProjectData {
51
- state: ProjectState | null
52
- outcomes: OutcomeSummary | null
53
- agentPerformance: AgentPerformance[]
54
- insights: ProjectInsights
55
- legacyFallback: boolean
56
- // Legacy data when fallback
57
- legacyData?: ProjectStats
58
- legacyRaw?: RawProjectFiles
59
- }
60
-
61
- async function fetchUnifiedProjectData(projectId: string): Promise<UnifiedProjectData> {
62
- const res = await fetch(`/api/v2/projects/${projectId}/unified`, {
63
- cache: 'no-store',
64
- })
65
- if (!res.ok) throw new Error('Failed to fetch unified project data')
66
- const json: UnifiedApiResponse = await res.json()
67
- if (!json.success) {
68
- throw new Error('Failed to fetch unified project data')
69
- }
70
- return {
71
- state: json.state,
72
- outcomes: json.outcomes,
73
- agentPerformance: json.agentPerformance,
74
- insights: json.insights,
75
- legacyFallback: json.legacyFallback,
76
- legacyData: json.legacyData as ProjectStats | undefined,
77
- legacyRaw: json.legacyRaw as RawProjectFiles | undefined,
78
- }
79
- }
80
-
81
- /**
82
- * Hook for fetching unified project data from v2 API.
83
- * Falls back to legacy data if unified state doesn't exist.
84
- */
85
- export function useUnifiedProjectStats(projectId: string) {
86
- return useQuery({
87
- queryKey: ['project-unified', projectId],
88
- queryFn: () => fetchUnifiedProjectData(projectId),
89
- staleTime: 30_000, // Cache 30s
90
- refetchOnWindowFocus: true,
91
- enabled: !!projectId,
92
- })
93
- }
@@ -1,73 +0,0 @@
1
- 'use client'
2
-
3
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
- import { queryPresets } from '@/lib/query-config'
5
- import { deleteProject as deleteProjectAction } from '@/lib/actions/projects'
6
-
7
- export interface Project {
8
- id: string
9
- name: string
10
- path: string
11
- repoPath?: string | null
12
- currentTask?: string | null
13
- hasActiveSession?: boolean
14
- lastActivity?: string | null
15
- ideasCount?: number
16
- nextTasksCount?: number
17
- techStack?: string[]
18
- iconPath?: string | null
19
- version?: string
20
- stack?: string
21
- filesCount?: number
22
- commitsCount?: number
23
- }
24
-
25
- // Fetch with no-cache headers for fresh data
26
- async function fetchJson<T>(url: string): Promise<T> {
27
- const res = await fetch(url, {
28
- cache: 'no-store',
29
- headers: {
30
- 'Cache-Control': 'no-cache',
31
- },
32
- })
33
- if (!res.ok) throw new Error(`Failed to fetch ${url}`)
34
- const json = await res.json()
35
- return json.data ?? json
36
- }
37
-
38
- // Hook for fetching all projects
39
- export function useProjects() {
40
- return useQuery({
41
- queryKey: ['projects'],
42
- queryFn: () => fetchJson<Project[]>('/api/projects'),
43
- ...queryPresets.normal,
44
- })
45
- }
46
-
47
- // Hook for fetching a single project
48
- export function useProject(projectId: string | null) {
49
- return useQuery({
50
- queryKey: ['project', projectId],
51
- queryFn: () => fetchJson<Project>(`/api/projects/${projectId}`),
52
- enabled: !!projectId,
53
- ...queryPresets.fast,
54
- })
55
- }
56
-
57
- // Hook for deleting a project (uses Server Action)
58
- export function useDeleteProject() {
59
- const queryClient = useQueryClient()
60
-
61
- return useMutation({
62
- mutationFn: async (projectId: string) => {
63
- const result = await deleteProjectAction(projectId)
64
- if (!result.success) {
65
- throw new Error(result.error || 'Failed to delete project')
66
- }
67
- return result
68
- },
69
- onSuccess: () => {
70
- queryClient.invalidateQueries({ queryKey: ['projects'] })
71
- },
72
- })
73
- }
@@ -1,15 +0,0 @@
1
- 'use server'
2
-
3
- import { revalidatePath } from 'next/cache'
4
- import { moveToTrash } from '@/lib/projects'
5
-
6
- export async function deleteProject(projectId: string) {
7
- try {
8
- const result = await moveToTrash(projectId)
9
- revalidatePath('/')
10
- return { success: true, ...result }
11
- } catch (error) {
12
- const message = error instanceof Error ? error.message : 'Failed to delete project'
13
- return { success: false, error: message }
14
- }
15
- }
@@ -1,81 +0,0 @@
1
- import {
2
- Play,
3
- Target,
4
- Lightbulb,
5
- ListTodo,
6
- Rocket,
7
- Sparkles,
8
- CheckCircle2,
9
- Pause,
10
- BarChart3,
11
- TrendingUp,
12
- Activity,
13
- History,
14
- Undo2,
15
- Redo2,
16
- RefreshCw,
17
- type LucideIcon,
18
- } from 'lucide-react'
19
-
20
- export interface WorkflowCommand {
21
- cmd: string
22
- icon: LucideIcon
23
- tip: string
24
- group: CommandGroup
25
- }
26
-
27
- export type CommandGroup = 'work' | 'session' | 'plan' | 'ship' | 'status' | 'recovery'
28
-
29
- // Commands ordered by real developer workflow
30
- export const WORKFLOW_COMMANDS: readonly WorkflowCommand[] = [
31
- { cmd: 'p. now', icon: Target, tip: 'Set task', group: 'work' },
32
- { cmd: 'p. done', icon: CheckCircle2, tip: 'Complete', group: 'work' },
33
- { cmd: 'p. pause', icon: Pause, tip: 'Pause', group: 'session' },
34
- { cmd: 'p. resume', icon: Play, tip: 'Resume', group: 'session' },
35
- { cmd: 'p. feature', icon: Sparkles, tip: 'Feature', group: 'plan' },
36
- { cmd: 'p. idea', icon: Lightbulb, tip: 'Idea', group: 'plan' },
37
- { cmd: 'p. next', icon: ListTodo, tip: 'Queue', group: 'plan' },
38
- { cmd: 'p. ship', icon: Rocket, tip: 'Ship', group: 'ship' },
39
- { cmd: 'p. recap', icon: BarChart3, tip: 'Recap', group: 'status' },
40
- { cmd: 'p. progress', icon: TrendingUp, tip: 'Progress', group: 'status' },
41
- { cmd: 'p. status', icon: Activity, tip: 'Status', group: 'status' },
42
- { cmd: 'p. history', icon: History, tip: 'History', group: 'status' },
43
- { cmd: 'p. undo', icon: Undo2, tip: 'Undo', group: 'recovery' },
44
- { cmd: 'p. redo', icon: Redo2, tip: 'Redo', group: 'recovery' },
45
- ] as const
46
-
47
- export const COMMAND_GROUPS: readonly CommandGroup[] = ['work', 'session', 'plan', 'ship', 'status', 'recovery']
48
-
49
- // Sync command - always first/prominent
50
- export const SYNC_COMMAND: WorkflowCommand = {
51
- cmd: 'p. sync',
52
- icon: RefreshCw,
53
- tip: 'Sync',
54
- group: 'status',
55
- }
56
-
57
- // Get commands by group
58
- export function getCommandsByGroup(group: CommandGroup): WorkflowCommand[] {
59
- return WORKFLOW_COMMANDS.filter(c => c.group === group)
60
- }
61
-
62
- // Project colors for tabs (based on project ID hash)
63
- export const PROJECT_COLORS = [
64
- 'bg-orange-500/20 border-orange-500/50 text-orange-400',
65
- 'bg-blue-500/20 border-blue-500/50 text-blue-400',
66
- 'bg-green-500/20 border-green-500/50 text-green-400',
67
- 'bg-purple-500/20 border-purple-500/50 text-purple-400',
68
- 'bg-pink-500/20 border-pink-500/50 text-pink-400',
69
- 'bg-cyan-500/20 border-cyan-500/50 text-cyan-400',
70
- 'bg-yellow-500/20 border-yellow-500/50 text-yellow-400',
71
- 'bg-red-500/20 border-red-500/50 text-red-400',
72
- ]
73
-
74
- export function getProjectColor(projectId: string): string {
75
- let hash = 0
76
- for (let i = 0; i < projectId.length; i++) {
77
- hash = ((hash << 5) - hash) + projectId.charCodeAt(i)
78
- hash = hash & hash
79
- }
80
- return PROJECT_COLORS[Math.abs(hash) % PROJECT_COLORS.length]
81
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Shared formatting utilities
3
- */
4
-
5
- export function formatRelativeTime(dateStr: string | null | undefined): string {
6
- if (!dateStr) return ''
7
- const date = new Date(dateStr)
8
- const now = new Date()
9
- const diffMs = now.getTime() - date.getTime()
10
- const diffMins = Math.floor(diffMs / 60000)
11
- const diffHours = Math.floor(diffMs / 3600000)
12
- const diffDays = Math.floor(diffMs / 86400000)
13
-
14
- if (diffMins < 1) return 'just now'
15
- if (diffMins < 60) return `${diffMins}m ago`
16
- if (diffHours < 24) return `${diffHours}h ago`
17
- if (diffDays < 7) return `${diffDays}d ago`
18
- return date.toLocaleDateString()
19
- }
20
-
21
- export function formatPath(path: string): string {
22
- return path.replace(/^\/Users\/[^/]+\//, '~/')
23
- }
@@ -1,285 +0,0 @@
1
- import type { StatsResult } from './services/stats.server'
2
-
3
- export interface ShippedItem {
4
- name: string
5
- date: string
6
- version?: string
7
- type?: string
8
- }
9
-
10
- export interface WeekData {
11
- year: number
12
- week: number
13
- startDate: Date
14
- endDate: Date
15
- shipped: ShippedItem[]
16
- tasksCompleted: number
17
- bugsFixed: number
18
- syncs: number
19
- activeDays: number
20
- }
21
-
22
- /**
23
- * Get ISO week number for a date
24
- */
25
- export function getWeekNumber(date: Date): number {
26
- const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
27
- const dayNum = d.getUTCDay() || 7
28
- d.setUTCDate(d.getUTCDate() + 4 - dayNum)
29
- const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1))
30
- return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7)
31
- }
32
-
33
- /**
34
- * Get current year and week
35
- */
36
- export function getCurrentYearWeek(): { year: number; week: number } {
37
- const now = new Date()
38
- return {
39
- year: now.getFullYear(),
40
- week: getWeekNumber(now)
41
- }
42
- }
43
-
44
- /**
45
- * Get start and end dates for a given ISO week
46
- */
47
- export function getWeekDateRange(year: number, week: number): { start: Date; end: Date } {
48
- // Find January 4th of the year (always in week 1)
49
- const jan4 = new Date(year, 0, 4)
50
- const dayOfWeek = jan4.getDay() || 7
51
-
52
- // Find Monday of week 1
53
- const week1Monday = new Date(jan4)
54
- week1Monday.setDate(jan4.getDate() - dayOfWeek + 1)
55
-
56
- // Calculate target week's Monday
57
- const targetMonday = new Date(week1Monday)
58
- targetMonday.setDate(week1Monday.getDate() + (week - 1) * 7)
59
-
60
- // Calculate Sunday
61
- const targetSunday = new Date(targetMonday)
62
- targetSunday.setDate(targetMonday.getDate() + 6)
63
-
64
- return { start: targetMonday, end: targetSunday }
65
- }
66
-
67
- /**
68
- * Format date range as string
69
- */
70
- export function formatDateRange(start: Date, end: Date): string {
71
- const startMonth = start.toLocaleDateString('en-US', { month: 'short' })
72
- const endMonth = end.toLocaleDateString('en-US', { month: 'short' })
73
-
74
- if (startMonth === endMonth) {
75
- return `${startMonth} ${start.getDate()}-${end.getDate()}`
76
- }
77
- return `${startMonth} ${start.getDate()} - ${endMonth} ${end.getDate()}`
78
- }
79
-
80
- /**
81
- * Check if a date falls within a week
82
- */
83
- function isDateInWeek(dateStr: string, weekStart: Date, weekEnd: Date): boolean {
84
- const date = new Date(dateStr)
85
- return date >= weekStart && date <= weekEnd
86
- }
87
-
88
- /**
89
- * Filter stats data by week
90
- */
91
- export function filterDataByWeek(
92
- stats: StatsResult,
93
- year: number,
94
- week: number
95
- ): WeekData {
96
- const { start, end } = getWeekDateRange(year, week)
97
-
98
- // 1. Shipped features from legacyStats.shipped (parsed from shipped.md)
99
- const shipped: ShippedItem[] = (stats.legacyStats?.shipped ?? [])
100
- .filter(item => {
101
- if (!item.date) return false
102
- return isDateInWeek(item.date, start, end)
103
- })
104
- .map(item => ({
105
- name: item.name,
106
- date: item.date!,
107
- version: item.version,
108
- type: item.type
109
- }))
110
-
111
- // 2. Timeline events from legacyStats.timeline (parsed from context.jsonl)
112
- const timeline = stats.legacyStats?.timeline ?? []
113
- const weekEvents = timeline.filter(e => {
114
- if (!e.ts) return false
115
- return isDateInWeek(e.ts, start, end)
116
- })
117
-
118
- // Count tasks completed
119
- const tasksCompleted = weekEvents.filter(e =>
120
- e.type === 'task_complete' ||
121
- e.type === 'task_completed' ||
122
- e.type === 'session_completed'
123
- ).length
124
-
125
- // Count bugs fixed
126
- const bugsFixed = weekEvents.filter(e =>
127
- e.type === 'bug_fix' ||
128
- e.type === 'bug_reported'
129
- ).length
130
-
131
- // Count syncs
132
- const syncs = weekEvents.filter(e => e.type === 'sync').length
133
-
134
- // 3. Also aggregate from sessions
135
- const sessions = stats.legacyStats?.sessions ?? []
136
- let sessionTasksCompleted = 0
137
- let sessionFeatures = 0
138
- const activeDaysSet = new Set<string>()
139
-
140
- for (const session of sessions) {
141
- if (session.date && isDateInWeek(session.date, start, end)) {
142
- activeDaysSet.add(session.date)
143
- sessionTasksCompleted += session.tasksCompleted || 0
144
- sessionFeatures += session.featuresShipped || 0
145
- }
146
- }
147
-
148
- // Add active days from timeline
149
- for (const e of weekEvents) {
150
- if (e.ts) {
151
- activeDaysSet.add(e.ts.split('T')[0])
152
- }
153
- }
154
-
155
- return {
156
- year,
157
- week,
158
- startDate: start,
159
- endDate: end,
160
- shipped,
161
- tasksCompleted: tasksCompleted + sessionTasksCompleted,
162
- bugsFixed,
163
- syncs,
164
- activeDays: activeDaysSet.size
165
- }
166
- }
167
-
168
- /**
169
- * Generate plain text report for WhatsApp/email - client friendly
170
- */
171
- export function generateReportText(
172
- weekData: WeekData | WeekData[],
173
- projectName: string
174
- ): string {
175
- const weeks = Array.isArray(weekData) ? weekData : [weekData]
176
-
177
- if (weeks.length === 0) {
178
- return 'No weeks selected'
179
- }
180
-
181
- const lines: string[] = []
182
-
183
- // Header - clean and professional
184
- lines.push(`*${projectName}*`)
185
-
186
- // Date range - human readable
187
- if (weeks.length === 1) {
188
- const w = weeks[0]
189
- lines.push(`Weekly Report: ${formatDateRange(w.startDate, w.endDate)}`)
190
- } else {
191
- const firstWeek = weeks[0]
192
- const lastWeek = weeks[weeks.length - 1]
193
- lines.push(`Report: ${formatDateRange(firstWeek.startDate, lastWeek.endDate)}`)
194
- }
195
- lines.push('')
196
-
197
- // Aggregate data
198
- const allShipped = weeks.flatMap(w => w.shipped)
199
- // Deduplicate by name, keeping first occurrence (with version info)
200
- const uniqueShipsMap = new Map<string, ShippedItem>()
201
- for (const ship of allShipped) {
202
- if (!uniqueShipsMap.has(ship.name)) {
203
- uniqueShipsMap.set(ship.name, ship)
204
- }
205
- }
206
- // Sort by date descending (most recent first)
207
- const uniqueShips = Array.from(uniqueShipsMap.values())
208
- .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
209
-
210
- // Group by date
211
- const shipsByDate = new Map<string, ShippedItem[]>()
212
- for (const ship of uniqueShips) {
213
- if (!shipsByDate.has(ship.date)) {
214
- shipsByDate.set(ship.date, [])
215
- }
216
- shipsByDate.get(ship.date)!.push(ship)
217
- }
218
-
219
- // What we delivered - grouped by date
220
- if (uniqueShips.length > 0) {
221
- lines.push('*Shipped:*')
222
- for (const [date, ships] of shipsByDate.entries()) {
223
- const dateStr = new Date(date).toLocaleDateString('en-US', {
224
- weekday: 'short',
225
- day: 'numeric',
226
- month: 'short'
227
- })
228
- lines.push(`_${dateStr}_`)
229
- for (const ship of ships) {
230
- const versionStr = ship.version ? ` (${ship.version})` : ''
231
- lines.push(`• ${ship.name}${versionStr}`)
232
- }
233
- }
234
- lines.push('')
235
- }
236
-
237
- // Aggregate progress metrics
238
- const totalTasks = weeks.reduce((sum, w) => sum + w.tasksCompleted, 0)
239
- const totalBugs = weeks.reduce((sum, w) => sum + w.bugsFixed, 0)
240
- const totalDays = weeks.reduce((sum, w) => sum + w.activeDays, 0)
241
-
242
- // Progress section
243
- if (totalTasks > 0 || totalBugs > 0 || totalDays > 0) {
244
- lines.push('*Progress:*')
245
- if (totalTasks > 0) {
246
- lines.push(`• ${totalTasks} task${totalTasks !== 1 ? 's' : ''} completed`)
247
- }
248
- if (totalBugs > 0) {
249
- lines.push(`• ${totalBugs} bug${totalBugs !== 1 ? 's' : ''} fixed`)
250
- }
251
- if (totalDays > 0) {
252
- lines.push(`• ${totalDays} active day${totalDays !== 1 ? 's' : ''}`)
253
- }
254
- lines.push('')
255
- }
256
-
257
- // If nothing happened, be honest
258
- if (uniqueShips.length === 0 && totalTasks === 0 && totalBugs === 0) {
259
- lines.push('_In progress, no deliveries this week._')
260
- lines.push('')
261
- }
262
-
263
- // Optional: Next steps placeholder
264
- lines.push('*Next:*')
265
- lines.push('• [To be defined]')
266
-
267
- return lines.join('\n')
268
- }
269
-
270
- /**
271
- * Get activity level for a week (for calendar indicator)
272
- */
273
- export function getWeekActivityLevel(
274
- stats: StatsResult,
275
- year: number,
276
- week: number
277
- ): 'none' | 'low' | 'medium' | 'high' {
278
- const data = filterDataByWeek(stats, year, week)
279
- const total = data.shipped.length + data.tasksCompleted + data.bugsFixed + data.syncs
280
-
281
- if (total === 0) return 'none'
282
- if (total <= 3) return 'low'
283
- if (total <= 10) return 'medium'
284
- return 'high'
285
- }