cli-agent-runner 0.1.1__tar.gz → 0.1.7__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.
Files changed (150) hide show
  1. cli_agent_runner-0.1.7/.githooks/commit-msg +33 -0
  2. cli_agent_runner-0.1.7/.github/workflows/ci.yml +105 -0
  3. cli_agent_runner-0.1.7/CHANGELOG.md +293 -0
  4. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/CONTRIBUTING.md +8 -0
  5. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/PKG-INFO +7 -12
  6. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/README.md +6 -11
  7. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/README.zh.md +1 -6
  8. cli_agent_runner-0.1.7/agent_runner/__init__.py +54 -0
  9. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/_docgen.py +2 -0
  10. cli_agent_runner-0.1.7/agent_runner/_registry.py +23 -0
  11. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/_version.py +2 -2
  12. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/agent_runtime.py +8 -19
  13. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/api.py +20 -6
  14. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/api_types.py +54 -4
  15. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/common.py +19 -1
  16. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/init_cmd.py +8 -2
  17. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/install_cmd.py +1 -1
  18. cli_agent_runner-0.1.7/agent_runner/config.py +148 -0
  19. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/defenses.py +6 -6
  20. cli_agent_runner-0.1.7/agent_runner/detector_helpers.py +115 -0
  21. cli_agent_runner-0.1.7/agent_runner/events.py +130 -0
  22. cli_agent_runner-0.1.7/agent_runner/hooks.py +143 -0
  23. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/monitor.py +107 -39
  24. cli_agent_runner-0.1.7/agent_runner/presets/aider.toml +30 -0
  25. cli_agent_runner-0.1.7/agent_runner/presets/claude.toml +29 -0
  26. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/prompt_loader.py +3 -2
  27. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/runner.py +99 -19
  28. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/scaffold.py +26 -40
  29. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/startup_check.py +4 -1
  30. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/docs/README.md +9 -6
  31. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/docs/architecture.md +19 -24
  32. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/docs/commands.md +1 -1
  33. cli_agent_runner-0.1.7/docs/configuration.md +130 -0
  34. cli_agent_runner-0.1.7/docs/plugins.md +311 -0
  35. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/docs/quickstart.md +39 -11
  36. cli_agent_runner-0.1.7/docs/recipes/aider.md +117 -0
  37. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/docs/runbook.md +14 -3
  38. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/conftest.py +27 -0
  39. cli_agent_runner-0.1.7/tests/contract/test_public_api_surface.py +130 -0
  40. cli_agent_runner-0.1.7/tests/integration/test_context_enricher_namespacing.py +57 -0
  41. cli_agent_runner-0.1.7/tests/integration/test_plugin_detector_loaded.py +105 -0
  42. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/integration/test_run_one_round_with_fake_agent.py +39 -0
  43. cli_agent_runner-0.1.7/tests/integration/test_scaffold_presets.py +80 -0
  44. cli_agent_runner-0.1.1/tests/invariants/test_phase2_architecture.py → cli_agent_runner-0.1.7/tests/invariants/test_architecture.py +2 -19
  45. cli_agent_runner-0.1.7/tests/invariants/test_event_kind_registry.py +74 -0
  46. cli_agent_runner-0.1.1/tests/invariants/test_phase2_module_sizes.py → cli_agent_runner-0.1.7/tests/invariants/test_module_sizes.py +10 -10
  47. cli_agent_runner-0.1.7/tests/invariants/test_no_ai_signatures.py +83 -0
  48. cli_agent_runner-0.1.7/tests/invariants/test_peek_schema_version.py +78 -0
  49. cli_agent_runner-0.1.7/tests/invariants/test_round_result_stable.py +75 -0
  50. cli_agent_runner-0.1.7/tests/literate/__init__.py +0 -0
  51. cli_agent_runner-0.1.7/tests/unit/__init__.py +0 -0
  52. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_agent_runtime.py +23 -12
  53. cli_agent_runner-0.1.7/tests/unit/test_auto_stop_gating.py +91 -0
  54. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_cli_init_install.py +27 -0
  55. cli_agent_runner-0.1.7/tests/unit/test_config.py +413 -0
  56. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_defenses.py +26 -2
  57. cli_agent_runner-0.1.7/tests/unit/test_detector_helpers.py +141 -0
  58. cli_agent_runner-0.1.7/tests/unit/test_detector_protocol.py +69 -0
  59. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_docgen.py +1 -1
  60. cli_agent_runner-0.1.7/tests/unit/test_events.py +150 -0
  61. cli_agent_runner-0.1.7/tests/unit/test_hook_failure_isolation.py +77 -0
  62. cli_agent_runner-0.1.7/tests/unit/test_hooks.py +139 -0
  63. cli_agent_runner-0.1.7/tests/unit/test_init_entry_points.py +107 -0
  64. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_monitor_detectors.py +55 -0
  65. cli_agent_runner-0.1.7/tests/unit/test_presets.py +87 -0
  66. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_prompt_loader.py +66 -0
  67. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_scaffold.py +38 -0
  68. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_service_unit.py +1 -1
  69. cli_agent_runner-0.1.1/.github/workflows/ci.yml +0 -57
  70. cli_agent_runner-0.1.1/CHANGELOG.md +0 -62
  71. cli_agent_runner-0.1.1/agent_runner/__init__.py +0 -3
  72. cli_agent_runner-0.1.1/agent_runner/config.py +0 -92
  73. cli_agent_runner-0.1.1/agent_runner/critic.py +0 -33
  74. cli_agent_runner-0.1.1/agent_runner/events.py +0 -53
  75. cli_agent_runner-0.1.1/docs/configuration.md +0 -78
  76. cli_agent_runner-0.1.1/tests/invariants/test_event_kind_registry.py +0 -39
  77. cli_agent_runner-0.1.1/tests/unit/test_config.py +0 -115
  78. cli_agent_runner-0.1.1/tests/unit/test_critic.py +0 -27
  79. cli_agent_runner-0.1.1/tests/unit/test_events.py +0 -79
  80. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.codecov.yml +0 -0
  81. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  82. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  83. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  84. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  85. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.github/workflows/release.yml +0 -0
  86. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.gitignore +0 -0
  87. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/.vulture-whitelist.py +0 -0
  88. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/CODE_OF_CONDUCT.md +0 -0
  89. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/LICENSE +0 -0
  90. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/SECURITY.md +0 -0
  91. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/__init__.py +0 -0
  92. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/__main__.py +0 -0
  93. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/monitor_cmd.py +0 -0
  94. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/peek_cmd.py +0 -0
  95. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/round_cmd.py +0 -0
  96. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/serve_cmd.py +0 -0
  97. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/cli/service_cmd.py +0 -0
  98. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/context_store.py +0 -0
  99. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/lifecycle.py +0 -0
  100. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/metrics.py +0 -0
  101. {cli_agent_runner-0.1.1/tests → cli_agent_runner-0.1.7/agent_runner/presets}/__init__.py +0 -0
  102. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/round_view.py +0 -0
  103. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/service_unit.py +0 -0
  104. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/agent_runner/vcs_state.py +0 -0
  105. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/build.sh +0 -0
  106. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/deploy/example-agent-runner.toml +0 -0
  107. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/deploy/launchd.plist.tmpl +0 -0
  108. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/deploy/run-loop.sh +0 -0
  109. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/deploy/systemd.service.tmpl +0 -0
  110. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/pyproject.toml +0 -0
  111. {cli_agent_runner-0.1.1/tests/e2e → cli_agent_runner-0.1.7/tests}/__init__.py +0 -0
  112. {cli_agent_runner-0.1.1/tests/integration → cli_agent_runner-0.1.7/tests/contract}/__init__.py +0 -0
  113. {cli_agent_runner-0.1.1/tests/invariants → cli_agent_runner-0.1.7/tests/e2e}/__init__.py +0 -0
  114. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/e2e/conftest.py +0 -0
  115. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  116. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/e2e/test_e2e_install_systemd.py +0 -0
  117. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  118. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  119. {cli_agent_runner-0.1.1/tests/literate → cli_agent_runner-0.1.7/tests/integration}/__init__.py +0 -0
  120. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/integration/test_install_dry_run.py +0 -0
  121. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/integration/test_monitor_seeded.py +0 -0
  122. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/integration/test_run_loop_backoff.py +0 -0
  123. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/integration/test_serve_loop.py +0 -0
  124. {cli_agent_runner-0.1.1/tests/unit → cli_agent_runner-0.1.7/tests/invariants}/__init__.py +0 -0
  125. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_atomic_write_enforced.py +0 -0
  126. /cli_agent_runner-0.1.1/tests/invariants/test_phase2_catalogs.py → /cli_agent_runner-0.1.7/tests/invariants/test_catalogs.py +0 -0
  127. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_docs_generated.py +0 -0
  128. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_module_boundaries.py +0 -0
  129. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  130. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  131. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  132. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/literate/parser.py +0 -0
  133. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/literate/test_parser.py +0 -0
  134. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/literate/test_quickstart.py +0 -0
  135. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_api_observation.py +0 -0
  136. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_api_service.py +0 -0
  137. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_api_types.py +0 -0
  138. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_cli.py +0 -0
  139. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_cli_common.py +0 -0
  140. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  141. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_context_store.py +0 -0
  142. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_lifecycle.py +0 -0
  143. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_metrics.py +0 -0
  144. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_monitor_assembly.py +0 -0
  145. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_monitor_remote.py +0 -0
  146. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_peek_argparse.py +0 -0
  147. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_round_view.py +0 -0
  148. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_runner.py +0 -0
  149. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_startup_check.py +0 -0
  150. {cli_agent_runner-0.1.1 → cli_agent_runner-0.1.7}/tests/unit/test_vcs_state.py +0 -0
