swarph-cli 0.72.33__tar.gz → 0.72.35__tar.gz

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 (544) hide show
  1. {swarph_cli-0.72.33/src/swarph_cli.egg-info → swarph_cli-0.72.35}/PKG-INFO +1 -1
  2. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/pyproject.toml +1 -1
  3. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/__init__.py +1 -1
  4. swarph_cli-0.72.35/src/swarph_cli/commands/commander.py +95 -0
  5. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/mcp_server.py +83 -9
  6. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/guide/GUIDE.md +7 -0
  7. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/main.py +2 -0
  8. {swarph_cli-0.72.33 → swarph_cli-0.72.35/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  9. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli.egg-info/SOURCES.txt +4 -0
  10. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1065_release.py +2 -0
  11. swarph_cli-0.72.35/tests/test_1286_dm_unread_cursor.py +192 -0
  12. swarph_cli-0.72.35/tests/test_1297_commander.py +60 -0
  13. swarph_cli-0.72.35/tests/test_1301_restart_skew.py +64 -0
  14. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_735_dm_mcp_tools.py +4 -1
  15. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/LICENSE +0 -0
  16. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/README.md +0 -0
  17. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/setup.cfg +0 -0
  18. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/__main__.py +0 -0
  19. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/__init__.py +0 -0
  20. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/backends.py +0 -0
  21. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/claude_cli.py +0 -0
  22. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/codex_cli.py +0 -0
  23. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/pack.py +0 -0
  24. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/prices.py +0 -0
  25. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/providers.py +0 -0
  26. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/quality.py +0 -0
  27. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/refresh_prices.py +0 -0
  28. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/rules.py +0 -0
  29. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/runner.py +0 -0
  30. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/signal_score.py +0 -0
  31. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/bench/validate.py +0 -0
  32. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/caller.py +0 -0
  33. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/__init__.py +0 -0
  34. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/harden.py +0 -0
  35. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/lineage.py +0 -0
  36. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/liveness.py +0 -0
  37. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/manifest.py +0 -0
  38. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/paths.py +0 -0
  39. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/capture/verify.py +0 -0
  40. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/cell.py +0 -0
  41. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/chain_token.py +0 -0
  42. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/channel.py +0 -0
  43. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/__init__.py +0 -0
  44. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/_content.py +0 -0
  45. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/_display.py +0 -0
  46. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/_gateway_client.py +0 -0
  47. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/add.py +0 -0
  48. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/bench.py +0 -0
  49. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/board.py +0 -0
  50. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/board_merge_check.py +0 -0
  51. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/brain.py +0 -0
  52. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/brain_ask.py +0 -0
  53. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/cell.py +0 -0
  54. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/cell_probe.py +0 -0
  55. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  56. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/channel.py +0 -0
  57. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/chat.py +0 -0
  58. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/codegraph.py +0 -0
  59. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  60. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  61. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/codex_waker.py +0 -0
  62. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/compress.py +0 -0
  63. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/daemon.py +0 -0
  64. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/dreaming.py +0 -0
  65. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/event.py +0 -0
  66. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/gateway.py +0 -0
  67. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/gh_route.py +0 -0
  68. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/group.py +0 -0
  69. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/guide.py +0 -0
  70. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/highlight.py +0 -0
  71. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/hook_interpreter.py +0 -0
  72. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/hook_output.py +0 -0
  73. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/hooks.py +0 -0
  74. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/import_session.py +0 -0
  75. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/init.py +0 -0
  76. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  77. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_hook.py +0 -0
  78. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  79. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
  80. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
  81. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/install_wake_hook.py +0 -0
  82. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/lane.py +0 -0
  83. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/memory.py +0 -0
  84. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  85. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/memory_sync.py +0 -0
  86. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/mesh.py +0 -0
  87. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/monitor.py +0 -0
  88. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/okf_links.py +0 -0
  89. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/onboard.py +0 -0
  90. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  91. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  92. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/protocol_handler.py +0 -0
  93. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/ratify.py +0 -0
  94. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/roster.py +0 -0
  95. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/rowclear_hook.py +0 -0
  96. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/schedule.py +0 -0
  97. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/security.py +0 -0
  98. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/service.py +0 -0
  99. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/spawn.py +0 -0
  100. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/sweep.py +0 -0
  101. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/timeline.py +0 -0
  102. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/triage.py +0 -0
  103. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/version.py +0 -0
  104. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  105. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/commands/watchdog.py +0 -0
  106. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/compress/__init__.py +0 -0
  107. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/compress/levers.py +0 -0
  108. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/compress/marker.py +0 -0
  109. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/compress/verify.py +0 -0
  110. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/console_safe.py +0 -0
  111. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/delivery_queue.py +0 -0
  112. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dm_frame.py +0 -0
  113. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/__init__.py +0 -0
  114. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/candidates.py +0 -0
  115. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/clone.py +0 -0
  116. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/enrich.py +0 -0
  117. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/index_check.py +0 -0
  118. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/notify.py +0 -0
  119. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/organize.py +0 -0
  120. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/probes.py +0 -0
  121. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/report.py +0 -0
  122. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/run.py +0 -0
  123. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/slm.py +0 -0
  124. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/transcripts.py +0 -0
  125. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/dreaming/verify.py +0 -0
  126. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/duplicate_session.py +0 -0
  127. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/__init__.py +0 -0
  128. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/feature_registry.py +0 -0
  129. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/lanes_control.py +0 -0
  130. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/schema.sql +0 -0
  131. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/server.py +0 -0
  132. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway/services_control.py +0 -0
  133. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gateway_default.py +0 -0
  134. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/gh_identity.py +0 -0
  135. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/identity.py +0 -0
  136. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
  137. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
  138. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
  139. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
  140. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
  141. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/launchd_render.py +0 -0
  142. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/multiplexer.py +0 -0
  143. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/__init__.py +0 -0
  144. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/__main__.py +0 -0
  145. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/api.py +0 -0
  146. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/cli.py +0 -0
  147. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/config.py +0 -0
  148. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/operator/fmt.py +0 -0
  149. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/orphan_daemons.py +0 -0
  150. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/pane_probe.py +0 -0
  151. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/parsers/__init__.py +0 -0
  152. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/parsers/claude.py +0 -0
  153. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
  154. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
  155. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
  156. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
  157. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
  158. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
  159. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
  160. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
  161. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
  162. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
  163. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
  164. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
  165. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_executor.py +0 -0
  166. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_executor_windows.py +0 -0
  167. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_reconciliation.py +0 -0
  168. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_reply_outbox.py +0 -0
  169. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_service_host.py +0 -0
  170. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_service_payload.py +0 -0
  171. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_service_reply.py +0 -0
  172. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/peer_staging.py +0 -0
  173. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/query_filters.py +0 -0
  174. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/quota_sync.py +0 -0
  175. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/rowclear.py +0 -0
  176. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/__init__.py +0 -0
  177. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  178. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  179. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  180. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
  181. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
  182. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/scrub.py +0 -0
  183. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/service/__init__.py +0 -0
  184. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/service/app.py +0 -0
  185. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/service/providers.py +0 -0
  186. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/session_bridge.py +0 -0
  187. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
  188. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
  189. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/source_text.py +0 -0
  190. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/stall_alert.py +0 -0
  191. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/codex-waker.default +0 -0
  192. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
  193. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  194. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  195. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  196. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  197. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
  198. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
  199. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
  200. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  201. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  202. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
  203. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
  204. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-triage.service +0 -0
  205. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
  206. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  207. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  208. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  209. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/the_ten.py +0 -0
  210. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/timeline_paths.py +0 -0
  211. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/tokens.py +0 -0
  212. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/__init__.py +0 -0
  213. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/backend.py +0 -0
  214. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/job.py +0 -0
  215. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/owners.json +0 -0
  216. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/router.py +0 -0
  217. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/rows.py +0 -0
  218. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli/triage/sensor.py +0 -0
  219. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  220. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  221. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli.egg-info/requires.txt +0 -0
  222. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/src/swarph_cli.egg-info/top_level.txt +0 -0
  223. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1006_board_publisher.py +0 -0
  224. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1006_session_board.py +0 -0
  225. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1010_strand_only.py +0 -0
  226. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1013_spawn_channel.py +0 -0
  227. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1017_muse_tui.py +0 -0
  228. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1034_copy_mode.py +0 -0
  229. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1050_surrogate_wake.py +0 -0
  230. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1052_opencode_deaf.py +0 -0
  231. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1059_watchdog_unread_env.py +0 -0
  232. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1061_roster_cli.py +0 -0
  233. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1064_stuck_sweep.py +0 -0
  234. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1066_quiet_wakes.py +0 -0
  235. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1068_structured_accept_cli.py +0 -0
  236. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1071_quota_sync.py +0 -0
  237. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1073_triage.py +0 -0
  238. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1073_triage_board.py +0 -0
  239. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1073_wheel_install.py +0 -0
  240. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1075_task_cli.py +0 -0
  241. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1083_openai_decisions.py +0 -0
  242. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1144_cell_yaml_github.py +0 -0
  243. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1197_commander_taps.py +0 -0
  244. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1203_swarph_me_refusal.py +0 -0
  245. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1206_offer_question.py +0 -0
  246. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1214_egress.py +0 -0
  247. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1219_egress_probe.py +0 -0
  248. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1221_quota_windows.py +0 -0
  249. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1222_maybe_start_cap.py +0 -0
  250. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1228_quota_reach.py +0 -0
  251. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1243_the_ten.py +0 -0
  252. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1248_ship_price_table.py +0 -0
  253. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_124_register_merge.py +0 -0
  254. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1250_price_cache.py +0 -0
  255. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1256_commander_wake.py +0 -0
  256. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1261_mcp_install.py +0 -0
  257. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1262_wake_followups.py +0 -0
  258. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1265_wake_note.py +0 -0
  259. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1269_triage_label.py +0 -0
  260. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_126_pending_from_complete.py +0 -0
  261. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_1278_dreaming_reach.py +0 -0
  262. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_132_ensure_monitor_state_dir.py +0 -0
  263. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_138_memory_navigate.py +0 -0
  264. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_139_tool_probe.py +0 -0
  265. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_183_card_anchor.py +0 -0
  266. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_184_notify_honesty.py +0 -0
  267. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_201_win32_bash_refusal.py +0 -0
  268. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_243_onboard_per_peer_token.py +0 -0
  269. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_307_task4_ask_reply.py +0 -0
  270. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
  271. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_319_refusal_exit_codes.py +0 -0
  272. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_323_mistral_backend.py +0 -0
  273. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_332_report_organize_findings.py +0 -0
  274. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_356_query_filters.py +0 -0
  275. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_397_gh_identity_router.py +0 -0
  276. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_397_router_preflight.py +0 -0
  277. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_402_one_identity_resolver.py +0 -0
  278. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_432_rowclear.py +0 -0
  279. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_467b_onboard_operator_credential.py +0 -0
  280. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_472_register_caller_binding.py +0 -0
  281. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_484_codex_queue_sink.py +0 -0
  282. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_513_duplicate_session.py +0 -0
  283. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_523_guide.py +0 -0
  284. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_524_conftest_hermeticity.py +0 -0
  285. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_532_accept_flag.py +0 -0
  286. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_535_version_capability.py +0 -0
  287. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_538_signal_bench.py +0 -0
  288. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_546_default_gateway.py +0 -0
  289. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_547_verbs_from_registry.py +0 -0
  290. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_548_gateway_remainder.py +0 -0
  291. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_552_codegraph_gateway.py +0 -0
  292. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_555_relay_row_shape.py +0 -0
  293. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_556_relay_freshness.py +0 -0
  294. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_559_edges_truncated.py +0 -0
  295. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_564_bootstrap_first_peer.py +0 -0
  296. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
  297. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_579_register_bootstrap_message.py +0 -0
  298. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_591_cli_step_graph.py +0 -0
  299. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_644_monitor_supervision.py +0 -0
  300. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_648_install_unit.py +0 -0
  301. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_654_reexec.py +0 -0
  302. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_656_owned_slm.py +0 -0
  303. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_684_dreaming_verb.py +0 -0
  304. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_689_pkg_version_order.py +0 -0
  305. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_715_grok_wake.py +0 -0
  306. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_716_timeline_one_path.py +0 -0
  307. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_723_grok_0251.py +0 -0
  308. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_724_ps1_ascii_or_bom.py +0 -0
  309. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_725_stdio_reconfigure.py +0 -0
  310. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_726_grok_limit_screen.py +0 -0
  311. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_729_arm_check_delivery_gap.py +0 -0
  312. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_729_dm_once.py +0 -0
  313. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_729_wake_watchdog.py +0 -0
  314. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_734_dreaming_enrich_degrade.py +0 -0
  315. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_739_wake_writer.py +0 -0
  316. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_740_edit_project.py +0 -0
  317. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_762_cards_edit_priority.py +0 -0
  318. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_764_enrich_collapse.py +0 -0
  319. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_801_confirm_flow_cli.py +0 -0
  320. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_802_stage_history.py +0 -0
  321. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_825_codegraph_hook_noise.py +0 -0
  322. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_825_install_posttooluse_only.py +0 -0
  323. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_825_remaining_cap_shred_bind.py +0 -0
  324. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_829_counterfactual_column.py +0 -0
  325. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_830_hook_binding_version.py +0 -0
  326. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_84_cli_labels.py +0 -0
  327. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_866_doctrine_guide.py +0 -0
  328. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_872_identity_refuses_not_warns.py +0 -0
  329. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_874_source_text.py +0 -0
  330. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
  331. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_931_board_search.py +0 -0
  332. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_935_deadline_clock.py +0 -0
  333. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_937_dreaming_notify.py +0 -0
  334. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_938_organize_line_limit.py +0 -0
  335. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_941_bench_backends.py +0 -0
  336. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_941_harness_342.py +0 -0
  337. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_945_same_line_pairing.py +0 -0
  338. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_955_prompt_audit.py +0 -0
  339. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_957_auth_http.py +0 -0
  340. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_959_venv_interpreter.py +0 -0
  341. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_960_channel.py +0 -0
  342. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
  343. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_961_cursor_grok_footer.py +0 -0
  344. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_961_loud_stop.py +0 -0
  345. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_963_plugin_manifest.py +0 -0
  346. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_964_channel_cell.py +0 -0
  347. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_965_mesh_wait.py +0 -0
  348. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_975_channel_hop.py +0 -0
  349. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_add_security_gate.py +0 -0
  350. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_add.py +0 -0
  351. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_hash.py +0 -0
  352. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_lib.py +0 -0
  353. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_mcp.py +0 -0
  354. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_skill.py +0 -0
  355. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_tool.py +0 -0
  356. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_artifact_uri.py +0 -0
  357. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_backends.py +0 -0
  358. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_claude_cli.py +0 -0
  359. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_codex_cli.py +0 -0
  360. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_command.py +0 -0
  361. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_pack.py +0 -0
  362. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_prices.py +0 -0
  363. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_quality.py +0 -0
  364. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_runner.py +0 -0
  365. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_bench_validate.py +0 -0
  366. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_board_card_thread.py +0 -0
  367. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_board_command.py +0 -0
  368. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_board_merge_check.py +0 -0
  369. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_board_obligation_close.py +0 -0
  370. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_brain_ask_command.py +0 -0
  371. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_brain_ask_env_diagnosis.py +0 -0
  372. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_brain_ask_identity_explicitness.py +0 -0
  373. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_brain_command.py +0 -0
  374. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_caller_meta_guard.py +0 -0
  375. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_capture_lineage.py +0 -0
  376. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_capture_liveness.py +0 -0
  377. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_capture_manifest.py +0 -0
  378. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_capture_paths.py +0 -0
  379. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_capture_security.py +0 -0
  380. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cell_command_dispatch.py +0 -0
  381. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cell_harden.py +0 -0
  382. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cell_loader.py +0 -0
  383. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cell_selfcheck.py +0 -0
  384. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cell_verify.py +0 -0
  385. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_chain_token.py +0 -0
  386. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_channel_command.py +0 -0
  387. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  388. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_channel_post.py +0 -0
  389. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_channel_wake_policy_filter.py +0 -0
  390. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_chat_command.py +0 -0
  391. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_claude_parser.py +0 -0
  392. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_claude_tmux_template.py +0 -0
  393. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_codegraph_caller_scope.py +0 -0
  394. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_codegraph_command.py +0 -0
  395. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_codegraph_hook.py +0 -0
  396. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_codegraph_mcp.py +0 -0
  397. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_codex_waker.py +0 -0
  398. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_compress_caller_convention.py +0 -0
  399. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_compress_command.py +0 -0
  400. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_compress_levers.py +0 -0
  401. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_compress_marker.py +0 -0
  402. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_compress_verify.py +0 -0
  403. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_console_encoding_e2e.py +0 -0
  404. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_content_composition.py +0 -0
  405. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_copilot_membrane.py +0 -0
  406. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_copilot_pane_reader.py +0 -0
  407. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_cursor_membrane.py +0 -0
  408. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_daemon_command.py +0 -0
  409. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_daemon_console_encoding.py +0 -0
  410. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_daemon_delivery.py +0 -0
  411. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_delivery_queue.py +0 -0
  412. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_display_sanitize.py +0 -0
  413. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  414. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_event_emit.py +0 -0
  415. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_feature_to_uri.py +0 -0
  416. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_gateway_brain.py +0 -0
  417. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_gateway_client.py +0 -0
  418. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_gateway_command.py +0 -0
  419. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_gateway_message_idempotency.py +0 -0
  420. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  421. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_group_command.py +0 -0
  422. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_guide_commands_parse.py +0 -0
  423. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_guide_selfcheck.py +0 -0
  424. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_heartbeat_check.py +0 -0
  425. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_highlight_command.py +0 -0
  426. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hook_output.py +0 -0
  427. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_activity_marker.py +0 -0
  428. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_add.py +0 -0
  429. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_bundle.py +0 -0
  430. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_init.py +0 -0
  431. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_legacy_generation_removal.py +0 -0
  432. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_lifecycle.py +0 -0
  433. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_merge.py +0 -0
  434. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_verify.py +0 -0
  435. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  436. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_hooks_win32_command_path.py +0 -0
  437. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_import_command.py +0 -0
  438. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_inbox_identity_provenance.py +0 -0
  439. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_init_command.py +0 -0
  440. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_init_provider_menu.py +0 -0
  441. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_install_codex_hooks.py +0 -0
  442. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_install_hook.py +0 -0
  443. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_install_multiplexer_command.py +0 -0
  444. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_install_postcompact_hook.py +0 -0
  445. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_install_wake_hook.py +0 -0
  446. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_lane_command.py +0 -0
  447. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_launchd_units.py +0 -0
  448. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_main.py +0 -0
  449. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_main_unknown_verb_guard.py +0 -0
  450. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mcp_missing_hint.py +0 -0
  451. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mcp_server.py +0 -0
  452. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_memory_command.py +0 -0
  453. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_memory_emit_snippet.py +0 -0
  454. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_memory_gateway.py +0 -0
  455. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_memory_sync.py +0 -0
  456. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_memory_sync_membrane.py +0 -0
  457. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_command.py +0 -0
  458. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_inbox_mark_read.py +0 -0
  459. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_inbox_sanitize.py +0 -0
  460. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_originate_thread_623.py +0 -0
  461. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_send_unknown_peer.py +0 -0
  462. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_sidecar.py +0 -0
  463. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  464. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_sidecar_muse_sink.py +0 -0
  465. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  466. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_meta_edge_identity.py +0 -0
  467. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_monitor_channel_poll.py +0 -0
  468. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_monitor_cli.py +0 -0
  469. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_monitor_daemon_collision.py +0 -0
  470. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_monitor_self_name_verification.py +0 -0
  471. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_monitor_state_split.py +0 -0
  472. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_multiplexer.py +0 -0
  473. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_muse_membrane.py +0 -0
  474. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_muse_own_cli.py +0 -0
  475. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_obligation_close_is_surfaced.py +0 -0
  476. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_okf_links.py +0 -0
  477. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_onboard_command.py +0 -0
  478. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_opencode_membrane.py +0 -0
  479. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_opencode_plugin.py +0 -0
  480. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_operator_me.py +0 -0
  481. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_orphan_daemons.py +0 -0
  482. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_packaged_artifacts_exist.py +0 -0
  483. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_packaged_skills.py +0 -0
  484. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_executor.py +0 -0
  485. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_executor_windows.py +0 -0
  486. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_reply_drain_command.py +0 -0
  487. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_reply_drain_supervisor.py +0 -0
  488. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_reply_outbox.py +0 -0
  489. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_reply_transport.py +0 -0
  490. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_service_e2e.py +0 -0
  491. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_service_host.py +0 -0
  492. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_service_payload.py +0 -0
  493. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_peer_service_reply.py +0 -0
  494. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_postcompact_recall.py +0 -0
  495. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_process_liveness.py +0 -0
  496. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_protocol_handler.py +0 -0
  497. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_providers_isolation.py +0 -0
  498. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_ratify_command.py +0 -0
  499. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_readme_claims_no_gateway_default.py +0 -0
  500. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_repl_caller_convention.py +0 -0
  501. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_schedule_command.py +0 -0
  502. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_schedule_create_contract.py +0 -0
  503. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_scrub.py +0 -0
  504. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_security.py +0 -0
  505. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_service_app.py +0 -0
  506. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_service_command.py +0 -0
  507. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_service_providers.py +0 -0
  508. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_session_bridge_inject.py +0 -0
  509. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_session_bridge_probe.py +0 -0
  510. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_session_bridge_resolve.py +0 -0
  511. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_session_path_role_gate.py +0 -0
  512. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_shipped_units_match_the_cli.py +0 -0
  513. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_smoke_chat.py +0 -0
  514. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_smoke_one_shot.py +0 -0
  515. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_smoke_phase_5_5.py +0 -0
  516. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_base_hoist.py +0 -0
  517. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_command.py +0 -0
  518. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_identity_stamp.py +0 -0
  519. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_live_pin.py +0 -0
  520. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_mitosis_lineage.py +0 -0
  521. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_tmux_session.py +0 -0
  522. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_spawn_windows_relaunch.py +0 -0
  523. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_stall_alert.py +0 -0
  524. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_subprocess_decode_encoding.py +0 -0
  525. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_timeline_command.py +0 -0
  526. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_tmux_wake_runstate_defer.py +0 -0
  527. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_tmux_wake_session_reanchor.py +0 -0
  528. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_tmux_wake_stale.py +0 -0
  529. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_tmux_wake_submit_verify.py +0 -0
  530. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_token_file_one_parser.py +0 -0
  531. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_token_precedence_explicit_first.py +0 -0
  532. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_unified_token_resolver.py +0 -0
  533. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_version_command.py +0 -0
  534. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_version_consistency.py +0 -0
  535. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_vibe_membrane.py +0 -0
  536. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog.py +0 -0
  537. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_dm_wake.py +0 -0
  538. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  539. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  540. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_emit_health.py +0 -0
  541. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_install_flags.py +0 -0
  542. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_model_rung.py +0 -0
  543. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_watchdog_stale_peers.py +0 -0
  544. {swarph_cli-0.72.33 → swarph_cli-0.72.35}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.33
3
+ Version: 0.72.35
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.72.33"
7
+ version = "0.72.35"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.72.33"
19
+ __version__ = "0.72.35"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -0,0 +1,95 @@
1
+ """``swarph commander`` — propose a decision or an action. Card #1297.
2
+
3
+ The cell asks. The commander decides, from the desktop, with a credential
4
+ this command never holds. ``ask`` posts a decision. ``request`` posts
5
+ ``merge_pr`` or ``release_finish``. An MCP tool for the same queue is a
6
+ later card.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import sys
12
+
13
+ from swarph_cli.commands import mesh as mesh_cmd
14
+
15
+
16
+ def ask_body(question: str, options: list[str], card: int | None) -> dict:
17
+ body: dict = {"type": "decision", "question": question, "options": options}
18
+ if card is not None:
19
+ body["card_id"] = card
20
+ return body
21
+
22
+
23
+ def request_body(kind: str, *, repo: str | None, pr: int | None,
24
+ head: str | None, version: str | None, card: int | None) -> dict:
25
+ body: dict = {"type": kind, "pr": pr, "head_sha": head}
26
+ if kind == "merge_pr":
27
+ body["repo"] = repo
28
+ if kind == "release_finish":
29
+ body["version"] = version
30
+ if card is not None:
31
+ body["card_id"] = card
32
+ return body
33
+
34
+
35
+ def _build_parser() -> argparse.ArgumentParser:
36
+ p = argparse.ArgumentParser(prog="swarph commander")
37
+ sub = p.add_subparsers(dest="command", required=True)
38
+
39
+ ask = sub.add_parser("ask", help="propose a decision")
40
+ mesh_cmd._add_common(ask)
41
+ ask.add_argument("--question", required=True)
42
+ ask.add_argument("--options", default=None,
43
+ help="comma-separated choices, e.g. yes,no")
44
+ ask.add_argument("--card", type=int, default=None)
45
+
46
+ req = sub.add_parser("request", help="propose merge_pr or release_finish")
47
+ mesh_cmd._add_common(req)
48
+ req.add_argument("type", choices=["merge_pr", "release_finish"])
49
+ req.add_argument("--repo", default=None)
50
+ req.add_argument("--pr", type=int, default=None)
51
+ req.add_argument("--head", default=None)
52
+ req.add_argument("--version", default=None)
53
+ req.add_argument("--card", type=int, default=None)
54
+ return p
55
+
56
+
57
+ def _options(raw: str | None) -> list[str]:
58
+ if not raw:
59
+ return []
60
+ return [part.strip() for part in raw.split(",") if part.strip()]
61
+
62
+
63
+ def run_commander(argv: list[str]) -> int:
64
+ args = _build_parser().parse_args(argv)
65
+ try:
66
+ self_name = mesh_cmd._resolve_self_name(args.self_name)
67
+ token = mesh_cmd._resolve_token(self_name, args.token_file)
68
+ except RuntimeError as exc:
69
+ print(f"swarph commander: {exc}", file=sys.stderr)
70
+ return 1
71
+ gw = args.gateway.rstrip("/")
72
+ if args.command == "ask":
73
+ body = ask_body(args.question, _options(args.options), args.card)
74
+ else:
75
+ if args.pr is None or not args.head:
76
+ print("swarph commander request: --pr and --head are required",
77
+ file=sys.stderr)
78
+ return 2
79
+ if args.type == "merge_pr" and not args.repo:
80
+ print("swarph commander request: merge_pr requires --repo",
81
+ file=sys.stderr)
82
+ return 2
83
+ if args.type == "release_finish" and not args.version:
84
+ print("swarph commander request: release_finish requires --version",
85
+ file=sys.stderr)
86
+ return 2
87
+ body = request_body(args.type, repo=args.repo, pr=args.pr, head=args.head,
88
+ version=args.version, card=args.card)
89
+ status, payload = mesh_cmd._post_json(f"{gw}/commander/requests", body, token)
90
+ if status in (200, 201):
91
+ print(f"commander request #{payload.get('id')} {payload.get('status')}")
92
+ return 0
93
+ detail = payload.get("detail") if isinstance(payload, dict) else payload
94
+ print(f"swarph commander: {detail}", file=sys.stderr)
95
+ return 1
@@ -33,9 +33,11 @@ without the SDK installed.
33
33
  from __future__ import annotations
34
34
 
35
35
  import argparse
36
+ import datetime
36
37
  import importlib.metadata
37
38
  import json
38
39
  import os
40
+ import time
39
41
  import urllib.error
40
42
  import urllib.request
41
43
 
@@ -301,17 +303,82 @@ def _dm_error(exc_or_text) -> dict:
301
303
  return {"error": str(exc_or_text), "messages": [], "n": 0}
302
304
 
303
305
 
306
+ # One poll records the cursor at the newest id it saw. The earlier DMs in that
307
+ # poll are behind the cursor and were not delivered. They stay visible while
308
+ # they are younger than this (card #1286). Older than this, they are hidden
309
+ # and counted.
310
+ _DM_GRACE_S = 24 * 60 * 60
311
+
312
+
313
+ def _created_epoch(message: dict) -> float | None:
314
+ raw = message.get("created_at")
315
+ if raw is None or raw == "":
316
+ return None
317
+ if isinstance(raw, bool):
318
+ return None
319
+ if isinstance(raw, (int, float)):
320
+ return float(raw)
321
+ text = str(raw).strip()
322
+ if text.endswith("Z"):
323
+ text = text[:-1] + "+00:00"
324
+ try:
325
+ return datetime.datetime.fromisoformat(text).timestamp()
326
+ except ValueError:
327
+ return None
328
+
329
+
330
+ def _younger_than_grace(message: dict, now: float) -> bool:
331
+ ts = _created_epoch(message)
332
+ if ts is None:
333
+ return False
334
+ return (now - ts) < _DM_GRACE_S
335
+
336
+
337
+ def _observation_cursor(who: str) -> int:
338
+ """The monitor's last_msg_id for this cell. Missing or unreadable is 0.
339
+
340
+ Peek only: nothing here writes cursor.json or marks a message read.
341
+ """
342
+ path = _mesh._default_sidecar_state_dir(who) / "cursor.json"
343
+ try:
344
+ return int(_mesh._read_cursor(path).get("last_msg_id") or 0)
345
+ except (TypeError, ValueError):
346
+ return 0
347
+
348
+
304
349
  def _dm_unread(self_name: str | None = None) -> dict:
305
- """ALL unread DMs for self, full bodies, one call. Peek semantics: marks nothing
306
- read. Above DM_CEILING the oldest DM_CEILING are returned in full and
307
- `not_returned` says how many remain — never a shortened body, never silence."""
350
+ """Unread DMs the monitor has not yet delivered, full bodies, one call.
351
+
352
+ `read_at` is still the reply-ack (#735). The observation cursor advances
353
+ before delivery, so a DM with `id == last_msg_id` was observed and must
354
+ be returned (card #1286). One poll records the cursor at the newest id,
355
+ so the earlier DMs in that batch are behind it. A DM younger than 24 h
356
+ (`created_at`) is never hidden for that reason. A DM strictly behind the
357
+ cursor and not younger than 24 h stays hidden, and `hidden_stale` counts
358
+ those read_at-null DMs. Peek semantics: marks nothing read and does not
359
+ advance the cursor. Above DM_CEILING the oldest DM_CEILING are returned
360
+ in full and `not_returned` says how many remain.
361
+ """
308
362
  try:
309
363
  who, gw, tok = _dm_session(self_name)
310
364
  st, d = _mesh._http_get_json(
311
365
  f"{gw}/messages?to={who}&unread_only=true&limit={DM_GATEWAY_WINDOW}", tok)
312
366
  if not (200 <= st < 300):
313
367
  return _dm_error(f"gateway {st}: {d.get('detail', d) if isinstance(d, dict) else d}")
314
- msgs = sorted(d.get("messages", []), key=lambda m: int(m.get("id", 0)))
368
+ raw = sorted(d.get("messages", []), key=lambda m: int(m.get("id") or 0))
369
+ last_id = _observation_cursor(who)
370
+ now = time.time()
371
+ msgs = []
372
+ hidden_stale = 0
373
+ for message in raw:
374
+ if message.get("from_node") == who:
375
+ continue
376
+ mid = int(message.get("id") or 0)
377
+ if mid >= last_id or _younger_than_grace(message, now):
378
+ msgs.append(message)
379
+ continue
380
+ if message.get("read_at") in (None, ""):
381
+ hidden_stale += 1
315
382
  keep = msgs[:DM_CEILING]
316
383
  return {
317
384
  "self": who,
@@ -319,7 +386,8 @@ def _dm_unread(self_name: str | None = None) -> dict:
319
386
  "n": len(keep),
320
387
  "unread_seen": len(msgs),
321
388
  "not_returned": len(msgs) - len(keep),
322
- "window_full": len(msgs) >= DM_GATEWAY_WINDOW,
389
+ "hidden_stale": hidden_stale,
390
+ "window_full": len(raw) >= DM_GATEWAY_WINDOW,
323
391
  }
324
392
  except Exception as exc: # loud, never an empty inbox
325
393
  return _dm_error(f"{type(exc).__name__}: {exc}")
@@ -473,10 +541,16 @@ try:
473
541
 
474
542
  @mcp.tool()
475
543
  def swarph_dm_unread() -> dict:
476
- """ALL unread DMs for this cell, FULL bodies, ONE call (card #735). Peek semantics:
477
- nothing is marked read. No paging, no cursor. Replying with swarph_dm_reply is the
478
- ack; anything unanswered keeps appearing here. `not_returned` > 0 means more than
479
- the ceiling were unread and the oldest were returned in full."""
544
+ """Unread DMs for this cell, FULL bodies, ONE call (card #735, card #1286).
545
+
546
+ Peek semantics: nothing is marked read and the monitor cursor is not moved.
547
+ The DM whose id equals the cursor was observed and not yet delivered, so
548
+ it is returned. A DM strictly behind the cursor is still returned when
549
+ its created_at is younger than 24 h. Older than that, it stays hidden
550
+ and hidden_stale counts it. No paging parameter.
551
+ Replying with swarph_dm_reply is the ack.
552
+ `not_returned` > 0 means more than the ceiling were unread and the oldest
553
+ were returned in full."""
480
554
  return _dm_unread()
481
555
 
482
556
  @mcp.tool()
@@ -328,6 +328,13 @@ swarph board cards say <id> --to <peer> --content-file <path>
328
328
  swarph board cards ask <id> "<what is owed>" --step <step> --holder <peer> # mint an obligation
329
329
  ```
330
330
 
331
+ Propose a decision or an action. The commander decides it from the desktop; this
332
+ command only posts the request (card #1297).
333
+
334
+ `swarph commander ask --question "..." --options a,b --card N`
335
+ `swarph commander request merge_pr --repo ORG/REPO --pr N --head SHA --card N`
336
+ `swarph commander request release_finish --version V --pr N --head SHA --card N`
337
+
331
338
  **Obligations** exist because "waiting on a review" in someone's prose is not a fact
332
339
  anyone can query. `ask` writes a row: who owes what, since when. It closes when the holder
333
340
  **replies in the thread** -- which is why the distinction between `reply` and `send` above
@@ -108,6 +108,8 @@ _VERB_HANDLERS: dict[str, str] = {
108
108
  "peer-reply-drain": "swarph_cli.commands.peer_reply_drain.run_peer_reply_drain",
109
109
  "monitor": "swarph_cli.commands.monitor.run_monitor",
110
110
  "board": "swarph_cli.commands.board.run_board",
111
+ # card #1297: propose a decision or an action. The commander decides.
112
+ "commander": "swarph_cli.commands.commander.run_commander",
111
113
  "group": "swarph_cli.commands.group.run_group",
112
114
  "rights": "swarph_cli.commands.group.run_rights",
113
115
  "roster": "swarph_cli.commands.roster.run_roster",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.72.33
3
+ Version: 0.72.35
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -82,6 +82,7 @@ src/swarph_cli/commands/codegraph.py
82
82
  src/swarph_cli/commands/codegraph_hook.py
83
83
  src/swarph_cli/commands/codex_hook_output.py
84
84
  src/swarph_cli/commands/codex_waker.py
85
+ src/swarph_cli/commands/commander.py
85
86
  src/swarph_cli/commands/compress.py
86
87
  src/swarph_cli/commands/daemon.py
87
88
  src/swarph_cli/commands/dreaming.py
@@ -252,6 +253,9 @@ tests/test_1265_wake_note.py
252
253
  tests/test_1269_triage_label.py
253
254
  tests/test_126_pending_from_complete.py
254
255
  tests/test_1278_dreaming_reach.py
256
+ tests/test_1286_dm_unread_cursor.py
257
+ tests/test_1297_commander.py
258
+ tests/test_1301_restart_skew.py
255
259
  tests/test_132_ensure_monitor_state_dir.py
256
260
  tests/test_138_memory_navigate.py
257
261
  tests/test_139_tool_probe.py
@@ -146,6 +146,7 @@ esac
146
146
  env["RELEASE_UNITS_FILE"] = str(units)
147
147
  env["RELEASE_VERIFY_CMD"] = "true"
148
148
  env["RELEASE_PYPI_SLEEP"] = "0"
149
+ env["RELEASE_INSTALL_EPOCH"] = "1"
149
150
  proc = _run(["bash", str(RELEASE), "--resume", "9.9.9"], env)
150
151
  assert proc.returncode == 0, proc.stderr + proc.stdout
151
152
  text = log.read_text()
@@ -201,6 +202,7 @@ esac
201
202
  env["RELEASE_UNITS_FILE"] = str(units)
202
203
  env["RELEASE_VERIFY_CMD"] = "true"
203
204
  env["RELEASE_PYPI_SLEEP"] = "0"
205
+ env["RELEASE_INSTALL_EPOCH"] = "1"
204
206
  if extra_env:
205
207
  env.update(extra_env)
206
208
  proc = _run(["bash", str(RELEASE), "--resume", "9.9.9"], env)
@@ -0,0 +1,192 @@
1
+ """Card #1286: swarph_dm_unread agrees with the monitor cursor.
2
+
3
+ A DM with read_at NULL that the cursor has already passed is not unread.
4
+ The call is a peek: cursor.json is not rewritten and nothing is marked read.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import datetime
9
+ import json
10
+ import time
11
+
12
+ from swarph_cli.commands import mesh
13
+ from swarph_cli.commands import mcp_server
14
+ from swarph_cli.dm_frame import frame_text
15
+
16
+
17
+ def test_1286_stale_unread_behind_the_cursor_is_not_new(monkeypatch, tmp_path):
18
+ monkeypatch.setattr(
19
+ mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
20
+ )
21
+ state = tmp_path / "mesh-sidecar"
22
+ state.mkdir()
23
+ # Path.home() ignores HOME on Windows, so point the sidecar helper itself.
24
+ monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state)
25
+ cursor = state / "cursor.json"
26
+ cursor.write_text(json.dumps({"last_msg_id": 200}), encoding="utf-8")
27
+ before = cursor.read_bytes()
28
+ rows = [
29
+ {"id": 100, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
30
+ "content": "STALE1286", "read_at": None},
31
+ {"id": 200, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
32
+ "content": "BOUNDARY1286", "read_at": None},
33
+ {"id": 300, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
34
+ "content": "NEW1286 full body", "read_at": None},
35
+ {"id": 400, "from_node": "lab-ovh", "to_node": "cursor-lin", "kind": "status",
36
+ "content": "SELF1286", "read_at": None},
37
+ ]
38
+
39
+ def fake_get(url, token, **kw):
40
+ assert "unread_only=true" in url
41
+ return 200, {"messages": list(reversed(rows)), "n": len(rows)}
42
+
43
+ monkeypatch.setattr(mcp_server._mesh, "_http_get_json", fake_get)
44
+ out = mcp_server._dm_unread()
45
+ # 200 equals the cursor: observed, not yet delivered, so it is returned.
46
+ # 100 is strictly behind and stays hidden.
47
+ assert [m["id"] for m in out["messages"]] == [200, 300]
48
+ assert out["messages"][1]["content"] == frame_text(rows[2])
49
+ assert "NEW1286 full body" in out["messages"][1]["content"]
50
+ assert "BOUNDARY1286" in out["messages"][0]["content"]
51
+ assert "STALE1286" not in json.dumps(out)
52
+ assert "SELF1286" not in json.dumps(out)
53
+ assert out["n"] == 2 and out["not_returned"] == 0
54
+ # 100 is behind the cursor and has no created_at, so it is not within 24 h.
55
+ assert out.get("hidden_stale") == 1
56
+ assert cursor.read_bytes() == before
57
+
58
+
59
+ def test_1286_monitor_iteration_then_unread_returns_the_woken_dm(monkeypatch, tmp_path):
60
+ """One real monitor iteration observes a new DM, then unread still returns it.
61
+
62
+ The cursor advances before delivery, so the woken id equals last_msg_id.
63
+ On 74347164 unread uses a strict `>` and the DM is gone.
64
+ """
65
+ state_dir = tmp_path / "mesh-sidecar"
66
+ state_dir.mkdir()
67
+ (state_dir / "cursor.json").write_text(
68
+ json.dumps({"last_msg_id": 100}), encoding="utf-8")
69
+ monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state_dir)
70
+ monkeypatch.setattr(
71
+ mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
72
+ )
73
+ woken = {
74
+ "id": 501, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "question",
75
+ "content": "WOKEN1286 full body", "read_at": None,
76
+ }
77
+ stale = {
78
+ "id": 50, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
79
+ "content": "BEHIND1286", "read_at": None,
80
+ }
81
+
82
+ def fake_get(url, token, **kw):
83
+ return 200, {"messages": [stale, woken]}
84
+
85
+ monkeypatch.setattr(mesh, "_http_get_json", fake_get)
86
+ state = mesh.MonitorState(
87
+ self_name="lab-ovh",
88
+ state_dir=state_dir,
89
+ gateway="http://gw",
90
+ token="tok",
91
+ sinks=[mesh.parse_sink("none")],
92
+ )
93
+ mesh._monitor_iteration(state, poll_channels=False)
94
+ out = mcp_server._dm_unread()
95
+ ids = [m["id"] for m in out["messages"]]
96
+ assert ids == [501]
97
+ assert "WOKEN1286 full body" in out["messages"][0]["content"]
98
+ assert "BEHIND1286" not in json.dumps(out)
99
+ assert out.get("hidden_stale") == 1
100
+
101
+
102
+ def _iso(offset_s: float) -> str:
103
+ stamp = datetime.datetime.fromtimestamp(time.time() + offset_s, datetime.timezone.utc)
104
+ return stamp.isoformat()
105
+
106
+
107
+ def _row(mid: int, content: str, created_at: str) -> dict:
108
+ return {
109
+ "id": mid, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "question",
110
+ "content": content, "read_at": None, "created_at": created_at,
111
+ }
112
+
113
+
114
+ def _monitor(monkeypatch, tmp_path, rows, *, polls: int = 1, windows=None):
115
+ state_dir = tmp_path / "mesh-sidecar"
116
+ state_dir.mkdir()
117
+ (state_dir / "cursor.json").write_text(
118
+ json.dumps({"last_msg_id": 100}), encoding="utf-8")
119
+ monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state_dir)
120
+ monkeypatch.setattr(
121
+ mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
122
+ )
123
+ seen = {"n": 0}
124
+
125
+ def fake_get(url, token, **kw):
126
+ if "unread_only" in url:
127
+ return 200, {"messages": list(rows)}
128
+ if windows is None:
129
+ return 200, {"messages": list(rows)}
130
+ index = min(seen["n"], len(windows) - 1)
131
+ seen["n"] += 1
132
+ wanted = windows[index]
133
+ return 200, {"messages": [row for row in rows if row["id"] in wanted]}
134
+
135
+ monkeypatch.setattr(mesh, "_http_get_json", fake_get)
136
+ state = mesh.MonitorState(
137
+ self_name="lab-ovh",
138
+ state_dir=state_dir,
139
+ gateway="http://gw",
140
+ token="tok",
141
+ sinks=[mesh.parse_sink("none")],
142
+ )
143
+ for _ in range(polls):
144
+ mesh._monitor_iteration(state, poll_channels=False)
145
+ return mcp_server._dm_unread(), state
146
+
147
+
148
+ def test_1286_one_poll_observing_three_returns_all_three(monkeypatch, tmp_path):
149
+ """One poll records the cursor at the newest id. The two earlier DMs are young."""
150
+ young = _iso(0)
151
+ rows = [
152
+ _row(501, "ONE1286", young),
153
+ _row(502, "TWO1286", young),
154
+ _row(503, "THREE1286", young),
155
+ ]
156
+ out, state = _monitor(monkeypatch, tmp_path, rows)
157
+ assert state.observed["last_msg_id"] == 503
158
+ assert [m["id"] for m in out["messages"]] == [501, 502, 503]
159
+ assert out.get("hidden_stale") == 0
160
+
161
+
162
+ def test_1286_three_polls_then_unread_returns_all_three(monkeypatch, tmp_path):
163
+ """A busy host calls the tool after three polls, one new DM each."""
164
+ young = _iso(-60)
165
+ rows = [
166
+ _row(501, "POLL1286a", young),
167
+ _row(502, "POLL1286b", young),
168
+ _row(503, "POLL1286c", young),
169
+ ]
170
+ out, state = _monitor(
171
+ monkeypatch, tmp_path, rows, polls=3, windows=([501], [501, 502], [501, 502, 503]),
172
+ )
173
+ assert state.observed["last_msg_id"] == 503
174
+ assert [m["id"] for m in out["messages"]] == [501, 502, 503]
175
+ assert "POLL1286a" in out["messages"][0]["content"]
176
+ assert out.get("hidden_stale") == 0
177
+
178
+
179
+ def test_1286_old_dm_behind_the_cursor_is_hidden_and_counted(monkeypatch, tmp_path):
180
+ young = _iso(0)
181
+ old = _iso(-(25 * 60 * 60))
182
+ rows = [
183
+ _row(50, "OLD1286", old),
184
+ _row(80, "YOUNGBEHIND1286", young),
185
+ _row(501, "FRESH1286", young),
186
+ ]
187
+ out, state = _monitor(monkeypatch, tmp_path, rows)
188
+ assert state.observed["last_msg_id"] == 501
189
+ assert "OLD1286" not in json.dumps(out)
190
+ assert out.get("hidden_stale") == 1
191
+ assert [m["id"] for m in out["messages"]] == [80, 501]
192
+ assert "YOUNGBEHIND1286" in out["messages"][0]["content"]
@@ -0,0 +1,60 @@
1
+ """card #1297 — `swarph commander ask` and `swarph commander request`.
2
+
3
+ On current main the verb is unknown, so these assert exit 0 and fail there.
4
+ """
5
+ from swarph_cli.main import main
6
+
7
+ HEAD = "a" * 40
8
+
9
+
10
+ def _patch(monkeypatch):
11
+ posted = {}
12
+
13
+ def fake_post(url, body, token, timeout=10.0):
14
+ posted["url"] = url
15
+ posted["body"] = body
16
+ posted["token"] = token
17
+ return 201, {"id": 7, "status": "pending"}
18
+
19
+ monkeypatch.setattr("swarph_cli.commands.mesh._post_json", fake_post)
20
+ monkeypatch.setattr("swarph_cli.commands.mesh._resolve_token", lambda *a, **k: "tok")
21
+ return posted
22
+
23
+
24
+ def test_commander_ask_posts_a_decision(monkeypatch):
25
+ posted = _patch(monkeypatch)
26
+ rc = main(["commander", "ask", "--question", "ship it?", "--options", "yes,no",
27
+ "--card", "1297", "--gateway", "http://127.0.0.1:9", "--as", "cursor-lin"])
28
+ assert rc == 0
29
+ assert posted["url"].endswith("/commander/requests")
30
+ assert posted["body"] == {
31
+ "type": "decision",
32
+ "question": "ship it?",
33
+ "options": ["yes", "no"],
34
+ "card_id": 1297,
35
+ }
36
+
37
+
38
+ def test_commander_request_posts_merge_and_release(monkeypatch):
39
+ posted = _patch(monkeypatch)
40
+ rc = main(["commander", "request", "merge_pr",
41
+ "--repo", "BrainSurfing-tech/swarph-cli", "--pr", "12",
42
+ "--head", HEAD, "--card", "1297",
43
+ "--gateway", "http://127.0.0.1:9", "--as", "cursor-lin"])
44
+ assert rc == 0
45
+ assert posted["body"]["type"] == "merge_pr"
46
+ assert posted["body"]["repo"] == "BrainSurfing-tech/swarph-cli"
47
+ assert posted["body"]["pr"] == 12
48
+ assert posted["body"]["head_sha"] == HEAD
49
+ assert posted["body"]["card_id"] == 1297
50
+
51
+ rc = main(["commander", "request", "release_finish",
52
+ "--version", "0.9.0", "--pr", "12", "--head", HEAD,
53
+ "--gateway", "http://127.0.0.1:9", "--as", "cursor-lin"])
54
+ assert rc == 0
55
+ assert posted["body"] == {
56
+ "type": "release_finish",
57
+ "pr": 12,
58
+ "head_sha": HEAD,
59
+ "version": "0.9.0",
60
+ }
@@ -0,0 +1,64 @@
1
+ """card #1301: a same-second restart is not a failed restart.
2
+
3
+ /proc btime is a whole second, so the derived start can read up to 1s
4
+ before the install cutoff. A process that did not restart still fails.
5
+ """
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ ROOT = Path(__file__).resolve().parents[1]
11
+ CHECK = ROOT / "scripts" / "release_check.py"
12
+ RELEASE = ROOT / "release.sh"
13
+
14
+
15
+ def _run(args, env):
16
+ return subprocess.run(args, cwd=ROOT, env=env, text=True, capture_output=True)
17
+
18
+
19
+ def test_1301_same_second_restart_passes_and_an_old_process_fails(tmp_path):
20
+ image = tmp_path / "image"
21
+ image.write_bytes(b"swarph_cli-9.9.9.dist-info")
22
+ env = os.environ.copy()
23
+ env["RELEASE_IMAGE"] = str(image)
24
+ env["RELEASE_START_EPOCH"] = "648.800"
25
+ ok = _run(
26
+ ["python3", str(CHECK), "verify", "--pid", "42", "--expected", "9.9.9",
27
+ "--install-epoch", "649.000"],
28
+ env,
29
+ )
30
+ assert ok.returncode == 0, ok.stderr
31
+ assert "before the install" not in ok.stderr
32
+ env["RELEASE_START_EPOCH"] = "100.000"
33
+ bad = _run(
34
+ ["python3", str(CHECK), "verify", "--pid", "42", "--expected", "9.9.9",
35
+ "--install-epoch", "649.000"],
36
+ env,
37
+ )
38
+ assert bad.returncode != 0
39
+ assert "before the install" in bad.stderr
40
+ env["RELEASE_START_EPOCH"] = "648.800"
41
+ image.write_bytes(b"swarph_cli-0.1.0.dist-info")
42
+ wrong = _run(
43
+ ["python3", str(CHECK), "verify", "--pid", "42", "--expected", "9.9.9",
44
+ "--install-epoch", "649.000"],
45
+ env,
46
+ )
47
+ assert wrong.returncode != 0
48
+ assert "0.1.0" in wrong.stderr
49
+
50
+
51
+ def test_1301_record_mtime_is_the_install_moment(tmp_path):
52
+ record = tmp_path / "RECORD"
53
+ record.write_bytes(b"x")
54
+ os.utime(record, (649.0, 649.0))
55
+ proc = _run(
56
+ ["python3", str(CHECK), "record-mtime", "--version", "9.9.9",
57
+ "--record", str(record)],
58
+ os.environ.copy(),
59
+ )
60
+ assert proc.returncode == 0, proc.stderr
61
+ assert abs(float(proc.stdout.strip()) - 649.0) < 0.01
62
+ text = RELEASE.read_text()
63
+ assert 'record-mtime --version "$VERSION"' in text
64
+ assert "install_epoch=$(date +%s)" not in text
@@ -44,7 +44,10 @@ FIXTURE = [
44
44
 
45
45
 
46
46
  @pytest.fixture(autouse=True)
47
- def _session(monkeypatch: pytest.MonkeyPatch):
47
+ def _session(monkeypatch: pytest.MonkeyPatch, tmp_path):
48
+ # Card #1286 reads the monitor cursor. These tests are about the unread
49
+ # payload, so they observe from a private home whose cursor is absent.
50
+ monkeypatch.setenv("HOME", str(tmp_path))
48
51
  monkeypatch.setattr(mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"))
49
52
  if MUTATE == "truncate":
50
53
  real = mcp_server._lean
File without changes
File without changes
File without changes