crewly 1.0.7 → 1.0.10

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 (468) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +50 -3
  3. package/config/constants.ts +59 -0
  4. package/config/index.ts +2 -0
  5. package/config/roles/architect/prompt.md +17 -5
  6. package/config/roles/backend-developer/prompt.md +35 -5
  7. package/config/roles/designer/prompt.md +17 -5
  8. package/config/roles/developer/prompt.md +43 -9
  9. package/config/roles/frontend-developer/prompt.md +35 -5
  10. package/config/roles/fullstack-dev/prompt.md +35 -5
  11. package/config/roles/generalist/prompt.md +18 -5
  12. package/config/roles/orchestrator/prompt.md +126 -3
  13. package/config/roles/product-manager/prompt.md +24 -9
  14. package/config/roles/qa/prompt.md +35 -5
  15. package/config/roles/qa-engineer/prompt.md +35 -5
  16. package/config/roles/sales/prompt.md +17 -5
  17. package/config/roles/support/prompt.md +17 -5
  18. package/config/roles/tpm/prompt.md +17 -5
  19. package/config/skills/agent/_common/lib.sh +4 -0
  20. package/config/skills/agent/core/accept-task/execute.sh +21 -0
  21. package/config/skills/agent/core/accept-task/instructions.md +20 -0
  22. package/config/skills/agent/core/accept-task/skill.json +20 -0
  23. package/config/skills/agent/core/block-task/execute.sh +26 -0
  24. package/config/skills/agent/core/block-task/instructions.md +22 -0
  25. package/config/skills/agent/core/block-task/skill.json +20 -0
  26. package/config/skills/agent/core/check-quality-gates/execute.sh +20 -0
  27. package/config/skills/agent/core/check-quality-gates/instructions.md +23 -0
  28. package/config/skills/agent/core/check-quality-gates/skill.json +20 -0
  29. package/config/skills/agent/core/complete-task/execute.sh +29 -0
  30. package/config/skills/agent/core/complete-task/instructions.md +53 -0
  31. package/config/skills/agent/core/complete-task/skill.json +20 -0
  32. package/config/skills/agent/core/get-my-context/execute.sh +23 -0
  33. package/config/skills/agent/core/get-my-context/instructions.md +21 -0
  34. package/config/skills/agent/core/get-my-context/skill.json +20 -0
  35. package/config/skills/agent/core/get-sops/execute.sh +24 -0
  36. package/config/skills/agent/core/get-sops/instructions.md +21 -0
  37. package/config/skills/agent/core/get-sops/skill.json +20 -0
  38. package/config/skills/agent/core/get-team-status/execute.sh +8 -0
  39. package/config/skills/agent/core/get-team-status/instructions.md +17 -0
  40. package/config/skills/agent/core/get-team-status/skill.json +20 -0
  41. package/config/skills/agent/core/heartbeat/execute.sh +22 -0
  42. package/config/skills/agent/core/heartbeat/instructions.md +23 -0
  43. package/config/skills/agent/core/heartbeat/skill.json +20 -0
  44. package/config/skills/agent/core/manage-knowledge/execute.sh +60 -0
  45. package/config/skills/agent/core/manage-knowledge/instructions.md +46 -0
  46. package/config/skills/agent/core/marketplace-search/execute.sh +77 -0
  47. package/config/skills/agent/core/marketplace-search/instructions.md +59 -0
  48. package/config/skills/agent/core/marketplace-search/skill.json +20 -0
  49. package/config/skills/agent/core/query-knowledge/execute.sh +30 -0
  50. package/config/skills/agent/core/query-knowledge/instructions.md +47 -0
  51. package/config/skills/agent/core/query-knowledge/skill.json +20 -0
  52. package/config/skills/agent/core/read-task/execute.sh +15 -0
  53. package/config/skills/agent/core/read-task/instructions.md +19 -0
  54. package/config/skills/agent/core/read-task/skill.json +20 -0
  55. package/config/skills/agent/core/recall/execute.sh +24 -0
  56. package/config/skills/agent/core/recall/instructions.md +23 -0
  57. package/config/skills/agent/core/recall/skill.json +20 -0
  58. package/config/skills/agent/core/record-learning/execute.sh +29 -0
  59. package/config/skills/agent/core/record-learning/instructions.md +24 -0
  60. package/config/skills/agent/core/record-learning/skill.json +20 -0
  61. package/config/skills/agent/core/register-self/execute.sh +28 -0
  62. package/config/skills/agent/core/register-self/instructions.md +18 -0
  63. package/config/skills/agent/core/register-self/skill.json +20 -0
  64. package/config/skills/agent/core/remember/execute.sh +29 -0
  65. package/config/skills/agent/core/remember/instructions.md +24 -0
  66. package/config/skills/agent/core/remember/skill.json +20 -0
  67. package/config/skills/agent/core/report-progress/execute.sh +28 -0
  68. package/config/skills/agent/core/report-progress/instructions.md +25 -0
  69. package/config/skills/agent/core/report-progress/skill.json +20 -0
  70. package/config/skills/agent/core/report-status/execute.sh +35 -0
  71. package/config/skills/agent/core/report-status/instructions.md +36 -0
  72. package/config/skills/agent/core/report-status/skill.json +20 -0
  73. package/config/skills/agent/core/send-chat-response/execute.sh +26 -0
  74. package/config/skills/agent/core/send-chat-response/instructions.md +22 -0
  75. package/config/skills/agent/core/send-chat-response/skill.json +20 -0
  76. package/config/skills/agent/core/send-message/execute.sh +17 -0
  77. package/config/skills/agent/core/send-message/instructions.md +20 -0
  78. package/config/skills/agent/core/send-message/skill.json +20 -0
  79. package/config/skills/orchestrator/complete-task/execute.sh +1 -0
  80. package/config/skills/orchestrator/delegate-task/execute.sh +1 -1
  81. package/config/skills/registry.json +850 -0
  82. package/config/templates/agent-claude-md.md +16 -0
  83. package/config/templates/research-team.json +22 -0
  84. package/config/templates/startup-team.json +22 -0
  85. package/config/templates/web-dev-team.json +22 -0
  86. package/dist/backend/backend/src/constants.d.ts +127 -1
  87. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  88. package/dist/backend/backend/src/constants.js +119 -8
  89. package/dist/backend/backend/src/constants.js.map +1 -1
  90. package/dist/backend/backend/src/controllers/marketplace/index.d.ts +1 -1
  91. package/dist/backend/backend/src/controllers/marketplace/index.d.ts.map +1 -1
  92. package/dist/backend/backend/src/controllers/marketplace/index.js +1 -1
  93. package/dist/backend/backend/src/controllers/marketplace/index.js.map +1 -1
  94. package/dist/backend/backend/src/controllers/marketplace/marketplace.controller.d.ts +64 -8
  95. package/dist/backend/backend/src/controllers/marketplace/marketplace.controller.d.ts.map +1 -1
  96. package/dist/backend/backend/src/controllers/marketplace/marketplace.controller.js +194 -96
  97. package/dist/backend/backend/src/controllers/marketplace/marketplace.controller.js.map +1 -1
  98. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.d.ts.map +1 -1
  99. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.js +7 -2
  100. package/dist/backend/backend/src/controllers/marketplace/marketplace.routes.js.map +1 -1
  101. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts +7 -0
  102. package/dist/backend/backend/src/controllers/task-management/task-management.controller.d.ts.map +1 -1
  103. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js +174 -4
  104. package/dist/backend/backend/src/controllers/task-management/task-management.controller.js.map +1 -1
  105. package/dist/backend/backend/src/index.d.ts.map +1 -1
  106. package/dist/backend/backend/src/index.js +34 -0
  107. package/dist/backend/backend/src/index.js.map +1 -1
  108. package/dist/backend/backend/src/routes/modules/task-management.routes.d.ts.map +1 -1
  109. package/dist/backend/backend/src/routes/modules/task-management.routes.js +2 -0
  110. package/dist/backend/backend/src/routes/modules/task-management.routes.js.map +1 -1
  111. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +8 -0
  112. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  113. package/dist/backend/backend/src/services/agent/agent-registration.service.js +89 -17
  114. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  115. package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts +265 -0
  116. package/dist/backend/backend/src/services/agent/context-window-monitor.service.d.ts.map +1 -0
  117. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js +811 -0
  118. package/dist/backend/backend/src/services/agent/context-window-monitor.service.js.map +1 -0
  119. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  120. package/dist/backend/backend/src/services/agent/idle-detection.service.js +11 -4
  121. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  122. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts +11 -0
  123. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.d.ts.map +1 -1
  124. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js +18 -0
  125. package/dist/backend/backend/src/services/agent/pty-activity-tracker.service.js.map +1 -1
  126. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts +12 -0
  127. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.d.ts.map +1 -1
  128. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js +51 -1
  129. package/dist/backend/backend/src/services/agent/runtime-exit-monitor.service.js.map +1 -1
  130. package/dist/backend/backend/src/services/continuation/patterns/waiting-patterns.d.ts +6 -0
  131. package/dist/backend/backend/src/services/continuation/patterns/waiting-patterns.d.ts.map +1 -1
  132. package/dist/backend/backend/src/services/continuation/patterns/waiting-patterns.js +10 -0
  133. package/dist/backend/backend/src/services/continuation/patterns/waiting-patterns.js.map +1 -1
  134. package/dist/backend/backend/src/services/index.d.ts +3 -0
  135. package/dist/backend/backend/src/services/index.d.ts.map +1 -1
  136. package/dist/backend/backend/src/services/index.js +5 -0
  137. package/dist/backend/backend/src/services/index.js.map +1 -1
  138. package/dist/backend/backend/src/services/marketplace/index.d.ts +1 -0
  139. package/dist/backend/backend/src/services/marketplace/index.d.ts.map +1 -1
  140. package/dist/backend/backend/src/services/marketplace/index.js +2 -0
  141. package/dist/backend/backend/src/services/marketplace/index.js.map +1 -1
  142. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.d.ts +10 -9
  143. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.d.ts.map +1 -1
  144. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.js +262 -73
  145. package/dist/backend/backend/src/services/marketplace/marketplace-installer.service.js.map +1 -1
  146. package/dist/backend/backend/src/services/marketplace/marketplace-submission.service.d.ts +71 -0
  147. package/dist/backend/backend/src/services/marketplace/marketplace-submission.service.d.ts.map +1 -0
  148. package/dist/backend/backend/src/services/marketplace/marketplace-submission.service.js +339 -0
  149. package/dist/backend/backend/src/services/marketplace/marketplace-submission.service.js.map +1 -0
  150. package/dist/backend/backend/src/services/marketplace/marketplace.service.d.ts +4 -15
  151. package/dist/backend/backend/src/services/marketplace/marketplace.service.d.ts.map +1 -1
  152. package/dist/backend/backend/src/services/marketplace/marketplace.service.js +172 -27
  153. package/dist/backend/backend/src/services/marketplace/marketplace.service.js.map +1 -1
  154. package/dist/backend/backend/src/services/mcp-client.d.ts +233 -0
  155. package/dist/backend/backend/src/services/mcp-client.d.ts.map +1 -0
  156. package/dist/backend/backend/src/services/mcp-client.js +297 -0
  157. package/dist/backend/backend/src/services/mcp-client.js.map +1 -0
  158. package/dist/backend/backend/src/services/mcp-server.d.ts +167 -0
  159. package/dist/backend/backend/src/services/mcp-server.d.ts.map +1 -0
  160. package/dist/backend/backend/src/services/mcp-server.js +586 -0
  161. package/dist/backend/backend/src/services/mcp-server.js.map +1 -0
  162. package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts +32 -2
  163. package/dist/backend/backend/src/services/messaging/message-queue.service.d.ts.map +1 -1
  164. package/dist/backend/backend/src/services/messaging/message-queue.service.js +85 -4
  165. package/dist/backend/backend/src/services/messaging/message-queue.service.js.map +1 -1
  166. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts +10 -0
  167. package/dist/backend/backend/src/services/messaging/queue-processor.service.d.ts.map +1 -1
  168. package/dist/backend/backend/src/services/messaging/queue-processor.service.js +129 -41
  169. package/dist/backend/backend/src/services/messaging/queue-processor.service.js.map +1 -1
  170. package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.d.ts.map +1 -1
  171. package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.js +31 -0
  172. package/dist/backend/backend/src/services/orchestrator/orchestrator-status.service.js.map +1 -1
  173. package/dist/backend/backend/src/services/quality/index.d.ts +1 -0
  174. package/dist/backend/backend/src/services/quality/index.d.ts.map +1 -1
  175. package/dist/backend/backend/src/services/quality/index.js +1 -0
  176. package/dist/backend/backend/src/services/quality/index.js.map +1 -1
  177. package/dist/backend/backend/src/services/quality/task-output-validator.service.d.ts +84 -0
  178. package/dist/backend/backend/src/services/quality/task-output-validator.service.d.ts.map +1 -0
  179. package/dist/backend/backend/src/services/quality/task-output-validator.service.js +163 -0
  180. package/dist/backend/backend/src/services/quality/task-output-validator.service.js.map +1 -0
  181. package/dist/backend/backend/src/services/runtime-adapter.d.ts +254 -0
  182. package/dist/backend/backend/src/services/runtime-adapter.d.ts.map +1 -0
  183. package/dist/backend/backend/src/services/runtime-adapter.js +201 -0
  184. package/dist/backend/backend/src/services/runtime-adapter.js.map +1 -0
  185. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts +19 -0
  186. package/dist/backend/backend/src/services/session/pty/pty-session-backend.d.ts.map +1 -1
  187. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js +31 -2
  188. package/dist/backend/backend/src/services/session/pty/pty-session-backend.js.map +1 -1
  189. package/dist/backend/backend/src/services/session/session-backend.interface.d.ts +14 -0
  190. package/dist/backend/backend/src/services/session/session-backend.interface.d.ts.map +1 -1
  191. package/dist/backend/backend/src/services/session/session-backend.interface.js.map +1 -1
  192. package/dist/backend/backend/src/services/session/session-command-helper.d.ts +9 -0
  193. package/dist/backend/backend/src/services/session/session-command-helper.d.ts.map +1 -1
  194. package/dist/backend/backend/src/services/session/session-command-helper.js +24 -1
  195. package/dist/backend/backend/src/services/session/session-command-helper.js.map +1 -1
  196. package/dist/backend/backend/src/services/skill/skill-catalog.service.d.ts +7 -13
  197. package/dist/backend/backend/src/services/skill/skill-catalog.service.d.ts.map +1 -1
  198. package/dist/backend/backend/src/services/skill/skill-catalog.service.js +38 -47
  199. package/dist/backend/backend/src/services/skill/skill-catalog.service.js.map +1 -1
  200. package/dist/backend/backend/src/services/skill/skill.service.d.ts +3 -0
  201. package/dist/backend/backend/src/services/skill/skill.service.d.ts.map +1 -1
  202. package/dist/backend/backend/src/services/skill/skill.service.js +17 -0
  203. package/dist/backend/backend/src/services/skill/skill.service.js.map +1 -1
  204. package/dist/backend/backend/src/types/event-bus.types.d.ts +2 -2
  205. package/dist/backend/backend/src/types/event-bus.types.d.ts.map +1 -1
  206. package/dist/backend/backend/src/types/event-bus.types.js +2 -0
  207. package/dist/backend/backend/src/types/event-bus.types.js.map +1 -1
  208. package/dist/backend/backend/src/types/index.d.ts +1 -0
  209. package/dist/backend/backend/src/types/index.d.ts.map +1 -1
  210. package/dist/backend/backend/src/types/index.js +2 -0
  211. package/dist/backend/backend/src/types/index.js.map +1 -1
  212. package/dist/backend/backend/src/types/marketplace.types.d.ts +37 -0
  213. package/dist/backend/backend/src/types/marketplace.types.d.ts.map +1 -1
  214. package/dist/backend/backend/src/types/messaging.types.d.ts +9 -6
  215. package/dist/backend/backend/src/types/messaging.types.d.ts.map +1 -1
  216. package/dist/backend/backend/src/types/messaging.types.js +10 -3
  217. package/dist/backend/backend/src/types/messaging.types.js.map +1 -1
  218. package/dist/backend/backend/src/types/task-output.types.d.ts +78 -0
  219. package/dist/backend/backend/src/types/task-output.types.d.ts.map +1 -0
  220. package/dist/backend/backend/src/types/task-output.types.js +27 -0
  221. package/dist/backend/backend/src/types/task-output.types.js.map +1 -0
  222. package/dist/backend/backend/src/websocket/terminal.gateway.d.ts +13 -0
  223. package/dist/backend/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
  224. package/dist/backend/backend/src/websocket/terminal.gateway.js +12 -0
  225. package/dist/backend/backend/src/websocket/terminal.gateway.js.map +1 -1
  226. package/dist/backend/config/constants.d.ts +41 -0
  227. package/dist/backend/config/constants.d.ts.map +1 -1
  228. package/dist/backend/config/constants.js +57 -0
  229. package/dist/backend/config/constants.js.map +1 -1
  230. package/dist/backend/config/index.d.ts +2 -2
  231. package/dist/backend/config/index.d.ts.map +1 -1
  232. package/dist/backend/config/index.js +2 -2
  233. package/dist/backend/config/index.js.map +1 -1
  234. package/dist/cli/backend/src/constants.d.ts +817 -0
  235. package/dist/cli/backend/src/constants.d.ts.map +1 -0
  236. package/dist/cli/backend/src/constants.js +603 -0
  237. package/dist/cli/backend/src/constants.js.map +1 -0
  238. package/dist/cli/backend/src/models/Project.d.ts +18 -0
  239. package/dist/cli/backend/src/models/Project.d.ts.map +1 -0
  240. package/dist/cli/backend/src/models/Project.js +70 -0
  241. package/dist/cli/backend/src/models/Project.js.map +1 -0
  242. package/dist/cli/backend/src/models/ScheduledMessage.d.ts +27 -0
  243. package/dist/cli/backend/src/models/ScheduledMessage.d.ts.map +1 -0
  244. package/dist/cli/backend/src/models/ScheduledMessage.js +50 -0
  245. package/dist/cli/backend/src/models/ScheduledMessage.js.map +1 -0
  246. package/dist/cli/backend/src/models/Team.d.ts +20 -0
  247. package/dist/cli/backend/src/models/Team.d.ts.map +1 -0
  248. package/dist/cli/backend/src/models/Team.js +120 -0
  249. package/dist/cli/backend/src/models/Team.js.map +1 -0
  250. package/dist/cli/backend/src/models/Ticket.d.ts +24 -0
  251. package/dist/cli/backend/src/models/Ticket.d.ts.map +1 -0
  252. package/dist/cli/backend/src/models/Ticket.js +102 -0
  253. package/dist/cli/backend/src/models/Ticket.js.map +1 -0
  254. package/dist/cli/backend/src/models/index.d.ts +5 -0
  255. package/dist/cli/backend/src/models/index.d.ts.map +1 -0
  256. package/dist/cli/backend/src/models/index.js +5 -0
  257. package/dist/cli/backend/src/models/index.js.map +1 -0
  258. package/dist/cli/backend/src/services/continuation/patterns/waiting-patterns.d.ts +40 -0
  259. package/dist/cli/backend/src/services/continuation/patterns/waiting-patterns.d.ts.map +1 -0
  260. package/dist/cli/backend/src/services/continuation/patterns/waiting-patterns.js +74 -0
  261. package/dist/cli/backend/src/services/continuation/patterns/waiting-patterns.js.map +1 -0
  262. package/dist/cli/backend/src/services/core/config.service.d.ts +91 -0
  263. package/dist/cli/backend/src/services/core/config.service.d.ts.map +1 -0
  264. package/dist/cli/backend/src/services/core/config.service.js +246 -0
  265. package/dist/cli/backend/src/services/core/config.service.js.map +1 -0
  266. package/dist/cli/backend/src/services/core/logger.service.d.ts +70 -0
  267. package/dist/cli/backend/src/services/core/logger.service.d.ts.map +1 -0
  268. package/dist/cli/backend/src/services/core/logger.service.js +350 -0
  269. package/dist/cli/backend/src/services/core/logger.service.js.map +1 -0
  270. package/dist/cli/backend/src/services/core/storage.service.d.ts +269 -0
  271. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -0
  272. package/dist/cli/backend/src/services/core/storage.service.js +1406 -0
  273. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -0
  274. package/dist/cli/backend/src/services/core/teams-backup.service.d.ts +92 -0
  275. package/dist/cli/backend/src/services/core/teams-backup.service.d.ts.map +1 -0
  276. package/dist/cli/backend/src/services/core/teams-backup.service.js +120 -0
  277. package/dist/cli/backend/src/services/core/teams-backup.service.js.map +1 -0
  278. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts +125 -0
  279. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts.map +1 -0
  280. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js +247 -0
  281. package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js.map +1 -0
  282. package/dist/cli/backend/src/services/knowledge/knowledge.service.d.ts +153 -0
  283. package/dist/cli/backend/src/services/knowledge/knowledge.service.d.ts.map +1 -0
  284. package/dist/cli/backend/src/services/knowledge/knowledge.service.js +409 -0
  285. package/dist/cli/backend/src/services/knowledge/knowledge.service.js.map +1 -0
  286. package/dist/cli/backend/src/services/mcp-server.d.ts +167 -0
  287. package/dist/cli/backend/src/services/mcp-server.d.ts.map +1 -0
  288. package/dist/cli/backend/src/services/mcp-server.js +586 -0
  289. package/dist/cli/backend/src/services/mcp-server.js.map +1 -0
  290. package/dist/cli/backend/src/services/memory/agent-memory.service.d.ts +259 -0
  291. package/dist/cli/backend/src/services/memory/agent-memory.service.d.ts.map +1 -0
  292. package/dist/cli/backend/src/services/memory/agent-memory.service.js +539 -0
  293. package/dist/cli/backend/src/services/memory/agent-memory.service.js.map +1 -0
  294. package/dist/cli/backend/src/services/memory/memory.service.d.ts +306 -0
  295. package/dist/cli/backend/src/services/memory/memory.service.d.ts.map +1 -0
  296. package/dist/cli/backend/src/services/memory/memory.service.js +517 -0
  297. package/dist/cli/backend/src/services/memory/memory.service.js.map +1 -0
  298. package/dist/cli/backend/src/services/memory/project-memory.service.d.ts +252 -0
  299. package/dist/cli/backend/src/services/memory/project-memory.service.d.ts.map +1 -0
  300. package/dist/cli/backend/src/services/memory/project-memory.service.js +600 -0
  301. package/dist/cli/backend/src/services/memory/project-memory.service.js.map +1 -0
  302. package/dist/cli/backend/src/types/auto-assign.types.d.ts +271 -0
  303. package/dist/cli/backend/src/types/auto-assign.types.d.ts.map +1 -0
  304. package/dist/cli/backend/src/types/auto-assign.types.js +136 -0
  305. package/dist/cli/backend/src/types/auto-assign.types.js.map +1 -0
  306. package/dist/cli/backend/src/types/budget.types.d.ts +217 -0
  307. package/dist/cli/backend/src/types/budget.types.d.ts.map +1 -0
  308. package/dist/cli/backend/src/types/budget.types.js +82 -0
  309. package/dist/cli/backend/src/types/budget.types.js.map +1 -0
  310. package/dist/cli/backend/src/types/chat.types.d.ts +550 -0
  311. package/dist/cli/backend/src/types/chat.types.d.ts.map +1 -0
  312. package/dist/cli/backend/src/types/chat.types.js +743 -0
  313. package/dist/cli/backend/src/types/chat.types.js.map +1 -0
  314. package/dist/cli/backend/src/types/continuation.types.d.ts +237 -0
  315. package/dist/cli/backend/src/types/continuation.types.d.ts.map +1 -0
  316. package/dist/cli/backend/src/types/continuation.types.js +10 -0
  317. package/dist/cli/backend/src/types/continuation.types.js.map +1 -0
  318. package/dist/cli/backend/src/types/index.d.ts +164 -0
  319. package/dist/cli/backend/src/types/index.d.ts.map +1 -0
  320. package/dist/cli/backend/src/types/index.js +25 -0
  321. package/dist/cli/backend/src/types/index.js.map +1 -0
  322. package/dist/cli/backend/src/types/knowledge.types.d.ts +195 -0
  323. package/dist/cli/backend/src/types/knowledge.types.d.ts.map +1 -0
  324. package/dist/cli/backend/src/types/knowledge.types.js +38 -0
  325. package/dist/cli/backend/src/types/knowledge.types.js.map +1 -0
  326. package/dist/cli/backend/src/types/memory.types.d.ts +587 -0
  327. package/dist/cli/backend/src/types/memory.types.d.ts.map +1 -0
  328. package/dist/cli/backend/src/types/memory.types.js +47 -0
  329. package/dist/cli/backend/src/types/memory.types.js.map +1 -0
  330. package/dist/cli/backend/src/types/quality-gate.types.d.ts +171 -0
  331. package/dist/cli/backend/src/types/quality-gate.types.d.ts.map +1 -0
  332. package/dist/cli/backend/src/types/quality-gate.types.js +42 -0
  333. package/dist/cli/backend/src/types/quality-gate.types.js.map +1 -0
  334. package/dist/cli/backend/src/types/role.types.d.ts +260 -0
  335. package/dist/cli/backend/src/types/role.types.d.ts.map +1 -0
  336. package/dist/cli/backend/src/types/role.types.js +238 -0
  337. package/dist/cli/backend/src/types/role.types.js.map +1 -0
  338. package/dist/cli/backend/src/types/scheduler.types.d.ts +254 -0
  339. package/dist/cli/backend/src/types/scheduler.types.d.ts.map +1 -0
  340. package/dist/cli/backend/src/types/scheduler.types.js +32 -0
  341. package/dist/cli/backend/src/types/scheduler.types.js.map +1 -0
  342. package/dist/cli/backend/src/types/settings.types.d.ts +178 -0
  343. package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -0
  344. package/dist/cli/backend/src/types/settings.types.js +206 -0
  345. package/dist/cli/backend/src/types/settings.types.js.map +1 -0
  346. package/dist/cli/backend/src/types/skill.types.d.ts +515 -0
  347. package/dist/cli/backend/src/types/skill.types.d.ts.map +1 -0
  348. package/dist/cli/backend/src/types/skill.types.js +481 -0
  349. package/dist/cli/backend/src/types/skill.types.js.map +1 -0
  350. package/dist/cli/backend/src/types/sop.types.d.ts +224 -0
  351. package/dist/cli/backend/src/types/sop.types.d.ts.map +1 -0
  352. package/dist/cli/backend/src/types/sop.types.js +85 -0
  353. package/dist/cli/backend/src/types/sop.types.js.map +1 -0
  354. package/dist/cli/backend/src/types/task-output.types.d.ts +78 -0
  355. package/dist/cli/backend/src/types/task-output.types.d.ts.map +1 -0
  356. package/dist/cli/backend/src/types/task-output.types.js +27 -0
  357. package/dist/cli/backend/src/types/task-output.types.js.map +1 -0
  358. package/dist/cli/backend/src/utils/file-io.utils.d.ts +102 -0
  359. package/dist/cli/backend/src/utils/file-io.utils.d.ts.map +1 -0
  360. package/dist/cli/backend/src/utils/file-io.utils.js +214 -0
  361. package/dist/cli/backend/src/utils/file-io.utils.js.map +1 -0
  362. package/dist/cli/backend/src/utils/terminal-output.utils.d.ts +54 -0
  363. package/dist/cli/backend/src/utils/terminal-output.utils.d.ts.map +1 -0
  364. package/dist/cli/backend/src/utils/terminal-output.utils.js +97 -0
  365. package/dist/cli/backend/src/utils/terminal-output.utils.js.map +1 -0
  366. package/dist/cli/cli/src/commands/mcp-server.d.ts +38 -0
  367. package/dist/cli/cli/src/commands/mcp-server.d.ts.map +1 -0
  368. package/dist/cli/cli/src/commands/mcp-server.js +49 -0
  369. package/dist/cli/cli/src/commands/mcp-server.js.map +1 -0
  370. package/dist/cli/cli/src/commands/onboard.d.ts +72 -8
  371. package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
  372. package/dist/cli/cli/src/commands/onboard.js +306 -33
  373. package/dist/cli/cli/src/commands/onboard.js.map +1 -1
  374. package/dist/cli/cli/src/commands/publish.d.ts +35 -0
  375. package/dist/cli/cli/src/commands/publish.d.ts.map +1 -0
  376. package/dist/cli/cli/src/commands/publish.js +93 -0
  377. package/dist/cli/cli/src/commands/publish.js.map +1 -0
  378. package/dist/cli/cli/src/commands/seed-marketplace.d.ts +28 -0
  379. package/dist/cli/cli/src/commands/seed-marketplace.d.ts.map +1 -0
  380. package/dist/cli/cli/src/commands/seed-marketplace.js +148 -0
  381. package/dist/cli/cli/src/commands/seed-marketplace.js.map +1 -0
  382. package/dist/cli/cli/src/index.js +23 -0
  383. package/dist/cli/cli/src/index.js.map +1 -1
  384. package/dist/cli/cli/src/utils/archive-creator.d.ts +82 -0
  385. package/dist/cli/cli/src/utils/archive-creator.d.ts.map +1 -0
  386. package/dist/cli/cli/src/utils/archive-creator.js +105 -0
  387. package/dist/cli/cli/src/utils/archive-creator.js.map +1 -0
  388. package/dist/cli/cli/src/utils/marketplace.d.ts +17 -4
  389. package/dist/cli/cli/src/utils/marketplace.d.ts.map +1 -1
  390. package/dist/cli/cli/src/utils/marketplace.js +172 -58
  391. package/dist/cli/cli/src/utils/marketplace.js.map +1 -1
  392. package/dist/cli/cli/src/utils/package-validator.d.ts +62 -0
  393. package/dist/cli/cli/src/utils/package-validator.d.ts.map +1 -0
  394. package/dist/cli/cli/src/utils/package-validator.js +122 -0
  395. package/dist/cli/cli/src/utils/package-validator.js.map +1 -0
  396. package/dist/cli/cli/src/utils/templates.d.ts +71 -0
  397. package/dist/cli/cli/src/utils/templates.d.ts.map +1 -0
  398. package/dist/cli/cli/src/utils/templates.js +91 -0
  399. package/dist/cli/cli/src/utils/templates.js.map +1 -0
  400. package/dist/cli/config/constants.d.ts +41 -0
  401. package/dist/cli/config/constants.d.ts.map +1 -1
  402. package/dist/cli/config/constants.js +57 -0
  403. package/dist/cli/config/constants.js.map +1 -1
  404. package/dist/cli/config/index.d.ts +2 -2
  405. package/dist/cli/config/index.d.ts.map +1 -1
  406. package/dist/cli/config/index.js +2 -2
  407. package/dist/cli/config/index.js.map +1 -1
  408. package/frontend/dist/assets/{index-523c7fce.js → index-0a245b0d.js} +247 -247
  409. package/frontend/dist/assets/{index-4c050f52.css → index-6972eeee.css} +1 -1
  410. package/frontend/dist/assets/nunito-cyrillic-400-normal-e44e669f.woff2 +0 -0
  411. package/frontend/dist/assets/nunito-cyrillic-400-normal-ff8e8bdd.woff +0 -0
  412. package/frontend/dist/assets/nunito-cyrillic-500-normal-2159679b.woff +0 -0
  413. package/frontend/dist/assets/nunito-cyrillic-500-normal-61a3b80e.woff2 +0 -0
  414. package/frontend/dist/assets/nunito-cyrillic-600-normal-ac046097.woff +0 -0
  415. package/frontend/dist/assets/nunito-cyrillic-600-normal-e61eb97b.woff2 +0 -0
  416. package/frontend/dist/assets/nunito-cyrillic-700-normal-8fcefcc9.woff2 +0 -0
  417. package/frontend/dist/assets/nunito-cyrillic-700-normal-b9684104.woff +0 -0
  418. package/frontend/dist/assets/nunito-cyrillic-800-normal-40253beb.woff +0 -0
  419. package/frontend/dist/assets/nunito-cyrillic-800-normal-d80292de.woff2 +0 -0
  420. package/frontend/dist/assets/nunito-cyrillic-ext-400-normal-20d73ae7.woff2 +0 -0
  421. package/frontend/dist/assets/nunito-cyrillic-ext-400-normal-d48c37c9.woff +0 -0
  422. package/frontend/dist/assets/nunito-cyrillic-ext-500-normal-16197abd.woff +0 -0
  423. package/frontend/dist/assets/nunito-cyrillic-ext-500-normal-9dcfe9b5.woff2 +0 -0
  424. package/frontend/dist/assets/nunito-cyrillic-ext-600-normal-d53e9851.woff2 +0 -0
  425. package/frontend/dist/assets/nunito-cyrillic-ext-600-normal-e3d0201f.woff +0 -0
  426. package/frontend/dist/assets/nunito-cyrillic-ext-700-normal-5936f6ac.woff2 +0 -0
  427. package/frontend/dist/assets/nunito-cyrillic-ext-700-normal-c8c02775.woff +0 -0
  428. package/frontend/dist/assets/nunito-cyrillic-ext-800-normal-217b8f51.woff +0 -0
  429. package/frontend/dist/assets/nunito-cyrillic-ext-800-normal-796cf7bd.woff2 +0 -0
  430. package/frontend/dist/assets/nunito-latin-400-normal-a5906e15.woff2 +0 -0
  431. package/frontend/dist/assets/nunito-latin-400-normal-b51e7635.woff +0 -0
  432. package/frontend/dist/assets/nunito-latin-500-normal-23ae3083.woff2 +0 -0
  433. package/frontend/dist/assets/nunito-latin-500-normal-be14dbc6.woff +0 -0
  434. package/frontend/dist/assets/nunito-latin-600-normal-06a9c8b3.woff +0 -0
  435. package/frontend/dist/assets/nunito-latin-600-normal-45f437de.woff2 +0 -0
  436. package/frontend/dist/assets/nunito-latin-700-normal-ce9107dc.woff +0 -0
  437. package/frontend/dist/assets/nunito-latin-700-normal-fa89300b.woff2 +0 -0
  438. package/frontend/dist/assets/nunito-latin-800-normal-0ca02785.woff +0 -0
  439. package/frontend/dist/assets/nunito-latin-800-normal-2363d3ed.woff2 +0 -0
  440. package/frontend/dist/assets/nunito-latin-ext-400-normal-67250a41.woff2 +0 -0
  441. package/frontend/dist/assets/nunito-latin-ext-400-normal-d7e2415e.woff +0 -0
  442. package/frontend/dist/assets/nunito-latin-ext-500-normal-06f35d1c.woff +0 -0
  443. package/frontend/dist/assets/nunito-latin-ext-500-normal-343e7adc.woff2 +0 -0
  444. package/frontend/dist/assets/nunito-latin-ext-600-normal-5a8efd17.woff +0 -0
  445. package/frontend/dist/assets/nunito-latin-ext-600-normal-a7ba5f4f.woff2 +0 -0
  446. package/frontend/dist/assets/nunito-latin-ext-700-normal-0a4e4a02.woff2 +0 -0
  447. package/frontend/dist/assets/nunito-latin-ext-700-normal-0c607961.woff +0 -0
  448. package/frontend/dist/assets/nunito-latin-ext-800-normal-39f54b55.woff2 +0 -0
  449. package/frontend/dist/assets/nunito-latin-ext-800-normal-466d0211.woff +0 -0
  450. package/frontend/dist/assets/nunito-vietnamese-400-normal-2a755616.woff2 +0 -0
  451. package/frontend/dist/assets/nunito-vietnamese-400-normal-9c01ea9f.woff +0 -0
  452. package/frontend/dist/assets/nunito-vietnamese-500-normal-452e5e08.woff +0 -0
  453. package/frontend/dist/assets/nunito-vietnamese-500-normal-dc98d965.woff2 +0 -0
  454. package/frontend/dist/assets/nunito-vietnamese-600-normal-2ffbb85f.woff +0 -0
  455. package/frontend/dist/assets/nunito-vietnamese-600-normal-cf95b95d.woff2 +0 -0
  456. package/frontend/dist/assets/nunito-vietnamese-700-normal-0e29c28c.woff2 +0 -0
  457. package/frontend/dist/assets/nunito-vietnamese-700-normal-7793b75e.woff +0 -0
  458. package/frontend/dist/assets/nunito-vietnamese-800-normal-5baf507e.woff +0 -0
  459. package/frontend/dist/assets/nunito-vietnamese-800-normal-fac6740e.woff2 +0 -0
  460. package/frontend/dist/index.html +2 -2
  461. package/package.json +16 -6
  462. package/config/skills/chrome-browser/instructions.md +0 -42
  463. package/config/skills/chrome-browser/skill.json +0 -39
  464. package/config/skills/nano-banana-image/generate.sh +0 -73
  465. package/config/skills/nano-banana-image/instructions.md +0 -50
  466. package/config/skills/nano-banana-image/skill.json +0 -39
  467. package/config/skills/playwright-chrome-browser/instructions.md +0 -95
  468. package/config/skills/playwright-chrome-browser/skill.json +0 -44
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Marketplace Submission Service
3
+ *
4
+ * Handles skill submissions to the marketplace, including validation,
5
+ * archive storage, and review workflow management.
6
+ *
7
+ * Submissions are stored locally in ~/.crewly/marketplace/submissions/
8
+ * with a manifest.json tracking all submissions and their status.
9
+ *
10
+ * @module services/marketplace/marketplace-submission.service
11
+ */
12
+ import path from 'path';
13
+ import { homedir } from 'os';
14
+ import { mkdir, readFile, writeFile, copyFile, rm } from 'fs/promises';
15
+ import { existsSync } from 'fs';
16
+ import { createHash, randomUUID } from 'crypto';
17
+ import * as tar from 'tar';
18
+ import { MARKETPLACE_CONSTANTS } from '../../constants.js';
19
+ const MARKETPLACE_DIR = path.join(homedir(), '.crewly', MARKETPLACE_CONSTANTS.DIR_NAME);
20
+ const SUBMISSIONS_DIR = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.SUBMISSIONS_DIR);
21
+ const SUBMISSIONS_MANIFEST_PATH = path.join(SUBMISSIONS_DIR, 'manifest.json');
22
+ /** Kebab-case pattern for skill IDs */
23
+ const KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
24
+ /** Semver pattern (major.minor.patch) */
25
+ const SEMVER_RE = /^\d+\.\d+\.\d+$/;
26
+ /**
27
+ * Loads the submissions manifest from disk.
28
+ *
29
+ * Returns an empty manifest if the file does not exist or is unparsable.
30
+ *
31
+ * @returns The submissions manifest
32
+ */
33
+ export async function loadSubmissionsManifest() {
34
+ try {
35
+ const data = await readFile(SUBMISSIONS_MANIFEST_PATH, 'utf-8');
36
+ return JSON.parse(data);
37
+ }
38
+ catch {
39
+ return { schemaVersion: 1, submissions: [] };
40
+ }
41
+ }
42
+ /**
43
+ * Saves the submissions manifest to disk.
44
+ *
45
+ * Creates the submissions directory if it does not exist.
46
+ *
47
+ * @param manifest - The manifest to persist
48
+ */
49
+ export async function saveSubmissionsManifest(manifest) {
50
+ await mkdir(SUBMISSIONS_DIR, { recursive: true });
51
+ await writeFile(SUBMISSIONS_MANIFEST_PATH, JSON.stringify(manifest, null, 2) + '\n');
52
+ }
53
+ /**
54
+ * Validates a skill.json manifest from a submitted archive.
55
+ *
56
+ * @param manifest - Parsed skill.json content
57
+ * @returns Array of validation error messages (empty if valid)
58
+ */
59
+ function validateSkillManifest(manifest) {
60
+ const errors = [];
61
+ if (!manifest.id || typeof manifest.id !== 'string') {
62
+ errors.push('skill.json missing required field: id');
63
+ }
64
+ else if (!KEBAB_CASE_RE.test(manifest.id)) {
65
+ errors.push(`skill.json id must be kebab-case: "${manifest.id}"`);
66
+ }
67
+ if (!manifest.name || typeof manifest.name !== 'string') {
68
+ errors.push('skill.json missing required field: name');
69
+ }
70
+ if (!manifest.description || typeof manifest.description !== 'string') {
71
+ errors.push('skill.json missing required field: description');
72
+ }
73
+ if (!manifest.version || typeof manifest.version !== 'string') {
74
+ errors.push('skill.json missing required field: version');
75
+ }
76
+ else if (!SEMVER_RE.test(manifest.version)) {
77
+ errors.push(`skill.json version must be semver (x.y.z): "${manifest.version}"`);
78
+ }
79
+ if (!manifest.category || typeof manifest.category !== 'string') {
80
+ errors.push('skill.json missing required field: category');
81
+ }
82
+ if (!Array.isArray(manifest.assignableRoles) || manifest.assignableRoles.length === 0) {
83
+ errors.push('skill.json must have a non-empty assignableRoles array');
84
+ }
85
+ if (!Array.isArray(manifest.tags) || manifest.tags.length === 0) {
86
+ errors.push('skill.json must have a non-empty tags array');
87
+ }
88
+ return errors;
89
+ }
90
+ /**
91
+ * Submits a skill package (tar.gz archive) to the marketplace for review.
92
+ *
93
+ * Performs the following steps:
94
+ * 1. Verifies the archive exists and is a tar.gz
95
+ * 2. Extracts and validates skill.json from the archive
96
+ * 3. Checks for duplicate submissions
97
+ * 4. Stores the archive in the submissions directory
98
+ * 5. Creates a submission record in the manifest
99
+ *
100
+ * @param archivePath - Path to the tar.gz archive to submit
101
+ * @returns Operation result with the submission record
102
+ */
103
+ export async function submitSkill(archivePath) {
104
+ try {
105
+ if (!existsSync(archivePath)) {
106
+ return { success: false, message: `Archive not found: ${archivePath}` };
107
+ }
108
+ if (!archivePath.endsWith('.tar.gz')) {
109
+ return { success: false, message: 'Archive must be a .tar.gz file' };
110
+ }
111
+ // Extract skill.json first, then read archive for checksum (avoids reading file twice)
112
+ let skillManifest;
113
+ try {
114
+ skillManifest = await extractSkillJson(archivePath);
115
+ }
116
+ catch (err) {
117
+ const msg = err instanceof Error ? err.message : String(err);
118
+ return { success: false, message: `Invalid skill archive: ${msg}` };
119
+ }
120
+ // Compute checksum
121
+ const archiveData = await readFile(archivePath);
122
+ const hash = createHash('sha256').update(archiveData).digest('hex');
123
+ const checksum = `sha256:${hash}`;
124
+ // Validate skill.json
125
+ const validationErrors = validateSkillManifest(skillManifest);
126
+ if (validationErrors.length > 0) {
127
+ return { success: false, message: `Validation failed: ${validationErrors.join('; ')}` };
128
+ }
129
+ const skillId = skillManifest.id;
130
+ // Check for duplicate submissions
131
+ const manifest = await loadSubmissionsManifest();
132
+ const existing = manifest.submissions.find((s) => s.skillId === skillId && s.status === 'pending');
133
+ if (existing) {
134
+ return { success: false, message: `Skill "${skillId}" already has a pending submission` };
135
+ }
136
+ // Store the archive
137
+ await mkdir(SUBMISSIONS_DIR, { recursive: true });
138
+ const storedArchiveName = `${skillId}-${skillManifest.version}.tar.gz`;
139
+ const storedArchivePath = path.join(SUBMISSIONS_DIR, storedArchiveName);
140
+ await copyFile(archivePath, storedArchivePath);
141
+ // Create submission record
142
+ const submission = {
143
+ id: randomUUID(),
144
+ skillId,
145
+ name: skillManifest.name,
146
+ description: skillManifest.description,
147
+ author: skillManifest.author || 'Community',
148
+ version: skillManifest.version,
149
+ category: skillManifest.category,
150
+ tags: skillManifest.tags,
151
+ license: skillManifest.license || 'MIT',
152
+ status: 'pending',
153
+ archivePath: storedArchivePath,
154
+ checksum,
155
+ sizeBytes: archiveData.length,
156
+ metadata: {
157
+ assignableRoles: skillManifest.assignableRoles,
158
+ triggers: skillManifest.triggers || [],
159
+ },
160
+ submittedAt: new Date().toISOString(),
161
+ };
162
+ manifest.submissions.push(submission);
163
+ await saveSubmissionsManifest(manifest);
164
+ return {
165
+ success: true,
166
+ message: `Skill "${submission.name}" submitted for review`,
167
+ submission,
168
+ };
169
+ }
170
+ catch (error) {
171
+ const msg = error instanceof Error ? error.message : String(error);
172
+ return { success: false, message: `Submission failed: ${msg}` };
173
+ }
174
+ }
175
+ /**
176
+ * Lists all submissions, optionally filtered by status.
177
+ *
178
+ * @param status - Optional status filter
179
+ * @returns Array of submissions
180
+ */
181
+ export async function listSubmissions(status) {
182
+ const manifest = await loadSubmissionsManifest();
183
+ if (status) {
184
+ return manifest.submissions.filter((s) => s.status === status);
185
+ }
186
+ return manifest.submissions;
187
+ }
188
+ /**
189
+ * Gets a single submission by ID.
190
+ *
191
+ * @param id - Submission ID
192
+ * @returns The submission, or null if not found
193
+ */
194
+ export async function getSubmission(id) {
195
+ const manifest = await loadSubmissionsManifest();
196
+ return manifest.submissions.find((s) => s.id === id) || null;
197
+ }
198
+ /**
199
+ * Reviews a submission — approving or rejecting it.
200
+ *
201
+ * When approved, the skill is added to the local registry and becomes
202
+ * available for installation. When rejected, the archive is removed.
203
+ *
204
+ * @param id - Submission ID
205
+ * @param action - 'approve' or 'reject'
206
+ * @param notes - Optional review notes
207
+ * @returns Operation result
208
+ */
209
+ export async function reviewSubmission(id, action, notes) {
210
+ const manifest = await loadSubmissionsManifest();
211
+ const submission = manifest.submissions.find((s) => s.id === id);
212
+ if (!submission) {
213
+ return { success: false, message: `Submission not found: ${id}` };
214
+ }
215
+ if (submission.status !== 'pending') {
216
+ return { success: false, message: `Submission already ${submission.status}` };
217
+ }
218
+ const newStatus = action === 'approve' ? 'approved' : 'rejected';
219
+ submission.status = newStatus;
220
+ submission.reviewedAt = new Date().toISOString();
221
+ submission.reviewNotes = notes;
222
+ if (action === 'approve') {
223
+ // Add to local registry by creating the item in the assets directory
224
+ const assetsDir = path.join(MARKETPLACE_DIR, 'assets', 'skills', submission.skillId);
225
+ await mkdir(assetsDir, { recursive: true });
226
+ const assetArchiveName = `${submission.skillId}-${submission.version}.tar.gz`;
227
+ const assetPath = path.join(assetsDir, assetArchiveName);
228
+ if (existsSync(submission.archivePath)) {
229
+ await copyFile(submission.archivePath, assetPath);
230
+ }
231
+ // Add to local registry file
232
+ await addToLocalRegistry(submission, assetArchiveName);
233
+ }
234
+ else {
235
+ // Remove archive for rejected submissions
236
+ if (existsSync(submission.archivePath)) {
237
+ await rm(submission.archivePath, { force: true });
238
+ }
239
+ }
240
+ await saveSubmissionsManifest(manifest);
241
+ return {
242
+ success: true,
243
+ message: action === 'approve'
244
+ ? `Approved "${submission.name}" — now available in marketplace`
245
+ : `Rejected "${submission.name}"`,
246
+ };
247
+ }
248
+ /**
249
+ * Adds an approved submission to the local registry file.
250
+ *
251
+ * The local registry augments the remote registry so that locally
252
+ * published skills appear in the marketplace alongside remote ones.
253
+ *
254
+ * @param submission - The approved submission
255
+ * @param archiveFilename - Name of the archive file in assets/
256
+ */
257
+ async function addToLocalRegistry(submission, archiveFilename) {
258
+ const localRegistryPath = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.LOCAL_REGISTRY_FILE);
259
+ let registry;
260
+ try {
261
+ const data = await readFile(localRegistryPath, 'utf-8');
262
+ registry = JSON.parse(data);
263
+ }
264
+ catch {
265
+ registry = { items: [] };
266
+ }
267
+ // Remove any existing entry for this skill
268
+ registry.items = registry.items.filter((i) => i.id !== submission.skillId);
269
+ // Add the new entry
270
+ const item = {
271
+ id: submission.skillId,
272
+ type: 'skill',
273
+ name: submission.name,
274
+ description: submission.description,
275
+ author: submission.author,
276
+ version: submission.version,
277
+ category: submission.category,
278
+ tags: submission.tags,
279
+ license: submission.license,
280
+ downloads: 0,
281
+ rating: 0,
282
+ createdAt: submission.submittedAt,
283
+ updatedAt: submission.reviewedAt || submission.submittedAt,
284
+ assets: {
285
+ archive: `skills/${submission.skillId}/${archiveFilename}`,
286
+ checksum: submission.checksum,
287
+ sizeBytes: submission.sizeBytes,
288
+ },
289
+ metadata: submission.metadata,
290
+ };
291
+ registry.items.push(item);
292
+ await writeFile(localRegistryPath, JSON.stringify(registry, null, 2) + '\n');
293
+ }
294
+ /**
295
+ * Extracts and parses skill.json from a tar.gz archive.
296
+ *
297
+ * Handles archives with various prefix structures (skill-id/skill.json,
298
+ * skill-name/skill.json, or flat skill.json).
299
+ *
300
+ * @param archivePath - Path to the tar.gz archive
301
+ * @returns Parsed skill.json content
302
+ * @throws Error if skill.json is not found or invalid
303
+ */
304
+ async function extractSkillJson(archivePath) {
305
+ return new Promise((resolve, reject) => {
306
+ let settled = false;
307
+ const chunks = [];
308
+ const extract = tar.t({
309
+ file: archivePath,
310
+ onReadEntry: (entry) => {
311
+ const entryPath = entry.path;
312
+ // Match skill.json at any level
313
+ if (entryPath.endsWith('skill.json') && !settled) {
314
+ settled = true;
315
+ entry.on('data', (chunk) => chunks.push(chunk));
316
+ entry.on('end', () => {
317
+ try {
318
+ const content = Buffer.concat(chunks).toString('utf-8');
319
+ resolve(JSON.parse(content));
320
+ }
321
+ catch (err) {
322
+ reject(new Error(`Invalid JSON in skill.json: ${err instanceof Error ? err.message : String(err)}`));
323
+ }
324
+ });
325
+ }
326
+ },
327
+ });
328
+ extract.then(() => {
329
+ if (!settled) {
330
+ reject(new Error('skill.json not found in archive'));
331
+ }
332
+ }).catch((err) => {
333
+ if (!settled) {
334
+ reject(err);
335
+ }
336
+ });
337
+ });
338
+ }
339
+ //# sourceMappingURL=marketplace-submission.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketplace-submission.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/marketplace/marketplace-submission.service.ts"],"names":[],"mappings":"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"}
@@ -7,10 +7,11 @@
7
7
  *