@@ -0,0 +1,33 @@
1
+ #!/bin/sh
2
+ # Reject commit messages that contain AI-tool attribution.
3
+ #
4
+ # Rationale: this repository's history is curated to credit only the human
5
+ # author. AI-tool trailers (Co-Authored-By: Claude, 🤖 Generated with ..., etc.)
6
+ # create false "claude" / "anthropic" entries in GitHub's Contributors widget
7
+ # and bloat commit log noise.
8
+ #
9
+ # Activate per clone: git config core.hooksPath .githooks
10
+ # Bypass (rarely needed): git commit --no-verify
11
+
12
+ MSG_FILE="$1"
13
+
14
+ # Strip git-comment lines before checking
15
+ CONTENT=$(grep -v '^#' "$MSG_FILE" || true)
16
+
17
+ # Case-insensitive regex; tab/space tolerant
18
+ FORBIDDEN_REGEX='([Cc]o-[Aa]uthored-[Bb]y:|🤖|[Gg]enerated with [Cc]laude|[Gg]enerated with \[Cursor\]|noreply@anthropic\.com)'
19
+
20
+ if printf '%s\n' "$CONTENT" | grep -E -q "$FORBIDDEN_REGEX"; then
21
+ echo "" >&2
22
+ echo "commit-msg hook: AI-tool attribution detected in commit message." >&2
23
+ echo "" >&2
24
+ echo "Offending lines:" >&2
25
+ printf '%s\n' "$CONTENT" | grep -E -n "$FORBIDDEN_REGEX" | sed 's/^/ /' >&2
26
+ echo "" >&2
27
+ echo "Strip Co-Authored-By trailers, 🤖, 'Generated with ...', and" >&2
28
+ echo "noreply@anthropic.com refs, then retry." >&2
29
+ echo "" >&2
30
+ echo "(Bypass: git commit --no-verify — use sparingly, requires explicit" >&2
31
+ echo " review since CI lint-commits job will also reject the push.)" >&2
32
+ exit 1
33
+ fi
@@ -0,0 +1,105 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ # Cancel in-flight runs on the same PR ref when a new push lands.
10
+ # Push-to-main runs always complete (cancel-in-progress only on PRs).
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14
+
15
+ jobs:
16
+ lint-commits:
17
+ name: lint commit messages (no AI-tool attribution)
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 2
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ # PR: scan only new commits the PR introduces (base..head).
24
+ # Push to main: scan the just-pushed commits (before..after).
25
+ # fetch-depth=0 ensures both refs are available.
26
+ fetch-depth: 0
27
+ - name: scan new commit messages
28
+ env:
29
+ GH_EVENT: ${{ github.event_name }}
30
+ BEFORE_SHA: ${{ github.event.before }}
31
+ BASE_REF: ${{ github.event.pull_request.base.sha }}
32
+ HEAD_REF: ${{ github.event.pull_request.head.sha }}
33
+ run: |
34
+ set -euo pipefail
35
+ if [ "$GH_EVENT" = "pull_request" ]; then
36
+ RANGE="${BASE_REF}..${HEAD_REF}"
37
+ else
38
+ # push event; before is 0..0 on first push or branch creation
39
+ if [ -z "${BEFORE_SHA:-}" ] || [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ]; then
40
+ RANGE="HEAD~1..HEAD"
41
+ else
42
+ RANGE="${BEFORE_SHA}..HEAD"
43
+ fi
44
+ fi
45
+ echo "Scanning commit-message bodies in $RANGE"
46
+ # Collect messages joined by NUL bytes; grep -P for fixed alternation.
47
+ BODIES=$(git log --format='%B%x00' "$RANGE" || true)
48
+ if [ -z "$BODIES" ]; then
49
+ echo " (no commits in range)"
50
+ exit 0
51
+ fi
52
+ PATTERN='(Co-Authored-By:|🤖|Generated with Claude|Generated with \[Cursor\]|noreply@anthropic\.com)'
53
+ if printf '%s' "$BODIES" | grep -i -E -q "$PATTERN"; then
54
+ echo "::error::AI-tool attribution detected in new commit messages."
55
+ echo "Offending lines:"
56
+ printf '%s' "$BODIES" | grep -i -n -E "$PATTERN" || true
57
+ echo ""
58
+ echo "Strip Co-Authored-By trailers, 🤖, 'Generated with ...',"
59
+ echo "and noreply@anthropic.com refs, then force-push the fixed range."
60
+ exit 1
61
+ fi
62
+ echo " clean"
63
+
64
+ test:
65
+ name: test py${{ matrix.python }} / ${{ matrix.os }}
66
+ runs-on: ${{ matrix.os }}
67
+ timeout-minutes: 15
68
+ strategy:
69
+ fail-fast: false
70
+ matrix:
71
+ python: ["3.11", "3.12", "3.13"]
72
+ os: [ubuntu-latest, macos-latest]
73
+ # Steps below mirror ./build.sh check, inlined for per-step Actions UI
74
+ # granularity AND because coverage requires extra pytest flags not in
75
+ # the local `test` task. CONTRIBUTING.md states the parity intentionally.
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+ with:
79
+ fetch-depth: 0
80
+ - uses: actions/setup-python@v5
81
+ with:
82
+ python-version: ${{ matrix.python }}
83
+ cache: pip
84
+ - name: install
85
+ run: pip install -e ".[dev]"
86
+ - name: ruff
87
+ run: |
88
+ ruff check .
89
+ ruff format --check .
90
+ - name: tests with coverage
91
+ run: pytest -q --ignore=tests/e2e --ignore=tests/literate --cov --cov-report=xml --cov-report=term
92
+ - name: docs CI gate
93
+ run: |
94
+ python -m agent_runner._docgen
95
+ git diff --exit-code docs/
96
+ - name: literate quickstart
97
+ run: pytest tests/literate/ -q
98
+ - name: upload coverage
99
+ if: matrix.python == '3.12' && matrix.os == 'ubuntu-latest'
100
+ uses: codecov/codecov-action@v4
101
+ with:
102
+ files: ./coverage.xml
103
+ fail_ci_if_error: false
104
+ env:
105
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,293 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.7] - 2026-05-13
11
+
12
+ ### Migration for existing 0.1.6 users (DOWNSTREAM CONSUMERS READ THIS)
13
+
14
+ If you maintain an `agent-runner.toml` by hand (rather than via `agent-runner init`),
15
+ you must add an `[agent.env]` block to preserve the Claude self-update suppression
16
+ that 0.1.6 injected implicitly. **Without this, mid-loop self-updates can race with
17
+ the supervisor.**
18
+
19
+ Add to your `agent-runner.toml`:
20
+
21
+ ```toml
22
+ [agent.env]
23
+ DISABLE_AUTOUPDATER = "1"
24
+ CLAUDE_CODE_EFFORT_LEVEL = "xhigh"
25
+ ```
26
+
27
+ Or regenerate cleanly:
28
+
29
+ ```bash
30
+ agent-runner init --preset claude --force
31
+ ```
32
+
33
+ Plugin authors (Argus Gateway, etc.): no public API was renamed or removed
34
+ from your import surface. The deleted symbols (`agent_runner.agent_runtime.CRITICAL_ENV_DEFAULTS`,
35
+ `agent_runner.agent_runtime.merge_critical_envs`) were internal — not part of
36
+ the documented plugin API. A new public-API contract test
37
+ (`tests/contract/test_public_api_surface.py`) locks in `api_types`, `events`,
38
+ `hooks`, `monitor`, `detector_helpers` import surfaces so future refactors
39
+ can't silently drop names you rely on.
40
+
41
+ ### Added
42
+ - `agent-runner init --preset {claude,aider}` selects between bundled CLI presets
43
+ (default: `claude`). New preset directory: `agent_runner/presets/`.
44
+ - `[agent.env]` TOML block — per-CLI env injections, replacing the hardcoded
45
+ `CRITICAL_ENV_DEFAULTS` constant. Empty dict by default.
46
+ - `docs/recipes/aider.md` — aider integration recipe.
47
+ - `tests/contract/test_public_api_surface.py` — public-API surface snapshot for
48
+ plugin authors.
49
+
50
+ ### Changed
51
+ - Core code (`agent_runtime.py`, `config.py`, `runner.py`, `scaffold.py`,
52
+ `defenses.py`) is now truly provider-agnostic: zero hardcoded Claude defaults.
53
+ Claude remains the reference example throughout the docs, but its specifics
54
+ live in `agent_runner/presets/claude.toml` (shipped as package data).
55
+ - `MonitorConfig.auth_fail_hint` default is now empty string; per-CLI hint
56
+ comes from the preset.
57
+ - `PEEK_SCHEMA_VERSION` bumped 1.3 → 1.4. `InitResult` gains `preset: str` field.
58
+ - `agent_runner/defenses.py` `critical_envs_injection` row now reads from
59
+ `cfg.agent.env.keys()` (not the deleted constant); state is "active" iff
60
+ the config defines any env injections, else "off".
61
+
62
+ ### Removed
63
+ - `agent_runner.agent_runtime.CRITICAL_ENV_DEFAULTS` constant.
64
+ - `agent_runner.agent_runtime.merge_critical_envs()` function.
65
+ - `agent_runner.config._DEFAULT_AUTH_HINT` constant's Claude-specific default
66
+ string (replaced with `""`; per-CLI hint now in preset files).
67
+
68
+ ## [0.1.6] - 2026-05-12
69
+
70
+ Zero-feature maintenance release — internal cleanup pass after the 0.1.x plugin
71
+ extension surface completed in 0.1.5. No runtime behavior change for users
72
+ without plugins; plugin authors written against 0.1.5 keep working unchanged.
73
+
74
+ ### Changed
75
+ - Public `Alert.severity` / `Alert.auto_action` and `Detector.severity` /
76
+ `Detector.auto_action` are now typed with `Severity = Literal["info", "warning",
77
+ "critical"]` and `AutoAction = Literal["none", "stop_service"]` aliases
78
+ exported from `agent_runner.api_types`. Plain-string values continue to work
79
+ - `__version__` is read from the hatch-vcs generated `_version.py` instead of a
80
+ hardcoded constant; released packages report the actual release version
81
+ - Internal duplication reduced: shared `agent_runner._registry.ensure_unique`
82
+ for hooks + detectors registries; `cli/common.emit()` plugin-namespace imports
83
+ hoisted to module top; `events.parse_iso_ms` centralizes the ISO-8601 trailing-`Z`
84
+ parsing workaround; `MonitorConfig.auto_stop_on` default now references the
85
+ `_DEFAULT_AUTO_STOP_ON` constant
86
+ - `monitor.dual_source_silence` is now TOCTOU-safe (uses `try/except
87
+ FileNotFoundError` instead of `exists()` + `stat()`)
88
+ - `api._poll_once` short-circuits when no plugin detectors are registered,
89
+ skipping `assemble_project_state` (saves a file read per local poll and an
90
+ SSH round-trip per remote poll)
91
+ - `agent_runner/__init__.py` plugin loaders share a single
92
+ `_load_plugins_from_group` helper
93
+ - `monitor._alert` no longer asserts builtin detector names against
94
+ `KNOWN_ALERT_KINDS` (assertion was redundant and would crash under
95
+ `python -O`); test + docgen layers continue to validate the builtin name set
96
+
97
+ ### Removed
98
+ - `agent_runner.critic` — empty Protocol stub for an unimplemented Critic
99
+ concept that was retired during the framework-first redesign
100
+ - `[llm]` commented block from the scaffold TOML template and its corresponding
101
+ section in `docs/configuration.md` — unused placeholder
102
+ - Legacy phase nomenclature from source docstrings, error messages, and CLI
103
+ help text; `tests/invariants/test_phase2_*.py` files renamed to drop the
104
+ `phase2_` prefix
105
+
106
+ ### Backward compatibility
107
+ - Existing `agent-runner.toml` files continue to load (the removed `[llm]`
108
+ block was never parsed by the supervisor)
109
+ - Plugin code written for 0.1.5 continues to work — Protocol contracts and
110
+ `peek --json` schema 1.3 unchanged
111
+
112
+ ## [0.1.5] - 2026-05-12
113
+
114
+ ### Added
115
+ - `agent_runner.api_types.Detector` — public Protocol for plugin detectors
116
+ (attributes `name`, `severity`, `auto_action`; method `detect(state) -> Alert | None`).
117
+ `@runtime_checkable` — plugin classes structurally satisfying the shape are accepted
118
+ - `agent_runner.monitor.register_detector(detector)` and `plugin_detectors()` — public
119
+ registration API + sorted list of currently-registered detector names
120
+ - `agent_runner.monitor.run_plugin_detectors(state)` — invokes each registered plugin
121
+ detector with the assembled `ProjectState`; per-detector exceptions surface as
122
+ `UserWarning` (round continues)
123
+ - `agent_runner` package now also discovers and loads entry_points in group
124
+ `agent_runner.detectors` at first import; failures degrade to `UserWarning`
125
+ - `agent_runner.detector_helpers` module — three production-tested helpers for
126
+ plugin detector authors:
127
+ - `cumulative_window_check(events, *, kind, window_s, min_count)` —
128
+ sliding-window event counter; robust against wall-clock skew at boundaries
129
+ - `dual_source_silence(scheduler_log, round_log, threshold_s)` — both-source
130
+ silence check; avoids false positives during long rounds when only the
131
+ scheduler log is stale
132
+ - `phase_filter(state, *, exclude_phases)` — skip detection during phases
133
+ that intentionally produce no commits (e.g., retrospective rounds)
134
+ - `MonitorConfig.auto_stop_on: list[str]` — explicit allow-list of detector
135
+ names whose `stop_service` action is honored. Defaults to
136
+ `["oauth_fail", "disk_critical"]` (builtin pair); operators must add plugin
137
+ detector names to opt them into auto-stop
138
+ - `monitor.on_alert` gains `allowed_stop_names: list[str] | None` keyword
139
+ argument; backward-compatible default falls back to the legacy builtin pair
140
+ - `peek --json` schema bumped to `"1.3"`; new `plugins.detectors: list[str]`
141
+ surfaces what's registered
142
+ - `docs/plugins.md` gains a Detector Protocol chapter + DetectorHelpers
143
+ chapter with worked examples for each helper
144
+
145
+ ### Changed
146
+ - `agent_runner.api._poll_once` now concatenates builtin detector alerts
147
+ with plugin detector alerts (`run_plugin_detectors(state)`) before returning
148
+ - `agent_runner.api.monitor_loop` threads `cfg.monitor.auto_stop_on` into
149
+ `monitor.on_alert` for strict gating
150
+ - `tests/invariants/test_peek_schema_version.py` tightened to require
151
+ `schema_version >= "1.3"` and the `plugins.detectors` list shape
152
+
153
+ ### Backward compatibility
154
+ - Zero plugins installed → monitor behavior identical to 0.1.4
155
+ - Existing 9 builtin detectors keep their current signatures unchanged
156
+ - Existing `agent-runner.toml` files load without modification; default
157
+ `auto_stop_on` matches the previously-implicit behavior
158
+ - `on_alert(...)` callers without the new kwarg continue to work via the
159
+ default `["oauth_fail", "disk_critical"]` allow-list
160
+
161
+ ## [0.1.4] - 2026-05-12
162
+
163
+ ### Added
164
+ - `agent_runner.hooks` module — three Protocol-typed plugin extension points:
165
+ `PreRoundHook` (runs after lock acquire, before context write),
166
+ `ContextEnricher` (returns a per-plugin slice stitched into `round-context.json`
167
+ under `base_context[enricher.name]`), and `PostRoundHook` (runs after agent exit,
168
+ before `round_end` event)
169
+ - `agent_runner.hooks.HookContext` — narrow runtime context passed to all hooks
170
+ (`work_dir`, `log_dir`, `project`, `round_num`, `phase`, `agent_name`); does NOT
171
+ expose the full `Config` so internal refactors remain safe
172
+ - `agent_runner.hooks.register_pre_round_hook` / `register_context_enricher` /
173
+ `register_post_round_hook` — public registration API; rejects duplicate `name`
174
+ - `agent_runner.hooks.plugin_context_enrichers()` — sorted list of registered
175
+ enricher names, surfaced via `peek --json`
176
+ - `agent_runner.api_types.RoundResult` — promoted from `runner.py`; superset of
177
+ the prior internal fields plus `phase`, `started_at`, `ended_at`, `log_path` for
178
+ stable `PostRoundHook` consumption
179
+ - `agent_runner` package now also discovers and loads three new entry_points groups
180
+ at first import: `agent_runner.pre_round_hooks`, `agent_runner.context_enrichers`,
181
+ `agent_runner.post_round_hooks`; plugin failures degrade to a `UserWarning`
182
+ - Built-in event kind `hook_failed` — emitted by the runner whenever a plugin hook
183
+ raises; payload includes `{hook_name, hook_kind, error_type, error_message, traceback}`
184
+ with traceback truncated to 2KB (head 1KB + tail 1KB joined by `[truncated]`)
185
+ - `peek --json` schema bumped to `"1.2"`; new `plugins.context_enrichers: list[str]`
186
+ - `docs/plugins.md` — new chapter with end-to-end ContextEnricher example
187
+ - New invariant `tests/invariants/test_round_result_stable.py` guards `RoundResult`
188
+ field set + types across future minors
189
+
190
+ ### Changed
191
+ - `agent_runner.runner.run_one_round` integrates hooks at three checkpoints; each
192
+ call is wrapped in `try/except` and surfaces failures via `hook_failed`
193
+ - `tests/invariants/test_peek_schema_version.py` tightened to require
194
+ `schema_version >= "1.2"` and the `plugins.context_enrichers` list shape
195
+
196
+ ### Backward compatibility
197
+ - Zero plugins installed → runner behavior identical to 0.1.3
198
+ - `RoundResult` field set is a superset; every prior field is preserved
199
+ - Existing 0.1.x user `agent-runner.toml` files load without modification
200
+
201
+ ## [0.1.3] - 2026-05-12
202
+
203
+ ### Added
204
+ - `agent_runner.events.register_event_kind(name, *, source)` — public API for plugins to register custom event kinds
205
+ - `agent_runner.events.plugin_event_kinds()` — sorted list of currently-registered plugin event kind names
206
+ - `agent_runner` package now discovers and loads entry_points in group `agent_runner.event_kinds` at first import; plugin failures degrade to a `UserWarning` instead of crashing the supervisor
207
+ - `peek --json` schema bumped to `"1.1"`; new top-level `plugins.event_kinds: list[str]` surfaces what's registered
208
+ - `docs/plugins.md` — plugin authoring stub covering the entry_points convention and the event-kind example
209
+
210
+ ### Changed
211
+ - `agent_runner.events.KNOWN_EVENT_KINDS` is now a read-only union view (built-in + plugin) instead of a `frozenset`; `in` and iteration semantics preserved
212
+ - `tests/invariants/test_event_kind_registry.py` rewritten to validate the built-in/plugin split and the registration conflict rules
213
+ - `tests/invariants/test_peek_schema_version.py` tightened to require `schema_version >= "1.1"` and the `plugins.event_kinds` list shape
214
+
215
+ ### Backward compatibility
216
+ - Every existing `from agent_runner.events import KNOWN_EVENT_KINDS` import continues to work
217
+ - Every existing `events.emit(...)` callsite continues to work with the same kind strings
218
+ - Existing 0.1.x user `agent-runner.toml` files load without modification
219
+
220
+ ## [0.1.2] - 2026-05-12
221
+
222
+ ### Added
223
+ - `cfg.agent.name` — optional provider identifier; defaults to `None` (consumers may fall back to `command[0]`)
224
+ - `cfg.prompt.context_injection_mode` — `prepend` (default) / `file` / `none`; controls how round-context reaches the agent
225
+ - `cfg.monitor.auth_fail_patterns` and `cfg.monitor.auth_fail_hint` — generalize the OAuth-fail detector for any provider
226
+ - `cfg.plugins` — placeholder for 0.1.3+ plugin enable/disable; parsed-but-unused in 0.1.2
227
+ - `peek --json` now emits top-level `schema_version: "1.0"` and `plugins: {}` namespace
228
+
229
+ ### Changed
230
+ - `startup_check` prompt-smoke error text reframed away from provider-specific wording
231
+ - `scaffold.py` generated TOML annotates `[agent]` block as the reference; encourages swapping for other CLIs
232
+ - Public docs (architecture, quickstart, configuration) reframed: the reference agent is one of many supported CLIs
233
+ - `monitor.detect_oauth_fail` now reads patterns + hint from `cfg.monitor` (SSOT migrated from a hardcoded module constant to `MonitorConfig`)
234
+
235
+ ### Backward compatibility
236
+ - All defaults preserve the existing prepend-mode behavior
237
+ - Existing `agent-runner.toml` files continue to load without modification
238
+ - Existing tests pass without modification
239
+
240
+ ## [0.1.1] — 2026-05-12
241
+
242
+ Post-release polish: PyPI install path is documented as the primary entry,
243
+ the repository was renamed for naming parity with the distribution, and the
244
+ release workflow now auto-creates GitHub Releases.
245
+
246
+ ### Changed
247
+ - README + README.zh now show `pip install cli-agent-runner` as the primary
248
+ install path; the previous `git clone` flow moved to the Development section.
249
+ - Repository renamed from `wan9yu/agent-runner` to `wan9yu/cli-agent-runner`
250
+ for parity with the PyPI distribution name. All in-tree URL references
251
+ updated; GitHub redirects keep old links working.
252
+
253
+ ### Build & CI
254
+ - Release workflow now creates a GitHub Release after PyPI publish, attaches
255
+ the sdist + wheel as release artifacts, and pulls release notes from the
256
+ matching CHANGELOG section.
257
+
258
+ ## [0.1.0] — 2026-05-12
259
+
260
+ Initial public release on PyPI as `cli-agent-runner`.
261
+
262
+ ### Added
263
+ - Three-layer model: Round / Loop / Witness.
264
+ - 13 CLI verbs: `init`, `install`, `uninstall`, `start`, `stop`, `kill`,
265
+ `cancel`, `restart`, `status`, `round`, `serve`, `peek`, `watch`, `monitor`.
266
+ - 11 named defenses (round timeout, process group isolation, orphan stash
267
+ with SHA lock, set-diff classification, smoke check, flock concurrency,
268
+ atomic state writes, event kind registry, and others).
269
+ - 9 monitor detectors (`timeout_rate`, `hung`, `orphan_chain`,
270
+ `disk_warning`, `disk_critical`, `mem_pressure`, `smoke_fail_rate`,
271
+ `oauth_fail`, `network_fail`); `oauth_fail` and `disk_critical` auto-stop
272
+ the service.
273
+ - Local-only and remote (via ssh) monitor modes.
274
+ - Auto-generated docs (`./build.sh docs`) for defenses table, detectors,
275
+ events, config schema, CLI verbs.
276
+ - Literate quickstart: `docs/quickstart.md` is executed as a pytest test.
277
+ - Python public API (`from agent_runner import api`) mirroring CLI verbs.
278
+ - Phase 3 reservation: `[llm]` config section + `agent_runner.critic`
279
+ Protocol stubs (no implementation).
280
+
281
+ ### Documentation
282
+ - English README + full Chinese `README.zh.md`.
283
+ - 5 user-facing docs: quickstart, commands, configuration, runbook, architecture.
284
+
285
+ ### Build & CI
286
+ - GitHub Actions matrix: Python 3.11/3.12/3.13 × ubuntu / macos.
287
+ - Codecov-uploaded coverage from one canonical matrix cell.
288
+ - Tag-triggered release publishing to PyPI via Trusted Publishing OIDC,
289
+ gated by a manual approval on the `pypi` GitHub environment.
290
+
291
+ [Unreleased]: https://github.com/wan9yu/cli-agent-runner/compare/v0.1.1...HEAD
292
+ [0.1.1]: https://github.com/wan9yu/cli-agent-runner/releases/tag/v0.1.1
293
+ [0.1.0]: https://github.com/wan9yu/cli-agent-runner/releases/tag/v0.1.0
@@ -9,6 +9,7 @@ git clone https://github.com/wan9yu/cli-agent-runner.git
9
9
  cd cli-agent-runner
