prjct-cli 0.11.4 → 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 (385) hide show
  1. package/CHANGELOG.md +72 -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 +246 -54
  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.ts +405 -0
  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} +99 -89
  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} +35 -18
  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} +62 -23
  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 +203 -403
  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/BentoGrid/BentoGrid.tsx +18 -0
  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/EmptyState/EmptyState.tsx +58 -0
  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/IdeasCard/IdeasCard.tsx +48 -0
  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/ProgressRing/ProgressRing.tsx +51 -0
  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/RoadmapCard/RoadmapCard.tsx +77 -0
  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/ShipsCard/ShipsCard.tsx +52 -0
  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/SparklineChart/SparklineChart.tsx +38 -0
  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/StreakCard/StreakCard.tsx +53 -0
  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/components/ui/tooltip.tsx +2 -2
  333. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  334. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  335. package/packages/web/hooks/useProjectStats.ts +55 -0
  336. package/packages/web/hooks/useProjects.ts +6 -6
  337. package/packages/web/lib/actions/projects.ts +15 -0
  338. package/packages/web/lib/json-loader.ts +630 -0
  339. package/packages/web/lib/services/index.ts +9 -0
  340. package/packages/web/lib/services/migration.server.ts +598 -0
  341. package/packages/web/lib/services/projects.server.ts +52 -0
  342. package/packages/web/lib/services/stats.server.ts +264 -0
  343. package/packages/web/lib/unified-loader.ts +396 -0
  344. package/packages/web/next-env.d.ts +1 -1
  345. package/packages/web/package.json +10 -6
  346. package/packages/web/server.ts +36 -6
  347. package/templates/commands/done.md +76 -32
  348. package/templates/commands/feature.md +121 -47
  349. package/templates/commands/idea.md +81 -8
  350. package/templates/commands/now.md +41 -17
  351. package/templates/commands/ship.md +64 -25
  352. package/templates/commands/sync.md +28 -3
  353. package/core/agentic/agent-router.js +0 -128
  354. package/core/agentic/chain-of-thought.js +0 -578
  355. package/core/agentic/command-executor.js +0 -421
  356. package/core/agentic/context-filter.js +0 -354
  357. package/core/agentic/ground-truth.js +0 -591
  358. package/core/agentic/loop-detector.js +0 -406
  359. package/core/agentic/memory-system.js +0 -850
  360. package/core/agentic/parallel-tools.js +0 -366
  361. package/core/agentic/plan-mode.js +0 -572
  362. package/core/agentic/prompt-builder.js +0 -338
  363. package/core/agentic/response-templates.js +0 -290
  364. package/core/agentic/semantic-compression.js +0 -517
  365. package/core/agentic/think-blocks.js +0 -657
  366. package/core/agentic/tool-registry.js +0 -184
  367. package/core/agentic/validation-rules.js +0 -380
  368. package/core/command-registry.js +0 -698
  369. package/core/commands.js +0 -2237
  370. package/core/domain/task-stack.js +0 -497
  371. package/core/infrastructure/legacy-installer-detector.js +0 -546
  372. package/core/infrastructure/migrator.js +0 -799
  373. package/core/infrastructure/session-manager.js +0 -390
  374. package/core/utils/file-helper.js +0 -329
  375. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  376. package/packages/web/app/api/stats/route.ts +0 -38
  377. package/packages/web/components/AppSidebar.tsx +0 -113
  378. package/packages/web/hooks/useStats.ts +0 -28
  379. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  380. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  381. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  382. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  383. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  384. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  385. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -0,0 +1,15 @@
