cli-agent-runner 0.1.6__tar.gz → 0.1.8__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 (143) hide show
  1. cli_agent_runner-0.1.8/.githooks/commit-msg +33 -0
  2. cli_agent_runner-0.1.8/.github/workflows/ci.yml +105 -0
  3. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/CHANGELOG.md +111 -0
  4. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/CONTRIBUTING.md +8 -0
  5. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/PKG-INFO +6 -5
  6. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/README.md +5 -4
  7. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/_version.py +2 -2
  8. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/agent_runtime.py +8 -19
  9. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/api.py +22 -2
  10. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/api_types.py +2 -0
  11. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/common.py +6 -2
  12. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/init_cmd.py +8 -2
  13. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/config.py +8 -4
  14. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/defenses.py +4 -4
  15. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/events.py +6 -1
  16. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/monitor.py +2 -2
  17. cli_agent_runner-0.1.8/agent_runner/presets/aider.toml +30 -0
  18. cli_agent_runner-0.1.8/agent_runner/presets/claude.toml +29 -0
  19. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/runner.py +4 -4
  20. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/scaffold.py +26 -38
  21. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/vcs_state.py +60 -1
  22. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/README.md +9 -6
  23. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/architecture.md +6 -6
  24. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/configuration.md +33 -6
  25. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/plugins.md +107 -10
  26. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/quickstart.md +11 -6
  27. cli_agent_runner-0.1.8/docs/recipes/aider.md +117 -0
  28. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/runbook.md +14 -3
  29. cli_agent_runner-0.1.8/tests/_test_helpers.py +44 -0
  30. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/conftest.py +27 -0
  31. cli_agent_runner-0.1.8/tests/contract/test_public_api_surface.py +147 -0
  32. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_context_enricher_namespacing.py +2 -10
  33. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_plugin_detector_loaded.py +2 -10
  34. cli_agent_runner-0.1.8/tests/integration/test_plugin_owned_paths.py +88 -0
  35. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_run_one_round_with_fake_agent.py +39 -0
  36. cli_agent_runner-0.1.8/tests/integration/test_scaffold_presets.py +80 -0
  37. cli_agent_runner-0.1.8/tests/invariants/test_module_sizes.py +22 -0
  38. cli_agent_runner-0.1.8/tests/invariants/test_no_ai_signatures.py +83 -0
  39. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_peek_schema_version.py +2 -2
  40. cli_agent_runner-0.1.8/tests/literate/__init__.py +0 -0
  41. cli_agent_runner-0.1.8/tests/unit/__init__.py +0 -0
  42. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_agent_runtime.py +23 -12
  43. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_api_observation.py +59 -0
  44. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_api_types.py +33 -0
  45. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_cli_init_install.py +27 -0
  46. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_config.py +96 -14
  47. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_defenses.py +26 -2
  48. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_detector_protocol.py +3 -10
  49. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_events.py +3 -10
  50. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_hook_failure_isolation.py +2 -10
  51. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_hooks.py +6 -17
  52. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_monitor_detectors.py +5 -5
  53. cli_agent_runner-0.1.8/tests/unit/test_presets.py +87 -0
  54. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_scaffold.py +38 -0
  55. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_service_unit.py +1 -1
  56. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_vcs_state.py +79 -0
  57. cli_agent_runner-0.1.6/.github/workflows/ci.yml +0 -57
  58. cli_agent_runner-0.1.6/tests/invariants/test_module_sizes.py +0 -46
  59. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.codecov.yml +0 -0
  60. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  61. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  62. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  63. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  64. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.github/workflows/release.yml +0 -0
  65. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.gitignore +0 -0
  66. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/.vulture-whitelist.py +0 -0
  67. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/CODE_OF_CONDUCT.md +0 -0
  68. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/LICENSE +0 -0
  69. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/README.zh.md +0 -0
  70. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/SECURITY.md +0 -0
  71. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/__init__.py +0 -0
  72. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/_docgen.py +0 -0
  73. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/_registry.py +0 -0
  74. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/__init__.py +0 -0
  75. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/__main__.py +0 -0
  76. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/install_cmd.py +0 -0
  77. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/monitor_cmd.py +0 -0
  78. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/peek_cmd.py +0 -0
  79. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/round_cmd.py +0 -0
  80. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/serve_cmd.py +0 -0
  81. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/cli/service_cmd.py +0 -0
  82. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/context_store.py +0 -0
  83. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/detector_helpers.py +0 -0
  84. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/hooks.py +0 -0
  85. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/lifecycle.py +0 -0
  86. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/metrics.py +0 -0
  87. {cli_agent_runner-0.1.6/tests → cli_agent_runner-0.1.8/agent_runner/presets}/__init__.py +0 -0
  88. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/prompt_loader.py +0 -0
  89. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/round_view.py +0 -0
  90. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/service_unit.py +0 -0
  91. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/agent_runner/startup_check.py +0 -0
  92. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/build.sh +0 -0
  93. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/deploy/example-agent-runner.toml +0 -0
  94. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/deploy/launchd.plist.tmpl +0 -0
  95. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/deploy/run-loop.sh +0 -0
  96. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/deploy/systemd.service.tmpl +0 -0
  97. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/docs/commands.md +0 -0
  98. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/pyproject.toml +0 -0
  99. {cli_agent_runner-0.1.6/tests/e2e → cli_agent_runner-0.1.8/tests}/__init__.py +0 -0
  100. {cli_agent_runner-0.1.6/tests/integration → cli_agent_runner-0.1.8/tests/contract}/__init__.py +0 -0
  101. {cli_agent_runner-0.1.6/tests/invariants → cli_agent_runner-0.1.8/tests/e2e}/__init__.py +0 -0
  102. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/e2e/conftest.py +0 -0
  103. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  104. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/e2e/test_e2e_install_systemd.py +0 -0
  105. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  106. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  107. {cli_agent_runner-0.1.6/tests/literate → cli_agent_runner-0.1.8/tests/integration}/__init__.py +0 -0
  108. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_install_dry_run.py +0 -0
  109. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_monitor_seeded.py +0 -0
  110. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_run_loop_backoff.py +0 -0
  111. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/integration/test_serve_loop.py +0 -0
  112. {cli_agent_runner-0.1.6/tests/unit → cli_agent_runner-0.1.8/tests/invariants}/__init__.py +0 -0
  113. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_architecture.py +0 -0
  114. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_atomic_write_enforced.py +0 -0
  115. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_catalogs.py +0 -0
  116. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_docs_generated.py +0 -0
  117. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_event_kind_registry.py +0 -0
  118. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_module_boundaries.py +0 -0
  119. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  120. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  121. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_round_result_stable.py +0 -0
  122. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  123. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/literate/parser.py +0 -0
  124. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/literate/test_parser.py +0 -0
  125. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/literate/test_quickstart.py +0 -0
  126. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_api_service.py +0 -0
  127. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_auto_stop_gating.py +0 -0
  128. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_cli.py +0 -0
  129. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_cli_common.py +0 -0
  130. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  131. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_context_store.py +0 -0
  132. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_detector_helpers.py +0 -0
  133. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_docgen.py +0 -0
  134. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_init_entry_points.py +0 -0
  135. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_lifecycle.py +0 -0
  136. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_metrics.py +0 -0
  137. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_monitor_assembly.py +0 -0
  138. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_monitor_remote.py +0 -0
  139. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_peek_argparse.py +0 -0
  140. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_prompt_loader.py +0 -0
  141. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_round_view.py +0 -0
  142. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_runner.py +0 -0
  143. {cli_agent_runner-0.1.6 → cli_agent_runner-0.1.8}/tests/unit/test_startup_check.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 }}
