prjct-cli 0.11.4 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +246 -54
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/date-helper.test.ts +405 -0
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +99 -89
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +35 -18
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +62 -23
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +203 -403
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +134 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/BentoGrid/BentoGrid.tsx +18 -0
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/EmptyState/EmptyState.tsx +58 -0
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/IdeasCard/IdeasCard.tsx +48 -0
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/ProgressRing/ProgressRing.tsx +51 -0
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +77 -0
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/ShipsCard/ShipsCard.tsx +52 -0
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/SparklineChart/SparklineChart.tsx +38 -0
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/StreakCard/StreakCard.tsx +53 -0
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/components/ui/tooltip.tsx +2 -2
  333. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  334. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  335. package/packages/web/hooks/useProjectStats.ts +55 -0
  336. package/packages/web/hooks/useProjects.ts +6 -6
  337. package/packages/web/lib/actions/projects.ts +15 -0
  338. package/packages/web/lib/json-loader.ts +630 -0
  339. package/packages/web/lib/services/index.ts +9 -0
  340. package/packages/web/lib/services/migration.server.ts +598 -0
  341. package/packages/web/lib/services/projects.server.ts +52 -0
  342. package/packages/web/lib/services/stats.server.ts +264 -0
  343. package/packages/web/lib/unified-loader.ts +396 -0
  344. package/packages/web/next-env.d.ts +1 -1
  345. package/packages/web/package.json +10 -6
  346. package/packages/web/server.ts +36 -6
  347. package/templates/commands/done.md +76 -32
  348. package/templates/commands/feature.md +121 -47
  349. package/templates/commands/idea.md +81 -8
  350. package/templates/commands/now.md +41 -17
  351. package/templates/commands/ship.md +64 -25
  352. package/templates/commands/sync.md +28 -3
  353. package/core/agentic/agent-router.js +0 -128
  354. package/core/agentic/chain-of-thought.js +0 -578
  355. package/core/agentic/command-executor.js +0 -421
  356. package/core/agentic/context-filter.js +0 -354
  357. package/core/agentic/ground-truth.js +0 -591
  358. package/core/agentic/loop-detector.js +0 -406
  359. package/core/agentic/memory-system.js +0 -850
  360. package/core/agentic/parallel-tools.js +0 -366
  361. package/core/agentic/plan-mode.js +0 -572
  362. package/core/agentic/prompt-builder.js +0 -338
  363. package/core/agentic/response-templates.js +0 -290
  364. package/core/agentic/semantic-compression.js +0 -517
  365. package/core/agentic/think-blocks.js +0 -657
  366. package/core/agentic/tool-registry.js +0 -184
  367. package/core/agentic/validation-rules.js +0 -380
  368. package/core/command-registry.js +0 -698
  369. package/core/commands.js +0 -2237
  370. package/core/domain/task-stack.js +0 -497
  371. package/core/infrastructure/legacy-installer-detector.js +0 -546
  372. package/core/infrastructure/migrator.js +0 -799
  373. package/core/infrastructure/session-manager.js +0 -390
  374. package/core/utils/file-helper.js +0 -329
  375. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  376. package/packages/web/app/api/stats/route.ts +0 -38
  377. package/packages/web/components/AppSidebar.tsx +0 -113
  378. package/packages/web/hooks/useStats.ts +0 -28
  379. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  380. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  381. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  382. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  383. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  384. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  385. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -0,0 +1,598 @@
