prjct-cli 0.11.4 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +246 -54
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/date-helper.test.ts +405 -0
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +99 -89
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +35 -18
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +62 -23
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +203 -403
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +134 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/BentoGrid/BentoGrid.tsx +18 -0
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/EmptyState/EmptyState.tsx +58 -0
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/IdeasCard/IdeasCard.tsx +48 -0
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/ProgressRing/ProgressRing.tsx +51 -0
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +77 -0
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/ShipsCard/ShipsCard.tsx +52 -0
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/SparklineChart/SparklineChart.tsx +38 -0
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/StreakCard/StreakCard.tsx +53 -0
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/components/ui/tooltip.tsx +2 -2
  333. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  334. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  335. package/packages/web/hooks/useProjectStats.ts +55 -0
  336. package/packages/web/hooks/useProjects.ts +6 -6
  337. package/packages/web/lib/actions/projects.ts +15 -0
  338. package/packages/web/lib/json-loader.ts +630 -0
  339. package/packages/web/lib/services/index.ts +9 -0
  340. package/packages/web/lib/services/migration.server.ts +598 -0
  341. package/packages/web/lib/services/projects.server.ts +52 -0
  342. package/packages/web/lib/services/stats.server.ts +264 -0
  343. package/packages/web/lib/unified-loader.ts +396 -0
  344. package/packages/web/next-env.d.ts +1 -1
  345. package/packages/web/package.json +10 -6
  346. package/packages/web/server.ts +36 -6
  347. package/templates/commands/done.md +76 -32
  348. package/templates/commands/feature.md +121 -47
  349. package/templates/commands/idea.md +81 -8
  350. package/templates/commands/now.md +41 -17
  351. package/templates/commands/ship.md +64 -25
  352. package/templates/commands/sync.md +28 -3
  353. package/core/agentic/agent-router.js +0 -128
  354. package/core/agentic/chain-of-thought.js +0 -578
  355. package/core/agentic/command-executor.js +0 -421
  356. package/core/agentic/context-filter.js +0 -354
  357. package/core/agentic/ground-truth.js +0 -591
  358. package/core/agentic/loop-detector.js +0 -406
  359. package/core/agentic/memory-system.js +0 -850
  360. package/core/agentic/parallel-tools.js +0 -366
  361. package/core/agentic/plan-mode.js +0 -572
  362. package/core/agentic/prompt-builder.js +0 -338
  363. package/core/agentic/response-templates.js +0 -290
  364. package/core/agentic/semantic-compression.js +0 -517
  365. package/core/agentic/think-blocks.js +0 -657
  366. package/core/agentic/tool-registry.js +0 -184
  367. package/core/agentic/validation-rules.js +0 -380
  368. package/core/command-registry.js +0 -698
  369. package/core/commands.js +0 -2237
  370. package/core/domain/task-stack.js +0 -497
  371. package/core/infrastructure/legacy-installer-detector.js +0 -546
  372. package/core/infrastructure/migrator.js +0 -799
  373. package/core/infrastructure/session-manager.js +0 -390
  374. package/core/utils/file-helper.js +0 -329
  375. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  376. package/packages/web/app/api/stats/route.ts +0 -38
  377. package/packages/web/components/AppSidebar.tsx +0 -113
  378. package/packages/web/hooks/useStats.ts +0 -28
  379. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  380. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  381. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  382. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  383. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  384. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  385. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -1,850 +0,0 @@
