Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
44ec036
Merge pull request #157 from ForkHorizon/release/1.5.0-back-sync
Daliys Jul 11, 2026
2c8283c
Harden integration setup and add project memory hooks
Daliys Jul 11, 2026
d42386a
Ignore generated Claude MCP registration
Daliys Jul 11, 2026
2b182de
Merge pull request #158 from ForkHorizon/daliys/issue-75-autosetup-cl…
Daliys Jul 12, 2026
624bc0f
fix: report Submitted status for async run_tests and dedupe Ollama re…
Daliys Jul 12, 2026
b0e6ce5
Merge pull request #159 from ForkHorizon/daliys/issue-99-run-tests-st…
Daliys Jul 12, 2026
d062b52
Fix UI verification state isolation
Daliys Jul 12, 2026
7f53d48
Merge pull request #160 from ForkHorizon/daliys/issue-89-window-verif…
Daliys Jul 13, 2026
2e9fbd1
fix(mcp): resolve TriggerSafeAssetRefresh callback leaks and add time…
Daliys Jul 14, 2026
c4042f2
Merge pull request #161 from ForkHorizon/daliys/issue-135-asset-refre…
Daliys Jul 15, 2026
42fb28d
fix(ci): prevent candidate tool execution in fork PR validation (#81)
Daliys Jul 15, 2026
5352718
Merge pull request #162 from ForkHorizon/daliys/issue-81-ci-security
Daliys Jul 16, 2026
fc698eb
fix: reject abstract ScriptableObject types before calling CreateInst…
Daliys Jul 16, 2026
7f95c6b
ci: restore package asmdefs and configure workflow to run all C# unit…
Daliys Jul 16, 2026
614e893
fix: remove untracked meta file under ignored Tests~ folder
Daliys Jul 16, 2026
9a69e9a
Merge pull request #163 from ForkHorizon/daliys/issue-120
Daliys Jul 17, 2026
3106999
fix: resolve symbolic link targets in ValidatePath (#141)
Daliys Jul 17, 2026
8e1f7e1
Merge pull request #164 from ForkHorizon/daliys/issue-141-symlink-val…
Daliys Jul 17, 2026
35a2ac5
fix(security): restrict type resolution and add component/ScriptableO…
Daliys Jul 19, 2026
df3377a
Merge branch 'development' into daliys/issue-139-type-resolution
Daliys Jul 20, 2026
e963f70
Merge pull request #165 from ForkHorizon/daliys/issue-139-type-resolu…
Daliys Jul 20, 2026
202cd78
Add Linter Checker 300 Lines
Daliys Jul 22, 2026
738d2f6
Merge pull request #168 from ForkHorizon/ci-scope/install-ai-readability
Daliys Jul 22, 2026
219789e
fix(search): handle invalid regex and add match timeout in find_objec…
Daliys Jul 22, 2026
0a11fec
refactor(search): extract helper methods for readability gate limits
Daliys Jul 22, 2026
288e126
refactor(search): split FindReferences into References.cs and isolate…
Daliys Jul 22, 2026
d10b867
chore: add missing Unity .meta files
Daliys Jul 22, 2026
50cb599
Merge pull request #169 from ForkHorizon/fix/issue-119-find-objects-r…
Daliys Jul 22, 2026
14a8063
security: harden external PR replay workflow with actor gate and pre-…
Daliys Jul 22, 2026
af6611e
security: block trusted replay when critical CI files are touched
Daliys Jul 22, 2026
d7915e0
Merge pull request #170 from ForkHorizon/daliys/issue-144-external-pr…
Daliys Jul 22, 2026
669034e
fix(snapshot): set enterChildren = false in GetSceneDependencies (#71)
Daliys Jul 23, 2026
79160b5
fix(harness): discover auth token from Library/NexusUnityAuthToken.tx…
Daliys Jul 23, 2026
bc63ae2
fix(server): dynamically resolve package version from PackageInfo and…
Daliys Jul 23, 2026
4d82ae0
fix(server): restore _logCounter field and add missing Newtonsoft.Jso…
Daliys Jul 23, 2026
2744293
fix(server): fallback to Library/NexusUnityAuthToken.txt in IsAuthori…
Daliys Jul 23, 2026
47401e7
fix(server): enable AppNapBypass and runInBackground in Init
Daliys Jul 23, 2026
a331447
fix(server): clear isPlayModeTransition once play mode is running
Daliys Jul 24, 2026
f4bd107
fix(input): stop dropping a click issued during another click's release
Daliys Jul 24, 2026
1a175f1
fix(bridge): re-read auth token on HTTP 401 after a domain reload
Daliys Jul 24, 2026
f374956
refactor(server): satisfy readability gate (<=300 lines/file, <=50/fu…
Daliys Jul 24, 2026
be06e3b
Merge pull request #171 from ForkHorizon/daliys/issue-71-scene-depend…
Daliys Jul 24, 2026
cc11a37
fix(security): require confirm and OS trash for delete_asset (#140)
Daliys Jul 27, 2026
bc758cd
fix(security): sanitize delete_asset, system folder write protection …
Daliys Jul 27, 2026
2255790
refactor(server): extract serialization helpers to keep Utils.cs unde…
Daliys Jul 27, 2026
ca28f5f
Merge pull request #172 from ForkHorizon/daliys/issue-140-delete-asse…
Daliys Jul 28, 2026
3c9a53f
fix(auth): persist auth token across editor process restarts (#166)
Daliys Jul 28, 2026
e744784
fix(auth): thread-safe auth token persistence via Library file (#166)
Daliys Jul 28, 2026
ec09740
Merge pull request #173 from ForkHorizon/daliys/issue-166-auth-token-…
Daliys Jul 28, 2026
e30080b
fix(playerprefs): use ArgumentList for defaults read on macOS (#143)
Daliys Jul 31, 2026
cdd8602
refactor(playerprefs): split ListPlayerPrefs helper methods to satisf…
Daliys Jul 31, 2026
344de45
fix(playerprefs): resolve deadlock, Windows registry hash truncation,…
Daliys Jul 31, 2026
c87d48f
Merge pull request #174 from ForkHorizon/daliys/issue-143-playerprefs…
Daliys Aug 1, 2026
b50f110
fix(editor): make HandlePostCompileFocusReturn and runtime log subscr…
Daliys Aug 7, 2026
f7833b8
Merge branch 'development' into daliys/issue-138-focus-return-idempot…
Daliys Aug 7, 2026
ae95ca1
Merge pull request #175 from ForkHorizon/daliys/issue-138-focus-retur…
Daliys Aug 7, 2026
dc955be
fix(networking): dispatch websocket requests asynchronously in server…
Daliys Aug 7, 2026
99e10da
fix(server): harden concurrent websocket handling and frame integrity…
Daliys Aug 7, 2026
b8273bc
style(server): satisfy readability linters for method and file length…
Daliys Aug 7, 2026
c53d47f
refactor(server): extract instance probing and port discovery into MC…
Daliys Aug 7, 2026
9fdba1c
Add Slop Review
Aug 7, 2026
bde6a1e
refactor(tests): extract StartTestServerAsync helper to reduce test m…
Daliys Aug 7, 2026
91941f0
Merge branch 'development' into ci-scope/install-slop-review
Daliys Aug 7, 2026
0b17df1
Merge pull request #176 from ForkHorizon/daliys/issue-129-websocket-a…
Daliys Aug 7, 2026
f1e6694
Merge branch 'development' into ci-scope/install-slop-review
Daliys Aug 20, 2026
3a2d99d
Merge pull request #178 from ForkHorizon/ci-scope/install-slop-review
Daliys Aug 20, 2026
b79046d
chore: sync main into development (#183)
Daliys Aug 21, 2026
a2672fe
chore: sync main (Python Quality Gate) into development (#185)
Daliys Aug 21, 2026
24ac3df
chore: sync main into development and remove legacy linter (#188)
Daliys Aug 21, 2026
04e1f8c
fix: sanitize and redact exception messages in tool usage stats (#142…
Daliys Aug 22, 2026
6128159
fix(ui): add traversal depth and result count bounds for UI hierarchy…
Daliys Aug 22, 2026
353c03b
chore(release): prepare 1.6.0
Daliys Aug 23, 2026
27945a5
Merge remote-tracking branch 'origin/main' into release/1.6.0
Daliys Aug 23, 2026
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
12 changes: 6 additions & 6 deletions .code-linter.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{

Check failure on line 1 in .code-linter.json

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit b79046d2 modified protected policy file '.code-linter.json' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .code-linter.json

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit a2672feb modified protected policy file '.code-linter.json' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .code-linter.json

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit 61281597 modified protected policy file '.code-linter.json' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).
"max_file_lines": 750,
"max_function_lines": 150,
"max_nesting_depth": 8,
"max_parameters": 8,
"max_comment_lines": 15,
"max_types_per_file": 6,
"max_file_lines": 300,
"max_function_lines": 50,
"max_nesting_depth": 4,
"max_parameters": 5,
"max_comment_lines": 5,
"max_types_per_file": 2,
"include_extensions": [
".cs",
".py"
Expand Down
8 changes: 8 additions & 0 deletions .githooks/post-commit
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions .githooks/post-merge
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
* @daliys

Check failure on line 1 in .github/CODEOWNERS

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit b79046d2 modified protected policy file '.github/CODEOWNERS' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .github/CODEOWNERS

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit a2672feb modified protected policy file '.github/CODEOWNERS' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .github/CODEOWNERS

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit 61281597 modified protected policy file '.github/CODEOWNERS' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

# Quality Gate & CI Policy files strictly owned by @daliys
/.code-linter.json @daliys
/.ruff.toml @daliys
/.unity-quality-gate.json @daliys
/.swift-quality-gate.json @daliys
/.slop-review.json @daliys
/.github/workflows/ @daliys
/.github/CODEOWNERS @daliys
69 changes: 69 additions & 0 deletions .github/workflows/approve-external-pr.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Approve external PR for CI

Check failure on line 1 in .github/workflows/approve-external-pr.yml

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit a2672feb modified protected policy file '.github/workflows/approve-external-pr.yml' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .github/workflows/approve-external-pr.yml

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit 61281597 modified protected policy file '.github/workflows/approve-external-pr.yml' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

# ──────────────────────────────────────────────────────────────────────────────
# Maintainer "Approve & run CI" button for external (fork) pull requests.
Expand Down Expand Up @@ -29,6 +29,11 @@
description: "External PR number to replay (e.g. 30)"
required: true
type: string
acknowledge_critical_files:
description: "Set true only after reviewing the diff, to proceed even though the PR touches .github/workflows, .githooks, or scripts"
required: false
type: boolean
default: false

permissions:
contents: write # push the trusted/pr-N branch
Expand Down Expand Up @@ -79,6 +84,70 @@
exit 1
fi

# ── 0.1. Authorize maintainer actor ─────────────────────────────────────
- name: Verify maintainer authorization
env:
ACTOR: ${{ github.actor }}
REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

echo "Verifying authorization for actor @$ACTOR..."

# Explicit maintainer bypass
if [ "$ACTOR" = "Daliys" ]; then
echo "Actor @$ACTOR is authorized maintainer."
exit 0
fi

# Check collaborator permission via GitHub API
PERM=$(gh api "repos/$REPOSITORY/collaborators/$ACTOR/permission" --jq '.permission' 2>/dev/null || echo "none")
case "$PERM" in
admin|write)
echo "Actor @$ACTOR authorized with permission: $PERM"
;;
*)
echo "::error::Actor @$ACTOR does not have maintainer authorization (permission: $PERM)."
exit 1
;;
esac

# ── 0.2. Content & Security Pre-scan ────────────────────────────────────
- name: Run security pre-scan on PR files
env:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
ACKNOWLEDGE: ${{ inputs.acknowledge_critical_files }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

echo "Scanning changed files in PR #$PR_NUMBER..."
FILES=$(gh api "repos/$REPOSITORY/pulls/$PR_NUMBER/files" --paginate --jq '.[].filename')

SUSPICIOUS_COUNT=0
while IFS= read -r file; do
[ -z "$file" ] && continue
case "$file" in
.github/workflows/*|.githooks/*|scripts/*)
echo "::warning::PR #$PR_NUMBER modifies critical CI/script file: $file"
SUSPICIOUS_COUNT=$((SUSPICIOUS_COUNT + 1))
;;
esac
done <<< "$FILES"

if [ "$SUSPICIOUS_COUNT" -gt 0 ]; then
echo "::notice::PR #$PR_NUMBER modifies $SUSPICIOUS_COUNT critical file(s)."
if [ "$ACKNOWLEDGE" != "true" ]; then
echo "::error::Refusing to replay PR #$PR_NUMBER: it modifies critical CI/script file(s) (see warnings above). Review the diff, then re-run this workflow with 'acknowledge_critical_files' set to true if it's safe to proceed."
exit 1
fi
echo "Critical file changes acknowledged by @${{ github.actor }} — proceeding."
else
echo "Content pre-scan passed cleanly: no critical CI/workflow files modified."
fi

# ── 1. Create trusted branch with the fork's commits ───────────────────
- name: Create trusted/pr-${{ inputs.pr_number }} branch
env:
Expand Down
21 changes: 14 additions & 7 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Validate package

Check failure on line 1 in .github/workflows/validate.yml

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit a2672feb modified protected policy file '.github/workflows/validate.yml' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

Check failure on line 1 in .github/workflows/validate.yml

View workflow job for this annotation

GitHub Actions / code-linter / Code Linter

Policy Signature Required

Commit 61281597 modified protected policy file '.github/workflows/validate.yml' without a valid signature from a trusted code owner. Reason: Commit is unsigned (no cryptographic signature present).

on:
push:
Expand Down Expand Up @@ -128,17 +128,21 @@
path: candidate

- name: Run required Ollama documentation and checklist review
env:
IS_FORK_PR: ${{ github.event.pull_request && github.event.pull_request.head.repo.fork || false }}
run: |
set -euo pipefail
dotnet --version

QUALITY_GATE_PROJECT="trusted/tools~/NexusQualityGate/NexusQualityGate.csproj"
if [ ! -f "$QUALITY_GATE_PROJECT" ]; then
echo "::notice::Trusted branch does not have NexusQualityGate yet; using candidate tool for bootstrap validation."
QUALITY_GATE_PROJECT="candidate/tools~/NexusQualityGate/NexusQualityGate.csproj"
elif ! grep -q -- "--checklist-ai" trusted/tools~/NexusQualityGate/QualityGateOptions.cs; then
echo "::notice::Trusted NexusQualityGate does not support checklist AI yet; using candidate tool for this bootstrap validation."
QUALITY_GATE_PROJECT="candidate/tools~/NexusQualityGate/NexusQualityGate.csproj"
if [ ! -f "$QUALITY_GATE_PROJECT" ] || ! grep -q -- "--checklist-ai" trusted/tools~/NexusQualityGate/QualityGateOptions.cs; then
if [ "${IS_FORK_PR:-false}" = "true" ]; then
echo "::warning::Trusted branch does not support the required NexusQualityGate version, and candidate tool cannot be executed for fork PRs due to security restrictions. Skipping AI documentation check."
exit 0
else
echo "::notice::Trusted branch does not have the required NexusQualityGate version; using candidate tool for bootstrap validation."
QUALITY_GATE_PROJECT="candidate/tools~/NexusQualityGate/NexusQualityGate.csproj"
fi
fi

dotnet run --project "$QUALITY_GATE_PROJECT" -- \
Expand Down Expand Up @@ -239,6 +243,9 @@
dependencies = manifest.setdefault("dependencies", {})
dependencies["com.forkhorizon.nexus.unity"] = f"file:{os.environ['GITHUB_WORKSPACE']}"
dependencies["com.unity.test-framework"] = "1.5.1"
testables = manifest.setdefault("testables", [])
if "com.forkhorizon.nexus.unity" not in testables:
testables.append("com.forkhorizon.nexus.unity")
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
PY

Expand Down Expand Up @@ -361,7 +368,7 @@
-projectPath "$SMOKE_ROOT" \
-runTests \
-testPlatform editmode \
-assemblyNames NexusUnity.PackageSmoke.EditorTests \
-assemblyNames "NexusUnity.PackageSmoke.EditorTests;UnityMCP.Editor.Tests" \
-testResults "$SMOKE_TEST_RESULTS" \
-logFile "$SMOKE_TEST_LOG"; then
echo "::error::Unity package Editor tests failed."
Expand Down
1 change: 1 addition & 0 deletions .projectmem/.current_issue
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0005
47 changes: 47 additions & 0 deletions .projectmem/PROJECT_MAP.md
Original file line number Diff line number Diff line change
@@ -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`.
3 changes: 3 additions & 0 deletions .projectmem/config.toml
Original file line number Diff line number Diff line change
@@ -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."
22 changes: 22 additions & 0 deletions .projectmem/plan.md
Original file line number Diff line number Diff line change
@@ -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._
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- SOMA_MEMORY_TOOLS_START -->
## 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.
<!-- SOMA_MEMORY_TOOLS_END -->
9 changes: 6 additions & 3 deletions API_REFERENCE.MD
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Nexus Unity API Reference

Version: `1.5.0`
Version: `1.6.0`

Nexus Unity exposes two supported public API surfaces:

Expand Down Expand Up @@ -187,12 +187,12 @@ Actions: `search`, `explore`, `create_material`, `import`, `refresh`, `instantia
### `unity_editor_controller`
Actions: `undo`, `redo`, `play`, `pause`, `step`, `menu`, `read_logs`, `clear_logs`, `get_state`, `get_server_status`, `refresh_assets`, `run_tests`, `get_test_results`, `run_tests_wait`, `get_tool_usage_stats`, `reset_tool_usage_stats`.

`run_tests_wait` triggers `run_tests` and polls raw `get_test_results` from the Python bridge so Unity's main thread is not blocked while the agent waits.
`get_tool_usage_stats` returns in-memory call counts, average/total durations, `last_error_type`, and sanitized `last_error` with sensitive paths redacted; `reset_tool_usage_stats` resets the counters. Raw `run_tests` returns `status: "Submitted"` after Unity accepts the asynchronous request; it does not claim that execution has begun or completed. `run_tests_wait` triggers `run_tests` and polls raw `get_test_results` from the Python bridge so Unity's main thread is not blocked while the agent waits. If submission is rejected, `run_tests_wait` returns that result without polling.

### `unity_ui_automation`
Actions: `list_windows`, `get_hierarchy`, `query`, `get_window_rect`, `set_window_rect`, `capture_window_snapshot`, `click`, `input`.

`get_hierarchy` accepts `deep`. `query` accepts `name`, `text`, and `class_name`. Window rect and snapshot actions are intended for resize/layout QA; window snapshots include rect and hierarchy, with best-effort PNG image capture on macOS.
`get_hierarchy` accepts `deep`, `max_depth`, and `max_elements` (with `children_truncated` and root `truncated` markers when capped). `query` accepts `name`, `text`, `class_name`, `max_depth`, and `max_results`. Window rect and snapshot actions are intended for resize/layout QA; window snapshots include rect and hierarchy (supporting `max_depth` and `max_elements`), with best-effort PNG image capture on macOS.

### `unity_wait`
Conditions: `compilation`, `play_mode`, `import`, `editor_idle`.
Expand All @@ -209,6 +209,9 @@ This is the public MCP bridge macro for code edits. Pass `confirm: true` when wr

Raw `attach_script`, `write_file`, and `write_files_batch` calls also require `confirm: true` before writing `.cs` files.

### `delete_asset`
Deletes a file or folder in the project. Requires `path` and `confirm: true`. Deletions use `AssetDatabase.MoveAssetToTrash` to send items to the OS Trash. Deletion of `ProjectSettings/` or `Packages/` paths is forbidden.

### `unity_invoke_method`
Invokes a C# method on a component through reflection.

Expand Down
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ All notable public changes to Nexus Unity are documented here.

## [Unreleased]

## [1.6.0] - 2026-08-23

### Security
- Sanitize and redact sensitive exception messages in tool usage stats (`get_tool_usage_stats` and `RecordToolUsage`), replacing absolute filesystem paths, user directories, and multiline frames with generic placeholders and safe truncated summaries, and exposing `last_error_type` (#142).
- Require explicit confirmation (`confirm: true`) for `delete_asset`, enforce `AssetDatabase.MoveAssetToTrash` for OS trash recovery, and block creation, modification, moving, or deletion of `ProjectSettings/` and `Packages/` paths across all asset tools (#140).
- Hardened external PR replay workflow (`approve-external-pr.yml`) with an in-workflow actor authorization gate and a PR content security pre-scan to prevent unauthorized execution and flag critical CI modifications on self-hosted runners (#144).
- The content pre-scan now blocks the replay when a PR touches `.github/workflows/*`, `.githooks/*`, or `scripts/*`, requiring the maintainer to re-run with `acknowledge_critical_files: true` after reviewing the diff, instead of only logging a warning (#144).

### Changed
- Consolidated duplicate internal Ollama-review and serialized-property write helpers.

### Fixed
- Add traversal depth and element/result bounds to UI Toolkit hierarchy serialization and element queries (`ui_get_hierarchy`, `ui_query_elements`, and `ui_capture_window_snapshot`), preventing editor stalls and payload bloat on complex UI windows, with `children_truncated` and `truncated` markers on partial hierarchy responses (#126).
- Dispatch WebSocket request handling asynchronously in `Task.Run`, ensuring `ServerLoop` remains non-blocking for concurrent HTTP JSON-RPC requests and new connections (#129).
- `EditorApplication.update` subscription for `HandlePostCompileFocusReturn` is now idempotent (unsubscribes before subscribing), preventing stacked duplicate frame handlers on repeated initialization or domain reloads (#138).
- `ListPlayerPrefs` now uses `ProcessStartInfo.ArgumentList` and timeout guards for macOS `defaults read` execution, preventing process hangs, alongside regex unhashing for Windows registry keys, Linux XML prefs support, and double-default type verification (#143).
- Persist authentication tokens across Unity Editor process restarts in Library token file, preventing persistent HTTP 401 Unauthorized errors for external MCP CLI integrations after restarting Unity (#166).
- `get_scene_dependencies` now uses `enterChildren = false` after the initial property iteration step, preventing deep recursive traversal into child properties and eliminating duplicated dependency references (#71).
- `find_objects` now safely constructs name search regexes with a 100ms match timeout and falls back gracefully to literal substring search on invalid regex patterns or match timeouts (#119).
- Restrict type resolution (`FindType`) to user project assemblies and standard public `UnityEngine` assemblies, disallow internal system/editor assemblies and namespaces, and enforce strict type allowlist constraints for component and ScriptableObject creation/inspection tools (#139).
- Resolve symlinks and directory junctions to their real filesystem targets in `ValidatePath` before checking project boundaries to prevent path traversal.
- Reject abstract ScriptableObject types before calling `CreateInstance` in `create_scriptable_object_asset` and `list_fields_for_type`.
- `TriggerSafeAssetRefresh` callback is now tracked to prevent callback leaks, duplicate refresh hooks, and memory leaks across assembly reload/shutdown boundaries, with a fail-safe 15-second timeout guard.
- `run_tests` now reports `Submitted` rather than `Success` after Unity accepts its asynchronous request, and `run_tests_wait` now returns rejected submissions immediately instead of polling until timeout.
- 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
Expand Down
Loading
Loading