shadow-claw 1.27.1 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/.well-known/ai-catalog.json +11 -0
  2. package/.well-known/mcp/index.html +19 -0
  3. package/.well-known/mcp/server-card.json +52 -0
  4. package/.well-known/mcp/servers.json +15 -0
  5. package/.well-known/mcp.json +52 -0
  6. package/.well-known/oauth-protected-resource +10 -0
  7. package/.well-known/oauth-protected-resource.json +10 -0
  8. package/bin/assert-version-bump.mjs +61 -3
  9. package/bin/assert-version-bump.test.mjs +77 -1
  10. package/bin/build/build.mjs +99 -34
  11. package/bin/build/build.test.mjs +32 -0
  12. package/bin/cli.mjs +290 -237
  13. package/bin/cli.test.mjs +27 -0
  14. package/bin/commands/mcp.mjs +132 -2
  15. package/bin/commands/mcp.test.mjs +166 -0
  16. package/bin/utils/control-client.mjs +17 -0
  17. package/bin/utils/control-client.test.mjs +46 -0
  18. package/bin/version.mjs +208 -0
  19. package/bin/version.test.mjs +138 -0
  20. package/dist/electron/main.cjs +195 -185
  21. package/dist/lib/components/index.js +21 -21
  22. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  23. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  24. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  25. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  26. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  27. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  28. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  29. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  30. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  31. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  32. package/dist/lib/components/shadow-claw/shadow-claw.d.ts +1 -0
  33. package/dist/lib/components/shadow-claw/utils/setupAppLifecycle.d.ts +23 -0
  34. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  35. package/dist/lib/components/shadow-claw-channels.js +1 -1
  36. package/dist/lib/components/shadow-claw-chat.js +1 -1
  37. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  38. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  39. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  40. package/dist/lib/components/shadow-claw-files.js +1 -1
  41. package/dist/lib/components/shadow-claw-git.js +1 -1
  42. package/dist/lib/components/shadow-claw-llm.js +1 -1
  43. package/dist/lib/components/shadow-claw-networking.js +1 -1
  44. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  45. package/dist/lib/components/shadow-claw-pages.js +1 -1
  46. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  47. package/dist/lib/components/shadow-claw-settings.js +1 -1
  48. package/dist/lib/components/shadow-claw-storage.js +1 -1
  49. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  50. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  51. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  52. package/dist/lib/components/shadow-claw-tools.js +1 -1
  53. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  54. package/dist/lib/components/shadow-claw.js +1 -1
  55. package/dist/lib/{configurePeerJs-oODzltaC.js → configurePeerJs-D8Ph0-A9.js} +2 -2
  56. package/dist/lib/{configurePeerJs-oODzltaC.js.map → configurePeerJs-D8Ph0-A9.js.map} +1 -1
  57. package/dist/lib/core/control-plane-client.d.ts +2 -0
  58. package/dist/lib/core/orchestrator/orchestrator.d.ts +10 -0
  59. package/dist/lib/core/orchestrator/utils/operations/channel.d.ts +2 -2
  60. package/dist/lib/core/utils/initControlPlane.d.ts +6 -0
  61. package/dist/lib/index.js +21 -21
  62. package/dist/lib/{initControlPlane-C2zURVIS.js → initControlPlane-BIrWmpzc.js} +76 -9
  63. package/dist/lib/initControlPlane-BIrWmpzc.js.map +1 -0
  64. package/dist/lib/{markdown-C6EHl9zs.js → markdown-DEUoM1c6.js} +2 -2
  65. package/dist/lib/{markdown-C6EHl9zs.js.map → markdown-DEUoM1c6.js.map} +1 -1
  66. package/dist/lib/{orchestrator-KRjx0eba.js → orchestrator-BtVj9bBB.js} +11 -10
  67. package/dist/lib/{orchestrator-KRjx0eba.js.map → orchestrator-BtVj9bBB.js.map} +1 -1
  68. package/dist/lib/{orchestrator-CACLa_Ro.js → orchestrator-CFCW-l1O.js} +52 -4
  69. package/dist/lib/orchestrator-CFCW-l1O.js.map +1 -0
  70. package/dist/lib/{peerjs-DFWOBcXf.js → peerjs-BoBkx9DJ.js} +23 -3
  71. package/dist/lib/peerjs-BoBkx9DJ.js.map +1 -0
  72. package/dist/lib/{push-client-_tiWQsCT.js → push-client-D6hbzDNV.js} +10 -4
  73. package/dist/lib/push-client-D6hbzDNV.js.map +1 -0
  74. package/dist/lib/{shadow-claw-6UHdiy4a.js → shadow-claw-Cuf2O5bZ.js} +71 -4
  75. package/dist/lib/shadow-claw-Cuf2O5bZ.js.map +1 -0
  76. package/dist/lib/{shadow-claw-channel-config-DmfwJc6G.js → shadow-claw-channel-config-BDPKt6Bd.js} +3 -3
  77. package/dist/lib/{shadow-claw-channel-config-DmfwJc6G.js.map → shadow-claw-channel-config-BDPKt6Bd.js.map} +1 -1
  78. package/dist/lib/{shadow-claw-channels-1N6Jl-By.js → shadow-claw-channels-BsP5JABM.js} +2 -2
  79. package/dist/lib/{shadow-claw-channels-1N6Jl-By.js.map → shadow-claw-channels-BsP5JABM.js.map} +1 -1
  80. package/dist/lib/{shadow-claw-chat-BMoqQJRK.js → shadow-claw-chat-Cam2lX4k.js} +4 -4
  81. package/dist/lib/{shadow-claw-chat-BMoqQJRK.js.map → shadow-claw-chat-Cam2lX4k.js.map} +1 -1
  82. package/dist/lib/{shadow-claw-control-plane-UG0o7pCz.js → shadow-claw-control-plane-DB2qcCFs.js} +20 -10
  83. package/dist/lib/shadow-claw-control-plane-DB2qcCFs.js.map +1 -0
  84. package/dist/lib/{shadow-claw-conversations-BwXep3iu.js → shadow-claw-conversations-C9Vf4XGx.js} +2 -2
  85. package/dist/lib/{shadow-claw-conversations-BwXep3iu.js.map → shadow-claw-conversations-C9Vf4XGx.js.map} +1 -1
  86. package/dist/lib/{shadow-claw-file-viewer-DUxzVxDT.js → shadow-claw-file-viewer-B4HYiI0v.js} +3 -3
  87. package/dist/lib/{shadow-claw-file-viewer-DUxzVxDT.js.map → shadow-claw-file-viewer-B4HYiI0v.js.map} +1 -1
  88. package/dist/lib/{shadow-claw-files-DIREEnT2.js → shadow-claw-files-DVROUESO.js} +2 -2
  89. package/dist/lib/{shadow-claw-files-DIREEnT2.js.map → shadow-claw-files-DVROUESO.js.map} +1 -1
  90. package/dist/lib/{shadow-claw-git-Bg1SWgsF.js → shadow-claw-git-BqrO7cHJ.js} +3 -3
  91. package/dist/lib/{shadow-claw-git-Bg1SWgsF.js.map → shadow-claw-git-BqrO7cHJ.js.map} +1 -1
  92. package/dist/lib/{shadow-claw-llm-CazBrVpM.js → shadow-claw-llm-BexUQdYo.js} +2 -2
  93. package/dist/lib/{shadow-claw-llm-CazBrVpM.js.map → shadow-claw-llm-BexUQdYo.js.map} +1 -1
  94. package/dist/lib/{shadow-claw-networking-Df85tCbO.js → shadow-claw-networking-CfXRGpUD.js} +2 -2
  95. package/dist/lib/{shadow-claw-networking-Df85tCbO.js.map → shadow-claw-networking-CfXRGpUD.js.map} +1 -1
  96. package/dist/lib/{shadow-claw-notifications-v62FryTc.js → shadow-claw-notifications-C9KPMgx_.js} +2 -2
  97. package/dist/lib/{shadow-claw-notifications-v62FryTc.js.map → shadow-claw-notifications-C9KPMgx_.js.map} +1 -1
  98. package/dist/lib/{shadow-claw-pages-BgPyIEZO.js → shadow-claw-pages-CGzGXmrI.js} +3 -3
  99. package/dist/lib/{shadow-claw-pages-BgPyIEZO.js.map → shadow-claw-pages-CGzGXmrI.js.map} +1 -1
  100. package/dist/lib/{shadow-claw-peerjs-xkBXILed.js → shadow-claw-peerjs-B0ZB7yhB.js} +3 -3
  101. package/dist/lib/{shadow-claw-peerjs-xkBXILed.js.map → shadow-claw-peerjs-B0ZB7yhB.js.map} +1 -1
  102. package/dist/lib/{shadow-claw-settings-CN89xRVU.js → shadow-claw-settings-DBfMZdDS.js} +2 -2
  103. package/dist/lib/{shadow-claw-settings-CN89xRVU.js.map → shadow-claw-settings-DBfMZdDS.js.map} +1 -1
  104. package/dist/lib/{shadow-claw-storage-VnhcObCe.js → shadow-claw-storage-CM-w6mgG.js} +2 -2
  105. package/dist/lib/{shadow-claw-storage-VnhcObCe.js.map → shadow-claw-storage-CM-w6mgG.js.map} +1 -1
  106. package/dist/lib/{shadow-claw-task-server-jgEC2hHB.js → shadow-claw-task-server-DV4b1g2-.js} +4 -2
  107. package/dist/lib/{shadow-claw-task-server-jgEC2hHB.js.map → shadow-claw-task-server-DV4b1g2-.js.map} +1 -1
  108. package/dist/lib/{shadow-claw-tasks-i_evIufy.js → shadow-claw-tasks-K-foOHSE.js} +3 -3
  109. package/dist/lib/{shadow-claw-tasks-i_evIufy.js.map → shadow-claw-tasks-K-foOHSE.js.map} +1 -1
  110. package/dist/lib/{shadow-claw-terminal-CwScROlu.js → shadow-claw-terminal-CpAknKyA.js} +2 -2
  111. package/dist/lib/{shadow-claw-terminal-CwScROlu.js.map → shadow-claw-terminal-CpAknKyA.js.map} +1 -1
  112. package/dist/lib/{shadow-claw-tools-DD3fyXP4.js → shadow-claw-tools-D98XZ0Ep.js} +3 -3
  113. package/dist/lib/{shadow-claw-tools-DD3fyXP4.js.map → shadow-claw-tools-D98XZ0Ep.js.map} +1 -1
  114. package/dist/lib/{shadow-claw-webvm-DbJq4I3-.js → shadow-claw-webvm-DLOb8dMI.js} +2 -2
  115. package/dist/lib/{shadow-claw-webvm-DbJq4I3-.js.map → shadow-claw-webvm-DLOb8dMI.js.map} +1 -1
  116. package/dist/lib/subsystems/channels/imessage.d.ts +1 -0
  117. package/dist/lib/subsystems/channels/peerjs.d.ts +1 -0
  118. package/dist/lib/subsystems/channels/telegram.d.ts +1 -0
  119. package/dist/lib/subsystems/channels/types.d.ts +2 -0
  120. package/dist/lib/subsystems/notifications/push-client.d.ts +1 -1
  121. package/dist/lib/{syncWebMcpRegistration-DNLnQ-jz.js → syncWebMcpRegistration-NspKefOI.js} +2 -2
  122. package/dist/lib/{syncWebMcpRegistration-DNLnQ-jz.js.map → syncWebMcpRegistration-NspKefOI.js.map} +1 -1
  123. package/dist/public/.well-known/ai-catalog.json +11 -0
  124. package/dist/public/.well-known/mcp/index.html +19 -0
  125. package/dist/public/.well-known/mcp/server-card.json +52 -0
  126. package/dist/public/.well-known/mcp/servers.json +15 -0
  127. package/dist/public/.well-known/mcp.json +52 -0
  128. package/dist/public/.well-known/oauth-protected-resource +10 -0
  129. package/dist/public/.well-known/oauth-protected-resource.json +10 -0
  130. package/dist/public/404.html +28 -1
  131. package/dist/public/agent.worker.js +1 -1
  132. package/dist/public/{configurePeerJs-BqgeBjP7.js → configurePeerJs-BhBokQlC.js} +1 -1
  133. package/dist/public/docs/example/article/index.html +3 -3
  134. package/dist/public/docs/publishing/index.html +3 -3
  135. package/dist/public/docs/skill-creator/index.html +3 -3
  136. package/dist/public/docs/subsystems/mcp-server.md +1 -0
  137. package/dist/public/docs/subsystems/notifications.md +10 -0
  138. package/dist/public/{e2e-bridge-Ceqr5kGc.js → e2e-bridge-DcBoeFvd.js} +1 -1
  139. package/dist/public/{file-viewer-C-6Jc_tb.js → file-viewer-CKtVC9Ix.js} +1 -1
  140. package/dist/public/index.css +1 -1
  141. package/dist/public/index.html +3 -3
  142. package/dist/public/index.js +1 -1
  143. package/dist/public/initControlPlane-Bi8ouFEb.js +1 -0
  144. package/dist/public/llms.txt +36 -1
  145. package/dist/public/main/index.html +3 -3
  146. package/dist/public/main/memory/index.html +3 -3
  147. package/dist/public/{markdown-C3M4OW_A.js → markdown-C-hCVJtc.js} +1 -1
  148. package/dist/public/orchestrator-8qNGfw8r.js +15 -0
  149. package/dist/public/{orchestrator-DHjdLtqa.js → orchestrator-BT3Ks5ip.js} +2 -2
  150. package/dist/public/peerjs-DfhwEiiq.js +1 -0
  151. package/dist/public/push-client-BVLrE9pK.js +1 -0
  152. package/dist/public/service-worker/fetch-proxy.js +1 -1
  153. package/dist/public/service-worker.js +1 -1
  154. package/dist/public/{shadow-claw-EAt2ml5Y.js → shadow-claw-DCjxNDCX.js} +3 -3
  155. package/dist/public/{shadow-claw-channels-XsfuWFF8.js → shadow-claw-channels-CITDrqMl.js} +1 -1
  156. package/dist/public/{shadow-claw-chat-CbSaOZ4q.js → shadow-claw-chat-BQrSgSKF.js} +1 -1
  157. package/dist/public/{shadow-claw-control-plane-DHvN3Ukg.js → shadow-claw-control-plane-Cp001OY0.js} +2 -2
  158. package/dist/public/{shadow-claw-conversations-BWs5VVfs.js → shadow-claw-conversations-EhX4RvIj.js} +1 -1
  159. package/dist/public/{shadow-claw-file-viewer-BWsoW5QS.js → shadow-claw-file-viewer-DiQXIjZD.js} +1 -1
  160. package/dist/public/{shadow-claw-files-CG7MqWiV.js → shadow-claw-files-lkTnSJsn.js} +1 -1
  161. package/dist/public/{shadow-claw-git-DBRPZNav.js → shadow-claw-git-BlcA_z8x.js} +2 -2
  162. package/dist/public/{shadow-claw-llm-DMvLgKXo.js → shadow-claw-llm-CW16X9Rg.js} +1 -1
  163. package/dist/public/{shadow-claw-networking-Dd8Rw31D.js → shadow-claw-networking-ySsMBY_i.js} +1 -1
  164. package/dist/public/{shadow-claw-notifications-Dbevj-lq.js → shadow-claw-notifications-CyV2zCQ8.js} +1 -1
  165. package/dist/public/{shadow-claw-pages-3_lQAv0t.js → shadow-claw-pages-Cx7cF9ah.js} +1 -1
  166. package/dist/public/{shadow-claw-settings-t8TIlgrM.js → shadow-claw-settings-zm4PHHGi.js} +2 -2
  167. package/dist/public/{shadow-claw-storage-CfEXoSIE.js → shadow-claw-storage-dy9JAEGd.js} +1 -1
  168. package/dist/public/{shadow-claw-task-server-DxQSsgRZ.js → shadow-claw-task-server-Bleww48s.js} +2 -2
  169. package/dist/public/{shadow-claw-tasks-DNpicQwz.js → shadow-claw-tasks-CTI5oaLN.js} +1 -1
  170. package/dist/public/{shadow-claw-terminal-DpY6zU_r.js → shadow-claw-terminal-mZ3i4HaQ.js} +1 -1
  171. package/dist/public/{shadow-claw-tools-WSwWohnk.js → shadow-claw-tools-B_nuk8Zl.js} +1 -1
  172. package/dist/public/{shadow-claw-webvm-CP6oFjg5.js → shadow-claw-webvm-Vl_jH5YL.js} +1 -1
  173. package/dist/public/{syncWebMcpRegistration-DOn9gLnD.js → syncWebMcpRegistration-n2vAMJ4w.js} +1 -1
  174. package/dist/server.js +197 -187
  175. package/package.json +2 -1
  176. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +6 -0
  177. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +19 -9
  178. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +14 -0
  179. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +12 -0
  180. package/src/components/shadow-claw/shadow-claw.ts +6 -0
  181. package/src/components/shadow-claw/utils/bindEventListeners.test.ts +57 -0
  182. package/src/components/shadow-claw/utils/bindEventListeners.ts +14 -0
  183. package/src/components/shadow-claw/utils/setupAppLifecycle.test.ts +201 -0
  184. package/src/components/shadow-claw/utils/setupAppLifecycle.ts +101 -0
  185. package/src/core/control-plane-client.test.ts +82 -1
  186. package/src/core/control-plane-client.ts +35 -1
  187. package/src/core/orchestrator/orchestrator.test.ts +49 -0
  188. package/src/core/orchestrator/orchestrator.ts +43 -0
  189. package/src/core/orchestrator/utils/operations/channel.test.ts +17 -0
  190. package/src/core/orchestrator/utils/operations/channel.ts +11 -5
  191. package/src/core/orchestrator/utils/operations/task.test.ts +26 -0
  192. package/src/core/orchestrator/utils/operations/task.ts +8 -1
  193. package/src/core/utils/initControlPlane.test.ts +138 -0
  194. package/src/core/utils/initControlPlane.ts +82 -0
  195. package/src/core/utils/initializeApp.test.ts +27 -1
  196. package/src/core/utils/initializeApp.ts +6 -13
  197. package/src/server/mcp/mcp-server.test.ts +6 -0
  198. package/src/server/mcp/mcp-server.ts +2 -1
  199. package/src/server/mcp/tools/built-in-tools.ts +88 -1
  200. package/src/server/mcp/tools/client-tool-relay.ts +6 -0
  201. package/src/server/mcp/tools/mcp-tools.test.ts +128 -0
  202. package/src/server/openapi/openapi-coverage.test.ts +2 -1
  203. package/src/server/openapi/openapi-spec.ts +2 -1
  204. package/src/server/openapi/paths/mcp.ts +104 -0
  205. package/src/server/openapi/paths/push.ts +3 -0
  206. package/src/server/routes/mcp.test.ts +92 -0
  207. package/src/server/routes/mcp.ts +138 -2
  208. package/src/server/utils/packageVersion.test.ts +10 -0
  209. package/src/server/utils/packageVersion.ts +32 -0
  210. package/src/service-worker/fetch-proxy-rules.test.ts +73 -0
  211. package/src/service-worker/fetch-proxy-rules.ts +44 -0
  212. package/src/service-worker/fetch-proxy.ts +1 -1
  213. package/src/subsystems/channels/imessage.test.ts +36 -0
  214. package/src/subsystems/channels/imessage.ts +11 -0
  215. package/src/subsystems/channels/peerjs.test.ts +56 -0
  216. package/src/subsystems/channels/peerjs.ts +29 -0
  217. package/src/subsystems/channels/telegram.test.ts +38 -0
  218. package/src/subsystems/channels/telegram.ts +13 -0
  219. package/src/subsystems/channels/types.ts +2 -0
  220. package/src/subsystems/notifications/push-client.test.ts +70 -0
  221. package/src/subsystems/notifications/push-client.ts +28 -3
  222. package/src/subsystems/notifications/push-routes.test.ts +82 -0
  223. package/src/subsystems/notifications/push-routes.ts +70 -16
  224. package/src/subsystems/notifications/push-store.test.ts +59 -0
  225. package/src/subsystems/notifications/push-store.ts +113 -19
  226. package/src/testing/jest-setup.ts +2 -0
  227. package/dist/lib/initControlPlane-C2zURVIS.js.map +0 -1
  228. package/dist/lib/orchestrator-CACLa_Ro.js.map +0 -1
  229. package/dist/lib/peerjs-DFWOBcXf.js.map +0 -1
  230. package/dist/lib/push-client-_tiWQsCT.js.map +0 -1
  231. package/dist/lib/shadow-claw-6UHdiy4a.js.map +0 -1
  232. package/dist/lib/shadow-claw-control-plane-UG0o7pCz.js.map +0 -1
  233. package/dist/public/initControlPlane-BN9cWyO0.js +0 -1
  234. package/dist/public/orchestrator-CMbCTjBf.js +0 -15
  235. package/dist/public/peerjs-DUCKH-m9.js +0 -1
  236. package/dist/public/push-client-D0lkwrK0.js +0 -1
