swarph-cli 0.72.9__tar.gz → 0.72.10__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.72.9/src/swarph_cli.egg-info → swarph_cli-0.72.10}/PKG-INFO +1 -1
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/pyproject.toml +1 -1
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/__init__.py +1 -1
- swarph_cli-0.72.10/src/swarph_cli/commands/sweep.py +413 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/guide/GUIDE.md +1 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/main.py +1 -0
- swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.service +15 -0
- swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.timer +14 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/SOURCES.txt +5 -0
- swarph_cli-0.72.10/tests/test_1064_stuck_sweep.py +332 -0
- swarph_cli-0.72.10/tests/test_1065_release.py +295 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/LICENSE +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/README.md +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/setup.cfg +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/roster.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_main.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_security.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.9 → swarph_cli-0.72.10}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.10
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.72.
|
|
7
|
+
version = "0.72.10"
|
|
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" }
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"""``swarph sweep`` — the card-#1064 stuck-work sweep, one daily shot.
|
|
2
|
+
|
|
3
|
+
Finds (a) open/taken rows blocked >=48h on an unmoving dependency and (b)
|
|
4
|
+
approved-or-old open PRs with no obligation row naming them; DMs each
|
|
5
|
+
finding's BLOCKING holder once per 24h (never the waiter) and appends one
|
|
6
|
+
line per finding to the nightly watchtower report file. Never closes or
|
|
7
|
+
reassigns: the only board write is the DM itself (POST /messages).
|
|
8
|
+
|
|
9
|
+
swarph sweep [--dry-run] [--repos R1,R2] [--state-file P] [--report-file P]
|
|
10
|
+
[--as SELF] [--gateway URL] [--token-file F] [--json]
|
|
11
|
+
|
|
12
|
+
--dry-run reads the live board and GitHub, prints the findings, and writes
|
|
13
|
+
nothing (no DMs, no state, no report) — the rehearsal shape.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
|
+
|
|
25
|
+
_STUCK_H = 48
|
|
26
|
+
_RESEND_H = 24
|
|
27
|
+
|
|
28
|
+
_DEFAULT_REPOS = (
|
|
29
|
+
"BrainSurfing-tech/swarph-cli",
|
|
30
|
+
"BrainSurfing-tech/mesh-gateway",
|
|
31
|
+
"BrainSurfing-tech/swarph-codegraph-fed",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_PR_URL_RE = re.compile(
|
|
35
|
+
r"github\.com/([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)/pull/(\d+)",
|
|
36
|
+
re.IGNORECASE)
|
|
37
|
+
_PR_NUM_RE = re.compile(r"\bPR\s*#?(\d+)\b", re.IGNORECASE)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _parse_ts(v):
|
|
41
|
+
"""Tolerant ISO parse -> epoch seconds, or None. Never raises: an
|
|
42
|
+
unparseable timestamp is unevaluable, not zero."""
|
|
43
|
+
if not v:
|
|
44
|
+
return None
|
|
45
|
+
try:
|
|
46
|
+
dt = datetime.fromisoformat(str(v).replace("Z", "+00:00"))
|
|
47
|
+
except (TypeError, ValueError):
|
|
48
|
+
return None
|
|
49
|
+
try:
|
|
50
|
+
return dt.timestamp()
|
|
51
|
+
except (TypeError, ValueError, OverflowError, OSError):
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _age_days(now, ts):
|
|
56
|
+
return round((now - ts) / 86400)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _row_last_change(row):
|
|
60
|
+
for k in ("closed_at", "taken_at", "created_at"):
|
|
61
|
+
ts = _parse_ts(row.get(k))
|
|
62
|
+
if ts is not None:
|
|
63
|
+
return ts
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _named_prs(rows):
|
|
68
|
+
"""(repo-or-None, number) pairs obligation rows name: full URLs match
|
|
69
|
+
exactly; bare PR numbers match by number across tracked repos."""
|
|
70
|
+
named = set()
|
|
71
|
+
for row in rows:
|
|
72
|
+
if not isinstance(row, dict):
|
|
73
|
+
continue
|
|
74
|
+
blob = " ".join(
|
|
75
|
+
str(row.get(k) or "") for k in ("accept", "close_evidence"))
|
|
76
|
+
for m in _PR_URL_RE.finditer(blob):
|
|
77
|
+
try:
|
|
78
|
+
named.add((m.group(1).lower(), int(m.group(2))))
|
|
79
|
+
except ValueError:
|
|
80
|
+
continue
|
|
81
|
+
for m in _PR_NUM_RE.finditer(blob):
|
|
82
|
+
try:
|
|
83
|
+
named.add((None, int(m.group(1))))
|
|
84
|
+
except ValueError:
|
|
85
|
+
continue
|
|
86
|
+
return named
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _graph_steps(http_get, gateway, token, card_id, cache):
|
|
90
|
+
if card_id in cache:
|
|
91
|
+
return cache[card_id]
|
|
92
|
+
try:
|
|
93
|
+
status, body = http_get(
|
|
94
|
+
f"{gateway}/board/cards/{card_id}/graph", token)
|
|
95
|
+
except Exception:
|
|
96
|
+
return None
|
|
97
|
+
if status != 200 or not isinstance(body, dict):
|
|
98
|
+
return None
|
|
99
|
+
steps = body.get("steps")
|
|
100
|
+
if not isinstance(steps, list):
|
|
101
|
+
return None
|
|
102
|
+
cache[card_id] = steps
|
|
103
|
+
return steps
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _row_findings(rows, by_id, http_get, gateway, token, now):
|
|
107
|
+
"""Kind (a): one finding per (open taken waiter, unsatisfied old need)."""
|
|
108
|
+
out = []
|
|
109
|
+
cache = {}
|
|
110
|
+
for w in rows:
|
|
111
|
+
if w.get("status") != "open" or not w.get("taken_at"):
|
|
112
|
+
continue
|
|
113
|
+
step = w.get("step")
|
|
114
|
+
if not step:
|
|
115
|
+
continue
|
|
116
|
+
card_id = w.get("card_id")
|
|
117
|
+
if not isinstance(card_id, int) or isinstance(card_id, bool):
|
|
118
|
+
continue
|
|
119
|
+
steps = _graph_steps(http_get, gateway, token, card_id, cache)
|
|
120
|
+
if steps is None:
|
|
121
|
+
continue
|
|
122
|
+
entry = next((e for e in steps
|
|
123
|
+
if isinstance(e, dict) and e.get("step") == step), None)
|
|
124
|
+
if entry is None:
|
|
125
|
+
continue
|
|
126
|
+
needs = entry.get("needs")
|
|
127
|
+
if not isinstance(needs, list):
|
|
128
|
+
continue
|
|
129
|
+
for need in needs:
|
|
130
|
+
if not isinstance(need, dict):
|
|
131
|
+
continue
|
|
132
|
+
sat = need.get("satisfied")
|
|
133
|
+
if sat is True:
|
|
134
|
+
continue
|
|
135
|
+
if sat is not False:
|
|
136
|
+
continue # unevaluable need names no finding
|
|
137
|
+
rid = need.get("row_id")
|
|
138
|
+
dep = by_id.get(rid)
|
|
139
|
+
if not isinstance(dep, dict):
|
|
140
|
+
continue
|
|
141
|
+
if dep.get("status") == "closed":
|
|
142
|
+
continue # terminal: satisfied or a rework case, not unmoving
|
|
143
|
+
changed = _row_last_change(dep)
|
|
144
|
+
if changed is None or now - changed < _STUCK_H * 3600:
|
|
145
|
+
continue
|
|
146
|
+
holder = dep.get("holder")
|
|
147
|
+
blocker = holder if holder and dep.get("holder_known", True) else None
|
|
148
|
+
out.append({
|
|
149
|
+
"kind": "row",
|
|
150
|
+
"waiter_id": w.get("id"),
|
|
151
|
+
"waiter": w.get("holder"),
|
|
152
|
+
"waiter_step": step,
|
|
153
|
+
"card_id": card_id,
|
|
154
|
+
"need": need.get("step"),
|
|
155
|
+
"need_id": rid,
|
|
156
|
+
"blocker": blocker,
|
|
157
|
+
"age_days": _age_days(now, changed),
|
|
158
|
+
})
|
|
159
|
+
return out
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _pr_findings(named, gh_prs, repos, peers, now):
|
|
163
|
+
"""Kind (b): approved-or-old open PRs no row names. An author with no
|
|
164
|
+
mesh peer is report-only — the CLI must not invent a recipient."""
|
|
165
|
+
out = []
|
|
166
|
+
for repo in repos:
|
|
167
|
+
try:
|
|
168
|
+
prs = gh_prs(repo)
|
|
169
|
+
except Exception:
|
|
170
|
+
continue
|
|
171
|
+
if not isinstance(prs, list):
|
|
172
|
+
continue
|
|
173
|
+
for pr in prs:
|
|
174
|
+
if not isinstance(pr, dict):
|
|
175
|
+
continue
|
|
176
|
+
try:
|
|
177
|
+
number = int(pr.get("number"))
|
|
178
|
+
except (TypeError, ValueError):
|
|
179
|
+
continue
|
|
180
|
+
if (repo.lower(), number) in named or (None, number) in named:
|
|
181
|
+
continue
|
|
182
|
+
approved = pr.get("reviewDecision") == "APPROVED"
|
|
183
|
+
created = _parse_ts(pr.get("createdAt"))
|
|
184
|
+
old = created is not None and now - created >= _STUCK_H * 3600
|
|
185
|
+
if not approved and not old:
|
|
186
|
+
continue
|
|
187
|
+
author = pr.get("author") or {}
|
|
188
|
+
login = author.get("login") if isinstance(author, dict) else None
|
|
189
|
+
# blocker names the responsible party even when they have no
|
|
190
|
+
# mesh peer — the SEND step DMs only peers (report-only else).
|
|
191
|
+
out.append({
|
|
192
|
+
"kind": "pr",
|
|
193
|
+
"repo": repo,
|
|
194
|
+
"number": number,
|
|
195
|
+
"title": pr.get("title") or "",
|
|
196
|
+
"url": pr.get("url") or "",
|
|
197
|
+
"approved": approved,
|
|
198
|
+
"age_days": _age_days(now, created) if created else None,
|
|
199
|
+
"blocker": login,
|
|
200
|
+
})
|
|
201
|
+
return out
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _finding_key(f):
|
|
205
|
+
if f["kind"] == "row":
|
|
206
|
+
return f"row:{f['waiter_id']}:{f['need']}"
|
|
207
|
+
return f"pr:{f['repo']}#{f['number']}"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _report_line(now_iso, f):
|
|
211
|
+
if f["kind"] == "row":
|
|
212
|
+
return (f"{now_iso} STUCK row waiter=#{f['waiter_id']}({f['waiter']}) "
|
|
213
|
+
f"step={f['waiter_step']} card=#{f['card_id']} "
|
|
214
|
+
f"blocked-by=#{f['need_id']}({f['blocker']}) "
|
|
215
|
+
f"need={f['need']} need-age={f['age_days']}d")
|
|
216
|
+
how = "approved" if f["approved"] else f"age={f['age_days']}d"
|
|
217
|
+
return (f"{now_iso} STUCK pr {f['repo']}#{f['number']} "
|
|
218
|
+
f"'{f['title']}' ({how}, no review row) author={f['blocker']}")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _dm_content(f):
|
|
222
|
+
if f["kind"] == "row":
|
|
223
|
+
return (f"STUCK-WORK SWEEP: row #{f['waiter_id']} ({f['waiter']}, "
|
|
224
|
+
f"{f['waiter_step']}, card #{f['card_id']}) has waited on "
|
|
225
|
+
f"your row #{f['need_id']} ({f['need']}, unchanged "
|
|
226
|
+
f"{f['age_days']}d). Please move it, fail it, or say what "
|
|
227
|
+
f"it waits on.")
|
|
228
|
+
state = "approved" if f["approved"] else "open %sd" % (f['age_days'],)
|
|
229
|
+
return (f"STUCK-WORK SWEEP: your PR {f['repo']}#{f['number']} "
|
|
230
|
+
f"'{f['title']}' ({f['url']}) is {state} "
|
|
231
|
+
f"with no review row naming it. Please land it or ask for review.")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def run_sweep(*, gateway, token, sender, repos, state_file, report_file,
|
|
235
|
+
now, http_get, http_post, gh_prs, dry_run=False):
|
|
236
|
+
"""One sweep. Returns (rc, findings): rc 1 only when the board itself is
|
|
237
|
+
unreadable (nothing evaluable); findings are the SENT (or dry-run, would
|
|
238
|
+
send) findings. Only ever writes DMs + the two local files."""
|
|
239
|
+
gateway = gateway.rstrip("/")
|
|
240
|
+
try:
|
|
241
|
+
status, body = http_get(f"{gateway}/board/obligations", token)
|
|
242
|
+
except Exception:
|
|
243
|
+
status, body = None, None
|
|
244
|
+
if status != 200 or not isinstance(body, dict) or not isinstance(
|
|
245
|
+
body.get("obligations"), list):
|
|
246
|
+
print("swarph sweep: board unreadable — no findings, nothing sent",
|
|
247
|
+
file=sys.stderr)
|
|
248
|
+
return 1, []
|
|
249
|
+
rows = [r for r in body["obligations"] if isinstance(r, dict)]
|
|
250
|
+
by_id = {r.get("id"): r for r in rows
|
|
251
|
+
if isinstance(r.get("id"), int)}
|
|
252
|
+
try:
|
|
253
|
+
pstatus, pbody = http_get(f"{gateway}/peers", token)
|
|
254
|
+
peers = {p["name"] for p in pbody.get("peers", [])
|
|
255
|
+
if isinstance(p, dict) and p.get("name")}
|
|
256
|
+
if pstatus != 200:
|
|
257
|
+
peers = set()
|
|
258
|
+
except Exception:
|
|
259
|
+
peers = set()
|
|
260
|
+
|
|
261
|
+
findings = _row_findings(rows, by_id, http_get, gateway, token, now)
|
|
262
|
+
findings += _pr_findings(_named_prs(rows), gh_prs, repos, peers, now)
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
with open(state_file, encoding="utf-8") as fp:
|
|
266
|
+
sent = json.load(fp)
|
|
267
|
+
sent = sent if isinstance(sent, dict) else {}
|
|
268
|
+
except (OSError, ValueError):
|
|
269
|
+
sent = {}
|
|
270
|
+
now_iso = datetime.fromtimestamp(now, timezone.utc).isoformat()
|
|
271
|
+
fresh, updates = [], {}
|
|
272
|
+
for f in findings:
|
|
273
|
+
key = _finding_key(f)
|
|
274
|
+
try:
|
|
275
|
+
last = _parse_ts(sent.get(key))
|
|
276
|
+
except Exception:
|
|
277
|
+
last = None
|
|
278
|
+
if last is not None and now - last < _RESEND_H * 3600:
|
|
279
|
+
continue
|
|
280
|
+
fresh.append(f)
|
|
281
|
+
updates[key] = now_iso
|
|
282
|
+
|
|
283
|
+
if dry_run:
|
|
284
|
+
for f in fresh:
|
|
285
|
+
print("DRY " + _report_line(now_iso, f))
|
|
286
|
+
return 0, fresh
|
|
287
|
+
|
|
288
|
+
for f in fresh:
|
|
289
|
+
# DM only a blocker with a mesh peer: pinging the waiter instead is
|
|
290
|
+
# the FAIL, and inventing a recipient for a gh login is its cousin.
|
|
291
|
+
if f["blocker"] is not None and f["blocker"] in peers:
|
|
292
|
+
status, payload = http_post(
|
|
293
|
+
f"{gateway}/messages",
|
|
294
|
+
{"from_node": sender, "to_node": f["blocker"],
|
|
295
|
+
"kind": "fyi", "content": _dm_content(f)},
|
|
296
|
+
token)
|
|
297
|
+
if status < 200 or status >= 300:
|
|
298
|
+
detail = (payload.get("detail", "<gateway error>")
|
|
299
|
+
if isinstance(payload, dict) else "<gateway error>")
|
|
300
|
+
print(f"swarph sweep: DM to {f['blocker']} failed "
|
|
301
|
+
f"{status}: {detail}", file=sys.stderr)
|
|
302
|
+
continue
|
|
303
|
+
line = _report_line(now_iso, f)
|
|
304
|
+
print(line)
|
|
305
|
+
try:
|
|
306
|
+
with open(report_file, "a", encoding="utf-8") as fp:
|
|
307
|
+
fp.write(line + "\n")
|
|
308
|
+
except OSError as exc:
|
|
309
|
+
print(f"swarph sweep: report append failed: {exc}",
|
|
310
|
+
file=sys.stderr)
|
|
311
|
+
if updates:
|
|
312
|
+
sent.update(updates)
|
|
313
|
+
try:
|
|
314
|
+
with open(state_file, "w", encoding="utf-8") as fp:
|
|
315
|
+
json.dump(sent, fp)
|
|
316
|
+
except OSError as exc:
|
|
317
|
+
print(f"swarph sweep: state write failed: {exc}", file=sys.stderr)
|
|
318
|
+
return 0, fresh
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _gh_prs(repo):
|
|
322
|
+
"""Live gh transport: open PRs with the fields the matcher needs, or
|
|
323
|
+
None when gh is unusable (that repo's findings are skipped, not
|
|
324
|
+
invented)."""
|
|
325
|
+
try:
|
|
326
|
+
p = subprocess.run(
|
|
327
|
+
["gh", "pr", "list", "--repo", repo, "--state", "open",
|
|
328
|
+
"--limit", "100", "--json",
|
|
329
|
+
"number,title,createdAt,reviewDecision,url,author"],
|
|
330
|
+
capture_output=True, text=True, encoding="utf-8",
|
|
331
|
+
errors="replace", timeout=60)
|
|
332
|
+
except (OSError, subprocess.SubprocessError):
|
|
333
|
+
return None
|
|
334
|
+
if p.returncode != 0:
|
|
335
|
+
return None
|
|
336
|
+
try:
|
|
337
|
+
data = json.loads(p.stdout or "[]")
|
|
338
|
+
except ValueError:
|
|
339
|
+
return None
|
|
340
|
+
return data if isinstance(data, list) else None
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _default_state_file():
|
|
344
|
+
xdg = os.environ.get("XDG_STATE_HOME", "").strip()
|
|
345
|
+
root = xdg if xdg else os.path.join(os.path.expanduser("~"), ".local", "state")
|
|
346
|
+
return os.path.join(root, "swarph", "stuck-sweep.json")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _build_parser():
|
|
350
|
+
p = argparse.ArgumentParser(
|
|
351
|
+
prog="swarph sweep",
|
|
352
|
+
description="Daily stuck-work sweep: blocked rows + unreviewed PRs "
|
|
353
|
+
"(card #1064). DMs blockers, never closes/reassigns.")
|
|
354
|
+
p.add_argument("--dry-run", action="store_true",
|
|
355
|
+
help="print findings; send/record/write nothing")
|
|
356
|
+
p.add_argument("--repos", default=None,
|
|
357
|
+
help="comma-separated owner/repo list (default "
|
|
358
|
+
"$SWARPH_SWEEP_REPOS or the fleet three)")
|
|
359
|
+
p.add_argument("--state-file", default=None)
|
|
360
|
+
p.add_argument("--report-file", default=None,
|
|
361
|
+
help="nightly watchtower report path "
|
|
362
|
+
"(default $SWARPH_SWEEP_REPORT or state-adjacent log)")
|
|
363
|
+
p.add_argument("--json", action="store_true")
|
|
364
|
+
p.add_argument("--as", dest="self_name", default=None,
|
|
365
|
+
help="DM sender identity")
|
|
366
|
+
from swarph_cli.gateway_default import env_gateway
|
|
367
|
+
p.add_argument("--gateway", default=env_gateway())
|
|
368
|
+
p.add_argument("--token-file", default=None)
|
|
369
|
+
return p
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def run_sweep_cmd(argv):
|
|
373
|
+
import time as _time
|
|
374
|
+
from swarph_cli.commands import mesh as _mesh
|
|
375
|
+
from swarph_cli.tokens import resolve_token
|
|
376
|
+
args = _build_parser().parse_args(argv)
|
|
377
|
+
try:
|
|
378
|
+
# #872 doctrine: an automated DM must name its sender. Dry-run sends
|
|
379
|
+
# nothing and needs no identity.
|
|
380
|
+
if not args.self_name and not args.dry_run:
|
|
381
|
+
print("swarph sweep: --as a sender is required (automated DMs "
|
|
382
|
+
"must name their sender; --dry-run needs no identity)",
|
|
383
|
+
file=sys.stderr)
|
|
384
|
+
return 2
|
|
385
|
+
resolution = resolve_token(
|
|
386
|
+
args.self_name, args.token_file,
|
|
387
|
+
identity_is_explicit=bool(args.self_name))
|
|
388
|
+
if resolution is None:
|
|
389
|
+
print("swarph sweep: no token (set MESH_GATEWAY_TOKEN, pass "
|
|
390
|
+
"--token-file, or --as a sender)", file=sys.stderr)
|
|
391
|
+
return 2
|
|
392
|
+
sender = args.self_name or "(dry-run)"
|
|
393
|
+
repos = ([r.strip() for r in args.repos.split(",") if r.strip()]
|
|
394
|
+
if args.repos else
|
|
395
|
+
[r.strip() for r in os.environ.get(
|
|
396
|
+
"SWARPH_SWEEP_REPOS", ",".join(_DEFAULT_REPOS)
|
|
397
|
+
).split(",") if r.strip()])
|
|
398
|
+
state_file = args.state_file or os.environ.get(
|
|
399
|
+
"SWARPH_SWEEP_STATE", _default_state_file())
|
|
400
|
+
report_file = (args.report_file or os.environ.get("SWARPH_SWEEP_REPORT")
|
|
401
|
+
or os.path.splitext(state_file)[0] + ".log")
|
|
402
|
+
rc, findings = run_sweep(
|
|
403
|
+
gateway=args.gateway, token=resolution.token, sender=sender,
|
|
404
|
+
repos=repos, state_file=state_file, report_file=report_file,
|
|
405
|
+
now=_time.time(), http_get=_mesh._http_get_json,
|
|
406
|
+
http_post=_mesh._post_json, gh_prs=_gh_prs,
|
|
407
|
+
dry_run=args.dry_run)
|
|
408
|
+
if args.json:
|
|
409
|
+
print(json.dumps(findings, indent=2))
|
|
410
|
+
return rc
|
|
411
|
+
except RuntimeError as exc:
|
|
412
|
+
print(f"swarph sweep: {exc}", file=sys.stderr)
|
|
413
|
+
return 2
|
|
@@ -549,6 +549,7 @@ answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not comman
|
|
|
549
549
|
- `swarph schedule` -- the gateway's scheduled events: create, list, get, enable, disable, delete, fire-now.
|
|
550
550
|
- `swarph service` -- stand up a $0 subscription-LLM HTTP lane.
|
|
551
551
|
- `swarph spawn` -- run a cell from its role name or yaml path.
|
|
552
|
+
- `swarph sweep` -- the card-#1064 stuck-work sweep: blocked rows and unreviewed PRs, DMs the blocker, `swarph sweep --dry-run` reads only.
|
|
552
553
|
- `swarph timeline` -- deterministic temporal lookup over the timeline: around, since, range (see [Code and history](#code-and-history)).
|
|
553
554
|
- `swarph version` -- per-module versions and install origin (PEP 610).
|
|
554
555
|
- `swarph wake-hook-output` -- hook plumbing: the silent-wake SessionStart callback.
|
|
@@ -111,6 +111,7 @@ _VERB_HANDLERS: dict[str, str] = {
|
|
|
111
111
|
"group": "swarph_cli.commands.group.run_group",
|
|
112
112
|
"rights": "swarph_cli.commands.group.run_rights",
|
|
113
113
|
"roster": "swarph_cli.commands.roster.run_roster",
|
|
114
|
+
"sweep": "swarph_cli.commands.sweep.run_sweep_cmd",
|
|
114
115
|
"mcp-server": "swarph_cli.commands.mcp_server.run_mcp_server",
|
|
115
116
|
"compress": "swarph_cli.commands.compress.run_compress",
|
|
116
117
|
"cell": "swarph_cli.commands.cell.run_cell",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Stuck-work sweep one-shot: DM blockers of >=48h-stuck rows and unreviewed PRs (card #1064)
|
|
3
|
+
Documentation=https://github.com/BrainSurfing-tech/swarph-cli
|
|
4
|
+
After=network-online.target
|
|
5
|
+
Wants=network-online.target
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=oneshot
|
|
9
|
+
EnvironmentFile=-/etc/default/swarph-stuck-sweep
|
|
10
|
+
ExecStart=/usr/local/bin/swarph sweep --as lab-ovh
|
|
11
|
+
StandardOutput=append:/var/log/swarph-stuck-sweep.log
|
|
12
|
+
StandardError=append:/var/log/swarph-stuck-sweep.log
|
|
13
|
+
|
|
14
|
+
[Install]
|
|
15
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Run the stuck-work sweep: swarph sweep daily (blocked rows + unreviewed PRs, card #1064)
|
|
3
|
+
Documentation=https://github.com/BrainSurfing-tech/swarph-cli
|
|
4
|
+
Requires=swarph-stuck-sweep.service
|
|
5
|
+
|
|
6
|
+
[Timer]
|
|
7
|
+
OnBootSec=15min
|
|
8
|
+
OnCalendar=daily
|
|
9
|
+
AccuracySec=30min
|
|
10
|
+
Persistent=true
|
|
11
|
+
RandomizedDelaySec=15min
|
|
12
|
+
|
|
13
|
+
[Install]
|
|
14
|
+
WantedBy=timers.target
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.10
|
|
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
|
|
@@ -116,6 +116,7 @@ src/swarph_cli/commands/schedule.py
|
|
|
116
116
|
src/swarph_cli/commands/security.py
|
|
117
117
|
src/swarph_cli/commands/service.py
|
|
118
118
|
src/swarph_cli/commands/spawn.py
|
|
119
|
+
src/swarph_cli/commands/sweep.py
|
|
119
120
|
src/swarph_cli/commands/timeline.py
|
|
120
121
|
src/swarph_cli/commands/version.py
|
|
121
122
|
src/swarph_cli/commands/wake_hook_output.py
|
|
@@ -186,6 +187,8 @@ src/swarph_cli/systemd/swarph-monitor-reexec.service
|
|
|
186
187
|
src/swarph_cli/systemd/swarph-monitor@.service
|
|
187
188
|
src/swarph_cli/systemd/swarph-peer-reply-drain@.service
|
|
188
189
|
src/swarph_cli/systemd/swarph-peer-reply-drain@.timer
|
|
190
|
+
src/swarph_cli/systemd/swarph-stuck-sweep.service
|
|
191
|
+
src/swarph_cli/systemd/swarph-stuck-sweep.timer
|
|
189
192
|
src/swarph_cli/systemd/swarph-watchdog.default
|
|
190
193
|
src/swarph_cli/systemd/swarph-watchdog.service
|
|
191
194
|
src/swarph_cli/systemd/swarph-watchdog.timer
|
|
@@ -198,6 +201,8 @@ tests/test_1050_surrogate_wake.py
|
|
|
198
201
|
tests/test_1052_opencode_deaf.py
|
|
199
202
|
tests/test_1059_watchdog_unread_env.py
|
|
200
203
|
tests/test_1061_roster_cli.py
|
|
204
|
+
tests/test_1064_stuck_sweep.py
|
|
205
|
+
tests/test_1065_release.py
|
|
201
206
|
tests/test_124_register_merge.py
|
|
202
207
|
tests/test_126_pending_from_complete.py
|
|
203
208
|
tests/test_132_ensure_monitor_state_dir.py
|