oh-my-codex 0.7.6 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/README.de.md +315 -0
  2. package/README.es.md +296 -17
  3. package/README.fr.md +315 -0
  4. package/README.it.md +315 -0
  5. package/README.ja.md +297 -18
  6. package/README.ko.md +296 -17
  7. package/README.md +110 -13
  8. package/README.pt.md +296 -17
  9. package/README.ru.md +296 -17
  10. package/README.tr.md +315 -0
  11. package/README.vi.md +297 -18
  12. package/README.zh-TW.md +362 -0
  13. package/README.zh.md +293 -17
  14. package/dist/catalog/__tests__/generator.test.js +2 -0
  15. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  16. package/dist/catalog/__tests__/schema.test.js +7 -0
  17. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  18. package/dist/cli/__tests__/ask.test.d.ts +2 -0
  19. package/dist/cli/__tests__/ask.test.d.ts.map +1 -0
  20. package/dist/cli/__tests__/ask.test.js +236 -0
  21. package/dist/cli/__tests__/ask.test.js.map +1 -0
  22. package/dist/cli/__tests__/doctor-warning-copy.test.d.ts +2 -0
  23. package/dist/cli/__tests__/doctor-warning-copy.test.d.ts.map +1 -0
  24. package/dist/cli/__tests__/doctor-warning-copy.test.js +45 -0
  25. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -0
  26. package/dist/cli/__tests__/index.test.js +85 -2
  27. package/dist/cli/__tests__/index.test.js.map +1 -1
  28. package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts +2 -0
  29. package/dist/cli/__tests__/ralph-prd-deep-interview.test.d.ts.map +1 -0
  30. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +15 -0
  31. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -0
  32. package/dist/cli/__tests__/ralph.test.js +19 -43
  33. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  34. package/dist/cli/__tests__/setup-scope.test.js +2 -0
  35. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  36. package/dist/cli/__tests__/team.test.js +219 -1
  37. package/dist/cli/__tests__/team.test.js.map +1 -1
  38. package/dist/cli/__tests__/version.test.d.ts +2 -0
  39. package/dist/cli/__tests__/version.test.d.ts.map +1 -0
  40. package/dist/cli/__tests__/version.test.js +21 -0
  41. package/dist/cli/__tests__/version.test.js.map +1 -0
  42. package/dist/cli/ask.d.ts +13 -0
  43. package/dist/cli/ask.d.ts.map +1 -0
  44. package/dist/cli/ask.js +174 -0
  45. package/dist/cli/ask.js.map +1 -0
  46. package/dist/cli/constants.d.ts +10 -0
  47. package/dist/cli/constants.d.ts.map +1 -0
  48. package/dist/cli/constants.js +10 -0
  49. package/dist/cli/constants.js.map +1 -0
  50. package/dist/cli/doctor.js +16 -5
  51. package/dist/cli/doctor.js.map +1 -1
  52. package/dist/cli/index.d.ts +8 -2
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +150 -52
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/cli/ralph.d.ts +3 -11
  57. package/dist/cli/ralph.d.ts.map +1 -1
  58. package/dist/cli/ralph.js +64 -45
  59. package/dist/cli/ralph.js.map +1 -1
  60. package/dist/cli/setup.d.ts.map +1 -1
  61. package/dist/cli/setup.js +17 -18
  62. package/dist/cli/setup.js.map +1 -1
  63. package/dist/cli/team.d.ts.map +1 -1
  64. package/dist/cli/team.js +257 -0
  65. package/dist/cli/team.js.map +1 -1
  66. package/dist/hooks/__tests__/deep-interview-contract.test.d.ts +2 -0
  67. package/dist/hooks/__tests__/deep-interview-contract.test.d.ts.map +1 -0
  68. package/dist/hooks/__tests__/deep-interview-contract.test.js +55 -0
  69. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -0
  70. package/dist/hooks/__tests__/emulator.test.js +6 -0
  71. package/dist/hooks/__tests__/emulator.test.js.map +1 -1
  72. package/dist/hooks/__tests__/keyword-detector.test.js +44 -22
  73. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -7
  75. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +59 -0
  77. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +264 -1
  79. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  80. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +61 -1
  81. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  82. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +17 -7
  83. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  84. package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts +2 -0
  85. package/dist/hooks/__tests__/openclaw-setup-contract.test.d.ts.map +1 -0
  86. package/dist/hooks/__tests__/openclaw-setup-contract.test.js +61 -0
  87. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -0
  88. package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts +2 -0
  89. package/dist/hooks/__tests__/pre-context-gate-skills.test.d.ts.map +1 -0
  90. package/dist/hooks/__tests__/pre-context-gate-skills.test.js +34 -0
  91. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -0
  92. package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts +2 -0
  93. package/dist/hooks/__tests__/visual-verdict-loop.test.d.ts.map +1 -0
  94. package/dist/hooks/__tests__/visual-verdict-loop.test.js +35 -0
  95. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -0
  96. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  97. package/dist/hooks/agents-overlay.js +18 -16
  98. package/dist/hooks/agents-overlay.js.map +1 -1
  99. package/dist/hooks/codebase-map.d.ts.map +1 -1
  100. package/dist/hooks/codebase-map.js +6 -2
  101. package/dist/hooks/codebase-map.js.map +1 -1
  102. package/dist/hooks/emulator.d.ts.map +1 -1
  103. package/dist/hooks/emulator.js +2 -0
  104. package/dist/hooks/emulator.js.map +1 -1
  105. package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
  106. package/dist/hooks/extensibility/sdk.js +2 -1
  107. package/dist/hooks/extensibility/sdk.js.map +1 -1
  108. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  109. package/dist/hooks/keyword-registry.js +6 -0
  110. package/dist/hooks/keyword-registry.js.map +1 -1
  111. package/dist/hud/index.d.ts.map +1 -1
  112. package/dist/hud/index.js +2 -24
  113. package/dist/hud/index.js.map +1 -1
  114. package/dist/mcp/__tests__/path-traversal.test.js +9 -227
  115. package/dist/mcp/__tests__/path-traversal.test.js.map +1 -1
  116. package/dist/mcp/__tests__/state-server-schema.test.js +16 -20
  117. package/dist/mcp/__tests__/state-server-schema.test.js.map +1 -1
  118. package/dist/mcp/__tests__/state-server-team-tools.test.js +30 -487
  119. package/dist/mcp/__tests__/state-server-team-tools.test.js.map +1 -1
  120. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  121. package/dist/mcp/code-intel-server.js +18 -8
  122. package/dist/mcp/code-intel-server.js.map +1 -1
  123. package/dist/mcp/memory-server.js +72 -11
  124. package/dist/mcp/memory-server.js.map +1 -1
  125. package/dist/mcp/state-paths.d.ts.map +1 -1
  126. package/dist/mcp/state-paths.js +4 -1
  127. package/dist/mcp/state-paths.js.map +1 -1
  128. package/dist/mcp/state-server.d.ts +179 -0
  129. package/dist/mcp/state-server.d.ts.map +1 -1
  130. package/dist/mcp/state-server.js +221 -1111
  131. package/dist/mcp/state-server.js.map +1 -1
  132. package/dist/mcp/team-server.d.ts.map +1 -1
  133. package/dist/mcp/team-server.js +9 -3
  134. package/dist/mcp/team-server.js.map +1 -1
  135. package/dist/mcp/trace-server.d.ts.map +1 -1
  136. package/dist/mcp/trace-server.js +8 -3
  137. package/dist/mcp/trace-server.js.map +1 -1
  138. package/dist/notifications/__tests__/dispatch-cooldown.test.d.ts +5 -0
  139. package/dist/notifications/__tests__/dispatch-cooldown.test.d.ts.map +1 -0
  140. package/dist/notifications/__tests__/dispatch-cooldown.test.js +100 -0
  141. package/dist/notifications/__tests__/dispatch-cooldown.test.js.map +1 -0
  142. package/dist/notifications/__tests__/temp-mode.test.d.ts +2 -0
  143. package/dist/notifications/__tests__/temp-mode.test.d.ts.map +1 -0
  144. package/dist/notifications/__tests__/temp-mode.test.js +172 -0
  145. package/dist/notifications/__tests__/temp-mode.test.js.map +1 -0
  146. package/dist/notifications/config.d.ts.map +1 -1
  147. package/dist/notifications/config.js +67 -7
  148. package/dist/notifications/config.js.map +1 -1
  149. package/dist/notifications/dispatch-cooldown.d.ts +36 -0
  150. package/dist/notifications/dispatch-cooldown.d.ts.map +1 -0
  151. package/dist/notifications/dispatch-cooldown.js +109 -0
  152. package/dist/notifications/dispatch-cooldown.js.map +1 -0
  153. package/dist/notifications/dispatcher.d.ts.map +1 -1
  154. package/dist/notifications/dispatcher.js +4 -4
  155. package/dist/notifications/dispatcher.js.map +1 -1
  156. package/dist/notifications/index.d.ts +5 -0
  157. package/dist/notifications/index.d.ts.map +1 -1
  158. package/dist/notifications/index.js +39 -8
  159. package/dist/notifications/index.js.map +1 -1
  160. package/dist/notifications/reply-listener.d.ts.map +1 -1
  161. package/dist/notifications/reply-listener.js +6 -2
  162. package/dist/notifications/reply-listener.js.map +1 -1
  163. package/dist/notifications/session-registry.d.ts.map +1 -1
  164. package/dist/notifications/session-registry.js +2 -2
  165. package/dist/notifications/session-registry.js.map +1 -1
  166. package/dist/notifications/temp-contract.d.ts +22 -0
  167. package/dist/notifications/temp-contract.d.ts.map +1 -0
  168. package/dist/notifications/temp-contract.js +147 -0
  169. package/dist/notifications/temp-contract.js.map +1 -0
  170. package/dist/notifications/tmux.js +2 -2
  171. package/dist/notifications/tmux.js.map +1 -1
  172. package/dist/notifications/types.d.ts +18 -0
  173. package/dist/notifications/types.d.ts.map +1 -1
  174. package/dist/openclaw/__tests__/config.test.js +81 -0
  175. package/dist/openclaw/__tests__/config.test.js.map +1 -1
  176. package/dist/openclaw/__tests__/dispatcher.test.js +40 -1
  177. package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
  178. package/dist/openclaw/config.d.ts +4 -0
  179. package/dist/openclaw/config.d.ts.map +1 -1
  180. package/dist/openclaw/config.js +110 -16
  181. package/dist/openclaw/config.js.map +1 -1
  182. package/dist/openclaw/dispatcher.d.ts +9 -3
  183. package/dist/openclaw/dispatcher.d.ts.map +1 -1
  184. package/dist/openclaw/dispatcher.js +42 -9
  185. package/dist/openclaw/dispatcher.js.map +1 -1
  186. package/dist/openclaw/types.d.ts +5 -1
  187. package/dist/openclaw/types.d.ts.map +1 -1
  188. package/dist/ralph/__tests__/persistence.test.js +28 -1
  189. package/dist/ralph/__tests__/persistence.test.js.map +1 -1
  190. package/dist/ralph/persistence.d.ts +21 -0
  191. package/dist/ralph/persistence.d.ts.map +1 -1
  192. package/dist/ralph/persistence.js +85 -2
  193. package/dist/ralph/persistence.js.map +1 -1
  194. package/dist/state/paths.d.ts +3 -0
  195. package/dist/state/paths.d.ts.map +1 -0
  196. package/dist/state/paths.js +2 -0
  197. package/dist/state/paths.js.map +1 -0
  198. package/dist/team/__tests__/api-interop.test.d.ts +2 -0
  199. package/dist/team/__tests__/api-interop.test.d.ts.map +1 -0
  200. package/dist/team/__tests__/api-interop.test.js +1052 -0
  201. package/dist/team/__tests__/api-interop.test.js.map +1 -0
  202. package/dist/team/__tests__/idle-nudge.test.d.ts +2 -0
  203. package/dist/team/__tests__/idle-nudge.test.d.ts.map +1 -0
  204. package/dist/team/__tests__/idle-nudge.test.js +225 -0
  205. package/dist/team/__tests__/idle-nudge.test.js.map +1 -0
  206. package/dist/team/__tests__/mcp-comm.test.js +30 -0
  207. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  208. package/dist/team/__tests__/runtime.test.js +33 -26
  209. package/dist/team/__tests__/runtime.test.js.map +1 -1
  210. package/dist/team/__tests__/state-root.test.d.ts +2 -0
  211. package/dist/team/__tests__/state-root.test.d.ts.map +1 -0
  212. package/dist/team/__tests__/state-root.test.js +9 -0
  213. package/dist/team/__tests__/state-root.test.js.map +1 -0
  214. package/dist/team/__tests__/state.test.js +52 -17
  215. package/dist/team/__tests__/state.test.js.map +1 -1
  216. package/dist/team/__tests__/team-ops-contract.test.d.ts +2 -0
  217. package/dist/team/__tests__/team-ops-contract.test.d.ts.map +1 -0
  218. package/dist/team/__tests__/team-ops-contract.test.js +90 -0
  219. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -0
  220. package/dist/team/__tests__/tmux-claude-workers-demo.test.d.ts +2 -0
  221. package/dist/team/__tests__/tmux-claude-workers-demo.test.d.ts.map +1 -0
  222. package/dist/team/__tests__/tmux-claude-workers-demo.test.js +176 -0
  223. package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +1 -0
  224. package/dist/team/__tests__/tmux-session.test.js +8 -0
  225. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  226. package/dist/team/__tests__/worker-bootstrap.test.js +29 -0
  227. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  228. package/dist/team/__tests__/worktree.test.js +54 -1
  229. package/dist/team/__tests__/worktree.test.js.map +1 -1
  230. package/dist/team/api-interop.d.ts +19 -0
  231. package/dist/team/api-interop.d.ts.map +1 -0
  232. package/dist/team/api-interop.js +578 -0
  233. package/dist/team/api-interop.js.map +1 -0
  234. package/dist/team/mcp-comm.d.ts.map +1 -1
  235. package/dist/team/mcp-comm.js +32 -2
  236. package/dist/team/mcp-comm.js.map +1 -1
  237. package/dist/team/orchestrator.d.ts +1 -10
  238. package/dist/team/orchestrator.d.ts.map +1 -1
  239. package/dist/team/orchestrator.js +8 -0
  240. package/dist/team/orchestrator.js.map +1 -1
  241. package/dist/team/runtime-cli.js +14 -8
  242. package/dist/team/runtime-cli.js.map +1 -1
  243. package/dist/team/runtime.d.ts +2 -1
  244. package/dist/team/runtime.d.ts.map +1 -1
  245. package/dist/team/runtime.js +103 -30
  246. package/dist/team/runtime.js.map +1 -1
  247. package/dist/team/scaling.d.ts.map +1 -1
  248. package/dist/team/scaling.js +33 -12
  249. package/dist/team/scaling.js.map +1 -1
  250. package/dist/team/state/approvals.d.ts +25 -0
  251. package/dist/team/state/approvals.d.ts.map +1 -0
  252. package/dist/team/state/approvals.js +31 -0
  253. package/dist/team/state/approvals.js.map +1 -0
  254. package/dist/team/state/config.d.ts +2 -0
  255. package/dist/team/state/config.d.ts.map +1 -0
  256. package/dist/team/state/config.js +2 -0
  257. package/dist/team/state/config.js.map +1 -0
  258. package/dist/team/state/dispatch-lock.d.ts +3 -0
  259. package/dist/team/state/dispatch-lock.d.ts.map +1 -0
  260. package/dist/team/state/dispatch-lock.js +81 -0
  261. package/dist/team/state/dispatch-lock.js.map +1 -0
  262. package/dist/team/state/dispatch.d.ts +61 -0
  263. package/dist/team/state/dispatch.d.ts.map +1 -0
  264. package/dist/team/state/dispatch.js +158 -0
  265. package/dist/team/state/dispatch.js.map +1 -0
  266. package/dist/team/state/events.d.ts +2 -0
  267. package/dist/team/state/events.d.ts.map +1 -0
  268. package/dist/team/state/events.js +2 -0
  269. package/dist/team/state/events.js.map +1 -0
  270. package/dist/team/state/index.d.ts +11 -0
  271. package/dist/team/state/index.d.ts.map +1 -0
  272. package/dist/team/state/index.js +11 -0
  273. package/dist/team/state/index.js.map +1 -0
  274. package/dist/team/state/io.d.ts +2 -0
  275. package/dist/team/state/io.d.ts.map +1 -0
  276. package/dist/team/state/io.js +2 -0
  277. package/dist/team/state/io.js.map +1 -0
  278. package/dist/team/state/locks.d.ts +16 -0
  279. package/dist/team/state/locks.d.ts.map +1 -0
  280. package/dist/team/state/locks.js +201 -0
  281. package/dist/team/state/locks.js.map +1 -0
  282. package/dist/team/state/mailbox.d.ts +39 -0
  283. package/dist/team/state/mailbox.d.ts.map +1 -0
  284. package/dist/team/state/mailbox.js +58 -0
  285. package/dist/team/state/mailbox.js.map +1 -0
  286. package/dist/team/state/monitor.d.ts +96 -0
  287. package/dist/team/state/monitor.d.ts.map +1 -0
  288. package/dist/team/state/monitor.js +163 -0
  289. package/dist/team/state/monitor.js.map +1 -0
  290. package/dist/team/state/shutdown.d.ts +2 -0
  291. package/dist/team/state/shutdown.d.ts.map +1 -0
  292. package/dist/team/state/shutdown.js +2 -0
  293. package/dist/team/state/shutdown.js.map +1 -0
  294. package/dist/team/state/summary.d.ts +2 -0
  295. package/dist/team/state/summary.d.ts.map +1 -0
  296. package/dist/team/state/summary.js +2 -0
  297. package/dist/team/state/summary.js.map +1 -0
  298. package/dist/team/state/tasks.d.ts +49 -0
  299. package/dist/team/state/tasks.d.ts.map +1 -0
  300. package/dist/team/state/tasks.js +182 -0
  301. package/dist/team/state/tasks.js.map +1 -0
  302. package/dist/team/state/types.d.ts +281 -0
  303. package/dist/team/state/types.d.ts.map +1 -0
  304. package/dist/team/state/types.js +3 -0
  305. package/dist/team/state/types.js.map +1 -0
  306. package/dist/team/state/workers.d.ts +2 -0
  307. package/dist/team/state/workers.d.ts.map +1 -0
  308. package/dist/team/state/workers.js +2 -0
  309. package/dist/team/state/workers.js.map +1 -0
  310. package/dist/team/state-root.d.ts +5 -0
  311. package/dist/team/state-root.d.ts.map +1 -0
  312. package/dist/team/state-root.js +8 -0
  313. package/dist/team/state-root.js.map +1 -0
  314. package/dist/team/state.d.ts +4 -1
  315. package/dist/team/state.d.ts.map +1 -1
  316. package/dist/team/state.js +200 -881
  317. package/dist/team/state.js.map +1 -1
  318. package/dist/team/tmux-session.d.ts.map +1 -1
  319. package/dist/team/tmux-session.js +11 -10
  320. package/dist/team/tmux-session.js.map +1 -1
  321. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  322. package/dist/team/worker-bootstrap.js +58 -26
  323. package/dist/team/worker-bootstrap.js.map +1 -1
  324. package/dist/team/worktree.d.ts.map +1 -1
  325. package/dist/team/worktree.js +43 -1
  326. package/dist/team/worktree.js.map +1 -1
  327. package/dist/utils/safe-json.d.ts +3 -0
  328. package/dist/utils/safe-json.d.ts.map +1 -0
  329. package/dist/utils/safe-json.js +19 -0
  330. package/dist/utils/safe-json.js.map +1 -0
  331. package/dist/utils/sleep.d.ts +3 -0
  332. package/dist/utils/sleep.d.ts.map +1 -0
  333. package/dist/utils/sleep.js +15 -0
  334. package/dist/utils/sleep.js.map +1 -0
  335. package/dist/visual/__tests__/verdict.test.d.ts +2 -0
  336. package/dist/visual/__tests__/verdict.test.d.ts.map +1 -0
  337. package/dist/visual/__tests__/verdict.test.js +81 -0
  338. package/dist/visual/__tests__/verdict.test.js.map +1 -0
  339. package/dist/visual/constants.d.ts +4 -0
  340. package/dist/visual/constants.d.ts.map +1 -0
  341. package/dist/visual/constants.js +3 -0
  342. package/dist/visual/constants.js.map +1 -0
  343. package/dist/visual/verdict.d.ts +17 -0
  344. package/dist/visual/verdict.d.ts.map +1 -0
  345. package/dist/visual/verdict.js +61 -0
  346. package/dist/visual/verdict.js.map +1 -0
  347. package/package.json +10 -3
  348. package/scripts/ask-claude.sh +17 -0
  349. package/scripts/ask-gemini.sh +14 -0
  350. package/scripts/demo-claude-workers.sh +241 -0
  351. package/scripts/demo-team-e2e.sh +179 -0
  352. package/scripts/fixtures/ask-advisor-stub.js +12 -0
  353. package/scripts/notify-hook/team-dispatch.js +234 -12
  354. package/scripts/notify-hook/team-leader-nudge.js +42 -2
  355. package/scripts/notify-hook/team-worker.js +63 -4
  356. package/scripts/notify-hook/visual-verdict.js +50 -1
  357. package/scripts/notify-hook.js +1 -0
  358. package/scripts/run-provider-advisor.js +179 -0
  359. package/skills/ask-claude/SKILL.md +61 -0
  360. package/skills/ask-gemini/SKILL.md +61 -0
  361. package/skills/autopilot/SKILL.md +32 -2
  362. package/skills/configure-notifications/SKILL.md +188 -186
  363. package/skills/deep-interview/SKILL.md +247 -0
  364. package/skills/omx-setup/SKILL.md +1 -1
  365. package/skills/ralph/SKILL.md +42 -11
  366. package/skills/ralplan/SKILL.md +17 -0
  367. package/skills/team/SKILL.md +64 -5
  368. package/skills/visual-verdict/SKILL.md +76 -0
  369. package/skills/web-clone/SKILL.md +366 -0
  370. package/skills/worker/SKILL.md +42 -11
  371. package/templates/AGENTS.md +9 -0
  372. package/templates/catalog-manifest.json +39 -18
  373. package/skills/configure-discord/SKILL.md +0 -256
  374. package/skills/configure-openclaw/SKILL.md +0 -267
  375. package/skills/configure-slack/SKILL.md +0 -226
  376. package/skills/configure-telegram/SKILL.md +0 -232
