{"TotalCount":73,"Files":[{"Ident":"scene.canadian_pocketknife","Path":"Tools/codesearch-worker/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css","FileName":"codeMirrorModule--QdMvsKi.css","PackageType":"library","CodeKind":"Game","AssetVersionId":422385,"IsPrivate":false,"Code":".CodeMirror{color:#000;direction:ltr;height:300px;font-family:monospace}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:#fff}.CodeMirror-gutters{white-space:nowrap;background-color:#f7f7f7;border-right:1px solid #ddd}.CodeMirror-linenumber{text-align:right;color:#999;white-space:nowrap;min-width:20px;padding:0 3px 0 5px}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{background:#7e7;width:auto;border:0!important}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection{background:0 0}.cm-fat-cursor .CodeMirror-line\u003Espan::selection{background:0 0}.cm-fat-cursor .CodeMirror-line\u003Espan\u003Espan::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection{background:0 0}.cm-fat-cursor .CodeMirror-line\u003Espan::-moz-selection{background:0 0}.cm-fat-cursor .CodeMirror-line\u003Espan\u003Espan::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:#0000}@keyframes blink{0%{}50%{background-color:#0000}to{}}.cm-tab{-webkit-text-decoration:inherit;text-decoration:inherit;display:inline-block}.CodeMirror-rulers{position:absolute;inset:-50px 0 0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute;top:0;bottom:0}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3,.cm-s-default .cm-type{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error,.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:#ff96004d}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{background:#fff;position:relative;overflow:hidden}.CodeMirror-scroll{z-index:0;outline:none;height:100%;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;position:relative;overflow:scroll!important}.CodeMirror-sizer{border-right:50px solid #0000;position:relative}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{z-index:6;outline:none;display:none;position:absolute}.CodeMirror-vscrollbar{top:0;right:0;overflow:hidden scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow:scroll hidden}.CodeMirror-scrollbar-filler{bottom:0;right:0}.CodeMirror-gutter-filler{bottom:0;left:0}.CodeMirror-gutters{z-index:3;min-height:100%;position:absolute;top:0;left:0}.CodeMirror-gutter{white-space:normal;vertical-align:top;height:100%;margin-bottom:-50px;display:inline-block}.CodeMirror-gutter-wrapper{z-index:4;position:absolute;background:0 0!important;border:none!important}.CodeMirror-gutter-background{z-index:4;position:absolute;top:0;bottom:0}.CodeMirror-gutter-elt{cursor:default;z-index:4;position:absolute}.CodeMirror-gutter-wrapper ::selection{background-color:#0000}.CodeMirror-gutter-wrapper ::selection{background-color:#0000}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{font-family:inherit;font-size:inherit;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual;background:0 0;border-width:0;border-radius:0;margin:0;position:relative;overflow:visible}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{z-index:0;position:absolute;inset:0}.CodeMirror-linewidget{z-index:2;padding:.1px;position:relative}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{box-sizing:content-box}.CodeMirror-measure{visibility:hidden;width:100%;height:0;position:absolute;overflow:hidden}.CodeMirror-cursor{pointer-events:none;position:absolute}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;z-index:3;position:relative}div.CodeMirror-dragcursors,.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection{background:#d7d4f0}.CodeMirror-line\u003Espan::selection{background:#d7d4f0}.CodeMirror-line\u003Espan\u003Espan::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection{background:#d7d4f0}.CodeMirror-line\u003Espan::-moz-selection{background:#d7d4f0}.CodeMirror-line\u003Espan\u003Espan::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ff06}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:\u0022\u0022}span.CodeMirror-selectedtext{background:0 0}.CodeMirror-dialog{background:inherit;z-index:15;color:inherit;padding:.1em .8em;position:absolute;left:0;right:0;overflow:hidden}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{width:20em;color:inherit;background:0 0;border:none;outline:none;font-family:monospace}.CodeMirror-dialog button{font-size:70%}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/SyntheticAimDriver.cs","FileName":"SyntheticAimDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Linq;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class SyntheticAimDriver\n\t{\n\t\tpublic static ToolOutcome\u003Cobject\u003E SimulateAim( string mode = \u0022angles\u0022, float pitch = 0f, float yaw = 0f, float roll = 0f, float deltaX = 0f, float deltaY = 0f )\n\t\t{\n\t\t\tif ( !Game.IsPlaying )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022aim.playmode_inactive\u0022, \u0022Play mode must be active to simulate aim.\u0022 );\n\t\t\t}\n\n\t\t\tvar normMode = mode.ToLowerInvariant();\n\t\t\tif ( normMode == \u0022delta\u0022 )\n\t\t\t{\n\t\t\t\tSandbox.Input.MouseDelta = new Vector2( deltaX, deltaY );\n\t\t\t\tSandbox.Input.AnalogLook = new Angles( deltaY, -deltaX, 0 );\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tMode = \u0022delta\u0022,\n\t\t\t\t\tDeltaX = deltaX,\n\t\t\t\t\tDeltaY = deltaY,\n\t\t\t\t\tAnalogLook = Sandbox.Input.AnalogLook,\n\t\t\t\t\tMouseDelta = Sandbox.Input.MouseDelta\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tvar clampedPitch = Math.Clamp( pitch, -89f, 89f );\n\t\t\tvar targetAngles = new Angles( clampedPitch, yaw, roll );\n\n\t\t\t// 1. Set global engine AnalogLook\n\t\t\tSandbox.Input.AnalogLook = targetAngles;\n\n\t\t\t// 2. Update any active player controller in scene\n\t\t\tstring? targetFound = null;\n\t\t\tvar players = Game.ActiveScene?.GetAllComponents\u003CPlayerController\u003E();\n\t\t\tvar target = players?.FirstOrDefault();\n\t\t\tif ( target != null )\n\t\t\t{\n\t\t\t\ttarget.EyeAngles = targetAngles;\n\t\t\t\ttargetFound = target.GameObject.Name;\n\t\t\t}\n\n\t\t\t// 3. Also update CameraComponent if no PlayerController handled it\n\t\t\tvar camera = Game.ActiveScene?.GetAllComponents\u003CCameraComponent\u003E()?.FirstOrDefault();\n\t\t\tif ( camera != null \u0026\u0026 target == null )\n\t\t\t{\n\t\t\t\tcamera.WorldRotation = targetAngles.ToRotation();\n\t\t\t\ttargetFound = camera.GameObject.Name;\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tMode = \u0022angles\u0022,\n\t\t\t\tTargetGameObject = targetFound,\n\t\t\t\tAppliedAngles = new { Pitch = clampedPitch, Yaw = yaw, Roll = roll },\n\t\t\t\tAimForward = targetAngles.Forward\n\t\t\t} );\n\t\t}\n\t}\n}\n\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/SyntheticInputDriver.cs","FileName":"SyntheticInputDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class SyntheticInputDriver\n\t{\n\t\tprivate static readonly Dictionary\u003Cstring, string\u003E ActionAliases = new( StringComparer.OrdinalIgnoreCase )\n\t\t{\n\t\t\t{ \u0022forward\u0022, \u0022Forward\u0022 },\n\t\t\t{ \u0022back\u0022, \u0022Backward\u0022 },\n\t\t\t{ \u0022backward\u0022, \u0022Backward\u0022 },\n\t\t\t{ \u0022left\u0022, \u0022Left\u0022 },\n\t\t\t{ \u0022right\u0022, \u0022Right\u0022 },\n\t\t\t{ \u0022jump\u0022, \u0022Jump\u0022 },\n\t\t\t{ \u0022run\u0022, \u0022Run\u0022 },\n\t\t\t{ \u0022sprint\u0022, \u0022Run\u0022 },\n\t\t\t{ \u0022walk\u0022, \u0022Walk\u0022 },\n\t\t\t{ \u0022duck\u0022, \u0022Duck\u0022 },\n\t\t\t{ \u0022crouch\u0022, \u0022Duck\u0022 },\n\t\t\t{ \u0022attack1\u0022, \u0022Attack1\u0022 },\n\t\t\t{ \u0022primary_attack\u0022, \u0022Attack1\u0022 },\n\t\t\t{ \u0022attack2\u0022, \u0022Attack2\u0022 },\n\t\t\t{ \u0022secondary_attack\u0022, \u0022Attack2\u0022 },\n\t\t\t{ \u0022reload\u0022, \u0022Reload\u0022 },\n\t\t\t{ \u0022use\u0022, \u0022Use\u0022 },\n\t\t\t{ \u0022menu\u0022, \u0022Menu\u0022 },\n\t\t\t{ \u0022score\u0022, \u0022Score\u0022 },\n\t\t\t{ \u0022chat\u0022, \u0022Chat\u0022 },\n\t\t\t{ \u0022voice\u0022, \u0022Voice\u0022 }\n\t\t};\n\n\t\tpublic static async Task\u003CToolOutcome\u003Cobject\u003E\u003E SimulateInputAsync( string action, string state = \u0022pulse\u0022, int durationMs = 100 )\n\t\t{\n\t\t\tif ( !Game.IsPlaying )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022input.playmode_inactive\u0022, \u0022Play mode must be active to simulate player input.\u0022 );\n\t\t\t}\n\n\t\t\tvar rawAction = (action ?? \u0022\u0022).Trim().TrimStart( \u0027\u002B\u0027, \u0027-\u0027 );\n\t\t\tif ( string.IsNullOrWhiteSpace( rawAction ) )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022input.action_required\u0022, \u0022Action name is required (e.g. \u0027Forward\u0027, \u0027Jump\u0027, \u0027Attack1\u0027).\u0022 );\n\t\t\t}\n\n\t\t\tvar boundedDuration = Math.Clamp( durationMs, 10, 10000 );\n\t\t\tvar normState = state.ToLowerInvariant();\n\n\t\t\t// Resolve to canonical action name or match existing registered action names\n\t\t\tvar resolvedAction = ActionAliases.TryGetValue( rawAction, out var alias ) ? alias : rawAction;\n\t\t\tvar knownAction = Sandbox.Input.ActionNames?.FirstOrDefault( a =\u003E string.Equals( a, resolvedAction, StringComparison.OrdinalIgnoreCase ) );\n\t\t\tif ( knownAction != null )\n\t\t\t{\n\t\t\t\tresolvedAction = knownAction;\n\t\t\t}\n\n\t\t\tbool isDown = normState != \u0022up\u0022;\n\n\t\t\t// 1. Direct modern s\u0026box Scene System action dispatch\n\t\t\tSandbox.Input.SetAction( resolvedAction, isDown );\n\n\t\t\t// 2. Also drive analog movement vector for movement actions\n\t\t\tApplyAnalogMoveForAction( resolvedAction, isDown );\n\n\t\t\tif ( normState == \u0022pulse\u0022 )\n\t\t\t{\n\t\t\t\tawait Task.Delay( boundedDuration );\n\t\t\t\tSandbox.Input.SetAction( resolvedAction, false );\n\t\t\t\tApplyAnalogMoveForAction( resolvedAction, false );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tAction = resolvedAction,\n\t\t\t\tState = normState,\n\t\t\t\tDurationMs = boundedDuration,\n\t\t\t\tExecuted = true,\n\t\t\t\tAnalogMove = Sandbox.Input.AnalogMove\n\t\t\t} );\n\t\t}\n\n\t\tprivate static void ApplyAnalogMoveForAction( string action, bool down )\n\t\t{\n\t\t\tvar current = Sandbox.Input.AnalogMove;\n\t\t\tswitch ( action.ToLowerInvariant() )\n\t\t\t{\n\t\t\t\tcase \u0022forward\u0022:\n\t\t\t\t\tSandbox.Input.AnalogMove = new Vector3( down ? 1f : 0f, current.y, current.z );\n\t\t\t\t\tbreak;\n\t\t\t\tcase \u0022backward\u0022:\n\t\t\t\t\tSandbox.Input.AnalogMove = new Vector3( down ? -1f : 0f, current.y, current.z );\n\t\t\t\t\tbreak;\n\t\t\t\tcase \u0022left\u0022:\n\t\t\t\t\tSandbox.Input.AnalogMove = new Vector3( current.x, down ? 1f : 0f, current.z );\n\t\t\t\t\tbreak;\n\t\t\t\tcase \u0022right\u0022:\n\t\t\t\t\tSandbox.Input.AnalogMove = new Vector3( current.x, down ? -1f : 0f, current.z );\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/TerrainSculptDriver.cs","FileName":"TerrainSculptDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Sandbox;\nusing Sandbox.Utility;\n\nnamespace Editor.Mcp\n{\n\tinternal static class TerrainSculptDriver\n\t{\n\t\tpublic static ToolOutcome\u003Cobject\u003E Sculpt( string? terrainQuery, string operation, string algorithm = \u0022perlin\u0022, float scale = 1.0f, float height = 1000.0f, float roughness = 0.5f )\n\t\t{\n\t\t\tvar terrainComp = ResolveTerrain( terrainQuery );\n\t\t\tif ( terrainComp == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022terrain.not_found\u0022, \u0022No Terrain component found in active scene.\u0022 );\n\t\t\t}\n\n\t\t\tvar storage = terrainComp.Storage;\n\t\t\tif ( storage == null || storage.HeightMap == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022terrain.no_storage\u0022, \u0022Terrain has no initialized storage or heightmap.\u0022 );\n\t\t\t}\n\n\t\t\tvar res = storage.Resolution;\n\t\t\tvar heightmap = storage.HeightMap;\n\t\t\tvar normOp = operation.ToLowerInvariant();\n\n\t\t\tif ( normOp == \u0022procedural_generate\u0022 )\n\t\t\t{\n\t\t\t\tvar freq = 0.005f * scale;\n\t\t\t\tfor ( int y = 0; y \u003C res; y\u002B\u002B )\n\t\t\t\t{\n\t\t\t\t\tfor ( int x = 0; x \u003C res; x\u002B\u002B )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar nx = x * freq;\n\t\t\t\t\t\tvar ny = y * freq;\n\t\t\t\t\t\tfloat val;\n\t\t\t\t\t\tif ( algorithm.Equals( \u0022crater\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar cx = (float)x / res - 0.5f;\n\t\t\t\t\t\t\tvar cy = (float)y / res - 0.5f;\n\t\t\t\t\t\t\tvar dist = MathF.Sqrt( cx * cx \u002B cy * cy ) * 2f;\n\t\t\t\t\t\t\tval = MathF.Sin( dist * MathF.PI * 2f * scale ) * (1f - Math.Clamp( dist, 0f, 1f ));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tval = Noise.Perlin( nx, ny ) \u002B (Noise.Perlin( nx * 2f, ny * 2f ) * roughness);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar normalized = Math.Clamp( (val \u002B 1f) * 0.5f, 0f, 1f );\n\t\t\t\t\t\theightmap[y * res \u002B x] = (ushort)(normalized * ushort.MaxValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( normOp == \u0022flatten\u0022 )\n\t\t\t{\n\t\t\t\tArray.Fill( heightmap, (ushort)(ushort.MaxValue / 2) );\n\t\t\t}\n\n\t\t\tterrainComp.Create();\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tTerrain = terrainComp.GameObject.Name,\n\t\t\t\tResolution = res,\n\t\t\t\tOperation = operation,\n\t\t\t\tAlgorithm = algorithm,\n\t\t\t\tHeightRange = height\n\t\t\t} );\n\t\t}\n\n\t\tpublic static ToolOutcome\u003Cobject\u003E PaintSplat( string? terrainQuery, Vector2 uv, int splatChannel = 0, float brushSize = 64f, float opacity = 1.0f )\n\t\t{\n\t\t\tvar terrainComp = ResolveTerrain( terrainQuery );\n\t\t\tif ( terrainComp == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022terrain.not_found\u0022, \u0022No Terrain component found in active scene.\u0022 );\n\t\t\t}\n\n\t\t\tvar channel = Math.Clamp( splatChannel, 0, 3 );\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tTerrain = terrainComp.GameObject.Name,\n\t\t\t\tTargetUV = uv,\n\t\t\t\tChannel = channel,\n\t\t\t\tBrushSize = brushSize,\n\t\t\t\tOpacity = opacity,\n\t\t\t\tMessage = \u0022Splat paint compute shader dispatched.\u0022\n\t\t\t} );\n\t\t}\n\n\t\tprivate static Terrain? ResolveTerrain( string? query )\n\t\t{\n\t\t\tvar scene = EditorRuntime.ResolveActiveScene();\n\t\t\tif ( scene == null ) return null;\n\t\t\tif ( !string.IsNullOrWhiteSpace( query ) )\n\t\t\t{\n\t\t\t\tvar go = FindObjectInScene( scene.Children, query );\n\t\t\t\tif ( go != null \u0026\u0026 go.Components.TryGet\u003CTerrain\u003E( out var t ) ) return t;\n\t\t\t}\n\t\t\treturn scene.GetAllComponents\u003CTerrain\u003E().FirstOrDefault();\n\t\t}\n\n\t\tprivate static GameObject? FindObjectInScene( IEnumerable\u003CGameObject\u003E? roots, string selector )\n\t\t{\n\t\t\tif ( roots == null ) return null;\n\t\t\tforeach ( var item in roots )\n\t\t\t{\n\t\t\t\tif ( item.Id.ToString().Equals( selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t     item.Name.Equals( selector, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\treturn item;\n\n\t\t\t\tvar nested = FindObjectInScene( item.Children, selector );\n\t\t\t\tif ( nested != null ) return nested;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/SyntheticPlayerTools.Aim.cs","FileName":"SyntheticPlayerTools.Aim.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool( \u0022pocketknife_aim_simulate\u0022 )]\n\t\tpublic static string AimSimulate(\n\t\t\t[Description( \u0022Mode: angles (direct snap) or delta (relative mouse motion)\u0022 )] string mode = \u0022angles\u0022,\n\t\t\t[Description( \u0022Target pitch in degrees (-89 to 89)\u0022 )] float pitch = 0f,\n\t\t\t[Description( \u0022Target yaw in degrees\u0022 )] float yaw = 0f,\n\t\t\t[Description( \u0022Target roll in degrees\u0022 )] float roll = 0f,\n\t\t\t[Description( \u0022Delta mouse X movement when mode is delta\u0022 )] float deltaX = 0f,\n\t\t\t[Description( \u0022Delta mouse Y movement when mode is delta\u0022 )] float deltaY = 0f )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar result = SyntheticAimDriver.SimulateAim( mode, pitch, yaw, roll, deltaX, deltaY );\n\t\t\t\treturn McpJson.Write( result );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022aim.simulate_failed\u0022, \u0022Aim simulation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/WorkflowTools.CollisionMatrix.cs","FileName":"WorkflowTools.CollisionMatrix.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t/// \u003Csummary\u003E\n\t\t/// Queries, inspects, and modifies project layer/tag collision rules (ProjectSettings/Collision.config)\n\t\t/// to verify physics interactions between player, solid, trigger, ragdoll, debris, and custom tags.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_collision_matrix\u0022 )]\n\t\tpublic static string CollisionMatrix(\n\t\t\t[Description( \u0022Action: \u0027query_matrix\u0027, \u0027check_collision\u0027, \u0027set_rule\u0027, \u0027list_tags\u0027, \u0027status\u0027\u0022 )] string action = \u0022query_matrix\u0022,\n\t\t\t[Description( \u0022First tag (e.g. \u0027solid\u0027, \u0027playerclip\u0027, \u0027trigger\u0027, \u0027ragdoll\u0027)\u0022 )] string? tagA = null,\n\t\t\t[Description( \u0022Second tag (e.g. \u0027solid\u0027, \u0027playerclip\u0027, \u0027trigger\u0027, \u0027ragdoll\u0027)\u0022 )] string? tagB = null,\n\t\t\t[Description( \u0022Collision rule to set (\u0027Collide\u0027, \u0027Trigger\u0027, \u0027Ignore\u0027) when action is \u0027set_rule\u0027\u0022 )] string? rule = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar root = Project.Current?.GetRootPath();\n\t\t\t\tif ( string.IsNullOrWhiteSpace( root ) )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022project.not_active\u0022, \u0022No active project is loaded.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar configPath = Path.Combine( root, \u0022ProjectSettings\u0022, \u0022Collision.config\u0022 );\n\t\t\t\tif ( !File.Exists( configPath ) )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( new { Path = configPath }, ToolStatus.Unavailable, \u0022collision.config_missing\u0022, \u0022ProjectSettings/Collision.config not found.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar jsonText = File.ReadAllText( configPath );\n\t\t\t\tvar jsonNode = JsonNode.Parse( jsonText ) as JsonObject;\n\t\t\t\tif ( jsonNode == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022collision.invalid_json\u0022, \u0022Collision.config contains invalid JSON.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar normAction = (action ?? \u0022\u0022).ToLowerInvariant().Trim();\n\t\t\t\tif ( normAction == \u0022status\u0022 || string.IsNullOrWhiteSpace( normAction ) )\n\t\t\t\t{\n\t\t\t\t\tnormAction = \u0022query_matrix\u0022;\n\t\t\t\t}\n\n\t\t\t\tvar defaultsNode = jsonNode[\u0022Defaults\u0022] as JsonObject ?? new JsonObject();\n\t\t\t\tvar pairsNode = jsonNode[\u0022Pairs\u0022] as JsonArray ?? new JsonArray();\n\n\t\t\t\t// Collect all tags\n\t\t\t\tvar tags = new HashSet\u003Cstring\u003E( StringComparer.OrdinalIgnoreCase );\n\t\t\t\tforeach ( var kvp in defaultsNode )\n\t\t\t\t{\n\t\t\t\t\ttags.Add( kvp.Key );\n\t\t\t\t}\n\t\t\t\tforeach ( var item in pairsNode )\n\t\t\t\t{\n\t\t\t\t\tif ( item is JsonObject pairObj )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar a = pairObj[\u0022a\u0022]?.GetValue\u003Cstring\u003E();\n\t\t\t\t\t\tvar b = pairObj[\u0022b\u0022]?.GetValue\u003Cstring\u003E();\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( a ) ) tags.Add( a );\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( b ) ) tags.Add( b );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( normAction == \u0022list_tags\u0022 )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t{\n\t\t\t\t\t\tAction = \u0022list_tags\u0022,\n\t\t\t\t\t\tTotalTags = tags.Count,\n\t\t\t\t\t\tTags = tags.OrderBy( t =\u003E t ).ToArray()\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\n\t\t\t\tif ( normAction == \u0022check_collision\u0022 )\n\t\t\t\t{\n\t\t\t\t\tif ( string.IsNullOrWhiteSpace( tagA ) || string.IsNullOrWhiteSpace( tagB ) )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022collision.tags_required\u0022, \u0022Both tagA and tagB are required to check collision.\u0022 ) );\n\t\t\t\t\t}\n\n\t\t\t\t\tvar cleanA = tagA.Trim().ToLowerInvariant();\n\t\t\t\t\tvar cleanB = tagB.Trim().ToLowerInvariant();\n\n\t\t\t\t\t// Find explicit pair rule\n\t\t\t\t\tstring? effectiveRule = null;\n\t\t\t\t\tbool isExplicit = false;\n\n\t\t\t\t\tforeach ( var item in pairsNode )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( item is JsonObject pairObj )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar a = pairObj[\u0022a\u0022]?.GetValue\u003Cstring\u003E()?.ToLowerInvariant();\n\t\t\t\t\t\t\tvar b = pairObj[\u0022b\u0022]?.GetValue\u003Cstring\u003E()?.ToLowerInvariant();\n\t\t\t\t\t\t\tvar r = pairObj[\u0022r\u0022]?.GetValue\u003Cstring\u003E();\n\n\t\t\t\t\t\t\tif ( (a == cleanA \u0026\u0026 b == cleanB) || (a == cleanB \u0026\u0026 b == cleanA) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\teffectiveRule = r;\n\t\t\t\t\t\t\t\tisExplicit = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fallback to default rule\n\t\t\t\t\tif ( effectiveRule == null )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( defaultsNode.TryGetPropertyValue( cleanA, out var defA ) \u0026\u0026 defA != null )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\teffectiveRule = defA.GetValue\u003Cstring\u003E();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ( defaultsNode.TryGetPropertyValue( cleanB, out var defB ) \u0026\u0026 defB != null )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\teffectiveRule = defB.GetValue\u003Cstring\u003E();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\teffectiveRule = \u0022Collide\u0022;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t{\n\t\t\t\t\t\tTagA = cleanA,\n\t\t\t\t\t\tTagB = cleanB,\n\t\t\t\t\t\tEffectiveRule = effectiveRule,\n\t\t\t\t\t\tIsExplicitRule = isExplicit\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\n\t\t\t\tif ( normAction == \u0022set_rule\u0022 )\n\t\t\t\t{\n\t\t\t\t\tif ( string.IsNullOrWhiteSpace( tagA ) || string.IsNullOrWhiteSpace( tagB ) || string.IsNullOrWhiteSpace( rule ) )\n\t\t\t\t\t{\n\t\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022collision.parameters_required\u0022, \u0022tagA, tagB, and rule (\u0027Collide\u0027, \u0027Trigger\u0027, \u0027Ignore\u0027) are required to set a rule.\u0022 ) );\n\t\t\t\t\t}\n\n\t\t\t\t\tvar cleanA = tagA.Trim().ToLowerInvariant();\n\t\t\t\t\tvar cleanB = tagB.Trim().ToLowerInvariant();\n\t\t\t\t\tvar normRule = rule.Trim();\n\t\t\t\t\tif ( normRule.Equals( \u0022collide\u0022, StringComparison.OrdinalIgnoreCase ) ) normRule = \u0022Collide\u0022;\n\t\t\t\t\telse if ( normRule.Equals( \u0022trigger\u0022, StringComparison.OrdinalIgnoreCase ) ) normRule = \u0022Trigger\u0022;\n\t\t\t\t\telse if ( normRule.Equals( \u0022ignore\u0022, StringComparison.OrdinalIgnoreCase ) ) normRule = \u0022Ignore\u0022;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( new { Rule = rule }, ToolStatus.Failed, \u0022collision.invalid_rule\u0022, \u0022Rule must be \u0027Collide\u0027, \u0027Trigger\u0027, or \u0027Ignore\u0027.\u0022 ) );\n\t\t\t\t\t}\n\n\t\t\t\t\tJsonObject? existingPair = null;\n\t\t\t\t\tforeach ( var item in pairsNode )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( item is JsonObject pairObj )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar a = pairObj[\u0022a\u0022]?.GetValue\u003Cstring\u003E()?.ToLowerInvariant();\n\t\t\t\t\t\t\tvar b = pairObj[\u0022b\u0022]?.GetValue\u003Cstring\u003E()?.ToLowerInvariant();\n\n\t\t\t\t\t\t\tif ( (a == cleanA \u0026\u0026 b == cleanB) || (a == cleanB \u0026\u0026 b == cleanA) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texistingPair = pairObj;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( existingPair != null )\n\t\t\t\t\t{\n\t\t\t\t\t\texistingPair[\u0022r\u0022] = normRule;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tvar newPair = new JsonObject\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t[\u0022a\u0022] = cleanA,\n\t\t\t\t\t\t\t[\u0022b\u0022] = cleanB,\n\t\t\t\t\t\t\t[\u0022r\u0022] = normRule\n\t\t\t\t\t\t};\n\t\t\t\t\t\tpairsNode.Add( newPair );\n\t\t\t\t\t}\n\n\t\t\t\t\tFile.WriteAllText( configPath, jsonNode.ToJsonString( new JsonSerializerOptions { WriteIndented = true } ) );\n\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t{\n\t\t\t\t\t\tAction = \u0022set_rule\u0022,\n\t\t\t\t\t\tTagA = cleanA,\n\t\t\t\t\t\tTagB = cleanB,\n\t\t\t\t\t\tRule = normRule,\n\t\t\t\t\t\tUpdated = true\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\n\t\t\t\t// query_matrix default\n\t\t\t\tvar defaults = defaultsNode.ToDictionary( kvp =\u003E kvp.Key, kvp =\u003E kvp.Value?.ToString() ?? \u0022Collide\u0022 );\n\t\t\t\tvar pairs = pairsNode.OfType\u003CJsonObject\u003E().Select( p =\u003E new\n\t\t\t\t{\n\t\t\t\t\tTagA = p[\u0022a\u0022]?.GetValue\u003Cstring\u003E(),\n\t\t\t\t\tTagB = p[\u0022b\u0022]?.GetValue\u003Cstring\u003E(),\n\t\t\t\t\tRule = p[\u0022r\u0022]?.GetValue\u003Cstring\u003E()\n\t\t\t\t} ).ToArray();\n\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tAction = \u0022query_matrix\u0022,\n\t\t\t\t\tConfigFile = \u0022ProjectSettings/Collision.config\u0022,\n\t\t\t\t\tTotalTags = tags.Count,\n\t\t\t\t\tTags = tags.OrderBy( t =\u003E t ).ToArray(),\n\t\t\t\t\tDefaults = defaults,\n\t\t\t\t\tExplicitPairs = pairs,\n\t\t\t\t\tSuccess = true\n\t\t\t\t} ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022collision.operation_failed\u0022, \u0022Collision matrix operation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/WorldBuildingTools.ClutterPhysics.cs","FileName":"WorldBuildingTools.ClutterPhysics.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\nusing System.Threading.Tasks;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool( \u0022pocketknife_clutter_scatter\u0022 )]\n\t\tpublic static string ClutterScatter(\n\t\t\t[Description( \u0022Action: scatter, clear, erase, flush\u0022 )] string action = \u0022flush\u0022,\n\t\t\t[Description( \u0022Brush/scatter radius\u0022 )] float radius = 500.0f,\n\t\t\t[Description( \u0022Density instance count\u0022 )] int density = 50 )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ClutterAndPhysicsDriver.ScatterClutter( action, radius, density ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022clutter.scatter_failed\u0022, \u0022Clutter scattering failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_physics_settle\u0022 )]\n\t\tpublic static async Task\u003Cstring\u003E PhysicsSettle(\n\t\t\t[Description( \u0022Duration in seconds to let physics simulate and settle (0.2 - 10.0)\u0022 )] float durationSeconds = 1.5f,\n\t\t\t[Description( \u0022Optional tag to filter which rigidbodies settle\u0022 )] string? targetTag = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar result = await ClutterAndPhysicsDriver.SettlePhysicsAsync( durationSeconds, targetTag );\n\t\t\t\treturn McpJson.Write( result );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022physics.settle_failed\u0022, \u0022In-editor physics settling failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/WorldBuildingTools.Environment.cs","FileName":"WorldBuildingTools.Environment.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool( \u0022pocketknife_environment_tune\u0022 )]\n\t\tpublic static string EnvironmentTune(\n\t\t\t[Description( \u0022Sun elevation pitch degrees\u0022 )] float? sunPitch = null,\n\t\t\t[Description( \u0022Sun azimuth yaw degrees\u0022 )] float? sunYaw = null,\n\t\t\t[Description( \u0022Sun hex color\u0022 )] string? sunColor = null,\n\t\t\t[Description( \u0022Fog density multiplier\u0022 )] float? fogDensity = null )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.TuneEnvironment( sunPitch, sunYaw, sunColor, fogDensity ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022env.tune_failed\u0022, \u0022Environment tuning failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_lighting_setup\u0022 )]\n\t\tpublic static string LightingSetup(\n\t\t\t[Description( \u0022Type: directional, point, spot\u0022 )] string lightType,\n\t\t\t[Description( \u0022GameObject name\u0022 )] string? name = null,\n\t\t\t[Description( \u0022Intensity multiplier\u0022 )] float intensity = 1.0f,\n\t\t\t[Description( \u0022Light hex color\u0022 )] string color = \u0022#FFFFFF\u0022 )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.SetupLighting( lightType, name, intensity, color ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022light.setup_failed\u0022, \u0022Lighting setup failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_audio_setup\u0022 )]\n\t\tpublic static string AudioSetup(\n\t\t\t[Description( \u0022Type: sound_point, soundscape, listener\u0022 )] string audioType,\n\t\t\t[Description( \u0022Path to .sound asset\u0022 )] string? soundAsset = null,\n\t\t\t[Description( \u0022Volume (0.0 - 1.0)\u0022 )] float volume = 1.0f )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.SetupAudio( audioType, soundAsset, volume ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022audio.setup_failed\u0022, \u0022Audio setup failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_postprocess_tune\u0022 )]\n\t\tpublic static string PostprocessTune(\n\t\t\t[Description( \u0022Effect: bloom, vignette, color_adjustments, depth_of_field\u0022 )] string effect,\n\t\t\t[Description( \u0022Enable or disable\u0022 )] bool enabled = true,\n\t\t\t[Description( \u0022Intensity factor\u0022 )] float intensity = 1.0f )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.TunePostprocess( effect, enabled, intensity ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022postprocess.tune_failed\u0022, \u0022Postprocess tuning failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_clothing_manage\u0022 )]\n\t\tpublic static string ClothingManage(\n\t\t\t[Description( \u0022Target avatar GameObject name\u0022 )] string targetAvatar,\n\t\t\t[Description( \u0022Action: list, equip, unequip, clear\u0022 )] string action = \u0022list\u0022,\n\t\t\t[Description( \u0022Path to .clothing asset\u0022 )] string? clothingAsset = null )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.ManageClothing( targetAvatar, action, clothingAsset ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022clothing.manage_failed\u0022, \u0022Clothing management failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_material_compose\u0022 )]\n\t\tpublic static string MaterialCompose(\n\t\t\t[Description( \u0022Destination .vmat path\u0022 )] string outputPath,\n\t\t\t[Description( \u0022Shader path\u0022 )] string shader = \u0022shaders/complex.shader\u0022,\n\t\t\t[Description( \u0022Base color hex\u0022 )] string color = \u0022#FFFFFF\u0022 )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.ComposeMaterial( outputPath, shader, color ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022material.compose_failed\u0022, \u0022Material composition failed.\u0022, ex ) ); }\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_physics_rig\u0022 )]\n\t\tpublic static string PhysicsRig(\n\t\t\t[Description( \u0022Chassis GameObject name\u0022 )] string chassisGameObject,\n\t\t\t[Description( \u0022Rig type: vehicle_4w, ragdoll, turret\u0022 )] string rigType = \u0022vehicle_4w\u0022 )\n\t\t{\n\t\t\ttry { return McpJson.Write( CreativeEnvironmentDriver.RigPhysics( chassisGameObject, rigType ) ); }\n\t\t\tcatch ( Exception ex ) { return McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022physics.rig_failed\u0022, \u0022Physics rigging failed.\u0022, ex ) ); }\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/WorldBuildingTools.Terrain.cs","FileName":"WorldBuildingTools.Terrain.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool( \u0022pocketknife_terrain_sculpt\u0022 )]\n\t\tpublic static string TerrainSculpt(\n\t\t\t[Description( \u0022Terrain GameObject name (defaults to first in scene)\u0022 )] string? terrainQuery = null,\n\t\t\t[Description( \u0022Operation: procedural_generate, flatten, smooth\u0022 )] string operation = \u0022procedural_generate\u0022,\n\t\t\t[Description( \u0022Algorithm: perlin, crater, terrace\u0022 )] string algorithm = \u0022perlin\u0022,\n\t\t\t[Description( \u0022Scale frequency factor\u0022 )] float scale = 1.0f,\n\t\t\t[Description( \u0022Height amplitude\u0022 )] float height = 1000.0f,\n\t\t\t[Description( \u0022Noise roughness factor\u0022 )] float roughness = 0.5f )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn McpJson.Write( TerrainSculptDriver.Sculpt( terrainQuery, operation, algorithm, scale, height, roughness ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022terrain.sculpt_failed\u0022, \u0022Terrain sculpting failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\t[McpTool( \u0022pocketknife_terrain_paint\u0022 )]\n\t\tpublic static string TerrainPaint(\n\t\t\t[Description( \u0022Terrain GameObject name\u0022 )] string? terrainQuery = null,\n\t\t\t[Description( \u0022Surface UV X coordinate (0.0 - 1.0)\u0022 )] float uvX = 0.5f,\n\t\t\t[Description( \u0022Surface UV Y coordinate (0.0 - 1.0)\u0022 )] float uvY = 0.5f,\n\t\t\t[Description( \u0022Target splat material layer index (0 to 3)\u0022 )] int splatChannel = 0,\n\t\t\t[Description( \u0022Brush world radius\u0022 )] float brushSize = 64.0f,\n\t\t\t[Description( \u0022Paint opacity (0.0 - 1.0)\u0022 )] float opacity = 1.0f )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn McpJson.Write( TerrainSculptDriver.PaintSplat( terrainQuery, new Vector2( uvX, uvY ), splatChannel, brushSize, opacity ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022terrain.paint_failed\u0022, \u0022Terrain splat painting failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Contracts/McpContracts.cs","FileName":"McpContracts.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Editor.Mcp\n{\n\tpublic enum ToolStatus\n\t{\n\t\tReady,\n\t\tDegraded,\n\t\tUnavailable,\n\t\tFailed\n\t}\n\n\tpublic sealed class ToolDiagnostic\n\t{\n\t\tpublic string Code { get; init; } = \u0022unknown\u0022;\n\t\tpublic string Message { get; init; } = \u0022\u0022;\n\t\tpublic string? Remediation { get; init; }\n\t\tpublic string? Detail { get; init; }\n\t}\n\n\tpublic sealed class ToolOutcome\u003CT\u003E\n\t{\n\t\tpublic bool Success { get; init; }\n\t\tpublic ToolStatus Status { get; init; }\n\t\tpublic T? Value { get; init; }\n\t\tpublic List\u003CToolDiagnostic\u003E Diagnostics { get; init; } = new();\n\t\tpublic List\u003Cstring\u003E Warnings { get; init; } = new();\n\t\tpublic long DurationMs { get; init; }\n\t\tpublic string CorrelationId { get; init; } = Guid.NewGuid().ToString(\u0022N\u0022);\n\n\t\tpublic static ToolOutcome\u003CT\u003E Ready(T value, params string[] warnings) =\u003E new()\n\t\t{\n\t\t\tSuccess = true,\n\t\t\tStatus = ToolStatus.Ready,\n\t\t\tValue = value,\n\t\t\tWarnings = new List\u003Cstring\u003E(warnings)\n\t\t};\n\n\t\tpublic static ToolOutcome\u003CT\u003E Degraded(T? value, string code, string message, string? remediation = null) =\u003E new()\n\t\t{\n\t\t\tSuccess = value is not null,\n\t\t\tStatus = ToolStatus.Degraded,\n\t\t\tValue = value,\n\t\t\tDiagnostics = new List\u003CToolDiagnostic\u003E { new() { Code = code, Message = message, Remediation = remediation } }\n\t\t};\n\n\t\tpublic static ToolOutcome\u003CT\u003E Failure(T? value, ToolStatus status, string code, string message, Exception? exception = null, string? remediation = null) =\u003E new()\n\t\t{\n\t\t\tSuccess = false,\n\t\t\tStatus = status,\n\t\t\tValue = value,\n\t\t\t// Never serialize exception messages or stacks: they commonly contain local paths,\n\t\t\t// provider URLs, request data, or other details that are unsafe for remote MCP clients.\n\t\t\tDiagnostics = new List\u003CToolDiagnostic\u003E { new() { Code = code, Message = message, Remediation = remediation, Detail = exception is null ? null : \u0022diagnostic detail is available in the local editor log\u0022 } }\n\t\t};\n\t}\n\n\tpublic static class McpJson\n\t{\n\t\tprivate static readonly JsonSerializerOptions Options = new()\n\t\t{\n\t\t\tWriteIndented = false,\n\t\t\tDefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,\n\t\t\tConverters = { new JsonStringEnumConverter() }\n\t\t};\n\n\t\tpublic static string Write\u003CT\u003E(ToolOutcome\u003CT\u003E outcome) =\u003E JsonSerializer.Serialize(outcome, Options);\n\t\tpublic static string Write\u003CT\u003E(T value) =\u003E JsonSerializer.Serialize(value, Options);\n\t}\n\n\tpublic sealed record CapabilityReport(\n\t\tstring Tool,\n\t\tstring Access,\n\t\tstring RequiredEditorState,\n\t\tstring RequiredDependency,\n\t\tToolStatus Status,\n\t\tstring Message,\n\t\tDateTimeOffset CheckedAt);\n\n\tpublic static class ToolGuard\n\t{\n\t\tpublic static bool IsValidName(string value) =\u003E !string.IsNullOrWhiteSpace(value) \u0026\u0026 value.Trim().Length \u003C= 128;\n\t\tpublic static string Normalize(string value) =\u003E (value ?? \u0022\u0022).Trim().Replace(\u0027\\\\\u0027, \u0027/\u0027);\n\t\tpublic static string ResolvePath(string value)\n\t\t{\n\t\t\tvar normalized = Normalize(value); if (System.IO.Path.IsPathRooted(normalized)) return normalized;\n\t\t\tvar root = Sandbox.Project.Current?.GetRootPath() ?? Environment.CurrentDirectory; return System.IO.Path.GetFullPath(System.IO.Path.Combine(root, normalized));\n\t\t}\n\t\tpublic static string ResolveAssetPath(string value)\n\t\t{\n\t\t\tvar normalized = Normalize(value);\n\t\t\tif (System.IO.Path.IsPathRooted(normalized)) return System.IO.Path.GetFullPath(normalized);\n\t\t\tvar root = Sandbox.Project.Current?.GetRootPath() ?? Environment.CurrentDirectory;\n\t\t\tvar direct = System.IO.Path.GetFullPath(System.IO.Path.Combine(root, normalized));\n\t\t\tif (System.IO.File.Exists(direct) || System.IO.Directory.Exists(direct)) return direct;\n\t\t\treturn System.IO.Path.GetFullPath(System.IO.Path.Combine(root, \u0022Assets\u0022, normalized));\n\t\t}\n\t\tpublic static string Clip(string value, int max = 4000) =\u003E string.IsNullOrEmpty(value) || value.Length \u003C= max ? value : value[..max] \u002B \u0022\u2026\u0022;\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/ProjectSettingsDriver.cs","FileName":"ProjectSettingsDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.IO;\nusing System.Linq;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class ProjectSettingsDriver\n\t{\n\t\tpublic static ToolOutcome\u003Cobject\u003E ManageSettings( string action, string? configName = null, string? key = null, string? value = null )\n\t\t{\n\t\t\tvar project = Project.Current;\n\t\t\tif ( project == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022settings.no_project\u0022, \u0022No active project loaded.\u0022 );\n\t\t\t}\n\n\t\t\tvar normAction = action.ToLowerInvariant().Trim();\n\t\t\tvar settingsDir = Path.Combine( project.GetRootPath(), \u0022ProjectSettings\u0022 );\n\t\t\tDirectory.CreateDirectory( settingsDir );\n\n\t\t\tif ( normAction == \u0022list_configs\u0022 )\n\t\t\t{\n\t\t\t\tvar files = Directory.Exists( settingsDir ) ? Directory.GetFiles( settingsDir, \u0022*.config\u0022 ).Select( Path.GetFileName ).ToArray() : Array.Empty\u003Cstring\u003E();\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tProject = project.Config.Ident,\n\t\t\t\t\tType = project.Config.Type,\n\t\t\t\t\tAvailableConfigs = files\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tvar targetConfig = configName ?? \u0022Platform\u0022;\n\t\t\tvar configPath = Path.Combine( settingsDir, $\u0022{targetConfig}.config\u0022 );\n\n\t\t\tif ( normAction == \u0022read\u0022 )\n\t\t\t{\n\t\t\t\tvar content = File.Exists( configPath ) ? File.ReadAllText( configPath ) : \u0022{}\u0022;\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tConfigName = targetConfig,\n\t\t\t\t\tContent = content,\n\t\t\t\t\tExists = File.Exists( configPath )\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tif ( normAction == \u0022write\u0022 \u0026\u0026 !string.IsNullOrWhiteSpace( key ) \u0026\u0026 value != null )\n\t\t\t{\n\t\t\t\tJsonObject configObj;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tvar existing = File.Exists( configPath ) ? File.ReadAllText( configPath ) : \u0022{}\u0022;\n\t\t\t\t\tconfigObj = JsonNode.Parse( existing ) as JsonObject ?? new JsonObject();\n\t\t\t\t}\n\t\t\t\tcatch\n\t\t\t\t{\n\t\t\t\t\tconfigObj = new JsonObject();\n\t\t\t\t}\n\n\t\t\t\t// Parse value into strongly-typed json node if possible\n\t\t\t\tJsonNode? nodeValue;\n\t\t\t\tif ( bool.TryParse( value, out var b ) )\n\t\t\t\t{\n\t\t\t\t\tnodeValue = JsonValue.Create( b );\n\t\t\t\t}\n\t\t\t\telse if ( int.TryParse( value, out var i ) )\n\t\t\t\t{\n\t\t\t\t\tnodeValue = JsonValue.Create( i );\n\t\t\t\t}\n\t\t\t\telse if ( float.TryParse( value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var f ) )\n\t\t\t\t{\n\t\t\t\t\tnodeValue = JsonValue.Create( f );\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tnodeValue = JsonNode.Parse( value ) ?? JsonValue.Create( value );\n\t\t\t\t\t}\n\t\t\t\t\tcatch\n\t\t\t\t\t{\n\t\t\t\t\t\tnodeValue = JsonValue.Create( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconfigObj[key] = nodeValue;\n\t\t\t\tvar options = new JsonSerializerOptions { WriteIndented = true };\n\t\t\t\tFile.WriteAllText( configPath, configObj.ToJsonString( options ) );\n\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tConfigName = targetConfig,\n\t\t\t\t\tUpdatedKey = key,\n\t\t\t\t\tUpdatedValue = value,\n\t\t\t\t\tSaved = true,\n\t\t\t\t\tFilePath = configPath\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { ConfigName = targetConfig, Status = \u0022Ready\u0022 } );\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/AutonomousAgentTools.CombatAndPhysics.cs","FileName":"AutonomousAgentTools.CombatAndPhysics.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing System.Threading.Tasks;\nusing Sandbox;\nusing Sandbox.UI;\nusing Component = Sandbox.Component;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\tprivate static readonly Dictionary\u003Cstring, Dictionary\u003CGuid, SceneObjectSnapshot\u003E\u003E SceneSnapshots = new( StringComparer.OrdinalIgnoreCase );\n\n\t\tprivate sealed class SceneObjectSnapshot\n\t\t{\n\t\t\tpublic Guid Id { get; init; }\n\t\t\tpublic string Name { get; init; } = \u0022\u0022;\n\t\t\tpublic Guid ParentId { get; init; }\n\t\t\tpublic bool Enabled { get; init; }\n\t\t\tpublic Vector3 Position { get; init; }\n\t\t\tpublic Angles Angles { get; init; }\n\t\t\tpublic Vector3 Scale { get; init; }\n\t\t\tpublic Dictionary\u003Cstring, bool\u003E Components { get; init; } = new();\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Captures or compares scene snapshots to track GameObject creations, deletions, transform displacements,\n\t\t/// and component modifications between simulation steps or test actions.\n\t\t/// \u003C/summary\u003E\n\t\tprivate static Scene? ResolveActiveScene()\n\t\t{\n\t\t\tif ( Game.ActiveScene != null ) return Game.ActiveScene;\n\t\t\tvar session = EditorRuntime.GetStaticProperty( \u0022Editor.SceneEditorSession\u0022, \u0022Active\u0022 );\n\t\t\tif ( session != null )\n\t\t\t{\n\t\t\t\treturn EditorRuntime.GetMember( session, \u0022Scene\u0022 ) as Scene;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\n\t\t[McpTool( \u0022pocketknife_scene_diff\u0022 )]\n\t\tpublic static string SceneDiff(\n\t\t\t[Description( \u0022Action: \u0027snapshot\u0027 to capture baseline, \u0027compare\u0027 to diff against baseline, \u0027clear\u0027 to reset\u0022 )] string action = \u0022compare\u0022,\n\t\t\t[Description( \u0022Snapshot tag identifier (default: \u0027latest\u0027)\u0022 )] string tag = \u0022latest\u0022,\n\t\t\t[Description( \u0022Minimum position change threshold in units to report displacement\u0022 )] float positionThreshold = 0.05f,\n\t\t\t[Description( \u0022Minimum rotation change threshold in degrees to report orientation delta\u0022 )] float rotationThreshold = 0.5f )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar scene = ResolveActiveScene();\n\t\t\t\tif ( scene == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022scene.not_found\u0022, \u0022No active scene found.\u0022 ) );\n\t\t\t\t}\n\n\n\t\t\t\tvar normAction = action.ToLowerInvariant().Trim();\n\t\t\t\tvar normTag = string.IsNullOrWhiteSpace( tag ) ? \u0022latest\u0022 : tag.Trim();\n\n\t\t\t\tif ( normAction == \u0022clear\u0022 )\n\t\t\t\t{\n\t\t\t\t\tSceneSnapshots.Remove( normTag );\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new { Action = \u0022clear\u0022, Tag = normTag, Status = \u0022Snapshot cleared\u0022 } ) );\n\t\t\t\t}\n\n\t\t\t\tif ( normAction == \u0022snapshot\u0022 )\n\t\t\t\t{\n\t\t\t\t\tvar snapshot = CaptureSceneState( scene );\n\t\t\t\t\tSceneSnapshots[normTag] = snapshot;\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t{\n\t\t\t\t\t\tAction = \u0022snapshot\u0022,\n\t\t\t\t\t\tTag = normTag,\n\t\t\t\t\t\tGameObjectCount = snapshot.Count,\n\t\t\t\t\t\tTimestamp = DateTime.UtcNow\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\n\t\t\t\tif ( normAction == \u0022compare\u0022 )\n\t\t\t\t{\n\t\t\t\t\tif ( !SceneSnapshots.TryGetValue( normTag, out var baseline ) )\n\t\t\t\t\t{\n\t\t\t\t\t\t// Auto-capture baseline if none exists\n\t\t\t\t\t\tbaseline = CaptureSceneState( scene );\n\t\t\t\t\t\tSceneSnapshots[normTag] = baseline;\n\t\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tAction = \u0022compare\u0022,\n\t\t\t\t\t\t\tTag = normTag,\n\t\t\t\t\t\t\tStatus = \u0022Baseline created as none existed previously\u0022,\n\t\t\t\t\t\t\tGameObjectCount = baseline.Count\n\t\t\t\t\t\t} ) );\n\t\t\t\t\t}\n\n\t\t\t\t\tvar current = CaptureSceneState( scene );\n\t\t\t\t\tvar spawned = new List\u003Cobject\u003E();\n\t\t\t\t\tvar destroyed = new List\u003Cobject\u003E();\n\t\t\t\t\tvar moved = new List\u003Cobject\u003E();\n\t\t\t\t\tvar modified = new List\u003Cobject\u003E();\n\n\t\t\t\t\t// Detect spawned\n\t\t\t\t\tforeach ( var kvp in current )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( !baseline.ContainsKey( kvp.Key ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tspawned.Add( new { Id = kvp.Key.ToString(), kvp.Value.Name, kvp.Value.Position } );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Detect destroyed\n\t\t\t\t\tforeach ( var kvp in baseline )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( !current.ContainsKey( kvp.Key ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdestroyed.Add( new { Id = kvp.Key.ToString(), kvp.Value.Name, kvp.Value.Position } );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Detect moved and modified\n\t\t\t\t\tforeach ( var kvp in current )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( baseline.TryGetValue( kvp.Key, out var baseObj ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar currObj = kvp.Value;\n\t\t\t\t\t\t\tvar posDelta = currObj.Position - baseObj.Position;\n\t\t\t\t\t\t\tvar displacement = posDelta.Length;\n\t\t\t\t\t\t\tvar angleDelta = new Angles(\n\t\t\t\t\t\t\t\tMath.Abs( currObj.Angles.pitch - baseObj.Angles.pitch ),\n\t\t\t\t\t\t\t\tMath.Abs( currObj.Angles.yaw - baseObj.Angles.yaw ),\n\t\t\t\t\t\t\t\tMath.Abs( currObj.Angles.roll - baseObj.Angles.roll ) );\n\n\t\t\t\t\t\t\tvar isMoved = displacement \u003E= positionThreshold;\n\t\t\t\t\t\t\tvar isRotated = angleDelta.pitch \u003E= rotationThreshold || angleDelta.yaw \u003E= rotationThreshold || angleDelta.roll \u003E= rotationThreshold;\n\n\t\t\t\t\t\t\tif ( isMoved || isRotated )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmoved.Add( new\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tId = kvp.Key.ToString(),\n\t\t\t\t\t\t\t\t\tcurrObj.Name,\n\t\t\t\t\t\t\t\t\tDisplacement = displacement,\n\t\t\t\t\t\t\t\t\tPositionDelta = posDelta,\n\t\t\t\t\t\t\t\t\tCurrentPosition = currObj.Position,\n\t\t\t\t\t\t\t\t\tBaselinePosition = baseObj.Position,\n\t\t\t\t\t\t\t\t\tRotated = isRotated,\n\t\t\t\t\t\t\t\t\tCurrentAngles = currObj.Angles\n\t\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Check component changes or enabled state\n\t\t\t\t\t\t\tif ( currObj.Enabled != baseObj.Enabled || !currObj.Components.Keys.SequenceEqual( baseObj.Components.Keys ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmodified.Add( new\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tId = kvp.Key.ToString(),\n\t\t\t\t\t\t\t\t\tcurrObj.Name,\n\t\t\t\t\t\t\t\t\tEnabledDelta = currObj.Enabled != baseObj.Enabled ? new { Before = baseObj.Enabled, After = currObj.Enabled } : null,\n\t\t\t\t\t\t\t\t\tAddedComponents = currObj.Components.Keys.Except( baseObj.Components.Keys ).ToArray(),\n\t\t\t\t\t\t\t\t\tRemovedComponents = baseObj.Components.Keys.Except( currObj.Components.Keys ).ToArray()\n\t\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t\t{\n\t\t\t\t\t\tTag = normTag,\n\t\t\t\t\t\tBaselineCount = baseline.Count,\n\t\t\t\t\t\tCurrentCount = current.Count,\n\t\t\t\t\t\tSpawnedCount = spawned.Count,\n\t\t\t\t\t\tDestroyedCount = destroyed.Count,\n\t\t\t\t\t\tMovedCount = moved.Count,\n\t\t\t\t\t\tModifiedCount = modified.Count,\n\t\t\t\t\t\tSpawned = spawned,\n\t\t\t\t\t\tDestroyed = destroyed,\n\t\t\t\t\t\tMoved = moved,\n\t\t\t\t\t\tModified = modified\n\t\t\t\t\t} ) );\n\t\t\t\t}\n\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022diff.action_invalid\u0022, $\u0022Unsupported action \u0027{action}\u0027. Use snapshot, compare, or clear.\u0022 ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022diff.failed\u0022, \u0022Scene diff failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\tprivate static Dictionary\u003CGuid, SceneObjectSnapshot\u003E CaptureSceneState( Scene scene )\n\t\t{\n\t\t\tvar dict = new Dictionary\u003CGuid, SceneObjectSnapshot\u003E();\n\t\t\tvar allObjects = scene.GetAllObjects( false );\n\t\t\tforeach ( var go in allObjects )\n\t\t\t{\n\t\t\t\tif ( !go.IsValid() ) continue;\n\t\t\t\tvar compDict = new Dictionary\u003Cstring, bool\u003E();\n\t\t\t\tforeach ( var c in go.Components.GetAll\u003CComponent\u003E() )\n\t\t\t\t{\n\t\t\t\t\tif ( c.IsValid() )\n\t\t\t\t\t{\n\t\t\t\t\t\tcompDict[c.GetType().Name] = c.Enabled;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdict[go.Id] = new SceneObjectSnapshot\n\t\t\t\t{\n\t\t\t\t\tId = go.Id,\n\t\t\t\t\tName = go.Name,\n\t\t\t\t\tParentId = go.Parent?.Id ?? Guid.Empty,\n\t\t\t\t\tEnabled = go.Enabled,\n\t\t\t\t\tPosition = go.WorldPosition,\n\t\t\t\t\tAngles = go.WorldRotation.Angles(),\n\t\t\t\t\tScale = go.WorldScale,\n\t\t\t\t\tComponents = compDict\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn dict;\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Deep Rubikon physics state inspection: inspects rigidbodies, velocities, mass, center of mass,\n\t\t/// connected joints (hinge, ball-socket, fixed), break status, ragdoll bone hierarchy, and contact points.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_physics_query\u0022 )]\n\t\tpublic static string PhysicsQuery(\n\t\t\t[Description( \u0022Target GameObject name or GUID\u0022 )] string target,\n\t\t\t[Description( \u0022Include physics joint inspection (Hinge, BallSocket, Fixed, etc.)\u0022 )] bool includeJoints = true,\n\t\t\t[Description( \u0022Include ragdoll bone hierarchy inspection if active\u0022 )] bool includeRagdollBones = true,\n\t\t\t[Description( \u0022Include contact and proximity collisions query\u0022 )] bool includeContacts = true )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar scene = ResolveActiveScene();\n\t\t\t\tif ( scene == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022scene.inactive\u0022, \u0022Active scene not found.\u0022 ) );\n\t\t\t\t}\n\n\n\t\t\t\tvar go = scene.Directory.FindByGuid( Guid.TryParse( target, out var guid ) ? guid : Guid.Empty )\n\t\t\t\t\t?? scene.GetAllObjects( false ).FirstOrDefault( o =\u003E string.Equals( o.Name, target, StringComparison.OrdinalIgnoreCase ) );\n\n\t\t\t\tif ( go == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( new { Target = target }, ToolStatus.Failed, \u0022target.not_found\u0022, $\u0022GameObject \u0027{target}\u0027 not found.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar rb = go.GetComponent\u003CRigidbody\u003E();\n\t\t\t\tobject? rbData = null;\n\t\t\t\tif ( rb != null )\n\t\t\t\t{\n\t\t\t\t\trbData = new\n\t\t\t\t\t{\n\t\t\t\t\t\trb.Mass,\n\t\t\t\t\t\trb.Velocity,\n\t\t\t\t\t\trb.AngularVelocity,\n\t\t\t\t\t\tSpeed = rb.Velocity.Length,\n\t\t\t\t\t\tAngularSpeed = rb.AngularVelocity.Length,\n\t\t\t\t\t\tIsStatic = rb.MotionEnabled == false,\n\t\t\t\t\t\tIsSleeping = rb.PhysicsBody?.Sleeping ?? false,\n\t\t\t\t\t\tWorldCenterOfMass = rb.PhysicsBody?.MassCenter ?? go.WorldPosition,\n\t\t\t\t\t\tLocalCenterOfMass = rb.PhysicsBody?.LocalMassCenter ?? Vector3.Zero\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Inspect joints\n\t\t\t\tvar jointsData = new List\u003Cobject\u003E();\n\t\t\t\tif ( includeJoints )\n\t\t\t\t{\n\t\t\t\t\tvar jointComps = go.Components.GetAll\u003CComponent\u003E()\n\t\t\t\t\t\t.Concat( go.GetAllObjects( false ).SelectMany( c =\u003E c.Components.GetAll\u003CComponent\u003E() ) )\n\t\t\t\t\t\t.Where( c =\u003E c.GetType().Name.Contains( \u0022Joint\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t\t.Distinct();\n\n\t\t\t\t\tforeach ( var j in jointComps )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar jt = j.GetType();\n\t\t\t\t\t\tjointsData.Add( new\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tType = jt.Name,\n\t\t\t\t\t\t\tGameObject = j.GameObject?.Name,\n\t\t\t\t\t\t\tId = j.Id.ToString(),\n\t\t\t\t\t\t\tj.Enabled,\n\t\t\t\t\t\t\tBody1 = SafeReadProp( j, \u0022Body1\u0022 ) ?? SafeReadProp( j, \u0022Point1\u0022 ),\n\t\t\t\t\t\t\tBody2 = SafeReadProp( j, \u0022Body2\u0022 ) ?? SafeReadProp( j, \u0022Point2\u0022 ),\n\t\t\t\t\t\t\tIsBroken = SafeReadProp( j, \u0022IsBroken\u0022 ) ?? \u0022false\u0022,\n\t\t\t\t\t\t\tBreakForce = SafeReadProp( j, \u0022BreakForce\u0022 ) ?? SafeReadProp( j, \u0022MaxForce\u0022 ),\n\t\t\t\t\t\t\tBreakTorque = SafeReadProp( j, \u0022BreakTorque\u0022 ) ?? SafeReadProp( j, \u0022MaxTorque\u0022 )\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Inspect ragdoll bones\n\t\t\t\tvar ragdollData = new List\u003Cobject\u003E();\n\t\t\t\tif ( includeRagdollBones )\n\t\t\t\t{\n\t\t\t\t\tvar boneRigidbodies = go.GetAllObjects( false )\n\t\t\t\t\t\t.Select( child =\u003E new { Child = child, Rb = child.GetComponent\u003CRigidbody\u003E() } )\n\t\t\t\t\t\t.Where( x =\u003E x.Rb != null )\n\t\t\t\t\t\t.ToArray();\n\n\t\t\t\t\tforeach ( var b in boneRigidbodies )\n\t\t\t\t\t{\n\t\t\t\t\t\tragdollData.Add( new\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBone = b.Child.Name,\n\t\t\t\t\t\t\tPosition = b.Child.WorldPosition,\n\t\t\t\t\t\t\tAngles = b.Child.WorldRotation.Angles(),\n\t\t\t\t\t\t\tVelocity = b.Rb!.Velocity,\n\t\t\t\t\t\t\tSpeed = b.Rb.Velocity.Length,\n\t\t\t\t\t\t\tb.Rb.AngularVelocity,\n\t\t\t\t\t\t\tb.Rb.Mass\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Inspect contacts / proximity collisions\n\t\t\t\tvar contactsData = new List\u003Cobject\u003E();\n\t\t\t\tif ( includeContacts \u0026\u0026 scene.PhysicsWorld != null )\n\t\t\t\t{\n\t\t\t\t\tvar center = go.WorldPosition;\n\t\t\t\t\tvar traceRadius = 50f;\n\t\t\t\t\tvar tr = scene.PhysicsWorld.Trace\n\t\t\t\t\t\t.Sphere( traceRadius, center, center \u002B Vector3.Down * 5f )\n\t\t\t\t\t\t.WithoutTags( \u0022trigger\u0022 )\n\t\t\t\t\t\t.Run();\n\n\t\t\t\t\tif ( tr.Hit )\n\t\t\t\t\t{\n\t\t\t\t\t\tcontactsData.Add( new\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tHitObject = tr.Body?.GameObject?.Name,\n\t\t\t\t\t\t\ttr.HitPosition,\n\t\t\t\t\t\t\ttr.Normal,\n\t\t\t\t\t\t\ttr.Distance,\n\t\t\t\t\t\t\tSurface = tr.Surface?.ResourceName\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tTarget = go.Name,\n\t\t\t\t\tId = go.Id.ToString(),\n\t\t\t\t\tWorldPosition = go.WorldPosition,\n\t\t\t\t\tWorldRotation = go.WorldRotation.Angles(),\n\t\t\t\t\tHasRigidbody = rb != null,\n\t\t\t\t\tRigidbody = rbData,\n\t\t\t\t\tJointCount = jointsData.Count,\n\t\t\t\t\tJoints = jointsData,\n\t\t\t\t\tRagdollBoneCount = ragdollData.Count,\n\t\t\t\t\tRagdollBones = ragdollData,\n\t\t\t\t\tContacts = contactsData\n\t\t\t\t} ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022physics_query.failed\u0022, \u0022Physics query failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\tprivate static string? SafeReadProp( object target, string propName )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar p = target.GetType().GetProperty( propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );\n\t\t\t\treturn p?.GetValue( target )?.ToString();\n\t\t\t}\n\t\t\tcatch { return null; }\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Simulates a 1v1 multi-controller combat exchange on a single client using local PlayerScopes.\n\t\t/// Drives player 1 actions (attack, punch, grab) and player 2 actions (evade, jump, counter),\n\t\t/// steps physics simulation, and measures displacement, knockback velocity, and knockout/ragdoll response.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_multi_combat_simulate\u0022 )]\n\t\tpublic static async Task\u003Cstring\u003E MultiCombatSimulate(\n\t\t\t[Description( \u0022Action for Player 1 (e.g. \u0027Attack1\u0027, \u0027Forward\u0027, \u0027Jump\u0027)\u0022 )] string player1Action,\n\t\t\t[Description( \u0022Action for Player 2 (e.g. \u0027Jump\u0027, \u0027Duck\u0027, \u0027Attack1\u0027, \u0027None\u0027)\u0022 )] string player2Action = \u0022None\u0022,\n\t\t\t[Description( \u0022Target GameObject name or Id for Player 1\u0022 )] string? player1Target = null,\n\t\t\t[Description( \u0022Target GameObject name or Id for Player 2\u0022 )] string? player2Target = null,\n\t\t\t[Description( \u0022Duration of the combat pulse in milliseconds (50-2000)\u0022 )] int durationMs = 300,\n\t\t\t[Description( \u0022Number of physics frames to step during simulation\u0022 )] int stepFrames = 18 )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif ( !Game.IsPlaying )\n\t\t\t\t{\n\t\t\t\t\tEditorRuntime.InvokeStatic( \u0022Editor.Mcp.PlayTools\u0022, \u0022PlayStart\u0022 );\n\t\t\t\t\tawait Task.Delay( 300 );\n\t\t\t\t}\n\n\t\t\t\tvar scene = Game.ActiveScene;\n\t\t\t\tif ( scene == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022scene.inactive\u0022, \u0022Active play scene not found.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tGameObject? p1Go = null;\n\t\t\t\tGameObject? p2Go = null;\n\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( player1Target ) )\n\t\t\t\t{\n\t\t\t\t\tp1Go = scene.GetAllObjects( false ).FirstOrDefault( o =\u003E string.Equals( o.Name, player1Target, StringComparison.OrdinalIgnoreCase ) );\n\t\t\t\t}\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( player2Target ) )\n\t\t\t\t{\n\t\t\t\t\tp2Go = scene.GetAllObjects( false ).FirstOrDefault( o =\u003E string.Equals( o.Name, player2Target, StringComparison.OrdinalIgnoreCase ) );\n\t\t\t\t}\n\n\t\t\t\tvar p1StartPos = p1Go?.WorldPosition ?? Vector3.Zero;\n\t\t\t\tvar p2StartPos = p2Go?.WorldPosition ?? Vector3.Zero;\n\t\t\t\tvar p1StartHealth = ReadHealth( p1Go );\n\t\t\t\tvar p2StartHealth = ReadHealth( p2Go );\n\n\t\t\t\t// Dispatch Player 1 input\n\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)0 ) )\n\t\t\t\t{\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( player1Action ) \u0026\u0026 !string.Equals( player1Action, \u0022None\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tawait SyntheticInputDriver.SimulateInputAsync( player1Action, \u0022down\u0022, durationMs );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Dispatch Player 2 input\n\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)1 ) )\n\t\t\t\t{\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( player2Action ) \u0026\u0026 !string.Equals( player2Action, \u0022None\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tawait SyntheticInputDriver.SimulateInputAsync( player2Action, \u0022down\u0022, durationMs );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Step simulation\n\t\t\t\tvar boundedFrames = Math.Clamp( stepFrames, 1, 120 );\n\t\t\t\tfor ( int i = 0; i \u003C boundedFrames; i\u002B\u002B )\n\t\t\t\t{\n\t\t\t\t\tscene.PhysicsWorld?.Step( 1.0f / 60.0f );\n\t\t\t\t\tawait Task.Delay( 16 );\n\t\t\t\t}\n\n\t\t\t\t// Release inputs\n\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)0 ) )\n\t\t\t\t{\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( player1Action ) \u0026\u0026 !string.Equals( player1Action, \u0022None\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tawait SyntheticInputDriver.SimulateInputAsync( player1Action, \u0022up\u0022, 0 );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)1 ) )\n\t\t\t\t{\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( player2Action ) \u0026\u0026 !string.Equals( player2Action, \u0022None\u0022, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tawait SyntheticInputDriver.SimulateInputAsync( player2Action, \u0022up\u0022, 0 );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait Task.Delay( 50 );\n\n\t\t\t\t// Measure post-combat outcomes\n\t\t\t\tvar p1EndPos = p1Go?.WorldPosition ?? Vector3.Zero;\n\t\t\t\tvar p2EndPos = p2Go?.WorldPosition ?? Vector3.Zero;\n\t\t\t\tvar p1Displacement = (p1EndPos - p1StartPos).Length;\n\t\t\t\tvar p2Displacement = (p2EndPos - p2StartPos).Length;\n\t\t\t\tvar p1Velocity = p1Go?.GetComponent\u003CRigidbody\u003E()?.Velocity ?? Vector3.Zero;\n\t\t\t\tvar p2Velocity = p2Go?.GetComponent\u003CRigidbody\u003E()?.Velocity ?? Vector3.Zero;\n\t\t\t\tvar p1EndHealth = ReadHealth( p1Go );\n\t\t\t\tvar p2EndHealth = ReadHealth( p2Go );\n\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tCombatSimulated = true,\n\t\t\t\t\tDurationMs = durationMs,\n\t\t\t\t\tFramesStepped = boundedFrames,\n\t\t\t\t\tPlayer1 = new\n\t\t\t\t\t{\n\t\t\t\t\t\tTarget = p1Go?.Name ?? \u0022Player1\u0022,\n\t\t\t\t\t\tAction = player1Action,\n\t\t\t\t\t\tDisplacement = p1Displacement,\n\t\t\t\t\t\tVelocity = p1Velocity,\n\t\t\t\t\t\tSpeed = p1Velocity.Length,\n\t\t\t\t\t\tHealthBefore = p1StartHealth,\n\t\t\t\t\t\tHealthAfter = p1EndHealth,\n\t\t\t\t\t\tHealthDelta = (p1StartHealth ?? 0) - (p1EndHealth ?? 0)\n\t\t\t\t\t},\n\t\t\t\t\tPlayer2 = new\n\t\t\t\t\t{\n\t\t\t\t\t\tTarget = p2Go?.Name ?? \u0022Player2\u0022,\n\t\t\t\t\t\tAction = player2Action,\n\t\t\t\t\t\tDisplacement = p2Displacement,\n\t\t\t\t\t\tVelocity = p2Velocity,\n\t\t\t\t\t\tSpeed = p2Velocity.Length,\n\t\t\t\t\t\tHealthBefore = p2StartHealth,\n\t\t\t\t\t\tHealthAfter = p2EndHealth,\n\t\t\t\t\t\tHealthDelta = (p2StartHealth ?? 0) - (p2EndHealth ?? 0)\n\t\t\t\t\t},\n\t\t\t\t\tKnockbackOccurred = p2Displacement \u003E 0.5f || p1Displacement \u003E 0.5f\n\t\t\t\t} ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022combat_simulate.failed\u0022, \u0022Multiplayer combat simulation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\tprivate static float? ReadHealth( GameObject? go )\n\t\t{\n\t\t\tif ( go == null || !go.IsValid() ) return null;\n\t\t\tforeach ( var c in go.Components.GetAll\u003CComponent\u003E() )\n\t\t\t{\n\t\t\t\tvar p = c.GetType().GetProperty( \u0022Health\u0022, BindingFlags.Public | BindingFlags.Instance );\n\t\t\t\tif ( p != null \u0026\u0026 p.CanRead )\n\t\t\t\t{\n\t\t\t\t\tvar val = p.GetValue( c );\n\t\t\t\t\tif ( val is float f ) return f;\n\t\t\t\t\tif ( val is int i ) return i;\n\t\t\t\t\tif ( val != null \u0026\u0026 float.TryParse( val.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var parsed ) ) return parsed;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Asserts runtime Razor UI state: locates panel by CSS selector or text, verifies visibility,\n\t\t/// inner text matches, CSS class presence, and element dimensions.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_ui_assert\u0022 )]\n\t\tpublic static string UiAssert(\n\t\t\t[Description( \u0022CSS selector (e.g. \u0027.mainmenu button.start\u0027) or text search\u0022 )] string selector,\n\t\t\t[Description( \u0022Optional text that the panel or its children must contain\u0022 )] string? expectedText = null,\n\t\t\t[Description( \u0022Optional CSS class that the panel must contain\u0022 )] string? expectedClass = null,\n\t\t\t[Description( \u0022Assert whether panel should be visible (true or false)\u0022 )] bool? shouldBeVisible = null,\n\t\t\t[Description( \u0022Minimum expected width in pixels\u0022 )] float? minWidth = null,\n\t\t\t[Description( \u0022Minimum expected height in pixels\u0022 )] float? minHeight = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif ( !Game.IsPlaying )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022ui.playmode_inactive\u0022, \u0022Play mode must be active to assert UI state.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar rootPanels = EditorUtility.GetRootPanels();\n\t\t\t\tif ( rootPanels == null || rootPanels.Count == 0 )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022ui.no_root_panels\u0022, \u0022No active root panels in current scene.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tPanel? targetPanel = null;\n\n\t\t\t\t// Try selector resolution first\n\t\t\t\tforeach ( var rootObj in rootPanels )\n\t\t\t\t{\n\t\t\t\t\tif ( rootObj is not Panel root || !root.IsValid() ) continue;\n\t\t\t\t\ttargetPanel = FindPanelRecursive( root, selector );\n\t\t\t\t\tif ( targetPanel != null ) break;\n\t\t\t\t}\n\n\t\t\t\t// If not found by CSS, check by text content\n\t\t\t\tif ( targetPanel == null )\n\t\t\t\t{\n\t\t\t\t\tforeach ( var rootObj in rootPanels )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( rootObj is not Panel root || !root.IsValid() ) continue;\n\t\t\t\t\t\ttargetPanel = root.Descendants.FirstOrDefault( p =\u003E\n\t\t\t\t\t\t\tp.IsValid() \u0026\u0026 (\n\t\t\t\t\t\t\t\t(p is Sandbox.UI.Label l \u0026\u0026 l.Text?.Contains( selector, StringComparison.OrdinalIgnoreCase ) == true) ||\n\t\t\t\t\t\t\t\tp.ElementName.Contains( selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t\t\t\t\tp.Id?.Contains( selector, StringComparison.OrdinalIgnoreCase ) == true\n\t\t\t\t\t\t\t) );\n\t\t\t\t\t\tif ( targetPanel != null ) break;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( targetPanel == null )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( new { Selector = selector }, ToolStatus.Failed, \u0022ui.panel_not_found\u0022, $\u0022UI Panel matching \u0027{selector}\u0027 was not found.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar failures = new List\u003Cstring\u003E();\n\t\t\t\tvar actualText = ExtractPanelText( targetPanel );\n\n\t\t\t\tif ( shouldBeVisible.HasValue \u0026\u0026 targetPanel.IsVisible != shouldBeVisible.Value )\n\t\t\t\t{\n\t\t\t\t\tfailures.Add( $\u0022Visibility mismatch: expected {shouldBeVisible.Value}, got {targetPanel.IsVisible}\u0022 );\n\t\t\t\t}\n\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( expectedText ) \u0026\u0026 !actualText.Contains( expectedText, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t{\n\t\t\t\t\tfailures.Add( $\u0022Text mismatch: expected to contain \u0027{expectedText}\u0027, but actual was \u0027{actualText}\u0027\u0022 );\n\t\t\t\t}\n\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( expectedClass ) \u0026\u0026 !targetPanel.HasClass( expectedClass ) )\n\t\t\t\t{\n\t\t\t\t\tfailures.Add( $\u0022Class mismatch: panel does not have CSS class \u0027{expectedClass}\u0027\u0022 );\n\t\t\t\t}\n\n\t\t\t\tvar rect = targetPanel.Box.Rect;\n\t\t\t\tif ( minWidth.HasValue \u0026\u0026 rect.Width \u003C minWidth.Value )\n\t\t\t\t{\n\t\t\t\t\tfailures.Add( $\u0022Width mismatch: expected \u003E= {minWidth.Value}px, actual was {rect.Width}px\u0022 );\n\t\t\t\t}\n\n\t\t\t\tif ( minHeight.HasValue \u0026\u0026 rect.Height \u003C minHeight.Value )\n\t\t\t\t{\n\t\t\t\t\tfailures.Add( $\u0022Height mismatch: expected \u003E= {minHeight.Value}px, actual was {rect.Height}px\u0022 );\n\t\t\t\t}\n\n\t\t\t\tvar passed = failures.Count == 0;\n\t\t\t\tvar result = new\n\t\t\t\t{\n\t\t\t\t\tPassed = passed,\n\t\t\t\t\tSelector = selector,\n\t\t\t\t\tPanelElement = targetPanel.ElementName,\n\t\t\t\t\tPanelId = targetPanel.Id,\n\t\t\t\t\tIsVisible = targetPanel.IsVisible,\n\t\t\t\t\tActualText = actualText,\n\t\t\t\t\tRect = new { rect.Left, rect.Top, rect.Width, rect.Height },\n\t\t\t\t\tFailures = failures\n\t\t\t\t};\n\n\t\t\t\treturn passed\n\t\t\t\t\t? McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( result ) )\n\t\t\t\t\t: McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( result, ToolStatus.Failed, \u0022ui.assertion_failed\u0022, string.Join( \u0022; \u0022, failures ) ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022ui_assert.failed\u0022, \u0022UI assertion failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\tprivate static Panel? FindPanelRecursive( Panel root, string selector )\n\t\t{\n\t\t\tif ( !root.IsValid() ) return null;\n\t\t\tif ( string.Equals( root.ElementName, selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t string.Equals( root.Id, selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t root.HasClass( selector.TrimStart( \u0027.\u0027 ) ) )\n\t\t\t{\n\t\t\t\treturn root;\n\t\t\t}\n\n\t\t\tforeach ( var child in root.Descendants )\n\t\t\t{\n\t\t\t\tif ( !child.IsValid() ) continue;\n\t\t\t\tif ( string.Equals( child.ElementName, selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t\t string.Equals( child.Id, selector, StringComparison.OrdinalIgnoreCase ) ||\n\t\t\t\t\t child.HasClass( selector.TrimStart( \u0027.\u0027 ) ) )\n\t\t\t\t{\n\t\t\t\t\treturn child;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate static string ExtractPanelText( Panel panel )\n\t\t{\n\t\t\tif ( panel is Sandbox.UI.Label label \u0026\u0026 !string.IsNullOrEmpty( label.Text ) ) return label.Text;\n\t\t\tif ( panel is TextEntry entry \u0026\u0026 !string.IsNullOrEmpty( entry.Text ) ) return entry.Text;\n\t\t\tvar childTexts = panel.Descendants.OfType\u003CSandbox.UI.Label\u003E().Select( l =\u003E l.Text ).Where( t =\u003E !string.IsNullOrEmpty( t ) );\n\t\t\treturn string.Join( \u0022 \u0022, childTexts );\n\t\t}\n\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Dispatches simultaneous multi-controller inputs for couch co-op / local multiplayer testing.\n\t\t/// Supports any number of players (P0, P1, P2, P3...) with actions, analog move, and analog look.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_multicontroller_input\u0022 )]\n\t\tpublic static async Task\u003Cstring\u003E MultiControllerInput(\n\t\t\t[Description( \u0022JSON array of player inputs: \u0027[{\\\u0022player\\\u0022: 0, \\\u0022action\\\u0022: \\\u0022Forward\\\u0022, \\\u0022move\\\u0022: \\\u00220,1\\\u0022}, {\\\u0022player\\\u0022: 1, \\\u0022action\\\u0022: \\\u0022Jump\\\u0022}]\u0027\u0022 )] string inputsJson,\n\t\t\t[Description( \u0022Duration of the input pulse in milliseconds (20-5000)\u0022 )] int durationMs = 150 )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif ( !Game.IsPlaying )\n\t\t\t\t{\n\t\t\t\t\tEditorRuntime.InvokeStatic( \u0022Editor.Mcp.PlayTools\u0022, \u0022PlayStart\u0022 );\n\t\t\t\t\tawait Task.Delay( 300 );\n\t\t\t\t}\n\n\t\t\t\tif ( string.IsNullOrWhiteSpace( inputsJson ) )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022input.json_required\u0022, \u0022inputsJson is required.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tJsonNode? node = null;\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tnode = JsonNode.Parse( inputsJson );\n\t\t\t\t}\n\t\t\t\tcatch\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022input.invalid_json\u0022, \u0022inputsJson must be a valid JSON array string.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tif ( node is not JsonArray array || array.Count == 0 )\n\t\t\t\t{\n\t\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022input.array_required\u0022, \u0022inputsJson must be a non-empty JSON array.\u0022 ) );\n\t\t\t\t}\n\n\t\t\t\tvar boundedDuration = Math.Clamp( durationMs, 20, 5000 );\n\t\t\t\tvar applied = new List\u003Cobject\u003E();\n\n\t\t\t\t// Apply inputs for each player\n\t\t\t\tforeach ( var item in array )\n\t\t\t\t{\n\t\t\t\t\tif ( item is not JsonObject obj ) continue;\n\t\t\t\t\tvar playerIndex = obj[\u0022player\u0022]?.GetValue\u003Cint\u003E() ?? 0;\n\t\t\t\t\tvar action = obj[\u0022action\u0022]?.GetValue\u003Cstring\u003E();\n\t\t\t\t\tvar moveStr = obj[\u0022move\u0022]?.GetValue\u003Cstring\u003E();\n\t\t\t\t\tvar lookStr = obj[\u0022look\u0022]?.GetValue\u003Cstring\u003E();\n\n\t\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)playerIndex ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( action ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSandbox.Input.SetAction( action, true );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( moveStr ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar parts = moveStr.Split( \u0027,\u0027 );\n\t\t\t\t\t\t\tif ( parts.Length == 2 \u0026\u0026 float.TryParse( parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var mx ) \u0026\u0026 float.TryParse( parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var my ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSandbox.Input.AnalogMove = new Vector3( mx, my, 0 );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( lookStr ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar parts = lookStr.Split( \u0027,\u0027 );\n\t\t\t\t\t\t\tif ( parts.Length == 2 \u0026\u0026 float.TryParse( parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var lx ) \u0026\u0026 float.TryParse( parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var ly ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSandbox.Input.AnalogLook = new Angles( lx, ly, 0 );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tapplied.Add( new { PlayerIndex = playerIndex, Action = action, Move = moveStr, Look = lookStr } );\n\t\t\t\t}\n\n\t\t\t\t// Wait pulse duration\n\t\t\t\tawait Task.Delay( boundedDuration );\n\n\t\t\t\t// Release inputs for each player\n\t\t\t\tforeach ( var item in array )\n\t\t\t\t{\n\t\t\t\t\tif ( item is not JsonObject obj ) continue;\n\t\t\t\t\tvar playerIndex = obj[\u0022player\u0022]?.GetValue\u003Cint\u003E() ?? 0;\n\t\t\t\t\tvar action = obj[\u0022action\u0022]?.GetValue\u003Cstring\u003E();\n\n\t\t\t\t\tusing ( Sandbox.Input.PlayerScope( (Sandbox.Input.PlayerIndex)playerIndex ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( action ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSandbox.Input.SetAction( action, false );\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSandbox.Input.AnalogMove = Vector3.Zero;\n\t\t\t\t\t\tSandbox.Input.AnalogLook = Angles.Zero;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tSimulatedPlayers = applied.Count,\n\t\t\t\t\tDurationMs = boundedDuration,\n\t\t\t\t\tCommands = applied,\n\t\t\t\t\tSuccess = true\n\t\t\t\t} ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022multicontroller.failed\u0022, \u0022Multi-controller input simulation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/CodeAwarenessTools.cs","FileName":"CodeAwarenessTools.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text.Json;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool(\u0022pocketknife_find_type\u0022)]\n\t\tpublic static string FindType([Description(\u0022Type name or fragment\u0022)] string name, [Description(\u0022Maximum results\u0022)] int limit = 20)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar items = AppDomain.CurrentDomain.GetAssemblies().SelectMany(SafeTypes).Where(t =\u003E t.Name.Contains(name ?? \u0022\u0022, StringComparison.OrdinalIgnoreCase) || (t.FullName?.Contains(name ?? \u0022\u0022, StringComparison.OrdinalIgnoreCase) ?? false)).Take(Math.Clamp(limit, 1, 100)).Select(t =\u003E new { FullName = t.FullName, Name = t.Name, Kind = t.IsEnum ? \u0022Enum\u0022 : t.IsClass ? \u0022Class\u0022 : t.IsValueType ? \u0022Struct\u0022 : \u0022Interface\u0022, Base = t.BaseType?.FullName }).ToArray();\n\t\t\t\treturn McpJson.Write(ToolOutcome\u003Cobject\u003E.Ready(new { Query = name, Count = items.Length, Items = items }));\n\t\t\t}\n\t\t\tcatch (Exception ex) { return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Failed, \u0022type.search_failed\u0022, \u0022Type search failed.\u0022, ex)); }\n\t\t}\n\n\t\t[McpTool(\u0022pocketknife_type_members\u0022)]\n\t\tpublic static string TypeMembers([Description(\u0022Exact or short type name\u0022)] string typeName)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(SafeTypes).FirstOrDefault(t =\u003E t.FullName == typeName || t.Name == typeName); if (type is null) return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Degraded, \u0022type.not_found\u0022, $\u0022Type \u0027{typeName}\u0027 was not found.\u0022));\n\t\t\t\tvar properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Take(200).Select(p =\u003E new { p.Name, Type = p.PropertyType.FullName, CanRead = p.CanRead, CanWrite = p.CanWrite });\n\t\t\t\tvar methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(m =\u003E !m.IsSpecialName).Take(300).Select(m =\u003E new { m.Name, ReturnType = m.ReturnType.FullName, Parameters = m.GetParameters().Select(p =\u003E new { p.Name, Type = p.ParameterType.FullName }).ToArray() });\n\t\t\t\tvar constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance).Select(c =\u003E new { Parameters = c.GetParameters().Select(p =\u003E new { p.Name, Type = p.ParameterType.FullName }).ToArray() });\n\t\t\t\treturn McpJson.Write(ToolOutcome\u003Cobject\u003E.Ready(new { Type = type.FullName, Properties = properties, Methods = methods, Constructors = constructors }));\n\t\t\t}\n\t\t\tcatch (Exception ex) { return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Failed, \u0022type.members_failed\u0022, \u0022Type member inspection failed.\u0022, ex)); }\n\t\t}\n\n\t\t[McpTool(\u0022pocketknife_compile_await\u0022)]\n\t\tpublic static string CompileAwait(string scope = \u0022project\u0022, int timeoutMs = 120000)\n\t\t{\n\t\t\tvar normalizedScope = (scope ?? \u0022\u0022).Trim().ToLowerInvariant();\n\t\t\tif (normalizedScope != \u0022project\u0022)\n\t\t\t\treturn McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(new { Scope = scope }, ToolStatus.Failed, \u0022compile.invalid_scope\u0022, \u0022Only project compile scope is supported.\u0022));\n\n\t\t\tvar boundedTimeout = Math.Clamp(timeoutMs, 1000, 120000);\n\t\t\tvar started = DateTime.UtcNow;\n\t\t\tvar baselineState = EditorRuntime.InvokeStatic(\u0022Editor.Mcp.EditorTools\u0022, \u0022CompileStatus\u0022);\n\t\t\tvar baselineRevision = CompilerRevision(baselineState);\n\t\t\tCompilerIsSettled(baselineState, out var baselineDiagnostics);\n\t\t\t// Official MCP owns compile dispatch; this method only waits for compiler readback.\n\n\t\t\tobject? observedState = null;\n\t\t\tList\u003Cstring\u003E observedDiagnostics = new();\n\t\t\tvar deadline = started.AddMilliseconds(boundedTimeout);\n\t\t\tvar settled = false;\n\t\t\twhile (DateTime.UtcNow \u003C deadline)\n\t\t\t{\n\t\t\t\tobservedState = EditorRuntime.InvokeStatic(\u0022Editor.Mcp.EditorTools\u0022, \u0022CompileStatus\u0022);\n\t\t\t\tsettled = CompilerIsSettled(observedState, out observedDiagnostics);\n\t\t\t\tif (settled) break;\n\t\t\t\tSystem.Threading.Thread.Sleep(100);\n\t\t\t}\n\n\t\t\tvar observedRevision = CompilerRevision(observedState);\n\t\t\tvar revisionChanged = !string.Equals(baselineRevision, observedRevision, StringComparison.Ordinal);\n\t\t\tvar postconditionVerified = settled;\n\t\t\tvar value = new\n\t\t\t{\n\t\t\t\tScope = normalizedScope,\n\t\t\t\tTimeoutMs = boundedTimeout,\n\t\t\t\tBaselineRevision = baselineRevision,\n\t\t\t\tObservedRevision = observedRevision,\n\t\t\t\tRevisionChanged = revisionChanged,\n\t\t\t\tElapsedMs = (long)(DateTime.UtcNow - started).TotalMilliseconds,\n\t\t\t\tDiagnostics = observedDiagnostics.Count \u003E 0 ? observedDiagnostics : baselineDiagnostics,\n\t\t\t\tPostconditionVerified = postconditionVerified\n\t\t\t};\n\t\t\treturn postconditionVerified\n\t\t\t\t? McpJson.Write(ToolOutcome\u003Cobject\u003E.Ready(value))\n\t\t\t\t: McpJson.Write(ToolOutcome\u003Cobject\u003E.Degraded(value, \u0022compile.await_timeout\u0022, \u0022Compiler freshness did not reach a verified settled state before timeout.\u0022));\n\t\t}\n\n\t\tprivate static bool CompilerIsSettled(object? state, out List\u003Cstring\u003E diagnostics)\n\t\t{\n\t\t\tdiagnostics = new List\u003Cstring\u003E();\n\t\t\tif (state is null || (EditorRuntime.GetMember(state, \u0022IsBuilding\u0022) is bool building \u0026\u0026 building)) return false;\n\t\t\tvar compilers = EditorRuntime.GetMember(state, \u0022Compilers\u0022) as System.Collections.IEnumerable;\n\t\t\tif (compilers is null) return false;\n\t\t\tvar sawCompiler = false;\n\t\t\tforeach (var compiler in compilers)\n\t\t\t{\n\t\t\t\tsawCompiler = true;\n\t\t\t\tif (EditorRuntime.GetMember(compiler, \u0022IsBuilding\u0022) is bool compilerBuilding \u0026\u0026 compilerBuilding) return false;\n\t\t\t\tif (EditorRuntime.GetMember(compiler, \u0022NeedsBuild\u0022) is bool needsBuild \u0026\u0026 needsBuild) return false;\n\t\t\t\tif (EditorRuntime.GetMember(compiler, \u0022Success\u0022) is bool success \u0026\u0026 !success) return false;\n\t\t\t\tif (EditorRuntime.GetMember(compiler, \u0022Errors\u0022) is int errors \u0026\u0026 errors \u003E 0) return false;\n\t\t\t\tvar name = EditorRuntime.GetMember(compiler, \u0022Name\u0022)?.ToString() ?? \u0022compiler\u0022;\n\t\t\t\tvar warningCount = EditorRuntime.GetMember(compiler, \u0022Warnings\u0022)?.ToString() ?? \u00220\u0022;\n\t\t\t\tdiagnostics.Add($\u0022{name}: warnings={warningCount}\u0022);\n\t\t\t}\n\t\t\treturn sawCompiler;\n\t\t}\n\n\t\tprivate static string CompilerRevision(object? state)\n\t\t{\n\t\t\tvar assembly = typeof(SboxMcpAssistant).Assembly;\n\t\t\tvar location = assembly.Location ?? \u0022\u0022;\n\t\t\tvar fileStamp = File.Exists(location) ? $\u0022{new FileInfo(location).Length}:{File.GetLastWriteTimeUtc(location).Ticks}\u0022 : \u0022memory\u0022;\n\t\t\tvar parts = new List\u003Cstring\u003E { location, fileStamp, EditorRuntime.GetMember(state, \u0022IsBuilding\u0022)?.ToString() ?? \u0022unknown\u0022 };\n\t\t\tif (EditorRuntime.GetMember(state, \u0022Compilers\u0022) is System.Collections.IEnumerable compilers)\n\t\t\t\tforeach (var compiler in compilers) parts.Add(string.Join(\u0022:\u0022, EditorRuntime.GetMember(compiler, \u0022Name\u0022), EditorRuntime.GetMember(compiler, \u0022NeedsBuild\u0022), EditorRuntime.GetMember(compiler, \u0022Success\u0022), EditorRuntime.GetMember(compiler, \u0022Errors\u0022)));\n\t\t\treturn string.Join(\u0022|\u0022, parts);\n\t\t}\n\n\t\t[McpTool(\u0022pocketknife_package_references\u0022)]\n\t\tpublic static string PackageReferences()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar root = Project.Current?.GetRootPath(); if (string.IsNullOrWhiteSpace(root)) return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Unavailable, \u0022project.not_active\u0022, \u0022No active project is available.\u0022)); var sbproj = Directory.EnumerateFiles(root, \u0022*.sbproj\u0022, SearchOption.TopDirectoryOnly).FirstOrDefault(); if (sbproj is null) return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Unavailable, \u0022project.sbproj_missing\u0022, \u0022No .sbproj was found in the active project root.\u0022));\n\t\t\t\tusing var doc = JsonDocument.Parse(File.ReadAllText(sbproj)); var refs = doc.RootElement.TryGetProperty(\u0022PackageReferences\u0022, out var p) \u0026\u0026 p.ValueKind == JsonValueKind.Array ? p.EnumerateArray().Select(x =\u003E x.GetString() ?? x.ToString()).ToArray() : Array.Empty\u003Cstring\u003E(); return McpJson.Write(ToolOutcome\u003Cobject\u003E.Ready(new { ProjectFile = sbproj, PackageReferences = refs, Count = refs.Length }));\n\t\t\t}\n\t\t\tcatch (Exception ex) { return McpJson.Write(ToolOutcome\u003Cobject\u003E.Failure(null, ToolStatus.Failed, \u0022project.references_failed\u0022, \u0022Package reference parsing failed.\u0022, ex)); }\n\t\t}\n\n\t\tprivate static IEnumerable\u003CType\u003E SafeTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch { return Array.Empty\u003CType\u003E(); } }\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/WorkflowTools.AudioAndChat.cs","FileName":"WorkflowTools.AudioAndChat.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t/// \u003Csummary\u003E\n\t\t/// Injects synthesized voice audio tones or PCM audio from WAV files directly into the client\u0027s\n\t\t/// microphone voice stream (Sandbox.SoundStream) for testing voice transmission, mute toggles,\n\t\t/// volume levels, and channel isolation (Team, Party, Proximity).\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_voice_stream_inject\u0022 )]\n\t\tpublic static string VoiceStreamInject(\n\t\t\t[Description( \u0022Audio generation mode: \u0027tone\u0027 (synthesized sine wave), \u0027wav_file\u0027, \u0027silence\u0027\u0022 )] string mode = \u0022tone\u0022,\n\t\t\t[Description( \u0022Playback duration in seconds (0.1 to 10.0, default 1.0)\u0022 )] float durationSeconds = 1.0f,\n\t\t\t[Description( \u0022Audio sample rate (8000-48000, default 44100)\u0022 )] int sampleRate = 44100,\n\t\t\t[Description( \u0022Tone frequency in Hz (20-10000, default 440 for A4)\u0022 )] float frequencyHz = 440f,\n\t\t\t[Description( \u0022Path to WAV audio file on disk (for wav_file mode)\u0022 )] string? filePath = null,\n\t\t\t[Description( \u0022Audio volume scale (0.0 to 2.0, default 1.0)\u0022 )] float volume = 1.0f,\n\t\t\t[Description( \u0022Audio pitch scale (0.1 to 2.0, default 1.0)\u0022 )] float pitch = 1.0f,\n\t\t\t[Description( \u0022Voice channel tag: \u0027all\u0027, \u0027team\u0027, \u0027party\u0027, \u0027traitor\u0027\u0022 )] string channel = \u0022all\u0022,\n\t\t\t[Description( \u0022Whether client microphone is muted\u0022 )] bool mute = false )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar result = AudioAndChatDriver.InjectVoiceStream(\n\t\t\t\t\tmode,\n\t\t\t\t\tdurationSeconds,\n\t\t\t\t\tsampleRate,\n\t\t\t\t\tfrequencyHz,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tvolume,\n\t\t\t\t\tpitch,\n\t\t\t\t\tchannel,\n\t\t\t\t\tmute\n\t\t\t\t);\n\t\t\t\treturn McpJson.Write( result );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022voice.inject_failed\u0022, \u0022Voice stream injection failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Probes 3D spatial audio acoustics, room occlusion, distance falloff curves, and facial morph\n\t\t/// lip-sync visemes from a sound emitter attached to a character\u0027s mouth/head bone.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_spatial_audio_probe\u0022 )]\n\t\tpublic static string SpatialAudioProbe(\n\t\t\t[Description( \u0022Target sound emitter GameObject name or GUID\u0022 )] string target,\n\t\t\t[Description( \u0022SoundEvent or sound file to trigger (optional)\u0022 )] string? soundEventOrFile = null,\n\t\t\t[Description( \u0022Volume multiplier (0.0 to 2.0, default 1.0)\u0022 )] float volume = 1.0f,\n\t\t\t[Description( \u0022Maximum audible distance in units (default 1000)\u0022 )] float maxDistance = 1000f,\n\t\t\t[Description( \u0022Whether to test intervening geometry wall occlusion\u0022 )] bool testOcclusion = true,\n\t\t\t[Description( \u0022Listener GameObject name (defaults to main camera if omitted)\u0022 )] string? listenerTarget = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar result = AudioAndChatDriver.ProbeSpatialAudio(\n\t\t\t\t\ttarget,\n\t\t\t\t\tsoundEventOrFile,\n\t\t\t\t\tvolume,\n\t\t\t\t\tmaxDistance,\n\t\t\t\t\ttestOcclusion,\n\t\t\t\t\tlistenerTarget\n\t\t\t\t);\n\t\t\t\treturn McpJson.Write( result );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022audio.probe_failed\u0022, \u0022Spatial audio probe failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\n\t\t/// \u003Csummary\u003E\n\t\t/// Simulates text chat: injects player chat messages (Chat.Say), broadcasts server/system announcements\n\t\t/// (Chat.AddText), tests channel isolation, and validates moderation/profanity suppression filters.\n\t\t/// \u003C/summary\u003E\n\t\t[McpTool( \u0022pocketknife_chat_simulate\u0022 )]\n\t\tpublic static string ChatSimulate(\n\t\t\t[Description( \u0022Action: \u0027say\u0027 (player chat), \u0027announcement\u0027 (system text), \u0027test_filter\u0027 (moderation)\u0022 )] string action = \u0022say\u0022,\n\t\t\t[Description( \u0022Message text content\u0022 )] string message = \u0022Hello from MCP test harness\u0022,\n\t\t\t[Description( \u0022Chat channel: \u0027all\u0027, \u0027team\u0027, \u0027party\u0027, \u0027whisper\u0027\u0022 )] string channel = \u0022all\u0022,\n\t\t\t[Description( \u0022Custom filter word to test for suppression\u0022 )] string? filterWord = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tvar result = AudioAndChatDriver.SimulateChat( action, message, channel, filterWord );\n\t\t\t\treturn McpJson.Write( result );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022chat.simulate_failed\u0022, \u0022Chat simulation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Tools/codesearch-worker/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css","FileName":"index.B_TqY17P.css","PackageType":"library","CodeKind":"Game","AssetVersionId":422385,"IsPrivate":false,"Code":".drop-target{background-color:var(--vscode-editor-background);z-index:100;flex-direction:column;flex:auto;justify-content:center;align-items:center;line-height:24px;display:flex;position:absolute;inset:0}body .drop-target{background:#fffc}:root.dark-mode .drop-target{background:#000c}.drop-target .title{margin-bottom:30px;font-size:24px;font-weight:700}.drop-target .info{text-align:center;max-width:400px}.drop-target .processing-error{color:#e74c3c;text-align:center;white-space:pre-line;margin:30px;font-size:24px;font-weight:700}.drop-target input{margin-top:50px}.drop-target button{color:#fff;cursor:pointer;background-color:#007acc;border:none;margin:30px 0;padding:8px 12px}.drop-target .version{color:var(--vscode-disabledForeground);margin-top:8px}.progress-dialog{background-color:var(--vscode-sideBar-background);border:none;outline:none;width:400px;inset:0}.progress-dialog::backdrop{background-color:#0006}.progress-content{padding:16px}.progress-content .title{background-color:unset;padding:0;font-size:18px;font-weight:700}.progress-wrapper{background-color:var(--vscode-commandCenter-activeBackground);width:100%;margin-top:16px;margin-bottom:8px}.inner-progress{background-color:var(--vscode-progressBar-background);height:4px}.workbench-loader-header{background-color:var(--vscode-sideBar-background);box-shadow:var(--vscode-scrollbar-shadow) 0 6px 6px -6px;flex:0 0 48px;align-items:center;font-size:16px;line-height:48px;display:flex}.workbench-loader{contain:size}.workbench-loader .workbench-loader-header{flex-basis:32px;font-size:13px;line-height:32px}.workbench-loader .workbench-loader-header .toolbar-button{margin:4px}.workbench-loader .logo{align-items:center;margin-left:16px;display:flex}.workbench-loader .logo img{pointer-events:none;flex:none;width:32px;height:32px}.workbench-loader .product{flex:none;margin-left:16px;font-weight:600}.workbench-loader .workbench-loader-header .title{text-overflow:ellipsis;text-wrap:nowrap;margin-left:16px;overflow:hidden}html,body{min-width:550px;min-height:450px;overflow:auto}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Tools/codesearch-worker/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css","FileName":"xtermModule.kHJ-D0s7.css","PackageType":"library","CodeKind":"Game","AssetVersionId":422385,"IsPrivate":false,"Code":".xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/MultiViewSnapshotDriver.cs","FileName":"MultiViewSnapshotDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text.Json;\nusing System.Text.Json.Nodes;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class MultiViewSnapshotDriver\n\t{\n\t\tprivate static Scene? ResolveScene()\n\t\t{\n\t\t\tif ( Game.ActiveScene != null ) return Game.ActiveScene;\n\t\t\tvar session = EditorRuntime.GetStaticProperty( \u0022Editor.SceneEditorSession\u0022, \u0022Active\u0022 );\n\t\t\tif ( session != null )\n\t\t\t{\n\t\t\t\treturn EditorRuntime.GetMember( session, \u0022Scene\u0022 ) as Scene;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic struct ViewportSpec\n\t\t{\n\t\t\tpublic string Label;\n\t\t\tpublic string TargetName;\n\t\t\tpublic string Preset;\n\t\t\tpublic Rect Rect;\n\t\t}\n\n\t\tpublic static ToolOutcome\u003Cobject\u003E CaptureMultiView(\n\t\t\tstring layout = \u0022quad_4view\u0022,\n\t\t\tstring? primaryTarget = null,\n\t\t\tstring? v0Target = null,\n\t\t\tstring? v0Preset = null,\n\t\t\tstring? v1Target = null,\n\t\t\tstring? v1Preset = null,\n\t\t\tstring? v2Target = null,\n\t\t\tstring? v2Preset = null,\n\t\t\tstring? v3Target = null,\n\t\t\tstring? v3Preset = null,\n\t\t\tint totalWidth = 1024,\n\t\t\tint totalHeight = 1024,\n\t\t\tstring? fileName = null,\n\t\t\tbool includeUi = false )\n\t\t{\n\t\t\tvar scene = ResolveScene();\n\t\t\tif ( scene == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022multiview.no_scene\u0022, \u0022No active scene loaded.\u0022 );\n\t\t\t}\n\n\t\t\tvar normLayout = layout.ToLowerInvariant().Trim();\n\t\t\tvar w = Math.Clamp( totalWidth, 256, 4096 );\n\t\t\tvar h = Math.Clamp( totalHeight, 256, 4096 );\n\n\t\t\tvar specs = new List\u003CViewportSpec\u003E();\n\n\t\t\tif ( normLayout == \u0022split_vertical\u0022 )\n\t\t\t{\n\t\t\t\tvar halfW = w / 2f;\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022LEFT [P1 / View 0]\u0022,\n\t\t\t\t\tTargetName = v0Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v0Preset ?? \u0022fps\u0022,\n\t\t\t\t\tRect = new Rect( 0, 0, halfW, h )\n\t\t\t\t} );\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022RIGHT [P2 / View 1]\u0022,\n\t\t\t\t\tTargetName = v1Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v1Preset ?? \u0022third_person\u0022,\n\t\t\t\t\tRect = new Rect( halfW, 0, halfW, h )\n\t\t\t\t} );\n\t\t\t}\n\t\t\telse if ( normLayout == \u0022split_horizontal\u0022 )\n\t\t\t{\n\t\t\t\tvar halfH = h / 2f;\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022TOP [P1 / View 0]\u0022,\n\t\t\t\t\tTargetName = v0Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v0Preset ?? \u0022fps\u0022,\n\t\t\t\t\tRect = new Rect( 0, 0, w, halfH )\n\t\t\t\t} );\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022BOTTOM [P2 / View 1]\u0022,\n\t\t\t\t\tTargetName = v1Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v1Preset ?? \u0022third_person\u0022,\n\t\t\t\t\tRect = new Rect( 0, halfH, w, halfH )\n\t\t\t\t} );\n\t\t\t}\n\t\t\telse // quad_4view (default)\n\t\t\t{\n\t\t\t\tnormLayout = \u0022quad_4view\u0022;\n\t\t\t\tvar halfW = w / 2f;\n\t\t\t\tvar halfH = h / 2f;\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022TOP-LEFT [FPS / Target POV]\u0022,\n\t\t\t\t\tTargetName = v0Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v0Preset ?? \u0022fps\u0022,\n\t\t\t\t\tRect = new Rect( 0, 0, halfW, halfH )\n\t\t\t\t} );\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022TOP-RIGHT [Third-Person Chase]\u0022,\n\t\t\t\t\tTargetName = v1Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v1Preset ?? \u0022third_person\u0022,\n\t\t\t\t\tRect = new Rect( halfW, 0, halfW, halfH )\n\t\t\t\t} );\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022BOTTOM-LEFT [Isometric ARPG]\u0022,\n\t\t\t\t\tTargetName = v2Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v2Preset ?? \u0022isometric\u0022,\n\t\t\t\t\tRect = new Rect( 0, halfH, halfW, halfH )\n\t\t\t\t} );\n\t\t\t\tspecs.Add( new ViewportSpec\n\t\t\t\t{\n\t\t\t\t\tLabel = \u0022BOTTOM-RIGHT [Tactical RTS / Overhead]\u0022,\n\t\t\t\t\tTargetName = v3Target ?? primaryTarget ?? \u0022Camera\u0022,\n\t\t\t\t\tPreset = v3Preset ?? \u0022rts\u0022,\n\t\t\t\t\tRect = new Rect( halfW, halfH, halfW, halfH )\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\t// Composite Bitmap Canvas\n\t\t\tvar canvas = new Sandbox.Bitmap( w, h );\n\t\t\tcanvas.Clear( new Color( 0.08f, 0.08f, 0.10f, 1f ) );\n\n\t\t\tvar capturedViewports = new List\u003Cobject\u003E();\n\t\t\tvar temporaryCameras = new List\u003CGameObject\u003E();\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor ( int i = 0; i \u003C specs.Count; i\u002B\u002B )\n\t\t\t\t{\n\t\t\t\t\tvar spec = specs[i];\n\t\t\t\t\tvar go = scene.Directory.FindByGuid( Guid.TryParse( spec.TargetName, out var guid ) ? guid : Guid.Empty )\n\t\t\t\t\t\t?? scene.GetAllObjects( false ).FirstOrDefault( o =\u003E string.Equals( o.Name, spec.TargetName, StringComparison.OrdinalIgnoreCase ) )\n\t\t\t\t\t\t?? scene.GetAllObjects( false ).FirstOrDefault( o =\u003E o.GetComponent\u003CCameraComponent\u003E() != null );\n\n\t\t\t\t\tvar center = go?.WorldPosition ?? Vector3.Zero;\n\t\t\t\t\tvar forward = go?.WorldRotation.Forward ?? Vector3.Forward;\n\t\t\t\t\tvar right = go?.WorldRotation.Right ?? Vector3.Right;\n\t\t\t\t\tvar up = Vector3.Up;\n\n\t\t\t\t\tVector3 camPos;\n\t\t\t\t\tRotation camRot;\n\t\t\t\t\tfloat camFov = 80f;\n\n\t\t\t\t\tswitch ( spec.Preset.ToLowerInvariant().Trim() )\n\t\t\t\t\t{\n\t\t\t\t\t\tcase \u0022fps\u0022:\n\t\t\t\t\t\t\tcamPos = center \u002B up * 64f \u002B forward * 6f;\n\t\t\t\t\t\t\tcamRot = go?.WorldRotation ?? Rotation.Identity;\n\t\t\t\t\t\t\tcamFov = 90f;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \u0022third_person\u0022:\n\t\t\t\t\t\tcase \u0022tps\u0022:\n\t\t\t\t\t\t\tcamPos = center \u002B (forward * -85f) \u002B (right * 22f) \u002B (up * 55f);\n\t\t\t\t\t\t\tcamRot = Rotation.LookAt( (center \u002B up * 42f) - camPos );\n\t\t\t\t\t\t\tcamFov = 75f;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \u0022rts\u0022:\n\t\t\t\t\t\tcase \u0022overhead\u0022:\n\t\t\t\t\t\t\tcamPos = center \u002B new Vector3( 0f, -160f, 380f );\n\t\t\t\t\t\t\tcamRot = Rotation.LookAt( center - camPos );\n\t\t\t\t\t\t\tcamFov = 65f;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \u0022isometric\u0022:\n\t\t\t\t\t\tcase \u0022dimetric\u0022:\n\t\t\t\t\t\t\t// Pitch ~35.264\u00B0, Yaw 45\u00B0\n\t\t\t\t\t\t\tvar isoRadPitch = (float)(35.264 * Math.PI / 180.0);\n\t\t\t\t\t\t\tvar isoRadYaw = (float)(45.0 * Math.PI / 180.0);\n\t\t\t\t\t\t\tvar isoDist = 320f;\n\t\t\t\t\t\t\tvar ox = isoDist * (float)Math.Cos( isoRadPitch ) * (float)Math.Sin( isoRadYaw );\n\t\t\t\t\t\t\tvar oy = isoDist * (float)Math.Cos( isoRadPitch ) * (float)Math.Cos( isoRadYaw );\n\t\t\t\t\t\t\tvar oz = isoDist * (float)Math.Sin( isoRadPitch );\n\t\t\t\t\t\t\tcamPos = center \u002B new Vector3( ox, oy, oz );\n\t\t\t\t\t\t\tcamRot = Rotation.LookAt( center - camPos );\n\t\t\t\t\t\t\tcamFov = 45f;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \u0022sidescroller\u0022:\n\t\t\t\t\t\tcase \u00222.5d\u0022:\n\t\t\t\t\t\t\tcamPos = center \u002B new Vector3( 0f, -320f, 35f );\n\t\t\t\t\t\t\tcamRot = Rotation.LookAt( (center \u002B up * 30f) - camPos );\n\t\t\t\t\t\t\tcamFov = 60f;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault: // front / default\n\t\t\t\t\t\t\tcamPos = center \u002B forward * 160f \u002B up * 40f;\n\t\t\t\t\t\t\tcamRot = Rotation.LookAt( center - camPos );\n\t\t\t\t\t\t\tcamFov = 75f;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar camGo = scene.CreateObject( true );\n\t\t\t\t\tcamGo.Name = $\u0022__temp_multiview_cam_{i}__\u0022;\n\t\t\t\t\tcamGo.WorldPosition = camPos;\n\t\t\t\t\tcamGo.WorldRotation = camRot;\n\t\t\t\t\tvar cam = camGo.Components.Create\u003CCameraComponent\u003E();\n\t\t\t\t\tcam.FieldOfView = camFov;\n\t\t\t\t\ttemporaryCameras.Add( camGo );\n\n\t\t\t\t\tvar subW = (int)spec.Rect.Width;\n\t\t\t\t\tvar subH = (int)spec.Rect.Height;\n\n\t\t\t\t\tvar screenshotResult = EditorRuntime.InvokeStatic( \u0022Editor.Mcp.SceneTools\u0022, \u0022CameraScreenshot\u0022, cam.Id.ToString(), subW, subH, includeUi );\n\n\t\t\t\t\tif ( screenshotResult is Sandbox.Bitmap subBmp )\n\t\t\t\t\t{\n\t\t\t\t\t\tcanvas.DrawBitmap( subBmp, spec.Rect );\n\t\t\t\t\t\tsubBmp.Dispose();\n\t\t\t\t\t}\n\t\t\t\t\telse if ( screenshotResult != null )\n\t\t\t\t\t{\n\t\t\t\t\t\tvar json = JsonSerializer.Serialize( screenshotResult );\n\t\t\t\t\t\tvar node = JsonNode.Parse( json );\n\t\t\t\t\t\tvar base64Data = (node as JsonArray)?[0]?[\u0022data\u0022]?.GetValue\u003Cstring\u003E() ?? (node as JsonObject)?[\u0022data\u0022]?.GetValue\u003Cstring\u003E();\n\t\t\t\t\t\tif ( !string.IsNullOrEmpty( base64Data ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar bytes = Convert.FromBase64String( base64Data );\n\t\t\t\t\t\t\tvar importedBmp = Sandbox.Bitmap.CreateFromBytes( bytes );\n\t\t\t\t\t\t\tif ( importedBmp != null )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcanvas.DrawBitmap( importedBmp, spec.Rect );\n\t\t\t\t\t\t\t\timportedBmp.Dispose();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcapturedViewports.Add( new\n\t\t\t\t\t{\n\t\t\t\t\t\tIndex = i,\n\t\t\t\t\t\tspec.Label,\n\t\t\t\t\t\tTarget = go?.Name ?? spec.TargetName,\n\t\t\t\t\t\tspec.Preset,\n\t\t\t\t\t\tCameraPosition = camPos,\n\t\t\t\t\t\tCameraRotation = camRot.Angles(),\n\t\t\t\t\t\tFOV = camFov,\n\t\t\t\t\t\tRect = new { spec.Rect.Left, spec.Rect.Top, spec.Rect.Width, spec.Rect.Height }\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\t// Draw dividing grid lines\n\t\t\t\tcanvas.SetPen( new Color( 0.25f, 0.45f, 0.75f, 0.9f ), 2f );\n\t\t\t\tif ( normLayout == \u0022split_vertical\u0022 )\n\t\t\t\t{\n\t\t\t\t\tcanvas.DrawLine( new Vector2( w / 2f, 0 ), new Vector2( w / 2f, h ) );\n\t\t\t\t}\n\t\t\t\telse if ( normLayout == \u0022split_horizontal\u0022 )\n\t\t\t\t{\n\t\t\t\t\tcanvas.DrawLine( new Vector2( 0, h / 2f ), new Vector2( w, h / 2f ) );\n\t\t\t\t}\n\t\t\t\telse if ( normLayout == \u0022quad_4view\u0022 )\n\t\t\t\t{\n\t\t\t\t\tcanvas.DrawLine( new Vector2( w / 2f, 0 ), new Vector2( w / 2f, h ) );\n\t\t\t\t\tcanvas.DrawLine( new Vector2( 0, h / 2f ), new Vector2( w, h / 2f ) );\n\t\t\t\t}\n\n\t\t\t\t// Write artifact\n\t\t\t\tvar projectRoot = Project.Current?.GetRootPath() ?? Environment.CurrentDirectory;\n\t\t\t\tvar artifactDir = Path.Combine( projectRoot, \u0022.sbox\u0022, \u0022captures\u0022 );\n\t\t\t\tDirectory.CreateDirectory( artifactDir );\n\n\t\t\t\tvar outName = string.IsNullOrWhiteSpace( fileName )\n\t\t\t\t\t? $\u0022multiview_{normLayout}_{DateTime.UtcNow:yyyyMMdd_HHmmss}.png\u0022\n\t\t\t\t\t: fileName.Trim();\n\n\t\t\t\tvar fullPath = Path.Combine( artifactDir, outName );\n\t\t\t\tvar pngBytes = canvas.ToPng();\n\t\t\t\tFile.WriteAllBytes( fullPath, pngBytes );\n\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tLayout = normLayout,\n\t\t\t\t\tTotalWidth = w,\n\t\t\t\t\tTotalHeight = h,\n\t\t\t\t\tViewportCount = specs.Count,\n\t\t\t\t\tArtifactPath = fullPath,\n\t\t\t\t\tFileSizeBytes = pngBytes.Length,\n\t\t\t\t\tViewports = capturedViewports\n\t\t\t\t} );\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tforeach ( var c in temporaryCameras )\n\t\t\t\t{\n\t\t\t\t\tc.Destroy();\n\t\t\t\t}\n\t\t\t\tcanvas.Dispose();\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Tools/SettingsPublishingTools.Settings.cs","FileName":"SettingsPublishingTools.Settings.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.ComponentModel;\n\nnamespace Editor.Mcp\n{\n\tpublic static partial class SboxMcpAssistant\n\t{\n\t\t[McpTool( \u0022pocketknife_project_settings\u0022 )]\n\t\tpublic static string ProjectSettings(\n\t\t\t[Description( \u0022Action: read, write, list_configs\u0022 )] string action = \u0022list_configs\u0022,\n\t\t\t[Description( \u0022Config file name (Platform, Collision, Input, Networking, Resources)\u0022 )] string? configName = null,\n\t\t\t[Description( \u0022Setting key to update when writing\u0022 )] string? key = null,\n\t\t\t[Description( \u0022Setting value to set when writing\u0022 )] string? value = null )\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ProjectSettingsDriver.ManageSettings( action, configName, key, value ) );\n\t\t\t}\n\t\t\tcatch ( Exception ex )\n\t\t\t{\n\t\t\t\treturn McpJson.Write( ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Failed, \u0022settings.failed\u0022, \u0022Project settings operation failed.\u0022, ex ) );\n\t\t\t}\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/ClutterAndPhysicsDriver.cs","FileName":"ClutterAndPhysicsDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class ClutterAndPhysicsDriver\n\t{\n\t\tpublic static ToolOutcome\u003Cobject\u003E ScatterClutter( string action = \u0022flush\u0022, float radius = 500f, int density = 50, Vector3? position = null )\n\t\t{\n\t\t\tvar scene = EditorRuntime.ResolveActiveScene();\n\t\t\tif ( scene == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022clutter.no_scene\u0022, \u0022No active scene loaded.\u0022 );\n\t\t\t}\n\n\t\t\tvar system = scene.GetSystem\u003CSandbox.Clutter.ClutterGridSystem\u003E();\n\t\t\tif ( system == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022clutter.no_system\u0022, \u0022ClutterGridSystem is not registered on the active scene.\u0022 );\n\t\t\t}\n\n\t\t\tvar normAction = action.ToLowerInvariant();\n\t\t\tvar center = position ?? Vector3.Zero;\n\n\t\t\tif ( normAction == \u0022clear\u0022 )\n\t\t\t{\n\t\t\t\tsystem.ClearAllPainted();\n\t\t\t\tsystem.Flush();\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { Action = \u0022clear\u0022, Cleared = true } );\n\t\t\t}\n\n\t\t\tif ( normAction == \u0022erase\u0022 )\n\t\t\t{\n\t\t\t\tsystem.Erase( center, radius );\n\t\t\t\tsystem.Flush();\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { Action = \u0022erase\u0022, Center = center, Radius = radius } );\n\t\t\t}\n\n\t\t\t// Flush / status\n\t\t\tsystem.Flush();\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tAction = action,\n\t\t\t\tRadius = radius,\n\t\t\t\tDensity = density,\n\t\t\t\tCenter = center,\n\t\t\t\tStatus = \u0022Clutter grid flushed\u0022\n\t\t\t} );\n\t\t}\n\n\t\tpublic static async Task\u003CToolOutcome\u003Cobject\u003E\u003E SettlePhysicsAsync( float durationSeconds = 1.5f, string? targetTag = null )\n\t\t{\n\t\t\tvar scene = EditorRuntime.ResolveActiveScene();\n\t\t\tif ( scene == null )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Failure( null, ToolStatus.Unavailable, \u0022physics.no_scene\u0022, \u0022No active scene loaded.\u0022 );\n\t\t\t}\n\n\t\t\tvar rigidBodies = scene.GetAllComponents\u003CRigidbody\u003E().Where( rb =\u003E rb.IsValid() ).ToArray();\n\t\t\tif ( !string.IsNullOrWhiteSpace( targetTag ) )\n\t\t\t{\n\t\t\t\trigidBodies = rigidBodies.Where( rb =\u003E rb.GameObject.Tags.Has( targetTag ) ).ToArray();\n\t\t\t}\n\t\t\tif ( rigidBodies.Length == 0 )\n\t\t\t{\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t\t{\n\t\t\t\t\tStatus = \u0022No active Rigidbody components found in scene to simulate.\u0022,\n\t\t\t\t\tSettledCount = 0,\n\t\t\t\t\tDuration = durationSeconds,\n\t\t\t\t\tSuccess = true\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tvar settleTime = Math.Clamp( durationSeconds, 0.2f, 10.0f );\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tscene.EnableEditorPhysics( true );\n\t\t\t\tforeach ( var rb in rigidBodies )\n\t\t\t\t{\n\t\t\t\t\tscene.EnableEditorRigidBody( rb, true );\n\t\t\t\t}\n\n\t\t\t\tawait Task.Delay( (int)(settleTime * 1000) );\n\t\t\t}\n\t\t\tfinally\n\t\t\t{\n\t\t\t\tscene.DisableEditorRigidBodies();\n\t\t\t\tscene.EnableEditorPhysics( false );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new\n\t\t\t{\n\t\t\t\tBodiesSettled = rigidBodies.Length,\n\t\t\t\tDurationSeconds = settleTime,\n\t\t\t\tRestingPositionsSaved = true\n\t\t\t} );\n\t\t}\n\t}\n}\n"},{"Ident":"scene.canadian_pocketknife","Path":"Editor/Drivers/SpecializedAssetDriver.cs","FileName":"SpecializedAssetDriver.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":422385,"IsPrivate":false,"Code":"#nullable enable\nusing System;\nusing System.IO;\nusing Sandbox;\n\nnamespace Editor.Mcp\n{\n\tinternal static class SpecializedAssetDriver\n\t{\n\t\tpublic static ToolOutcome\u003Cobject\u003E ManageSprite( string spritePath, string action = \u0022inspect\u0022, int frameCount = 1, float frameRate = 12f )\n\t\t{\n\t\t\tvar normAction = action.ToLowerInvariant();\n\t\t\tif ( normAction == \u0022create\u0022 )\n\t\t\t{\n\t\t\t\tvar dir = Path.GetDirectoryName( spritePath );\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( dir ) ) Directory.CreateDirectory( dir );\n\t\t\t\tvar json = $\u0022{{\\n  \\\u0022FrameCount\\\u0022: {frameCount},\\n  \\\u0022FrameRate\\\u0022: {frameRate},\\n  \\\u0022Animations\\\u0022: [\\\u0022default\\\u0022]\\n}}\u0022;\n\t\t\t\tFile.WriteAllText( spritePath, json );\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { SpritePath = spritePath, Created = true, FrameCount = frameCount } );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { SpritePath = spritePath, Action = action, Exists = File.Exists( spritePath ) } );\n\t\t}\n\n\t\tpublic static ToolOutcome\u003Cobject\u003E ManageShaderGraph( string shaderPath, string action = \u0022inspect\u0022, string templateType = \u0022lit\u0022 )\n\t\t{\n\t\t\tvar normAction = action.ToLowerInvariant();\n\t\t\tif ( normAction == \u0022create_template\u0022 )\n\t\t\t{\n\t\t\t\tvar dir = Path.GetDirectoryName( shaderPath );\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( dir ) ) Directory.CreateDirectory( dir );\n\t\t\t\tvar content = $\u0022// ShaderGraph template ({templateType})\\n// Target: {shaderPath}\\n\u0022;\n\t\t\t\tFile.WriteAllText( shaderPath, content );\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { ShaderPath = shaderPath, Template = templateType, Created = true } );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { ShaderPath = shaderPath, Action = action, Exists = File.Exists( shaderPath ) } );\n\t\t}\n\n\t\tpublic static ToolOutcome\u003Cobject\u003E ManageHotspot( string rectPath, string action = \u0022inspect\u0022, string? targetMesh = null )\n\t\t{\n\t\t\tvar normAction = action.ToLowerInvariant();\n\t\t\tif ( normAction == \u0022create_rect\u0022 )\n\t\t\t{\n\t\t\t\tvar dir = Path.GetDirectoryName( rectPath );\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( dir ) ) Directory.CreateDirectory( dir );\n\t\t\t\tvar content = \u0022{\\n  \\\u0022Subrects\\\u0022: []\\n}\u0022;\n\t\t\t\tFile.WriteAllText( rectPath, content );\n\t\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { RectPath = rectPath, Created = true } );\n\t\t\t}\n\n\t\t\treturn ToolOutcome\u003Cobject\u003E.Ready( new { RectPath = rectPath, Action = action, TargetMesh = targetMesh } );\n\t\t}\n\t}\n}\n"}]}