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 (
sourceNameintemplate.json, currently this repo's own folder name) → whatever-n/--namethe consumer passes todotnet new. - The placeholder value of the
PluginAuthorsymbol (the string currently sitting inEntryPoint.cs'sAuthorproperty) → the consumer's--PluginAuthor. - The placeholder value of the
LabApiVersionsymbol (the version string currently in the.csproj'sPackageReferenceand inEntryPoint.cs'sRequiredApiVersion) → 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. EntryPointandConfig(the class names) are not substituted and must stay literally named that — LabAPI'sPluginLoaderdiscovers plugins by type inheritance, not by name, and the file namesEntryPoint.cs/Config.csare 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 plusskipGit(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 twopostActions: the standard dotnet-templating "restore NuGet packages" action (id210D431B-...; skipped in Visual Studio, which restores on its own) and a "display manual instructions" action (idAC1156F7-...) that prints a reminder to rungit init— see "Git on generation" below for why it's just a printed reminder and not an executed command.- The
.csprojat 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 (seegit 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 (id3A7C4B45-...). Actually wiring it to rungit init/git add/git commitwas tested and it hangs forever in any non-interactive shell: the CLI's script-execution security gate demands an explicitYtyped at aDo 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
skipGitsymbol) 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
TargetFrameworkisnetstandard2.1on purpose, even though LabAPI's own docs and theNorthwood.LabAPINuGet package only targetnet48/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 tonet48, don't add multi-targeting, unless the user asks. LangVersionis pinned explicitly (currently14.0) — don't let it drift tolatest.- The
.csprojsuppresses 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) andMSB3270(viaResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch=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.csare 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.jsonor the scaffolded files — the template cache keys off the installed path and stale content otherwise gets regenerated. dotnet buildalso 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 actualdotnet newgeneration before calling a change done.- On Windows, if cleaning up
../_verifyfails with "resource busy", rundotnet build-server shutdownfirst, then retry. - Delete the
../_verifyscratch output when done; it isn't part of the template.