@@ -3,63 +3,34 @@
3
3
  * Provides state read/write/clear/list tools for workflow modes
4
4
  * Storage: .omx/state/{mode}-state.json
5
5
  */
6
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
- import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
9
- import { readFile, writeFile, readdir, mkdir, unlink, rename } from 'fs/promises';
10
- import { existsSync, readFileSync } from 'fs';
11
- import { dirname, join, resolve as resolvePath } from 'path';
12
- import { getAllScopedStatePaths, getReadScopedStateDirs, getReadScopedStatePaths, resolveStateScope, getStateDir, getStatePath, resolveWorkingDirectoryForState, validateSessionId, } from './state-paths.js';
13
- import { withModeRuntimeContext } from '../state/mode-state-context.js';
14
- import { ensureTmuxHookInitialized } from '../cli/tmux-hook.js';
15
- import { RALPH_PHASES, validateAndNormalizeRalphState } from '../ralph/contract.js';
16
- import { ensureCanonicalRalphArtifacts } from '../ralph/persistence.js';
17
- import { shouldAutoStartMcpServer } from './bootstrap.js';
18
- import { TEAM_NAME_SAFE_PATTERN, WORKER_NAME_SAFE_PATTERN, TASK_ID_SAFE_PATTERN, TEAM_TASK_STATUSES, TEAM_EVENT_TYPES, TEAM_TASK_APPROVAL_STATUSES, } from '../team/contracts.js';
19
- import { teamSendMessage as sendDirectMessage, teamBroadcast as broadcastMessage, teamListMailbox as listMailboxMessages, teamMarkMessageDelivered as markMessageDelivered, teamMarkMessageNotified as markMessageNotified, teamCreateTask, teamReadTask, teamListTasks, teamUpdateTask, teamClaimTask, teamTransitionTaskStatus, teamReleaseTaskClaim, teamReadConfig, teamReadManifest, teamReadWorkerStatus, teamReadWorkerHeartbeat, teamUpdateWorkerHeartbeat, teamWriteWorkerInbox, teamWriteWorkerIdentity, teamAppendEvent, teamGetSummary, teamCleanup, teamWriteShutdownRequest, teamReadShutdownAck, teamReadMonitorSnapshot, teamWriteMonitorSnapshot, teamReadTaskApproval, teamWriteTaskApproval, } from '../team/team-ops.js';
6
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
9
+ import { readFile, writeFile, readdir, mkdir, unlink, rename, } from "fs/promises";
10
+ import { existsSync } from "fs";
11
+ import { join } from "path";
12
+ import { getAllScopedStatePaths, getReadScopedStateDirs, getReadScopedStatePaths, resolveStateScope, getStateDir, getStatePath, resolveWorkingDirectoryForState, validateSessionId, } from "./state-paths.js";
13
+ import { withModeRuntimeContext } from "../state/mode-state-context.js";
14
+ import { RALPH_PHASES, validateAndNormalizeRalphState, } from "../ralph/contract.js";
15
+ import { ensureCanonicalRalphArtifacts } from "../ralph/persistence.js";
16
+ import { shouldAutoStartMcpServer } from "./bootstrap.js";
17
+ import { LEGACY_TEAM_MCP_TOOLS, buildLegacyTeamDeprecationHint, } from "../team/api-interop.js";
20
18
  const SUPPORTED_MODES = [
21
- 'autopilot', 'team',
22
- 'ralph', 'ultrawork', 'ultraqa', 'ralplan',
19
+ "autopilot",
20
+ "team",
21
+ "ralph",
22
+ "ultrawork",
23
+ "ultraqa",
24
+ "ralplan",
23
25
  ];
