prjct-cli 0.11.5 → 0.12.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 (391) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +226 -50
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +204 -163
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +134 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  333. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  334. package/packages/web/hooks/useProjectStats.ts +55 -0
  335. package/packages/web/hooks/useProjects.ts +6 -6
  336. package/packages/web/lib/actions/projects.ts +15 -0
  337. package/packages/web/lib/json-loader.ts +630 -0
  338. package/packages/web/lib/services/index.ts +9 -0
  339. package/packages/web/lib/services/migration.server.ts +598 -0
  340. package/packages/web/lib/services/projects.server.ts +52 -0
  341. package/packages/web/lib/services/stats.server.ts +264 -0
  342. package/packages/web/lib/unified-loader.ts +396 -0
  343. package/packages/web/package.json +10 -7
  344. package/packages/web/server.ts +36 -6
  345. package/templates/commands/done.md +76 -32
  346. package/templates/commands/feature.md +121 -47
  347. package/templates/commands/idea.md +81 -8
  348. package/templates/commands/now.md +41 -17
  349. package/templates/commands/ship.md +64 -25
  350. package/templates/commands/sync.md +28 -3
  351. package/core/agentic/agent-router.js +0 -140
  352. package/core/agentic/chain-of-thought.js +0 -578
  353. package/core/agentic/command-executor.js +0 -417
  354. package/core/agentic/context-filter.js +0 -354
  355. package/core/agentic/ground-truth.js +0 -591
  356. package/core/agentic/loop-detector.js +0 -406
  357. package/core/agentic/memory-system.js +0 -845
  358. package/core/agentic/parallel-tools.js +0 -366
  359. package/core/agentic/plan-mode.js +0 -572
  360. package/core/agentic/prompt-builder.js +0 -352
  361. package/core/agentic/response-templates.js +0 -290
  362. package/core/agentic/semantic-compression.js +0 -517
  363. package/core/agentic/think-blocks.js +0 -657
  364. package/core/agentic/tool-registry.js +0 -184
  365. package/core/agentic/validation-rules.js +0 -380
  366. package/core/command-registry.js +0 -698
  367. package/core/commands.js +0 -2237
  368. package/core/domain/task-stack.js +0 -497
  369. package/core/infrastructure/legacy-installer-detector.js +0 -546
  370. package/core/infrastructure/migrator.js +0 -796
  371. package/core/infrastructure/session-manager.js +0 -390
  372. package/core/utils/file-helper.js +0 -329
  373. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  374. package/packages/web/app/api/stats/route.ts +0 -38
  375. package/packages/web/components/AppSidebar.tsx +0 -113
  376. package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
  377. package/packages/web/components/stats/AgentsCard.tsx +0 -56
  378. package/packages/web/components/stats/BentoCard.tsx +0 -88
  379. package/packages/web/components/stats/HeroSection.tsx +0 -172
  380. package/packages/web/components/stats/NowCard.tsx +0 -71
  381. package/packages/web/components/stats/QueueCard.tsx +0 -58
  382. package/packages/web/components/stats/VelocityCard.tsx +0 -60
  383. package/packages/web/components/stats/index.ts +0 -17
  384. package/packages/web/hooks/useStats.ts +0 -28
  385. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  386. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  387. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  388. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  389. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  390. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  391. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -0,0 +1,254 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback, useTransition } from 'react'
