prjct-cli 0.11.5 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +81 -25
  3. package/bin/dev.js +1 -1
  4. package/bin/generate-views.js +209 -0
  5. package/bin/migrate-to-json.js +742 -0
  6. package/bin/prjct +5 -5
  7. package/bin/serve.js +226 -50
  8. package/core/__tests__/agentic/{memory-system.test.js → memory-system.test.ts} +12 -23
  9. package/core/__tests__/agentic/{plan-mode.test.js → plan-mode.test.ts} +26 -24
  10. package/core/__tests__/agentic/{prompt-builder.test.js → prompt-builder.test.ts} +3 -8
  11. package/core/__tests__/utils/{date-helper.test.js → date-helper.test.ts} +19 -30
  12. package/core/__tests__/utils/{output.test.js → output.test.ts} +12 -24
  13. package/core/agentic/agent-router.ts +137 -0
  14. package/core/agentic/chain-of-thought.ts +228 -0
  15. package/core/agentic/command-executor/command-executor.ts +384 -0
  16. package/core/agentic/command-executor/index.ts +16 -0
  17. package/core/agentic/command-executor/status-signal.ts +38 -0
  18. package/core/agentic/command-executor/types.ts +79 -0
  19. package/core/agentic/command-executor.ts +8 -0
  20. package/core/agentic/{context-builder.js → context-builder.ts} +92 -81
  21. package/core/agentic/context-filter.ts +365 -0
  22. package/core/agentic/ground-truth/index.ts +76 -0
  23. package/core/agentic/ground-truth/types.ts +33 -0
  24. package/core/agentic/ground-truth/utils.ts +48 -0
  25. package/core/agentic/ground-truth/verifiers/analyze.ts +54 -0
  26. package/core/agentic/ground-truth/verifiers/done.ts +75 -0
  27. package/core/agentic/ground-truth/verifiers/feature.ts +70 -0
  28. package/core/agentic/ground-truth/verifiers/index.ts +37 -0
  29. package/core/agentic/ground-truth/verifiers/init.ts +52 -0
  30. package/core/agentic/ground-truth/verifiers/now.ts +57 -0
  31. package/core/agentic/ground-truth/verifiers/ship.ts +85 -0
  32. package/core/agentic/ground-truth/verifiers/spec.ts +45 -0
  33. package/core/agentic/ground-truth/verifiers/sync.ts +47 -0
  34. package/core/agentic/ground-truth/verifiers.ts +6 -0
  35. package/core/agentic/ground-truth.ts +8 -0
  36. package/core/agentic/loop-detector/error-analysis.ts +97 -0
  37. package/core/agentic/loop-detector/hallucination.ts +71 -0
  38. package/core/agentic/loop-detector/index.ts +41 -0
  39. package/core/agentic/loop-detector/loop-detector.ts +222 -0
  40. package/core/agentic/loop-detector/types.ts +66 -0
  41. package/core/agentic/loop-detector.ts +8 -0
  42. package/core/agentic/memory-system/history.ts +53 -0
  43. package/core/agentic/memory-system/index.ts +192 -0
  44. package/core/agentic/memory-system/patterns.ts +156 -0
  45. package/core/agentic/memory-system/semantic-memories.ts +277 -0
  46. package/core/agentic/memory-system/session.ts +21 -0
  47. package/core/agentic/memory-system/types.ts +159 -0
  48. package/core/agentic/memory-system.ts +8 -0
  49. package/core/agentic/parallel-tools.ts +165 -0
  50. package/core/agentic/plan-mode/approval.ts +57 -0
  51. package/core/agentic/plan-mode/constants.ts +44 -0
  52. package/core/agentic/plan-mode/index.ts +28 -0
  53. package/core/agentic/plan-mode/plan-mode.ts +406 -0
  54. package/core/agentic/plan-mode/types.ts +193 -0
  55. package/core/agentic/plan-mode.ts +8 -0
  56. package/core/agentic/prompt-builder.ts +566 -0
  57. package/core/agentic/response-templates.ts +164 -0
  58. package/core/agentic/semantic-compression.ts +273 -0
  59. package/core/agentic/services.ts +206 -0
  60. package/core/agentic/smart-context.ts +476 -0
  61. package/core/agentic/{template-loader.js → template-loader.ts} +27 -16
  62. package/core/agentic/think-blocks.ts +202 -0
  63. package/core/agentic/tool-registry.ts +119 -0
  64. package/core/agentic/validation-rules.ts +313 -0
  65. package/core/agents/index.ts +28 -0
  66. package/core/agents/performance.ts +444 -0
  67. package/core/agents/types.ts +126 -0
  68. package/core/bus/{index.js → index.ts} +57 -61
  69. package/core/command-registry/categories.ts +23 -0
  70. package/core/command-registry/commands.ts +15 -0
  71. package/core/command-registry/core-commands.ts +319 -0
  72. package/core/command-registry/index.ts +158 -0
  73. package/core/command-registry/optional-commands.ts +119 -0
  74. package/core/command-registry/setup-commands.ts +53 -0
  75. package/core/command-registry/types.ts +59 -0
  76. package/core/command-registry.ts +9 -0
  77. package/core/commands/analysis.ts +298 -0
  78. package/core/commands/analytics.ts +288 -0
  79. package/core/commands/base.ts +273 -0
  80. package/core/commands/index.ts +211 -0
  81. package/core/commands/maintenance.ts +226 -0
  82. package/core/commands/planning.ts +311 -0
  83. package/core/commands/setup.ts +309 -0
  84. package/core/commands/shipping.ts +188 -0
  85. package/core/commands/types.ts +183 -0
  86. package/core/commands/workflow.ts +226 -0
  87. package/core/commands.ts +11 -0
  88. package/core/constants/formats.ts +187 -0
  89. package/core/constants/index.ts +7 -0
  90. package/core/{context-sync.js → context-sync.ts} +59 -26
  91. package/core/data/agents-manager.ts +76 -0
  92. package/core/data/analysis-manager.ts +83 -0
  93. package/core/data/base-manager.ts +156 -0
  94. package/core/data/ideas-manager.ts +81 -0
  95. package/core/data/index.ts +32 -0
  96. package/core/data/outcomes-manager.ts +96 -0
  97. package/core/data/project-manager.ts +75 -0
  98. package/core/data/roadmap-manager.ts +118 -0
  99. package/core/data/shipped-manager.ts +65 -0
  100. package/core/data/state-manager.ts +214 -0
  101. package/core/domain/{agent-generator.js → agent-generator.ts} +77 -57
  102. package/core/domain/{agent-loader.js → agent-loader.ts} +65 -56
  103. package/core/domain/{agent-matcher.js → agent-matcher.ts} +51 -24
  104. package/core/domain/{agent-validator.js → agent-validator.ts} +70 -37
  105. package/core/domain/{analyzer.js → analyzer.ts} +91 -85
  106. package/core/domain/{architect-session.js → architect-session.ts} +49 -34
  107. package/core/domain/{architecture-generator.js → architecture-generator.ts} +25 -13
  108. package/core/domain/{context-estimator.js → context-estimator.ts} +57 -36
  109. package/core/domain/{product-standards.js → product-standards.ts} +40 -26
  110. package/core/domain/{smart-cache.js → smart-cache.ts} +39 -30
  111. package/core/domain/{snapshot-manager.js → snapshot-manager.ts} +103 -100
  112. package/core/domain/{task-analyzer.js → task-analyzer.ts} +82 -43
  113. package/core/domain/task-stack/index.ts +19 -0
  114. package/core/domain/task-stack/parser.ts +86 -0
  115. package/core/domain/task-stack/storage.ts +123 -0
  116. package/core/domain/task-stack/task-stack.ts +340 -0
  117. package/core/domain/task-stack/types.ts +51 -0
  118. package/core/domain/task-stack.ts +8 -0
  119. package/core/{index.js → index.ts} +61 -18
  120. package/core/infrastructure/{agent-detector.js → agent-detector.ts} +55 -19
  121. package/core/infrastructure/agents/{claude-agent.js → claude-agent.ts} +61 -21
  122. package/core/infrastructure/{author-detector.js → author-detector.ts} +42 -49
  123. package/core/infrastructure/{capability-installer.js → capability-installer.ts} +51 -27
  124. package/core/infrastructure/{command-installer.js → command-installer/command-installer.ts} +43 -144
  125. package/core/infrastructure/command-installer/global-config.ts +106 -0
  126. package/core/infrastructure/command-installer/index.ts +25 -0
  127. package/core/infrastructure/command-installer/types.ts +41 -0
  128. package/core/infrastructure/command-installer.ts +8 -0
  129. package/core/infrastructure/{config-manager.js → config-manager.ts} +60 -80
  130. package/core/infrastructure/{editors-config.js → editors-config.ts} +33 -31
  131. package/core/infrastructure/legacy-installer-detector/cleanup.ts +216 -0
  132. package/core/infrastructure/legacy-installer-detector/detection.ts +95 -0
  133. package/core/infrastructure/legacy-installer-detector/index.ts +171 -0
  134. package/core/infrastructure/legacy-installer-detector/migration.ts +87 -0
  135. package/core/infrastructure/legacy-installer-detector/types.ts +42 -0
  136. package/core/infrastructure/legacy-installer-detector.ts +7 -0
  137. package/core/infrastructure/migrator/file-operations.ts +125 -0
  138. package/core/infrastructure/migrator/index.ts +288 -0
  139. package/core/infrastructure/migrator/project-scanner.ts +89 -0
  140. package/core/infrastructure/migrator/reports.ts +117 -0
  141. package/core/infrastructure/migrator/types.ts +124 -0
  142. package/core/infrastructure/migrator/validation.ts +94 -0
  143. package/core/infrastructure/migrator/version-migration.ts +117 -0
  144. package/core/infrastructure/migrator.ts +10 -0
  145. package/core/infrastructure/{path-manager.js → path-manager.ts} +51 -91
  146. package/core/infrastructure/session-manager/index.ts +23 -0
  147. package/core/infrastructure/session-manager/migration.ts +88 -0
  148. package/core/infrastructure/session-manager/session-manager.ts +307 -0
  149. package/core/infrastructure/session-manager/types.ts +45 -0
  150. package/core/infrastructure/session-manager.ts +8 -0
  151. package/core/infrastructure/{setup.js → setup.ts} +29 -21
  152. package/core/infrastructure/{update-checker.js → update-checker.ts} +40 -18
  153. package/core/outcomes/analyzer.ts +333 -0
  154. package/core/outcomes/index.ts +34 -0
  155. package/core/outcomes/recorder.ts +194 -0
  156. package/core/outcomes/types.ts +145 -0
  157. package/core/plugin/{hooks.js → hooks.ts} +56 -58
  158. package/core/plugin/{index.js → index.ts} +19 -8
  159. package/core/plugin/{loader.js → loader.ts} +87 -69
  160. package/core/plugin/{registry.js → registry.ts} +49 -45
  161. package/core/plugins/{webhook.js → webhook.ts} +43 -27
  162. package/core/schemas/agents.ts +27 -0
  163. package/core/schemas/analysis.ts +41 -0
  164. package/core/schemas/ideas.ts +83 -0
  165. package/core/schemas/index.ts +73 -0
  166. package/core/schemas/outcomes.ts +22 -0
  167. package/core/schemas/project.ts +26 -0
  168. package/core/schemas/roadmap.ts +90 -0
  169. package/core/schemas/shipped.ts +82 -0
  170. package/core/schemas/state.ts +107 -0
  171. package/core/session/index.ts +17 -0
  172. package/core/session/{metrics.js → metrics.ts} +64 -46
  173. package/core/session/{index.js → session-manager.ts} +51 -117
  174. package/core/session/types.ts +29 -0
  175. package/core/session/utils.ts +57 -0
  176. package/core/state/index.ts +25 -0
  177. package/core/state/manager.ts +376 -0
  178. package/core/state/types.ts +185 -0
  179. package/core/tsconfig.json +22 -0
  180. package/core/types/index.ts +506 -0
  181. package/core/utils/{animations.js → animations.ts} +74 -28
  182. package/core/utils/{branding.js → branding.ts} +29 -4
  183. package/core/utils/{date-helper.js → date-helper.ts} +31 -74
  184. package/core/utils/file-helper.ts +262 -0
  185. package/core/utils/{jsonl-helper.js → jsonl-helper.ts} +71 -107
  186. package/core/utils/{logger.js → logger.ts} +24 -12
  187. package/core/utils/{output.js → output.ts} +25 -13
  188. package/core/utils/{project-capabilities.js → project-capabilities.ts} +31 -18
  189. package/core/utils/{session-helper.js → session-helper.ts} +79 -66
  190. package/core/utils/{version.js → version.ts} +23 -31
  191. package/core/view-generator.ts +536 -0
  192. package/package.json +23 -17
  193. package/packages/shared/.turbo/turbo-build.log +14 -0
  194. package/packages/shared/dist/index.d.ts +8 -613
  195. package/packages/shared/dist/index.d.ts.map +1 -0
  196. package/packages/shared/dist/index.js +4110 -118
  197. package/packages/shared/dist/schemas.d.ts +408 -0
  198. package/packages/shared/dist/schemas.d.ts.map +1 -0
  199. package/packages/shared/dist/types.d.ts +144 -0
  200. package/packages/shared/dist/types.d.ts.map +1 -0
  201. package/packages/shared/dist/unified.d.ts +139 -0
  202. package/packages/shared/dist/unified.d.ts.map +1 -0
  203. package/packages/shared/dist/utils.d.ts +60 -0
  204. package/packages/shared/dist/utils.d.ts.map +1 -0
  205. package/packages/shared/package.json +4 -4
  206. package/packages/shared/src/index.ts +1 -0
  207. package/packages/shared/src/unified.ts +174 -0
  208. package/packages/web/app/api/claude/sessions/route.ts +1 -1
  209. package/packages/web/app/api/claude/status/route.ts +1 -1
  210. package/packages/web/app/api/migrate/route.ts +46 -0
  211. package/packages/web/app/api/projects/[id]/route.ts +1 -1
  212. package/packages/web/app/api/projects/[id]/stats/route.ts +30 -2
  213. package/packages/web/app/api/projects/[id]/status/route.ts +1 -1
  214. package/packages/web/app/api/projects/route.ts +1 -1
  215. package/packages/web/app/api/settings/route.ts +97 -0
  216. package/packages/web/app/api/v2/projects/[id]/unified/route.ts +57 -0
  217. package/packages/web/app/globals.css +38 -0
  218. package/packages/web/app/layout.tsx +10 -2
  219. package/packages/web/app/page.tsx +9 -224
  220. package/packages/web/app/project/[id]/page.tsx +191 -63
  221. package/packages/web/app/project/[id]/stats/loading.tsx +43 -0
  222. package/packages/web/app/project/[id]/stats/page.tsx +204 -163
  223. package/packages/web/app/settings/page.tsx +222 -2
  224. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +2 -0
  225. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +50 -0
  226. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +8 -0
  227. package/packages/web/components/ActivityTimeline/hooks/index.ts +2 -0
  228. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +9 -0
  229. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +23 -0
  230. package/packages/web/components/ActivityTimeline/index.ts +2 -0
  231. package/packages/web/components/AgentsCard/AgentsCard.tsx +63 -0
  232. package/packages/web/components/AgentsCard/AgentsCard.types.ts +13 -0
  233. package/packages/web/components/AgentsCard/index.ts +2 -0
  234. package/packages/web/components/AppSidebar/AppSidebar.tsx +134 -0
  235. package/packages/web/components/AppSidebar/index.ts +1 -0
  236. package/packages/web/components/BackLink/BackLink.tsx +18 -0
  237. package/packages/web/components/BackLink/BackLink.types.ts +5 -0
  238. package/packages/web/components/BackLink/index.ts +2 -0
  239. package/packages/web/components/BentoCard/BentoCard.constants.ts +16 -0
  240. package/packages/web/components/BentoCard/BentoCard.tsx +47 -0
  241. package/packages/web/components/BentoCard/BentoCard.types.ts +15 -0
  242. package/packages/web/components/BentoCard/index.ts +2 -0
  243. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +9 -0
  244. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +18 -0
  245. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +5 -0
  246. package/packages/web/components/BentoCardSkeleton/index.ts +2 -0
  247. package/packages/web/components/{stats → BentoGrid}/BentoGrid.tsx +4 -8
  248. package/packages/web/components/BentoGrid/BentoGrid.types.ts +4 -0
  249. package/packages/web/components/BentoGrid/index.ts +2 -0
  250. package/packages/web/components/CommandButton/index.ts +1 -0
  251. package/packages/web/components/ConnectionStatus/index.ts +1 -0
  252. package/packages/web/components/DashboardContent/DashboardContent.tsx +254 -0
  253. package/packages/web/components/DashboardContent/index.ts +1 -0
  254. package/packages/web/components/DateGroup/DateGroup.tsx +18 -0
  255. package/packages/web/components/DateGroup/DateGroup.types.ts +6 -0
  256. package/packages/web/components/DateGroup/DateGroup.utils.ts +11 -0
  257. package/packages/web/components/DateGroup/index.ts +2 -0
  258. package/packages/web/components/{stats → EmptyState}/EmptyState.tsx +1 -10
  259. package/packages/web/components/EmptyState/EmptyState.types.ts +10 -0
  260. package/packages/web/components/EmptyState/index.ts +2 -0
  261. package/packages/web/components/EventRow/EventRow.constants.ts +10 -0
  262. package/packages/web/components/EventRow/EventRow.tsx +49 -0
  263. package/packages/web/components/EventRow/EventRow.types.ts +7 -0
  264. package/packages/web/components/EventRow/EventRow.utils.ts +49 -0
  265. package/packages/web/components/EventRow/index.ts +2 -0
  266. package/packages/web/components/ExpandButton/ExpandButton.tsx +18 -0
  267. package/packages/web/components/ExpandButton/ExpandButton.types.ts +6 -0
  268. package/packages/web/components/ExpandButton/index.ts +2 -0
  269. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +14 -0
  270. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +5 -0
  271. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +13 -0
  272. package/packages/web/components/HealthGradientBackground/index.ts +2 -0
  273. package/packages/web/components/HeroSection/HeroSection.tsx +55 -0
  274. package/packages/web/components/HeroSection/HeroSection.types.ts +14 -0
  275. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -0
  276. package/packages/web/components/HeroSection/hooks/index.ts +2 -0
  277. package/packages/web/components/HeroSection/hooks/useCountUp.ts +45 -0
  278. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +18 -0
  279. package/packages/web/components/HeroSection/index.ts +2 -0
  280. package/packages/web/components/{stats → IdeasCard}/IdeasCard.tsx +3 -14
  281. package/packages/web/components/IdeasCard/IdeasCard.types.ts +9 -0
  282. package/packages/web/components/IdeasCard/index.ts +2 -0
  283. package/packages/web/components/InsightMessage/InsightMessage.tsx +9 -0
  284. package/packages/web/components/InsightMessage/InsightMessage.types.ts +3 -0
  285. package/packages/web/components/InsightMessage/index.ts +2 -0
  286. package/packages/web/components/Logo/index.ts +1 -0
  287. package/packages/web/components/MarkdownContent/index.ts +1 -0
  288. package/packages/web/components/NowCard/NowCard.tsx +93 -0
  289. package/packages/web/components/NowCard/NowCard.types.ts +15 -0
  290. package/packages/web/components/NowCard/index.ts +2 -0
  291. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +20 -0
  292. package/packages/web/components/{stats → ProgressRing}/ProgressRing.tsx +4 -27
  293. package/packages/web/components/ProgressRing/ProgressRing.types.ts +11 -0
  294. package/packages/web/components/ProgressRing/index.ts +2 -0
  295. package/packages/web/components/ProjectAvatar/index.ts +1 -0
  296. package/packages/web/components/Providers/index.ts +1 -0
  297. package/packages/web/components/QueueCard/QueueCard.tsx +72 -0
  298. package/packages/web/components/QueueCard/QueueCard.types.ts +11 -0
  299. package/packages/web/components/QueueCard/QueueCard.utils.ts +12 -0
  300. package/packages/web/components/QueueCard/index.ts +2 -0
  301. package/packages/web/components/{stats → RoadmapCard}/RoadmapCard.tsx +3 -23
  302. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +15 -0
  303. package/packages/web/components/RoadmapCard/index.ts +2 -0
  304. package/packages/web/components/{stats → ShipsCard}/ShipsCard.tsx +4 -22
  305. package/packages/web/components/ShipsCard/ShipsCard.types.ts +12 -0
  306. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +4 -0
  307. package/packages/web/components/ShipsCard/index.ts +2 -0
  308. package/packages/web/components/{stats → SparklineChart}/SparklineChart.tsx +1 -7
  309. package/packages/web/components/SparklineChart/SparklineChart.types.ts +6 -0
  310. package/packages/web/components/SparklineChart/index.ts +2 -0
  311. package/packages/web/components/StreakCard/StreakCard.constants.ts +2 -0
  312. package/packages/web/components/{stats → StreakCard}/StreakCard.tsx +5 -11
  313. package/packages/web/components/StreakCard/StreakCard.types.ts +4 -0
  314. package/packages/web/components/StreakCard/index.ts +2 -0
  315. package/packages/web/components/TasksCounter/TasksCounter.tsx +14 -0
  316. package/packages/web/components/TasksCounter/TasksCounter.types.ts +3 -0
  317. package/packages/web/components/TasksCounter/index.ts +2 -0
  318. package/packages/web/components/TechStackBadges/index.ts +1 -0
  319. package/packages/web/components/{TerminalTab.tsx → TerminalTabs/TerminalTab.tsx} +11 -0
  320. package/packages/web/components/{TerminalTabs.tsx → TerminalTabs/TerminalTabs.tsx} +29 -28
  321. package/packages/web/components/TerminalTabs/index.ts +1 -0
  322. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +27 -0
  323. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +3 -0
  324. package/packages/web/components/VelocityBadge/index.ts +2 -0
  325. package/packages/web/components/VelocityCard/VelocityCard.tsx +71 -0
  326. package/packages/web/components/VelocityCard/VelocityCard.types.ts +7 -0
  327. package/packages/web/components/VelocityCard/index.ts +2 -0
  328. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +13 -0
  329. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +3 -0
  330. package/packages/web/components/WeeklySparkline/index.ts +2 -0
  331. package/packages/web/components/ui/input.tsx +21 -0
  332. package/packages/web/context/TerminalTabsContext.tsx +46 -1
  333. package/packages/web/hooks/useClaudeTerminal.ts +71 -21
  334. package/packages/web/hooks/useProjectStats.ts +55 -0
  335. package/packages/web/hooks/useProjects.ts +6 -6
  336. package/packages/web/lib/actions/projects.ts +15 -0
  337. package/packages/web/lib/json-loader.ts +630 -0
  338. package/packages/web/lib/services/index.ts +9 -0
  339. package/packages/web/lib/services/migration.server.ts +598 -0
  340. package/packages/web/lib/services/projects.server.ts +52 -0
  341. package/packages/web/lib/services/stats.server.ts +264 -0
  342. package/packages/web/lib/unified-loader.ts +396 -0
  343. package/packages/web/package.json +10 -7
  344. package/packages/web/server.ts +36 -6
  345. package/templates/commands/done.md +76 -32
  346. package/templates/commands/feature.md +121 -47
  347. package/templates/commands/idea.md +81 -8
  348. package/templates/commands/now.md +41 -17
  349. package/templates/commands/ship.md +64 -25
  350. package/templates/commands/sync.md +28 -3
  351. package/core/agentic/agent-router.js +0 -140
  352. package/core/agentic/chain-of-thought.js +0 -578
  353. package/core/agentic/command-executor.js +0 -417
  354. package/core/agentic/context-filter.js +0 -354
  355. package/core/agentic/ground-truth.js +0 -591
  356. package/core/agentic/loop-detector.js +0 -406
  357. package/core/agentic/memory-system.js +0 -845
  358. package/core/agentic/parallel-tools.js +0 -366
  359. package/core/agentic/plan-mode.js +0 -572
  360. package/core/agentic/prompt-builder.js +0 -352
  361. package/core/agentic/response-templates.js +0 -290
  362. package/core/agentic/semantic-compression.js +0 -517
  363. package/core/agentic/think-blocks.js +0 -657
  364. package/core/agentic/tool-registry.js +0 -184
  365. package/core/agentic/validation-rules.js +0 -380
  366. package/core/command-registry.js +0 -698
  367. package/core/commands.js +0 -2237
  368. package/core/domain/task-stack.js +0 -497
  369. package/core/infrastructure/legacy-installer-detector.js +0 -546
  370. package/core/infrastructure/migrator.js +0 -796
  371. package/core/infrastructure/session-manager.js +0 -390
  372. package/core/utils/file-helper.js +0 -329
  373. package/packages/web/app/api/projects/[id]/delete/route.ts +0 -21
  374. package/packages/web/app/api/stats/route.ts +0 -38
  375. package/packages/web/components/AppSidebar.tsx +0 -113
  376. package/packages/web/components/stats/ActivityTimeline.tsx +0 -201
  377. package/packages/web/components/stats/AgentsCard.tsx +0 -56
  378. package/packages/web/components/stats/BentoCard.tsx +0 -88
  379. package/packages/web/components/stats/HeroSection.tsx +0 -172
  380. package/packages/web/components/stats/NowCard.tsx +0 -71
  381. package/packages/web/components/stats/QueueCard.tsx +0 -58
  382. package/packages/web/components/stats/VelocityCard.tsx +0 -60
  383. package/packages/web/components/stats/index.ts +0 -17
  384. package/packages/web/hooks/useStats.ts +0 -28
  385. /package/packages/web/components/{CommandButton.tsx → CommandButton/CommandButton.tsx} +0 -0
  386. /package/packages/web/components/{ConnectionStatus.tsx → ConnectionStatus/ConnectionStatus.tsx} +0 -0
  387. /package/packages/web/components/{Logo.tsx → Logo/Logo.tsx} +0 -0
  388. /package/packages/web/components/{MarkdownContent.tsx → MarkdownContent/MarkdownContent.tsx} +0 -0
  389. /package/packages/web/components/{ProjectAvatar.tsx → ProjectAvatar/ProjectAvatar.tsx} +0 -0
  390. /package/packages/web/components/{providers.tsx → Providers/Providers.tsx} +0 -0
  391. /package/packages/web/components/{TechStackBadges.tsx → TechStackBadges/TechStackBadges.tsx} +0 -0
