@hybridlabor-api/aos 4.4.1 → 4.4.2-beta.0

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 (305) hide show
  1. package/.agents/skills/firecrawl/SKILL.md +149 -0
  2. package/.agents/skills/firecrawl/rules/install.md +82 -0
  3. package/.agents/skills/firecrawl/rules/security.md +26 -0
  4. package/.agents/skills/firecrawl-agent/SKILL.md +58 -0
  5. package/.agents/skills/firecrawl-build/SKILL.md +39 -0
  6. package/.agents/skills/firecrawl-build-interact/SKILL.md +68 -0
  7. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +103 -0
  8. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
  9. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
  10. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
  11. package/.agents/skills/firecrawl-build-scrape/SKILL.md +69 -0
  12. package/.agents/skills/firecrawl-build-search/SKILL.md +69 -0
  13. package/.agents/skills/firecrawl-crawl/SKILL.md +59 -0
  14. package/.agents/skills/firecrawl-download/SKILL.md +70 -0
  15. package/.agents/skills/firecrawl-interact/SKILL.md +84 -0
  16. package/.agents/skills/firecrawl-map/SKILL.md +51 -0
  17. package/.agents/skills/firecrawl-scrape/SKILL.md +69 -0
  18. package/.agents/skills/firecrawl-search/SKILL.md +60 -0
  19. package/CLAUDE.md +573 -0
  20. package/README.de.md +0 -8
  21. package/README.md +0 -8
  22. package/README.pt.md +0 -8
  23. package/assets/brand/ao-ant-mark.svg +91 -0
  24. package/assets/brand/bdb-core-mark.svg +34 -0
  25. package/assets/brand/heimdall-guardian-mark.svg +16 -0
  26. package/assets/brand/memb-crystal-mark.svg +18 -0
  27. package/bin/aos-config.mjs +89 -0
  28. package/bin/aos-dashboard.mjs +608 -0
  29. package/bin/aos-uninstall.mjs +227 -0
  30. package/docs/sessions/audit-2026-09-14-silent-failures.md +114 -0
  31. package/docs/skills_table.md +0 -3
  32. package/installer.js +387 -237
  33. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +10 -0
  34. package/mcps/after-effects-mcp/build/index.js +840 -0
  35. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +153 -0
  36. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +218 -0
  37. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +71 -0
  38. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +147 -0
  39. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +114 -0
  40. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +115 -0
  41. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +192 -0
  42. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +90 -0
  43. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +50 -0
  44. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +1773 -0
  45. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +160 -0
  46. package/mcps/computer-use-mcp/dist/client.d.ts +150 -0
  47. package/mcps/computer-use-mcp/dist/client.js +136 -0
  48. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +16 -0
  49. package/mcps/computer-use-mcp/dist/entrypoint.js +26 -0
  50. package/mcps/computer-use-mcp/dist/native.d.ts +212 -0
  51. package/mcps/computer-use-mcp/dist/native.js +50 -0
  52. package/mcps/computer-use-mcp/dist/server.d.ts +32 -0
  53. package/mcps/computer-use-mcp/dist/server.js +342 -0
  54. package/mcps/computer-use-mcp/dist/session.d.ts +101 -0
  55. package/mcps/computer-use-mcp/dist/session.js +2372 -0
  56. package/package.json +13 -4
  57. package/skills/global_config/aos-project-init/SKILL.md +97 -130
  58. package/skills/global_config/aos-project-init/assets/openwikiignore.template +62 -0
  59. package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +38 -1
  60. package/skills/global_config/aos-setup/SKILL.md +98 -8
  61. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +21 -2
  62. package/skills/global_config/ask-tim/SKILL.md +2 -7
  63. package/skills/global_config/openwiki-skill/scripts/openwiki_daemon.py +65 -12
  64. package/bin/setup-saas.mjs +0 -792
  65. package/mcps/blender-mcp-server/.dockerignore +0 -19
  66. package/mcps/blender-mcp-server/.github/workflows/ci.yml +0 -67
  67. package/mcps/blender-mcp-server/.github/workflows/publish-pypi.yml +0 -54
  68. package/mcps/blender-mcp-server/CHANGELOG.md +0 -64
  69. package/mcps/blender-mcp-server/CONTRIBUTING.md +0 -107
  70. package/mcps/blender-mcp-server/Dockerfile +0 -19
  71. package/mcps/blender-mcp-server/LICENSE +0 -21
  72. package/mcps/blender-mcp-server/README.md +0 -409
  73. package/mcps/blender-mcp-server/addon/__init__.py +0 -1440
  74. package/mcps/blender-mcp-server/addon/models.py +0 -223
  75. package/mcps/blender-mcp-server/pyproject.toml +0 -120
  76. package/mcps/blender-mcp-server/scripts/blender_bridge_request.py +0 -71
  77. package/mcps/blender-mcp-server/scripts/blender_create_test_cube.py +0 -49
  78. package/mcps/blender-mcp-server/scripts/blender_scene_info.py +0 -38
  79. package/mcps/blender-mcp-server/scripts/build_addon_zip.sh +0 -29
  80. package/mcps/blender-mcp-server/scripts/demos/README.md +0 -186
  81. package/mcps/blender-mcp-server/scripts/demos/dam_break_scene.py +0 -276
  82. package/mcps/blender-mcp-server/scripts/demos/open_dam_break_gui.py +0 -33
  83. package/mcps/blender-mcp-server/scripts/demos/pipe_movement_study.py +0 -518
  84. package/mcps/blender-mcp-server/scripts/demos/pipe_pressure_failure_scene.py +0 -806
  85. package/mcps/blender-mcp-server/scripts/demos/procedural_dam_break_scene.py +0 -600
  86. package/mcps/blender-mcp-server/scripts/demos/run_dam_break.py +0 -510
  87. package/mcps/blender-mcp-server/scripts/launch_blender_gui.py +0 -41
  88. package/mcps/blender-mcp-server/scripts/library/README.md +0 -64
  89. package/mcps/blender-mcp-server/scripts/library/apply_transforms.py +0 -46
  90. package/mcps/blender-mcp-server/scripts/library/camera.py +0 -51
  91. package/mcps/blender-mcp-server/scripts/library/collections.py +0 -65
  92. package/mcps/blender-mcp-server/scripts/library/create_mesh.py +0 -41
  93. package/mcps/blender-mcp-server/scripts/library/effector.py +0 -48
  94. package/mcps/blender-mcp-server/scripts/library/fluid_domain.py +0 -80
  95. package/mcps/blender-mcp-server/scripts/library/fluid_inflow.py +0 -98
  96. package/mcps/blender-mcp-server/scripts/library/frame_range.py +0 -35
  97. package/mcps/blender-mcp-server/scripts/library/keyframes.py +0 -59
  98. package/mcps/blender-mcp-server/scripts/library/rigid_body.py +0 -52
  99. package/mcps/blender-mcp-server/scripts/library/save_blend.py +0 -40
  100. package/mcps/blender-mcp-server/scripts/server_start.sh +0 -26
  101. package/mcps/blender-mcp-server/src/blender_mcp_server/__init__.py +0 -3
  102. package/mcps/blender-mcp-server/src/blender_mcp_server/headless.py +0 -330
  103. package/mcps/blender-mcp-server/src/blender_mcp_server/server.py +0 -606
  104. package/mcps/davinci-mcp-professional/.bandit +0 -35
  105. package/mcps/davinci-mcp-professional/.claude/commands/security-review.md +0 -191
  106. package/mcps/davinci-mcp-professional/.claude/settings.json +0 -1
  107. package/mcps/davinci-mcp-professional/.editorconfig +0 -27
  108. package/mcps/davinci-mcp-professional/.gitattributes +0 -207
  109. package/mcps/davinci-mcp-professional/.github/CODEOWNERS +0 -2
  110. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
  111. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  112. package/mcps/davinci-mcp-professional/.github/PULL_REQUEST_TEMPLATE.md +0 -20
  113. package/mcps/davinci-mcp-professional/.github/SECURITY.md +0 -97
  114. package/mcps/davinci-mcp-professional/.github/dependabot.yml +0 -27
  115. package/mcps/davinci-mcp-professional/.github/workflows/ci.yml +0 -39
  116. package/mcps/davinci-mcp-professional/ATTRIBUTION.md +0 -4
  117. package/mcps/davinci-mcp-professional/AUTHORS.md +0 -9
  118. package/mcps/davinci-mcp-professional/BUGS.md +0 -55
  119. package/mcps/davinci-mcp-professional/CLAUDE.md +0 -229
  120. package/mcps/davinci-mcp-professional/CONTRIBUTING.md +0 -51
  121. package/mcps/davinci-mcp-professional/COPYING.md +0 -228
  122. package/mcps/davinci-mcp-professional/Doxyfile +0 -379
  123. package/mcps/davinci-mcp-professional/README.md +0 -143
  124. package/mcps/davinci-mcp-professional/SECURITY.md +0 -40
  125. package/mcps/davinci-mcp-professional/USING.md +0 -301
  126. package/mcps/davinci-mcp-professional/VERSION.md +0 -13
  127. package/mcps/davinci-mcp-professional/build.py +0 -169
  128. package/mcps/davinci-mcp-professional/claude_desktop_config_template.json +0 -11
  129. package/mcps/davinci-mcp-professional/hooks/ci-check.sh +0 -29
  130. package/mcps/davinci-mcp-professional/hooks/pre-push +0 -46
  131. package/mcps/davinci-mcp-professional/main.py +0 -40
  132. package/mcps/davinci-mcp-professional/mcp_server.py +0 -30
  133. package/mcps/davinci-mcp-professional/pyproject.toml +0 -115
  134. package/mcps/davinci-mcp-professional/security_audit.py +0 -379
  135. package/mcps/davinci-mcp-professional/src/davinci_mcp/__init__.py +0 -19
  136. package/mcps/davinci-mcp-professional/src/davinci_mcp/cli.py +0 -138
  137. package/mcps/davinci-mcp-professional/src/davinci_mcp/resolve_client.py +0 -342
  138. package/mcps/davinci-mcp-professional/src/davinci_mcp/resources/__init__.py +0 -60
  139. package/mcps/davinci-mcp-professional/src/davinci_mcp/server.py +0 -185
  140. package/mcps/davinci-mcp-professional/src/davinci_mcp/tools/__init__.py +0 -146
  141. package/mcps/davinci-mcp-professional/src/davinci_mcp/types.py +0 -125
  142. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/__init__.py +0 -19
  143. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/platform.py +0 -239
  144. package/mcps/davinci-mcp-professional/test.py +0 -186
  145. package/mcps/davinci-mcp-professional/uv.lock +0 -2073
  146. package/mcps/davinci-resolve-mcp/.claude/skills/media-analysis.md +0 -44
  147. package/mcps/davinci-resolve-mcp/.clinerules +0 -17
  148. package/mcps/davinci-resolve-mcp/.cursorrules +0 -17
  149. package/mcps/davinci-resolve-mcp/.github/copilot-instructions.md +0 -17
  150. package/mcps/davinci-resolve-mcp/.github/workflows/npm-publish.yml +0 -109
  151. package/mcps/davinci-resolve-mcp/.windsurfrules +0 -17
  152. package/mcps/davinci-resolve-mcp/AGENTS.md +0 -114
  153. package/mcps/davinci-resolve-mcp/CHANGELOG.md +0 -2648
  154. package/mcps/davinci-resolve-mcp/CLAUDE.md +0 -15
  155. package/mcps/davinci-resolve-mcp/LICENSE +0 -21
  156. package/mcps/davinci-resolve-mcp/README.md +0 -173
  157. package/mcps/davinci-resolve-mcp/SECURITY.md +0 -53
  158. package/mcps/davinci-resolve-mcp/bin/davinci-resolve-mcp.mjs +0 -450
  159. package/mcps/davinci-resolve-mcp/examples/README.md +0 -53
  160. package/mcps/davinci-resolve-mcp/examples/markers/README.md +0 -81
  161. package/mcps/davinci-resolve-mcp/examples/media/README.md +0 -94
  162. package/mcps/davinci-resolve-mcp/examples/timeline/README.md +0 -98
  163. package/mcps/davinci-resolve-mcp/install.py +0 -1687
  164. package/mcps/davinci-resolve-mcp/package.json +0 -52
  165. package/mcps/davinci-resolve-mcp/release-notes/v2.22.0.md +0 -51
  166. package/mcps/davinci-resolve-mcp/release-notes/v2.23.0.md +0 -51
  167. package/mcps/davinci-resolve-mcp/release-notes/v2.23.1.md +0 -35
  168. package/mcps/davinci-resolve-mcp/release-notes/v2.24.1.md +0 -32
  169. package/mcps/davinci-resolve-mcp/scripts/audit_api_parity.py +0 -275
  170. package/mcps/davinci-resolve-mcp/scripts/audit_readwrite_symmetry.py +0 -80
  171. package/mcps/davinci-resolve-mcp/scripts/doctor.py +0 -227
  172. package/mcps/davinci-resolve-mcp/scripts/gen_api_limitations.py +0 -144
  173. package/mcps/davinci-resolve-mcp/scripts/live_media_analysis_polish_probe.py +0 -65
  174. package/mcps/davinci-resolve-mcp/scripts/measure_bridge_cost.py +0 -65
  175. package/mcps/davinci-resolve-mcp/scripts/regen_panel_screenshots.py +0 -76
  176. package/mcps/davinci-resolve-mcp/src/__init__.py +0 -3
  177. package/mcps/davinci-resolve-mcp/src/analysis_dashboard.py +0 -15505
  178. package/mcps/davinci-resolve-mcp/src/batch_cli.py +0 -540
  179. package/mcps/davinci-resolve-mcp/src/control_panel.py +0 -13
  180. package/mcps/davinci-resolve-mcp/src/granular/__init__.py +0 -17
  181. package/mcps/davinci-resolve-mcp/src/granular/common.py +0 -741
  182. package/mcps/davinci-resolve-mcp/src/granular/folder.py +0 -420
  183. package/mcps/davinci-resolve-mcp/src/granular/gallery.py +0 -306
  184. package/mcps/davinci-resolve-mcp/src/granular/graph.py +0 -309
  185. package/mcps/davinci-resolve-mcp/src/granular/media_pool.py +0 -679
  186. package/mcps/davinci-resolve-mcp/src/granular/media_pool_item.py +0 -1020
  187. package/mcps/davinci-resolve-mcp/src/granular/media_storage.py +0 -179
  188. package/mcps/davinci-resolve-mcp/src/granular/project.py +0 -1654
  189. package/mcps/davinci-resolve-mcp/src/granular/resolve_control.py +0 -538
  190. package/mcps/davinci-resolve-mcp/src/granular/timeline.py +0 -1075
  191. package/mcps/davinci-resolve-mcp/src/granular/timeline_item.py +0 -2251
  192. package/mcps/davinci-resolve-mcp/src/resolve_mcp_server.py +0 -43
  193. package/mcps/davinci-resolve-mcp/src/server.py +0 -24116
  194. package/mcps/davinci-resolve-mcp/src/utils/__init__.py +0 -3
  195. package/mcps/davinci-resolve-mcp/src/utils/actor_identity.py +0 -46
  196. package/mcps/davinci-resolve-mcp/src/utils/analysis_caps.py +0 -678
  197. package/mcps/davinci-resolve-mcp/src/utils/analysis_memory.py +0 -755
  198. package/mcps/davinci-resolve-mcp/src/utils/analysis_runs.py +0 -307
  199. package/mcps/davinci-resolve-mcp/src/utils/analysis_store.py +0 -945
  200. package/mcps/davinci-resolve-mcp/src/utils/api_truth.py +0 -525
  201. package/mcps/davinci-resolve-mcp/src/utils/app_control.py +0 -319
  202. package/mcps/davinci-resolve-mcp/src/utils/audio_fairlight_live_probe.py +0 -263
  203. package/mcps/davinci-resolve-mcp/src/utils/brain_edits.py +0 -387
  204. package/mcps/davinci-resolve-mcp/src/utils/bridge_metrics.py +0 -76
  205. package/mcps/davinci-resolve-mcp/src/utils/cdl.py +0 -20
  206. package/mcps/davinci-resolve-mcp/src/utils/clip_query.py +0 -85
  207. package/mcps/davinci-resolve-mcp/src/utils/cloud_operations.py +0 -192
  208. package/mcps/davinci-resolve-mcp/src/utils/color_grade_live_probe.py +0 -444
  209. package/mcps/davinci-resolve-mcp/src/utils/contracts.py +0 -102
  210. package/mcps/davinci-resolve-mcp/src/utils/cut_ir.py +0 -115
  211. package/mcps/davinci-resolve-mcp/src/utils/dctl_templates.py +0 -368
  212. package/mcps/davinci-resolve-mcp/src/utils/deep_vision.py +0 -643
  213. package/mcps/davinci-resolve-mcp/src/utils/destructive_hook.py +0 -606
  214. package/mcps/davinci-resolve-mcp/src/utils/edit_engine.py +0 -737
  215. package/mcps/davinci-resolve-mcp/src/utils/embeddings.py +0 -922
  216. package/mcps/davinci-resolve-mcp/src/utils/entities.py +0 -579
  217. package/mcps/davinci-resolve-mcp/src/utils/extension_authoring_live_probe.py +0 -292
  218. package/mcps/davinci-resolve-mcp/src/utils/failure_tracker.py +0 -119
  219. package/mcps/davinci-resolve-mcp/src/utils/fuse_templates.py +0 -1968
  220. package/mcps/davinci-resolve-mcp/src/utils/fusion_composition_live_probe.py +0 -284
  221. package/mcps/davinci-resolve-mcp/src/utils/fusion_group_settings.py +0 -334
  222. package/mcps/davinci-resolve-mcp/src/utils/layout_presets.py +0 -333
  223. package/mcps/davinci-resolve-mcp/src/utils/mcp_stdio.py +0 -32
  224. package/mcps/davinci-resolve-mcp/src/utils/mcp_transport.py +0 -128
  225. package/mcps/davinci-resolve-mcp/src/utils/media_analysis.py +0 -7690
  226. package/mcps/davinci-resolve-mcp/src/utils/media_analysis_jobs.py +0 -805
  227. package/mcps/davinci-resolve-mcp/src/utils/media_pool_changes.py +0 -121
  228. package/mcps/davinci-resolve-mcp/src/utils/media_pool_ingest_live_probe.py +0 -592
  229. package/mcps/davinci-resolve-mcp/src/utils/multicam.py +0 -393
  230. package/mcps/davinci-resolve-mcp/src/utils/object_inspection.py +0 -301
  231. package/mcps/davinci-resolve-mcp/src/utils/page_lock.py +0 -75
  232. package/mcps/davinci-resolve-mcp/src/utils/platform.py +0 -157
  233. package/mcps/davinci-resolve-mcp/src/utils/proc.py +0 -26
  234. package/mcps/davinci-resolve-mcp/src/utils/project_cleanup.py +0 -73
  235. package/mcps/davinci-resolve-mcp/src/utils/project_lifecycle_live_probe.py +0 -376
  236. package/mcps/davinci-resolve-mcp/src/utils/project_lint.py +0 -131
  237. package/mcps/davinci-resolve-mcp/src/utils/project_properties.py +0 -601
  238. package/mcps/davinci-resolve-mcp/src/utils/project_spec.py +0 -459
  239. package/mcps/davinci-resolve-mcp/src/utils/readback.py +0 -91
  240. package/mcps/davinci-resolve-mcp/src/utils/render_deliver_live_probe.py +0 -384
  241. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_governance.py +0 -214
  242. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_ledger.py +0 -244
  243. package/mcps/davinci-resolve-mcp/src/utils/resolve_busy.py +0 -171
  244. package/mcps/davinci-resolve-mcp/src/utils/resolve_connection.py +0 -77
  245. package/mcps/davinci-resolve-mcp/src/utils/review_annotation_live_probe.py +0 -352
  246. package/mcps/davinci-resolve-mcp/src/utils/script_templates.py +0 -1193
  247. package/mcps/davinci-resolve-mcp/src/utils/shot_relationships.py +0 -544
  248. package/mcps/davinci-resolve-mcp/src/utils/structural_diff.py +0 -175
  249. package/mcps/davinci-resolve-mcp/src/utils/sync_detection.py +0 -889
  250. package/mcps/davinci-resolve-mcp/src/utils/timeline_brain_db.py +0 -826
  251. package/mcps/davinci-resolve-mcp/src/utils/timeline_conform_live_probe.py +0 -280
  252. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_live_probe.py +0 -1092
  253. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_probe.py +0 -185
  254. package/mcps/davinci-resolve-mcp/src/utils/timeline_title_text.py +0 -87
  255. package/mcps/davinci-resolve-mcp/src/utils/timeline_versioning.py +0 -730
  256. package/mcps/davinci-resolve-mcp/src/utils/update_check.py +0 -685
  257. package/mcps/vectorworks-mcp/.dockerignore +0 -8
  258. package/mcps/vectorworks-mcp/Dockerfile +0 -28
  259. package/mcps/vectorworks-mcp/README.md +0 -156
  260. package/mcps/vectorworks-mcp/app/__init__.py +0 -2
  261. package/mcps/vectorworks-mcp/app/api.py +0 -52
  262. package/mcps/vectorworks-mcp/app/chunking.py +0 -56
  263. package/mcps/vectorworks-mcp/app/config.py +0 -29
  264. package/mcps/vectorworks-mcp/app/embeddings.py +0 -28
  265. package/mcps/vectorworks-mcp/app/indexer.py +0 -106
  266. package/mcps/vectorworks-mcp/app/main.py +0 -33
  267. package/mcps/vectorworks-mcp/app/mcp_server.py +0 -71
  268. package/mcps/vectorworks-mcp/app/search.py +0 -54
  269. package/mcps/vectorworks-mcp/app/utils.py +0 -14
  270. package/mcps/vectorworks-mcp/data/.gitkeep +0 -1
  271. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_PluginScript.htm +0 -95
  272. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_ScriptEditor.htm +0 -266
  273. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_Scripting.htm +0 -106
  274. package/mcps/vectorworks-mcp/data/vw-app-help/VW2023_CreatingScriptedPlugins.htm +0 -327
  275. package/mcps/vectorworks-mcp/data/vw-app-help/VW2024_RunningScripts.htm +0 -116
  276. package/mcps/vectorworks-mcp/data/vw-devwiki/VS_Function_Reference_category.html +0 -2129
  277. package/mcps/vectorworks-mcp/data/vw-jp-ref/ObjectsSymbols.html +0 -2035
  278. package/mcps/vectorworks-mcp/data/vw-jp-ref/ScriptFunctionReference.html +0 -29
  279. package/mcps/vectorworks-mcp/design.md +0 -57
  280. package/mcps/vectorworks-mcp/docker-compose.yml +0 -18
  281. package/mcps/vectorworks-mcp/index/.gitkeep +0 -1
  282. package/mcps/vectorworks-mcp/index/meta.jsonl +0 -3956
  283. package/mcps/vectorworks-mcp/index/vw.faiss +0 -0
  284. package/mcps/vectorworks-mcp/requirements.txt +0 -10
  285. package/mcps/vectorworks-mcp/scripts/fetch_docs_minimal.sh +0 -84
  286. package/mcps/vectorworks-mcp/scripts/fetch_github_vectorworks.sh +0 -85
  287. package/mcps/vectorworks-mcp/templates/index.html +0 -40
  288. package/scripts/ecosystem-health-audit.js +0 -257
  289. package/skills/bdb-dev-os-skill/SKILL.md +0 -49
  290. package/skills/bdb-dev-os-skill/package.json +0 -11
  291. package/skills/bdbsaastraining/SKILL.md +0 -316
  292. package/skills/bdbsaastraining/references/exam-pool.md +0 -313
  293. package/skills/bdbsaastraining/references/interaction-modes.md +0 -94
  294. package/skills/bdbsaastraining/references/track-a-ai-agent.md +0 -111
  295. package/skills/bdbsaastraining/references/track-b-wordpress.md +0 -111
  296. package/skills/bdbsaastraining/references/track-c-mailserver.md +0 -107
  297. package/skills/bdbsaastraining/references/track-d-debian.md +0 -109
  298. package/skills/bdbsaastraining/references/track-e-custom.md +0 -83
  299. package/skills/bdbsaastraining/scripts/build_profile.py +0 -529
  300. package/skills/bdbsaastraining/scripts/generate_certificate.py +0 -161
  301. package/skills/bdbsaastraining/scripts/preflight_check.sh +0 -150
  302. package/skills/bdbsaastraining/templates/certificate_template.html +0 -508
  303. package/skills/global_config/bdb-ecosystem-health/SKILL.md +0 -68
  304. package/skills/global_config/bdbsaas-ops/SKILL.md +0 -16
  305. package/skills/global_config/bdbsaashost/SKILL.md +0 -125
