Guidance for AI agents and coding assistants working in this repository.
Prereqs
- Install the .NET SDK from
global.json:dotnet --list-sdksto verify. - Install Blazor WASM tooling once:
dotnet workload install wasm-tools. - IDE users can open
Pkmds.slnx. CLI builds target individual projects (e.g.,Pkmds.Web.csproj).
Local dev (Blazor WASM)
- From repo root (PowerShell):
./watch.ps1- Equivalent from
Pkmds.Web:dotnet watch run -c Debug -v n --no-hot-reload - Launch profiles expose: http
http://localhost:5283, httpshttps://localhost:7267(seePkmds.Web/Properties/launchSettings.json).
- Equivalent from
Local dev with a PKHeX.Core source checkout (manual UI testing)
- From repo root (PowerShell):
./watch-local-pkhex.ps1- Optional custom path:
./watch-local-pkhex.ps1 -PKHeXSourcePath C:\Code\PKHeX-dev\PKHeX.Core\PKHeX.Core.csproj - See Local dev override (
UseLocalPKHeX) under the PKHeX.Core section for full details.
- Optional custom path:
Restore and build
- Restore:
dotnet restore - Build Web (Debug/Release):
dotnet build Pkmds.Web/Pkmds.Web.csproj -c Debugdotnet build Pkmds.Web/Pkmds.Web.csproj -c Release
Format/lint
- Uses
.editorconfig; Debug builds treat warnings as errors. - Run:
dotnet formatthendotnet build -c Debug.
Tests
- Test project:
Pkmds.Tests(xUnit + bUnit; coverage via coverlet collector). - Run all:
dotnet test -c Release - Filter:
dotnet test --filter "FullyQualifiedName~Namespace.Class.TestMethod"
Publish locally (static site)
dotnet publish Pkmds.Web/Pkmds.Web.csproj -c Release -o release --nologoCopy-Item release/wwwroot/index.html release/wwwroot/404.html -ForceNew-Item -ItemType File release/wwwroot/.nojekyll -Force | Out-Null- Deployable output:
release/wwwroot/.
- Single route
/; all UI lives insideSaveFileComponentas aMudTabs-based layout. RefreshAwareComponentbase class (inPkmds.Rcl) auto-subscribes toIRefreshService.OnAppStateChanged— prefer it overComponentBasefor any component that reacts to save-file state changes.GlobalUsings.csinPkmds.Rclimports:MudBlazor,PKHeX.Core,Pkmds.Rcl.Components,Pkmds.Rcl.Services, and aliasesSeverity = MudBlazor.Severity(avoids ambiguity with PKHeX's ownSeverity).- New tabs: add a
<MudTabPanel>entry inSaveFileComponent.razor; put the tab component underPkmds.Rcl/Components/MainTabPages/; use namespacePkmds.Rcl.Components.MainTabPagesin the code-behind. - Tab navigation: bind
@bind-ActivePanelIndexonMudTabsand pass anEventCallbackparameter to child tabs that need to trigger navigation.
Directory.Build.props: sets globalTargetFrameworktonet10.0, repo metadata, nullable + implicit usings, andTreatWarningsAsErrorsin Debug.Directory.Packages.props: central package management (PKHeX.Core, MudBlazor, Serilog, FileSystemAccess, etc.).- Projects
Pkmds.Core/(Class Library): PKHeX utilities and extensions. Reusable, UI-independent logic for working with PKHeX.Core (species validation, shiny handling, markings, etc.).Pkmds.Rcl/(Razor Class Library): shared UI/components and services. Tailwind integrated viaTailwind.targets(input atPkmds.Rcl/wwwroot/css/tailwind.input.css). ReferencesPkmds.Core.Pkmds.Web/(Blazor WebAssembly): PWA host. Linking and compression enabled; service worker assets generated on publish. JS libs vialibman.json(crypto-js).Pkmds.Tests/(tests):net10.0, references RCL and Core.
Runtime wiring (Web)
- Serilog to Browser Console with adjustable level (via
LoggingLevelSwitch). - DI:
IAppState,IRefreshService,IAppService,ILegalizationService(Auto-Legality engine; singleton),IBackupService(IndexedDB save backups),IBankService(IndexedDB Pokémon bank), drag/drop, logging service, File System Access, MudBlazor, JS interop. - PKHeX crypto bridged to JS:
RuntimeCryptographyProvider.Aes/Md5set to Blazor providers at startup.
.github/workflows/buildandtest.yml(dev branches; PRs to main):- Setup .NET (from
global.json), install wasm tools, restore/build Web (Release), rundotnet test.
- Setup .NET (from
.github/workflows/main.yml(main):- Setup .NET + wasm tools, restore, publish Web to
release/, copyindex.html→404.html, add.nojekyll, replace%%CACHE_VERSION%%inservice-worker.published.js, deployrelease/wwwroottogh-pages.
- Setup .NET + wasm tools, restore, publish Web to
.github/workflows/codeql.yml: CodeQL for C# and JS/TS (manual C# build step).
This app depends heavily on PKHeX.Core. When implementing features, use the PKHeX WinForms app as a reference for how to leverage PKHeX.Core — both for UI/UX patterns and for understanding the correct API usage. The first place you should look when referencing PKHeX is ~/Code/codemonkey85/PKHeX (macOS) or C:\Code\PKHeX (Windows), which contains the PKHeX WinForms app source code. The PKHeX.Core project within that solution is the library we consume here, and the WinForms app is a separate project that references it. The PKHeX WinForms app is a great reference for how to use PKHeX.Core effectively, as it demonstrates real-world usage of the library's APIs in a production application. If you can't find the source there, the PKHeX Wiki and source code are also valuable resources for understanding how to work with PKHeX.Core.
If you encounter bugs or limitations in PKHeX.Core while working on an issue or PR, note them in a code comment at the relevant site and report them on the GitHub issue or PR you are working on.
Directory.Build.targets at the repo root supports a UseLocalPKHeX MSBuild flag that swaps the NuGet PKHeX.Core package for a ProjectReference to a local source checkout. Use this when you want to:
- Manually test the UI against a PKHeX dev build
- Verify whether a PKHeX bug/regression affects this app
- Test a PKHeX fix before it is released on NuGet
Manual UI testing (watch)
# Default paths: C:\Code\PKHeX (Windows), ~/Code/codemonkey85/PKHeX (macOS/Linux)
./watch-local-pkhex.ps1
# Custom path (e.g. a specific branch checkout)
./watch-local-pkhex.ps1 -PKHeXSourcePath C:\Code\PKHeX-dev\PKHeX.Core\PKHeX.Core.csprojBuild / automated tests
# Build
dotnet build Pkmds.Web/Pkmds.Web.csproj -c Debug -p:UseLocalPKHeX=true
# Test
dotnet test -c Release -p:UseLocalPKHeX=true
# Custom path
dotnet build Pkmds.Web/Pkmds.Web.csproj -c Debug -p:UseLocalPKHeX=true -p:PKHeXSourcePath=/path/to/PKHeX.Core.csprojDefault PKHeXSourcePath values (set in Directory.Build.targets):
- Windows:
C:\Code\PKHeX\PKHeX.Core\PKHeX.Core.csproj - macOS/Linux:
~/Code/codemonkey85/PKHeX/PKHeX.Core/PKHeX.Core.csproj
The default build (no flag) is completely unaffected.
CheckResultis a struct (value type) — never useFirstOrDefault()or other null-returning LINQ on collections of it.- Spelling:
result.Judgement(British English), notJudgment. - Human-readable legality messages:
var ctx = LegalityLocalizationContext.Create(la); ctx.Humanize(in result, verbose: false). - Box iteration:
saveFile.BoxCount,saveFile.BoxSlotCount,saveFile.GetBoxSlotAtIndex(box, slot). - Party iteration:
saveFile.PartyCount,saveFile.GetPartySlotAtIndex(i). ParseSettings.ActiveTraineris internal and set byInitFromSaveFileData(sav)— this enables the handler check inHistoryVerifier.VerifyHandlerState.ParseSettings.AllowGBCartErais set byInitFromSaveFileData—truefor physical Gen 1/2 saves (enables GB era events),falsefor VC saves. Do not override it globally tofalse; that breaks legitimate events (e.g. Nintendo Event Mew, GS Ball Celebi) on physical cartridge saves.
Static JSON data files consumed by DescriptionService are generated from external sources using .NET 10 file-based apps in tools/. Run them whenever the upstream data changes.
Generates ability-info.json, move-info.json, and item-info.json from PokeAPI CSV data, with optional supplements from Pokémon Showdown.
- Source: PokeAPI repo (CSV files under
data/v2/csv/) - Source (optional): Pokémon Showdown repo — supplies two fallbacks when PokeAPI is incomplete:
data/moves.ts— secondary effects (stat changes, status, flinch, drain, multi-hit, crit rate) for Gen 8+ moves that PokeAPI'smove_metaCSV doesn't cover yetdata/text/items.ts—shortDescused as a fallback description for items whose PokeAPIitem_prose.csvrow is empty (most Gen 9 held items, plus Ability Shield, Booster Energy, etc.)
- Output:
Pkmds.Rcl/wwwroot/data/
# Without Showdown (PokeAPI data only)
# macOS:
dotnet run tools/generate-descriptions.cs -- --pokeapi ~/Code/codemonkey85/pokeapi
# Windows:
dotnet run tools/generate-descriptions.cs -- --pokeapi C:\Code\pokeapi
# With Showdown supplement (recommended — fills Gen 8+ move secondary effects and Gen 9 item descriptions)
# macOS:
dotnet run tools/generate-descriptions.cs -- --pokeapi ~/Code/codemonkey85/pokeapi --showdown ~/Code/codemonkey85/pokemon-showdown
# Windows:
dotnet run tools/generate-descriptions.cs -- --pokeapi C:\Code\pokeapi --showdown C:\Code\pokemon-showdownGenerates tm-data.json from the Bulbapedia "List of TMs" page. Also merges Sword/Shield TR data (TR00–TR99) from hardcoded PKHeX.Core move IDs. Requires move-info.json to already exist (run generate-descriptions.cs first).
- Source: Fetched directly from https://bulbapedia.bulbagarden.net/wiki/List_of_TMs (or supply a saved HTML file with
--input) - Output:
Pkmds.Rcl/wwwroot/data/tm-data.json
# Fetch live from Bulbapedia (default)
dotnet run tools/generate-tm-data.cs
# Use a previously saved HTML file
dotnet run tools/generate-tm-data.cs -- --input "path/to/List of TMs - Bulbapedia.html"Both scripts default output to Pkmds.Rcl/wwwroot/data/ by walking up from the working directory to find the repo root. Pass --output /path to override.
Diagnostic script that scans the generated ability-info.json / move-info.json / item-info.json and produces a plain-text report of entries with missing descriptions, missing per-gen flavor, or both. Useful for tracking coverage as upstream data sources fill in.
- Input:
Pkmds.Rcl/wwwroot/data/(override with--data) - Output:
missing-flavor-report.txtat the repo root (override with--output; pass--output -to write to stdout). The report file itself is gitignored — the script is the source of truth.
dotnet run tools/report-missing-descriptions.cs
dotnet run tools/report-missing-descriptions.cs -- --output - # print to stdoutCategorizes entries as:
- Runtime UI gaps — no description AND no flavor; tooltip shows "No description available". Priority list.
- Data completeness gaps — missing description OR flavor but not both.
DescriptionServicerenders gen-appropriate flavor, so these look fine in the UI today; chase them for 100% data parity.
Scrapes pokemondb.net for item and move descriptions that are missing from both PokeAPI and Showdown. Populates tools/data/description-overrides.json, which generate-descriptions.cs reads as a last-resort fallback (applied after PokeAPI short_effect and Showdown shortDesc).
- Source: https://pokemondb.net/item/\ and https://pokemondb.net/move/\. Primary extraction target is the Effects section; falls back to the first row of the Game descriptions table when Effects is empty.
- Output:
tools/data/description-overrides.json(committed — the scrape is slow, so the cache persists). - Rate limit: pokemondb.net's
robots.txtrequiresCrawl-delay: 2; the script defaults to 2500ms between requests. A full scrape of the gap list (~540 entries) takes ~22 minutes.
dotnet run tools/scrape-pokemondb-descriptions.cs # scrape all remaining gaps
dotnet run tools/scrape-pokemondb-descriptions.cs -- --limit 10 # smoke test
dotnet run tools/scrape-pokemondb-descriptions.cs -- --retry-notfound # re-try prior 404s
dotnet run tools/scrape-pokemondb-descriptions.cs -- --force # re-scrape everythingThe scraper is incremental — re-running it only fetches entries that aren't already in the cache (or that were previously 404, unless --retry-notfound is passed). Ctrl+C saves partial progress. After running the scraper, rerun generate-descriptions.cs to pick up the new overrides; passing --overrides is optional because it auto-discovers tools/data/description-overrides.json from the repo root.
ComboItem(PKHeX) is a sealed record (reference type). UsingComboItem?in a Razor@bind-Valuetriggers CS8669 in the Razor-generated code — useint?with.Valuefor select bindings instead.MudExpansionPanelin MudBlazor 9 has noIsInitiallyExpandedorIsExpandedparameters (triggers MUD0002 analyzer error) — omit them; panels start collapsed by default.- Razor integer literals in attributes must be parenthesised:
Value="@((int?)0)", notValue="@0". - Nullable reference type casts in Razor:
(string?)nulltriggers CS8669 — usedefault(string)instead. MudTableRowStyleFuncsignature isFunc<T, int, string>(item + row index), notFunc<T, string>.
Prefer reading local source over fetching from GitHub or relying solely on docs:
- PKHeX: macOS
~/Code/codemonkey85/PKHeX, WindowsC:\Code\PKHeX - MudBlazor: macOS
~/Code/codemonkey85/MudBlazor, WindowsC:\Code\MudBlazor - Pokémon Showdown: macOS
~/Code/codemonkey85/pokemon-showdown, WindowsC:\Code\pokemon-showdown - PokeAPI: macOS
~/Code/codemonkey85/pokeapi, WindowsC:\Code\pokeapi
- Tests: Do not run
dotnet testlocally — leave it to the CI GitHub Actions workflow (.github/workflows/buildandtest.yml). Run onlydotnet formatanddotnet build -c Debugto verify changes locally. - PR review feedback: (1) Review all comments and plan the response; (2) reply to each individual comment on the PR explaining what you're doing and why; (3) make code changes, commit, and push; (4) mark all addressed comments as resolved on the PR.
- Respect the existing code style. Reference
.editorconfigfor formatting rules; Debug builds treat warnings as errors. - Use
watch.ps1for a consistent local dev experience. - If WASM crypto errors occur, ensure
libman restorehas brought downcrypto-js(or run LibMan in your IDE). CI publishes without requiring LibMan on the runner because the published output contains required assets.