10
10
  python3 -m venv .venv && source .venv/bin/activate
11
11
  pip install -e ".[dev]"
12
+ git config core.hooksPath .githooks # enables the commit-msg lint hook
12
13
  ./build.sh check
13
14
  ```
14
15
 
@@ -16,6 +17,13 @@ pip install -e ".[dev]"
16
17
  + integration tests, the literate quickstart, and the docs CI gate. It's
17
18
  what GitHub Actions runs on every push and PR.
18
19
 
20
+ `git config core.hooksPath .githooks` activates the in-repo
21
+ [`.githooks/commit-msg`](.githooks/commit-msg) hook which rejects commit
22
+ messages containing `Co-Authored-By:` trailers, robot emojis, or other
23
+ AI-tool attribution patterns. The same check runs in CI (`lint-commits`
24
+ job) and as a pytest invariant (`tests/invariants/test_no_ai_signatures.py`)
25
+ — defense in depth.
26
+
19
27
  ## Workflow
20
28
 
21
29
  1. Open an issue first for non-trivial changes — saves wasted work on both sides.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-agent-runner
3
- Version: 0.1.1
3
+ Version: 0.1.7
4
4
  Summary: Restart-on-exit supervisor for autonomous CLI agents
5
5
  Project-URL: Homepage, https://github.com/wan9yu/cli-agent-runner
6
6
  Project-URL: Documentation, https://github.com/wan9yu/cli-agent-runner#readme
@@ -37,14 +37,15 @@ Description-Content-Type: text/markdown
37
37
 
38
38
  > **[中文](README.zh.md)** · English
39
39
 
40
- [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Python](https://img.shields.io/pypi/pyversions/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
40
+ [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Downloads](https://img.shields.io/pypi/dm/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner)
41
41
 
42
42
  # agent-runner
43
43
 
44
- A restart-on-exit supervisor for autonomous CLI agents. Spawn an agent (Claude
45
- Code, a custom CLI, anything) round-after-round under defenses that prevent
46
- the failure modes that bite in production: stuck rounds, orphan commits,
47
- OAuth burn loops, full disks, runaway memory.
44
+ A restart-on-exit supervisor for autonomous coding CLIs. Tested with Claude
45
+ Code and aider out of the box; any prompt-arg CLI via custom config. Spawn
46
+ the agent round-after-round under defenses that prevent the failure modes
47
+ that bite in production: stuck rounds, orphan commits, OAuth burn loops,
48
+ full disks, runaway memory.
48
49
 
49
50
  ```
