2025-03-31 03:58:01 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2025-04-08 18:14:13 +08:00
|
|
|
using System.Linq;
|
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
|
|
|
using System.IO;
|
2025-04-08 18:14:13 +08:00
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using UnityEditor;
|
2025-03-31 03:58:01 +08:00
|
|
|
using UnityEditorInternal; // Required for tag management
|
2025-09-27 07:28:56 +08:00
|
|
|
using UnityEditor.SceneManagement;
|
2025-04-08 18:14:13 +08:00
|
|
|
using UnityEngine;
|
2025-09-27 07:28:56 +08:00
|
|
|
using MCPForUnity.Editor.Helpers;
|
2025-03-31 03:58:01 +08:00
|
|
|
|
2025-08-21 03:59:49 +08:00
|
|
|
namespace MCPForUnity.Editor.Tools
|
2025-03-31 03:58:01 +08:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles operations related to controlling and querying the Unity Editor state,
|
|
|
|
|
/// including managing Tags and Layers.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class ManageEditor
|
|
|
|
|
{
|
|
|
|
|
// Constant for starting user layer index
|
|
|
|
|
private const int FirstUserLayerIndex = 8;
|
2025-04-08 18:14:13 +08:00
|
|
|
|
2025-03-31 03:58:01 +08:00
|
|
|
// Constant for total layer count
|
|
|
|
|
private const int TotalLayerCount = 32;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Main handler for editor management actions.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static object HandleCommand(JObject @params)
|
|
|
|
|
{
|
|
|
|
|
string action = @params["action"]?.ToString().ToLower();
|
|
|
|
|
// Parameters for specific actions
|
|
|
|
|
string tagName = @params["tagName"]?.ToString();
|
|
|
|
|
string layerName = @params["layerName"]?.ToString();
|
|
|
|
|
bool waitForCompletion = @params["waitForCompletion"]?.ToObject<bool>() ?? false; // Example - not used everywhere
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(action))
|
|
|
|
|
{
|
|
|
|
|
return Response.Error("Action parameter is required.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Route action
|
|
|
|
|
switch (action)
|
|
|
|
|
{
|
|
|
|
|
// Play Mode Control
|
|
|
|
|
case "play":
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!EditorApplication.isPlaying)
|
|
|
|
|
{
|
|
|
|
|
EditorApplication.isPlaying = true;
|
|
|
|
|
return Response.Success("Entered play mode.");
|
|
|
|
|
}
|
|
|
|
|
return Response.Success("Already in play mode.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error entering play mode: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
case "pause":
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (EditorApplication.isPlaying)
|
|
|
|
|
{
|
|
|
|
|
EditorApplication.isPaused = !EditorApplication.isPaused;
|
2025-04-08 18:14:13 +08:00
|
|
|
return Response.Success(
|
|
|
|
|
EditorApplication.isPaused ? "Game paused." : "Game resumed."
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
return Response.Error("Cannot pause/resume: Not in play mode.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error pausing/resuming game: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
case "stop":
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (EditorApplication.isPlaying)
|
|
|
|
|
{
|
|
|
|
|
EditorApplication.isPlaying = false;
|
|
|
|
|
return Response.Success("Exited play mode.");
|
|
|
|
|
}
|
|
|
|
|
return Response.Success("Already stopped (not in play mode).");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error stopping play mode: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Editor State/Info
|
|
|
|
|
case "get_state":
|
|
|
|
|
return GetEditorState();
|
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
|
|
|
case "get_project_root":
|
|
|
|
|
return GetProjectRoot();
|
2025-03-31 03:58:01 +08:00
|
|
|
case "get_windows":
|
|
|
|
|
return GetEditorWindows();
|
|
|
|
|
case "get_active_tool":
|
|
|
|
|
return GetActiveTool();
|
|
|
|
|
case "get_selection":
|
|
|
|
|
return GetSelection();
|
2025-09-27 07:28:56 +08:00
|
|
|
case "get_prefab_stage":
|
|
|
|
|
return GetPrefabStageInfo();
|
2025-03-31 03:58:01 +08:00
|
|
|
case "set_active_tool":
|
|
|
|
|
string toolName = @params["toolName"]?.ToString();
|
2025-04-08 18:14:13 +08:00
|
|
|
if (string.IsNullOrEmpty(toolName))
|
|
|
|
|
return Response.Error("'toolName' parameter required for set_active_tool.");
|
2025-03-31 03:58:01 +08:00
|
|
|
return SetActiveTool(toolName);
|
|
|
|
|
|
|
|
|
|
// Tag Management
|
|
|
|
|
case "add_tag":
|
2025-04-08 18:14:13 +08:00
|
|
|
if (string.IsNullOrEmpty(tagName))
|
|
|
|
|
return Response.Error("'tagName' parameter required for add_tag.");
|
2025-03-31 03:58:01 +08:00
|
|
|
return AddTag(tagName);
|
|
|
|
|
case "remove_tag":
|
2025-04-08 18:14:13 +08:00
|
|
|
if (string.IsNullOrEmpty(tagName))
|
|
|
|
|
return Response.Error("'tagName' parameter required for remove_tag.");
|
2025-03-31 03:58:01 +08:00
|
|
|
return RemoveTag(tagName);
|
|
|
|
|
case "get_tags":
|
|
|
|
|
return GetTags(); // Helper to list current tags
|
|
|
|
|
|
|
|
|
|
// Layer Management
|
|
|
|
|
case "add_layer":
|
2025-04-08 18:14:13 +08:00
|
|
|
if (string.IsNullOrEmpty(layerName))
|
|
|
|
|
return Response.Error("'layerName' parameter required for add_layer.");
|
2025-03-31 03:58:01 +08:00
|
|
|
return AddLayer(layerName);
|
|
|
|
|
case "remove_layer":
|
2025-04-08 18:14:13 +08:00
|
|
|
if (string.IsNullOrEmpty(layerName))
|
|
|
|
|
return Response.Error("'layerName' parameter required for remove_layer.");
|
2025-03-31 03:58:01 +08:00
|
|
|
return RemoveLayer(layerName);
|
|
|
|
|
case "get_layers":
|
|
|
|
|
return GetLayers(); // Helper to list current layers
|
|
|
|
|
|
|
|
|
|
// --- Settings (Example) ---
|
|
|
|
|
// case "set_resolution":
|
|
|
|
|
// int? width = @params["width"]?.ToObject<int?>();
|
|
|
|
|
// int? height = @params["height"]?.ToObject<int?>();
|
|
|
|
|
// if (!width.HasValue || !height.HasValue) return Response.Error("'width' and 'height' parameters required.");
|
|
|
|
|
// return SetGameViewResolution(width.Value, height.Value);
|
|
|
|
|
// case "set_quality":
|
|
|
|
|
// // Handle string name or int index
|
|
|
|
|
// return SetQualityLevel(@params["qualityLevel"]);
|
|
|
|
|
|
|
|
|
|
default:
|
2025-04-08 18:14:13 +08:00
|
|
|
return Response.Error(
|
2025-09-27 07:28:56 +08:00
|
|
|
$"Unknown action: '{action}'. Supported actions include play, pause, stop, get_state, get_project_root, get_windows, get_active_tool, get_selection, get_prefab_stage, set_active_tool, add_tag, remove_tag, get_tags, add_layer, remove_layer, get_layers."
|
2025-04-08 18:14:13 +08:00
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Editor State/Info Methods ---
|
|
|
|
|
private static object GetEditorState()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var state = new
|
|
|
|
|
{
|
|
|
|
|
isPlaying = EditorApplication.isPlaying,
|
|
|
|
|
isPaused = EditorApplication.isPaused,
|
|
|
|
|
isCompiling = EditorApplication.isCompiling,
|
|
|
|
|
isUpdating = EditorApplication.isUpdating,
|
|
|
|
|
applicationPath = EditorApplication.applicationPath,
|
|
|
|
|
applicationContentsPath = EditorApplication.applicationContentsPath,
|
2025-04-08 18:14:13 +08:00
|
|
|
timeSinceStartup = EditorApplication.timeSinceStartup,
|
2025-03-31 03:58:01 +08:00
|
|
|
};
|
|
|
|
|
return Response.Success("Retrieved editor state.", state);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting editor state: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Claude‑friendly edit tools + framed transport + live Unity NL test framework (#243)
* CI: gate desktop-parity on Anthropic key; pass anthropic_api_key like NL suite
* Add quickprobe prompt and CI workflow (mcp-quickprobe.md, unity-mcp-quickprobe.yml)
* strictier tool use to prevent subagent spawning and force mcp tools
* update workflow filesto reduce likelihood of subagent spawning
* improve permissions for claude agent, fix mcpbridge timeout/token issue
* increase max turns to 10
* ci: align NL suite to new permissions schema; prevent subagent drift
* ci: NL suite -> mini prompt for e2e; add full NL/T prompt; server: ctx optional + project_root fallback; workflows: set UNITY_PROJECT_ROOT for CI
* ci: add checks:write; revert local project hardcodes (manifest, ProjectVersion.txt)
* tools: text-edit routing fixes (anchor_insert via text, CRLF calc); prompts: mini NL/T clarifications
* ci: use absolute UNITY_PROJECT_ROOT; prompts target TestProjects; server: accept relative UNITY_PROJECT_ROOT and bare spec URI
* ci: ignore Unity test project's packages-lock.json; remove from repo to avoid absolute paths
* CI: start persistent Unity Editor for MCP (guarded by license) + allow batch-mode bridge via UNITY_MCP_ALLOW_BATCH
* CI: hide license and pass via env to docker; fix invalid ref format
* CI: readiness probe uses handshake on Unity MCP port (deterministic)
* CI: fix YAML; use TCP handshake readiness probe (FRAMING=1)
* CI: prime Unity license via game-ci; mount ULF into container; extend readiness timeout
* CI: use ULF write + mount for Unity licensing; remove serial/email/pass from container
* CI: entitlement activation (UNITY_SERIAL=''); verify host ULF cache; keep mount
* CI: write ULF from secret and verify; drop entitlement activation step
* CI: detect any licensing path; GameCI prime; status dir env; log+probe readiness; fix YAML
* CI: add GameCI license prime; conditional ULF write; one-shot license validation; explicit status dir + license env
* CI: fix YAML (inline python), add Anthropic key detect via GITHUB_ENV; ready to run happy path
* CI: mount Unity token/ulf/cache dirs into container to share host license; create dirs before start
* CI: fix YAML indentation; write ULF on host; activate in container with shared mounts; mount .config and .cache too
* CI: gate Claude via outputs; mount all Unity license dirs; fix inline probe python; stabilize licensing flow
* CI: normalize detect to step outputs; ensure license dirs mounted and validated; fix indentation
* Bridge: honor UNITY_MCP_STATUS_DIR for heartbeat/status file (CI-friendly)
* CI: guard project path for activation/start; align tool allowlist; run MCP server with python; tighten secret scoping
* CI: finalize Unity licensing mounts + status dir; mode-detect (ULF/EBL); readiness logs+probe; Claude gating via outputs
* CI: fix YAML probe (inline python -c) and finalize happy-path Unity licensing and MCP/Claude wiring
* CI: inline python probe; unify Unity image and cache mounts; ready to test
* CI: fix docker run IMAGE placement; ignore cache find perms; keep same editor image
* CI: pass -manualLicenseFile to persistent Editor; keep mounts and single image
* CI: mount full GameCI cache to /root in persistent Unity; set HOME=/root; add optional license check
* CI: make -manualLicenseFile conditional; keep full /root mount and license check
* CI: set HOME=/github/home; mount GameCI cache there; adjust manualLicenseFile path; expand license check
* CI: EBL sign-in for persistent Unity (email/password/serial); revert HOME=/root and full /root mount; keep conditional manualLicenseFile and improved readiness
* CI: run full NL/T suite prompt (nl-unity-suite-full.md) instead of mini
* NL/T: require unified diffs + explicit verdicts in JUnit; CI: remove short sanity step, publish JUnit, upload artifacts
* NL/T prompt: require CDATA wrapping for JUnit XML fields; guidance for splitting embedded ]]>; keep VERDICT in CDATA only
* CI: remove in-container license check step; keep readiness and full suite
* NL/T prompt: add version header, stricter JUnit schema, hashing/normalization, anchors, statuses, atomic semantics, tool logging
* CI: increase Claude NL/T suite timeout to 30 minutes
* CI: pre-create reports dir and result files to avoid tool approval prompts
* CI: skip wait if container not running; skip Editor start if project missing; broaden MCP deps detection; expand allowed tools
* fixies to harden ManageScript
* CI: sanitize NL/T markdown report to avoid NUL/encoding issues
* revert breaking yyaml changes
* CI: prime license, robust Unity start/wait, sanitize markdown via heredoc
* Resolve merge: accept upstream renames/installer (fix/installer-cleanup-v2) and keep local framing/script-editing
- Restored upstream server.py, EditorWindow, uv.lock\n- Preserved ManageScript editing/validation; switched to atomic write + debounced refresh\n- Updated tools/__init__.py to keep script_edits/resources and adopt new logger name\n- All Python tests via uv: 7 passed, 6 skipped, 9 xpassed; Unity compile OK
* Fix Claude Desktop config path and atomic write issues
- Fix macOS path for Claude Desktop config: use ~/Library/Application Support/Claude/ instead of ~/.config/Claude/
- Improve atomic write pattern with backup/restore safety
- Replace File.Replace() with File.Move() for better macOS compatibility
- Add proper error handling and cleanup for file operations
- Resolves issue where installer couldn't find Claude Desktop config on macOS
* Editor: use macConfigPath on macOS for MCP client config writes (Claude Desktop, etc.). Fallback to linuxConfigPath only if mac path missing.
* Models: add macConfigPath to McpClient for macOS config path selection (fixes CS1061 in editor window).
* Editor: on macOS, prefer macConfigPath in ManualConfigEditorWindow (fallback to linux path); Linux/Windows unchanged.
* Fix McpClient: align with upstream/main, prep for framing split
* NL suite: shard workflow; tighten bridge readiness; add MCP preflight; use env-based shard vars
* NL suite: fix shard step indentation; move shard vars to env; remove invalid inputs
* MCP clients: split VSCode Copilot config paths into macConfigPath and linuxConfigPath
* Unity bridge: clean stale status; bind host; robust wait probe with IPv4/IPv6 + diagnostics
* CI: use MCPForUnity.Editor.MCPForUnityBridge.StartAutoConnect as executeMethod
* Action wiring: inline mcpServers in settings for all shards; remove redundant .claude/mcp.json step
* CI: embed mcpServers in settings for all shards; fix startup sanity step; lint clean
* CI: pin claude-code-base-action to e6f32c8; use claude_args --mcp-config; switch to allowed_tools; ensure MCP config per step
* CI: unpin claude-code-base-action to @beta (commit ref not found)
* CI: align with claude-code-base-action @beta; pass MCP via claude_args and allowedTools
* Editor: Fix apply_text_edits heuristic when edits shift positions; recompute method span on candidate text with fallback delta adjustment
* CI: unify MCP wiring across workflows; write .claude/mcp.json; switch to claude_args with --mcp-config/--allowedTools; remove unsupported inputs
* CI: collapse NL suite shards into a single run to avoid repeated test execution
* CI: minimize allowedTools for NL suite to essential Unity MCP + Bash("git:*") + Write
* CI: mkdir -p reports before run; remove unsupported --timeout-minutes from claude_args
* CI: broaden allowedTools to include find_in_file and mcp__unity__*
* CI: enable use_node_cache and switch NL suite model to claude-3-7-haiku-20250219
* CI: disable use_node_cache to avoid setup-node lockfile error
* CI: set NL suite model to claude-3-haiku-20240307
* CI: cap Haiku output with --max-tokens 2048 for NL suite
* CI: switch to claude-3-7-sonnet-latest and remove unsupported --max-tokens
* CI: update allowedTools to Bash(*) and explicit Unity MCP tool list
* CI: update NL suite workflow (latest tweaks)
* Tests: tighten NL suite prompt for logging, hash discipline, stale retry, evidence windows, diff cap, and VERDICT line
* Add disallowed tools to NL suite workflow
* docs: clarify stale write retry
* Add fallback JUnit report and adjust publisher
* Indent fallback JUnit XML in workflow
* fix: correct fallback JUnit report generation
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update mcp-quickprobe.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update Response.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* Update MCPForUnityBridge.cs
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: correct McpTypes reference
* Add directory existence checks for symlink and XDG paths
* fix: only set installation flag after successful server install
* Update resource_tools.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: respect mac config paths
* Use File.Replace for atomic config write
* Remove unused imports in manage_script
* bump server version
* Tests: update NL suite prompt and workflows; remove deprecated smoke/desktop-parity; quickprobe tidy
* Editor: atomic config write via File.Replace fallback; remove redundant backups and racey exists checks
* CI: harden NL suite - idempotent docker, gate on unity_ok, safer port probe, least-priv perms
* Editor: make atomic config write restoration safe (flag writeDone; copy-overwrite restore; cleanup in finally)
* CI: fix fallback JUnit heredoc by using printf lines (no EOF delimiter issues)
* CI: switch NL suite to mini prompt; mini prompt honors / and NL discipline
* CI: replace claude_args with allowed_tools/model/mcp_config per action schema
* CI: expand UNITY_PROJECT_ROOT via in MCP config heredoc
* EditorWindow: add cross-platform fallback for File.Replace; macOS-insensitive PathsEqual; safer uv resolve; honor macConfigPath
* CI: strengthen JUnit publishing for NL mini suite (normalize, debug list, publish both, fail_on_parse_error)
* CI: set job-wide JUNIT_OUT/MD_OUT; normalization uses env; publish references env and ungroup reports
* CI: publish a single normalized JUnit (reports/junit-for-actions.xml); fallback writes same; avoid checkName/reportPaths mismatch
* CI: align mini prompt report filenames; redact Unity log tail in diagnostics
* chore: sync workflow and mini prompt; redacted logs; JUnit normalization/publish tweaks
* CI: redact sensitive tokens in Stop Unity; docs: CI usage + edit tools
* prompts: update nl-unity-suite-full (mini-style setup + reporting discipline); remove obsolete prompts
* CI: harden NL workflows (timeout_minutes, robust normalization); prompts: unify JUnit suite name and reporting discipline
* prompts: add guarded write pattern (LF hash, stale_file retry) to full suite
* prompts: enforce continue-on-failure, driver flow, and status handling in full suite
* Make test list more explicit in prompt. Get rid of old test prompts for hygeine.
* prompts: add stale fast-retry (server hash) + in-memory buf guidance
* CI: standardize JUNIT_OUT to reports/junit-nl-suite.xml; fix artifact upload indentation; prompt copy cleanups
* prompts: reporting discipline — append-only fragments, batch writes, no model round-trip
* prompts: stale fast-retry preference, buffer/sha carry, snapshot revert, essential logging
* workflows(nl-suite): precreate report skeletons, assemble junit, synthesize markdown; restrict allowed_tools to append-only Bash + MCP tools
* thsis too
* Update README-DEV.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite-mini.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/workflows/claude-nl-suite.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* workflows(nl-mini): fix YAML indentation/trailing spaces under with: and cleanup heredoc spacing
* workflows(nl-suite): fix indentation on docker logs redaction line (YAML lint)
* Add write to allowlist
* nl-suite: harden reporting discipline (fragment-only writes, forbid alt paths); workflow: clean stray junit-*updated*.xml
* nl-suite: enforce end-of-suite single Write (no bash redirection); workflow: restrict allowed_tools to Write+MCP only
* prompts(nl-full): end-of-suite results must be valid XML with single <cases> root and only <testcase> children; no raw text outside CDATA
* workflows(nl-suite): make Claude step non-fatal; tolerant normalizer extracts <testcase> via regex on bad fragments
* nl-suite: fix stale classname to UnityMCP.NL-T in mini fallback; prompt: require re-read after every revert; correct PLAN/PROGRESS to 15
* nl-suite: fix fallback JUnit classname to UnityMCP.NL-T; prompt: forbid create_script and env/mkdir checks, enforce single baseline-byte revert flow and post-revert re-read; add corruption-handling guidance
* prompts(nl-full): after each write re-read raw bytes to refresh pre_sha; prefer script_apply_edits for anchors; avoid header/using changes
* prompts(nl-full): canonicalize outputs to /; allow small fragment appends via Write or Bash(printf/echo); forbid wrappers and full-file round-trips
* prompts(nl-full): finalize markdown formatting for guarded write, execution order, specs, status
* workflows(nl-suite, mini): header/lint fixes and constrained Bash append path; align allowed_tools
* prompts(nl-full): format Fast Restore, Guarded Write, Execution, Specs, Status as proper markdown lists and code fences
* workflows(nl-suite): keep header tidy and append-path alignment with prompt
* minor fix
* workflows(nl-suite): fix indentation and dispatch; align allowed_tools and revert helper
* prompts(nl-full): switch to read_resource for buf/sha; re-read only when needed; convert 'Print this once' to heading; note snapshot helper creates parent dirs
* workflows(nl-suite): normalize step removes bootstrap when real testcases present; recompute tests/failures
* workflows(nl-suite): enrich Markdown summary by extracting per-test <system-out> blocks (truncated)
* clarify prompt resilience instructions
* ci(nl-suite): revert prompt and workflow to known-good e0f8a72 for green run; remove extra MD details
* ci(nl-suite): minimal fixes — no-mkdir guard in prompt; drop bootstrap and recompute JUnit counts
* ci(nl-suite): richer JUnit→Markdown report (per-test system-out)
* Small guard to incorret asset read call.
* ci(nl-suite): refine MD builder — unescape XML entities, safe code fences, PASS/FAIL badges
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .github/scripts/mark_skipped.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* server(manage_script): robust URI handling — percent-decode file://, normalize, strip host/leading slashes, return Assets-relative if present
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* tests(framing): reduce handshake poll window, nonblocking peek to avoid disconnect race; still enforce pre-handshake data drop
* tests(manage_script): add _split_uri tests for unity://path, file:// URLs (decoded/Assets-relative), and plain paths
* server+tests: fix handshake syntax error; robust file:// URI normalization in manage_script; add _split_uri tests; adjust stdout scan to ignore venv/site-packages
* bridge(framing): accept zero-length frames (treat as empty keepalive)
* tests(logging): use errors='replace' on decode fallback to avoid silent drops
* resources(list): restrict to Assets/, resolve symlinks, enforce .cs; add traversal/outside-path tests
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* misc: framing keepalive (zero-length), regex preview consistency, resource.list hardening, URI parsing, legacy update routing, test cleanups
* docs(tools): richer MCP tool descriptions; tests accept decorator kwargs; resource URI parsing hardened
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/unity_connection.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* net+docs: hard-reject zero-length frames; TCP_NODELAY on connect; Assets detection case-insensitive; NL prompt statuses aligned
* prompt(nl-suite): constrain Write destinations under reports/, forbid traversal
* prompt+net: harden Write path rules; use monotonic deadline and plain-text advisory for non-framed peers
* unity_connection: restore recv timeout via try/finally; make global connection getter thread-safe with module lock and double-checked init
* NL/T prompt: pin structured edit ops for T-D/T-E; add schema-error guarded write behavior; keep existing path/URI and revert rules
* unity_connection: add FRAMED_MAX; use ValueError for framed length violations; lower framed receive log to debug; serialize connect() with per-instance lock
* ManageScript: use UTF8Encoding(without BOM) for atomic writes in ApplyTextEdits/EditScript to align with Create/Update and avoid BOM-related diffs/hash mismatches
* NL/T prompt: make helper deletion regex multiline-safe ((?ms) so ^ anchors line starts)
* ManageScript: emit structured overlap status {status:"overlap"} for overlapping edit ranges in apply_text_edits and edit paths
* NL/T prompt: clarify fallback vs failure — fallback only for unsupported/missing_field; treat bad_request as failure; note unsupported after fallback as failure
* NL/T prompt: pin deterministic overlap probe (apply_text_edits two ranges from same snapshot); gate too_large behind RUN_TOO_LARGE env hint
* TB update
* NL/T prompt: harden Output Rules — constrain Bash(printf|echo) to stdout-only; forbid redirection/here-docs/tee; only scripts/nlt-revert.sh may mutate FS
* Prompt: enumerate allowed script_apply_edits ops; add manage_editor/read_console guidance; fix T‑F atomic batch to single script_apply_edits. ManageScript: regex timeout for diagnostics; symlink ancestor guard; complete allowed-modes list.
* Fixes
* ManageScript: add rich overlap diagnostics (conflicts + hint) for both text range and structured batch paths
* ManageScript: return structured {status:"validation_failed"} diagnostics in create/update/edits and validate before commit
* ManageScript: echo canonical uri in responses (create/read/update/apply_text_edits/structured edits) to reinforce resource identity
* improve clarity of capabilities message
* Framing: allow zero-length frames on both ends (C# bridge, Python server). Prompt: harden T-F to single text-range apply_text_edits batch (descending order, one snapshot). URI: normalize file:// outside Assets by stripping leading slash.
* ManageScript: include new sha256 in success payload for apply_text_edits; harden TryResolveUnderAssets by rejecting symlinked ancestors up to Assets/.
* remove claudetest dir
* manage_script_edits: normalize method-anchored anchor_insert to insert_method (map text->replacement); improves CI compatibility for T‑A/T‑E without changing Editor behavior.
* tighten testing protocol around mkdir
* manage_script: validate create_script inputs (Assets/.cs/name/no traversal); add Assets/ guard to delete_script; validate level+Assets in validate_script; make legacy manage_script optional params; harden legacy update routing with base64 reuse and payload size preflight.
* Tighten prompt for testing
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script_edits: honor ignore_case on anchor_insert and regex_replace in both direct and text-conversion paths (MULTILINE|IGNORECASE).
* remove extra file
* workflow: use python3 for inline scripts and port detection on ubuntu-latest.
* Tighten prompt + manage_script
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update UnityMcpBridge/Editor/Tools/ManageScript.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update .claude/prompts/nl-unity-suite-full.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* manage_script: improve file:// UNC handling; preserve POSIX absolute semantics internally; keep test-expected slash stripping for non-Assets paths.
* ManageScript.cs: add TimeSpan timeouts to all Regex uses (IsMatch/Match/new Regex) and keep CultureInvariant/Multiline options; reduces risk of catastrophic backtracking stalls.
* workflow: ensure reports/ exists in markdown build step to avoid FileNotFoundError when writing MD_OUT.
* fix brace
* manage_script_edits: expand backrefs for regex_replace in preview->text conversion and translate to \g<n> in local apply; keeps previews and actual edits consistent.
* anchor_insert: default to position=after, normalize surrounding newlines in Python conversion paths; C# path ensures trailing newline and skips duplicate insertion within class.
* feat(mcp): add get_sha tool; apply_text_edits normalization+overlap preflight+strict; no-op evidence in C#; update NL suite prompt; add unit tests
* feat(frames): accept zero-length heartbeat frames in client; add heartbeat test
* feat(edits): guard destructive regex_replace with structural preflight; add robust tests; prompt uses delete_method for temp helper
* feat(frames): bound heartbeat loop with timeout/threshold; align zero-length response with C#; update test
* SDK hardening: atomic multi-span text edits; stop forcing sequential for structured ops; forward options on apply_text_edits; add validate=relaxed support and scoped checks; update NL/T prompt; add tests for options forwarding, relaxed mode, and atomic batches
* Router: default applyMode=atomic for multi-span apply_text_edits; add tests
* CI prompt: pass options.validate=relaxed for T-B/C; options.applyMode=atomic for T-F; emphasize always writing testcase and restoring on errors
* Validation & DX: add validate=syntax (scoped), standardize evidence windows; early regex compile with hints; debug_preview for apply_text_edits
* Update UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* NL/T suite-driven edits: LongUnityScriptClaudeTest, bridge helpers, server_version; prepare framing tests
* Fix duplicate macConfigPath field in McpClient to resolve CS0102
* Editor threading: run EnsureServerInstalled on main thread; marshal EditorPrefs/DeleteKey + logging via delayCall
* Docs(apply_text_edits): strengthen guidance on 1-based positions, verify-before-edit, and recommend anchors/structured edits
* Docs(script_apply_edits): add safety guidance (anchors, method ops, validators) and recommended practices
* Framed VerifyBridgePing in editor window; docs hardening for apply_text_edits and script_apply_edits
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-31 00:55:38 +08:00
|
|
|
private static object GetProjectRoot()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Application.dataPath points to <Project>/Assets
|
|
|
|
|
string assetsPath = Application.dataPath.Replace('\\', '/');
|
|
|
|
|
string projectRoot = Directory.GetParent(assetsPath)?.FullName.Replace('\\', '/');
|
|
|
|
|
if (string.IsNullOrEmpty(projectRoot))
|
|
|
|
|
{
|
|
|
|
|
return Response.Error("Could not determine project root from Application.dataPath");
|
|
|
|
|
}
|
|
|
|
|
return Response.Success("Project root resolved.", new { projectRoot });
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting project root: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-31 03:58:01 +08:00
|
|
|
private static object GetEditorWindows()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Get all types deriving from EditorWindow
|
2025-04-08 18:14:13 +08:00
|
|
|
var windowTypes = AppDomain
|
|
|
|
|
.CurrentDomain.GetAssemblies()
|
2025-03-31 03:58:01 +08:00
|
|
|
.SelectMany(assembly => assembly.GetTypes())
|
|
|
|
|
.Where(type => type.IsSubclassOf(typeof(EditorWindow)))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
var openWindows = new List<object>();
|
2025-04-08 18:14:13 +08:00
|
|
|
|
2025-03-31 03:58:01 +08:00
|
|
|
// Find currently open instances
|
|
|
|
|
// Resources.FindObjectsOfTypeAll seems more reliable than GetWindow for finding *all* open windows
|
|
|
|
|
EditorWindow[] allWindows = Resources.FindObjectsOfTypeAll<EditorWindow>();
|
|
|
|
|
|
|
|
|
|
foreach (EditorWindow window in allWindows)
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
if (window == null)
|
|
|
|
|
continue; // Skip potentially destroyed windows
|
|
|
|
|
|
2025-03-31 03:58:01 +08:00
|
|
|
try
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
openWindows.Add(
|
|
|
|
|
new
|
|
|
|
|
{
|
|
|
|
|
title = window.titleContent.text,
|
|
|
|
|
typeName = window.GetType().FullName,
|
|
|
|
|
isFocused = EditorWindow.focusedWindow == window,
|
|
|
|
|
position = new
|
|
|
|
|
{
|
|
|
|
|
x = window.position.x,
|
|
|
|
|
y = window.position.y,
|
|
|
|
|
width = window.position.width,
|
|
|
|
|
height = window.position.height,
|
|
|
|
|
},
|
|
|
|
|
instanceID = window.GetInstanceID(),
|
|
|
|
|
}
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
Debug.LogWarning(
|
|
|
|
|
$"Could not get info for window {window.GetType().Name}: {ex.Message}"
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Response.Success("Retrieved list of open editor windows.", openWindows);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting editor windows: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-27 07:28:56 +08:00
|
|
|
private static object GetPrefabStageInfo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
PrefabStage stage = PrefabStageUtility.GetCurrentPrefabStage();
|
|
|
|
|
if (stage == null)
|
|
|
|
|
{
|
|
|
|
|
return Response.Success
|
|
|
|
|
("No prefab stage is currently open.", new { isOpen = false });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Response.Success(
|
|
|
|
|
"Prefab stage info retrieved.",
|
|
|
|
|
new
|
|
|
|
|
{
|
|
|
|
|
isOpen = true,
|
|
|
|
|
assetPath = stage.assetPath,
|
|
|
|
|
prefabRootName = stage.prefabContentsRoot != null ? stage.prefabContentsRoot.name : null,
|
|
|
|
|
mode = stage.mode.ToString(),
|
|
|
|
|
isDirty = stage.scene.isDirty
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting prefab stage info: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 18:14:13 +08:00
|
|
|
private static object GetActiveTool()
|
2025-03-31 03:58:01 +08:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Tool currentTool = UnityEditor.Tools.current;
|
|
|
|
|
string toolName = currentTool.ToString(); // Enum to string
|
|
|
|
|
bool customToolActive = UnityEditor.Tools.current == Tool.Custom; // Check if a custom tool is active
|
2025-04-08 18:14:13 +08:00
|
|
|
string activeToolName = customToolActive
|
|
|
|
|
? EditorTools.GetActiveToolName()
|
|
|
|
|
: toolName; // Get custom name if needed
|
|
|
|
|
|
|
|
|
|
var toolInfo = new
|
|
|
|
|
{
|
2025-03-31 03:58:01 +08:00
|
|
|
activeTool = activeToolName,
|
|
|
|
|
isCustom = customToolActive,
|
|
|
|
|
pivotMode = UnityEditor.Tools.pivotMode.ToString(),
|
|
|
|
|
pivotRotation = UnityEditor.Tools.pivotRotation.ToString(),
|
|
|
|
|
handleRotation = UnityEditor.Tools.handleRotation.eulerAngles, // Euler for simplicity
|
2025-04-08 18:14:13 +08:00
|
|
|
handlePosition = UnityEditor.Tools.handlePosition,
|
2025-03-31 03:58:01 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return Response.Success("Retrieved active tool information.", toolInfo);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting active tool: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object SetActiveTool(string toolName)
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
try
|
2025-03-31 03:58:01 +08:00
|
|
|
{
|
|
|
|
|
Tool targetTool;
|
|
|
|
|
if (Enum.TryParse<Tool>(toolName, true, out targetTool)) // Case-insensitive parse
|
|
|
|
|
{
|
|
|
|
|
// Check if it's a valid built-in tool
|
|
|
|
|
if (targetTool != Tool.None && targetTool <= Tool.Custom) // Tool.Custom is the last standard tool
|
|
|
|
|
{
|
|
|
|
|
UnityEditor.Tools.current = targetTool;
|
|
|
|
|
return Response.Success($"Set active tool to '{targetTool}'.");
|
|
|
|
|
}
|
|
|
|
|
else
|
2025-04-08 18:14:13 +08:00
|
|
|
{
|
|
|
|
|
return Response.Error(
|
|
|
|
|
$"Cannot directly set tool to '{toolName}'. It might be None, Custom, or invalid."
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Potentially try activating a custom tool by name here if needed
|
|
|
|
|
// This often requires specific editor scripting knowledge for that tool.
|
2025-04-08 18:14:13 +08:00
|
|
|
return Response.Error(
|
|
|
|
|
$"Could not parse '{toolName}' as a standard Unity Tool (View, Move, Rotate, Scale, Rect, Transform, Custom)."
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error setting active tool: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object GetSelection()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var selectionInfo = new
|
|
|
|
|
{
|
|
|
|
|
activeObject = Selection.activeObject?.name,
|
|
|
|
|
activeGameObject = Selection.activeGameObject?.name,
|
|
|
|
|
activeTransform = Selection.activeTransform?.name,
|
|
|
|
|
activeInstanceID = Selection.activeInstanceID,
|
|
|
|
|
count = Selection.count,
|
2025-04-08 18:14:13 +08:00
|
|
|
objects = Selection
|
|
|
|
|
.objects.Select(obj => new
|
|
|
|
|
{
|
|
|
|
|
name = obj?.name,
|
|
|
|
|
type = obj?.GetType().FullName,
|
|
|
|
|
instanceID = obj?.GetInstanceID(),
|
|
|
|
|
})
|
|
|
|
|
.ToList(),
|
|
|
|
|
gameObjects = Selection
|
|
|
|
|
.gameObjects.Select(go => new
|
|
|
|
|
{
|
|
|
|
|
name = go?.name,
|
|
|
|
|
instanceID = go?.GetInstanceID(),
|
|
|
|
|
})
|
|
|
|
|
.ToList(),
|
|
|
|
|
assetGUIDs = Selection.assetGUIDs, // GUIDs for selected assets in Project view
|
2025-03-31 03:58:01 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return Response.Success("Retrieved current selection details.", selectionInfo);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Error getting selection: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Tag Management Methods ---
|
|
|
|
|
|
|
|
|
|
private static object AddTag(string tagName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(tagName))
|
|
|
|
|
return Response.Error("Tag name cannot be empty or whitespace.");
|
|
|
|
|
|
|
|
|
|
// Check if tag already exists
|
|
|
|
|
if (InternalEditorUtility.tags.Contains(tagName))
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Tag '{tagName}' already exists.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Add the tag using the internal utility
|
|
|
|
|
InternalEditorUtility.AddTag(tagName);
|
|
|
|
|
// Force save assets to ensure the change persists in the TagManager asset
|
2025-04-08 18:14:13 +08:00
|
|
|
AssetDatabase.SaveAssets();
|
2025-03-31 03:58:01 +08:00
|
|
|
return Response.Success($"Tag '{tagName}' added successfully.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Failed to add tag '{tagName}': {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object RemoveTag(string tagName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(tagName))
|
|
|
|
|
return Response.Error("Tag name cannot be empty or whitespace.");
|
|
|
|
|
if (tagName.Equals("Untagged", StringComparison.OrdinalIgnoreCase))
|
2025-04-08 18:14:13 +08:00
|
|
|
return Response.Error("Cannot remove the built-in 'Untagged' tag.");
|
2025-03-31 03:58:01 +08:00
|
|
|
|
|
|
|
|
// Check if tag exists before attempting removal
|
|
|
|
|
if (!InternalEditorUtility.tags.Contains(tagName))
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Tag '{tagName}' does not exist.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Remove the tag using the internal utility
|
|
|
|
|
InternalEditorUtility.RemoveTag(tagName);
|
|
|
|
|
// Force save assets
|
2025-04-08 18:14:13 +08:00
|
|
|
AssetDatabase.SaveAssets();
|
2025-03-31 03:58:01 +08:00
|
|
|
return Response.Success($"Tag '{tagName}' removed successfully.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
// Catch potential issues if the tag is somehow in use or removal fails
|
|
|
|
|
return Response.Error($"Failed to remove tag '{tagName}': {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object GetTags()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string[] tags = InternalEditorUtility.tags;
|
|
|
|
|
return Response.Success("Retrieved current tags.", tags);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Failed to retrieve tags: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Layer Management Methods ---
|
|
|
|
|
|
|
|
|
|
private static object AddLayer(string layerName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(layerName))
|
|
|
|
|
return Response.Error("Layer name cannot be empty or whitespace.");
|
|
|
|
|
|
|
|
|
|
// Access the TagManager asset
|
|
|
|
|
SerializedObject tagManager = GetTagManager();
|
2025-04-08 18:14:13 +08:00
|
|
|
if (tagManager == null)
|
|
|
|
|
return Response.Error("Could not access TagManager asset.");
|
2025-03-31 03:58:01 +08:00
|
|
|
|
|
|
|
|
SerializedProperty layersProp = tagManager.FindProperty("layers");
|
|
|
|
|
if (layersProp == null || !layersProp.isArray)
|
2025-04-08 18:14:13 +08:00
|
|
|
return Response.Error("Could not find 'layers' property in TagManager.");
|
2025-03-31 03:58:01 +08:00
|
|
|
|
|
|
|
|
// Check if layer name already exists (case-insensitive check recommended)
|
|
|
|
|
for (int i = 0; i < TotalLayerCount; i++)
|
|
|
|
|
{
|
|
|
|
|
SerializedProperty layerSP = layersProp.GetArrayElementAtIndex(i);
|
2025-04-08 18:14:13 +08:00
|
|
|
if (
|
|
|
|
|
layerSP != null
|
|
|
|
|
&& layerName.Equals(layerSP.stringValue, StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
)
|
2025-03-31 03:58:01 +08:00
|
|
|
{
|
|
|
|
|
return Response.Error($"Layer '{layerName}' already exists at index {i}.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the first empty user layer slot (indices 8 to 31)
|
|
|
|
|
int firstEmptyUserLayer = -1;
|
|
|
|
|
for (int i = FirstUserLayerIndex; i < TotalLayerCount; i++)
|
|
|
|
|
{
|
|
|
|
|
SerializedProperty layerSP = layersProp.GetArrayElementAtIndex(i);
|
|
|
|
|
if (layerSP != null && string.IsNullOrEmpty(layerSP.stringValue))
|
|
|
|
|
{
|
|
|
|
|
firstEmptyUserLayer = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (firstEmptyUserLayer == -1)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error("No empty User Layer slots available (8-31 are full).");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assign the name to the found slot
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
SerializedProperty targetLayerSP = layersProp.GetArrayElementAtIndex(
|
|
|
|
|
firstEmptyUserLayer
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
targetLayerSP.stringValue = layerName;
|
|
|
|
|
// Apply the changes to the TagManager asset
|
|
|
|
|
tagManager.ApplyModifiedProperties();
|
|
|
|
|
// Save assets to make sure it's written to disk
|
2025-04-08 18:14:13 +08:00
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
return Response.Success(
|
|
|
|
|
$"Layer '{layerName}' added successfully to slot {firstEmptyUserLayer}."
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Failed to add layer '{layerName}': {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object RemoveLayer(string layerName)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(layerName))
|
|
|
|
|
return Response.Error("Layer name cannot be empty or whitespace.");
|
|
|
|
|
|
|
|
|
|
// Access the TagManager asset
|
|
|
|
|
SerializedObject tagManager = GetTagManager();
|
2025-04-08 18:14:13 +08:00
|
|
|
if (tagManager == null)
|
|
|
|
|
return Response.Error("Could not access TagManager asset.");
|
2025-03-31 03:58:01 +08:00
|
|
|
|
|
|
|
|
SerializedProperty layersProp = tagManager.FindProperty("layers");
|
2025-04-08 18:14:13 +08:00
|
|
|
if (layersProp == null || !layersProp.isArray)
|
|
|
|
|
return Response.Error("Could not find 'layers' property in TagManager.");
|
2025-03-31 03:58:01 +08:00
|
|
|
|
|
|
|
|
// Find the layer by name (must be user layer)
|
|
|
|
|
int layerIndexToRemove = -1;
|
|
|
|
|
for (int i = FirstUserLayerIndex; i < TotalLayerCount; i++) // Start from user layers
|
|
|
|
|
{
|
|
|
|
|
SerializedProperty layerSP = layersProp.GetArrayElementAtIndex(i);
|
|
|
|
|
// Case-insensitive comparison is safer
|
2025-04-08 18:14:13 +08:00
|
|
|
if (
|
|
|
|
|
layerSP != null
|
|
|
|
|
&& layerName.Equals(layerSP.stringValue, StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
)
|
2025-03-31 03:58:01 +08:00
|
|
|
{
|
|
|
|
|
layerIndexToRemove = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (layerIndexToRemove == -1)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"User layer '{layerName}' not found.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the name for that index
|
|
|
|
|
try
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
SerializedProperty targetLayerSP = layersProp.GetArrayElementAtIndex(
|
|
|
|
|
layerIndexToRemove
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
targetLayerSP.stringValue = string.Empty; // Set to empty string to remove
|
|
|
|
|
// Apply the changes
|
|
|
|
|
tagManager.ApplyModifiedProperties();
|
|
|
|
|
// Save assets
|
2025-04-08 18:14:13 +08:00
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
return Response.Success(
|
|
|
|
|
$"Layer '{layerName}' (slot {layerIndexToRemove}) removed successfully."
|
|
|
|
|
);
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Failed to remove layer '{layerName}': {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object GetLayers()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var layers = new Dictionary<int, string>();
|
|
|
|
|
for (int i = 0; i < TotalLayerCount; i++)
|
|
|
|
|
{
|
|
|
|
|
string layerName = LayerMask.LayerToName(i);
|
|
|
|
|
if (!string.IsNullOrEmpty(layerName)) // Only include layers that have names
|
|
|
|
|
{
|
|
|
|
|
layers.Add(i, layerName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Response.Success("Retrieved current named layers.", layers);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
return Response.Error($"Failed to retrieve layers: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Helper Methods ---
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the SerializedObject for the TagManager asset.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static SerializedObject GetTagManager()
|
|
|
|
|
{
|
2025-04-08 18:14:13 +08:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Load the TagManager asset from the ProjectSettings folder
|
|
|
|
|
UnityEngine.Object[] tagManagerAssets = AssetDatabase.LoadAllAssetsAtPath(
|
|
|
|
|
"ProjectSettings/TagManager.asset"
|
|
|
|
|
);
|
|
|
|
|
if (tagManagerAssets == null || tagManagerAssets.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError("[ManageEditor] TagManager.asset not found in ProjectSettings.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
// The first object in the asset file should be the TagManager
|
|
|
|
|
return new SerializedObject(tagManagerAssets[0]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"[ManageEditor] Error accessing TagManager.asset: {e.Message}");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Example Implementations for Settings ---
|
|
|
|
|
/*
|
|
|
|
|
private static object SetGameViewResolution(int width, int height) { ... }
|
|
|
|
|
private static object SetQualityLevel(JToken qualityLevelToken) { ... }
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper class to get custom tool names (remains the same)
|
2025-04-08 18:14:13 +08:00
|
|
|
internal static class EditorTools
|
|
|
|
|
{
|
|
|
|
|
public static string GetActiveToolName()
|
|
|
|
|
{
|
|
|
|
|
// This is a placeholder. Real implementation depends on how custom tools
|
2025-03-31 03:58:01 +08:00
|
|
|
// are registered and tracked in the specific Unity project setup.
|
|
|
|
|
// It might involve checking static variables, calling methods on specific tool managers, etc.
|
|
|
|
|
if (UnityEditor.Tools.current == Tool.Custom)
|
|
|
|
|
{
|
|
|
|
|
// Example: Check a known custom tool manager
|
|
|
|
|
// if (MyCustomToolManager.IsActive) return MyCustomToolManager.ActiveToolName;
|
2025-04-08 18:14:13 +08:00
|
|
|
return "Unknown Custom Tool";
|
2025-03-31 03:58:01 +08:00
|
|
|
}
|
|
|
|
|
return UnityEditor.Tools.current.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-08 18:14:13 +08:00
|
|
|
}
|