package/bin/cli.mjs CHANGED
@@ -62,17 +62,34 @@ program
62
62
  .description("Browser-native personal AI assistant and static site publisher")
63
63
  .version(version, "-v, --version", "Output the current version");
64
64
 
65
+ // ---------------------------------------------------------------------------
66
+ // DEVELOPMENT COMMANDS
67
+ // ---------------------------------------------------------------------------
68
+ program.commandsGroup("Development:");
69
+
65
70
  // ---------------------------------------------------------------------------
66
71
  // BUILD COMMAND
67
72
  // ---------------------------------------------------------------------------
68
73
  program
69
74
  .command("build")
70
75
  .description("Build the static site bundle for production or staging")
76
+ .optionsGroup("Build Mode Options:")
71
77
  .option(
72
78
  "--prod, --production",
73
79
  "Build in production mode with minification and optimizations",
74
80
  false,
75
81
  )
82
+ .option(
83
+ "--prerender-pages <mode>",
84
+ "Prerender page mode (all, auto, none)",
85
+ "auto",
86
+ )
87
+ .option(
88
+ "--copy-all-assets",
89
+ "Copy entire assets/ directory into output",
90
+ false,
91
+ )
92
+ .optionsGroup("Path & Routing Options:")
76
93
  .option(
77
94
  "--origin <url>",
78
95
  "Canonical origin URL (e.g. https://user.github.io/repo/)",
@@ -84,16 +101,6 @@ program
84
101
  "dist/public",
85
102
  )