8
8
  * @module services/marketplace/marketplace.service
9
9
  */
10
- import { MarketplaceRegistry, MarketplaceItemWithStatus, MarketplaceFilter, InstalledItemsManifest, InstalledItemRecord } from '../../types/marketplace.types.js';
10
+ import { MarketplaceRegistry, MarketplaceItemWithStatus, MarketplaceFilter, InstalledItemsManifest, InstalledItemRecord, type MarketplaceItemType } from '../../types/marketplace.types.js';
11
11
  /**
12
12
  * Fetches the marketplace registry from the Crewly webapp API.
13
- * Uses in-memory caching with a 1-hour TTL.
13
+ * Uses in-memory caching with a 1-hour TTL. Concurrent callers share
14
+ * the same in-flight request to avoid duplicate network fetches.
14
15
  *
15
16
  * On network failure, falls back to the previously cached registry
16
17
  * if one is available. Throws only when no cached data exists.
@@ -79,19 +80,7 @@ export declare function getInstalledItems(): Promise<InstalledItemRecord[]>;
79
80
  * @returns Matching items with install status
80
81
  */
81
82
  export declare function searchItems(query: string): Promise<MarketplaceItemWithStatus[]>;
82
- /**
83
- * Returns the local install path for a marketplace item.
84
- *
85
- * Maps item types to directory names:
86
- * - 'skill' -> 'skills'
87
- * - 'model' -> 'models'
88
- * - 'role' -> 'roles'
89
- *
90
- * @param type - The marketplace item type
91
- * @param id - The marketplace item ID
92
- * @returns Absolute path to the item's install directory
93
- */
94
- export declare function getInstallPath(type: string, id: string): string;
83
+ export declare function getInstallPath(type: MarketplaceItemType, id: string): string;
95
84
  /**
96
85
  * Resets the in-memory registry cache.
97
86
  *
@@ -1 +1 @@
1
- {"version":3,"file":"marketplace.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/marketplace/marketplace.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EACL,mBAAmB,EAEnB,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAa1C;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAgBtF;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAOpE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlF;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAKnF;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAoChG;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAG9E;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAGxE;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAErF;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
1
+ {"version":3,"file":"marketplace.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/marketplace/marketplace.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EACL,mBAAmB,EAEnB,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,mBAAmB,EACzB,MAAM,kCAAkC,CAAC;AAyB1C;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuBtF;AA+HD;;;;;;;GAOG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAOpE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlF;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAOnF;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAuChG;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAG9E;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAGxE;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAErF;AA0CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAM5E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC"}
@@ -10,17 +10,29 @@
10
10
  import { homedir } from 'os';
11
11
  import path from 'path';
12
12
  import { readFile, writeFile, mkdir } from 'fs/promises';
13
- const MARKETPLACE_BASE_URL = 'https://crewly.stevesprompt.com';
14
- const REGISTRY_ENDPOINT = '/api/registry';
15
- const CACHE_TTL = 60 * 60 * 1000; // 1 hour
13
+ import { MARKETPLACE_CONSTANTS } from '../../constants.js';
16
14
  const CREWLY_HOME = path.join(homedir(), '.crewly');
17
- const MARKETPLACE_DIR = path.join(CREWLY_HOME, 'marketplace');
18
- const MANIFEST_PATH = path.join(MARKETPLACE_DIR, 'manifest.json');
15
+ const MARKETPLACE_DIR = path.join(CREWLY_HOME, MARKETPLACE_CONSTANTS.DIR_NAME);
16
+ const MANIFEST_PATH = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.MANIFEST_FILE);
17
+ const LOCAL_REGISTRY_PATH = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.LOCAL_REGISTRY_FILE);
18
+ /** Pattern for valid marketplace item IDs: lowercase alphanumeric with hyphens */
19
+ const VALID_ITEM_ID_PATTERN = /^[a-z0-9][a-z0-9\-]*$/;
19
20
  let cachedRegistry = null;
