Split Beat Saber plugin agent skills
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: beatsaber-plugin-builder
|
||||
description: Build, test-compile, or package Beat Saber PC BSIPA plugin source on Linux from local checkouts, GitHub branches, or pull requests. Use when asked to build a Beat Saber plugin, compile a plugin PR, produce a DLL/zip artifact, configure BeatSaberDir/local refs for dotnet builds, or diagnose Linux build failures for net472 BSIPA projects.
|
||||
---
|
||||
|
||||
# Build Beat Saber Plugin
|
||||
|
||||
Use this skill to compile PC BSIPA plugin projects on this Linux host, especially from the `plugin-helper` repo. The workflow is adapted from the Setlist repo's Linux/Cursor build notes.
|
||||
|
||||
For detailed Linux/BSMT behavior, read [linux-bsipa-build.md](references/linux-bsipa-build.md) when you need to configure a project, fix missing references, package artifacts, or explain a failure.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
1. Confirm context.
|
||||
|
||||
```bash
|
||||
pwd
|
||||
git status --short
|
||||
```
|
||||
|
||||
In `plugin-helper`, run commands from repo root and keep temporary source checkouts under `.state/build/<name>` unless the user asks for another location. Do not disturb unrelated dirty files.
|
||||
|
||||
2. Resolve source.
|
||||
|
||||
For a GitHub PR, clone or reuse a checkout under `.state/build`, add/fetch the upstream remote if needed, and check out the PR head:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<owner>/<repo>.git .state/build/<name>
|
||||
git -C .state/build/<name> fetch origin pull/<pr>/head:pr-<pr>
|
||||
git -C .state/build/<name> checkout pr-<pr>
|
||||
```
|
||||
|
||||
If the PR is from a fork and the repo already has a fork remote, preserve it. Never overwrite local source changes without explicit approval.
|
||||
|
||||
3. Inspect build shape.
|
||||
|
||||
```bash
|
||||
find <checkout> -maxdepth 3 -name '*.sln' -o -name '*.csproj' -o -name 'manifest.json' -o -name 'Directory.Build.props'
|
||||
sed -n '1,240p' <project>.csproj
|
||||
```
|
||||
|
||||
Note target framework, `BeatSaberDir`, `LocalRefsDir`, package references, and whether `DisableCopyToPlugins` is already set.
|
||||
|
||||
4. Choose Beat Saber references.
|
||||
|
||||
Prefer a BSManager instance matching the plugin or manifest `gameVersion`. Common local roots:
|
||||
|
||||
```text
|
||||
/home/pleb/.local/share/BSManager/BSInstances/<version>
|
||||
/home/pleb/Windows/Users/pleb/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Use a local `.csproj.user` or MSBuild properties rather than committing machine paths. For test builds, pass `-p:DisableCopyToPlugins=True` and, for BSMT projects that expose it, `-p:DisableCopyToGame=True` so compilation does not mutate the game install.
|
||||
|
||||
5. Restore and build.
|
||||
|
||||
Prefer the solution when present; otherwise build the plugin `.csproj`:
|
||||
|
||||
```bash
|
||||
dotnet restore <solution-or-project>
|
||||
dotnet build <solution-or-project> -c Release -p:DisableCopyToPlugins=True -p:DisableCopyToGame=True
|
||||
```
|
||||
|
||||
If the project lacks .NET Framework reference assemblies on Linux, add or pass `Microsoft.NETFramework.ReferenceAssemblies.net472` as described in the reference file.
|
||||
|
||||
6. Collect artifacts.
|
||||
|
||||
Find produced DLLs and release zips:
|
||||
|
||||
```bash
|
||||
find <checkout> -path '*/bin/*' \( -name '*.dll' -o -name '*.zip' \) -print
|
||||
```
|
||||
|
||||
Verify the DLL name, version, and manifest. If the result is meant for `plugin-helper`, place a copy under `.state/instances/<instance>/downloads/<plugin-id>/` and use the helper plan/apply workflow rather than hand-copying into a BSManager instance.
|
||||
|
||||
7. Validate.
|
||||
|
||||
For skill edits inside this repo, run:
|
||||
|
||||
```bash
|
||||
python /home/pleb/.codex/skills/.system/skill-creator/scripts/quick_validate.py .agents/skills/beatsaber-plugin-builder
|
||||
PYTHONPATH=src python -m compileall -q src tests
|
||||
PYTHONPATH=src python -m unittest discover -s tests
|
||||
```
|
||||
|
||||
For a plugin build, at minimum report the exact `dotnet build` result and artifact paths. For live game validation, use `docs/SMOKETEST.md` and tear down Beat Saber processes afterward.
|
||||
|
||||
## Failure Triage
|
||||
|
||||
- Missing `Microsoft.NETFramework.ReferenceAssemblies`: add the net472 reference-assemblies package or pass an equivalent MSBuild/package restore fix.
|
||||
- Missing `Main.dll`, `HMUI.dll`, `IPA.Loader.dll`, `BSML.dll`, `SongCore.dll`, or similar: `BeatSaberDir` points at the wrong/unmodded instance, or dependencies are absent from `Plugins/`/`Libs/`.
|
||||
- BSMT copies to `IPA/Pending` or `Plugins` during build: rebuild with `-p:DisableCopyToPlugins=True -p:DisableCopyToGame=True` unless the user explicitly wants deployment.
|
||||
- NuGet package restore fails because a source is missing: inspect `NuGet.config` and installed package sources; use repo-local configuration where possible.
|
||||
- API compile errors from a PR: inspect the target game/dependency versions before changing code. Prefer one target version rather than compatibility branches unless the user asks for multi-version support.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Build Beat Saber Plugin"
|
||||
short_description: "Build BSIPA plugins on Linux"
|
||||
default_prompt: "Use $beatsaber-plugin-builder to build this Beat Saber plugin PR on Linux."
|
||||
@@ -0,0 +1,131 @@
|
||||
# Linux BSIPA Build Reference
|
||||
|
||||
## Source Notes
|
||||
|
||||
This workflow comes from `/home/pleb/ops/beatsaber/setlist/docs/pc-modding.md` and `/home/pleb/ops/beatsaber/setlist/docs/bootstrap.md`. Prefer those local files and local clones under `~/src` over web copies when deeper reference material is needed.
|
||||
|
||||
## Toolchain
|
||||
|
||||
- PC BSIPA plugins are usually .NET Framework `net472` class libraries.
|
||||
- Linux `dotnet` SDK 6+ can build them because output DLLs are platform-agnostic CIL loaded by Beat Saber under Proton.
|
||||
- `BeatSaberModdingTools.Tasks` supplies the MSBuild targets normally driven by Visual Studio/Rider BSMT extensions.
|
||||
- On this host, `dotnet --list-sdks` should show a usable SDK. NuGet is available for package inspection.
|
||||
|
||||
## Game References
|
||||
|
||||
Point `BeatSaberDir` at a modded BSManager instance containing:
|
||||
|
||||
```text
|
||||
Beat Saber.exe
|
||||
Beat Saber_Data/Managed/
|
||||
IPA/
|
||||
Libs/
|
||||
Plugins/
|
||||
winhttp.dll
|
||||
```
|
||||
|
||||
Preferred local managed instance root:
|
||||
|
||||
```text
|
||||
/home/pleb/.local/share/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Windows mirror root:
|
||||
|
||||
```text
|
||||
/home/pleb/Windows/Users/pleb/BSManager/BSInstances/<version>
|
||||
```
|
||||
|
||||
Use `BeatSaberVersion.txt` for the exact game version. The manifest `gameVersion` normally uses the `major.minor.patch` prefix, not the build suffix.
|
||||
|
||||
## Project Configuration
|
||||
|
||||
Prefer machine-local configuration in `<Project>.csproj.user`:
|
||||
|
||||
```xml
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<BeatSaberDir>/home/pleb/.local/share/BSManager/BSInstances/1.40.8</BeatSaberDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
Some projects use `LocalRefsDir` and set:
|
||||
|
||||
```xml
|
||||
<BeatSaberDir>$(LocalRefsDir)</BeatSaberDir>
|
||||
```
|
||||
|
||||
For those, pass `-p:LocalRefsDir=/path/to/instance` or add a local `.csproj.user` property if the project imports it.
|
||||
|
||||
When changing a project file for Linux portability, prefer the smallest explicit fix:
|
||||
|
||||
- Add `Microsoft.NETFramework.ReferenceAssemblies.net472` when MSBuild reports missing .NET Framework reference assemblies.
|
||||
- Set or pass `DisableCopyToPlugins=True` for artifact-only builds.
|
||||
- Keep hint paths rooted at `$(BeatSaberDir)` where possible.
|
||||
- Do not add broad multi-version compatibility logic unless requested.
|
||||
|
||||
## Build Commands
|
||||
|
||||
From the plugin checkout:
|
||||
|
||||
```bash
|
||||
dotnet restore <solution-or-project>
|
||||
dotnet build <solution-or-project> -c Release -p:DisableCopyToPlugins=True
|
||||
```
|
||||
|
||||
For a Debug smoke build:
|
||||
|
||||
```bash
|
||||
dotnet build <solution-or-project> -c Debug -p:DisableCopyToPlugins=True
|
||||
```
|
||||
|
||||
Expected outputs:
|
||||
|
||||
```text
|
||||
bin/Debug/<Plugin>.dll
|
||||
bin/Release/<Plugin>.dll
|
||||
bin/Release/zip/<Plugin>-<version>.zip
|
||||
bin/<Configuration>/Artifact/Plugins/<Plugin>.dll
|
||||
```
|
||||
|
||||
The exact layout depends on BSMT version and project customization.
|
||||
|
||||
## BSMT Copy Behavior
|
||||
|
||||
`BeatSaberModdingTools.Tasks` may copy built DLLs into the game directory. On Unix hosts, its process check can behave differently and some projects add a custom target to copy into `<BeatSaberDir>/Plugins/`.
|
||||
|
||||
For builds that should not alter a live instance, pass both property names; different BSMT projects/targets surface different copy switches:
|
||||
|
||||
```bash
|
||||
-p:DisableCopyToPlugins=True -p:DisableCopyToGame=True
|
||||
```
|
||||
|
||||
If the purpose is to install the built artifact, copy it into plugin-helper's state downloads and use `plugin-helper plan/apply`, or follow `docs/SMOKETEST.md` for intentional live validation.
|
||||
|
||||
## Common Reference Failures
|
||||
|
||||
- `MSB3644` or missing `.NETFramework,Version=v4.7.2` reference assemblies: add `Microsoft.NETFramework.ReferenceAssemblies.net472`.
|
||||
- Missing game assemblies such as `Main.dll`, `HMUI.dll`, `UnityEngine.CoreModule.dll`: `BeatSaberDir` is wrong or incomplete.
|
||||
- Missing mod dependencies such as `BSML.dll`, `SongCore.dll`, `SiraUtil.dll`, `BeatSaberPlaylistsLib.dll`: install or point at an instance containing those plugins, or fetch the dependency DLL from its verified release only when appropriate.
|
||||
- `IPA.Loader.dll` missing: BSIPA is not bootstrapped in that instance.
|
||||
|
||||
## Artifact Handoff To plugin-helper
|
||||
|
||||
For a built plugin DLL intended for a managed instance:
|
||||
|
||||
```bash
|
||||
mkdir -p .state/instances/<instance>/downloads/<plugin-id>
|
||||
cp <checkout>/<path>/bin/Release/<Plugin>.dll .state/instances/<instance>/downloads/<plugin-id>/<Plugin>.dll
|
||||
sha256sum .state/instances/<instance>/downloads/<plugin-id>/<Plugin>.dll
|
||||
```
|
||||
|
||||
Then update registry/lock data only if the user asked to manage/install the artifact, and use:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state check --instance <instance>
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state plan --instance <instance> --plugin <plugin-id>
|
||||
PYTHONPATH=src python -m plugin_helper --state-dir .state apply <plan-path>
|
||||
```
|
||||
|
||||
Inspect the generated plan before applying.
|
||||
+52
-16
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: install-beatsaber-plugin
|
||||
name: beatsaber-plugin-manager
|
||||
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.
|
||||
---
|
||||
|
||||
@@ -12,11 +12,16 @@ Use the repository's own `plugin-helper` commands to manage plugins for BSManage
|
||||
|
||||
## Hard Guardrail
|
||||
|
||||
For ordinary GitHub-hosted plugins, 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 repository or
|
||||
release URL from the user's prompt or from a user-provided local planning note
|
||||
before selecting any release.
|
||||
|
||||
- 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.
|
||||
- If the user has provided a GitHub repository URL but not a release URL, use
|
||||
that exact repository's release API and choose the most appropriate
|
||||
non-draft, non-prerelease release/asset for the target Beat Saber instance.
|
||||
- If the user has provided no GitHub repository or release URL for the plugin,
|
||||
stop and ask the user for one.
|
||||
- Do not search the web to discover a repository or "correct" project URL.
|
||||
- Do not substitute a similar repo, fork, project, or package name.
|
||||
- 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.
|
||||
@@ -75,15 +80,38 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
||||
For BeatMods bootstrap or verified packages, query BeatMods with a browser-like user agent:
|
||||
|
||||
```bash
|
||||
python - <<'PY'
|
||||
PYTHONPATH=src python - <<'PY'
|
||||
import json, urllib.request
|
||||
from plugin_helper.beatmods import by_version_id, normalize_mods
|
||||
|
||||
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])
|
||||
mods = normalize_mods(data)
|
||||
mods_by_version_id = by_version_id(mods)
|
||||
print(json.dumps(
|
||||
[
|
||||
{
|
||||
"name": mod.name,
|
||||
"modId": mod.mod_id,
|
||||
"gitUrl": mod.git_url,
|
||||
"category": mod.category,
|
||||
"versionId": mod.version_id,
|
||||
"modVersion": mod.mod_version,
|
||||
"zipHash": mod.zip_hash,
|
||||
"dependencies": mod.dependencies,
|
||||
"dependencyNames": [
|
||||
mods_by_version_id[dep].name
|
||||
for dep in mod.dependencies
|
||||
if dep in mods_by_version_id
|
||||
],
|
||||
}
|
||||
for mod in mods
|
||||
],
|
||||
indent=2,
|
||||
)[:20000])
|
||||
PY
|
||||
```
|
||||
|
||||
@@ -103,7 +131,8 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
||||
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.
|
||||
For GitHub URLs, resolve the release from the user-provided repository or
|
||||
release URL only.
|
||||
|
||||
Derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
|
||||
|
||||
@@ -189,13 +218,20 @@ 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.
|
||||
After any successful apply that changes a live BSManager instance, always
|
||||
run the documented live smoketest before the final response unless the user
|
||||
explicitly says not to. Do not stop at helper check, unit tests, compile
|
||||
checks, or file-hash verification for live installs. For live Beat Saber
|
||||
validation, follow `docs/SMOKETEST.md`. Before starting the launch, announce
|
||||
in agent chat how long the smoketest window will run for, using the current
|
||||
duration from `docs/SMOKETEST.md` unless the user requested a different
|
||||
duration. 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 and
|
||||
enough menu/UI initialization evidence for the plugin under test. 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:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
interface:
|
||||
display_name: "Install Beat Saber Plugin"
|
||||
short_description: "Update Beat Saber plugins via helper"
|
||||
default_prompt: "Use $install-beatsaber-plugin to install or update a Beat Saber plugin from this release URL: <url>."
|
||||
default_prompt: "Use $beatsaber-plugin-manager to install or update a Beat Saber plugin from this release URL: <url>."
|
||||
Reference in New Issue
Block a user