@@ -7,6 +7,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.8] - 2026-05-13
11
+
12
+ ### Acknowledgements
13
+
14
+ Thanks to the argus-gateway team for Phase 4 dogfooding feedback that drove
15
+ every item in this release. 3 audit memos (~90KB) silently swept into an
16
+ orphan stash is a real-world failure mode; this release closes that loop.
17
+
18
+ ### Added
19
+
20
+ - `agent_runner.vcs_state.register_plugin_owned_paths()` — plugins opt-out
21
+ files/dirs from orphan-stash defense. Matching: trailing-slash prefix or
22
+ `pathlib.PurePath.match` glob (recognizes `**` for recursive segments via
23
+ `fnmatch` fallback on Python 3.11). Call at module import (entry_point
24
+ side-effect).
25
+ - `agent_runner.vcs_state.plugin_owned_paths()` — snapshot accessor for peek.
26
+ - `ProjectState.recent_hook_failures: list[dict]` — last 10 `hook_failed`
27
+ events filtered from `recent_events` for debugging hook integration.
28
+ - peek schema bumped 1.4 → 1.5. `plugins` block now includes
29
+ `pre_round_hooks`, `post_round_hooks`, `owned_paths` lists.
30
+
31
+ ### Changed
32
+
33
+ - `docs/plugins.md` register-pattern examples corrected: registration must
34
+ happen as module-top side effect; entry_point loaders only import, they
35
+ do not invoke. Old `_register()` wrapper pattern silently didn't fire.
36
+ - `docs/plugins.md` gained "Declaring plugin-owned paths" and "Plugin tests
37
+ + consumer pytest collision" sections.
38
+
39
+ ### Fixed
40
+
41
+ - Plugin outputs in plugin-declared paths (e.g. `proposals/`,
42
+ `logs/plugins/my_plugin/`) no longer silently swept into orphan stashes
43
+ by `process_orphan_wip`. Previously: 90KB Argus audit memos invisible
44
+ after Phase 4 round; required stash archaeology to recover.
45
+
46
+ ### Migration
47
+
48
+ No breaking changes. Plugin authors:
49
+
50
+ - If your plugin writes files to `work_dir` and they keep getting stashed
51
+ between rounds, opt them out:
52
+ ```python
53
+ from agent_runner.vcs_state import register_plugin_owned_paths
54
+ register_plugin_owned_paths(["your-output-dir/", "logs/your-plugin/**/*"])
55
+ ```
56
+ - If you followed the old `_register()` pattern from docs and noticed
57
+ registrations not firing: move the call to module top:
58
+ ```python
59
+ # was: def _register(): register_pre_round_hook(MyHook())
60
+ # now: register_pre_round_hook(MyHook()) # module-top side-effect
61
+ ```
62
+
63
+ ## [0.1.7] - 2026-05-13
64
+
65
+ ### Migration for existing 0.1.6 users (DOWNSTREAM CONSUMERS READ THIS)
66
+
67
+ If you maintain an `agent-runner.toml` by hand (rather than via `agent-runner init`),
68
+ you must add an `[agent.env]` block to preserve the Claude self-update suppression
69
+ that 0.1.6 injected implicitly. **Without this, mid-loop self-updates can race with
70
+ the supervisor.**
71
+
72
+ Add to your `agent-runner.toml`:
73
+
74
+ ```toml
75
+ [agent.env]
76
+ DISABLE_AUTOUPDATER = "1"
77
+ CLAUDE_CODE_EFFORT_LEVEL = "xhigh"
78
+ ```
79
+
80
+ Or regenerate cleanly:
81
+
82
+ ```bash
83
+ agent-runner init --preset claude --force
84
+ ```
85
+
86
+ Plugin authors (Argus Gateway, etc.): no public API was renamed or removed
87
+ from your import surface. The deleted symbols (`agent_runner.agent_runtime.CRITICAL_ENV_DEFAULTS`,
88
+ `agent_runner.agent_runtime.merge_critical_envs`) were internal — not part of
89
+ the documented plugin API. A new public-API contract test
90
+ (`tests/contract/test_public_api_surface.py`) locks in `api_types`, `events`,
91
+ `hooks`, `monitor`, `detector_helpers` import surfaces so future refactors
92
+ can't silently drop names you rely on.
93
+
94
+ ### Added
95
+ - `agent-runner init --preset {claude,aider}` selects between bundled CLI presets
96
+ (default: `claude`). New preset directory: `agent_runner/presets/`.
97
+ - `[agent.env]` TOML block — per-CLI env injections, replacing the hardcoded
98
+ `CRITICAL_ENV_DEFAULTS` constant. Empty dict by default.
99
+ - `docs/recipes/aider.md` — aider integration recipe.
100
+ - `tests/contract/test_public_api_surface.py` — public-API surface snapshot for
101
+ plugin authors.
102
+
103
+ ### Changed
104
+ - Core code (`agent_runtime.py`, `config.py`, `runner.py`, `scaffold.py`,
105
+ `defenses.py`) is now truly provider-agnostic: zero hardcoded Claude defaults.
106
+ Claude remains the reference example throughout the docs, but its specifics
107
+ live in `agent_runner/presets/claude.toml` (shipped as package data).
108
+ - `MonitorConfig.auth_fail_hint` default is now empty string; per-CLI hint
109
+ comes from the preset.
110
+ - `PEEK_SCHEMA_VERSION` bumped 1.3 → 1.4. `InitResult` gains `preset: str` field.
111
+ - `agent_runner/defenses.py` `critical_envs_injection` row now reads from
112
+ `cfg.agent.env.keys()` (not the deleted constant); state is "active" iff
113
+ the config defines any env injections, else "off".
114
+
115
+ ### Removed
116
+ - `agent_runner.agent_runtime.CRITICAL_ENV_DEFAULTS` constant.
117
+ - `agent_runner.agent_runtime.merge_critical_envs()` function.
118
+ - `agent_runner.config._DEFAULT_AUTH_HINT` constant's Claude-specific default
119
+ string (replaced with `""`; per-CLI hint now in preset files).
120
+
10
121
  ## [0.1.6] - 2026-05-12
