swarph-cli 0.41.0__tar.gz → 0.41.1__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 (228) hide show
  1. {swarph_cli-0.41.0/src/swarph_cli.egg-info → swarph_cli-0.41.1}/PKG-INFO +34 -7
  2. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/README.md +33 -5
  3. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/pyproject.toml +8 -2
  4. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/__init__.py +1 -1
  5. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/codegraph_hook.py +40 -1
  6. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/server.py +15 -1
  7. {swarph_cli-0.41.0 → swarph_cli-0.41.1/src/swarph_cli.egg-info}/PKG-INFO +34 -7
  8. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_brain_ask_command.py +1 -1
  9. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_codegraph_hook.py +48 -0
  10. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_gateway_brain.py +33 -0
  11. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog.py +1 -1
  12. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/LICENSE +0 -0
  13. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/setup.cfg +0 -0
  14. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/__init__.py +0 -0
  15. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/backends.py +0 -0
  16. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/pack.py +0 -0
  17. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/prices.py +0 -0
  18. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/quality.py +0 -0
  19. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/refresh_prices.py +0 -0
  20. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/runner.py +0 -0
  21. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/bench/validate.py +0 -0
  22. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/caller.py +0 -0
  23. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/__init__.py +0 -0
  24. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/harden.py +0 -0
  25. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/lineage.py +0 -0
  26. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/liveness.py +0 -0
  27. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/manifest.py +0 -0
  28. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/paths.py +0 -0
  29. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/capture/verify.py +0 -0
  30. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/cell.py +0 -0
  31. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/chain_token.py +0 -0
  32. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/__init__.py +0 -0
  33. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/_display.py +0 -0
  34. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/_gateway_client.py +0 -0
  35. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/add.py +0 -0
  36. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/bench.py +0 -0
  37. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/board.py +0 -0
  38. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/board_merge_check.py +0 -0
  39. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/brain.py +0 -0
  40. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/brain_ask.py +0 -0
  41. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/cell.py +0 -0
  42. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  43. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/channel.py +0 -0
  44. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/chat.py +0 -0
  45. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/codegraph.py +0 -0
  46. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/compress.py +0 -0
  47. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/daemon.py +0 -0
  48. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/event.py +0 -0
  49. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/gateway.py +0 -0
  50. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/group.py +0 -0
  51. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/highlight.py +0 -0
  52. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/hook_output.py +0 -0
  53. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/hooks.py +0 -0
  54. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/import_session.py +0 -0
  55. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/init.py +0 -0
  56. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/install_hook.py +0 -0
  57. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  58. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/lane.py +0 -0
  59. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/mcp_server.py +0 -0
  60. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/memory.py +0 -0
  61. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/memory_sync.py +0 -0
  62. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/mesh.py +0 -0
  63. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/monitor.py +0 -0
  64. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/okf_links.py +0 -0
  65. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/onboard.py +0 -0
  66. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/protocol_handler.py +0 -0
  67. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/ratify.py +0 -0
  68. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/schedule.py +0 -0
  69. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/security.py +0 -0
  70. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/service.py +0 -0
  71. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/spawn.py +0 -0
  72. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/timeline.py +0 -0
  73. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/commands/watchdog.py +0 -0
  74. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/compress/__init__.py +0 -0
  75. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/compress/levers.py +0 -0
  76. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/compress/marker.py +0 -0
  77. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/compress/verify.py +0 -0
  78. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/console_safe.py +0 -0
  79. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/delivery_queue.py +0 -0
  80. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/__init__.py +0 -0
  81. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/feature_registry.py +0 -0
  82. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/lanes_control.py +0 -0
  83. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/schema.sql +0 -0
  84. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/gateway/services_control.py +0 -0
  85. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/main.py +0 -0
  86. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/multiplexer.py +0 -0
  87. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/parsers/__init__.py +0 -0
  88. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/parsers/claude.py +0 -0
  89. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  90. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/service/__init__.py +0 -0
  91. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/service/app.py +0 -0
  92. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/service/providers.py +0 -0
  93. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/session_bridge.py +0 -0
  94. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/stall_alert.py +0 -0
  95. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  96. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  97. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  98. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli.egg-info/SOURCES.txt +0 -0
  99. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  100. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  101. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli.egg-info/requires.txt +0 -0
  102. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/src/swarph_cli.egg-info/top_level.txt +0 -0
  103. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_add_security_gate.py +0 -0
  104. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_add.py +0 -0
  105. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_hash.py +0 -0
  106. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_lib.py +0 -0
  107. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_mcp.py +0 -0
  108. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_skill.py +0 -0
  109. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_tool.py +0 -0
  110. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_artifact_uri.py +0 -0
  111. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_backends.py +0 -0
  112. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_command.py +0 -0
  113. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_pack.py +0 -0
  114. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_prices.py +0 -0
  115. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_quality.py +0 -0
  116. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_runner.py +0 -0
  117. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_bench_validate.py +0 -0
  118. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_board_command.py +0 -0
  119. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_board_merge_check.py +0 -0
  120. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_brain_ask_env_diagnosis.py +0 -0
  121. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_brain_command.py +0 -0
  122. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_caller_meta_guard.py +0 -0
  123. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_capture_lineage.py +0 -0
  124. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_capture_liveness.py +0 -0
  125. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_capture_manifest.py +0 -0
  126. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_capture_paths.py +0 -0
  127. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_capture_security.py +0 -0
  128. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_cell_command_dispatch.py +0 -0
  129. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_cell_harden.py +0 -0
  130. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_cell_loader.py +0 -0
  131. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_cell_selfcheck.py +0 -0
  132. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_cell_verify.py +0 -0
  133. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_chain_token.py +0 -0
  134. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_channel_command.py +0 -0
  135. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_channel_post.py +0 -0
  136. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_chat_command.py +0 -0
  137. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_claude_parser.py +0 -0
  138. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_claude_tmux_template.py +0 -0
  139. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_codegraph_caller_scope.py +0 -0
  140. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_codegraph_command.py +0 -0
  141. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_codegraph_mcp.py +0 -0
  142. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_compress_caller_convention.py +0 -0
  143. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_compress_command.py +0 -0
  144. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_compress_levers.py +0 -0
  145. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_compress_marker.py +0 -0
  146. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_compress_verify.py +0 -0
  147. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_console_encoding_e2e.py +0 -0
  148. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_daemon_command.py +0 -0
  149. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_daemon_console_encoding.py +0 -0
  150. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_daemon_delivery.py +0 -0
  151. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_delivery_queue.py +0 -0
  152. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_display_sanitize.py +0 -0
  153. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_event_emit.py +0 -0
  154. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_feature_to_uri.py +0 -0
  155. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_gateway_client.py +0 -0
  156. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_gateway_command.py +0 -0
  157. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_group_command.py +0 -0
  158. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_highlight_command.py +0 -0
  159. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hook_output.py +0 -0
  160. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_activity_marker.py +0 -0
  161. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_add.py +0 -0
  162. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_bundle.py +0 -0
  163. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_init.py +0 -0
  164. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_lifecycle.py +0 -0
  165. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_hooks_merge.py +0 -0
  166. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_import_command.py +0 -0
  167. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_init_command.py +0 -0
  168. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_install_hook.py +0 -0
  169. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_install_multiplexer_command.py +0 -0
  170. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_lane_command.py +0 -0
  171. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_main.py +0 -0
  172. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_main_unknown_verb_guard.py +0 -0
  173. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mcp_server.py +0 -0
  174. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_memory_command.py +0 -0
  175. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_memory_gateway.py +0 -0
  176. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_memory_sync.py +0 -0
  177. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_memory_sync_membrane.py +0 -0
  178. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_command.py +0 -0
  179. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_inbox_mark_read.py +0 -0
  180. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_inbox_sanitize.py +0 -0
  181. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_sidecar.py +0 -0
  182. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  183. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  184. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_meta_edge_identity.py +0 -0
  185. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_monitor_cli.py +0 -0
  186. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_monitor_daemon_collision.py +0 -0
  187. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_monitor_self_name_verification.py +0 -0
  188. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_monitor_state_split.py +0 -0
  189. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_multiplexer.py +0 -0
  190. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_okf_links.py +0 -0
  191. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_onboard_command.py +0 -0
  192. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_packaged_artifacts_exist.py +0 -0
  193. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_process_liveness.py +0 -0
  194. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_protocol_handler.py +0 -0
  195. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_providers_isolation.py +0 -0
  196. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_ratify_command.py +0 -0
  197. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_repl_caller_convention.py +0 -0
  198. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_schedule_command.py +0 -0
  199. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_schedule_create_contract.py +0 -0
  200. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_security.py +0 -0
  201. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_service_app.py +0 -0
  202. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_service_command.py +0 -0
  203. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_service_providers.py +0 -0
  204. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_session_bridge_inject.py +0 -0
  205. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_session_bridge_probe.py +0 -0
  206. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_session_bridge_resolve.py +0 -0
  207. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_session_path_role_gate.py +0 -0
  208. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_shipped_units_match_the_cli.py +0 -0
  209. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_smoke_chat.py +0 -0
  210. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_smoke_one_shot.py +0 -0
  211. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_smoke_phase_5_5.py +0 -0
  212. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_base_hoist.py +0 -0
  213. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_command.py +0 -0
  214. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_live_pin.py +0 -0
  215. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_mitosis_lineage.py +0 -0
  216. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_tmux_session.py +0 -0
  217. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_spawn_windows_relaunch.py +0 -0
  218. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_stall_alert.py +0 -0
  219. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_timeline_command.py +0 -0
  220. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_token_file_one_parser.py +0 -0
  221. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_version_consistency.py +0 -0
  222. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_dm_wake.py +0 -0
  223. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  224. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  225. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_emit_health.py +0 -0
  226. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_install_flags.py +0 -0
  227. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_model_rung.py +0 -0
  228. {swarph_cli-0.41.0 → swarph_cli-0.41.1}/tests/test_watchdog_stale_peers.py +0 -0
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.41.0
3
+ Version: 0.41.1
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`, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/BrainSurfing-tech/swarph-cli
8
8
  Project-URL: Source, https://github.com/BrainSurfing-tech/swarph-cli
9
9
  Project-URL: Substrate, https://github.com/BrainSurfing-tech/swarph-mesh
10
- Project-URL: Spec, https://github.com/darw007d/hedge-fund-mcp/blob/main/research/swarph_cli/PLAN.md
11
10
  Keywords: swarph,llm,cli,mesh,gemini,claude,deepseek
12
11
  Classifier: Development Status :: 3 - Alpha
13
12
  Classifier: Environment :: Console
@@ -467,13 +466,41 @@ swarph watchdog --check --peer researcher --gateway http://localhost:8788 --dm-w
467
466
  Installs Claude Code hooks as **content** wired into `~/.claude/settings.json` — a hook becomes an installable artifact (a script + its event/matcher bindings merged into your settings) with no swarph-cli version bump per hook, the same way `watchdog --install-service` ships systemd units as bundled data.
468
467
 
469
468
  ```bash
