Files
LabApiPluginTemplate/CLAUDE.md
T

9.7 KiB

CLAUDE.md

This repo is a dotnet new project template (identity SCPSL.LabApi.PluginTemplate, shortName labapi-plugin), not a runnable plugin. It scaffolds SCP: Secret Laboratory LabAPI plugins.

The next section only applies while you're editing this template repository (i.e. a .template.config/ folder is present next to this file). It does not apply if you're instead looking at a plugin someone already generated from this template — that folder never contains .template.config/, and its EntryPoint.cs/Config.cs are ordinary project files at that point, not template scaffolding.

Repo is template source — read before touching any file

.template.config/template.json sets this folder's name as the template's sourceName and defines two text-replacement symbols (plus skipRestore and skipGit, which only toggle post-generation actions and replace no text). The dotnet templating engine does a literal, repo-wide string replacement at generation time — not semantic renaming — driven by that config:

  • The folder/project name (sourceName in template.json, currently this repo's own folder name) → whatever -n/--name the consumer passes to dotnet new.
  • The placeholder value of the PluginAuthor symbol (the string currently sitting in EntryPoint.cs's Author property) → the consumer's --PluginAuthor.
  • The placeholder value of the LabApiVersion symbol (the version string currently in the .csproj's PackageReference and in EntryPoint.cs's RequiredApiVersion) → the consumer's --LabApiVersion.

Open template.json to see the exact current literal for each — don't hardcode them again here or anywhere else in prose, because this file itself ships inside every generated project and any exact match gets rewritten too (self-referential text like "the project is called X" would silently turn into nonsense after generation).

Consequences:

  • Never introduce an exact match of one of those three literals elsewhere in the repo unless you intend it to be substituted too.
  • If you add a new thing that should be configurable at generation time, add a symbol to template.json (type: parameter, replaces: "<literal>") instead of hardcoding a magic string.
  • EntryPoint and Config (the class names) are not substituted and must stay literally named that — LabAPI's PluginLoader discovers plugins by type inheritance, not by name, and the file names EntryPoint.cs/Config.cs are a fixed part of this template's contract. Only the namespace tracks the project name.

Repo layout (only what affects decisions)

  • .template.config/template.json — template identity, the symbols above plus skipGit (a display-only toggle, see below), primaryOutputs (points at the project file using its pre-substitution literal name — that's correct and expected, not a bug), and two postActions: the standard dotnet-templating "restore NuGet packages" action (id 210D431B-...; skipped in Visual Studio, which restores on its own) and a "display manual instructions" action (id AC1156F7-...) that prints a reminder to run git init — see "Git on generation" below for why it's just a printed reminder and not an executed command.
  • The .csproj at the repo root — the project file every generated plugin ships with.
  • EntryPoint.cs / Config.cs — the scaffolded plugin (Plugin<Config>) and its YAML-backed settings class.
  • .editorconfig — formatting/naming only (indentation, brace style). It does not enforce the comment/language rules below — those aren't analyzer-checkable.
  • No .sln, no test project, no CI, no packaging (.nupkg) project, no GitHub Copilot instructions file currently exist in this folder. Don't assume any of them. This repo itself does have its own git history (see git log) — that's unrelated to whether a generated project gets one, which is what "Git on generation" below covers.

Git on generation — why there's no automated git init

A generated project is not a git repository by default; a postAction only prints a reminder to run git init && git add -A && git commit -m "Initial commit". This was a deliberate choice after testing the alternative:

  • dotnet new's only postAction capable of running an arbitrary process is the "Run script" action (id 3A7C4B45-...). Actually wiring it to run git init/ git add/git commit was tested and it hangs forever in any non-interactive shell: the CLI's script-execution security gate demands an explicit Y typed at a Do you want to run this action [Y(yes)|N(no)]? prompt, once per script action, with no flag on this SDK to pre-approve it — unlike the NuGet-restore postAction, which the CLI treats as trusted and never gates. Do not reintroduce "Run script" postActions for this without re-verifying that gate no longer applies.
  • Visual Studio's own "New Project" dialog already has a built-in "Create new Git repository" checkbox that works for any template, including this one, with zero template-side configuration — so VS users get this for free regardless.
  • CLI users see the printed reminder above (governed by the skipGit symbol) and run the one-liner themselves.

LabAPI contract (verified against northwood-studios/LabAPI source, not guessed)

Plugin<TConfig> (LabApi.Loader.Features.Plugins) requires implementing: Name, Description, Author, RequiredApiVersion (all abstract get-only) plus Enable()/Disable(). Overridable but optional: Version (defaults to the assembly's version), Priority (LoadPriority, defaults to Medium), ConfigFileName (defaults to "config.yml"). Config is a plain class with a public parameterless constructor and public get/set properties — the base class (de)serializes it via YamlDotNet automatically; don't hand-roll config I/O.

Never invent a LabAPI class/namespace/method/property/attribute. Before using one you haven't already seen in this repo, check the restored package (LabApi.dll + LabApi.xml under %userprofile%\.nuget\packages\northwood.labapi\<version>\lib\net48\) or https://github.com/northwood-studios/LabAPI. If you can't verify a member exists, say so instead of guessing. No Skill/plugin specific to C#, .NET, MSBuild, NuGet, or LabAPI exists in the Claude Code skill catalog as of this writing — check ToolSearch/SearchSkills for one before editing in case that has changed, but don't assume one exists.

Toolchain choices — don't "fix" these without being asked

  • The project's TargetFramework is netstandard2.1 on purpose, even though LabAPI's own docs and the Northwood.LabAPI NuGet package only target net48/net481. This is a deliberate compatibility choice (compiled output still loads under the game's Mono/.NET Framework 4.8 host) that trades away some netstandard2.1 BCL/language surface .NET Framework can't actually run (e.g. default interface members). Don't switch it to net48, don't add multi-targeting, unless the user asks.
  • LangVersion is pinned explicitly (currently 14.0) — don't let it drift to latest.
  • The .csproj suppresses exactly two warnings, both scoped as narrowly as MSBuild allows, because of the netstandard2.1/net48-only-package combination above: NU1701 (via a per-PackageReference <NoWarn>, not a project-wide one) and MSB3270 (via ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch=None). Don't broaden either into a blanket suppression and don't remove them without understanding why (comments sit next to both in the .csproj).

Code style for anything you write here

  • No //, /* */, or /// comments unless the user explicitly asks for comments/documentation in that request. No comments restating self-explanatory code. EntryPoint.cs/Config.cs are already comment-free — keep them that way and follow the same standard for any new file.
  • All text embedded in code defaults to English: string/log/console messages, exception messages, [Description]/config text, any user-facing string. Example: Logger.Info("Plugin initialized successfully"), not a Spanish equivalent.
  • This never applies to identifiers the user picked — project name, namespace, class/method/property/variable names stay exactly as requested (standard C# casing rules aside). Honor an explicit request for a specific piece of text in another language.

How to verify a change (no test suite exists — this is the actual check)

There is no automated test project. Verify any template change by actually generating a project from it and building that output. Replace <repo-name> below with this repo's actual folder name (the value you'd see in sourceName):

dotnet new uninstall .
dotnet new install .
dotnet new labapi-plugin -n VerifyPlugin -o ../_verify/VerifyPlugin
cd ../_verify/VerifyPlugin
dotnet build

Expect Build succeeded. 0 Warning(s) 0 Error(s). If it isn't 0/0, the change broke either the template's own warning suppressions or something in EntryPoint.cs/ Config.cs/the .csproj.

Notes:

  • Re-run dotnet new uninstall . + dotnet new install . after any edit to .template.config/template.json or the scaffolded files — the template cache keys off the installed path and stale content otherwise gets regenerated.
  • dotnet build also works directly inside <repo-name> (the placeholder project is itself valid and compilable), but that only proves the unsubstituted placeholder compiles — it does not exercise symbol substitution. Always additionally verify through an actual dotnet new generation before calling a change done.
  • On Windows, if cleaning up ../_verify fails with "resource busy", run dotnet build-server shutdown first, then retry.
  • Delete the ../_verify scratch output when done; it isn't part of the template.