1
+ import type { LucideIcon } from 'lucide-react'
2
+
3
+ export type BentoSize = '1x1' | '1x2' | '2x1' | '2x2' | 'full'
4
+ export type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
5
+
6
+ export interface BentoCardProps {
7
+ size?: BentoSize
8
+ title?: string
9
+ count?: number | string
10
+ icon?: LucideIcon
11
+ accentColor?: AccentColor
12
+ className?: string
13
+ headerClassName?: string
14
+ children: React.ReactNode
15
+ }
@@ -0,0 +1,2 @@
1
+ export { BentoCard } from './BentoCard'
2
+ export type { BentoCardProps } from './BentoCard.types'
@@ -0,0 +1,9 @@
1
+ import type { BentoSize } from './BentoCardSkeleton.types'
2
+
3
+ export const BENTO_SIZE_CLASSES: Record<BentoSize, string> = {
4
+ '1x1': 'col-span-1 row-span-1',
5
+ '1x2': 'col-span-1 row-span-2',
6
+ '2x1': 'col-span-1 sm:col-span-2 row-span-1',
7
+ '2x2': 'col-span-1 sm:col-span-2 row-span-2',
8
+ 'full': 'col-span-1 sm:col-span-2 lg:col-span-full',
9
+ }
@@ -0,0 +1,18 @@
1
+ import { cn } from '@/lib/utils'
2
+ import { BENTO_SIZE_CLASSES } from './BentoCardSkeleton.constants'
3
+ import type { BentoCardSkeletonProps } from './BentoCardSkeleton.types'
4
+
5
+ export function BentoCardSkeleton({ size = '1x1' }: BentoCardSkeletonProps) {
6
+ return (
7
+ <div
8
+ className={cn(
9
+ 'rounded-xl border bg-card p-4 animate-pulse',
10
+ BENTO_SIZE_CLASSES[size]
11
+ )}
12
+ >
13
+ <div className="h-3 w-16 bg-muted rounded mb-3" />
14
+ <div className="h-6 w-24 bg-muted rounded mb-2" />
15
+ <div className="h-3 w-full bg-muted rounded" />
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,5 @@
1
+ export type BentoSize = '1x1' | '1x2' | '2x1' | '2x2' | 'full'
2
+
3
+ export interface BentoCardSkeletonProps {
4
+ size?: BentoSize
5
+ }
@@ -0,0 +1,2 @@
1
+ export { BentoCardSkeleton } from './BentoCardSkeleton'
2
+ export type { BentoCardSkeletonProps } from './BentoCardSkeleton.types'
@@ -0,0 +1,18 @@
1
+ import { cn } from '@/lib/utils'
2
+ import type { BentoGridProps } from './BentoGrid.types'
3
+
4
+ export function BentoGrid({ className, children }: BentoGridProps) {
5
+ return (
6
+ <div
7
+ className={cn(
8
+ 'grid',
9
+ 'gap-3 sm:gap-4',
10
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
11
+ 'auto-rows-[minmax(120px,auto)] sm:auto-rows-[minmax(140px,auto)]',
12
+ className
13
+ )}
14
+ >
15
+ {children}
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,4 @@
1
+ export interface BentoGridProps {
2
+ children: React.ReactNode
3
+ className?: string
4
+ }
@@ -0,0 +1,2 @@
1
+ export { BentoGrid } from './BentoGrid'
2
+ export type { BentoGridProps } from './BentoGrid.types'
@@ -0,0 +1 @@
1
+ export { CommandButton } from './CommandButton'
@@ -0,0 +1 @@
1
+ export { ConnectionStatus } from './ConnectionStatus'
@@ -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'
@@ -0,0 +1,58 @@
1
+ 'use client'
2
+
3
+ import { cn } from '@/lib/utils'
4
+ import { Copy } from 'lucide-react'
5
+ import type { EmptyStateProps } from './EmptyState.types'
6
+
7
+ export function EmptyState({
8
+ icon: Icon,
9
+ title,
10
+ description,
11
+ command,
12
+ className,
13
+ compact = false,
14
+ }: EmptyStateProps) {
15
+ const handleCopy = () => {
16
+ if (command) {
17
+ navigator.clipboard.writeText(command)
18
+ }
19
+ }
20
+
21
+ if (compact) {
22
+ return (
23
+ <div className={cn('flex items-center gap-2 text-muted-foreground', className)}>
24
+ <Icon className="h-4 w-4" />
25
+ <span className="text-sm">{title}</span>
26
+ {command && (
27
+ <button
28
+ onClick={handleCopy}
29
+ className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs font-mono hover:bg-muted/80"
30
+ >
31
+ {command}
32
+ </button>
33
+ )}
34
+ </div>
35
+ )
36
+ }
37
+
38
+ return (
39
+ <div className={cn('flex flex-col items-center justify-center text-center py-4', className)}>
40
+ <div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3">
41
+ <Icon className="h-5 w-5 text-muted-foreground" />
42
+ </div>
43
+ <p className="text-sm font-medium text-muted-foreground">{title}</p>
44
+ {description && (
45
+ <p className="text-xs text-muted-foreground/70 mt-1">{description}</p>
46
+ )}
47
+ {command && (
48
+ <button
49
+ onClick={handleCopy}
50
+ className="mt-2 inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-muted text-xs font-mono text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors group"
51
+ >
52
+ {command}
53
+ <Copy className="h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity" />
54
+ </button>
55
+ )}
56
+ </div>
57
+ )
58
+ }
@@ -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
+ }