diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 331ca7a..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,222 +0,0 @@ -# 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 one text-replacement `symbol`, `PluginAuthor`. 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`. - -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 two 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: ""`) 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) - -- Repo root holds only template-source/maintenance files that must **never** ship in - a generated project: `.git/`, `.gitignore`, `.editorconfig`, `CLAUDE.md`, and - `.template.config/`. None of these are copied to generated output — see "content/ - vs. repo root" below. -- `.template.config/template.json` — template identity, the `PluginAuthor` symbol, - a `sources` entry that maps `./content` → the output root (this is what keeps the - root-level files above out of every generated project), `primaryOutputs` (points at - the project file using its pre-substitution literal name, relative to `content/` — - that's correct and expected, not a bug), and one `postAction`: the standard - dotnet-templating "restore NuGet packages" action (id `210D431B-...`; skipped in - Visual Studio, which restores on its own). -- `.template.config/ide.host.json` — Visual Studio-only UI hints for the New Project - wizard. It does **not** rename the `PluginAuthor` symbol (a flag can't contain a - space); it only maps the symbol's `id` to a human-readable `name.text` label shown - in VS's UI, via `isVisible: "true"` (VS hides a symbol from that UI unless - explicitly marked visible here). Ignored entirely by `dotnet new` on the CLI. -- `content/` — the actual buildable project, and the only folder the `sources` - mapping in `template.json` copies into generated output: `LabApiPluginTemplate.csproj`, - `EntryPoint.cs`, `Config.cs`, and `content/CLAUDE.md`. Add any new file that should - ship with generated plugins in here, not at the repo root. -- `content/CLAUDE.md` is a **different file** from this one (the repo-root - `CLAUDE.md`, which is template-editing guidance and never ships). It's the project - memory a generated plugin ships with, so Claude Code has LabAPI/toolchain/style - context automatically in every plugin generated from this template, without - depending on any C#/.NET/LabAPI Skill existing in the catalog (none currently does — - re-check with `SearchSkills`/`ToolSearch` before assuming that's still true). Keep - its LabAPI-contract/toolchain/style sections in sync with this file's equivalent - sections when either changes; `content/CLAUDE.md` omits everything specific to - editing *this* template repo (symbols, `sources` mapping, `sourceName` traps, the - verification workflow below) since none of that applies to a generated plugin. -- 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. - -## `content/` vs. repo root — why the project isn't at the repo root - -Early versions of this template had the `.csproj`/`.cs` files sitting directly next -to `.gitignore`/`.editorconfig`/`CLAUDE.md`/`.template.config/` at the repo root, with -no `sources` entry in `template.json`. Because `dotnet new` copies everything under -the mounted template root except `.template.config/` by default, every one of those -maintenance files silently ended up inside every generated plugin too. - -The fix: `content/` holds only what should be copied, and `template.json` declares -`"sources": [{ "source": "./content", "target": "./" }]`. Files outside `./content` -(root-level `.gitignore`, `.editorconfig`, `CLAUDE.md`) are never touched by -generation — no exclude-list to maintain. - -Do **not** nest a folder named `LabApiPluginTemplate` inside `content/` (e.g. -`content/LabApiPluginTemplate/EntryPoint.cs`). `sourceName` substitution renames any -folder whose name matches it, so an inner folder named after the project would get -renamed too, producing a doubled-up output path like -`///.csproj` instead of the flat -`//.csproj` consumers expect. Keep the `.csproj`/ -`.cs` files directly under `content/`. - -## Git on generation — why there's no automated `git init` - -A generated project is **not** a git repository, and there is no `postAction` that -tries to make it one. This was verified, not assumed — re-verified against the -`dotnet` 10.0.400 SDK on this machine while working on this exact question: - -- `dotnet new`'s only postAction capable of running an arbitrary process is the - "Run script" action (id `3A7C4B45-...`). Wiring it to run `git init`/`git add`/ - `git commit` 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. - `dotnet new create --help` on this SDK exposes no `--allow-scripts` or equivalent - flag 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 on whatever SDK is current then. -- A previous revision compensated with a `SkipGit` symbol plus a "display manual - instructions" postAction that just printed the `git init` one-liner. That has been - removed — it wasn't automation, just a reminder to run a command any git user - already knows, so it wasn't worth the extra symbol/postAction/`ide.host.json` entry. -- 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 real automatic git init for free. - CLI users just run `git init` themselves after generating. - -## LabAPI contract (verified against northwood-studios/LabAPI source, not guessed) - -`Plugin` (`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\\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` ``, 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 (both are explained here, not in the `.csproj` — this repo's - code style forbids comments, including in the `.csproj`, see "Code style" below). -- The `Northwood.LabAPI` `PackageReference` uses a floating `Version="*"` on purpose, - so every `dotnet restore` always pulls whatever is the latest stable release on - NuGet at that moment — there's no version to bump here. This is intentionally - **not** wired to `EntryPoint.cs`'s `RequiredApiVersion`, which can't be a floating - value (`System.Version` has no such concept and would throw at runtime if it tried). - `RequiredApiVersion` is a separate, manually-maintained literal representing the - minimum LabAPI version the scaffolded code is known to work with — bump it by hand - if you verify compatibility with a newer release, don't try to auto-sync it with the - floating restore. Also be aware a floating version means a plugin generated today can - restore a different (newer) LabAPI release than one generated last week, with no - pinning — that's the deliberate trade-off of "always latest" over reproducibility. - -## Code style for anything you write here - -- No `//`, `/* */`, `///`, or XML (``) comments unless the user explicitly - asks for comments/documentation in that request. No comments restating - self-explanatory code. `EntryPoint.cs`/`Config.cs`/the `.csproj` 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 `` below -with this repo's actual folder name (the value you'd see in `sourceName`): - -```bash -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 `content/` (the placeholder project is - itself valid and compilable), but that only proves the *unsubstituted* placeholder - compiles — it does not exercise symbol substitution or the `sources` mapping. - Always additionally verify through an actual `dotnet new` generation before calling - a change done. -- If `dotnet new install .` warns about another installed template sharing this - identity (`SCPSL.LabApi.PluginTemplate`) from a different path — e.g. this repo's - own parent folder having been installed by an earlier mistaken - `dotnet new install .` run one level up — that other install shadows this one and - your edits won't be exercised. Run `dotnet new uninstall ` first. -- On Windows, if cleaning up `../_verify` fails with "resource busy", run - `dotnet build-server shutdown` first, then retry; if it still won't delete, fall - back to PowerShell's `Remove-Item -Recurse -Force`. -- Delete the `../_verify` scratch output when done; it isn't part of the template.