50
51
  ┌──────────────────────────────────────────┐
@@ -130,12 +131,6 @@ agent-runner monitor --json | jq -c # pipe to downstream consumers
130
131
  - [`docs/runbook.md`](docs/runbook.md) — operator troubleshooting (OAuth, disk, orphan)
131
132
  - [`docs/architecture.md`](docs/architecture.md) — 3-layer model, defenses-as-data
132
133
 
133
- ## Status
134
-
135
- Phase 2 (operator surface) shipped. Phase 3 (LLM-augmented Critic) reserved —
136
- `[llm]` config block and `agent_runner.critic` Protocol stubs are in place,
137
- implementation TBD.
138
-
139
134
  ## Development
140
135
 
141
136
  ```bash
@@ -1,13 +1,14 @@
1
1
  > **[中文](README.zh.md)** · English
2
2
 
3
- [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Python](https://img.shields.io/pypi/pyversions/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
3
+ [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Downloads](https://img.shields.io/pypi/dm/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner)
4
4
 
5
5
  # agent-runner
6
6
 
7
- A restart-on-exit supervisor for autonomous CLI agents. Spawn an agent (Claude
8
- Code, a custom CLI, anything) round-after-round under defenses that prevent
9
- the failure modes that bite in production: stuck rounds, orphan commits,
10
- OAuth burn loops, full disks, runaway memory.
7
+ A restart-on-exit supervisor for autonomous coding CLIs. Tested with Claude
8
+ Code and aider out of the box; any prompt-arg CLI via custom config. Spawn
9
+ the agent round-after-round under defenses that prevent the failure modes
10
+ that bite in production: stuck rounds, orphan commits, OAuth burn loops,
11
+ full disks, runaway memory.
11
12
 
12
13
  ```
