{"id":"32fa5a72-07d9-4b5b-a40f-f55e8091487c","shortId":"Aqrp8V","kind":"skill","title":"jahro-watcher","tagline":"Analyzes C# fields and properties and generates [JahroWatch] attributes with groups and performance-safe patterns. Use when the user wants to monitor variables at runtime, add watchers, track game state, replace Debug.Log polling, or mentions JahroWatch, real-time inspection, or ","description":"# Jahro Watcher\n\nHelp users monitor game variables in real-time using Jahro's `[JahroWatch]` attribute system.\n\n## Workflow\n\n1. **Analyze** the user's code — identify fields/properties worth monitoring\n2. **Generate** correct `[JahroWatch]` attributes\n3. **Add registration** if needed (instance members require `RegisterObject`)\n4. **VERIFY** — \"Enter Play Mode, open the Watcher tab, confirm values update\"\n\n## Analyzing Code for Watcher Candidates\n\nWhen the user shares a class, identify members worth monitoring:\n\n**Good candidates:**\n- Game state fields: health, score, level, currency, inventory counts\n- Physics values: velocity, position, rotation (especially via properties wrapping Rigidbody)\n- Performance metrics: FPS, memory, draw calls\n- Enum state fields: game state, player state, AI state\n- Key counters: enemy count, player count, item count\n\n**Skip these:**\n- Constants and readonly compile-time values\n- Private implementation details that change every frame with no debugging value\n- References to other objects (Transform, GameObject) — watch their properties instead\n- Collections and dictionaries (arrays are supported, but dictionaries are not)\n\n**Suggest replacing Debug.Log polling:**\nIf the user has `Debug.Log($\"Health: {health}\")` in `Update()`, recommend `[JahroWatch]` instead — it eliminates log spam and provides a clean real-time dashboard.\n\n## Attribute Syntax\n\n```csharp\n[JahroWatch(\"Display Name\", \"GroupName\", \"Description for detail modal\")]\n```\n\nConstructor: `[JahroWatch(string name, string group, string description)]`\n\nAll parameters are optional. Defaults: name = member name (leading `_` stripped), group = \"Default\", description = \"\".\n\n### Complete example\n\n```csharp\nusing JahroConsole;\nusing UnityEngine;\n\npublic class PlayerController : MonoBehaviour\n{\n    [JahroWatch(\"Health\", \"Player\", \"Current hit points\")]\n    public float health = 100f;\n\n    [JahroWatch(\"Stamina\", \"Player\", \"Current stamina\")]\n    public float stamina = 50f;\n\n    [JahroWatch(\"Position\", \"Player\", \"World position\")]\n    public Vector3 Position => transform.position;\n\n    [JahroWatch(\"Velocity\", \"Player\", \"Movement velocity\")]\n    public Vector3 Velocity => GetComponent<Rigidbody>().velocity;\n\n    [JahroWatch(\"Is Grounded\", \"Player\", \"Touching ground\")]\n    public bool isGrounded;\n\n    void OnEnable()  => Jahro.RegisterObject(this);\n    void OnDisable() => Jahro.UnregisterObject(this);\n}\n```\n\n## Registration\n\n### Instance members — require RegisterObject\n\n```csharp\nvoid OnEnable()  => Jahro.RegisterObject(this);\nvoid OnDisable() => Jahro.UnregisterObject(this);\n```\n\nThis same call also registers `[JahroCommand]` attributes on the class. If the class already has `RegisterObject` for commands, do not add a second call — one call handles both.\n\nRead `references/common-patterns.md` for the canonical lifecycle pattern.\n\n### Static members — no registration needed\n\nStatic fields and properties with `[JahroWatch]` are discovered via assembly scanning:\n\n```csharp\npublic static class GameStats\n{\n    [JahroWatch(\"Total Score\", \"Game\")]\n    public static int Score;\n\n    [JahroWatch(\"Session Time\", \"Game\")]\n    public static float SessionTime => Time.realtimeSinceStartup;\n}\n```\n\n### Adding watchers to a class that already has commands\n\nIf the class already has `[JahroCommand]` attributes and `RegisterObject`, just add `[JahroWatch]` attributes — no registration changes:\n\n```csharp\npublic class GameManager : MonoBehaviour\n{\n    // Existing command\n    [JahroCommand(\"reset-game\", \"Game\", \"Reset game\")]\n    public void ResetGame() { /* ... */ }\n\n    // New watchers — just add attributes\n    [JahroWatch(\"Player Count\", \"Game\")]\n    public int playerCount;\n\n    [JahroWatch(\"Game Time\", \"Game\")]\n    public float gameTime;\n\n    // Already present — no changes needed\n    void OnEnable()  => Jahro.RegisterObject(this);\n    void OnDisable() => Jahro.UnregisterObject(this);\n}\n```\n\n## Supported Types\n\n| Type | List View Display | Detail Modal |\n|:-----|:-----------------|:-------------|\n| `int`, `float`, `double`, `bool` | Value as-is | Same |\n| `string` | Truncated | Full text |\n| `Vector2` | Compact coords | Coords + magnitude |\n| `Vector3` | Compact coords | Coords + magnitude |\n| `Quaternion` | Raw values | Raw + Euler angles |\n| `Transform` | Position | Position, rotation, scale, child count |\n| `Rigidbody` | Summary | Mass, kinematic, gravity, velocity, angular velocity |\n| `Collider` | Summary | Trigger status, material, bounds |\n| `AudioSource` | Summary | Clip, volume, loop, pitch, mute |\n| `Camera` | Summary | FOV, clip planes, aspect ratio |\n| Arrays (any `T[]`) | `TypeName[length]` | Full contents |\n\nFor custom types not in this table, the watcher calls `.ToString()`. If you need rich display, consider watching individual primitive properties instead.\n\nRead `references/api-reference.md` for the full type display details.\n\n## Performance\n\nWatchers are designed to be safe for development and testing:\n\n- **Values are only read when the Watcher UI tab is visible.** No continuous polling when the console is closed or on another tab.\n- **No overhead when disabled.** If Jahro is disabled via `JAHRO_DISABLE` or auto-disable, watchers are never evaluated.\n- **Be mindful of expensive property getters.** A property like `public int Count => expensiveList.Where(...).Count()` runs its getter every frame the Watcher is open. Cache expensive computations.\n\n### Performance-safe property pattern\n\n```csharp\nprivate float _cachedFps;\nprivate float _lastFpsUpdate;\n\nvoid Update()\n{\n    if (Time.time - _lastFpsUpdate > 0.25f)\n    {\n        _cachedFps = 1f / Time.unscaledDeltaTime;\n        _lastFpsUpdate = Time.time;\n    }\n}\n\n[JahroWatch(\"FPS\", \"Performance\")]\npublic float FPS => _cachedFps;\n```\n\n## Group Organization\n\n### By system (recommended default)\n\n```\n\"Player\"      — Health, Stamina, Position, Velocity\n\"Physics\"     — Is Grounded, Angular Velocity, Collision Count\n\"Performance\" — FPS, Memory, Draw Calls\n\"Game\"        — Game State, Level Progress, Player Count\n\"AI\"          — AI State, Target, Path Length\n```\n\n### By priority\n\n```\n\"Critical\"    — Health, Frame Time (always need these)\n\"Gameplay\"    — Enemy Count, Spawn Timer\n\"Diagnostics\" — GC Allocs, Memory\n```\n\n### Watcher UI behavior\n\n- **Favorites** group always appears at top (user stars individual watchers)\n- Custom groups are alphabetically sorted\n- Groups are collapsible to reduce clutter\n- Tapping a watcher opens a detail modal with description and full type info\n\n## Contextual Awareness\n\n| Pattern in code | Suggestion |\n|:---------------|:-----------|\n| `Debug.Log` in `Update()` logging variable values | Replace with `[JahroWatch]` — cleaner, no log spam |\n| `[JahroWatch]` already present | Suggest additional watchers, better groups, performance tips |\n| `[JahroCommand]` but no watchers | Suggest adding watchers for key state the commands modify |\n| Rigidbody or physics-heavy code | Suggest velocity, angular velocity, isGrounded watchers |\n| Game manager with state fields | Suggest watching game state enum, counts, timers |\n\n## Verification\n\nAfter generating watchers, always include:\n\n> **Verify:** Enter Play Mode → press ~ → switch to the **Watcher** tab. Confirm your watched values appear in the correct groups and update in real-time as the game runs. Tap a watcher to see its detail modal.\n\nIf watchers appear but don't update, or don't appear at all, suggest the jahro-troubleshooting skill — common causes: missing RegisterObject, Watcher tab not open, object destroyed without unregistering.","tags":["jahro","watcher","unity","agent","skills","jahro-console","agent-skills","ai-assistant","ai-coding","claude-code","cursor","debugging"],"capabilities":["skill","source-jahro-console","skill-jahro-watcher","topic-agent-skills","topic-ai-assistant","topic-ai-coding","topic-claude-code","topic-cursor","topic-debugging","topic-gamedev","topic-in-game-console","topic-jahro","topic-logging","topic-unity","topic-unity-package"],"categories":["unity-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/jahro-console/unity-agent-skills/jahro-watcher","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add jahro-console/unity-agent-skills","source_repo":"https://github.com/jahro-console/unity-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.456","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 12 github stars · SKILL.md body (7,557 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:07:09.648Z","embedding":null,"createdAt":"2026-04-23T13:04:14.441Z","updatedAt":"2026-05-18T19:07:09.648Z","lastSeenAt":"2026-05-18T19:07:09.648Z","tsv":"'0.25':691 '1':64 '100f':279 '1f':694 '2':74 '3':79 '4':88 '50f':288 'ad':412,830 'add':30,80,359,431,457 'addit':819 'ai':149,735,736 'alloc':757 'alphabet':775 'alreadi':352,418,424,473,816 'also':342 'alway':747,764,866 'analyz':4,65,100 'angl':522 'angular':536,719,846 'anoth':627 'appear':765,882,907,915 'array':192,558 'as-i':499 'aspect':556 'assembl':388 'attribut':12,61,78,227,345,427,433,458 'audiosourc':544 'auto':642 'auto-dis':641 'awar':797 'behavior':761 'better':821 'bool':315,497 'bound':543 'c':5 'cach':671 'cachedfp':682,693,704 'call':141,341,362,364,574,727 'camera':551 'candid':104,116 'canon':371 'caus':925 'chang':172,436,476 'child':528 'class':110,267,348,351,393,416,423,439 'clean':222 'cleaner':811 'clip':546,554 'close':624 'clutter':782 'code':69,101,800,843 'collaps':779 'collect':189 'collid':538 'collis':721 'command':356,420,443,836 'common':924 'compact':508,513 'compil':165 'compile-tim':164 'complet':259 'comput':673 'confirm':97,878 'consid':581 'consol':622 'constant':161 'constructor':238 'content':564 'contextu':796 'continu':618 'coord':509,510,514,515 'correct':76,885 'count':125,154,156,158,461,529,659,661,722,734,752,860 'counter':152 'critic':743 'csharp':229,261,330,390,437,679 'currenc':123 'current':273,283 'custom':566,772 'dashboard':226 'debug':177 'debug.log':36,201,207,802 'default':250,257,710 'descript':234,245,258,791 'design':598 'destroy':933 'detail':170,236,492,594,788,903 'develop':603 'diagnost':755 'dictionari':191,196 'disabl':632,636,639,643 'discov':386 'display':231,491,580,593 'doubl':496 'draw':140,726 'elimin':216 'enemi':153,751 'enter':90,869 'enum':142,859 'especi':131 'euler':521 'evalu':647 'everi':173,665 'exampl':260 'exist':442 'expens':651,672 'expensivelist.where':660 'f':692 'favorit':762 'field':6,119,144,380,854 'fields/properties':71 'float':277,286,409,471,495,681,684,702 'fov':553 'fps':138,699,703,724 'frame':174,666,745 'full':505,563,591,793 'game':33,51,117,145,398,406,447,448,450,462,467,469,728,729,850,857,895 'gamemanag':440 'gameobject':184 'gameplay':750 'gamestat':394 'gametim':472 'gc':756 'generat':10,75,864 'getcompon':306 'getter':653,664 'good':115 'graviti':534 'ground':310,313,718 'group':14,243,256,705,763,773,777,822,886 'groupnam':233 'handl':365 'health':120,208,209,271,278,712,744 'heavi':842 'help':48 'hit':274 'identifi':70,111 'implement':169 'includ':867 'individu':583,770 'info':795 'inspect':44 'instanc':84,326 'instead':188,214,586 'int':401,464,494,658 'inventori':124 'isground':316,848 'item':157 'jahro':2,46,58,634,638,921 'jahro-troubleshoot':920 'jahro-watch':1 'jahro.registerobject':319,333,480 'jahro.unregisterobject':323,337,484 'jahrocommand':344,426,444,825 'jahroconsol':263 'jahrowatch':11,40,60,77,213,230,239,270,280,289,298,308,384,395,403,432,459,466,698,810,815 'key':151,833 'kinemat':533 'lastfpsupd':685,690,696 'lead':254 'length':562,740 'level':122,731 'lifecycl':372 'like':656 'list':489 'log':217,805,813 'loop':548 'magnitud':511,516 'manag':851 'mass':532 'materi':542 'member':85,112,252,327,375 'memori':139,725,758 'mention':39 'metric':137 'mind':649 'miss':926 'modal':237,493,789,904 'mode':92,871 'modifi':837 'monitor':26,50,73,114 'monobehaviour':269,441 'movement':301 'mute':550 'name':232,241,251,253 'need':83,378,477,578,748 'never':646 'new':454 'object':182,932 'ondis':322,336,483 'one':363 'onen':318,332,479 'open':93,670,786,931 'option':249 'organ':706 'overhead':630 'paramet':247 'path':739 'pattern':19,373,678,798 'perform':17,136,595,675,700,723,823 'performance-saf':16,674 'physic':126,716,841 'physics-heavi':840 'pitch':549 'plane':555 'play':91,870 'player':147,155,272,282,291,300,311,460,711,733 'playercontrol':268 'playercount':465 'point':275 'poll':37,202,619 'posit':129,290,293,296,524,525,714 'present':474,817 'press':872 'primit':584 'prioriti':742 'privat':168,680,683 'progress':732 'properti':8,133,187,382,585,652,655,677 'provid':220 'public':266,276,285,294,303,314,391,399,407,438,451,463,470,657,701 'quaternion':517 'ratio':557 'raw':518,520 'read':367,587,609 'readon':163 'real':42,55,224,891 'real-tim':41,54,223,890 'recommend':212,709 'reduc':781 'refer':179 'references/api-reference.md':588 'references/common-patterns.md':368 'regist':343 'registerobject':87,329,354,429,927 'registr':81,325,377,435 'replac':35,200,808 'requir':86,328 'reset':446,449 'reset-gam':445 'resetgam':453 'rich':579 'rigidbodi':135,530,838 'rotat':130,526 'run':662,896 'runtim':29 'safe':18,601,676 'scale':527 'scan':389 'score':121,397,402 'second':361 'see':901 'session':404 'sessiontim':410 'share':108 'skill':923 'skill-jahro-watcher' 'skip':159 'sort':776 'source-jahro-console' 'spam':218,814 'spawn':753 'stamina':281,284,287,713 'star':769 'state':34,118,143,146,148,150,730,737,834,853,858 'static':374,379,392,400,408 'status':541 'string':240,242,244,503 'strip':255 'suggest':199,801,818,829,844,855,918 'summari':531,539,545,552 'support':194,486 'switch':873 'syntax':228 'system':62,708 'tab':96,614,628,877,929 'tabl':571 'tap':783,897 'target':738 'test':605 'text':506 'time':43,56,166,225,405,468,746,892 'time.realtimesincestartup':411 'time.time':689,697 'time.unscaleddeltatime':695 'timer':754,861 'tip':824 'top':767 'topic-agent-skills' 'topic-ai-assistant' 'topic-ai-coding' 'topic-claude-code' 'topic-cursor' 'topic-debugging' 'topic-gamedev' 'topic-in-game-console' 'topic-jahro' 'topic-logging' 'topic-unity' 'topic-unity-package' 'tostr':575 'total':396 'touch':312 'track':32 'transform':183,523 'transform.position':297 'trigger':540 'troubleshoot':922 'truncat':504 'type':487,488,567,592,794 'typenam':561 'ui':613,760 'unityengin':265 'unregist':935 'updat':99,211,687,804,888,911 'use':20,57,262,264 'user':23,49,67,107,205,768 'valu':98,127,167,178,498,519,606,807,881 'variabl':27,52,806 'vector2':507 'vector3':295,304,512 'veloc':128,299,302,305,307,535,537,715,720,845,847 'verif':862 'verifi':89,868 'via':132,387,637 'view':490 'visibl':616 'void':317,321,331,335,452,478,482,686 'volum':547 'want':24 'watch':185,582,856,880 'watcher':3,31,47,95,103,413,455,573,596,612,644,668,759,771,785,820,828,831,849,865,876,899,906,928 'without':934 'workflow':63 'world':292 'worth':72,113 'wrap':134","prices":[{"id":"ced6b593-302f-46b2-9a98-efe7afb16611","listingId":"32fa5a72-07d9-4b5b-a40f-f55e8091487c","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"jahro-console","category":"unity-agent-skills","install_from":"skills.sh"},"createdAt":"2026-04-23T13:04:14.441Z"}],"sources":[{"listingId":"32fa5a72-07d9-4b5b-a40f-f55e8091487c","source":"github","sourceId":"jahro-console/unity-agent-skills/jahro-watcher","sourceUrl":"https://github.com/jahro-console/unity-agent-skills/tree/main/skills/jahro-watcher","isPrimary":false,"firstSeenAt":"2026-04-23T13:04:14.441Z","lastSeenAt":"2026-05-18T19:07:09.648Z"}],"details":{"listingId":"32fa5a72-07d9-4b5b-a40f-f55e8091487c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"jahro-console","slug":"jahro-watcher","github":{"repo":"jahro-console/unity-agent-skills","stars":12,"topics":["agent-skills","ai-assistant","ai-coding","claude-code","cursor","debugging","gamedev","in-game-console","jahro","logging","unity","unity-package","unity3d"],"license":"mit","html_url":"https://github.com/jahro-console/unity-agent-skills","pushed_at":"2026-03-20T15:39:45Z","description":"Agent skills for AI-assisted Unity debugging — structured logging, runtime commands, variable watching, and more","skill_md_sha":"f54a8db4ebe00af9286ee3703811a37551d3309c","skill_md_path":"skills/jahro-watcher/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/jahro-console/unity-agent-skills/tree/main/skills/jahro-watcher"},"layout":"multi","source":"github","category":"unity-agent-skills","frontmatter":{"name":"jahro-watcher","description":"Analyzes C# fields and properties and generates [JahroWatch] attributes with groups and performance-safe patterns. Use when the user wants to monitor variables at runtime, add watchers, track game state, replace Debug.Log polling, or mentions JahroWatch, real-time inspection, or variable monitoring."},"skills_sh_url":"https://skills.sh/jahro-console/unity-agent-skills/jahro-watcher"},"updatedAt":"2026-05-18T19:07:09.648Z"}}