swarph-cli 0.72.28__tar.gz → 0.72.29__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.28/src/swarph_cli.egg-info → swarph_cli-0.72.29}/PKG-INFO +2 -2
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/README.md +1 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/pyproject.toml +4 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/claude_cli.py +5 -2
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/board.py +84 -12
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/hook_output.py +45 -37
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/lane.py +16 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/mesh.py +83 -3
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/roster.py +11 -5
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/schedule.py +12 -7
- swarph_cli-0.72.29/src/swarph_cli/duplicate_session.py +284 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/stall_alert.py +35 -63
- swarph_cli-0.72.29/src/swarph_cli/the_ten.py +223 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29/src/swarph_cli.egg-info}/PKG-INFO +2 -2
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli.egg-info/SOURCES.txt +9 -0
- swarph_cli-0.72.29/tests/test_1034_copy_mode.py +61 -0
- swarph_cli-0.72.29/tests/test_1222_maybe_start_cap.py +126 -0
- swarph_cli-0.72.29/tests/test_1228_quota_reach.py +53 -0
- swarph_cli-0.72.29/tests/test_1243_the_ten.py +274 -0
- swarph_cli-0.72.29/tests/test_513_duplicate_session.py +215 -0
- swarph_cli-0.72.29/tests/test_931_board_search.py +88 -0
- swarph_cli-0.72.29/tests/test_935_deadline_clock.py +55 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_claude_cli.py +15 -4
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_copilot_pane_reader.py +5 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hook_output.py +3 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_lane_command.py +80 -3
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_schedule_command.py +35 -12
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_stall_alert.py +101 -62
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/LICENSE +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/setup.cfg +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/launchd_render.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/quota_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/backend.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1068_structured_accept_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1071_quota_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1083_openai_decisions.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1144_cell_yaml_github.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1197_commander_taps.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1203_swarph_me_refusal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1206_offer_question.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1214_egress.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1219_egress_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_1221_quota_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_launchd_units.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_main.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.29}/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.29
|
|
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
|
|
@@ -179,7 +179,7 @@ The mesh **board** — the projects + cards kanban the mesh coordinates on — a
|
|
|
179
179
|
```
|
|
180
180
|
swarph board projects list
|
|
181
181
|
swarph board projects add <slug> --title "…" [--goal "…"]
|
|
182
|
-
swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
|
|
182
|
+
swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>] [--search <text>] # --search: title OR body, says how many cards it searched
|
|
183
183
|
swarph board cards show <id>
|
|
184
184
|
swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
|
|
185
185
|
swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
|
|
@@ -130,7 +130,7 @@ The mesh **board** — the projects + cards kanban the mesh coordinates on — a
|
|
|
130
130
|
```
|
|
131
131
|
swarph board projects list
|
|
132
132
|
swarph board projects add <slug> --title "…" [--goal "…"]
|
|
133
|
-
swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>]
|
|
133
|
+
swarph board cards list [--project <id|slug>] [--stage <s>] [--assignee <who>] [--search <text>] # --search: title OR body, says how many cards it searched
|
|
134
134
|
swarph board cards show <id>
|
|
135
135
|
swarph board cards add --project <id|slug> --title "…" [--body "…"] [--ai2] [--priority N]
|
|
136
136
|
swarph board cards edit <id> [--title "…"] [--body "…"] [--project <id|slug>] [--priority N]
|
|
@@ -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.29"
|
|
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" }
|
|
@@ -173,3 +173,6 @@ testpaths = ["tests"]
|
|
|
173
173
|
# nearly identical. That agreement is a polluted baseline, not isolation.
|
|
174
174
|
pythonpath = ["src"]
|
|
175
175
|
addopts = "-v --tb=short"
|
|
176
|
+
markers = [
|
|
177
|
+
"live_tmux: drives a real tmux server/pane; opt-in, and the test must state its reason (card #1034)",
|
|
178
|
+
]
|
|
@@ -23,9 +23,12 @@ class CliResult:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def build_argv(model_id: str, system: str) -> list:
|
|
26
|
+
# Card #1090: one token each. --mcp-config is variadic, so a following
|
|
27
|
+
# JSON token is consumed only while the token after that is a flag.
|
|
26
28
|
return ["claude", "-p", "--model", model_id, "--system-prompt", system, "--max-turns", "1",
|
|
27
|
-
"--output-format", "stream-json", "--verbose",
|
|
28
|
-
"--
|
|
29
|
+
"--output-format", "stream-json", "--verbose",
|
|
30
|
+
"--tools=", "--strict-mcp-config",
|
|
31
|
+
'--mcp-config={"mcpServers":{}}', "--setting-sources="]
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
def clean_env(env: dict) -> dict:
|
|
@@ -71,9 +71,11 @@ def _patch_json(url: str, body: dict, token: str, *, timeout: float = 10.0) -> t
|
|
|
71
71
|
# ── pure builders (unit-tested) ───────────────────────────────────────────────
|
|
72
72
|
|
|
73
73
|
def _cards_list_url(gateway: str, *, project=None, stage=None, assignee=None,
|
|
74
|
-
label=None) -> str:
|
|
74
|
+
label=None, search=None) -> str:
|
|
75
75
|
q = {k: v for k, v in (("project", project), ("stage", stage),
|
|
76
76
|
("assignee", assignee), ("label", label)) if v}
|
|
77
|
+
if search is not None: # blank still goes: the gateway refuses it, we do not drop it
|
|
78
|
+
q["q"] = search
|
|
77
79
|
base = f"{gateway.rstrip('/')}/board/cards"
|
|
78
80
|
return f"{base}?{urllib.parse.urlencode(q)}" if q else base
|
|
79
81
|
|
|
@@ -353,6 +355,10 @@ def _format_due_head(data) -> list[str]:
|
|
|
353
355
|
def _format_cards(data) -> str:
|
|
354
356
|
head = _format_due_head(data)
|
|
355
357
|
rows = _cards_of(data)
|
|
358
|
+
if isinstance(data, dict) and "searched" in data:
|
|
359
|
+
# #931: a zero must say how many cards it looked at.
|
|
360
|
+
head = [f"searched {data['searched']} cards: {len(rows)} match"] + (
|
|
361
|
+
[""] + head if head else [])
|
|
356
362
|
if not rows:
|
|
357
363
|
body = ["(no cards)"]
|
|
358
364
|
else:
|
|
@@ -604,14 +610,49 @@ def _thread_line(d) -> str:
|
|
|
604
610
|
f"(#509/#562 — the VERB is the mechanism, the thread is where it lands)")
|
|
605
611
|
|
|
606
612
|
|
|
613
|
+
def _hours_token(n) -> str:
|
|
614
|
+
if isinstance(n, float) and n.is_integer():
|
|
615
|
+
n = int(n)
|
|
616
|
+
return str(n)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def _clock_line(d) -> str | None:
|
|
620
|
+
"""The due-date clock (#935). It starts when the row is taken, not now.
|
|
621
|
+
|
|
622
|
+
`clock_hours` is the menu step's hours, or the `--hours` override.
|
|
623
|
+
`timeout_at` is a different clock — when the row reads OVERDUE — and
|
|
624
|
+
is not this line. Absent fields mean an older gateway; the caller
|
|
625
|
+
keeps its previous sentence rather than inventing a clock.
|
|
626
|
+
"""
|
|
627
|
+
hours = d.get("clock_hours")
|
|
628
|
+
if hours is None:
|
|
629
|
+
return None
|
|
630
|
+
source = d.get("clock_source") or "menu"
|
|
631
|
+
menu = d.get("menu_hours")
|
|
632
|
+
if (source == "override" and isinstance(menu, (int, float))
|
|
633
|
+
and not isinstance(menu, bool) and menu
|
|
634
|
+
and isinstance(hours, (int, float)) and not isinstance(hours, bool)):
|
|
635
|
+
if hours % menu == 0:
|
|
636
|
+
detail = f"override, {int(hours // menu)}x menu {_hours_token(menu)}h"
|
|
637
|
+
else:
|
|
638
|
+
detail = f"override, menu {_hours_token(menu)}h"
|
|
639
|
+
else:
|
|
640
|
+
detail = str(source)
|
|
641
|
+
return f"clock: {_hours_token(hours)}h ({detail}) — STARTS ON TAKE, not now"
|
|
642
|
+
|
|
643
|
+
|
|
607
644
|
def _format_ask(d) -> str:
|
|
608
|
-
"""
|
|
645
|
+
"""Name the obligation, who owes it, and the deadline clock.
|
|
609
646
|
|
|
610
647
|
>>> NAMING THE DEADLINE OR ITS ABSENCE IS THE POINT, NOT DECORATION. <<< #145's
|
|
611
648
|
lesson, one layer over: "0 overdue" must not be able to mean "nobody set dates".
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
649
|
+
The deadline is the due-date clock (`clock_hours`): the menu step's hours,
|
|
650
|
+
or the `--hours` override, and it STARTS ON TAKE, not at mint (#935).
|
|
651
|
+
`timeout_at` is the other clock — when the row READS OVERDUE — and is
|
|
652
|
+
printed under that name, never as the deadline. A response that carries
|
|
653
|
+
neither clock says NO TIMEOUT, because an obligation with no timeout never
|
|
654
|
+
goes red on its own and the operator has to be told that AT THE MOMENT
|
|
655
|
+
THEY CREATE IT.
|
|
615
656
|
|
|
616
657
|
#532 applies the same rule to the falsifier: an obligation with no accept
|
|
617
658
|
check reads RED in the sweep, and one whose check has no FAIL branch is
|
|
@@ -619,9 +660,17 @@ def _format_ask(d) -> str:
|
|
|
619
660
|
rows look identical to sound ones.
|
|
620
661
|
"""
|
|
621
662
|
when = d.get("timeout_at")
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
f"{d.get('holder')} owes it, status={d.get('status')}
|
|
663
|
+
clock = _clock_line(d)
|
|
664
|
+
head = (f"obligation #{d.get('id')} on card #{d.get('card_id')}: "
|
|
665
|
+
f"{d.get('holder')} owes it, status={d.get('status')}")
|
|
666
|
+
if clock:
|
|
667
|
+
extra = f"\n {clock}"
|
|
668
|
+
extra += f"\n overdue after {when}" if when else f"\n {_NO_TIMEOUT}"
|
|
669
|
+
else:
|
|
670
|
+
deadline = f"overdue after {when}" if when else _NO_TIMEOUT
|
|
671
|
+
head = f"{head}, {deadline}"
|
|
672
|
+
extra = ""
|
|
673
|
+
return (f"{head}{extra}\n"
|
|
625
674
|
f" {_accept_line(d.get('accept'))}\n"
|
|
626
675
|
f" {_thread_line(d)}")
|
|
627
676
|
|
|
@@ -763,6 +812,13 @@ def _ask_line(d) -> str:
|
|
|
763
812
|
if missing:
|
|
764
813
|
line += f" · still missing: {missing}"
|
|
765
814
|
if not str(state).startswith("closed"):
|
|
815
|
+
clock = _clock_line(d)
|
|
816
|
+
if clock:
|
|
817
|
+
# #935: the due-date clock, then the overdue clock under its own name.
|
|
818
|
+
# A stepped mint used to say neither, so --timeout-hours was read as the due date.
|
|
819
|
+
line += "\n " + clock
|
|
820
|
+
if d.get("timeout_at"):
|
|
821
|
+
line += "\n overdue after " + _s(d["timeout_at"])
|
|
766
822
|
line += "\n " + _accept_line(d.get("accept"))
|
|
767
823
|
if d.get("step") is None:
|
|
768
824
|
# unstepped: no menu clock, no container — the pre-#591 rules hold
|
|
@@ -974,6 +1030,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
974
1030
|
cl = cards.add_parser("list", help="list cards")
|
|
975
1031
|
cl.add_argument("--project"); cl.add_argument("--stage"); cl.add_argument("--assignee")
|
|
976
1032
|
cl.add_argument("--label", help="only cards carrying this label (exact match)")
|
|
1033
|
+
cl.add_argument("--search", metavar="TEXT",
|
|
1034
|
+
help="only cards whose title OR body contains TEXT (case-insensitive, "
|
|
1035
|
+
"matched by the gateway); prints how many cards were searched")
|
|
977
1036
|
cl.add_argument("--json", action="store_true"); _add_common(cl)
|
|
978
1037
|
cs = cards.add_parser("show", help="show one card"); cs.add_argument("id", type=int)
|
|
979
1038
|
cs.add_argument("--json", action="store_true"); _add_common(cs)
|
|
@@ -1034,7 +1093,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1034
1093
|
ck.add_argument("what", help="what is owed, in one line")
|
|
1035
1094
|
ck.add_argument("--timeout-hours", type=int, default=None,
|
|
1036
1095
|
help="hours until this obligation reads OVERDUE (default: none, "
|
|
1037
|
-
"which means it never goes red on its own)"
|
|
1096
|
+
"which means it never goes red on its own). This is not "
|
|
1097
|
+
"the due-date clock; that is --hours, and it starts on take")
|
|
1038
1098
|
ck.add_argument("--accept", default=None, metavar='"PASS = ... | FAIL = ..."',
|
|
1039
1099
|
help="the falsifiable check that closes this obligation "
|
|
1040
1100
|
"(#532). Omitting it mints an obligation that reads RED "
|
|
@@ -1057,7 +1117,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1057
1117
|
help="explicit edges by step name (added to the menu's); repeatable, "
|
|
1058
1118
|
"comma lists and the contract's `+step` spelling all accepted")
|
|
1059
1119
|
ck.add_argument("--hours", type=int, default=None,
|
|
1060
|
-
help="clock
|
|
1120
|
+
help="the due-date clock in hours. Starts on take, not now. "
|
|
1121
|
+
"Default is the menu step's hours. Up to 4x that value. "
|
|
1122
|
+
"Not the overdue timeout (--timeout-hours)")
|
|
1061
1123
|
ck.add_argument("--holder", dest="holder_flag", default=None, metavar="PEER|me",
|
|
1062
1124
|
help="who owes it: a peer (offered), `me` (open: a bound self-ask "
|
|
1063
1125
|
"is a take), or omit (requested: the eligible set is DM'd)")
|
|
@@ -1186,7 +1248,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1186
1248
|
oa.add_argument("--accept-json", default=None, metavar="'{...}'",
|
|
1187
1249
|
help="replace the check with a structured one (#1068); "
|
|
1188
1250
|
"mutually exclusive with --accept")
|
|
1189
|
-
oa.add_argument("--hours", type=int, default=None,
|
|
1251
|
+
oa.add_argument("--hours", type=int, default=None,
|
|
1252
|
+
help="the due-date clock, starts on take, up to 4x the menu value. "
|
|
1253
|
+
"Not the overdue timeout")
|
|
1190
1254
|
oa.add_argument("--needs-add", action="append", dest="needs_add", default=None,
|
|
1191
1255
|
metavar="STEP", help="add an edge (repeatable)")
|
|
1192
1256
|
oa.add_argument("--needs-remove", action="append", dest="needs_remove", default=None,
|
|
@@ -1338,7 +1402,15 @@ def run_board(argv: list[str]) -> int:
|
|
|
1338
1402
|
return 1
|
|
1339
1403
|
st, d = _http_get_json(_cards_list_url(gw, project=pid, stage=args.stage,
|
|
1340
1404
|
assignee=args.assignee,
|
|
1341
|
-
label=getattr(args, "label", None)
|
|
1405
|
+
label=getattr(args, "label", None),
|
|
1406
|
+
search=args.search), token)
|
|
1407
|
+
if (args.search is not None and st and 200 <= st < 300
|
|
1408
|
+
and not (isinstance(d, dict) and "searched" in d)):
|
|
1409
|
+
# A gateway older than #931 ignores q and returns EVERY card.
|
|
1410
|
+
print("swarph board: the gateway ignored --search (its reply has no "
|
|
1411
|
+
"`searched` count), so this list is NOT filtered; refusing to "
|
|
1412
|
+
"show it as a search result", file=sys.stderr)
|
|
1413
|
+
return 1
|
|
1342
1414
|
return _out(st, d, _format_cards, aj)
|
|
1343
1415
|
if args.command == "show":
|
|
1344
1416
|
st, d = _http_get_json(f"{gw}/board/cards/{args.id}", token)
|
|
@@ -5,10 +5,10 @@ Receives Claude Code's hook-input JSON on stdin, discovers the
|
|
|
5
5
|
applicable cell.yaml, and emits a hook-output JSON to stdout
|
|
6
6
|
containing the starter prompt as ``additionalContext``.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
``SWARPH_SPAWN=1`` means ``swarph spawn`` already put the starter on
|
|
9
|
+
``--append-system-prompt``. This hook then emits the ten and only the
|
|
10
|
+
ten, so the starter is not doubled. With no ten to read, that path is
|
|
11
|
+
still a no-op.
|
|
12
12
|
|
|
13
13
|
Cell discovery:
|
|
14
14
|
1. Cwd-local ``./cell.yaml`` (alpha #891 D3 auto-discovery)
|
|
@@ -39,13 +39,14 @@ from swarph_cli.cell import (
|
|
|
39
39
|
load_cell,
|
|
40
40
|
read_starter_prompt,
|
|
41
41
|
)
|
|
42
|
+
from swarph_cli.the_ten import session_block
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
def _emit_no_op() -> int:
|
|
45
46
|
"""Emit empty additionalContext + exit 0.
|
|
46
47
|
|
|
47
|
-
Used when there
|
|
48
|
-
|
|
48
|
+
Used when there is nothing to add: no starter and no ten, or
|
|
49
|
+
``SWARPH_SPAWN=1`` with no ten. Hook MUST NOT block session startup.
|
|
49
50
|
"""
|
|
50
51
|
output = {
|
|
51
52
|
"hookSpecificOutput": {
|
|
@@ -77,12 +78,34 @@ def _discover_cell_path() -> Path | None:
|
|
|
77
78
|
return None
|
|
78
79
|
|
|
79
80
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
def _emit(additional: str) -> int:
|
|
82
|
+
output: dict[str, Any] = {
|
|
83
|
+
"hookSpecificOutput": {
|
|
84
|
+
"hookEventName": "SessionStart",
|
|
85
|
+
"additionalContext": additional,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
print(json.dumps(output))
|
|
89
|
+
return 0
|
|
90
|
+
|
|
85
91
|
|
|
92
|
+
def _starter_text() -> str:
|
|
93
|
+
"""The cell's starter prompt, or ``""`` when there is nothing to add.
|
|
94
|
+
|
|
95
|
+
A missing cell, a malformed cell, or an unreadable starter is an
|
|
96
|
+
empty starter. The hook still emits the ten when that block exists.
|
|
97
|
+
"""
|
|
98
|
+
cell_path = _discover_cell_path()
|
|
99
|
+
if cell_path is None:
|
|
100
|
+
return ""
|
|
101
|
+
try:
|
|
102
|
+
cell = load_cell(cell_path)
|
|
103
|
+
return read_starter_prompt(cell) or ""
|
|
104
|
+
except CellError:
|
|
105
|
+
return ""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def run_hook_output(argv: list[str] | None = None) -> int:
|
|
86
109
|
# Drain hook-input from stdin (we don't actually use any field
|
|
87
110
|
# currently, but Claude Code's protocol expects us to consume it
|
|
88
111
|
# without blocking on TTY-detection edge cases).
|
|
@@ -92,32 +115,17 @@ def run_hook_output(argv: list[str] | None = None) -> int:
|
|
|
92
115
|
except Exception:
|
|
93
116
|
pass # swallow stdin-read errors; hook output is the only thing that matters
|
|
94
117
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
try:
|
|
100
|
-
cell = load_cell(cell_path)
|
|
101
|
-
except CellError:
|
|
102
|
-
# cell.yaml exists but is malformed — emit no-op + skip rather
|
|
103
|
-
# than blocking session startup. Operator can fix cell.yaml
|
|
104
|
-
# offline; meanwhile sessions still start.
|
|
105
|
-
return _emit_no_op()
|
|
118
|
+
# Commit-addressed. Empty when the repo or the ref is missing, and
|
|
119
|
+
# that emptiness must not change the starter-only behaviour.
|
|
120
|
+
block = session_block()
|
|
106
121
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return _emit_no_op()
|
|
122
|
+
# SWARPH_SPAWN=1 means the starter is already on --append-system-prompt.
|
|
123
|
+
# The ten is not part of that flag, so this path still emits the block
|
|
124
|
+
# and only the block.
|
|
125
|
+
if os.environ.get("SWARPH_SPAWN", "").strip() == "1":
|
|
126
|
+
return _emit(block) if block else _emit_no_op()
|
|
112
127
|
|
|
113
|
-
if
|
|
128
|
+
parts = [part for part in (_starter_text(), block) if part]
|
|
129
|
+
if not parts:
|
|
114
130
|
return _emit_no_op()
|
|
115
|
-
|
|
116
|
-
output: dict[str, Any] = {
|
|
117
|
-
"hookSpecificOutput": {
|
|
118
|
-
"hookEventName": "SessionStart",
|
|
119
|
-
"additionalContext": starter,
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
print(json.dumps(output))
|
|
123
|
-
return 0
|
|
131
|
+
return _emit("\n\n".join(parts))
|
|
@@ -111,7 +111,22 @@ def _run_create(args: argparse.Namespace) -> int:
|
|
|
111
111
|
status, payload = post_json(f"{base}/lanes", body, token)
|
|
112
112
|
if not _ok(status):
|
|
113
113
|
return _fail("create", status, payload)
|
|
114
|
-
|
|
114
|
+
# Card #949. The gateway persists the row at n=0 and starts workers only
|
|
115
|
+
# in scale_lane. Sending n on create is not enough on a server that
|
|
116
|
+
# ignores it, and a bare "created" line reads as if the workers exist.
|
|
117
|
+
# When the operator asked for workers, scale and report both results.
|
|
118
|
+
# n=0 stays a create, and the line says so.
|
|
119
|
+
if args.n and args.n > 0:
|
|
120
|
+
name = urllib.parse.quote(args.name, safe="")
|
|
121
|
+
scale_status, scale_payload = post_json(
|
|
122
|
+
f"{base}/lanes/{name}/scale", {"n": args.n}, token)
|
|
123
|
+
print(f"created lane {args.name}")
|
|
124
|
+
if not _ok(scale_status):
|
|
125
|
+
return _fail("scale", scale_status, scale_payload)
|
|
126
|
+
got = scale_payload.get("n", args.n) if isinstance(scale_payload, dict) else args.n
|
|
127
|
+
print(f"scaled {args.name} -> {got}")
|
|
128
|
+
return 0
|
|
129
|
+
print(f"created lane {args.name} n=0; run lane scale")
|
|
115
130
|
return 0
|
|
116
131
|
|
|
117
132
|
|
|
@@ -1511,6 +1511,15 @@ class TmuxSink(Sink):
|
|
|
1511
1511
|
print(f"[monitor] {self.name}: {reason} — deferring, zero keys",
|
|
1512
1512
|
flush=True)
|
|
1513
1513
|
return None
|
|
1514
|
+
# #1034: a pane in copy-mode (or view-mode) swallows the wake keys --
|
|
1515
|
+
# tmux runs them as copy-mode commands, the TUI never sees them, and the
|
|
1516
|
+
# capture still reads idle. Ask tmux, not the screen; if in a mode, send
|
|
1517
|
+
# nothing and keep the DM owed (the same owed-state #1010/#1126 use).
|
|
1518
|
+
if _pane_in_mode(self.target):
|
|
1519
|
+
self.deferred_reason = "pane in copy-mode"
|
|
1520
|
+
print(f"[monitor] {self.name}: pane in copy-mode — deferring, zero keys",
|
|
1521
|
+
flush=True)
|
|
1522
|
+
return None
|
|
1514
1523
|
# #620: a wake's job is done the moment a TURN is observed running —
|
|
1515
1524
|
# the cell is awake, whoever woke it (our injected wake fired, or
|
|
1516
1525
|
# the human typed). Both older clearing paths miss the zombie
|
|
@@ -2589,6 +2598,16 @@ def _opencode_turn_finished(lines: list[str]) -> bool:
|
|
|
2589
2598
|
# when the same pane is seen unchanged on a later poll at least this
|
|
2590
2599
|
# many seconds later (the monitor polls each minute, so about 2 polls).
|
|
2591
2600
|
_MAYBE_START_GRACE_S = 60
|
|
2601
|
+
# Card #1222: the grace above is reset every time the footer CHANGES ("a
|
|
2602
|
+
# progressing turn rewrites this region every poll"), which is right for a
|
|
2603
|
+
# booting turn — but a pane whose footer churns FOREVER (a recreated session
|
|
2604
|
+
# showing a bare header — the 2026-10-18 opencode deafness) re-arms first_seen
|
|
2605
|
+
# on every poll and NEVER matures to idle. wake_outstanding then stands
|
|
2606
|
+
# indefinitely: 8h of deafness, three offers never woke the cell. This ABSOLUTE
|
|
2607
|
+
# cap is anchored to the FIRST sighting of maybe-starting and is NOT reset by
|
|
2608
|
+
# churn, so a bare header past it reads idle and the wake can re-inject. 600 s
|
|
2609
|
+
# matches #616's _WAKE_STALE_S bound.
|
|
2610
|
+
_MAYBE_START_MAX_S = 600.0
|
|
2592
2611
|
|
|
2593
2612
|
|
|
2594
2613
|
def _opencode_pane_fingerprint(lines: list[str]) -> str:
|
|
@@ -2625,19 +2644,39 @@ def _opencode_poll_verdict(lines: list[str], now: float,
|
|
|
2625
2644
|
if last is None or _OPENCODE_DONE.search(last):
|
|
2626
2645
|
return "idle", fingerprint, None
|
|
2627
2646
|
first_seen = None
|
|
2647
|
+
capped_since = None
|
|
2628
2648
|
if isinstance(prior, dict):
|
|
2629
2649
|
seen = prior.get("first_seen")
|
|
2630
2650
|
if (prior.get("fingerprint") == fingerprint
|
|
2631
2651
|
and isinstance(seen, (int, float))
|
|
2632
2652
|
and not isinstance(seen, bool)):
|
|
2633
2653
|
first_seen = float(seen)
|
|
2654
|
+
cap = prior.get("capped_since")
|
|
2655
|
+
if isinstance(cap, (int, float)) and not isinstance(cap, bool):
|
|
2656
|
+
capped_since = float(cap)
|
|
2657
|
+
# Card #1222: the first sighting of maybe-starting is remembered in its own
|
|
2658
|
+
# field so FOOTER CHURN cannot reset the hard cap. A bare header that has
|
|
2659
|
+
# deferred for _MAYBE_START_MAX_S is aborting or idle, never still booting.
|
|
2660
|
+
if capped_since is None:
|
|
2661
|
+
capped_since = first_seen if first_seen is not None else now
|
|
2662
|
+
if now - capped_since >= _MAYBE_START_MAX_S:
|
|
2663
|
+
# Card #1222: past the hard cap the pane is idle, AND the record is KEPT
|
|
2664
|
+
# (non-None) so the caller does not drop the anchor. Dropping it would
|
|
2665
|
+
# make the very next poll a first sight -> maybe-starting again: the
|
|
2666
|
+
# deafness would sawtooth instead of ending.
|
|
2667
|
+
return "idle", fingerprint, {
|
|
2668
|
+
"fingerprint": fingerprint,
|
|
2669
|
+
"first_seen": first_seen if first_seen is not None else now,
|
|
2670
|
+
"capped_since": capped_since}
|
|
2634
2671
|
if first_seen is None:
|
|
2635
2672
|
return ("maybe-starting", fingerprint,
|
|
2636
|
-
{"fingerprint": fingerprint, "first_seen": now
|
|
2673
|
+
{"fingerprint": fingerprint, "first_seen": now,
|
|
2674
|
+
"capped_since": capped_since})
|
|
2637
2675
|
if now - first_seen >= _MAYBE_START_GRACE_S:
|
|
2638
2676
|
return "idle", fingerprint, None
|
|
2639
2677
|
return ("maybe-starting", fingerprint,
|
|
2640
|
-
{"fingerprint": fingerprint, "first_seen": first_seen
|
|
2678
|
+
{"fingerprint": fingerprint, "first_seen": first_seen,
|
|
2679
|
+
"capped_since": capped_since})
|
|
2641
2680
|
|
|
2642
2681
|
|
|
2643
2682
|
def _opencode_in_progress(target: str, ledger=None, now=None):
|
|
@@ -2659,7 +2698,10 @@ def _opencode_in_progress(target: str, ledger=None, now=None):
|
|
|
2659
2698
|
moment = time.time() if now is None else now
|
|
2660
2699
|
verdict, _fingerprint, record = _opencode_poll_verdict(
|
|
2661
2700
|
lines, moment, ledger.get("opencode_maybe_start"))
|
|
2662
|
-
|
|
2701
|
+
# Card #1222: keep the record whenever the verdict returns one. maybe-starting
|
|
2702
|
+
# carries the grace anchor; the cap-‘idle’ carries the anchor too so the cap
|
|
2703
|
+
# stays anchored across polls. running / grace-idle return None and clear it.
|
|
2704
|
+
if record is not None:
|
|
2663
2705
|
ledger["opencode_maybe_start"] = record
|
|
2664
2706
|
else:
|
|
2665
2707
|
ledger.pop("opencode_maybe_start", None)
|
|
@@ -3192,6 +3234,28 @@ def _composer_state(target: str) -> Optional[str]:
|
|
|
3192
3234
|
return "busy"
|
|
3193
3235
|
|
|
3194
3236
|
|
|
3237
|
+
def _pane_in_mode(target: str) -> bool:
|
|
3238
|
+
"""True if the pane is in a tmux MODE (copy-mode / view-mode), card #1034.
|
|
3239
|
+
|
|
3240
|
+
In copy-mode the TUI receives nothing: a keystroke is a copy-mode command,
|
|
3241
|
+
the captured screen still reads idle (it shows the scrolled view), and the
|
|
3242
|
+
monitor would mark the DM delivered while the cell never woke -- "delivered
|
|
3243
|
+
is not submitted" with a new cause. Ask tmux, not the screen:
|
|
3244
|
+
#{pane_in_mode} is 1 in a mode. Unknown (tmux cannot answer) reads as NOT in
|
|
3245
|
+
mode, matching the pre-fix behaviour; the composer gate already fails closed
|
|
3246
|
+
on an unreadable pane.
|
|
3247
|
+
"""
|
|
3248
|
+
try:
|
|
3249
|
+
r = subprocess.run(
|
|
3250
|
+
["tmux", "display-message", "-p", "-t", target, "#{pane_in_mode}"],
|
|
3251
|
+
capture_output=True, timeout=5,
|
|
3252
|
+
text=True, encoding="utf-8", errors="replace",
|
|
3253
|
+
)
|
|
3254
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
3255
|
+
return False
|
|
3256
|
+
return r.returncode == 0 and (r.stdout or "").strip() == "1"
|
|
3257
|
+
|
|
3258
|
+
|
|
3195
3259
|
def _tmux_session_created(target: str) -> Optional[float]:
|
|
3196
3260
|
"""Epoch seconds the target's tmux SESSION was created, or None.
|
|
3197
3261
|
|
|
@@ -3726,6 +3790,22 @@ def _monitor_iteration(state: MonitorState, *, poll_channels: bool = True) -> No
|
|
|
3726
3790
|
_write_cursor_atomic(state.heartbeat_path, {
|
|
3727
3791
|
"ts": time.time(), "iterations": state.iterations, "pid": os.getpid()})
|
|
3728
3792
|
|
|
3793
|
+
# Card #513: detect a SECOND live session under this identity and raise ONE
|
|
3794
|
+
# alarm row. Per-cell, IN the monitor (not the send path). It NEVER kills
|
|
3795
|
+
# anything and never raises out of the loop. Throttled inside.
|
|
3796
|
+
_maybe_duplicate_check(state)
|
|
3797
|
+
|
|
3798
|
+
|
|
3799
|
+
def _maybe_duplicate_check(state: MonitorState) -> None:
|
|
3800
|
+
"""Card #513. Enumerate at most once every duplicate_session.CHECK_EVERY ticks
|
|
3801
|
+
(the check spawns `ps`), and never on the first tick so an ordinary monitor
|
|
3802
|
+
test drives one clean iteration. duplicate_session.check swallows its own
|
|
3803
|
+
errors, so this can never break delivery. It NEVER kills anything."""
|
|
3804
|
+
from swarph_cli import duplicate_session
|
|
3805
|
+
if state.iterations % duplicate_session.CHECK_EVERY != 0:
|
|
3806
|
+
return
|
|
3807
|
+
duplicate_session.check(state)
|
|
3808
|
+
|
|
3729
3809
|
|
|
3730
3810
|
def _monitor_loop(state: MonitorState) -> int:
|
|
3731
3811
|
print(
|
|
@@ -297,12 +297,18 @@ def _local_tmux_peers() -> list:
|
|
|
297
297
|
|
|
298
298
|
|
|
299
299
|
def _capture_peer_pane(peer: str):
|
|
300
|
-
"""One read-only
|
|
301
|
-
|
|
300
|
+
"""One read-only capture of the peer's pane, including scrollback.
|
|
301
|
+
|
|
302
|
+
Card #1228 F2. `capture-pane -p` alone is the visible screen. A Claude
|
|
303
|
+
Code /usage stdout display scrolls off it, so the multi-window parser
|
|
304
|
+
never sees the form it knows. `-S -` starts at the top of history.
|
|
305
|
+
Never sends keys (ruling 6); None on any failure — the peer reads unknown.
|
|
306
|
+
"""
|
|
302
307
|
try:
|
|
303
|
-
out = subprocess.run(
|
|
304
|
-
|
|
305
|
-
|
|
308
|
+
out = subprocess.run(
|
|
309
|
+
["tmux", "capture-pane", "-p", "-S", "-", "-t", peer],
|
|
310
|
+
capture_output=True, timeout=10, text=True,
|
|
311
|
+
encoding="utf-8", errors="replace")
|
|
306
312
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
307
313
|
return None
|
|
308
314
|
if out.returncode != 0:
|
|
@@ -57,10 +57,11 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
57
57
|
create.add_argument(
|
|
58
58
|
"--deliver",
|
|
59
59
|
choices=["wake", "dm"],
|
|
60
|
-
default="
|
|
61
|
-
help="delivery mode:
|
|
62
|
-
"
|
|
63
|
-
"cell
|
|
60
|
+
default="dm",
|
|
61
|
+
help="delivery mode: dm (default) schedules a deferred DM to the target "
|
|
62
|
+
"peer and works for any cell; wake types into a host-local tmux pane "
|
|
63
|
+
"and the gateway refuses it for any cell off lab's box, so pass "
|
|
64
|
+
"--deliver wake only when you mean it (card #1081)",
|
|
64
65
|
)
|
|
65
66
|
add_common_args(create)
|
|
66
67
|
|
|
@@ -196,11 +197,15 @@ def _run_create(args: argparse.Namespace) -> int:
|
|
|
196
197
|
body["out_channel"] = args.out_channel
|
|
197
198
|
if args.min_interval is not None:
|
|
198
199
|
body["min_interval_sec"] = args.min_interval
|
|
199
|
-
|
|
200
|
-
body["deliver"] = "dm"
|
|
200
|
+
body["deliver"] = args.deliver
|
|
201
201
|
status, payload = post_json(f"{base}/scheduled-events", body, token)
|
|
202
202
|
if not _ok(status):
|
|
203
|
-
|
|
203
|
+
rc = _fail("create", status, payload)
|
|
204
|
+
if status == 403 and "operator-gated" in str(payload.get("detail", "")):
|
|
205
|
+
print("hint: a wake-delivery create is refused as operator-gated for any "
|
|
206
|
+
"cell off lab's box. Use `--deliver dm` (the default) to schedule a "
|
|
207
|
+
"deferred DM to the target peer instead.", file=sys.stderr)
|
|
208
|
+
return rc
|
|
204
209
|
print(f"scheduled {args.name}")
|
|
205
210
|
return 0
|
|
206
211
|
|