24
26
  const STATE_TOOL_NAMES = new Set([
25
- 'state_read',
26
- 'state_write',
27
- 'state_clear',
28
- 'state_list_active',
29
- 'state_get_status',
27
+ "state_read",
28
+ "state_write",
29
+ "state_clear",
30
+ "state_list_active",
31
+ "state_get_status",
30
32
  ]);
31
- const TEAM_COMM_TOOL_NAMES = new Set([
32
- 'team_send_message',
33
- 'team_broadcast',
34
- 'team_mailbox_list',
35
- 'team_mailbox_mark_delivered',
36
- 'team_mailbox_mark_notified',
37
- 'team_create_task',
38
- 'team_read_task',
39
- 'team_list_tasks',
40
- 'team_update_task',
41
- 'team_claim_task',
42
- 'team_transition_task_status',
43
- 'team_release_task_claim',
44
- 'team_read_config',
45
- 'team_read_manifest',
46
- 'team_read_worker_status',
47
- 'team_read_worker_heartbeat',
48
- 'team_update_worker_heartbeat',
49
- 'team_write_worker_inbox',
50
- 'team_write_worker_identity',
51
- 'team_append_event',
52
- 'team_get_summary',
53
- 'team_cleanup',
54
- 'team_write_shutdown_request',
55
- 'team_read_shutdown_ack',
56
- 'team_read_monitor_snapshot',
57
- 'team_write_monitor_snapshot',
58
- 'team_read_task_approval',
59
- 'team_write_task_approval',
60
- ]);
61
- const TEAM_UPDATE_TASK_MUTABLE_FIELDS = new Set(['subject', 'description', 'blocked_by', 'requires_code_change']);
62
- const TEAM_UPDATE_TASK_REQUEST_FIELDS = new Set(['team_name', 'task_id', 'workingDirectory', ...TEAM_UPDATE_TASK_MUTABLE_FIELDS]);
33
+ const TEAM_COMM_TOOL_NAMES = new Set([...LEGACY_TEAM_MCP_TOOLS]);
63
34
  const stateWriteQueues = new Map();
