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
@@ -5,11 +5,11 @@
5
5
  * OPTIMIZED: Tests updated to match compressed prompt structure
6
6
  */
7
7
 
8
- import { describe, it, expect, beforeEach } from 'vitest'
9
- import promptBuilder from '../../agentic/prompt-builder.js'
8
+ import { describe, it, expect, beforeEach } from 'bun:test'
9
+ import promptBuilder from '../../agentic/prompt-builder'
10
10
 
11
11
  describe('PromptBuilder', () => {
12
- let builder
12
+ let builder: typeof promptBuilder
13
13
 
14
14
  beforeEach(() => {
15
15
  builder = promptBuilder
@@ -122,7 +122,6 @@ describe('PromptBuilder', () => {
122
122
 
123
123
  const prompt = builder.build(template, context, state)
124
124
 
125
- // Non-code commands should NOT include patterns section
126
125
  expect(prompt).not.toContain('## PATTERNS')
127
126
  })
128
127
  })
@@ -143,7 +142,6 @@ describe('PromptBuilder', () => {
143
142
 
144
143
  const prompt = builder.build(template, context, state)
145
144
 
146
- // OPTIMIZED: New compressed format uses ## FILES:
147
145
  expect(prompt).toContain('## FILES:')
148
146
  expect(prompt).toContain('3 available')
149
147
  expect(prompt).toContain('file1.js')
@@ -161,7 +159,6 @@ describe('PromptBuilder', () => {
161
159
 
162
160
  const prompt = builder.build(template, context, state)
163
161
 
164
- // OPTIMIZED: New compressed format uses ## PROJECT:
165
162
  expect(prompt).toContain('## PROJECT:')
166
163
  expect(prompt).toContain('/test/project')
167
164
  })
@@ -191,7 +188,6 @@ describe('PromptBuilder', () => {
191
188
  expect(prompt).toContain('TOOLS:')
192
189
  expect(prompt).toContain('Flow')
193
190
  expect(prompt).toContain('RULES (CRITICAL)')
194
- // OPTIMIZED: New compressed format uses ## FILES:
195
191
  expect(prompt).toContain('## FILES:')
196
192
  })
197
193
 
@@ -206,7 +202,6 @@ describe('PromptBuilder', () => {
206
202
 
207
203
  const prompt = builder.build(template, context, state)
208
204
 
209
- // Optimized prompts should be under 2000 chars for simple cases
210
205
  expect(prompt.length).toBeLessThan(2000)
211
206
  })
212
207
  })
@@ -0,0 +1,405 @@
1
+ /**
2
+ * Date Helper Tests
3
+ * Tests for centralized date operations and formatting
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach, setSystemTime } from 'bun:test'
7
+ import {
8
+ formatDate,
9
+ formatMonth,
10
+ getTodayKey,
11
+ getDateKey,
12
+ getYearMonthDay,
13
+ parseDate,
14
+ getTimestamp,
15
+ getDaysAgo,
16
+ getDaysFromNow,
17
+ getDateRange,
18
+ isToday,
19
+ isWithinLastDays,
20
+ formatDuration,
21
+ calculateDuration,
22
+ getStartOfDay,
23
+ getEndOfDay,
24
+ } from '../../utils/date-helper'
25
+
26
+ describe('DateHelper', () => {
27
+ describe('formatDate', () => {
28
+ it('should format date to YYYY-MM-DD', () => {
29
+ const date = new Date(2025, 9, 4) // Oct 4, 2025
30
+ expect(formatDate(date)).toBe('2025-10-04')
31
+ })
32
+
33
+ it('should pad single digit months', () => {
34
+ const date = new Date(2025, 0, 15) // Jan 15, 2025
35
+ expect(formatDate(date)).toBe('2025-01-15')
36
+ })
37
+
38
+ it('should pad single digit days', () => {
39
+ const date = new Date(2025, 11, 5) // Dec 5, 2025
40
+ expect(formatDate(date)).toBe('2025-12-05')
41
+ })
42
+
43
+ it('should handle year boundaries', () => {
44
+ const date = new Date(2024, 11, 31) // Dec 31, 2024
45
+ expect(formatDate(date)).toBe('2024-12-31')
46
+ })
47
+ })
48
+
49
+ describe('formatMonth', () => {
50
+ it('should format date to YYYY-MM', () => {
51
+ const date = new Date(2025, 9, 15) // Oct 15, 2025
52
+ expect(formatMonth(date)).toBe('2025-10')
53
+ })
54
+
55
+ it('should pad single digit months', () => {
56
+ const date = new Date(2025, 2, 1) // Mar 1, 2025
57
+ expect(formatMonth(date)).toBe('2025-03')
58
+ })
59
+
60
+ it('should handle December', () => {
61
+ const date = new Date(2025, 11, 25) // Dec 25, 2025
62
+ expect(formatMonth(date)).toBe('2025-12')
63
+ })
64
+ })
65
+
66
+ describe('getTodayKey', () => {
67
+ it('should return today in YYYY-MM-DD format', () => {
68
+ setSystemTime(new Date(2025, 5, 15)) // June 15, 2025
69
+ expect(getTodayKey()).toBe('2025-06-15')
70
+ setSystemTime()
71
+ })
72
+ })
73
+
74
+ describe('getDateKey', () => {
75
+ it('should return date in YYYY-MM-DD format (alias for formatDate)', () => {
76
+ const date = new Date(2025, 7, 20) // Aug 20, 2025
77
+ expect(getDateKey(date)).toBe('2025-08-20')
78
+ })
79
+ })
80
+
81
+ describe('getYearMonthDay', () => {
82
+ it('should return separate year, month, day strings', () => {
83
+ const date = new Date(2025, 9, 4) // Oct 4, 2025
84
+ const result = getYearMonthDay(date)
85
+
86
+ expect(result.year).toBe('2025')
87
+ expect(result.month).toBe('10')
88
+ expect(result.day).toBe('04')
89
+ })
90
+
91
+ it('should pad month values', () => {
92
+ const date = new Date(2025, 0, 15) // Jan 15, 2025
93
+ const result = getYearMonthDay(date)
94
+ expect(result.month).toBe('01')
95
+ })
96
+
97
+ it('should pad day values', () => {
98
+ const date = new Date(2025, 5, 7) // June 7, 2025
99
+ const result = getYearMonthDay(date)
100
+ expect(result.day).toBe('07')
101
+ })
102
+ })
103
+
104
+ describe('parseDate', () => {
105
+ it('should parse YYYY-MM-DD format', () => {
106
+ const result = parseDate('2025-10-04')
107
+ expect(result.getFullYear()).toBe(2025)
108
+ expect(result.getMonth()).toBe(9) // 0-indexed
109
+ expect(result.getDate()).toBe(4)
110
+ })
111
+
112
+ it('should parse ISO strings', () => {
113
+ const result = parseDate('2025-10-04T14:30:00.000Z')
114
+ expect(result.getFullYear()).toBe(2025)
115
+ expect(result.getMonth()).toBe(9)
116
+ })
117
+ })
118
+
119
+ describe('getTimestamp', () => {
120
+ it('should return ISO timestamp', () => {
121
+ setSystemTime(new Date('2025-10-04T14:30:00.000Z'))
122
+ expect(getTimestamp()).toBe('2025-10-04T14:30:00.000Z')
123
+ setSystemTime()
124
+ })
125
+
126
+ it('should include milliseconds', () => {
127
+ const timestamp = getTimestamp()
128
+ expect(timestamp).toMatch(/\.\d{3}Z$/)
129
+ })
130
+ })
131
+
132
+ describe('getDaysAgo', () => {
133
+ beforeEach(() => {
134
+ setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
135
+ })
136
+
137
+ afterEach(() => {
138
+ setSystemTime()
139
+ })
140
+
141
+ it('should calculate past dates correctly', () => {
142
+ const result = getDaysAgo(5)
143
+ expect(formatDate(result)).toBe('2025-10-10')
144
+ })
145
+
146
+ it('should handle month boundaries', () => {
147
+ const result = getDaysAgo(20)
148
+ expect(formatDate(result)).toBe('2025-09-25')
149
+ })
150
+
151
+ it('should return today for 0 days ago', () => {
152
+ const result = getDaysAgo(0)
153
+ expect(formatDate(result)).toBe('2025-10-15')
154
+ })
155
+ })
156
+
157
+ describe('getDaysFromNow', () => {
158
+ beforeEach(() => {
159
+ setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
160
+ })
161
+
162
+ afterEach(() => {
163
+ setSystemTime()
164
+ })
165
+
166
+ it('should calculate future dates correctly', () => {
167
+ const result = getDaysFromNow(5)
168
+ expect(formatDate(result)).toBe('2025-10-20')
169
+ })
170
+
171
+ it('should handle month boundaries', () => {
172
+ const result = getDaysFromNow(20)
173
+ expect(formatDate(result)).toBe('2025-11-04')
174
+ })
175
+
176
+ it('should return today for 0 days from now', () => {
177
+ const result = getDaysFromNow(0)
178
+ expect(formatDate(result)).toBe('2025-10-15')
179
+ })
180
+ })
181
+
182
+ describe('getDateRange', () => {
183
+ it('should return array of dates in range', () => {
184
+ const from = new Date(2025, 9, 1) // Oct 1
185
+ const to = new Date(2025, 9, 5) // Oct 5
186
+
187
+ const result = getDateRange(from, to)
188
+
189
+ expect(result.length).toBe(5)
190
+ expect(formatDate(result[0])).toBe('2025-10-01')
191
+ expect(formatDate(result[4])).toBe('2025-10-05')
192
+ })
193
+
194
+ it('should include start and end dates', () => {
195
+ const from = new Date(2025, 9, 10)
196
+ const to = new Date(2025, 9, 12)
197
+
198
+ const result = getDateRange(from, to)
199
+
200
+ expect(formatDate(result[0])).toBe('2025-10-10')
201
+ expect(formatDate(result[result.length - 1])).toBe('2025-10-12')
202
+ })
203
+
204
+ it('should return single date if from equals to', () => {
205
+ const date = new Date(2025, 9, 15)
206
+ const result = getDateRange(date, date)
207
+
208
+ expect(result.length).toBe(1)
209
+ expect(formatDate(result[0])).toBe('2025-10-15')
210
+ })
211
+
212
+ it('should handle month boundaries', () => {
213
+ const from = new Date(2025, 9, 30) // Oct 30
214
+ const to = new Date(2025, 10, 2) // Nov 2
215
+
216
+ const result = getDateRange(from, to)
217
+
218
+ expect(result.length).toBe(4)
219
+ expect(formatDate(result[0])).toBe('2025-10-30')
220
+ expect(formatDate(result[3])).toBe('2025-11-02')
221
+ })
222
+
223
+ it('should return empty array if from is after to', () => {
224
+ const from = new Date(2025, 9, 15)
225
+ const to = new Date(2025, 9, 10)
226
+
227
+ const result = getDateRange(from, to)
228
+
229
+ expect(result.length).toBe(0)
230
+ })
231
+ })
232
+
233
+ describe('isToday', () => {
234
+ beforeEach(() => {
235
+ setSystemTime(new Date(2025, 9, 15)) // Oct 15, 2025
236
+ })
237
+
238
+ afterEach(() => {
239
+ setSystemTime()
240
+ })
241
+
242
+ it('should return true for today', () => {
243
+ const today = new Date(2025, 9, 15)
244
+ expect(isToday(today)).toBe(true)
245
+ })
246
+
247
+ it('should return false for yesterday', () => {
248
+ const yesterday = new Date(2025, 9, 14)
249
+ expect(isToday(yesterday)).toBe(false)
250
+ })
251
+
252
+ it('should return false for tomorrow', () => {
253
+ const tomorrow = new Date(2025, 9, 16)
254
+ expect(isToday(tomorrow)).toBe(false)
255
+ })
256
+
257
+ it('should ignore time component', () => {
258
+ const todayLate = new Date(2025, 9, 15, 23, 59, 59)
259
+ expect(isToday(todayLate)).toBe(true)
260
+ })
261
+ })
262
+
263
+ describe('isWithinLastDays', () => {
264
+ beforeEach(() => {
265
+ setSystemTime(new Date(2025, 9, 15, 12, 0, 0)) // Oct 15, 2025 at noon
266
+ })
267
+
268
+ afterEach(() => {
269
+ setSystemTime()
270
+ })
271
+
272
+ it('should return true for dates within range', () => {
273
+ const recent = new Date(2025, 9, 12) // 3 days ago
274
+ expect(isWithinLastDays(recent, 7)).toBe(true)
275
+ })
276
+
277
+ it('should return false for dates outside range', () => {
278
+ const old = new Date(2025, 9, 1) // 14 days ago
279
+ expect(isWithinLastDays(old, 7)).toBe(false)
280
+ })
281
+
282
+ it('should include today', () => {
283
+ const today = new Date(2025, 9, 15)
284
+ expect(isWithinLastDays(today, 7)).toBe(true)
285
+ })
286
+
287
+ it('should include boundary date', () => {
288
+ // Oct 8 at noon is exactly 7 days before Oct 15 at noon
289
+ const boundary = new Date(2025, 9, 8, 12, 0, 0)
290
+ expect(isWithinLastDays(boundary, 7)).toBe(true)
291
+ })
292
+ })
293
+
294
+ describe('formatDuration', () => {
295
+ it('should format seconds', () => {
296
+ expect(formatDuration(5000)).toBe('5s')
297
+ expect(formatDuration(45000)).toBe('45s')
298
+ })
299
+
300
+ it('should format minutes', () => {
301
+ expect(formatDuration(60000)).toBe('1m')
302
+ expect(formatDuration(120000)).toBe('2m')
303
+ expect(formatDuration(90000)).toBe('1m') // 1.5 min rounds down
304
+ })
305
+
306
+ it('should format hours and minutes', () => {
307
+ expect(formatDuration(3600000)).toBe('1h 0m')
308
+ expect(formatDuration(5400000)).toBe('1h 30m')
309
+ expect(formatDuration(7200000)).toBe('2h 0m')
310
+ })
311
+
312
+ it('should format days and hours', () => {
313
+ expect(formatDuration(86400000)).toBe('1d 0h')
314
+ expect(formatDuration(90000000)).toBe('1d 1h')
315
+ expect(formatDuration(172800000)).toBe('2d 0h')
316
+ })
317
+
318
+ it('should handle zero', () => {
319
+ expect(formatDuration(0)).toBe('0s')
320
+ })
321
+ })
322
+
323
+ describe('calculateDuration', () => {
324
+ it('should calculate duration between two dates', () => {
325
+ const start = new Date('2025-10-15T10:00:00.000Z')
326
+ const end = new Date('2025-10-15T12:30:00.000Z')
327
+
328
+ expect(calculateDuration(start, end)).toBe('2h 30m')
329
+ })
330
+
331
+ it('should default to now if no end date', () => {
332
+ setSystemTime(new Date('2025-10-15T12:00:00.000Z'))
333
+
334
+ const start = new Date('2025-10-15T10:00:00.000Z')
335
+ expect(calculateDuration(start)).toBe('2h 0m')
336
+
337
+ setSystemTime()
338
+ })
339
+
340
+ it('should handle short durations', () => {
341
+ const start = new Date('2025-10-15T10:00:00.000Z')
342
+ const end = new Date('2025-10-15T10:00:30.000Z')
343
+
344
+ expect(calculateDuration(start, end)).toBe('30s')
345
+ })
346
+ })
347
+
348
+ describe('getStartOfDay', () => {
349
+ it('should set time to 00:00:00.000', () => {
350
+ const date = new Date(2025, 9, 15, 14, 30, 45, 500)
351
+ const result = getStartOfDay(date)
352
+
353
+ expect(result.getHours()).toBe(0)
354
+ expect(result.getMinutes()).toBe(0)
355
+ expect(result.getSeconds()).toBe(0)
356
+ expect(result.getMilliseconds()).toBe(0)
357
+ })
358
+
359
+ it('should preserve the date', () => {
360
+ const date = new Date(2025, 9, 15, 23, 59, 59)
361
+ const result = getStartOfDay(date)
362
+
363
+ expect(result.getFullYear()).toBe(2025)
364
+ expect(result.getMonth()).toBe(9)
365
+ expect(result.getDate()).toBe(15)
366
+ })
367
+
368
+ it('should not mutate original date', () => {
369
+ const original = new Date(2025, 9, 15, 14, 30)
370
+ getStartOfDay(original)
371
+
372
+ expect(original.getHours()).toBe(14)
373
+ expect(original.getMinutes()).toBe(30)
374
+ })
375
+ })
376
+
377
+ describe('getEndOfDay', () => {
378
+ it('should set time to 23:59:59.999', () => {
379
+ const date = new Date(2025, 9, 15, 10, 0, 0, 0)
380
+ const result = getEndOfDay(date)
381
+
382
+ expect(result.getHours()).toBe(23)
383
+ expect(result.getMinutes()).toBe(59)
384
+ expect(result.getSeconds()).toBe(59)
385
+ expect(result.getMilliseconds()).toBe(999)
386
+ })
387
+
388
+ it('should preserve the date', () => {
389
+ const date = new Date(2025, 9, 15, 0, 0, 0)
390
+ const result = getEndOfDay(date)
391
+
392
+ expect(result.getFullYear()).toBe(2025)
393
+ expect(result.getMonth()).toBe(9)
394
+ expect(result.getDate()).toBe(15)
395
+ })
396
+
397
+ it('should not mutate original date', () => {
398
+ const original = new Date(2025, 9, 15, 10, 30)
399
+ getEndOfDay(original)
400
+
401
+ expect(original.getHours()).toBe(10)
402
+ expect(original.getMinutes()).toBe(30)
403
+ })
404
+ })
405
+ })
@@ -3,18 +3,16 @@
3
3
  * Minimal output system for prjct-cli
4
4
  */
5
5
 
6
- const out = require('../../utils/output')
6
+ import { describe, it, expect, beforeEach, afterEach, spyOn } from 'bun:test'
7
+ import out from '../../utils/output'
7
8
 
8
9
  describe('Output Module', () => {
9
- let consoleLogSpy
10
- let stdoutWriteSpy
10
+ let consoleLogSpy: ReturnType<typeof spyOn>
11
+ let stdoutWriteSpy: ReturnType<typeof spyOn>
11
12
 
12
13
  beforeEach(() => {
13
- // Mock console.log
14
- consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
15
- // Mock process.stdout.write
16
- stdoutWriteSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => {})
17
- // Ensure spinner is stopped before each test
14
+ consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
15
+ stdoutWriteSpy = spyOn(process.stdout, 'write').mockImplementation(() => true)
18
16
  out.stop()
19
17
  })
20
18
 
@@ -39,7 +37,6 @@ describe('Output Module', () => {
39
37
  out.done(longMessage)
40
38
 
41
39
  const output = consoleLogSpy.mock.calls[0][0]
42
- // Should be truncated to ~65 chars + checkmark
43
40
  expect(output.length).toBeLessThan(80)
44
41
  })
45
42
 
@@ -90,20 +87,16 @@ describe('Output Module', () => {
90
87
  })
91
88
 
92
89
  describe('spin()', () => {
93
- it('should start spinner with message', () => {
94
- vi.useFakeTimers()
95
-
90
+ it('should start spinner with message', async () => {
96
91
  out.spin('loading')
97
92
 
98
- // Advance timer to trigger interval
99
- vi.advanceTimersByTime(100)
93
+ await new Promise(resolve => setTimeout(resolve, 150))
100
94
 
101
95
  expect(stdoutWriteSpy).toHaveBeenCalled()
102
96
  const output = stdoutWriteSpy.mock.calls[0][0]
103
97
  expect(output).toContain('loading')
104
98
 
105
99
  out.stop()
106
- vi.useRealTimers()
107
100
  })
108
101
 
109
102
  it('should return self for chaining', () => {
@@ -114,19 +107,14 @@ describe('Output Module', () => {
114
107
  })
115
108
 
116
109
  describe('stop()', () => {
117
- it('should stop spinner and clear line', () => {
118
- vi.useFakeTimers()
119
-
110
+ it('should stop spinner and clear line', async () => {
120
111
  out.spin('loading')
121
- vi.advanceTimersByTime(100)
112
+ await new Promise(resolve => setTimeout(resolve, 150))
122
113
 
123
114
  stdoutWriteSpy.mockClear()
124
115
  out.stop()
125
116
 
126
- // Should write clearing spaces
127
117
  expect(stdoutWriteSpy).toHaveBeenCalled()
128
-
129
- vi.useRealTimers()
130
118
  })
131
119
 
132
120
  it('should be safe to call multiple times', () => {
@@ -151,8 +139,8 @@ describe('Output Module', () => {
151
139
  })
152
140
 
153
141
  it('should handle null/undefined messages', () => {
154
- expect(() => out.done(null)).not.toThrow()
155
- expect(() => out.done(undefined)).not.toThrow()
142
+ expect(() => out.done(null as unknown as string)).not.toThrow()
143
+ expect(() => out.done(undefined as unknown as string)).not.toThrow()
156
144
  })
157
145
 
158
146
  it('should handle messages with special characters', () => {
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Agent Router
3
+ * Orchestrates agent loading and context building for Claude delegation.
4
+ *
5
+ * @module agentic/agent-router
6
+ * @version 2.0.0
7
+ */
8
+
9
+ import fs from 'fs/promises'
10
+ import path from 'path'
11
+ import configManager from '../infrastructure/config-manager'
12
+ import pathManager from '../infrastructure/path-manager'
13
+
14
+ interface Agent {
15
+ name: string
16
+ content: string
17
+ }
18
+
19
+ interface AssignmentContext {
20
+ task: string
21
+ availableAgents: string[]
22
+ projectPath: string
23
+ projectId: string | null
24
+ _template: string
25
+ }
26
+
27
+ /**
28
+ * Routes tasks to specialized agents based on Claude's decisions.
29
+ * Handles agent loading, context building, and usage logging.
30
+ */
31
+ class AgentRouter {
32
+ projectId: string | null = null
33
+ agentsPath: string | null = null
34
+
35
+ /**
36
+ * Initialize router with project context
37
+ */
38
+ async initialize(projectPath: string): Promise<void> {
39
+ this.projectId = await configManager.getProjectId(projectPath)
40
+ this.agentsPath = pathManager.getPath(this.projectId!, 'agents')
41
+ }
42
+
43
+ /**
44
+ * Load all available agents from project
45
+ */
46
+ async loadAvailableAgents(): Promise<Agent[]> {
47
+ try {
48
+ const files = await fs.readdir(this.agentsPath!)
49
+ const agents: Agent[] = []
50
+
51
+ for (const file of files) {
52
+ if (file.endsWith('.md')) {
53
+ const name = file.replace('.md', '')
54
+ const content = await fs.readFile(path.join(this.agentsPath!, file), 'utf-8')
55
+ agents.push({ name, content })
56
+ }
57
+ }
58
+
59
+ return agents
60
+ } catch {
61
+ return []
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get list of available agent names
67
+ */
68
+ async getAgentNames(): Promise<string[]> {
69
+ const agents = await this.loadAvailableAgents()
70
+ return agents.map((a) => a.name)
71
+ }
72
+
73
+ /**
74
+ * Load a specific agent by name
75
+ */
76
+ async loadAgent(name: string): Promise<Agent | null> {
77
+ try {
78
+ const filePath = path.join(this.agentsPath!, `${name}.md`)
79
+ const content = await fs.readFile(filePath, 'utf-8')
80
+ return { name, content }
81
+ } catch {
82
+ return null
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Build context for Claude to decide agent assignment
88
+ */
89
+ async buildAssignmentContext(
90
+ task: string | { description?: string },
91
+ projectPath: string
92
+ ): Promise<AssignmentContext> {
93
+ const agents = await this.getAgentNames()
94
+
95
+ return {
96
+ task: typeof task === 'string' ? task : task.description || '',
97
+ availableAgents: agents,
98
+ projectPath,
99
+ projectId: this.projectId,
100
+ // Claude reads this and decides via template
101
+ _template: 'templates/agent-assignment.md',
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Log agent usage to JSONL file
107
+ */
108
+ async logUsage(
109
+ task: string | { description?: string },
110
+ agent: string | { name?: string },
111
+ _projectPath: string
112
+ ): Promise<void> {
113
+ try {
114
+ const logPath = path.join(
115
+ process.env.HOME || '',
116
+ '.prjct-cli',
117
+ 'projects',
118
+ this.projectId || '',
119
+ 'agent-usage.jsonl'
120
+ )
121
+
122
+ const entry =
123
+ JSON.stringify({
124
+ timestamp: new Date().toISOString(),
125
+ task: typeof task === 'string' ? task : task.description,
126
+ agent: typeof agent === 'string' ? agent : agent.name,
127
+ projectId: this.projectId,
128
+ }) + '\n'
129
+
130
+ await fs.appendFile(logPath, entry)
131
+ } catch {
132
+ // Silent fail for logging
133
+ }
134
+ }
135
+ }
136
+
137
+ export default AgentRouter