@salesforce/afv-skills 1.13.0 → 1.15.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 (359) hide show
  1. package/package.json +3 -3
  2. package/skills/applying-slds/SKILL.md +322 -0
  3. package/skills/applying-slds/checklists.md +83 -0
  4. package/skills/applying-slds/examples.md +283 -0
  5. package/skills/applying-slds/guidance/README.md +83 -0
  6. package/skills/applying-slds/guidance/blueprints-index.md +213 -0
  7. package/skills/applying-slds/guidance/icons-guidance.md +186 -0
  8. package/skills/applying-slds/guidance/overviews/borders.md +236 -0
  9. package/skills/applying-slds/guidance/overviews/color.md +266 -0
  10. package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
  11. package/skills/applying-slds/guidance/overviews/icons.md +240 -0
  12. package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
  13. package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
  14. package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
  15. package/skills/applying-slds/guidance/overviews/typography.md +323 -0
  16. package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
  17. package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
  18. package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
  19. package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
  20. package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
  21. package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
  22. package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
  23. package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
  24. package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
  25. package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
  26. package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
  27. package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
  28. package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
  29. package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
  30. package/skills/applying-slds/guidance/utilities/borders.md +131 -0
  31. package/skills/applying-slds/guidance/utilities/box.md +125 -0
  32. package/skills/applying-slds/guidance/utilities/color.md +165 -0
  33. package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
  34. package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
  35. package/skills/applying-slds/guidance/utilities/floats.md +117 -0
  36. package/skills/applying-slds/guidance/utilities/grid.md +264 -0
  37. package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
  38. package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
  39. package/skills/applying-slds/guidance/utilities/index.md +205 -0
  40. package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
  41. package/skills/applying-slds/guidance/utilities/layout.md +109 -0
  42. package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
  43. package/skills/applying-slds/guidance/utilities/margin.md +155 -0
  44. package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
  45. package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
  46. package/skills/applying-slds/guidance/utilities/padding.md +155 -0
  47. package/skills/applying-slds/guidance/utilities/position.md +177 -0
  48. package/skills/applying-slds/guidance/utilities/print.md +114 -0
  49. package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
  50. package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
  51. package/skills/applying-slds/guidance/utilities/themes.md +121 -0
  52. package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
  53. package/skills/applying-slds/guidance/utilities/typography.md +166 -0
  54. package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
  55. package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
  56. package/skills/applying-slds/metadata/README.md +84 -0
  57. package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
  58. package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
  59. package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
  60. package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
  61. package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
  62. package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
  63. package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
  64. package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
  65. package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
  66. package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
  67. package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
  68. package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
  69. package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
  70. package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
  71. package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
  72. package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
  73. package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
  74. package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
  75. package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
  76. package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
  77. package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
  78. package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
  79. package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
  80. package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
  81. package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
  82. package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
  83. package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
  84. package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
  85. package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
  86. package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
  87. package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
  88. package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
  89. package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
  90. package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
  91. package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
  92. package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
  93. package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
  94. package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
  95. package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
  96. package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
  97. package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
  98. package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
  99. package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
  100. package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
  101. package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
  102. package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
  103. package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
  104. package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
  105. package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
  106. package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
  107. package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
  108. package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
  109. package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
  110. package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
  111. package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
  112. package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
  113. package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
  114. package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
  115. package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
  116. package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
  117. package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
  118. package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
  119. package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
  120. package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
  121. package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
  122. package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
  123. package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
  124. package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
  125. package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
  126. package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
  127. package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
  128. package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
  129. package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
  130. package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
  131. package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
  132. package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
  133. package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
  134. package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
  135. package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
  136. package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
  137. package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
  138. package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
  139. package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
  140. package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
  141. package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
  142. package/skills/applying-slds/metadata/hooks-index.json +6272 -0
  143. package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
  144. package/skills/applying-slds/metadata/utilities-index.json +21912 -0
  145. package/skills/applying-slds/references/component-selection.md +112 -0
  146. package/skills/applying-slds/references/icons-decision-guide.md +124 -0
  147. package/skills/applying-slds/references/styling-decision-guide.md +228 -0
  148. package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
  149. package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
  150. package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
  151. package/skills/applying-slds/scripts/search-icons.cjs +174 -0
  152. package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
  153. package/skills/building-ui-bundle-app/SKILL.md +33 -8
  154. package/skills/generating-custom-application/SKILL.md +1 -1
  155. package/skills/generating-custom-lightning-type/SKILL.md +17 -39
  156. package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
  157. package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
  158. package/skills/generating-ui-bundle-custom-app/SKILL.md +93 -0
  159. package/skills/generating-ui-bundle-custom-app/docs/configure-metadata-custom-application.md +70 -0
  160. package/skills/generating-ui-bundle-metadata/SKILL.md +39 -1
  161. package/skills/investigating-agentforce-architecture/README.md +156 -0
  162. package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
  163. package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
  164. package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
  165. package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
  166. package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
  167. package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
  168. package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
  169. package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
  170. package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
  171. package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
  172. package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
  173. package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
  174. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
  175. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
  176. package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
  177. package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
  178. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
  179. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
  180. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
  181. package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
  182. package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
  183. package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
  184. package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
  185. package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
  186. package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
  187. package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
  188. package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
  189. package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
  190. package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
  191. package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
  192. package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
  193. package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
  194. package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
  195. package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
  196. package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
  197. package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
  198. package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
  199. package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
  200. package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
  201. package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
  202. package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
  203. package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
  204. package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
  205. package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
  206. package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
  207. package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
  208. package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
  209. package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
  210. package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
  211. package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
  212. package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
  213. package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
  214. package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
  215. package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
  216. package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
  217. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
  218. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
  219. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
  220. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
  221. package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
  222. package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
  223. package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
  224. package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
  225. package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
  226. package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
  227. package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
  228. package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
  229. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
  230. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
  231. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
  232. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
  233. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
  234. package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
  235. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
  236. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
  237. package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
  238. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
  239. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
  240. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
  241. package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
  242. package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
  243. package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
  244. package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
  245. package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
  246. package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
  247. package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
  248. package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
  249. package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
  250. package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
  251. package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
  252. package/skills/investigating-agentforce-d360/README.md +123 -0
  253. package/skills/investigating-agentforce-d360/SKILL.md +163 -0
  254. package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
  255. package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
  256. package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
  257. package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
  258. package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
  259. package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
  260. package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
  261. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
  262. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
  263. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
  264. package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
  265. package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
  266. package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
  267. package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
  268. package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
  269. package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
  270. package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
  271. package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
  272. package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
  273. package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
  274. package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
  275. package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
  276. package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
  277. package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
  278. package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
  279. package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
  280. package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
  281. package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
  282. package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
  283. package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
  284. package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
  285. package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
  286. package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
  287. package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
  288. package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
  289. package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
  290. package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
  291. package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
  292. package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
  293. package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
  294. package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
  295. package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
  296. package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
  297. package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
  298. package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
  299. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
  300. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
  301. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
  302. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
  303. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
  304. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
  305. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
  306. package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
  307. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
  308. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
  309. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
  310. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
  311. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
  312. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
  313. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
  314. package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
  315. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
  316. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
  317. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
  318. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
  319. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
  320. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
  321. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
  322. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
  323. package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
  324. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
  325. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
  326. package/skills/managing-managed-event-subscription/SKILL.md +152 -0
  327. package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
  328. package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
  329. package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
  330. package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
  331. package/skills/reviewing-lwc-mobile-offline/SKILL.md +168 -0
  332. package/skills/reviewing-lwc-mobile-offline/references/grounding.md +7 -0
  333. package/skills/reviewing-lwc-mobile-offline/references/inline-graphql.md +43 -0
  334. package/skills/reviewing-lwc-mobile-offline/references/komaci-eslint.md +125 -0
  335. package/skills/reviewing-lwc-mobile-offline/references/lwc-if.md +78 -0
  336. package/skills/reviewing-lwc-mobile-offline/scripts/komaci.config.mjs +18 -0
  337. package/skills/reviewing-lwc-mobile-offline/scripts/package.json +10 -0
  338. package/skills/reviewing-lwc-mobile-offline/scripts/run-komaci.sh +69 -0
  339. package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
  340. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
  341. package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
  342. package/skills/using-mobile-native-capabilities/SKILL.md +182 -0
  343. package/skills/using-mobile-native-capabilities/references/app-review.md +68 -0
  344. package/skills/using-mobile-native-capabilities/references/ar-space-capture.md +125 -0
  345. package/skills/using-mobile-native-capabilities/references/barcode-scanner.md +219 -0
  346. package/skills/using-mobile-native-capabilities/references/base-capability.md +22 -0
  347. package/skills/using-mobile-native-capabilities/references/biometrics.md +90 -0
  348. package/skills/using-mobile-native-capabilities/references/calendar.md +213 -0
  349. package/skills/using-mobile-native-capabilities/references/contacts.md +232 -0
  350. package/skills/using-mobile-native-capabilities/references/document-scanner.md +342 -0
  351. package/skills/using-mobile-native-capabilities/references/geofencing.md +123 -0
  352. package/skills/using-mobile-native-capabilities/references/location.md +158 -0
  353. package/skills/using-mobile-native-capabilities/references/mobile-capabilities.md +30 -0
  354. package/skills/using-mobile-native-capabilities/references/nfc.md +181 -0
  355. package/skills/using-mobile-native-capabilities/references/payments.md +95 -0
  356. package/skills/validating-slds/SKILL.md +262 -0
  357. package/skills/validating-slds/references/quality-checks.md +308 -0
  358. package/skills/validating-slds/references/report-format.md +302 -0
  359. package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