470
- swarph hooks init # install the recommended bundled set (cell-resilience)
471
- swarph hooks add cell-resilience # install one builtin by name
472
- swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
473
- swarph hooks list # builtins + install status (installed|available)
469
+ swarph hooks init # install the recommended bundled set
470
+ swarph hooks add codegraph-on-grep # structural search alongside grep (see below)
471
+ swarph hooks add cell-resilience # install one builtin by name
472
+ swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
473
+ swarph hooks list # builtins + install status (installed|available)
474
474
  swarph hooks remove cell-resilience
475
475
  ```
476
476
 
477
+ **Bundled `codegraph-on-grep`** *(0.41.0)*. Binds `PostToolUse`/`Bash`. When a `grep`/`rg` searches
478
+ **code**, it also queries the mesh gateway's structural codegraph and hands back the symbol-level
479
+ answer — definitions, `file:line`, and **caller counts, which grep structurally cannot see**:
480
+
481
+ ```
482
+ CODEGRAPH (structural, index 2.1h old) for 'def _board_grant' — grep found text; this is
483
+ the symbol graph, incl. CALLER COUNTS grep cannot see:
484
+ mesh-gateway/server.py:1352 function _board_grant callers=11
485
+ ```
486
+
487
+ The point is **context cost**: a grep returning 200 matching lines is far more expensive than the
488
+ handful of symbols that answer *"where is this defined and who calls it"*.
489
+
490
+ - **It supplements grep; it never blocks it.** grep stays correct for config, logs and string
491
+ literals — the codegraph indexes *symbols*.
492
+ - **An unavailable index is reported loudly, never as "no matches."** A failed lookup rendered as a
493
+ real negative teaches exactly the wrong thing, so the hook says so and marks the answer
494
+ unverified. A genuinely empty result from a *working* index says `a REAL negative` explicitly, and
495
+ a stale index reports its age.
496
+ - **No local index required.** It asks the gateway, which scopes both rows and caller counts to your
497
+ visibility — resolved from your **bearer token**, never from a field you send, so it cannot be
498
+ self-asserted.
499
+
500
+ Needs `SWARPH_SELF` (or `--as`), your peer token at `~/.config/swarph/<cell>.peer_token`, and a
501
+ reachable gateway. Do not run a hand-rolled copy alongside it: two hooks per `Bash` call means two
502
+ lookups, doubling the cost the hook exists to cut.
503
+
477
504
  **Trust model.** Three tiers: `builtin` (trusted, bundled with swarph-cli — installs without a prompt), `local` (a bundle dir you point at — shown then confirmed before any write), and `published`/`@cell/name` (**fails closed in v1** — never installs another cell's unreviewed code). Signed-publisher identity plus a publish-time security gate is the v2 model.
478
505
 
479
506
  **Bundled `cell-resilience`.** Binds `StopFailure`/`rate_limit` + `Stop`/`(all)` to a script that writes `$XDG_STATE_HOME/swarph/idle_since.json` (`{"session","reason","hook_event","ts"}`, `reason=throttle|normal`) — the push-side throttle detector the watchdog's `--dm-wake` can read instead of polling. Observational only: it never blocks the session and always exits 0 (jq if present, printf/sed fallback otherwise).
@@ -655,7 +682,7 @@ Dry-run is the default — nothing mutates without `--apply`. Cron-friendly exit
655
682
  | `8` | refused — not idempotent (second pass kept cutting; signal-eating alarm) |
656
683
 
657
684
  Design spec: `docs/superpowers/specs/2026-06-11-swarph-context-compressor-design.md`
658
- (in the hedge-fund-mcp repo).
685
+ (in a private design repo; not published).
659
686
 
660
687
  ### `--json` mode semantics
661
688
 
@@ -420,13 +420,41 @@ swarph watchdog --check --peer researcher --gateway http://localhost:8788 --dm-w
420
420
  Installs Claude Code hooks as **content** wired into `~/.claude/settings.json` — a hook becomes an installable artifact (a script + its event/matcher bindings merged into your settings) with no swarph-cli version bump per hook, the same way `watchdog --install-service` ships systemd units as bundled data.
421
421
 
422
422
  ```bash
