prjct-cli 0.11.5 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +226 -50
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +204 -163
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +190 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  333. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  334. package/packages/web/hooks/useProjectStats.ts +55 -0
  335. package/packages/web/hooks/useProjects.ts +6 -6
  336. package/packages/web/lib/actions/projects.ts +15 -0
  337. package/packages/web/lib/json-loader.ts +630 -0
  338. package/packages/web/lib/services/index.ts +9 -0
  339. package/packages/web/lib/services/migration.server.ts +600 -0
  340. package/packages/web/lib/services/projects.server.ts +52 -0
  341. package/packages/web/lib/services/stats.server.ts +264 -0
  342. package/packages/web/lib/unified-loader.ts +396 -0
  343. package/packages/web/package.json +10 -7
  344. package/packages/web/server.ts +58 -8
  345. package/templates/commands/done.md +76 -32
  346. package/templates/commands/feature.md +121 -47
  347. package/templates/commands/idea.md +81 -8
  348. package/templates/commands/now.md +41 -17
  349. package/templates/commands/ship.md +64 -25
  350. package/templates/commands/sync.md +28 -3
  351. package/core/agentic/agent-router.js +0 -140
  352. package/core/agentic/chain-of-thought.js +0 -578
  353. package/core/agentic/command-executor.js +0 -417
  354. package/core/agentic/context-filter.js +0 -354
  355. package/core/agentic/ground-truth.js +0 -591
  356. package/core/agentic/loop-detector.js +0 -406
  357. package/core/agentic/memory-system.js +0 -845
  358. package/core/agentic/parallel-tools.js +0 -366
  359. package/core/agentic/plan-mode.js +0 -572
  360. package/core/agentic/prompt-builder.js +0 -352
  361. package/core/agentic/response-templates.js +0 -290
  362. package/core/agentic/semantic-compression.js +0 -517
  363. package/core/agentic/think-blocks.js +0 -657
  364. package/core/agentic/tool-registry.js +0 -184
  365. package/core/agentic/validation-rules.js +0 -380
  366. package/core/command-registry.js +0 -698
  367. package/core/commands.js +0 -2237
  368. package/core/domain/task-stack.js +0 -497
  369. package/core/infrastructure/legacy-installer-detector.js +0 -546
  370. package/core/infrastructure/migrator.js +0 -796
  371. package/core/infrastructure/session-manager.js +0 -390
  372. package/core/utils/file-helper.js +0 -329
  373. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  374. package/packages/web/app/api/stats/route.ts +0 -38
  375. package/packages/web/components/AppSidebar.tsx +0 -113
  376. package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
  377. package/packages/web/components/stats/AgentsCard.tsx +0 -56
  378. package/packages/web/components/stats/BentoCard.tsx +0 -88
  379. package/packages/web/components/stats/HeroSection.tsx +0 -172
  380. package/packages/web/components/stats/NowCard.tsx +0 -71
  381. package/packages/web/components/stats/QueueCard.tsx +0 -58
  382. package/packages/web/components/stats/VelocityCard.tsx +0 -60
  383. package/packages/web/components/stats/index.ts +0 -17
  384. package/packages/web/hooks/useStats.ts +0 -28
  385. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  386. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  387. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  388. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  389. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  390. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  391. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