@@ -0,0 +1,224 @@
1
+ """Tests for ``parse_wave`` classifiers + BFS step helper.
2
+
3
+ Targets the high-density branch surface in ``classify_bundle_action``
4
+ (Flow / Apex / PromptTemplate / StandardAction / Unknown) and
5
+ ``classify_action_call`` (apex / generatePromptResponse / other / empty).
6
+ Plus the central ``bfs_step`` helper that drives wave-level fetches.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import unittest
11
+
12
+ from . import _bootstrap # noqa: F401 — sys.path setup
13
+
14
+ import parse_wave # type: ignore
15
+
16
+
17
+ # -----------------------------------------------------------------------------
18
+ # classify_bundle_action — every branch
19
+ # -----------------------------------------------------------------------------
20
+
21
+
22
+ class ClassifyBundleActionTests(unittest.TestCase):
23
+
24
+ def test_no_target_returns_none_pair(self):
25
+ unwraps, leaf = parse_wave.classify_bundle_action({
26
+ "invocationTarget": "",
27
+ "invocationTargetType": "flow",
28
+ })
29
+ self.assertIsNone(unwraps)
30
+ self.assertIsNone(leaf)
31
+
32
+ def test_flow_branch(self):
33
+ unwraps, leaf = parse_wave.classify_bundle_action({
34
+ "invocationTarget": "MyFlow",
35
+ "invocationTargetType": "flow",
36
+ })
37
+ self.assertEqual(unwraps["kind"], "FLOW")
38
+ self.assertEqual(unwraps["api_name"], "MyFlow")
39
+ self.assertEqual(leaf["children"], [])
40
+
41
+ def test_apex_branch(self):
42
+ unwraps, leaf = parse_wave.classify_bundle_action({
43
+ "invocationTarget": "MyClass.method",
44
+ "invocationTargetType": "apex",
45
+ })
46
+ self.assertEqual(unwraps["kind"], "APEX")
47
+ self.assertEqual(leaf["api_name"], "MyClass.method")
48
+
49
+ def test_prompt_template_via_generatepromptresponse(self):
50
+ unwraps, _ = parse_wave.classify_bundle_action({
51
+ "invocationTarget": "MyPrompt",
52
+ "invocationTargetType": "generatepromptresponse",
53
+ })
54
+ self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
55
+
56
+ def test_prompt_template_via_prompt_prefix(self):
57
+ unwraps, _ = parse_wave.classify_bundle_action({
58
+ "invocationTarget": "MyPrompt",
59
+ "invocationTargetType": "prompt-template",
60
+ })
61
+ self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
62
+
63
+ def test_prompt_template_via_genai_prefix(self):
64
+ unwraps, _ = parse_wave.classify_bundle_action({
65
+ "invocationTarget": "MyPrompt",
66
+ "invocationTargetType": "genaifunction",
67
+ })
68
+ self.assertEqual(unwraps["kind"], "PROMPT_TEMPLATE")
69
+
70
+ def test_standard_invocable_action_branch(self):
71
+ unwraps, leaf = parse_wave.classify_bundle_action({
72
+ "invocationTarget": "createRecord",
73
+ "invocationTargetType": "standardinvocableaction",
74
+ })
75
+ self.assertEqual(unwraps["kind"], "STANDARD_ACTION")
76
+ self.assertEqual(unwraps["invocation_type"], "standardinvocableaction")
77
+ self.assertEqual(leaf["api_name"], "createRecord")
78
+
79
+ def test_unknown_action_type_falls_to_unknown(self):
80
+ unwraps, leaf = parse_wave.classify_bundle_action({
81
+ "invocationTarget": "MysteryAction",
82
+ "invocationTargetType": "MYSTERY",
83
+ })
84
+ self.assertEqual(unwraps["kind"], "UNKNOWN")
85
+ self.assertEqual(unwraps["invocation_type"], "mystery") # lowercased
86
+ self.assertEqual(leaf["kind"], "UNKNOWN")
87
+
88
+ def test_invocationTargetType_missing_normalizes_to_empty_string(self):
89
+ unwraps, _ = parse_wave.classify_bundle_action({
90
+ "invocationTarget": "MyAction",
91
+ # no invocationTargetType — coerced to ""
92
+ })
93
+ self.assertEqual(unwraps["kind"], "UNKNOWN")
94
+ self.assertEqual(unwraps["invocation_type"], "")
95
+
96
+
97
+ # -----------------------------------------------------------------------------
98
+ # classify_action_call — every branch
99
+ # -----------------------------------------------------------------------------
100
+
101
+
102
+ class ClassifyActionCallTests(unittest.TestCase):
103
+
104
+ def test_apex_branch(self):
105
+ out = parse_wave.classify_action_call("apex", "MyClass", "callA")
106
+ self.assertEqual(out["kind"], "APEX")
107
+ self.assertEqual(out["api_name"], "MyClass")
108
+ self.assertEqual(out["element_name"], "callA")
109
+
110
+ def test_generate_prompt_response_branch(self):
111
+ out = parse_wave.classify_action_call(
112
+ "generatePromptResponse", "MyPrompt", "callB",
113
+ )
114
+ self.assertEqual(out["kind"], "PROMPT_TEMPLATE")
115
+ self.assertEqual(out["api_name"], "MyPrompt")
116
+
117
+ def test_other_action_type_falls_to_standard_action(self):
118
+ out = parse_wave.classify_action_call("emailAlert", "MyAlert", "callC")
119
+ self.assertEqual(out["kind"], "STANDARD_ACTION")
120
+ self.assertEqual(out["invocation_type"], "emailAlert")
121
+ self.assertEqual(out["api_name"], "MyAlert")
122
+
123
+ def test_other_action_type_uses_action_type_when_name_missing(self):
124
+ out = parse_wave.classify_action_call("emailAlert", "", "callC")
125
+ self.assertEqual(out["api_name"], "emailAlert")
126
+
127
+ def test_empty_action_type_falls_to_unknown(self):
128
+ out = parse_wave.classify_action_call("", "MyName", "callD")
129
+ self.assertEqual(out["kind"], "UNKNOWN")
130
+ self.assertEqual(out["api_name"], "MyName")
131
+
132
+ def test_unknown_with_no_name_falls_to_question_mark(self):
133
+ out = parse_wave.classify_action_call("", "", "callE")
134
+ self.assertEqual(out["api_name"], "?")
135
+
136
+
137
+ # -----------------------------------------------------------------------------
138
+ # bfs_step — pure helper
139
+ # -----------------------------------------------------------------------------
140
+
141
+
142
+ class BfsStepTests(unittest.TestCase):
143
+
144
+ def test_new_refs_added_to_pending(self):
145
+ pending = parse_wave.empty_kind_sets()
146
+ visited = parse_wave.empty_kind_sets()
147
+ new_refs = {
148
+ "FLOW": {"FlowA", "FlowB"},
149
+ "APEX": {"ApexA"},
150
+ "PROMPT_TEMPLATE": set(),
151
+ "STANDARD_ACTION": set(),
152
+ }
153
+ merged, cycles = parse_wave.bfs_step(pending, visited, new_refs)
154
+ self.assertEqual(merged["FLOW"], {"FlowA", "FlowB"})
155
+ self.assertEqual(merged["APEX"], {"ApexA"})
156
+ self.assertEqual(cycles, [])
157
+
158
+ def test_visited_refs_recorded_as_cycles(self):
159
+ pending = parse_wave.empty_kind_sets()
160
+ visited = parse_wave.empty_kind_sets()
161
+ visited["FLOW"].add("FlowA")
162
+ new_refs = {
163
+ "FLOW": {"FlowA", "FlowB"},
164
+ "APEX": set(), "PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
165
+ }
166
+ merged, cycles = parse_wave.bfs_step(pending, visited, new_refs)
167
+ self.assertEqual(merged["FLOW"], {"FlowB"})
168
+ self.assertIn(("FLOW", "FlowA"), cycles)
169
+
170
+ def test_cross_kind_same_name_stays_distinct(self):
171
+ pending = parse_wave.empty_kind_sets()
172
+ visited = parse_wave.empty_kind_sets()
173
+ new_refs = {
174
+ "FLOW": {"Foo"}, "APEX": {"Foo"},
175
+ "PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
176
+ }
177
+ merged, _ = parse_wave.bfs_step(pending, visited, new_refs)
178
+ self.assertEqual(merged["FLOW"], {"Foo"})
179
+ self.assertEqual(merged["APEX"], {"Foo"})
180
+
181
+ def test_existing_pending_preserved_via_merge(self):
182
+ pending = parse_wave.empty_kind_sets()
183
+ pending["FLOW"].add("FlowExisting")
184
+ visited = parse_wave.empty_kind_sets()
185
+ new_refs = {
186
+ "FLOW": {"FlowNew"},
187
+ "APEX": set(), "PROMPT_TEMPLATE": set(), "STANDARD_ACTION": set(),
188
+ }
189
+ merged, _ = parse_wave.bfs_step(pending, visited, new_refs)
190
+ self.assertEqual(merged["FLOW"], {"FlowExisting", "FlowNew"})
191
+
192
+ def test_unknown_kind_raises(self):
193
+ pending = parse_wave.empty_kind_sets()
194
+ visited = parse_wave.empty_kind_sets()
195
+ with self.assertRaises(ValueError) as ctx:
196
+ parse_wave.bfs_step(
197
+ pending, visited,
198
+ {"BOGUS_KIND": {"x"}},
199
+ )
200
+ self.assertIn("unknown BFS kind", str(ctx.exception))
201
+
202
+
203
+ # -----------------------------------------------------------------------------
204
+ # empty_kind_sets — fresh dict per call
205
+ # -----------------------------------------------------------------------------
206
+
207
+
208
+ class EmptyKindSetsTests(unittest.TestCase):
209
+
210
+ def test_returns_one_set_per_BFS_KIND(self):
211
+ out = parse_wave.empty_kind_sets()
212
+ self.assertEqual(set(out.keys()), set(parse_wave.BFS_KINDS))
213
+ for v in out.values():
214
+ self.assertEqual(v, set())
215
+
216
+ def test_each_call_returns_fresh_dict(self):
217
+ a = parse_wave.empty_kind_sets()
218
+ a["FLOW"].add("x")
219
+ b = parse_wave.empty_kind_sets()
220
+ self.assertEqual(b["FLOW"], set())
221
+
222
+
223
+ if __name__ == "__main__":
224
+ unittest.main()
@@ -0,0 +1,380 @@
1
+ """Tests for ``parse_wave`` standalone helpers — non-classifier surface.
2
+
3
+ Covers:
4
+ - ``compute_stats`` node count + depth + per-kind counter
5
+ - ``finalize_cap`` drains pending → unresolved + sets partial
6
+ - ``atomic_write_json`` tmp + os.replace
7
+ - ``init_tree`` bundle + bot_def shape
8
+ - ``harvest_waves`` Flow XML walking with subflows + actionCalls
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import unittest
15
+ from pathlib import Path
16
+ from tempfile import TemporaryDirectory
17
+
18
+ from . import _bootstrap # noqa: F401 — sys.path setup
19
+
20
+ import parse_wave # type: ignore
21
+
22
+
23
+ _FLOW_NS = "http://soap.sforce.com/2006/04/metadata"
24
+
25
+
26
+ # -----------------------------------------------------------------------------
27
+ # compute_stats
28
+ # -----------------------------------------------------------------------------
29
+
30
+
31
+ class ComputeStatsTests(unittest.TestCase):
32
+
33
+ def test_empty_root_returns_zero_counts(self):
34
+ n, d, counts = parse_wave.compute_stats({})
35
+ self.assertEqual(n, 0)
36
+ self.assertEqual(d, 0)
37
+ self.assertEqual(counts, {})
38
+
39
+ def test_single_node_counts_kind(self):
40
+ n, d, counts = parse_wave.compute_stats({"kind": "BOT_DEFINITION"})
41
+ self.assertEqual(n, 1)
42
+ self.assertEqual(counts["BOT_DEFINITION"], 1)
43
+ self.assertEqual(d, 0)
44
+
45
+ def test_nested_tree_returns_max_depth(self):
46
+ root = {
47
+ "kind": "BOT_DEFINITION",
48
+ "children": [
49
+ {"kind": "TOPIC", "children": [
50
+ {"kind": "FLOW", "children": [
51
+ {"kind": "APEX"},
52
+ ]},
53
+ ]},
54
+ {"kind": "STANDARD_ACTION"},
55
+ ],
56
+ }
57
+ n, d, counts = parse_wave.compute_stats(root)
58
+ self.assertEqual(n, 5)
59
+ self.assertEqual(d, 3)
60
+ self.assertEqual(counts["BOT_DEFINITION"], 1)
61
+ self.assertEqual(counts["TOPIC"], 1)
62
+ self.assertEqual(counts["FLOW"], 1)
63
+ self.assertEqual(counts["APEX"], 1)
64
+ self.assertEqual(counts["STANDARD_ACTION"], 1)
65
+
66
+
67
+ # -----------------------------------------------------------------------------
68
+ # finalize_cap
69
+ # -----------------------------------------------------------------------------
70
+
71
+
72
+ class FinalizeCapTests(unittest.TestCase):
73
+
74
+ def test_drains_pending_into_unresolved(self):
75
+ tree = {
76
+ "_pending_fetches": {
77
+ "FLOW": ["FlowA", "FlowB"],
78
+ "APEX": ["ClassA"],
79
+ "PROMPT_TEMPLATE": [], "STANDARD_ACTION": [],
80
+ },
81
+ "_unresolved": [],
82
+ }
83
+ out = parse_wave.finalize_cap(tree)
84
+ self.assertEqual(len(out["_unresolved"]), 3)
85
+ # Pending wiped per-kind
86
+ for kind in parse_wave.BFS_KINDS:
87
+ self.assertEqual(out["_pending_fetches"][kind], [])
88
+ # _partial flipped
89
+ self.assertTrue(out["_partial"])
90
+ self.assertEqual(out["_partial_reason"], "max-wave-depth")
91
+
92
+ def test_does_not_overwrite_existing_partial_reason(self):
93
+ tree = {
94
+ "_pending_fetches": {"FLOW": ["FlowA"], "APEX": [],
95
+ "PROMPT_TEMPLATE": [], "STANDARD_ACTION": []},
96
+ "_unresolved": [],
97
+ "_partial_reason": "max-depth-cap", # earlier-set, more specific
98
+ }
99
+ out = parse_wave.finalize_cap(tree)
100
+ # Reason kept; depth-cap wins priority over wave-depth.
101
+ self.assertEqual(out["_partial_reason"], "max-depth-cap")
102
+
103
+ def test_no_pending_no_changes(self):
104
+ tree = {
105
+ "_pending_fetches": {k: [] for k in parse_wave.BFS_KINDS},
106
+ "_unresolved": [],
107
+ "_partial": False,
108
+ "_partial_reason": None,
109
+ }
110
+ out = parse_wave.finalize_cap(tree)
111
+ self.assertEqual(out["_unresolved"], [])
112
+ # Nothing drained, _partial untouched
113
+ self.assertFalse(out["_partial"])
114
+
115
+ def test_unresolved_entry_shape(self):
116
+ tree = {
117
+ "_pending_fetches": {"FLOW": ["FlowA"], "APEX": [],
118
+ "PROMPT_TEMPLATE": [], "STANDARD_ACTION": []},
119
+ "_unresolved": [],
120
+ }
121
+ out = parse_wave.finalize_cap(tree)
122
+ entry = out["_unresolved"][0]
123
+ self.assertEqual(entry["kind"], "FLOW")
124
+ self.assertEqual(entry["api_name"], "FlowA")
125
+ self.assertEqual(entry["reason"], "max-wave-depth exceeded")
126
+
127
+
128
+ # -----------------------------------------------------------------------------
129
+ # atomic_write_json
130
+ # -----------------------------------------------------------------------------
131
+
132
+
133
+ class AtomicWriteJsonTests(unittest.TestCase):
134
+
135
+ def test_writes_indented_json(self):
136
+ with TemporaryDirectory() as t:
137
+ target = Path(t) / "out.json"
138
+ parse_wave.atomic_write_json(target, {"a": 1, "b": [1, 2]})
139
+ data = json.loads(target.read_text())
140
+ self.assertEqual(data, {"a": 1, "b": [1, 2]})
141
+
142
+ def test_overwrites_existing(self):
143
+ with TemporaryDirectory() as t:
144
+ target = Path(t) / "out.json"
145
+ target.write_text("old")
146
+ parse_wave.atomic_write_json(target, {"new": True})
147
+ data = json.loads(target.read_text())
148
+ self.assertEqual(data, {"new": True})
149
+
150
+ def test_no_tmp_left_behind(self):
151
+ with TemporaryDirectory() as t:
152
+ target = Path(t) / "out.json"
153
+ parse_wave.atomic_write_json(target, {"x": 1})
154
+ tmp_sibling = target.with_suffix(target.suffix + ".tmp")
155
+ self.assertFalse(tmp_sibling.exists())
156
+
157
+
158
+ # -----------------------------------------------------------------------------
159
+ # init_tree
160
+ # -----------------------------------------------------------------------------
161
+
162
+
163
+ class InitTreeTests(unittest.TestCase):
164
+
165
+ def _setup_env(self, **overrides) -> dict:
166
+ defaults = {
167
+ "AGENT_API_NAME": "DemoAgent",
168
+ "AGENT_VERSION": "v3",
169
+ "BOT_ID": "0Xx000000000ABC",
170
+ "BOT_MASTER_LABEL": "Demo Agent",
171
+ "VERSION_AUTO_PICKED": "false",
172
+ }
173
+ defaults.update(overrides)
174
+ return defaults
175
+
176
+ def _run(self, *, work_dir: Path, env: dict, bundle: dict) -> dict:
177
+ old = dict(os.environ)
178
+ try:
179
+ os.environ.update(env)
180
+ return parse_wave.init_tree(work_dir, bundle)
181
+ finally:
182
+ os.environ.clear()
183
+ os.environ.update(old)
184
+
185
+ def test_initial_tree_shape(self):
186
+ with TemporaryDirectory() as t:
187
+ tree = self._run(
188
+ work_dir=Path(t),
189
+ env=self._setup_env(),
190
+ bundle={"generation": "nga", "plannerName": "MyPlanner",
191
+ "plannerType": "Atlas__Reasoning"},
192
+ )
193
+ self.assertEqual(tree["_schema_version"], "3.1")
194
+ self.assertEqual(tree["agent"]["api_name"], "DemoAgent")
195
+ self.assertEqual(tree["agent"]["version"], "v3")
196
+ self.assertEqual(tree["agent"]["generation"], "nga")
197
+ self.assertEqual(tree["agent"]["planner_name"], "MyPlanner")
198
+ self.assertEqual(tree["root"]["kind"], "BOT_DEFINITION")
199
+ self.assertEqual(tree["root"]["children"], [])
200
+ self.assertTrue(tree["_partial"])
201
+ self.assertIsNone(tree["_partial_reason"])
202
+ # _pending_fetches has all 4 BFS kinds
203
+ for kind in parse_wave.BFS_KINDS:
204
+ self.assertIn(kind, tree["_pending_fetches"])
205
+
206
+ def test_pulls_bot_definition_metadata_from_disk(self):
207
+ with TemporaryDirectory() as t:
208
+ work_dir = Path(t)
209
+ (work_dir / "_bot_definition.json").write_text(json.dumps({
210
+ "result": {"records": [{
211
+ "DeveloperName": "DemoAgent",
212
+ "MasterLabel": "Demo (from disk)",
213
+ "Description": "via _bot_definition.json",
214
+ "AgentType": "Internal",
215
+ "Type": "AiCopilot",
216
+ "AgentTemplate": "TPL",
217
+ "BotSource": "AgentforceAgentCopilot",
218
+ }]},
219
+ }))
220
+ tree = self._run(
221
+ work_dir=work_dir,
222
+ env=self._setup_env(),
223
+ bundle={"generation": "classic"},
224
+ )
225
+ # MasterLabel from _bot_definition.json wins over env
226
+ self.assertEqual(tree["agent"]["master_label"], "Demo (from disk)")
227
+ self.assertEqual(tree["agent"]["description"], "via _bot_definition.json")
228
+ self.assertEqual(tree["agent"]["agent_type"], "Internal")
229
+
230
+ def test_version_auto_picked_flag_propagates(self):
231
+ with TemporaryDirectory() as t:
232
+ tree = self._run(
233
+ work_dir=Path(t),
234
+ env=self._setup_env(VERSION_AUTO_PICKED="true"),
235
+ bundle={"generation": "nga"},
236
+ )
237
+ self.assertTrue(tree["agent"]["_version_auto_picked"])
238
+
239
+
240
+ # -----------------------------------------------------------------------------
241
+ # harvest_waves
242
+ # -----------------------------------------------------------------------------
243
+
244
+
245
+ def _flow_xml(*, name: str, action_calls: list[dict] | None = None,
246
+ subflows: list[str] | None = None) -> str:
247
+ """Build a minimal Flow XML body."""
248
+ acs = "".join(
249
+ f""" <actionCalls>
250
+ <name>{ac['name']}</name>
251
+ <actionType>{ac['actionType']}</actionType>
252
+ <actionName>{ac['actionName']}</actionName>
253
+ </actionCalls>
254
+ """
255
+ for ac in (action_calls or [])
256
+ )
257
+ subs = "".join(
258
+ f""" <subflows>
259
+ <name>sub_{i}</name>
260
+ <flowName>{fn}</flowName>
261
+ </subflows>
262
+ """
263
+ for i, fn in enumerate(subflows or [])
264
+ )
265
+ return f"""<?xml version="1.0" encoding="UTF-8"?>
266
+ <Flow xmlns="{_FLOW_NS}">
267
+ <fullName>{name}</fullName>
268
+ {acs}{subs}</Flow>
269
+ """
270
+
271
+
272
+ class HarvestWavesTests(unittest.TestCase):
273
+
274
+ def _setup_wave(self, work_dir: Path, *, flow_files: dict[str, str],
275
+ apex_class_names: list[str] | None = None,
276
+ prompt_template_names: list[str] | None = None) -> None:
277
+ """Plant a wave1/unpackaged/{flows,classes,genAiPromptTemplates}/
278
+ directory tree under work_dir/sf_meta/."""
279
+ wave = work_dir / "sf_meta" / "wave1" / "unpackaged"
280
+ flows_dir = wave / "flows"
281
+ flows_dir.mkdir(parents=True)
282
+ for filename, body in flow_files.items():
283
+ (flows_dir / filename).write_text(body)
284
+ if apex_class_names:
285
+ classes_dir = wave / "classes"
286
+ classes_dir.mkdir()
287
+ for n in apex_class_names:
288
+ (classes_dir / f"{n}.cls-meta.xml").write_text("<x/>")
289
+ if prompt_template_names:
290
+ prompt_dir = wave / "genAiPromptTemplates"
291
+ prompt_dir.mkdir()
292
+ for n in prompt_template_names:
293
+ (prompt_dir / f"{n}.genAiPromptTemplate").write_text("<x/>")
294
+
295
+ def test_returns_empty_when_no_sf_meta(self):
296
+ with TemporaryDirectory() as t:
297
+ flow_children, vis, pend, cycles = parse_wave.harvest_waves(Path(t))
298
+ self.assertEqual(flow_children, {})
299
+ self.assertEqual(cycles, [])
300
+ for v in vis.values():
301
+ self.assertEqual(v, set())
302
+
303
+ def test_harvests_flow_action_calls_and_subflows(self):
304
+ with TemporaryDirectory() as t:
305
+ work_dir = Path(t)
306
+ self._setup_wave(work_dir, flow_files={
307
+ "MainFlow.flow": _flow_xml(
308
+ name="MainFlow",
309
+ action_calls=[
310
+ {"name": "callA", "actionType": "apex",
311
+ "actionName": "MyClass"},
312
+ {"name": "callB", "actionType": "generatePromptResponse",
313
+ "actionName": "MyPrompt"},
314
+ ],
315
+ subflows=["NestedFlow"],
316
+ ),
317
+ })
318
+ flow_children, visited, pending, _cycles = parse_wave.harvest_waves(work_dir)
319
+ # MainFlow visited
320
+ self.assertIn("MainFlow", visited["FLOW"])
321
+ # Apex + prompt-template + nested flow ref harvested into pending
322
+ self.assertIn("MyClass", pending["APEX"])
323
+ self.assertIn("MyPrompt", pending["PROMPT_TEMPLATE"])
324
+ self.assertIn("NestedFlow", pending["FLOW"])
325
+ # MainFlow's children include the apex + prompt-template + subflow
326
+ self.assertEqual(len(flow_children["MainFlow"]), 3)
327
+
328
+ def test_apex_classes_marked_visited(self):
329
+ with TemporaryDirectory() as t:
330
+ work_dir = Path(t)
331
+ self._setup_wave(
332
+ work_dir,
333
+ flow_files={"F.flow": _flow_xml(name="F")},
334
+ apex_class_names=["AlreadyApex"],
335
+ )
336
+ _, visited, _, _ = parse_wave.harvest_waves(work_dir)
337
+ self.assertIn("AlreadyApex", visited["APEX"])
338
+
339
+ def test_prompt_templates_marked_visited(self):
340
+ with TemporaryDirectory() as t:
341
+ work_dir = Path(t)
342
+ self._setup_wave(
343
+ work_dir,
344
+ flow_files={"F.flow": _flow_xml(name="F")},
345
+ prompt_template_names=["AlreadyPrompt"],
346
+ )
347
+ _, visited, _, _ = parse_wave.harvest_waves(work_dir)
348
+ self.assertIn("AlreadyPrompt", visited["PROMPT_TEMPLATE"])
349
+
350
+ def test_already_visited_apex_pruned_from_pending(self):
351
+ """When pass-2/3 marks an Apex class visited AFTER pass-1 routed it
352
+ into pending, the post-pass prune drops it from pending."""
353
+ with TemporaryDirectory() as t:
354
+ work_dir = Path(t)
355
+ self._setup_wave(work_dir,
356
+ flow_files={"F.flow": _flow_xml(
357
+ name="F",
358
+ action_calls=[{
359
+ "name": "callA", "actionType": "apex",
360
+ "actionName": "ClassX",
361
+ }],
362
+ )},
363
+ apex_class_names=["ClassX"])
364
+ _, visited, pending, _ = parse_wave.harvest_waves(work_dir)
365
+ self.assertIn("ClassX", visited["APEX"])
366
+ # Pruned because already-visited (Apex class file present this wave)
367
+ self.assertNotIn("ClassX", pending["APEX"])
368
+
369
+ def test_malformed_flow_xml_returns_empty_children(self):
370
+ with TemporaryDirectory() as t:
371
+ work_dir = Path(t)
372
+ self._setup_wave(work_dir, flow_files={
373
+ "Broken.flow": "<<<not xml>>>",
374
+ })
375
+ flow_children, _, _, _ = parse_wave.harvest_waves(work_dir)
376
+ self.assertEqual(flow_children["Broken"], [])
377
+
378
+
379
+ if __name__ == "__main__":
380
+ unittest.main()