1
+ import { createOpenAI } from '@ai-sdk/openai'
2
+ import { generateText } from 'ai'
3
+ import { promises as fs } from 'fs'
4
+ import { join } from 'path'
5
+ import { homedir } from 'os'
6
+ import { exec } from 'child_process'
7
+ import { promisify } from 'util'
8
+
9
+ const execAsync = promisify(exec)
10
+ const SETTINGS_PATH = join(homedir(), '.prjct-cli', 'settings.json')
11
+ const GLOBAL_STORAGE = join(homedir(), '.prjct-cli', 'projects')
12
+ const PRJCT_CLI_PATH = join(__dirname, '..', '..', '..', '..')
13
+
14
+ // Complete JSON Schema definitions for new architecture
15
+ // JSON is source of truth, MD is generated for Claude
16
+ // ENRICHED SCHEMAS - Extract all rich data from MD files
17
+ const SCHEMAS = {
18
+ state: `{
19
+ "currentTask": {
20
+ "id": "task_xxxxxxxx - unique ID",
21
+ "description": "string",
22
+ "startedAt": "ISO-8601 timestamp",
23
+ "sessionId": "sess_xxxxxxxx",
24
+ "featureId": "feat_xxxxxxxx (optional)"
25
+ } | null,
26
+ "previousTask": {
27
+ "id": "string",
28
+ "description": "string",
29
+ "status": "paused",
30
+ "startedAt": "ISO-8601",
31
+ "pausedAt": "ISO-8601"
32
+ } | null (optional - for paused tasks),
33
+ "lastUpdated": "ISO-8601 timestamp"
34
+ }`,
35
+
36
+ queue: `{
37
+ "tasks": [{
38
+ "id": "task_xxxxxxxx",
39
+ "description": "string",
40
+ "priority": "critical|high|medium|low",
41
+ "type": "feature|bug|improvement|chore (detect from emoji 🐛=bug)",
42
+ "featureId": "feat_xxx (optional)",
43
+ "originFeature": "string - from (from: Feature Name) pattern (optional)",
44
+ "completed": boolean,
45
+ "completedAt": "ISO-8601 (if completed)",
46
+ "createdAt": "ISO-8601",
47
+ "section": "active|backlog|previously_active (based on MD section)",
48
+ "agent": "fe|be|fe + be (extract from **Agent**: pattern in task group)",
49
+ "groupName": "string - group/section name like 'Sales Reports', 'Stock Audits'",
50
+ "groupId": "string - unique ID for the group (optional)"
51
+ }],
52
+ "lastUpdated": "ISO-8601"
53
+ }`,
54
+
55
+ ideas: `{
56
+ "ideas": [{
57
+ "id": "idea_xxxxxxxx",
58
+ "text": "title/summary",
59
+ "details": "expanded description (optional)",
60
+ "priority": "high|medium|low",
61
+ "status": "pending|converted|completed|archived",
62
+ "tags": ["array", "of", "tags"],
63
+ "addedAt": "ISO-8601",
64
+ "completedAt": "ISO-8601 (if status=completed, extract from 'COMPLETED YYYY-MM-DD')",
65
+ "convertedTo": "feat_xxx (if status=converted)",
66
+ "source": "docs/technical-spec-v1.md, docs/edr-v1.md (from **Source**: pattern)",
67
+ "sourceFiles": ["array of source file paths"],
68
+ "painPoints": ["array of pain points from ### Pain Points or ### Riesgos section"],
69
+ "solutions": ["array of solutions from ### Solutions section"],
70
+ "filesAffected": ["array of file paths from **Files:** section"],
71
+ "impactEffort": {"impact": "high|medium|low", "effort": "high|medium|low"} (optional),
72
+ "stack": {
73
+ "frontend": "Next.js 14, HeroUI",
74
+ "backend": "Supabase (Auth, DB, RLS, Realtime)",
75
+ "payments": "Stripe Billing",
76
+ "ai": "Vercel AI SDK",
77
+ "deploy": "Vercel"
78
+ } (extract from ### Stack section),
79
+ "modules": [{"name": "Multi-tenant", "description": "Empresas con RLS estricto"}] (from ### Módulos section),
80
+ "roles": [{"name": "SUPER_ADMIN", "description": "(global, impersonation)"}] (from ### Roles section),
81
+ "risks": ["array of risks from ### Riesgos Críticos section"],
82
+ "risksCount": number (from '33 pitfalls documented')
83
+ }],
84
+ "lastUpdated": "ISO-8601"
85
+ }`,
86
+
87
+ roadmap: `{
88
+ "strategy": {
89
+ "goal": "strategic goal string (optional)",
90
+ "phases": [{"id": "P0", "name": "string", "status": "completed|active|planned", "completedAt": "ISO"}],
91
+ "successMetrics": ["array of KPIs"]
92
+ } | null (optional),
93
+ "features": [{
94
+ "id": "feat_xxxxxxxx",
95
+ "name": "string",
96
+ "description": "string (optional)",
97
+ "date": "YYYY-MM-DD creation date",
98
+ "impact": "high|medium|low (from Impact: HIGH)",
99
+ "effort": "1-2 days or similar string",
100
+ "status": "planned|active|completed|shipped",
101
+ "progress": 0-100 (calculate from tasks completed/total),
102
+ "type": "feature|breaking_change|refactor|infrastructure (from Type: BREAKING CHANGE)",
103
+ "roi": 1-5 (count ⭐ stars, optional),
104
+ "why": ["array from ### Why This Feature? section"],
105
+ "technicalNotes": ["array from ### Technical Notes section"],
106
+ "compatibility": "string (optional)",
107
+ "phase": "P0|P1|P2|P3 (optional)",
108
+ "tasks": [{
109
+ "id": "task_xxxxxxxx",
110
+ "description": "string",
111
+ "completed": boolean ([ ]=false, [x]=true),
112
+ "completedAt": "ISO-8601 (if completed)"
113
+ }],
114
+ "createdAt": "ISO-8601",
115
+ "shippedAt": "ISO-8601 (if shipped)",
116
+ "version": "0.11.6 (extract from v0.11.6 pattern)",
117
+ "duration": {"hours": number, "minutes": number, "totalMinutes": number, "display": "~25m"} (parse from '~25m', '~1h'),
118
+ "taskCount": number (extract from '7 tasks' in header),
119
+ "agent": "fe+be|fe|be (from **Agent**: pattern)",
120
+ "sprintName": "Sprint 6 - Reports + Audits (full sprint name)",
121
+ "completedDate": "2025-12-09 (exact completion date from MD)"
122
+ }],
123
+ "backlog": ["string array"],
124
+ "lastUpdated": "ISO-8601"
125
+ }`,
126
+
127
+ shipped: `{
128
+ "items": [{
129
+ "id": "ship_xxxxxxxx",
130
+ "name": "string",
131
+ "version": "0.11.6 (extract from (v0.11.6) or v0.11.6 patterns, null if none)",
132
+ "type": "feature|fix|improvement|refactor",
133
+ "agent": "fe|be|fe+be|devops|ai (extract from **Agent**: pattern)",
134
+ "description": "string - full narrative description text (NOT bullet points)",
135
+ "changes": [{"description": "string", "type": "added|changed|fixed|removed"}] (from bullet points),
136
+ "codeSnippets": ["string array of code blocks if any"] (optional),
137
+ "commit": {
138
+ "hash": "0a7bbea (short hash from **Commit**: pattern)",
139
+ "message": "feat(security): Multi-tenant... (commit message)"
140
+ } (optional),
141
+ "codeMetrics": {
142
+ "filesChanged": number|null,
143
+ "linesAdded": number|null,
144
+ "linesRemoved": number|null,
145
+ "commits": number|null
146
+ } (extract from 'Files: 4 | +160/-31 | Commits: 0'),
147
+ "qualityMetrics": {
148
+ "lintStatus": "pass|warning|fail|skipped|null",
149
+ "lintDetails": "string (optional)",
150
+ "testStatus": "pass|warning|fail|skipped|null",
151
+ "testDetails": "string - e.g. (2 date-helper tests)"
152
+ } (extract from 'Lint: warnings | Tests: failed (details)'),
153
+ "quantitativeImpact": "81% (1,079 → 204 lines) (optional)",
154
+ "duration": {"hours": number, "minutes": number, "totalMinutes": number} (parse from '~45m', '~1h', '13h 38m'),
155
+ "tasksCompleted": number|null (extract from 'Tasks: 6' or '6 tasks'),
156
+ "shippedAt": "ISO-8601",
157
+ "featureId": "feat_xxx (optional)"
158
+ }],
159
+ "lastUpdated": "ISO-8601"
160
+ }`,
161
+
162
+ metrics: `{
163
+ "currentSprint": {
164
+ "tasksStarted": number,
165
+ "tasksCompleted": number,
166
+ "inProgress": number
167
+ },
168
+ "allTime": {
169
+ "featuresShipped": number,
170
+ "tasksCompleted": number,
171
+ "totalTimeTracked": {"hours": number, "minutes": number, "totalMinutes": number},
172
+ "daysActive": number
173
+ },
174
+ "velocity": {
175
+ "featuresPerWeek": number,
176
+ "tasksPerDay": number
177
+ },
178
+ "recentActivity": [{
179
+ "timestamp": "ISO-8601",
180
+ "action": "started|completed|shipped|paused",
181
+ "description": "string",
182
+ "duration": {"hours": number, "minutes": number} (optional, parse from '(1m)' or '(7h 39m)'),
183
+ "codeChanges": {"files": number, "added": number, "removed": number, "commits": number} (optional)
184
+ }] (parse from Last Activity section and activity lines),
185
+ "lastUpdated": "ISO-8601"
186
+ }`
187
+ }
188
+
189
+ async function getApiKey(): Promise<string | null> {
190
+ try {
191
+ const content = await fs.readFile(SETTINGS_PATH, 'utf-8')
192
+ const settings = JSON.parse(content)
193
+ return settings.openRouterApiKey || null
194
+ } catch {
195
+ return null
196
+ }
197
+ }
198
+
199
+ async function readMdFile(projectId: string, relativePath: string): Promise<string | null> {
200
+ try {
201
+ const fullPath = join(GLOBAL_STORAGE, projectId, relativePath)
202
+ return await fs.readFile(fullPath, 'utf-8')
203
+ } catch {
204
+ return null
205
+ }
206
+ }
207
+
208
+ async function writeJsonFile(projectId: string, relativePath: string, data: unknown): Promise<void> {
209
+ const fullPath = join(GLOBAL_STORAGE, projectId, relativePath)
210
+ const dir = fullPath.substring(0, fullPath.lastIndexOf('/'))
211
+ await fs.mkdir(dir, { recursive: true })
212
+ await fs.writeFile(fullPath, JSON.stringify(data, null, 2))
213
+ }
214
+
215
+ type MigrationFile = {
216
+ mdPaths: string[] // Multiple source files to read
217
+ jsonPath: string // Destination in data/ directory
218
+ schemaKey: keyof typeof SCHEMAS
219
+ name: string
220
+ }
221
+
222
+ // New architecture: MD → data/*.json
223
+ // All JSON files go to data/ directory
224
+ const MIGRATION_FILES: MigrationFile[] = [
225
+ {
226
+ mdPaths: ['core/now.md', 'core/now.json', 'sessions/current.json'],
227
+ jsonPath: 'data/state.json',
228
+ schemaKey: 'state',
229
+ name: 'state'
230
+ },
231
+ {
232
+ mdPaths: ['core/next.md', 'core/next.json'],
233
+ jsonPath: 'data/queue.json',
234
+ schemaKey: 'queue',
235
+ name: 'queue'
236
+ },
237
+ {
238
+ mdPaths: ['planning/ideas.md', 'planning/ideas.json'],
239
+ jsonPath: 'data/ideas.json',
240
+ schemaKey: 'ideas',
241
+ name: 'ideas'
242
+ },
243
+ {
244
+ mdPaths: ['planning/roadmap.md', 'planning/roadmap.json'],
245
+ jsonPath: 'data/roadmap.json',
246
+ schemaKey: 'roadmap',
247
+ name: 'roadmap'
248
+ },
249
+ {
250
+ mdPaths: ['progress/shipped.md', 'progress/shipped.json'],
251
+ jsonPath: 'data/shipped.json',
252
+ schemaKey: 'shipped',
253
+ name: 'shipped'
254
+ },
255
+ {
256
+ mdPaths: ['progress/metrics.md'],
257
+ jsonPath: 'data/metrics.json',
258
+ schemaKey: 'metrics',
259
+ name: 'metrics'
260
+ }
261
+ ]
262
+
263
+ // Legacy files to delete after successful migration
264
+ const LEGACY_FILES = [
265
+ 'core/now.md',
266
+ 'core/now.json',
267
+ 'core/next.md',
268
+ 'core/next.json',
269
+ 'core/context.md',
270
+ 'planning/ideas.md',
271
+ 'planning/ideas.json',
272
+ 'planning/roadmap.md',
273
+ 'planning/roadmap.json',
274
+ 'progress/shipped.md',
275
+ 'progress/shipped.json',
276
+ 'progress/metrics.md',
277
+ ]
278
+
279
+ async function deleteLegacyFiles(projectId: string): Promise<string[]> {
280
+ const deleted: string[] = []
281
+ for (const file of LEGACY_FILES) {
282
+ try {
283
+ await fs.unlink(join(GLOBAL_STORAGE, projectId, file))
284
+ deleted.push(file)
285
+ } catch {
286
+ // File doesn't exist or can't be deleted
287
+ }
288
+ }
289
+
290
+ // Try to remove empty directories
291
+ const dirsToCheck = ['core', 'planning', 'progress']
292
+ for (const dir of dirsToCheck) {
293
+ try {
294
+ const dirPath = join(GLOBAL_STORAGE, projectId, dir)
295
+ const files = await fs.readdir(dirPath)
296
+ if (files.length === 0) {
297
+ await fs.rmdir(dirPath)
298
+ deleted.push(dir + '/')
299
+ }
300
+ } catch {
301
+ // Directory doesn't exist or not empty
302
+ }
303
+ }
304
+
305
+ return deleted
306
+ }
307
+
308
+ export type MigrationResult = {
309
+ file: string
310
+ success: boolean
311
+ error?: string
312
+ }
313
+
314
+ export async function migrateProject(projectId: string, deleteLegacy: boolean = true): Promise<{
315
+ success: boolean
316
+ results: MigrationResult[]
317
+ deletedFiles: string[]
318
+ viewsGenerated?: boolean
319
+ error?: string
320
+ }> {
321
+ const apiKey = await getApiKey()
322
+ if (!apiKey) {
323
+ return { success: false, results: [], deletedFiles: [], error: 'No OpenRouter API key configured' }
324
+ }
325
+
326
+ const openrouter = createOpenAI({
327
+ baseURL: 'https://openrouter.ai/api/v1',
328
+ apiKey
329
+ })
330
+
331
+ const results: MigrationResult[] = []
332
+
333
+ for (const file of MIGRATION_FILES) {
334
+ // Read all source files for this migration
335
+ const sourceContents: string[] = []
336
+ for (const mdPath of file.mdPaths) {
337
+ const content = await readMdFile(projectId, mdPath)
338
+ if (content && content.trim()) {
339
+ sourceContents.push(`## Source: ${mdPath}\n${content}`)
340
+ }
341
+ }
342
+
343
+ if (sourceContents.length === 0) {
344
+ results.push({ file: file.name, success: true, error: 'No source files found, skipped' })
345
+ continue
346
+ }
347
+
348
+ const combinedContent = sourceContents.join('\n\n---\n\n')
349
+
350
+ try {
351
+ const schema = SCHEMAS[file.schemaKey]
352
+ const { text } = await generateText({
353
+ model: openrouter('anthropic/claude-3.5-haiku'),
354
+ prompt: `Parse these source files and extract ALL structured data. Return ONLY valid JSON matching this schema (no markdown, no explanation):
355
+
356
+ ## Target Schema:
357
+ ${schema}
358
+
359
+ ## Source Files Content:
360
+ ${combinedContent}
361
+
362
+ ## CRITICAL Pattern Extraction Rules - ZERO DATA LOSS:
363
+
364
+ AGENT PATTERNS - Extract agent type:
365
+ - "**Agent**: be" → agent: "be"
366
+ - "**Agent**: fe + be" → agent: "fe+be"
367
+ - "**Agent**: fe" → agent: "fe"
368
+
369
+ COMMIT PATTERNS - Extract git commit info:
370
+ - "**Commit**: \`0a7bbea\` feat(security): Multi-tenant..." → commit: {hash: "0a7bbea", message: "feat(security): Multi-tenant..."}
371
+ - "**Commits**: \`feat(sprint-6): Sales Reports\`, \`feat(sprint-6): Stock audit\`" → extract multiple commit messages
372
+
373
+ DURATION PATTERNS - Parse to {hours, minutes, totalMinutes, display}:
374
+ - "~1m" or "(1m)" → {hours: 0, minutes: 1, totalMinutes: 1, display: "~1m"}
375
+ - "~45m" → {hours: 0, minutes: 45, totalMinutes: 45, display: "~45m"}
376
+ - "~1h" or "(1h)" → {hours: 1, minutes: 0, totalMinutes: 60, display: "~1h"}
377
+ - "(7h 39m)" → {hours: 7, minutes: 39, totalMinutes: 499, display: "7h 39m"}
378
+ - "**Time**: ~28h" → {hours: 28, minutes: 0, totalMinutes: 1680, display: "~28h"}
379
+
380
+ TASK COUNT PATTERNS:
381
+ - "**Tasks**: 7" → taskCount: 7
382
+ - "(7 tasks, ~25m)" → taskCount: 7, duration: {hours: 0, minutes: 25, totalMinutes: 25}
383
+ - "(8 tasks)" → taskCount: 8
384
+
385
+ CODE METRICS - Parse "Files: X | +Y/-Z | Commits: N":
386
+ - "Files: 4 | +160/-31 | Commits: 0" → {filesChanged: 4, linesAdded: 160, linesRemoved: 31, commits: 0}
387
+ - "**Files**: 59" → {filesChanged: 59, linesAdded: null, linesRemoved: null, commits: null}
388
+
389
+ QUALITY STATUS - Parse "Lint: X | Tests: Y":
390
+ - "Lint: warnings" → lintStatus: "warning"
391
+ - "Tests: failed (2 date-helper tests)" → testStatus: "fail", testDetails: "2 date-helper tests"
392
+
393
+ IMPACT/EFFORT - Parse "Impact: X | Effort: Y":
394
+ - "Impact: **HIGH** | Effort: 1-2 days" → impact: "high", effort: "1-2 days"
395
+
396
+ ROI - Count stars:
397
+ - "⭐⭐⭐⭐⭐" → roi: 5
398
+
399
+ TASK TYPE - Detect from emoji:
400
+ - "🐛 [HIGH]" → type: "bug", priority: "high"
401
+ - "[ ] Task" → completed: false
402
+ - "[x] Done" → completed: true
403
+
404
+ VERSION - Extract number:
405
+ - "(v0.11.6)" or "v0.11.6" → version: "0.11.6"
406
+ - "Security Hardening v0.2.0" → name: "Security Hardening", version: "0.2.0"
407
+
408
+ SECTIONS - Detect from headers:
409
+ - "## Active Tasks" → section: "active"
410
+ - "## Previously Active" → section: "previously_active"
411
+ - "### Pain Points:" or "### Riesgos Críticos" → painPoints[] or risks[]
412
+ - "### Why This Feature?" → why[]
413
+ - "### Technical Notes" → technicalNotes[]
414
+ - "### Stack Definido" → stack: {frontend, backend, payments, ai, deploy}
415
+ - "### Módulos V1" → modules: [{name, description}]
416
+ - "### Roles" → roles: [{name, description}]
417
+
418
+ SOURCE PATTERNS:
419
+ - "**Source**: docs/technical-spec-v1.md, docs/edr-v1.md" → source: "docs/...", sourceFiles: ["docs/technical-spec-v1.md", "docs/edr-v1.md"]
420
+
421
+ STATUS WITH DATE:
422
+ - "**Status**: COMPLETED 2025-11-29" → status: "completed", completedAt: "2025-11-29T00:00:00.000Z"
423
+ - "### Sprint 6 - Reports + Audits (7 tasks, ~25m) - 2025-12-09 ✅" → sprintName, taskCount, duration, completedDate, status: "completed"
424
+
425
+ DESCRIPTION EXTRACTION:
426
+ - Capture ALL narrative text between the title and bullet points as "description"
427
+ - Code blocks (triple backticks) → codeSnippets: ["code string"]
428
+ - "CRITICAL: Multi-tenant isolation..." → description: "CRITICAL: Multi-tenant isolation..."
429
+
430
+ ## Instructions:
431
+ - Return ONLY the JSON object, nothing else
432
+ - Generate unique IDs: task_xxxxxxxx, feat_xxxxxxxx, idea_xxxxxxxx, ship_xxxxxxxx
433
+ - ALL dates must be ISO-8601: YYYY-MM-DDTHH:mm:ss.sssZ
434
+ - Set lastUpdated to: "${new Date().toISOString()}"
435
+ - Preserve ALL rich data from MD - don't lose any information
436
+ - If a field is optional and no data exists, omit it completely`
437
+ })
438
+
439
+ // Parse the JSON response
440
+ const jsonMatch = text.match(/\{[\s\S]*\}/)
441
+ if (!jsonMatch) {
442
+ throw new Error('No valid JSON found in response')
443
+ }
444
+ const parsedData = JSON.parse(jsonMatch[0])
445
+
446
+ await writeJsonFile(projectId, file.jsonPath, parsedData)
447
+ results.push({ file: file.name, success: true })
448
+ } catch (error) {
449
+ results.push({
450
+ file: file.name,
451
+ success: false,
452
+ error: error instanceof Error ? error.message : 'Unknown error'
453
+ })
454
+ }
455
+ }
456
+
457
+ // Copy project.json to data/ if exists
458
+ try {
459
+ const projectJson = await readMdFile(projectId, 'project.json')
460
+ if (projectJson) {
461
+ await writeJsonFile(projectId, 'data/project.json', JSON.parse(projectJson))
462
+ results.push({ file: 'project', success: true })
463
+ }
464
+ } catch {
465
+ // project.json doesn't exist or invalid, skip
466
+ }
467
+
468
+ const allSuccess = results.filter(r => !r.error?.includes('skipped')).every(r => r.success)
469
+
470
+ // Delete legacy files only if all migrations succeeded
471
+ let deletedFiles: string[] = []
472
+ if (allSuccess && deleteLegacy) {
473
+ deletedFiles = await deleteLegacyFiles(projectId)
474
+ }
475
+
476
+ // Generate views from new JSON files
477
+ let viewsGenerated = false
478
+ if (allSuccess) {
479
+ try {
480
+ // Try to run the generate-views CLI command
481
+ await execAsync(`bun ${join(PRJCT_CLI_PATH, 'bin', 'generate-views.js')} --project=${projectId}`)
482
+ viewsGenerated = true
483
+ results.push({ file: 'views', success: true })
484
+ } catch (viewError) {
485
+ // Views generation failed but migration still succeeded
486
+ results.push({
487
+ file: 'views',
488
+ success: false,
489
+ error: viewError instanceof Error ? viewError.message : 'Failed to generate views'
490
+ })
491
+ }
492
+ }
493
+
494
+ return { success: allSuccess, results, deletedFiles, viewsGenerated }
495
+ }
496
+
497
+ export type ProjectInfo = {
498
+ id: string
499
+ name: string
500
+ needsMigration: boolean
501
+ hasMdFiles: boolean
502
+ hasDataDir: boolean
503
+ }
504
+
505
+ export async function getProjectsToMigrate(): Promise<ProjectInfo[]> {
506
+ try {
507
+ const dirs = await fs.readdir(GLOBAL_STORAGE)
508
+ const validProjects: ProjectInfo[] = []
509
+
510
+ for (const dir of dirs) {
511
+ // Skip hidden directories
512
+ if (dir.startsWith('.')) continue
513
+
514
+ const projectPath = join(GLOBAL_STORAGE, dir)
515
+
516
+ // Must have CLAUDE.md or project.json to be a valid project
517
+ let claudeMd: string | null = null
518
+ try {
519
+ claudeMd = await fs.readFile(join(projectPath, 'CLAUDE.md'), 'utf-8')
520
+ } catch {
521
+ // No CLAUDE.md
522
+ }
523
+
524
+ // Check for project.json in root or data/
525
+ let projectJson: Record<string, unknown> | null = null
526
+ try {
527
+ projectJson = JSON.parse(await fs.readFile(join(projectPath, 'project.json'), 'utf-8'))
528
+ } catch {
529
+ try {
530
+ projectJson = JSON.parse(await fs.readFile(join(projectPath, 'data', 'project.json'), 'utf-8'))
531
+ } catch {
532
+ // No project.json
533
+ }
534
+ }
535
+
536
+ // Must have at least one identifier
537
+ if (!claudeMd && !projectJson) continue
538
+
539
+ // Check if project has legacy MD files to migrate
540
+ let hasMdFiles = false
541
+ for (const file of MIGRATION_FILES) {
542
+ for (const mdPath of file.mdPaths) {
543
+ if (mdPath.endsWith('.md')) {
544
+ try {
545
+ const content = await fs.readFile(join(projectPath, mdPath), 'utf-8')
546
+ if (content && content.trim().length > 20) {
547
+ hasMdFiles = true
548
+ break
549
+ }
550
+ } catch {
551
+ // File doesn't exist
552
+ }
553
+ }
554
+ }
555
+ if (hasMdFiles) break
556
+ }
557
+
558
+ // Check if data/ directory exists with JSON files
559
+ let hasDataDir = false
560
+ try {
561
+ const dataFiles = await fs.readdir(join(projectPath, 'data'))
562
+ hasDataDir = dataFiles.some(f => f.endsWith('.json'))
563
+ } catch {
564
+ // data/ doesn't exist
565
+ }
566
+
567
+ // Project needs migration if it has MD files but no data/ or incomplete data/
568
+ const needsMigration = hasMdFiles
569
+
570
+ // Get project name
571
+ let name = dir
572
+ if (projectJson && typeof projectJson.name === 'string') {
573
+ name = projectJson.name
574
+ } else if (claudeMd) {
575
+ const match = claudeMd.match(/# (.+) - Project Context/)
576
+ if (match) name = match[1]
577
+ }
578
+
579
+ validProjects.push({
580
+ id: dir,
581
+ name,
582
+ needsMigration,
583
+ hasMdFiles,
584
+ hasDataDir
585
+ })
586
+ }
587
+
588
+ // Sort: projects needing migration first, then by name
589
+ return validProjects.sort((a, b) => {
590
+ if (a.needsMigration !== b.needsMigration) {
591
+ return a.needsMigration ? -1 : 1
592
+ }
593
+ return a.name.localeCompare(b.name)
594
+ })
595
+ } catch {
596
+ return []
597
+ }
598
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Projects Service (Server-only)
3
+ *
4
+ * Direct data access for Server Components.
5
+ * No API calls needed - reads directly from filesystem.
6
+ */
7
+
8
+ import 'server-only'
9
+ import { cache } from 'react'
10
+ import { loadProject, type ProjectJson } from '@/lib/json-loader'
11
+ import { getProjects as getProjectsList, getProject as getProjectLegacy } from '@/lib/projects'
12
+
13
+ export type { ProjectJson }
14
+
15
+ /**
16
+ * Get single project by ID - cached per request
17
+ */
18
+ export const getProject = cache(async (projectId: string): Promise<ProjectJson | null> => {
19
+ // Try JSON first
20
+ const jsonProject = await loadProject(projectId)
21
+ if (jsonProject) {
22
+ return jsonProject
23
+ }
24
+
25
+ // Fallback to legacy
26
+ try {
27
+ const legacyProject = await getProjectLegacy(projectId)
28
+ if (legacyProject) {
29
+ return {
30
+ projectId: legacyProject.id,
31
+ name: legacyProject.name,
32
+ repoPath: legacyProject.path,
33
+ techStack: legacyProject.techStack || [],
34
+ fileCount: legacyProject.filesCount ? parseInt(legacyProject.filesCount) : 0,
35
+ commitCount: legacyProject.commitsCount ? parseInt(legacyProject.commitsCount) : 0,
36
+ createdAt: new Date().toISOString(),
37
+ lastSync: new Date().toISOString()
38
+ }
39
+ }
40
+ } catch {
41
+ // Ignore errors
42
+ }
43
+
44
+ return null
45
+ })
46
+
47
+ /**
48
+ * Get all projects - cached per request
49
+ */
50
+ export const getProjects = cache(async () => {
51
+ return getProjectsList()
52
+ })