add-terminal-command
Skill3 starsChanged 2 months agoMITView on GitHub
Add or change an ICommand type or Game.Components domain-id field so the Game.WebClient debug terminal and ECS inspector keep working with zero extra UI code — mandates ParamSuggestionAttribute annotations and the matching provider for genuinely new id kinds.
In this repository45 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .cursor/rules/issue-clarification-questions.mdcCursor rule
- .cursor/rules/reset-font-assets.mdcCursor rule
- .cursor/rules/src-dotnet-build-release.mdcCursor rule
- .cursor/rules/unity-plugins-before-work.mdcCursor rule
- codex-issueSkill
- commitSkill
- complete-prdSkill
- dependency-docsSkill
- dotnet-buildSkill
- dotnet-testSkill
- generate-imageSkill
- implementSkill
- meeting-joinSkill
- meeting-scheduleSkill
- meeting-startSkill
- plan-reviewSkill
- planSkill
- prSkill
- specifySkill
- unity-e2e-runSkill
- unity-pluginsSkill
- dependency-docsSkill
- dotnet-benchmarkSkill
- dotnet-buildSkill
- dotnet-testSkill
- end-game-score-calibrationSkill
- flag-assetsSkill
- github-issue-automationSkill
- image-generationSkill
- localizationSkill
- meeting-joinSkill
- meeting-scheduleSkill
- meeting-startSkill
- milestone-completeSkill
- province-config-generatorSkill
- unity-e2e-runSkill
- unity-pluginsSkill
- unity-prefabsSkill
- unity-scenesSkill
- update-branchSkill
- cursor-meeting-joinSkill
- cursor-meeting-startSkill
- unity-pluginsSkill
The file
Files
add-terminal-command
The web client's debug terminal and the Unity E2E runner share src/Game.Commands.Text/ (CommandRegistry, TerminalParser, ValueCoercion, CommandExecutor) to discover every ICommand type in src/Game.Commands/ via reflection — a new command ships with just a new build, no web-client or runner code change required. Tab completion and ECS inspector domain-id dropdowns depend on every domain-id member being annotated with a ParamSuggestionAttribute from src/Game.Common/ParamSuggestion.cs. Suggestion providers live in src/Game.Commands.Text (not WebClient-only types). This skill is the checklist for keeping that contract intact whenever an ICommand type or a Game.Components domain-id field is added or changed.
When this applies
- Adding a new
ICommandrecord/struct tosrc/Game.Commands/. - Adding a new field or record-positional property to an existing
ICommandtype. - Changing a parameter's type on an existing
ICommandtype (e.g.string→int, or widening a closed string set). - Adding or renaming a public string domain-id field/property on a
src/Game.Componentstype (*Id, plusLocale/TaskId.Value/Locale.Value/OccupierId).
Checklist
Every domain-id parameter gets a
ParamSuggestionAttribute. A "domain-id parameter" is any public field or record-positional property whose name ends inId, or is namedLocale/Interval(the two known non-Idexceptions already in the codebase). Pick the attribute fromsrc/Game.Common/ParamSuggestion.csthat matches what the id refers to:[CountryId]— a country id (CountryConfig.Countries)[OrgId]— an organization id (OrganizationConfig.Organizations)[ProvinceId]— a province id (ProvinceConfig.Provinces)[ActionId]— an action id (ActionConfig.Actions)[RoleId]— a character role id (CharacterConfig.Roles)[CharacterOwnerId]— a character owner, which can be either a country or an org id (union of both)[LocaleId]— a locale code (closed set:en,ru)[ResourceId]— a resource id (ResourceDefinitionsplus live world ids)[CharacterId]— a character id[WarId]/[BattleId]/[EffectId]/[TaskId]/[CollectorId]— matching live/config catalogs[OwnerId]— owner id; default sibling field name isOwnerType; passOwnerTypeSibling = nullfor a live union with no sibling[AllowEmpty = true]on any of the above when the empty string is a valid value
For record types, the attribute goes on the primary-constructor parameter with the
[property: ...]target, matching every existing record command:public record struct SelectCountryCommand([property: CountryId] string CountryId) : ICommand;For plain structs, the attribute goes directly on the public field:
public struct DebugChangeGoldCommand : ICommand { [OrgId] public string OrgId; public double Amount; }Use
[OneOf(...)]for a closed literal set that isn't backed by a config.ChangeAutoSaveIntervalCommand.Intervalis the existing example — the set of valid intervals (daily/monthly/yearly) isn't a config-driven id kind, it's just fixed strings:public record struct ChangeAutoSaveIntervalCommand( [property: OneOf("daily", "monthly", "yearly")] string Interval ) : ICommand;- Enum-typed parameters need no attribute at all.
SuggestionValueResolverauto-suggestsEnum.GetNamesfor any parameter whose CLR type is an enum, regardless of attribute. Don't add aParamSuggestionAttributeto an enum field — it isn't necessary and there is no attribute for it. - A parameter with no attribute and no enum type gets no suggestions — this is
fine, not an error. Plain
int/double/boolparameters, or a genuinely free-form string (not a domain id), are expected to be typed manually. Don't force an attribute onto something that isn't actually a lookup id. - If the parameter is a genuinely new kind of id (not one of the existing
ParamSuggestionAttributesubclasses), add both pieces together in the same change:- A new sealed attribute in
src/Game.Common/ParamSuggestion.cs, following the existing shape ([AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)], derives fromParamSuggestionAttribute). - A matching
ISuggestionValueProviderimplementation insrc/Game.Commands.Text/Suggestions/, wired intoSuggestionValueResolverin the same project. Adding only the attribute without the provider means the terminal silently offers no suggestions for that parameter — not a build error, so it's easy to miss; don't ship one without the other.
- A new sealed attribute in
- The convention test enforces the mandatory part.
src/Game.Tests/ParamSuggestionAttributeTests.cs(EveryCommand_DomainIdMember_CarriesSuggestionAttribute) reflects over every non-abstractICommandtype and fails the build if any public field/property named*Id(orLocale/Interval) lacks aParamSuggestionAttribute.src/Game.Tests/ComponentDomainIdAttributeTests.csdoes the same forGame.Componentsstring*Idmembers (plusLocale/TaskId.Value/Locale.Value). Forgetting step 1 fails these tests, not silently — rundotnet test src/GlobalStrategy.Core.sln(or thedotnet-testskill) after adding a command or component id field to confirm it passes.
What this skill does not cover
- Command execution semantics (what the command does once pushed) — that's
ordinary
ICommand/system work, unrelated to terminal discovery. - Presentation-side rendering of suggestions in
Terminal.razor— that's generic onceSuggestionValueResolverreturns the right list; no per-command UI work is ever needed.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

