claudecode-omc 5.6.7 → 5.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/.local/skills/THIRD_PARTY_LICENSES/AvdLee-SwiftUI-Agent-Skill.LICENSE +21 -0
  2. package/.local/skills/THIRD_PARTY_LICENSES/Dimillian-Skills.LICENSE +21 -0
  3. package/.local/skills/THIRD_PARTY_LICENSES/README.md +36 -0
  4. package/.local/skills/THIRD_PARTY_LICENSES/twostraws-swiftui-agent-skill.LICENSE +21 -0
  5. package/.local/skills/ios-debugger-agent/SKILL.md +51 -0
  6. package/.local/skills/ios-debugger-agent/agents/openai.yaml +4 -0
  7. package/.local/skills/prompt-optimizer/SKILL.md +262 -19
  8. package/.local/skills/swift-concurrency-expert/SKILL.md +105 -0
  9. package/.local/skills/swift-concurrency-expert/agents/openai.yaml +4 -0
  10. package/.local/skills/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
  11. package/.local/skills/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
  12. package/.local/skills/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
  13. package/.local/skills/swiftui-expert-skill/SKILL.md +162 -0
  14. package/.local/skills/swiftui-expert-skill/references/accessibility-patterns.md +215 -0
  15. package/.local/skills/swiftui-expert-skill/references/animation-advanced.md +403 -0
  16. package/.local/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
  17. package/.local/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
  18. package/.local/skills/swiftui-expert-skill/references/charts-accessibility.md +135 -0
  19. package/.local/skills/swiftui-expert-skill/references/charts.md +602 -0
  20. package/.local/skills/swiftui-expert-skill/references/focus-patterns.md +299 -0
  21. package/.local/skills/swiftui-expert-skill/references/image-optimization.md +203 -0
  22. package/.local/skills/swiftui-expert-skill/references/latest-apis.md +488 -0
  23. package/.local/skills/swiftui-expert-skill/references/layout-best-practices.md +266 -0
  24. package/.local/skills/swiftui-expert-skill/references/liquid-glass.md +423 -0
  25. package/.local/skills/swiftui-expert-skill/references/list-patterns.md +446 -0
  26. package/.local/skills/swiftui-expert-skill/references/macos-scenes.md +318 -0
  27. package/.local/skills/swiftui-expert-skill/references/macos-views.md +357 -0
  28. package/.local/skills/swiftui-expert-skill/references/macos-window-styling.md +303 -0
  29. package/.local/skills/swiftui-expert-skill/references/performance-patterns.md +403 -0
  30. package/.local/skills/swiftui-expert-skill/references/scroll-patterns.md +293 -0
  31. package/.local/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
  32. package/.local/skills/swiftui-expert-skill/references/state-management.md +388 -0
  33. package/.local/skills/swiftui-expert-skill/references/text-patterns.md +32 -0
  34. package/.local/skills/swiftui-expert-skill/references/trace-analysis.md +295 -0
  35. package/.local/skills/swiftui-expert-skill/references/trace-recording.md +134 -0
  36. package/.local/skills/swiftui-expert-skill/references/view-structure.md +780 -0
  37. package/.local/skills/swiftui-expert-skill/scripts/__pycache__/analyze_trace.cpython-313.pyc +0 -0
  38. package/.local/skills/swiftui-expert-skill/scripts/__pycache__/record_trace.cpython-313.pyc +0 -0
  39. package/.local/skills/swiftui-expert-skill/scripts/analyze_trace.py +301 -0
  40. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__init__.py +1 -0
  41. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/__init__.cpython-313.pyc +0 -0
  42. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/causes.cpython-313.pyc +0 -0
  43. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/correlate.cpython-313.pyc +0 -0
  44. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/events.cpython-313.pyc +0 -0
  45. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/hangs.cpython-313.pyc +0 -0
  46. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/hitches.cpython-313.pyc +0 -0
  47. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/summary.cpython-313.pyc +0 -0
  48. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/swiftui.cpython-313.pyc +0 -0
  49. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/time_profiler.cpython-313.pyc +0 -0
  50. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/xctrace.cpython-313.pyc +0 -0
  51. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/__pycache__/xml_utils.cpython-313.pyc +0 -0
  52. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/causes.py +187 -0
  53. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/correlate.py +179 -0
  54. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/events.py +291 -0
  55. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/hangs.py +108 -0
  56. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/hitches.py +145 -0
  57. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/summary.py +243 -0
  58. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/swiftui.py +195 -0
  59. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/time_profiler.py +135 -0
  60. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/xctrace.py +117 -0
  61. package/.local/skills/swiftui-expert-skill/scripts/instruments_parser/xml_utils.py +224 -0
  62. package/.local/skills/swiftui-expert-skill/scripts/record_trace.py +252 -0
  63. package/.local/skills/swiftui-liquid-glass/SKILL.md +90 -0
  64. package/.local/skills/swiftui-liquid-glass/agents/openai.yaml +4 -0
  65. package/.local/skills/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  66. package/.local/skills/swiftui-performance-audit/SKILL.md +106 -0
  67. package/.local/skills/swiftui-performance-audit/agents/openai.yaml +4 -0
  68. package/.local/skills/swiftui-performance-audit/references/code-smells.md +150 -0
  69. package/.local/skills/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  70. package/.local/skills/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  71. package/.local/skills/swiftui-performance-audit/references/profiling-intake.md +44 -0
  72. package/.local/skills/swiftui-performance-audit/references/report-template.md +47 -0
  73. package/.local/skills/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  74. package/.local/skills/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  75. package/.local/skills/swiftui-pro/SKILL.md +108 -0
  76. package/.local/skills/swiftui-pro/agents/openai.yaml +10 -0
  77. package/.local/skills/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  78. package/.local/skills/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  79. package/.local/skills/swiftui-pro/references/accessibility.md +13 -0
  80. package/.local/skills/swiftui-pro/references/api.md +39 -0
  81. package/.local/skills/swiftui-pro/references/data.md +43 -0
  82. package/.local/skills/swiftui-pro/references/design.md +32 -0
  83. package/.local/skills/swiftui-pro/references/hygiene.md +9 -0
  84. package/.local/skills/swiftui-pro/references/navigation.md +14 -0
  85. package/.local/skills/swiftui-pro/references/performance.md +46 -0
  86. package/.local/skills/swiftui-pro/references/swift.md +56 -0
  87. package/.local/skills/swiftui-pro/references/views.md +36 -0
  88. package/.local/skills/swiftui-ui-patterns/SKILL.md +95 -0
  89. package/.local/skills/swiftui-ui-patterns/agents/openai.yaml +4 -0
  90. package/.local/skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
  91. package/.local/skills/swiftui-ui-patterns/references/async-state.md +96 -0
  92. package/.local/skills/swiftui-ui-patterns/references/components-index.md +50 -0
  93. package/.local/skills/swiftui-ui-patterns/references/controls.md +57 -0
  94. package/.local/skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
  95. package/.local/skills/swiftui-ui-patterns/references/focus.md +90 -0
  96. package/.local/skills/swiftui-ui-patterns/references/form.md +97 -0
  97. package/.local/skills/swiftui-ui-patterns/references/grids.md +71 -0
  98. package/.local/skills/swiftui-ui-patterns/references/haptics.md +71 -0
  99. package/.local/skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  100. package/.local/skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  101. package/.local/skills/swiftui-ui-patterns/references/list.md +86 -0
  102. package/.local/skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  103. package/.local/skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
  104. package/.local/skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  105. package/.local/skills/swiftui-ui-patterns/references/media.md +73 -0
  106. package/.local/skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
  107. package/.local/skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
  108. package/.local/skills/swiftui-ui-patterns/references/overlay.md +45 -0
  109. package/.local/skills/swiftui-ui-patterns/references/performance.md +62 -0
  110. package/.local/skills/swiftui-ui-patterns/references/previews.md +48 -0
  111. package/.local/skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  112. package/.local/skills/swiftui-ui-patterns/references/scrollview.md +87 -0
  113. package/.local/skills/swiftui-ui-patterns/references/searchable.md +71 -0
  114. package/.local/skills/swiftui-ui-patterns/references/sheets.md +155 -0
  115. package/.local/skills/swiftui-ui-patterns/references/split-views.md +72 -0
  116. package/.local/skills/swiftui-ui-patterns/references/tabview.md +114 -0
  117. package/.local/skills/swiftui-ui-patterns/references/theming.md +71 -0
  118. package/.local/skills/swiftui-ui-patterns/references/title-menus.md +93 -0
  119. package/.local/skills/swiftui-ui-patterns/references/top-bar.md +49 -0
  120. package/.local/skills/swiftui-view-refactor/SKILL.md +202 -0
  121. package/.local/skills/swiftui-view-refactor/agents/openai.yaml +4 -0
  122. package/.local/skills/swiftui-view-refactor/references/mv-patterns.md +161 -0
  123. package/.omc-curation/ecc-selection.json +80 -0
  124. package/.omc-curation/governance.json +113 -0
  125. package/.omc-curation/sources.lock.json +25 -0
  126. package/README.md +69 -4
  127. package/bundled/manifest.json +5 -5
  128. package/bundled/upstream/anthropic-skills/.omc-source/bundle.json +18 -0
  129. package/bundled/upstream/anthropic-skills/.omc-source/provenance.json +399 -0
  130. package/bundled/upstream/anthropic-skills/skills/claude-api/SKILL.md +18 -17
  131. package/bundled/upstream/anthropic-skills/skills/claude-api/curl/examples.md +9 -9
  132. package/bundled/upstream/anthropic-skills/skills/claude-api/curl/managed-agents.md +4 -4
  133. package/bundled/upstream/anthropic-skills/skills/claude-api/go/managed-agents/README.md +2 -2
  134. package/bundled/upstream/anthropic-skills/skills/claude-api/java/claude-api.md +2 -2
  135. package/bundled/upstream/anthropic-skills/skills/claude-api/java/managed-agents/README.md +2 -2
  136. package/bundled/upstream/anthropic-skills/skills/claude-api/php/claude-api.md +10 -10
  137. package/bundled/upstream/anthropic-skills/skills/claude-api/php/managed-agents/README.md +2 -2
  138. package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/README.md +16 -16
  139. package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/batches.md +3 -3
  140. package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/files-api.md +3 -3
  141. package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/streaming.md +7 -7
  142. package/bundled/upstream/anthropic-skills/skills/claude-api/python/claude-api/tool-use.md +19 -19
  143. package/bundled/upstream/anthropic-skills/skills/claude-api/python/managed-agents/README.md +3 -3
  144. package/bundled/upstream/anthropic-skills/skills/claude-api/ruby/claude-api.md +4 -4
  145. package/bundled/upstream/anthropic-skills/skills/claude-api/ruby/managed-agents/README.md +2 -2
  146. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/error-codes.md +5 -5
  147. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/live-sources.md +3 -1
  148. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-api-reference.md +10 -4
  149. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-core.md +19 -1
  150. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-environments.md +6 -2
  151. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-multiagent.md +1 -1
  152. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-onboarding.md +3 -3
  153. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-overview.md +3 -2
  154. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +173 -0
  155. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/managed-agents-tools.md +10 -4
  156. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/model-migration.md +113 -13
  157. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/models.md +14 -11
  158. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/prompt-caching.md +2 -2
  159. package/bundled/upstream/anthropic-skills/skills/claude-api/shared/tool-use-concepts.md +4 -4
  160. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/README.md +15 -15
  161. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/batches.md +2 -2
  162. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/files-api.md +1 -1
  163. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/streaming.md +5 -5
  164. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/claude-api/tool-use.md +15 -15
  165. package/bundled/upstream/anthropic-skills/skills/claude-api/typescript/managed-agents/README.md +3 -3
  166. package/bundled/upstream/ecc/.omc-source/bundle.json +2 -1
  167. package/bundled/upstream/ecc/.omc-source/last-plan-apply.json +108 -24
  168. package/bundled/upstream/ecc/.omc-source/manifests/.claude-plugin/marketplace.json +3 -3
  169. package/bundled/upstream/ecc/.omc-source/provenance.json +563 -0
  170. package/bundled/upstream/ecc/agents/marketing-agent.md +159 -0
  171. package/bundled/upstream/ecc/agents/react-build-resolver.md +215 -0
  172. package/bundled/upstream/ecc/agents/react-reviewer.md +167 -0
  173. package/bundled/upstream/ecc/agents/typescript-reviewer.md +3 -0
  174. package/bundled/upstream/ecc/commands/harness-audit.md +17 -10
  175. package/bundled/upstream/ecc/commands/marketing-campaign.md +129 -0
  176. package/bundled/upstream/ecc/commands/react-build.md +187 -0
  177. package/bundled/upstream/ecc/commands/react-review.md +170 -0
  178. package/bundled/upstream/ecc/commands/react-test.md +265 -0
  179. package/bundled/upstream/ecc/skills/benchmark-optimization-loop/SKILL.md +69 -0
  180. package/bundled/upstream/ecc/skills/blender-motion-state-inspection/SKILL.md +164 -0
  181. package/bundled/upstream/ecc/skills/canary-watch/SKILL.md +9 -1
  182. package/bundled/upstream/ecc/skills/continuous-learning-v2/hooks/observe.sh +31 -9
  183. package/bundled/upstream/ecc/skills/continuous-learning-v2/scripts/detect-project.sh +38 -4
  184. package/bundled/upstream/ecc/skills/continuous-learning-v2/scripts/instinct-cli.py +319 -12
  185. package/bundled/upstream/ecc/skills/data-throughput-accelerator/SKILL.md +72 -0
  186. package/bundled/upstream/ecc/skills/dynamic-workflow-mode/SKILL.md +123 -0
  187. package/bundled/upstream/ecc/skills/frontend-a11y/SKILL.md +446 -0
  188. package/bundled/upstream/ecc/skills/ito-basket-compare/SKILL.md +63 -0
  189. package/bundled/upstream/ecc/skills/ito-data-atlas-agent/SKILL.md +63 -0
  190. package/bundled/upstream/ecc/skills/ito-market-intelligence/SKILL.md +60 -0
  191. package/bundled/upstream/ecc/skills/ito-trade-planner/SKILL.md +67 -0
  192. package/bundled/upstream/ecc/skills/latency-critical-systems/SKILL.md +73 -0
  193. package/bundled/upstream/ecc/skills/marketing-campaign/SKILL.md +113 -0
  194. package/bundled/upstream/ecc/skills/nextjs-turbopack/SKILL.md +13 -0
  195. package/bundled/upstream/ecc/skills/parallel-execution-optimizer/SKILL.md +72 -0
  196. package/bundled/upstream/ecc/skills/prediction-market-oracle-research/SKILL.md +63 -0
  197. package/bundled/upstream/ecc/skills/prediction-market-risk-review/SKILL.md +60 -0
  198. package/bundled/upstream/ecc/skills/react-patterns/SKILL.md +341 -0
  199. package/bundled/upstream/ecc/skills/react-performance/SKILL.md +574 -0
  200. package/bundled/upstream/ecc/skills/react-testing/SKILL.md +423 -0
  201. package/bundled/upstream/ecc/skills/recsys-pipeline-architect/SKILL.md +114 -0
  202. package/bundled/upstream/ecc/skills/recursive-decision-ledger/SKILL.md +79 -0
  203. package/bundled/upstream/ecc/skills/social-publisher/SKILL.md +115 -0
  204. package/bundled/upstream/ecc/skills/team-agent-orchestration/SKILL.md +110 -0
  205. package/bundled/upstream/ecc/skills/uncloud/SKILL.md +343 -0
  206. package/bundled/upstream/ecc/skills/windows-desktop-e2e/SKILL.md +99 -0
  207. package/bundled/upstream/oh-my-claudecode/.omc-source/bundle.json +2 -1
  208. package/bundled/upstream/oh-my-claudecode/.omc-source/provenance.json +116 -0
  209. package/bundled/upstream/oh-my-claudecode/skills/autopilot/SKILL.md +7 -0
  210. package/bundled/upstream/oh-my-claudecode/skills/cancel/SKILL.md +1 -0
  211. package/bundled/upstream/oh-my-claudecode/skills/deep-interview/SKILL.md +39 -5
  212. package/bundled/upstream/oh-my-claudecode/skills/hud/SKILL.md +1 -0
  213. package/bundled/upstream/oh-my-claudecode/skills/local-build-reminder/SKILL.md +78 -0
  214. package/bundled/upstream/oh-my-claudecode/skills/omc-doctor/SKILL.md +1 -1
  215. package/bundled/upstream/oh-my-claudecode/skills/omc-setup/SKILL.md +26 -10
  216. package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/01-install-claude-md.md +3 -3
  217. package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/02-configure.md +6 -4
  218. package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/03-integrations.md +1 -1
  219. package/bundled/upstream/oh-my-claudecode/skills/omc-setup/phases/04-welcome.md +2 -2
  220. package/bundled/upstream/oh-my-claudecode/skills/omc-teams/SKILL.md +6 -6
  221. package/bundled/upstream/oh-my-claudecode/skills/plan/SKILL.md +44 -32
  222. package/bundled/upstream/oh-my-claudecode/skills/ralph/SKILL.md +45 -21
  223. package/bundled/upstream/oh-my-claudecode/skills/ralplan/SKILL.md +1 -1
  224. package/bundled/upstream/oh-my-claudecode/skills/self-improve/SKILL.md +7 -0
  225. package/bundled/upstream/oh-my-claudecode/skills/self-improve/scripts/resolve-paths.mjs +39 -15
  226. package/bundled/upstream/oh-my-claudecode/skills/team/SKILL.md +132 -90
  227. package/bundled/upstream/oh-my-claudecode/skills/ultragoal/SKILL.md +93 -0
  228. package/bundled/upstream/oh-my-claudecode/skills/ultraqa/SKILL.md +28 -13
  229. package/bundled/upstream/oh-my-claudecode/skills/ultrawork/SKILL.md +7 -0
  230. package/bundled/upstream/superpowers/.omc-source/bundle.json +2 -1
  231. package/bundled/upstream/superpowers/.omc-source/provenance.json +63 -0
  232. package/package.json +2 -1
  233. package/src/catalog/source-catalog.js +10 -4
  234. package/src/cli/index.js +4 -0
  235. package/src/cli/plan.js +14 -2
  236. package/src/cli/setup.js +52 -13
  237. package/src/cli/skill.js +1 -1
  238. package/src/cli/source.js +265 -14
  239. package/src/config/sources.js +67 -1
  240. package/src/merge/content-patch.js +84 -0
  241. package/templates/merge-config.json +1 -8
  242. package/bundled/upstream/ecc/skills/strategic-compact/suggest-compact.sh +0 -54
