prjct-cli 0.11.5 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +190 -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 +600 -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 +58 -8
  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
@@ -35,6 +35,7 @@ export function TerminalTab({ session, projectDir, isActive }: TerminalTabProps)
35
35
  disconnect,
36
36
  sendInput,
37
37
  focusTerminal,
38
+ fit,
38
39
  } = useClaudeTerminal({
39
40
  sessionId: session.id,
40
41
  projectDir,
@@ -57,6 +58,16 @@ export function TerminalTab({ session, projectDir, isActive }: TerminalTabProps)
57
58
  }
58
59
  }, []) // Empty deps - run only on mount
59
60
 
61
+ // Re-fit terminal when tab becomes active (fixes resize issues when tab was hidden)
62
+ useEffect(() => {
63
+ if (isActive && hasInitializedRef.current) {
64
+ // Use requestAnimationFrame to ensure container is fully visible
65
+ requestAnimationFrame(() => {
66
+ fit()
67
+ })
68
+ }
69
+ }, [isActive, fit])
70
+
60
71
  // Register sendInput and focusTerminal for this session
61
72
  useEffect(() => {
62
73
  registerSendInput(session.id, sendInput)
@@ -90,8 +90,8 @@ export function TerminalTabs({ projectDir }: TerminalTabsProps) {
90
90
 
91
91
  return (
92
92
  <div className="flex flex-col h-full">
93
- {/* Tab bar */}
94
- <div className="flex items-center gap-1 px-2 py-1 bg-card border-b border-border min-h-[40px]">
93
+ {/* Tab bar - scrollable on mobile */}
94
+ <div className="flex items-center gap-1 px-2 py-1.5 bg-card border-b border-border min-h-[44px] md:min-h-[40px] overflow-x-auto scrollbar-hide">
95
95
  {sessions.map(session => (
96
96
  <div
97
97
  key={session.id}
@@ -101,22 +101,23 @@ export function TerminalTabs({ projectDir }: TerminalTabsProps) {
101
101
  tabIndex={0}
102
102
  onKeyDown={(e) => e.key === 'Enter' && setActiveSession(session.id)}
103
103
  className={cn(
104
- 'flex items-center gap-2 px-3 py-1.5 rounded-md text-sm transition-colors cursor-pointer',
105
- 'hover:bg-muted',
104
+ 'flex items-center gap-2 px-3 py-2 md:py-1.5 rounded-md text-sm transition-colors cursor-pointer shrink-0',
105
+ 'hover:bg-muted active:bg-muted/80',
106
+ 'min-h-[36px] md:min-h-0', // Touch-friendly height on mobile
106
107
  session.id === activeSessionId
107
108
  ? 'bg-muted text-foreground'
108
109
  : 'text-muted-foreground'
109
110
  )}
110
111
  >
111
112
  {session.isLoading ? (
112
- <Loader2 className="w-3.5 h-3.5 animate-spin" />
113
+ <Loader2 className="w-3.5 h-3.5 animate-spin shrink-0" />
113
114
  ) : session.isConnected ? (
114
- <span className="relative flex h-2 w-2">
115
+ <span className="relative flex h-2.5 w-2.5 shrink-0">
115
116
  <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
116
- <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
117
+ <span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500" />
117
118
  </span>
118
119
  ) : (
119
- <span className="h-2 w-2 rounded-full bg-muted-foreground/50" />
120
+ <span className="h-2.5 w-2.5 rounded-full bg-muted-foreground/50 shrink-0" />
120
121
  )}
121
122
  {editingSessionId === session.id ? (
122
123
  <input
@@ -130,41 +131,41 @@ export function TerminalTabs({ projectDir }: TerminalTabsProps) {
130
131
  className="w-[100px] bg-background border border-border rounded px-1 py-0.5 text-sm outline-none focus:ring-1 focus:ring-ring"
131
132
  />
132
133
  ) : (
133
- <span className="truncate max-w-[100px]">{session.label}</span>
134
+ <span className="truncate max-w-[80px] md:max-w-[100px]">{session.label}</span>
134
135
  )}
135
136
  <button
136
137
  onClick={(e) => handleCloseTab(session.id, e)}
137
- className="p-0.5 rounded hover:bg-background/50 text-muted-foreground hover:text-foreground"
138
+ className="p-1 md:p-0.5 rounded hover:bg-background/50 text-muted-foreground hover:text-foreground shrink-0"
138
139
  >
139
- <X className="w-3 h-3" />
140
+ <X className="w-4 h-4 md:w-3 md:h-3" />
140
141
  </button>
141
142
  </div>
142
143
  ))}
143
144
 
144
- {/* New tab button */}
145
+ {/* New tab button - larger touch target on mobile */}
145
146
  <Button
146
147
  variant="ghost"
147
148
  size="icon"
148
- className="h-7 w-7 ml-1"
149
+ className="h-9 w-9 md:h-7 md:w-7 ml-1 shrink-0"
149
150
  onClick={createSession}
150
151
  >
151
- <Plus className="w-4 h-4" />
152
+ <Plus className="w-5 h-5 md:w-4 md:h-4" />
152
153
  </Button>
153
154
  </div>
154
155
 
155
156
  {/* Terminal area */}
156
157
  <div className="flex-1 relative">
157
158
  {hasNoSessions ? (
158
- <div className="absolute inset-0 flex items-center justify-center bg-background">
159
- <div className="text-center">
160
- <div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center mx-auto mb-4">
161
- <TerminalIcon className="w-8 h-8 text-muted-foreground" />
159
+ <div className="absolute inset-0 flex items-center justify-center bg-background p-4">
160
+ <div className="text-center max-w-xs">
161
+ <div className="w-14 h-14 md:w-16 md:h-16 rounded-full bg-muted flex items-center justify-center mx-auto mb-3 md:mb-4">
162
+ <TerminalIcon className="w-7 h-7 md:w-8 md:h-8 text-muted-foreground" />
162
163
  </div>
163
- <h2 className="text-lg font-medium mb-2">No active sessions</h2>
164
- <p className="text-muted-foreground text-sm mb-4">
165
- Click + or press Start to create a new terminal
164
+ <h2 className="text-base md:text-lg font-medium mb-1.5 md:mb-2">No active sessions</h2>
165
+ <p className="text-muted-foreground text-sm mb-3 md:mb-4">
166
+ Tap + to create a new terminal
166
167
  </p>
167
- <Button onClick={createSession}>
168
+ <Button onClick={createSession} className="min-h-[44px]">
168
169
  <Plus className="w-4 h-4 mr-2" />
169
170
  New Terminal
170
171
  </Button>
@@ -182,23 +183,23 @@ export function TerminalTabs({ projectDir }: TerminalTabsProps) {
182
183
  )}
183
184
  </div>
184
185
 
185
- {/* Close confirmation dialog */}
186
+ {/* Close confirmation dialog - responsive */}
186
187
  <AlertDialog open={!!sessionToClose} onOpenChange={(open: boolean) => !open && setSessionToClose(null)}>
187
- <AlertDialogContent>
188
+ <AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
188
189
  <AlertDialogHeader>
189
190
  <AlertDialogTitle className="flex items-center gap-2">
190
- <AlertTriangle className="w-5 h-5 text-destructive" />
191
+ <AlertTriangle className="w-5 h-5 text-destructive shrink-0" />
191
192
  Close Terminal?
192
193
  </AlertDialogTitle>
193
194
  <AlertDialogDescription>
194
195
  This terminal has an active session. Closing it will terminate the connection.
195
196
  </AlertDialogDescription>
196
197
  </AlertDialogHeader>
197
- <AlertDialogFooter>
198
- <AlertDialogCancel>Cancel</AlertDialogCancel>
198
+ <AlertDialogFooter className="flex-col sm:flex-row gap-2">
199
+ <AlertDialogCancel className="w-full sm:w-auto">Cancel</AlertDialogCancel>
199
200
  <AlertDialogAction
200
201
  onClick={handleConfirmClose}
201
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
202
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
202
203
  >
203
204
  Close Terminal
204
205
  </AlertDialogAction>
@@ -0,0 +1 @@
1
+ export { TerminalTabs } from './TerminalTabs'
@@ -0,0 +1,27 @@
1
+ import { TrendingUp, TrendingDown } from 'lucide-react'
2
+ import { cn } from '@/lib/utils'
3
+ import type { VelocityBadgeProps } from './VelocityBadge.types'
4
+
5
+ export function VelocityBadge({ change }: VelocityBadgeProps) {
6
+ if (change === 0) return null
7
+
8
+ const isPositive = change >= 0
9
+ const Icon = isPositive ? TrendingUp : TrendingDown
10
+
11
+ return (
12
+ <div className="flex items-center gap-2 mt-2">
13
+ <span
14
+ className={cn(
15
+ 'inline-flex items-center gap-1 text-xs sm:text-sm font-medium px-2 py-0.5 rounded-md',
16
+ isPositive
17
+ ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
18
+ : 'bg-muted text-muted-foreground'
19
+ )}
20
+ >
21
+ <Icon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
22
+ {isPositive ? '+' : ''}{change}%
23
+ </span>
24
+ <span className="text-xs sm:text-sm text-muted-foreground">vs last week</span>
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,3 @@
1
+ export interface VelocityBadgeProps {
2
+ change: number
3
+ }
@@ -0,0 +1,2 @@
1
+ export { VelocityBadge } from './VelocityBadge'
2
+ export type { VelocityBadgeProps } from './VelocityBadge.types'
@@ -0,0 +1,71 @@
1
+ import { BentoCard } from '@/components/BentoCard'
2
+ import { SparklineChart } from '@/components/SparklineChart'
3
+ import { Zap, TrendingUp, TrendingDown, Target } from 'lucide-react'
4
+ import { cn } from '@/lib/utils'
5
+ import type { VelocityCardProps } from './VelocityCard.types'
6
+
7
+ export function VelocityCard({
8
+ tasksPerDay,
9
+ weeklyData = [],
10
+ change = 0,
11
+ estimateAccuracy,
12
+ className,
13
+ }: VelocityCardProps) {
14
+ return (
15
+ <BentoCard
16
+ size="1x1"
17
+ title="Velocity"
18
+ icon={Zap}
19
+ className={className}
20
+ >
21
+ <div className="flex flex-col h-full justify-between">
22
+ <div>
23
+ <p className="text-3xl font-bold tabular-nums">{tasksPerDay}</p>
24
+ <p className="text-xs text-muted-foreground">tasks/day</p>
25
+ </div>
26
+
27
+ {weeklyData.length > 0 && (
28
+ <div className="mt-2">
29
+ <SparklineChart data={weeklyData} height={28} />
30
+ </div>
31
+ )}
32
+
33
+ <div className="flex items-center justify-between mt-2">
34
+ {change !== 0 && (
35
+ <div className="flex items-center gap-1">
36
+ {change >= 0 ? (
37
+ <TrendingUp className="h-3 w-3 text-emerald-500" />
38
+ ) : (
39
+ <TrendingDown className="h-3 w-3 text-muted-foreground" />
40
+ )}
41
+ <span
42
+ className={cn(
43
+ 'text-xs font-medium',
44
+ change >= 0 ? 'text-emerald-600 dark:text-emerald-400' : 'text-muted-foreground'
45
+ )}
46
+ >
47
+ {change >= 0 ? '+' : ''}{change}%
48
+ </span>
49
+ </div>
50
+ )}
51
+
52
+ {estimateAccuracy !== undefined && estimateAccuracy > 0 && (
53
+ <div className="flex items-center gap-1">
54
+ <Target className="h-3 w-3 text-muted-foreground" />
55
+ <span
56
+ className={cn(
57
+ 'text-xs font-medium',
58
+ estimateAccuracy >= 70 ? 'text-emerald-600 dark:text-emerald-400' :
59
+ estimateAccuracy >= 40 ? 'text-amber-600 dark:text-amber-400' :
60
+ 'text-muted-foreground'
61
+ )}
62
+ >
63
+ {estimateAccuracy}% acc
64
+ </span>
65
+ </div>
66
+ )}
67
+ </div>
68
+ </div>
69
+ </BentoCard>
70
+ )
71
+ }
@@ -0,0 +1,7 @@
1
+ export interface VelocityCardProps {
2
+ tasksPerDay: number
3
+ weeklyData?: number[]
4
+ change?: number
5
+ estimateAccuracy?: number
6
+ className?: string
7
+ }
@@ -0,0 +1,2 @@
1
+ export { VelocityCard } from './VelocityCard'
2
+ export type { VelocityCardProps } from './VelocityCard.types'
@@ -0,0 +1,13 @@
1
+ import { SparklineChart } from '@/components/SparklineChart'
2
+ import type { WeeklySparklineProps } from './WeeklySparkline.types'
3
+
4
+ export function WeeklySparkline({ data }: WeeklySparklineProps) {
5
+ return (
6
+ <div className="w-full sm:w-32">
7
+ <p className="text-[10px] uppercase tracking-wider text-muted-foreground mb-1 md:text-right">
8
+ 7-day activity
9
+ </p>
10
+ <SparklineChart data={data} height={40} />
11
+ </div>
12
+ )
13
+ }
@@ -0,0 +1,3 @@
1
+ export interface WeeklySparklineProps {
2
+ data: number[]
3
+ }
@@ -0,0 +1,2 @@
1
+ export { WeeklySparkline } from './WeeklySparkline'
2
+ export type { WeeklySparklineProps } from './WeeklySparkline.types'
@@ -0,0 +1,21 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ export { Input }
@@ -4,7 +4,7 @@
4
4
  * Terminal Tabs Context - Manage multiple terminal sessions
5
5
  */
6
6
 
7
- import { createContext, useContext, useCallback, useState, useRef, ReactNode } from 'react'
7
+ import { createContext, useContext, useCallback, useState, useRef, useEffect, ReactNode } from 'react'
8
8
 
9
9
  export interface TerminalSession {
10
10
  id: string
@@ -108,6 +108,51 @@ export function TerminalTabsProvider({ children, projectId }: { children: ReactN
108
108
  }
109
109
  }, [activeSessionId, sessions])
110
110
 
111
+ // Check if any session is connected
112
+ const hasConnectedSessions = sessions.some(s => s.isConnected)
113
+
114
+ // Prevent page unload when terminal sessions are active
115
+ useEffect(() => {
116
+ if (!hasConnectedSessions) return
117
+
118
+ const handleBeforeUnload = (e: BeforeUnloadEvent) => {
119
+ e.preventDefault()
120
+ // Modern browsers require returnValue to be set
121
+ e.returnValue = 'You have active terminal sessions. Are you sure you want to leave?'
122
+ return e.returnValue
123
+ }
124
+
125
+ window.addEventListener('beforeunload', handleBeforeUnload)
126
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload)
127
+ }, [hasConnectedSessions])
128
+
129
+ // Prevent back/forward navigation when terminal sessions are active
130
+ useEffect(() => {
131
+ if (!hasConnectedSessions) return
132
+
133
+ // Push a state to history so we can intercept back navigation
134
+ const currentPath = window.location.pathname + window.location.search
135
+ window.history.pushState({ terminalActive: true }, '', currentPath)
136
+
137
+ const handlePopState = (e: PopStateEvent) => {
138
+ // User pressed back/forward
139
+ const confirmLeave = window.confirm(
140
+ 'You have active terminal sessions. Leaving will terminate them. Are you sure?'
141
+ )
142
+
143
+ if (!confirmLeave) {
144
+ // Cancel navigation by pushing state back
145
+ window.history.pushState({ terminalActive: true }, '', currentPath)
146
+ }
147
+ // If confirmed, let the navigation happen naturally
148
+ }
149
+
150
+ window.addEventListener('popstate', handlePopState)
151
+ return () => {
152
+ window.removeEventListener('popstate', handlePopState)
153
+ }
154
+ }, [hasConnectedSessions])
155
+
111
156
  return (
112
157
  <TerminalTabsContext.Provider value={{
113
158
  sessions,
@@ -89,6 +89,9 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
89
89
  const reconnectAttemptsRef = useRef(0)
90
90
  const intentionalDisconnectRef = useRef(false)
91
91
  const currentSessionIdRef = useRef<string | null>(null)
92
+ const resizeObserverRef = useRef<ResizeObserver | null>(null)
93
+ const outputBufferRef = useRef<string[]>([])
94
+ const flushScheduledRef = useRef(false)
92
95
 
93
96
  const [isConnected, setIsConnected] = useState(false)
94
97
  const [isLoading, setIsLoading] = useState(false)
@@ -122,7 +125,10 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
122
125
  fontFamily: '"JetBrains Mono", "Fira Code", monospace',
123
126
  fontSize: 14,
124
127
  lineHeight: 1.2,
125
- theme: initialTheme
128
+ theme: initialTheme,
129
+ // Performance optimizations
130
+ scrollback: 5000,
131
+ smoothScrollDuration: 0, // Instant scroll for better performance
126
132
  })
127
133
 
128
134
  const fitAddon = new FitAddon()
@@ -138,26 +144,27 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
138
144
  fitAddonRef.current = fitAddon
139
145
  containerRef.current = container
140
146
 
141
- // Handle window resize
142
- const handleResize = () => {
143
- if (fitAddonRef.current) {
144
- fitAddonRef.current.fit()
145
-
146
- // Send resize to server
147
- if (wsRef.current?.readyState === WebSocket.OPEN && terminalRef.current) {
148
- wsRef.current.send(JSON.stringify({
149
- type: 'resize',
150
- cols: terminalRef.current.cols,
151
- rows: terminalRef.current.rows
152
- }))
147
+ // Use ResizeObserver for better resize handling (works with container, not just window)
148
+ const resizeObserver = new ResizeObserver(() => {
149
+ if (fitAddonRef.current && containerRef.current) {
150
+ // Only fit if container has dimensions (not hidden)
151
+ if (containerRef.current.offsetWidth > 0 && containerRef.current.offsetHeight > 0) {
152
+ fitAddonRef.current.fit()
153
+
154
+ // Send resize to server
155
+ if (wsRef.current?.readyState === WebSocket.OPEN && terminalRef.current) {
156
+ wsRef.current.send(JSON.stringify({
157
+ type: 'resize',
158
+ cols: terminalRef.current.cols,
159
+ rows: terminalRef.current.rows
160
+ }))
161
+ }
153
162
  }
154
163
  }
155
- }
156
-
157
- window.addEventListener('resize', handleResize)
164
+ })
158
165
 
159
- // Note: cleanup is handled by the component unmount, not here
160
- // The terminal persists for the lifetime of the TerminalTab component
166
+ resizeObserver.observe(container)
167
+ resizeObserverRef.current = resizeObserver
161
168
  }, [])
162
169
 
163
170
  // Clear reconnect timeout
@@ -238,13 +245,28 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
238
245
  }
239
246
  }
240
247
 
248
+ // Flush buffered output using requestAnimationFrame for smooth rendering
249
+ const flushOutput = () => {
250
+ if (outputBufferRef.current.length > 0 && terminalRef.current) {
251
+ const combined = outputBufferRef.current.join('')
252
+ terminalRef.current.write(combined)
253
+ outputBufferRef.current = []
254
+ }
255
+ flushScheduledRef.current = false
256
+ }
257
+
241
258
  ws.onmessage = (event) => {
242
259
  try {
243
260
  const message = JSON.parse(event.data)
244
261
 
245
262
  switch (message.type) {
246
263
  case 'output':
247
- terminalRef.current?.write(message.data)
264
+ // Buffer output and flush on next animation frame for smooth rendering
265
+ outputBufferRef.current.push(message.data)
266
+ if (!flushScheduledRef.current) {
267
+ flushScheduledRef.current = true
268
+ requestAnimationFrame(flushOutput)
269
+ }
248
270
  break
249
271
 
250
272
  case 'connected':
@@ -261,8 +283,12 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
261
283
  break
262
284
  }
263
285
  } catch {
264
- // Raw data, write directly
265
- terminalRef.current?.write(event.data)
286
+ // Raw data, write directly (also buffered)
287
+ outputBufferRef.current.push(event.data)
288
+ if (!flushScheduledRef.current) {
289
+ flushScheduledRef.current = true
290
+ requestAnimationFrame(flushOutput)
291
+ }
266
292
  }
267
293
  }
268
294
 
@@ -347,6 +373,25 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
347
373
  }
348
374
  }, [])
349
375
 
376
+ // Fit terminal to container (useful when tab becomes visible)
377
+ const fit = useCallback(() => {
378
+ if (fitAddonRef.current && containerRef.current) {
379
+ // Only fit if container has dimensions (not hidden)
380
+ if (containerRef.current.offsetWidth > 0 && containerRef.current.offsetHeight > 0) {
381
+ fitAddonRef.current.fit()
382
+
383
+ // Send resize to server
384
+ if (wsRef.current?.readyState === WebSocket.OPEN && terminalRef.current) {
385
+ wsRef.current.send(JSON.stringify({
386
+ type: 'resize',
387
+ cols: terminalRef.current.cols,
388
+ rows: terminalRef.current.rows
389
+ }))
390
+ }
391
+ }
392
+ }
393
+ }, [])
394
+
350
395
  // Cleanup on unmount - empty deps to run only on unmount
351
396
  useEffect(() => {
352
397
  return () => {
@@ -357,6 +402,10 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
357
402
  if (wsRef.current) {
358
403
  wsRef.current.close()
359
404
  }
405
+ // Cleanup ResizeObserver
406
+ if (resizeObserverRef.current) {
407
+ resizeObserverRef.current.disconnect()
408
+ }
360
409
  }
361
410
  }, []) // Empty deps - run cleanup only on unmount
362
411
 
@@ -366,6 +415,7 @@ export function useClaudeTerminal(options: UseClaudeTerminalOptions) {
366
415
  disconnect,
367
416
  sendInput,
368
417
  focusTerminal,
418
+ fit,
369
419
  isConnected,
370
420
  isLoading,
371
421
  isReconnecting,
@@ -2,6 +2,13 @@
2
2
 
3
3
  import { useQuery } from '@tanstack/react-query'
4
4
  import type { ProjectStats, RawProjectFiles } from '@/lib/parse-prjct-files'
5
+ import type {
6
+ UnifiedApiResponse,
7
+ ProjectState,
8
+ OutcomeSummary,
9
+ AgentPerformance,
10
+ ProjectInsights,
11
+ } from '@prjct/shared'
5
12
 
6
13
  interface ProjectStatsResponse {
7
14
  success: boolean
@@ -15,6 +22,7 @@ interface ProjectStatsData {
15
22
  raw: RawProjectFiles
16
23
  }
17
24
 
25
+ // Legacy fetch
18
26
  async function fetchProjectStats(projectId: string): Promise<ProjectStatsData> {
19
27
  const res = await fetch(`/api/projects/${projectId}/stats`, {
20
28
  cache: 'no-store',
@@ -36,3 +44,50 @@ export function useProjectStats(projectId: string) {
36
44
  enabled: !!projectId,
37
45
  })
38
46
  }
47
+
48
+ // ============== Unified API ==============
49
+
50
+ interface UnifiedProjectData {
51
+ state: ProjectState | null
52
+ outcomes: OutcomeSummary | null
53
+ agentPerformance: AgentPerformance[]
54
+ insights: ProjectInsights
55
+ legacyFallback: boolean
56
+ // Legacy data when fallback
57
+ legacyData?: ProjectStats
58
+ legacyRaw?: RawProjectFiles
59
+ }
60
+
61
+ async function fetchUnifiedProjectData(projectId: string): Promise<UnifiedProjectData> {
62
+ const res = await fetch(`/api/v2/projects/${projectId}/unified`, {
63
+ cache: 'no-store',
64
+ })
65
+ if (!res.ok) throw new Error('Failed to fetch unified project data')
66
+ const json: UnifiedApiResponse = await res.json()
67
+ if (!json.success) {
68
+ throw new Error('Failed to fetch unified project data')
69
+ }
70
+ return {
71
+ state: json.state,
72
+ outcomes: json.outcomes,
73
+ agentPerformance: json.agentPerformance,
74
+ insights: json.insights,
75
+ legacyFallback: json.legacyFallback,
76
+ legacyData: json.legacyData as ProjectStats | undefined,
77
+ legacyRaw: json.legacyRaw as RawProjectFiles | undefined,
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Hook for fetching unified project data from v2 API.
83
+ * Falls back to legacy data if unified state doesn't exist.
84
+ */
85
+ export function useUnifiedProjectStats(projectId: string) {
86
+ return useQuery({
87
+ queryKey: ['project-unified', projectId],
88
+ queryFn: () => fetchUnifiedProjectData(projectId),
89
+ staleTime: 30_000, // Cache 30s
90
+ refetchOnWindowFocus: true,
91
+ enabled: !!projectId,
92
+ })
93
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
4
4
  import { queryPresets } from '@/lib/query-config'
5
+ import { deleteProject as deleteProjectAction } from '@/lib/actions/projects'
5
6
 
6
7
  export interface Project {
7
8
  id: string
@@ -53,18 +54,17 @@ export function useProject(projectId: string | null) {
53
54
  })
54
55
  }
55
56
 
56
- // Hook for deleting a project
57
+ // Hook for deleting a project (uses Server Action)
57
58
  export function useDeleteProject() {
58
59
  const queryClient = useQueryClient()
59
60
 
60
61
  return useMutation({
61
62
  mutationFn: async (projectId: string) => {
62
- const res = await fetch(`/api/projects/${projectId}/delete`, { method: 'POST' })
63
- if (!res.ok) {
64
- const error = await res.json()
65
- throw new Error(error.error || 'Failed to delete project')
63
+ const result = await deleteProjectAction(projectId)
64
+ if (!result.success) {
65
+ throw new Error(result.error || 'Failed to delete project')
66
66
  }
67
- return res.json()
67
+ return result
68
68
  },
69
69
  onSuccess: () => {
70
70
  queryClient.invalidateQueries({ queryKey: ['projects'] })
@@ -0,0 +1,15 @@
1
+ 'use server'
2
+
3
+ import { revalidatePath } from 'next/cache'
4
+ import { moveToTrash } from '@/lib/projects'
5
+
6
+ export async function deleteProject(projectId: string) {
7
+ try {
8
+ const result = await moveToTrash(projectId)
9
+ revalidatePath('/')
10
+ return { success: true, ...result }
11
+ } catch (error) {
12
+ const message = error instanceof Error ? error.message : 'Failed to delete project'
13
+ return { success: false, error: message }
14
+ }
15
+ }