4
+ import Link from 'next/link'
5
+ import { useRouter } from 'next/navigation'
6
+ import { SessionsChart } from '@/components/charts/SessionsChart'
7
+ import { Button } from '@/components/ui/button'
8
+ import { ProjectAvatar } from '@/components/ProjectAvatar'
9
+ import { TechStackBadges } from '@/components/TechStackBadges'
10
+ import { formatRelativeTime, formatPath } from '@/lib/format'
11
+ import {
12
+ MoreHorizontal,
13
+ Trash2,
14
+ AlertTriangle,
15
+ Target,
16
+ Lightbulb,
17
+ ListTodo,
18
+ Zap,
19
+ FolderGit2
20
+ } from 'lucide-react'
21
+ import {
22
+ DropdownMenu,
23
+ DropdownMenuContent,
24
+ DropdownMenuItem,
25
+ DropdownMenuTrigger,
26
+ } from '@/components/ui/dropdown-menu'
27
+ import {
28
+ AlertDialog,
29
+ AlertDialogAction,
30
+ AlertDialogCancel,
31
+ AlertDialogContent,
32
+ AlertDialogDescription,
33
+ AlertDialogFooter,
34
+ AlertDialogHeader,
35
+ AlertDialogTitle,
36
+ } from '@/components/ui/alert-dialog'
37
+ import { deleteProject } from '@/lib/actions/projects'
38
+
39
+ export interface Project {
40
+ id: string
41
+ name: string
42
+ path: string
43
+ repoPath?: string | null
44
+ currentTask?: string | null
45
+ hasActiveSession?: boolean
46
+ lastActivity?: string | null
47
+ ideasCount?: number
48
+ nextTasksCount?: number
49
+ techStack?: string[]
50
+ iconPath?: string | null
51
+ version?: string
52
+ stack?: string
53
+ filesCount?: number
54
+ commitsCount?: number
55
+ }
56
+
57
+ export interface GlobalStats {
58
+ userName: string
59
+ totalProjects: number
60
+ }
61
+
62
+ interface DashboardContentProps {
63
+ projects: Project[]
64
+ stats: GlobalStats
65
+ }
66
+
67
+ export function DashboardContent({ projects, stats }: DashboardContentProps) {
68
+ const [projectToDelete, setProjectToDelete] = useState<Project | null>(null)
69
+ const [isPending, startTransition] = useTransition()
70
+ const router = useRouter()
71
+
72
+ const handleDeleteClick = useCallback((project: Project, e: React.MouseEvent) => {
73
+ e.preventDefault()
74
+ e.stopPropagation()
75
+ setProjectToDelete(project)
76
+ }, [])
77
+
78
+ const handleConfirmDelete = useCallback(() => {
79
+ if (projectToDelete) {
80
+ startTransition(async () => {
81
+ await deleteProject(projectToDelete.id)
82
+ setProjectToDelete(null)
83
+ router.refresh()
84
+ })
85
+ }
86
+ }, [projectToDelete, router])
87
+
88
+ const projectCount = projects?.length || 0
89
+
90
+ return (
91
+ <div className="p-4 sm:p-6 md:p-8 h-full overflow-auto">
92
+ {/* Mobile: Add padding for hamburger menu */}
93
+ <div className="pl-10 md:pl-0">
94
+ <p className="text-sm sm:text-base text-muted-foreground">Hola! {stats?.userName || 'Developer'}</p>
95
+
96
+ <h1 className="text-6xl sm:text-7xl md:text-8xl font-bold tracking-tighter tabular-nums mt-2">{projectCount}</h1>
97
+ <p className="text-sm sm:text-base text-muted-foreground mt-1">{projectCount === 1 ? 'project' : 'projects'}</p>
98
+ </div>
99
+
100
+ <section className="mt-6 sm:mt-8">
101
+ <SessionsChart />
102
+ </section>
103
+
104
+ <section className="mt-8 sm:mt-10 md:mt-12">
105
+ <h2 className="font-bold uppercase tracking-wide text-muted-foreground mb-4 sm:mb-6 text-sm">Active Projects</h2>
106
+
107
+ {projects?.length === 0 ? (
108
+ <div className="border-2 border-dashed border-border rounded-xl p-6 sm:p-8 text-center">
109
+ <p className="text-muted-foreground text-sm sm:text-base">
110
+ No projects yet. Initialize with <code className="bg-muted px-2 py-1 rounded font-mono text-xs sm:text-sm">/p:init</code>
111
+ </p>
112
+ </div>
113
+ ) : (
114
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
115
+ {projects?.map((project: Project) => (
116
+ <ProjectCard key={project.id} project={project} onDeleteClick={handleDeleteClick} />
117
+ ))}
118
+ </div>
119
+ )}
120
+ </section>
121
+
122
+ <AlertDialog open={!!projectToDelete} onOpenChange={(open: boolean) => !open && setProjectToDelete(null)}>
123
+ <AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
124
+ <AlertDialogHeader>
125
+ <AlertDialogTitle className="flex items-center gap-2">
126
+ <AlertTriangle className="w-5 h-5 text-destructive shrink-0" />
127
+ Delete Project?
128
+ </AlertDialogTitle>
129
+ <AlertDialogDescription>
130
+ <strong>&quot;{projectToDelete?.name}&quot;</strong> will be moved to trash.
131
+ <br />
132
+ <span className="text-muted-foreground text-xs sm:text-sm break-all">
133
+ Location: ~/.prjct-cli/.trash/{projectToDelete?.id}
134
+ </span>
135
+ </AlertDialogDescription>
136
+ </AlertDialogHeader>
137
+ <AlertDialogFooter className="flex-col sm:flex-row gap-2">
138
+ <AlertDialogCancel disabled={isPending} className="w-full sm:w-auto">Cancel</AlertDialogCancel>
139
+ <AlertDialogAction
140
+ onClick={handleConfirmDelete}
141
+ disabled={isPending}
142
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
143
+ >
144
+ {isPending ? 'Deleting...' : 'Delete'}
145
+ </AlertDialogAction>
146
+ </AlertDialogFooter>
147
+ </AlertDialogContent>
148
+ </AlertDialog>
149
+
150
+ {/* Bottom safe area padding */}
151
+ <div className="h-6 sm:h-8" />
152
+ </div>
153
+ )
154
+ }
155
+
156
+ interface ProjectCardProps {
157
+ project: Project
158
+ onDeleteClick: (project: Project, e: React.MouseEvent) => void
159
+ }
160
+
161
+ function ProjectCard({ project, onDeleteClick }: ProjectCardProps) {
162
+ const hasStats = project.currentTask || project.nextTasksCount || project.ideasCount || project.lastActivity
163
+
164
+ return (
165
+ <div className="group relative bg-card border border-border rounded-lg overflow-hidden hover:border-primary/50 active:scale-[0.99] transition-all">
166
+ {project.hasActiveSession && <div className="absolute top-0 left-0 right-0 h-0.5 bg-green-500" />}
167
+
168
+ <Link href={`/project/${project.id}`} className="block p-3 sm:p-4">
169
+ <div className="flex items-start gap-3">
170
+ <ProjectAvatar projectId={project.id} name={project.name} iconPath={project.iconPath} size="lg" />
171
+
172
+ <div className="flex-1 min-w-0">
173
+ <div className="flex items-center gap-2">
174
+ <h3 className="font-bold truncate text-sm sm:text-base">{project.name}</h3>
175
+ {project.hasActiveSession && (
176
+ <span className="flex h-2 w-2 shrink-0">
177
+ <span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-green-400 opacity-75" />
178
+ <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
179
+ </span>
180
+ )}
181
+ </div>
182
+ {project.repoPath && (
183
+ <p className="text-[11px] sm:text-xs text-muted-foreground truncate mt-0.5 flex items-center gap-1">
184
+ <FolderGit2 className="w-3 h-3 shrink-0" />
185
+ {formatPath(project.repoPath)}
186
+ </p>
187
+ )}
188
+ </div>
189
+
190
+ {/* Mobile: Always show menu button */}
191
+ <DropdownMenu>
192
+ <DropdownMenuTrigger asChild>
193
+ <Button
194
+ variant="ghost"
195
+ size="icon-sm"
196
+ className="shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
197
+ onClick={(e) => e.preventDefault()}
198
+ >
199
+ <MoreHorizontal className="w-4 h-4" />
200
+ </Button>
201
+ </DropdownMenuTrigger>
202
+ <DropdownMenuContent align="end">
203
+ <DropdownMenuItem
204
+ className="text-destructive focus:text-destructive min-h-[44px] sm:min-h-0"
205
+ onClick={(e: React.MouseEvent) => onDeleteClick(project, e)}
206
+ >
207
+ <Trash2 className="w-4 h-4 mr-2" />
208
+ Delete
209
+ </DropdownMenuItem>
210
+ </DropdownMenuContent>
211
+ </DropdownMenu>
212
+ </div>
213
+
214
+ {project.currentTask && (
215
+ <div className="mt-3 p-2 bg-primary/5 rounded border border-primary/10">
216
+ <div className="flex items-start gap-2">
217
+ <Target className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
218
+ <span className="text-xs sm:text-sm text-foreground line-clamp-2">{project.currentTask}</span>
219
+ </div>
220
+ </div>
221
+ )}
222
+
223
+ {hasStats && (
224
+ <div className="mt-3 flex flex-wrap items-center gap-2 sm:gap-3 text-[11px] sm:text-xs text-muted-foreground">
225
+ {(project.nextTasksCount ?? 0) > 0 && (
226
+ <div className="flex items-center gap-1">
227
+ <ListTodo className="w-3.5 h-3.5" />
228
+ <span>{project.nextTasksCount} queued</span>
229
+ </div>
230
+ )}
231
+ {(project.ideasCount ?? 0) > 0 && (
232
+ <div className="flex items-center gap-1">
233
+ <Lightbulb className="w-3.5 h-3.5" />
234
+ <span>{project.ideasCount} ideas</span>
235
+ </div>
236
+ )}
237
+ {project.lastActivity && (
238
+ <div className="flex items-center gap-1 ml-auto">
239
+ <Zap className="w-3.5 h-3.5" />
240
+ <span>{formatRelativeTime(project.lastActivity)}</span>
241
+ </div>
242
+ )}
243
+ </div>
244
+ )}
245
+
246
+ {project.techStack && project.techStack.length > 0 && (
247
+ <div className="mt-3">
248
+ <TechStackBadges techStack={project.techStack} />
249
+ </div>
250
+ )}
251
+ </Link>
252
+ </div>
253
+ )
254
+ }
@@ -0,0 +1 @@
1
+ export { DashboardContent } from './DashboardContent'
@@ -0,0 +1,18 @@
1
+ import { EventRow } from '@/components/EventRow'
2
+ import { formatTimelineDate } from './DateGroup.utils'
3
+ import type { DateGroupProps } from './DateGroup.types'
4
+
5
+ export function DateGroup({ dateKey, events }: DateGroupProps) {
6
+ return (
7
+ <div>
8
+ <p className="text-[11px] sm:text-[10px] font-bold uppercase tracking-wider text-muted-foreground mb-2">
9
+ {formatTimelineDate(dateKey + 'T00:00:00')}
10
+ </p>
11
+ <div className="space-y-1">
12
+ {events.map((event, i) => (
13
+ <EventRow key={i} event={event} />
14
+ ))}
15
+ </div>
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,6 @@
1
+ import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
+
3
+ export interface DateGroupProps {
4
+ dateKey: string
5
+ events: TimelineEvent[]
6
+ }
@@ -0,0 +1,11 @@
1
+ export function formatTimelineDate(dateString: string): string {
2
+ const date = new Date(dateString)
3
+ const today = new Date()
4
+ const yesterday = new Date(today)
5
+ yesterday.setDate(yesterday.getDate() - 1)
6
+
7
+ if (date.toDateString() === today.toDateString()) return 'Today'
8
+ if (date.toDateString() === yesterday.toDateString()) return 'Yesterday'
9
+
10
+ return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })
11
+ }
@@ -0,0 +1,2 @@
1
+ export { DateGroup } from './DateGroup'
2
+ export type { DateGroupProps } from './DateGroup.types'
@@ -2,16 +2,7 @@
2
2
 