package/core/commands.js DELETED
@@ -1,2237 +0,0 @@
1
- /**
2
- * Agentic Commands Handler for prjct CLI
3
- *
4
- * 100% AGENTIC - Claude decides everything based on templates.
5
- * ZERO if/else business logic.
6
- *
7
- * All commands use the agentic execution engine.
8
- * Templates define what Claude should do.
9
- *
10
- * MIGRATED COMMANDS (18 total):
11
- * - Sprint 1 (9 CRITICAL): init, analyze, sync, feature, bug, now, done, next, ship
12
- * - Sprint 2 (4 IMPORTANT): context, recap, stuck, design
13
- * - Sprint 3 (5 OPTIONAL): cleanup, progress, roadmap, status, build
14
- *
15
- * PENDING (3 total):
16
- * - Sprint 4 (3 SETUP): start, setup, migrateAll
17
- */
18
-
19
- const path = require('path')
20
-
21
- const commandExecutor = require('./agentic/command-executor')
22
- const contextBuilder = require('./agentic/context-builder')
23
- const toolRegistry = require('./agentic/tool-registry')
24
- const memorySystem = require('./agentic/memory-system')
25
- const AgentRouter = require('./agentic/agent-router')
26
- const pathManager = require('./infrastructure/path-manager')
27
- const configManager = require('./infrastructure/config-manager')
28
- const authorDetector = require('./infrastructure/author-detector')
29
- const agentDetector = require('./infrastructure/agent-detector')
30
- const migrator = require('./infrastructure/migrator')
31
- const UpdateChecker = require('./infrastructure/update-checker')
32
- const { VERSION } = require('./utils/version')
33
- const dateHelper = require('./utils/date-helper')
34
- const jsonlHelper = require('./utils/jsonl-helper')
35
- const fileHelper = require('./utils/file-helper')
36
- const out = require('./utils/output')
37
-
38
- /**
39
- * Agentic Commands - Template-driven execution
40
- */
41
- class PrjctCommands {
42
- constructor() {
43
- this.agent = null
44
- this.agentInfo = null
45
- this.currentAuthor = null
46
- this.prjctDir = '.prjct'
47
- this.updateChecker = new UpdateChecker()
48
- this.updateNotificationShown = false
49
- this.commandExecutor = commandExecutor
50
- this.agentRouter = new AgentRouter()
51
- }
52
-
53
- /**
54
- * Initialize agent (Claude Code, Desktop, or Terminal)
55
- */
56
- async initializeAgent() {
57
- if (this.agent) return this.agent
58
-
59
- this.agentInfo = await agentDetector.detect()
60
-
61
- if (!this.agentInfo.isSupported) {
62
- throw new Error('Unsupported agent. Please use Claude Code, Claude Desktop, or Terminal.')
63
- }
64
-
65
- const Agent = require(`./infrastructure/agents/${this.agentInfo.type}-agent`)
66
- this.agent = new Agent()
67
-
68
- return this.agent
69
- }
70
-
71
- /**
72
- * Ensure project is initialized
73
- */
74
- async ensureProjectInit(projectPath) {
75
- if (await configManager.isConfigured(projectPath)) {
76
- return { success: true }
77
- }
78
-
79
- out.spin('initializing project...')
80
- const initResult = await this.init(null, projectPath)
81
- if (!initResult.success) {
82
- return initResult
83
- }
84
- return { success: true }
85
- }
86
-
87
- /**
88
- * Ensure author information is loaded
89
- */
90
- async ensureAuthor() {
91
- if (this.currentAuthor) return this.currentAuthor
92
- this.currentAuthor = await authorDetector.detectAuthorForLogs()
93
- return this.currentAuthor
94
- }
95
-
96
- /**
97
- * Get global project path
98
- */
99
- async getGlobalProjectPath(projectPath) {
100
- if (await migrator.needsMigration(projectPath)) {
101
- throw new Error('Project needs migration. Run /p:migrate first.')
102
- }
103
-
104
- const projectId = await configManager.getProjectId(projectPath)
105
- await pathManager.ensureProjectStructure(projectId)
106
- return pathManager.getGlobalProjectPath(projectId)
107
- }
108
-
109
- /**
110
- * Log to memory
111
- */
112
- async logToMemory(projectPath, action, data) {
113
- try {
114
- const author = await this.ensureAuthor()
115
- const projectId = await configManager.getProjectId(projectPath)
116
- const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
117
-
118
- const entry = {
119
- timestamp: dateHelper.getTimestamp(),
120
- action,
121
- data,
122
- author: author.name,
123
- }
124
-
125
- await jsonlHelper.appendJsonLine(memoryPath, entry)
126
- } catch (error) {
127
- // Non-critical - don't fail the command
128
- }
129
- }
130
-
131
- /**
132
- * /p:now - Set or show current task
133
- * AGENTIC EXECUTION
134
- */
135
- async now(task = null, projectPath = process.cwd()) {
136
- try {
137
- const initResult = await this.ensureProjectInit(projectPath)
138
- if (!initResult.success) return initResult
139
-
140
- const context = await contextBuilder.build(projectPath, { task })
141
-
142
- if (task) {
143
- // MANDATORY: Assign agent before setting task
144
- const agentResult = await this._assignAgentForTask(task, projectPath, context)
145
- const agent = agentResult.agent?.name || 'generalist'
146
- const confidence = agentResult.routing?.confidence || 0.5
147
-
148
- // Set task WITH agent
149
- const nowContent = `# NOW\n\n**${task}**\n\nStarted: ${new Date().toLocaleString()}\nAgent: ${agent} (${Math.round(confidence * 100)}% confidence)\n`
150
- await toolRegistry.get('Write')(context.paths.now, nowContent)
151
-
152
- out.done(`${task} [${agent}]`)
153
-
154
- await this.logToMemory(projectPath, 'task_started', {
155
- task,
156
- agent,
157
- confidence,
158
- timestamp: dateHelper.getTimestamp(),
159
- })
160
- return { success: true, task, agent }
161
- } else {
162
- // Show current task
163
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
164
-
165
- if (!nowContent || nowContent.includes('No current task')) {
166
- out.warn('no active task')
167
- return { success: true, message: 'No active task' }
168
- }
169
-
170
- // Extract task name and agent for minimal output
171
- const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
172
- const agentMatch = nowContent.match(/Agent: ([^\s(]+)/)
173
- const currentTask = taskMatch ? taskMatch[1] : 'unknown'
174
- const currentAgent = agentMatch ? agentMatch[1] : ''
175
- out.done(`working on: ${currentTask}${currentAgent ? ` [${currentAgent}]` : ''}`)
176
- return { success: true, content: nowContent }
177
- }
178
- } catch (error) {
179
- out.fail(error.message)
180
- return { success: false, error: error.message }
181
- }
182
- }
183
-
184
- /**
185
- * /p:done - Complete current task
186
- * AGENTIC EXECUTION
187
- */
188
- async done(projectPath = process.cwd()) {
189
- try {
190
- const initResult = await this.ensureProjectInit(projectPath)
191
- if (!initResult.success) return initResult
192
-
193
- const context = await contextBuilder.build(projectPath)
194
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
195
-
196
- // Validate: must have active task
197
- if (!nowContent || nowContent.includes('No current task') || nowContent.trim() === '# NOW') {
198
- out.warn('no active task')
199
- return { success: true, message: 'No active task to complete' }
200
- }
201
-
202
- // Extract task and duration
203
- const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
204
- const task = taskMatch ? taskMatch[1] : 'task'
205
-
206
- const startedMatch = nowContent.match(/Started: (.+)/)
207
- let duration = ''
208
- if (startedMatch) {
209
- const started = new Date(startedMatch[1])
210
- duration = dateHelper.calculateDuration(started)
211
- }
212
-
213
- // Clear now.md
214
- const emptyNow = '# NOW\n\nNo current task. Use `/p:now` to set focus.\n'
215
- await toolRegistry.get('Write')(context.paths.now, emptyNow)
216
-
217
- out.done(`${task}${duration ? ` (${duration})` : ''}`)
218
-
219
- await this.logToMemory(projectPath, 'task_completed', {
220
- task,
221
- duration,
222
- timestamp: dateHelper.getTimestamp(),
223
- })
224
- return { success: true, task, duration }
225
- } catch (error) {
226
- out.fail(error.message)
227
- return { success: false, error: error.message }
228
- }
229
- }
230
-
231
- /**
232
- * /p:next - Show priority queue
233
- * AGENTIC EXECUTION
234
- */
235
- async next(projectPath = process.cwd()) {
236
- try {
237
- const initResult = await this.ensureProjectInit(projectPath)
238
- if (!initResult.success) return initResult
239
-
240
- const context = await contextBuilder.build(projectPath)
241
- const nextContent = await toolRegistry.get('Read')(context.paths.next)
242
-
243
- if (!nextContent || nextContent.trim() === '# NEXT\n\n## Priority Queue') {
244
- out.warn('queue empty')
245
- return { success: true, message: 'Queue is empty' }
246
- }
247
-
248
- // Count tasks for minimal output
249
- const taskCount = (nextContent.match(/^- \[/gm) || []).length
250
- out.done(`${taskCount} task${taskCount !== 1 ? 's' : ''} queued`)
251
-
252
- return { success: true, content: nextContent }
253
- } catch (error) {
254
- out.fail(error.message)
255
- return { success: false, error: error.message }
256
- }
257
- }
258
-
259
- /**
260
- * /p:init - Initialize prjct project
261
- * AGENTIC EXECUTION
262
- *
263
- * 3 modes:
264
- * 1. Existing project → analyze + generate agents
265
- * 2. Blank project + idea → ARCHITECT MODE
266
- * 3. Blank project no idea → ask for idea
267
- */
268
- async init(idea = null, projectPath = process.cwd()) {
269
- try {
270
- await this.initializeAgent()
271
-
272
- // Check if already configured
273
- const isConfigured = await configManager.isConfigured(projectPath)
274
-
275
- if (isConfigured) {
276
- out.warn('already initialized')
277
- return { success: false, message: 'Already initialized' }
278
- }
279
-
280
- out.spin('initializing...')
281
-
282
- // Detect author from git
283
- const author = await authorDetector.detect()
284
-
285
- // Generate project ID from path hash
286
- const config = await configManager.createConfig(projectPath, author)
287
- const projectId = config.projectId
288
-
289
- out.spin('creating structure...')
290
-
291
- // Ensure global structure exists
292
- await pathManager.ensureProjectStructure(projectId)
293
- const globalPath = pathManager.getGlobalProjectPath(projectId)
294
-
295
- // Create base files
296
- // P1.1: Added patterns.json for Layered Memory System
297
- const baseFiles = {
298
- 'core/now.md': '# NOW\n\nNo current task. Use `/p:now` to set focus.\n',
299
- 'core/next.md': '# NEXT\n\n## Priority Queue\n\n',
300
- 'core/context.md': '# CONTEXT\n\n',
301
- 'progress/shipped.md': '# SHIPPED 🚀\n\n',
302
- 'progress/metrics.md': '# METRICS\n\n',
303
- 'planning/ideas.md': '# IDEAS 💡\n\n## Brain Dump\n\n',
304
- 'planning/roadmap.md': '# ROADMAP\n\n',
305
- 'planning/specs/.gitkeep': '# Specs directory - created by /p:spec\n',
306
- 'memory/context.jsonl': '',
307
- 'memory/patterns.json': JSON.stringify({
308
- version: 1,
309
- decisions: {},
310
- preferences: {},
311
- workflows: {},
312
- counters: {}
313
- }, null, 2),
314
- }
315
-
316
- for (const [filePath, content] of Object.entries(baseFiles)) {
317
- await toolRegistry.get('Write')(path.join(globalPath, filePath), content)
318
- }
319
-
320
-
321
- // Detect project state
322
- const isEmpty = await this._detectEmptyDirectory(projectPath)
323
- const hasCode = await this._detectExistingCode(projectPath)
324
-
325
- // MODE 1: Existing project
326
- if (hasCode || !isEmpty) {
327
- out.spin('analyzing project...')
328
- const analysisResult = await this.analyze({}, projectPath)
329
-
330
- if (analysisResult.success) {
331
- out.spin('generating agents...')
332
- await this.sync(projectPath)
333
- out.done('initialized')
334
- return { success: true, mode: 'existing', projectId }
335
- }
336
- }
337
-
338
- // MODE 2 & 3: Blank project
339
- if (isEmpty && !hasCode) {
340
- if (!idea) {
341
- out.done('blank project - provide idea for architect mode')
342
- return { success: true, mode: 'blank_no_idea', projectId }
343
- }
344
-
345
- // MODE 2: ARCHITECT MODE
346
- out.spin('architect mode...')
347
- const sessionPath = path.join(globalPath, 'planning', 'architect-session.md')
348
- const sessionContent = `# Architect Session\n\n## Idea\n${idea}\n\n## Status\nInitialized - awaiting stack recommendation\n\nGenerated: ${new Date().toLocaleString()}\n`
349
- await toolRegistry.get('Write')(sessionPath, sessionContent)
350
-
351
- const commandInstaller = require('./infrastructure/command-installer')
352
- await commandInstaller.installGlobalConfig()
353
-
354
- out.done('architect mode ready')
355
- return { success: true, mode: 'architect', projectId, idea }
356
- }
357
-
358
- // Update global CLAUDE.md with latest instructions (fallback for any case)
359
- const commandInstaller = require('./infrastructure/command-installer')
360
- await commandInstaller.installGlobalConfig()
361
-
362
- out.done('initialized')
363
- return { success: true, projectId }
364
- } catch (error) {
365
- out.fail(error.message)
366
- return { success: false, error: error.message }
367
- }
368
- }
369
-
370
- /**
371
- * Detect if directory is empty (excluding common files)
372
- * @private
373
- */
374
- async _detectEmptyDirectory(projectPath) {
375
- try {
376
- const entries = await fileHelper.listFiles(projectPath)
377
- const meaningfulFiles = entries.filter(
378
- (name) =>
379
- !name.startsWith('.') &&
380
- name !== 'node_modules' &&
381
- name !== 'package.json' &&
382
- name !== 'package-lock.json' &&
383
- name !== 'README.md'
384
- )
385
- return meaningfulFiles.length === 0
386
- } catch {
387
- return true
388
- }
389
- }
390
-
391
- /**
392
- * Detect if directory has existing code
393
- * @private
394
- */
395
- async _detectExistingCode(projectPath) {
396
- try {
397
- const codePatterns = [
398
- 'src',
399
- 'lib',
400
- 'app',
401
- 'components',
402
- 'pages',
403
- 'api',
404
- 'main.go',
405
- 'main.rs',
406
- 'main.py',
407
- ]
408
- const entries = await fileHelper.listFiles(projectPath)
409
-
410
- return entries.some((name) => codePatterns.includes(name))
411
- } catch {
412
- return false
413
- }
414
- }
415
-
416
- /**
417
- * All other commands - TODO: Migrate to agentic execution
418
- * For now, return "not implemented" message
419
- */
420
- /**
421
- * /p:feature - Add feature with value analysis, roadmap, and task breakdown
422
- * AGENTIC EXECUTION
423
- */
424
- async feature(description, projectPath = process.cwd()) {
425
- try {
426
- const initResult = await this.ensureProjectInit(projectPath)
427
- if (!initResult.success) return initResult
428
-
429
- if (!description) {
430
- out.fail('description required')
431
- return { success: false, error: 'Description required' }
432
- }
433
-
434
- out.spin(`planning ${description}...`)
435
-
436
- const context = await contextBuilder.build(projectPath, { description })
437
-
438
- // Task breakdown
439
- const tasks = this._breakdownFeatureTasks(description)
440
-
441
- // MANDATORY: Assign agent to each task
442
- const tasksWithAgents = []
443
- for (const taskDesc of tasks) {
444
- const agentResult = await this._assignAgentForTask(taskDesc, projectPath, context)
445
- const agent = agentResult.agent?.name || 'generalist'
446
- tasksWithAgents.push({ task: taskDesc, agent })
447
- }
448
-
449
- // Write to next.md with agents
450
- const nextContent =
451
- (await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
452
- const taskSection =
453
- `\n## Feature: ${description}\n\n` +
454
- tasksWithAgents.map((t, i) => `${i + 1}. [${t.agent}] [ ] ${t.task}`).join('\n') +
455
- `\n\nEstimated: ${tasks.length * 2}h\n`
456
-
457
- await toolRegistry.get('Write')(context.paths.next, nextContent + taskSection)
458
-
459
- // Log to memory with agent assignments
460
- await this.logToMemory(projectPath, 'feature_planned', {
461
- feature: description,
462
- tasks: tasksWithAgents.length,
463
- assignments: tasksWithAgents.map(t => ({ task: t.task, agent: t.agent })),
464
- timestamp: dateHelper.getTimestamp(),
465
- })
466
-
467
- // Show summary with agent distribution
468
- const agentCounts = tasksWithAgents.reduce((acc, t) => {
469
- acc[t.agent] = (acc[t.agent] || 0) + 1
470
- return acc
471
- }, {})
472
- const agentSummary = Object.entries(agentCounts).map(([a, c]) => `${a}:${c}`).join(' ')
473
-
474
- out.done(`${tasks.length} tasks [${agentSummary}]`)
475
-
476
- return { success: true, feature: description, tasks: tasksWithAgents }
477
- } catch (error) {
478
- out.fail(error.message)
479
- return { success: false, error: error.message }
480
- }
481
- }
482
-
483
- /**
484
- * Breakdown feature into tasks
485
- * Claude would do intelligent breakdown based on feature description
486
- * @private
487
- */
488
- _breakdownFeatureTasks(description) {
489
- // AGENTIC: Claude analyzes and creates tasks via templates/analysis/task-breakdown.md
490
- // This returns a placeholder - real breakdown happens in template execution
491
- return [
492
- `Analyze and plan: ${description}`,
493
- 'Implement core functionality',
494
- 'Test and validate',
495
- 'Document changes',
496
- ]
497
- }
498
-
499
- /**
500
- * /p:bug - Report and track bugs with auto-prioritization
501
- * AGENTIC EXECUTION
502
- */
503
- async bug(description, projectPath = process.cwd()) {
504
- try {
505
- const initResult = await this.ensureProjectInit(projectPath)
506
- if (!initResult.success) return initResult
507
-
508
- if (!description) {
509
- out.fail('bug description required')
510
- return { success: false, error: 'Description required' }
511
- }
512
-
513
- out.spin('tracking bug...')
514
-
515
- const context = await contextBuilder.build(projectPath, { description })
516
- const severity = this._detectBugSeverity(description)
517
-
518
- // MANDATORY: Assign agent to bug
519
- const agentResult = await this._assignAgentForTask(`fix bug: ${description}`, projectPath, context)
520
- const agent = agentResult.agent?.name || 'generalist'
521
-
522
- // Add to next.md with priority and agent
523
- const nextContent =
524
- (await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
525
- const bugEntry = `\n## 🐛 BUG [${severity.toUpperCase()}] [${agent}]: ${description}\n\nReported: ${new Date().toLocaleString()}\nPriority: ${severity === 'critical' ? '⚠️ URGENT' : severity === 'high' ? '🔴 High' : '🟡 Normal'}\nAssigned: ${agent}\n`
526
-
527
- // Insert at top if critical/high, at bottom otherwise
528
- const updatedContent =
529
- severity === 'critical' || severity === 'high'
530
- ? nextContent.replace('## Priority Queue\n\n', `## Priority Queue\n\n${bugEntry}\n`)
531
- : nextContent + bugEntry
532
-
533
- await toolRegistry.get('Write')(context.paths.next, updatedContent)
534
-
535
- // Log to memory with agent
536
- await this.logToMemory(projectPath, 'bug_reported', {
537
- bug: description,
538
- severity,
539
- agent,
540
- timestamp: dateHelper.getTimestamp(),
541
- })
542
-
543
- out.done(`bug [${severity}] → ${agent}`)
544
-
545
- return { success: true, bug: description, severity, agent }
546
- } catch (error) {
547
- out.fail(error.message)
548
- return { success: false, error: error.message }
549
- }
550
- }
551
-
552
- /**
553
- * Detect bug severity from description
554
- * Claude would do intelligent analysis
555
- * @private
556
- */
557
- _detectBugSeverity(description) {
558
- // AGENTIC: Claude assesses severity via templates/analysis/bug-severity.md
559
- // Returns default - real assessment happens in template execution
560
- return 'medium'
561
- }
562
-
563
- /**
564
- * /p:ship - Ship feature with complete automated workflow
565
- * AGENTIC EXECUTION
566
- */
567
- async ship(feature, projectPath = process.cwd()) {
568
- try {
569
- const initResult = await this.ensureProjectInit(projectPath)
570
- if (!initResult.success) return initResult
571
-
572
- if (!feature) {
573
- // Try to infer from current task
574
- const context = await contextBuilder.build(projectPath)
575
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
576
- if (nowContent && nowContent.includes('**')) {
577
- const match = nowContent.match(/\*\*(.+?)\*\*/)
578
- feature = match ? match[1] : 'current work'
579
- } else {
580
- feature = 'current work'
581
- }
582
- }
583
-
584
- out.spin(`shipping ${feature}...`)
585
-
586
- // Step 1: Lint
587
- const lintResult = await this._runLint(projectPath)
588
-
589
- // Step 2: Tests
590
- out.spin('running tests...')
591
- const testResult = await this._runTests(projectPath)
592
-
593
- // Step 3-5: Version + changelog
594
- out.spin('updating version...')
595
- const newVersion = await this._bumpVersion(projectPath)
596
- await this._updateChangelog(feature, newVersion, projectPath)
597
-
598
- // Step 6-7: Git commit + push
599
- out.spin('committing...')
600
- const commitResult = await this._createShipCommit(feature, projectPath)
601
-
602
- if (commitResult.success) {
603
- out.spin('pushing...')
604
- await this._gitPush(projectPath)
605
- }
606
-
607
- // Log to memory and shipped.md
608
- const context = await contextBuilder.build(projectPath)
609
- const shippedContent =
610
- (await toolRegistry.get('Read')(context.paths.shipped)) || '# SHIPPED 🚀\n\n'
611
- const shippedEntry = `\n## ${feature}\n\nShipped: ${new Date().toLocaleString()}\nVersion: ${newVersion}\n`
612
- await toolRegistry.get('Write')(context.paths.shipped, shippedContent + shippedEntry)
613
-
614
- await this.logToMemory(projectPath, 'feature_shipped', {
615
- feature,
616
- version: newVersion,
617
- timestamp: dateHelper.getTimestamp(),
618
- })
619
-
620
- // P1.1: Learn patterns from this ship
621
- const config = await configManager.getConfig(projectPath)
622
- const projectId = config.projectId
623
-
624
- // Record shipping workflow patterns
625
- await memorySystem.learnDecision(projectId, 'commit_footer', 'prjct', 'ship')
626
-
627
- // Track if tests were run (for quick_ship pattern learning)
628
- if (testResult.success) {
629
- await memorySystem.recordDecision(projectId, 'test_before_ship', 'true', 'ship')
630
- }
631
-
632
- // Record workflow if it's a quick ship (small changes)
633
- const isQuickShip = !lintResult.success || !testResult.success
634
- if (isQuickShip) {
635
- await memorySystem.recordWorkflow(projectId, 'quick_ship', {
636
- description: 'Ship without full checks',
637
- feature_type: feature.toLowerCase().includes('doc') ? 'docs' : 'other'
638
- })
639
- }
640
-
641
- out.done(`v${newVersion} shipped`)
642
-
643
- return { success: true, feature, version: newVersion }
644
- } catch (error) {
645
- out.fail(error.message)
646
- return { success: false, error: error.message }
647
- }
648
- }
649
-
650
- /**
651
- * Run lint checks
652
- * @private
653
- */
654
- async _runLint(_projectPath) {
655
- try {
656
- const { stderr } = await toolRegistry.get('Bash')('npm run lint 2>&1 || true')
657
- return { success: !stderr.includes('error'), message: 'passed' }
658
- } catch {
659
- return { success: false, message: 'no lint script (skipped)' }
660
- }
661
- }
662
-
663
- /**
664
- * Run tests
665
- * @private
666
- */
667
- async _runTests(_projectPath) {
668
- try {
669
- const { stderr } = await toolRegistry.get('Bash')(
670
- 'npm test -- --passWithNoTests 2>&1 || true'
671
- )
672
- return { success: !stderr.includes('FAIL'), message: 'passed' }
673
- } catch {
674
- return { success: false, message: 'no test script (skipped)' }
675
- }
676
- }
677
-
678
- /**
679
- * Bump version
680
- * @private
681
- */
682
- async _bumpVersion(projectPath) {
683
- try {
684
- const pkgPath = path.join(projectPath, 'package.json')
685
- const pkg = await fileHelper.readJson(pkgPath, {})
686
- const oldVersion = pkg.version || '0.0.0'
687
- const [major, minor, patch] = oldVersion.split('.').map(Number)
688
- const newVersion = `${major}.${minor}.${patch + 1}`
689
- pkg.version = newVersion
690
- await fileHelper.writeJson(pkgPath, pkg)
691
- return newVersion
692
- } catch {
693
- return '0.0.1'
694
- }
695
- }
696
-
697
- /**
698
- * Update CHANGELOG
699
- * @private
700
- */
701
- async _updateChangelog(feature, version, projectPath) {
702
- try {
703
- const changelogPath = path.join(projectPath, 'CHANGELOG.md')
704
- const changelog = await fileHelper.readFile(changelogPath, '# Changelog\n\n')
705
-
706
- const entry = `## [${version}] - ${dateHelper.formatDate(new Date())}\n\n### Added\n- ${feature}\n\n`
707
- const updated = changelog.replace('# Changelog\n\n', `# Changelog\n\n${entry}`)
708
-
709
- await fileHelper.writeFile(changelogPath, updated)
710
- } catch (error) {
711
- console.error(' Warning: Could not update CHANGELOG')
712
- }
713
- }
714
-
715
- /**
716
- * Create git commit for ship
717
- * @private
718
- */
719
- async _createShipCommit(feature, _projectPath) {
720
- try {
721
- await toolRegistry.get('Bash')('git add .')
722
-
723
- const commitMsg = `feat: ${feature}\n\n🤖 Generated with [p/](https://www.prjct.app/)\nDesigned for [Claude](https://www.anthropic.com/claude)`
724
-
725
- await toolRegistry.get('Bash')(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`)
726
-
727
- return { success: true, message: 'Committed' }
728
- } catch {
729
- return { success: false, message: 'No changes to commit' }
730
- }
731
- }
732
-
733
- /**
734
- * Push to remote
735
- * @private
736
- */
737
- async _gitPush(_projectPath) {
738
- try {
739
- await toolRegistry.get('Bash')('git push')
740
- return { success: true, message: 'Pushed to remote' }
741
- } catch {
742
- return { success: false, message: 'Push failed (no remote or auth issue)' }
743
- }
744
- }
745
-
746
- /**
747
- * /p:context - Show project context and recent activity
748
- * AGENTIC EXECUTION
749
- */
750
- async context(projectPath = process.cwd()) {
751
- try {
752
- const initResult = await this.ensureProjectInit(projectPath)
753
- if (!initResult.success) return initResult
754
-
755
- out.spin('loading context...')
756
- const context = await contextBuilder.build(projectPath)
757
-
758
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
759
- const nextContent = await toolRegistry.get('Read')(context.paths.next)
760
-
761
- // Extract summary
762
- let task = 'none'
763
- if (nowContent && !nowContent.includes('No current task')) {
764
- const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
765
- task = taskMatch ? taskMatch[1] : 'active'
766
- }
767
-
768
- const nextLines = nextContent?.split('\n').filter((line) => line.trim() && !line.startsWith('#')) || []
769
- const queueCount = nextLines.length
770
-
771
- await this.logToMemory(projectPath, 'context_viewed', { timestamp: dateHelper.getTimestamp() })
772
-
773
- out.done(`task: ${task} | queue: ${queueCount}`)
774
- return { success: true }
775
- } catch (error) {
776
- out.fail(error.message)
777
- return { success: false, error: error.message }
778
- }
779
- }
780
-
781
- /**
782
- * /p:recap - Show project overview with progress
783
- * AGENTIC EXECUTION
784
- */
785
- async recap(projectPath = process.cwd()) {
786
- try {
787
- const initResult = await this.ensureProjectInit(projectPath)
788
- if (!initResult.success) return initResult
789
-
790
- out.spin('loading recap...')
791
- const context = await contextBuilder.build(projectPath)
792
-
793
- const shippedContent = await toolRegistry.get('Read')(context.paths.shipped)
794
- const shippedFeatures = shippedContent?.split('##').filter((s) => s.trim() && !s.includes('SHIPPED')) || []
795
-
796
- const nextContent = await toolRegistry.get('Read')(context.paths.next)
797
- const nextTasks = nextContent?.split('\n').filter((l) => l.match(/^\d+\./) || l.includes('[ ]')).length || 0
798
-
799
- const ideasContent = await toolRegistry.get('Read')(context.paths.ideas)
800
- const ideas = ideasContent?.split('##').filter((s) => s.trim() && !s.includes('IDEAS') && !s.includes('Brain')).length || 0
801
-
802
- await this.logToMemory(projectPath, 'recap_viewed', {
803
- shipped: shippedFeatures.length,
804
- tasks: nextTasks,
805
- timestamp: dateHelper.getTimestamp(),
806
- })
807
-
808
- out.done(`shipped: ${shippedFeatures.length} | queue: ${nextTasks} | ideas: ${ideas}`)
809
- return { success: true, stats: { shipped: shippedFeatures.length, tasks: nextTasks, ideas } }
810
- } catch (error) {
811
- out.fail(error.message)
812
- return { success: false, error: error.message }
813
- }
814
- }
815
-
816
- /**
817
- * /p:stuck - Get contextual help with problems
818
- * AGENTIC EXECUTION
819
- */
820
- async stuck(issue, projectPath = process.cwd()) {
821
- try {
822
- const initResult = await this.ensureProjectInit(projectPath)
823
- if (!initResult.success) return initResult
824
-
825
- if (!issue) {
826
- out.fail('issue description required')
827
- return { success: false, error: 'Issue description required' }
828
- }
829
-
830
- out.spin('logging issue...')
831
-
832
- const analyzer = require('./domain/analyzer')
833
- analyzer.init(projectPath)
834
- const packageJson = await analyzer.readPackageJson()
835
- const detectedStack = packageJson?.name || 'project'
836
-
837
- await this.logToMemory(projectPath, 'help_requested', {
838
- issue,
839
- stack: detectedStack,
840
- timestamp: dateHelper.getTimestamp(),
841
- })
842
-
843
- out.done(`issue logged: ${issue.slice(0, 40)}`)
844
- return { success: true, issue, stack: detectedStack }
845
- } catch (error) {
846
- out.fail(error.message)
847
- return { success: false, error: error.message }
848
- }
849
- }
850
-
851
- /**
852
- * /p:design - Design system architecture, APIs, and components
853
- * AGENTIC EXECUTION
854
- */
855
- /**
856
- * Memory cleanup helper
857
- * Rotates large JSONL files, archives old sessions, reports disk usage
858
- * @private
859
- */
860
- async _cleanupMemory(projectPath) {
861
- const projectId = await configManager.getProjectId(projectPath)
862
-
863
- const results = { rotated: [], totalSize: 0, freedSpace: 0 }
864
- const jsonlFiles = [
865
- pathManager.getFilePath(projectId, 'memory', 'context.jsonl'),
866
- pathManager.getFilePath(projectId, 'progress', 'shipped.md'),
867
- pathManager.getFilePath(projectId, 'planning', 'ideas.md'),
868
- ]
869
-
870
- for (const filePath of jsonlFiles) {
871
- try {
872
- const sizeMB = await jsonlHelper.getFileSizeMB(filePath)
873
- if (sizeMB > 0) {
874
- results.totalSize += sizeMB
875
- const rotated = await jsonlHelper.rotateJsonLinesIfNeeded(filePath, 10)
876
- if (rotated) {
877
- results.rotated.push(path.basename(filePath))
878
- results.freedSpace += sizeMB
879
- }
880
- }
881
- } catch {
882
- // skip
883
- }
884
- }
885
-
886
- return { success: true, results }
887
- }
888
-
889
- /**
890
- * Internal cleanup helper for memory during normal cleanup
891
- * @private
892
- */
893
- async _cleanupMemoryInternal(projectPath) {
894
- const projectId = await configManager.getProjectId(projectPath)
895
-
896
- // Silently rotate large files
897
- const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
898
- await jsonlHelper.rotateJsonLinesIfNeeded(memoryPath, 10)
899
- }
900
-
901
- async design(target = null, options = {}, projectPath = process.cwd()) {
902
- try {
903
- const initResult = await this.ensureProjectInit(projectPath)
904
- if (!initResult.success) return initResult
905
-
906
- const designType = options.type || 'architecture'
907
- const validTypes = ['architecture', 'api', 'component', 'database', 'flow']
908
-
909
- if (!validTypes.includes(designType)) {
910
- out.fail(`invalid type: ${designType}`)
911
- return { success: false, error: 'Invalid design type' }
912
- }
913
-
914
- const designTarget = target || 'system'
915
- out.spin(`designing ${designType}...`)
916
-
917
- // Create designs directory if it doesn't exist
918
- const projectId = await configManager.getProjectId(projectPath)
919
- const designsPath = path.join(
920
- pathManager.getGlobalProjectPath(projectId),
921
- 'planning',
922
- 'designs'
923
- )
924
- await fileHelper.ensureDir(designsPath)
925
-
926
- // Generate design document based on type
927
- let designContent = ''
928
-
929
- switch (designType) {
930
- case 'architecture':
931
- designContent = this._generateArchitectureDesign(designTarget, projectPath)
932
- break
933
- case 'api':
934
- designContent = this._generateApiDesign(designTarget)
935
- break
936
- case 'component':
937
- designContent = this._generateComponentDesign(designTarget)
938
- break
939
- case 'database':
940
- designContent = this._generateDatabaseDesign(designTarget)
941
- break
942
- case 'flow':
943
- designContent = this._generateFlowDesign(designTarget)
944
- break
945
- }
946
-
947
- // Save design document
948
- const designFileName = `${designType}-${designTarget.toLowerCase().replace(/\s+/g, '-')}.md`
949
- const designFilePath = path.join(designsPath, designFileName)
950
- await fileHelper.writeFile(designFilePath, designContent)
951
-
952
- await this.logToMemory(projectPath, 'design_created', {
953
- type: designType,
954
- target: designTarget,
955
- timestamp: dateHelper.getTimestamp(),
956
- })
957
-
958
- out.done(`${designType} design created`)
959
- return { success: true, designPath: designFilePath, type: designType, target: designTarget }
960
- } catch (error) {
961
- out.fail(error.message)
962
- return { success: false, error: error.message }
963
- }
964
- }
965
-
966
- /**
967
- * Generate architecture design document
968
- * @private
969
- */
970
- _generateArchitectureDesign(target, projectPath) {
971
- // AGENTIC: Claude generates via templates/design/architecture.md
972
- return `# Architecture Design: ${target}\n\n*Use templates/design/architecture.md for full design*\n`
973
- }
974
-
975
- /**
976
- * Generate API design document
977
- * @private
978
- */
979
- _generateApiDesign(target) {
980
- // AGENTIC: Claude generates via templates/design/api.md
981
- return `# API Design: ${target}\n\n*Use templates/design/api.md for full design*\n`
982
- }
983
-
984
- /**
985
- * Generate component design document
986
- * @private
987
- */
988
- _generateComponentDesign(target) {
989
- // AGENTIC: Claude generates via templates/design/component.md
990
- return `# Component Design: ${target}\n\n*Use templates/design/component.md for full design*\n`
991
- }
992
-
993
- /**
994
- * Generate database design document
995
- * @private
996
- */
997
- _generateDatabaseDesign(target) {
998
- // AGENTIC: Claude generates via templates/design/database.md
999
- return `# Database Design: ${target}\n\n*Use templates/design/database.md for full design*\n`
1000
- }
1001
-
1002
- /**
1003
- * Generate flow design document
1004
- * @private
1005
- */
1006
- _generateFlowDesign(target) {
1007
- // AGENTIC: Claude generates via templates/design/flow.md
1008
- return `# Flow Design: ${target}\n\n*Use templates/design/flow.md for full design*\n`
1009
- }
1010
-
1011
- /**
1012
- * /p:cleanup - Clean temp files and old entries
1013
- * AGENTIC EXECUTION
1014
- */
1015
- async cleanup(_options = {}, projectPath = process.cwd()) {
1016
- try {
1017
- const initResult = await this.ensureProjectInit(projectPath)
1018
- if (!initResult.success) return initResult
1019
-
1020
- const isMemoryMode = _options.memory === true || _options.type === 'memory'
1021
-
1022
- if (isMemoryMode) {
1023
- out.spin('cleaning memory...')
1024
- const result = await this._cleanupMemory(projectPath)
1025
- out.done('memory cleaned')
1026
- return result
1027
- }
1028
-
1029
- out.spin('cleaning up...')
1030
-
1031
- const context = await contextBuilder.build(projectPath)
1032
- const projectId = await configManager.getProjectId(projectPath)
1033
-
1034
- const cleaned = []
1035
-
1036
- // Clean old memory entries (keep last 100)
1037
- const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
1038
- try {
1039
- const entries = await jsonlHelper.readJsonLines(memoryPath)
1040
-
1041
- if (entries.length > 100) {
1042
- const kept = entries.slice(-100)
1043
- await jsonlHelper.writeJsonLines(memoryPath, kept)
1044
- cleaned.push(`Memory: ${entries.length - 100} old entries removed`)
1045
- } else {
1046
- cleaned.push('Memory: No cleanup needed')
1047
- }
1048
- } catch {
1049
- cleaned.push('Memory: No file found')
1050
- }
1051
-
1052
- // Clean empty ideas sections
1053
- const ideasPath = context.paths.ideas
1054
- try {
1055
- const ideasContent = await toolRegistry.get('Read')(ideasPath)
1056
- const sections = ideasContent.split('##').filter((s) => s.trim())
1057
-
1058
- // Remove empty sections
1059
- const nonEmpty = sections.filter((section) => {
1060
- const lines = section
1061
- .trim()
1062
- .split('\n')
1063
- .filter((l) => l.trim())
1064
- return lines.length > 1 // Keep if has more than just title
1065
- })
1066
-
1067
- if (sections.length !== nonEmpty.length) {
1068
- const newContent =
1069
- '# IDEAS 💡\n\n## Brain Dump\n\n' +
1070
- nonEmpty
1071
- .slice(1)
1072
- .map((s) => '## ' + s.trim())
1073
- .join('\n\n')
1074
- await toolRegistry.get('Write')(ideasPath, newContent)
1075
- cleaned.push(`Ideas: ${sections.length - nonEmpty.length} empty sections removed`)
1076
- } else {
1077
- cleaned.push('Ideas: No cleanup needed')
1078
- }
1079
- } catch {
1080
- cleaned.push('Ideas: No file found')
1081
- }
1082
-
1083
- // Clean completed tasks from next.md (optional - user decides)
1084
- const nextPath = context.paths.next
1085
- try {
1086
- const nextContent = await toolRegistry.get('Read')(nextPath)
1087
- const completedTasks = (nextContent.match(/\[x\]/gi) || []).length
1088
-
1089
- if (completedTasks > 0) {
1090
- cleaned.push(
1091
- `Queue: ${completedTasks} completed tasks found (not removed - use /p:done to clear)`
1092
- )
1093
- } else {
1094
- cleaned.push('Queue: No completed tasks')
1095
- }
1096
- } catch {
1097
- cleaned.push('Queue: No file found')
1098
- }
1099
-
1100
- await this._cleanupMemoryInternal(projectPath)
1101
-
1102
- await this.logToMemory(projectPath, 'cleanup_performed', {
1103
- items: cleaned.length,
1104
- timestamp: dateHelper.getTimestamp(),
1105
- })
1106
-
1107
- out.done(`${cleaned.length} items cleaned`)
1108
- return { success: true, cleaned }
1109
- } catch (error) {
1110
- out.fail(error.message)
1111
- return { success: false, error: error.message }
1112
- }
1113
- }
1114
-
1115
- /**
1116
- * /p:progress - Show metrics for period
1117
- * AGENTIC EXECUTION
1118
- */
1119
- async progress(period = 'week', projectPath = process.cwd()) {
1120
- try {
1121
- const initResult = await this.ensureProjectInit(projectPath)
1122
- if (!initResult.success) return initResult
1123
-
1124
- const validPeriods = ['day', 'week', 'month', 'all']
1125
- if (!validPeriods.includes(period)) period = 'week'
1126
-
1127
- out.spin(`loading ${period} progress...`)
1128
-
1129
- const projectId = await configManager.getProjectId(projectPath)
1130
- const memoryPath = pathManager.getFilePath(projectId, 'memory', 'context.jsonl')
1131
-
1132
- const startDate = period === 'day' ? dateHelper.getDaysAgo(1) :
1133
- period === 'week' ? dateHelper.getDaysAgo(7) :
1134
- period === 'month' ? dateHelper.getDaysAgo(30) : new Date(0)
1135
-
1136
- let entries = []
1137
- try {
1138
- const allEntries = await jsonlHelper.readJsonLines(memoryPath)
1139
- entries = allEntries.filter((e) => new Date(e.timestamp) >= startDate)
1140
- } catch { entries = [] }
1141
-
1142
- const metrics = {
1143
- tasksCompleted: entries.filter((e) => e.action === 'task_completed').length,
1144
- featuresShipped: entries.filter((e) => e.action === 'feature_shipped').length,
1145
- totalActions: entries.length,
1146
- }
1147
-
1148
- await this.logToMemory(projectPath, 'progress_viewed', { period, metrics, timestamp: dateHelper.getTimestamp() })
1149
-
1150
- out.done(`${period}: ${metrics.tasksCompleted} tasks | ${metrics.featuresShipped} shipped`)
1151
- return { success: true, period, metrics }
1152
- } catch (error) {
1153
- out.fail(error.message)
1154
- return { success: false, error: error.message }
1155
- }
1156
- }
1157
-
1158
- /**
1159
- * /p:roadmap - Show roadmap with ASCII logic maps
1160
- * AGENTIC EXECUTION
1161
- */
1162
- async roadmap(projectPath = process.cwd()) {
1163
- try {
1164
- const initResult = await this.ensureProjectInit(projectPath)
1165
- if (!initResult.success) return initResult
1166
-
1167
- out.spin('loading roadmap...')
1168
- const context = await contextBuilder.build(projectPath)
1169
- const roadmapContent = await toolRegistry.get('Read')(context.paths.roadmap)
1170
-
1171
- if (!roadmapContent || roadmapContent.trim() === '# ROADMAP') {
1172
- out.warn('no roadmap yet')
1173
- return { success: true, message: 'No roadmap' }
1174
- }
1175
-
1176
- // Count features in roadmap
1177
- const features = (roadmapContent.match(/##/g) || []).length
1178
-
1179
- await this.logToMemory(projectPath, 'roadmap_viewed', { timestamp: dateHelper.getTimestamp() })
1180
-
1181
- out.done(`${features} features in roadmap`)
1182
- return { success: true, content: roadmapContent }
1183
- } catch (error) {
1184
- out.fail(error.message)
1185
- return { success: false, error: error.message }
1186
- }
1187
- }
1188
-
1189
- /**
1190
- * Generate roadmap template
1191
- * @private
1192
- */
1193
- _generateRoadmapTemplate() {
1194
- return `
1195
- # ROADMAP
1196
-
1197
- ## Q1 2025 - Foundation
1198
-
1199
- \`\`\`
1200
- [Authentication] ──┐
1201
- ├──> [User Management] ──> [Dashboard]
1202
- [Database Setup] ──┘
1203
- \`\`\`
1204
-
1205
- Status: 🟢 In Progress
1206
-
1207
- ## Q2 2025 - Core Features
1208
-
1209
- \`\`\`
1210
- [API v1] ──┐
1211
- ├──> [Integration] ──> [Beta Launch]
1212
- [UI v2] ───┘
1213
- \`\`\`
1214
-
1215
- Status: ⏸️ Planned
1216
-
1217
- ## Dependencies
1218
-
1219
- - Authentication → User Management
1220
- - Database Setup → Authentication
1221
- - API v1 + UI v2 → Integration
1222
- `
1223
- }
1224
-
1225
- /**
1226
- * /p:status - KPI dashboard with ASCII graphics
1227
- * AGENTIC EXECUTION
1228
- */
1229
- async status(projectPath = process.cwd()) {
1230
- try {
1231
- const initResult = await this.ensureProjectInit(projectPath)
1232
- if (!initResult.success) return initResult
1233
-
1234
- console.log('📊 Project Status Dashboard\n')
1235
-
1236
- const context = await contextBuilder.build(projectPath)
1237
-
1238
- // Read project data
1239
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
1240
- const nextContent = await toolRegistry.get('Read')(context.paths.next)
1241
- const shippedContent = await toolRegistry.get('Read')(context.paths.shipped)
1242
- const ideasContent = await toolRegistry.get('Read')(context.paths.ideas)
1243
-
1244
- // Calculate stats
1245
- const stats = {
1246
- activeTask: nowContent && !nowContent.includes('No current task'),
1247
- tasksInQueue:
1248
- nextContent
1249
- ?.split('\n')
1250
- .filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]')).length || 0,
1251
- featuresShipped:
1252
- shippedContent
1253
- ?.split('##')
1254
- .filter((section) => section.trim() && !section.includes('SHIPPED 🚀')).length || 0,
1255
- ideasCaptured:
1256
- ideasContent
1257
- ?.split('##')
1258
- .filter(
1259
- (section) =>
1260
- section.trim() && !section.includes('IDEAS 💡') && !section.includes('Brain Dump')
1261
- ).length || 0,
1262
- }
1263
-
1264
- // Header
1265
- console.log('═══════════════════════════════════════════════════')
1266
- console.log(` ${path.basename(projectPath)} - Status Overview`)
1267
- console.log('═══════════════════════════════════════════════════\n')
1268
-
1269
- // Current Focus
1270
- console.log('## 🎯 Current Focus\n')
1271
- if (stats.activeTask) {
1272
- const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
1273
- const task = taskMatch ? taskMatch[1] : 'Active task'
1274
- const startedMatch = nowContent.match(/Started: (.+)/)
1275
- const started = startedMatch ? startedMatch[1] : 'Unknown'
1276
- console.log(` 📌 ${task}`)
1277
- console.log(` ⏱️ Started: ${started}\n`)
1278
- } else {
1279
- console.log(' No active task\n')
1280
- }
1281
-
1282
- // Queue Status
1283
- console.log('## 📋 Queue Status\n')
1284
- console.log(` Tasks in Queue: ${stats.tasksInQueue}`)
1285
- this._renderProgressBar('Queue Load', stats.tasksInQueue, 20)
1286
- console.log('')
1287
-
1288
- // Shipped Features
1289
- console.log('## 🚀 Shipped Features\n')
1290
- console.log(` Features Shipped: ${stats.featuresShipped}`)
1291
- this._renderProgressBar('Progress', stats.featuresShipped, 10)
1292
- console.log('')
1293
-
1294
- // Ideas Backlog
1295
- console.log('## 💡 Ideas Backlog\n')
1296
- console.log(` Ideas Captured: ${stats.ideasCaptured}`)
1297
- this._renderProgressBar('Backlog', stats.ideasCaptured, 15)
1298
- console.log('')
1299
-
1300
- // Overall Health
1301
- console.log('## 💚 Overall Health\n')
1302
- const health = this._calculateHealth(stats)
1303
- console.log(` Health Score: ${health.score}/100`)
1304
- this._renderProgressBar('Health', health.score, 100)
1305
- console.log(` ${health.message}\n`)
1306
-
1307
- console.log('💡 Next steps:')
1308
- console.log('• /p:now → Start working on a task')
1309
- console.log('• /p:feature → Add new feature')
1310
- console.log('• /p:ship → Ship completed work')
1311
-
1312
- await this.logToMemory(projectPath, 'status_viewed', {
1313
- stats,
1314
- health: health.score,
1315
- timestamp: dateHelper.getTimestamp(),
1316
- })
1317
-
1318
- return { success: true, stats, health }
1319
- } catch (error) {
1320
- console.error('❌ Error:', error.message)
1321
- return { success: false, error: error.message }
1322
- }
1323
- }
1324
-
1325
- /**
1326
- * Render ASCII progress bar
1327
- * @private
1328
- */
1329
- _renderProgressBar(label, value, max) {
1330
- const percentage = Math.min(100, Math.round((value / max) * 100))
1331
- const barLength = 30
1332
- const filled = Math.round((percentage / 100) * barLength)
1333
- const empty = barLength - filled
1334
-
1335
- const bar = '█'.repeat(filled) + '░'.repeat(empty)
1336
- console.log(` ${label}: [${bar}] ${percentage}%`)
1337
- }
1338
-
1339
- /**
1340
- * Calculate project health score
1341
- * @private
1342
- */
1343
- _calculateHealth(stats) {
1344
- // AGENTIC: Claude evaluates health via templates/analysis/health.md
1345
- // Simple calculation - real assessment happens in template execution
1346
- const hasActivity = stats.activeTask || stats.featuresShipped > 0
1347
- return {
1348
- score: hasActivity ? 70 : 50,
1349
- message: hasActivity ? '🟢 Active' : '🟡 Ready to start',
1350
- }
1351
- }
1352
-
1353
- /**
1354
- * /p:build - Start task with agent assignment
1355
- * AGENTIC EXECUTION
1356
- */
1357
- async build(taskOrNumber, projectPath = process.cwd()) {
1358
- try {
1359
- const initResult = await this.ensureProjectInit(projectPath)
1360
- if (!initResult.success) return initResult
1361
-
1362
- const context = await contextBuilder.build(projectPath, { task: taskOrNumber })
1363
-
1364
- // Check if already working on something
1365
- const nowContent = await toolRegistry.get('Read')(context.paths.now)
1366
- if (nowContent && !nowContent.includes('No current task')) {
1367
- console.log('⚠️ Already working on a task!')
1368
- console.log(' Complete it with /p:done first\n')
1369
- const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
1370
- const currentTask = taskMatch ? taskMatch[1] : 'current task'
1371
- console.log(` Current: ${currentTask}`)
1372
- return { success: false, message: 'Task already active' }
1373
- }
1374
-
1375
- let task = taskOrNumber
1376
-
1377
- // If number, get from queue
1378
- if (!isNaN(taskOrNumber)) {
1379
- const nextContent = await toolRegistry.get('Read')(context.paths.next)
1380
- const tasks = nextContent
1381
- .split('\n')
1382
- .filter((line) => line.trim().match(/^\d+\./) || line.includes('[ ]'))
1383
-
1384
- const index = parseInt(taskOrNumber) - 1
1385
- if (index >= 0 && index < tasks.length) {
1386
- task = tasks[index].replace(/^\d+\.\s*\[.\]\s*/, '').trim()
1387
- console.log(`📋 Selected from queue: ${task}\n`)
1388
- } else {
1389
- console.log(`❌ Invalid task number. Queue has ${tasks.length} tasks.`)
1390
- console.log(' Use /p:next to see queue')
1391
- return { success: false, error: 'Invalid task number' }
1392
- }
1393
- }
1394
-
1395
- if (!task) {
1396
- console.log('❌ Task description required')
1397
- console.log('Usage: /p:build "task description"')
1398
- console.log(' or: /p:build 1 (select from queue)')
1399
- return { success: false, error: 'Task required' }
1400
- }
1401
-
1402
- console.log(`🏗️ Building: ${task}\n`)
1403
-
1404
- // Detect complexity and estimate
1405
- const complexity = this._detectComplexity(task)
1406
- const estimate = complexity.hours
1407
-
1408
- console.log('📊 Analysis:')
1409
- console.log(` Complexity: ${complexity.level}`)
1410
- console.log(` Estimated: ${estimate}h`)
1411
- console.log(` Type: ${complexity.type}\n`)
1412
-
1413
- // MANDATORY: Assign agent using router
1414
- const agentResult = await this._assignAgentForTask(task, projectPath, context)
1415
- const agent = agentResult.agent?.name || 'generalist'
1416
- const confidence = agentResult.routing?.confidence || 0.5
1417
- console.log(`🤖 Agent: ${agent} (${Math.round(confidence * 100)}% confidence)\n`)
1418
-
1419
- // Set as current task with metadata
1420
- const nowContentNew = `# NOW
1421
-
1422
- **${task}**
1423
-
1424
- Started: ${new Date().toLocaleString()}
1425
- Estimated: ${estimate}h
1426
- Complexity: ${complexity.level}
1427
- Agent: ${agent} (${Math.round(confidence * 100)}% confidence)
1428
- `
1429
- await toolRegistry.get('Write')(context.paths.now, nowContentNew)
1430
-
1431
- console.log('✅ Task started!\n')
1432
- console.log('💡 Next steps:')
1433
- console.log('• Start coding')
1434
- console.log('• /p:done → Mark complete')
1435
- console.log('• /p:stuck → Get help if needed')
1436
-
1437
- await this.logToMemory(projectPath, 'task_built', {
1438
- task,
1439
- complexity: complexity.level,
1440
- estimate,
1441
- agent,
1442
- confidence,
1443
- timestamp: dateHelper.getTimestamp(),
1444
- })
1445
-
1446
- return { success: true, task, complexity, estimate, agent }
1447
- } catch (error) {
1448
- console.error('❌ Error:', error.message)
1449
- return { success: false, error: error.message }
1450
- }
1451
- }
1452
-
1453
- /**
1454
- * Detect task complexity
1455
- * @private
1456
- */
1457
- _detectComplexity(task) {
1458
- // AGENTIC: Claude analyzes complexity via templates/analysis/complexity.md
1459
- // Returns default - real analysis happens in template execution
1460
- return { level: 'medium', hours: 4, type: 'feature' }
1461
- }
1462
-
1463
- /**
1464
- * Assign agent for a task
1465
- * AGENTIC: Claude decides via templates/agent-assignment.md
1466
- * JS only orchestrates: load agents → build context → delegate to Claude
1467
- * @private
1468
- */
1469
- async _assignAgentForTask(taskDescription, projectPath, context) {
1470
- try {
1471
- const projectId = await configManager.getProjectId(projectPath)
1472
-
1473
- // ORCHESTRATION ONLY: Load available agents
1474
- const agentsPath = pathManager.getPath(projectId, 'agents')
1475
- const agentFiles = await fileHelper.listFiles(agentsPath, '.md')
1476
- const agents = agentFiles.map(f => f.replace('.md', ''))
1477
-
1478
- // ORCHESTRATION ONLY: Build context for Claude
1479
- const assignmentContext = {
1480
- task: taskDescription,
1481
- agents: agents.join(', ') || 'generalist',
1482
- projectPath,
1483
- // Claude will use this context + template to decide
1484
- }
1485
-
1486
- // AGENTIC: Claude decides agent via template
1487
- // The template templates/agent-assignment.md guides Claude's decision
1488
- // For now, return structure that prompt-builder will use with template
1489
- return {
1490
- agent: { name: agents[0] || 'generalist', domain: 'auto' },
1491
- routing: {
1492
- confidence: 0.8,
1493
- reason: 'Claude assigns via templates/agent-assignment.md',
1494
- availableAgents: agents
1495
- },
1496
- _agenticNote: 'Use templates/agent-assignment.md for actual assignment'
1497
- }
1498
- } catch (error) {
1499
- // Fallback - still return structure
1500
- return {
1501
- agent: { name: 'generalist', domain: 'general' },
1502
- routing: { confidence: 0.5, reason: 'Fallback - no agents found' }
1503
- }
1504
- }
1505
- }
1506
-
1507
- /**
1508
- * Auto-assign agent based on task (sync wrapper for backward compat)
1509
- * DEPRECATED: Use _assignAgentForTask instead
1510
- * @private
1511
- */
1512
- _autoAssignAgent(task) {
1513
- // For backward compatibility, return generalist synchronously
1514
- // New code should use _assignAgentForTask() which is async
1515
- console.warn('DEPRECATED: Use _assignAgentForTask() for proper agent routing')
1516
- return 'generalist'
1517
- }
1518
-
1519
- /**
1520
- * /p:analyze - Analyze repository and generate summary
1521
- * AGENTIC EXECUTION
1522
- */
1523
- async analyze(options = {}, projectPath = process.cwd()) {
1524
- try {
1525
- await this.initializeAgent()
1526
-
1527
- console.log('🔍 Analyzing repository...\n')
1528
-
1529
- // Initialize analyzer for this project
1530
- const analyzer = require('./domain/analyzer')
1531
- analyzer.init(projectPath)
1532
-
1533
- // Build context
1534
- const context = await contextBuilder.build(projectPath, options)
1535
-
1536
- // Collect data using analyzer helpers (ZERO predetermined patterns)
1537
- const analysisData = {
1538
- // Package managers
1539
- packageJson: await analyzer.readPackageJson(),
1540
- cargoToml: await analyzer.readCargoToml(),
1541
- goMod: await analyzer.readGoMod(),
1542
- requirements: await analyzer.readRequirements(),
1543
-
1544
- // Project structure
1545
- directories: await analyzer.listDirectories(),
1546
- fileCount: await analyzer.countFiles(),
1547
-
1548
- // Git data
1549
- gitStats: await analyzer.getGitStats(),
1550
- gitLog: await analyzer.getGitLog(20),
1551
-
1552
- // Common files
1553
- hasDockerfile: await analyzer.fileExists('Dockerfile'),
1554
- hasDockerCompose: await analyzer.fileExists('docker-compose.yml'),
1555
- hasReadme: await analyzer.fileExists('README.md'),
1556
- hasTsconfig: await analyzer.fileExists('tsconfig.json'),
1557
- hasViteConfig:
1558
- (await analyzer.fileExists('vite.config.ts')) ||
1559
- (await analyzer.fileExists('vite.config.js')),
1560
- hasNextConfig:
1561
- (await analyzer.fileExists('next.config.js')) ||
1562
- (await analyzer.fileExists('next.config.mjs')),
1563
- }
1564
-
1565
- // Generate summary (Claude decides what's relevant based on data found)
1566
- const summary = this._generateAnalysisSummary(analysisData, projectPath)
1567
-
1568
- // Save to analysis/repo-summary.md
1569
- const summaryPath =
1570
- context.paths.analysis ||
1571
- pathManager.getFilePath(
1572
- await configManager.getProjectId(projectPath),
1573
- 'analysis',
1574
- 'repo-summary.md'
1575
- )
1576
-
1577
- await toolRegistry.get('Write')(summaryPath, summary)
1578
-
1579
- // Log to memory
1580
- await this.logToMemory(projectPath, 'repository_analyzed', {
1581
- timestamp: dateHelper.getTimestamp(),
1582
- fileCount: analysisData.fileCount,
1583
- gitCommits: analysisData.gitStats.totalCommits,
1584
- })
1585
-
1586
- // Generate dynamic context for Claude
1587
- const contextSync = require('./context-sync')
1588
- const projectId = await configManager.getProjectId(projectPath)
1589
- await contextSync.generateLocalContext(projectPath, projectId)
1590
-
1591
- // Update global CLAUDE.md with latest instructions
1592
- const commandInstaller = require('./infrastructure/command-installer')
1593
- const globalConfigResult = await commandInstaller.installGlobalConfig()
1594
- if (globalConfigResult.success) {
1595
- console.log('📝 Updated ~/.claude/CLAUDE.md')
1596
- }
1597
-
1598
- console.log('✅ Analysis complete!\n')
1599
- console.log('📄 Full report: analysis/repo-summary.md')
1600
- console.log('📝 Context: ~/.prjct-cli/projects/' + projectId + '/CLAUDE.md\n')
1601
- console.log('Next steps:')
1602
- console.log('• /p:sync → Generate agents based on stack')
1603
- console.log('• /p:feature → Add a new feature')
1604
-
1605
- return {
1606
- success: true,
1607
- summaryPath,
1608
- data: analysisData,
1609
- }
1610
- } catch (error) {
1611
- console.error('❌ Error:', error.message)
1612
- return { success: false, error: error.message }
1613
- }
1614
- }
1615
-
1616
- /**
1617
- * Generate analysis summary from collected data
1618
- * Claude decides what's relevant - NO predetermined patterns
1619
- * @private
1620
- */
1621
- _generateAnalysisSummary(data, projectPath) {
1622
- const lines = []
1623
-
1624
- lines.push('# Repository Analysis\n')
1625
- lines.push(`Generated: ${new Date().toLocaleString()}\n`)
1626
-
1627
- // Project name from path
1628
- const projectName = path.basename(projectPath)
1629
- lines.push(`## Project: ${projectName}\n`)
1630
-
1631
- // Technologies detected (based on what files exist)
1632
- lines.push('## Stack Detected\n')
1633
-
1634
- if (data.packageJson) {
1635
- lines.push('### JavaScript/TypeScript\n')
1636
- lines.push('- **Package Manager**: npm/yarn/pnpm')
1637
- if (data.packageJson.dependencies) {
1638
- const deps = Object.keys(data.packageJson.dependencies)
1639
- if (deps.length > 0) {
1640
- lines.push(
1641
- `- **Dependencies**: ${deps.slice(0, 10).join(', ')}${deps.length > 10 ? ` (+${deps.length - 10} more)` : ''}`
1642
- )
1643
- }
1644
- }
1645
- if (data.hasNextConfig) lines.push('- **Framework**: Next.js detected')
1646
- if (data.hasViteConfig) lines.push('- **Build Tool**: Vite detected')
1647
- if (data.hasTsconfig) lines.push('- **Language**: TypeScript')
1648
- lines.push('')
1649
- }
1650
-
1651
- if (data.cargoToml) {
1652
- lines.push('### Rust\n')
1653
- lines.push('- **Package Manager**: Cargo')
1654
- lines.push('- **Language**: Rust\n')
1655
- }
1656
-
1657
- if (data.goMod) {
1658
- lines.push('### Go\n')
1659
- lines.push('- **Package Manager**: Go modules')
1660
- lines.push('- **Language**: Go\n')
1661
- }
1662
-
1663
- if (data.requirements) {
1664
- lines.push('### Python\n')
1665
- lines.push('- **Package Manager**: pip')
1666
- lines.push('- **Language**: Python\n')
1667
- }
1668
-
1669
- // Project structure
1670
- lines.push('## Structure\n')
1671
- lines.push(`- **Total Files**: ${data.fileCount}`)
1672
- lines.push(
1673
- `- **Directories**: ${data.directories.slice(0, 15).join(', ')}${data.directories.length > 15 ? ` (+${data.directories.length - 15} more)` : ''}`
1674
- )
1675
-
1676
- if (data.hasDockerfile) lines.push('- **Docker**: Detected')
1677
- if (data.hasDockerCompose) lines.push('- **Docker Compose**: Detected')
1678
- if (data.hasReadme) lines.push('- **Documentation**: README.md found')
1679
- lines.push('')
1680
-
1681
- // Git stats
1682
- lines.push('## Git Statistics\n')
1683
- lines.push(`- **Total Commits**: ${data.gitStats.totalCommits}`)
1684
- lines.push(`- **Contributors**: ${data.gitStats.contributors}`)
1685
- lines.push(`- **Age**: ${data.gitStats.age}`)
1686
- lines.push('')
1687
-
1688
- // Recent activity (if available)
1689
- if (data.gitLog) {
1690
- lines.push('## Recent Activity\n')
1691
- const logLines = data.gitLog.split('\n').slice(0, 5)
1692
- logLines.forEach((line) => {
1693
- if (line.trim()) {
1694
- const [hash, , time, msg] = line.split('|')
1695
- lines.push(`- \`${hash}\` ${msg} (${time})`)
1696
- }
1697
- })
1698
- lines.push('')
1699
- }
1700
-
1701
- // Recommendations
1702
- lines.push('## Recommendations\n')
1703
- lines.push('Based on detected stack, consider generating specialized agents using `/p:sync`.\n')
1704
-
1705
- lines.push('---\n')
1706
- lines.push(
1707
- '*This analysis was generated automatically. For updated information, run `/p:analyze` again.*\n'
1708
- )
1709
-
1710
- return lines.join('\n')
1711
- }
1712
-
1713
- /**
1714
- * /p:sync - Sync project state and generate dynamic agents
1715
- * AGENTIC EXECUTION
1716
- */
1717
- async sync(projectPath = process.cwd()) {
1718
- try {
1719
- const initResult = await this.ensureProjectInit(projectPath)
1720
- if (!initResult.success) return initResult
1721
-
1722
- await this.initializeAgent()
1723
-
1724
- console.log('🔄 Syncing project state...\n')
1725
-
1726
- // Build context
1727
- const context = await contextBuilder.build(projectPath)
1728
-
1729
- // Step 1: Run analysis to get current state
1730
- console.log('📊 Running analysis...')
1731
- const analysisResult = await this.analyze({}, projectPath)
1732
-
1733
- if (!analysisResult.success) {
1734
- console.error('❌ Analysis failed')
1735
- return analysisResult
1736
- }
1737
-
1738
- // Step 2: Read analysis/repo-summary.md
1739
- const summaryContent = await toolRegistry.get('Read')(context.paths.analysis)
1740
-
1741
- if (!summaryContent) {
1742
- console.error('❌ No analysis found. Run /p:analyze first.')
1743
- return { success: false, error: 'No analysis found' }
1744
- }
1745
-
1746
- console.log('✅ Analysis loaded\n')
1747
-
1748
- // Step 3: Generate dynamic agents based on stack detected
1749
- // Claude reads the summary and decides what specialists to create
1750
- console.log('🤖 Generating specialized agents...\n')
1751
-
1752
- const projectId = await configManager.getProjectId(projectPath)
1753
- const AgentGenerator = require('./domain/agent-generator')
1754
- const generator = new AgentGenerator(projectId)
1755
-
1756
- const generatedAgents = await this._generateAgentsFromAnalysis(summaryContent, generator, projectPath)
1757
-
1758
- // Step 4: Log to memory
1759
- await this.logToMemory(projectPath, 'agents_generated', {
1760
- timestamp: dateHelper.getTimestamp(),
1761
- agents: generatedAgents,
1762
- count: generatedAgents.length,
1763
- })
1764
-
1765
- // Generate dynamic context for Claude
1766
- const contextSync = require('./context-sync')
1767
- await contextSync.generateLocalContext(projectPath, projectId)
1768
-
1769
- // Update global CLAUDE.md with latest instructions
1770
- const commandInstaller = require('./infrastructure/command-installer')
1771
- const globalConfigResult = await commandInstaller.installGlobalConfig()
1772
- if (globalConfigResult.success) {
1773
- console.log('📝 Updated ~/.claude/CLAUDE.md')
1774
- }
1775
-
1776
- console.log('\n✅ Sync complete!\n')
1777
- console.log(`🤖 Agents Generated: ${generatedAgents.length}`)
1778
- generatedAgents.forEach((agent) => {
1779
- console.log(` • ${agent}`)
1780
- })
1781
- console.log('📝 Context: ~/.prjct-cli/projects/' + projectId + '/CLAUDE.md')
1782
- console.log('\n📋 Based on: analysis/repo-summary.md')
1783
- console.log('💡 See templates/agents/AGENTS.md for reference\n')
1784
- console.log('Next steps:')
1785
- console.log('• /p:context → View project state')
1786
- console.log('• /p:feature → Add a feature')
1787
-
1788
- return {
1789
- success: true,
1790
- agents: generatedAgents,
1791
- }
1792
- } catch (error) {
1793
- console.error('❌ Error:', error.message)
1794
- return { success: false, error: error.message }
1795
- }
1796
- }
1797
-
1798
- /**
1799
- * Generate agents dynamically from analysis summary
1800
- * 100% AGENTIC - Uses analyzer for raw data, Claude decides
1801
- * NO hardcoded categorization or framework lists
1802
- * @private
1803
- */
1804
- async _generateAgentsFromAnalysis(summaryContent, generator, projectPath) {
1805
- const agents = []
1806
-
1807
- // 100% AGENTIC: Get raw project data, let Claude decide
1808
- const analyzer = require('./domain/analyzer')
1809
- analyzer.init(projectPath)
1810
-
1811
- const projectData = {
1812
- packageJson: await analyzer.readPackageJson(),
1813
- extensions: await analyzer.getFileExtensions(),
1814
- directories: await analyzer.listDirectories(),
1815
- configFiles: await analyzer.listConfigFiles(),
1816
- analysisSummary: summaryContent,
1817
- projectPath
1818
- }
1819
-
1820
- // Let the generator decide what agents to create
1821
- // It reads templates/agents/AGENTS.md and decides based on actual project
1822
- const generatedAgents = await generator.generateAgentsFromTech(projectData)
1823
-
1824
- // Return agent names
1825
- generatedAgents.forEach(agent => agents.push(agent.name || agent))
1826
-
1827
- return agents
1828
- }
1829
-
1830
- /**
1831
- * First-time setup - Install commands to editors
1832
- */
1833
- async start() {
1834
- const commandInstaller = require('./infrastructure/command-installer')
1835
-
1836
- console.log('🚀 Setting up prjct for Claude...\n')
1837
-
1838
- // Check if Claude is installed
1839
- const status = await commandInstaller.checkInstallation()
1840
-
1841
- if (!status.claudeDetected) {
1842
- return {
1843
- success: false,
1844
- message:
1845
- '❌ Claude not detected.\n\nPlease install Claude Code or Claude Desktop first:\n' +
1846
- ' - Claude Code: https://claude.com/code\n' +
1847
- ' - Claude Desktop: https://claude.com/desktop',
1848
- }
1849
- }
1850
-
1851
- // Install commands
1852
- console.log('📦 Installing /p:* commands...')
1853
- const result = await commandInstaller.installCommands()
1854
-
1855
- if (!result.success) {
1856
- return {
1857
- success: false,
1858
- message: `❌ Installation failed: ${result.error}`,
1859
- }
1860
- }
1861
-
1862
- console.log(`\n✅ Installed ${result.installed.length} commands to:\n ${result.path}`)
1863
-
1864
- if (result.errors.length > 0) {
1865
- console.log(`\n⚠️ ${result.errors.length} errors:`)
1866
- result.errors.forEach((e) => console.log(` - ${e.file}: ${e.error}`))
1867
- }
1868
-
1869
- console.log('\n🎉 Setup complete!')
1870
- console.log('\nNext steps:')
1871
- console.log(' 1. Open Claude Code or Claude Desktop')
1872
- console.log(' 2. Navigate to your project')
1873
- console.log(' 3. Run: /p:init')
1874
-
1875
- return {
1876
- success: true,
1877
- message: '',
1878
- }
1879
- }
1880
-
1881
- /**
1882
- * Reconfigure editor installations
1883
- */
1884
- async setup(options = {}) {
1885
- const commandInstaller = require('./infrastructure/command-installer')
1886
-
1887
- console.log('🔧 Reconfiguring prjct...\n')
1888
-
1889
- if (options.force) {
1890
- console.log('🗑️ Removing existing installation...')
1891
- await commandInstaller.uninstallCommands()
1892
- }
1893
-
1894
- // Reinstall commands
1895
- console.log('📦 Installing /p:* commands...')
1896
- const result = await commandInstaller.updateCommands()
1897
-
1898
- if (!result.success) {
1899
- return {
1900
- success: false,
1901
- message: `❌ Setup failed: ${result.error}`,
1902
- }
1903
- }
1904
-
1905
- console.log(`\n✅ Installed ${result.installed.length} commands`)
1906
-
1907
- if (result.errors.length > 0) {
1908
- console.log(`\n⚠️ ${result.errors.length} errors:`)
1909
- result.errors.forEach((e) => console.log(` - ${e.file}: ${e.error}`))
1910
- }
1911
-
1912
- // Install global configuration
1913
- console.log('\n📝 Installing global configuration...')
1914
- const configResult = await commandInstaller.installGlobalConfig()
1915
-
1916
- if (configResult.success) {
1917
- if (configResult.action === 'created') {
1918
- console.log('✅ Created ~/.claude/CLAUDE.md')
1919
- } else if (configResult.action === 'updated') {
1920
- console.log('✅ Updated ~/.claude/CLAUDE.md')
1921
- } else if (configResult.action === 'appended') {
1922
- console.log('✅ Added prjct config to ~/.claude/CLAUDE.md')
1923
- }
1924
- } else {
1925
- console.log(`⚠️ ${configResult.error}`)
1926
- }
1927
-
1928
- // Install status line for Claude Code
1929
- console.log('\n⚡ Installing status line...')
1930
- const statusLineResult = await this.installStatusLine()
1931
- if (statusLineResult.success) {
1932
- console.log('✅ Status line configured')
1933
- } else {
1934
- console.log(`⚠️ ${statusLineResult.error}`)
1935
- }
1936
-
1937
- console.log('\n🎉 Setup complete!\n')
1938
-
1939
- // Show beautiful ASCII art
1940
- this.showAsciiArt()
1941
-
1942
- return {
1943
- success: true,
1944
- message: '',
1945
- }
1946
- }
1947
-
1948
- /**
1949
- * Install status line script and configure settings.json
1950
- */
1951
- async installStatusLine() {
1952
- const fs = require('fs')
1953
- const path = require('path')
1954
- const os = require('os')
1955
-
1956
- try {
1957
- const claudeDir = path.join(os.homedir(), '.claude')
1958
- const settingsPath = path.join(claudeDir, 'settings.json')
1959
- const statusLinePath = path.join(claudeDir, 'prjct-statusline.sh')
1960
-
1961
- // Copy status line script
1962
- const scriptContent = `#!/bin/bash
1963
- # prjct Status Line for Claude Code
1964
- # Shows ⚡ prjct with animated spinner when command is running
1965
-
1966
- # Read JSON context from stdin (provided by Claude Code)
1967
- read -r json
1968
-
1969
- # Spinner frames
1970
- frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
1971
-
1972
- # Calculate frame based on time (changes every 80ms)
1973
- frame=$(($(date +%s%N 2>/dev/null || echo 0) / 80000000 % 10))
1974
-
1975
- # Check if prjct command is running
1976
- running_file="$HOME/.prjct-cli/.running"
1977
-
1978
- if [ -f "$running_file" ]; then
1979
- task=$(cat "$running_file" 2>/dev/null || echo "working")
1980
- echo "⚡ prjct \${frames[$frame]} $task"
1981
- else
1982
- echo "⚡ prjct"
1983
- fi
1984
- `
1985
- fs.writeFileSync(statusLinePath, scriptContent, { mode: 0o755 })
1986
-
1987
- // Update settings.json
1988
- let settings = {}
1989
- if (fs.existsSync(settingsPath)) {
1990
- try {
1991
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
1992
- } catch {
1993
- // Invalid JSON, start fresh
1994
- }
1995
- }
1996
-
1997
- // Set status line configuration
1998
- settings.statusLine = {
1999
- type: 'command',
2000
- command: statusLinePath
2001
- }
2002
-
2003
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2))
2004
-
2005
- return { success: true }
2006
- } catch (error) {
2007
- return { success: false, error: error.message }
2008
- }
2009
- }
2010
-
2011
- /**
2012
- * Show beautiful ASCII art with quick start
2013
- */
2014
- showAsciiArt() {
2015
- const chalk = require('chalk')
2016
-
2017
- console.log(chalk.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
2018
- console.log('')
2019
- console.log(chalk.bold.cyan(' ██████╗ ██████╗ ██╗ ██████╗████████╗'))
2020
- console.log(chalk.bold.cyan(' ██╔══██╗██╔══██╗ ██║██╔════╝╚══██╔══╝'))
2021
- console.log(chalk.bold.cyan(' ██████╔╝██████╔╝ ██║██║ ██║'))
2022
- console.log(chalk.bold.cyan(' ██╔═══╝ ██╔══██╗██ ██║██║ ██║'))
2023
- console.log(chalk.bold.cyan(' ██║ ██║ ██║╚█████╔╝╚██████╗ ██║'))
2024
- console.log(chalk.bold.cyan(' ╚═╝ ╚═╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝'))
2025
- console.log('')
2026
- console.log(` ${chalk.bold.cyan('prjct')}${chalk.magenta('/')}${chalk.green('cli')} ${chalk.dim.white('v' + VERSION + ' installed')}`)
2027
- console.log('')
2028
- console.log(` ${chalk.yellow('⚡')} Ship faster with zero friction`)
2029
- console.log(` ${chalk.green('📝')} From idea to technical tasks in minutes`)
2030
- console.log(` ${chalk.cyan('🤖')} Perfect context for AI agents`)
2031
- console.log('')
2032
- console.log(chalk.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
2033
- console.log('')
2034
- console.log(chalk.bold.cyan('🚀 Quick Start'))
2035
- console.log(chalk.dim('─────────────────────────────────────────────────'))
2036
- console.log('')
2037
- console.log(` ${chalk.bold('1.')} Initialize your project:`)
2038
- console.log(` ${chalk.green('cd your-project && prjct init')}`)
2039
- console.log('')
2040
- console.log(` ${chalk.bold('2.')} Set your current focus:`)
2041
- console.log(` ${chalk.green('prjct now "build auth"')}`)
2042
- console.log('')
2043
- console.log(` ${chalk.bold('3.')} Ship & celebrate:`)
2044
- console.log(` ${chalk.green('prjct ship "user login"')}`)
2045
- console.log('')
2046
- console.log(chalk.dim('─────────────────────────────────────────────────'))
2047
- console.log('')
2048
- console.log(` ${chalk.dim('Documentation:')} ${chalk.cyan('https://prjct.app')}`)
2049
- console.log(` ${chalk.dim('Report issues:')} ${chalk.cyan('https://github.com/jlopezlira/prjct-cli/issues')}`)
2050
- console.log('')
2051
- console.log(chalk.bold.magenta('Happy shipping! 🚀'))
2052
- console.log('')
2053
- }
2054
-
2055
- /**
2056
- * Migrate all legacy projects
2057
- */
2058
- async migrateAll(options = {}) {
2059
- const fs = require('fs').promises
2060
- const path = require('path')
2061
-
2062
- console.log('🔄 Scanning for legacy prjct projects...\n')
2063
-
2064
- const homeDir = require('os').homedir()
2065
- const globalRoot = path.join(homeDir, '.prjct-cli', 'projects')
2066
-
2067
- // Get all project IDs
2068
- let projectIds = []
2069
- try {
2070
- const dirs = await fs.readdir(globalRoot)
2071
- projectIds = dirs.filter((d) => !d.startsWith('.'))
2072
- } catch (error) {
2073
- return {
2074
- success: false,
2075
- message: '❌ No prjct projects found',
2076
- }
2077
- }
2078
-
2079
- console.log(`📁 Found ${projectIds.length} projects in global storage\n`)
2080
-
2081
- const migrated = []
2082
- const failed = []
2083
- const skipped = []
2084
-
2085
- for (const projectId of projectIds) {
2086
- // Read global config to get project path
2087
- const globalConfig = await configManager.readGlobalConfig(projectId)
2088
- if (!globalConfig || !globalConfig.projectPath) {
2089
- skipped.push({ projectId, reason: 'No project path in config' })
2090
- continue
2091
- }
2092
-
2093
- const projectPath = globalConfig.projectPath
2094
-
2095
- // Check if needs migration
2096
- if (!(await migrator.needsMigration(projectPath))) {
2097
- skipped.push({ projectId, reason: 'Already migrated' })
2098
- continue
2099
- }
2100
-
2101
- console.log(`🔄 Migrating: ${projectPath}`)
2102
-
2103
- try {
2104
- const result = await migrator.migrate(projectPath, options)
2105
-
2106
- if (result.success) {
2107
- migrated.push({ projectId, path: projectPath })
2108
- console.log(` ✅ ${result.message}`)
2109
- } else {
2110
- failed.push({ projectId, path: projectPath, error: result.message })
2111
- console.log(` ❌ ${result.message}`)
2112
- }
2113
- } catch (error) {
2114
- failed.push({ projectId, path: projectPath, error: error.message })
2115
- console.log(` ❌ ${error.message}`)
2116
- }
2117
-
2118
- console.log('')
2119
- }
2120
-
2121
- // Summary
2122
- console.log('\n📊 Migration Summary:')
2123
- console.log(` ✅ Migrated: ${migrated.length}`)
2124
- console.log(` ⏭️ Skipped: ${skipped.length}`)
2125
- console.log(` ❌ Failed: ${failed.length}`)
2126
-
2127
- if (failed.length > 0) {
2128
- console.log('\n❌ Failed migrations:')
2129
- failed.forEach((f) => console.log(` - ${f.path}: ${f.error}`))
2130
- }
2131
-
2132
- return {
2133
- success: failed.length === 0,
2134
- message: '',
2135
- }
2136
- }
2137
-
2138
- /**
2139
- * Execute architect plan and generate code
2140
- */
2141
- async architect(action = 'execute', projectPath = process.cwd()) {
2142
- if (action !== 'execute') {
2143
- return {
2144
- success: false,
2145
- message: '❌ Invalid action. Use: /p:architect execute',
2146
- }
2147
- }
2148
-
2149
- try {
2150
- const initResult = await this.ensureProjectInit(projectPath)
2151
- if (!initResult.success) return initResult
2152
-
2153
- console.log('🏗️ Architect Mode - Code Generation\n')
2154
-
2155
- const globalPath = await this.getGlobalProjectPath(projectPath)
2156
-
2157
- // Check if there's a completed plan
2158
- const planPath = path.join(globalPath, 'planning', 'architect-session.md')
2159
-
2160
- let planContent
2161
- try {
2162
- planContent = await fileHelper.readFile(planPath)
2163
- } catch (error) {
2164
- return {
2165
- success: false,
2166
- message:
2167
- '❌ No architect plan found.\n\n' +
2168
- 'Create a plan first:\n' +
2169
- ' 1. Run /p:init in an empty directory\n' +
2170
- ' 2. Answer the discovery questions\n' +
2171
- ' 3. Plan will be auto-generated\n' +
2172
- ' 4. Then run /p:architect execute',
2173
- }
2174
- }
2175
-
2176
- if (!planContent || planContent.trim() === '') {
2177
- return {
2178
- success: false,
2179
- message: '❌ Architect plan is empty',
2180
- }
2181
- }
2182
-
2183
- console.log('📋 Reading architect plan...\n')
2184
-
2185
- // Extract key information from plan
2186
- const ideaMatch = planContent.match(/## Project Idea\n(.+)/s)
2187
- const stackMatch = planContent.match(/\*\*Stack:\*\*\n([\s\S]+?)\n\n/)
2188
- const stepsMatch = planContent.match(/\*\*Implementation Steps:\*\*\n([\s\S]+?)\n\n/)
2189
-
2190
- const idea = ideaMatch ? ideaMatch[1].split('\n')[0].trim() : 'Unknown project'
2191
- const stack = stackMatch ? stackMatch[1] : 'Not specified'
2192
- const steps = stepsMatch ? stepsMatch[1] : 'Not specified'
2193
-
2194
- console.log(`📝 Project: ${idea}`)
2195
- console.log(`\n🔧 Stack:\n${stack}`)
2196
- console.log(`\n📋 Implementation Steps:\n${steps}`)
2197
-
2198
- console.log('\n' + '='.repeat(60))
2199
- console.log('🤖 READY TO GENERATE CODE')
2200
- console.log('='.repeat(60))
2201
-
2202
- console.log(
2203
- '\nThe architect plan is ready. Claude will now:\n' +
2204
- ' 1. Read the architectural plan\n' +
2205
- ' 2. Use Context7 for official documentation\n' +
2206
- ' 3. Generate project structure\n' +
2207
- ' 4. Create starter files with boilerplate\n'
2208
- )
2209
-
2210
- console.log('\n💡 This command shows the plan.')
2211
- console.log(' For code generation, Claude Code will read this plan')
2212
- console.log(' and generate the structure automatically.\n')
2213
-
2214
- await this.logToMemory(projectPath, 'architect_executed', {
2215
- timestamp: dateHelper.getTimestamp(),
2216
- idea,
2217
- })
2218
-
2219
- return {
2220
- success: true,
2221
- plan: planContent,
2222
- idea,
2223
- }
2224
- } catch (error) {
2225
- console.error('❌ Error:', error.message)
2226
- return { success: false, error: error.message }
2227
- }
2228
- }
2229
- }
2230
-
2231
- // Export both class and singleton instance
2232
- // Class for CLI (new PrjctCommands())
2233
- // Instance for direct use (require('./commands').sync())
2234
- const instance = new PrjctCommands()
2235
-
2236
- module.exports = instance
2237
- module.exports.PrjctCommands = PrjctCommands