13
14
  ┌──────────────────────────────────────────┐
@@ -93,12 +94,6 @@ agent-runner monitor --json | jq -c # pipe to downstream consumers
93
94
  - [`docs/runbook.md`](docs/runbook.md) — operator troubleshooting (OAuth, disk, orphan)
94
95
  - [`docs/architecture.md`](docs/architecture.md) — 3-layer model, defenses-as-data
95
96
 
96
- ## Status
97
-
98
- Phase 2 (operator surface) shipped. Phase 3 (LLM-augmented Critic) reserved —
99
- `[llm]` config block and `agent_runner.critic` Protocol stubs are in place,
100
- implementation TBD.
101
-
102
97
  ## Development
103
98
 
104
99
  ```bash
@@ -1,6 +1,6 @@
1
1
  > 中文 · **[English](README.md)**
2
2
 
3
- [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Python](https://img.shields.io/pypi/pyversions/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
3
+ [![CI](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml/badge.svg)](https://github.com/wan9yu/cli-agent-runner/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![Downloads](https://img.shields.io/pypi/dm/cli-agent-runner.svg)](https://pypi.org/project/cli-agent-runner/) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![codecov](https://codecov.io/gh/wan9yu/cli-agent-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/wan9yu/cli-agent-runner)
4
4
 
5
5
  # agent-runner
6
6
 
@@ -151,11 +151,6 @@ pip install -e ".[dev]"
151
151
 
152
152
  只支持 POSIX(Linux / macOS)。Python 3.11+。x86_64 与 aarch64 都跑过。
153
153
 
154
- ## 项目状态
155
-
156
- Phase 2(运维界面)已发。Phase 3(LLM Critic)留了接口:`[llm]` 配置段 +
157
- `agent_runner.critic` Protocol stubs,实现待定。
158
-
159
154
  ## License
160
155
 
161
156
  [Apache License 2.0](LICENSE).
@@ -0,0 +1,54 @@
1
+ """Agent Runner — restart-on-exit supervisor for autonomous CLI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ try:
6
+ from agent_runner._version import __version__
7
+ except ImportError: # editable install before hatch-vcs has generated _version.py
8
+ __version__ = "0.0.0+unknown"
9
+
10
+ _HOOK_GROUPS = (
11
+ "agent_runner.pre_round_hooks",
12
+ "agent_runner.context_enrichers",
13
+ "agent_runner.post_round_hooks",
14
+ )
15
+
16
+
17
+ def _load_plugins_from_group(group: str) -> None:
18
+ """Discover and load entry_points in ``group``, isolating per-plugin failures.
19
+
20
+ Called at package import. A broken plugin must not crash the supervisor;
21
+ each failure surfaces as a ``UserWarning``.
22
+ """
23
+ import warnings
24
+ from importlib.metadata import entry_points
25
+
26
+ for ep in entry_points(group=group):
27
+ try:
28
+ ep.load()
29
+ except Exception as e:
30
+ warnings.warn(
31
+ f"failed to load {group} plugin {ep.name!r}: {e}",
32
+ stacklevel=3,
33
+ )
34
+
35
+
36
+ def _load_event_kind_plugins() -> None:
37
+ """Load plugins that register custom event kinds via ``events.register_event_kind``."""
38
+ _load_plugins_from_group("agent_runner.event_kinds")
39
+
40
+
41
+ def _load_hook_plugins() -> None:
42
+ """Load plugins that register pre_round / context_enricher / post_round hooks."""
43
+ for group in _HOOK_GROUPS:
44
+ _load_plugins_from_group(group)
45
+
46
+
47
+ def _load_detector_plugins() -> None:
48
+ """Load plugins that register custom monitor detectors via ``monitor.register_detector``."""
49
+ _load_plugins_from_group("agent_runner.detectors")
50
+
51
+
52
+ _load_event_kind_plugins()
53
+ _load_hook_plugins()
54
+ _load_detector_plugins()
@@ -16,6 +16,7 @@ from pathlib import Path
16
16
  from agent_runner.config import (
17
17
  AgentConfig,
18
18
  Config,
19
+ MonitorConfig,
19
20
  PromptConfig,
20
21
  RuntimeConfig,
21
22
  VcsConfig,
@@ -29,6 +30,7 @@ _SECTIONS = [
29
30
  ("runtime", RuntimeConfig),
30
31
  ("prompt", PromptConfig),
31
32
  ("vcs", VcsConfig),
33
+ ("monitor", MonitorConfig),
32
34
  ]
33
35
 
34
36
 
@@ -0,0 +1,23 @@
1
+ """Shared registry helpers for plugin-extension surfaces.
2
+
3
+ The `name`-keyed unique-registration check is identical across hooks
4
+ (:mod:`agent_runner.hooks`) and detectors (:mod:`agent_runner.monitor`).
5
+ This module is its single source of truth.
6
+
7
+ Event-kind registration in :mod:`agent_runner.events` has DIFFERENT
8
+ semantics (idempotent for same-source re-registration, conflict on
9
+ different-source) and stays in that module.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+
15
+ def ensure_unique(name: str, existing: list, kind: str) -> None:
16
+ """Raise ValueError if any item in ``existing`` already has ``.name == name``.
17
+
18
+ ``kind`` is a short label embedded in the error message (e.g. ``"detector"``,
19
+ ``"context_enricher"``).
20
+ """
21
+ for item in existing:
22
+ if getattr(item, "name", None) == name:
23
+ raise ValueError(f"{kind} {name!r} already registered; refusing to add a second")
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.1'
22
- __version_tuple__ = version_tuple = (0, 1, 1)
21
+ __version__ = version = '0.1.7'
22
+ __version_tuple__ = version_tuple = (0, 1, 7)
23
23
 
24
24
  __commit_id__ = commit_id = None