@@ -1,390 +0,0 @@
1
- /**
2
- * Session Manager
3
- * Manages temporal fragmentation of logs and progress data.
4
- *
5
- * @module infrastructure/session-manager
6
- * @version 0.2.1
7
- */
8
-
9
- const path = require('path')
10
- const pathManager = require('./path-manager')
11
- const { VERSION } = require('../utils/version')
12
- const dateHelper = require('../utils/date-helper')
13
- const jsonlHelper = require('../utils/jsonl-helper')
14
- const fileHelper = require('../utils/file-helper')
15
-
16
- /**
17
- * Manages daily sessions for logs and progress data.
18
- * Handles rotation, historical queries, and legacy migration.
19
- */
20
- class SessionManager {
21
- constructor() {
22
- this.currentSessionCache = new Map() // Cache current session paths
23
- this.sessionMetadataCache = new Map() // Cache session metadata
24
- }
25
-
26
- /**
27
- * Get or create current session directory for a project
28
- *
29
- * @param {string} projectId - The project identifier
30
- * @returns {Promise<string>} - Path to today's session directory
31
- */
32
- async getCurrentSession(projectId) {
33
- const cacheKey = `${projectId}-${this._getTodayKey()}`
34
-
35
- if (this.currentSessionCache.has(cacheKey)) {
36
- return this.currentSessionCache.get(cacheKey)
37
- }
38
-
39
- const sessionPath = await pathManager.ensureSessionPath(projectId)
40
- this.currentSessionCache.set(cacheKey, sessionPath)
41
-
42
- await this._ensureSessionMetadata(sessionPath)
43
-
44
- return sessionPath
45
- }
46
-
47
- /**
48
- * Write log entry to current session
49
- *
50
- * @param {string} projectId - The project identifier
51
- * @param {Object} entry - Log entry object
52
- * @param {string} filename - Target filename (default: context.jsonl)
53
- * @returns {Promise<void>}
54
- */
55
- async writeToSession(projectId, entry, filename = 'context.jsonl') {
56
- const sessionPath = await this.getCurrentSession(projectId)
57
- const filePath = path.join(sessionPath, filename)
58
-
59
- // Use automatic rotation to prevent large files (>10MB)
60
- await jsonlHelper.appendJsonLineWithRotation(filePath, entry, 10)
61
-
62
- await this._updateSessionMetadata(sessionPath, {
63
- lastActivity: dateHelper.getTimestamp(),
64
- entryCount: await jsonlHelper.countJsonLines(filePath),
65
- })
66
- }
67
-
68
- /**
69
- * Append content to a session file (for markdown files like shipped.md)
70
- *
71
- * @param {string} projectId - The project identifier
72
- * @param {string} content - Content to append
73
- * @param {string} filename - Target filename
74
- * @returns {Promise<void>}
75
- */
76
- async appendToSession(projectId, content, filename) {
77
- const sessionPath = await this.getCurrentSession(projectId)
78
- const filePath = path.join(sessionPath, filename)
79
-
80
- const exists = await fileHelper.fileExists(filePath)
81
- if (!exists && filename === 'shipped.md') {
82
- await fileHelper.writeFile(filePath, '# SHIPPED 🚀\n\n' + content)
83
- } else {
84
- await fileHelper.appendToFile(filePath, content)
85
- }
86
-
87
- await this._updateSessionMetadata(sessionPath, {
88
- lastActivity: dateHelper.getTimestamp(),
89
- })
90
- }
91
-
92
- /**
93
- * Read logs from current session
94
- * Uses streaming for large files (>50MB)
95
- *
96
- * @param {string} projectId - The project identifier
97
- * @param {string} filename - Source filename (default: context.jsonl)
98
- * @param {number} maxLines - Max lines to read for large files (default: 1000)
99
- * @returns {Promise<Array<Object>>} - Array of parsed log entries
100
- */
101
- async readCurrentSession(projectId, filename = 'context.jsonl', maxLines = 1000) {
102
- const sessionPath = await this.getCurrentSession(projectId)
103
- const filePath = path.join(sessionPath, filename)
104
-
105
- // Check file size and warn if large
106
- const { isLarge } = await jsonlHelper.checkFileSizeWarning(filePath, 50)
107
-
108
- if (isLarge) {
109
- // Use streaming for large files
110
- return await jsonlHelper.readJsonLinesStreaming(filePath, maxLines)
111
- }
112
-
113
- // Use normal read for small files
114
- return await jsonlHelper.readJsonLines(filePath)
115
- }
116
-
117
- /**
118
- * Read logs from a specific date range
119
- *
120
- * @param {string} projectId - The project identifier
121
- * @param {Date} fromDate - Start date
122
- * @param {Date} toDate - End date (defaults to today)
123
- * @param {string} filename - Source filename (default: context.jsonl)
124
- * @returns {Promise<Array<Object>>} - Array of parsed log entries from all sessions in range
125
- */
126
- async readSessionRange(projectId, fromDate, toDate = new Date(), filename = 'context.jsonl') {
127
- const sessions = await pathManager.getSessionsInRange(projectId, fromDate, toDate)
128
- const allEntries = []
129
-
130
- for (const session of sessions) {
131
- const filePath = path.join(session.path, filename)
132
- const entries = await jsonlHelper.readJsonLines(filePath)
133
-
134
- entries.forEach((entry) => {
135
- entry._sessionDate = session.date
136
- })
137
-
138
- allEntries.push(...entries)
139
- }
140
-
141
- return allEntries
142
- }
143
-
144
- /**
145
- * Read markdown content from sessions in date range
146
- *
147
- * @param {string} projectId - The project identifier
148
- * @param {Date} fromDate - Start date
149
- * @param {Date} toDate - End date
150
- * @param {string} filename - Source filename (e.g., 'shipped.md')
151
- * @returns {Promise<string>} - Concatenated content from all sessions
152
- */
153
- async readMarkdownRange(projectId, fromDate, toDate, filename) {
154
- const sessions = await pathManager.getSessionsInRange(projectId, fromDate, toDate)
155
- const allContent = []
156
-
157
- for (const session of sessions) {
158
- const filePath = path.join(session.path, filename)
159
- const content = await fileHelper.readFile(filePath, '')
160
-
161
- if (content.trim()) {
162
- allContent.push(
163
- `## Session: ${session.year}-${session.month}-${session.day}\n\n${content}`
164
- )
165
- }
166
- }
167
-
168
- return allContent.join('\n---\n\n')
169
- }
170
-
171
- /**
172
- * Get recent logs (last N days)
173
- *
174
- * @param {string} projectId - The project identifier
175
- * @param {number} days - Number of days to look back
176
- * @param {string} filename - Source filename
177
- * @returns {Promise<Array<Object>>} - Recent log entries
178
- */
179
- async getRecentLogs(projectId, days = 7, filename = 'context.jsonl') {
180
- const toDate = new Date()
181
- const fromDate = dateHelper.getDaysAgo(days)
182
-
183
- return await this.readSessionRange(projectId, fromDate, toDate, filename)
184
- }
185
-
186
- /**
187
- * Get session statistics
188
- *
189
- * @param {string} projectId - The project identifier
190
- * @param {Date} fromDate - Start date
191
- * @param {Date} toDate - End date
192
- * @returns {Promise<Object>} - Statistics object
193
- */
194
- async getSessionStats(projectId, fromDate, toDate) {
195
- const sessions = await pathManager.getSessionsInRange(projectId, fromDate, toDate)
196
-
197
- let totalEntries = 0
198
- let totalShips = 0
199
- let activeDays = 0
200
-
201
- for (const session of sessions) {
202
- const metadata = await this._getSessionMetadata(session.path)
203
- if (metadata) {
204
- totalEntries += metadata.entryCount || 0
205
- totalShips += metadata.shipCount || 0
206
- if (metadata.entryCount > 0) {
207
- activeDays++
208
- }
209
- }
210
- }
211
-
212
- return {
213
- totalSessions: sessions.length,
214
- activeDays,
215
- totalEntries,
216
- totalShips,
217
- averageEntriesPerDay: activeDays > 0 ? Math.round(totalEntries / activeDays) : 0,
218
- }
219
- }
220
-
221
- /**
222
- * Migrate legacy single-file logs to session structure
223
- *
224
- * @param {string} projectId - The project identifier
225
- * @param {string} legacyFilePath - Path to legacy log file
226
- * @param {string} sessionFilename - Target filename in sessions
227
- * @returns {Promise<Object>} - Migration result
228
- */
229
- async migrateLegacyLogs(projectId, legacyFilePath, sessionFilename) {
230
- try {
231
- const content = await fileHelper.readFile(legacyFilePath)
232
-
233
- if (sessionFilename.endsWith('.jsonl')) {
234
- return await this._migrateLegacyJsonl(projectId, content, sessionFilename)
235
- } else {
236
- return await this._migrateLegacyMarkdown(projectId, content, sessionFilename)
237
- }
238
- } catch (error) {
239
- return {
240
- success: false,
241
- message: `Migration failed: ${error.message}`,
242
- entriesMigrated: 0,
243
- }
244
- }
245
- }
246
-
247
- /**
248
- * Migrate legacy JSONL file
249
- * @private
250
- */
251
- async _migrateLegacyJsonl(projectId, content, sessionFilename) {
252
- const entries = jsonlHelper.parseJsonLines(content)
253
- const sessionGroups = new Map()
254
-
255
- for (const entry of entries) {
256
- const date = new Date(entry.timestamp || entry.data?.timestamp || Date.now())
257
- const dateKey = dateHelper.getDateKey(date)
258
-
259
- if (!sessionGroups.has(dateKey)) {
260
- sessionGroups.set(dateKey, [])
261
- }
262
- sessionGroups.get(dateKey).push(entry)
263
- }
264
-
265
- let migratedCount = 0
266
- for (const [dateKey, groupEntries] of sessionGroups) {
267
- const [year, month, day] = dateKey.split('-')
268
- const date = new Date(year, month - 1, day)
269
- const sessionPath = await pathManager.ensureSessionPath(projectId, date)
270
- const filePath = path.join(sessionPath, sessionFilename)
271
-
272
- await jsonlHelper.writeJsonLines(filePath, groupEntries)
273
-
274
- migratedCount += groupEntries.length
275
-
276
- await this._ensureSessionMetadata(sessionPath)
277
- await this._updateSessionMetadata(sessionPath, {
278
- entryCount: groupEntries.length,
279
- migrated: true,
280
- migratedAt: dateHelper.getTimestamp(),
281
- })
282
- }
283
-
284
- return {
285
- success: true,
286
- message: `Migrated ${migratedCount} entries to ${sessionGroups.size} sessions`,
287
- entriesMigrated: migratedCount,
288
- sessionsCreated: sessionGroups.size,
289
- }
290
- }
291
-
292
- /**
293
- * Migrate legacy markdown file
294
- * @private
295
- */
296
- async _migrateLegacyMarkdown(projectId, content, sessionFilename) {
297
- const sessionPath = await this.getCurrentSession(projectId)
298
- const filePath = path.join(sessionPath, sessionFilename)
299
-
300
- await fileHelper.writeFile(filePath, content)
301
-
302
- await this._updateSessionMetadata(sessionPath, {
303
- migrated: true,
304
- migratedAt: dateHelper.getTimestamp(),
305
- })
306
-
307
- return {
308
- success: true,
309
- message: 'Migrated markdown content to current session',
310
- entriesMigrated: 1,
311
- sessionsCreated: 1,
312
- }
313
- }
314
-
315
- /**
316
- * Get session metadata
317
- * @private
318
- */
319
- async _getSessionMetadata(sessionPath) {
320
- const metadataPath = path.join(sessionPath, 'session-meta.json')
321
-
322
- if (this.sessionMetadataCache.has(sessionPath)) {
323
- return this.sessionMetadataCache.get(sessionPath)
324
- }
325
-
326
- const metadata = await fileHelper.readJson(metadataPath, null)
327
- if (metadata) {
328
- this.sessionMetadataCache.set(sessionPath, metadata)
329
- }
330
- return metadata
331
- }
332
-
333
- /**
334
- * Ensure session metadata exists
335
- * @private
336
- */
337
- async _ensureSessionMetadata(sessionPath) {
338
- const metadataPath = path.join(sessionPath, 'session-meta.json')
339
-
340
- const exists = await fileHelper.fileExists(metadataPath)
341
- if (!exists) {
342
- const metadata = {
343
- created: dateHelper.getTimestamp(),
344
- lastActivity: dateHelper.getTimestamp(),
345
- entryCount: 0,
346
- shipCount: 0,
347
- version: VERSION,
348
- }
349
- await fileHelper.writeJson(metadataPath, metadata)
350
- this.sessionMetadataCache.set(sessionPath, metadata)
351
- }
352
- }
353
-
354
- /**
355
- * Update session metadata
356
- * @private
357
- */
358
- async _updateSessionMetadata(sessionPath, updates) {
359
- const metadata = (await this._getSessionMetadata(sessionPath)) || {}
360
- Object.assign(metadata, updates)
361
-
362
- const metadataPath = path.join(sessionPath, 'session-meta.json')
363
- await fileHelper.writeJson(metadataPath, metadata)
364
-
365
- this.sessionMetadataCache.set(sessionPath, metadata)
366
- }
367
-
368
- /**
369
- * Get today's date key (YYYY-MM-DD)
370
- * @private
371
- */
372
- _getTodayKey() {
373
- return dateHelper.getTodayKey()
374
- }
375
-
376
- /**
377
- * Get date key for any date (YYYY-MM-DD)
378
- * @private
379
- */
380
- _getDateKey(date) {
381
- return dateHelper.getDateKey(date)
382
- }
383
-
384
- clearCache() {
385
- this.currentSessionCache.clear()
386
- this.sessionMetadataCache.clear()
387
- }
388
- }
389
-
390
- module.exports = new SessionManager()
@@ -1,329 +0,0 @@
1
- const fs = require('fs').promises
2
- const path = require('path')
3
-
4
- /**
5
- * File Helper - Centralized file operations with error handling
6
- *
7
- * Eliminates duplicated fs operations across:
8
- * - 101 fs.readFile/writeFile calls in 18 files
9
- * - Consistent error handling
10
- * - JSON read/write patterns
11
- *
12
- * @module file-helper
13
- */
14
-
15
- /**
16
- * Read JSON file and parse
17
- *
18
- * @param {string} filePath - Path to JSON file
19
- * @param {*} defaultValue - Default value if file doesn't exist (default: null)
20
- * @returns {Promise<Object|*>} - Parsed JSON or default value
21
- */
22
- async function readJson(filePath, defaultValue = null) {
23
- try {
24
- const content = await fs.readFile(filePath, 'utf-8')
25
- return JSON.parse(content)
26
- } catch (error) {
27
- if (error.code === 'ENOENT') {
28
- return defaultValue
29
- }
30
- throw error
31
- }
32
- }
33
-
34
- /**
35
- * Write object to JSON file (pretty-printed)
36
- *
37
- * @param {string} filePath - Path to JSON file
38
- * @param {Object} data - Data to write
39
- * @param {number} indent - Indentation spaces (default: 2)
40
- * @returns {Promise<void>}
41
- */
42
- async function writeJson(filePath, data, indent = 2) {
43
- const content = JSON.stringify(data, null, indent)
44
- await fs.writeFile(filePath, content, 'utf-8')
45
- }
46
-
47
- /**
48
- * Read text file
49
- *
50
- * @param {string} filePath - Path to file
51
- * @param {string} defaultValue - Default value if file doesn't exist (default: '')
52
- * @returns {Promise<string>} - File content or default value
53
- */
54
- async function readFile(filePath, defaultValue = '') {
55
- try {
56
- return await fs.readFile(filePath, 'utf-8')
57
- } catch (error) {
58
- if (error.code === 'ENOENT') {
59
- return defaultValue
60
- }
61
- throw error
62
- }
63
- }
64
-
65
- /**
66
- * Write text file
67
- *
68
- * @param {string} filePath - Path to file
69
- * @param {string} content - Content to write
70
- * @returns {Promise<void>}
71
- */
72
- async function writeFile(filePath, content) {
73
- const dir = path.dirname(filePath)
74
- await fs.mkdir(dir, { recursive: true })
75
- await fs.writeFile(filePath, content, 'utf-8')
76
- }
77
-
78
- /**
79
- * Append to text file
80
- *
81
- * @param {string} filePath - Path to file
82
- * @param {string} content - Content to append
83
- * @returns {Promise<void>}
84
- */
85
- async function appendToFile(filePath, content) {
86
- await fs.appendFile(filePath, content, 'utf-8')
87
- }
88
-
89
- /**
90
- * Prepend to text file (adds content at beginning)
91
- *
92
- * @param {string} filePath - Path to file
93
- * @param {string} content - Content to prepend
94
- * @returns {Promise<void>}
95
- */
96
- async function prependToFile(filePath, content) {
97
- try {
98
- const existing = await fs.readFile(filePath, 'utf-8')
99
- await fs.writeFile(filePath, content + existing, 'utf-8')
100
- } catch (error) {
101
- if (error.code === 'ENOENT') {
102
- await fs.writeFile(filePath, content, 'utf-8')
103
- } else {
104
- throw error
105
- }
106
- }
107
- }
108
-
109
- /**
110
- * Check if file exists
111
- *
112
- * @param {string} filePath - Path to file
113
- * @returns {Promise<boolean>} - True if file exists
114
- */
115
- async function fileExists(filePath) {
116
- try {
117
- await fs.access(filePath)
118
- return true
119
- } catch {
120
- return false
121
- }
122
- }
123
-
124
- /**
125
- * Check if directory exists
126
- *
127
- * @param {string} dirPath - Path to directory
128
- * @returns {Promise<boolean>} - True if directory exists
129
- */
130
- async function dirExists(dirPath) {
131
- try {
132
- const stats = await fs.stat(dirPath)
133
- return stats.isDirectory()
134
- } catch {
135
- return false
136
- }
137
- }
138
-
139
- /**
140
- * Ensure directory exists (create if not)
141
- *
142
- * @param {string} dirPath - Path to directory
143
- * @returns {Promise<void>}
144
- */
145
- async function ensureDir(dirPath) {
146
- await fs.mkdir(dirPath, { recursive: true })
147
- }
148
-
149
- /**
150
- * Delete file if it exists
151
- *
152
- * @param {string} filePath - Path to file
153
- * @returns {Promise<boolean>} - True if file was deleted
154
- */
155
- async function deleteFile(filePath) {
156
- try {
157
- await fs.unlink(filePath)
158
- return true
159
- } catch (error) {
160
- if (error.code === 'ENOENT') {
161
- return false // File didn't exist
162
- }
163
- throw error
164
- }
165
- }
166
-
167
- /**
168
- * Delete directory and all contents
169
- *
170
- * @param {string} dirPath - Path to directory
171
- * @returns {Promise<boolean>} - True if directory was deleted
172
- */
173
- async function deleteDir(dirPath) {
174
- try {
175
- await fs.rm(dirPath, { recursive: true, force: true })
176
- return true
177
- } catch (error) {
178
- if (error.code === 'ENOENT') {
179
- return false
180
- }
181
- throw error
182
- }
183
- }
184
-
185
- /**
186
- * List files in directory
187
- *
188
- * @param {string} dirPath - Path to directory
189
- * @param {Object} options - Options
190
- * @param {boolean} options.filesOnly - Only return files (not directories)
191
- * @param {boolean} options.dirsOnly - Only return directories
192
- * @param {string} options.extension - Filter by file extension (e.g., '.md')
193
- * @returns {Promise<Array<string>>} - Array of filenames
194
- */
195
- async function listFiles(dirPath, options = {}) {
196
- try {
197
- const entries = await fs.readdir(dirPath, { withFileTypes: true })
198
- let files = entries
199
-
200
- if (options.filesOnly) {
201
- files = files.filter((entry) => entry.isFile())
202
- }
203
-
204
- if (options.dirsOnly) {
205
- files = files.filter((entry) => entry.isDirectory())
206
- }
207
-
208
- if (options.extension) {
209
- files = files.filter((entry) => entry.name.endsWith(options.extension))
210
- }
211
-
212
- return files.map((entry) => entry.name)
213
- } catch (error) {
214
- if (error.code === 'ENOENT') {
215
- return []
216
- }
217
- throw error
218
- }
219
- }
220
-
221
- /**
222
- * Get file size in bytes
223
- *
224
- * @param {string} filePath - Path to file
225
- * @returns {Promise<number>} - File size in bytes
226
- */
227
- async function getFileSize(filePath) {
228
- const stats = await fs.stat(filePath)
229
- return stats.size
230
- }
231
-
232
- /**
233
- * Get file modification time
234
- *
235
- * @param {string} filePath - Path to file
236
- * @returns {Promise<Date>} - Last modification time
237
- */
238
- async function getFileModifiedTime(filePath) {
239
- const stats = await fs.stat(filePath)
240
- return stats.mtime
241
- }
242
-
243
- /**
244
- * Copy file
245
- *
246
- * @param {string} sourcePath - Source file path
247
- * @param {string} destPath - Destination file path
248
- * @returns {Promise<void>}
249
- */
250
- async function copyFile(sourcePath, destPath) {
251
- await fs.copyFile(sourcePath, destPath)
252
- }
253
-
254
- /**
255
- * Move/rename file
256
- *
257
- * @param {string} oldPath - Current file path
258
- * @param {string} newPath - New file path
259
- * @returns {Promise<void>}
260
- */
261
- async function moveFile(oldPath, newPath) {
262
- await fs.rename(oldPath, newPath)
263
- }
264
-
265
- /**
266
- * Read file and split into lines
267
- *
268
- * @param {string} filePath - Path to file
269
- * @returns {Promise<Array<string>>} - Array of lines
270
- */
271
- async function readLines(filePath) {
272
- const content = await readFile(filePath, '')
273
- return content.split('\n')
274
- }
275
-
276
- /**
277
- * Write lines to file
278
- *
279
- * @param {string} filePath - Path to file
280
- * @param {Array<string>} lines - Array of lines
281
- * @returns {Promise<void>}
282
- */
283
- async function writeLines(filePath, lines) {
284
- const content = lines.join('\n')
285
- await writeFile(filePath, content)
286
- }
287
-
288
- /**
289
- * Get file extension
290
- *
291
- * @param {string} filePath - Path to file
292
- * @returns {string} - File extension (e.g., '.md')
293
- */
294
- function getFileExtension(filePath) {
295
- return path.extname(filePath)
296
- }
297
-
298
- /**
299
- * Get filename without extension
300
- *
301
- * @param {string} filePath - Path to file
302
- * @returns {string} - Filename without extension
303
- */
304
- function getFileNameWithoutExtension(filePath) {
305
- return path.basename(filePath, path.extname(filePath))
306
- }
307
-
308
- module.exports = {
309
- readJson,
310
- writeJson,
311
- readFile,
312
- writeFile,
313
- appendToFile,
314
- prependToFile,
315
- fileExists,
316
- dirExists,
317
- ensureDir,
318
- deleteFile,
319
- deleteDir,
320
- listFiles,
321
- getFileSize,
322
- getFileModifiedTime,
323
- copyFile,
324
- moveFile,
325
- readLines,
326
- writeLines,
327
- getFileExtension,
328
- getFileNameWithoutExtension,
329
- }