prjct-cli 0.11.5 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +226 -50
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +204 -163
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +134 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  333. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  334. package/packages/web/hooks/useProjectStats.ts +55 -0
  335. package/packages/web/hooks/useProjects.ts +6 -6
  336. package/packages/web/lib/actions/projects.ts +15 -0
  337. package/packages/web/lib/json-loader.ts +630 -0
  338. package/packages/web/lib/services/index.ts +9 -0
  339. package/packages/web/lib/services/migration.server.ts +598 -0
  340. package/packages/web/lib/services/projects.server.ts +52 -0
  341. package/packages/web/lib/services/stats.server.ts +264 -0
  342. package/packages/web/lib/unified-loader.ts +396 -0
  343. package/packages/web/package.json +10 -7
  344. package/packages/web/server.ts +36 -6
  345. package/templates/commands/done.md +76 -32
  346. package/templates/commands/feature.md +121 -47
  347. package/templates/commands/idea.md +81 -8
  348. package/templates/commands/now.md +41 -17
  349. package/templates/commands/ship.md +64 -25
  350. package/templates/commands/sync.md +28 -3
  351. package/core/agentic/agent-router.js +0 -140
  352. package/core/agentic/chain-of-thought.js +0 -578
  353. package/core/agentic/command-executor.js +0 -417
  354. package/core/agentic/context-filter.js +0 -354
  355. package/core/agentic/ground-truth.js +0 -591
  356. package/core/agentic/loop-detector.js +0 -406
  357. package/core/agentic/memory-system.js +0 -845
  358. package/core/agentic/parallel-tools.js +0 -366
  359. package/core/agentic/plan-mode.js +0 -572
  360. package/core/agentic/prompt-builder.js +0 -352
  361. package/core/agentic/response-templates.js +0 -290
  362. package/core/agentic/semantic-compression.js +0 -517
  363. package/core/agentic/think-blocks.js +0 -657
  364. package/core/agentic/tool-registry.js +0 -184
  365. package/core/agentic/validation-rules.js +0 -380
  366. package/core/command-registry.js +0 -698
  367. package/core/commands.js +0 -2237
  368. package/core/domain/task-stack.js +0 -497
  369. package/core/infrastructure/legacy-installer-detector.js +0 -546
  370. package/core/infrastructure/migrator.js +0 -796
  371. package/core/infrastructure/session-manager.js +0 -390
  372. package/core/utils/file-helper.js +0 -329
  373. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  374. package/packages/web/app/api/stats/route.ts +0 -38
  375. package/packages/web/components/AppSidebar.tsx +0 -113
  376. package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
  377. package/packages/web/components/stats/AgentsCard.tsx +0 -56
  378. package/packages/web/components/stats/BentoCard.tsx +0 -88
  379. package/packages/web/components/stats/HeroSection.tsx +0 -172
  380. package/packages/web/components/stats/NowCard.tsx +0 -71
  381. package/packages/web/components/stats/QueueCard.tsx +0 -58
  382. package/packages/web/components/stats/VelocityCard.tsx +0 -60
  383. package/packages/web/components/stats/index.ts +0 -17
  384. package/packages/web/hooks/useStats.ts +0 -28
  385. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  386. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  387. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  388. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  389. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  390. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  391. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -1,206 +1,247 @@
1
- 'use client'
2
-
3
- import { use, useMemo } from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import { useProject } from '@/hooks/useProjects'
6
- import { useProjectStats } from '@/hooks/useProjectStats'
7
- import { Button } from '@/components/ui/button'
8
- import { ArrowLeft } from 'lucide-react'
1
+ import { notFound } from 'next/navigation'
2
+ import {
3
+ getStats,
4
+ getInsightMessage,
5
+ calculateStreak,
6
+ calculateHealthScore,
7
+ getVelocityChange,
8
+ getWeeklyVelocityData,
9
+ type StatsResult
10
+ } from '@/lib/services/stats.server'
11
+ import { getProject } from '@/lib/services/projects.server'
9
12
  import type { TimelineEvent } from '@/lib/parse-prjct-files'
10
13
 