3
3
  import { cn } from '@/lib/utils'
4
4
  import { Copy } from 'lucide-react'
5
- import type { LucideIcon } from 'lucide-react'
6
-
7
- interface EmptyStateProps {
8
- icon: LucideIcon
9
- title: string
10
- description?: string
11
- command?: string
12
- className?: string
13
- compact?: boolean
14
- }
5
+ import type { EmptyStateProps } from './EmptyState.types'
15
6
 
16
7
  export function EmptyState({
17
8
  icon: Icon,
@@ -0,0 +1,10 @@
1
+ import type { LucideIcon } from 'lucide-react'
2
+
3
+ export interface EmptyStateProps {
4
+ icon: LucideIcon
5
+ title: string
6
+ description?: string
7
+ command?: string
8
+ className?: string
9
+ compact?: boolean
10
+ }
@@ -0,0 +1,2 @@
1
+ export { EmptyState } from './EmptyState'
2
+ export type { EmptyStateProps } from './EmptyState.types'
@@ -0,0 +1,10 @@
1
+ import { Activity, CheckCircle2, Rocket, Target, RefreshCw } from 'lucide-react'
2
+ import type { EventIconName } from './EventRow.types'
3
+
4
+ export const EVENT_ICON_MAP: Record<EventIconName, typeof Activity> = {
5
+ check: CheckCircle2,
6
+ target: Target,
7
+ rocket: Rocket,
8
+ refresh: RefreshCw,
9
+ activity: Activity,
10
+ }
@@ -0,0 +1,49 @@
1
+ import { cn } from '@/lib/utils'
2
+ import { EVENT_ICON_MAP } from './EventRow.constants'
3
+ import {
4
+ formatTime,
5
+ getEventIconName,
6
+ getEventColor,
7
+ getEventBadge,
8
+ getEventLabel,
9
+ } from './EventRow.utils'
10
+ import type { EventRowProps } from './EventRow.types'
11
+
12
+ export function EventRow({ event }: EventRowProps) {
13
+ const iconName = getEventIconName(event.type)
14
+ const Icon = EVENT_ICON_MAP[iconName]
15
+ const duration = 'duration' in event && typeof event.duration === 'string' ? event.duration : null
16
+
17
+ return (
18
+ <div className="flex items-center gap-2 sm:gap-3 py-2 sm:py-1.5 px-2 -mx-2 rounded-md hover:bg-muted/50 active:bg-muted/70 transition-colors group">
19
+ {event.ts && (
20
+ <span className="hidden sm:block text-[10px] text-muted-foreground w-14 shrink-0 tabular-nums">
21
+ {formatTime(event.ts)}
22
+ </span>
23
+ )}
24
+
25
+ <Icon className={cn('h-4 w-4 sm:h-3.5 sm:w-3.5 shrink-0', getEventColor(event.type))} />
26
+
27
+ <div className="flex-1 min-w-0">
28
+ <span className="text-sm truncate block group-hover:text-foreground transition-colors">
29
+ {getEventLabel(event)}
30
+ </span>
31
+ {event.ts && (
32
+ <span className="sm:hidden text-[10px] text-muted-foreground">
33
+ {formatTime(event.ts)}
34
+ </span>
35
+ )}
36
+ </div>
37
+
38
+ <span className="text-[9px] font-bold tracking-wider text-muted-foreground shrink-0">
39
+ {getEventBadge(event.type)}
40
+ </span>
41
+
42
+ {duration && (
43
+ <span className="text-[10px] text-muted-foreground shrink-0">
44
+ {duration}
45
+ </span>
46
+ )}
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,7 @@
1
+ import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
+
3
+ export type EventIconName = 'check' | 'target' | 'rocket' | 'refresh' | 'activity'
4
+
5
+ export interface EventRowProps {
6
+ event: TimelineEvent
7
+ }
@@ -0,0 +1,49 @@
1
+ import type { EventIconName } from './EventRow.types'
2
+
3
+ export function formatTime(dateString: string): string {
4
+ const date = new Date(dateString)
5
+ return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })
6
+ }
7
+
8
+ export function getEventIconName(type: string): EventIconName {
9
+ const iconMap: Record<string, EventIconName> = {
10
+ task_complete: 'check',
11
+ task_start: 'target',
12
+ feature_ship: 'rocket',
13
+ sync: 'refresh',
14
+ }
15
+ return iconMap[type] ?? 'activity'
16
+ }
17
+
18
+ export function getEventColor(type: string): string {
19
+ const colorMap: Record<string, string> = {
20
+ task_complete: 'text-emerald-500',
21
+ task_start: 'text-amber-500',
22
+ feature_ship: 'text-blue-500',
23
+ sync: 'text-muted-foreground',
24
+ }
25
+ return colorMap[type] ?? 'text-muted-foreground'
26
+ }
27
+
28
+ export function getEventBadge(type: string): string {
29
+ const badgeMap: Record<string, string> = {
30
+ task_complete: 'DONE',
31
+ task_start: 'START',
32
+ feature_ship: 'SHIP',
33
+ sync: 'SYNC',
34
+ }
35
+ return badgeMap[type] ?? type.toUpperCase()
36
+ }
37
+
38
+ export function getEventLabel(event: unknown): string {
39
+ const e = event as { type?: string; task?: string; name?: string }
40
+ const type = e.type ?? ''
41
+
42
+ const labelMap: Record<string, string> = {
43
+ task_complete: e.task ?? 'Task completed',
44
+ task_start: e.task ?? 'Task started',
45
+ feature_ship: e.name ?? 'Feature shipped',
46
+ sync: 'Project synced',
47
+ }
48
+ return labelMap[type] ?? type
49
+ }
@@ -0,0 +1,2 @@
1
+ export { EventRow } from './EventRow'
2
+ export type { EventRowProps } from './EventRow.types'
@@ -0,0 +1,18 @@
1
+ import { ChevronDown } from 'lucide-react'
2
+ import { cn } from '@/lib/utils'
3
+ import { Button } from '@/components/ui/button'
4
+ import type { ExpandButtonProps } from './ExpandButton.types'
5
+
6
+ export function ExpandButton({ expanded, totalCount, collapsedLimit, onToggle }: ExpandButtonProps) {
7
+ return (
8
+ <Button
9
+ variant="ghost"
10
+ size="sm"
11
+ onClick={onToggle}
12
+ className="w-full text-muted-foreground min-h-[44px]"
13
+ >
14
+ <ChevronDown className={cn('h-4 w-4 mr-1 transition-transform', expanded && 'rotate-180')} />
15
+ {expanded ? 'Show less' : `Show ${totalCount - collapsedLimit} more`}
16
+ </Button>
17
+ )
18
+ }
@@ -0,0 +1,6 @@
1
+ export interface ExpandButtonProps {
2
+ expanded: boolean
3
+ totalCount: number
4
+ collapsedLimit: number
5
+ onToggle: () => void
6
+ }
@@ -0,0 +1,2 @@
1
+ export { ExpandButton } from './ExpandButton'
2
+ export type { ExpandButtonProps } from './ExpandButton.types'
@@ -0,0 +1,14 @@
1
+ import { cn } from '@/lib/utils'
2
+ import { getHealthGradient } from './HealthGradientBackground.utils'
3
+ import type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'
4
+
5
+ export function HealthGradientBackground({ score }: HealthGradientBackgroundProps) {
6
+ return (
7
+ <div
8
+ className={cn(
9
+ 'absolute inset-0 -m-4 md:-m-8 rounded-2xl opacity-30 blur-3xl transition-colors duration-1000',
10
+ getHealthGradient(score)
11
+ )}
12
+ />
13
+ )
14
+ }
@@ -0,0 +1,5 @@
1
+ export type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
2
+
3
+ export interface HealthGradientBackgroundProps {
4
+ score: number
5
+ }
@@ -0,0 +1,13 @@
1
+ import type { AccentColor } from './HealthGradientBackground.types'
2
+
3
+ export function getHealthColor(score: number): AccentColor {
4
+ if (score >= 70) return 'success'
5
+ if (score >= 40) return 'warning'
6
+ return 'destructive'
7
+ }
8
+
9
+ export function getHealthGradient(score: number): string {
10
+ if (score >= 70) return 'bg-gradient-to-br from-emerald-500/20 to-transparent'
11
+ if (score >= 40) return 'bg-gradient-to-br from-amber-500/20 to-transparent'
12
+ return 'bg-gradient-to-br from-destructive/20 to-transparent'
13
+ }
@@ -0,0 +1,2 @@
1
+ export { HealthGradientBackground } from './HealthGradientBackground'
2
+ export type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'
@@ -0,0 +1,55 @@
1
+ 'use client'
2
+
3
+ import { ProgressRing } from '@/components/ProgressRing'
4
+ import { BackLink } from '@/components/BackLink'
5
+ import { TasksCounter } from '@/components/TasksCounter'
6
+ import { VelocityBadge } from '@/components/VelocityBadge'
7
+ import { InsightMessage } from '@/components/InsightMessage'
8
+ import { WeeklySparkline } from '@/components/WeeklySparkline'
9
+ import { HealthGradientBackground } from '@/components/HealthGradientBackground'
10
+ import { useCountUp, useWeeklyActivity } from './hooks'
11
+ import { getHealthColor } from './HeroSection.utils'
12
+ import type { HeroProps } from './HeroSection.types'
13
+
14
+ export function HeroSection({
15
+ projectId,
16
+ projectName,
17
+ tasksCompleted,
18
+ healthScore,
19
+ velocityChange,
20
+ insightMessage,
21
+ timeline = [],
22
+ }: HeroProps) {
23
+ const animatedCount = useCountUp(tasksCompleted)
24
+ const weeklyData = useWeeklyActivity(timeline)
25
+ const healthColor = getHealthColor(healthScore)
26
+
27
+ return (
28
+ <div className="relative mb-6 md:mb-8">
29
+ <HealthGradientBackground score={healthScore} />
30
+ <BackLink projectId={projectId} projectName={projectName} className="md:hidden mb-4" />
31
+
32
+ <div className="relative flex flex-col md:flex-row md:items-start md:justify-between gap-4 md:gap-0">
33
+ <div className="flex items-center md:items-start gap-4 md:gap-6">
34
+ <ProgressRing
35
+ value={healthScore}
36
+ size="xl"
37
+ accentColor={healthColor}
38
+ className="shrink-0"
39
+ />
40
+
41
+ <div className="flex-1 min-w-0">
42
+ <TasksCounter count={animatedCount} />
43
+ <VelocityBadge change={velocityChange} />
44
+ <InsightMessage message={insightMessage} />
45
+ </div>
46
+ </div>
47
+
48
+ <div className="flex flex-col items-start md:items-end gap-3 md:gap-4">
49
+ <BackLink projectId={projectId} projectName={projectName} className="hidden md:flex" />
50
+ <WeeklySparkline data={weeklyData} />
51
+ </div>
52
+ </div>
53
+ </div>
54
+ )
55
+ }
@@ -0,0 +1,14 @@
1
+ import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
+
3
+ export type { TimelineEvent }
4
+
5
+ export interface HeroProps {
6
+ projectId: string
7
+ projectName: string
8
+ tasksCompleted: number
9
+ healthScore: number
10
+ velocity: number
11
+ velocityChange: number
12
+ insightMessage: string
13
+ timeline?: TimelineEvent[]
14
+ }
@@ -0,0 +1,7 @@
1
+ export type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
2
+
3
+ export function getHealthColor(score: number): AccentColor {
4
+ if (score >= 70) return 'success'
5
+ if (score >= 40) return 'warning'
6
+ return 'destructive'
7
+ }
@@ -0,0 +1,2 @@
1
+ export { useCountUp } from './useCountUp'
2
+ export { useWeeklyActivity } from './useWeeklyActivity'
@@ -0,0 +1,45 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, useState } from 'react'
4
+
5
+ export function useCountUp(target: number, duration: number = 800): number {
6
+ const [count, setCount] = useState(0)
7
+ const prevTargetRef = useRef(target)
8
+
9
+ useEffect(() => {
10
+ if (target === 0) {
11
+ // Only reset if target changed to 0 (not if it was already 0)
12
+ if (prevTargetRef.current !== 0) {
13
+ // Defer state update to avoid synchronous setState in effect
14
+ const timeoutId = setTimeout(() => setCount(0), 0)
15
+ prevTargetRef.current = target
16
+ return () => clearTimeout(timeoutId)
17
+ }
18
+ prevTargetRef.current = target
19
+ return
20
+ }
21
+
22
+ prevTargetRef.current = target
23
+
24
+ const startTime = performance.now()
25
+
26
+ const animate = (currentTime: number) => {
27
+ const elapsed = currentTime - startTime
28
+ const progress = Math.min(elapsed / duration, 1)
29
+ const easeOut = 1 - Math.pow(1 - progress, 3)
30
+ const current = Math.floor(easeOut * target)
31
+
32
+ setCount(current)
33
+
34
+ if (progress < 1) {
35
+ requestAnimationFrame(animate)
36
+ } else {
37
+ setCount(target)
38
+ }
39
+ }
40
+
41
+ requestAnimationFrame(animate)
42
+ }, [target, duration])
43
+
44
+ return count
45
+ }