From 2ebe4560b6310d85effa0006bfbc455b54600b21 Mon Sep 17 00:00:00 2001 From: TM Hospitality Strategies <154358121+TMHSDigital@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:42:30 -0400 Subject: [PATCH 1/8] chore: add standards-version marker to agent context files (#2) Companion to the skill/rule rollout completed on 2026-04-24 (see TMHSDigital/Developer-Tools-Directory#1). Adds `` as the first line of AGENTS.md and/or CLAUDE.md in this repo. HTML comment format used instead of YAML frontmatter to preserve the pure-markdown convention of these prose guidance documents and avoid risk with GitHub Pages generators. Enables the agent-file drift checker (Phase 2) to verify project-level guidance files reference the current meta-repo standards version. Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 44a3a29..d1e6a9d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,5 @@ + + # AGENTS.md This file tells AI coding agents how the Unity Developer Tools repo works and how to contribute correctly. From 1ff229f8ab4b2b8693f6492c6876cbc8053d902c Mon Sep 17 00:00:00 2001 From: TM Hospitality Strategies <154358121+TMHSDigital@users.noreply.github.com> Date: Fri, 24 Apr 2026 21:40:09 -0400 Subject: [PATCH 2/8] fix: paths-ignore should exclude agent files from release triggers (#3) GitHub Actions paths-ignore globs are root-only - the existing *.md filter does not match subdirectory files like skills/foo/SKILL.md or rules/bar.mdc. As a result, agent file changes were triggering release.yml on push to main, even though those files are documentation, not features. Adds explicit paths-ignore entries for skills/**, rules/**/*.mdc, AGENTS.md, and CLAUDE.md so agent-file-only commits do not trigger releases. Discovered during Phase 2 Session D D-0 audit. Prerequisite for the v1.7.x signal rollout (Session D main batch). See TMHSDigital/Developer-Tools-Directory#1 Phase 2 Session D. Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com Made-with: Cursor Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 335a8c4..fba8232 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,10 @@ on: - "docs/**" - "*.md" - "LICENSE" + - "skills/**" + - "rules/**/*.mdc" + - "AGENTS.md" + - "CLAUDE.md" permissions: contents: write From bc35a2d275dcde727130cfcf78c83451dfdbc184 Mon Sep 17 00:00:00 2001 From: TM Hospitality Strategies <154358121+TMHSDigital@users.noreply.github.com> Date: Fri, 24 Apr 2026 22:48:42 -0400 Subject: [PATCH 3/8] feat: align with ecosystem standards v1.7.0 and add drift-check workflow (#4) Bumps standards-version signals across all skills, rules, and agent context files from 1.6.3 to 1.7.0 to align with the meta-repo's v1.7 standards generation. Adds .github/workflows/drift-check.yml as a standalone workflow that runs the meta-repo's drift checker against this repo's checkout on every PR and push to main. Standalone rather than integrated into existing CI workflows, matching the pattern established in CFX canary (TMHSDigital/CFX-Developer-Tools#4). Phase 2 Session D rollout. Parallel batch following the validated canary pattern. See TMHSDigital/Developer-Tools-Directory#1 Phase 2 Session D. Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com Made-with: Cursor Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com --- .github/workflows/drift-check.yml | 21 +++++++++++++++++++++ AGENTS.md | 2 +- rules/csharp-unity-conventions.mdc | 2 +- rules/monobehaviour-lifecycle.mdc | 2 +- rules/naming-conventions.mdc | 2 +- rules/performance-rules.mdc | 2 +- rules/security-and-builds.mdc | 2 +- rules/serialization-rules.mdc | 2 +- rules/shader-conventions.mdc | 2 +- rules/visual-scripting-conventions.mdc | 2 +- skills/addressables-assets/SKILL.md | 2 +- skills/animation-systems/SKILL.md | 2 +- skills/audio-systems/SKILL.md | 2 +- skills/ecs-dots/SKILL.md | 2 +- skills/editor-scripting/SKILL.md | 2 +- skills/input-systems/SKILL.md | 2 +- skills/monobehaviour-patterns/SKILL.md | 2 +- skills/networking/SKILL.md | 2 +- skills/performance-optimization/SKILL.md | 2 +- skills/physics-2d-3d/SKILL.md | 2 +- skills/platform-targeting/SKILL.md | 2 +- skills/project-setup/SKILL.md | 2 +- skills/render-pipeline-detection/SKILL.md | 2 +- skills/scriptableobjects/SKILL.md | 2 +- skills/shader-development/SKILL.md | 2 +- skills/testing/SKILL.md | 2 +- skills/ui-development/SKILL.md | 2 +- skills/visual-scripting/SKILL.md | 2 +- 28 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/drift-check.yml diff --git a/.github/workflows/drift-check.yml b/.github/workflows/drift-check.yml new file mode 100644 index 0000000..29d5abf --- /dev/null +++ b/.github/workflows/drift-check.yml @@ -0,0 +1,21 @@ +name: Ecosystem drift check + +on: + pull_request: + branches: [main] + push: + branches: [main] + workflow_dispatch: + +jobs: + drift-check: + name: Ecosystem drift check + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v5 + - uses: TMHSDigital/Developer-Tools-Directory/.github/actions/drift-check@v1.7 + with: + mode: self + format: gh-summary diff --git a/AGENTS.md b/AGENTS.md index d1e6a9d..e2a91cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ - + # AGENTS.md diff --git a/rules/csharp-unity-conventions.mdc b/rules/csharp-unity-conventions.mdc index a3d149e..9b69bb1 100644 --- a/rules/csharp-unity-conventions.mdc +++ b/rules/csharp-unity-conventions.mdc @@ -3,7 +3,7 @@ title: C# Unity conventions description: C# coding conventions for Unity development globs: ["**/*.cs"] alwaysApply: false -standards-version: 1.6.3 +standards-version: 1.7.0 --- # C# Unity conventions diff --git a/rules/monobehaviour-lifecycle.mdc b/rules/monobehaviour-lifecycle.mdc index 4c3fddf..f93c961 100644 --- a/rules/monobehaviour-lifecycle.mdc +++ b/rules/monobehaviour-lifecycle.mdc @@ -3,7 +3,7 @@ title: MonoBehaviour lifecycle description: Correct usage of MonoBehaviour lifecycle methods globs: ["**/*.cs"] alwaysApply: true -standards-version: 1.6.3 +standards-version: 1.7.0 --- # MonoBehaviour lifecycle rules diff --git a/rules/naming-conventions.mdc b/rules/naming-conventions.mdc index 8e36646..3c72036 100644 --- a/rules/naming-conventions.mdc +++ b/rules/naming-conventions.mdc @@ -3,7 +3,7 @@ title: Unity naming conventions description: Naming conventions for Unity C# code globs: ["**/*.cs"] alwaysApply: true -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Unity naming conventions diff --git a/rules/performance-rules.mdc b/rules/performance-rules.mdc index ea67a2d..47f0a84 100644 --- a/rules/performance-rules.mdc +++ b/rules/performance-rules.mdc @@ -3,7 +3,7 @@ title: Performance rules description: Performance optimization rules for Unity globs: ["**/*.cs"] alwaysApply: true -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Unity performance rules diff --git a/rules/security-and-builds.mdc b/rules/security-and-builds.mdc index 1c80bd3..e397a91 100644 --- a/rules/security-and-builds.mdc +++ b/rules/security-and-builds.mdc @@ -3,7 +3,7 @@ title: Security and builds description: Security and build configuration rules globs: ["**/*.cs", "**/*.json", "**/*.asset"] alwaysApply: false -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Security and build rules diff --git a/rules/serialization-rules.mdc b/rules/serialization-rules.mdc index 7ee2d42..3c3cc52 100644 --- a/rules/serialization-rules.mdc +++ b/rules/serialization-rules.mdc @@ -3,7 +3,7 @@ title: Serialization rules description: Unity serialization best practices globs: ["**/*.cs"] alwaysApply: false -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Unity serialization rules diff --git a/rules/shader-conventions.mdc b/rules/shader-conventions.mdc index 6fe2a50..08e3c45 100644 --- a/rules/shader-conventions.mdc +++ b/rules/shader-conventions.mdc @@ -3,7 +3,7 @@ title: Shader conventions description: Conventions for Unity shader development globs: ["**/*.shader", "**/*.hlsl", "**/*.cginc", "**/*.shadergraph"] alwaysApply: false -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Shader conventions diff --git a/rules/visual-scripting-conventions.mdc b/rules/visual-scripting-conventions.mdc index 47689a8..36a1e19 100644 --- a/rules/visual-scripting-conventions.mdc +++ b/rules/visual-scripting-conventions.mdc @@ -3,7 +3,7 @@ title: Visual scripting conventions description: Best practices for Unity Visual Scripting globs: ["**/*.asset"] alwaysApply: false -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Visual scripting conventions diff --git a/skills/addressables-assets/SKILL.md b/skills/addressables-assets/SKILL.md index b1d410a..cbcc75e 100644 --- a/skills/addressables-assets/SKILL.md +++ b/skills/addressables-assets/SKILL.md @@ -2,7 +2,7 @@ title: Addressables and Asset Management description: Managing assets with the Addressables system for async loading, memory management, and remote content delivery. globs: ["**/*.cs", "**/*.asset"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Addressables and Asset Management diff --git a/skills/animation-systems/SKILL.md b/skills/animation-systems/SKILL.md index 3a51af0..1265eb6 100644 --- a/skills/animation-systems/SKILL.md +++ b/skills/animation-systems/SKILL.md @@ -2,7 +2,7 @@ title: Animation Systems description: Unity animation workflows including Animator Controller, Timeline, DOTween, and sprite animation for 2D. globs: ["**/*.cs", "**/*.controller", "**/*.anim", "**/*.playable"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Animation Systems diff --git a/skills/audio-systems/SKILL.md b/skills/audio-systems/SKILL.md index 5ab836c..d7d7eb4 100644 --- a/skills/audio-systems/SKILL.md +++ b/skills/audio-systems/SKILL.md @@ -2,7 +2,7 @@ title: Audio Systems description: Audio implementation patterns including AudioSource, AudioMixer, spatial audio, and audio management. globs: ["**/*.cs", "**/*.mixer"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Audio Systems diff --git a/skills/ecs-dots/SKILL.md b/skills/ecs-dots/SKILL.md index 8c2b187..4dd6ae3 100644 --- a/skills/ecs-dots/SKILL.md +++ b/skills/ecs-dots/SKILL.md @@ -2,7 +2,7 @@ title: ECS and DOTS description: Entity Component System development with Unity Entities, Jobs, and Burst for high-performance simulation. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # ECS and DOTS diff --git a/skills/editor-scripting/SKILL.md b/skills/editor-scripting/SKILL.md index 2e4db62..8916be4 100644 --- a/skills/editor-scripting/SKILL.md +++ b/skills/editor-scripting/SKILL.md @@ -2,7 +2,7 @@ title: Editor Scripting description: Extending the Unity Editor with custom inspectors, editor windows, property drawers, gizmos, and Scene View overlays using UI Toolkit. globs: ["**/Editor/**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Editor Scripting diff --git a/skills/input-systems/SKILL.md b/skills/input-systems/SKILL.md index 23cb92e..8b286ed 100644 --- a/skills/input-systems/SKILL.md +++ b/skills/input-systems/SKILL.md @@ -2,7 +2,7 @@ title: Input Systems description: Input handling with the New Input System package and legacy Input Manager migration guidance. globs: ["**/*.cs", "**/*.inputactions"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Input Systems diff --git a/skills/monobehaviour-patterns/SKILL.md b/skills/monobehaviour-patterns/SKILL.md index 4f2813e..01d90ce 100644 --- a/skills/monobehaviour-patterns/SKILL.md +++ b/skills/monobehaviour-patterns/SKILL.md @@ -2,7 +2,7 @@ title: MonoBehaviour Patterns description: Comprehensive guide to MonoBehaviour lifecycle, async patterns with Awaitable, and common Unity design patterns. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # MonoBehaviour Patterns diff --git a/skills/networking/SKILL.md b/skills/networking/SKILL.md index b74efb7..dbe4e3b 100644 --- a/skills/networking/SKILL.md +++ b/skills/networking/SKILL.md @@ -2,7 +2,7 @@ title: Multiplayer Networking description: Multiplayer networking patterns with Netcode for GameObjects, Netcode for Entities, Mirror, and Photon Fusion. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Multiplayer Networking diff --git a/skills/performance-optimization/SKILL.md b/skills/performance-optimization/SKILL.md index fe0b2e3..4644a6e 100644 --- a/skills/performance-optimization/SKILL.md +++ b/skills/performance-optimization/SKILL.md @@ -2,7 +2,7 @@ title: Performance Optimization description: Unity-specific performance best practices for CPU, GPU, memory, and profiling tools. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Performance Optimization diff --git a/skills/physics-2d-3d/SKILL.md b/skills/physics-2d-3d/SKILL.md index 90ca7ab..70cd590 100644 --- a/skills/physics-2d-3d/SKILL.md +++ b/skills/physics-2d-3d/SKILL.md @@ -2,7 +2,7 @@ title: Physics Systems (2D and 3D) description: Physics programming for both 2D and 3D Unity projects including collision, raycasting, layers, and rigidbody management. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Physics Systems (2D and 3D) diff --git a/skills/platform-targeting/SKILL.md b/skills/platform-targeting/SKILL.md index 2d08356..c02f3d7 100644 --- a/skills/platform-targeting/SKILL.md +++ b/skills/platform-targeting/SKILL.md @@ -2,7 +2,7 @@ title: Platform Targeting description: Platform-specific compilation, scripting defines, build settings, and cross-platform considerations. globs: ["**/*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Platform Targeting diff --git a/skills/project-setup/SKILL.md b/skills/project-setup/SKILL.md index e4e5fcc..92a3a2a 100644 --- a/skills/project-setup/SKILL.md +++ b/skills/project-setup/SKILL.md @@ -2,7 +2,7 @@ title: Unity Project Setup description: Guide for creating and configuring Unity projects with recommended folder structure, assembly definitions, version control, and package management. globs: ["**/*.asmdef", "**/*.asmref", "**/ProjectSettings/**"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Unity Project Setup diff --git a/skills/render-pipeline-detection/SKILL.md b/skills/render-pipeline-detection/SKILL.md index 5d5d0e6..c585bcf 100644 --- a/skills/render-pipeline-detection/SKILL.md +++ b/skills/render-pipeline-detection/SKILL.md @@ -2,7 +2,7 @@ title: Render Pipeline Detection description: Detecting the active render pipeline (URP, HDRP, or Built-in) and adapting code, shaders, and settings accordingly. globs: ["**/*.cs", "**/*.shader", "**/*.shadergraph"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Render Pipeline Detection diff --git a/skills/scriptableobjects/SKILL.md b/skills/scriptableobjects/SKILL.md index f40eaf5..0514a22 100644 --- a/skills/scriptableobjects/SKILL.md +++ b/skills/scriptableobjects/SKILL.md @@ -2,7 +2,7 @@ title: ScriptableObject Architecture description: Data-driven design patterns using ScriptableObjects for events, variables, runtime sets, and configuration. globs: ["**/*.cs", "**/*.asset"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # ScriptableObject Architecture diff --git a/skills/shader-development/SKILL.md b/skills/shader-development/SKILL.md index fe1a0fe..5544485 100644 --- a/skills/shader-development/SKILL.md +++ b/skills/shader-development/SKILL.md @@ -2,7 +2,7 @@ title: Shader Development description: Shader creation with Shader Graph, HLSL, and ShaderLab for URP and HDRP projects. globs: ["**/*.shader", "**/*.hlsl", "**/*.cginc", "**/*.shadergraph"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Shader Development diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md index a7d23ff..4c925f5 100644 --- a/skills/testing/SKILL.md +++ b/skills/testing/SKILL.md @@ -2,7 +2,7 @@ title: Unity Testing description: Unity Test Framework usage for Edit Mode and Play Mode tests with async Awaitable support. globs: ["**/Tests/**/*.cs", "**/*Tests*.cs", "**/*Test*.cs"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Unity Testing diff --git a/skills/ui-development/SKILL.md b/skills/ui-development/SKILL.md index ca0a174..4906746 100644 --- a/skills/ui-development/SKILL.md +++ b/skills/ui-development/SKILL.md @@ -2,7 +2,7 @@ title: UI Development description: Building user interfaces with UI Toolkit (primary) and Canvas/UGUI, including data binding, styling, and responsive layouts. globs: ["**/*.cs", "**/*.uxml", "**/*.uss", "**/*.prefab"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # UI Development diff --git a/skills/visual-scripting/SKILL.md b/skills/visual-scripting/SKILL.md index da24104..08b242a 100644 --- a/skills/visual-scripting/SKILL.md +++ b/skills/visual-scripting/SKILL.md @@ -2,7 +2,7 @@ title: Visual Scripting description: Unity Visual Scripting guidance for Script Graphs, State Graphs, Subgraphs, and custom units. globs: ["**/*.asset"] -standards-version: 1.6.3 +standards-version: 1.7.0 --- # Visual Scripting From 4d8ff27d57efa8d66e1dc1877d385ac77551e07b Mon Sep 17 00:00:00 2001 From: TM Hospitality Strategies <154358121+TMHSDigital@users.noreply.github.com> Date: Sat, 25 Apr 2026 09:42:06 -0400 Subject: [PATCH 4/8] fix: replace inline python3 -c blocks and bare heredocs with run: | block scalars in two workflows (#6) Fixes #5. Same root cause class as the CFX YAML quoting cluster (TMHSDigital/CFX-Developer-Tools#5, #6, #7, fixed in CFX PR #8). `validate.yml` and `update-unity-api.yml` had two distinct flavors of the same bug, both producing YAML scanner errors that prevented all jobs from running: 1. Three `python3 -c "..."` blocks in `validate-plugin-manifest` (lines 100-130 of validate.yml). The unquoted multiline form is parsed as a plain scalar and the embedded colons (`for n in data:`, `assert ...:`) terminate the scalar prematurely. 2. Seven `python3 << 'PYEOF'` blocks across both files where the python body started at column 0 with no `run: |` block-scalar marker. The `run` value is parsed as the single-line plain scalar `python3 << 'PYEOF'` and the column-0 python body is then treated as top-level YAML tokens, producing `ScannerError: while scanning a simple key, could not find expected ':'`. Both flavors are fixed by wrapping each `run` value in a `run: |` literal block scalar with the python heredoc body indented to match. Heredoc delimiter `PYEOF` is preserved across both files for consistency with the rest of the original file. Surfaced during ecosystem audit on 2026-04-25 (TMHSDigital/Developer-Tools-Directory#1 close-out, audit report ecosystem-audit-2026-04-25.md). Verified locally: - Both workflow files parse cleanly under PyYAML safe_load with newlines preserved in every python script body (10 blocks total). - Every python validation block runs successfully against the actual repo data: - 5 MCP data JSONs validated (unity_api_common 59, deprecated_patterns 17, lifecycle_order 25, shader_properties 6, platform_defines 9). - plugin manifest schema (name, version, author, keywords) valid. - All 18 skills + 8 rules pass frontmatter checks. - All 20 snippets non-empty. - All 5 templates have README.md and C# scripts. - Counts confirmed: 18 skills, 8 rules, 20 snippets, 5 templates match plugin.json and README.md. - Representative heredocs exercised end-to-end via bash to confirm the heredoc-to-python pipe works. Closes #5. Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com Made-with: Cursor Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com --- .github/workflows/update-unity-api.yml | 37 +- .github/workflows/validate.yml | 485 +++++++++++++------------ 2 files changed, 266 insertions(+), 256 deletions(-) diff --git a/.github/workflows/update-unity-api.yml b/.github/workflows/update-unity-api.yml index 408a3cc..a7fa647 100644 --- a/.github/workflows/update-unity-api.yml +++ b/.github/workflows/update-unity-api.yml @@ -23,25 +23,26 @@ jobs: run: python3 .github/scripts/refresh_unity_data.py - name: Validate updated data - run: python3 << 'PYEOF' -import json + run: | + python3 << 'PYEOF' + import json -for name, required_fields in [ - ('lifecycle_order.json', ['method', 'phase', 'description']), - ('deprecated_patterns.json', ['legacy', 'replacement', 'reason']), - ('unity_api_common.json', ['name', 'namespace', 'category']), - ('shader_properties.json', ['effect', 'description', 'pipelines']), - ('platform_defines.json', ['platform', 'display_name', 'define']), -]: - path = f'mcp-server/data/{name}' - data = json.load(open(path)) - assert isinstance(data, list), f'{name}: expected array' - assert len(data) > 0, f'{name}: empty array' - for entry in data: - for field in required_fields: - assert field in entry, f'{name}: missing {field}' - print(f'{name}: {len(data)} entries valid') -PYEOF + for name, required_fields in [ + ('lifecycle_order.json', ['method', 'phase', 'description']), + ('deprecated_patterns.json', ['legacy', 'replacement', 'reason']), + ('unity_api_common.json', ['name', 'namespace', 'category']), + ('shader_properties.json', ['effect', 'description', 'pipelines']), + ('platform_defines.json', ['platform', 'display_name', 'define']), + ]: + path = f'mcp-server/data/{name}' + data = json.load(open(path)) + assert isinstance(data, list), f'{name}: expected array' + assert len(data) > 0, f'{name}: empty array' + for entry in data: + for field in required_fields: + assert field in entry, f'{name}: missing {field}' + print(f'{name}: {len(data)} entries valid') + PYEOF - name: Check for changes id: diff diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 0cee825..acdafa4 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -23,73 +23,74 @@ jobs: run: python3 -c "import json; json.load(open('.cursor/mcp.json'))" - name: Validate MCP data file schemas - run: python3 << 'PYEOF' -import json, os, sys - -errors = [] - -def check(condition, msg): - if not condition: - errors.append(msg) - -# unity_api_common.json -data = json.load(open('mcp-server/data/unity_api_common.json')) -check(isinstance(data, list), 'unity_api_common.json must be an array') -for i, entry in enumerate(data): - check('name' in entry, f'unity_api_common[{i}]: missing name') - check('namespace' in entry, f'unity_api_common[{i}]: missing namespace') - check('category' in entry, f'unity_api_common[{i}]: missing category') - check('description' in entry, f'unity_api_common[{i}]: missing description') - check('signature' in entry, f'unity_api_common[{i}]: missing signature') -print(f'unity_api_common.json: {len(data)} entries') - -# deprecated_patterns.json -data = json.load(open('mcp-server/data/deprecated_patterns.json')) -check(isinstance(data, list), 'deprecated_patterns.json must be an array') -for i, entry in enumerate(data): - check('legacy' in entry, f'deprecated_patterns[{i}]: missing legacy') - check('replacement' in entry, f'deprecated_patterns[{i}]: missing replacement') - check('reason' in entry, f'deprecated_patterns[{i}]: missing reason') - check('since_version' in entry, f'deprecated_patterns[{i}]: missing since_version') -print(f'deprecated_patterns.json: {len(data)} entries') - -# lifecycle_order.json -data = json.load(open('mcp-server/data/lifecycle_order.json')) -check(isinstance(data, list), 'lifecycle_order.json must be an array') -for i, entry in enumerate(data): - check('method' in entry, f'lifecycle_order[{i}]: missing method') - check('phase' in entry, f'lifecycle_order[{i}]: missing phase') - check('description' in entry, f'lifecycle_order[{i}]: missing description') - check(isinstance(entry.get('runs_per_frame'), bool), f'lifecycle_order[{i}]: runs_per_frame must be bool') -print(f'lifecycle_order.json: {len(data)} entries') - -# shader_properties.json -data = json.load(open('mcp-server/data/shader_properties.json')) -check(isinstance(data, list), 'shader_properties.json must be an array') -for i, entry in enumerate(data): - check('effect' in entry, f'shader_properties[{i}]: missing effect') - check('description' in entry, f'shader_properties[{i}]: missing description') - check(isinstance(entry.get('pipelines'), list), f'shader_properties[{i}]: pipelines must be array') - check(isinstance(entry.get('properties'), list), f'shader_properties[{i}]: properties must be array') -print(f'shader_properties.json: {len(data)} entries') - -# platform_defines.json -data = json.load(open('mcp-server/data/platform_defines.json')) -check(isinstance(data, list), 'platform_defines.json must be an array') -for i, entry in enumerate(data): - check('platform' in entry, f'platform_defines[{i}]: missing platform') - check('display_name' in entry, f'platform_defines[{i}]: missing display_name') - check('define' in entry, f'platform_defines[{i}]: missing define') - check(isinstance(entry.get('capabilities'), list), f'platform_defines[{i}]: capabilities must be array') -print(f'platform_defines.json: {len(data)} entries') - -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) - -print('All MCP data schemas valid') -PYEOF + run: | + python3 << 'PYEOF' + import json, os, sys + + errors = [] + + def check(condition, msg): + if not condition: + errors.append(msg) + + # unity_api_common.json + data = json.load(open('mcp-server/data/unity_api_common.json')) + check(isinstance(data, list), 'unity_api_common.json must be an array') + for i, entry in enumerate(data): + check('name' in entry, f'unity_api_common[{i}]: missing name') + check('namespace' in entry, f'unity_api_common[{i}]: missing namespace') + check('category' in entry, f'unity_api_common[{i}]: missing category') + check('description' in entry, f'unity_api_common[{i}]: missing description') + check('signature' in entry, f'unity_api_common[{i}]: missing signature') + print(f'unity_api_common.json: {len(data)} entries') + + # deprecated_patterns.json + data = json.load(open('mcp-server/data/deprecated_patterns.json')) + check(isinstance(data, list), 'deprecated_patterns.json must be an array') + for i, entry in enumerate(data): + check('legacy' in entry, f'deprecated_patterns[{i}]: missing legacy') + check('replacement' in entry, f'deprecated_patterns[{i}]: missing replacement') + check('reason' in entry, f'deprecated_patterns[{i}]: missing reason') + check('since_version' in entry, f'deprecated_patterns[{i}]: missing since_version') + print(f'deprecated_patterns.json: {len(data)} entries') + + # lifecycle_order.json + data = json.load(open('mcp-server/data/lifecycle_order.json')) + check(isinstance(data, list), 'lifecycle_order.json must be an array') + for i, entry in enumerate(data): + check('method' in entry, f'lifecycle_order[{i}]: missing method') + check('phase' in entry, f'lifecycle_order[{i}]: missing phase') + check('description' in entry, f'lifecycle_order[{i}]: missing description') + check(isinstance(entry.get('runs_per_frame'), bool), f'lifecycle_order[{i}]: runs_per_frame must be bool') + print(f'lifecycle_order.json: {len(data)} entries') + + # shader_properties.json + data = json.load(open('mcp-server/data/shader_properties.json')) + check(isinstance(data, list), 'shader_properties.json must be an array') + for i, entry in enumerate(data): + check('effect' in entry, f'shader_properties[{i}]: missing effect') + check('description' in entry, f'shader_properties[{i}]: missing description') + check(isinstance(entry.get('pipelines'), list), f'shader_properties[{i}]: pipelines must be array') + check(isinstance(entry.get('properties'), list), f'shader_properties[{i}]: properties must be array') + print(f'shader_properties.json: {len(data)} entries') + + # platform_defines.json + data = json.load(open('mcp-server/data/platform_defines.json')) + check(isinstance(data, list), 'platform_defines.json must be an array') + for i, entry in enumerate(data): + check('platform' in entry, f'platform_defines[{i}]: missing platform') + check('display_name' in entry, f'platform_defines[{i}]: missing display_name') + check('define' in entry, f'platform_defines[{i}]: missing define') + check(isinstance(entry.get('capabilities'), list), f'platform_defines[{i}]: capabilities must be array') + print(f'platform_defines.json: {len(data)} entries') + + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) + + print('All MCP data schemas valid') + PYEOF validate-plugin-manifest: name: Validate plugin manifest @@ -98,7 +99,8 @@ PYEOF - uses: actions/checkout@v4 - name: Check required manifest fields - run: python3 -c " + run: | + python3 << 'PYEOF' import json, re m = json.load(open('.cursor-plugin/plugin.json')) required = ['name', 'displayName', 'description', 'version', 'author', 'license', 'skills', 'rules'] @@ -109,25 +111,27 @@ PYEOF assert isinstance(m.get('author'), dict) and 'name' in m['author'], 'author must have name' assert isinstance(m.get('keywords'), list), 'keywords must be an array' print('Plugin manifest valid') - " + PYEOF - name: Check skill files exist - run: python3 -c " + run: | + python3 << 'PYEOF' import json, os m = json.load(open('.cursor-plugin/plugin.json')) for skill in m.get('skills', []): assert os.path.exists(skill), f'Skill not found: {skill}' - print(f'All {len(m[\"skills\"])} skill files exist') - " + print(f'All {len(m["skills"])} skill files exist') + PYEOF - name: Check rule files exist - run: python3 -c " + run: | + python3 << 'PYEOF' import json, os m = json.load(open('.cursor-plugin/plugin.json')) for rule in m.get('rules', []): assert os.path.exists(rule), f'Rule not found: {rule}' - print(f'All {len(m[\"rules\"])} rule files exist') - " + print(f'All {len(m["rules"])} rule files exist') + PYEOF validate-skills: name: Validate skill files @@ -136,50 +140,51 @@ PYEOF - uses: actions/checkout@v4 - name: Check SKILL.md frontmatter - run: python3 << 'PYEOF' -import os, sys, re - -errors = [] -skill_dirs = [ - d for d in os.listdir('skills') - if os.path.isdir(os.path.join('skills', d)) -] - -for d in skill_dirs: - path = os.path.join('skills', d, 'SKILL.md') - if not os.path.exists(path): - errors.append(f'{path}: SKILL.md missing') - continue - - content = open(path).read() - if not content.startswith('---'): - errors.append(f'{path}: missing YAML frontmatter') - continue - - parts = content.split('---', 2) - if len(parts) < 3: - errors.append(f'{path}: malformed frontmatter (no closing ---)') - continue - - fm = parts[1] - if 'title:' not in fm: - errors.append(f'{path}: frontmatter missing title') - if 'description:' not in fm: - errors.append(f'{path}: frontmatter missing description') - if 'globs:' not in fm: - errors.append(f'{path}: frontmatter missing globs') - - body = parts[2].strip() - if len(body) < 100: - errors.append(f'{path}: body too short ({len(body)} chars, minimum 100)') - -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) - -print(f'All {len(skill_dirs)} skills have valid frontmatter and content') -PYEOF + run: | + python3 << 'PYEOF' + import os, sys, re + + errors = [] + skill_dirs = [ + d for d in os.listdir('skills') + if os.path.isdir(os.path.join('skills', d)) + ] + + for d in skill_dirs: + path = os.path.join('skills', d, 'SKILL.md') + if not os.path.exists(path): + errors.append(f'{path}: SKILL.md missing') + continue + + content = open(path).read() + if not content.startswith('---'): + errors.append(f'{path}: missing YAML frontmatter') + continue + + parts = content.split('---', 2) + if len(parts) < 3: + errors.append(f'{path}: malformed frontmatter (no closing ---)') + continue + + fm = parts[1] + if 'title:' not in fm: + errors.append(f'{path}: frontmatter missing title') + if 'description:' not in fm: + errors.append(f'{path}: frontmatter missing description') + if 'globs:' not in fm: + errors.append(f'{path}: frontmatter missing globs') + + body = parts[2].strip() + if len(body) < 100: + errors.append(f'{path}: body too short ({len(body)} chars, minimum 100)') + + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) + + print(f'All {len(skill_dirs)} skills have valid frontmatter and content') + PYEOF validate-rules: name: Validate rule files @@ -188,42 +193,43 @@ PYEOF - uses: actions/checkout@v4 - name: Check .mdc frontmatter - run: python3 << 'PYEOF' -import os, sys + run: | + python3 << 'PYEOF' + import os, sys -errors = [] -rule_files = [f for f in os.listdir('rules') if f.endswith('.mdc')] + errors = [] + rule_files = [f for f in os.listdir('rules') if f.endswith('.mdc')] -for f in rule_files: - path = os.path.join('rules', f) - content = open(path).read() + for f in rule_files: + path = os.path.join('rules', f) + content = open(path).read() - if not content.startswith('---'): - errors.append(f'{path}: missing YAML frontmatter') - continue + if not content.startswith('---'): + errors.append(f'{path}: missing YAML frontmatter') + continue - parts = content.split('---', 2) - if len(parts) < 3: - errors.append(f'{path}: malformed frontmatter (no closing ---)') - continue + parts = content.split('---', 2) + if len(parts) < 3: + errors.append(f'{path}: malformed frontmatter (no closing ---)') + continue - fm = parts[1] - required_fields = ['title', 'description', 'globs', 'alwaysApply'] - for field in required_fields: - if f'{field}:' not in fm: - errors.append(f'{path}: frontmatter missing {field}') + fm = parts[1] + required_fields = ['title', 'description', 'globs', 'alwaysApply'] + for field in required_fields: + if f'{field}:' not in fm: + errors.append(f'{path}: frontmatter missing {field}') - body = parts[2].strip() - if len(body) < 20: - errors.append(f'{path}: body too short ({len(body)} chars)') + body = parts[2].strip() + if len(body) < 20: + errors.append(f'{path}: body too short ({len(body)} chars)') -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) -print(f'All {len(rule_files)} rules have valid frontmatter') -PYEOF + print(f'All {len(rule_files)} rules have valid frontmatter') + PYEOF validate-content: name: Validate content quality @@ -249,27 +255,28 @@ PYEOF echo "No hardcoded credentials found" - name: Check snippets are non-empty - run: python3 << 'PYEOF' -import os, sys - -errors = [] -for root, dirs, files in os.walk('snippets'): - for f in files: - if f == 'README.md': - continue - path = os.path.join(root, f) - size = os.path.getsize(path) - if size < 10: - errors.append(f'{path}: file is empty or too small ({size} bytes)') - -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) - -count = sum(len(files) for _, _, files in os.walk('snippets')) -print(f'All {count} snippet files are non-empty') -PYEOF + run: | + python3 << 'PYEOF' + import os, sys + + errors = [] + for root, dirs, files in os.walk('snippets'): + for f in files: + if f == 'README.md': + continue + path = os.path.join(root, f) + size = os.path.getsize(path) + if size < 10: + errors.append(f'{path}: file is empty or too small ({size} bytes)') + + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) + + count = sum(len(files) for _, _, files in os.walk('snippets')) + print(f'All {count} snippet files are non-empty') + PYEOF validate-templates: name: Validate templates @@ -278,38 +285,39 @@ PYEOF - uses: actions/checkout@v4 - name: Check template structure - run: python3 << 'PYEOF' -import os, sys - -errors = [] -template_dirs = [ - d for d in os.listdir('templates') - if os.path.isdir(os.path.join('templates', d)) -] - -for d in template_dirs: - tdir = os.path.join('templates', d) - readme = os.path.join(tdir, 'README.md') - if not os.path.exists(readme): - errors.append(f'{tdir}: missing README.md') - - cs_files = [f for f in os.listdir(tdir) if f.endswith('.cs')] - if not cs_files: - errors.append(f'{tdir}: no .cs files found') - - for cs in cs_files: - path = os.path.join(tdir, cs) - content = open(path).read() - if len(content) < 50: - errors.append(f'{path}: file too small ({len(content)} chars)') - -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) - -print(f'All {len(template_dirs)} templates have README.md and C# scripts') -PYEOF + run: | + python3 << 'PYEOF' + import os, sys + + errors = [] + template_dirs = [ + d for d in os.listdir('templates') + if os.path.isdir(os.path.join('templates', d)) + ] + + for d in template_dirs: + tdir = os.path.join('templates', d) + readme = os.path.join(tdir, 'README.md') + if not os.path.exists(readme): + errors.append(f'{tdir}: missing README.md') + + cs_files = [f for f in os.listdir(tdir) if f.endswith('.cs')] + if not cs_files: + errors.append(f'{tdir}: no .cs files found') + + for cs in cs_files: + path = os.path.join(tdir, cs) + content = open(path).read() + if len(content) < 50: + errors.append(f'{path}: file too small ({len(content)} chars)') + + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) + + print(f'All {len(template_dirs)} templates have README.md and C# scripts') + PYEOF validate-counts: name: Validate content counts @@ -318,44 +326,45 @@ PYEOF - uses: actions/checkout@v4 - name: Check content counts match README - run: python3 << 'PYEOF' -import json, os, sys - -m = json.load(open('.cursor-plugin/plugin.json')) -errors = [] - -skill_count = len(m.get('skills', [])) -rule_count = len(m.get('rules', [])) - -snippet_count = sum( - len(files) - for root, dirs, files in os.walk('snippets') - if files -) - -template_count = sum( - 1 for d in os.listdir('templates') - if os.path.isdir(os.path.join('templates', d)) - and os.path.exists(os.path.join('templates', d, 'README.md')) -) - -readme = open('README.md').read() -if f'{skill_count} skills' not in readme: - errors.append(f'README skill count mismatch (expected {skill_count})') -if f'{snippet_count} snippets' not in readme: - errors.append(f'README snippet count mismatch (expected {snippet_count})') -if f'{template_count} templates' not in readme: - errors.append(f'README template count mismatch (expected {template_count})') -if f'{rule_count} rules' not in readme: - errors.append(f'README rule count mismatch (expected {rule_count})') - -if errors: - for e in errors: - print(f'::error::{e}', file=sys.stderr) - sys.exit(1) - -print(f'Counts verified: {skill_count} skills, {rule_count} rules, {snippet_count} snippets, {template_count} templates') -PYEOF + run: | + python3 << 'PYEOF' + import json, os, sys + + m = json.load(open('.cursor-plugin/plugin.json')) + errors = [] + + skill_count = len(m.get('skills', [])) + rule_count = len(m.get('rules', [])) + + snippet_count = sum( + len(files) + for root, dirs, files in os.walk('snippets') + if files + ) + + template_count = sum( + 1 for d in os.listdir('templates') + if os.path.isdir(os.path.join('templates', d)) + and os.path.exists(os.path.join('templates', d, 'README.md')) + ) + + readme = open('README.md').read() + if f'{skill_count} skills' not in readme: + errors.append(f'README skill count mismatch (expected {skill_count})') + if f'{snippet_count} snippets' not in readme: + errors.append(f'README snippet count mismatch (expected {snippet_count})') + if f'{template_count} templates' not in readme: + errors.append(f'README template count mismatch (expected {template_count})') + if f'{rule_count} rules' not in readme: + errors.append(f'README rule count mismatch (expected {rule_count})') + + if errors: + for e in errors: + print(f'::error::{e}', file=sys.stderr) + sys.exit(1) + + print(f'Counts verified: {skill_count} skills, {rule_count} rules, {snippet_count} snippets, {template_count} templates') + PYEOF validate-python: name: Validate MCP server From f844dcbb901972fd0714236a75baace2e211eb48 Mon Sep 17 00:00:00 2001 From: TM Hospitality Strategies <154358121+TMHSDigital@users.noreply.github.com> Date: Sat, 25 Apr 2026 11:34:29 -0400 Subject: [PATCH 5/8] feat: integrate release-doc-sync action to keep CHANGELOG aligned (#7) Adds the release-doc-sync@v1.0 composite action from the meta-repo to release.yml. After plugin.json is bumped on auto-release, the action prepends a stub entry to CHANGELOG.md for the new version. This repo does not ship CLAUDE.md or ROADMAP.md, so the action's CLAUDE/ROADMAP edits are no-ops here (the action reports them as "missing" and exits cleanly). The existing "Commit version bump" step uses a selective `git add .cursor-plugin/plugin.json README.md` rather than `git add -A`, so CHANGELOG.md is added to that list to ensure the prepended entry actually lands in the release commit. Without this change the action would run but its sole useful output (the CHANGELOG prepend) would be silently discarded. Phase 2c rollout following the Docker smoke test pattern. The action is verified working at v1.8.2 (pinned via @v1.0 floating tag). Refs TMHSDigital/Developer-Tools-Directory#5. Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com Signed-off-by: 154358121+TMHSDigital@users.noreply.github.com --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fba8232..30828f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -122,6 +122,13 @@ jobs: print(f'Updated README.md badge to {new_version}') " + - name: Sync release docs + if: steps.check.outputs.skip == 'false' + uses: TMHSDigital/Developer-Tools-Directory/.github/actions/release-doc-sync@v1.0 + with: + plugin-version: ${{ steps.new.outputs.version }} + previous-version: ${{ steps.current.outputs.version }} + - name: Generate release notes from commits if: steps.check.outputs.skip == 'false' id: notes @@ -200,7 +207,7 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add .cursor-plugin/plugin.json README.md + git add .cursor-plugin/plugin.json README.md CHANGELOG.md git commit -m "chore: bump version to ${{ steps.new.outputs.version }} [skip ci]" git push From 610bc509b55d432b17c1e44b56072c27edb6f840 Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Sat, 25 Apr 2026 11:35:27 -0400 Subject: [PATCH 6/8] chore: trigger release for Phase 2c verification From 958b6db5da1cd18e79c96d0e118c3bcfc6ad98fd Mon Sep 17 00:00:00 2001 From: fOuttaMyPaint Date: Sat, 25 Apr 2026 11:39:50 -0400 Subject: [PATCH 7/8] chore: trigger release for Phase 2c release-doc-sync verification Empty commits are suppressed by release.yml paths-ignore (vacuous match), so this touches a non-ignored path to force a release run that exercises the newly-integrated release-doc-sync@v1.0 action. Refs TMHSDigital/Developer-Tools-Directory#5. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cbfe952..23eca4d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ desktop.ini # Research (reference only, not needed in published plugin) RESEARCH.md +# Phase 2c: release-doc-sync verification trigger (DTD#5) From fa76a7660a4e2cb31566c0576c333a9d06c3684e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 15:40:03 +0000 Subject: [PATCH 8/8] chore: bump version to 1.4.0 [skip ci] --- .cursor-plugin/plugin.json | 2 +- CHANGELOG.md | 4 ++++ README.md | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 7002d78..618c07d 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "unity-developer-tools", "displayName": "Unity Developer Tools", "description": "AI-powered development toolkit for Unity. Scaffold scripts, look up APIs, write shaders, and build games with best-practice rules for C#, Visual Scripting, and HLSL.", - "version": "1.3.3", + "version": "1.4.0", "author": { "name": "TMHSDigital", "email": "contact@tmhospitalitystrategies.com" diff --git a/CHANGELOG.md b/CHANGELOG.md index 97e076c..7f013f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [1.4.0] - 2026-04-25 + +See [release notes](https://github.com/TMHSDigital/Unity-Developer-Tools/releases/tag/v1.4.0) for details. + ## [0.1.0] - 2026-04-08 ### Added diff --git a/README.md b/README.md index cb7f92f..71bcba2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

License: CC BY-NC-ND 4.0 - Version + Version GitHub stars docs online