prjct-cli 0.13.3 → 0.15.1

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 (195) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/bin/prjct +10 -13
  3. package/core/agentic/memory-system/semantic-memories.ts +2 -1
  4. package/core/agentic/plan-mode/plan-mode.ts +2 -1
  5. package/core/agentic/prompt-builder.ts +22 -43
  6. package/core/agentic/services.ts +5 -5
  7. package/core/agentic/smart-context.ts +7 -2
  8. package/core/command-registry/core-commands.ts +54 -29
  9. package/core/command-registry/optional-commands.ts +64 -0
  10. package/core/command-registry/setup-commands.ts +18 -3
  11. package/core/commands/analysis.ts +21 -68
  12. package/core/commands/analytics.ts +247 -213
  13. package/core/commands/base.ts +1 -1
  14. package/core/commands/index.ts +41 -36
  15. package/core/commands/maintenance.ts +300 -31
  16. package/core/commands/planning.ts +233 -22
  17. package/core/commands/setup.ts +3 -8
  18. package/core/commands/shipping.ts +14 -18
  19. package/core/commands/types.ts +8 -6
  20. package/core/commands/workflow.ts +105 -100
  21. package/core/context/generator.ts +317 -0
  22. package/core/context-sync.ts +7 -350
  23. package/core/data/index.ts +13 -32
  24. package/core/data/md-ideas-manager.ts +155 -0
  25. package/core/data/md-queue-manager.ts +4 -3
  26. package/core/data/md-shipped-manager.ts +90 -0
  27. package/core/data/md-state-manager.ts +11 -7
  28. package/core/domain/agent-generator.ts +23 -63
  29. package/core/events/index.ts +143 -0
  30. package/core/index.ts +17 -14
  31. package/core/infrastructure/capability-installer.ts +13 -149
  32. package/core/infrastructure/migrator/project-scanner.ts +2 -1
  33. package/core/infrastructure/path-manager.ts +4 -6
  34. package/core/infrastructure/setup.ts +3 -0
  35. package/core/infrastructure/uuid-migration.ts +750 -0
  36. package/core/outcomes/recorder.ts +2 -1
  37. package/core/plugin/loader.ts +4 -7
  38. package/core/plugin/registry.ts +3 -3
  39. package/core/schemas/index.ts +23 -25
  40. package/core/schemas/state.ts +1 -0
  41. package/core/serializers/ideas-serializer.ts +187 -0
  42. package/core/serializers/index.ts +16 -0
  43. package/core/serializers/shipped-serializer.ts +108 -0
  44. package/core/session/utils.ts +3 -9
  45. package/core/storage/ideas-storage.ts +273 -0
  46. package/core/storage/index.ts +204 -0
  47. package/core/storage/queue-storage.ts +297 -0
  48. package/core/storage/shipped-storage.ts +223 -0
  49. package/core/storage/state-storage.ts +235 -0
  50. package/core/storage/storage-manager.ts +175 -0
  51. package/package.json +1 -1
  52. package/packages/web/app/api/projects/[id]/momentum/route.ts +257 -0
  53. package/packages/web/app/api/sessions/current/route.ts +132 -0
  54. package/packages/web/app/api/sessions/history/route.ts +96 -14
  55. package/packages/web/app/globals.css +5 -0
  56. package/packages/web/app/layout.tsx +2 -0
  57. package/packages/web/app/project/[id]/code/layout.tsx +18 -0
  58. package/packages/web/app/project/[id]/code/page.tsx +408 -0
  59. package/packages/web/app/project/[id]/page.tsx +359 -389
  60. package/packages/web/app/project/[id]/reports/page.tsx +59 -0
  61. package/packages/web/app/project/[id]/reports/print/page.tsx +58 -0
  62. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -1
  63. package/packages/web/components/AgentsCard/AgentsCard.tsx +64 -34
  64. package/packages/web/components/AgentsCard/AgentsCard.types.ts +1 -0
  65. package/packages/web/components/AppSidebar/AppSidebar.tsx +135 -11
  66. package/packages/web/components/BentoCard/BentoCard.constants.ts +3 -3
  67. package/packages/web/components/BentoCard/BentoCard.tsx +2 -1
  68. package/packages/web/components/BentoGrid/BentoGrid.tsx +2 -2
  69. package/packages/web/components/BlockersCard/BlockersCard.tsx +65 -57
  70. package/packages/web/components/BlockersCard/BlockersCard.types.ts +1 -0
  71. package/packages/web/components/CommandBar/CommandBar.tsx +67 -0
  72. package/packages/web/components/CommandBar/index.ts +1 -0
  73. package/packages/web/components/DashboardContent/DashboardContent.tsx +35 -5
  74. package/packages/web/components/DateGroup/DateGroup.tsx +1 -1
  75. package/packages/web/components/EmptyState/EmptyState.tsx +39 -21
  76. package/packages/web/components/EmptyState/EmptyState.types.ts +1 -0
  77. package/packages/web/components/EventRow/EventRow.tsx +4 -4
  78. package/packages/web/components/EventRow/EventRow.utils.ts +3 -3
  79. package/packages/web/components/HeroSection/HeroSection.tsx +52 -15
  80. package/packages/web/components/HeroSection/HeroSection.types.ts +4 -4
  81. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -3
  82. package/packages/web/components/IdeasCard/IdeasCard.tsx +94 -27
  83. package/packages/web/components/IdeasCard/IdeasCard.types.ts +1 -0
  84. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +18 -0
  85. package/packages/web/components/MasonryGrid/index.ts +1 -0
  86. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +119 -0
  87. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +16 -0
  88. package/packages/web/components/MomentumWidget/index.ts +2 -0
  89. package/packages/web/components/NowCard/NowCard.tsx +81 -56
  90. package/packages/web/components/NowCard/NowCard.types.ts +1 -0
  91. package/packages/web/components/PageHeader/PageHeader.tsx +24 -0
  92. package/packages/web/components/PageHeader/index.ts +1 -0
  93. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +2 -2
  94. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +2 -2
  95. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +37 -0
  96. package/packages/web/components/ProjectColorDot/index.ts +1 -0
  97. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +104 -0
  98. package/packages/web/components/ProjectSelectorModal/index.ts +1 -0
  99. package/packages/web/components/Providers/Providers.tsx +4 -1
  100. package/packages/web/components/QueueCard/QueueCard.tsx +78 -25
  101. package/packages/web/components/QueueCard/QueueCard.types.ts +1 -0
  102. package/packages/web/components/QueueCard/QueueCard.utils.ts +3 -3
  103. package/packages/web/components/RecoverCard/RecoverCard.tsx +72 -0
  104. package/packages/web/components/RecoverCard/RecoverCard.types.ts +16 -0
  105. package/packages/web/components/RecoverCard/index.ts +2 -0
  106. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +101 -33
  107. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +1 -0
  108. package/packages/web/components/ShipsCard/ShipsCard.tsx +71 -28
  109. package/packages/web/components/ShipsCard/ShipsCard.types.ts +2 -0
  110. package/packages/web/components/SparklineChart/SparklineChart.tsx +20 -18
  111. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +95 -0
  112. package/packages/web/components/StatsMasonry/index.ts +1 -0
  113. package/packages/web/components/StreakCard/StreakCard.tsx +37 -35
  114. package/packages/web/components/TasksCounter/TasksCounter.tsx +1 -1
  115. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +12 -4
  116. package/packages/web/components/TerminalDock/DockToggleTab.tsx +29 -0
  117. package/packages/web/components/TerminalDock/TerminalDock.tsx +386 -0
  118. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +130 -0
  119. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +142 -0
  120. package/packages/web/components/TerminalDock/index.ts +2 -0
  121. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +8 -3
  122. package/packages/web/components/VelocityCard/VelocityCard.tsx +49 -47
  123. package/packages/web/components/WeeklyReports/PrintableReport.tsx +259 -0
  124. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +187 -0
  125. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +288 -0
  126. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +149 -0
  127. package/packages/web/components/WeeklyReports/index.ts +4 -0
  128. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +16 -4
  129. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +1 -0
  130. package/packages/web/components/charts/SessionsChart.tsx +6 -3
  131. package/packages/web/components/ui/dialog.tsx +143 -0
  132. package/packages/web/components/ui/drawer.tsx +135 -0
  133. package/packages/web/components/ui/select.tsx +187 -0
  134. package/packages/web/context/GlobalTerminalContext.tsx +538 -0
  135. package/packages/web/lib/commands.ts +81 -0
  136. package/packages/web/lib/generate-week-report.ts +285 -0
  137. package/packages/web/lib/parse-prjct-files.ts +56 -55
  138. package/packages/web/lib/project-colors.ts +58 -0
  139. package/packages/web/lib/projects.ts +58 -5
  140. package/packages/web/lib/services/projects.server.ts +11 -1
  141. package/packages/web/next-env.d.ts +1 -1
  142. package/packages/web/package.json +5 -1
  143. package/templates/commands/analyze.md +39 -3
  144. package/templates/commands/ask.md +58 -3
  145. package/templates/commands/bug.md +117 -26
  146. package/templates/commands/dash.md +95 -158
  147. package/templates/commands/done.md +130 -148
  148. package/templates/commands/feature.md +125 -103
  149. package/templates/commands/git.md +18 -3
  150. package/templates/commands/idea.md +121 -38
  151. package/templates/commands/init.md +124 -20
  152. package/templates/commands/migrate-all.md +63 -28
  153. package/templates/commands/migrate.md +140 -0
  154. package/templates/commands/next.md +115 -5
  155. package/templates/commands/now.md +146 -82
  156. package/templates/commands/pause.md +89 -74
  157. package/templates/commands/redo.md +6 -4
  158. package/templates/commands/resume.md +141 -59
  159. package/templates/commands/setup.md +18 -3
  160. package/templates/commands/ship.md +103 -231
  161. package/templates/commands/spec.md +98 -8
  162. package/templates/commands/suggest.md +22 -2
  163. package/templates/commands/sync.md +192 -203
  164. package/templates/commands/undo.md +6 -4
  165. package/templates/mcp-config.json +20 -1
  166. package/core/data/agents-manager.ts +0 -76
  167. package/core/data/analysis-manager.ts +0 -83
  168. package/core/data/base-manager.ts +0 -156
  169. package/core/data/ideas-manager.ts +0 -81
  170. package/core/data/outcomes-manager.ts +0 -96
  171. package/core/data/project-manager.ts +0 -75
  172. package/core/data/roadmap-manager.ts +0 -118
  173. package/core/data/shipped-manager.ts +0 -65
  174. package/core/data/state-manager.ts +0 -214
  175. package/core/state/index.ts +0 -25
  176. package/core/state/manager.ts +0 -376
  177. package/core/state/types.ts +0 -185
  178. package/core/utils/project-capabilities.ts +0 -156
  179. package/core/view-generator.ts +0 -536
  180. package/packages/web/app/project/[id]/stats/loading.tsx +0 -43
  181. package/packages/web/app/project/[id]/stats/page.tsx +0 -253
  182. package/templates/agent-assignment.md +0 -72
  183. package/templates/analysis/project-analysis.md +0 -78
  184. package/templates/checklists/accessibility.md +0 -33
  185. package/templates/commands/build.md +0 -17
  186. package/templates/commands/decision.md +0 -226
  187. package/templates/commands/fix.md +0 -79
  188. package/templates/commands/help.md +0 -61
  189. package/templates/commands/progress.md +0 -14
  190. package/templates/commands/recap.md +0 -14
  191. package/templates/commands/roadmap.md +0 -52
  192. package/templates/commands/status.md +0 -17
  193. package/templates/commands/task.md +0 -63
  194. package/templates/commands/work.md +0 -44
  195. package/templates/commands/workflow.md +0 -12