423
- swarph hooks init # install the recommended bundled set (cell-resilience)
424
- swarph hooks add cell-resilience # install one builtin by name
425
- swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
426
- swarph hooks list # builtins + install status (installed|available)
423
+ swarph hooks init # install the recommended bundled set
424
+ swarph hooks add codegraph-on-grep # structural search alongside grep (see below)
425
+ swarph hooks add cell-resilience # install one builtin by name
426
+ swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
427
+ swarph hooks list # builtins + install status (installed|available)
427
428
  swarph hooks remove cell-resilience
428
429
  ```
429
430
 
431
+ **Bundled `codegraph-on-grep`** *(0.41.0)*. Binds `PostToolUse`/`Bash`. When a `grep`/`rg` searches
432
+ **code**, it also queries the mesh gateway's structural codegraph and hands back the symbol-level
433
+ answer — definitions, `file:line`, and **caller counts, which grep structurally cannot see**:
434
+
435
+ ```
436
+ CODEGRAPH (structural, index 2.1h old) for 'def _board_grant' — grep found text; this is
437
+ the symbol graph, incl. CALLER COUNTS grep cannot see:
438
+ mesh-gateway/server.py:1352 function _board_grant callers=11
439
+ ```
440
+
441
+ The point is **context cost**: a grep returning 200 matching lines is far more expensive than the
442
+ handful of symbols that answer *"where is this defined and who calls it"*.
443
+
444
+ - **It supplements grep; it never blocks it.** grep stays correct for config, logs and string
445
+ literals — the codegraph indexes *symbols*.
446
+ - **An unavailable index is reported loudly, never as "no matches."** A failed lookup rendered as a
447
+ real negative teaches exactly the wrong thing, so the hook says so and marks the answer
448
+ unverified. A genuinely empty result from a *working* index says `a REAL negative` explicitly, and
449
+ a stale index reports its age.
450
+ - **No local index required.** It asks the gateway, which scopes both rows and caller counts to your
451
+ visibility — resolved from your **bearer token**, never from a field you send, so it cannot be
452
+ self-asserted.
453
+
454
+ Needs `SWARPH_SELF` (or `--as`), your peer token at `~/.config/swarph/<cell>.peer_token`, and a
455
+ reachable gateway. Do not run a hand-rolled copy alongside it: two hooks per `Bash` call means two
456
+ lookups, doubling the cost the hook exists to cut.
457
+
430
458
  **Trust model.** Three tiers: `builtin` (trusted, bundled with swarph-cli — installs without a prompt), `local` (a bundle dir you point at — shown then confirmed before any write), and `published`/`@cell/name` (**fails closed in v1** — never installs another cell's unreviewed code). Signed-publisher identity plus a publish-time security gate is the v2 model.
431
459
 
432
460
  **Bundled `cell-resilience`.** Binds `StopFailure`/`rate_limit` + `Stop`/`(all)` to a script that writes `$XDG_STATE_HOME/swarph/idle_since.json` (`{"session","reason","hook_event","ts"}`, `reason=throttle|normal`) — the push-side throttle detector the watchdog's `--dm-wake` can read instead of polling. Observational only: it never blocks the session and always exits 0 (jq if present, printf/sed fallback otherwise).
@@ -608,7 +636,7 @@ Dry-run is the default — nothing mutates without `--apply`. Cron-friendly exit
608
636
  | `8` | refused — not idempotent (second pass kept cutting; signal-eating alarm) |
609
637
 
610
638
  Design spec: `docs/superpowers/specs/2026-06-11-swarph-context-compressor-design.md`
611
- (in the hedge-fund-mcp repo).
639
+ (in a private design repo; not published).
612
640
 
613
641
  ### `--json` mode semantics
614
642
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.41.0"
7
+ version = "0.41.1"
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`, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -51,7 +51,6 @@ dependencies = [
51
51
  Homepage = "https://github.com/BrainSurfing-tech/swarph-cli"
52
52
  Source = "https://github.com/BrainSurfing-tech/swarph-cli"
53
53
  Substrate = "https://github.com/BrainSurfing-tech/swarph-mesh"
54
- Spec = "https://github.com/darw007d/hedge-fund-mcp/blob/main/research/swarph_cli/PLAN.md"
55
54
 
56
55
  [project.optional-dependencies]
57
56
  dev = ["pytest>=7.0"]
@@ -111,4 +110,11 @@ swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
111
110
 
112
111
  [tool.pytest.ini_options]
113
112
  testpaths = ["tests"]
113
+ # The tree must win over any installed copy. Without this, `pip install swarph-cli`
114
+ # on the same box shadows src/ and the suite silently tests the PUBLISHED wheel
115
+ # instead of your edits — every local change reads green whether or not it works.
116
+ # Measured 2026-08-01: 1689 passed against site-packages vs 1690 against the tree;
117
+ # the only test that noticed was a brand-new one, because the tree and 0.41.0 were
118
+ # nearly identical. That agreement is a polluted baseline, not isolation.
119
+ pythonpath = ["src"]
114
120
  addopts = "-v --tb=short"
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.41.0"
19
+ __version__ = "0.41.1"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -152,6 +152,35 @@ def query_gateway(term: str, gateway: str, token: str, limit: int = MAX_ROWS) ->
152
152
  return {"error": f"{type(e).__name__}: {e}"}
153
153
 
154
154
 
155
+ def _match_quality(term: str, rows: list) -> tuple:
156
+ """Did the results actually match what was ASKED, or only a common token?
157
+
158
+ >>> THE EMPTY ANSWER WAS MADE HONEST; THE NON-EMPTY ONE WAS NOT. <<<
159
+ (Reported first-hand by a peer, 2026-08-01.) The index's query sanitiser
160
+ OR-JOINS tokens, so `def command_beta_executor` matches anything containing
161
+ "command". He grepped a private-repo file and got six confident-looking
162
+ swarph-cli symbols WITH CALLER COUNTS — plausible structure from the wrong
163
+ repository, and caller counts make it read authoritative. His words: "the
164
+ failure mode you engineered out of the empty case walked back in through the
165
+ non-empty one."
166
+
167
+ So: find the query's DISTINCTIVE token — the longest content token, which is
168
+ the one carrying the intent (`command_beta_executor`, not `def`) — and check
169
+ whether ANY returned symbol name actually contains it. If none does, every
170
+ row is a common-token coincidence and must be labelled as such rather than
171
+ served as an answer.
172
+
173
+ Returns (distinctive_token, n_rows_matching_it).
174
+ """
175
+ toks = [t for t in re.findall(r"[A-Za-z0-9_]+", (term or "").lower())
176
+ if len(t) > 2 and t not in {"def", "class", "async", "the", "for"}]
177
+ if not toks:
178
+ return ("", len(rows))
179
+ key = max(toks, key=len)
180
+ hits = sum(1 for r in rows if key in str(r.get("name", "")).lower())
181
+ return (key, hits)
182
+
183
+
155
184
  def render(term: str, env: dict) -> str:
156
185
  """Format the envelope for injection. Errors are LOUD and named."""
157
186
  if "error" in env:
@@ -179,8 +208,18 @@ def render(term: str, env: dict) -> str:
179
208
  f"{r.get('kind')} {r.get('name')} callers={r.get('callers')}"
180
209
  for r in rows[:MAX_ROWS]
181
210
  )
