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
@@ -1,12 +1,18 @@
1
1
  'use client'
2
2
 
3
- import { useQuery } from '@tanstack/react-query'
3
+ import { useState } from 'react'
4
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
5
  import { useTheme } from 'next-themes'
5
- import { Settings as SettingsIcon, CheckCircle, XCircle, Terminal, Sun, Moon, Monitor } from 'lucide-react'
6
+ import { Settings as SettingsIcon, CheckCircle, XCircle, Terminal, Sun, Moon, Monitor, Key, Eye, EyeOff, Loader2, Trash2, RefreshCw, Database } from 'lucide-react'
6
7
  import { Button } from '@/components/ui/button'
8
+ import { Input } from '@/components/ui/input'
7
9
 
8
10
  export default function Settings() {
9
11
  const { theme, setTheme } = useTheme()
12
+ const queryClient = useQueryClient()
13
+ const [apiKey, setApiKey] = useState('')
14
+ const [showKey, setShowKey] = useState(false)
15
+
10
16
  const { data: claudeStatus } = useQuery({
11
17
  queryKey: ['claude-status'],
12
18
  queryFn: async () => {
@@ -16,6 +22,79 @@ export default function Settings() {
16
22
  }
17
23
  })
18
24
 
25
+ const { data: settings } = useQuery({
26
+ queryKey: ['settings'],
27
+ queryFn: async () => {
28
+ const res = await fetch('/api/settings')
29
+ const json = await res.json()
30
+ return json.data
31
+ }
32
+ })
33
+
34
+ const saveKeyMutation = useMutation({
35
+ mutationFn: async (key: string) => {
36
+ const res = await fetch('/api/settings', {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ openRouterApiKey: key })
40
+ })
41
+ if (!res.ok) throw new Error('Failed to save')
42
+ return res.json()
43
+ },
44
+ onSuccess: () => {
45
+ queryClient.invalidateQueries({ queryKey: ['settings'] })
46
+ setApiKey('')
47
+ }
48
+ })
49
+
50
+ const deleteKeyMutation = useMutation({
51
+ mutationFn: async () => {
52
+ const res = await fetch('/api/settings', { method: 'DELETE' })
53
+ if (!res.ok) throw new Error('Failed to delete')
54
+ return res.json()
55
+ },
56
+ onSuccess: () => {
57
+ queryClient.invalidateQueries({ queryKey: ['settings'] })
58
+ }
59
+ })
60
+
61
+ const { data: projects } = useQuery({
62
+ queryKey: ['migrate-projects'],
63
+ queryFn: async () => {
64
+ const res = await fetch('/api/migrate')
65
+ const json = await res.json()
66
+ return json.data?.projects || [] as { id: string; name: string }[]
67
+ }
68
+ })
69
+
70
+ const [selectedProject, setSelectedProject] = useState<string>('')
71
+ const [migrationResults, setMigrationResults] = useState<{
72
+ success: boolean
73
+ results: Array<{ file: string; success: boolean; error?: string }>
74
+ } | null>(null)
75
+
76
+ const migrateMutation = useMutation({
77
+ mutationFn: async (projectId: string) => {
78
+ const res = await fetch('/api/migrate', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({ projectId })
82
+ })
83
+ const json = await res.json()
84
+ if (!res.ok) throw new Error(json.error || 'Migration failed')
85
+ return json.data
86
+ },
87
+ onSuccess: (data) => {
88
+ setMigrationResults(data)
89
+ },
90
+ onError: (error) => {
91
+ setMigrationResults({
92
+ success: false,
93
+ results: [{ file: 'migration', success: false, error: error.message }]
94
+ })
95
+ }
96
+ })
97
+
19
98
  return (
20
99
  <div className="p-6 h-full overflow-auto">
21
100
  <header className="mb-6">
@@ -71,6 +150,147 @@ export default function Settings() {
71
150
  </div>
72
151
  </section>
73
152
 
153
+ {/* OpenRouter API Key */}
154
+ <section className="bg-card border border-border rounded-lg p-6">
155
+ <h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
156
+ <Key className="w-5 h-5" />
157
+ OpenRouter API Key
158
+ </h2>
159
+
160
+ <p className="text-sm text-muted-foreground mb-4">
161
+ Required for AI-powered features like MD→JSON migration. Get your key at{' '}
162
+ <a
163
+ href="https://openrouter.ai/keys"
164
+ target="_blank"
165
+ rel="noopener noreferrer"
166
+ className="text-primary underline"
167
+ >
168
+ openrouter.ai/keys
169
+ </a>
170
+ </p>
171
+
172
+ {settings?.hasApiKey ? (
173
+ <div className="space-y-3">
174
+ <div className="flex items-center gap-3 p-3 bg-muted/50 rounded-md">
175
+ <CheckCircle className="w-5 h-5 text-green-500 shrink-0" />
176
+ <span className="text-sm font-mono">{settings.maskedKey}</span>
177
+ <Button
178
+ variant="ghost"
179
+ size="icon-sm"
180
+ onClick={() => deleteKeyMutation.mutate()}
181
+ disabled={deleteKeyMutation.isPending}
182
+ className="ml-auto text-muted-foreground hover:text-destructive"
183
+ >
184
+ {deleteKeyMutation.isPending ? (
185
+ <Loader2 className="w-4 h-4 animate-spin" />
186
+ ) : (
187
+ <Trash2 className="w-4 h-4" />
188
+ )}
189
+ </Button>
190
+ </div>
191
+ <p className="text-xs text-muted-foreground mb-4">
192
+ API key is stored locally in ~/.prjct-cli/settings.json
193
+ </p>
194
+
195
+ {/* Data Migration Section - only shown when API key exists */}
196
+ <div className="pt-4 border-t border-border">
197
+ <h3 className="text-sm font-medium mb-3 flex items-center gap-2">
198
+ <Database className="w-4 h-4" />
199
+ Data Migration (MD → JSON)
200
+ </h3>
201
+ <p className="text-xs text-muted-foreground mb-3">
202
+ Convert markdown files to structured JSON using AI. This enables better parsing and querying.
203
+ </p>
204
+
205
+ <div className="flex gap-2 mb-3">
206
+ <select
207
+ value={selectedProject}
208
+ onChange={(e) => {
209
+ setSelectedProject(e.target.value)
210
+ setMigrationResults(null)
211
+ }}
212
+ className="flex-1 h-9 rounded-md border border-input bg-transparent px-3 text-sm"
213
+ >
214
+ <option value="">Select a project...</option>
215
+ {(projects as { id: string; name: string }[] || []).map((p) => (
216
+ <option key={p.id} value={p.id}>{p.name}</option>
217
+ ))}
218
+ </select>
219
+ <Button
220
+ onClick={() => selectedProject && migrateMutation.mutate(selectedProject)}
221
+ disabled={!selectedProject || migrateMutation.isPending}
222
+ size="sm"
223
+ >
224
+ {migrateMutation.isPending ? (
225
+ <Loader2 className="w-4 h-4 animate-spin" />
226
+ ) : (
227
+ <>
228
+ <RefreshCw className="w-4 h-4" />
229
+ Sync
230
+ </>
231
+ )}
232
+ </Button>
233
+ </div>
234
+
235
+ {migrationResults && (
236
+ <div className={`p-3 rounded-md text-sm ${migrationResults.success ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}`}>
237
+ <p className={`font-medium mb-2 ${migrationResults.success ? 'text-green-500' : 'text-red-500'}`}>
238
+ {migrationResults.success ? 'Migration complete!' : 'Migration had errors'}
239
+ </p>
240
+ <ul className="space-y-1 text-xs">
241
+ {migrationResults.results.map((r, i) => (
242
+ <li key={i} className="flex items-center gap-2">
243
+ {r.success ? (
244
+ <CheckCircle className="w-3 h-3 text-green-500" />
245
+ ) : (
246
+ <XCircle className="w-3 h-3 text-red-500" />
247
+ )}
248
+ <span>{r.file}</span>
249
+ {r.error && <span className="text-muted-foreground">- {r.error}</span>}
250
+ </li>
251
+ ))}
252
+ </ul>
253
+ </div>
254
+ )}
255
+ </div>
256
+ </div>
257
+ ) : (
258
+ <div className="space-y-3">
259
+ <div className="flex gap-2">
260
+ <div className="relative flex-1">
261
+ <Input
262
+ type={showKey ? 'text' : 'password'}
263
+ placeholder="sk-or-v1-..."
264
+ value={apiKey}
265
+ onChange={(e) => setApiKey(e.target.value)}
266
+ className="pr-10 font-mono"
267
+ />
268
+ <button
269
+ type="button"
270
+ onClick={() => setShowKey(!showKey)}
271
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
272
+ >
273
+ {showKey ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
274
+ </button>
275
+ </div>
276
+ <Button
277
+ onClick={() => saveKeyMutation.mutate(apiKey)}
278
+ disabled={!apiKey || saveKeyMutation.isPending}
279
+ >
280
+ {saveKeyMutation.isPending ? (
281
+ <Loader2 className="w-4 h-4 animate-spin" />
282
+ ) : (
283
+ 'Save'
284
+ )}
285
+ </Button>
286
+ </div>
287
+ {saveKeyMutation.isError && (
288
+ <p className="text-sm text-destructive">Failed to save API key</p>
289
+ )}
290
+ </div>
291
+ )}
292
+ </section>
293
+
74
294
  {/* Claude Code Status */}
75
295
  <section className="bg-card border border-border rounded-lg p-6">
76
296
  <h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
@@ -0,0 +1,2 @@
1
+ export const TIMELINE_COLLAPSED_LIMIT = 8
2
+ export const TIMELINE_EXPANDED_LIMIT = 20
@@ -0,0 +1,50 @@
1
+ 'use client'
2
+
3
+ import { Activity } from 'lucide-react'
4
+ import { BentoCard } from '@/components/BentoCard'
5
+ import { EmptyState } from '@/components/EmptyState'
6
+ import { DateGroup } from '@/components/DateGroup'
7
+ import { ExpandButton } from '@/components/ExpandButton'
8
+ import { useExpandable, useGroupedEvents } from './hooks'
9
+ import { TIMELINE_COLLAPSED_LIMIT, TIMELINE_EXPANDED_LIMIT } from './ActivityTimeline.constants'
10
+ import type { ActivityTimelineProps } from './ActivityTimeline.types'
11
+
12
+ export function ActivityTimeline({ timeline, className }: ActivityTimelineProps) {
13
+ const { expanded, toggle } = useExpandable(false)
14
+ const limit = expanded ? TIMELINE_EXPANDED_LIMIT : TIMELINE_COLLAPSED_LIMIT
15
+ const { grouped, hasMore } = useGroupedEvents(timeline, limit)
16
+
17
+ return (
18
+ <BentoCard
19
+ size="full"
20
+ title="Recent Activity"
21
+ icon={Activity}
22
+ count={timeline.length > 0 ? `${timeline.length} events` : undefined}
23
+ className={className}
24
+ >
25
+ {timeline.length === 0 ? (
26
+ <EmptyState
27
+ icon={Activity}
28
+ title="No recent activity"
29
+ description="Activity will appear as you work"
30
+ compact
31
+ />
32
+ ) : (
33
+ <div className="space-y-3 sm:space-y-4">
34
+ {Array.from(grouped.entries()).map(([dateKey, events]) => (
35
+ <DateGroup key={dateKey} dateKey={dateKey} events={events} />
36
+ ))}
37
+
38
+ {hasMore && (
39
+ <ExpandButton
40
+ expanded={expanded}
41
+ totalCount={timeline.length}
42
+ collapsedLimit={TIMELINE_COLLAPSED_LIMIT}
43
+ onToggle={toggle}
44
+ />
45
+ )}
46
+ </div>
47
+ )}
48
+ </BentoCard>
49
+ )
50
+ }
@@ -0,0 +1,8 @@
1
+ import type { TimelineEvent } from '@/lib/parse-prjct-files'
2
+
3
+ export type { TimelineEvent }
4
+
5
+ export interface ActivityTimelineProps {
6
+ timeline: TimelineEvent[]
7
+ className?: string
8
+ }
@@ -0,0 +1,2 @@
1
+ export { useExpandable } from './useExpandable'
2
+ export { useGroupedEvents } from './useGroupedEvents'
@@ -0,0 +1,9 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+
5
+ export function useExpandable(initialExpanded: boolean = false) {
6
+ const [expanded, setExpanded] = useState(initialExpanded)
7
+ const toggle = () => setExpanded(prev => !prev)
8
+ return { expanded, toggle }
9
+ }
@@ -0,0 +1,23 @@
1
+ 'use client'
2
+
3
+ import { useMemo } from 'react'
4
+ import type { TimelineEvent } from '../ActivityTimeline.types'
5
+
6
+ export function useGroupedEvents(events: TimelineEvent[], limit: number) {
7
+ return useMemo(() => {
8
+ const displayEvents = events.slice(0, limit)
9
+ const groups = new Map<string, TimelineEvent[]>()
10
+
11
+ displayEvents.forEach(event => {
12
+ if (!event.ts) return
13
+ const dateKey = event.ts.split('T')[0]
14
+ const existing = groups.get(dateKey) ?? []
15
+ groups.set(dateKey, [...existing, event])
16
+ })
17
+
18
+ return {
19
+ grouped: groups,
20
+ hasMore: events.length > limit,
21
+ }
22
+ }, [events, limit])
23
+ }
@@ -0,0 +1,2 @@
1
+ export { ActivityTimeline } from './ActivityTimeline'
2
+ export type { ActivityTimelineProps } from './ActivityTimeline.types'
@@ -0,0 +1,63 @@
1
+ import { BentoCard } from '@/components/BentoCard'
2
+ import { EmptyState } from '@/components/EmptyState'
3
+ import { Bot, Star, TrendingUp } from 'lucide-react'
4
+ import { Badge } from '@/components/ui/badge'
5
+ import { cn } from '@/lib/utils'
6
+ import type { AgentsCardProps } from './AgentsCard.types'
7
+
8
+ export function AgentsCard({ agents, className }: AgentsCardProps) {
9
+ const displayAgents = agents.slice(0, 8)
10
+
11
+ return (
12
+ <BentoCard
13
+ size="1x1"
14
+ title="Agents"
15
+ icon={Bot}
16
+ count={agents.length}
17
+ className={className}
18
+ >
19
+ {agents.length === 0 ? (
20
+ <EmptyState
21
+ icon={Bot}
22
+ title="No agents"
23
+ description="Run /p:sync to generate"
24
+ compact
25
+ />
26
+ ) : (
27
+ <div className="flex flex-wrap gap-1.5">
28
+ {displayAgents.map((agent) => {
29
+ const hasPerformance = agent.successRate !== undefined
30
+ const isTopPerformer = hasPerformance && agent.successRate! >= 80
31
+
32
+ return (
33
+ <Badge
34
+ key={agent.name}
35
+ variant="secondary"
36
+ className={cn(
37
+ "text-xs px-2 py-0.5 font-mono inline-flex items-center gap-1",
38
+ isTopPerformer && "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20"
39
+ )}
40
+ >
41
+ {isTopPerformer && <Star className="h-2.5 w-2.5" />}
42
+ @{agent.name}
43
+ {hasPerformance && (
44
+ <span className="text-[9px] opacity-70">
45
+ {agent.successRate}%
46
+ </span>
47
+ )}
48
+ {agent.improving && (
49
+ <TrendingUp className="h-2.5 w-2.5 text-emerald-500" />
50
+ )}
51
+ </Badge>
52
+ )
53
+ })}
54
+ {agents.length > 8 && (
55
+ <Badge variant="outline" className="text-xs px-2 py-0.5">
56
+ +{agents.length - 8}
57
+ </Badge>
58
+ )}
59
+ </div>
60
+ )}
61
+ </BentoCard>
62
+ )
63
+ }
@@ -0,0 +1,13 @@
1
+ export interface Agent {
2
+ name: string
3
+ description?: string
4
+ successRate?: number
5
+ tasksCompleted?: number
6
+ improving?: boolean
7
+ bestFor?: string[]
8
+ }
9
+
10
+ export interface AgentsCardProps {
11
+ agents: Agent[]
12
+ className?: string
13
+ }
@@ -0,0 +1,2 @@
1
+ export { AgentsCard } from './AgentsCard'
2
+ export type { AgentsCardProps } from './AgentsCard.types'
@@ -0,0 +1,134 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { usePathname } from 'next/navigation'
5
+ import {
6
+ LayoutDashboard,
7
+ Settings,
8
+ HelpCircle,
9
+ Menu,
10
+ } from 'lucide-react'
11
+ import { Logo } from '@/components/Logo'
12
+ import { cn } from '@/lib/utils'
13
+ import { useState, useEffect } from 'react'
14
+ import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
15
+
16
+ const navItems = [
17
+ { href: '/', icon: LayoutDashboard, label: 'Dashboard' },
18
+ ]
19
+
20
+ function SidebarContent({ onNavigate }: { onNavigate?: () => void }) {
21
+ const pathname = usePathname()
22
+
23
+ return (
24
+ <>
25
+ {/* Header */}
26
+ <div className="flex h-14 items-center justify-between px-3 border-b border-border">
27
+ <Link href="/" onClick={onNavigate}>
28
+ <Logo size="xs" showText rounded />
29
+ </Link>
30
+ </div>
31
+
32
+ {/* Nav */}
33
+ <nav className="flex-1 overflow-y-auto py-4 px-3">
34
+ <div className="space-y-1">
35
+ {navItems.map(({ href, icon: Icon, label }) => {
36
+ const isActive = pathname === href || (href !== '/' && pathname.startsWith(href))
37
+ return (
38
+ <Link
39
+ key={href}
40
+ href={href}
41
+ onClick={onNavigate}
42
+ className={cn(
43
+ 'flex items-center gap-3 rounded-md px-3 py-2.5 transition-colors min-h-[44px]',
44
+ isActive
45
+ ? 'bg-accent text-accent-foreground'
46
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
47
+ )}
48
+ >
49
+ <Icon className="h-5 w-5 shrink-0" />
50
+ <span className="text-sm font-medium">{label}</span>
51
+ </Link>
52
+ )
53
+ })}
54
+ </div>
55
+ </nav>
56
+
57
+ {/* Footer */}
58
+ <div className="border-t border-border p-3 space-y-1">
59
+ <Link
60
+ href="/settings"
61
+ onClick={onNavigate}
62
+ className={cn(
63
+ 'flex items-center gap-3 rounded-md px-3 py-2.5 transition-colors min-h-[44px]',
64
+ pathname === '/settings'
65
+ ? 'bg-accent text-accent-foreground'
66
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
67
+ )}
68
+ >
69
+ <Settings className="h-5 w-5 shrink-0" />
70
+ <span className="text-sm font-medium">Settings</span>
71
+ </Link>
72
+ <Link
73
+ href="/help"
74
+ onClick={onNavigate}
75
+ className={cn(
76
+ 'flex items-center gap-3 rounded-md px-3 py-2.5 transition-colors min-h-[44px]',
77
+ pathname === '/help'
78
+ ? 'bg-accent text-accent-foreground'
79
+ : 'text-muted-foreground hover:bg-accent hover:text-accent-foreground'
80
+ )}
81
+ >
82
+ <HelpCircle className="h-5 w-5 shrink-0" />
83
+ <span className="text-sm font-medium">Need help?</span>
84
+ </Link>
85
+ </div>
86
+ </>
87
+ )
88
+ }
89
+
90
+ export function AppSidebar() {
91
+ const [isOpen, setIsOpen] = useState(false)
92
+ const [isMobile, setIsMobile] = useState(false)
93
+
94
+ // Detect mobile on mount and resize
95
+ useEffect(() => {
96
+ const checkMobile = () => {
97
+ setIsMobile(window.innerWidth < 768)
98
+ }
99
+ checkMobile()
100
+ window.addEventListener('resize', checkMobile)
101
+ return () => window.removeEventListener('resize', checkMobile)
102
+ }, [])
103
+
104
+ // Mobile: Sheet/Drawer
105
+ if (isMobile) {
106
+ return (
107
+ <Sheet open={isOpen} onOpenChange={setIsOpen}>
108
+ <SheetTrigger asChild>
109
+ <button
110
+ className="fixed top-3 left-3 z-50 h-10 w-10 rounded-lg bg-card border border-border flex items-center justify-center text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors shadow-sm"
111
+ aria-label="Open menu"
112
+ >
113
+ <Menu className="h-5 w-5" />
114
+ </button>
115
+ </SheetTrigger>
116
+ <SheetContent side="left" className="w-[280px] p-0 flex flex-col">
117
+ <SidebarContent onNavigate={() => setIsOpen(false)} />
118
+ </SheetContent>
119
+ </Sheet>
120
+ )
121
+ }
122
+
123
+ // Desktop: Static sidebar
124
+ return (
125
+ <aside className="hidden md:flex h-full w-60 flex-col border-r border-border bg-card">
126
+ <SidebarContent />
127
+ </aside>
128
+ )
129
+ }
130
+
131
+ // Mobile header spacer - use in pages that need top padding for the menu button
132
+ export function MobileHeaderSpacer() {
133
+ return <div className="h-16 md:h-0" />
134
+ }
@@ -0,0 +1 @@
1
+ export { AppSidebar, MobileHeaderSpacer } from './AppSidebar'
@@ -0,0 +1,18 @@
1
+ import Link from 'next/link'
2
+ import { ArrowLeft } from 'lucide-react'
3
+ import { cn } from '@/lib/utils'
4
+ import type { BackLinkProps } from './BackLink.types'
5
+
6
+ export function BackLink({ projectId, projectName, className }: BackLinkProps) {
7
+ return (
8
+ <div className={cn('relative flex', className)}>
9
+ <Link
10
+ href={`/project/${projectId}`}
11
+ className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors min-h-[44px]"
12
+ >
13
+ <ArrowLeft className="w-4 h-4" />
14
+ {projectName}
15
+ </Link>
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,5 @@
1
+ export interface BackLinkProps {
2
+ projectId: string
3
+ projectName: string
4
+ className?: string
5
+ }
@@ -0,0 +1,2 @@
1
+ export { BackLink } from './BackLink'
2
+ export type { BackLinkProps } from './BackLink.types'
@@ -0,0 +1,16 @@
1
+ import type { AccentColor, BentoSize } from './BentoCard.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
+ }
10
+
11
+ export const ACCENT_STYLES: Record<AccentColor, string> = {
12
+ default: '',
13
+ success: 'border-emerald-500/20 bg-emerald-500/5',
14
+ warning: 'border-amber-500/20 bg-amber-500/5',
15
+ destructive: 'border-destructive/20 bg-destructive/5',
16
+ }
@@ -0,0 +1,47 @@
1
+ import { cn } from '@/lib/utils'
2
+ import { BENTO_SIZE_CLASSES, ACCENT_STYLES } from './BentoCard.constants'
3
+ import type { BentoCardProps } from './BentoCard.types'
4
+
5
+ export function BentoCard({
6
+ size = '1x1',
7
+ title,
8
+ count,
9
+ icon: Icon,
10
+ accentColor = 'default',
11
+ className,
12
+ headerClassName,
13
+ children,
14
+ }: BentoCardProps) {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ 'relative overflow-hidden rounded-xl border bg-card transition-all duration-200',
19
+ 'p-3 sm:p-4',
20
+ 'hover:shadow-md hover:border-foreground/20',
21
+ 'active:scale-[0.99]',
22
+ BENTO_SIZE_CLASSES[size],
23
+ ACCENT_STYLES[accentColor],
24
+ className
25
+ )}
26
+ >
27
+ {(title || count !== undefined || Icon) && (
28
+ <div className={cn('flex items-center justify-between mb-2 sm:mb-3', headerClassName)}>
29
+ <div className="flex items-center gap-2">
30
+ {Icon && <Icon className="h-4 w-4 sm:h-3.5 sm:w-3.5 text-muted-foreground" />}
31
+ {title && (
32
+ <span className="text-[11px] sm:text-[10px] font-bold uppercase tracking-[0.15em] text-muted-foreground">
33
+ {title}
34
+ </span>
35
+ )}
36
+ </div>
37
+ {count !== undefined && (
38
+ <span className="text-sm sm:text-xs font-medium text-muted-foreground tabular-nums">
39
+ {count}
40
+ </span>
41
+ )}
42
+ </div>
43
+ )}
44
+ {children}
45
+ </div>
46
+ )
47
+ }