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