Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ repro/
# Local agent and analysis artifacts
.agents/
.codex/
.gemini/
.hermes/
.serena/
.soma/
graphify-out/
graphify-out/cache/
Expand Down
45 changes: 45 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Agent Instructions: NexusUnity

## Repository Scope

This directory is the canonical Nexus Unity package repository.

Use this directory for all Nexus Unity package work:

- Branches, commits, pushes, pull requests, and issue fixes.
- Package source, bridge source, tests, changelog, README, and documentation.
- GitHub issue/PR work for `ForkHorizon/NexusUnity`.

The parent Unity project is only the local test harness:

`/Users/daliys/Daliys/UnityProjects/UnityTestForNexus`

Use the parent project to run the Unity Editor, MCP server, scenes, `unity_lint_project`, and live package validation. Do not modify parent project files unless the user explicitly asks for harness work.

## Branching

- Base branch: `development`.
- Verify the real branch names before creating history.
- Do not auto-push.

## Required Package Updates

When package behavior or public API changes, keep these aligned:

- `CHANGELOG.md`
- `README.md`
- `DOCUMENTATION.MD`
- `API_REFERENCE.MD` when tool contracts or public API shapes change
- Relevant tests under `Tests~/` or `Editor/tests/`

## Validation

Fast package validation:

```bash
PYTHONDONTWRITEBYTECODE=1 scripts/prepush-validate.sh --static-only
```

For Unity-facing behavior, also use the running test harness through MCP tools such as `unity_wait`, `unity_lint_project`, and `unity_editor_controller`.

Clean generated Python caches before validation if needed; `__pycache__` or `*.pyc` files are not part of package changes.
7 changes: 7 additions & 0 deletions AGENTS.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ All notable public changes to Nexus Unity are documented here.
- Invalid `NEXUS_UNITY_TIMEOUT_SECONDS` values now fall back to the default bridge timeout instead of crashing the Python bridge at import time.
- Python bridge type helpers no longer require Python 3.11-only typing features.
- Path security tests now exercise JSON-RPC traversal rejection for representative file and asset tools.
- `get_test_results` now reads messages only from scoped NUnit result nodes and falls back to the test result instead of unrelated nested messages.
- `unity_scene_manager` schema aliases now preserve action-specific required parameters.

## [1.4.2] - 2026-06-13

Expand Down
3 changes: 2 additions & 1 deletion DOCUMENTATION.MD
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Raw HTTP JSON-RPC:
- Intended for diagnostics, custom integrations, and direct automation.
- The complete raw catalog is returned by `list_tools`.
- Runtime schemas intentionally include compatibility fields where needed; for example `update_component` accepts both `properties` and the legacy `json_data` payload, `create_scene` accepts `path` / `open_if_exists`, `create_primitive` accepts transform/material fields, and `create_material` accepts an optional explicit asset `path` plus visible color fields.
- Agent diagnostics include `get_test_results` for Unity `TestResults*.xml` summaries, `get_tool_usage_stats` / `reset_tool_usage_stats` for scoped in-memory call counters and timing, and `ui_get_window_rect` / `ui_set_window_rect` / `ui_capture_window_snapshot` for resize-based editor UI QA.
- Agent diagnostics include `get_test_results` for Unity `TestResults*.xml` summaries with scoped non-passing test messages, `get_tool_usage_stats` / `reset_tool_usage_stats` for scoped in-memory call counters and timing, and `ui_get_window_rect` / `ui_set_window_rect` / `ui_capture_window_snapshot` for resize-based editor UI QA.
- `batch_execute` runs requests serially, accepts at most 50 requests, and rejects nested `batch_execute` calls.

MCP bridge:
Expand All @@ -120,6 +120,7 @@ The current development line preserves backward compatibility while correcting p

- `unity_write_and_compile` is the supported public MCP macro for write, wait, and compiler-feedback workflows. Any old documentation wording that says `unity_apply_code_change` should be treated as stale.
- `update_component` should use a `properties` object for new callers; `json_data` remains accepted for existing callers.
- `unity_scene_manager` aliases such as `create_scene`, `open_scene`, `save_scene`, and `list_scenes` preserve action-specific required parameters.
- `create_scene` accepts optional `path` and `open_if_exists` for deterministic scene assets.
- `create_primitive` accepts optional `name`, `parent_id`, `position`, `rotation`, `scale`, and `material_path`.
- Vector3 fields accept either `{ "x": 0, "y": 1, "z": 0 }` or `[0, 1, 0]`.
Expand Down
4 changes: 3 additions & 1 deletion Editor/MCPServerMethods.TestResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ private static JToken GetTestResults(JToken p)
continue;

