ctrlrelay 0.18.0__tar.gz → 0.19.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CHANGELOG.md +77 -1
- ctrlrelay-0.19.1/PKG-INFO +164 -0
- ctrlrelay-0.19.1/README.md +123 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/config/orchestrator.yaml.example +17 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/_config.yml +7 -2
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/adding-a-machine.md +9 -6
- ctrlrelay-0.19.1/docs/architecture.md +231 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/bridge.md +115 -87
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/cli.md +32 -21
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/configuration.md +131 -15
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/development.md +18 -15
- ctrlrelay-0.19.1/docs/feedback-loop.md +209 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/getting-started.md +32 -21
- ctrlrelay-0.19.1/docs/index.md +89 -0
- ctrlrelay-0.19.1/docs/llms.txt +36 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/operations.md +17 -10
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/personalization.md +142 -10
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/pyproject.toml +1 -1
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/cli.py +47 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/code_review.py +16 -2
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/config.py +190 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/worktree.py +280 -34
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/manager.py +470 -6
- ctrlrelay-0.19.1/src/ctrlrelay/personalization/merge.py +568 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/dev.py +625 -261
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/_publish_fakes.py +2 -0
- ctrlrelay-0.19.1/tests/test_dev_step_switches.py +1004 -0
- ctrlrelay-0.19.1/tests/test_dev_work_steps.py +214 -0
- ctrlrelay-0.19.1/tests/test_personalization_merge.py +1626 -0
- ctrlrelay-0.19.1/tests/test_relay_git_263.py +621 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_review_copy.py +24 -9
- ctrlrelay-0.18.0/PKG-INFO +0 -263
- ctrlrelay-0.18.0/README.md +0 -222
- ctrlrelay-0.18.0/docs/architecture.md +0 -222
- ctrlrelay-0.18.0/docs/feedback-loop.md +0 -197
- ctrlrelay-0.18.0/docs/index.md +0 -62
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/dependabot.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/build.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/cla.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/pages.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/publish.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.github/workflows/test.yml +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/.gitignore +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CODE_OF_CONDUCT.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/CONTRIBUTING.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/LICENSE +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/SECURITY.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/Gemfile +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/registered-mode.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/superpowers/plans/2026-10-30-status-page.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/docs/superpowers/specs/2026-10-30-status-page-design.md +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/__main__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/factory.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/handler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/mattermost_handler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/node_tag.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/protocol.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/server.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/bridge/telegram_handler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/agent_env.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/archived.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/audit.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/binaries.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/checkpoint.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/claims.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/dispatch_health.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/dispatcher.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/events.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/github.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/github_budget.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/heartbeat.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/job_claims.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/network.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/node_check.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/obs.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/poller.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/pr_verifier.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/pr_watcher.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/publish.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/question_expiry.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/redact.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/scheduler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/state.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/core/usage.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/dashboard/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/dashboard/client.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/gh_protocol.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/install.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/personalization/paths.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/base.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/post_merge.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/secops.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/pipelines/task.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/setup.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/global-CLAUDE.md.snippet +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/bridge.plist.template +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/launchd/poller.plist.template +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/bridge.service.template +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/poller.service.template +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/templates/systemd/web.service.template +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/base.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/file_mock.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/transports/socket_client.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/fleet.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/health.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/links.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/numbers.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/server.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/static/index.html +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/status.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/transcript.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/src/ctrlrelay/web/waiting.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/__init__.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/_poller_fakes.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/conftest.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/gh_search_issues.json +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/stream_json_rate_limit.jsonl +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/fixtures/transcript_sample.jsonl +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_backend_wiring.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_no_keys.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_routing.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_agent_sandbox.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_archived.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_audit.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_binary_resolution.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_protocol.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_server.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_bridge_start_command.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_chat_machines.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_checkpoint.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_claim_reconciliation.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_ci_wait.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_dev.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_repos.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_secops.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_start.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_cli_version.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_code_review.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_config.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dashboard_client.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dev_integration.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dev_pipeline.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatch_health.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatch_outcome_hook.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_dispatcher.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_docs_site.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_event_emission.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_events.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_fleet.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_gh_protocol.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_github.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_github_budget.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_install.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_issue_outcome_announcement.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_job_claims.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_mattermost_handler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_network.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_node_check.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_node_claims.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_notify_transport.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_obs.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_opencode_adapter.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_personalization.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pipeline_base.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_poller.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_poller_search.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_post_merge.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pr_verifier.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pr_watcher.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_pre_dispatch_retry.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_publish_failure_retry.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_question_backoff.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_question_expiry.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_redact.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_relay_alert_off_lock.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_resume_prompt_contract.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_scheduler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_secops_integration.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_secops_pipeline.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_setup.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_state.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_task_pipeline.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_telegram_handler.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_transport.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_transport_resolution.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_unreviewed_pr_announcement.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_usage.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_health.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_links.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_numbers.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_server.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_status.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_transcript.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_web_waiting.py +0 -0
- {ctrlrelay-0.18.0 → ctrlrelay-0.19.1}/tests/test_worktree.py +0 -0
|
@@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.19.1] - 2026-11-03
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A synced or live `settings.json` nested more than 64 levels deep is now
|
|
15
|
+
refused right after it is read. On Python 3.14 the parser accepts about
|
|
16
|
+
65,000 levels, and writing that back out used all the memory on the
|
|
17
|
+
machine instead of failing (#313).
|
|
18
|
+
|
|
19
|
+
## [0.19.0] - 2026-11-03
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `repos[].steps` sets each dev pipeline work step (`code`, `publish`,
|
|
24
|
+
`verify`, `review`) to `off`, `on` or `required`, and `repos[].max_fix_attempts`
|
|
25
|
+
caps the CI fix rounds. `required` stops the run on a failure, including a
|
|
26
|
+
reviewer that cannot run or CI that never finished; the PR stays open and the
|
|
27
|
+
run is announced as failed. Unset keys keep today's behaviour, and settings
|
|
28
|
+
the pipeline could not honour (`code: off`, `verify: on`, a review switch
|
|
29
|
+
that disagrees with `code_review.method`) are refused at load (#226).
|
|
30
|
+
- `mode: merge` for a personalization path keeps the live file real and, on
|
|
31
|
+
pull, writes only the top-level JSON keys listed in the local `merge_keys`,
|
|
32
|
+
so `~/.claude/settings.json` can carry fleet policy while the keys the app
|
|
33
|
+
writes on its own stay on that machine and no longer dirty the checkout. The
|
|
34
|
+
synced file cannot widen the list; it must be a JSON object without
|
|
35
|
+
duplicate keys; links (at the file or any directory under `$HOME` on the
|
|
36
|
+
way), non-regular and foreign-owned files are refused; writes are atomic at
|
|
37
|
+
mode 0600 or stricter and are redone once if the app writes the file
|
|
38
|
+
meanwhile; push refuses while the checkout copy differs from HEAD (`migrate
|
|
39
|
+
--reset-copy` drops the change) and a configuration with no merge entry
|
|
40
|
+
pushes exactly as before; logs name keys, never values, and a change to
|
|
41
|
+
`hooks`, `env`, `permissions`, `apiKeyHelper` or
|
|
42
|
+
`skipDangerousModePermissionPrompt` logs a warning. `ctrlrelay
|
|
43
|
+
personalization migrate` converts the old symlink once, on request, and
|
|
44
|
+
resets the checkout copy to the fleet's version; pull and auto-pull refuse
|
|
45
|
+
the symlink instead (#287).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- A personalization pull on a machine still wired the old way (`~/.claude/settings.json`
|
|
50
|
+
a symlink into the checkout, with the app having written through it) failed
|
|
51
|
+
with "rebase hit conflicts" and never mentioned the fix. Pull and auto-pull
|
|
52
|
+
now add: run `ctrlrelay personalization migrate` (#287)
|
|
53
|
+
|
|
54
|
+
### Security
|
|
55
|
+
|
|
56
|
+
- The relay's own git touches of agent-written trees no longer reach past
|
|
57
|
+
the agent sandbox (#263).
|
|
58
|
+
- Every `git worktree add` the relay runs now reads no global or system
|
|
59
|
+
git config and runs no hooks or fsmonitor, whether or not the sandbox is
|
|
60
|
+
on. A committed or planted attributes file therefore cannot select a
|
|
61
|
+
filter driver the operator's config defines. For that checkout this
|
|
62
|
+
drops `lfs.*` and `filter.*` drivers, post-checkout hooks,
|
|
63
|
+
`safe.directory`, `core.autocrlf`/`core.eol`, `core.longpaths` and
|
|
64
|
+
`url.*.insteadOf` (not needed: the add does no fetch).
|
|
65
|
+
- A repository using git-lfs now gives the agent pointer files. None of
|
|
66
|
+
the repositories the relay currently works on uses LFS: 0 of 90 bare
|
|
67
|
+
clones have `filter=lfs` in HEAD's `.gitattributes`.
|
|
68
|
+
- The bare clone's own config is still read. The relay writes it, and the
|
|
69
|
+
sandbox keeps it read-only.
|
|
70
|
+
- The `CLAUDE.md` exclude line is written only to the worktree's git
|
|
71
|
+
directory found from the path the relay made (never from the
|
|
72
|
+
worktree's `.git` file): `<clone>.git/worktrees/<basename>[N]`, whose
|
|
73
|
+
back-pointer must name this worktree. It is opened without following
|
|
74
|
+
any link, a hard-linked file is refused, and every refusal is logged
|
|
75
|
+
and skipped rather than failing the resume.
|
|
76
|
+
- The reviewer's base branch and its commit come from GitHub rather than
|
|
77
|
+
the bare clone, are pinned in the review copy, and are named in the
|
|
78
|
+
review comment.
|
|
79
|
+
|
|
10
80
|
## [0.18.0] - 2026-11-03
|
|
11
81
|
|
|
12
82
|
### Added
|
|
@@ -38,6 +108,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
38
108
|
|
|
39
109
|
### Changed
|
|
40
110
|
|
|
111
|
+
- Internal, no behaviour change: the dev pipeline's work (code, publish,
|
|
112
|
+
verify, review) runs as a fixed, ordered list of steps inside a frame that
|
|
113
|
+
keeps the lock, claim, worktree, cleanup and claim reconciliation as they
|
|
114
|
+
were. Groundwork for per-repo step switches (refs #226)
|
|
41
115
|
- The subprocess machinery every CLI-driven adapter shares — the timeout, the
|
|
42
116
|
kill on cancel, the replacing decode, the checkpoint read and #204's outcome
|
|
43
117
|
report on every exit path — moved from `ClaudeDispatcher` to a
|
|
@@ -2192,7 +2266,9 @@ pipeline).
|
|
|
2192
2266
|
per-phase implementation plans (Phase 0 through Phase 4).
|
|
2193
2267
|
- `docs/Claude_Code_Project_Guide.md` — project development guide.
|
|
2194
2268
|
|
|
2195
|
-
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.
|
|
2269
|
+
[Unreleased]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.1...HEAD
|
|
2270
|
+
[0.19.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.19.0...v0.19.1
|
|
2271
|
+
[0.19.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.18.0...v0.19.0
|
|
2196
2272
|
[0.18.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.17.1...v0.18.0
|
|
2197
2273
|
[0.17.1]: https://github.com/AInvirion/ctrlrelay/compare/v0.17.0...v0.17.1
|
|
2198
2274
|
[0.17.0]: https://github.com/AInvirion/ctrlrelay/compare/v0.16.4...v0.17.0
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ctrlrelay
|
|
3
|
+
Version: 0.19.1
|
|
4
|
+
Summary: Local-first orchestrator for headless coding agents across multiple GitHub repos
|
|
5
|
+
Project-URL: Homepage, https://github.com/AInvirion/ctrlrelay
|
|
6
|
+
Project-URL: Documentation, https://ainvirion.github.io/ctrlrelay/
|
|
7
|
+
Project-URL: Repository, https://github.com/AInvirion/ctrlrelay
|
|
8
|
+
Project-URL: Issues, https://github.com/AInvirion/ctrlrelay/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/AInvirion/ctrlrelay/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Oscar Valenzuela <oscar.valenzuela.b@gmail.com>
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: automation,claude,github,orchestrator
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
24
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
25
|
+
Classifier: Topic :: System :: Systems Administration
|
|
26
|
+
Requires-Python: >=3.12
|
|
27
|
+
Requires-Dist: apscheduler>=3.10.0
|
|
28
|
+
Requires-Dist: httpx>=0.27.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Requires-Dist: python-telegram-bot>=21.0
|
|
31
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
32
|
+
Requires-Dist: rich>=13.0.0
|
|
33
|
+
Requires-Dist: typer>=0.12.0
|
|
34
|
+
Requires-Dist: websockets>=12.0
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# ctrlrelay
|
|
43
|
+
|
|
44
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
45
|
+
[](https://pypi.org/project/ctrlrelay/)
|
|
46
|
+
[](https://pypi.org/project/ctrlrelay/)
|
|
47
|
+
[](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml)
|
|
48
|
+
|
|
49
|
+
**Assign an issue. Get a reviewed pull request. Answer questions from your phone.**
|
|
50
|
+
|
|
51
|
+
ctrlrelay turns the coding agent you already use into a teammate that works
|
|
52
|
+
your GitHub backlog in the background. It picks up issues, writes the code in
|
|
53
|
+
a clean branch, opens a pull request, waits for CI, gets the change reviewed,
|
|
54
|
+
and pings you on **Telegram** or **Mattermost** only when it truly needs you.
|
|
55
|
+
|
|
56
|
+
It runs on your own machine. Your code, your credentials, your agent
|
|
57
|
+
subscription. No hosted service in the middle.
|
|
58
|
+
|
|
59
|
+
**[Documentation](https://ainvirion.github.io/ctrlrelay/)** ·
|
|
60
|
+
**[Get started](https://ainvirion.github.io/ctrlrelay/getting-started/)** ·
|
|
61
|
+
**[Why ctrlrelay](https://ainvirion.github.io/ctrlrelay/#why-ctrlrelay)**
|
|
62
|
+
|
|
63
|
+
## Why ctrlrelay
|
|
64
|
+
|
|
65
|
+
Coding agents are great when you sit and watch them. ctrlrelay is for the
|
|
66
|
+
work you don't want to sit and watch.
|
|
67
|
+
|
|
68
|
+
- **Your backlog is the to-do list.** No new tool to learn. Assign an issue
|
|
69
|
+
(or add a label) and the work starts. The result is a normal pull request
|
|
70
|
+
your team already knows how to review.
|
|
71
|
+
- **Runs where your code already lives.** ctrlrelay is a small program on your
|
|
72
|
+
laptop or server — not a cloud service. Nothing leaves your machine except
|
|
73
|
+
the calls your agent and GitHub already make. No per-seat platform fee on
|
|
74
|
+
top of the agent you already pay for.
|
|
75
|
+
- **It asks instead of guessing.** When the agent is stuck, you get the
|
|
76
|
+
question in chat. You reply from your phone and the *same* session picks up
|
|
77
|
+
where it stopped.
|
|
78
|
+
- **Safe by default.** Every issue gets its own isolated copy of the repo.
|
|
79
|
+
One job per repo at a time. Optional Linux sandbox that hides your secrets
|
|
80
|
+
from the agent. Nothing merges without your rules allowing it.
|
|
81
|
+
- **Done means done.** It waits for CI, fixes failing checks, and posts an
|
|
82
|
+
independent review on the pull request — so what reaches you is ready to
|
|
83
|
+
read, not a first draft.
|
|
84
|
+
- **Pick your agent per repo.** Claude Code today, or OpenCode — which can
|
|
85
|
+
run on a local model — for the repos where that fits.
|
|
86
|
+
- **Grows with you.** Start on one laptop. Add more machines later; they share
|
|
87
|
+
the work without stepping on each other.
|
|
88
|
+
- **Open source.** Apache 2.0. Read every line of what runs on your machine.
|
|
89
|
+
|
|
90
|
+
## What it does
|
|
91
|
+
|
|
92
|
+
- **Issues → pull requests.** Watches every repo you choose and works each new
|
|
93
|
+
issue end to end.
|
|
94
|
+
- **Questions in your chat.** Telegram (a private bot) or Mattermost (a channel
|
|
95
|
+
on your own server).
|
|
96
|
+
- **Automatic review.** Every agent pull request gets a second-opinion review
|
|
97
|
+
comment before you look at it.
|
|
98
|
+
- **Daily security sweep.** Reviews Dependabot alerts and pull requests across
|
|
99
|
+
your repos every morning.
|
|
100
|
+
- **Status page.** See what each agent is doing, what is waiting, and what
|
|
101
|
+
needs you — at a glance.
|
|
102
|
+
- **Many machines, one account.** Spread work across machines, with one chat
|
|
103
|
+
and one status page.
|
|
104
|
+
- **Settings follow you.** Optional sync of your agent setup across machines
|
|
105
|
+
through a private GitHub repo.
|
|
106
|
+
|
|
107
|
+
## How it works
|
|
108
|
+
|
|
109
|
+
```mermaid
|
|
110
|
+
flowchart LR
|
|
111
|
+
GH[("GitHub<br/>issues")] -->|new issue| CR["ctrlrelay<br/>on your machine"]
|
|
112
|
+
CR -->|works in a clean branch| AG["Coding agent<br/>Claude Code · OpenCode"]
|
|
113
|
+
AG -->|stuck?| CH["Your chat<br/>Telegram · Mattermost"]
|
|
114
|
+
CH -->|your answer| AG
|
|
115
|
+
AG -->|opens| PR["Pull request"]
|
|
116
|
+
PR -->|CI + review| YOU(["You merge"])
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
1. **You assign an issue** to yourself, or add a label you pick.
|
|
120
|
+
2. **ctrlrelay starts the agent** in a fresh, isolated copy of the repo.
|
|
121
|
+
3. **If the agent has a question**, it lands in your chat. Your reply goes
|
|
122
|
+
straight back to the same session.
|
|
123
|
+
4. **A pull request opens.** ctrlrelay waits for CI, fixes what fails, and
|
|
124
|
+
posts a review.
|
|
125
|
+
5. **You merge** — or ask for changes, like with any teammate.
|
|
126
|
+
|
|
127
|
+
## Quick start
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pipx install ctrlrelay
|
|
131
|
+
ctrlrelay setup # finds your repos and writes your config
|
|
132
|
+
ctrlrelay poller start # starts working assigned issues
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
You need Python 3.12+, the GitHub CLI, and a coding agent CLI. The
|
|
136
|
+
**[getting started guide](https://ainvirion.github.io/ctrlrelay/getting-started/)**
|
|
137
|
+
covers prerequisites, chat setup and running it as a background service.
|
|
138
|
+
|
|
139
|
+
## Documentation
|
|
140
|
+
|
|
141
|
+
Everything lives at **[ainvirion.github.io/ctrlrelay](https://ainvirion.github.io/ctrlrelay/)**:
|
|
142
|
+
|
|
143
|
+
- [Getting started](https://ainvirion.github.io/ctrlrelay/getting-started/) — install and first run
|
|
144
|
+
- [Chat bridge](https://ainvirion.github.io/ctrlrelay/bridge/) — set up Telegram or Mattermost
|
|
145
|
+
- [Configuration](https://ainvirion.github.io/ctrlrelay/configuration/) — every setting
|
|
146
|
+
- [Operations](https://ainvirion.github.io/ctrlrelay/operations/) — background services, status page, several machines
|
|
147
|
+
- [CLI reference](https://ainvirion.github.io/ctrlrelay/cli/) — every command
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
Contributions are welcome. Read the [contributing guide](CONTRIBUTING.md) and
|
|
152
|
+
the [code of conduct](CODE_OF_CONDUCT.md). First-time contributors sign a
|
|
153
|
+
one-time Contributor Assignment Agreement in the pull request.
|
|
154
|
+
|
|
155
|
+
## Security
|
|
156
|
+
|
|
157
|
+
Found a vulnerability? Please follow the [security policy](SECURITY.md) and open
|
|
158
|
+
a private advisory — not a public issue.
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
Apache License 2.0 — see [LICENSE](LICENSE).
|
|
163
|
+
|
|
164
|
+
Copyright (c) 2026 AInvirion LLC. All Rights Reserved.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# ctrlrelay
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
|
+
[](https://pypi.org/project/ctrlrelay/)
|
|
5
|
+
[](https://pypi.org/project/ctrlrelay/)
|
|
6
|
+
[](https://github.com/AInvirion/ctrlrelay/actions/workflows/test.yml)
|
|
7
|
+
|
|
8
|
+
**Assign an issue. Get a reviewed pull request. Answer questions from your phone.**
|
|
9
|
+
|
|
10
|
+
ctrlrelay turns the coding agent you already use into a teammate that works
|
|
11
|
+
your GitHub backlog in the background. It picks up issues, writes the code in
|
|
12
|
+
a clean branch, opens a pull request, waits for CI, gets the change reviewed,
|
|
13
|
+
and pings you on **Telegram** or **Mattermost** only when it truly needs you.
|
|
14
|
+
|
|
15
|
+
It runs on your own machine. Your code, your credentials, your agent
|
|
16
|
+
subscription. No hosted service in the middle.
|
|
17
|
+
|
|
18
|
+
**[Documentation](https://ainvirion.github.io/ctrlrelay/)** ·
|
|
19
|
+
**[Get started](https://ainvirion.github.io/ctrlrelay/getting-started/)** ·
|
|
20
|
+
**[Why ctrlrelay](https://ainvirion.github.io/ctrlrelay/#why-ctrlrelay)**
|
|
21
|
+
|
|
22
|
+
## Why ctrlrelay
|
|
23
|
+
|
|
24
|
+
Coding agents are great when you sit and watch them. ctrlrelay is for the
|
|
25
|
+
work you don't want to sit and watch.
|
|
26
|
+
|
|
27
|
+
- **Your backlog is the to-do list.** No new tool to learn. Assign an issue
|
|
28
|
+
(or add a label) and the work starts. The result is a normal pull request
|
|
29
|
+
your team already knows how to review.
|
|
30
|
+
- **Runs where your code already lives.** ctrlrelay is a small program on your
|
|
31
|
+
laptop or server — not a cloud service. Nothing leaves your machine except
|
|
32
|
+
the calls your agent and GitHub already make. No per-seat platform fee on
|
|
33
|
+
top of the agent you already pay for.
|
|
34
|
+
- **It asks instead of guessing.** When the agent is stuck, you get the
|
|
35
|
+
question in chat. You reply from your phone and the *same* session picks up
|
|
36
|
+
where it stopped.
|
|
37
|
+
- **Safe by default.** Every issue gets its own isolated copy of the repo.
|
|
38
|
+
One job per repo at a time. Optional Linux sandbox that hides your secrets
|
|
39
|
+
from the agent. Nothing merges without your rules allowing it.
|
|
40
|
+
- **Done means done.** It waits for CI, fixes failing checks, and posts an
|
|
41
|
+
independent review on the pull request — so what reaches you is ready to
|
|
42
|
+
read, not a first draft.
|
|
43
|
+
- **Pick your agent per repo.** Claude Code today, or OpenCode — which can
|
|
44
|
+
run on a local model — for the repos where that fits.
|
|
45
|
+
- **Grows with you.** Start on one laptop. Add more machines later; they share
|
|
46
|
+
the work without stepping on each other.
|
|
47
|
+
- **Open source.** Apache 2.0. Read every line of what runs on your machine.
|
|
48
|
+
|
|
49
|
+
## What it does
|
|
50
|
+
|
|
51
|
+
- **Issues → pull requests.** Watches every repo you choose and works each new
|
|
52
|
+
issue end to end.
|
|
53
|
+
- **Questions in your chat.** Telegram (a private bot) or Mattermost (a channel
|
|
54
|
+
on your own server).
|
|
55
|
+
- **Automatic review.** Every agent pull request gets a second-opinion review
|
|
56
|
+
comment before you look at it.
|
|
57
|
+
- **Daily security sweep.** Reviews Dependabot alerts and pull requests across
|
|
58
|
+
your repos every morning.
|
|
59
|
+
- **Status page.** See what each agent is doing, what is waiting, and what
|
|
60
|
+
needs you — at a glance.
|
|
61
|
+
- **Many machines, one account.** Spread work across machines, with one chat
|
|
62
|
+
and one status page.
|
|
63
|
+
- **Settings follow you.** Optional sync of your agent setup across machines
|
|
64
|
+
through a private GitHub repo.
|
|
65
|
+
|
|
66
|
+
## How it works
|
|
67
|
+
|
|
68
|
+
```mermaid
|
|
69
|
+
flowchart LR
|
|
70
|
+
GH[("GitHub<br/>issues")] -->|new issue| CR["ctrlrelay<br/>on your machine"]
|
|
71
|
+
CR -->|works in a clean branch| AG["Coding agent<br/>Claude Code · OpenCode"]
|
|
72
|
+
AG -->|stuck?| CH["Your chat<br/>Telegram · Mattermost"]
|
|
73
|
+
CH -->|your answer| AG
|
|
74
|
+
AG -->|opens| PR["Pull request"]
|
|
75
|
+
PR -->|CI + review| YOU(["You merge"])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
1. **You assign an issue** to yourself, or add a label you pick.
|
|
79
|
+
2. **ctrlrelay starts the agent** in a fresh, isolated copy of the repo.
|
|
80
|
+
3. **If the agent has a question**, it lands in your chat. Your reply goes
|
|
81
|
+
straight back to the same session.
|
|
82
|
+
4. **A pull request opens.** ctrlrelay waits for CI, fixes what fails, and
|
|
83
|
+
posts a review.
|
|
84
|
+
5. **You merge** — or ask for changes, like with any teammate.
|
|
85
|
+
|
|
86
|
+
## Quick start
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pipx install ctrlrelay
|
|
90
|
+
ctrlrelay setup # finds your repos and writes your config
|
|
91
|
+
ctrlrelay poller start # starts working assigned issues
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
You need Python 3.12+, the GitHub CLI, and a coding agent CLI. The
|
|
95
|
+
**[getting started guide](https://ainvirion.github.io/ctrlrelay/getting-started/)**
|
|
96
|
+
covers prerequisites, chat setup and running it as a background service.
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
Everything lives at **[ainvirion.github.io/ctrlrelay](https://ainvirion.github.io/ctrlrelay/)**:
|
|
101
|
+
|
|
102
|
+
- [Getting started](https://ainvirion.github.io/ctrlrelay/getting-started/) — install and first run
|
|
103
|
+
- [Chat bridge](https://ainvirion.github.io/ctrlrelay/bridge/) — set up Telegram or Mattermost
|
|
104
|
+
- [Configuration](https://ainvirion.github.io/ctrlrelay/configuration/) — every setting
|
|
105
|
+
- [Operations](https://ainvirion.github.io/ctrlrelay/operations/) — background services, status page, several machines
|
|
106
|
+
- [CLI reference](https://ainvirion.github.io/ctrlrelay/cli/) — every command
|
|
107
|
+
|
|
108
|
+
## Contributing
|
|
109
|
+
|
|
110
|
+
Contributions are welcome. Read the [contributing guide](CONTRIBUTING.md) and
|
|
111
|
+
the [code of conduct](CODE_OF_CONDUCT.md). First-time contributors sign a
|
|
112
|
+
one-time Contributor Assignment Agreement in the pull request.
|
|
113
|
+
|
|
114
|
+
## Security
|
|
115
|
+
|
|
116
|
+
Found a vulnerability? Please follow the [security policy](SECURITY.md) and open
|
|
117
|
+
a private advisory — not a public issue.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
Apache License 2.0 — see [LICENSE](LICENSE).
|
|
122
|
+
|
|
123
|
+
Copyright (c) 2026 AInvirion LLC. All Rights Reserved.
|
|
@@ -254,3 +254,20 @@ repos: []
|
|
|
254
254
|
# # generated while blocked in this call, so a slow default here
|
|
255
255
|
# # just adds idle wall-clock time. Defaults to 600 (10 minutes).
|
|
256
256
|
# ci_wait_timeout_seconds: 600
|
|
257
|
+
# # How strict each dev pipeline work step is for this repo. The
|
|
258
|
+
# # order (code, publish, verify, review) is fixed. Each key is
|
|
259
|
+
# # off, on or required; leave a key out for today's behaviour.
|
|
260
|
+
# # required: a failure stops the run, and nothing skips the step
|
|
261
|
+
# # (a reviewer that cannot run is not a pass, and CI
|
|
262
|
+
# # that never finished is not a pass)
|
|
263
|
+
# # on: a failure is reported and the PR is handed over
|
|
264
|
+
# # off: the step does not run
|
|
265
|
+
# # code and publish accept only required. verify accepts off or
|
|
266
|
+
# # required. review: off is the same as code_review.method: off;
|
|
267
|
+
# # set both only if they agree.
|
|
268
|
+
# steps:
|
|
269
|
+
# verify: required
|
|
270
|
+
# review: required
|
|
271
|
+
# # Fix rounds for a PR whose CI is red or whose branch conflicts,
|
|
272
|
+
# # before the run fails. Defaults to 3; 0 means none.
|
|
273
|
+
# max_fix_attempts: 3
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
title: ctrlrelay
|
|
2
2
|
description: >-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Assign a GitHub issue, get a reviewed pull request. ctrlrelay runs your
|
|
4
|
+
coding agent in the background and asks you on Telegram or Mattermost
|
|
5
|
+
when it needs you.
|
|
5
6
|
url: "https://ainvirion.github.io"
|
|
6
7
|
baseurl: "/ctrlrelay"
|
|
7
8
|
|
|
@@ -26,6 +27,10 @@ search:
|
|
|
26
27
|
|
|
27
28
|
heading_anchors: true
|
|
28
29
|
|
|
30
|
+
# Render ```mermaid fences as diagrams
|
|
31
|
+
mermaid:
|
|
32
|
+
version: "10.9.1"
|
|
33
|
+
|
|
29
34
|
# Aux links appear in the top right of the layout
|
|
30
35
|
aux_links:
|
|
31
36
|
"GitHub":
|
|
@@ -39,10 +39,12 @@ A token copied between machines is revoked for all of them at once.
|
|
|
39
39
|
|
|
40
40
|
## 3. Its own agent login
|
|
41
41
|
|
|
42
|
-
Log the agent CLI in on this machine
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
Log the agent CLI in on this machine — whichever your repos use (`claude`,
|
|
43
|
+
`opencode` or both, per `agent.type` and `repos[].agent_type`). For Claude Code, each
|
|
44
|
+
machine can use its own subscription; it pauses on its own when its usage
|
|
45
|
+
nears the limit (`usage.pause_at`). Two machines on one subscription share one
|
|
46
|
+
set of usage numbers and pause together, which is correct. OpenCode has no
|
|
47
|
+
usage reading of its own.
|
|
46
48
|
|
|
47
49
|
## 4. A config with its own `node_id`
|
|
48
50
|
|
|
@@ -103,8 +105,9 @@ ctrlrelay node check
|
|
|
103
105
|
✓ peer http://node-a:8787: reachable, node-a
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
`--no-agent` skips the agent check, which makes one tiny call on the
|
|
107
|
-
subscription.
|
|
108
|
+
`--no-agent` skips the agent check, which makes one tiny `claude` call on the
|
|
109
|
+
subscription. The row is skipped when no repo routes to `claude`, and it never
|
|
110
|
+
checks `opencode`: run that CLI once by hand. A peer that answers with this machine's own name means two
|
|
108
111
|
machines share a `node_id` (or the URL points back here).
|
|
109
112
|
|
|
110
113
|
## 8. Start it
|