11
122
 
12
123
  Zero-feature maintenance release — internal cleanup pass after the 0.1.x plugin
@@ -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.6
3
+ Version: 0.1.8
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
@@ -41,10 +41,11 @@ Description-Content-Type: text/markdown
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
  ┌──────────────────────────────────────────┐
@@ -4,10 +4,11 @@
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
  ┌──────────────────────────────────────────┐
@@ -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.6'
22
- __version_tuple__ = version_tuple = (0, 1, 6)
21
+ __version__ = version = '0.1.8'
22
+ __version_tuple__ = version_tuple = (0, 1, 8)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1,10 +1,11 @@
1
- """Agent subprocess management — ONLY module that spawns the claude CLI.
1
+ """Agent subprocess management — spawns the configured agent CLI process.
2
2
 
3
3
  Defenses encoded here:
4
4
  - R725: SIGTERM handler reaps process group before runner exits
5
5
  - R1128: ROUND_TIMEOUT is wall-clock hard wall (no activity-based extension)
6
6
  - #307: start_new_session=True isolates subprocess in its own pgrp
7
- - env injection: DISABLE_AUTOUPDATER=1 + CLAUDE_CODE_EFFORT_LEVEL caller-provided
7
+ - env injection: per-CLI envs come from AgentConfig.env (preset-supplied);
8
+ no implicit injection in this module.
8
9
  """
9
10
 
10
11
  from __future__ import annotations
@@ -97,26 +98,14 @@ def run(
97
98
  log_file.close()
98
99
 
99
100
 
100
- CRITICAL_ENV_DEFAULTS: dict[str, str] = {
101
- "DISABLE_AUTOUPDATER": "1", # do not let claude self-update mid-loop
102
- "CLAUDE_CODE_EFFORT_LEVEL": "xhigh", # full effort, not default
103
- }
104
-
105
-
106
- def merge_critical_envs(user_env: dict[str, str]) -> dict[str, str]:
107
- """Merge user env with CRITICAL_ENV_DEFAULTS — critical always wins."""
108
- merged = dict(user_env)
109
- merged.update(CRITICAL_ENV_DEFAULTS)
110
- return merged
111
-
112
-
113
101
  def install_sigterm_reaper(reaper: Callable[[], None]) -> object:
114
102
  """Install a SIGTERM handler that calls ``reaper()`` first.