string message = testCase.Element("failure")?.Element("message")?.Value
?? testCase.Descendants("message").FirstOrDefault()?.Value;
?? testCase.Element("reason")?.Element("message")?.Value
?? testCase.Element("output")?.Value
?? result;
failedTests.Add(new JObject
{
["name"] = Attr(testCase, "name"),
Expand Down
13 changes: 6 additions & 7 deletions Editor/nexus_bridge/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,12 @@
"description": "Unified scene management (create, open, save, list)",
"inputSchema": {
"type": "object",
"properties": {
"action": {"type": "string", "enum": ["create", "create_scene", "open", "open_scene", "save", "save_scene", "list", "list_scenes"]},
"name": {"type": "string"},
"path": {"type": "string"},
"open_if_exists": {"type": "boolean"}
},
"required": ["action"]
"oneOf": [
{"properties": {"action": {"type": "string", "enum": ["create", "create_scene"]}, "name": {"type": "string"}, "path": {"type": "string"}, "open_if_exists": {"type": "boolean"}}, "required": ["action"]},
{"properties": {"action": {"type": "string", "enum": ["open", "open_scene"]}, "path": {"type": "string"}}, "required": ["action", "path"]},
{"properties": {"action": {"type": "string", "enum": ["save", "save_scene"]}, "path": {"type": "string"}}, "required": ["action"]},
{"properties": {"action": {"type": "string", "enum": ["list", "list_scenes"]}}, "required": ["action"]}
]
}
},
{
Expand Down
11 changes: 11 additions & 0 deletions Editor/tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ def _action_values(variant: dict[str, Any]) -> set[str]:
return set(action_schema["enum"])


class SceneManagerSchemaTests(unittest.TestCase):
def test_scene_manager_keeps_action_specific_requirements(self) -> None:
scene_manager = _get_tool("unity_scene_manager")
variants = scene_manager["inputSchema"]["oneOf"]
open_variant = next(variant for variant in variants if "open_scene" in _action_values(variant))
list_variant = next(variant for variant in variants if "list_scenes" in _action_values(variant))

self.assertCountEqual(["action", "path"], open_variant["required"])
self.assertCountEqual(["action"], list_variant["required"])


class HierarchyManagerSchemaTests(unittest.TestCase):
def test_hierarchy_manager_uses_action_specific_variants(self) -> None:
hierarchy_manager = _get_tool("unity_hierarchy_manager")
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Current development keeps the public API backward-compatible while tightening sc

- Use `unity_write_and_compile` for code-edit workflows. Older references to `unity_apply_code_change` are outdated and should not be used for the public MCP bridge.
- `update_component` accepts the preferred `properties` object and the legacy `json_data` string form.
- `unity_scene_manager` accepts obvious aliases such as `list_scenes`, `create_scene`, `open_scene`, and `save_scene`; invalid manager actions now report the valid action names.
- `unity_scene_manager` accepts obvious aliases such as `list_scenes`, `create_scene`, `open_scene`, and `save_scene` while preserving action-specific required parameters; invalid manager actions now report the valid action names.
- `create_scene` accepts optional `path` and `open_if_exists` so agents can create or reopen a deterministic scene asset in one call.
- `create_primitive` accepts optional `name`, `parent_id`, `position`, `rotation`, `scale`, and `material_path`, which lets agents build visible non-origin objects without fragile follow-up calls.
- Vector3 fields accept either `{ "x": 0, "y": 1, "z": 0 }` or `[0, 1, 0]`.
Expand All @@ -182,7 +182,7 @@ Current development keeps the public API backward-compatible while tightening sc
- `invoke_method.arguments` is an optional positional JSON array.
- `click_object_in_game` accepts a documented `instance_id` target and still supports hierarchy `path` lookup.
- Unity 6.x editor identity differences are handled internally: Nexus keeps JSON `instance_id` values stable while using Unity 6.4+ `EntityId` APIs when available and Unity 6.3-compatible instance ID APIs otherwise.
- `get_test_results` reads Unity `TestResults*.xml` summaries from the project root or Unity persistent data path; `unity_editor_controller` exposes `run_tests_wait` as a bridge-side polling workflow.
- `get_test_results` reads Unity `TestResults*.xml` summaries from the project root or Unity persistent data path, using scoped failure/reason/output messages for non-passing cases; `unity_editor_controller` exposes `run_tests_wait` as a bridge-side polling workflow.
- `get_tool_usage_stats` reports in-memory raw tool counts, durations, and errors since Unity domain load without storing request payloads; `reset_tool_usage_stats` clears that state for scoped diagnostics. Both are also available through `unity_editor_controller`.
- `ui_get_window_rect`, `ui_set_window_rect`, and `ui_capture_window_snapshot` support automated layout checks for editor windows.

Expand Down
12 changes: 12 additions & 0 deletions Tests~/Editor/AgentToolingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ public void GetTestResultsParsesFailingXml()
Assert.AreEqual("Expected true", failure?["message"]?.ToString());
}

[Test]
public void GetTestResultsUsesOnlyScopedMessagesForNonPassingXml()
{
WriteTestResults("<test-run result=\"Failed\" total=\"2\" passed=\"0\" failed=\"0\" inconclusive=\"1\" skipped=\"0\" duration=\"0.2\"><test-suite><test-case name=\"Reason\" fullname=\"Agent.Reason\" result=\"Inconclusive\"><reason><message>No assertions</message></reason><metadata><message>Wrong nested message</message></metadata></test-case><test-case name=\"Error\" fullname=\"Agent.Error\" result=\"Error\"><metadata><message>Wrong nested message</message></metadata></test-case></test-suite></test-run>");

JObject result = RpcResult("get_test_results", new JObject { ["result_path"] = _resultPath });
JArray failures = (JArray)result["failed_tests"];

Assert.AreEqual("No assertions", failures[0]?["message"]?.ToString());
Assert.AreEqual("Error", failures[1]?["message"]?.ToString());
}

[Test]
public void ToolUsageStatsTrackCountsAndErrorsWithoutPayloads()
{
Expand Down
Loading