20
21
  let cacheTimestamp = 0;
22
+ let fetchInFlight = null;
23
+ /**
24
+ * Validates that a marketplace item ID is safe (no path traversal).
25
+ *
26
+ * @param id - The item ID to validate
27
+ * @returns True if the ID matches the allowed pattern
28
+ */
29
+ function isValidItemId(id) {
30
+ return VALID_ITEM_ID_PATTERN.test(id);
31
+ }
21
32
  /**
22
33
  * Fetches the marketplace registry from the Crewly webapp API.
23
- * Uses in-memory caching with a 1-hour TTL.
34
+ * Uses in-memory caching with a 1-hour TTL. Concurrent callers share
35
+ * the same in-flight request to avoid duplicate network fetches.
24
36
  *
25
37
  * On network failure, falls back to the previously cached registry
26
38
  * if one is available. Throws only when no cached data exists.
@@ -31,19 +43,140 @@ let cacheTimestamp = 0;
31
43
  */
32
44
  export async function fetchRegistry(forceRefresh = false) {
33
45
  const now = Date.now();
34
- if (!forceRefresh && cachedRegistry && now - cacheTimestamp < CACHE_TTL) {
46
+ if (!forceRefresh && cachedRegistry && now - cacheTimestamp < MARKETPLACE_CONSTANTS.CACHE_TTL) {
35
47
  return cachedRegistry;
36
48
  }
37
- const url = `${MARKETPLACE_BASE_URL}${REGISTRY_ENDPOINT}`;
38
- const res = await fetch(url);
39
- if (!res.ok) {
40
- if (cachedRegistry)
41
- return cachedRegistry;
42
- throw new Error(`Failed to fetch registry: ${res.status} ${res.statusText}`);
49
+ // When forceRefresh is true, wait for any in-flight request to complete
50
+ // before starting a new fetch to avoid returning stale data
51
+ if (forceRefresh && fetchInFlight) {
52
+ await fetchInFlight.catch(() => { });
53
+ // Fall through to start a new fetch
54
+ }
55
+ // Deduplicate concurrent requests
56
+ if (fetchInFlight) {
57
+ return fetchInFlight;
58
+ }
59
+ fetchInFlight = doFetchRegistry(now).finally(() => {
60
+ fetchInFlight = null;
61
+ });
62
+ return fetchInFlight;
63
+ }
64
+ /**
65
+ * Internal fetch implementation. Separated to enable in-flight deduplication.
66
+ *
67
+ * Fetches from two sources in parallel:
68
+ * 1. Public registry -- GitHub raw content (config/skills/registry.json in crewly repo)
69
+ * 2. Premium registry -- crewly.stevesprompt.com/api/registry/skills (private/paid skills)
70
+ *
71
+ * Results are merged with local registry items (locally published skills).
72
+ *
73
+ * When both remote sources fail and no local items exist, the empty registry
74
+ * is returned but NOT cached so the next call will retry immediately.
75
+ */
76
+ async function doFetchRegistry(now) {
77
+ // Fetch public and premium registries in parallel
78
+ const [publicResult, premiumResult] = await Promise.allSettled([
79
+ fetchPublicRegistry(),
80
+ fetchPremiumRegistry(),
81
+ ]);
82
+ const publicItems = publicResult.status === 'fulfilled' ? publicResult.value : [];
83
+ const premiumItems = premiumResult.status === 'fulfilled' ? premiumResult.value : [];
84
+ // If both failed and we have a cache, return cache
85
+ if (publicItems.length === 0 && premiumItems.length === 0 && cachedRegistry) {
86
+ return cachedRegistry;
87
+ }
88
+ // Merge: public + premium (premium overwrites by ID if conflict)
89
+ const mergedMap = new Map();
90
+ for (const item of publicItems) {
91
+ mergedMap.set(item.id, item);
92
+ }
93
+ for (const item of premiumItems) {
94
+ mergedMap.set(item.id, item);
95
+ }
96
+ // Merge locally published skills
97
+ const localItems = await loadLocalRegistry();
98
+ for (const item of localItems) {
99
+ if (!mergedMap.has(item.id)) {
100
+ mergedMap.set(item.id, item);
101
+ }
102
+ }
103
+ const registry = {
104
+ schemaVersion: MARKETPLACE_CONSTANTS.SCHEMA_VERSION,
105
+ lastUpdated: new Date().toISOString(),
106
+ cdnBaseUrl: MARKETPLACE_CONSTANTS.PUBLIC_CDN_BASE,
107
+ items: Array.from(mergedMap.values()),
108
+ };
109
+ // Only cache non-empty registries so that next call retries on total failure
110
+ if (mergedMap.size > 0) {
111
+ cachedRegistry = registry;
112
+ cacheTimestamp = now;
113
+ }
114
+ return registry;
115
+ }
116
+ /**
117
+ * Fetches items from the public registry (GitHub raw content).
118
+ *
119
+ * @returns Array of public marketplace items, empty on failure
120
+ */
121
+ async function fetchPublicRegistry() {
122
+ try {
123
+ const res = await fetch(MARKETPLACE_CONSTANTS.PUBLIC_REGISTRY_URL, {
124
+ signal: AbortSignal.timeout(MARKETPLACE_CONSTANTS.REGISTRY_FETCH_TIMEOUT),
125
+ });
126
+ if (res.ok) {
127
+ const data = (await res.json());
128
+ return data.items || [];
129
+ }
130
+ }
131
+ catch {
132
+ // Public registry unavailable -- continue with empty
133
+ }
134
+ return [];
135
+ }
136
+ /**
137
+ * Fetches items from the premium registry (crewly.stevesprompt.com).
138
+ *
139
+ * @returns Array of premium marketplace items with premium flag, empty on failure
140
+ */
141
+ async function fetchPremiumRegistry() {
142
+ try {
143
+ const url = `${MARKETPLACE_CONSTANTS.PREMIUM_BASE_URL}${MARKETPLACE_CONSTANTS.PREMIUM_REGISTRY_ENDPOINT}`;
144
+ const res = await fetch(url, {
145
+ signal: AbortSignal.timeout(MARKETPLACE_CONSTANTS.REGISTRY_FETCH_TIMEOUT),
146
+ });
147
+ if (res.ok) {
148
+ const data = (await res.json());
149
+ return (data.items || []).map((item) => ({
150
+ ...item,
151
+ metadata: { ...item.metadata, premium: true },
152
+ }));
153
+ }
154
+ }
155
+ catch {
156
+ // Premium registry unavailable -- continue without premium skills
157
+ }
158
+ return [];
159
+ }
160
+ /**
161
+ * Loads locally published marketplace items from the local registry file.
162
+ *
163
+ * These are skills that were submitted and approved through the local
164
+ * submission workflow.
165
+ *
166
+ * @returns Array of locally published marketplace items
167
+ */
168
+ async function loadLocalRegistry() {
169
+ try {
170
+ const data = await readFile(LOCAL_REGISTRY_PATH, 'utf-8');
171
+ const registry = JSON.parse(data);
172
+ if (!Array.isArray(registry.items)) {
173
+ return [];
174
+ }
175
+ return registry.items;
176
+ }
177
+ catch {
178
+ return [];
43
179
  }
44
- cachedRegistry = (await res.json());
45
- cacheTimestamp = now;
46
- return cachedRegistry;
47
180
  }
48
181
  /**
49
182
  * Loads the locally installed items manifest from disk.
@@ -84,7 +217,9 @@ export async function getItem(id) {
84
217
  const item = registry.items.find((i) => i.id === id);
85
218
  if (!item)
86
219
  return null;
87
- return enrichWithStatus(item, await loadManifest());
220
+ const manifest = await loadManifest();
221
+ const installedMap = new Map(manifest.items.map((r) => [r.id, r]));
222
+ return enrichWithStatusFromMap(item, installedMap);
88
223
  }
89
224
  /**
90
225
  * Lists marketplace items with optional filtering, enriched with install status.
@@ -98,7 +233,8 @@ export async function getItem(id) {
98
233
  */
99
234
  export async function listItems(filter) {
100
235
  const registry = await fetchRegistry();
101
- let items = registry.items;
236
+ // Shallow copy to avoid mutating the cached registry's items array via .sort()
237
+ let items = [...registry.items];
102
238
  if (filter?.type) {
103
239
  items = items.filter((i) => i.type === filter.type);
104
240
  }
@@ -125,7 +261,9 @@ export async function listItems(filter) {
125
261
  items.sort((a, b) => b.downloads - a.downloads);
126
262
  }
127
263
  const manifest = await loadManifest();
128
- return items.map((i) => enrichWithStatus(i, manifest));
264
+ // Use Map for O(n+m) enrichment instead of O(n*m) linear scan per item
265
+ const installedMap = new Map(manifest.items.map((r) => [r.id, r]));
266
+ return items.map((i) => enrichWithStatusFromMap(i, installedMap));
129
267
  }
130
268
  /**
131
269
  * Returns marketplace items that have updates available.
@@ -160,17 +298,14 @@ export async function searchItems(query) {
160
298
  return listItems({ search: query });
161
299
  }
162
300
  /**
163
- * Enriches a marketplace item with its local install status.
164
- *
165
- * Compares the registry item against the local manifest to determine
166
- * whether the item is not installed, installed, or has an update available.
301
+ * Enriches a marketplace item with its local install status using a pre-built Map.
167
302
  *
168
303
  * @param item - The marketplace item from the registry
169
- * @param manifest - The local installed items manifest
304
+ * @param installedMap - Map of installed item ID to record for O(1) lookup
170
305
  * @returns The item enriched with installStatus and optional installedVersion
171
306
  */
172
- function enrichWithStatus(item, manifest) {
173
- const installed = manifest.items.find((r) => r.id === item.id);
307
+ function enrichWithStatusFromMap(item, installedMap) {
308
+ const installed = installedMap.get(item.id);
174
309
  if (!installed) {
175
310
  return { ...item, installStatus: 'not_installed' };
176
311
  }
@@ -190,9 +325,18 @@ function enrichWithStatus(item, manifest) {
190
325
  * @param type - The marketplace item type
191
326
  * @param id - The marketplace item ID
192
327
  * @returns Absolute path to the item's install directory
328
+ * @throws Error if the id contains path traversal characters
193
329
  */
330
+ const TYPE_DIR_MAP = {
331
+ skill: 'skills',
332
+ model: 'models',
333
+ role: 'roles',
334
+ };
194
335
  export function getInstallPath(type, id) {
195
- const typeDir = type === 'skill' ? 'skills' : type === 'model' ? 'models' : 'roles';
336
+ if (!isValidItemId(id)) {
337
+ throw new Error(`Invalid marketplace item ID "${id}": must match ${VALID_ITEM_ID_PATTERN}`);
338
+ }
339
+ const typeDir = TYPE_DIR_MAP[type];
196
340
  return path.join(MARKETPLACE_DIR, typeDir, id);
197
341
  }
198
342
  /**
@@ -203,5 +347,6 @@ export function getInstallPath(type, id) {
203
347
  export function resetRegistryCache() {
204
348
  cachedRegistry = null;
205
349
  cacheTimestamp = 0;
350
+ fetchInFlight = null;
206
351
  }
207
352
  //# sourceMappingURL=marketplace.service.js.map