-
Notifications
You must be signed in to change notification settings - Fork 41
Add the mngr_donate plugin (spend spare Claude capacity on a donation skill) #2417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jean-imbue
wants to merge
9
commits into
main
Choose a base branch
from
add-mngr-donate
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
feea6e2
Add the mngr_donate plugin (spend spare Claude capacity on a donation…
robocall 635a993
donate: pin the skill ref to a reviewed commit by default
robocall ca232f5
donate: sort test imports (ruff isort) after the plugin rename
robocall bdfe65e
Fix CI: ruff format, register donate in CLI docs
robocall 763c03d
donate: export a stashed long-lived OAuth token to the headless agent
robocall f940f9a
donate: forbid the donation agent from committing/pushing/opening PRs
robocall c677cc9
donate README: move authentication up front as one-time setup
robocall e490c18
Fix host-listing crash on Apple Silicon M4/M5 (psutil cpu_freq)
MT-GoCode a2beb0a
Fold cpu_freq fix into the add-mngr-donate changelog entry
MT-GoCode File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Registered the new `libs/mngr_donate` workspace member (root `pyproject.toml` coverage list + `uv.lock`). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| - Registered the new `imbue-mngr-donate` plugin (`mngr donate`) in the plugin catalog as a default-installable DEPENDENT gated on `imbue-mngr-usage`. | ||
|
|
||
| - Fixed a crash when listing the local host on newer Apple Silicon Macs (M4/M5). psutil's `cpu_freq()` reports the metric as available but raises when read (Apple changed the private CPU voltage-state data it parses), which surfaced as `Error processing host ...` during `mngr list` and any command built on it -- including `mngr donate`, which then wrongly reported "No Claude usage data". CPU frequency is now read defensively: if psutil can't read it, the frequency is reported as unavailable instead of aborting host discovery. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <!-- This file is auto-generated. Do not edit directly. --> | ||
| <!-- To modify, edit the command's help metadata and run: uv run python scripts/make_cli_docs.py --> | ||
|
|
||
| # mngr donate | ||
| **Usage:** | ||
|
|
||
| ```text | ||
| mngr donate [OPTIONS] | ||
| ``` | ||
| **Options:** | ||
|
|
||
| ## Common | ||
|
|
||
| | Name | Type | Description | Default | | ||
| | ---- | ---- | ----------- | ------- | | ||
| | `--format` | text | Output format (human, json, jsonl, FORMAT): Output format for results. When a template is provided, fields use standard python templating like 'name: {agent.name}' See below for available fields. | `human` | | ||
| | `-q`, `--quiet` | boolean | Suppress all console output | `False` | | ||
| | `-v`, `--verbose` | integer range | Increase verbosity (default: BUILD); -v for DEBUG, -vv for TRACE | `0` | | ||
| | `--log-file` | path | Path to log file (overrides default ~/.mngr/events/logs/<timestamp>-<pid>.json) | None | | ||
| | `--log-commands`, `--no-log-commands` | boolean | Log commands that were executed | None | | ||
| | `--headless` | boolean | Disable all interactive behavior (prompts, TUI, editor). Also settable via MNGR_HEADLESS env var or 'headless' config key. | `False` | | ||
| | `--safe` | boolean | Always query all providers during discovery (disable event-stream optimization). Use this when interfacing with mngr from multiple machines. | `False` | | ||
| | `--plugin`, `--enable-plugin` | text | Enable a plugin [repeatable] | None | | ||
| | `--disable-plugin` | text | Disable a plugin [repeatable] | None | | ||
| | `-S`, `--setting` | text | Override a config setting for this invocation (KEY=VALUE, dot-separated paths; append __extend to the leaf key to extend list/dict/set fields) [repeatable] | None | | ||
|
|
||
| ## Other Options | ||
|
|
||
| | Name | Type | Description | Default | | ||
| | ---- | ---- | ----------- | ------- | | ||
| | `--skill` | text | Donation skill to run (the subdir name under the host-dir skill cache). | `document-review` | | ||
| | `--skill-repo` | text | Upstream git repo the skill (code + prompts) is checked out from. | `https://gitlab.com/sinnott-armstrong-lab/elsi-checklist/credits-for-science/document-review-skill.git` | | ||
| | `--skill-ref` | text | Git ref to check out: a branch to track, or a pinned commit for a reviewed version. | `c2e9bbe799c20c9da3896c2205991164f10555fd` | | ||
| | `--agent-name` | text | Name for the created donation agent. | `donate-extra-quota-bio` | | ||
| | `--dry-run` | boolean | Report the spare-capacity decision without creating an agent. | `False` | | ||
| | `--start` | boolean | Schedule donate to run automatically (installs a launchd LaunchAgent; macOS only) and exit. | `False` | | ||
| | `--stop` | boolean | Remove the scheduled donate LaunchAgent and exit. | `False` | | ||
| | `--interval-minutes` | integer range | With --start: how often the scheduled donate runs. | `10` | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Changelog - mngr_donate | ||
|
|
||
| A concise, human-friendly summary of changes for the `mngr_donate` library. Entries are categorized using the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) categories: Added, Changed, Deprecated, Removed, Fixed, Security. | ||
|
|
||
| For the full, unedited changelog entries, see [UNABRIDGED_CHANGELOG.md](UNABRIDGED_CHANGELOG.md). | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
|
|
||
| - New `imbue-mngr-donate` plugin, extracted from `imbue-mngr-usage`: provides `mngr donate`, which spends spare Claude capacity on a donation skill (default: scientific `document-review`). Depends on `imbue-mngr-usage` for the spare-capacity snapshot. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # imbue-mngr-donate | ||
|
|
||
| `mngr donate` -- spend spare Claude capacity on a donation skill instead of letting | ||
| idle quota expire. | ||
|
|
||
| One invocation is a single tick: read the account-level usage snapshot (the same one | ||
| `mngr usage` shows), and *if* there's spare capacity (5h window under budget **and** the | ||
| week under its pace line), launch a headless Claude agent that runs a donation skill | ||
| (default: scientific `document-review`) to completion, then auto-cleans up. When there's | ||
| no spare capacity -- or no usage data to judge from -- it does nothing and says so. | ||
|
|
||
| This is a separate plugin from `imbue-mngr-usage` on purpose (measuring usage and donating | ||
| spare quota are orthogonal), but it depends on it: installing `imbue-mngr-donate` pulls in | ||
| `imbue-mngr-usage`, whose snapshot API + `usage` plugin config donate reads at runtime. | ||
|
|
||
| ## Before you start: authentication (one-time setup) | ||
|
|
||
| Do this first. The donation agent is **headless**, and headless Claude authenticates | ||
| differently from the Claude app you use interactively -- skipping this step is why a | ||
| donate setup "randomly breaks" hours after it worked: | ||
|
|
||
| - The desktop app's login popup (`/login`) renews **only the app's own session**. It does | ||
| not refresh the credentials headless `claude` reads. | ||
| - A plain web login yields a **short-lived (~8h) access token with no refresh token**. A | ||
| donate setup leaning on it works for a few hours, then every tick fails with | ||
| `Failed to authenticate. API Error: 401 Invalid authentication credentials` -- typically | ||
| overnight. | ||
|
|
||
| What donate needs instead is a **long-lived (~1 year) token** minted for exactly this | ||
| use case: | ||
|
|
||
| ```bash | ||
| # 1. Mint it (same login popup, but prints a long sk-ant-oat... token in the terminal -- | ||
| # copy ALL of it; it usually wraps across lines): | ||
| claude setup-token | ||
|
|
||
| # 2. Stash it in the macOS keychain (prompts for a "password" -- paste the token there, | ||
| # so it never lands in a file or shell history). Add -U to overwrite an existing entry: | ||
| security add-generic-password -s mngr-donate-oauth -a "$USER" -w | ||
| ``` | ||
|
|
||
| At each tick, donate looks for that `mngr-donate-oauth` keychain entry and exports it to the | ||
| agent as `CLAUDE_CODE_OAUTH_TOKEN` (which takes precedence over the session token). If | ||
| `CLAUDE_CODE_OAUTH_TOKEN` is already set in the environment, or the entry doesn't exist, the | ||
| environment is inherited unchanged -- so without the stash, donate still runs, but only as | ||
| long as your current session token does (and starts 401ing when it lapses). | ||
|
|
||
| ## Run a donation | ||
|
|
||
| ```bash | ||
| # From inside a trusted git repo (the agent is sourced from the current dir): | ||
| mngr donate # one tick: donate now if there's spare capacity | ||
| mngr donate --dry-run # show the decision + numbers, launch nothing | ||
| mngr donate --skill my-skill # run a different skill (default: document-review) | ||
| ``` | ||
|
|
||
| ## Schedule it (drain spare capacity over time) | ||
|
|
||
| A single tick spends at most one skill run's worth of quota. To actually *drain* spare | ||
| capacity, schedule it -- the schedule, not any one tick, is what uses up the idle quota: | ||
|
|
||
| ```bash | ||
| mngr donate --start # install a launchd LaunchAgent (every 10 min by default) | ||
| mngr donate --start --interval-minutes 5 | ||
| mngr donate --stop # remove it | ||
| ``` | ||
|
|
||
| `--start`/`--stop` are **macOS-only** and install a **launchd LaunchAgent** | ||
| (`com.imbue.mngr.donate` in `~/Library/LaunchAgents/`). launchd -- not cron -- because the | ||
| agent must run inside your **login session** to reach the macOS keychain where Claude's | ||
| subscription token lives; a cron job runs outside it and every tick fails `Not logged in`. | ||
| launchd also catches up after sleep. On other platforms, schedule `mngr donate` yourself. | ||
|
|
||
| ## Skills: pinned code, dynamic prompts | ||
|
|
||
| The donation skill (its code **and** prompts) lives in the lab's own upstream git repo -- | ||
| the single source of truth. `mngr donate` **checks it out** into a host-dir cache | ||
| (`<host_dir>/donate-skills/<skill>/`) and points the agent at it, so the lab can revise the | ||
| skill without an mngr release: | ||
|
|
||
| - `--skill-repo` — the upstream repo (default: the `document-review` skill's GitLab repo). | ||
| - `--skill-ref` — the git ref to check out. A **branch** to *track* (each tick adopts the | ||
| latest — good for a fast-moving lab), or a **pinned commit** for a reviewed, reproducible | ||
| version that imbue bumps deliberately. | ||
|
|
||
| **Pin the ref for anything unattended.** The donation agent runs with | ||
| `--dangerously-skip-permissions`, so whatever code is at `--skill-ref` executes on your | ||
| machine. Tracking a branch auto-adopts the lab's changes; pinning a reviewed commit is the | ||
| safe default for scheduled runs (bump the ref to adopt updates after review). | ||
|
|
||
| ## Notes | ||
|
|
||
| - **Run it from a trusted git repo.** The donation agent is created from the current | ||
| directory; `--start` bakes that directory (and your `PATH`) into the LaunchAgent. | ||
| - **It needs usage data.** Spare capacity is judged from the account-level snapshot, which is | ||
| populated by mngr-managed Claude agents. With none recorded recently, `donate` reports | ||
| "can't tell" and skips rather than guessing. | ||
| - **Logs.** Each run's full event stream is tee'd to `<host_dir>/donate-logs/<agent>-<ts>.jsonl`, | ||
| and scheduled runs also append to `<host_dir>/donate-logs/schedule.log`, so a run survives the | ||
| agent's auto-destroy for later inspection. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Unabridged Changelog - mngr_donate | ||
|
|
||
| Full, unedited changelog entries consolidated nightly from individual files in `libs/mngr_donate/changelog/`. | ||
|
|
||
| For a concise summary, see [CHANGELOG.md](CHANGELOG.md). |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| New `imbue-mngr-donate` plugin: `mngr donate` spends spare Claude capacity (from the `imbue-mngr-usage` snapshot, a dependency) on a donation skill instead of letting idle quota expire. When the 5h window has budget and the week is under pace, it launches a headless Claude agent that runs the skill to completion, then auto-cleans up; `--start`/`--stop` install/remove a launchd LaunchAgent (macOS) so idle quota drains on a schedule (launchd, not cron, so it runs in the login session where Claude's keychain token lives). The donation skill (code + prompts) is owned by its upstream git repo -- `mngr donate` checks it out at `--skill-ref` (a branch to track, or a pinned commit for a reviewed version) into a host-dir cache and points the agent at it, so the skill updates without an mngr release. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| """Project-level conftest for mngr_donate.""" | ||
|
|
||
| from imbue.imbue_common.conftest_hooks import register_conftest_hooks | ||
| from imbue.mngr.utils.logging import suppress_warnings | ||
| from imbue.mngr.utils.plugin_testing import register_plugin_test_fixtures | ||
|
|
||
| suppress_warnings() | ||
| register_conftest_hooks(globals()) | ||
| register_plugin_test_fixtures(globals()) |
Empty file.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this is fairly experimental, I'd probably keep is_recommended False by default for now
Once we're ready to release it (and after we've done some testing, have some initial science partners, etc), then we can flip it to True