diff --git a/.githooks/post-commit b/.githooks/post-commit new file mode 100755 index 0000000..419f1cc --- /dev/null +++ b/.githooks/post-commit @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -eu + +REPO_ROOT=$(git rev-parse --show-toplevel) + +if [ -d "$REPO_ROOT/.projectmem" ] && command -v pjm >/dev/null 2>&1; then + (cd "$REPO_ROOT" && pjm _auto-capture commit >/dev/null 2>&1 &) +fi diff --git a/.githooks/post-merge b/.githooks/post-merge new file mode 100755 index 0000000..768f500 --- /dev/null +++ b/.githooks/post-merge @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -eu + +REPO_ROOT=$(git rev-parse --show-toplevel) + +if [ -d "$REPO_ROOT/.projectmem" ] && command -v pjm >/dev/null 2>&1; then + (cd "$REPO_ROOT" && pjm _auto-capture merge >/dev/null 2>&1 &) +fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..e0f454f --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +set -eu + +REPO_ROOT=$(git rev-parse --show-toplevel) + +if [ -d "$REPO_ROOT/.projectmem" ] && command -v pjm >/dev/null 2>&1; then + (cd "$REPO_ROOT" && pjm precheck --level warn) || true +fi diff --git a/.gitignore b/.gitignore index 04c5246..500f31b 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,19 @@ LINT_REPORT.txt temp_verify/ *.tmp AUDIT_FINDINGS.md* + +# ProjectMem runtime state is local and may contain private work history. +# Keep only the reviewed project configuration, map, and plan in Git. +.projectmem/AI_INSTRUCTIONS.md +.projectmem/events.jsonl +.projectmem/issues/ +.projectmem/summary.md +.projectmem/watch.pid +.projectmem/watch.log +.projectmem/structure.json + +# Claude Code project registration is generated locally and includes the Nexus auth token. +.mcp.json +.mcp.json.bak-* +CLAUDE.md +AGENTS.md.soma-backup-* diff --git a/.projectmem/PROJECT_MAP.md b/.projectmem/PROJECT_MAP.md new file mode 100644 index 0000000..f07960a --- /dev/null +++ b/.projectmem/PROJECT_MAP.md @@ -0,0 +1,47 @@ +# Project Map - NexusUnity + +Status: curated project map. Update this map when package boundaries or primary entry points change. + +## Project purpose +Open source Unity Editor automation server for local AI tools and developer workflows. + +## Stack +- Unity Package Manager package: `com.forkhorizon.nexus.unity` for Unity `6000.0+`. +- C# Editor implementation under `Editor/`, with runtime support under `Runtime/`. +- Python 3 MCP bridge under `Editor/nexus_unity_bridge.py` and `Editor/nexus_bridge/`. +- Tests: Unity EditMode tests under `Tests~/Editor/` and Python bridge tests under `Editor/tests/`. +- Validation: `scripts/prepush-validate.sh`, GitHub Actions, and the .NET `tools~/NexusQualityGate` tool. + +## Entry points +- `package.json` — UPM package identity, dependencies, and public version. +- `Editor/MCPServer.cs` — local JSON-RPC server lifecycle and editor state. +- `Editor/MCPServerMethods.cs` — raw JSON-RPC dispatch; partial method files implement tool groups. +- `Editor/nexus_unity_bridge.py` — Python MCP bridge process entry point. +- `Editor/nexus_bridge/routing.py` — MCP manager routing and macros. + +## Structure +- `Editor/` — Unity Editor server, raw tools, integration UI, and Python bridge. + - `MCPServer.cs` — server state/lifecycle; `MCPServer.Networking.cs` owns HTTP/WebSocket handling. + - `MCPServerMethods.cs` — tool dispatch; `MCPServerMethods.*.cs` group the raw tool implementations. + - `NexusMcpConfigGenerator.cs` — generated MCP client configuration and bridge health checks. + - `nexus_unity_bridge.py` — MCP protocol entry point. + - `nexus_bridge/` — bridge transport, schemas, and manager routing. + - `tests/` — Python bridge unit tests. +- `Runtime/` — runtime assembly support used by Editor-facing features. +- `Tests~/Editor/` — Unity EditMode/API/security/integration configuration tests. +- `scripts/` — package validation and optional agent-tooling smoke script. +- `tools~/NexusQualityGate/` — standalone .NET documentation and source-quality gate. +- `.github/workflows/validate.yml` — protected-branch CI validation and Unity package smoke. +- `README.md`, `DOCUMENTATION.MD`, `API_REFERENCE.MD`, `RELEASE.md`, `CHANGELOG.md` — public package and release documentation. + +## Relationships +- `Editor/MCPServer.Networking.cs` validates a local request before `Editor/MCPServerMethods.cs` dispatches the raw JSON-RPC method on the Unity main thread. +- `Editor/nexus_unity_bridge.py` loads `Editor/nexus_bridge/routing.py`, which maps MCP manager tools to the raw Unity JSON-RPC API. +- `Editor/nexus_bridge/schemas.py` defines the public bridge contracts exercised by `Editor/tests/`. +- `Editor/NexusMcpConfigGenerator.cs` deploys the bridge and writes client configuration containing the current auth token. +- `scripts/prepush-validate.sh` runs bridge tests and `tools~/NexusQualityGate`; GitHub CI additionally imports the package into a clean Unity project. + +## Suggested first reads +- For API or server behavior: `API_REFERENCE.MD`, `Editor/MCPServer.cs`, and the relevant `Editor/MCPServerMethods.*.cs` file. +- For MCP bridge work: `Editor/nexus_unity_bridge.py`, `Editor/nexus_bridge/routing.py`, and `Editor/nexus_bridge/schemas.py`. +- For package publication: `RELEASE.md`, `CHANGELOG.md`, and `.github/workflows/validate.yml`. diff --git a/.projectmem/config.toml b/.projectmem/config.toml new file mode 100644 index 0000000..47db68c --- /dev/null +++ b/.projectmem/config.toml @@ -0,0 +1,3 @@ +summary_size_limit_kb = 20 +recent_days = 30 +project_description = "Nexus Unity is an open-source Unity Editor automation package with a C# JSON-RPC server and a Python MCP bridge." diff --git a/.projectmem/plan.md b/.projectmem/plan.md new file mode 100644 index 0000000..3843185 --- /dev/null +++ b/.projectmem/plan.md @@ -0,0 +1,22 @@ +# NexusUnity — plan + +> Editable **intent** file: ideas + plans — what we *mean to do*. +> This is NOT the event log. `events.jsonl` -> `summary.md` records what +> *happened*; this file records what we *intend*. The AI reads it at +> session start and edits it directly (like `PROJECT_MAP.md`): add ideas +> and plans, check items off, move done work down to Shipped. Plans are +> never logged as events. + +## Ideas +_Loose thoughts, not yet committed to._ + +## Active plans +_What we're working toward now. Use `- [ ]` / `- [x]` checklists._ + +## Next +_Queued, but not started._ + +## Someday / maybe + +## Shipped +_Move completed plans here so the top stays about the future._ diff --git a/AGENTS.md b/AGENTS.md index fa5b301..f84b7bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,3 +43,14 @@ 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. + + +## Memory Tools + +Default mode: light. Do not spend tokens on memory tools for small, obvious, single-file tasks. + +- projectmem: use for bugs, regressions, multi-step changes, repeated attempts, or architecture decisions. For small self-contained edits, skip full memory startup and use targeted history checks only when useful. +- `.projectmem/config.toml`, `PROJECT_MAP.md`, and `plan.md` are shared project configuration. The append-only event log, issue files, generated summary/structure, watcher state, and generated `CLAUDE.md` bridge are local and ignored. +- This package uses the tracked `.githooks` directory. When `pjm` is installed, its `pre-commit`, `post-commit`, and `post-merge` wrappers provide warnings and local auto-capture; do not run `pjm hooks install`, which assumes `.git/hooks` exists. +- Verify that the projectmem MCP server is bound to this package before using write tools. If it is bound elsewhere, use the local `pjm` CLI from this package root instead. + diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f80c51..2ade587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable public changes to Nexus Unity are documented here. ## [Unreleased] +### Fixed +- Auto Setup now reports a clear refresh-and-retry message if a client is unavailable after the integration list is regenerated, instead of throwing an exception. + ## [1.5.0] - 2026-07-12 ### Changed diff --git a/Editor/MCPServerWindow.Integrations.cs b/Editor/MCPServerWindow.Integrations.cs index 4838141..6229a1c 100644 --- a/Editor/MCPServerWindow.Integrations.cs +++ b/Editor/MCPServerWindow.Integrations.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; @@ -107,7 +108,13 @@ private void AutoSetupIntegration(NexusMcpClientInfo client) return; } - var refreshed = NexusMcpConfigGenerator.BuildAllForCurrentProject().First(item => item.Kind == client.Kind); + var refreshed = FindIntegrationByKind(NexusMcpConfigGenerator.BuildAllForCurrentProject(), client.Kind); + if (refreshed == null) + { + SetIntegrationMessage(client.DisplayName + " is no longer available. Refresh integrations and try again."); + return; + } + refreshed.BridgePath = bridgePath.Replace("\\", "/"); if (refreshed.CustomAutoSetup != null) @@ -134,6 +141,11 @@ private void AutoSetupIntegration(NexusMcpClientInfo client) SetIntegrationMessage(message); } + internal static NexusMcpClientInfo FindIntegrationByKind(IEnumerable clients, NexusMcpClientKind kind) + { + return clients.FirstOrDefault(item => item.Kind == kind); + } + private void CopyIntegrationConfig(NexusMcpClientInfo client) { EditorGUIUtility.systemCopyBuffer = client.ConfigText; diff --git a/Tests~/Editor/EditorWindowUiToolkitTests.cs b/Tests~/Editor/EditorWindowUiToolkitTests.cs index a175794..5a8fbb4 100644 --- a/Tests~/Editor/EditorWindowUiToolkitTests.cs +++ b/Tests~/Editor/EditorWindowUiToolkitTests.cs @@ -84,6 +84,13 @@ public void ServerWindowBuildsResponsiveUiToolkitSurface() } } + [Test] + public void IntegrationLookupReturnsNullWhenTheClientIsNoLongerAvailable() + { + Assert.IsNull(MCPServerWindow.FindIntegrationByKind( + Enumerable.Empty(), NexusMcpClientKind.Codex)); + } + [Test] public void VerificationWindowBuildsNamedControls() {