211
+ warn = ""
212
+ if rows:
213
+ key, hits = _match_quality(term, rows)
214
+ if key and hits == 0:
215
+ repos = sorted({str(r.get("repo")) for r in rows})
216
+ warn = (f"\n >>> FUZZY MATCH — NOT AN ANSWER TO YOUR QUERY. No returned symbol's "
217
+ f"name contains {key!r}. These matched a COMMON TOKEN only"
218
+ + (f", and all are from: {', '.join(repos)}" if repos else "")
219
+ + f". Treat them as unrelated: the symbol you grepped for is NOT in "
220
+ f"what this index can see. <<<")
182
221
  return (f"CODEGRAPH (structural{age}) for '{term}' — grep found text; this is "
183
- f"the symbol graph, incl. CALLER COUNTS grep cannot see:\n{body}\n\n"
222
+ f"the symbol graph, incl. CALLER COUNTS grep cannot see:\n{body}{warn}\n\n"
184
223
  f"Use this for definitions/callers/blast-radius. grep remains correct "
185
224
  f"for config, logs and string literals the codegraph does not index.")
186
225
 
@@ -76,7 +76,12 @@ PORT = int(os.environ.get("PORT", "8788"))
76
76
  # Gateway-held gbrain proxy token (POST /brain/query). Cells authenticate to