64
35
  async function withStateWriteLock(path, fn) {
65
36
  const tail = stateWriteQueues.get(path) ?? Promise.resolve();
@@ -82,7 +53,7 @@ async function withStateWriteLock(path, fn) {
82
53
  }
83
54
  async function writeAtomicFile(path, data) {
84
55
  const tmpPath = `${path}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
85
- await writeFile(tmpPath, data, 'utf-8');
56
+ await writeFile(tmpPath, data, "utf-8");
86
57
  try {
87
58
  await rename(tmpPath, path);
88
59
  }
@@ -91,585 +62,110 @@ async function writeAtomicFile(path, data) {
91
62
  throw error;
92
63
  }
93
64
  }
94
- function isFiniteInteger(value) {
95
- return typeof value === 'number' && Number.isInteger(value) && Number.isFinite(value);
96
- }
97
- function parseValidatedTaskIdArray(value, fieldName) {
98
- if (!Array.isArray(value)) {
99
- throw new Error(`${fieldName} must be an array of task IDs (strings)`);
100
- }
101
- const taskIds = [];
102
- for (const item of value) {
103
- if (typeof item !== 'string') {
104
- throw new Error(`${fieldName} entries must be strings`);
105
- }
106
- const normalized = item.trim();
107
- if (!TASK_ID_SAFE_PATTERN.test(normalized)) {
108
- throw new Error(`${fieldName} contains invalid task ID: "${item}"`);
109
- }
110
- taskIds.push(normalized);
111
- }
112
- return taskIds;
113
- }
114
- function teamStateExists(teamName, candidateCwd) {
115
- if (!TEAM_NAME_SAFE_PATTERN.test(teamName))
116
- return false;
117
- const teamRoot = join(candidateCwd, '.omx', 'state', 'team', teamName);
118
- return (existsSync(join(teamRoot, 'config.json')) ||
119
- existsSync(join(teamRoot, 'tasks')) ||
120
- existsSync(teamRoot));
121
- }
122
- function parseTeamWorkerEnv(raw) {
123
- if (typeof raw !== 'string' || raw.trim() === '')
124
- return null;
125
- const match = /^([a-z0-9][a-z0-9-]{0,29})\/(worker-\d+)$/.exec(raw.trim());
126
- if (!match)
127
- return null;
128
- return { teamName: match[1], workerName: match[2] };
129
- }
130
- function readTeamStateRootFromFile(path) {
131
- if (!existsSync(path))
132
- return null;
133
- try {
134
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
135
- return typeof parsed.team_state_root === 'string' && parsed.team_state_root.trim() !== ''
136
- ? parsed.team_state_root.trim()
137
- : null;
138
- }
139
- catch {
140
- return null;
141
- }
142
- }
143
- function stateRootToWorkingDirectory(stateRoot) {
144
- const absolute = resolvePath(stateRoot);
145
- return dirname(dirname(absolute));
146
- }
147
- function resolveTeamWorkingDirectoryFromMetadata(teamName, candidateCwd, workerContext) {
148
- const teamRoot = join(candidateCwd, '.omx', 'state', 'team', teamName);
149
- if (!existsSync(teamRoot))
150
- return null;
151
- if (workerContext?.teamName === teamName) {
152
- const workerRoot = readTeamStateRootFromFile(join(teamRoot, 'workers', workerContext.workerName, 'identity.json'));
153
- if (workerRoot)
154
- return stateRootToWorkingDirectory(workerRoot);
155
- }
156
- const fromManifest = readTeamStateRootFromFile(join(teamRoot, 'manifest.v2.json'));
157
- if (fromManifest)
158
- return stateRootToWorkingDirectory(fromManifest);
159
- const fromConfig = readTeamStateRootFromFile(join(teamRoot, 'config.json'));
160
- if (fromConfig)
161
- return stateRootToWorkingDirectory(fromConfig);
162
- return null;
163
- }
164
- function resolveTeamWorkingDirectory(teamName, preferredCwd) {
165
- const normalizedTeamName = String(teamName || '').trim();
166
- if (!normalizedTeamName)
167
- return preferredCwd;
168
- const envTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
169
- if (typeof envTeamStateRoot === 'string' && envTeamStateRoot.trim() !== '') {
170
- return stateRootToWorkingDirectory(envTeamStateRoot.trim());
171
- }
172
- const seeds = [];
173
- for (const seed of [preferredCwd, process.cwd()]) {
174
- if (typeof seed !== 'string' || seed.trim() === '')
175
- continue;
176
- if (!seeds.includes(seed))
177
- seeds.push(seed);
178
- }
179
- const workerContext = parseTeamWorkerEnv(process.env.OMX_TEAM_WORKER);
180
- for (const seed of seeds) {
181
- let cursor = seed;
182
- while (cursor) {
183
- if (teamStateExists(normalizedTeamName, cursor)) {
184
- return resolveTeamWorkingDirectoryFromMetadata(normalizedTeamName, cursor, workerContext) ?? cursor;
185
- }
186
- const parent = dirname(cursor);
187
- if (!parent || parent === cursor)
188
- break;
189
- cursor = parent;
190
- }
191
- }
192
- return preferredCwd;
193
- }
194
- const server = new Server({ name: 'omx-state', version: '0.1.0' }, { capabilities: { tools: {} } });
195
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
196
- tools: [
65
+ const server = new Server({ name: "omx-state", version: "0.1.0" }, { capabilities: { tools: {} } });
66
+ export function buildStateServerTools() {
67
+ return [
197
68
  {
198
- name: 'state_read',
199
- description: 'Read state for a specific mode. Returns JSON state data or indicates no state exists.',
69
+ name: "state_read",
70
+ description: "Read state for a specific mode. Returns JSON state data or indicates no state exists.",
200
71
  inputSchema: {
201
- type: 'object',
72
+ type: "object",
202
73
  properties: {
203
- mode: { type: 'string', enum: [...SUPPORTED_MODES], description: 'The mode to read state for' },
204
- workingDirectory: { type: 'string', description: 'Working directory override' },
205
- session_id: { type: 'string', description: 'Optional session scope ID' },
74
+ mode: {
75
+ type: "string",
76
+ enum: [...SUPPORTED_MODES],
77
+ description: "The mode to read state for",
78
+ },
79
+ workingDirectory: {
80
+ type: "string",
81
+ description: "Working directory override",
82
+ },
83
+ session_id: {
84
+ type: "string",
85
+ description: "Optional session scope ID",
86
+ },
206
87
  },
207
- required: ['mode'],
88
+ required: ["mode"],
208
89
  },
209
90
  },
210
91
  {
211
- name: 'state_write',
212
- description: 'Write/update state for a specific mode. Creates directories if needed.',
92
+ name: "state_write",
93
+ description: "Write/update state for a specific mode. Creates directories if needed.",
213
94
  inputSchema: {
214
- type: 'object',
95
+ type: "object",
215
96
  properties: {
216
- mode: { type: 'string', enum: [...SUPPORTED_MODES] },
217
- active: { type: 'boolean' },
218
- iteration: { type: 'number' },
219
- max_iterations: { type: 'number' },
220
- current_phase: { type: 'string' },
221
- task_description: { type: 'string' },
222
- started_at: { type: 'string' },
223
- completed_at: { type: 'string' },
224
- error: { type: 'string' },
225
- state: { type: 'object', description: 'Additional custom fields' },
226
- workingDirectory: { type: 'string' },
227
- session_id: { type: 'string', description: 'Optional session scope ID' },
97
+ mode: { type: "string", enum: [...SUPPORTED_MODES] },
98
+ active: { type: "boolean" },
99
+ iteration: { type: "number" },
100
+ max_iterations: { type: "number" },
101
+ current_phase: { type: "string" },
102
+ task_description: { type: "string" },
103
+ started_at: { type: "string" },
104
+ completed_at: { type: "string" },
105
+ error: { type: "string" },
106
+ state: { type: "object", description: "Additional custom fields" },
107
+ workingDirectory: { type: "string" },
108
+ session_id: {
109
+ type: "string",
110
+ description: "Optional session scope ID",
111
+ },
228
112
  },
229
- required: ['mode'],
113
+ required: ["mode"],
230
114
  },
231
115
  },
232
116
  {
233
- name: 'state_clear',
234
- description: 'Clear/delete state for a specific mode.',
117
+ name: "state_clear",
118
+ description: "Clear/delete state for a specific mode.",
235
119
  inputSchema: {
236
- type: 'object',
120
+ type: "object",
237
121
  properties: {
238
- mode: { type: 'string', enum: [...SUPPORTED_MODES] },
239
- workingDirectory: { type: 'string' },
240
- session_id: { type: 'string', description: 'Optional session scope ID' },
241
- all_sessions: { type: 'boolean', description: 'Clear matching mode in global and all session scopes' },
122
+ mode: { type: "string", enum: [...SUPPORTED_MODES] },
123
+ workingDirectory: { type: "string" },
124
+ session_id: {
125
+ type: "string",
126
+ description: "Optional session scope ID",
127
+ },
128
+ all_sessions: {
129
+ type: "boolean",
130
+ description: "Clear matching mode in global and all session scopes",
131
+ },
242
132
  },
243
- required: ['mode'],
133
+ required: ["mode"],
244
134
  },
245
135
  },
246
136
  {
247
- name: 'state_list_active',
248
- description: 'List all currently active modes.',
137
+ name: "state_list_active",
138
+ description: "List all currently active modes.",
249
139
  inputSchema: {
250
- type: 'object',
140
+ type: "object",
251
141
  properties: {
252
- workingDirectory: { type: 'string' },
253
- session_id: { type: 'string', description: 'Optional session scope ID' },
142
+ workingDirectory: { type: "string" },
143
+ session_id: {
144
+ type: "string",
145
+ description: "Optional session scope ID",
146
+ },
254
147
  },
255
148
  },
256
149
  },
257
150
  {
258
- name: 'state_get_status',
259
- description: 'Get detailed status for a specific mode or all modes.',
151
+ name: "state_get_status",
152
+ description: "Get detailed status for a specific mode or all modes.",
260
153
  inputSchema: {
261
- type: 'object',
154
+ type: "object",
262
155
  properties: {
263
- mode: { type: 'string', enum: [...SUPPORTED_MODES] },
264
- workingDirectory: { type: 'string' },
265
- session_id: { type: 'string', description: 'Optional session scope ID' },
156
+ mode: { type: "string", enum: [...SUPPORTED_MODES] },
157
+ workingDirectory: { type: "string" },
158
+ session_id: {
159
+ type: "string",
160
+ description: "Optional session scope ID",
161
+ },
266
162
  },
267
163
  },
268
164
  },
269
- {
270
- name: 'team_send_message',
271
- description: 'Send a direct team mailbox message from one worker to another worker/leader.',
272
- inputSchema: {
273
- type: 'object',
274
- properties: {
275
- team_name: { type: 'string', description: 'Sanitized team name (e.g., my-team)' },
276
- from_worker: { type: 'string', description: 'Sender worker id (e.g., worker-1)' },
277
- to_worker: { type: 'string', description: 'Recipient worker id (e.g., worker-2 or leader-fixed)' },
278
- body: { type: 'string', description: 'Message content' },
279
- workingDirectory: { type: 'string' },
280
- },
281
- required: ['team_name', 'from_worker', 'to_worker', 'body'],
282
- },
283
- },
284
- {
285
- name: 'team_broadcast',
286
- description: 'Broadcast a message from one worker to all other workers in the team.',
287
- inputSchema: {
288
- type: 'object',
289
- properties: {
290
- team_name: { type: 'string', description: 'Sanitized team name (e.g., my-team)' },
291
- from_worker: { type: 'string', description: 'Sender worker id (e.g., worker-1)' },
292
- body: { type: 'string', description: 'Message content' },
293
- workingDirectory: { type: 'string' },
294
- },
295
- required: ['team_name', 'from_worker', 'body'],
296
- },
297
- },
298
- {
299
- name: 'team_mailbox_list',
300
- description: 'List mailbox messages for a specific worker (including leader-fixed).',
301
- inputSchema: {
302
- type: 'object',
303
- properties: {
304
- team_name: { type: 'string', description: 'Sanitized team name (e.g., my-team)' },
305
- worker: { type: 'string', description: 'Mailbox owner worker id' },
306
- include_delivered: { type: 'boolean', description: 'Include delivered messages (default: true)' },
307
- workingDirectory: { type: 'string' },
308
- },
309
- required: ['team_name', 'worker'],
310
- },
311
- },
312
- {
313
- name: 'team_mailbox_mark_delivered',
314
- description: 'Mark a mailbox message as delivered for a worker.',
315
- inputSchema: {
316
- type: 'object',
317
- properties: {
318
- team_name: { type: 'string', description: 'Sanitized team name (e.g., my-team)' },
319
- worker: { type: 'string', description: 'Mailbox owner worker id' },
320
- message_id: { type: 'string', description: 'Message ID to mark delivered' },
321
- workingDirectory: { type: 'string' },
322
- },
323
- required: ['team_name', 'worker', 'message_id'],
324
- },
325
- },
326
- {
327
- name: 'team_mailbox_mark_notified',
328
- description: 'Mark a mailbox message as notified (tmux trigger sent) for a worker.',
329
- inputSchema: {
330
- type: 'object',
331
- properties: {
332
- team_name: { type: 'string', description: 'Sanitized team name' },
333
- worker: { type: 'string', description: 'Mailbox owner worker id' },
334
- message_id: { type: 'string', description: 'Message ID to mark notified' },
335
- workingDirectory: { type: 'string' },
336
- },
337
- required: ['team_name', 'worker', 'message_id'],
338
- },
339
- },
340
- {
341
- name: 'team_create_task',
342
- description: 'Create a new task in the team task list. Returns the created task with auto-incremented ID.',
343
- inputSchema: {
344
- type: 'object',
345
- properties: {
346
- team_name: { type: 'string', description: 'Sanitized team name' },
347
- subject: { type: 'string', description: 'Task subject/title' },
348
- description: { type: 'string', description: 'Task description' },
349
- owner: { type: 'string', description: 'Worker name to assign (optional)' },
350
- blocked_by: { type: 'array', items: { type: 'string' }, description: 'Task IDs this task depends on' },
351
- requires_code_change: { type: 'boolean', description: 'Whether the task involves code changes' },
352
- workingDirectory: { type: 'string' },
353
- },
354
- required: ['team_name', 'subject', 'description'],
355
- },
356
- },
357
- {
358
- name: 'team_read_task',
359
- description: 'Read a single task by ID.',
360
- inputSchema: {
361
- type: 'object',
362
- properties: {
363
- team_name: { type: 'string', description: 'Sanitized team name' },
364
- task_id: { type: 'string', description: 'Task ID to read' },
365
- workingDirectory: { type: 'string' },
366
- },
367
- required: ['team_name', 'task_id'],
368
- },
369
- },
370
- {
371
- name: 'team_list_tasks',
372
- description: 'List all tasks in a team, sorted by numeric ID.',
373
- inputSchema: {
374
- type: 'object',
375
- properties: {
376
- team_name: { type: 'string', description: 'Sanitized team name' },
377
- workingDirectory: { type: 'string' },
378
- },
379
- required: ['team_name'],
380
- },
381
- },
382
- {
383
- name: 'team_update_task',
384
- description: 'Update non-lifecycle task metadata (subject, description, blocked_by, requires_code_change). Status/owner/result/error are lifecycle fields that must be changed via team_claim_task + team_transition_task_status.',
385
- inputSchema: {
386
- type: 'object',
387
- properties: {
388
- team_name: { type: 'string', description: 'Sanitized team name' },
389
- task_id: { type: 'string', description: 'Task ID to update' },
390
- subject: { type: 'string', description: 'Task subject/title' },
391
- description: { type: 'string', description: 'Task description' },
392
- blocked_by: { type: 'array', items: { type: 'string' }, description: 'Task IDs this task depends on' },
393
- requires_code_change: { type: 'boolean', description: 'Whether this task requires a code change' },
394
- workingDirectory: { type: 'string' },
395
- },
396
- required: ['team_name', 'task_id'],
397
- },
398
- },
399
- {
400
- name: 'team_claim_task',
401
- description: 'Atomically claim a task for a worker. Checks dependencies and version.',
402
- inputSchema: {
403
- type: 'object',
404
- properties: {
405
- team_name: { type: 'string', description: 'Sanitized team name' },
406
- task_id: { type: 'string', description: 'Task ID to claim' },
407
- worker: { type: 'string', description: 'Worker name claiming the task' },
408
- expected_version: { type: 'number', description: 'Expected task version for optimistic locking. Omitting does not allow claiming an already in-progress task.' },
409
- workingDirectory: { type: 'string' },
410
- },
411
- required: ['team_name', 'task_id', 'worker'],
412
- },
413
- },
414
- {
415
- name: 'team_transition_task_status',
416
- description: 'Atomically transition task status with claim token validation.',
417
- inputSchema: {
418
- type: 'object',
419
- properties: {
420
- team_name: { type: 'string', description: 'Sanitized team name' },
421
- task_id: { type: 'string', description: 'Task ID to transition' },
422
- from: { type: 'string', enum: [...TEAM_TASK_STATUSES] },
423
- to: { type: 'string', enum: [...TEAM_TASK_STATUSES] },
424
- claim_token: { type: 'string', description: 'Claim token from team_claim_task' },
425
- workingDirectory: { type: 'string' },
426
- },
427
- required: ['team_name', 'task_id', 'from', 'to', 'claim_token'],
428
- },
429
- },
430
- {
431
- name: 'team_release_task_claim',
432
- description: 'Release a task claim, returning task to pending status.',
433
- inputSchema: {
434
- type: 'object',
435
- properties: {
436
- team_name: { type: 'string', description: 'Sanitized team name' },
437
- task_id: { type: 'string', description: 'Task ID' },
438
- claim_token: { type: 'string', description: 'Claim token from the claim operation' },
439
- worker: { type: 'string', description: 'Worker name that holds the claim' },
440
- workingDirectory: { type: 'string' },
441
- },
442
- required: ['team_name', 'task_id', 'claim_token', 'worker'],
443
- },
444
- },
445
- {
446
- name: 'team_read_config',
447
- description: 'Read team configuration (workers, tmux session, task counter).',
448
- inputSchema: {
449
- type: 'object',
450
- properties: {
451
- team_name: { type: 'string', description: 'Sanitized team name' },
452
- workingDirectory: { type: 'string' },
453
- },
454
- required: ['team_name'],
455
- },
456
- },
457
- {
458
- name: 'team_read_manifest',
459
- description: 'Read team manifest v2 (leader, policy, permissions snapshot).',
460
- inputSchema: {
461
- type: 'object',
462
- properties: {
463
- team_name: { type: 'string', description: 'Sanitized team name' },
464
- workingDirectory: { type: 'string' },
465
- },
466
- required: ['team_name'],
467
- },
468
- },
469
- {
470
- name: 'team_read_worker_status',
471
- description: 'Read current worker status (state, current_task_id, reason).',
472
- inputSchema: {
473
- type: 'object',
474
- properties: {
475
- team_name: { type: 'string', description: 'Sanitized team name' },
476
- worker: { type: 'string', description: 'Worker name (e.g., worker-1)' },
477
- workingDirectory: { type: 'string' },
478
- },
479
- required: ['team_name', 'worker'],
480
- },
481
- },
482
- {
483
- name: 'team_read_worker_heartbeat',
484
- description: 'Read worker heartbeat (pid, turn count, alive flag).',
485
- inputSchema: {
486
- type: 'object',
487
- properties: {
488
- team_name: { type: 'string', description: 'Sanitized team name' },
489
- worker: { type: 'string', description: 'Worker name' },
490
- workingDirectory: { type: 'string' },
491
- },
492
- required: ['team_name', 'worker'],
493
- },
494
- },
495
- {
496
- name: 'team_update_worker_heartbeat',
497
- description: 'Write/update a worker heartbeat.',
498
- inputSchema: {
499
- type: 'object',
500
- properties: {
501
- team_name: { type: 'string', description: 'Sanitized team name' },
502
- worker: { type: 'string', description: 'Worker name' },
503
- pid: { type: 'number', description: 'Worker process ID' },
504
- turn_count: { type: 'number', description: 'Cumulative turn count' },
505
- alive: { type: 'boolean', description: 'Whether the worker is alive' },
506
- workingDirectory: { type: 'string' },
507
- },
508
- required: ['team_name', 'worker', 'pid', 'turn_count', 'alive'],
509
- },
510
- },
511
- {
512
- name: 'team_write_worker_inbox',
513
- description: 'Write a prompt/instruction to a worker inbox file.',
514
- inputSchema: {
515
- type: 'object',
516
- properties: {
517
- team_name: { type: 'string', description: 'Sanitized team name' },
518
- worker: { type: 'string', description: 'Worker name' },
519
- content: { type: 'string', description: 'Inbox content (markdown)' },
520
- workingDirectory: { type: 'string' },
521
- },
522
- required: ['team_name', 'worker', 'content'],
523
- },
524
- },
525
- {
526
- name: 'team_write_worker_identity',
527
- description: 'Write worker identity file (name, index, role, assigned tasks).',
528
- inputSchema: {
529
- type: 'object',
530
- properties: {
531
- team_name: { type: 'string', description: 'Sanitized team name' },
532
- worker: { type: 'string', description: 'Worker name' },
533
- index: { type: 'number', description: 'Worker index (1-based)' },
534
- role: { type: 'string', description: 'Agent role/type' },
535
- assigned_tasks: { type: 'array', items: { type: 'string' }, description: 'Assigned task IDs' },
536
- pid: { type: 'number', description: 'Worker process ID (optional)' },
537
- pane_id: { type: 'string', description: 'Tmux pane ID (optional)' },
538
- working_dir: { type: 'string', description: 'Worker working directory (optional)' },
539
- worktree_path: { type: 'string', description: 'Worker git worktree path (optional)' },
540
- worktree_branch: { type: 'string', description: 'Worker git worktree branch (optional)' },
541
- worktree_detached: { type: 'boolean', description: 'Whether worker worktree is detached (optional)' },
542
- team_state_root: { type: 'string', description: 'Canonical team state root path (optional)' },
543
- workingDirectory: { type: 'string' },
544
- },
545
- required: ['team_name', 'worker', 'index', 'role'],
546
- },
547
- },
548
- {
549
- name: 'team_append_event',
550
- description: 'Append an event to the team event log (ndjson).',
551
- inputSchema: {
552
- type: 'object',
553
- properties: {
554
- team_name: { type: 'string', description: 'Sanitized team name' },
555
- type: { type: 'string', enum: [...TEAM_EVENT_TYPES] },
556
- worker: { type: 'string', description: 'Worker name associated with the event' },
557
- task_id: { type: 'string', description: 'Related task ID (optional)' },
558
- message_id: { type: 'string', description: 'Related message ID (optional)' },
559
- reason: { type: 'string', description: 'Event reason (optional)' },
560
- workingDirectory: { type: 'string' },
561
- },
562
- required: ['team_name', 'type', 'worker'],
563
- },
564
- },
565
- {
566
- name: 'team_get_summary',
567
- description: 'Get team summary with task counts, worker status, and non-reporting detection.',
568
- inputSchema: {
569
- type: 'object',
570
- properties: {
571
- team_name: { type: 'string', description: 'Sanitized team name' },
572
- workingDirectory: { type: 'string' },
573
- },
574
- required: ['team_name'],
575
- },
576
- },
577
- {
578
- name: 'team_cleanup',
579
- description: 'Delete all team state files on disk (config, tasks, workers, events). Does NOT kill tmux panes -- use omx_run_team_cleanup for that.',
580
- inputSchema: {
581
- type: 'object',
582
- properties: {
583
- team_name: { type: 'string', description: 'Sanitized team name' },
584
- workingDirectory: { type: 'string' },
585
- },
586
- required: ['team_name'],
587
- },
588
- },
589
- {
590
- name: 'team_write_shutdown_request',
591
- description: 'Write a shutdown request for a worker.',
592
- inputSchema: {
593
- type: 'object',
594
- properties: {
595
- team_name: { type: 'string', description: 'Sanitized team name' },
596
- worker: { type: 'string', description: 'Worker name to shut down' },
597
- requested_by: { type: 'string', description: 'Requester identity (e.g., leader-fixed)' },
598
- workingDirectory: { type: 'string' },
599
- },
600
- required: ['team_name', 'worker', 'requested_by'],
601
- },
602
- },
603
- {
604
- name: 'team_read_shutdown_ack',
605
- description: 'Read a worker shutdown acknowledgment.',
606
- inputSchema: {
607
- type: 'object',
608
- properties: {
609
- team_name: { type: 'string', description: 'Sanitized team name' },
610
- worker: { type: 'string', description: 'Worker name' },
611
- min_updated_at: { type: 'string', description: 'ISO timestamp - ignore acks older than this' },
612
- workingDirectory: { type: 'string' },
613
- },
614
- required: ['team_name', 'worker'],
615
- },
616
- },
617
- {
618
- name: 'team_read_monitor_snapshot',
619
- description: 'Read the monitor snapshot (task/worker state from last poll).',
620
- inputSchema: {
621
- type: 'object',
622
- properties: {
623
- team_name: { type: 'string', description: 'Sanitized team name' },
624
- workingDirectory: { type: 'string' },
625
- },
626
- required: ['team_name'],
627
- },
628
- },
629
- {
630
- name: 'team_write_monitor_snapshot',
631
- description: 'Write the monitor snapshot for change detection across poll cycles.',
632
- inputSchema: {
633
- type: 'object',
634
- properties: {
635
- team_name: { type: 'string', description: 'Sanitized team name' },
636
- snapshot: { type: 'object', description: 'Monitor snapshot data' },
637
- workingDirectory: { type: 'string' },
638
- },
639
- required: ['team_name', 'snapshot'],
640
- },
641
- },
642
- {
643
- name: 'team_read_task_approval',
644
- description: 'Read task approval record (for plan-approval-required policy).',
645
- inputSchema: {
646
- type: 'object',
647
- properties: {
648
- team_name: { type: 'string', description: 'Sanitized team name' },
649
- task_id: { type: 'string', description: 'Task ID' },
650
- workingDirectory: { type: 'string' },
651
- },
652
- required: ['team_name', 'task_id'],
653
- },
654
- },
655
- {
656
- name: 'team_write_task_approval',
657
- description: 'Write a task approval decision.',
658
- inputSchema: {
659
- type: 'object',
660
- properties: {
661
- team_name: { type: 'string', description: 'Sanitized team name' },
662
- task_id: { type: 'string', description: 'Task ID' },
663
- required: { type: 'boolean', description: 'Whether approval was required' },
664
- status: { type: 'string', enum: [...TEAM_TASK_APPROVAL_STATUSES] },
665
- reviewer: { type: 'string', description: 'Reviewer identity' },
666
- decision_reason: { type: 'string', description: 'Reason for the decision' },
667
- workingDirectory: { type: 'string' },
668
- },
669
- required: ['team_name', 'task_id', 'status', 'reviewer', 'decision_reason'],
670
- },
671
- },
672
- ],
165
+ ];
166
+ }
167
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
168
+ tools: buildStateServerTools(),
673
169
  }));
674
170
  export async function handleStateToolCall(request) {
675
171
  const { name, arguments: args } = request.params;
@@ -680,7 +176,12 @@ export async function handleStateToolCall(request) {
680
176
  }
681
177
  catch (error) {
682
178
  return {
683
- content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }],
179
+ content: [
180
+ {
181
+ type: "text",
182
+ text: JSON.stringify({ error: error.message }),
183
+ },
184
+ ],
684
185
  isError: true,
685
186
  };
686
187
  }
@@ -691,7 +192,12 @@ export async function handleStateToolCall(request) {
691
192
  }
692
193
  catch (error) {
693
194
  return {
694
- content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }],
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: JSON.stringify({ error: error.message }),
199
+ },
200
+ ],
695
201
  isError: true,
696
202
  };
697
203
  }
@@ -705,55 +211,54 @@ export async function handleStateToolCall(request) {
705
211
  if (effectiveSessionId) {
706
212
  await mkdir(getStateDir(cwd, effectiveSessionId), { recursive: true });
707
213
  }
214
+ const { ensureTmuxHookInitialized } = await import("../cli/tmux-hook.js");
708
215
  await ensureTmuxHookInitialized(cwd);
709
216
  }
710
217
  if (TEAM_COMM_TOOL_NAMES.has(name)) {
711
- const teamName = String(args?.team_name || '').trim();
712
- if (teamName && !TEAM_NAME_SAFE_PATTERN.test(teamName)) {
713
- return {
714
- content: [{ type: 'text', text: JSON.stringify({ error: `Invalid team_name: "${teamName}". Must match /^[a-z0-9][a-z0-9-]{0,29}$/ (lowercase alphanumeric + hyphens, max 30 chars).` }) }],
715
- isError: true,
716
- };
717
- }
718
- for (const workerField of ['worker', 'from_worker', 'to_worker']) {
719
- const workerVal = String(args?.[workerField] || '').trim();
720
- if (workerVal && !WORKER_NAME_SAFE_PATTERN.test(workerVal)) {
721
- return {
722
- content: [{ type: 'text', text: JSON.stringify({ error: `Invalid ${workerField}: "${workerVal}". Must match /^[a-z0-9][a-z0-9-]{0,63}$/ (lowercase alphanumeric + hyphens, max 64 chars).` }) }],
723
- isError: true,
724
- };
725
- }
726
- }
727
- const rawTaskId = String(args?.task_id || '').trim();
728
- if (rawTaskId && !TASK_ID_SAFE_PATTERN.test(rawTaskId)) {
729
- return {
730
- content: [{ type: 'text', text: JSON.stringify({ error: `Invalid task_id: "${rawTaskId}". Must be a positive integer (digits only, max 20 digits).` }) }],
731
- isError: true,
732
- };
733
- }
734
- if (teamName) {
735
- cwd = resolveTeamWorkingDirectory(teamName, cwd);
736
- }
737
- await mkdir(getStateDir(cwd, explicitSessionId), { recursive: true });
218
+ const hint = buildLegacyTeamDeprecationHint(name, args ?? {});
219
+ return {
220
+ content: [
221
+ {
222
+ type: "text",
223
+ text: JSON.stringify({
224
+ error: `MCP tool "${name}" is hard-deprecated. Team mutations now require CLI interop.`,
225
+ code: "deprecated_cli_only",
226
+ hint,
227
+ }),
228
+ },
229
+ ],
230
+ isError: true,
231
+ };
738
232
  }
739
233
  switch (name) {
740
- case 'state_read': {
234
+ case "state_read": {
741
235
  const mode = args.mode;
742
236
  if (!SUPPORTED_MODES.includes(mode)) {
743
237
  return {
744
- content: [{ type: 'text', text: JSON.stringify({ error: `mode must be one of: ${SUPPORTED_MODES.join(', ')}` }) }],
238
+ content: [
239
+ {
240
+ type: "text",
241
+ text: JSON.stringify({
242
+ error: `mode must be one of: ${SUPPORTED_MODES.join(", ")}`,
243
+ }),
244
+ },
245
+ ],
745
246
  isError: true,
746
247
  };
747
248
  }
748
249
  const paths = await getReadScopedStatePaths(mode, cwd, explicitSessionId);
749
250
  const path = paths.find((candidate) => existsSync(candidate));
750
251
  if (!path) {
751
- return { content: [{ type: 'text', text: JSON.stringify({ exists: false, mode }) }] };
252
+ return {
253
+ content: [
254
+ { type: "text", text: JSON.stringify({ exists: false, mode }) },
255
+ ],
256
+ };
752
257
  }
753
- const data = await readFile(path, 'utf-8');
754
- return { content: [{ type: 'text', text: data }] };
258
+ const data = await readFile(path, "utf-8");
259
+ return { content: [{ type: "text", text: data }] };
755
260
  }
756
- case 'state_write': {
261
+ case "state_write": {
757
262
  const mode = args.mode;
758
263
  const path = getStatePath(mode, cwd, effectiveSessionId);
759
264
  const { mode: _m, workingDirectory: _w, session_id: _sid, state: customState, ...fields } = args;
@@ -762,21 +267,29 @@ export async function handleStateToolCall(request) {
762
267
  let existing = {};
763
268
  if (existsSync(path)) {
764
269
  try {
765
- existing = JSON.parse(await readFile(path, 'utf-8'));
270
+ existing = JSON.parse(await readFile(path, "utf-8"));
271
+ }
272
+ catch (e) {
273
+ process.stderr.write("[state-server] Failed to parse state file: " + e + "\n");
766
274
  }
767
- catch { /* start fresh */ }
768
275
  }
769
- const mergedRaw = { ...existing, ...fields, ...(customState || {}) };
770
- if (mode === 'ralph') {
276
+ const mergedRaw = {
277
+ ...existing,
278
+ ...fields,
279
+ ...(customState || {}),
280
+ };
281
+ if (mode === "ralph") {
771
282
  const originalPhase = mergedRaw.current_phase;
772
283
  const validation = validateAndNormalizeRalphState(mergedRaw);
773
284
  if (!validation.ok || !validation.state) {
774
- validationError = validation.error || `ralph.current_phase must be one of: ${RALPH_PHASES.join(', ')}`;
285
+ validationError =
286
+ validation.error ||
287
+ `ralph.current_phase must be one of: ${RALPH_PHASES.join(", ")}`;
775
288
  return;
776
289
  }
777
- if (typeof originalPhase === 'string'
778
- && typeof validation.state.current_phase === 'string'
779
- && validation.state.current_phase !== originalPhase) {
290
+ if (typeof originalPhase === "string" &&
291
+ typeof validation.state.current_phase === "string" &&
292
+ validation.state.current_phase !== originalPhase) {
780
293
  validation.state.ralph_phase_normalized_from = originalPhase;
781
294
  }
782
295
  Object.assign(mergedRaw, validation.state);
@@ -787,16 +300,25 @@ export async function handleStateToolCall(request) {
787
300
  });
788
301
  if (validationError) {
789
302
  return {
790
- content: [{
791
- type: 'text',
303
+ content: [
304
+ {
305
+ type: "text",
792
306
  text: JSON.stringify({ error: validationError }),
793
- }],
307
+ },
308
+ ],
794
309
  isError: true,
795
310
  };
796
311
  }
797
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, mode, path }) }] };
312
+ return {
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text: JSON.stringify({ success: true, mode, path }),
317
+ },
318
+ ],
319
+ };
798
320
  }
799
- case 'state_clear': {
321
+ case "state_clear": {
800
322
  const mode = args.mode;
801
323
  const allSessions = args.all_sessions === true;
802
324
  if (!allSessions) {
@@ -804,7 +326,14 @@ export async function handleStateToolCall(request) {
804
326
  if (existsSync(path)) {
805
327
  await unlink(path);
806
328
  }
807
- return { content: [{ type: 'text', text: JSON.stringify({ cleared: true, mode, path }) }] };
329
+ return {
330
+ content: [
331
+ {
332
+ type: "text",
333
+ text: JSON.stringify({ cleared: true, mode, path }),
334
+ },
335
+ ],
336
+ };
808
337
  }
809
338
  const removedPaths = [];
810
339
  const paths = await getAllScopedStatePaths(mode, cwd);
@@ -815,20 +344,22 @@ export async function handleStateToolCall(request) {
815
344
  removedPaths.push(path);
816
345
  }
817
346
  return {
818
- content: [{
819
- type: 'text',
347
+ content: [
348
+ {
349
+ type: "text",
820
350
  text: JSON.stringify({
821
351
  cleared: true,
822
352
  mode,
823
353
  all_sessions: true,
824
354
  removed: removedPaths.length,
825
355
  paths: removedPaths,
826
- warning: 'all_sessions clears global and session-scoped state files',
356
+ warning: "all_sessions clears global and session-scoped state files",
827
357
  }),
828
- }],
358
+ },
359
+ ],
829
360
  };
830
361
  }
831
- case 'state_list_active': {
362
+ case "state_list_active": {
832
363
  const stateDirs = await getReadScopedStateDirs(cwd, explicitSessionId);
833
364
  const active = [];
834
365
  const seenModes = new Set();
@@ -837,24 +368,30 @@ export async function handleStateToolCall(request) {
837
368
  continue;
838
369
  const files = await readdir(stateDir);
839
370
  for (const f of files) {
840
- if (!f.endsWith('-state.json'))
371
+ if (!f.endsWith("-state.json"))
841
372
  continue;
842
- const mode = f.replace('-state.json', '');
373
+ const mode = f.replace("-state.json", "");
843
374
  if (seenModes.has(mode))
844
375
  continue;
845
376
  seenModes.add(mode);
846
377
  try {
847
- const data = JSON.parse(await readFile(join(stateDir, f), 'utf-8'));
378
+ const data = JSON.parse(await readFile(join(stateDir, f), "utf-8"));
848
379
  if (data.active) {
849
380
  active.push(mode);
850
381
  }
851
382
  }
852
- catch { /* skip malformed */ }
383
+ catch (e) {
384
+ process.stderr.write("[state-server] Failed to parse state file: " + e + "\n");
385
+ }
853
386
  }
854
387
  }
855
- return { content: [{ type: 'text', text: JSON.stringify({ active_modes: active }) }] };
388
+ return {
389
+ content: [
390
+ { type: "text", text: JSON.stringify({ active_modes: active }) },
391
+ ],
392
+ };
856
393
  }
857
- case 'state_get_status': {
394
+ case "state_get_status": {
858
395
  const mode = args?.mode;
859
396
  const stateDirs = await getReadScopedStateDirs(cwd, explicitSessionId);
860
397
  const statuses = {};
@@ -864,481 +401,54 @@ export async function handleStateToolCall(request) {
864
401
  continue;
865
402
  const files = await readdir(stateDir);
866
403
  for (const f of files) {
867
- if (!f.endsWith('-state.json'))
404
+ if (!f.endsWith("-state.json"))
868
405
  continue;
869
- const m = f.replace('-state.json', '');
406
+ const m = f.replace("-state.json", "");
870
407
  if (mode && m !== mode)
871
408
  continue;
872
409
  if (seenModes.has(m))
873
410
  continue;
874
411
  seenModes.add(m);
875
412
  try {
876
- const data = JSON.parse(await readFile(join(stateDir, f), 'utf-8'));
877
- statuses[m] = { active: data.active, phase: data.current_phase, path: join(stateDir, f), data };
413
+ const data = JSON.parse(await readFile(join(stateDir, f), "utf-8"));
414
+ statuses[m] = {
415
+ active: data.active,
416
+ phase: data.current_phase,
417
+ path: join(stateDir, f),
418
+ data,
419
+ };
878
420
  }
879
421
  catch {
880
- statuses[m] = { error: 'malformed state file' };
422
+ statuses[m] = { error: "malformed state file" };
881
423
  }
882
424
  }
883
425
  }
884
- return { content: [{ type: 'text', text: JSON.stringify({ statuses }) }] };
885
- }
886
- case 'team_send_message': {
887
- const teamName = String(args.team_name || '').trim();
888
- const fromWorker = String(args.from_worker || '').trim();
889
- const toWorker = String(args.to_worker || '').trim();
890
- const body = String(args.body || '').trim();
891
- if (!fromWorker) {
892
- return {
893
- content: [{ type: 'text', text: JSON.stringify({
894
- error: 'from_worker is required. You must identify yourself. Check your worker name in your inbox file or AGENTS.md overlay.',
895
- hint: 'Your worker name was set when you were spawned (e.g., "worker-1", "worker-2", or "leader-fixed").'
896
- }) }],
897
- isError: true,
898
- };
899
- }
900
- if (!teamName || !toWorker || !body) {
901
- return {
902
- content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, from_worker, to_worker, body are required' }) }],
903
- isError: true,
904
- };
905
- }
906
- const message = await sendDirectMessage(teamName, fromWorker, toWorker, body, cwd);
907
- return {
908
- content: [{ type: 'text', text: JSON.stringify({ ok: true, message }) }],
909
- };
910
- }
911
- case 'team_broadcast': {
912
- const teamName = String(args.team_name || '').trim();
913
- const fromWorker = String(args.from_worker || '').trim();
914
- const body = String(args.body || '').trim();
915
- if (!teamName || !fromWorker || !body) {
916
- return {
917
- content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, from_worker, body are required' }) }],
918
- isError: true,
919
- };
920
- }
921
- const messages = await broadcastMessage(teamName, fromWorker, body, cwd);
922
426
  return {
923
- content: [{ type: 'text', text: JSON.stringify({ ok: true, count: messages.length, messages }) }],
427
+ content: [{ type: "text", text: JSON.stringify({ statuses }) }],
924
428
  };
925
429
  }
926
- case 'team_mailbox_list': {
927
- const teamName = String(args.team_name || '').trim();
928
- const worker = String(args.worker || '').trim();
929
- const includeDelivered = args.include_delivered !== false;
930
- if (!teamName || !worker) {
931
- return {
932
- content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and worker are required' }) }],
933
- isError: true,
934
- };
935
- }
936
- const all = await listMailboxMessages(teamName, worker, cwd);
937
- const messages = includeDelivered ? all : all.filter((m) => !m.delivered_at);
938
- return {
939
- content: [{ type: 'text', text: JSON.stringify({ ok: true, worker, count: messages.length, messages }) }],
940
- };
941
- }
942
- case 'team_mailbox_mark_delivered': {
943
- const teamName = String(args.team_name || '').trim();
944
- const worker = String(args.worker || '').trim();
945
- const messageId = String(args.message_id || '').trim();
946
- if (!teamName || !worker || !messageId) {
947
- return {
948
- content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, message_id are required' }) }],
949
- isError: true,
950
- };
951
- }
952
- const updated = await markMessageDelivered(teamName, worker, messageId, cwd);
430
+ default:
953
431
  return {
954
- content: [{ type: 'text', text: JSON.stringify({ ok: true, updated, worker, message_id: messageId }) }],
432
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
433
+ isError: true,
955
434
  };
956
- }
957
- case 'team_mailbox_mark_notified': {
958
- const teamName = String(args.team_name || '').trim();
959
- const worker = String(args.worker || '').trim();
960
- const messageId = String(args.message_id || '').trim();
961
- if (!teamName || !worker || !messageId) {
962
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, message_id are required' }) }], isError: true };
963
- }
964
- const notified = await markMessageNotified(teamName, worker, messageId, cwd);
965
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, notified, worker, message_id: messageId }) }] };
966
- }
967
- case 'team_create_task': {
968
- const teamName = String(args.team_name || '').trim();
969
- const subject = String(args.subject || '').trim();
970
- const description = String(args.description || '').trim();
971
- if (!teamName || !subject || !description) {
972
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, subject, description are required' }) }], isError: true };
973
- }
974
- const owner = args.owner;
975
- const blockedBy = args.blocked_by;
976
- const requiresCodeChange = args.requires_code_change;
977
- const task = await teamCreateTask(teamName, {
978
- subject, description, status: 'pending',
979
- owner: owner || undefined,
980
- blocked_by: blockedBy,
981
- requires_code_change: requiresCodeChange,
982
- }, cwd);
983
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, task }) }] };
984
- }
985
- case 'team_read_task': {
986
- const teamName = String(args.team_name || '').trim();
987
- const taskId = String(args.task_id || '').trim();
988
- if (!teamName || !taskId) {
989
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and task_id are required' }) }], isError: true };
990
- }
991
- const task = await teamReadTask(teamName, taskId, cwd);
992
- if (!task)
993
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'task_not_found' }) }] };
994
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, task }) }] };
995
- }
996
- case 'team_list_tasks': {
997
- const teamName = String(args.team_name || '').trim();
998
- if (!teamName) {
999
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1000
- }
1001
- const tasks = await teamListTasks(teamName, cwd);
1002
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, count: tasks.length, tasks }) }] };
1003
- }
1004
- case 'team_update_task': {
1005
- const teamName = String(args.team_name || '').trim();
1006
- const taskId = String(args.task_id || '').trim();
1007
- if (!teamName || !taskId) {
1008
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and task_id are required' }) }], isError: true };
1009
- }
1010
- const lifecycleFields = ['status', 'owner', 'result', 'error'];
1011
- const presentLifecycleFields = lifecycleFields.filter((f) => f in args);
1012
- if (presentLifecycleFields.length > 0) {
1013
- return {
1014
- content: [{
1015
- type: 'text',
1016
- text: JSON.stringify({
1017
- error: `team_update_task cannot mutate lifecycle fields: ${presentLifecycleFields.join(', ')}. Use team_claim_task + team_transition_task_status to change task status/owner/result/error.`,
1018
- }),
1019
- }],
1020
- isError: true,
1021
- };
1022
- }
1023
- const unexpectedFields = Object.keys(args).filter((field) => !TEAM_UPDATE_TASK_REQUEST_FIELDS.has(field));
1024
- if (unexpectedFields.length > 0) {
1025
- return {
1026
- content: [{
1027
- type: 'text',
1028
- text: JSON.stringify({
1029
- error: `team_update_task received unsupported fields: ${unexpectedFields.join(', ')}. Allowed mutable fields: subject, description, blocked_by, requires_code_change.`,
1030
- }),
1031
- }],
1032
- isError: true,
1033
- };
1034
- }
1035
- const updates = {};
1036
- if ('subject' in args) {
1037
- const subject = args.subject;
1038
- if (typeof subject !== 'string') {
1039
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'subject must be a string when provided' }) }], isError: true };
1040
- }
1041
- updates.subject = subject.trim();
1042
- }
1043
- if ('description' in args) {
1044
- const description = args.description;
1045
- if (typeof description !== 'string') {
1046
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'description must be a string when provided' }) }], isError: true };
1047
- }
1048
- updates.description = description.trim();
1049
- }
1050
- if ('requires_code_change' in args) {
1051
- const requiresCodeChange = args.requires_code_change;
1052
- if (typeof requiresCodeChange !== 'boolean') {
1053
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'requires_code_change must be a boolean when provided' }) }], isError: true };
1054
- }
1055
- updates.requires_code_change = requiresCodeChange;
1056
- }
1057
- if ('blocked_by' in args) {
1058
- try {
1059
- updates.blocked_by = parseValidatedTaskIdArray(args.blocked_by, 'blocked_by');
1060
- }
1061
- catch (error) {
1062
- return { content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }], isError: true };
1063
- }
1064
- }
1065
- const task = await teamUpdateTask(teamName, taskId, updates, cwd);
1066
- if (!task)
1067
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'task_not_found' }) }] };
1068
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, task }) }] };
1069
- }
1070
- case 'team_claim_task': {
1071
- const teamName = String(args.team_name || '').trim();
1072
- const taskId = String(args.task_id || '').trim();
1073
- const worker = String(args.worker || '').trim();
1074
- if (!teamName || !taskId || !worker) {
1075
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, task_id, worker are required' }) }], isError: true };
1076
- }
1077
- const rawExpectedVersion = args.expected_version;
1078
- if (rawExpectedVersion !== undefined && (!isFiniteInteger(rawExpectedVersion) || rawExpectedVersion < 1)) {
1079
- return {
1080
- content: [{ type: 'text', text: JSON.stringify({ error: 'expected_version must be a positive integer when provided' }) }],
1081
- isError: true,
1082
- };
1083
- }
1084
- const expectedVersion = rawExpectedVersion;
1085
- const result = await teamClaimTask(teamName, taskId, worker, expectedVersion ?? null, cwd);
1086
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
1087
- }
1088
- case 'team_transition_task_status': {
1089
- const teamName = String(args.team_name || '').trim();
1090
- const taskId = String(args.task_id || '').trim();
1091
- const from = String(args.from || '').trim();
1092
- const to = String(args.to || '').trim();
1093
- const claimToken = String(args.claim_token || '').trim();
1094
- if (!teamName || !taskId || !from || !to || !claimToken) {
1095
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, task_id, from, to, claim_token are required' }) }], isError: true };
1096
- }
1097
- const allowed = new Set(TEAM_TASK_STATUSES);
1098
- if (!allowed.has(from) || !allowed.has(to)) {
1099
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'from and to must be valid task statuses' }) }], isError: true };
1100
- }
1101
- const result = await teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd);
1102
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
1103
- }
1104
- case 'team_release_task_claim': {
1105
- const teamName = String(args.team_name || '').trim();
1106
- const taskId = String(args.task_id || '').trim();
1107
- const claimToken = String(args.claim_token || '').trim();
1108
- const worker = String(args.worker || '').trim();
1109
- if (!teamName || !taskId || !claimToken || !worker) {
1110
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, task_id, claim_token, worker are required' }) }], isError: true };
1111
- }
1112
- const result = await teamReleaseTaskClaim(teamName, taskId, claimToken, worker, cwd);
1113
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
1114
- }
1115
- case 'team_read_config': {
1116
- const teamName = String(args.team_name || '').trim();
1117
- if (!teamName) {
1118
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1119
- }
1120
- const config = await teamReadConfig(teamName, cwd);
1121
- if (!config)
1122
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'team_not_found' }) }] };
1123
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, config }) }] };
1124
- }
1125
- case 'team_read_manifest': {
1126
- const teamName = String(args.team_name || '').trim();
1127
- if (!teamName) {
1128
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1129
- }
1130
- const manifest = await teamReadManifest(teamName, cwd);
1131
- if (!manifest)
1132
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'manifest_not_found' }) }] };
1133
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, manifest }) }] };
1134
- }
1135
- case 'team_read_worker_status': {
1136
- const teamName = String(args.team_name || '').trim();
1137
- const worker = String(args.worker || '').trim();
1138
- if (!teamName || !worker) {
1139
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and worker are required' }) }], isError: true };
1140
- }
1141
- const status = await teamReadWorkerStatus(teamName, worker, cwd);
1142
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker, status }) }] };
1143
- }
1144
- case 'team_read_worker_heartbeat': {
1145
- const teamName = String(args.team_name || '').trim();
1146
- const worker = String(args.worker || '').trim();
1147
- if (!teamName || !worker) {
1148
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and worker are required' }) }], isError: true };
1149
- }
1150
- const heartbeat = await teamReadWorkerHeartbeat(teamName, worker, cwd);
1151
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker, heartbeat }) }] };
1152
- }
1153
- case 'team_update_worker_heartbeat': {
1154
- const teamName = String(args.team_name || '').trim();
1155
- const worker = String(args.worker || '').trim();
1156
- const pid = args.pid;
1157
- const turnCount = args.turn_count;
1158
- const alive = args.alive;
1159
- if (!teamName || !worker || typeof pid !== 'number' || typeof turnCount !== 'number' || typeof alive !== 'boolean') {
1160
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, pid, turn_count, alive are required' }) }], isError: true };
1161
- }
1162
- await teamUpdateWorkerHeartbeat(teamName, worker, { pid, turn_count: turnCount, alive, last_turn_at: new Date().toISOString() }, cwd);
1163
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker }) }] };
1164
- }
1165
- case 'team_write_worker_inbox': {
1166
- const teamName = String(args.team_name || '').trim();
1167
- const worker = String(args.worker || '').trim();
1168
- const content = String(args.content || '').trim();
1169
- if (!teamName || !worker || !content) {
1170
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, content are required' }) }], isError: true };
1171
- }
1172
- await teamWriteWorkerInbox(teamName, worker, content, cwd);
1173
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker }) }] };
1174
- }
1175
- case 'team_write_worker_identity': {
1176
- const teamName = String(args.team_name || '').trim();
1177
- const worker = String(args.worker || '').trim();
1178
- const index = args.index;
1179
- const role = String(args.role || '').trim();
1180
- if (!teamName || !worker || typeof index !== 'number' || !role) {
1181
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, index, role are required' }) }], isError: true };
1182
- }
1183
- const assignedTasks = args.assigned_tasks ?? [];
1184
- const pid = args.pid;
1185
- const paneId = args.pane_id;
1186
- const workingDir = args.working_dir;
1187
- const worktreePath = args.worktree_path;
1188
- const worktreeBranch = args.worktree_branch;
1189
- const worktreeDetached = args.worktree_detached;
1190
- const teamStateRoot = args.team_state_root;
1191
- await teamWriteWorkerIdentity(teamName, worker, {
1192
- name: worker,
1193
- index,
1194
- role,
1195
- assigned_tasks: assignedTasks,
1196
- pid,
1197
- pane_id: paneId,
1198
- working_dir: workingDir,
1199
- worktree_path: worktreePath,
1200
- worktree_branch: worktreeBranch,
1201
- worktree_detached: worktreeDetached,
1202
- team_state_root: teamStateRoot,
1203
- }, cwd);
1204
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker }) }] };
1205
- }
1206
- case 'team_append_event': {
1207
- const teamName = String(args.team_name || '').trim();
1208
- const eventType = String(args.type || '').trim();
1209
- const worker = String(args.worker || '').trim();
1210
- if (!teamName || !eventType || !worker) {
1211
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, type, worker are required' }) }], isError: true };
1212
- }
1213
- if (!TEAM_EVENT_TYPES.includes(eventType)) {
1214
- return {
1215
- content: [{
1216
- type: 'text',
1217
- text: JSON.stringify({ error: `type must be one of: ${TEAM_EVENT_TYPES.join(', ')}` }),
1218
- }],
1219
- isError: true,
1220
- };
1221
- }
1222
- const event = await teamAppendEvent(teamName, {
1223
- type: eventType,
1224
- worker,
1225
- task_id: args.task_id,
1226
- message_id: args.message_id ?? null,
1227
- reason: args.reason,
1228
- }, cwd);
1229
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, event }) }] };
1230
- }
1231
- case 'team_get_summary': {
1232
- const teamName = String(args.team_name || '').trim();
1233
- if (!teamName) {
1234
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1235
- }
1236
- const summary = await teamGetSummary(teamName, cwd);
1237
- if (!summary)
1238
- return { content: [{ type: 'text', text: JSON.stringify({ ok: false, error: 'team_not_found' }) }] };
1239
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, summary }) }] };
1240
- }
1241
- case 'team_cleanup': {
1242
- const teamName = String(args.team_name || '').trim();
1243
- if (!teamName) {
1244
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1245
- }
1246
- await teamCleanup(teamName, cwd);
1247
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, team_name: teamName }) }] };
1248
- }
1249
- case 'team_write_shutdown_request': {
1250
- const teamName = String(args.team_name || '').trim();
1251
- const worker = String(args.worker || '').trim();
1252
- const requestedBy = String(args.requested_by || '').trim();
1253
- if (!teamName || !worker || !requestedBy) {
1254
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, worker, requested_by are required' }) }], isError: true };
1255
- }
1256
- await teamWriteShutdownRequest(teamName, worker, requestedBy, cwd);
1257
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker }) }] };
1258
- }
1259
- case 'team_read_shutdown_ack': {
1260
- const teamName = String(args.team_name || '').trim();
1261
- const worker = String(args.worker || '').trim();
1262
- if (!teamName || !worker) {
1263
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and worker are required' }) }], isError: true };
1264
- }
1265
- const minUpdatedAt = args.min_updated_at;
1266
- const ack = await teamReadShutdownAck(teamName, worker, cwd, minUpdatedAt);
1267
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, worker, ack }) }] };
1268
- }
1269
- case 'team_read_monitor_snapshot': {
1270
- const teamName = String(args.team_name || '').trim();
1271
- if (!teamName) {
1272
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name is required' }) }], isError: true };
1273
- }
1274
- const snapshot = await teamReadMonitorSnapshot(teamName, cwd);
1275
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, snapshot }) }] };
1276
- }
1277
- case 'team_write_monitor_snapshot': {
1278
- const teamName = String(args.team_name || '').trim();
1279
- const snapshot = args.snapshot;
1280
- if (!teamName || !snapshot) {
1281
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and snapshot are required' }) }], isError: true };
1282
- }
1283
- await teamWriteMonitorSnapshot(teamName, snapshot, cwd);
1284
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true }) }] };
1285
- }
1286
- case 'team_read_task_approval': {
1287
- const teamName = String(args.team_name || '').trim();
1288
- const taskId = String(args.task_id || '').trim();
1289
- if (!teamName || !taskId) {
1290
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name and task_id are required' }) }], isError: true };
1291
- }
1292
- const approval = await teamReadTaskApproval(teamName, taskId, cwd);
1293
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, approval }) }] };
1294
- }
1295
- case 'team_write_task_approval': {
1296
- const teamName = String(args.team_name || '').trim();
1297
- const taskId = String(args.task_id || '').trim();
1298
- const status = String(args.status || '').trim();
1299
- const reviewer = String(args.reviewer || '').trim();
1300
- const decisionReason = String(args.decision_reason || '').trim();
1301
- if (!teamName || !taskId || !status || !reviewer || !decisionReason) {
1302
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'team_name, task_id, status, reviewer, decision_reason are required' }) }], isError: true };
1303
- }
1304
- if (!TEAM_TASK_APPROVAL_STATUSES.includes(status)) {
1305
- return {
1306
- content: [{
1307
- type: 'text',
1308
- text: JSON.stringify({ error: `status must be one of: ${TEAM_TASK_APPROVAL_STATUSES.join(', ')}` }),
1309
- }],
1310
- isError: true,
1311
- };
1312
- }
1313
- const rawRequired = args.required;
1314
- if (rawRequired !== undefined && typeof rawRequired !== 'boolean') {
1315
- return { content: [{ type: 'text', text: JSON.stringify({ error: 'required must be a boolean when provided' }) }], isError: true };
1316
- }
1317
- const required = rawRequired !== false;
1318
- await teamWriteTaskApproval(teamName, {
1319
- task_id: taskId,
1320
- required,
1321
- status: status,
1322
- reviewer,
1323
- decision_reason: decisionReason,
1324
- decided_at: new Date().toISOString(),
1325
- }, cwd);
1326
- return { content: [{ type: 'text', text: JSON.stringify({ ok: true, task_id: taskId, status }) }] };
1327
- }
1328
- default:
1329
- return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
1330
435
  }
1331
436
  }
1332
437
  catch (error) {
1333
438
  return {
1334
- content: [{ type: 'text', text: JSON.stringify({ error: error.message }) }],
439
+ content: [
440
+ {
441
+ type: "text",
442
+ text: JSON.stringify({ error: error.message }),
443
+ },
444
+ ],
1335
445
  isError: true,
1336
446
  };
1337
447
  }
1338
448
  }
1339
449
  server.setRequestHandler(CallToolRequestSchema, handleStateToolCall);
1340
450
  // Start server
1341
- if (shouldAutoStartMcpServer('state')) {
451
+ if (shouldAutoStartMcpServer("state")) {
1342
452
  const transport = new StdioServerTransport();
1343
453
  server.connect(transport).catch(console.error);
1344
454
  }