@@ -0,0 +1,145 @@
1
+ """Animation hitches lane parser.
2
+
3
+ Xcode 26 schema `hitches` columns: start, duration (hitch time), process,
4
+ is-system, swap-id, label, display, narrative-description. The
5
+ narrative-description field carries Apple's own attribution (e.g.
6
+ "Potentially expensive app update(s)") which is the highest-signal column.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from collections import Counter
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from . import xctrace, xml_utils
15
+
16
+ CANDIDATE_SCHEMAS = ("hitches", "animation-hitch", "hitch")
17
+
18
+ START_KEYS = ("start", "time", "sample-time")
19
+ DURATION_KEYS = ("duration", "hitch-duration", "frame-duration")
20
+
21
+
22
+ def analyze(
23
+ trace_path: Path,
24
+ toc_schemas: frozenset[str],
25
+ top_n: int = 10,
26
+ window: tuple[int, int] | None = None,
27
+ run: int = 1,
28
+ ) -> dict[str, Any]:
29
+ schema = _pick_schema(toc_schemas)
30
+ if schema is None:
31
+ return {
32
+ "lane": "hitches",
33
+ "available": False,
34
+ "notes": ["Animation hitches not present in trace."],
35
+ }
36
+
37
+ xml_bytes = xctrace.export_schema(trace_path, schema, run=run)
38
+ stream = xml_utils.RowStream(xml_bytes)
39
+
40
+ events: list[dict] = []
41
+ narrative_counts: Counter[str] = Counter()
42
+ system_count = 0
43
+
44
+ for row in stream:
45
+ start_ns = _first_int(row, stream, START_KEYS)
46
+ duration_ns = _first_int(row, stream, DURATION_KEYS)
47
+ if start_ns is None or duration_ns is None:
48
+ continue
49
+ if not xml_utils.event_overlaps_window(start_ns, start_ns + duration_ns, window):
50
+ continue
51
+
52
+ process_el = row.get("process")
53
+ process = (
54
+ xml_utils.extract_process(process_el, stream)
55
+ if process_el is not None else None
56
+ )
57
+
58
+ narrative_el = row.get("narrative-description")
59
+ narrative = xml_utils.str_text(stream.resolve(narrative_el)) if narrative_el is not None else None
60
+ if narrative:
61
+ narrative_counts[narrative] += 1
62
+
63
+ is_system_el = row.get("is-system")
64
+ is_system = _bool_text(stream.resolve(is_system_el)) if is_system_el is not None else None
65
+ if is_system:
66
+ system_count += 1
67
+
68
+ events.append({
69
+ "start_ns": start_ns,
70
+ "end_ns": start_ns + duration_ns,
71
+ "duration_ns": duration_ns,
72
+ "hitch_duration_ns": duration_ns, # Xcode 26 `duration` == hitch time
73
+ "frame_duration_ns": None,
74
+ "hitch_duration_ms": round(duration_ns / 1_000_000, 2),
75
+ "frame_duration_ms": None,
76
+ "start_ms": round(start_ns / 1_000_000, 2),
77
+ "process": (process or {}).get("name"),
78
+ "narrative": narrative,
79
+ "is_system": bool(is_system) if is_system is not None else None,
80
+ })
81
+
82
+ events.sort(key=lambda e: e["duration_ns"], reverse=True)
83
+
84
+ total_hitch_ms = sum(e["hitch_duration_ms"] for e in events)
85
+ worst = events[0] if events else None
86
+
87
+ per_process: dict[str, int] = {}
88
+ for e in events:
89
+ key = e["process"] or "unknown"
90
+ per_process[key] = per_process.get(key, 0) + 1
91
+
92
+ top_offenders = [
93
+ {
94
+ "start_ms": e["start_ms"],
95
+ "hitch_duration_ms": e["hitch_duration_ms"],
96
+ "frame_duration_ms": e["frame_duration_ms"],
97
+ "process": e["process"],
98
+ "narrative": e["narrative"],
99
+ "is_system": e["is_system"],
100
+ }
101
+ for e in events[:top_n]
102
+ ]
103
+
104
+ return {
105
+ "lane": "hitches",
106
+ "available": True,
107
+ "schema_used": schema,
108
+ "metrics": {
109
+ "count": len(events),
110
+ "total_hitch_ms": round(total_hitch_ms, 2),
111
+ "worst_hitch_ms": worst["hitch_duration_ms"] if worst else 0,
112
+ "per_process": per_process,
113
+ "system_hitches": system_count,
114
+ "app_hitches": len(events) - system_count,
115
+ "narrative_breakdown": dict(narrative_counts.most_common()),
116
+ },
117
+ "top_offenders": top_offenders,
118
+ "notes": [],
119
+ "_events": events,
120
+ }
121
+
122
+
123
+ def _pick_schema(available: frozenset[str]) -> str | None:
124
+ for s in CANDIDATE_SCHEMAS:
125
+ if s in available:
126
+ return s
127
+ return None
128
+
129
+
130
+ def _first_int(row, stream, keys):
131
+ for key in keys:
132
+ el = row.get(key)
133
+ if el is None:
134
+ continue
135
+ val = xml_utils.int_text(stream.resolve(el))
136
+ if val is not None:
137
+ return val
138
+ return None
139
+
140
+
141
+ def _bool_text(elem) -> bool | None:
142
+ txt = xml_utils.str_text(elem)
143
+ if txt is None:
144
+ return None
145
+ return txt.strip() in ("1", "true", "True", "YES", "Yes")
@@ -0,0 +1,243 @@
1
+ """Markdown summary renderer for the combined trace analysis."""
2
+ from __future__ import annotations
3
+
4
+
5
+ def render(result: dict) -> str:
6
+ lines: list[str] = []
7
+ trace = result.get("trace", "?")
8
+ header = result.get("xctrace_version") or ""
9
+ template = result.get("template") or ""
10
+ duration_s = result.get("duration_s")
11
+ lines.append(f"# Instruments Trace Analysis")
12
+ meta = [p for p in [f"Trace: `{trace}`", header, template] if p]
13
+ lines.append(" • ".join(meta))
14
+ if duration_s is not None:
15
+ lines.append(f"Recording duration: {duration_s:.2f}s")
16
+ lines.append("")
17
+
18
+ lanes_by_name = {lane["lane"]: lane for lane in result.get("lanes", [])}
19
+
20
+ _render_time_profiler(lines, lanes_by_name.get("time-profiler"))
21
+ _render_hangs(lines, lanes_by_name.get("hangs"))
22
+ _render_hitches(lines, lanes_by_name.get("hitches"))
23
+ _render_swiftui(lines, lanes_by_name.get("swiftui"))
24
+ _render_causes(lines, lanes_by_name.get("swiftui-causes"))
25
+ _render_correlations(lines, result.get("correlations", []))
26
+
27
+ return "\n".join(lines).rstrip() + "\n"
28
+
29
+
30
+ def _skipped_block(title: str, lane: dict | None) -> list[str]:
31
+ if lane is None:
32
+ return [f"## {title} — skipped (lane module not run)", ""]
33
+ notes = lane.get("notes") or []
34
+ note_text = f" — {notes[0]}" if notes else ""
35
+ return [f"## {title} — skipped{note_text}", ""]
36
+
37
+
38
+ def _render_time_profiler(lines: list[str], lane: dict | None) -> None:
39
+ if not lane or not lane.get("available"):
40
+ lines.extend(_skipped_block("Time Profiler", lane))
41
+ return
42
+ m = lane["metrics"]
43
+ lines.append(
44
+ f"## Time Profiler — {m['total_samples']:,} samples, "
45
+ f"{m['total_weight_ms']:.0f}ms CPU time"
46
+ )
47
+ if m.get("processes"):
48
+ lines.append(f"Processes: {', '.join(m['processes'])}")
49
+ lines.append("")
50
+ if lane["top_offenders"]:
51
+ lines.append("Top offenders:")
52
+ for i, o in enumerate(lane["top_offenders"], 1):
53
+ lines.append(
54
+ f"{i}. `{_truncate(o['symbol'], 90)}` — "
55
+ f"{o['percent']:.1f}% ({o['weight_ms']:.0f}ms, "
56
+ f"{o['samples']} samples, {_short_thread(o['thread'])})"
57
+ )
58
+ for note in lane.get("notes") or []:
59
+ lines.append(f"> {note}")
60
+ lines.append("")
61
+
62
+
63
+ def _render_hangs(lines: list[str], lane: dict | None) -> None:
64
+ if not lane or not lane.get("available"):
65
+ lines.extend(_skipped_block("Hangs", lane))
66
+ return
67
+ m = lane["metrics"]
68
+ buckets = m["severity_buckets"]
69
+ lines.append(
70
+ f"## Hangs — {m['count']} hangs, {m['total_duration_ms']:.0f}ms total, "
71
+ f"worst {m['worst_duration_ms']:.0f}ms"
72
+ )
73
+ lines.append(
74
+ f"Severity: <250ms={buckets['lt_250ms']}, "
75
+ f"250ms–1s={buckets['250ms_1s']}, >1s={buckets['gt_1s']}"
76
+ )
77
+ lines.append("")
78
+ for i, h in enumerate(lane["top_offenders"], 1):
79
+ lines.append(
80
+ f"{i}. {h['duration_ms']:.0f}ms {h['hang_type']} at "
81
+ f"{h['start_ms']:.2f}ms on {_short_thread(h['thread'])}"
82
+ )
83
+ lines.append("")
84
+
85
+
86
+ def _render_hitches(lines: list[str], lane: dict | None) -> None:
87
+ if not lane or not lane.get("available"):
88
+ lines.extend(_skipped_block("Animation Hitches", lane))
89
+ return
90
+ m = lane["metrics"]
91
+ lines.append(
92
+ f"## Animation Hitches — {m['count']} hitches, "
93
+ f"{m['total_hitch_ms']:.0f}ms total, worst {m['worst_hitch_ms']:.0f}ms"
94
+ )
95
+ if m.get("per_process"):
96
+ pp = ", ".join(f"{k}={v}" for k, v in m["per_process"].items())
97
+ lines.append(f"By process: {pp}")
98
+ lines.append("")
99
+ if m.get("narrative_breakdown"):
100
+ nb = ", ".join(f"{k}={v}" for k, v in m["narrative_breakdown"].items() if k)
101
+ if nb:
102
+ lines.append(f"Apple attribution: {nb}")
103
+ if m.get("system_hitches") is not None:
104
+ lines.append(
105
+ f"System vs app: system={m['system_hitches']}, app={m['app_hitches']}"
106
+ )
107
+ lines.append("")
108
+ for i, h in enumerate(lane["top_offenders"], 1):
109
+ narrative = f" — {h['narrative']}" if h.get("narrative") else ""
110
+ src = " [system]" if h.get("is_system") else ""
111
+ proc = f" ({h['process']})" if h.get("process") else ""
112
+ lines.append(
113
+ f"{i}. {h['hitch_duration_ms']:.0f}ms at {h['start_ms']:.2f}ms"
114
+ f"{proc}{src}{narrative}"
115
+ )
116
+ lines.append("")
117
+
118
+
119
+ def _render_swiftui(lines: list[str], lane: dict | None) -> None:
120
+ if not lane or not lane.get("available"):
121
+ lines.extend(_skipped_block("SwiftUI", lane))
122
+ return
123
+ m = lane["metrics"]
124
+ lines.append(
125
+ f"## SwiftUI — {m['total_events']:,} updates across "
126
+ f"{m['unique_views']} views, {m['total_duration_ms']:.0f}ms total"
127
+ )
128
+ if m.get("severity_breakdown"):
129
+ sb = ", ".join(f"{k}={v}" for k, v in m["severity_breakdown"].items())
130
+ lines.append(f"Severity: {sb}")
131
+ if m.get("update_type_breakdown"):
132
+ ub = ", ".join(f"{k}={v}" for k, v in m["update_type_breakdown"].items())
133
+ lines.append(f"Update types: {ub}")
134
+ lines.append("")
135
+ if lane["top_offenders"]:
136
+ lines.append("Heaviest views (by total body time):")
137
+ for i, v in enumerate(lane["top_offenders"], 1):
138
+ lines.append(
139
+ f"{i}. `{_truncate(v['view'], 80)}` — {v['total_ms']:.0f}ms total, "
140
+ f"{v['count']} updates (avg {v['avg_ms']:.2f}ms)"
141
+ )
142
+ if lane.get("high_severity_events"):
143
+ lines.append("")
144
+ lines.append("High-severity updates:")
145
+ for i, e in enumerate(lane["high_severity_events"][:5], 1):
146
+ cat = f" [{e['category']}]" if e.get("category") else ""
147
+ lines.append(
148
+ f"{i}. `{_truncate(e['view'], 60)}` — "
149
+ f"{e['severity']} ({e['duration_ms']:.2f}ms at {e['start_ms']:.2f}ms){cat}"
150
+ )
151
+ lines.append("")
152
+
153
+
154
+ def _render_causes(lines: list[str], lane: dict | None) -> None:
155
+ if not lane or not lane.get("available"):
156
+ lines.extend(_skipped_block("SwiftUI Cause Graph", lane))
157
+ return
158
+ m = lane["metrics"]
159
+ lines.append(
160
+ f"## SwiftUI Cause Graph — {m['total_edges']:,} edges, "
161
+ f"{m['unique_sources']} sources → {m['unique_destinations']} destinations"
162
+ )
163
+ lines.append("")
164
+ if lane.get("top_sources"):
165
+ lines.append("Top sources (who's driving the most updates):")
166
+ for i, s in enumerate(lane["top_sources"][:5], 1):
167
+ lines.append(f"{i}. `{_truncate(s['source'], 80)}` — {s['edges']:,} edges")
168
+ for d in s["top_destinations"][:3]:
169
+ lines.append(
170
+ f" → `{_truncate(d['destination'], 70)}` {d['edges']:,}"
171
+ )
172
+ if lane.get("top_destinations"):
173
+ lines.append("")
174
+ lines.append("Top destinations (who's being invalidated most):")
175
+ for i, d in enumerate(lane["top_destinations"][:5], 1):
176
+ lines.append(f"{i}. `{_truncate(d['destination'], 80)}` — {d['edges']:,} edges")
177
+ for s in d["top_sources"][:3]:
178
+ lines.append(
179
+ f" ← `{_truncate(s['source'], 70)}` {s['edges']:,}"
180
+ )
181
+ lines.append("")
182
+
183
+
184
+ def _render_correlations(lines: list[str], correlations: list[dict]) -> None:
185
+ if not correlations:
186
+ return
187
+ lines.append("## Correlations")
188
+ lines.append("")
189
+ for c in correlations:
190
+ t = c["trigger"]
191
+ head = (
192
+ f"- **{t['lane']}** at {t['start_ms']:.2f}ms "
193
+ f"({t['duration_ms']:.0f}ms)"
194
+ )
195
+ if t.get("hang_type"):
196
+ head += f" — {t['hang_type']}"
197
+ lines.append(head)
198
+
199
+ tp = c.get("time_profiler_main_thread")
200
+ if tp is not None:
201
+ cov = tp["main_running_coverage_pct"]
202
+ lines.append(
203
+ f" - Main thread: {tp['samples_on_main']} running samples "
204
+ f"({cov:.0f}% coverage — "
205
+ f"{'blocked' if cov < 25 else 'mostly running'})"
206
+ )
207
+ for s in tp["hot_symbols"][:3]:
208
+ lines.append(
209
+ f" · `{_truncate(s['symbol'], 80)}` "
210
+ f"{s['percent_of_main']:.0f}% ({s['samples']} samples)"
211
+ )
212
+ if not tp["hot_symbols"]:
213
+ lines.append(" · no main-thread samples in window")
214
+
215
+ sui = c.get("swiftui_overlapping_updates")
216
+ if sui:
217
+ for s in sui[:3]:
218
+ lines.append(
219
+ f" - SwiftUI: `{s['view']}` {s['duration_ms']:.2f}ms "
220
+ f"(at {s['start_ms']:.2f}ms)"
221
+ )
222
+ lines.append("")
223
+
224
+
225
+ def _short_thread(name: str) -> str:
226
+ if not name:
227
+ return ""
228
+ if name.startswith("Main Thread") or name == "main":
229
+ return "main"
230
+ # "NowPlaying Gigs (0x251990d) (NowPlaying Gigs, pid: 28401)" -> "tid 0x251990d"
231
+ tid_start = name.find("(0x")
232
+ if tid_start != -1:
233
+ start = tid_start + 1
234
+ end = name.find(")", start)
235
+ if end != -1:
236
+ return f"tid {name[start:end]}"
237
+ return name[:40]
238
+
239
+
240
+ def _truncate(s: str, n: int) -> str:
241
+ if len(s) <= n:
242
+ return s
243
+ return s[: n - 1] + "…"
@@ -0,0 +1,195 @@
1
+ """SwiftUI lane parser (Xcode 26+).
2
+
3
+ Primary schema is `swiftui-updates` with columns: start, duration, id,
4
+ update-type, allocations, description, category, view-hierarchy, module,
5
+ view-name, process, thread, root-causes, severity, cause-graph-node,
6
+ full-cause-graph-node.
7
+
8
+ We aggregate by view-name across all SwiftUI schemas (future-proofing against
9
+ schema renames) and break severity out separately so the agent can focus on
10
+ the high-severity rows.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from collections import Counter, defaultdict
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ from . import xctrace, xml_utils
19
+
20
+ START_KEYS = ("start", "time", "sample-time", "timestamp")
21
+ DURATION_KEYS = ("duration", "body-duration", "update-duration")
22
+ VIEW_KEYS = ("view-name", "view", "view-type", "name", "type")
23
+ MODULE_KEYS = ("module",)
24
+ CATEGORY_KEYS = ("category",)
25
+ UPDATE_TYPE_KEYS = ("update-type",)
26
+ SEVERITY_KEYS = ("severity",)
27
+ DESCRIPTION_KEYS = ("description",)
28
+
29
+ HIGH_SEVERITIES = {"High", "Very High", "Severe", "Critical"}
30
+
31
+ # Ongoing / unterminated updates carry a sentinel duration (≈ UINT64_MAX-ish).
32
+ # Any duration longer than an hour is almost certainly that sentinel and would
33
+ # break aggregates + the correlation overlap check.
34
+ _SENTINEL_DURATION_NS = 60 * 60 * 1_000_000_000 # 1 hour
35
+
36
+
37
+ def analyze(
38
+ trace_path: Path,
39
+ toc_schemas: frozenset[str],
40
+ top_n: int = 10,
41
+ window: tuple[int, int] | None = None,
42
+ run: int = 1,
43
+ ) -> dict[str, Any]:
44
+ schemas = sorted(
45
+ s for s in toc_schemas
46
+ if s.startswith("swiftui") and not s.endswith("-causes")
47
+ )
48
+ if not schemas:
49
+ return {
50
+ "lane": "swiftui",
51
+ "available": False,
52
+ "notes": ["SwiftUI lane not in trace (Xcode 26+ SwiftUI template required)."],
53
+ }
54
+
55
+ events: list[dict] = []
56
+ per_view_total_ns: dict[str, int] = defaultdict(int)
57
+ per_view_count: dict[str, int] = defaultdict(int)
58
+ severity_counts: Counter[str] = Counter()
59
+ update_type_counts: Counter[str] = Counter()
60
+ category_counts: Counter[str] = Counter()
61
+
62
+ for schema in schemas:
63
+ xml_bytes = xctrace.export_schema(trace_path, schema, run=run)
64
+ stream = xml_utils.RowStream(xml_bytes)
65
+ for row in stream:
66
+ start_ns = _first_int(row, stream, START_KEYS)
67
+ dur_ns = _first_int(row, stream, DURATION_KEYS)
68
+ if start_ns is None or dur_ns is None:
69
+ continue
70
+ if dur_ns < 0 or dur_ns > _SENTINEL_DURATION_NS:
71
+ # Unterminated / ongoing update; skip so it doesn't poison
72
+ # totals and the correlation overlap check.
73
+ continue
74
+ if not xml_utils.event_overlaps_window(start_ns, start_ns + dur_ns, window):
75
+ continue
76
+
77
+ view = _first_str(row, stream, VIEW_KEYS)
78
+ module = _first_str(row, stream, MODULE_KEYS)
79
+ category = _first_str(row, stream, CATEGORY_KEYS)
80
+ update_type = _first_str(row, stream, UPDATE_TYPE_KEYS)
81
+ severity = _first_str(row, stream, SEVERITY_KEYS)
82
+ description = _first_str(row, stream, DESCRIPTION_KEYS)
83
+ # Fall back through description → category → update-type so the
84
+ # agent sees "EnvironmentWriter: RootEnvironment" instead of
85
+ # "<unknown>" when SwiftUI doesn't record a view type.
86
+ if not view:
87
+ view = description or category or update_type or "<unknown>"
88
+
89
+ per_view_total_ns[view] += dur_ns
90
+ per_view_count[view] += 1
91
+ if severity:
92
+ severity_counts[severity] += 1
93
+ if update_type:
94
+ update_type_counts[update_type] += 1
95
+ if category:
96
+ category_counts[category] += 1
97
+
98
+ events.append({
99
+ "schema": schema,
100
+ "start_ns": start_ns,
101
+ "end_ns": start_ns + dur_ns,
102
+ "duration_ns": dur_ns,
103
+ "duration_ms": round(dur_ns / 1_000_000, 2),
104
+ "start_ms": round(start_ns / 1_000_000, 2),
105
+ "view": view,
106
+ "module": module,
107
+ "category": category,
108
+ "update_type": update_type,
109
+ "severity": severity,
110
+ "description": description,
111
+ })
112
+
113
+ events.sort(key=lambda e: e["duration_ns"], reverse=True)
114
+
115
+ top_by_total = sorted(
116
+ per_view_total_ns.items(), key=lambda kv: kv[1], reverse=True
117
+ )[:top_n]
118
+ top_offenders = [
119
+ {
120
+ "view": view,
121
+ "total_ms": round(total_ns / 1_000_000, 2),
122
+ "count": per_view_count[view],
123
+ "avg_ms": round(total_ns / per_view_count[view] / 1_000_000, 2),
124
+ }
125
+ for view, total_ns in top_by_total
126
+ ]
127
+
128
+ high_severity = [
129
+ {
130
+ "view": e["view"],
131
+ "severity": e["severity"],
132
+ "duration_ms": e["duration_ms"],
133
+ "start_ms": e["start_ms"],
134
+ "category": e["category"],
135
+ "update_type": e["update_type"],
136
+ "description": e["description"],
137
+ }
138
+ for e in events if e["severity"] in HIGH_SEVERITIES
139
+ ][:top_n]
140
+
141
+ longest = [
142
+ {
143
+ "view": e["view"],
144
+ "duration_ms": e["duration_ms"],
145
+ "start_ms": e["start_ms"],
146
+ "category": e["category"],
147
+ "update_type": e["update_type"],
148
+ "severity": e["severity"],
149
+ }
150
+ for e in events[:top_n]
151
+ ]
152
+
153
+ return {
154
+ "lane": "swiftui",
155
+ "available": True,
156
+ "schemas_used": schemas,
157
+ "metrics": {
158
+ "total_events": len(events),
159
+ "unique_views": len(per_view_total_ns),
160
+ "total_duration_ms": round(
161
+ sum(per_view_total_ns.values()) / 1_000_000, 2
162
+ ),
163
+ "severity_breakdown": dict(severity_counts.most_common()),
164
+ "update_type_breakdown": dict(update_type_counts.most_common()),
165
+ "category_breakdown": dict(category_counts.most_common()),
166
+ },
167
+ "top_offenders": top_offenders,
168
+ "longest_single_events": longest,
169
+ "high_severity_events": high_severity,
170
+ "notes": [],
171
+ "_events": events,
172
+ }
173
+
174
+
175
+ def _first_int(row, stream, keys):
176
+ for key in keys:
177
+ el = row.get(key)
178
+ if el is None:
179
+ continue
180
+ val = xml_utils.int_text(stream.resolve(el))
181
+ if val is not None:
182
+ return val
183
+ return None
184
+
185
+
186
+ def _first_str(row, stream, keys):
187
+ for key in keys:
188
+ el = row.get(key)
189
+ if el is None:
190
+ continue
191
+ resolved = stream.resolve(el)
192
+ txt = xml_utils.str_text(resolved) or resolved.get("fmt")
193
+ if txt:
194
+ return txt
195
+ return None