@@ -0,0 +1,119 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { SparklineChart } from '@/components/SparklineChart'
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
6
+ import { Flame, TrendingUp, Activity, Heart } from 'lucide-react'
7
+ import { cn } from '@/lib/utils'
8
+ import type { MomentumWidgetProps, MomentumData, MomentumStatus } from './MomentumWidget.types'
9
+
10
+ const statusConfig: Record<MomentumStatus, {
11
+ color: string
12
+ bgColor: string
13
+ textColor: string
14
+ icon: typeof Flame
15
+ }> = {
16
+ // Growing - you're killing it!
17
+ hot: {
18
+ color: '#22c55e',
19
+ bgColor: 'bg-green-500/10',
20
+ textColor: 'text-green-500',
21
+ icon: Flame
22
+ },
23
+ // Normal activity - neutral tones
24
+ active: {
25
+ color: '#a1a1aa',
26
+ bgColor: 'bg-muted',
27
+ textColor: 'text-muted-foreground',
28
+ icon: Activity
29
+ },
30
+ // Slight slowdown - still neutral
31
+ cooling: {
32
+ color: '#a1a1aa',
33
+ bgColor: 'bg-muted',
34
+ textColor: 'text-muted-foreground',
35
+ icon: TrendingUp
36
+ },
37
+ // Abandoned (7+ days) - red alert
38
+ cold: {
39
+ color: '#ef4444',
40
+ bgColor: 'bg-red-500/10',
41
+ textColor: 'text-red-500',
42
+ icon: Heart
43
+ }
44
+ }
45
+
46
+ export function MomentumWidget({ projectId }: MomentumWidgetProps) {
47
+ const [data, setData] = useState<MomentumData | null>(null)
48
+ const [loading, setLoading] = useState(true)
49
+
50
+ useEffect(() => {
51
+ async function fetchMomentum() {
52
+ try {
53
+ const res = await fetch(`/api/projects/${projectId}/momentum`)
54
+ const json = await res.json()
55
+ if (json.success) {
56
+ setData(json.data)
57
+ }
58
+ } catch (error) {
59
+ console.error('Failed to fetch momentum:', error)
60
+ } finally {
61
+ setLoading(false)
62
+ }
63
+ }
64
+
65
+ fetchMomentum()
66
+ }, [projectId])
67
+
68
+ if (loading) {
69
+ return (
70
+ <div className="flex items-center gap-2 h-10 animate-pulse">
71
+ <div className="h-10 w-32 bg-muted rounded" />
72
+ <div className="h-4 w-16 bg-muted rounded" />
73
+ </div>
74
+ )
75
+ }
76
+
77
+ if (!data) return null
78
+
79
+ const config = statusConfig[data.status]
80
+ const Icon = config.icon
81
+
82
+ return (
83
+ <Tooltip>
84
+ <TooltipTrigger asChild>
85
+ <div className={cn(
86
+ 'flex items-center gap-2 px-2 py-1 rounded-md cursor-default transition-colors',
87
+ config.bgColor
88
+ )}>
89
+ {/* Mini sparkline - matching button width for visual weight */}
90
+ <div className="w-32 h-10">
91
+ <SparklineChart
92
+ data={data.dailyTasks}
93
+ color={config.color}
94
+ height={40}
95
+ showArea={true}
96
+ />
97
+ </div>
98
+
99
+ {/* Status badge */}
100
+ <div className={cn('flex items-center gap-1.5', config.textColor)}>
101
+ <Icon className="w-4 h-4" />
102
+ <span className="text-sm font-medium whitespace-nowrap">
103
+ {data.message}
104
+ </span>
105
+ </div>
106
+ </div>
107
+ </TooltipTrigger>
108
+ <TooltipContent side="bottom" className="text-xs">
109
+ <div className="space-y-1">
110
+ <p className="font-medium">7-day activity</p>
111
+ <p>{data.totalTasks} tasks, {data.totalShips} ships</p>
112
+ {data.streak > 0 && (
113
+ <p className="text-foreground font-medium">{data.streak} day streak!</p>
114
+ )}
115
+ </div>
116
+ </TooltipContent>
117
+ </Tooltip>
118
+ )
119
+ }
@@ -0,0 +1,16 @@
1
+ export type MomentumStatus = 'hot' | 'active' | 'cooling' | 'cold'
2
+
3
+ export interface MomentumData {
4
+ dailyTasks: number[]
5
+ totalTasks: number
6
+ totalShips: number
7
+ lastActivityDate: string | null
8
+ daysSinceActivity: number
9
+ streak: number
10
+ status: MomentumStatus
11
+ message: string
12
+ }
13
+
14
+ export interface MomentumWidgetProps {
15
+ projectId: string
16
+ }
@@ -0,0 +1,2 @@
1
+ export { MomentumWidget } from './MomentumWidget'
2
+ export type { MomentumWidgetProps, MomentumData, MomentumStatus } from './MomentumWidget.types'
@@ -1,84 +1,108 @@
1
- import { BentoCard } from '@/components/BentoCard'
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
2
4
  import { EmptyState } from '@/components/EmptyState'
