swarph-cli 0.61.0__tar.gz → 0.63.0__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.
- {swarph_cli-0.61.0/src/swarph_cli.egg-info → swarph_cli-0.63.0}/PKG-INFO +2 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/README.md +1 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/pyproject.toml +2 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/cell.py +1 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/board.py +59 -12
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codegraph.py +63 -8
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/guide.py +55 -2
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/init.py +1 -0
- swarph_cli-0.63.0/src/swarph_cli/commands/install_opencode_plugin.py +173 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/mcp_server.py +9 -2
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory_emit_hook.py +29 -7
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/mesh.py +24 -7
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/monitor.py +4 -2
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/onboard.py +8 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/spawn.py +344 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/wake_hook_output.py +1 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/watchdog.py +33 -7
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/enrich.py +28 -6
- swarph_cli-0.63.0/src/swarph_cli/dreaming/slm.py +120 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/guide/GUIDE.md +151 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/main.py +19 -0
- swarph_cli-0.63.0/src/swarph_cli/payloads/opencode/plugin.js +82 -0
- swarph_cli-0.63.0/src/swarph_cli/source_text.py +129 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0/src/swarph_cli.egg-info}/PKG-INFO +2 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/SOURCES.txt +14 -0
- swarph_cli-0.63.0/tests/test_126_pending_from_complete.py +116 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_397_gh_identity_router.py +4 -1
- swarph_cli-0.63.0/tests/test_547_verbs_from_registry.py +59 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_552_codegraph_gateway.py +15 -1
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_556_relay_freshness.py +15 -1
- swarph_cli-0.63.0/tests/test_656_owned_slm.py +184 -0
- swarph_cli-0.63.0/tests/test_866_doctrine_guide.py +71 -0
- swarph_cli-0.63.0/tests/test_872_identity_refuses_not_warns.py +129 -0
- swarph_cli-0.63.0/tests/test_874_source_text.py +116 -0
- swarph_cli-0.63.0/tests/test_888_opencode_data_dir_can_fail.py +71 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_board_card_thread.py +58 -2
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_codegraph_mcp.py +15 -1
- swarph_cli-0.63.0/tests/test_memory_emit_snippet.py +43 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_monitor_state_split.py +2 -1
- swarph_cli-0.63.0/tests/test_opencode_membrane.py +540 -0
- swarph_cli-0.63.0/tests/test_opencode_plugin.py +144 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/LICENSE +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/setup.cfg +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_board_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_group_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_import_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_init_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_main.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_security.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_service_app.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_service_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_version_command.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.61.0 → swarph_cli-0.63.0}/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.
|
|
3
|
+
Version: 0.63.0
|
|
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
|
|
@@ -148,6 +148,7 @@ swarph guide # the whole guide
|
|
|
148
148
|
swarph guide channels # one topic
|
|
149
149
|
swarph guide --list # every topic, with the commands it teaches
|
|
150
150
|
swarph guide --search subscribe # find a topic by INTENT, not by its name
|
|
151
|
+
swarph guide doctrine # the standard of evidence a cell is held to (v0.63.0)
|
|
151
152
|
```
|
|
152
153
|
|
|
153
154
|
Three ways in, because a new cell does not know which word it has:
|
|
@@ -100,6 +100,7 @@ swarph guide # the whole guide
|
|
|
100
100
|
swarph guide channels # one topic
|
|
101
101
|
swarph guide --list # every topic, with the commands it teaches
|
|
102
102
|
swarph guide --search subscribe # find a topic by INTENT, not by its name
|
|
103
|
+
swarph guide doctrine # the standard of evidence a cell is held to (v0.63.0)
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
Three ways in, because a new cell does not know which word it has:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.63.0"
|
|
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" }
|
|
@@ -146,6 +146,7 @@ swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
|
|
|
146
146
|
"systemd/*.path",
|
|
147
147
|
"scripts/*.sh", "scripts/*.ps1", "guide/*.md",
|
|
148
148
|
"payloads/postcompact/*.sh", "payloads/postcompact/*.cmd",
|
|
149
|
+
"payloads/opencode/*.js",
|
|
149
150
|
"skills/*/SKILL.md", "skills/*/LICENSE"]
|
|
150
151
|
# v0.15: ship the bundled mesh-gateway schema so `swarph gateway serve`
|
|
151
152
|
# can apply it at startup via MESH_SCHEMA_PATH (defaults next to server.py).
|
|
@@ -66,7 +66,7 @@ _validate_uuid = validate_uuid_str # historical helper name
|
|
|
66
66
|
# ONE swarph-cli release, and entry into swarph_shared.VALID_PROVIDERS only in a
|
|
67
67
|
# later one. spawn.py's guard is `VALID_PROVIDERS ⊆ MEMBRANES`, so this direction
|
|
68
68
|
# is inert while the reverse raises at import for every fresh install (#247).
|
|
69
|
-
CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor"}
|
|
69
|
+
CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor", "opencode"}
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def _config_root() -> Path:
|
|
@@ -390,8 +390,38 @@ def _thread_url(gateway: str, card_id: int, *, limit: Optional[int] = None) -> s
|
|
|
390
390
|
return f"{base}?limit={limit}" if limit else base
|
|
391
391
|
|
|
392
392
|
|
|
393
|
-
def
|
|
394
|
-
"""
|
|
393
|
+
def _project_owner_orchestrator(gw: str, token: str, project_id) -> Optional[str]:
|
|
394
|
+
"""Resolve ``projects.owner_orchestrator`` for a card's project_id.
|
|
395
|
+
|
|
396
|
+
>>> THE CARD PAYLOAD DOES NOT CARRY THE OWNER (#864). <<< Measured
|
|
397
|
+
2026-09-17: GET /board/cards/{id} has assignee/project_id and no owner
|
|
398
|
+
field; GET /board/projects rows carry ``owner_orchestrator`` (NOT NULL on
|
|
399
|
+
the server). A second call is required — inventing a sentinel peer is the
|
|
400
|
+
#259 failure mode this helper exists to avoid.
|
|
401
|
+
"""
|
|
402
|
+
if project_id is None:
|
|
403
|
+
return None
|
|
404
|
+
st, data = _http_get_json(f"{gw.rstrip('/')}/board/projects", token)
|
|
405
|
+
if st != 200:
|
|
406
|
+
return None
|
|
407
|
+
rows = data if isinstance(data, list) else (data or {}).get("projects", [])
|
|
408
|
+
for row in rows or []:
|
|
409
|
+
if row.get("id") == project_id or str(row.get("id")) == str(project_id):
|
|
410
|
+
owner = (row.get("owner_orchestrator") or "").strip()
|
|
411
|
+
return owner or None
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _thread_recipient(
|
|
416
|
+
card: dict,
|
|
417
|
+
explicit_to: Optional[str],
|
|
418
|
+
*,
|
|
419
|
+
owner_orchestrator: Optional[str] = None,
|
|
420
|
+
) -> "tuple[str, str]":
|
|
421
|
+
"""Who a card-thread post is addressed to, and why.
|
|
422
|
+
|
|
423
|
+
Order: ``--to`` > card assignee > project ``owner_orchestrator`` (#864).
|
|
424
|
+
Returns ``(to_node, reason)`` so the confirmation line can name the choice.
|
|
395
425
|
|
|
396
426
|
>>> RAISES RATHER THAN INVENTING A SENTINEL. <<< POST /messages requires
|
|
397
427
|
exactly one of {to_node, channel}, so a card post needs a real recipient. The
|
|
@@ -401,18 +431,21 @@ def _thread_recipient(card: dict, explicit_to: Optional[str]) -> str:
|
|
|
401
431
|
A placeholder here would manufacture that defect once per card post.
|
|
402
432
|
"""
|
|
403
433
|
if explicit_to:
|
|
404
|
-
return explicit_to
|
|
434
|
+
return explicit_to, "--to"
|
|
405
435
|
assignee = card.get("assignee")
|
|
406
436
|
if assignee:
|
|
407
|
-
return assignee
|
|
437
|
+
return assignee, "card assignee"
|
|
438
|
+
if owner_orchestrator:
|
|
439
|
+
return owner_orchestrator, "project owner_orchestrator"
|
|
408
440
|
raise RuntimeError(
|
|
409
|
-
f"card #{card.get('id')} has no assignee
|
|
410
|
-
f"
|
|
411
|
-
f"wire; it needs
|
|
441
|
+
f"card #{card.get('id')} has no assignee and project owner_orchestrator "
|
|
442
|
+
f"is unresolvable, so there is no default recipient for a thread post — "
|
|
443
|
+
f"pass --to <peer>. (A card post is still a DM on the wire; it needs "
|
|
444
|
+
f"someone to be addressed to.)"
|
|
412
445
|
)
|
|
413
446
|
|
|
414
447
|
|
|
415
|
-
def _say_line(resp: dict, card_id, to_node: str) -> str:
|
|
448
|
+
def _say_line(resp: dict, card_id, to_node: str, *, reason: Optional[str] = None) -> str:
|
|
416
449
|
"""The post confirmation — AND any obligation the post just DISCHARGED.
|
|
417
450
|
|
|
418
451
|
>>> THE GATEWAY ALREADY SAID SO AND THIS CLI THREW IT AWAY. <<< POST
|
|
@@ -435,7 +468,11 @@ def _say_line(resp: dict, card_id, to_node: str) -> str:
|
|
|
435
468
|
POLICY (that is #562's own question) -- it is the fix for the holder not
|
|
436
469
|
being told, which is what made six hours of divergence possible.
|
|
437
470
|
"""
|
|
438
|
-
|
|
471
|
+
if reason:
|
|
472
|
+
line = (f"posted id={resp.get('id')} onto card #{card_id} "
|
|
473
|
+
f"(to {to_node}; {reason})")
|
|
474
|
+
else:
|
|
475
|
+
line = f"posted id={resp.get('id')} onto card #{card_id} (to {to_node})"
|
|
439
476
|
closed = resp.get("closed_obligations") or []
|
|
440
477
|
if closed:
|
|
441
478
|
ids = ", ".join(f"#{i}" for i in closed)
|
|
@@ -855,7 +892,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
855
892
|
add_content_args(cy)
|
|
856
893
|
cy.add_argument("--kind", default="fyi", help="status|question|answer|unblock|fyi")
|
|
857
894
|
cy.add_argument("--to", dest="to_node", default=None,
|
|
858
|
-
help="recipient peer (default:
|
|
895
|
+
help="recipient peer (default: card assignee, else project "
|
|
896
|
+
"owner_orchestrator; refused if neither resolves)")
|
|
859
897
|
_add_common(cy)
|
|
860
898
|
|
|
861
899
|
ck = cards.add_parser(
|
|
@@ -1278,8 +1316,15 @@ def run_board(argv: list[str]) -> int:
|
|
|
1278
1316
|
f"(it predates #181a). Run scripts/migrate_card_threads.py on "
|
|
1279
1317
|
f"the gateway host.", file=sys.stderr)
|
|
1280
1318
|
return 1
|
|
1319
|
+
# #864: card GET has no owner field — only fetch projects when the
|
|
1320
|
+
# cheaper defaults (explicit --to, card assignee) are absent.
|
|
1321
|
+
owner = None
|
|
1322
|
+
if not args.to_node and not card.get("assignee"):
|
|
1323
|
+
owner = _project_owner_orchestrator(gw, token, card.get("project_id"))
|
|
1281
1324
|
try:
|
|
1282
|
-
to_node = _thread_recipient(
|
|
1325
|
+
to_node, reason = _thread_recipient(
|
|
1326
|
+
card, args.to_node, owner_orchestrator=owner,
|
|
1327
|
+
)
|
|
1283
1328
|
except RuntimeError as exc:
|
|
1284
1329
|
print(f"swarph board cards say: {exc}", file=sys.stderr)
|
|
1285
1330
|
return 1
|
|
@@ -1293,7 +1338,9 @@ def run_board(argv: list[str]) -> int:
|
|
|
1293
1338
|
# explicit `propose` grant. `_out` passes `detail` through whole; it does
|
|
1294
1339
|
# not need help, and a second copy of that logic is a second thing to
|
|
1295
1340
|
# keep in sync.
|
|
1296
|
-
return _out(
|
|
1341
|
+
return _out(
|
|
1342
|
+
st, d, lambda x: _say_line(x, args.id, to_node, reason=reason), aj,
|
|
1343
|
+
)
|
|
1297
1344
|
if args.command == "ready":
|
|
1298
1345
|
st, d = _patch_json(f"{gw}/board/cards/{args.id}", {"actor": self_name, "move_ready": not args.clear}, token)
|
|
1299
1346
|
return _out(st, d, lambda x: f"card #{x.get('id')} move_ready -> {x.get('move_ready')}", aj)
|
|
@@ -37,12 +37,65 @@ import sys
|
|
|
37
37
|
from swarph_cli.commands.codegraph_hook import _match_quality
|
|
38
38
|
|
|
39
39
|
DEFAULT_INDEX = os.path.expanduser("~/.swarph/codegraph/index.db")
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
|
|
40
|
+
# >>> THERE IS NO DEFAULT IDENTITY ANY MORE. THIS CONSTANT IS GONE ON PURPOSE. <<<
|
|
41
|
+
#
|
|
42
|
+
# It used to read `DEFAULT_CALLER_CELL = "lab-ovh"`, and that one line meant every
|
|
43
|
+
# unconfigured caller on lab-ovh queried with the ORCHESTRATOR'S READ AUTHORITY.
|
|
44
|
+
#
|
|
45
|
+
# MEASURED 2026-09-18 (science-claude, mesh 43183/43186/43188). The gateway was never
|
|
46
|
+
# bypassed and nothing was ever spoofed: `_query_via_gateway` sends NO caller field, so
|
|
47
|
+
# the server binds identity from the bearer token, correctly. What the default did was
|
|
48
|
+
# pick WHICH REAL TOKEN got presented — `_resolve_token(self_name, ...)` reads the file
|
|
49
|
+
# that name points at. A cell asking a question on this box authenticated AS lab-ovh,
|
|
50
|
+
# with lab-ovh's genuine credential, and got a full, plausible, correctly-authorised
|
|
51
|
+
# answer belonging to somebody else.
|
|
52
|
+
#
|
|
53
|
+
# >>> A WRONG-IDENTITY WRITE ANNOUNCES ITSELF — a DM arrives signed by the wrong cell
|
|
54
|
+
# and someone notices. A WRONG-IDENTITY READ ANNOUNCES NOTHING. <<< It was found only
|
|
55
|
+
# because a repo its owner KNEW they owned was missing from their own results.
|
|
56
|
+
#
|
|
57
|
+
# WHY REFUSE RATHER THAN WARN (commander, 2026-09-18 04:44Z): a warning on a read that
|
|
58
|
+
# returns a correct-looking answer is a line of text above a result the caller already
|
|
59
|
+
# believes. And the cost of refusing was measured first, not assumed — of 8 executable
|
|
60
|
+
# `swarph` invocations in systemd + cron on lab-ovh, 7 already pass `--as` or
|
|
61
|
+
# `--token-file`, and the 8th (`monitor reexec-on-change`) cannot authenticate at all:
|
|
62
|
+
# no --gateway, no --token-file, no --as exists on that verb. SCHEDULED WORK BREAKS IN
|
|
63
|
+
# ZERO PLACES. The population that pays is interactive use, which is the population
|
|
64
|
+
# that was silently mis-attributed.
|
|
65
|
+
#
|
|
66
|
+
# $SWARPH_SELF STILL WORKS AND IS THE INTENDED PATH. It is a DECLARED identity. What is
|
|
67
|
+
# refused is the *undeclared* case, where a package-level guess stood in for one.
|
|
68
|
+
IDENTITY_UNSET = object() # sentinel: argparse default when --caller-cell is omitted
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class IdentityNotDeclared(RuntimeError):
|
|
72
|
+
"""No caller identity was declared and there is no default to fall back to."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def require_caller_cell(explicit=None, *, verb: str) -> str:
|
|
76
|
+
"""Resolve the caller cell, or REFUSE.
|
|
77
|
+
|
|
78
|
+
Order: explicit flag -> $SWARPH_SELF -> $SWARPH_CELL -> refuse.
|
|
79
|
+
|
|
80
|
+
The refusal names the verb and every way to satisfy it, because an error that
|
|
81
|
+
says only "identity unset" moves the work to the reader.
|
|
82
|
+
"""
|
|
83
|
+
if explicit not in (None, IDENTITY_UNSET) and str(explicit).strip():
|
|
84
|
+
return str(explicit).strip()
|
|
85
|
+
for var in ("SWARPH_SELF", "SWARPH_CELL"):
|
|
86
|
+
v = (os.environ.get(var) or "").strip()
|
|
87
|
+
if v:
|
|
88
|
+
return v
|
|
89
|
+
raise IdentityNotDeclared(
|
|
90
|
+
f"swarph {verb}: NO CALLER IDENTITY DECLARED, and there is no default.\n"
|
|
91
|
+
f" This used to fall back to 'lab-ovh', which meant an undeclared caller\n"
|
|
92
|
+
f" queried with that cell's read authority and got somebody else's answer.\n"
|
|
93
|
+
f" Declare one of:\n"
|
|
94
|
+
f" --caller-cell <cell> for this invocation\n"
|
|
95
|
+
f" SWARPH_SELF=<cell> for this shell or unit\n"
|
|
96
|
+
f" SWARPH_CELL=<cell> (MCP hosts)\n"
|
|
97
|
+
f" It is NOT guessable: this box hosts several cells under one uid."
|
|
98
|
+
)
|
|
46
99
|
|
|
47
100
|
# NOT 1 and NOT 2. A distinct code so "nothing was searched" can never be confused
|
|
48
101
|
# with a crash or a usage error, and so a caller can branch on it specifically.
|
|
@@ -332,7 +385,7 @@ def _query_via_gateway(gateway: str, query: str, limit: int, token_file):
|
|
|
332
385
|
from swarph_cli.commands.mesh import _post_json, _resolve_token # local import: keeps
|
|
333
386
|
# the local-only path free of any mesh dependency, so an offline cell still works.
|
|
334
387
|
|
|
335
|
-
self_name =
|
|
388
|
+
self_name = require_caller_cell(verb="codegraph")
|
|
336
389
|
token = _resolve_token(self_name, token_file)
|
|
337
390
|
body = {"query": query, "limit": max(1, min(int(limit), 25))}
|
|
338
391
|
status, payload = _post_json(gateway.rstrip("/") + "/codegraph", body, token)
|
|
@@ -352,7 +405,9 @@ def run_codegraph(argv) -> int:
|
|
|
352
405
|
"is configured (the server searches and returns the answer); falls back to a local index.")
|
|
353
406
|
ap.add_argument("query", help="natural-language query, e.g. 'which function escapes HTML'")
|
|
354
407
|
ap.add_argument("--index", default=DEFAULT_INDEX, help=f"index db (default {DEFAULT_INDEX})")
|
|
355
|
-
ap.add_argument("--caller-cell", default=
|
|
408
|
+
ap.add_argument("--caller-cell", default=IDENTITY_UNSET,
|
|
409
|
+
help="A8 caller identity. NO DEFAULT — else $SWARPH_SELF / $SWARPH_CELL, "
|
|
410
|
+
"else the command REFUSES rather than guessing (it used to guess lab-ovh).")
|
|
356
411
|
ap.add_argument("--limit", type=int, default=8)
|
|
357
412
|
ap.add_argument("--json", action="store_true", help="machine-readable JSON output")
|
|
358
413
|
ap.add_argument("--gateway", default=None,
|
|
@@ -130,6 +130,38 @@ def _commands_in(section: str) -> "list[str]":
|
|
|
130
130
|
return out
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
def verbs_in_guide(topics: "dict[str, str] | None" = None) -> "set[str]":
|
|
134
|
+
"""#547: the verbs the guide TEACHES, counted PER VERB (a topic count said 11 and
|
|
135
|
+
meant nothing). A verb is covered when some section's commands include it."""
|
|
136
|
+
topics = topics if topics is not None else _split_topics(_load_guide())
|
|
137
|
+
out: set[str] = set()
|
|
138
|
+
for body in topics.values():
|
|
139
|
+
for cmd in _commands_in(body):
|
|
140
|
+
parts = cmd.split()
|
|
141
|
+
if len(parts) >= 2:
|
|
142
|
+
out.add(parts[1])
|
|
143
|
+
return out
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _registry() -> "list[str]":
|
|
147
|
+
# Lazy: main dispatches handlers by dotted path and does not import this module,
|
|
148
|
+
# but keep the direction one-way anyway.
|
|
149
|
+
from swarph_cli.main import registered_verbs
|
|
150
|
+
return registered_verbs()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _verb_entry(topics: "dict[str, str]", verb: str) -> "list[tuple[str, str]]":
|
|
154
|
+
"""The line in each topic that teaches `swarph <verb>` -- one per topic, first hit."""
|
|
155
|
+
pat = re.compile(r"`swarph " + re.escape(verb) + r"(?:[ `])")
|
|
156
|
+
hits: list[tuple[str, str]] = []
|
|
157
|
+
for name, body in topics.items():
|
|
158
|
+
for raw in body.splitlines():
|
|
159
|
+
if pat.search(raw):
|
|
160
|
+
hits.append((name, raw.strip()))
|
|
161
|
+
break
|
|
162
|
+
return hits
|
|
163
|
+
|
|
164
|
+
|
|
133
165
|
_TERM_RE = re.compile(r"\*\*(.+?)\*\*")
|
|
134
166
|
|
|
135
167
|
|
|
@@ -262,13 +294,21 @@ def run_guide(argv: "list[str]") -> int:
|
|
|
262
294
|
# `apropos`: an LLM arrives with an INTENT ("subscribe to updates"), not the
|
|
263
295
|
# topic's name ("channels"). Requiring the name is the #520 discovery defect
|
|
264
296
|
# reproduced inside its own fix.
|
|
297
|
+
term = args.search.strip().lower()
|
|
298
|
+
# #547: a VERB name resolves to the verb's own entry first -- `--search highlight`
|
|
299
|
+
# used to answer with a topic list that did not contain it.
|
|
300
|
+
verb_hits = _verb_entry(topics, term) if term in _registry() else []
|
|
265
301
|
hits = _search(topics, args.search)
|
|
266
|
-
if not hits:
|
|
302
|
+
if not hits and not verb_hits:
|
|
267
303
|
print_safe(f"swarph guide: nothing matches {args.search!r}. "
|
|
268
304
|
f"Topics: {', '.join(topics)}", stream=sys.stderr)
|
|
269
305
|
return 2
|
|
270
|
-
for name, line in
|
|
306
|
+
for name, line in verb_hits:
|
|
271
307
|
print_safe(f"{name:<22} {line}")
|
|
308
|
+
shown = {name for name, _ in verb_hits}
|
|
309
|
+
for name, line in hits:
|
|
310
|
+
if name not in shown:
|
|
311
|
+
print_safe(f"{name:<22} {line}")
|
|
272
312
|
return 0
|
|
273
313
|
|
|
274
314
|
if not args.topic:
|
|
@@ -279,6 +319,19 @@ def run_guide(argv: "list[str]") -> int:
|
|
|
279
319
|
if key in topics:
|
|
280
320
|
print_safe(topics[key])
|
|
281
321
|
return 0
|
|
322
|
+
if key in _registry():
|
|
323
|
+
# #547: every registered verb resolves, per verb (tests/test_547 pins the set):
|
|
324
|
+
# the lines that teach it, then -- when the name is also a topic's substring
|
|
325
|
+
# (`channel` -> channels) -- that one topic, as before.
|
|
326
|
+
for name, line in _verb_entry(topics, key):
|
|
327
|
+
print_safe(f"{name:<22} {line}")
|
|
328
|
+
near = [k for k in topics if key in k]
|
|
329
|
+
if len(near) == 1:
|
|
330
|
+
print_safe("")
|
|
331
|
+
print_safe(topics[near[0]])
|
|
332
|
+
else:
|
|
333
|
+
print_safe(f"\n more: swarph {key} --help")
|
|
334
|
+
return 0
|
|
282
335
|
|
|
283
336
|
# Substring fallback before failing: `swarph guide channel` should find `channels`.
|
|
284
337
|
hits = [k for k in topics if key in k]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""``swarph install-opencode-plugin`` — the OpenCode plugin that wires swarph hooks.
|
|
2
|
+
|
|
3
|
+
Part 2 of the opencode membrane. opencode has NO command-hook surface — its only
|
|
4
|
+
hook surface is a JavaScript plugin loaded from the config dir's ``plugins/``
|
|
5
|
+
tree (auto-loaded, no ``opencode.json`` ``plugin`` entry required; measured:
|
|
6
|
+
``~/.config/opencode/plugin/opensling.js`` loads against a config containing only
|
|
7
|
+
``$schema``). This verb renders the package payload (baking the pinned Python
|
|
8
|
+
interpreter, the same reason ``install_codex_hooks`` pins it: PATH presence is
|
|
9
|
+
not a liveness proof) and writes it as ``swarph-opencode.js``:
|
|
10
|
+
|
|
11
|
+
* ``--scope user`` → ``~/.config/opencode/plugins/swarph-opencode.js`` —
|
|
12
|
+
the OPERATOR's bare ``opencode`` sessions (outside a swarph cell).
|
|
13
|
+
* ``--scope project`` → ``<cwd>/.opencode/plugins/swarph-opencode.js`` — a
|
|
14
|
+
per-project wiring.
|
|
15
|
+
|
|
16
|
+
The swarph CELL does NOT read either of those locations: its membrane relocates
|
|
17
|
+
``XDG_CONFIG_HOME`` to ``<cwd>/.opencode-cell/config``, so its plugin lives at
|
|
18
|
+
``<cwd>/.opencode-cell/config/opencode/plugins/swarph-opencode.js`` and is
|
|
19
|
+
written by the membrane's ``_opencode_env`` via ``ensure_cell_plugin`` — a
|
|
20
|
+
spawned cell is wired without a separate onboarding step.
|
|
21
|
+
|
|
22
|
+
Identity (SWARPH_SELF / SWARPH_MEMORY_DIR) is read by the plugin from env AT
|
|
23
|
+
RUNTIME, not baked — the membrane already stamps SWARPH_SELF into the cell env,
|
|
24
|
+
and a bare-session install inherits the operator's env at hook time.
|
|
25
|
+
|
|
26
|
+
Idempotent, ``--uninstall``, ``--dry-run``, write-landed assert — the same
|
|
27
|
+
operator contract as every other hook installer (#527 task 3).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import json
|
|
34
|
+
import sys
|
|
35
|
+
from importlib import resources
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
from swarph_cli.cell import _atomic_write_text
|
|
39
|
+
|
|
40
|
+
_PAYLOAD = "swarph_cli.payloads.opencode"
|
|
41
|
+
_PLUGIN_NAME = "swarph-opencode.js"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _payload_text() -> str:
|
|
45
|
+
"""Read the plugin FROM THE PACKAGE — never a source-tree path, so tests
|
|
46
|
+
exercise the same importlib.resources lookup an installed wheel depends on
|
|
47
|
+
(the 0.39.3 undeclared-package-data lesson)."""
|
|
48
|
+
return resources.files(_PAYLOAD).joinpath("plugin.js").read_text(encoding="utf-8")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _render_plugin(interpreter: str) -> str:
|
|
52
|
+
r"""Render the plugin with @PYTHON@ replaced by ``interpreter`` AS A JS STRING.
|
|
53
|
+
|
|
54
|
+
>>> shlex.quote is WRONG here, and it was the shipped defect (#423 review).
|
|
55
|
+
The placeholder sits on the RHS of ``const PY = @PYTHON@;`` — a JavaScript
|
|
56
|
+
string literal position, NOT shell text. ``shlex.quote`` wraps in single
|
|
57
|
+
quotes and leaves backslashes raw, so on Windows
|
|
58
|
+
``C:\Program Files\Python\python.exe`` became
|
|
59
|
+
``const PY = "'C:\Program Files\Python\python.exe'";`` — invalid escapes, and
|
|
60
|
+
the single quotes land INSIDE the double quotes, so the value is a wrong path
|
|
61
|
+
even where it parses. json.dumps produces a valid JS string literal (double
|
|
62
|
+
quoted, backslashes escaped as ``\\``), which a JS parser accepts and decodes
|
|
63
|
+
back to the exact path. The template leaves the quotes to the substitution
|
|
64
|
+
(``const PY = @PYTHON@;``) so they are never doubled. <<<
|
|
65
|
+
"""
|
|
66
|
+
return _payload_text().replace("@PYTHON@", json.dumps(interpreter))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _render() -> str:
|
|
70
|
+
"""The rendered plugin, with @PYTHON@ pinned to THIS interpreter."""
|
|
71
|
+
return _render_plugin(str(Path(sys.executable).resolve()))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _target(scope: str) -> Path:
|
|
75
|
+
if scope == "project":
|
|
76
|
+
return Path.cwd() / ".opencode" / "plugins" / _PLUGIN_NAME
|
|
77
|
+
return Path.home() / ".config" / "opencode" / "plugins" / _PLUGIN_NAME
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def cell_plugin_path(cwd: Path) -> Path:
|
|
81
|
+
"""Where a cell's plugin lives inside its isolated config dir.
|
|
82
|
+
|
|
83
|
+
Mirrors ``_OPENCODE_CELL_SUBDIR`` + the XDG_CONFIG_HOME layout the membrane
|
|
84
|
+
sets in ``_opencode_env`` — kept here (not imported from spawn) so spawn can
|
|
85
|
+
call back into it without a cycle.
|
|
86
|
+
"""
|
|
87
|
+
return cwd / ".opencode-cell" / "config" / "opencode" / "plugins" / _PLUGIN_NAME
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ensure_cell_plugin(cwd: Path) -> None:
|
|
91
|
+
"""Write the cell's plugin if absent or changed. Idempotent + best-effort:
|
|
92
|
+
a failure must not block the spawn (worst case the cell runs unhooked)."""
|
|
93
|
+
try:
|
|
94
|
+
rendered = _render()
|
|
95
|
+
target = cell_plugin_path(cwd)
|
|
96
|
+
if target.exists() and target.read_text(encoding="utf-8") == rendered:
|
|
97
|
+
return
|
|
98
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
_atomic_write_text(target, rendered)
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _read_existing(target: Path) -> str:
|
|
105
|
+
return target.read_text(encoding="utf-8") if target.exists() else ""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def run_install_opencode_plugin(argv: list[str] | None = None) -> int:
|
|
109
|
+
if argv is None:
|
|
110
|
+
argv = sys.argv[2:]
|
|
111
|
+
|
|
112
|
+
p = argparse.ArgumentParser(prog="swarph install-opencode-plugin")
|
|
113
|
+
p.add_argument("--scope", choices=("user", "project"), default="user")
|
|
114
|
+
p.add_argument("--uninstall", action="store_true")
|
|
115
|
+
p.add_argument("--dry-run", action="store_true")
|
|
116
|
+
args = p.parse_args(argv)
|
|
117
|
+
|
|
118
|
+
target = _target(args.scope)
|
|
119
|
+
|
|
120
|
+
if args.dry_run:
|
|
121
|
+
print(f"# swarph install-opencode-plugin --dry-run", file=sys.stderr)
|
|
122
|
+
print(f"# scope: {args.scope}", file=sys.stderr)
|
|
123
|
+
print(f"# target: {target}", file=sys.stderr)
|
|
124
|
+
print(f"# action: {'uninstall' if args.uninstall else 'install'}", file=sys.stderr)
|
|
125
|
+
if not args.uninstall:
|
|
126
|
+
print(_render())
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
if args.uninstall:
|
|
130
|
+
existed = target.exists()
|
|
131
|
+
if existed:
|
|
132
|
+
target.unlink()
|
|
133
|
+
print(
|
|
134
|
+
f"swarph install-opencode-plugin: uninstalled {_PLUGIN_NAME} from "
|
|
135
|
+
f"{target} ({'removed' if existed else 'was absent'}).",
|
|
136
|
+
file=sys.stderr,
|
|
137
|
+
)
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
rendered = _render()
|
|
141
|
+
if _read_existing(target) == rendered:
|
|
142
|
+
print(
|
|
143
|
+
f"swarph install-opencode-plugin: no change needed at {target} "
|
|
144
|
+
"(plugin already in desired state).",
|
|
145
|
+
file=sys.stderr,
|
|
146
|
+
)
|
|
147
|
+
return 0
|
|
148
|
+
|
|
149
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
_atomic_write_text(target, rendered)
|
|
151
|
+
# The #527 task-3 assert: "installed" is a claim about the filesystem.
|
|
152
|
+
if target.read_text(encoding="utf-8") != rendered:
|
|
153
|
+
print(
|
|
154
|
+
f"swarph install-opencode-plugin: WRITE DID NOT LAND — {target} "
|
|
155
|
+
"re-reads as different content than was written. The install is NOT "
|
|
156
|
+
"in effect.",
|
|
157
|
+
file=sys.stderr,
|
|
158
|
+
)
|
|
159
|
+
return 2
|
|
160
|
+
print(
|
|
161
|
+
f"swarph install-opencode-plugin: installed {_PLUGIN_NAME} at {target}.",
|
|
162
|
+
file=sys.stderr,
|
|
163
|
+
)
|
|
164
|
+
print(
|
|
165
|
+
"NOTE: opencode loads the plugin table at session start. Already-open "
|
|
166
|
+
"sessions keep the pre-edit table — open a FRESH session before testing.",
|
|
167
|
+
file=sys.stderr,
|
|
168
|
+
)
|
|
169
|
+
return 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
raise SystemExit(run_install_opencode_plugin(sys.argv[1:]))
|
|
@@ -151,7 +151,8 @@ def _codegraph_query(query: str, *, limit: int = 8) -> list[dict]:
|
|
|
151
151
|
|
|
152
152
|
Delegates to :func:`codegraph.structural_query` against the module-level
|
|
153
153
|
``_CODEGRAPH_INDEX`` path, using ``SWARPH_CELL`` (else
|
|
154
|
-
``codegraph.
|
|
154
|
+
``codegraph.require_caller_cell``, which REFUSES rather than defaulting) as
|
|
155
|
+
the A8 caller identity and the
|
|
155
156
|
default operate-what-you-own allowlist (``allowlist=None``).
|
|
156
157
|
|
|
157
158
|
NEVER raises — any exception (missing index, corrupt db, bad query, or
|
|
@@ -162,7 +163,13 @@ def _codegraph_query(query: str, *, limit: int = 8) -> list[dict]:
|
|
|
162
163
|
to keep the tool output tight for the calling agent.
|
|
163
164
|
"""
|
|
164
165
|
try:
|
|
165
|
-
|
|
166
|
+
# NO DEFAULT IDENTITY (2026-09-18). `codegraph.DEFAULT_CALLER_CELL` is gone;
|
|
167
|
+
# an undeclared caller used to become lab-ovh and receive that cell's read
|
|
168
|
+
# authority. require_caller_cell raises IdentityNotDeclared, which the
|
|
169
|
+
# NEVER-RAISES contract below turns into an empty result — FAIL-CLOSED: the
|
|
170
|
+
# MCP host gets nothing rather than somebody else's answer.
|
|
171
|
+
caller_cell = codegraph.require_caller_cell(
|
|
172
|
+
os.environ.get("SWARPH_CELL"), verb="mcp codegraph_query")
|
|
166
173
|
rows = codegraph.structural_query(
|
|
167
174
|
query,
|
|
168
175
|
index_path=_CODEGRAPH_INDEX,
|
|
@@ -67,15 +67,37 @@ def _memory_slug(file_path: str) -> Optional[str]:
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
def _snippet(path: Path) -> str:
|
|
70
|
-
"""
|
|
71
|
-
empty snippet (the pointer still emits — the write happened
|
|
70
|
+
"""One-line summary of a memory: its frontmatter `description`, else the first body
|
|
71
|
+
line. Unreadable file → empty snippet (the pointer still emits — the write happened).
|
|
72
|
+
|
|
73
|
+
Every memory file opens with YAML frontmatter, so "first non-empty line" returned the
|
|
74
|
+
literal `---` delimiter. That is a NON-EMPTY STRING, so the caller's `if snippet` guard
|
|
75
|
+
passed and the timeline took `memory cached: ---`. MEASURED 2026-09-15: 21 such entries
|
|
76
|
+
on the shared timeline between 2026-08-25 and 2026-09-15, every one of them useless.
|
|
77
|
+
An emptiness guard cannot catch a delimiter; the fix has to be here, not at the guard.
|
|
78
|
+
"""
|
|
72
79
|
try:
|
|
73
|
-
|
|
74
|
-
s = line.strip().lstrip("#").strip()
|
|
75
|
-
if s:
|
|
76
|
-
return " ".join(s.split())[:_SNIPPET_CHARS]
|
|
80
|
+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
77
81
|
except Exception:
|
|
78
|
-
|
|
82
|
+
return ""
|
|
83
|
+
body = lines
|
|
84
|
+
if lines and lines[0].strip() == "---":
|
|
85
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
86
|
+
if line.strip() == "---":
|
|
87
|
+
# `description:` exists precisely to be a one-line summary — prefer it.
|
|
88
|
+
for fm in lines[1:i]:
|
|
89
|
+
if fm.strip().lower().startswith("description:"):
|
|
90
|
+
d = fm.split(":", 1)[1].strip().strip('"').strip("'")
|
|
91
|
+
if d:
|
|
92
|
+
return " ".join(d.split())[:_SNIPPET_CHARS]
|
|
93
|
+
body = lines[i + 1:]
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
body = lines[1:] # unterminated frontmatter: skip the opening delimiter
|
|
97
|
+
for line in body:
|
|
98
|
+
s = line.strip().lstrip("#").strip()
|
|
99
|
+
if s and s != "---": # a stray delimiter is never a summary
|
|
100
|
+
return " ".join(s.split())[:_SNIPPET_CHARS]
|
|
79
101
|
return ""
|
|
80
102
|
|
|
81
103
|
|