77
77
  # THIS gateway with their mesh token; the gateway then presents ITS OWN
78
78
  # gbrain token upstream so cells never need a separate gbrain_ credential.
79
- GATEWAY_GBRAIN_URL = os.environ.get("GATEWAY_GBRAIN_URL", "http://100.107.222.72:8792/mcp")
79
+ # No default host. This shipped for 79 releases with a hardcoded PRIVATE TAILNET address as
80
+ # its fallback, so every public install pointed at one operator's box unless the deployer
81
+ # happened to set the variable. Unroutable from outside, so never an access hole — but it
82
+ # disclosed topology, and a default that silently targets someone else's host is wrong even
83
+ # when it cannot be reached. Unset now means UNCONFIGURED, which is the honest state.
84
+ GATEWAY_GBRAIN_URL = os.environ.get("GATEWAY_GBRAIN_URL", "")
80
85
  GATEWAY_GBRAIN_TOKEN = os.environ.get("GATEWAY_GBRAIN_TOKEN", "")
81
86
 
82
87
  # B1 Meta-Edge identity (META_EDGE_IDENTITY_CONTRACT.md). Meta-Edge SSO ISSUES
@@ -4219,6 +4224,15 @@ def _brain_query_upstream(question: str, limit: int) -> list:
4219
4224
  """Proxy a READ-ONLY gbrain query. Builds the MCP `query` call itself (never
4220
4225
  write/admin), POSTs to GATEWAY_GBRAIN_URL with the gateway-held token, returns
4221
4226
  the chunk array. Raises on any upstream/parse failure (caller maps to 502)."""