11
- import {
12
- BentoGrid,
13
- BentoCardSkeleton,
14
- HeroSection,
15
- NowCard,
16
- VelocityCard,
17
- StreakCard,
18
- QueueCard,
19
- ShipsCard,
20
- IdeasCard,
21
- AgentsCard,
22
- RoadmapCard,
23
- ActivityTimeline,
24
- } from '@/components/stats'
25
-
26
- // Calculate streak from timeline
27
- function calculateStreak(timeline: TimelineEvent[]): number {
28
- if (!timeline.length) return 0
29
- const dates = new Set(timeline.map(e => e.ts?.split('T')[0]).filter(Boolean))
30
- let streak = 0
31
- const today = new Date()
32
- for (let i = 0; i < 30; i++) {
33
- const date = new Date(today)
34
- date.setDate(date.getDate() - i)
35
- const dateStr = date.toISOString().split('T')[0]
36
- if (dates.has(dateStr)) streak++
37
- else if (i > 0) break
38
- }
39
- return streak
14
+ import { BentoGrid } from '@/components/BentoGrid'
15
+ import { HeroSection } from '@/components/HeroSection'
16
+ import { NowCard } from '@/components/NowCard'
17
+ import { VelocityCard } from '@/components/VelocityCard'
18
+ import { StreakCard } from '@/components/StreakCard'
19
+ import { QueueCard } from '@/components/QueueCard'
20
+ import { ShipsCard } from '@/components/ShipsCard'
21
+ import { IdeasCard } from '@/components/IdeasCard'
22
+ import { AgentsCard } from '@/components/AgentsCard'
23
+ import { RoadmapCard } from '@/components/RoadmapCard'
24
+ import { ActivityTimeline } from '@/components/ActivityTimeline'
25
+
26
+ // Types for normalized component data
27
+ interface NormalizedCurrentTask {
28
+ task: string
29
+ startedAt?: string
30
+ agent?: string
31
+ estimatedDuration?: string
32
+ pausedAt?: string
33
+ pauseReason?: string
40
34
  }
41
35
 
42
- // Health score (0-100)
43
- function getHealthScore(stats: any): number {
44
- if (!stats) return 0
45
- const velocity = stats?.metrics?.velocity?.tasksPerDay || 0
46
- const hasCurrentTask = !!stats?.currentTask
47
- const queueSize = stats?.queue?.length || 0
48
- const recentActivity = stats?.timeline?.slice(0, 7).length || 0
36
+ interface NormalizedQueueItem {
37
+ task: string
38
+ priority?: 'low' | 'medium' | 'high' | 'critical' | number
39
+ estimatedDuration?: string
40
+ }
49
41
 
50
- let score = 0
51
- score += Math.min(30, velocity * 15)
52
- score += hasCurrentTask ? 20 : 0
53
- score += queueSize > 0 && queueSize < 15 ? 20 : queueSize === 0 ? 5 : 10
54
- score += Math.min(30, recentActivity * 5)
42
+ interface NormalizedShip {
43
+ name: string
44
+ date: string
45
+ duration?: string
46
+ }
55
47
 
56
- return Math.min(100, Math.round(score))
48
+ interface NormalizedIdea {
49
+ title: string
50
+ impact?: string
57
51
  }
58
52
 
59
- // Contextual insight message
60
- function getInsightMessage(stats: any, streak: number): string {
61
- if (!stats) return ''
53
+ interface NormalizedAgent {
54
+ name: string
55
+ description?: string
56
+ successRate?: number
57
+ tasksCompleted?: number
58
+ bestFor?: string[]
59
+ }
62
60
 
63
- const velocity = stats?.metrics?.velocity?.tasksPerDay || 0
64
- const hasCurrentTask = !!stats?.currentTask
65
- const queueSize = stats?.queue?.length || 0
66
- const shipsCount = stats?.summary?.totalShipsEver || 0
61
+ interface NormalizedRoadmap {
62
+ phases: Array<{
63
+ name: string
64
+ progress: number
65
+ features?: Array<{ name: string; status: string }>
66
+ }>
67
+ progress: number
68
+ }
67
69
 
68
- if (hasCurrentTask && streak > 3) return 'Killing it. Keep the momentum.'
69
- if (hasCurrentTask) return 'Good focus. Ship when ready.'
70
- if (queueSize === 0) return 'Queue empty. Time to plan the next feature.'
71
- if (velocity > 2) return 'Fast pace. Watch for burnout.'
72
- if (shipsCount === 0) return 'No ships yet. Start small, ship fast.'
73
- if (streak === 0) return 'Get back in the flow. Start something.'
74
- return 'Steady progress. Pick the next task.'
70
+ // Data normalization functions
71
+ function normalizeCurrentTask(stats: StatsResult): NormalizedCurrentTask | null {
72
+ if (stats.state?.currentTask) {
73
+ return {
74
+ task: stats.state.currentTask.description,
75
+ startedAt: stats.state.currentTask.startedAt,
76
+ // Simplified - removed legacy fields not in new schema
77
+ }
78
+ }
79
+ return stats.legacyStats?.currentTask ?? null
75
80
  }
76
81
 
77
- // Calculate velocity change (simulated)
78
- function getVelocityChange(velocity: number): number {
79
- return velocity > 2 ? 15 : velocity > 1 ? 5 : velocity > 0 ? 0 : -10
82
+ function normalizeQueue(stats: StatsResult): NormalizedQueueItem[] {
83
+ if (stats.queue?.tasks) {
84
+ return stats.queue.tasks
85
+ .filter(t => !t.completed)
86
+ .map(q => ({
87
+ task: q.description,
88
+ priority: q.priority,
89
+ }))
90
+ }
91
+ return stats.legacyStats?.queue ?? []
80
92
  }
81
93
 
82
- // Get weekly velocity data from timeline
83
- function getWeeklyVelocityData(timeline: TimelineEvent[]): number[] {
84
- const today = new Date()
85
- const counts: number[] = []
94
+ function normalizeRoadmap(stats: StatsResult): NormalizedRoadmap | null {
95
+ const features = stats.roadmap?.features ?? []
96
+ if (features.length > 0) {
97
+ const completed = features.filter(f =>
98
+ f.status === 'shipped' || f.status === 'completed'
99
+ ).length
100
+
101
+ return {
102
+ phases: features.map(f => ({
103
+ name: f.name,
104
+ progress: f.status === 'shipped' || f.status === 'completed' ? 100 :
105
+ f.status === 'active' ? 50 : 0,
106
+ features: f.tasks.map(t => ({
107
+ name: t.description,
108
+ status: t.completed ? 'completed' : 'pending'
109
+ }))
110
+ })),
111
+ progress: Math.round((completed / features.length) * 100)
112
+ }
113
+ }
114
+ return stats.legacyStats?.roadmap ?? null
115
+ }
116
+
117
+ function normalizeShipped(stats: StatsResult): NormalizedShip[] {
118
+ const items = stats.shipped?.items ?? []
119
+ return items.map(s => ({
120
+ name: s.name,
121
+ date: s.shippedAt,
122
+ }))
123
+ }
86
124
 
87
- for (let i = 6; i >= 0; i--) {
88
- const date = new Date(today)
89
- date.setDate(date.getDate() - i)
90
- const dateStr = date.toISOString().split('T')[0]
125
+ function normalizeIdeas(stats: StatsResult): NormalizedIdea[] {
126
+ const ideas = stats.ideas?.ideas ?? []
127
+ return ideas
128
+ .filter(i => i.status === 'pending')
129
+ .map(i => ({
130
+ title: i.text,
131
+ impact: i.priority.toUpperCase()
132
+ }))
133
+ }
91
134
 
92
- const count = timeline.filter(e => {
93
- if (!e.ts) return false
94
- return e.ts.startsWith(dateStr) && e.type === 'task_complete'
95
- }).length
135
+ function normalizeAgents(stats: StatsResult): NormalizedAgent[] {
136
+ return stats.agents.map(a => ({
137
+ name: a.name,
138
+ description: a.description,
139
+ successRate: a.successRate,
140
+ tasksCompleted: a.tasksCompleted,
141
+ bestFor: a.bestFor,
142
+ }))
143
+ }
96
144
 
97
- counts.push(count)
145
+ function normalizeTimeline(stats: StatsResult): TimelineEvent[] {
146
+ if (stats.metrics?.recentActivity?.length) {
147
+ return stats.metrics.recentActivity.map((a: { timestamp: string; description: string; action?: string }) => ({
148
+ ts: a.timestamp,
149
+ type: a.action || 'task_completed',
150
+ task: a.description,
151
+ }))
98
152
  }
153
+ return stats.legacyStats?.timeline ?? []
154
+ }
99
155
 
100
- return counts
156
+ function getVelocity(stats: StatsResult): number {
157
+ if (stats.metrics?.velocity?.tasksPerDay) {
158
+ return stats.metrics.velocity.tasksPerDay
159
+ }
160
+ return stats.legacyStats?.metrics?.velocity?.tasksPerDay ?? 0
101
161
  }
102
162
 
103
- function LoadingSkeleton() {
104
- return (
105
- <div className="p-8 space-y-8">
106
- <div className="flex items-start gap-6">
107
- <div className="h-20 w-20 rounded-full bg-muted animate-pulse" />
108
- <div className="space-y-3">
109
- <div className="h-16 w-32 bg-muted rounded animate-pulse" />
110
- <div className="h-4 w-48 bg-muted rounded animate-pulse" />
111
- </div>
112
- </div>
113
- <BentoGrid>
114
- <BentoCardSkeleton size="2x2" />
115
- <BentoCardSkeleton size="1x1" />
116
- <BentoCardSkeleton size="2x2" />
117
- <BentoCardSkeleton size="1x1" />
118
- <BentoCardSkeleton size="1x2" />
119
- <BentoCardSkeleton size="1x2" />
120
- <BentoCardSkeleton size="1x1" />
121
- <BentoCardSkeleton size="1x1" />
122
- </BentoGrid>
123
- </div>
124
- )
163
+ function getTotalShips(stats: StatsResult): number {
164
+ return stats.shipped?.items?.length ?? stats.legacyStats?.summary?.totalShipsEver ?? 0
125
165
  }
126
166
 
127
- export default function ProjectStatsPage({ params }: { params: Promise<{ id: string }> }) {
128
- const { id: projectId } = use(params)
129
- const router = useRouter()
167
+ function getTasksCompleted(stats: StatsResult): number {
168
+ return stats.metrics?.currentSprint?.tasksCompleted ?? stats.legacyStats?.metrics?.tasksCompleted ?? 0
169
+ }
130
170
 
131
- const { data: project, isLoading: projectLoading } = useProject(projectId)
132
- const { data, isLoading: statsLoading } = useProjectStats(projectId)
133
- const stats = data?.stats
171
+ interface PageProps {
172
+ params: Promise<{ id: string }>
173
+ }
134
174
 
135
- const streak = useMemo(() => calculateStreak(stats?.timeline || []), [stats?.timeline])
136
- const healthScore = useMemo(() => getHealthScore(stats), [stats])
137
- const insightMessage = useMemo(() => getInsightMessage(stats, streak), [stats, streak])
138
- const velocity = stats?.metrics?.velocity?.tasksPerDay || 0
139
- const velocityChange = useMemo(() => getVelocityChange(velocity), [velocity])
140
- const weeklyVelocityData = useMemo(() => getWeeklyVelocityData(stats?.timeline || []), [stats?.timeline])
175
+ export default async function ProjectStatsPage({ params }: PageProps) {
176
+ const { id: projectId } = await params
141
177
 
142
- if (projectLoading || statsLoading) {
143
- return <LoadingSkeleton />
144
- }
178
+ // Fetch data directly on server - no API calls
179
+ const [project, stats] = await Promise.all([
180
+ getProject(projectId),
181
+ getStats(projectId)
182
+ ])
145
183
 
146
- if (!project || !stats) {
147
- return (
148
- <div className="flex items-center justify-center h-full">
149
- <div className="text-center space-y-4">
150
- <p className="text-4xl text-muted-foreground">404</p>
151
- <Button variant="ghost" onClick={() => router.back()}>
152
- <ArrowLeft className="w-4 h-4 mr-2" />
153
- Back
154
- </Button>
155
- </div>
156
- </div>
157
- )
184
+ if (!stats.hasData) {
185
+ notFound()
158
186
  }
159
187
 
188
+ // Compute derived values using service functions
189
+ const streak = calculateStreak(stats.metrics)
190
+ const healthScore = calculateHealthScore(stats)
191
+ const velocity = getVelocity(stats)
192
+ const velocityChange = getVelocityChange(velocity)
193
+ const insightMessage = getInsightMessage(stats, streak)
194
+ const weeklyVelocityData = getWeeklyVelocityData(stats.metrics)
195
+
196
+ // Normalize data for components
197
+ const currentTask = normalizeCurrentTask(stats)
198
+ const queue = normalizeQueue(stats)
199
+ const roadmap = normalizeRoadmap(stats)
200
+ const shipped = normalizeShipped(stats)
201
+ const ideas = normalizeIdeas(stats)
202
+ const agents = normalizeAgents(stats)
203
+ const timeline = normalizeTimeline(stats)
204
+ const totalShips = getTotalShips(stats)
205
+ const tasksCompleted = getTasksCompleted(stats)
206
+
160
207
  return (
161
- <div className="flex h-full flex-col p-8 overflow-auto">
162
- {/* Hero Section */}
163
- <HeroSection
164
- projectId={projectId}
165
- projectName={project.name || projectId}
166
- tasksCompleted={stats.metrics.tasksCompleted}
167
- healthScore={healthScore}
168
- velocity={velocity}
169
- velocityChange={velocityChange}
170
- insightMessage={insightMessage}
171
- timeline={stats.timeline}
172
- />
173
-
174
- {/* Bento Grid */}
175
- <BentoGrid className="mt-8">
176
- {/* Row 1: NOW (2x2), VELOCITY (1x1), ROADMAP (2x2) */}
177
- <NowCard currentTask={stats.currentTask} />
208
+ <div className="flex h-full flex-col p-4 md:p-8 overflow-auto">
209
+ {/* Mobile: Add padding for hamburger menu */}
210
+ <div className="pl-10 md:pl-0">
211
+ <HeroSection
212
+ projectId={projectId}
213
+ projectName={project?.name ?? projectId}
214
+ tasksCompleted={tasksCompleted}
215
+ healthScore={healthScore}
216
+ velocity={velocity}
217
+ velocityChange={velocityChange}
218
+ insightMessage={insightMessage}
219
+ timeline={timeline}
220
+ />
221
+ </div>
222
+
223
+ {/* Bento Grid - Server Components */}
224
+ <BentoGrid className="mt-6 md:mt-8">
225
+ <NowCard currentTask={currentTask} />
178
226
  <VelocityCard
179
227
  tasksPerDay={velocity}
180
228
  weeklyData={weeklyVelocityData}
181
229
  change={velocityChange}
182
230
  />
183
- <RoadmapCard roadmap={stats.roadmap} />
184
-
185
- {/* STREAK under VELOCITY */}
231
+ <RoadmapCard roadmap={roadmap} />
186
232
  <StreakCard streak={streak} />
187
-
188
- {/* Row 2: QUEUE (1x2), SHIPS (1x2), IDEAS (1x1), AGENTS (1x1) */}
189
- <QueueCard queue={stats.queue || []} />
190
- <ShipsCard
191
- ships={stats.shipped || []}
192
- totalShips={stats.summary?.totalShipsEver || 0}
193
- />
194
- <IdeasCard ideas={stats.ideas?.pending || []} />
195
- <AgentsCard agents={stats.agents || []} />
233
+ <QueueCard queue={queue} />
234
+ <ShipsCard ships={shipped} totalShips={totalShips} />
235
+ <IdeasCard ideas={ideas} />
236
+ <AgentsCard agents={agents} />
196
237
  </BentoGrid>
197
238
 
198
- {/* Activity Timeline */}
199
- <div className="mt-8">
200
- <ActivityTimeline timeline={stats.timeline || []} />
239
+ {/* Activity Timeline - Client Component */}
240
+ <div className="mt-6 md:mt-8">
241
+ <ActivityTimeline timeline={timeline} />
201
242
  </div>
202
243
 
203
- <div className="h-8" />
244
+ <div className="h-6 md:h-8" />
204
245
  </div>
205
246
  )
206
247
  }
@@ -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