Document plugin helper agent workflow

This commit is contained in:
pleb
2026-06-28 14:35:21 -07:00
parent f085bbb802
commit 17bd736e59
3 changed files with 270 additions and 6 deletions
@@ -1,15 +1,19 @@
---
name: install-beatsaber-plugin
description: Install or update a Beat Saber plugin in the plugin-helper repo by using the local helper workflow. Use when the user asks to add, install, update, bump, lock, or manage a Beat Saber plugin release for a BSManager instance. The user must provide an explicit GitHub release URL in the prompt; if no release URL is present, ask for one and do not infer, search for, or substitute a different repository.
description: Install or update a Beat Saber plugin in the plugin-helper repo by using the local helper workflow. Use when the user asks to add, install, update, bump, lock, bootstrap BSIPA, or manage a Beat Saber plugin release for a BSManager instance. Prefer upstream GitHub release artifacts for normal plugins; use BeatMods primarily as compatibility/dependency metadata, with CDN artifacts only for inaccessible upstream assets, BeatMods-only packages, or framework/library dependencies.
---
# Install Beat Saber Plugin
Use the repository's own `plugin-helper` commands to manage plugins for BSManager instances. Do not manually copy release files into the game instance except to undo your own mistaken install before rerunning the helper.
Use the repository's own `plugin-helper` commands to manage plugins for BSManager instances whenever the helper supports the operation. Do not manually copy release files into the game instance except:
- to bootstrap BSIPA/core packages before the helper has a first-class bootstrap command
- to undo your own mistaken install before rerunning the helper
## Hard Guardrail
Require an explicit GitHub release URL from the user's prompt before selecting any release or repository.
For ordinary GitHub-hosted plugins, require an explicit GitHub release URL from
the user's prompt before selecting any release or repository.
- If the prompt does not contain a GitHub release URL, stop and ask the user for it.
- Do not search the web to discover a repository or "correct" release URL.
@@ -17,6 +21,17 @@ Require an explicit GitHub release URL from the user's prompt before selecting a
- If the provided URL is a general releases page, use that repo's release API and choose the latest non-draft, non-prerelease release unless the user asks for a specific tag/version.
- If the provided URL is a tag URL, use that exact tag.
Exception: if the user explicitly asks to bootstrap a Beat Saber version or
install verified mods without providing GitHub URLs, use BeatMods metadata to
identify compatible versions and dependency closure. Still prefer upstream
GitHub release artifacts when BeatMods exposes a `gitUrl` and a matching
release/asset can be found. Use BeatMods CDN artifacts only when the upstream
artifact is inaccessible, no matching upstream release asset exists, the package
is effectively BeatMods-only, or the package is a framework/library dependency
such as .NET assemblies. Record the artifact source plus BeatMods `modVersion`,
version id, `zipHash`, dependencies, and supported game version in the repo
notes/lock data.
Accepted URL shapes include:
```text
@@ -44,6 +59,7 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
sed -n '1,220p' src/plugin_helper/models.py
sed -n '1,220p' registry/plugins.toml
sed -n '1,220p' locks/<instance>.lock.toml
sed -n '1,220p' docs/SMOKETEST.md
```
3. Determine the instance.
@@ -54,9 +70,42 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
PYTHONPATH=src python -m plugin_helper instances
```
4. Resolve the release from the user-provided URL only.
4. Resolve the release source.
For GitHub URLs, derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
For BeatMods bootstrap or verified packages, query BeatMods with a browser-like user agent:
```bash
python - <<'PY'
import json, urllib.request
game_version = "<instance>"
url = f"https://beatmods.com/api/mods?status=verified&gameVersion={game_version}&gameName=BeatSaber&platform=steampc"
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 plugin-helper"})
with urllib.request.urlopen(req, timeout=20) as response:
data = json.load(response)
mods = data["mods"] if isinstance(data, dict) and "mods" in data else data
print(json.dumps(mods, indent=2)[:20000])
PY
```
BeatMods dependency entries are mod-version ids. Resolve the selected mod's
dependency closure before downloading. For each resolved package, prefer its
upstream `gitUrl` release artifacts when a matching release asset exists.
Fall back to BeatMods CDN only for inaccessible/missing upstream assets,
BeatMods-only packages, or framework/library dependencies. CDN URLs are:
```text
https://beatmods.com/cdn/mod/<zipHash>.zip
```
For BSIPA bootstrap, expect the archive to contain root-relative `IPA/` and
`IPA.exe` files whether sourced from GitHub or BeatMods. Extract it into the
instance root and run `IPA.exe -n` under the same Proton environment used by
the smoketest. This creates/copies the root `winhttp.dll` and root `Libs/`
substrate that IPA needs.
For GitHub URLs, resolve the release from the user-provided URL only.
Derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
```bash
curl -sS https://api.github.com/repos/<owner>/<repo>/releases
@@ -85,7 +134,7 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
- `dll-to-plugins`: asset is a single `.dll` that belongs in `Plugins/`.
- `bsipa-zip`: zip top-level paths are only `IPA/`, `Libs/`, or `Plugins/`.
- `root-zip`: zip contains valid game-root paths outside the BSIPA top-level set.
- `root-zip`: zip contains valid game-root paths outside the BSIPA top-level set. Use this for BSIPA/bootstrap archives because `IPA.exe`, `IPA.runtimeconfig*.json`, and root `winhttp.dll` are game-root files.
- `zip-to-pending`: only when the release is intended for `IPA/Pending/`.
- `manual`: do not use for installable releases.
@@ -140,6 +189,28 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
Use `PYTHONPATH=src`; plain `python -m unittest` may fail in this source-layout repo.
For live Beat Saber validation, follow `docs/SMOKETEST.md`. Do not rely on
`timeout` to kill the full game process tree. Prefer the documented
foreground Proton launch with a background watchdog that sleeps for the smoke
window, then terminates Beat Saber by process name. Confirm `Logs/_latest.log`
has the expected IPA/plugin lines. If the game remains open after the
watchdog cleanup, say so and ask the user to close it manually rather than
leaving the turn with Beat Saber running.
For BSIPA/SongCore bootstrap, expected successful log lines include:
```text
Game version <version>
Loading plugins from Plugins and found <n>
Beat Saber IPA (BSIPA): <version>
SongCore (SongCore): <version>
```
Warnings about older mod target game-version metadata can be acceptable when
BeatMods verified that exact package for the target Beat Saber version, but
record them in the tracker or roadmap. Also record when a BeatMods CDN
artifact was used so it can be migrated to upstream GitHub later if possible.
9. Final response.
Include:
+49
View File
@@ -0,0 +1,49 @@
# AGENTS.md
Guidance for coding agents working in this repo.
## Project Shape
- This repo manages Beat Saber plugins for BSManager instances.
- Default instance roots are:
- `/home/pleb/Windows/Users/pleb/BSManager/BSInstances`
- `/home/pleb/.local/share/BSManager/BSInstances`
- A local BSManager source checkout may be available at
`/home/pleb/src/Zagrios/bs-manager`. Use it as a read-only reference when
investigating launch behavior, inherited Steam arguments, instance layout, or
Proton environment details unless the user explicitly asks for BSManager code
changes.
- Prefer repo-local state with `--state-dir .state` for planned installs unless
the task explicitly targets the user's live default state.
## Workflow Rules
- Run commands from the repo root with `PYTHONPATH=src`.
- For human-style inspection, prefer the menu with repo-local state:
`PYTHONPATH=src python -m plugin_helper --state-dir .state menu`.
- When targeting the local Linux BSManager install rather than the Windows
mirror, pass `--instances-root /home/pleb/.local/share/BSManager/BSInstances`
or choose that path explicitly in the menu.
- Use the helper commands instead of manually copying plugin files into an
instance.
- Treat BSIPA as a bootstrap phase:
- `bootstrap` installs the locked BSIPA archive and records generated files.
- ordinary plugin plans should depend on healthy bootstrap state.
- Be careful with duplicate instance names across Windows and local roots. Use
the menu or pass `--instances-root` explicitly when targeting one install.
## Validation
- Run `PYTHONPATH=src python -m unittest discover -s tests` after code changes.
- Run `PYTHONPATH=src python -m compileall -q src tests` for syntax/import
checks.
- For live game validation, follow `docs/SMOKETEST.md` and tear down Beat Saber
processes afterward.
## Launch Notes
- BSManager may inherit Beat Saber launch arguments configured in Steam.
- Do not assume a black screen is a plugin failure until checking
`Logs/_latest.log`, Unity `Player.log`, and the live process command line.
- Duplicate launch args such as `--no-yeet fpfc --no-yeet fpfc` can trigger a
fatal command-line parse error after BSIPA/plugin loading succeeds.
+144
View File
@@ -0,0 +1,144 @@
# Beat Saber Smoketest Workflow
Use this workflow after installing or removing a plugin batch. It is adapted
from the Setlist repo's working Proton/BSManager smoketest notes.
The routine smoketest should be short: about 10 seconds wall time for launch and
log check, followed by immediate teardown. If expected plugin log lines do not
appear inside that window, treat that as a failure to investigate instead of
stretching the timeout.
## Preconditions
- Run from the target BSManager instance directory, for example:
```sh
cd "$HOME/.local/share/BSManager/BSInstances/1.44.1"
```
- The instance should contain BSIPA (`IPA/`, `IPA.exe`, `winhttp.dll`, and
`Plugins/`).
- If those files are absent, the run can still produce Unity `Player.log`
output, but it will not produce `Logs/_latest.log` or any IPA plugin-loading
evidence. Run `plugin-helper bootstrap --instance <version>` before using
this workflow to validate plugins.
- On Plasma + Wayland, the shell needs working desktop session variables:
`DISPLAY`, `WAYLAND_DISPLAY`, and `XDG_RUNTIME_DIR`. If the IDE terminal is
missing them, copy values from the logged-in desktop session.
## Launch
Run the Proton launch in the foreground with a short watchdog. Backgrounding
the Proton launch itself from an IDE terminal has been observed to exit early
before Beat Saber writes useful `_latest.log` lines.
Important: `timeout` may stop the launcher wrapper without killing the full
Beat Saber/Proton process tree. Prefer a watchdog that sleeps for the smoke
window and then kills the game process by name.
```sh
export SteamAppId=620980 SteamOverlayGameId=620980 SteamGameId=620980
export WINEDLLOVERRIDES='winhttp=n,b'
export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/BSManager/SharedContent/compatdata"
export STEAM_COMPAT_INSTALL_PATH="$PWD"
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.local/share/Steam"
export STEAM_COMPAT_APP_ID=620980
export SteamEnv=1
export OXR_PARALLEL_VIEWS=1
(
sleep 10
pkill -TERM -f "[B]eat Saber.exe" || pkill -TERM -f "[B]eat Saber" || true
sleep 2
pkill -KILL -f "[B]eat Saber.exe" || pkill -KILL -f "[B]eat Saber" || true
) &
smoke_watchdog_pid=$!
trap 'kill "$smoke_watchdog_pid" 2>/dev/null || true' EXIT
steam-run "$HOME/.local/share/Steam/steamapps/common/Proton - Experimental/proton" \
run "$PWD/Beat Saber.exe" --no-yeet fpfc 2>&1 | tee /tmp/bs-smoke.log
wait "$smoke_watchdog_pid" 2>/dev/null || true
trap - EXIT
```
Useful launch arguments:
| Argument | Use |
| --- | --- |
| `--verbose` | Opens the BSIPA console window. |
| `--debug` | Promotes debug logs to console output. |
| `--trace` | Enables very noisy BSIPA/internal traces. |
| `fpfc` | First-Person Flying Controller, useful for non-VR menu testing. |
| `--auto_play` | Built-in autoplayer for gameplay checks. |
## Log Checks
In another terminal, or immediately after the launch returns:
```sh
tail -F Logs/_latest.log
```
For a batch install, check for:
- BSIPA startup reaches plugin loading.
- the expected plugin names and versions appear in `Logs/_latest.log`.
- there are no missing assembly or dependency-resolution failures.
- there are no repeated unhandled exceptions from newly installed plugins.
- the game reaches the main menu.
After the first successful launch, `plugin-helper bootstrap-check --instance
<version>` should pass. Ordinary plugin plans for lockfiles that include BSIPA
depend on that recorded bootstrap state and the latest IPA log.
For Setlist specifically:
```sh
grep Setlist Logs/_latest.log
```
Expected Setlist signals include `platformUserId=...`, playlist lines with
`hasSyncUrl=...`, and `beatLeaderOwnerConfirmed=...`. When testing real playlist
sync, adding a map to an owned BeatLeader-synced playlist should log an HTTP
success line.
## Teardown
When the expected log lines are present before the watchdog fires, stop Beat
Saber immediately from a second terminal instead of waiting for the smoke window
to expire.
```sh
pkill -TERM -f "[B]eat Saber.exe" || pkill -TERM -f "[B]eat Saber" || true
sleep 2
pkill -KILL -f "[B]eat Saber.exe" || pkill -KILL -f "[B]eat Saber" || true
```
The bracketed pattern avoids matching the shell command that is running the
cleanup. If Beat Saber still remains open, close the game window manually and
then check for leftovers:
```sh
ps -eo pid,ppid,stat,comm,args | rg -i '[B]eat Saber|[P]roton.*Beat Saber|[w]ineserver|[s]team-run'
```
If the smoketest fails, kill leftover Beat Saber, Wine, or Proton processes
before retrying, then inspect `/tmp/bs-smoke.log`, Unity `Player.log` in the
Proton compatdata, and the timestamped files under `Logs/`.
## Plugin Development Notes From Setlist
These are relevant when `plugin-helper` installs locally built personal plugins.
- PC BSIPA plugins are .NET Framework class libraries loaded by BSIPA. Linux
`dotnet build` output is CIL and can be loaded by the Proton game instance.
- BSMT-style builds may copy the DLL directly into `<BeatSaberDir>/Plugins/`;
use `-p:DisableCopyToPlugins=True` when a build should not mutate the game
tree.
- `BeatSaberDir` in project user files or hint paths should point at the exact
BSManager instance being targeted.
- If a plugin build produces a shipped artifact, bump the plugin version before
the successful build so the IPA log line identifies the artifact under test.
- Setlist depends on BeatLeader being installed and signed in, PlaylistManager,
and BeatSaberPlaylistsLib. Its normal install target is `Plugins/Setlist.dll`.