4227
+ if not GATEWAY_GBRAIN_URL:
4228
+ # Named, not incidental. Removing the hardcoded default turned "unconfigured"
4229
+ # into a reachable state for the first time, and an empty URL would otherwise
4230
+ # surface as a urllib ValueError — a transport error for a CONFIGURATION fault,
4231
+ # which sends the reader looking at the network instead of their env.
4232
+ raise RuntimeError(
4233
+ "GATEWAY_GBRAIN_URL is not set — the brain proxy is UNCONFIGURED, not "
4234
+ "unreachable. Set it to your gbrain MCP endpoint. (Releases up to 0.41.0 "
4235
+ "silently defaulted to one operator's private host; that default is gone.)")
4222
4236
  body = {"jsonrpc": "2.0", "id": 1, "method": "tools/call",
4223
4237
  "params": {"name": "query",
4224
4238
  "arguments": {"query": question, "limit": limit, "expand": False}}}
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.41.0
3
+ Version: 0.41.1
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`, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/BrainSurfing-tech/swarph-cli
8
8
  Project-URL: Source, https://github.com/BrainSurfing-tech/swarph-cli
9
9
  Project-URL: Substrate, https://github.com/BrainSurfing-tech/swarph-mesh
10
- Project-URL: Spec, https://github.com/darw007d/hedge-fund-mcp/blob/main/research/swarph_cli/PLAN.md
11
10
  Keywords: swarph,llm,cli,mesh,gemini,claude,deepseek
12
11
  Classifier: Development Status :: 3 - Alpha
13
12
  Classifier: Environment :: Console
@@ -467,13 +466,41 @@ swarph watchdog --check --peer researcher --gateway http://localhost:8788 --dm-w
467
466
  Installs Claude Code hooks as **content** wired into `~/.claude/settings.json` — a hook becomes an installable artifact (a script + its event/matcher bindings merged into your settings) with no swarph-cli version bump per hook, the same way `watchdog --install-service` ships systemd units as bundled data.
468
467
 
469
468
  ```bash
470
- swarph hooks init # install the recommended bundled set (cell-resilience)
471
- swarph hooks add cell-resilience # install one builtin by name
472
- swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
473
- swarph hooks list # builtins + install status (installed|available)
469
+ swarph hooks init # install the recommended bundled set
470
+ swarph hooks add codegraph-on-grep # structural search alongside grep (see below)
471
+ swarph hooks add cell-resilience # install one builtin by name
472
+ swarph hooks add ./my-hook # install a local bundle dir (hook.json + script)
473
+ swarph hooks list # builtins + install status (installed|available)
474
474
  swarph hooks remove cell-resilience
