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
@@ -0,0 +1,187 @@
1
+ /**
2
+ * File Format Constants
3
+ *
4
+ * Single source of truth for all file patterns and formats used in prjct.
5
+ * Use these constants instead of hardcoded strings.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { FORMATS, STATUS } from '../constants/formats'
10
+ *
11
+ * // Instead of:
12
+ * const nowContent = `# NOW\n\n**${task}**`
13
+ *
14
+ * // Use:
15
+ * const nowContent = FORMATS.NOW.header(task)
16
+ * ```
17
+ */
18
+
19
+ /**
20
+ * NOW file format patterns.
21
+ */
22
+ export const NOW = {
23
+ /** Header marker for NOW file */
24
+ HEADER: '# NOW',
25
+
26
+ /** Pattern to extract task from NOW content */
27
+ TASK_PATTERN: /\*\*(.+?)\*\*/,
28
+
29
+ /** Generate NOW file content */
30
+ content: (task: string, startedAt: string, agent?: string, confidence?: number): string => {
31
+ const lines = [
32
+ '# NOW',
33
+ '',
34
+ `**${task}**`,
35
+ '',
36
+ `Started: ${startedAt}`,
37
+ ]
38
+ if (agent) {
39
+ lines.push(`Agent: ${agent}${confidence ? ` (${Math.round(confidence * 100)}% confidence)` : ''}`)
40
+ }
41
+ return lines.join('\n') + '\n'
42
+ },
43
+
44
+ /** Extract task from NOW content */
45
+ extractTask: (content: string): string | null => {
46
+ const match = content.match(NOW.TASK_PATTERN)
47
+ return match ? match[1] : null
48
+ },
49
+ } as const
50
+
51
+ /**
52
+ * SHIPPED file format patterns.
53
+ */
54
+ export const SHIPPED = {
55
+ /** Header marker for SHIPPED file */
56
+ HEADER: '# SHIPPED 🚀',
57
+
58
+ /** Generate ship entry */
59
+ entry: (feature: string, date: string, duration?: string): string => {
60
+ const lines = [
61
+ `## ${feature}`,
62
+ '',
63
+ `Shipped: ${date}`,
64
+ ]
65
+ if (duration) {
66
+ lines.push(`Duration: ${duration}`)
67
+ }
68
+ return lines.join('\n') + '\n\n'
69
+ },
70
+ } as const
71
+
72
+ /**
73
+ * NEXT file format patterns.
74
+ */
75
+ export const NEXT = {
76
+ /** Header marker for NEXT file */
77
+ HEADER: '# NEXT',
78
+
79
+ /** Pattern for task entries */
80
+ TASK_PATTERN: /^[-*]\s+\[([x ])\]\s+(.+)$/gm,
81
+
82
+ /** Generate task entry */
83
+ entry: (task: string, completed: boolean = false): string => {
84
+ return `- [${completed ? 'x' : ' '}] ${task}\n`
85
+ },
86
+ } as const
87
+
88
+ /**
89
+ * IDEAS file format patterns.
90
+ */
91
+ export const IDEAS = {
92
+ /** Header marker for IDEAS file */
93
+ HEADER: '# IDEAS 💡',
94
+
95
+ /** Generate idea entry */
96
+ entry: (idea: string, date: string): string => {
97
+ return `- ${idea} _(${date})_\n`
98
+ },
99
+ } as const
100
+
101
+ /**
102
+ * ROADMAP file format patterns.
103
+ */
104
+ export const ROADMAP = {
105
+ /** Header marker for ROADMAP file */
106
+ HEADER: '# ROADMAP 🗺️',
107
+
108
+ /** Status markers */
109
+ STATUS: {
110
+ PLANNED: '📋 Planned',
111
+ IN_PROGRESS: '🚧 In Progress',
112
+ COMPLETED: '✅ Completed',
113
+ BLOCKED: '🚫 Blocked',
114
+ },
115
+
116
+ /** Generate feature entry */
117
+ entry: (feature: string, status: keyof typeof ROADMAP.STATUS, tasks?: string[]): string => {
118
+ const lines = [
119
+ `## ${feature}`,
120
+ '',
121
+ `Status: ${ROADMAP.STATUS[status]}`,
122
+ ]
123
+ if (tasks && tasks.length > 0) {
124
+ lines.push('', '### Tasks', '')
125
+ tasks.forEach(task => lines.push(`- [ ] ${task}`))
126
+ }
127
+ return lines.join('\n') + '\n\n'
128
+ },
129
+ } as const
130
+
131
+ /**
132
+ * Session file paths.
133
+ */
134
+ export const SESSION = {
135
+ /** Date format for session directories */
136
+ DATE_FORMAT: 'YYYY-MM-DD',
137
+
138
+ /** Generate session path */
139
+ path: (year: string, month: string, day: string): string => {
140
+ return `sessions/${year}-${month}/${year}-${month}-${day}`
141
+ },
142
+
143
+ /** Session metadata filename */
144
+ METADATA_FILE: 'session-meta.json',
145
+
146
+ /** Context log filename */
147
+ CONTEXT_FILE: 'context.jsonl',
148
+ } as const
149
+
150
+ /**
151
+ * Status values used throughout prjct.
152
+ */
153
+ export const STATUS = {
154
+ PENDING: 'pending',
155
+ IN_PROGRESS: 'in_progress',
156
+ COMPLETED: 'completed',
157
+ BLOCKED: 'blocked',
158
+ PAUSED: 'paused',
159
+ } as const
160
+
161
+ export type Status = typeof STATUS[keyof typeof STATUS]
162
+
163
+ /**
164
+ * Priority levels.
165
+ */
166
+ export const PRIORITY = {
167
+ LOW: 'low',
168
+ MEDIUM: 'medium',
169
+ HIGH: 'high',
170
+ CRITICAL: 'critical',
171
+ } as const
172
+
173
+ export type Priority = typeof PRIORITY[keyof typeof PRIORITY]
174
+
175
+ /**
176
+ * Combined exports for easy import.
177
+ */
178
+ export const FORMATS = {
179
+ NOW,
180
+ SHIPPED,
181
+ NEXT,
182
+ IDEAS,
183
+ ROADMAP,
184
+ SESSION,
185
+ STATUS,
186
+ PRIORITY,
187
+ } as const
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Constants Module
3
+ *
4
+ * Centralized constants for prjct-cli.
5
+ */
6
+
7
+ export * from './formats'
@@ -13,24 +13,56 @@
13
13
  * Called by: /p:sync, /p:analyze, /p:init