@@ -1,1092 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Live timeline edit kernel boundary probe.
3
-
4
- Creates a disposable Resolve project with synthetic media, probes the deepest
5
- timeline-editing API surfaces this project knows about, writes JSON/Markdown
6
- evidence reports, and deletes the project unless --keep-open is provided.
7
-
8
- Run with Python 3.10-3.12 against a running Resolve Studio instance:
9
-
10
- python3.11 tests/live_duplicate_clips_validation.py --output-dir /tmp/timeline-kernel-probe
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- import json
16
- import platform
17
- import subprocess
18
- import sys
19
- import tempfile
20
- import time
21
- import types
22
- import traceback
23
- from pathlib import Path
24
- from typing import Any, Dict, Iterable, List, Optional, Tuple
25
-
26
- from src.utils.timeline_kernel_probe import (
27
- ProbeRecorder,
28
- ordered_unique,
29
- parse_api_class_methods,
30
- parse_timeline_item_property_keys,
31
- render_markdown_report,
32
- utc_timestamp,
33
- values_match,
34
- )
35
-
36
- PROPERTY_CANDIDATES: Dict[str, Any] = {
37
- "Pan": 0.33,
38
- "Tilt": -0.25,
39
- "ZoomX": 1.15,
40
- "ZoomY": 1.1,
41
- "ZoomGang": False,
42
- "RotationAngle": 8.0,
43
- "AnchorPointX": 0.05,
44
- "AnchorPointY": -0.05,
45
- "Pitch": 0.1,
46
- "Yaw": -0.1,
47
- "FlipX": True,
48
- "FlipY": False,
49
- "CropLeft": 2.0,
50
- "CropRight": 1.0,
51
- "CropTop": 1.0,
52
- "CropBottom": 2.0,
53
- "CropSoftness": 4.0,
54
- "CropRetain": True,
55
- "DynamicZoomEnable": True,
56
- "DynamicZoomMode": 1,
57
- "DynamicZoomEase": 2,
58
- "CompositeMode": 1,
59
- "Opacity": 72.0,
60
- "Distortion": 0.1,
61
- "Speed": 100.0,
62
- "RetimeProcess": 1,
63
- "MotionEstimation": 1,
64
- "Scaling": 2,
65
- "ResizeFilter": 3,
66
- "StabilizationEnable": True,
67
- "StabilizationMethod": 1,
68
- "StabilizationStrength": 0.75,
69
- "Volume": -6.0,
70
- "AudioSyncOffsetIsManual": False,
71
- "AudioSyncOffset": 0,
72
- "EQEnable": False,
73
- "NormalizeEnable": False,
74
- "NormalizeLevel": -12.0,
75
- }
76
-
77
- EXTRA_TIMELINE_METHODS = [
78
- "GetItemsInTrack",
79
- "GetCurrentVideoItem",
80
- "GetCurrentTimecode",
81
- "SetCurrentTimecode",
82
- ]
83
-
84
- EXTRA_TIMELINE_ITEM_METHODS = [
85
- "GetType",
86
- "GetMediaType",
87
- "AddKeyframe",
88
- "GetKeyframeCount",
89
- "GetKeyframeAtIndex",
90
- "GetPropertyAtKeyframeIndex",
91
- "SetKeyframeInterpolation",
92
- "GetIsColorOutputCacheEnabled",
93
- "SetColorOutputCache",
94
- "GetIsFusionOutputCacheEnabled",
95
- "SetFusionOutputCache",
96
- "GetVoiceIsolationState",
97
- "SetVoiceIsolationState",
98
- ]
99
-
100
-
101
- def _install_mcp_stubs() -> None:
102
- """Allow importing src.server when MCP deps are absent from Python 3.11."""
103
-
104
- class FastMCP:
105
- def __init__(self, *args, **kwargs):
106
- pass
107
-
108
- def tool(self, *args, **kwargs):
109
- def decorate(func):
110
- return func
111
-
112
- return decorate
113
-
114
- def resource(self, *args, **kwargs):
115
- def decorate(func):
116
- return func
117
-
118
- return decorate
119
-
120
- def stdio_server(*args, **kwargs):
121
- raise RuntimeError("stdio_server is not used by the live timeline kernel probe")
122
-
123
- anyio = types.ModuleType("anyio")
124
- anyio.run = lambda func: func()
125
-
126
- mcp = types.ModuleType("mcp")
127
- server = types.ModuleType("mcp.server")
128
- fastmcp = types.ModuleType("mcp.server.fastmcp")
129
- stdio = types.ModuleType("mcp.server.stdio")
130
-
131
- fastmcp.FastMCP = FastMCP
132
- stdio.stdio_server = stdio_server
133
-
134
- sys.modules.setdefault("anyio", anyio)
135
- sys.modules.setdefault("mcp", mcp)
136
- sys.modules.setdefault("mcp.server", server)
137
- sys.modules.setdefault("mcp.server.fastmcp", fastmcp)
138
- sys.modules.setdefault("mcp.server.stdio", stdio)
139
-
140
-
141
- def _require_success(label: str, result: Dict[str, Any]) -> Dict[str, Any]:
142
- if not isinstance(result, dict):
143
- raise AssertionError(f"{label}: expected dict, got {result!r}")
144
- if result.get("error"):
145
- raise AssertionError(f"{label}: {result['error']}")
146
- if "success" in result and result["success"] is not True:
147
- raise AssertionError(f"{label}: expected success=True, got {result!r}")
148
- return result
149
-
150
-
151
- def _frame_int(value) -> int:
152
- return int(round(float(value)))
153
-
154
-
155
- def _source_start(item) -> int:
156
- if hasattr(item, "GetSourceStartFrame"):
157
- try:
158
- value = item.GetSourceStartFrame()
159
- if value is not None:
160
- return _frame_int(value)
161
- except Exception:
162
- pass
163
- return _frame_int(item.GetLeftOffset())
164
-
165
-
166
- def _make_synthetic_media(work_dir: Path) -> Path:
167
- media_path = work_dir / "timeline_kernel_probe_source.mov"
168
- subprocess.run(
169
- [
170
- "ffmpeg",
171
- "-hide_banner",
172
- "-loglevel",
173
- "error",
174
- "-f",
175
- "lavfi",
176
- "-i",
177
- "testsrc2=size=320x180:rate=24:duration=5",
178
- "-f",
179
- "lavfi",
180
- "-i",
181
- "sine=frequency=440:sample_rate=48000:duration=5",
182
- "-shortest",
183
- "-pix_fmt",
184
- "yuv420p",
185
- "-c:v",
186
- "libx264",
187
- "-c:a",
188
- "aac",
189
- "-y",
190
- str(media_path),
191
- ],
192
- check=True,
193
- )
194
- return media_path
195
-
196
-
197
- def _safe_call(func, *args):
198
- try:
199
- return func(*args), None
200
- except Exception as exc:
201
- return None, exc
202
-
203
-
204
- def _safe_item_id(item) -> Optional[str]:
205
- if not item:
206
- return None
207
- try:
208
- return str(item.GetUniqueId())
209
- except Exception:
210
- return None
211
-
212
-
213
- def _safe_timeline_id(timeline) -> Optional[str]:
214
- if not timeline:
215
- return None
216
- try:
217
- return str(timeline.GetUniqueId())
218
- except Exception:
219
- return None
220
-
221
-
222
- def _ensure_current_timeline(
223
- recorder: ProbeRecorder,
224
- project,
225
- timeline,
226
- label: str,
227
- *,
228
- required: bool = False,
229
- ) -> bool:
230
- """Best-effort current timeline guard for Resolve bridge versions with flaky setters."""
231
- if not project or not timeline:
232
- if required:
233
- raise AssertionError("No project or timeline available to set current timeline")
234
- recorder.record(
235
- "runtime.current_timeline",
236
- label,
237
- "unsupported",
238
- details={"reason": "No project or timeline available"},
239
- )
240
- return False
241
-
242
- target_id = _safe_timeline_id(timeline)
243
- get_current = getattr(project, "GetCurrentTimeline", None)
244
- if callable(get_current):
245
- current, current_exc = _safe_call(get_current)
246
- if current:
247
- current_id = _safe_timeline_id(current)
248
- if not target_id or not current_id or current_id == target_id:
249
- return True
250
- elif current_exc:
251
- recorder.record(
252
- "runtime.current_timeline",
253
- f"{label}.get_current",
254
- "error",
255
- details={"exception": repr(current_exc)},
256
- )
257
-
258
- setter = getattr(project, "SetCurrentTimeline", None)
259
- if callable(setter):
260
- ok, set_exc = _safe_call(setter, timeline)
261
- if ok:
262
- return True
263
- recorder.record(
264
- "runtime.current_timeline",
265
- f"{label}.set_current",
266
- "partially_supported",
267
- details={"returned": ok, "exception": repr(set_exc) if set_exc else None},
268
- )
269
- else:
270
- recorder.record(
271
- "runtime.current_timeline",
272
- f"{label}.set_current",
273
- "version_or_page_dependent",
274
- details={"reason": "Project.SetCurrentTimeline is not callable in this Resolve bridge state"},
275
- )
276
-
277
- if callable(get_current):
278
- current, _ = _safe_call(get_current)
279
- if current:
280
- current_id = _safe_timeline_id(current)
281
- if not target_id or not current_id or current_id == target_id:
282
- return True
283
-
284
- if required:
285
- raise AssertionError("Failed to create or set current timeline")
286
- return False
287
-
288
-
289
- def _record_method_availability(
290
- recorder: ProbeRecorder,
291
- obj,
292
- category: str,
293
- class_name: str,
294
- method_names: Iterable[str],
295
- ) -> None:
296
- for method_name in ordered_unique(method_names):
297
- recorder.record(
298
- category,
299
- f"{class_name}.{method_name}",
300
- "supported" if callable(getattr(obj, method_name, None)) else "unsupported",
301
- )
302
-
303
-
304
- def _record_tool_result(
305
- recorder: ProbeRecorder,
306
- category: str,
307
- name: str,
308
- result: Dict[str, Any],
309
- *,
310
- expected_boundary: bool = False,
311
- ) -> None:
312
- if not isinstance(result, dict):
313
- recorder.record(category, name, "error", details={"reason": "non-dict result", "result": repr(result)})
314
- return
315
- if result.get("error"):
316
- recorder.record(
317
- category,
318
- name,
319
- "unsupported" if expected_boundary else "error",
320
- details={"reason": result.get("error"), "expected_boundary": expected_boundary},
321
- evidence=result,
322
- )
323
- return
324
- if "success" in result and result["success"] is not True:
325
- recorder.record(
326
- category,
327
- name,
328
- "partially_supported",
329
- details={"reason": "success flag was false"},
330
- evidence=result,
331
- )
332
- return
333
- if result.get("results") and any(row.get("success") is False for row in result["results"] if isinstance(row, dict)):
334
- recorder.record(category, name, "partially_supported", evidence=result)
335
- return
336
- recorder.record(category, name, "supported", evidence=result)
337
-
338
-
339
- def _probe_property(recorder: ProbeRecorder, item, item_label: str, key: str) -> None:
340
- get_property = getattr(item, "GetProperty", None)
341
- set_property = getattr(item, "SetProperty", None)
342
- if not callable(get_property):
343
- recorder.record(f"properties.{item_label}", key, "unsupported", details={"reason": "GetProperty missing"})
344
- return
345
-
346
- original, read_error = _safe_call(get_property, key)
347
- read_available = read_error is None and original is not None
348
- candidate = PROPERTY_CANDIDATES.get(key, original if original is not None else 1)
349
-
350
- if not callable(set_property):
351
- status = "read_only" if read_available else "unsupported"
352
- recorder.record(
353
- f"properties.{item_label}",
354
- key,
355
- status,
356
- details={"read": original, "reason": "SetProperty missing" if read_available else "property unavailable"},
357
- )
358
- return
359
-
360
- write_result, write_error = _safe_call(set_property, key, candidate)
361
- readback, readback_error = _safe_call(get_property, key)
362
- restore_result = None
363
- if read_available and write_result:
364
- restore_result, _ = _safe_call(set_property, key, original)
365
-
366
- details = {
367
- "read": original,
368
- "write": bool(write_result) if write_error is None else False,
369
- "write_error": repr(write_error) if write_error else None,
370
- "readback": readback,
371
- "readback_error": repr(readback_error) if readback_error else None,
372
- "restore": bool(restore_result) if restore_result is not None else None,
373
- }
374
-
375
- if read_error is not None:
376
- status = "error"
377
- details["read_error"] = repr(read_error)
378
- elif write_error is not None:
379
- status = "partially_supported" if read_available else "unsupported"
380
- elif not write_result:
381
- status = "partially_supported" if read_available else "unsupported"
382
- details["reason"] = "SetProperty returned false"
383
- elif readback is None:
384
- status = "write_only_unverifiable"
385
- elif values_match(readback, candidate):
386
- status = "supported"
387
- else:
388
- status = "partially_supported"
389
- details["reason"] = "SetProperty returned true but readback did not match"
390
-
391
- recorder.record(f"properties.{item_label}", key, status, details=details)
392
-
393
-
394
- def _probe_keyframe(recorder: ProbeRecorder, item, item_label: str, key: str) -> None:
395
- add_keyframe = getattr(item, "AddKeyframe", None)
396
- get_count = getattr(item, "GetKeyframeCount", None)
397
- get_at_index = getattr(item, "GetKeyframeAtIndex", None)
398
- get_value = getattr(item, "GetPropertyAtKeyframeIndex", None)
399
- if not callable(add_keyframe):
400
- recorder.record(f"keyframes.{item_label}", key, "unsupported", details={"reason": "AddKeyframe missing"})
401
- return
402
-
403
- before = None
404
- if callable(get_count):
405
- before, _ = _safe_call(get_count, key)
406
- value = PROPERTY_CANDIDATES.get(key, 1)
407
- added, add_error = _safe_call(add_keyframe, key, 0, value)
408
- after = None
409
- if callable(get_count):
410
- after, _ = _safe_call(get_count, key)
411
-
412
- evidence: List[Dict[str, Any]] = []
413
- if callable(get_at_index) and callable(get_value) and after:
414
- for index in range(int(after)):
415
- keyframe, keyframe_error = _safe_call(get_at_index, key, index)
416
- prop_value, value_error = _safe_call(get_value, key, index)
417
- evidence.append(
418
- {
419
- "index": index,
420
- "keyframe": keyframe,
421
- "keyframe_error": repr(keyframe_error) if keyframe_error else None,
422
- "value": prop_value,
423
- "value_error": repr(value_error) if value_error else None,
424
- }
425
- )
426
-
427
- details = {
428
- "before": before,
429
- "added": bool(added) if add_error is None else False,
430
- "add_error": repr(add_error) if add_error else None,
431
- "after": after,
432
- "read_methods": {
433
- "GetKeyframeCount": callable(get_count),
434
- "GetKeyframeAtIndex": callable(get_at_index),
435
- "GetPropertyAtKeyframeIndex": callable(get_value),
436
- },
437
- }
438
- if add_error:
439
- status = "error"
440
- elif added and after is not None and int(after) > int(before or 0):
441
- status = "supported"
442
- elif added:
443
- status = "write_only_unverifiable"
444
- elif before is not None:
445
- status = "partially_supported"
446
- details["reason"] = "AddKeyframe returned false"
447
- else:
448
- status = "unsupported"
449
- details["reason"] = "AddKeyframe returned false and keyframe readback is unavailable"
450
- recorder.record(f"keyframes.{item_label}", key, status, details=details, evidence=evidence)
451
-
452
-
453
- def _probe_markers_flags_color_enabled(recorder: ProbeRecorder, item, item_label: str) -> None:
454
- marker_frame = 9
455
- add_marker = getattr(item, "AddMarker", None)
456
- if callable(add_marker):
457
- added, exc = _safe_call(add_marker, marker_frame, "Green", "Probe marker", "Timeline kernel probe", 1, "kernel-probe")
458
- markers, markers_exc = _safe_call(item.GetMarkers) if callable(getattr(item, "GetMarkers", None)) else ({}, None)
459
- custom, custom_exc = (
460
- _safe_call(item.GetMarkerCustomData, marker_frame)
461
- if callable(getattr(item, "GetMarkerCustomData", None))
462
- else (None, None)
463
- )
464
- deleted, delete_exc = (
465
- _safe_call(item.DeleteMarkerByCustomData, "kernel-probe")
466
- if callable(getattr(item, "DeleteMarkerByCustomData", None))
467
- else (None, None)
468
- )
469
- status = "supported" if added and markers_exc is None else "partially_supported"
470
- recorder.record(
471
- f"metadata.{item_label}",
472
- "markers",
473
- status,
474
- details={
475
- "added": bool(added),
476
- "add_error": repr(exc) if exc else None,
477
- "marker_count": len(markers or {}) if isinstance(markers, dict) else None,
478
- "custom_data": custom,
479
- "custom_error": repr(custom_exc) if custom_exc else None,
480
- "deleted": bool(deleted) if deleted is not None else None,
481
- "delete_error": repr(delete_exc) if delete_exc else None,
482
- },
483
- )
484
- else:
485
- recorder.record(f"metadata.{item_label}", "markers", "unsupported", details={"reason": "AddMarker missing"})
486
-
487
- if callable(getattr(item, "AddFlag", None)):
488
- added, exc = _safe_call(item.AddFlag, "Green")
489
- flags, flags_exc = _safe_call(item.GetFlagList) if callable(getattr(item, "GetFlagList", None)) else ([], None)
490
- cleared, clear_exc = _safe_call(item.ClearFlags, "Green") if callable(getattr(item, "ClearFlags", None)) else (None, None)
491
- recorder.record(
492
- f"metadata.{item_label}",
493
- "flags",
494
- "supported" if added and flags_exc is None else "partially_supported",
495
- details={
496
- "added": bool(added),
497
- "add_error": repr(exc) if exc else None,
498
- "flags": flags,
499
- "flags_error": repr(flags_exc) if flags_exc else None,
500
- "cleared": bool(cleared) if cleared is not None else None,
501
- "clear_error": repr(clear_exc) if clear_exc else None,
502
- },
503
- )
504
- else:
505
- recorder.record(f"metadata.{item_label}", "flags", "unsupported", details={"reason": "AddFlag missing"})
506
-
507
- if callable(getattr(item, "SetClipColor", None)):
508
- original, _ = _safe_call(item.GetClipColor) if callable(getattr(item, "GetClipColor", None)) else (None, None)
509
- set_result, exc = _safe_call(item.SetClipColor, "Teal")
510
- readback, read_exc = _safe_call(item.GetClipColor) if callable(getattr(item, "GetClipColor", None)) else (None, None)
511
- clear_result, clear_exc = _safe_call(item.ClearClipColor) if callable(getattr(item, "ClearClipColor", None)) else (None, None)
512
- if original:
513
- _safe_call(item.SetClipColor, original)
514
- recorder.record(
515
- f"metadata.{item_label}",
516
- "clip_color",
517
- "supported" if set_result and readback == "Teal" else "partially_supported",
518
- details={
519
- "read": original,
520
- "write": bool(set_result),
521
- "write_error": repr(exc) if exc else None,
522
- "readback": readback,
523
- "readback_error": repr(read_exc) if read_exc else None,
524
- "clear": bool(clear_result) if clear_result is not None else None,
525
- "clear_error": repr(clear_exc) if clear_exc else None,
526
- },
527
- )
528
- else:
529
- recorder.record(f"metadata.{item_label}", "clip_color", "unsupported", details={"reason": "SetClipColor missing"})
530
-
531
- if callable(getattr(item, "SetClipEnabled", None)):
532
- original, _ = _safe_call(item.GetClipEnabled) if callable(getattr(item, "GetClipEnabled", None)) else (None, None)
533
- set_result, exc = _safe_call(item.SetClipEnabled, False)
534
- readback, read_exc = _safe_call(item.GetClipEnabled) if callable(getattr(item, "GetClipEnabled", None)) else (None, None)
535
- if original is not None:
536
- _safe_call(item.SetClipEnabled, bool(original))
537
- recorder.record(
538
- f"metadata.{item_label}",
539
- "enabled_state",
540
- "supported" if set_result and readback is False else "partially_supported",
541
- details={
542
- "read": original,
543
- "write": bool(set_result),
544
- "write_error": repr(exc) if exc else None,
545
- "readback": readback,
546
- "readback_error": repr(read_exc) if read_exc else None,
547
- },
548
- )
549
- else:
550
- recorder.record(f"metadata.{item_label}", "enabled_state", "unsupported", details={"reason": "SetClipEnabled missing"})
551
-
552
-
553
- def _probe_cache_voice_takes_fusion_grade(
554
- recorder: ProbeRecorder,
555
- item,
556
- duplicate_item,
557
- media_pool_item,
558
- item_label: str,
559
- output_dir: Path,
560
- ) -> None:
561
- for cache_name, getter_name, setter_name, value in (
562
- ("color_cache", "GetIsColorOutputCacheEnabled", "SetColorOutputCache", "On"),
563
- ("fusion_cache", "GetIsFusionOutputCacheEnabled", "SetFusionOutputCache", "Auto"),
564
- ):
565
- getter = getattr(item, getter_name, None)
566
- setter = getattr(item, setter_name, None)
567
- if not callable(getter) or not callable(setter):
568
- recorder.record(f"advanced.{item_label}", cache_name, "unsupported", details={"reason": "cache API missing"})
569
- continue
570
- original, read_exc = _safe_call(getter)
571
- set_result, set_exc = _safe_call(setter, value)
572
- readback, readback_exc = _safe_call(getter)
573
- if original is not None:
574
- _safe_call(setter, original)
575
- recorder.record(
576
- f"advanced.{item_label}",
577
- cache_name,
578
- "supported" if set_result and readback is not None else "partially_supported",
579
- details={
580
- "read": original,
581
- "read_error": repr(read_exc) if read_exc else None,
582
- "write": bool(set_result),
583
- "write_error": repr(set_exc) if set_exc else None,
584
- "readback": readback,
585
- "readback_error": repr(readback_exc) if readback_exc else None,
586
- },
587
- )
588
-
589
- if callable(getattr(item, "GetVoiceIsolationState", None)) and callable(getattr(item, "SetVoiceIsolationState", None)):
590
- original, read_exc = _safe_call(item.GetVoiceIsolationState)
591
- set_result, set_exc = _safe_call(item.SetVoiceIsolationState, {"isEnabled": True, "amount": 25})
592
- readback, readback_exc = _safe_call(item.GetVoiceIsolationState)
593
- if original:
594
- _safe_call(item.SetVoiceIsolationState, original)
595
- recorder.record(
596
- f"advanced.{item_label}",
597
- "item_voice_isolation",
598
- "supported" if set_result and readback else "partially_supported",
599
- details={
600
- "read": original,
601
- "read_error": repr(read_exc) if read_exc else None,
602
- "write": bool(set_result),
603
- "write_error": repr(set_exc) if set_exc else None,
604
- "readback": readback,
605
- "readback_error": repr(readback_exc) if readback_exc else None,
606
- },
607
- )
608
- else:
609
- recorder.record(
610
- f"advanced.{item_label}",
611
- "item_voice_isolation",
612
- "unsupported",
613
- details={"reason": "item voice isolation API missing"},
614
- )
615
-
616
- if callable(getattr(item, "AddTake", None)):
617
- added, add_exc = _safe_call(item.AddTake, media_pool_item, 24, 71)
618
- count, count_exc = _safe_call(item.GetTakesCount) if callable(getattr(item, "GetTakesCount", None)) else (None, None)
619
- take, take_exc = (
620
- _safe_call(item.GetTakeByIndex, int(count or 1))
621
- if callable(getattr(item, "GetTakeByIndex", None)) and count
622
- else (None, None)
623
- )
624
- selected, select_exc = (
625
- _safe_call(item.SelectTakeByIndex, int(count))
626
- if callable(getattr(item, "SelectTakeByIndex", None)) and count
627
- else (None, None)
628
- )
629
- deleted, delete_exc = (
630
- _safe_call(item.DeleteTakeByIndex, int(count))
631
- if callable(getattr(item, "DeleteTakeByIndex", None)) and count
632
- else (None, None)
633
- )
634
- recorder.record(
635
- f"advanced.{item_label}",
636
- "takes",
637
- "supported" if added and count else "partially_supported",
638
- details={
639
- "added": bool(added),
640
- "add_error": repr(add_exc) if add_exc else None,
641
- "count": count,
642
- "count_error": repr(count_exc) if count_exc else None,
643
- "take_read": bool(take),
644
- "take_error": repr(take_exc) if take_exc else None,
645
- "selected": bool(selected) if selected is not None else None,
646
- "select_error": repr(select_exc) if select_exc else None,
647
- "deleted": bool(deleted) if deleted is not None else None,
648
- "delete_error": repr(delete_exc) if delete_exc else None,
649
- },
650
- )
651
- else:
652
- recorder.record(f"advanced.{item_label}", "takes", "unsupported", details={"reason": "AddTake missing"})
653
-
654
- if callable(getattr(item, "AddFusionComp", None)):
655
- comp, add_exc = _safe_call(item.AddFusionComp)
656
- count, count_exc = _safe_call(item.GetFusionCompCount) if callable(getattr(item, "GetFusionCompCount", None)) else (None, None)
657
- names, names_exc = (
658
- _safe_call(item.GetFusionCompNameList)
659
- if callable(getattr(item, "GetFusionCompNameList", None))
660
- else (None, None)
661
- )
662
- export_path = output_dir / f"{item_label}_fusion_comp.setting"
663
- exported, export_exc = (
664
- _safe_call(item.ExportFusionComp, str(export_path), 1)
665
- if callable(getattr(item, "ExportFusionComp", None)) and count
666
- else (None, None)
667
- )
668
- recorder.record(
669
- f"advanced.{item_label}",
670
- "fusion_comps",
671
- "supported" if comp and count else "partially_supported",
672
- details={
673
- "added": bool(comp),
674
- "add_error": repr(add_exc) if add_exc else None,
675
- "count": count,
676
- "count_error": repr(count_exc) if count_exc else None,
677
- "names": names,
678
- "names_error": repr(names_exc) if names_exc else None,
679
- "exported": bool(exported) if exported is not None else None,
680
- "export_error": repr(export_exc) if export_exc else None,
681
- "export_path": str(export_path) if exported else None,
682
- },
683
- )
684
- else:
685
- recorder.record(f"advanced.{item_label}", "fusion_comps", "unsupported", details={"reason": "AddFusionComp missing"})
686
-
687
- if callable(getattr(item, "CopyGrades", None)) and duplicate_item:
688
- copied, copy_exc = _safe_call(item.CopyGrades, [duplicate_item])
689
- recorder.record(
690
- f"advanced.{item_label}",
691
- "copy_grades",
692
- "supported" if copied else "partially_supported",
693
- details={"copied": bool(copied), "copy_error": repr(copy_exc) if copy_exc else None},
694
- )
695
- else:
696
- recorder.record(f"advanced.{item_label}", "copy_grades", "unsupported", details={"reason": "CopyGrades missing"})
697
-
698
-
699
- def _probe_timeline_operations(recorder: ProbeRecorder, server, timeline, source_id: str, source_duration: int) -> Optional[str]:
700
- duplicate_ids: List[str] = []
701
-
702
- def duplicate(name: str, params: Dict[str, Any]) -> Optional[str]:
703
- payload = dict(params)
704
- payload.setdefault("clip_ids", [source_id])
705
- result = server.timeline("duplicate_clips", payload)
706
- _record_tool_result(recorder, "operations.duplicate", name, result)
707
- try:
708
- item_id = result["results"][0]["timeline_item_id"]
709
- except Exception:
710
- return None
711
- if item_id:
712
- duplicate_ids.append(item_id)
713
- return item_id
714
-
715
- duplicate("same_time_cross_track", {"placement": "same_time", "target_track_index": 2})
716
- duplicate("offset", {"record_frame_offset": 180, "target_track_index": 2})
717
- duplicate("track_above", {"placement": "track_above"})
718
- duplicate("after_source", {"placement": "after_source"})
719
- duplicate("next_gap", {"placement": "next_gap", "target_track_index": 2})
720
- server.timeline_markers("set_current_timecode", {"timecode": "00:00:28:00"})
721
- duplicate("at_playhead", {"placement": "at_playhead", "target_track_index": 2})
722
- duplicate("include_linked_audio", {"record_frame": 780, "target_track_index": 2, "include_linked": True})
723
-
724
- copy_alias = server.timeline("copy_clips", {"clip_ids": [source_id], "target_track_index": 2, "record_frame": 840})
725
- _record_tool_result(recorder, "operations.duplicate", "copy_clips_alias", copy_alias)
726
-
727
- selected = server.timeline("duplicate_clips", {"selected": True, "target_track_index": 2, "record_frame": 880})
728
- if isinstance(selected, dict) and selected.get("error"):
729
- recorder.record(
730
- "operations.duplicate",
731
- "selected_or_current_fallback",
732
- "version_or_page_dependent",
733
- details={"reason": selected.get("error")},
734
- evidence=selected,
735
- )
736
- else:
737
- _record_tool_result(recorder, "operations.duplicate", "selected_or_current_fallback", selected)
738
-
739
- copy_range = server.timeline(
740
- "copy_range",
741
- {"start_frame": 110, "end_frame": 130, "record_frame": 930, "track_types": ["video"], "target_track_index": 2},
742
- )
743
- _record_tool_result(recorder, "operations.range", "copy_range", copy_range)
744
-
745
- duplicate_range = server.timeline(
746
- "duplicate_range",
747
- {"start_frame": 112, "end_frame": 128, "record_frame": 970, "track_types": ["video"], "target_track_index": 2},
748
- )
749
- _record_tool_result(recorder, "operations.range", "duplicate_range", duplicate_range)
750
-
751
- occupant_id = duplicate("overwrite_destination_fixture", {"target_track_index": 2, "record_frame": 1020})
752
- overwrite = server.timeline(
753
- "overwrite_range",
754
- {"start_frame": 100, "end_frame": 120, "record_frame": 1020, "track_types": ["video"], "target_track_index": 2},
755
- )
756
- _record_tool_result(recorder, "operations.range", "overwrite_range", overwrite)
757
- if occupant_id and not server._find_timeline_item_by_id(timeline, occupant_id):
758
- recorder.record("operations.range", "overwrite_deleted_destination_overlap", "supported")
759
- elif occupant_id:
760
- recorder.record("operations.range", "overwrite_deleted_destination_overlap", "partially_supported")
761
-
762
- lift_fixture_id = duplicate("lift_fixture", {"target_track_index": 1, "record_frame": 1120})
763
- lift = server.timeline(
764
- "lift_range",
765
- {"start_frame": 1120, "end_frame": 1120 + source_duration, "track_types": ["video"], "track_indices": [1]},
766
- )
767
- _record_tool_result(recorder, "operations.range", "lift_range_exact_item", lift)
768
- if lift_fixture_id and not server._find_timeline_item_by_id(timeline, lift_fixture_id):
769
- recorder.record("operations.range", "lift_deleted_exact_item", "supported")
770
- elif lift_fixture_id:
771
- recorder.record("operations.range", "lift_deleted_exact_item", "partially_supported")
772
-
773
- partial_lift = server.timeline(
774
- "lift_range",
775
- {"start_frame": 105, "end_frame": 110, "track_types": ["video"], "track_indices": [1]},
776
- )
777
- _record_tool_result(recorder, "operations.boundaries", "partial_lift_without_razor", partial_lift, expected_boundary=True)
778
-
779
- move_fixture_id = duplicate("move_fixture", {"target_track_index": 1, "record_frame": 1220})
780
- if move_fixture_id:
781
- move = server.timeline(
782
- "move_clips",
783
- {"clip_ids": [move_fixture_id], "target_track_index": 2, "record_frame": 1280},
784
- )
785
- _record_tool_result(recorder, "operations.duplicate", "move_clips", move)
786
-
787
- invalid_track = server.timeline("duplicate_clips", {"clip_ids": [source_id], "target_track_index": 99})
788
- _record_tool_result(recorder, "operations.boundaries", "invalid_target_track", invalid_track, expected_boundary=True)
789
-
790
- return duplicate_ids[0] if duplicate_ids else None
791
-
792
-
793
- def _probe_source_less_boundaries(recorder: ProbeRecorder, server, timeline) -> None:
794
- probes: List[Tuple[str, Any]] = []
795
- for name, call in (
796
- ("insert_fusion_composition", lambda: timeline.InsertFusionCompositionIntoTimeline()),
797
- ("insert_title_text", lambda: timeline.InsertTitleIntoTimeline("Text")),
798
- ):
799
- item, exc = _safe_call(call)
800
- if exc:
801
- recorder.record("operations.source_less", name, "error", details={"exception": repr(exc)})
802
- continue
803
- if not item:
804
- recorder.record("operations.source_less", name, "unsupported", details={"reason": "Resolve returned no item"})
805
- continue
806
- probes.append((name, item))
807
- recorder.record("operations.source_less", name, "supported", details={"timeline_item_id": _safe_item_id(item)})
808
-
809
- for name, item in probes:
810
- item_id = _safe_item_id(item)
811
- if not item_id:
812
- continue
813
- result = server.timeline("duplicate_clips", {"clip_ids": [item_id], "target_track_index": 2, "record_frame": 1400})
814
- _record_tool_result(
815
- recorder,
816
- "operations.boundaries",
817
- f"{name}_append_clone_without_media_pool_item",
818
- result,
819
- expected_boundary=True,
820
- )
821
-
822
-
823
- def _probe_track_controls(recorder: ProbeRecorder, server, timeline) -> None:
824
- for track_type, index in (("video", 1), ("audio", 1)):
825
- name = f"{track_type}.{index}"
826
- original_name = None
827
- try:
828
- original_name = timeline.GetTrackName(track_type, index)
829
- except Exception:
830
- pass
831
- result = server.timeline("set_track_name", {"track_type": track_type, "index": index, "name": f"Probe {name}"})
832
- _record_tool_result(recorder, "operations.tracks", f"set_track_name.{name}", result)
833
- enabled = server.timeline("set_track_enable", {"track_type": track_type, "index": index, "enabled": True})
834
- _record_tool_result(recorder, "operations.tracks", f"set_track_enable.{name}", enabled)
835
- locked = server.timeline("set_track_lock", {"track_type": track_type, "index": index, "locked": False})
836
- _record_tool_result(recorder, "operations.tracks", f"set_track_lock.{name}", locked)
837
- if original_name:
838
- server.timeline("set_track_name", {"track_type": track_type, "index": index, "name": original_name})
839
-
840
- voice_get = server.timeline("get_voice_isolation_state", {"track_index": 1})
841
- _record_tool_result(recorder, "operations.tracks", "audio_track_voice_isolation_get", voice_get)
842
- voice_set = server.timeline("set_voice_isolation_state", {"track_index": 1, "state": {"isEnabled": True, "amount": 20}})
843
- _record_tool_result(recorder, "operations.tracks", "audio_track_voice_isolation_set", voice_set)
844
-
845
-
846
- def run_probe(server, output_dir: Path, keep_open: bool = False) -> Dict[str, Any]:
847
- recorder = ProbeRecorder()
848
- project_name = f"_mcp_timeline_kernel_probe_{int(time.time())}"
849
- timeline_name = "timeline_kernel_boundary_probe"
850
- work_dir = Path(tempfile.mkdtemp(prefix="mcp_timeline_kernel_probe_"))
851
- created_project = False
852
- delete_result = None
853
- metadata: Dict[str, Any] = {
854
- "timestamp_utc": utc_timestamp(),
855
- "python": sys.version.replace("\n", " "),
856
- "platform": platform.platform(),
857
- "project_name": project_name,
858
- }
859
-
860
- try:
861
- version = _require_success("resolve_control.get_version", server.resolve_control("get_version"))
862
- metadata.update(
863
- {
864
- "product": version.get("product"),
865
- "version": version.get("version"),
866
- "version_string": version.get("version_string"),
867
- }
868
- )
869
- print(f"Connected to {metadata['product']} {metadata['version_string']}")
870
-
871
- _require_success("project_manager.create", server.project_manager("create", {"name": project_name}))
872
- created_project = True
873
- print(f"Created disposable project: {project_name}")
874
- _require_success("resolve_control.open_page", server.resolve_control("open_page", {"page": "edit"}))
875
-
876
- media_path = _make_synthetic_media(work_dir)
877
- metadata["synthetic_media"] = str(media_path)
878
- print(f"Generated synthetic media: {media_path}")
879
-
880
- resolve = server.get_resolve()
881
- project = resolve.GetProjectManager().GetCurrentProject()
882
- media_pool = project.GetMediaPool()
883
- imported = media_pool.ImportMedia([str(media_path)])
884
- if not imported:
885
- raise AssertionError(f"Failed to import synthetic media: {media_path}")
886
- media_pool_item = imported[0]
887
- media_pool_item_id = media_pool_item.GetUniqueId()
888
-
889
- timeline = media_pool.CreateEmptyTimeline(timeline_name)
890
- if not timeline:
891
- raise AssertionError("Failed to create timeline")
892
- _ensure_current_timeline(recorder, project, timeline, "initial", required=True)
893
-
894
- for _ in range(max(0, 3 - int(timeline.GetTrackCount("video") or 0))):
895
- _require_success("timeline.add_track video", server.timeline("add_track", {"track_type": "video"}))
896
- for _ in range(max(0, 2 - int(timeline.GetTrackCount("audio") or 0))):
897
- _require_success(
898
- "timeline.add_track audio",
899
- server.timeline("add_track", {"track_type": "audio", "options": {"audio_type": "stereo"}}),
900
- )
901
- server.timeline("set_start_timecode", {"timecode": "00:00:00:00"})
902
-
903
- append = _require_success(
904
- "media_pool.append_to_timeline video",
905
- server.media_pool(
906
- "append_to_timeline",
907
- {
908
- "clip_infos": [
909
- {
910
- "media_pool_item_id": media_pool_item_id,
911
- "start_frame": 24,
912
- "end_frame": 71,
913
- "record_frame": 100,
914
- "track_index": 1,
915
- "media_type": 1,
916
- }
917
- ]
918
- },
919
- ),
920
- )
921
- source_id = append["items"][0]["timeline_item_id"]
922
- source_item = server._find_timeline_item_by_id(timeline, source_id)
923
- if not source_item:
924
- raise AssertionError(f"Could not recover source item: {source_id}")
925
-
926
- audio_append = _require_success(
927
- "media_pool.append_to_timeline linked audio",
928
- server.media_pool(
929
- "append_to_timeline",
930
- {
931
- "clip_infos": [
932
- {
933
- "media_pool_item_id": media_pool_item_id,
934
- "start_frame": 24,
935
- "end_frame": 71,
936
- "record_frame": 100,
937
- "track_index": 1,
938
- "media_type": 2,
939
- }
940
- ]
941
- },
942
- ),
943
- )
944
- audio_id = audio_append["items"][0]["timeline_item_id"]
945
- audio_item = server._find_timeline_item_by_id(timeline, audio_id)
946
- if not audio_item:
947
- raise AssertionError(f"Could not recover audio item: {audio_id}")
948
- timeline.SetClipsLinked([source_item, audio_item], True)
949
-
950
- source_duration = _frame_int(source_item.GetDuration())
951
- metadata["source"] = {
952
- "timeline_item_id": source_id,
953
- "duration": source_duration,
954
- "source_start": _source_start(source_item),
955
- }
956
-
957
- api_text = Path("docs/reference/resolve_scripting_api.txt").read_text(encoding="utf-8")
958
- documented_property_keys = parse_timeline_item_property_keys(api_text)
959
- local_property_keys = []
960
- for keys in server._DUPLICATE_COPY_PROPERTY_KEYS.values():
961
- local_property_keys.extend(keys)
962
- property_keys = ordered_unique(documented_property_keys + local_property_keys + list(PROPERTY_CANDIDATES))
963
-
964
- timeline_methods = ordered_unique(parse_api_class_methods(api_text, "Timeline") + EXTRA_TIMELINE_METHODS)
965
- timeline_item_methods = ordered_unique(parse_api_class_methods(api_text, "TimelineItem") + EXTRA_TIMELINE_ITEM_METHODS)
966
- _record_method_availability(recorder, timeline, "runtime_methods.timeline", "Timeline", timeline_methods)
967
- _record_method_availability(recorder, source_item, "runtime_methods.timeline_item.video", "TimelineItem", timeline_item_methods)
968
- _record_method_availability(recorder, audio_item, "runtime_methods.timeline_item.audio", "TimelineItem", timeline_item_methods)
969
-
970
- duplicate_for_advanced_id = _probe_timeline_operations(recorder, server, timeline, source_id, source_duration)
971
- duplicate_for_advanced = (
972
- server._find_timeline_item_by_id(timeline, duplicate_for_advanced_id) if duplicate_for_advanced_id else None
973
- )
974
- _probe_track_controls(recorder, server, timeline)
975
- _probe_source_less_boundaries(recorder, server, timeline)
976
- _ensure_current_timeline(recorder, project, timeline, "before_property_probe")
977
-
978
- for key in property_keys:
979
- _probe_property(recorder, source_item, "video", key)
980
- _probe_property(recorder, audio_item, "audio", key)
981
-
982
- for key in property_keys:
983
- _probe_keyframe(recorder, source_item, "video", key)
984
-
985
- _probe_markers_flags_color_enabled(recorder, source_item, "video")
986
- _probe_markers_flags_color_enabled(recorder, audio_item, "audio")
987
- _probe_cache_voice_takes_fusion_grade(
988
- recorder,
989
- source_item,
990
- duplicate_for_advanced,
991
- media_pool_item,
992
- "video",
993
- output_dir,
994
- )
995
-
996
- _ensure_current_timeline(recorder, project, timeline, "before_mcp_capabilities")
997
- capabilities = server.timeline("edit_kernel_capabilities")
998
- _record_tool_result(recorder, "mcp_capabilities", "edit_kernel_capabilities", capabilities)
999
- probe = server.timeline("probe_edit_kernel_item", {"clip_ids": [source_id, audio_id]})
1000
- if isinstance(probe, dict) and probe.get("error") == "No current timeline":
1001
- recorder.record(
1002
- "mcp_capabilities",
1003
- "probe_edit_kernel_item",
1004
- "version_or_page_dependent",
1005
- details={
1006
- "reason": (
1007
- "Project.SetCurrentTimeline was not callable after source-less item probing; "
1008
- "the live validation covers this tool when Resolve keeps a current timeline"
1009
- )
1010
- },
1011
- evidence=probe,
1012
- )
1013
- direct_probe = server._timeline_probe_edit_kernel_item(timeline, {"clip_ids": [source_id, audio_id]})
1014
- if isinstance(direct_probe, dict) and direct_probe.get("error"):
1015
- recorder.record(
1016
- "mcp_capabilities",
1017
- "probe_edit_kernel_item_direct_timeline_object",
1018
- "version_or_page_dependent",
1019
- details={
1020
- "reason": (
1021
- "The retained timeline object could not resolve the original item IDs after "
1022
- "source-less item probing changed Resolve's timeline focus"
1023
- )
1024
- },
1025
- evidence=direct_probe,
1026
- )
1027
- else:
1028
- _record_tool_result(
1029
- recorder,
1030
- "mcp_capabilities",
1031
- "probe_edit_kernel_item_direct_timeline_object",
1032
- direct_probe,
1033
- )
1034
- else:
1035
- _record_tool_result(recorder, "mcp_capabilities", "probe_edit_kernel_item", probe)
1036
-
1037
- recorder.record(
1038
- "operations.boundaries",
1039
- "transition_cloning",
1040
- "unsupported",
1041
- details={"reason": "Resolve public API exposes no transition cloning primitive on TimelineItem"},
1042
- )
1043
- recorder.record(
1044
- "operations.boundaries",
1045
- "razor_or_split",
1046
- "unsupported",
1047
- details={"reason": "Resolve public API exposes no direct timeline split/razor primitive"},
1048
- )
1049
- recorder.record(
1050
- "operations.boundaries",
1051
- "opaque_speed_ramp_internals",
1052
- "partially_supported",
1053
- details={
1054
- "reason": "Speed/RetimeProcess/MotionEstimation and keyframes are visible when Resolve exposes them; opaque speed ramp curves are not independently inspectable"
1055
- },
1056
- )
1057
-
1058
- if keep_open:
1059
- server.project_manager("save")
1060
- print(f"LEFT PROJECT OPEN FOR INSPECTION: {project_name}")
1061
- created_project = False
1062
-
1063
- except Exception as exc:
1064
- recorder.record(
1065
- "probe",
1066
- "fatal",
1067
- "error",
1068
- details={"exception": repr(exc), "traceback": traceback.format_exc()},
1069
- )
1070
- raise
1071
- finally:
1072
- if created_project:
1073
- server.project_manager("save")
1074
- server.project_manager("close")
1075
- delete_result = server.project_manager("delete", {"name": project_name})
1076
- print(f"Deleted disposable project: {delete_result}")
1077
-
1078
- if delete_result is not None and delete_result.get("success") is not True:
1079
- raise AssertionError(f"Cleanup failed for {project_name}: {delete_result!r}")
1080
-
1081
- output_dir.mkdir(parents=True, exist_ok=True)
1082
- metadata["output_dir"] = str(output_dir)
1083
- report = recorder.to_report(metadata, artifacts={})
1084
- json_path = output_dir / "timeline-edit-kernel-probe.json"
1085
- markdown_path = output_dir / "timeline-edit-kernel-probe.md"
1086
- report["artifacts"] = {"json": str(json_path), "markdown": str(markdown_path)}
1087
- json_path.write_text(json.dumps(report, indent=2, sort_keys=True, default=str), encoding="utf-8")
1088
- markdown_path.write_text(render_markdown_report(report), encoding="utf-8")
1089
- print(f"Wrote JSON report: {json_path}")
1090
- print(f"Wrote Markdown report: {markdown_path}")
1091
- print(f"Counts: {json.dumps(report['counts'], sort_keys=True)}")
1092
- return report