3
- import { Target, Clock, Bot, Pause } from 'lucide-react'
5
+ import { Target, Clock, Bot, Pause, Play, CheckCircle2 } from 'lucide-react'
4
6
  import { cn } from '@/lib/utils'
5
7
  import type { NowCardProps } from './NowCard.types'
6
8
 
7
- export function NowCard({ currentTask, className }: NowCardProps) {
9
+ export function NowCard({ currentTask, codeHref, className }: NowCardProps) {
8
10
  return (
9
- <BentoCard
10
- size="2x2"
11
- title="Now"
12
- icon={Target}
13
- accentColor={currentTask ? 'warning' : 'default'}
14
- className={className}
15
- >
16
- {currentTask ? (
17
- <div className="flex flex-col h-full">
18
- <div className="flex items-center gap-2 mb-3">
11
+ <div className={cn(
12
+ 'relative overflow-hidden rounded-xl border bg-card p-4',
13
+ className
14
+ )}>
15
+ <div className="flex items-center justify-between mb-3">
16
+ <div className="flex items-center gap-2">
17
+ <Target className="h-4 w-4 text-muted-foreground" />
18
+ <span className="text-xs font-bold uppercase tracking-[0.15em] text-muted-foreground">
19
+ Now
20
+ </span>
21
+ </div>
22
+ {currentTask && (
23
+ <div className="flex items-center gap-2">
19
24
  {currentTask.pausedAt ? (
20
- <>
21
- <Pause className="w-3 h-3 text-muted-foreground" />
22
- <span className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
23
- Paused
24
- </span>
25
- </>
25
+ <span className="inline-flex items-center gap-1 text-xs font-semibold text-muted-foreground bg-muted px-2 py-0.5 rounded-full">
26
+ <Pause className="w-2.5 h-2.5" />
27
+ Paused
28
+ </span>
26
29
  ) : (
27
- <>
28
- <div className="w-2 h-2 rounded-full bg-amber-500 animate-pulse" />
29
- <span className="text-[10px] font-semibold text-amber-600 dark:text-amber-400 uppercase tracking-wider">
30
- Working
31
- </span>
32
- </>
30
+ <span className="inline-flex items-center gap-1 text-xs font-semibold text-foreground bg-muted px-2 py-0.5 rounded-full">
31
+ <Play className="w-2.5 h-2.5 fill-current" />
32
+ Working
33
+ </span>
33
34
  )}
34
35
  </div>
36
+ )}
37
+ </div>
35
38
 
36
- <p className="text-xl font-semibold leading-tight flex-1">
39
+ {currentTask ? (
40
+ <div className="space-y-3">
41
+ <p className="text-sm font-medium leading-tight break-words">
37
42
  {currentTask.task}
38
43
  </p>
39
44
 
40
- {currentTask.agent && (
41
- <div className="flex items-center gap-2 mt-2 text-xs text-muted-foreground">
42
- <Bot className="h-3 w-3" />
43
- <span className="font-mono">{currentTask.agent}</span>
44
- {currentTask.agentConfidence !== undefined && (
45
- <span className={cn(
46
- 'px-1.5 py-0.5 rounded text-[10px]',
47
- currentTask.agentConfidence >= 0.8 ? 'bg-emerald-500/10 text-emerald-600' :
48
- currentTask.agentConfidence >= 0.5 ? 'bg-amber-500/10 text-amber-600' :
49
- 'bg-muted text-muted-foreground'
50
- )}>
51
- {Math.round(currentTask.agentConfidence * 100)}% confidence
52
- </span>
53
- )}
54
- </div>
55
- )}
45
+ <div className="flex flex-wrap items-center gap-3">
46
+ {currentTask.agent && (
47
+ <div className="inline-flex items-center gap-1.5 text-xs text-muted-foreground bg-muted/50 px-2 py-1 rounded">
48
+ <Bot className="h-3 w-3" />
49
+ <span className="font-mono">{currentTask.agent}</span>
50
+ </div>
51
+ )}
56
52
 
57
- <div className="flex items-center gap-4 mt-3 text-muted-foreground">
58
- {currentTask.duration && (
59
- <div className="flex items-center gap-1.5">
60
- <Clock className="h-3.5 w-3.5" />
61
- <span className="text-sm">{currentTask.duration}</span>
53
+ {currentTask.startedAt && (
54
+ <div className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
55
+ <Clock className="h-3 w-3" />
56
+ <span>Started {new Date(currentTask.startedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
62
57
  </div>
63
58
  )}
59
+
64
60
  {currentTask.estimatedDuration && (
65
- <span className="text-xs">
66
- / est. {currentTask.estimatedDuration}
61
+ <span className="text-xs text-muted-foreground">
62
+ Est. {currentTask.estimatedDuration}
67
63
  </span>
68
64
  )}
69
65
  </div>
70
66
 
71
- <div className="mt-4">
67
+ {/* Progress indicator */}
68
+ <div className="pt-1">
72
69
  <div className="h-1.5 bg-muted rounded-full overflow-hidden">
73
70
  <div
74
- className={cn(
75
- "h-full rounded-full",
76
- currentTask.pausedAt ? "bg-muted-foreground" : "bg-amber-500 animate-pulse"
77
- )}
78
- style={{ width: '60%' }}
71
+ className="h-full rounded-full transition-all duration-300 bg-foreground"
72
+ style={{ width: currentTask.pausedAt ? '30%' : '60%' }}
79
73
  />
80
74
  </div>
81
75
  </div>
76
+
77
+ {/* Action buttons */}
78
+ {codeHref && (
79
+ <div className="flex gap-2 pt-2">
80
+ <Link
81
+ href={`${codeHref}?cmd=p.%20done`}
82
+ className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
83
+ >
84
+ <CheckCircle2 className="h-3.5 w-3.5" />
85
+ Done
86
+ </Link>
87
+ {currentTask.pausedAt ? (
88
+ <Link
89
+ href={`${codeHref}?cmd=p.%20resume`}
90
+ className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
91
+ >
92
+ <Play className="h-3.5 w-3.5" />
93
+ Resume
94
+ </Link>
95
+ ) : (
96
+ <Link
97
+ href={`${codeHref}?cmd=p.%20pause`}
98
+ className="flex-1 flex items-center justify-center gap-1.5 text-xs font-medium px-3 py-2 rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors"
99
+ >
100
+ <Pause className="h-3.5 w-3.5" />
101
+ Pause
102
+ </Link>
103
+ )}
104
+ </div>
105
+ )}
82
106
  </div>
83
107
  ) : (
84
108
  <EmptyState
@@ -86,8 +110,9 @@ export function NowCard({ currentTask, className }: NowCardProps) {
86
110
  title="No active task"
87
111
  description="Start working on something"
88
112
  command="/p:now"
113
+ href={codeHref}
89
114
  />
90
115
  )}
91
- </BentoCard>
116
+ </div>
92
117
  )
93
118
  }
@@ -11,5 +11,6 @@ export interface CurrentTask {
11
11
 
12
12
  export interface NowCardProps {
13
13
  currentTask: CurrentTask | null
14
+ codeHref?: string
14
15
  className?: string
15
16
  }
@@ -0,0 +1,24 @@
1
+ import { getProjectBgClass } from '@/lib/project-colors'
2
+
3
+ interface PageHeaderProps {
4
+ projectId: string
5
+ projectName: string
6
+ section?: string
7
+ }
8
+
9
+ export function PageHeader({ projectId, projectName, section }: PageHeaderProps) {
10
+ const colorBg = getProjectBgClass(projectId)
11
+
12
+ return (
13
+ <div className="flex items-center gap-3 mb-6">
14
+ <div className={`w-3 h-3 rounded-full shrink-0 ${colorBg}`} />
15
+ <h1 className="text-2xl font-semibold">{projectName}</h1>
16
+ {section && (
17
+ <>
18
+ <span className="text-muted-foreground/50">/</span>
19
+ <span className="text-xl text-muted-foreground">{section}</span>
20
+ </>
21
+ )}
22
+ </div>
23
+ )
24
+ }
@@ -0,0 +1 @@
1
+ export { PageHeader } from './PageHeader'
@@ -6,7 +6,7 @@ export const PROGRESS_RING_SIZES: Record<ProgressRingSize, {
6
6
  viewBox: number
7
7
  radius: number
8
8
  }> = {
9
- sm: { container: 'h-8 w-8', text: 'text-[10px]', viewBox: 36, radius: 14 },
9
+ sm: { container: 'h-8 w-8', text: 'text-xs', viewBox: 36, radius: 14 },
10
10
  md: { container: 'h-12 w-12', text: 'text-xs', viewBox: 36, radius: 14 },
11
11
  lg: { container: 'h-16 w-16', text: 'text-sm', viewBox: 36, radius: 14 },
12
12
  xl: { container: 'h-20 w-20', text: 'text-base', viewBox: 36, radius: 14 },
@@ -16,5 +16,5 @@ export const PROGRESS_RING_COLOR_STYLES: Record<AccentColor, string> = {
16
16
  default: 'text-foreground',
17
17
  success: 'text-emerald-500',
18
18
  warning: 'text-amber-500',
19
- destructive: 'text-destructive',
19
+ destructive: 'text-red-500',
20
20
  }
@@ -28,7 +28,7 @@ export function ProjectAvatar({
28
28
  return (
29
29
  <div
30
30
  className={cn(
31
- 'rounded-lg bg-muted flex items-center justify-center overflow-hidden shrink-0',
31
+ 'rounded-lg flex items-center justify-center overflow-hidden shrink-0 bg-muted border border-border',
32
32
  sizeClasses[size],
33
33
  className
34
34
  )}
@@ -46,7 +46,7 @@ export function ProjectAvatar({
46
46
  }}
47
47
  />
48
48
  ) : null}
49
- <span className={cn('font-bold text-muted-foreground', iconPath ? 'hidden' : '')}>
49
+ <span className={cn('font-medium text-muted-foreground', iconPath ? 'hidden' : '')}>
50
50
  {initials}
51
51
  </span>
52
52
  </div>
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@/lib/utils'
4
+ import { getProjectColor } from '@/lib/project-colors'
5
+
6
+ interface ProjectColorDotProps {
7
+ projectId: string
8
+ size?: 'sm' | 'md' | 'lg' | 'xl'
9
+ className?: string
10
+ }
11
+
12
+ const sizeClasses = {
13
+ sm: 'w-2 h-2',
14
+ md: 'w-3 h-3',
15
+ lg: 'w-4 h-4',
16
+ xl: 'w-5 h-5',
17
+ }
18
+
19
+ export function ProjectColorDot({
20
+ projectId,
21
+ size = 'md',
22
+ className
23
+ }: ProjectColorDotProps) {
24
+ const color = getProjectColor(projectId)
25
+
26
+ return (
27
+ <div
28
+ className={cn(
29
+ 'rounded-full shrink-0',
30
+ sizeClasses[size],
31
+ color.bg,
32
+ className
33
+ )}
34
+ aria-hidden="true"
35
+ />
36
+ )
37
+ }
@@ -0,0 +1 @@
1
+ export { ProjectColorDot } from './ProjectColorDot'
@@ -0,0 +1,104 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { useProjects } from '@/hooks/useProjects'
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogDescription,
11
+ } from '@/components/ui/dialog'
12
+ import { Input } from '@/components/ui/input'
13
+ import { ProjectAvatar } from '@/components/ProjectAvatar'
14
+ import { Loader2, Search, FolderGit2 } from 'lucide-react'
15
+ import { cn } from '@/lib/utils'
16
+ import { formatPath } from '@/lib/format'
17
+
18
+ interface ProjectSelectorModalProps {
19
+ isOpen: boolean
20
+ onClose: () => void
21
+ onSelectProject: (projectId: string, projectName: string, projectPath: string) => void
22
+ }
23
+
24
+ export function ProjectSelectorModal({
25
+ isOpen,
26
+ onClose,
27
+ onSelectProject,
28
+ }: ProjectSelectorModalProps) {
29
+ const [search, setSearch] = useState('')
30
+ const { data: projects, isLoading } = useProjects()
31
+
32
+ const filteredProjects = projects?.filter((project) =>
33
+ project.name?.toLowerCase().includes(search.toLowerCase()) ||
34
+ project.repoPath?.toLowerCase().includes(search.toLowerCase())
35
+ ) || []
36
+
37
+ return (
38
+ <Dialog open={isOpen} onOpenChange={onClose}>
39
+ <DialogContent className="sm:max-w-md">
40
+ <DialogHeader>
41
+ <DialogTitle>Select a Project</DialogTitle>
42
+ <DialogDescription>
43
+ Choose a project to open a terminal session
44
+ </DialogDescription>
45
+ </DialogHeader>
46
+
47
+ <div className="relative">
48
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
49
+ <Input
50
+ placeholder="Search projects..."
51
+ value={search}
52
+ onChange={(e) => setSearch(e.target.value)}
53
+ className="pl-9"
54
+ />
55
+ </div>
56
+
57
+ <div className="max-h-[300px] overflow-y-auto space-y-1">
58
+ {isLoading ? (
59
+ <div className="flex items-center justify-center py-8">
60
+ <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
61
+ </div>
62
+ ) : filteredProjects.length === 0 ? (
63
+ <div className="text-center py-8 text-muted-foreground">
64
+ {search ? 'No projects found' : 'No projects available'}
65
+ </div>
66
+ ) : (
67
+ filteredProjects.map((project) => (
68
+ <button
69
+ key={project.id}
70
+ onClick={() => {
71
+ const projectPath = project.repoPath || project.path || '/tmp'
72
+ const projectName = project.name || project.id
73
+ onSelectProject(project.id, projectName, projectPath)
74
+ }}
75
+ className={cn(
76
+ 'w-full flex items-center gap-3 p-3 rounded-lg transition-colors',
77
+ 'hover:bg-accent text-left'
78
+ )}
79
+ >
80
+ <ProjectAvatar
81
+ projectId={project.id}
82
+ name={project.name || project.id}
83
+ iconPath={project.iconPath}
84
+ size="sm"
85
+ />
86
+ <div className="flex-1 min-w-0">
87
+ <div className="font-medium truncate">
88
+ {project.name || project.id}
89
+ </div>
90
+ {project.repoPath && (
91
+ <div className="text-xs text-muted-foreground flex items-center gap-1 truncate">
92
+ <FolderGit2 className="h-3 w-3 shrink-0" />
93
+ <span className="truncate">{formatPath(project.repoPath)}</span>
94
+ </div>
95
+ )}
96
+ </div>
97
+ </button>
98
+ ))
99
+ )}
100
+ </div>
101
+ </DialogContent>
102
+ </Dialog>
103
+ )
104
+ }
@@ -0,0 +1 @@
1
+ export { ProjectSelectorModal } from './ProjectSelectorModal'
@@ -4,6 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
4
4
  import { useState, type ReactNode } from 'react'
5
5
  import { ThemeProvider, type ThemeProviderProps } from 'next-themes'
6
6
  import { TerminalProvider } from '@/context/TerminalContext'
7
+ import { GlobalTerminalProvider } from '@/context/GlobalTerminalContext'
7
8
 
8
9
  function ThemeWrapper({ children, ...props }: ThemeProviderProps & { children: ReactNode }) {
9
10
  return <ThemeProvider {...props}>{children}</ThemeProvider>
@@ -37,7 +38,9 @@ export function Providers({ children }: { children: ReactNode }) {
37
38
  disableTransitionOnChange
38
39
  >
39
40
  <TerminalProvider>
40
- {children}
41
+ <GlobalTerminalProvider>
42
+ {children}
43
+ </GlobalTerminalProvider>
41
44
  </TerminalProvider>
42
45
  </ThemeWrapper>
43
46
  </QueryClientProvider>