475
475
  ```
476
476
 
477
+ **Bundled `codegraph-on-grep`** *(0.41.0)*. Binds `PostToolUse`/`Bash`. When a `grep`/`rg` searches
478
+ **code**, it also queries the mesh gateway's structural codegraph and hands back the symbol-level
479
+ answer — definitions, `file:line`, and **caller counts, which grep structurally cannot see**:
480
+
481
+ ```
482
+ CODEGRAPH (structural, index 2.1h old) for 'def _board_grant' — grep found text; this is
483
+ the symbol graph, incl. CALLER COUNTS grep cannot see:
484
+ mesh-gateway/server.py:1352 function _board_grant callers=11
485
+ ```
486
+
487
+ The point is **context cost**: a grep returning 200 matching lines is far more expensive than the
488
+ handful of symbols that answer *"where is this defined and who calls it"*.
489
+
490
+ - **It supplements grep; it never blocks it.** grep stays correct for config, logs and string
491
+ literals — the codegraph indexes *symbols*.
492
+ - **An unavailable index is reported loudly, never as "no matches."** A failed lookup rendered as a
493
+ real negative teaches exactly the wrong thing, so the hook says so and marks the answer
494
+ unverified. A genuinely empty result from a *working* index says `a REAL negative` explicitly, and
495
+ a stale index reports its age.
496
+ - **No local index required.** It asks the gateway, which scopes both rows and caller counts to your
497
+ visibility — resolved from your **bearer token**, never from a field you send, so it cannot be
498
+ self-asserted.
499
+
500
+ Needs `SWARPH_SELF` (or `--as`), your peer token at `~/.config/swarph/<cell>.peer_token`, and a
501
+ reachable gateway. Do not run a hand-rolled copy alongside it: two hooks per `Bash` call means two
502
+ lookups, doubling the cost the hook exists to cut.
503
+
477
504
  **Trust model.** Three tiers: `builtin` (trusted, bundled with swarph-cli — installs without a prompt), `local` (a bundle dir you point at — shown then confirmed before any write), and `published`/`@cell/name` (**fails closed in v1** — never installs another cell's unreviewed code). Signed-publisher identity plus a publish-time security gate is the v2 model.
478
505
 
479
506
  **Bundled `cell-resilience`.** Binds `StopFailure`/`rate_limit` + `Stop`/`(all)` to a script that writes `$XDG_STATE_HOME/swarph/idle_since.json` (`{"session","reason","hook_event","ts"}`, `reason=throttle|normal`) — the push-side throttle detector the watchdog's `--dm-wake` can read instead of polling. Observational only: it never blocks the session and always exits 0 (jq if present, printf/sed fallback otherwise).
@@ -655,7 +682,7 @@ Dry-run is the default — nothing mutates without `--apply`. Cron-friendly exit
655
682
  | `8` | refused — not idempotent (second pass kept cutting; signal-eating alarm) |
656
683
 
657
684
  Design spec: `docs/superpowers/specs/2026-06-11-swarph-context-compressor-design.md`
658
- (in the hedge-fund-mcp repo).
685
+ (in a private design repo; not published).
659
686
 
660
687
  ### `--json` mode semantics
661
688
 
@@ -17,7 +17,7 @@ from swarph_cli.commands import brain_ask as ba
17
17
 
18
18
  def test_version_is_0_33_0():
19
19
  import swarph_cli
20
- assert swarph_cli.__version__ == "0.41.0"
20
+ assert swarph_cli.__version__ == "0.41.1"
21
21
 
22
22
 
23
23
  # --- request / response plumbing -------------------------------------------
@@ -143,3 +143,51 @@ def test_the_hook_is_a_registered_builtin_bundle():
143
143
  def test_the_verb_is_registered():
144
144
  from swarph_cli.main import _VERB_HANDLERS # noqa: PLC0415
145
145
  assert "codegraph-hook" in _VERB_HANDLERS
146
+
147
+
148
+ # ── the non-empty answer must be as honest as the empty one ───────────────
149
+
150
+ def test_a_FUZZY_match_is_labelled_not_served_as_an_answer():
151
+ """>>> a peer's first-hand receipt, 2026-08-01: the defect this closes. <<<
152
+
153
+ He grepped a private-repo file for `def command_beta_executor` and got six
154
+ swarph-cli symbols WITH CALLER COUNTS — the sanitiser OR-joins tokens, so it
155
+ matched "command". His diagnosis: "the failure mode you engineered out of the
156
+ empty case walked back in through the non-empty one." An empty answer said
157
+ "REAL negative"; a wrong answer said nothing at all.
158
+ """
159
+ env = {"results": [
160
+ {"repo": "swarph-cli", "file_path": "src/x.py", "start_line": 46,
161
+ "kind": "variable", "name": "_HOOK_COMMAND", "callers": 0},
162
+ {"repo": "swarph-cli", "file_path": "src/y.py", "start_line": 62,
163
+ "kind": "function", "name": "provider_command", "callers": 1},
164
+ ], "freshness": [{"index_age_hours": 1.6}]}
165
+ out = ch.render("def command_beta_executor", env)
166
+ assert "FUZZY MATCH" in out
167
+ assert "NOT AN ANSWER TO YOUR QUERY" in out
168
+ assert "command_beta_executor" in out
169
+ assert "swarph-cli" in out # names the repo the strays came from
170
+
171
+
172
+ def test_a_GENUINE_match_carries_no_fuzzy_warning():
173
+ """Non-vacuity pair. If the warning fired on real hits it would be noise, and
174
+ a warning that always fires is one nobody reads."""
175
+ env = {"results": [
176
+ {"repo": "service-repo", "file_path": "server.py", "start_line": 1352,
177
+ "kind": "function", "name": "_board_grant", "callers": 11},
178
+ ], "freshness": [{"index_age_hours": 2.0}]}
179
+ out = ch.render("_board_grant", env)
180
+ assert "FUZZY MATCH" not in out
181
+ assert "_board_grant" in out
182
+
183
+
184
+ def test_a_PARTIAL_match_is_treated_as_genuine():
185
+ """One real hit among strays still answers the question — warn only when NOTHING
186
+ matches, or the signal degrades into noise."""
187
+ env = {"results": [
188
+ {"repo": "swarph-cli", "file_path": "a.py", "start_line": 1,
189
+ "kind": "variable", "name": "_HOOK_COMMAND", "callers": 0},
190
+ {"repo": "private-repo", "file_path": "workers/worker_beta.py",
191
+ "start_line": 900, "kind": "function", "name": "command_beta_executor", "callers": 3},
192
+ ], "freshness": [{"index_age_hours": 1.0}]}
193
+ assert "FUZZY MATCH" not in ch.render("def command_beta_executor", env)
@@ -102,3 +102,36 @@ def test_upstream_helper_is_read_only_by_construction(monkeypatch):
102
102
  assert out == [{"slug": "s"}]
103
103
  assert captured["body"]["params"]["name"] == "query" # read-only lock
104
104
  assert captured["auth"] == "Bearer gbrain_hostheld" # gateway's held token
105
+
106
+
107
+ def test_an_UNCONFIGURED_gbrain_url_names_itself_as_configuration_not_transport(monkeypatch):
108
+ """Releases up to 0.41.0 shipped a hardcoded PRIVATE TAILNET address as the
109
+ default for GATEWAY_GBRAIN_URL, so "unconfigured" was never a reachable state —
110
+ every install silently pointed at one operator's box.
111
+
112
+ Removing the default makes it reachable for the first time. An empty URL would
113
+ otherwise surface as a urllib ValueError: a TRANSPORT error for a CONFIGURATION
114
+ fault, which sends the reader to the network instead of their env.
115
+
116
+ Non-vacuity: this is the only test that exercises the empty branch. Every other
117
+ test in this file sets the variable in `_load_app`, which is exactly why the
118
+ hardcoded default survived 79 releases without a single test noticing it.
119
+ """
120
+ server = _load_app(monkeypatch)
121
+ monkeypatch.setattr(server, "GATEWAY_GBRAIN_URL", "")
122
+ with pytest.raises(RuntimeError) as e:
123
+ server._brain_query_upstream("anything", 5)
124
+ msg = str(e.value)
125
+ assert "UNCONFIGURED" in msg
126
+ assert "GATEWAY_GBRAIN_URL" in msg
127
+ assert "not" in msg and "unreachable" in msg # names which fault it is NOT
128
+
129
+
130
+ def test_a_CONFIGURED_url_does_not_raise_the_configuration_error(monkeypatch):
131
+ """Paired non-vacuity. If the guard fired on a configured gateway it would be a
132
+ regression, not a safety net — so the silent case is asserted too."""
133
+ server = _load_app(monkeypatch)
134
+ monkeypatch.setattr(server, "GATEWAY_GBRAIN_URL", "http://127.0.0.1:8792/mcp")
135
+ with pytest.raises(Exception) as e: # noqa: PT011 — any transport failure is fine
136
+ server._brain_query_upstream("anything", 5)
137
+ assert "UNCONFIGURED" not in str(e.value)
@@ -1043,4 +1043,4 @@ def test_parser_defaults_process_name_claude_liveness_cmd_none():
1043
1043
 
1044
1044
  def test_version_is_0_33_0():
1045
1045
  import swarph_cli
1046
- assert swarph_cli.__version__ == "0.41.0"
1046
+ assert swarph_cli.__version__ == "0.41.1"
File without changes
File without changes