1
- /**
2
- * Layered Memory System
3
- * Three-tier memory for learning user patterns and preferences
4
- *
5
- * OPTIMIZATION (P1.1): Pattern-Based Decision Making
6
- * - Tier 1: Session memory (current command context)
7
- * - Tier 2: Patterns (recurring decisions/preferences)
8
- * - Tier 3: History (append-only JSONL for audit)
9
- *
10
- * P3.3: Enhanced with semantic tags and CRUD operations
11
- * - Semantic tags for categorization
12
- * - Auto-memory from user decisions
13
- * - Relevance-based retrieval
14
- * - CRUD operations (create/update/delete)
15
- *
16
- * Source: Windsurf create_memory, Augment remember patterns
17
- */
18
-
19
- const fs = require('fs').promises
20
- const path = require('path')
21
- const pathManager = require('../infrastructure/path-manager')
22
-
23
- /**
24
- * P3.3: Semantic tags for memory categorization
25
- */
26
- const MEMORY_TAGS = {
27
- // Code preferences
28
- CODE_STYLE: 'code_style',
29
- NAMING_CONVENTION: 'naming_convention',
30
- FILE_STRUCTURE: 'file_structure',
31
-
32
- // Workflow preferences
33
- COMMIT_STYLE: 'commit_style',
34
- BRANCH_NAMING: 'branch_naming',
35
- TEST_BEHAVIOR: 'test_behavior',
36
- SHIP_WORKFLOW: 'ship_workflow',
37
-
38
- // Project context
39
- TECH_STACK: 'tech_stack',
40
- ARCHITECTURE: 'architecture',
41
- DEPENDENCIES: 'dependencies',
42
-
43
- // User preferences
44
- OUTPUT_VERBOSITY: 'output_verbosity',
45
- CONFIRMATION_LEVEL: 'confirmation_level',
46
- AGENT_PREFERENCE: 'agent_preference'
47
- }
48
-
49
- class MemorySystem {
50
- constructor() {
51
- // Session memory (in-process, cleared on restart)
52
- this._sessionMemory = new Map()
53
-
54
- // Pattern cache (loaded from disk)
55
- this._patterns = null
56
- this._patternsLoaded = false
57
-
58
- // P3.3: Memories database (semantic tagged)
59
- this._memories = null
60
- this._memoriesLoaded = false
61
- }
62
-
63
- // ═══════════════════════════════════════════════════════════
64
- // P3.3: SEMANTIC MEMORIES (tagged, searchable, CRUD)
65
- // ═══════════════════════════════════════════════════════════
66
-
67
- /**
68
- * Get path to memories database
69
- * @param {string} projectId
70
- * @returns {string}
71
- */
72
- _getMemoriesPath(projectId) {
73
- return path.join(
74
- pathManager.getGlobalProjectPath(projectId),
75
- 'memory',
76
- 'memories.json'
77
- )
78
- }
79
-
80
- /**
81
- * Load memories database
82
- * @param {string} projectId
83
- * @returns {Promise<Object>}
84
- */
85
- async loadMemories(projectId) {
86
- if (this._memoriesLoaded && this._memories) {
87
- return this._memories
88
- }
89
-
90
- try {
91
- const memoriesPath = this._getMemoriesPath(projectId)
92
- const content = await fs.readFile(memoriesPath, 'utf-8')
93
- this._memories = JSON.parse(content)
94
- this._memoriesLoaded = true
95
- return this._memories
96
- } catch {
97
- this._memories = {
98
- version: 1,
99
- memories: [], // Array of memory entries
100
- index: {} // Tag -> memory IDs index
101
- }
102
- this._memoriesLoaded = true
103
- return this._memories
104
- }
105
- }
106
-
107
- /**
108
- * Save memories database
109
- * @param {string} projectId
110
- */
111
- async saveMemories(projectId) {
112
- if (!this._memories) return
113
-
114
- const memoriesPath = this._getMemoriesPath(projectId)
115
- await fs.mkdir(path.dirname(memoriesPath), { recursive: true })
116
- await fs.writeFile(
117
- memoriesPath,
118
- JSON.stringify(this._memories, null, 2),
119
- 'utf-8'
120
- )
121
- }
122
-
123
- /**
124
- * Create a new memory (Windsurf pattern)
125
- *
126
- * @param {string} projectId
127
- * @param {Object} memory
128
- * @param {string} memory.title - Short title
129
- * @param {string} memory.content - Memory content
130
- * @param {string[]} memory.tags - Semantic tags
131
- * @param {boolean} memory.userTriggered - If user explicitly asked
132
- * @returns {Promise<string>} Memory ID
133
- */
134
- async createMemory(projectId, { title, content, tags = [], userTriggered = false }) {
135
- const db = await this.loadMemories(projectId)
136
-
137
- const memory = {
138
- id: `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
139
- title,
140
- content,
141
- tags,
142
- userTriggered,
143
- createdAt: new Date().toISOString(),
144
- updatedAt: new Date().toISOString()
145
- }
146
-
147
- db.memories.push(memory)
148
-
149
- // Update tag index
150
- for (const tag of tags) {
151
- if (!db.index[tag]) db.index[tag] = []
152
- db.index[tag].push(memory.id)
153
- }
154
-
155
- await this.saveMemories(projectId)
156
-
157
- // Log to history
158
- await this.appendHistory(projectId, {
159
- type: 'memory_create',
160
- memoryId: memory.id,
161
- title,
162
- tags,
163
- userTriggered
164
- })
165
-
166
- return memory.id
167
- }
168
-
169
- /**
170
- * Update an existing memory
171
- *
172
- * @param {string} projectId
173
- * @param {string} memoryId
174
- * @param {Object} updates
175
- * @returns {Promise<boolean>}
176
- */
177
- async updateMemory(projectId, memoryId, updates) {
178
- const db = await this.loadMemories(projectId)
179
-
180
- const index = db.memories.findIndex(m => m.id === memoryId)
181
- if (index === -1) return false
182
-
183
- const memory = db.memories[index]
184
- const oldTags = memory.tags || []
185
-
186
- // Apply updates
187
- if (updates.title) memory.title = updates.title
188
- if (updates.content) memory.content = updates.content
189
- if (updates.tags) {
190
- // Update tag index
191
- for (const tag of oldTags) {
192
- if (db.index[tag]) {
193
- db.index[tag] = db.index[tag].filter(id => id !== memoryId)
194
- }
195
- }
196
- for (const tag of updates.tags) {
197
- if (!db.index[tag]) db.index[tag] = []
198
- db.index[tag].push(memoryId)
199
- }
200
- memory.tags = updates.tags
201
- }
202
-
203
- memory.updatedAt = new Date().toISOString()
204
-
205
- await this.saveMemories(projectId)
206
-
207
- await this.appendHistory(projectId, {
208
- type: 'memory_update',
209
- memoryId,
210
- updates: Object.keys(updates)
211
- })
212
-
213
- return true
214
- }
215
-
216
- /**
217
- * Delete a memory
218
- *
219
- * @param {string} projectId
220
- * @param {string} memoryId
221
- * @returns {Promise<boolean>}
222
- */
223
- async deleteMemory(projectId, memoryId) {
224
- const db = await this.loadMemories(projectId)
225
-
226
- const index = db.memories.findIndex(m => m.id === memoryId)
227
- if (index === -1) return false
228
-
229
- const memory = db.memories[index]
230
-
231
- // Remove from tag index
232
- for (const tag of memory.tags || []) {
233
- if (db.index[tag]) {
234
- db.index[tag] = db.index[tag].filter(id => id !== memoryId)
235
- }
236
- }
237
-
238
- // Remove memory
239
- db.memories.splice(index, 1)
240
-
241
- await this.saveMemories(projectId)
242
-
243
- await this.appendHistory(projectId, {
244
- type: 'memory_delete',
245
- memoryId,
246
- title: memory.title
247
- })
248
-
249
- return true
250
- }
251
-
252
- /**
253
- * Find memories by tags
254
- *
255
- * @param {string} projectId
256
- * @param {string[]} tags - Tags to search for
257
- * @param {boolean} matchAll - If true, memory must have ALL tags
258
- * @returns {Promise<Object[]>}
259
- */
260
- async findByTags(projectId, tags, matchAll = false) {
261
- const db = await this.loadMemories(projectId)
262
-
263
- if (matchAll) {
264
- // Memory must have ALL tags
265
- return db.memories.filter(m =>
266
- tags.every(tag => (m.tags || []).includes(tag))
267
- )
268
- } else {
269
- // Memory must have ANY tag
270
- const matchingIds = new Set()
271
- for (const tag of tags) {
272
- const ids = db.index[tag] || []
273
- ids.forEach(id => matchingIds.add(id))
274
- }
275
- return db.memories.filter(m => matchingIds.has(m.id))
276
- }
277
- }
278
-
279
- /**
280
- * Search memories by content (simple text match)
281
- *
282
- * @param {string} projectId
283
- * @param {string} query
284
- * @returns {Promise<Object[]>}
285
- */
286
- async searchMemories(projectId, query) {
287
- const db = await this.loadMemories(projectId)
288
- const queryLower = query.toLowerCase()
289
-
290
- return db.memories.filter(m =>
291
- m.title.toLowerCase().includes(queryLower) ||
292
- m.content.toLowerCase().includes(queryLower)
293
- )
294
- }
295
-
296
- /**
297
- * Get relevant memories for current context
298
- * Scores memories by relevance to context
299
- *
300
- * @param {string} projectId
301
- * @param {Object} context - Current execution context
302
- * @param {number} limit - Max memories to return
303
- * @returns {Promise<Object[]>}
304
- */
305
- async getRelevantMemories(projectId, context, limit = 5) {
306
- const db = await this.loadMemories(projectId)
307
-
308
- // Score each memory by relevance
309
- const scored = db.memories.map(memory => {
310
- let score = 0
311
-
312
- // Tag relevance
313
- const contextTags = this._extractContextTags(context)
314
- for (const tag of memory.tags || []) {
315
- if (contextTags.includes(tag)) score += 10
316
- }
317
-
318
- // Recency boost (more recent = higher score)
319
- const age = Date.now() - new Date(memory.updatedAt).getTime()
320
- const daysSinceUpdate = age / (1000 * 60 * 60 * 24)
321
- score += Math.max(0, 5 - daysSinceUpdate) // Up to 5 points for recent
322
-
323
- // User triggered memories are more important
324
- if (memory.userTriggered) score += 5
325
-
326
- // Content keyword match
327
- const keywords = this._extractKeywords(context)
328
- for (const keyword of keywords) {
329
- if (memory.content.toLowerCase().includes(keyword)) score += 2
330
- if (memory.title.toLowerCase().includes(keyword)) score += 3
331
- }
332
-
333
- return { ...memory, _score: score }
334
- })
335
-
336
- // Sort by score and return top N
337
- return scored
338
- .filter(m => m._score > 0)
339
- .sort((a, b) => b._score - a._score)
340
- .slice(0, limit)
341
- .map(({ _score, ...memory }) => memory)
342
- }
343
-
344
- /**
345
- * Extract relevant tags from context
346
- * @private
347
- */
348
- _extractContextTags(context) {
349
- const tags = []
350
-
351
- // Command-based tags
352
- const commandTags = {
353
- ship: [MEMORY_TAGS.COMMIT_STYLE, MEMORY_TAGS.SHIP_WORKFLOW, MEMORY_TAGS.TEST_BEHAVIOR],
354
- feature: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE],
355
- done: [MEMORY_TAGS.SHIP_WORKFLOW],
356
- analyze: [MEMORY_TAGS.TECH_STACK, MEMORY_TAGS.ARCHITECTURE],
357
- spec: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE]
358
- }
359
-
360
- if (context.commandName && commandTags[context.commandName]) {
361
- tags.push(...commandTags[context.commandName])
362
- }
363
-
364
- return tags
365
- }
366
-
367
- /**
368
- * Extract keywords from context for matching
369
- * @private
370
- */
371
- _extractKeywords(context) {
372
- const keywords = []
373
-
374
- // From params
375
- if (context.params?.description) {
376
- keywords.push(...context.params.description.toLowerCase().split(/\s+/))
377
- }
378
- if (context.params?.feature) {
379
- keywords.push(...context.params.feature.toLowerCase().split(/\s+/))
380
- }
381
-
382
- // Filter common words
383
- const stopWords = ['the', 'a', 'an', 'is', 'are', 'to', 'for', 'and', 'or', 'in']
384
- return keywords.filter(k => k.length > 2 && !stopWords.includes(k))
385
- }
386
-
387
- /**
388
- * Auto-create memory from user decision
389
- * Called when user explicitly chooses something
390
- *
391
- * @param {string} projectId
392
- * @param {string} decisionType - Type of decision
393
- * @param {string} value - Chosen value
394
- * @param {string} context - Context of decision
395
- */
396
- async autoRemember(projectId, decisionType, value, context = '') {
397
- // Map decision types to tags
398
- const tagMap = {
399
- commit_footer: [MEMORY_TAGS.COMMIT_STYLE],
400
- branch_naming: [MEMORY_TAGS.BRANCH_NAMING],
401
- test_before_ship: [MEMORY_TAGS.TEST_BEHAVIOR, MEMORY_TAGS.SHIP_WORKFLOW],
402
- preferred_agent: [MEMORY_TAGS.AGENT_PREFERENCE],
403
- code_style: [MEMORY_TAGS.CODE_STYLE],
404
- verbosity: [MEMORY_TAGS.OUTPUT_VERBOSITY]
405
- }
406
-
407
- const tags = tagMap[decisionType] || []
408
-
409
- // Check if similar memory exists
410
- const existing = await this.searchMemories(projectId, decisionType)
411
- if (existing.length > 0) {
412
- // Update existing
413
- await this.updateMemory(projectId, existing[0].id, {
414
- content: `${decisionType}: ${value}`,
415
- tags
416
- })
417
- } else {
418
- // Create new
419
- await this.createMemory(projectId, {
420
- title: `Preference: ${decisionType}`,
421
- content: `${decisionType}: ${value}${context ? `\nContext: ${context}` : ''}`,
422
- tags,
423
- userTriggered: true
424
- })
425
- }
426
- }
427
-
428
- /**
429
- * Get all memories (for debugging/display)
430
- * @param {string} projectId
431
- * @returns {Promise<Object[]>}
432
- */
433
- async getAllMemories(projectId) {
434
- const db = await this.loadMemories(projectId)
435
- return db.memories
436
- }
437
-
438
- /**
439
- * Get memory stats
440
- * @param {string} projectId
441
- * @returns {Promise<Object>}
442
- */
443
- async getMemoryStats(projectId) {
444
- const db = await this.loadMemories(projectId)
445
-
446
- const tagCounts = {}
447
- for (const [tag, ids] of Object.entries(db.index)) {
448
- tagCounts[tag] = ids.length
449
- }
450
-
451
- return {
452
- totalMemories: db.memories.length,
453
- userTriggered: db.memories.filter(m => m.userTriggered).length,
454
- tagCounts,
455
- oldestMemory: db.memories[0]?.createdAt,
456
- newestMemory: db.memories[db.memories.length - 1]?.createdAt
457
- }
458
- }
459
-
460
- // ═══════════════════════════════════════════════════════════
461
- // TIER 1: Session Memory (ephemeral, single command context)
462
- // ═══════════════════════════════════════════════════════════
463
-
464
- /**
465
- * Store value in session memory
466
- * @param {string} key - Memory key
467
- * @param {any} value - Value to store
468
- */
469
- setSession(key, value) {
470
- this._sessionMemory.set(key, {
471
- value,
472
- timestamp: Date.now()
473
- })
474
- }
475
-
476
- /**
477
- * Get value from session memory
478
- * @param {string} key - Memory key
479
- * @returns {any} Stored value or undefined
480
- */
481
- getSession(key) {
482
- const entry = this._sessionMemory.get(key)
483
- return entry?.value
484
- }
485
-
486
- /**
487
- * Clear session memory
488
- */
489
- clearSession() {
490
- this._sessionMemory.clear()
491
- }
492
-
493
- // ═══════════════════════════════════════════════════════════
494
- // TIER 2: Patterns (persistent, learned preferences)
495
- // ═══════════════════════════════════════════════════════════
496
-
497
- /**
498
- * Get path to patterns file
499
- * @param {string} projectId - Project ID
500
- * @returns {string} Path to patterns.json
501
- */
502
- _getPatternsPath(projectId) {
503
- return path.join(
504
- pathManager.getGlobalProjectPath(projectId),
505
- 'memory',
506
- 'patterns.json'
507
- )
508
- }
509
-
510
- /**
511
- * Load patterns from disk
512
- * @param {string} projectId - Project ID
513
- * @returns {Promise<Object>} Patterns object
514
- */
515
- async loadPatterns(projectId) {
516
- if (this._patternsLoaded && this._patterns) {
517
- return this._patterns
518
- }
519
-
520
- try {
521
- const patternsPath = this._getPatternsPath(projectId)
522
- const content = await fs.readFile(patternsPath, 'utf-8')
523
- this._patterns = JSON.parse(content)
524
- this._patternsLoaded = true
525
- return this._patterns
526
- } catch {
527
- // Initialize empty patterns
528
- this._patterns = {
529
- version: 1,
530
- decisions: {}, // Key decisions (e.g., commit_footer, branch_naming)
531
- preferences: {}, // User preferences (e.g., output_verbosity)
532
- workflows: {}, // Workflow patterns (e.g., quick_ship for small changes)
533
- counters: {} // Usage counters for learning
534
- }
535
- this._patternsLoaded = true
536
- return this._patterns
537
- }
538
- }
539
-
540
- /**
541
- * Save patterns to disk
542
- * @param {string} projectId - Project ID
543
- */
544
- async savePatterns(projectId) {
545
- if (!this._patterns) return
546
-
547
- const patternsPath = this._getPatternsPath(projectId)
548
-
549
- // Ensure directory exists
550
- await fs.mkdir(path.dirname(patternsPath), { recursive: true })
551
-
552
- await fs.writeFile(
553
- patternsPath,
554
- JSON.stringify(this._patterns, null, 2),
555
- 'utf-8'
556
- )
557
- }
558
-
559
- /**
560
- * Record a decision pattern
561
- * After 3 consistent uses, pattern becomes "learned"
562
- *
563
- * @param {string} projectId - Project ID
564
- * @param {string} key - Decision key (e.g., "commit_footer")
565
- * @param {string} value - Decision value
566
- * @param {string} context - Context where decision was made
567
- */
568
- async recordDecision(projectId, key, value, context = '') {
569
- const patterns = await this.loadPatterns(projectId)
570
-
571
- // Initialize or update decision
572
- if (!patterns.decisions[key]) {
573
- patterns.decisions[key] = {
574
- value,
575
- count: 1,
576
- firstSeen: new Date().toISOString(),
577
- lastSeen: new Date().toISOString(),
578
- confidence: 'low',
579
- contexts: [context].filter(Boolean)
580
- }
581
- } else {
582
- const decision = patterns.decisions[key]
583
-
584
- if (decision.value === value) {
585
- // Same value - increase confidence
586
- decision.count++
587
- decision.lastSeen = new Date().toISOString()
588
- if (context && !decision.contexts.includes(context)) {
589
- decision.contexts.push(context)
590
- }
591
-
592
- // Update confidence based on count
593
- if (decision.count >= 5) {
594
- decision.confidence = 'high'
595
- } else if (decision.count >= 3) {
596
- decision.confidence = 'medium'
597
- }
598
- } else {
599
- // Different value - reset if new value is used more
600
- decision.value = value
601
- decision.count = 1
602
- decision.lastSeen = new Date().toISOString()
603
- decision.confidence = 'low'
604
- }
605
- }
606
-
607
- await this.savePatterns(projectId)
608
- }
609
-
610
- /**
611
- * Get a learned decision
612
- * Returns null if not learned (confidence < medium)
613
- *
614
- * @param {string} projectId - Project ID
615
- * @param {string} key - Decision key
616
- * @returns {Promise<{value: string, confidence: string}|null>}
617
- */
618
- async getDecision(projectId, key) {
619
- const patterns = await this.loadPatterns(projectId)
620
- const decision = patterns.decisions[key]
621
-
622
- if (!decision) return null
623
-
624
- // Only return if confidence is at least medium
625
- if (decision.confidence === 'low') return null
626
-
627
- return {
628
- value: decision.value,
629
- confidence: decision.confidence
630
- }
631
- }
632
-
633
- /**
634
- * Check if a pattern exists (for quick checks)
635
- * @param {string} projectId - Project ID
636
- * @param {string} key - Pattern key
637
- * @returns {Promise<boolean>}
638
- */
639
- async hasPattern(projectId, key) {
640
- const decision = await this.getDecision(projectId, key)
641
- return decision !== null
642
- }
643
-
644
- /**
645
- * Record a workflow pattern
646
- * E.g., "user ships docs changes without running tests"
647
- *
648
- * @param {string} projectId - Project ID
649
- * @param {string} workflowName - Workflow identifier
650
- * @param {Object} pattern - Workflow pattern details
651
- */
652
- async recordWorkflow(projectId, workflowName, pattern) {
653
- const patterns = await this.loadPatterns(projectId)
654
-
655
- if (!patterns.workflows[workflowName]) {
656
- patterns.workflows[workflowName] = {
657
- ...pattern,
658
- count: 1,
659
- firstSeen: new Date().toISOString(),
660
- lastSeen: new Date().toISOString()
661
- }
662
- } else {
663
- patterns.workflows[workflowName].count++
664
- patterns.workflows[workflowName].lastSeen = new Date().toISOString()
665
- }
666
-
667
- await this.savePatterns(projectId)
668
- }
669
-
670
- /**
671
- * Get workflow pattern if learned
672
- * @param {string} projectId - Project ID
673
- * @param {string} workflowName - Workflow identifier
674
- * @returns {Promise<Object|null>}
675
- */
676
- async getWorkflow(projectId, workflowName) {
677
- const patterns = await this.loadPatterns(projectId)
678
- const workflow = patterns.workflows[workflowName]
679
-
680
- if (!workflow || workflow.count < 3) return null
681
-
682
- return workflow
683
- }
684
-
685
- /**
686
- * Set user preference
687
- * @param {string} projectId - Project ID
688
- * @param {string} key - Preference key
689
- * @param {any} value - Preference value
690
- */
691
- async setPreference(projectId, key, value) {
692
- const patterns = await this.loadPatterns(projectId)
693
- patterns.preferences[key] = {
694
- value,
695
- updatedAt: new Date().toISOString()
696
- }
697
- await this.savePatterns(projectId)
698
- }
699
-
700
- /**
701
- * Get user preference
702
- * @param {string} projectId - Project ID
703
- * @param {string} key - Preference key
704
- * @param {any} defaultValue - Default if not set
705
- * @returns {Promise<any>}
706
- */
707
- async getPreference(projectId, key, defaultValue = null) {
708
- const patterns = await this.loadPatterns(projectId)
709
- return patterns.preferences[key]?.value ?? defaultValue
710
- }
711
-
712
- // ═══════════════════════════════════════════════════════════
713
- // TIER 3: History (append-only JSONL audit log)
714
- // ═══════════════════════════════════════════════════════════
715
-
716
- /**
717
- * Get path to today's session file
718
- * @param {string} projectId - Project ID
719
- * @returns {string} Path to session JSONL
720
- */
721
- _getSessionPath(projectId) {
722
- const now = new Date()
723
- const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
724
- const day = now.toISOString().split('T')[0]
725
-
726
- return path.join(
727
- pathManager.getGlobalProjectPath(projectId),
728
- 'memory',
729
- 'sessions',
730
- yearMonth,
731
- `${day}.jsonl`
732
- )
733
- }
734
-
735
- /**
736
- * Append entry to history (JSONL)
737
- * @param {string} projectId - Project ID
738
- * @param {Object} entry - Entry to log
739
- */
740
- async appendHistory(projectId, entry) {
741
- const sessionPath = this._getSessionPath(projectId)
742
-
743
- // Ensure directory exists
744
- await fs.mkdir(path.dirname(sessionPath), { recursive: true })
745
-
746
- const logEntry = {
747
- ts: new Date().toISOString(),
748
- ...entry
749
- }
750
-
751
- await fs.appendFile(
752
- sessionPath,
753
- JSON.stringify(logEntry) + '\n',
754
- 'utf-8'
755
- )
756
- }
757
-
758
- /**
759
- * Read recent history entries
760
- * @param {string} projectId - Project ID
761
- * @param {number} limit - Max entries to return
762
- * @returns {Promise<Object[]>}
763
- */
764
- async getRecentHistory(projectId, limit = 20) {
765
- try {
766
- const sessionPath = this._getSessionPath(projectId)
767
- const content = await fs.readFile(sessionPath, 'utf-8')
768
- const lines = content.trim().split('\n').filter(Boolean)
769
-
770
- return lines
771
- .slice(-limit)
772
- .map(line => {
773
- try {
774
- return JSON.parse(line)
775
- } catch {
776
- return null
777
- }
778
- })
779
- .filter(Boolean)
780
- } catch {
781
- return []
782
- }
783
- }
784
-
785
- // ═══════════════════════════════════════════════════════════
786
- // CONVENIENCE: Combined operations
787
- // ═══════════════════════════════════════════════════════════
788
-
789
- /**
790
- * Smart decision: Check pattern first, ask user only if unknown
791
- * Returns existing pattern or null (caller should ask user)
792
- *
793
- * @param {string} projectId - Project ID
794
- * @param {string} key - Decision key
795
- * @returns {Promise<string|null>} Known value or null
796
- */
797
- async getSmartDecision(projectId, key) {
798
- // Check session first (most recent)
799
- const sessionValue = this.getSession(`decision:${key}`)
800
- if (sessionValue !== undefined) return sessionValue
801
-
802
- // Check learned patterns
803
- const pattern = await this.getDecision(projectId, key)
804
- if (pattern) return pattern.value
805
-
806
- return null
807
- }
808
-
809
- /**
810
- * Record decision and store in session
811
- * @param {string} projectId - Project ID
812
- * @param {string} key - Decision key
813
- * @param {string} value - Decision value
814
- * @param {string} context - Context
815
- */
816
- async learnDecision(projectId, key, value, context = '') {
817
- // Store in session for immediate reuse
818
- this.setSession(`decision:${key}`, value)
819
-
820
- // Record in patterns for future sessions
821
- await this.recordDecision(projectId, key, value, context)
822
-
823
- // Log to history
824
- await this.appendHistory(projectId, {
825
- type: 'decision',
826
- key,
827
- value,
828
- context
829
- })
830
- }
831
-
832
- /**
833
- * Get all patterns summary (for debugging/display)
834
- * @param {string} projectId - Project ID
835
- * @returns {Promise<Object>}
836
- */
837
- async getPatternsSummary(projectId) {
838
- const patterns = await this.loadPatterns(projectId)
839
-
840
- return {
841
- decisions: Object.keys(patterns.decisions).length,
842
- learnedDecisions: Object.values(patterns.decisions)
843
- .filter(d => d.confidence !== 'low').length,
844
- workflows: Object.keys(patterns.workflows).length,
845
- preferences: Object.keys(patterns.preferences).length
846
- }
847
- }
848
- }
849
-
850
- module.exports = new MemorySystem()