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
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Intelligent Context Filtering System
3
+ *
4
+ * Reduces context window usage by 70-90% by loading only
5
+ * relevant files for each specialized agent
6
+ *
7
+ * @version 1.0.0
8
+ */
9
+
10
+ import fs from 'fs/promises'
11
+ import path from 'path'
12
+ import { glob } from 'glob'
13
+ import log from '../utils/logger'
14
+
15
+ interface Agent {
16
+ name: string
17
+ [key: string]: unknown
18
+ }
19
+
20
+ interface Task {
21
+ [key: string]: unknown
22
+ }
23
+
24
+ interface FullContext {
25
+ estimatedFiles?: string[]
26
+ fileCount?: number
27
+ [key: string]: unknown
28
+ }
29
+
30
+ interface Patterns {
31
+ include: string[]
32
+ exclude: string[]
33
+ realExtensions?: Record<string, number>
34
+ projectStructure?: string[]
35
+ configFiles?: string[]
36
+ detectedTech?: Record<string, unknown>
37
+ }
38
+
39
+ interface Metrics {
40
+ originalFiles: number
41
+ filteredFiles: number
42
+ reductionPercent: number
43
+ processingTime: number
44
+ effectiveness: string
45
+ }
46
+
47
+ interface FilterResult {
48
+ files: string[]
49
+ patterns: {
50
+ preEstimated?: boolean
51
+ detectedTech?: Record<string, unknown>
52
+ projectStructure?: string[]
53
+ agentic?: boolean
54
+ }
55
+ metrics: Metrics
56
+ agent: string
57
+ filtered: boolean
58
+ }
59
+
60
+ class ContextFilter {
61
+ fileCache: Map<string, unknown>
62
+
63
+ constructor() {
64
+ // Cache for file analysis
65
+ this.fileCache = new Map()
66
+ // NO HARDCODED PATTERNS - Everything is agentic
67
+ // Claude decides what files are needed based on analysis
68
+ }
69
+
70
+ /**
71
+ * Main entry point - filters context based on agent and task
72
+ * IMPROVED: Supports pre-estimated files for lazy loading
73
+ */
74
+ async filterForAgent(
75
+ agent: Agent,
76
+ task: Task,
77
+ projectPath: string,
78
+ fullContext: FullContext = {}
79
+ ): Promise<FilterResult> {
80
+ const startTime = Date.now()
81
+
82
+ // If files were pre-estimated (lazy loading), use them
83
+ if (fullContext.estimatedFiles && fullContext.estimatedFiles.length > 0) {
84
+ const filteredFiles = fullContext.estimatedFiles
85
+
86
+ const metrics = this.calculateMetrics(fullContext.fileCount || filteredFiles.length, filteredFiles.length, startTime)
87
+
88
+ return {
89
+ files: filteredFiles,
90
+ patterns: { preEstimated: true },
91
+ metrics,
92
+ agent: agent.name,
93
+ filtered: true,
94
+ }
95
+ }
96
+
97
+ // Fallback to traditional filtering if no pre-estimation
98
+ // Determine what files this agent needs
99
+ const relevantPatterns = await this.determineRelevantPatterns(agent, task, projectPath)
100
+
101
+ // Load only relevant files
102
+ const filteredFiles = await this.loadRelevantFiles(projectPath, relevantPatterns)
103
+
104
+ // Calculate reduction metrics
105
+ const metrics = this.calculateMetrics(
106
+ fullContext.fileCount || 1000, // estimate if not provided
107
+ filteredFiles.length,
108
+ startTime
109
+ )
110
+
111
+ return {
112
+ files: filteredFiles,
113
+ patterns: {
114
+ detectedTech: relevantPatterns.detectedTech,
115
+ projectStructure: relevantPatterns.projectStructure,
116
+ agentic: true, // Flag indicating this was agentic, not hardcoded
117
+ },
118
+ metrics,
119
+ agent: agent.name,
120
+ filtered: true,
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Determine which patterns to use based on agent and task
126
+ *
127
+ * 100% AGENTIC: Uses analyzer for I/O, no hardcoded tech detection.
128
+ * Claude decides what files matter based on actual project analysis.
129
+ */
130
+ async determineRelevantPatterns(_agent: Agent, _task: Task, projectPath: string): Promise<Patterns> {
131
+ const { default: analyzer } = await import('../domain/analyzer')
132
+ analyzer.init(projectPath)
133
+
134
+ // Get REAL file extensions from project (not assumed)
135
+ const realExtensions = await analyzer.getFileExtensions()
136
+
137
+ // Get REAL directory structure (not assumed)
138
+ const projectStructure = await analyzer.listDirectories()
139
+
140
+ // Get config files that exist (not hardcoded list)
141
+ const configFiles = await analyzer.listConfigFiles()
142
+
143
+ // Build patterns from ACTUAL project data
144
+ const patterns: Patterns = {
145
+ include: [],
146
+ exclude: ['node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt', 'target', 'vendor'],
147
+ realExtensions, // Actual extensions found in project
148
+ projectStructure, // Actual directories
149
+ configFiles, // Actual config files
150
+ }
151
+
152
+ return patterns
153
+ }
154
+
155
+ /**
156
+ * Detect actual project structure (no assumptions)
157
+ */
158
+ async detectProjectStructure(projectPath: string): Promise<string[]> {
159
+ try {
160
+ const entries = await fs.readdir(projectPath, { withFileTypes: true })
161
+ const directories = entries.filter((e) => e.isDirectory() && !e.name.startsWith('.')).map((e) => e.name)
162
+ return directories
163
+ } catch {
164
+ return []
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Detect technologies used in the project
170
+ *
171
+ * 100% AGENTIC: Uses analyzer for raw data.
172
+ * No categorization - Claude decides what's relevant.
173
+ */
174
+ async detectProjectTechnologies(
175
+ projectPath: string
176
+ ): Promise<{ extensions: Record<string, number>; directories: string[]; configFiles: string[] }> {
177
+ try {
178
+ const { default: analyzer } = await import('../domain/analyzer')
179
+ analyzer.init(projectPath)
180
+
181
+ // Return raw data for Claude to analyze
182
+ return {
183
+ extensions: await analyzer.getFileExtensions(),
184
+ directories: await analyzer.listDirectories(),
185
+ configFiles: await analyzer.listConfigFiles(),
186
+ }
187
+ } catch (error) {
188
+ log.error('Error detecting project data:', (error as Error).message)
189
+ return { extensions: {}, directories: [], configFiles: [] }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Load only relevant files based on patterns
195
+ */
196
+ async loadRelevantFiles(projectPath: string, patterns: Patterns): Promise<string[]> {
197
+ const files: string[] = []
198
+
199
+ try {
200
+ // Build glob patterns
201
+ const globPatterns = this.buildGlobPatterns(patterns)
202
+
203
+ // Execute glob searches
204
+ for (const pattern of globPatterns) {
205
+ const matches = await glob(pattern, {
206
+ cwd: projectPath,
207
+ ignore: patterns.exclude.map((ex) => `**/${ex}/**`),
208
+ nodir: true,
209
+ follow: false,
210
+ })
211
+
212
+ // Ensure matches is always an array (glob v10+ returns array, but be defensive)
213
+ if (Array.isArray(matches)) {
214
+ files.push(...matches)
215
+ } else if (matches) {
216
+ // Convert iterable to array if needed
217
+ files.push(...Array.from(matches as Iterable<string>))
218
+ }
219
+ }
220
+
221
+ // Remove duplicates and sort
222
+ const uniqueFiles = [...new Set(files)].sort()
223
+
224
+ // Limit to reasonable number
225
+ const maxFiles = 300
226
+ if (uniqueFiles.length > maxFiles) {
227
+ log.debug(`Limiting context to ${maxFiles} files`)
228
+ return uniqueFiles.slice(0, maxFiles)
229
+ }
230
+
231
+ // Expand context with related files
232
+ const expandedFiles = await this.expandContext(uniqueFiles, projectPath)
233
+
234
+ return expandedFiles.slice(0, maxFiles)
235
+ } catch (error) {
236
+ log.error('Error loading files:', (error as Error).message)
237
+ return []
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Build glob patterns from pattern configuration
243
+ *
244
+ * 100% AGENTIC: Uses REAL extensions from project, not hardcoded mapping.
245
+ * No language→extension assumptions.
246
+ */
247
+ buildGlobPatterns(patterns: Patterns): string[] {
248
+ const globs: string[] = []
249
+
250
+ // Use REAL extensions found in project (no hardcoded mapping)
251
+ if (patterns.realExtensions && Object.keys(patterns.realExtensions).length > 0) {
252
+ // Get extensions that actually exist in this project
253
+ const extensions = Object.keys(patterns.realExtensions)
254
+ .filter((ext) => ext.startsWith('.')) // Only valid extensions
255
+ .slice(0, 20) // Limit to top 20 most common
256
+
257
+ if (extensions.length > 0) {
258
+ globs.push(`**/*{${extensions.join(',')}}`)
259
+ }
260
+ }
261
+
262
+ // Use REAL project structure (no assumptions)
263
+ if (patterns.projectStructure && patterns.projectStructure.length > 0) {
264
+ patterns.projectStructure.forEach((dir) => {
265
+ // Exclude universal noise directories
266
+ if (!patterns.exclude.includes(dir)) {
267
+ globs.push(`${dir}/**/*`)
268
+ }
269
+ })
270
+ }
271
+
272
+ // Include REAL config files that exist (not hardcoded list)
273
+ if (patterns.configFiles && patterns.configFiles.length > 0) {
274
+ patterns.configFiles.forEach((file) => {
275
+ globs.push(file)
276
+ })
277
+ }
278
+
279
+ // Fallback: if no patterns detected, include all source-like files
280
+ if (globs.length === 0) {
281
+ globs.push('**/*')
282
+ }
283
+
284
+ return globs
285
+ }
286
+
287
+ /**
288
+ * Calculate metrics for context reduction
289
+ */
290
+ calculateMetrics(originalCount: number, filteredCount: number, startTime: number): Metrics {
291
+ const reduction = originalCount > 0 ? Math.round(((originalCount - filteredCount) / originalCount) * 100) : 0
292
+
293
+ return {
294
+ originalFiles: originalCount,
295
+ filteredFiles: filteredCount,
296
+ reductionPercent: reduction,
297
+ processingTime: Date.now() - startTime,
298
+ effectiveness: reduction > 70 ? 'high' : reduction > 40 ? 'medium' : 'low',
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Check if file exists
304
+ */
305
+ async fileExists(filePath: string): Promise<boolean> {
306
+ try {
307
+ await fs.access(filePath)
308
+ return true
309
+ } catch {
310
+ return false
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Expand context with related files (tests, styles, etc.)
316
+ */
317
+ async expandContext(files: string[], _projectPath?: string): Promise<string[]> {
318
+ const expanded = new Set(files)
319
+
320
+ for (const file of files) {
321
+ const ext = path.extname(file)
322
+ const basename = path.basename(file, ext)
323
+ const dirname = path.dirname(file)
324
+
325
+ // 1. Look for test files
326
+ const testPatterns = [
327
+ path.join(dirname, `${basename}.test${ext}`),
328
+ path.join(dirname, `${basename}.spec${ext}`),
329
+ path.join(dirname, '__tests__', `${basename}.test${ext}`),
330
+ path.join(dirname, 'tests', `${basename}.test${ext}`),
331
+ ]
332
+
333
+ // 2. Look for style files (for UI components)
334
+ const stylePatterns = [
335
+ path.join(dirname, `${basename}.css`),
336
+ path.join(dirname, `${basename}.scss`),
337
+ path.join(dirname, `${basename}.module.css`),
338
+ path.join(dirname, `${basename}.module.scss`),
339
+ ]
340
+
341
+ // Check if these related files exist
342
+ const potentialFiles = [...testPatterns, ...stylePatterns]
343
+
344
+ for (const potential of potentialFiles) {
345
+ if (!expanded.has(potential) && (await this.fileExists(potential))) {
346
+ expanded.add(potential)
347
+ }
348
+ }
349
+ }
350
+
351
+ return Array.from(expanded).sort()
352
+ }
353
+
354
+ /**
355
+ * Get filter statistics
356
+ */
357
+ getStatistics(): { cachedFiles: number; agentic: boolean } {
358
+ return {
359
+ cachedFiles: this.fileCache.size,
360
+ agentic: true, // All filtering is now agentic, no hardcoded patterns
361
+ }
362
+ }
363
+ }
364
+
365
+ export default ContextFilter
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Ground Truth Verification
3
+ * Verifies actual state before critical operations
4
+ *
5
+ * OPTIMIZATION (P1.3): Anti-Hallucination Pattern
6
+ * - Reads actual files before assuming state
7
+ * - Compares expected vs actual state
8
+ * - Provides specific warnings for mismatches
9
+ * - Logs verification results for debugging
10
+ *
11
+ * Source: Devin, Cursor, Augment Code patterns
12
+ */
13
+
14
+ import type { Context, VerificationResult } from './types'
15
+ import { verifiers } from './verifiers'
16
+ import { formatWarnings } from './utils'
17
+
18
+ export type { Context, VerificationResult, Verifier } from './types'
19
+ export { verifiers } from './verifiers'
20
+ export { formatDuration, escapeRegex, formatWarnings } from './utils'
21
+
22
+ /**
23
+ * Verify ground truth before command execution
24
+ */
25
+ async function verify(commandName: string, context: Context, state: unknown): Promise<VerificationResult> {
26
+ const verifier = verifiers[commandName]
27
+
28
+ if (!verifier) {
29
+ // No specific verification needed
30
+ return {
31
+ verified: true,
32
+ actual: {},
33
+ warnings: [],
34
+ recommendations: [],
35
+ }
36
+ }
37
+
38
+ try {
39
+ return await verifier(context, state)
40
+ } catch (error) {
41
+ return {
42
+ verified: false,
43
+ actual: {},
44
+ warnings: [`Verification error: ${(error as Error).message}`],
45
+ recommendations: ['Check file permissions and project configuration'],
46
+ }
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Prepare command by verifying ground truth
52
+ * Returns enhanced context with verification results
53
+ */
54
+ async function prepareCommand(commandName: string, context: Context, state: unknown) {
55
+ const verification = await verify(commandName, context, state)
56
+
57
+ return {
58
+ ...context,
59
+ groundTruth: {
60
+ ...verification,
61
+ verifiedAt: new Date().toISOString(),
62
+ command: commandName,
63
+ },
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Check if command requires ground truth verification
69
+ */
70
+ function requiresVerification(commandName: string): boolean {
71
+ // ANTI-HALLUCINATION: Expanded verification for more commands
72
+ return ['done', 'ship', 'feature', 'spec', 'now', 'init', 'sync', 'analyze'].includes(commandName)
73
+ }
74
+
75
+ export { verify, prepareCommand, requiresVerification }
76
+ export default { verify, prepareCommand, requiresVerification, formatWarnings, verifiers }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Ground Truth Types
3
+ */
4
+
5
+ export interface Context {
6
+ projectPath: string
7
+ projectId?: string | null
8
+ paths: {
9
+ now: string
10
+ next: string
11
+ metrics: string
12
+ shipped: string
13
+ roadmap: string
14
+ specs: string
15
+ [key: string]: string
16
+ }
17
+ params: {
18
+ feature?: string
19
+ description?: string
20
+ task?: string
21
+ name?: string
22
+ [key: string]: unknown
23
+ }
24
+ }
25
+
26
+ export interface VerificationResult {
27
+ verified: boolean
28
+ actual: Record<string, unknown>
29
+ warnings: string[]
30
+ recommendations: string[]
31
+ }
32
+
33
+ export type Verifier = (context: Context, state: unknown) => Promise<VerificationResult>
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Ground Truth Utilities
3
+ */
4
+
5
+ import type { VerificationResult } from './types'
6
+
7
+ /**
8
+ * Format duration from milliseconds to human-readable string
9
+ */
10
+ export function formatDuration(ms: number): string {
11
+ const hours = Math.floor(ms / (1000 * 60 * 60))
12
+ const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60))
13
+
14
+ if (hours > 0) {
15
+ return `${hours}h ${minutes}m`
16
+ }
17
+ return `${minutes}m`
18
+ }
19
+
20
+ /**
21
+ * Escape special regex characters in a string
22
+ */
23
+ export function escapeRegex(string: string): string {
24
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
25
+ }
26
+
27
+ /**
28
+ * Format verification warnings for display
29
+ */
30
+ export function formatWarnings(result: VerificationResult): string | null {
31
+ if (result.verified || result.warnings.length === 0) {
32
+ return null
33
+ }
34
+
35
+ let output = '⚠️ Ground Truth Warnings:\n'
36
+ result.warnings.forEach((w) => {
37
+ output += ` • ${w}\n`
38
+ })
39
+
40
+ if (result.recommendations.length > 0) {
41
+ output += '\nRecommendations:\n'
42
+ result.recommendations.forEach((r) => {
43
+ output += ` → ${r}\n`
44
+ })
45
+ }
46
+
47
+ return output
48
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Analyze Command Verifier
3
+ * Verify analysis can proceed
4
+ */
5
+
6
+ import fs from 'fs/promises'
7
+ import path from 'path'
8
+ import type { Context, VerificationResult } from '../types'
9
+
10
+ export async function verifyAnalyze(context: Context): Promise<VerificationResult> {
11
+ const warnings: string[] = []
12
+ const recommendations: string[] = []
13
+ const actual: Record<string, unknown> = {}
14
+
15
+ // 1. Check if project has recognizable structure
16
+ const files = ['package.json', 'Cargo.toml', 'go.mod', 'requirements.txt', 'Gemfile', 'pom.xml']
17
+ actual.detectedFiles = []
18
+
19
+ for (const file of files) {
20
+ try {
21
+ await fs.access(path.join(context.projectPath, file))
22
+ ;(actual.detectedFiles as string[]).push(file)
23
+ } catch {
24
+ // File doesn't exist
25
+ }
26
+ }
27
+
28
+ if ((actual.detectedFiles as string[]).length === 0) {
29
+ warnings.push('No recognizable project files detected')
30
+ recommendations.push('Analysis may be limited without package.json or similar')
31
+ }
32
+
33
+ // 2. Check for source directories
34
+ const srcDirs = ['src', 'lib', 'app', 'core', 'components']
35
+ actual.detectedSrcDirs = []
36
+
37
+ for (const dir of srcDirs) {
38
+ try {
39
+ const stat = await fs.stat(path.join(context.projectPath, dir))
40
+ if (stat.isDirectory()) {
41
+ ;(actual.detectedSrcDirs as string[]).push(dir)
42
+ }
43
+ } catch {
44
+ // Directory doesn't exist
45
+ }
46
+ }
47
+
48
+ return {
49
+ verified: true, // Analysis can always proceed, even with warnings
50
+ actual,
51
+ warnings,
52
+ recommendations,
53
+ }
54
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Done Command Verifier
3
+ * Verify task is actually complete-able
4
+ */
5
+
6
+ import fs from 'fs/promises'
7
+ import path from 'path'
8
+ import type { Context, VerificationResult } from '../types'
9
+ import { formatDuration } from '../utils'
10
+
11
+ export async function verifyDone(context: Context): Promise<VerificationResult> {
12
+ const warnings: string[] = []
13
+ const recommendations: string[] = []
14
+ const actual: Record<string, unknown> = {}
15
+
16
+ // 1. Verify now.md exists and has real content
17
+ const nowPath = context.paths.now
18
+ try {
19
+ const nowContent = await fs.readFile(nowPath, 'utf-8')
20
+ actual.nowExists = true
21
+ actual.nowContent = nowContent.trim()
22
+ actual.nowLength = nowContent.length
23
+
24
+ // Check for placeholder content
25
+ if (nowContent.includes('No current task') || nowContent.match(/^#\s*NOW\s*$/m)) {
26
+ warnings.push('now.md appears to be empty or placeholder')
27
+ recommendations.push('Start a task first with /p:now "task"')
28
+ }
29
+
30
+ // Check for task metadata (started time)
31
+ const startedMatch = nowContent.match(/Started:\s*(.+)/i)
32
+ if (startedMatch) {
33
+ actual.startedAt = startedMatch[1]
34
+ // Calculate duration
35
+ const startTime = new Date(startedMatch[1])
36
+ if (!isNaN(startTime.getTime())) {
37
+ actual.durationMs = Date.now() - startTime.getTime()
38
+ actual.durationFormatted = formatDuration(actual.durationMs as number)
39
+ }
40
+ }
41
+ } catch {
42
+ actual.nowExists = false
43
+ warnings.push('now.md does not exist')
44
+ recommendations.push('Create a task with /p:now "task"')
45
+ }
46
+
47
+ // 2. Verify next.md for auto-start
48
+ const nextPath = context.paths.next
49
+ try {
50
+ const nextContent = await fs.readFile(nextPath, 'utf-8')
51
+ actual.nextExists = true
52
+ const tasks = nextContent.match(/- \[ \]/g) || []
53
+ actual.pendingTasks = tasks.length
54
+ } catch {
55
+ actual.nextExists = false
56
+ actual.pendingTasks = 0
57
+ }
58
+
59
+ // 3. Verify metrics.md is writable
60
+ const metricsPath = context.paths.metrics
61
+ try {
62
+ await fs.access(path.dirname(metricsPath), fs.constants.W_OK)
63
+ actual.metricsWritable = true
64
+ } catch {
65
+ actual.metricsWritable = false
66
+ warnings.push('Cannot write to metrics directory')
67
+ }
68
+
69
+ return {
70
+ verified: warnings.length === 0,
71
+ actual,
72
+ warnings,
73
+ recommendations,
74
+ }
75
+ }