swarph-cli 0.72.28__tar.gz → 0.72.30__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.30}/PKG-INFO +2 -2
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/README.md +1 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/pyproject.toml +4 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/claude_cli.py +5 -2
- swarph_cli-0.72.30/src/swarph_cli/bench/prices.py +284 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/refresh_prices.py +40 -7
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/board.py +84 -12
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_output.py +45 -37
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/lane.py +16 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/mesh.py +83 -3
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/roster.py +11 -5
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/schedule.py +12 -7
- swarph_cli-0.72.30/src/swarph_cli/duplicate_session.py +284 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/wake_watchdog.py +251 -26
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/stall_alert.py +35 -63
- swarph_cli-0.72.30/src/swarph_cli/the_ten.py +223 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30/src/swarph_cli.egg-info}/PKG-INFO +2 -2
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/SOURCES.txt +12 -0
- swarph_cli-0.72.30/tests/test_1034_copy_mode.py +61 -0
- swarph_cli-0.72.30/tests/test_1222_maybe_start_cap.py +126 -0
- swarph_cli-0.72.30/tests/test_1228_quota_reach.py +53 -0
- swarph_cli-0.72.30/tests/test_1243_the_ten.py +274 -0
- swarph_cli-0.72.30/tests/test_1248_ship_price_table.py +157 -0
- swarph_cli-0.72.30/tests/test_1250_price_cache.py +274 -0
- swarph_cli-0.72.30/tests/test_1256_commander_wake.py +172 -0
- swarph_cli-0.72.30/tests/test_513_duplicate_session.py +215 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_wake_watchdog.py +45 -17
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_739_wake_writer.py +27 -1
- swarph_cli-0.72.30/tests/test_931_board_search.py +88 -0
- swarph_cli-0.72.30/tests/test_935_deadline_clock.py +55 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_960_channel.py +24 -5
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_claude_cli.py +15 -4
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_command.py +4 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_prices.py +6 -3
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_copilot_pane_reader.py +5 -1
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hook_output.py +3 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_lane_command.py +80 -3
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_schedule_command.py +35 -12
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_stall_alert.py +101 -62
- swarph_cli-0.72.28/src/swarph_cli/bench/prices.py +0 -103
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/LICENSE +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/setup.cfg +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/launchd_render.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/quota_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/backend.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1068_structured_accept_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1071_quota_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1083_openai_decisions.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1144_cell_yaml_github.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1197_commander_taps.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1203_swarph_me_refusal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1206_offer_question.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1214_egress.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1219_egress_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_1221_quota_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_launchd_units.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_main.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_security.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.28 → swarph_cli-0.72.30}/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.30
|
|
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.30"
|
|
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:
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""The auto-refreshed LLM LIST-price table (spec §4) — LIFTED from the
|
|
2
|
+
reference lab's already-tested ``workers/llm_prices.py`` (adapted imports/
|
|
3
|
+
cache path only; lookup semantics, fallback table, and substring-order bug
|
|
4
|
+
guard are unchanged). Do NOT reinvent this — see
|
|
5
|
+
``docs/2026-07-24-model-showdown-findings.md`` shipped-alongside note: it
|
|
6
|
+
already caught a real ~5x under-count on gemini-2.5-flash-lite.
|
|
7
|
+
|
|
8
|
+
The LIST-price BASE is auto-refreshed from the community-maintained LiteLLM
|
|
9
|
+
repo (:mod:`swarph_cli.bench.refresh_prices` -> a local JSON cache; TODO wire
|
|
10
|
+
a weekly cron the way the reference lab does), so new models are never
|
|
11
|
+
silently mis-priced by a stale hardcoded bucket.
|
|
12
|
+
|
|
13
|
+
Public API:
|
|
14
|
+
lookup(model) -> (input_$/Mtok, output_$/Mtok) # never raises
|
|
15
|
+
cost_usd(model, tokens_in, tokens_thought, tokens_out) -> $
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import tempfile
|
|
24
|
+
|
|
25
|
+
log = logging.getLogger("swarph.bench.prices")
|
|
26
|
+
|
|
27
|
+
# card #1250. A cache older than this is refreshed on load(). Exactly this
|
|
28
|
+
# age is still fresh: "older than", not "at least".
|
|
29
|
+
PRICE_CACHE_TTL_S = 7 * 24 * 60 * 60
|
|
30
|
+
# Both load()-triggered fetches. refresh_prices.main() keeps its own 30s.
|
|
31
|
+
LOAD_FETCH_TIMEOUT_S = 5
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _default_cache_path() -> str:
|
|
35
|
+
"""User cache, never the installed package.
|
|
36
|
+
|
|
37
|
+
card #1248: a table written next to this file lands in site-packages and
|
|
38
|
+
goes stale in the wheel. The cache lives under XDG_CACHE_HOME (or
|
|
39
|
+
~/.cache). SWARPH_BENCH_PRICE_CACHE overrides it.
|
|
40
|
+
"""
|
|
41
|
+
override = os.environ.get("SWARPH_BENCH_PRICE_CACHE")
|
|
42
|
+
if override:
|
|
43
|
+
return override
|
|
44
|
+
base = os.environ.get("XDG_CACHE_HOME") or os.path.join(
|
|
45
|
+
os.path.expanduser("~"), ".cache")
|
|
46
|
+
return os.path.join(base, "swarph", "llm_prices.json")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_CACHE_PATH = _default_cache_path()
|
|
50
|
+
_MAP: dict | None = None
|
|
51
|
+
# lookup() warns once per model per process when a loaded table has no row.
|
|
52
|
+
_FALLBACK_WARNED: set[str] = set()
|
|
53
|
+
|
|
54
|
+
# Reconciled fallbacks ($/Mtok input, output) — used ONLY when a model isn't in
|
|
55
|
+
# the refreshed cache. The cache is the real source; these are the safety net.
|
|
56
|
+
_FALLBACK = {
|
|
57
|
+
"pro": (1.25, 10.0),
|
|
58
|
+
"lite": (0.019, 0.075),
|
|
59
|
+
"flash": (0.30, 2.50),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _now() -> datetime.datetime:
|
|
64
|
+
return datetime.datetime.now(datetime.timezone.utc)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _read_payload() -> dict | None:
|
|
68
|
+
try:
|
|
69
|
+
with open(_CACHE_PATH) as f:
|
|
70
|
+
data = json.load(f) or {}
|
|
71
|
+
return data if isinstance(data, dict) else None
|
|
72
|
+
except Exception:
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _prices_of(payload: dict | None) -> dict:
|
|
77
|
+
if not payload:
|
|
78
|
+
return {}
|
|
79
|
+
prices = payload.get("prices")
|
|
80
|
+
return prices if isinstance(prices, dict) else {}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _generated_at(payload: dict | None) -> datetime.datetime | None:
|
|
84
|
+
"""UTC timestamp, or None when the field is missing or unparseable."""
|
|
85
|
+
if not payload:
|
|
86
|
+
return None
|
|
87
|
+
raw = payload.get("generated_at")
|
|
88
|
+
if not isinstance(raw, str) or not raw.strip():
|
|
89
|
+
return None
|
|
90
|
+
try:
|
|
91
|
+
parsed = datetime.datetime.fromisoformat(raw.strip())
|
|
92
|
+
except ValueError:
|
|
93
|
+
return None
|
|
94
|
+
if parsed.tzinfo is None:
|
|
95
|
+
parsed = parsed.replace(tzinfo=datetime.timezone.utc)
|
|
96
|
+
return parsed
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _age_days_label(generated: datetime.datetime | None) -> str:
|
|
100
|
+
if generated is None:
|
|
101
|
+
return "unknown"
|
|
102
|
+
return str(max(0, int((_now() - generated).total_seconds() // 86400)))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _read_cache() -> dict:
|
|
106
|
+
return _prices_of(_read_payload())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _write_cache(prices: dict) -> None:
|
|
110
|
+
"""Atomic write of the user cache. Never writes inside this package."""
|
|
111
|
+
dest = os.path.abspath(_CACHE_PATH)
|
|
112
|
+
pkg = os.path.dirname(os.path.abspath(__file__))
|
|
113
|
+
if dest == pkg or dest.startswith(pkg + os.sep):
|
|
114
|
+
log.warning(
|
|
115
|
+
"refusing to write the price table into the package tree (%s)", dest)
|
|
116
|
+
return
|
|
117
|
+
payload = {
|
|
118
|
+
"generated_at": _now().isoformat(),
|
|
119
|
+
"source": "BerriAI/litellm model_prices_and_context_window.json",
|
|
120
|
+
"count": len(prices),
|
|
121
|
+
"prices": prices,
|
|
122
|
+
}
|
|
123
|
+
try:
|
|
124
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
125
|
+
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
|
|
126
|
+
with os.fdopen(fd, "w") as f:
|
|
127
|
+
json.dump(payload, f)
|
|
128
|
+
os.replace(tmp, dest)
|
|
129
|
+
except OSError as e:
|
|
130
|
+
log.warning("llm price cache not written (%s)", e)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _fetch_into_cache() -> dict:
|
|
134
|
+
"""Fill the user cache from the price source. Never writes inside
|
|
135
|
+
this package (that path is site-packages once the wheel is installed).
|
|
136
|
+
|
|
137
|
+
This is the empty-cache path. A stale cache goes through
|
|
138
|
+
``refresh_prices.guard`` in ``load()`` before it is written.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
from .refresh_prices import fetch
|
|
142
|
+
fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
log.warning("llm price fetch failed (%s)", e)
|
|
145
|
+
return {}
|
|
146
|
+
if not fresh:
|
|
147
|
+
return {}
|
|
148
|
+
_write_cache(fresh)
|
|
149
|
+
return fresh
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _keep_stale(prior: dict, generated: datetime.datetime | None) -> dict:
|
|
153
|
+
log.warning(
|
|
154
|
+
"llm price refresh failed — keeping the stale table, %s days old",
|
|
155
|
+
_age_days_label(generated),
|
|
156
|
+
)
|
|
157
|
+
return prior
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def load(force: bool = False) -> dict:
|
|
161
|
+
"""The cached ``{normalized_model: {"in": $/Mtok, "out": $/Mtok}}`` map.
|
|
162
|
+
Cached in-process; ``force=True`` re-reads. Never raises.
|
|
163
|
+
|
|
164
|
+
A missing cache is fetched once into the user cache. If that also fails,
|
|
165
|
+
the empty table is a WARNING, not a DEBUG line: every later lookup is
|
|
166
|
+
then a built-in fallback (card #1248).
|
|
167
|
+
|
|
168
|
+
A cache older than ``PRICE_CACHE_TTL_S``, or one whose ``generated_at``
|
|
169
|
+
is missing or unparseable, is refreshed through ``refresh_prices.guard``.
|
|
170
|
+
A failed refresh keeps the stale table (card #1250).
|
|
171
|
+
"""
|
|
172
|
+
global _MAP
|
|
173
|
+
if _MAP is not None and not force:
|
|
174
|
+
return _MAP
|
|
175
|
+
payload = _read_payload()
|
|
176
|
+
prior = _prices_of(payload)
|
|
177
|
+
generated = _generated_at(payload)
|
|
178
|
+
if prior and generated is not None:
|
|
179
|
+
age = (_now() - generated).total_seconds()
|
|
180
|
+
if age <= PRICE_CACHE_TTL_S:
|
|
181
|
+
_MAP = prior
|
|
182
|
+
return _MAP
|
|
183
|
+
if prior:
|
|
184
|
+
table = _refresh_stale(prior, generated)
|
|
185
|
+
else:
|
|
186
|
+
table = _fetch_into_cache()
|
|
187
|
+
if not table:
|
|
188
|
+
log.warning(
|
|
189
|
+
"llm price table unavailable — pricing from the built-in "
|
|
190
|
+
"fallbacks (pro/lite/flash). No cache at %s and the price "
|
|
191
|
+
"source could not be fetched.",
|
|
192
|
+
_CACHE_PATH,
|
|
193
|
+
)
|
|
194
|
+
_MAP = {}
|
|
195
|
+
return _MAP
|
|
196
|
+
_MAP = table
|
|
197
|
+
return _MAP
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _refresh_stale(prior: dict, generated: datetime.datetime | None) -> dict:
|
|
201
|
+
"""Refetch a stale table. The guard runs only on this path, and the
|
|
202
|
+
guarded-count WARNING is logged only here — a first fill has no prior
|
|
203
|
+
to guard."""
|
|
204
|
+
try:
|
|
205
|
+
from .refresh_prices import fetch, guard
|
|
206
|
+
fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
log.warning("llm price fetch failed (%s)", e)
|
|
209
|
+
return _keep_stale(prior, generated)
|
|
210
|
+
if not fresh:
|
|
211
|
+
return _keep_stale(prior, generated)
|
|
212
|
+
merged, flagged = guard(fresh, prior)
|
|
213
|
+
log.warning(
|
|
214
|
+
"llm price refresh guarded %d change(s) against the stale table",
|
|
215
|
+
len(flagged),
|
|
216
|
+
)
|
|
217
|
+
_write_cache(merged)
|
|
218
|
+
return merged
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _norm(model: str) -> str:
|
|
222
|
+
"""Strip provider prefix (gemini/, vertex_ai/, ...) + lowercase."""
|
|
223
|
+
return (model or "").split("/")[-1].strip().lower()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def lookup(model: str) -> tuple[float, float]:
|
|
227
|
+
"""(input_$/Mtok, output_$/Mtok) for ``model``. Exact cache hit ->
|
|
228
|
+
substring fallback (lite before flash so 'flash-lite' resolves as lite —
|
|
229
|
+
order-sensitive, don't reorder). Never raises."""
|
|
230
|
+
m = _norm(model)
|
|
231
|
+
prices = load()
|
|
232
|
+
p = prices.get(m)
|
|
233
|
+
if p and "in" in p and "out" in p:
|
|
234
|
+
return (float(p["in"]), float(p["out"]))
|
|
235
|
+
# substring fallback — ORDER MATTERS: lite is a substring of flash-lite
|
|
236
|
+
# and must win over flash.
|
|
237
|
+
if "lite" in m:
|
|
238
|
+
chosen, label = _FALLBACK["lite"], "lite"
|
|
239
|
+
elif "pro" in m:
|
|
240
|
+
chosen, label = _FALLBACK["pro"], "pro"
|
|
241
|
+
else:
|
|
242
|
+
chosen, label = _FALLBACK["flash"], "flash"
|
|
243
|
+
# A loaded table that lacks this model is the #1248 defect class for a
|
|
244
|
+
# model upstream does not list yet. Once per model per process. An empty
|
|
245
|
+
# table already warned in load(); don't warn again on every lookup.
|
|
246
|
+
if prices and m not in _FALLBACK_WARNED:
|
|
247
|
+
_FALLBACK_WARNED.add(m)
|
|
248
|
+
log.warning(
|
|
249
|
+
"llm price for %s is not in the loaded table — using the %s "
|
|
250
|
+
"fallback %s",
|
|
251
|
+
m, label, chosen,
|
|
252
|
+
)
|
|
253
|
+
return chosen
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def is_known(model_id: str) -> bool:
|
|
257
|
+
"""True if ``model_id`` has an exact hit in the refreshed cache (as
|
|
258
|
+
opposed to resolving via the substring fallback)."""
|
|
259
|
+
p = load().get(_norm(model_id))
|
|
260
|
+
return bool(p and "in" in p and "out" in p)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def cost_usd(model_id: str, tokens_in: int, tokens_thought: int, tokens_out: int) -> float:
|
|
264
|
+
"""Metered $ for one call: in-tokens at the input price, (thought + out)
|
|
265
|
+
tokens at the output price (mirrors the reference ``metered_usd()``)."""
|
|
266
|
+
price_in, price_out = lookup(model_id)
|
|
267
|
+
thought = tokens_thought or 0
|
|
268
|
+
return (tokens_in * price_in + (thought + tokens_out) * price_out) / 1_000_000
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def all_cached(force: bool = False) -> dict[str, tuple[float, float]]:
|
|
272
|
+
"""Every ``{model_id: (in, out)}`` currently in the refreshed cache — the
|
|
273
|
+
full LiteLLM-sourced price list, not just the models ``bench`` happens to
|
|
274
|
+
price. Exposed so the cache is a reusable mesh asset (e.g. a
|
|
275
|
+
cost-governor/ledger can read the same cache via ``swarph bench prices``)
|
|
276
|
+
rather than plumbing private to the runner."""
|
|
277
|
+
return {m: (float(v["in"]), float(v["out"])) for m, v in load(force=force).items()
|
|
278
|
+
if isinstance(v, dict) and "in" in v and "out" in v}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def fallback_buckets() -> dict[str, tuple[float, float]]:
|
|
282
|
+
"""The 3 built-in substring-fallback buckets (pro/lite/flash), for
|
|
283
|
+
display when the cache is empty/stale."""
|
|
284
|
+
return dict(_FALLBACK)
|
|
@@ -19,6 +19,10 @@ not wired to any scheduler in this PR, just the ported fetch+guard logic.
|
|
|
19
19
|
|
|
20
20
|
Network call — never exercised in the offline test suite (mocked or skipped).
|
|
21
21
|
Read-only wrt the rest of swarph-cli: only ever writes the price cache.
|
|
22
|
+
|
|
23
|
+
``SWARPH_BENCH_PRICE_URL`` overrides the source URL. Only the ``https``,
|
|
24
|
+
``http`` and ``file`` schemes are accepted (card #1250). ``load()`` passes a
|
|
25
|
+
5s timeout; this CLI keeps 30s.
|
|
22
26
|
"""
|
|
23
27
|
from __future__ import annotations
|
|
24
28
|
|
|
@@ -26,22 +30,45 @@ import json
|
|
|
26
30
|
import os
|
|
27
31
|
import sys
|
|
28
32
|
import tempfile
|
|
33
|
+
import urllib.parse
|
|
29
34
|
import urllib.request
|
|
30
35
|
from datetime import datetime, timezone
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
def _cache_path() -> str:
|
|
38
|
+
"""Read at call time so a monkeypatch of prices._CACHE_PATH is honored,
|
|
39
|
+
and so a refresh never keeps an import-time path inside the package."""
|
|
40
|
+
from .prices import _CACHE_PATH
|
|
41
|
+
return _CACHE_PATH
|
|
33
42
|
|
|
34
43
|
LITELLM_URL = ("https://raw.githubusercontent.com/BerriAI/litellm/main/"
|
|
35
44
|
"model_prices_and_context_window.json")
|
|
36
45
|
GUARD_RATIO = 3.0 # reject a new price >3x or <1/3x the prior cached value
|
|
46
|
+
# The CLI's own fetch. load() passes prices.LOAD_FETCH_TIMEOUT_S instead.
|
|
47
|
+
CLI_FETCH_TIMEOUT_S = 30
|
|
48
|
+
_ALLOWED_SOURCE_SCHEMES = frozenset({"https", "http", "file"})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def price_source_url() -> str:
|
|
52
|
+
"""The JSON URL ``fetch`` reads.
|
|
53
|
+
|
|
54
|
+
``SWARPH_BENCH_PRICE_URL`` overrides ``LITELLM_URL``. Schemes other than
|
|
55
|
+
https, http and file are refused.
|
|
56
|
+
"""
|
|
57
|
+
raw = (os.environ.get("SWARPH_BENCH_PRICE_URL") or LITELLM_URL).strip()
|
|
58
|
+
scheme = urllib.parse.urlparse(raw).scheme.lower()
|
|
59
|
+
if scheme not in _ALLOWED_SOURCE_SCHEMES:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"SWARPH_BENCH_PRICE_URL scheme {scheme!r} is not allowed "
|
|
62
|
+
"(https, http, file)")
|
|
63
|
+
return raw
|
|
37
64
|
|
|
38
65
|
|
|
39
66
|
def _norm(key: str) -> str:
|
|
40
67
|
return key.split("/")[-1].strip().lower()
|
|
41
68
|
|
|
42
69
|
|
|
43
|
-
def fetch() -> dict:
|
|
44
|
-
raw = urllib.request.urlopen(
|
|
70
|
+
def fetch(timeout: float = CLI_FETCH_TIMEOUT_S) -> dict:
|
|
71
|
+
raw = urllib.request.urlopen(price_source_url(), timeout=timeout).read()
|
|
45
72
|
d = json.loads(raw)
|
|
46
73
|
out: dict[str, dict] = {}
|
|
47
74
|
for key, v in d.items():
|
|
@@ -59,7 +86,7 @@ def fetch() -> dict:
|
|
|
59
86
|
|
|
60
87
|
def load_prior() -> dict:
|
|
61
88
|
try:
|
|
62
|
-
with open(
|
|
89
|
+
with open(_cache_path()) as f:
|
|
63
90
|
return (json.load(f) or {}).get("prices", {}) or {}
|
|
64
91
|
except Exception:
|
|
65
92
|
return {}
|
|
@@ -98,11 +125,17 @@ def main() -> int:
|
|
|
98
125
|
payload = {"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
99
126
|
"source": "BerriAI/litellm model_prices_and_context_window.json",
|
|
100
127
|
"count": len(merged), "prices": merged}
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
dest = _cache_path()
|
|
129
|
+
pkg = os.path.dirname(os.path.abspath(os.path.join(os.path.dirname(__file__), "prices.py")))
|
|
130
|
+
if os.path.abspath(dest).startswith(pkg + os.sep):
|
|
131
|
+
print(f"refresh_prices: refusing to write into the package tree ({dest})",
|
|
132
|
+
file=sys.stderr)
|
|
133
|
+
return 1
|
|
134
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
135
|
+
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
|
|
103
136
|
with os.fdopen(fd, "w") as f:
|
|
104
137
|
json.dump(payload, f, indent=2, sort_keys=True)
|
|
105
|
-
os.replace(tmp,
|
|
138
|
+
os.replace(tmp, dest)
|
|
106
139
|
print(f"refresh_prices: {len(merged)} models cached "
|
|
107
140
|
f"({len(fresh)} fetched, {len(merged) - len(fresh)} prior-only kept). "
|
|
108
141
|
f"{len(flagged)} guarded.")
|
|
@@ -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)
|