86
103
  .option("--content-root <dir>", "Content root directory", process.cwd())
87
- .option(
88
- "--prerender-pages <mode>",
89
- "Prerender page mode (all, auto, none)",
90
- "auto",
91
- )
92
- .option(
93
- "--copy-all-assets",
94
- "Copy entire assets/ directory into output",
95
- false,
96
- )
97
104
  .action(async (options) => {
98
105
  try {
99
106
  const contentRoot = path.resolve(options.contentRoot || process.cwd());
@@ -243,26 +250,32 @@ async function handleDev(portArg, options) {
243
250
  program
244
251
  .command("dev [port]")
245
252
  .description("Build and launch the development server")
253
+ .optionsGroup("Server & Network Options:")
246
254
  .option("-p, --port <port>", "Port to listen on (default: 8888)")
247
255
  .option("--host <host>", "Bind host/IP", "127.0.0.1")
248
256
  .option("--ip <host>", "Bind host/IP (alias)")
257
+ .option("--open", "Automatically open default browser", false)
258
+ .option("-v, --verbose", "Enable verbose request/proxy logging", false)
259
+ .optionsGroup("Site & Build Options:")
249
260
  .option("--content-root <dir>", "Content root directory", process.cwd())
250
261
  .option(
251
262
  "--out-dir <dir>",
252
263
  "Output directory relative to content root",
253
264
  "dist/public",
254
265
  )
266
+ .optionsGroup("Security & Proxy Options:")
255
267
  .option(
256
268
  "--cors-mode <mode>",
257
269
  "CORS policy: localhost | private | all",
258
270
  "localhost",
259
271
  )
260
- .option("--peerjs", "Enable built-in PeerJS signaling server", false)
261
272
  .option(
262
273
  "--allow-private-proxy",
263
274
  "Allow proxy to reach private/loopback addresses",
264
275
  false,
265
276
  )
277
+ .option("--peerjs", "Enable built-in PeerJS signaling server", false)
278
+ .optionsGroup("TLS / HTTPS Options:")
266
279
  .option("--https", "Enable HTTPS dev server", false)
267
280
  .option("--cert <path>", "Path to existing TLS certificate file")
268
281
  .option("--key <path>", "Path to existing TLS private key file")
@@ -270,6 +283,7 @@ program
270
283
  "--ssl-dir <path>",
271
284
  "Directory for TLS certificate generation/storage",
272
285
  )
286
+ .optionsGroup("Storage & Database Options:")
273
287
  .option("--database-dir <dir>", "Directory where SQLite databases are stored")
274
288
  .option(
275
289
  "--cache-dir <dir>",
@@ -285,33 +299,37 @@ program
285
299
  "Skip interactive cache directory prompt and use default .cache",
286
300
  false,
287
301
  )
288
- .option("-v, --verbose", "Enable verbose request/proxy logging", false)
289
- .option("--open", "Automatically open default browser", false)
290
302
  .action(handleDev);
291
303
 
292
304
  program
293
305
  .command("run [port]")
294
306
  .description("Alias for dev: build and launch the development server")
307
+ .optionsGroup("Server & Network Options:")
295
308
  .option("-p, --port <port>", "Port to listen on (default: 8888)")
296
309
  .option("--host <host>", "Bind host/IP", "127.0.0.1")
297
310
  .option("--ip <host>", "Bind host/IP (alias)")
311
+ .option("--open", "Automatically open default browser", false)
312
+ .option("-v, --verbose", "Enable verbose request/proxy logging", false)
313
+ .optionsGroup("Site & Build Options:")
298
314
  .option("--content-root <dir>", "Content root directory", process.cwd())
299
315
  .option(
300
316
  "--out-dir <dir>",
301
317
  "Output directory relative to content root",
302
318
  "dist/public",
303
319
  )
320
+ .optionsGroup("Security & Proxy Options:")
304
321
  .option(
305
322
  "--cors-mode <mode>",
306
323
  "CORS policy: localhost | private | all",
307
324
  "localhost",
308
325
  )
309
- .option("--peerjs", "Enable built-in PeerJS signaling server", false)
310
326
  .option(
311
327
  "--allow-private-proxy",
312
328
  "Allow proxy to reach private/loopback addresses",
313
329
  false,
314
330
  )
331
+ .option("--peerjs", "Enable built-in PeerJS signaling server", false)
332
+ .optionsGroup("TLS / HTTPS Options:")
315
333
  .option("--https", "Enable HTTPS dev server", false)
316
334
  .option("--cert <path>", "Path to existing TLS certificate file")
317
335
  .option("--key <path>", "Path to existing TLS private key file")
@@ -319,6 +337,7 @@ program
319
337
  "--ssl-dir <path>",
320
338
  "Directory for TLS certificate generation/storage",
321
339
  )
340
+ .optionsGroup("Storage & Database Options:")
322
341
  .option("--database-dir <dir>", "Directory where SQLite databases are stored")
323
342
  .option(
324
343
  "--cache-dir <dir>",
@@ -334,10 +353,13 @@ program
334
353
  "Skip interactive cache directory prompt and use default .cache",
335
354
  false,
336
355
  )
337
- .option("-v, --verbose", "Enable verbose request/proxy logging", false)
338
- .option("--open", "Automatically open default browser", false)
339
356
  .action(handleDev);
340
357
 
358
+ // ---------------------------------------------------------------------------
359
+ // SERVER COMMANDS
360
+ // ---------------------------------------------------------------------------
361
+ program.commandsGroup("Server:");
362
+
341
363
  // ---------------------------------------------------------------------------
342
364
  // SERVE COMMAND
343
365
  // ---------------------------------------------------------------------------
@@ -346,25 +368,35 @@ program
346
368
  .description(
347
369
  "Serve an existing static site build and start the backend proxy",
348
370
  )
371
+ .optionsGroup("Server & Network Options:")
349
372
  .option("-p, --port <port>", "Port to listen on (default: 8888)")
350
373
  .option("--host <host>", "Bind host/IP", "127.0.0.1")
374
+ .option(
375
+ "--no-static",
376
+ "Disable static file and UI serving (services-only mode)",
377
+ )
378
+ .option("--open", "Automatically open default browser", false)
379
+ .option("-v, --verbose", "Enable verbose request/proxy logging", false)
380
+ .optionsGroup("Site Options:")
351
381
  .option("--content-root <dir>", "Content root directory", process.cwd())
352
382
  .option(
353
383
  "--out-dir <dir>",
354
384
  "Output directory relative to content root",
355
385
  "dist/public",
356
386
  )
387
+ .optionsGroup("Security & Proxy Options:")
357
388
  .option(
358
389
  "--cors-mode <mode>",
359
390
  "CORS policy: localhost | private | all",
360
391
  "localhost",
361
392
  )
362
- .option("--peerjs", "Enable built-in PeerJS signaling server", false)
363
393
  .option(
364
394
  "--allow-private-proxy",
365
395
  "Allow proxy to reach private/loopback addresses",
366
396
  false,
367
397
  )
398
+ .option("--peerjs", "Enable built-in PeerJS signaling server", false)
399
+ .optionsGroup("TLS / HTTPS Options:")
368
400
  .option("--https", "Enable HTTPS dev server", false)
369
401
  .option("--cert <path>", "Path to existing TLS certificate file")
370
402
  .option("--key <path>", "Path to existing TLS private key file")
@@ -372,6 +404,7 @@ program
372
404
  "--ssl-dir <path>",
373
405
  "Directory for TLS certificate generation/storage",
374
406
  )
407
+ .optionsGroup("Storage & Database Options:")
375
408
  .option("--database-dir <dir>", "Directory where SQLite databases are stored")
376
409
  .option(
377
410
  "--cache-dir <dir>",
@@ -387,12 +420,6 @@ program
387
420
  "Skip interactive cache directory prompt and use default .cache",
388
421
  false,
389
422
  )
390
- .option(
391
- "--no-static",
392
- "Disable static file and UI serving (services-only mode)",
393
- )
394
- .option("-v, --verbose", "Enable verbose request/proxy logging", false)
395
- .option("--open", "Automatically open default browser", false)
396
423
  .action(async (portArg, options) => {
397
424
  try {
398
425
  const contentRoot = path.resolve(options.contentRoot || process.cwd());
@@ -623,25 +650,12 @@ program
623
650
  .description(
624
651
  "Start backend services (Express, MCP, control plane) without building or serving the UI (aliases: services, api)",
625
652
  )
653
+ .optionsGroup("Server & Network Options:")
626
654
  .option("-p, --port <port>", "Port to listen on (default: 8888)")
627
655
  .option("--host <host>", "Bind host/IP", "127.0.0.1")
628
656
  .option("--ip <host>", "Bind host/IP (alias)")
629
- .option("--content-root <dir>", "Content root directory", process.cwd())
630
- .option("--database-dir <dir>", "Directory where SQLite databases are stored")
631
- .option(
632
- "--cache-dir <dir>",
633
- "Custom cache directory for databases, tokens, and logs",
634
- )
635
- .option(
636
- "--tmp, --temp",
637
- "Store cache and databases in system temporary directory",
638
- false,
639
- )
640
- .option(
641
- "-y, --yes",
642
- "Skip interactive cache directory prompt and use default .cache",
643
- false,
644
- )
657
+ .option("-v, --verbose", "Enable verbose request/proxy logging", false)
658
+ .optionsGroup("Security & Proxy Options:")
645
659
  .option(
646
660
  "--cors-mode <mode>",
647
661
  "CORS policy: localhost | private | all",
@@ -661,6 +675,7 @@ program
661
675
  "Allow proxy to reach private/loopback addresses",
662
676
  false,
663
677
  )
678
+ .optionsGroup("TLS / HTTPS Options:")
664
679
  .option("--https", "Enable HTTPS server", false)
665
680
  .option("--cert <path>", "Path to existing TLS certificate file")
666
681
  .option("--key <path>", "Path to existing TLS private key file")
@@ -668,184 +683,75 @@ program
668
683
  "--ssl-dir <path>",
669
684
  "Directory for TLS certificate generation/storage",
670
685
  )
671
- .option("-v, --verbose", "Enable verbose request/proxy logging", false)
672
- .action(handleServer);
673
-
674
- // ---------------------------------------------------------------------------
675
- // INIT COMMAND
676
- // ---------------------------------------------------------------------------
677
- program
678
- .command("init [dir]")
679
- .description(
680
- "Initialize a new ShadowClaw content template in the specified directory",
681
- )
682
- .action(async (dirArg) => {
683
- try {
684
- const targetDir = path.resolve(dirArg || process.cwd());
685
- await mkdir(path.join(targetDir, "pages/main"), { recursive: true });
686
-
687
- const configPath = path.join(targetDir, "shadow-claw-config.json");
688
- const legacyConfigPath1 = path.join(targetDir, "shadow-claw.config.json");
689
- const legacyConfigPath2 = path.join(targetDir, "site-config.json");
690
- if (
691
- !(await pathExists(configPath)) &&
692
- !(await pathExists(legacyConfigPath1)) &&
693
- !(await pathExists(legacyConfigPath2))
694
- ) {
695
- const defaultSiteConfig = {
696
- $schema: "https://json-schema.org/draft/2020-12/schema",
697
- site: {
698
- title: "My ShadowClaw Site",
699
- description:
700
- "Personal AI assistant and knowledge hub powered by ShadowClaw.",
701
- },
702
- branding: {
703
- titleText: "My ShadowClaw Site",
704
- },
705
- pages: {
706
- sortOrder: "asc",
707
- defaultPinnedPage: "/pages/main/index.html",
708
- },
709
- };
710
- await writeFile(
711
- configPath,
712
- JSON.stringify(defaultSiteConfig, null, 2) + "\n",
713
- "utf8",
714
- );
715
- console.log(`Created ${configPath}`);
716
- }
717
-
718
- const indexPath = path.join(targetDir, "pages/main/index.html");
719
- if (!(await pathExists(indexPath))) {
720
- const defaultIndexHtml = `---
721
- title: "Welcome to ShadowClaw"
722
- created: "${new Date().toISOString()}"
723
- updated: "${new Date().toISOString()}"
724
- slug: "home"
725
- ---
726
-
727
- <article>
728
- <h1>Welcome to ShadowClaw</h1>
729
- <p>Your browser-native personal AI assistant and static site publisher.</p>
730
- </article>
731
- `;
732
- await writeFile(indexPath, defaultIndexHtml, "utf8");
733
- console.log(`Created ${indexPath}`);
734
- }
735
-
736
- const gitignorePath = path.join(targetDir, ".gitignore");
737
- if (!(await pathExists(gitignorePath))) {
738
- await writeFile(
739
- gitignorePath,
740
- "dist/\n.cache/\nnode_modules/\n",
741
- "utf8",
742
- );
743
- console.log(`Created ${gitignorePath}`);
744
- }
745
-
746
- console.log(
747
- `\nShadowClaw template successfully initialized in ${targetDir}`,
748
- );
749
- console.log(`To start developing, run:`);
750
- console.log(` npx shadow-claw dev\n`);
751
- } catch (err) {
752
- console.error("Init failed:", err);
753
- process.exit(1);
754
- }
755
- });
756
-
757
- // ---------------------------------------------------------------------------
758
- // PEER-ID / WEBRTC COMMAND
759
- // ---------------------------------------------------------------------------
760
- program
761
- .command("peer-id [action] [customId]")
762
- .alias("webrtc-peer-id")
763
- .description(
764
- "Get, generate, or renew the WebRTC CLI peer ID stored in .cache/cli-peer-id",
765
- )
766
- .option(
767
- "-r, --renew",
768
- "Force renewal / generation of a new WebRTC CLI peer ID",
769
- )
770
- .option("--set <id>", "Set a specific custom WebRTC CLI peer ID")
771
- .option("--cache-dir <dir>", "Custom cache directory (defaults to .cache)")
772
- .option("-q, --quiet", "Output only the peer ID string")
773
- .option("--json", "Output peer ID and file path as JSON")
774
- .action(async (action, customId, options) => {
775
- const opts = { ...options };
776
- if (customId && !opts.set) {
777
- opts.set = customId;
778
- }
779
- await runPeerIdCommand(action, opts);
780
- });
781
-
782
- program
783
- .command("webrtc [action] [customId]")
784
- .description(
785
- "Manage WebRTC CLI configuration and peer identity (.cache/cli-peer-id)\n\n" +
786
- " Actions:\n" +
787
- " (default) Show or generate the CLI peer ID\n" +
788
- " listen Register as a live PeerJS peer — browser tabs can then\n" +
789
- " connect directly without a control-plane connection",
790
- )
686
+ .optionsGroup("Storage & Database Options:")
687
+ .option("--content-root <dir>", "Content root directory", process.cwd())
688
+ .option("--database-dir <dir>", "Directory where SQLite databases are stored")
791
689
  .option(
792
- "-r, --renew",
793
- "Force renewal / generation of a new WebRTC CLI peer ID",
690
+ "--cache-dir <dir>",
691
+ "Custom cache directory for databases, tokens, and logs",
794
692
  )
795
- .option("--set <id>", "Set a specific custom WebRTC CLI peer ID")
796
- .option("--cache-dir <dir>", "Custom cache directory (defaults to .cache)")
797
- .option("-q, --quiet", "Output only the peer ID string")
798
- .option("--json", "Output peer ID and file path as JSON")
799
- // listen-specific options
800
- .option("--host <host>", "PeerJS signaling server host (for listen)")
801
- .option("--port <port>", "PeerJS signaling server port (for listen)", "8888")
802
- .option("--path <path>", "PeerJS signaling server path (for listen)", "/")
803
693
  .option(
804
- "--secure",
805
- "Use TLS (wss://) for the signaling server (for listen)",
694
+ "--tmp, --temp",
695
+ "Store cache and databases in system temporary directory",
806
696
  false,
807
697
  )
808
698
  .option(
809
- "--https",
810
- "Alias for --secure: use TLS (wss://) for the signaling server",
699
+ "-y, --yes",
700
+ "Skip interactive cache directory prompt and use default .cache",
811
701
  false,
812
702
  )
813
- .option(
814
- "--trusted-peer <id>",
815
- "Accept connections only from this peer ID (repeatable, for listen)",
816
- (v, prev) => (prev ? [...prev, v] : [v]),
817
- [],
703
+ .action(handleServer);
704
+
705
+ // ---------------------------------------------------------------------------
706
+ // MCP COMMAND
707
+ // ---------------------------------------------------------------------------
708
+ program
709
+ .command("mcp")
710
+ .description(
711
+ "Run the official Stateless Model Context Protocol (MCP 2026-07-28) server",
818
712
  )
819
- .option("--verbose", "Verbose connection logging (for listen)", false)
820
- .option("--renew-peer-id", "Renew CLI peer ID before listening", false)
713
+ .optionsGroup("MCP Protocol Options:")
714
+ .option("--mcp-transport <transport>", "MCP transport: stdio | http", "stdio")
821
715
  .option(
822
- "-k, --insecure",
823
- "Allow self-signed TLS certificates for the signaling server (wss://)",
716
+ "--relay-client-tools",
717
+ "Discover and relay tools from connected browser clients",
824
718
  true,
825
719
  )
826
- .action(async (action, customId, options) => {
827
- if (action === "listen") {
828
- await runWebRtcListenCommand(options);
829
- return;
830
- }
831
- const opts = { ...options };
832
- if (customId && !opts.set) {
833
- opts.set = customId;
834
- }
835
- await runPeerIdCommand(action, opts);
720
+ .option("--no-relay-client-tools", "Disable relaying browser client tools")
721
+ .optionsGroup("Target Client Options:")
722
+ .option("--client <id>", "Target client ID (defaults to first active client)")
723
+ .optionsGroup("Control Plane Connection Options:")
724
+ .option("--host <host>", "Control plane host")
725
+ .option("--port <port>", "Control plane port or HTTP MCP port")
726
+ .option("--token <token>", "Control token")
727
+ .option("--https", "Connect to server via HTTPS", false)
728
+ .option("-k, --insecure", "Allow self-signed TLS certificates", true)
729
+ .optionsGroup("Transport & Peer Options:")
730
+ .option(
731
+ "--transport <transport>",
732
+ "Control plane client transport: http | webrtc",
733
+ "http",
734
+ )
735
+ .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
736
+ .action(async (options) => {
737
+ await runMcpCommand(options);
836
738
  });
837
739
 
838
740
  // ---------------------------------------------------------------------------
839
- // CLIENTS COMMAND
741
+ // CONTROL PLANE & CLIENT BRIDGE COMMANDS
840
742
  // ---------------------------------------------------------------------------
743
+ program.commandsGroup("Control Plane & Client Bridge:");
744
+
841
745
  program
842
746
  .command("clients")
843
747
  .description("List connected / registered browser and Electron clients")
748
+ .optionsGroup("Control Plane Connection Options:")
844
749
  .option("--host <host>", "Control plane host")
845
750
  .option("--port <port>", "Control plane port")
846
751
  .option("--token <token>", "Control token")
847
752
  .option("--https", "Connect to server via HTTPS", false)
848
753
  .option("-k, --insecure", "Allow self-signed TLS certificates", true)
754
+ .optionsGroup("Transport & Peer Options:")
849
755
  .option("--transport <transport>", "Transport to use: http | webrtc", "http")
850
756
  .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
851
757
  .option(
@@ -858,19 +764,19 @@ program
858
764
  await runClientsCommand(options);
859
765
  });
860
766
 
861
- // ---------------------------------------------------------------------------
862
- // SEND COMMAND
863
- // ---------------------------------------------------------------------------
864
767
  program
865
768
  .command("send <message>")
866
769
  .description("Send a message/prompt to a connected client")
770
+ .optionsGroup("Target Options:")
867
771
  .option("--client <id>", "Target client ID (defaults to first available)")
868
772
  .option("--group <groupId>", "Target conversation group ID")
773
+ .optionsGroup("Control Plane Connection Options:")
869
774
  .option("--host <host>", "Control plane host")
870
775
  .option("--port <port>", "Control plane port")
871
776
  .option("--token <token>", "Control token")
872
777
  .option("--https", "Connect to server via HTTPS", false)
873
778
  .option("-k, --insecure", "Allow self-signed TLS certificates", true)
779
+ .optionsGroup("Transport & Peer Options:")
874
780
  .option("--transport <transport>", "Transport to use: http | webrtc", "http")
875
781
  .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
876
782
  .option("--renew-peer-id", "Renew WebRTC CLI peer ID before sending", false)
@@ -879,22 +785,19 @@ program
879
785
  await runSendCommand(message, options);
880
786
  });
881
787
 
882
- // ---------------------------------------------------------------------------
883
- // BACKUP COMMAND
884
- // ---------------------------------------------------------------------------
885
788
  program
886
- .command("backup [action]")
887
- .description(
888
- "Trigger or manage client workspace backups (trigger | list | delete)",
889
- )
890
- .option("--client <id>", "Client ID")
891
- .option("--backup-id <id>", "Backup ID (for delete)")
892
- .option("--group <groupId>", "Specific workspace group ID")
789
+ .command("tasks")
790
+ .description("List scheduled tasks on a connected client")
791
+ .optionsGroup("Target Options:")
792
+ .option("--client <id>", "Target client ID")
793
+ .option("--group <groupId>", "Filter tasks by conversation group ID")
794
+ .optionsGroup("Control Plane Connection Options:")
893
795
  .option("--host <host>", "Control plane host")
894
796
  .option("--port <port>", "Control plane port")
895
797
  .option("--token <token>", "Control token")
896
798
  .option("--https", "Connect to server via HTTPS", false)
897
799
  .option("-k, --insecure", "Allow self-signed TLS certificates", true)
800
+ .optionsGroup("Transport & Peer Options:")
898
801
  .option("--transport <transport>", "Transport to use: http | webrtc", "http")
899
802
  .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
900
803
  .option(
@@ -903,23 +806,26 @@ program
903
806
  false,
904
807
  )
905
808
  .option("--cache-dir <dir>", "Custom cache directory")
906
- .action(async (action, options) => {
907
- await runBackupCommand(action || "trigger", options);
809
+ .action(async (options) => {
810
+ await runTasksCommand(options);
908
811
  });
909
812
 
910
- // ---------------------------------------------------------------------------
911
- // TASKS COMMAND
912
- // ---------------------------------------------------------------------------
913
813
  program
914
- .command("tasks")
915
- .description("List scheduled tasks on a connected client")
916
- .option("--client <id>", "Target client ID")
917
- .option("--group <groupId>", "Filter tasks by conversation group ID")
814
+ .command("backup [action]")
815
+ .description(
816
+ "Trigger or manage client workspace backups (trigger | list | delete)",
817
+ )
818
+ .optionsGroup("Target Options:")
819
+ .option("--client <id>", "Client ID")
820
+ .option("--backup-id <id>", "Backup ID (for delete)")
821
+ .option("--group <groupId>", "Specific workspace group ID")
822
+ .optionsGroup("Control Plane Connection Options:")
918
823
  .option("--host <host>", "Control plane host")
919
824
  .option("--port <port>", "Control plane port")
920
825
  .option("--token <token>", "Control token")
921
826
  .option("--https", "Connect to server via HTTPS", false)
922
827
  .option("-k, --insecure", "Allow self-signed TLS certificates", true)
828
+ .optionsGroup("Transport & Peer Options:")
923
829
  .option("--transport <transport>", "Transport to use: http | webrtc", "http")
924
830
  .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
925
831
  .option(
@@ -928,50 +834,190 @@ program
928
834
  false,
929
835
  )
930
836
  .option("--cache-dir <dir>", "Custom cache directory")
931
- .action(async (options) => {
932
- await runTasksCommand(options);
837
+ .action(async (action, options) => {
838
+ await runBackupCommand(action || "trigger", options);
933
839
  });
934
840
 
935
841
  // ---------------------------------------------------------------------------
936
- // MCP COMMAND
842
+ // WEBRTC COMMANDS
937
843
  // ---------------------------------------------------------------------------
844
+ program.commandsGroup("WebRTC:");
845
+
938
846
  program
939
- .command("mcp")
847
+ .command("webrtc [action] [customId]")
848
+ .summary(
849
+ "Manage WebRTC CLI configuration and peer identity (.cache/cli-peer-id)",
850
+ )
940
851
  .description(
941
- "Run the official Stateless Model Context Protocol (MCP 2026-07-28) server",
852
+ "Manage WebRTC CLI configuration and peer identity (.cache/cli-peer-id)\n\n" +
853
+ " Actions:\n" +
854
+ " (default) Show or generate the CLI peer ID\n" +
855
+ " listen Register as a live PeerJS peer — browser tabs can then\n" +
856
+ " connect directly without a control-plane connection",
942
857
  )
943
- .option("--mcp-transport <transport>", "MCP transport: stdio | http", "stdio")
944
- .option("--client <id>", "Target client ID (defaults to first active client)")
945
- .option("--host <host>", "Control plane host")
946
- .option("--port <port>", "Control plane port or HTTP MCP port")
947
- .option("--token <token>", "Control token")
948
- .option("--https", "Connect to server via HTTPS", false)
949
- .option("-k, --insecure", "Allow self-signed TLS certificates", true)
858
+ .optionsGroup("Identity Options:")
950
859
  .option(
951
- "--transport <transport>",
952
- "Control plane client transport: http | webrtc",
953
- "http",
860
+ "-r, --renew",
861
+ "Force renewal / generation of a new WebRTC CLI peer ID",
954
862
  )
955
- .option("--peer-id <id>", "Custom WebRTC CLI peer ID")
863
+ .option("--set <id>", "Set a specific custom WebRTC CLI peer ID")
864
+ .option("--cache-dir <dir>", "Custom cache directory (defaults to .cache)")
865
+ .option("-q, --quiet", "Output only the peer ID string")
866
+ .option("--json", "Output peer ID and file path as JSON")
867
+ .optionsGroup("Signaling & Listener Options (for listen action):")
868
+ .option("--host <host>", "PeerJS signaling server host (for listen)")
869
+ .option("--port <port>", "PeerJS signaling server port (for listen)", "8888")
870
+ .option("--path <path>", "PeerJS signaling server path (for listen)", "/")
956
871
  .option(
957
- "--relay-client-tools",
958
- "Discover and relay tools from connected browser clients",
872
+ "--secure",
873
+ "Use TLS (wss://) for the signaling server (for listen)",
874
+ false,
875
+ )
876
+ .option(
877
+ "--https",
878
+ "Alias for --secure: use TLS (wss://) for the signaling server",
879
+ false,
880
+ )
881
+ .option(
882
+ "--trusted-peer <id>",
883
+ "Accept connections only from this peer ID (repeatable, for listen)",
884
+ (v, prev) => (prev ? [...prev, v] : [v]),
885
+ [],
886
+ )
887
+ .option("--verbose", "Verbose connection logging (for listen)", false)
888
+ .option("--renew-peer-id", "Renew CLI peer ID before listening", false)
889
+ .option(
890
+ "-k, --insecure",
891
+ "Allow self-signed TLS certificates for the signaling server (wss://)",
959
892
  true,
960
893
  )
961
- .option("--no-relay-client-tools", "Disable relaying browser client tools")
962
- .action(async (options) => {
963
- await runMcpCommand(options);
894
+ .action(async (action, customId, options) => {
895
+ if (action === "listen") {
896
+ await runWebRtcListenCommand(options);
897
+ return;
898
+ }
899
+ const opts = { ...options };
900
+ if (customId && !opts.set) {
901
+ opts.set = customId;
902
+ }
903
+ await runPeerIdCommand(action, opts);
904
+ });
905
+
906
+ program
907
+ .command("peer-id [action] [customId]")
908
+ .alias("webrtc-peer-id")
909
+ .description(
910
+ "Get, generate, or renew the WebRTC CLI peer ID stored in .cache/cli-peer-id",
911
+ )
912
+ .optionsGroup("Identity Options:")
913
+ .option(
914
+ "-r, --renew",
915
+ "Force renewal / generation of a new WebRTC CLI peer ID",
916
+ )
917
+ .option("--set <id>", "Set a specific custom WebRTC CLI peer ID")
918
+ .option("--cache-dir <dir>", "Custom cache directory (defaults to .cache)")
919
+ .option("-q, --quiet", "Output only the peer ID string")
920
+ .option("--json", "Output peer ID and file path as JSON")
921
+ .action(async (action, customId, options) => {
922
+ const opts = { ...options };
923
+ if (customId && !opts.set) {
924
+ opts.set = customId;
925
+ }
926
+ await runPeerIdCommand(action, opts);
964
927
  });
965
928
 
966
929
  // ---------------------------------------------------------------------------
967
- // AGENT SKILLS INDEX COMMAND
930
+ // PROJECT & PROTOCOL COMMANDS
968
931
  // ---------------------------------------------------------------------------
932
+ program.commandsGroup("Project:");
933
+
934
+ program
935
+ .command("init [dir]")
936
+ .description(
937
+ "Initialize a new ShadowClaw content template in the specified directory",
938
+ )
939
+ .action(async (dirArg) => {
940
+ try {
941
+ const targetDir = path.resolve(dirArg || process.cwd());
942
+ await mkdir(path.join(targetDir, "pages/main"), { recursive: true });
943
+
944
+ const configPath = path.join(targetDir, "shadow-claw-config.json");
945
+ const legacyConfigPath1 = path.join(targetDir, "shadow-claw.config.json");
946
+ const legacyConfigPath2 = path.join(targetDir, "site-config.json");
947
+ if (
948
+ !(await pathExists(configPath)) &&
949
+ !(await pathExists(legacyConfigPath1)) &&
950
+ !(await pathExists(legacyConfigPath2))
951
+ ) {
952
+ const defaultSiteConfig = {
953
+ $schema: "https://json-schema.org/draft/2020-12/schema",
954
+ site: {
955
+ title: "My ShadowClaw Site",
956
+ description:
957
+ "Personal AI assistant and knowledge hub powered by ShadowClaw.",
958
+ },
959
+ branding: {
960
+ titleText: "My ShadowClaw Site",
961
+ },
962
+ pages: {
963
+ sortOrder: "asc",
964
+ defaultPinnedPage: "/pages/main/index.html",
965
+ },
966
+ };
967
+ await writeFile(
968
+ configPath,
969
+ JSON.stringify(defaultSiteConfig, null, 2) + "\n",
970
+ "utf8",
971
+ );
972
+ console.log(`Created ${configPath}`);
973
+ }
974
+
975
+ const indexPath = path.join(targetDir, "pages/main/index.html");
976
+ if (!(await pathExists(indexPath))) {
977
+ const defaultIndexHtml = `---
978
+ title: "Welcome to ShadowClaw"
979
+ created: "${new Date().toISOString()}"
980
+ updated: "${new Date().toISOString()}"
981
+ slug: "home"
982
+ ---
983
+
984
+ <article>
985
+ <h1>Welcome to ShadowClaw</h1>
986
+ <p>Your browser-native personal AI assistant and static site publisher.</p>
987
+ </article>
988
+ `;
989
+ await writeFile(indexPath, defaultIndexHtml, "utf8");
990
+ console.log(`Created ${indexPath}`);
991
+ }
992
+
993
+ const gitignorePath = path.join(targetDir, ".gitignore");
994
+ if (!(await pathExists(gitignorePath))) {
995
+ await writeFile(
996
+ gitignorePath,
997
+ "dist/\n.cache/\nnode_modules/\n",
998
+ "utf8",
999
+ );
1000
+ console.log(`Created ${gitignorePath}`);
1001
+ }
1002
+
1003
+ console.log(
1004
+ `\nShadowClaw template successfully initialized in ${targetDir}`,
1005
+ );
1006
+ console.log(`To start developing, run:`);
1007
+ console.log(` npx shadow-claw dev\n`);
1008
+ } catch (err) {
1009
+ console.error("Init failed:", err);
1010
+ process.exit(1);
1011
+ }
1012
+ });
1013
+
969
1014
  program
970
1015
  .command("skills:index [dir]")
971
1016
  .alias("agent-skills")
972
1017
  .description(
973
1018
  "Generate or update .well-known/agent-skills/index.json per the Agent Skills Discovery RFC",
974
1019
  )
1020
+ .optionsGroup("Output & Path Options:")
975
1021
  .option(
976
1022
  "--out-dir <dir>",
977
1023
  "Output directory relative to content root (default: .well-known/agent-skills)",
@@ -985,6 +1031,7 @@ program
985
1031
  "--config <file>",
986
1032
  "Explicit path to shadow-claw.config.json or site-config.json",
987
1033
  )
1034
+ .optionsGroup("Generation Options:")
988
1035
  .option("--no-bundled", "Exclude bundled ShadowClaw skills from index")
989
1036
  .option("--stdout", "Print generated index.json to stdout", false)
990
1037
  .option("--no-write", "Skip writing file to disk")
@@ -997,4 +1044,10 @@ program
997
1044
  }
998
1045
  });
999
1046
 
1047
+ // ---------------------------------------------------------------------------
1048
+ // HELP & UTILITY COMMANDS
1049
+ // ---------------------------------------------------------------------------
1050
+ program.commandsGroup("Help & Utility:");
1051
+ program.helpCommand("help [command]", "display help for command");
1052
+
1000
1053
  program.parse(process.argv);