14
14
  */
15
15
 
16
- const fs = require('fs').promises
17
- const path = require('path')
18
- const os = require('os')
16
+ import fs from 'fs/promises'
17
+ import path from 'path'
18
+ import os from 'os'
19
+
20
+ interface StackInfo {
21
+ primary: string | null
22
+ full: string | null
23
+ languages: string[]
24
+ frameworks: string[]
25
+ dependencies: string[]
26
+ }
27
+
28
+ interface ProjectStructure {
29
+ fileCount: number | null
30
+ directories: string[]
31
+ }
32
+
33
+ interface GitCommit {
34
+ hash: string
35
+ message: string
36
+ }
37
+
38
+ interface GitStats {
39
+ commits: number | null
40
+ contributors: number | null
41
+ recentCommits: GitCommit[]
42
+ }
43
+
44
+ interface AgentExpertise {
45
+ name: string
46
+ role: string
47
+ expertise: string
48
+ }
49
+
50
+ interface ContextResult {
51
+ agents: string[]
52
+ stack: StackInfo
53
+ currentTask: string | null
54
+ }
19
55
 
20
56
  /**
21
57
  * Generate RICH project context file for Claude
22
58
  * Embeds key information so Claude doesn't need to read multiple files
23
- *
24
- * @param {string} projectPath - Local project path
25
- * @param {string} projectId - Project ID from config
26
- * @returns {Promise<{agents: string[], stack: object, currentTask: string|null}>}
27
59
  */