115
103
 
116
- R725 defense: when supervisor receives SIGTERM (e.g. systemctl stop, manual
117
- kill), bash wrapper would otherwise respawn fresh runner while old claude
118
- keeps running → two claudes race on the same git tree, second commit can
119
- swallow first commit's chat-room entry. Reaper terminates pgroup first.
104
+ R725 defense: when the supervisor receives SIGTERM (e.g. systemctl stop,
105
+ manual kill), the bash wrapper would otherwise respawn a fresh runner
106
+ while the old agent keeps running → two agent processes race on the same
107
+ git tree, the second commit can swallow the first commit's chat-room
108
+ entry. Reaper terminates pgroup first.
120
109
 
121
110
  Returns the previous SIGTERM handler so caller can restore it.
122
111
  """
@@ -71,10 +71,16 @@ def _systemctl_user(*args: str) -> None:
71
71
  # init / install / uninstall
72
72
 
73
73
 
74
- def init(work_dir: Path | None = None, *, force: bool = False, commit: bool = True) -> InitResult:
74
+ def init(
75
+ work_dir: Path | None = None,
76
+ *,
77
+ preset: str = "claude",
78
+ force: bool = False,
79
+ commit: bool = True,
80
+ ) -> InitResult:
75
81
  if work_dir is None:
76
82
  work_dir = Path.cwd()
77
- return scaffold_project(work_dir, force=force, commit=commit)
83
+ return scaffold_project(work_dir, preset=preset, force=force, commit=commit)
78
84
 
79
85
 
80
86
  def install(
@@ -234,6 +240,9 @@ def _log_dir_for_project(project: str | Path) -> Path:
234
240
  # for callers that only use lifecycle verbs.
235
241
 
236
242
  from agent_runner import defenses, monitor # noqa: E402
243
+ from agent_runner.events import HOOK_FAILED # noqa: E402
244
+
245
+ _RECENT_HOOK_FAILURES_LIMIT = 10
237
246
 
238
247
 
239
248
  def peek(
@@ -260,6 +269,16 @@ def peek(
260
269
  if current is None:
261
270
  raise KeyError(f"round {round_num} not found under {log_dir}/rounds/")
262
271
  recent = parsed_events[-events:] if events else []
272
+ # Walk the tail in reverse so we stop as soon as the limit is filled.
273
+ # parsed_events grows unboundedly over a project's lifetime; a full-scan
274
+ # comprehension here would dominate watch-loop peek cost.
275
+ recent_hook_failures: list[dict[str, Any]] = []
276
+ for e in reversed(parsed_events):
277
+ if e.get("event") == HOOK_FAILED:
278
+ recent_hook_failures.append(e)
279
+ if len(recent_hook_failures) == _RECENT_HOOK_FAILURES_LIMIT:
280
+ break
281
+ recent_hook_failures.reverse()
263
282
 
264
283
  state = ProjectState(
265
284
  project=base_state.project,
@@ -280,6 +299,7 @@ def peek(
280
299
  system=base_state.system,
281
300
  service=status(project if project is not None else work_dir),
282
301
  recent_events=recent,
302
+ recent_hook_failures=recent_hook_failures,
283
303
  )
284
304
  return state if select is None else select_path(state, select)
285
305
 
@@ -72,6 +72,7 @@ class ProjectState:
72
72
  system: SystemMetrics
73
73
  service: ServiceStatus
74
74
  recent_events: list[dict[str, Any]] = field(default_factory=list)
75
+ recent_hook_failures: list[dict[str, Any]] = field(default_factory=list)
75
76
 
76
77
 
77
78
  @dataclass(frozen=True)
@@ -130,6 +131,7 @@ class InitResult:
130
131
  work_dir: Path
131
132
  files_created: list[Path]
132
133
  committed: bool
134
+ preset: str = "claude" # 0.1.7+; default for backward compat with synthesised InitResults
133
135
 
134
136
 
135
137
  @dataclass(frozen=True)
@@ -12,10 +12,11 @@ from typing import Any
12
12
  from agent_runner.api_types import ProjectState
13
13
  from agent_runner.config import Config, load_config
14
14
  from agent_runner.events import plugin_event_kinds
15
- from agent_runner.hooks import plugin_context_enrichers
15
+ from agent_runner.hooks import plugin_context_enrichers, post_round_hooks, pre_round_hooks
16
16
  from agent_runner.monitor import plugin_detectors
17
+ from agent_runner.vcs_state import plugin_owned_paths
17
18
 
18
- PEEK_SCHEMA_VERSION = "1.3"
19
+ PEEK_SCHEMA_VERSION = "1.5"
19
20
 
20
21
 
21
22
  def cfg_from_args(args) -> Config:
@@ -48,7 +49,10 @@ def emit(value: Any, *, json_mode: bool) -> None:
48
49
  "plugins": {
49
50
  "event_kinds": plugin_event_kinds(),
50
51
  "context_enrichers": plugin_context_enrichers(),
52
+ "pre_round_hooks": [h.name for h in pre_round_hooks()],
53
+ "post_round_hooks": [h.name for h in post_round_hooks()],
51
54
  "detectors": plugin_detectors(),
55
+ "owned_paths": plugin_owned_paths(),
52
56
  },
53
57
  **_to_jsonable(value),
54
58
  }
@@ -8,6 +8,12 @@ from agent_runner.cli.common import emit, fail, work_dir_from_args
8
8
 
9
9
  def add_parser(sub, parent) -> None:
10
10
  p = sub.add_parser("init", parents=[parent], help="Scaffold agent-runner project files")
11
+ p.add_argument(
12
+ "--preset",
13
+ choices=["claude", "aider"],
14
+ default="claude",
15
+ help="Which agent CLI preset to scaffold (default: claude)",
16
+ )
11
17
  p.add_argument("--force", action="store_true", help="Overwrite existing toml")
12
18
  g = p.add_mutually_exclusive_group()
13
19
  g.add_argument(
@@ -24,8 +30,8 @@ def add_parser(sub, parent) -> None:
24
30
  def cmd(args) -> int:
25
31
  work_dir = work_dir_from_args(args)
26
32
  try:
27
- result = api.init(work_dir, force=args.force, commit=args.commit)
28
- except (FileExistsError, RuntimeError) as e:
33
+ result = api.init(work_dir, preset=args.preset, force=args.force, commit=args.commit)
34
+ except (FileExistsError, RuntimeError, FileNotFoundError) as e:
29
35
  return fail(str(e))
30
36
  emit(result, json_mode=getattr(args, "json", False))
31
37
  return 0
@@ -15,6 +15,7 @@ class AgentConfig:
15
15
  command: list[str]
16
16
  prompt_arg_template: list[str]
17
17
  name: str | None = None
18
+ env: dict[str, str] = field(default_factory=dict)
18
19
 
19
20
 
20
21
  @dataclass(frozen=True)
@@ -38,14 +39,16 @@ class VcsConfig:
38
39
  stash_idempotency_s: int = 5
39
40
 
40
41
 
41
- # Default auth-failure detection regex — claude-aware. Migrated from monitor.py
42
- # to config.py as the SSOT for the oauth_fail detector. Plugins / non-claude
43
- # providers override via [monitor].auth_fail_patterns.
42
+ # Default auth-failure detection regex — matches common OAuth/401/expired-session
43
+ # vocabularies. Presets override [monitor].auth_fail_hint per CLI.
44
44
  _DEFAULT_AUTH_PATTERNS: list[str] = [
45
45
  r"\b(oauth|unauthorized|401|api[_ ]key|"
46
46
  r"auth(entication)?[_ -]?(failed|error|expired)|session.*expired)\b",
47
47
  ]
48
- _DEFAULT_AUTH_HINT: str = "Run `claude /login` on the supervisor host or refresh ANTHROPIC_API_KEY"
48
+ # Default auth-failure hint is empty per-CLI hints come from preset files
49
+ # (agent_runner/presets/*.toml) which write `[monitor].auth_fail_hint` into the
50
+ # user's agent-runner.toml at scaffold time.
51
+ _DEFAULT_AUTH_HINT: str = ""
49
52
 
50
53
  # Default allow-list of detector names whose ``stop_service`` action is honored.
51
54
  # Plugin detectors must be added explicitly by the operator to opt them in.
@@ -94,6 +97,7 @@ def load_config(toml_path: Path) -> Config:
94
97
  command=list(_require(agent_d, "command")),
95
98
  prompt_arg_template=list(_require(agent_d, "prompt_arg_template")),
96
99
  name=agent_d.get("name"),
100
+ env={str(k): str(v) for k, v in agent_d.get("env", {}).items()},
97
101
  )
98
102
  raw_work_dir = str(_require(raw, "runtime", "work_dir"))
99
103
  work_dir = _expand_path(raw_work_dir, "").resolve()
@@ -13,7 +13,6 @@ from dataclasses import dataclass
13
13
  from pathlib import Path
14
14
  from typing import Any
15
15
 
16
- from agent_runner.agent_runtime import CRITICAL_ENV_DEFAULTS
17
16
  from agent_runner.config import Config
18
17
 
19
18
 
@@ -73,12 +72,13 @@ def catalog(cfg: Config) -> list[Defense]:
73
72
  ),
74
73
  Defense(
75
74
  name="critical_envs_injection",
76
- value=list(CRITICAL_ENV_DEFAULTS.keys()),
75
+ value=sorted(cfg.agent.env.keys()),
77
76
  codifies=(
78
- "DISABLE_AUTOUPDATER + CLAUDE_CODE_EFFORT_LEVEL stop claude self-updates mid-loop"
77
+ "Env injection via [agent.env] block — preset-supplied per CLI "
78
+ "(e.g. DISABLE_AUTOUPDATER for claude prevents mid-loop self-updates)"
79
79
  ),
80
80
  guarded_by=None,
81
- current_state="active",
81
+ current_state="active" if cfg.agent.env else "off",
82
82
  ),
83
83
  Defense(
84
84
  name="startup_smoke_check",
@@ -22,6 +22,11 @@ from datetime import UTC, datetime
22
22
  from pathlib import Path
23
23
  from typing import Any
24
24
 
25
+ # Cross-module event-kind constants. Most kinds are emitted in only one place
26
+ # (runner.py), but kinds that are also CONSUMED elsewhere (filtered, surfaced
27
+ # in peek, asserted in tests) earn a constant to keep the spelling honest.
28
+ HOOK_FAILED = "hook_failed"
29
+
25
30
  _BUILTIN_KINDS: frozenset[str] = frozenset(
26
31
  {
27
32
  "round_start",
@@ -38,7 +43,7 @@ _BUILTIN_KINDS: frozenset[str] = frozenset(
38
43
  "round_end",
39
44
  "monitor_alert_emitted",
40
45
  "monitor_auto_stop_triggered",
41
- "hook_failed",
46
+ HOOK_FAILED,
42
47
  }
43
48
  )
44
49
 
@@ -28,7 +28,7 @@ from agent_runner.api_types import (
28
28
  ServiceStatus,
29
29
  SystemMetrics,
30
30
  )
31
- from agent_runner.config import _DEFAULT_AUTH_HINT, _DEFAULT_AUTH_PATTERNS
31
+ from agent_runner.config import _DEFAULT_AUTH_PATTERNS
32
32
  from agent_runner.context_store import read_json
33
33
  from agent_runner.events import emit as emit_event
34
34
  from agent_runner.events import now_iso_ms, parse_iso_ms
@@ -278,7 +278,7 @@ def detect_oauth_fail(
278
278
  "matches": matches,
279
279
  "window": total,
280
280
  "threshold": threshold,
281
- "hint": hint if hint is not None else _DEFAULT_AUTH_HINT,
281
+ "hint": hint if hint is not None else "",
282
282
  },
283
283
  auto_action="stop_service",
284
284
  )
@@ -0,0 +1,30 @@
1
+ # agent-runner.toml — generated by `agent-runner init --preset aider`.
2
+ #
3
+ # Prereqs:
4
+ # - aider installed (`pipx install aider-chat`)
5
+ # - your model provider env var set (OPENAI_API_KEY / ANTHROPIC_API_KEY /
6
+ # DEEPSEEK_API_KEY / OPENROUTER_API_KEY / etc.) — `aider --models` lists
7
+ # detected providers.
8
+
9
+ [agent]
10
+ command = ["aider", "--yes-always", "--no-stream", "--analytics-disable"]
11
+ prompt_arg_template = ["--message", "{prompt}"]
12
+ name = "aider"
13
+ # [agent.env] omitted — aider needs no env injection.
14
+
15
+ [runtime]
16
+ work_dir = "."
17
+ log_dir = "~/.agent-runner/{project}/logs"
18
+ round_timeout_s = 1800
19
+ restart_delay_s = 3
20
+
21
+ [prompt]
22
+ file = "./prompts/main.md"
23
+ inject_context = true
24
+
25
+ [vcs]
26
+ orphan_action = "stash"
27
+ stash_idempotency_s = 5
28
+
29
+ [monitor]
30
+ auth_fail_hint = "Verify your model provider env var (OPENAI_API_KEY / ANTHROPIC_API_KEY / DEEPSEEK_API_KEY / etc.); run `aider --models` to list detected providers."
@@ -0,0 +1,29 @@
1
+ # agent-runner.toml — generated by `agent-runner init --preset claude`.
2
+
3
+ [agent]
4
+ command = ["claude", "--model", "claude-opus-4-7",
5
+ "--dangerously-skip-permissions",
6
+ "--verbose", "--output-format", "stream-json"]
7
+ prompt_arg_template = ["-p", "{prompt}"]
8
+ name = "claude"
9
+
10
+ [agent.env]
11
+ DISABLE_AUTOUPDATER = "1"
12
+ CLAUDE_CODE_EFFORT_LEVEL = "xhigh"
13
+
14
+ [runtime]
15
+ work_dir = "."
16
+ log_dir = "~/.agent-runner/{project}/logs"
17
+ round_timeout_s = 1800
18
+ restart_delay_s = 3
19
+
20
+ [prompt]
21
+ file = "./prompts/main.md"
22
+ inject_context = true
23
+
24
+ [vcs]
25
+ orphan_action = "stash"
26
+ stash_idempotency_s = 5
27
+
28
+ [monitor]
29
+ auth_fail_hint = "Run `claude /login` on the supervisor host or refresh ANTHROPIC_API_KEY."