28
- async function generateLocalContext(projectPath, projectId) {
60
+ async function generateLocalContext(projectPath: string, projectId: string): Promise<ContextResult> {
29
61
  const globalPath = path.join(os.homedir(), '.prjct-cli/projects', projectId)
30
62
  const projectName = path.basename(projectPath)
31
63
 
32
64
  // Helper to read files safely
33
- const readSafe = async (p) => {
65
+ const readSafe = async (p: string): Promise<string | null> => {
34
66
  try {
35
67
  return await fs.readFile(p, 'utf-8')
36
68
  } catch {
@@ -40,7 +72,7 @@ async function generateLocalContext(projectPath, projectId) {
40
72
 
41
73
  // 1. Read ALL data sources in parallel
42
74
  const agentsDir = path.join(globalPath, 'agents')
43
- let agentFiles = []
75
+ let agentFiles: string[] = []
44
76
  try {
45
77
  agentFiles = await fs.readdir(agentsDir)
46
78
  agentFiles = agentFiles.filter(f => f.endsWith('.md'))
@@ -152,10 +184,10 @@ For detailed information, read these files:
152
184
  /**
153
185
  * Extract FULL stack info from repo-summary
154
186
  */
155
- function extractFullStack(summary) {
156
- if (!summary) return { primary: null, full: null }
187
+ function extractFullStack(summary: string | null): StackInfo {
188
+ if (!summary) return { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
157
189
 
158
- const result = { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
190
+ const result: StackInfo = { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
159
191
 
160
192
  // Extract languages (e.g., "### JavaScript/TypeScript")
161
193
  const langMatch = summary.match(/###\s*(JavaScript|TypeScript|Python|Go|Rust|Ruby|Java|C#|PHP)/i)
@@ -177,10 +209,10 @@ function extractFullStack(summary) {
177
209
  /**
178
210
  * Extract project structure from repo-summary
179
211
  */
180
- function extractProjectStructure(summary) {
212
+ function extractProjectStructure(summary: string | null): ProjectStructure {
181
213
  if (!summary) return { fileCount: null, directories: [] }
182
214
 
183
- const result = { fileCount: null, directories: [] }
215
+ const result: ProjectStructure = { fileCount: null, directories: [] }
184
216
 
185
217
  // Extract file count
186
218
  const fileMatch = summary.match(/\*\*Total Files\*\*:\s*(\d+)/i)
@@ -196,10 +228,10 @@ function extractProjectStructure(summary) {
196
228
  /**
197
229
  * Extract git stats from repo-summary
198
230
  */
199
- function extractGitStats(summary) {
231
+ function extractGitStats(summary: string | null): GitStats {
200
232
  if (!summary) return { commits: null, contributors: null, recentCommits: [] }
201
233
 
202
- const result = { commits: null, contributors: null, recentCommits: [] }
234
+ const result: GitStats = { commits: null, contributors: null, recentCommits: [] }
203
235
 
204
236
  // Extract commit count
205
237
  const commitMatch = summary.match(/\*\*Total Commits\*\*:\s*(\d+)/i)
@@ -217,7 +249,7 @@ function extractGitStats(summary) {
217
249
  result.recentCommits = commitLines.slice(0, 5).map(line => {
218
250
  const match = line.match(/- `([a-f0-9]+)` (.+?) \(/)
219
251
  return match ? { hash: match[1], message: match[2] } : null
220
- }).filter(Boolean)
252
+ }).filter((c): c is GitCommit => c !== null)
221
253
  }
222
254
  }
223
255
 
@@ -227,8 +259,8 @@ function extractGitStats(summary) {
227
259
  /**
228
260
  * Extract agent expertise from agent files
229
261
  */
230
- async function extractAgentExpertise(agentsDir, agentFiles) {
231
- const expertise = []
262
+ async function extractAgentExpertise(agentsDir: string, agentFiles: string[]): Promise<AgentExpertise[]> {
263
+ const expertise: AgentExpertise[] = []
232
264
 
233
265
  for (const file of agentFiles.slice(0, 6)) { // Max 6 agents to keep context small
234
266
  try {
@@ -257,7 +289,7 @@ async function extractAgentExpertise(agentsDir, agentFiles) {
257
289
  /**
258
290
  * Extract current task from now.md
259
291
  */
260
- function extractCurrentTask(now) {
292
+ function extractCurrentTask(now: string | null): string | null {
261
293
  if (!now) return null
262
294
 
263
295
  const lines = now.split('\n')
@@ -273,7 +305,7 @@ function extractCurrentTask(now) {
273
305
  /**
274
306
  * Extract top N tasks from next.md
275
307
  */
276
- function extractTopTasks(next, count) {
308
+ function extractTopTasks(next: string | null, count: number): string[] {
277
309
  if (!next) return []
278
310
 
279
311
  return next
@@ -287,10 +319,10 @@ function extractTopTasks(next, count) {
287
319
  /**
288
320
  * Extract active features from roadmap.md
289
321
  */
290
- function extractActiveFeatures(roadmap) {
322
+ function extractActiveFeatures(roadmap: string | null): string[] {
291
323
  if (!roadmap) return []
292
324
 
293
- const features = []
325
+ const features: string[] = []
294
326
  const lines = roadmap.split('\n')
295
327
  let inActive = false
296
328
 
@@ -314,7 +346,7 @@ function extractActiveFeatures(roadmap) {
314
346
  /**
315
347
  * Extract recent ideas from ideas.md
316
348
  */
317
- function extractRecentIdeas(ideas) {
349
+ function extractRecentIdeas(ideas: string | null): string[] {
318
350
  if (!ideas) return []
319
351
 
320
352
  return ideas
@@ -325,4 +357,5 @@ function extractRecentIdeas(ideas) {
325
357
  .filter(Boolean)
326
358
  }
327
359
 
328
- module.exports = { generateLocalContext }
360
+ export { generateLocalContext }
361
+ export default { generateLocalContext }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Agents Manager
3
+ *
4
+ * Manages agents.json - specialized AI agents.
5
+ */
6
+
7
+ import { ArrayManager } from './base-manager'
8
+ import type { AgentSchema, AgentsSchema } from '../schemas'
9
+ import { DEFAULT_AGENT } from '../schemas'
10
+
11
+ class AgentsManager extends ArrayManager<AgentSchema> {
12
+ constructor() {
13
+ super('agents.json')
14
+ }
15
+
16
+ async getAgents(projectId: string): Promise<AgentsSchema> {
17
+ return this.read(projectId)
18
+ }
19
+
20
+ async getAgent(projectId: string, name: string): Promise<AgentSchema | undefined> {
21
+ return this.find(projectId, (agent) => agent.name === name)
22
+ }
23
+
24
+ async addAgent(
25
+ projectId: string,
26
+ agent: Pick<AgentSchema, 'name' | 'description'> & Partial<AgentSchema>
27
+ ): Promise<AgentsSchema> {
28
+ const fullAgent: AgentSchema = {
29
+ ...DEFAULT_AGENT,
30
+ ...agent
31
+ }
32
+ return this.add(projectId, fullAgent)
33
+ }
34
+
35
+ async updateAgent(
36
+ projectId: string,
37
+ name: string,
38
+ updates: Partial<AgentSchema>
39
+ ): Promise<AgentsSchema> {
40
+ return this.updateItem(
41
+ projectId,
42
+ (agent) => agent.name === name,
43
+ (agent) => ({ ...agent, ...updates })
44
+ )
45
+ }
46
+
47
+ async removeAgent(projectId: string, name: string): Promise<AgentsSchema> {
48
+ return this.remove(projectId, (agent) => agent.name === name)
49
+ }
50
+
51
+ async setAgents(projectId: string, agents: AgentsSchema): Promise<void> {
52
+ await this.write(projectId, agents)
53
+ }
54
+
55
+ async incrementTasksCompleted(projectId: string, name: string): Promise<AgentsSchema> {
56
+ return this.updateItem(
57
+ projectId,
58
+ (agent) => agent.name === name,
59
+ (agent) => ({
60
+ ...agent,
61
+ tasksCompleted: (agent.tasksCompleted || 0) + 1
62
+ })
63
+ )
64
+ }
65
+
66
+ async updateSuccessRate(
67
+ projectId: string,
68
+ name: string,
69
+ successRate: number
70
+ ): Promise<AgentsSchema> {
71
+ return this.updateAgent(projectId, name, { successRate })
72
+ }
73
+ }
74
+
75
+ export const agentsManager = new AgentsManager()
76
+ export default agentsManager
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Analysis Manager
3
+ *
4
+ * Manages analysis.json - repository analysis data.
5
+ */
6
+
7
+ import { BaseManager } from './base-manager'
8
+ import type { AnalysisSchema, CodePattern, AntiPattern } from '../schemas'
9
+ import { DEFAULT_ANALYSIS } from '../schemas'
10
+
11
+ class AnalysisManager extends BaseManager<AnalysisSchema> {
12
+ constructor() {
13
+ super('analysis.json')
14
+ }
15
+
16
+ protected getDefault(projectId: string): AnalysisSchema {
17
+ return {
18
+ ...DEFAULT_ANALYSIS,
19
+ projectId,
20
+ analyzedAt: new Date().toISOString()
21
+ }
22
+ }
23
+
24
+ async getAnalysis(projectId: string): Promise<AnalysisSchema> {
25
+ return this.read(projectId)
26
+ }
27
+
28
+ async updateAnalysis(
29
+ projectId: string,
30
+ updates: Partial<Omit<AnalysisSchema, 'projectId'>>
31
+ ): Promise<AnalysisSchema> {
32
+ return this.update(projectId, (analysis) => ({
33
+ ...analysis,
34
+ ...updates,
35
+ analyzedAt: new Date().toISOString()
36
+ }))
37
+ }
38
+
39
+ async setLanguages(projectId: string, languages: string[]): Promise<AnalysisSchema> {
40
+ return this.updateAnalysis(projectId, { languages })
41
+ }
42
+
43
+ async setFrameworks(projectId: string, frameworks: string[]): Promise<AnalysisSchema> {
44
+ return this.updateAnalysis(projectId, { frameworks })
45
+ }
46
+
47
+ async addPattern(projectId: string, pattern: CodePattern): Promise<AnalysisSchema> {
48
+ return this.update(projectId, (analysis) => ({
49
+ ...analysis,
50
+ patterns: [...analysis.patterns, pattern],
51
+ analyzedAt: new Date().toISOString()
52
+ }))
53
+ }
54
+
55
+ async addAntiPattern(projectId: string, antiPattern: AntiPattern): Promise<AnalysisSchema> {
56
+ return this.update(projectId, (analysis) => ({
57
+ ...analysis,
58
+ antiPatterns: [...analysis.antiPatterns, antiPattern],
59
+ analyzedAt: new Date().toISOString()
60
+ }))
61
+ }
62
+
63
+ async setPatterns(projectId: string, patterns: CodePattern[]): Promise<AnalysisSchema> {
64
+ return this.updateAnalysis(projectId, { patterns })
65
+ }
66
+
67
+ async setAntiPatterns(projectId: string, antiPatterns: AntiPattern[]): Promise<AnalysisSchema> {
68
+ return this.updateAnalysis(projectId, { antiPatterns })
69
+ }
70
+
71
+ async getPatterns(projectId: string): Promise<CodePattern[]> {
72
+ const analysis = await this.read(projectId)
73
+ return analysis.patterns
74
+ }
75
+
76
+ async getAntiPatterns(projectId: string): Promise<AntiPattern[]> {
77
+ const analysis = await this.read(projectId)
78
+ return analysis.antiPatterns
79
+ }
80
+ }
81
+
82
+ export const analysisManager = new AnalysisManager()
83
+ export default analysisManager
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Base Manager
3
+ *
4
+ * Abstract base class for JSON file managers.
5
+ * Provides common CRUD operations for all data types.
6
+ */
7
+
8
+ import path from 'path'
9
+ import * as fileHelper from '../utils/file-helper'
10
+ import pathManager from '../infrastructure/path-manager'
11
+
12
+ export abstract class BaseManager<T> {
13
+ protected filename: string
14
+ protected cache: Map<string, T> = new Map()
15
+ protected cacheTimeout = 5000 // 5 seconds
16
+ protected lastRead: Map<string, number> = new Map()
17
+
18
+ constructor(filename: string) {
19
+ this.filename = filename
20
+ }
21
+
22
+ /**
23
+ * Get file path for a project.
24
+ */
25
+ protected getFilePath(projectId: string): string {
26
+ const globalPath = pathManager.getGlobalProjectPath(projectId)
27
+ return path.join(globalPath, this.filename)
28
+ }
29
+
30
+ /**
31
+ * Get default value for this data type.
32
+ */
33
+ protected abstract getDefault(projectId: string): T
34
+
35
+ /**
36
+ * Read data from JSON file.
37
+ */
38
+ async read(projectId: string): Promise<T> {
39
+ const now = Date.now()
40
+ const lastReadTime = this.lastRead.get(projectId) || 0
41
+
42
+ // Return cached if fresh
43
+ if (now - lastReadTime < this.cacheTimeout && this.cache.has(projectId)) {
44
+ return this.cache.get(projectId)!
45
+ }
46
+
47
+ const filePath = this.getFilePath(projectId)
48
+ const data = await fileHelper.readJson<T>(filePath, this.getDefault(projectId))
49
+
50
+ // Update cache
51
+ this.cache.set(projectId, data!)
52
+ this.lastRead.set(projectId, now)
53
+
54
+ return data!
55
+ }
56
+
57
+ /**
58
+ * Write data to JSON file.
59
+ */
60
+ async write(projectId: string, data: T): Promise<void> {
61
+ const filePath = this.getFilePath(projectId)
62
+
63
+ // Ensure directory exists
64
+ await fileHelper.ensureDir(path.dirname(filePath))
65
+
66
+ await fileHelper.writeJson(filePath, data)
67
+
68
+ // Update cache
69
+ this.cache.set(projectId, data)
70
+ this.lastRead.set(projectId, Date.now())
71
+ }
72
+
73
+ /**
74
+ * Check if file exists.
75
+ */
76
+ async exists(projectId: string): Promise<boolean> {
77
+ const filePath = this.getFilePath(projectId)
78
+ return fileHelper.fileExists(filePath)
79
+ }
80
+
81
+ /**
82
+ * Initialize with default data.
83
+ */
84
+ async initialize(projectId: string): Promise<T> {
85
+ const data = this.getDefault(projectId)
86
+ await this.write(projectId, data)
87
+ return data
88
+ }
89
+
90
+ /**
91
+ * Clear cache.
92
+ */
93
+ clearCache(projectId?: string): void {
94
+ if (projectId) {
95
+ this.cache.delete(projectId)
96
+ this.lastRead.delete(projectId)
97
+ } else {
98
+ this.cache.clear()
99
+ this.lastRead.clear()
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Update data with a partial update.
105
+ */
106
+ async update(projectId: string, updater: (data: T) => T): Promise<T> {
107
+ const current = await this.read(projectId)
108
+ const updated = updater(current)
109
+ await this.write(projectId, updated)
110
+ return updated
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Base manager for array-based JSON files.
116
+ */
117
+ export abstract class ArrayManager<T> extends BaseManager<T[]> {
118
+ protected getDefault(): T[] {
119
+ return []
120
+ }
121
+
122
+ /**
123
+ * Add item to array.
124
+ */
125
+ async add(projectId: string, item: T): Promise<T[]> {
126
+ return this.update(projectId, (data) => [...data, item])
127
+ }
128
+
129
+ /**
130
+ * Remove item by predicate.
131
+ */
132
+ async remove(projectId: string, predicate: (item: T) => boolean): Promise<T[]> {
133
+ return this.update(projectId, (data) => data.filter((item) => !predicate(item)))
134
+ }
135
+
136
+ /**
137
+ * Find item by predicate.
138
+ */
139
+ async find(projectId: string, predicate: (item: T) => boolean): Promise<T | undefined> {
140
+ const data = await this.read(projectId)
141
+ return data.find(predicate)
142
+ }
143
+
144
+ /**
145
+ * Update item by predicate.
146
+ */
147
+ async updateItem(
148
+ projectId: string,
149
+ predicate: (item: T) => boolean,
150
+ updater: (item: T) => T
151
+ ): Promise<T[]> {
152
+ return this.update(projectId, (data) =>
153
+ data.map((item) => (predicate(item) ? updater(item) : item))
154
+ )
155
+ }
156
+ }