@salesforce/afv-skills 1.14.0 → 1.16.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 (365) hide show
  1. package/package.json +1 -1
  2. package/skills/activating-datacloud/SKILL.md +0 -1
  3. package/skills/analyzing-omnistudio-dependencies/SKILL.md +0 -1
  4. package/skills/applying-slds/SKILL.md +322 -0
  5. package/skills/applying-slds/checklists.md +83 -0
  6. package/skills/applying-slds/examples.md +283 -0
  7. package/skills/applying-slds/guidance/README.md +83 -0
  8. package/skills/applying-slds/guidance/blueprints-index.md +213 -0
  9. package/skills/applying-slds/guidance/icons-guidance.md +186 -0
  10. package/skills/applying-slds/guidance/overviews/borders.md +236 -0
  11. package/skills/applying-slds/guidance/overviews/color.md +266 -0
  12. package/skills/applying-slds/guidance/overviews/display-density.md +366 -0
  13. package/skills/applying-slds/guidance/overviews/icons.md +240 -0
  14. package/skills/applying-slds/guidance/overviews/illustrations.md +235 -0
  15. package/skills/applying-slds/guidance/overviews/shadows.md +176 -0
  16. package/skills/applying-slds/guidance/overviews/spacing.md +216 -0
  17. package/skills/applying-slds/guidance/overviews/typography.md +323 -0
  18. package/skills/applying-slds/guidance/overviews/utilities.md +542 -0
  19. package/skills/applying-slds/guidance/slds-development-guide.md +288 -0
  20. package/skills/applying-slds/guidance/styling-hooks/borders.md +202 -0
  21. package/skills/applying-slds/guidance/styling-hooks/color/expressive-palette-hooks.md +153 -0
  22. package/skills/applying-slds/guidance/styling-hooks/color/index.md +171 -0
  23. package/skills/applying-slds/guidance/styling-hooks/color/semantic/accent-hooks.md +204 -0
  24. package/skills/applying-slds/guidance/styling-hooks/color/semantic/feedback-hooks.md +768 -0
  25. package/skills/applying-slds/guidance/styling-hooks/color/semantic/surface-hooks.md +337 -0
  26. package/skills/applying-slds/guidance/styling-hooks/color/system-hooks.md +132 -0
  27. package/skills/applying-slds/guidance/styling-hooks/index.md +327 -0
  28. package/skills/applying-slds/guidance/styling-hooks/shadows.md +238 -0
  29. package/skills/applying-slds/guidance/styling-hooks/spacing.md +254 -0
  30. package/skills/applying-slds/guidance/styling-hooks/typography.md +448 -0
  31. package/skills/applying-slds/guidance/utilities/alignment.md +119 -0
  32. package/skills/applying-slds/guidance/utilities/borders.md +131 -0
  33. package/skills/applying-slds/guidance/utilities/box.md +125 -0
  34. package/skills/applying-slds/guidance/utilities/color.md +165 -0
  35. package/skills/applying-slds/guidance/utilities/dark-mode.md +111 -0
  36. package/skills/applying-slds/guidance/utilities/description-list.md +168 -0
  37. package/skills/applying-slds/guidance/utilities/floats.md +117 -0
  38. package/skills/applying-slds/guidance/utilities/grid.md +264 -0
  39. package/skills/applying-slds/guidance/utilities/horizontal-list.md +110 -0
  40. package/skills/applying-slds/guidance/utilities/hyphenation.md +84 -0
  41. package/skills/applying-slds/guidance/utilities/index.md +205 -0
  42. package/skills/applying-slds/guidance/utilities/interactions.md +89 -0
  43. package/skills/applying-slds/guidance/utilities/layout.md +109 -0
  44. package/skills/applying-slds/guidance/utilities/line-clamp.md +131 -0
  45. package/skills/applying-slds/guidance/utilities/margin.md +155 -0
  46. package/skills/applying-slds/guidance/utilities/media-object.md +161 -0
  47. package/skills/applying-slds/guidance/utilities/name-value-list.md +152 -0
  48. package/skills/applying-slds/guidance/utilities/padding.md +155 -0
  49. package/skills/applying-slds/guidance/utilities/position.md +177 -0
  50. package/skills/applying-slds/guidance/utilities/print.md +114 -0
  51. package/skills/applying-slds/guidance/utilities/scrollable.md +126 -0
  52. package/skills/applying-slds/guidance/utilities/sizing.md +190 -0
  53. package/skills/applying-slds/guidance/utilities/themes.md +121 -0
  54. package/skills/applying-slds/guidance/utilities/truncate.md +127 -0
  55. package/skills/applying-slds/guidance/utilities/typography.md +166 -0
  56. package/skills/applying-slds/guidance/utilities/vertical-list.md +166 -0
  57. package/skills/applying-slds/guidance/utilities/visibility.md +228 -0
  58. package/skills/applying-slds/metadata/README.md +84 -0
  59. package/skills/applying-slds/metadata/blueprints/components/accordion.yaml +304 -0
  60. package/skills/applying-slds/metadata/blueprints/components/activity-timeline.yaml +92 -0
  61. package/skills/applying-slds/metadata/blueprints/components/alert.yaml +103 -0
  62. package/skills/applying-slds/metadata/blueprints/components/app-launcher.yaml +94 -0
  63. package/skills/applying-slds/metadata/blueprints/components/avatar-group.yaml +81 -0
  64. package/skills/applying-slds/metadata/blueprints/components/avatar.yaml +97 -0
  65. package/skills/applying-slds/metadata/blueprints/components/badges.yaml +102 -0
  66. package/skills/applying-slds/metadata/blueprints/components/brand-band.yaml +198 -0
  67. package/skills/applying-slds/metadata/blueprints/components/breadcrumbs.yaml +95 -0
  68. package/skills/applying-slds/metadata/blueprints/components/builder-header.yaml +192 -0
  69. package/skills/applying-slds/metadata/blueprints/components/button-groups.yaml +82 -0
  70. package/skills/applying-slds/metadata/blueprints/components/button-icons.yaml +295 -0
  71. package/skills/applying-slds/metadata/blueprints/components/buttons.yaml +230 -0
  72. package/skills/applying-slds/metadata/blueprints/components/cards.yaml +124 -0
  73. package/skills/applying-slds/metadata/blueprints/components/carousel.yaml +140 -0
  74. package/skills/applying-slds/metadata/blueprints/components/chat.yaml +179 -0
  75. package/skills/applying-slds/metadata/blueprints/components/checkbox-button-group.yaml +192 -0
  76. package/skills/applying-slds/metadata/blueprints/components/checkbox-button.yaml +204 -0
  77. package/skills/applying-slds/metadata/blueprints/components/checkbox-toggle.yaml +177 -0
  78. package/skills/applying-slds/metadata/blueprints/components/checkbox.yaml +108 -0
  79. package/skills/applying-slds/metadata/blueprints/components/color-picker.yaml +172 -0
  80. package/skills/applying-slds/metadata/blueprints/components/combobox.yaml +136 -0
  81. package/skills/applying-slds/metadata/blueprints/components/counter.yaml +147 -0
  82. package/skills/applying-slds/metadata/blueprints/components/data-tables.yaml +157 -0
  83. package/skills/applying-slds/metadata/blueprints/components/datepickers.yaml +130 -0
  84. package/skills/applying-slds/metadata/blueprints/components/datetime-picker.yaml +155 -0
  85. package/skills/applying-slds/metadata/blueprints/components/docked-composer.yaml +201 -0
  86. package/skills/applying-slds/metadata/blueprints/components/docked-form-footer.yaml +161 -0
  87. package/skills/applying-slds/metadata/blueprints/components/docked-utility-bar.yaml +175 -0
  88. package/skills/applying-slds/metadata/blueprints/components/drop-zone.yaml +115 -0
  89. package/skills/applying-slds/metadata/blueprints/components/dueling-picklist.yaml +196 -0
  90. package/skills/applying-slds/metadata/blueprints/components/dynamic-icons.yaml +128 -0
  91. package/skills/applying-slds/metadata/blueprints/components/dynamic-menu.yaml +141 -0
  92. package/skills/applying-slds/metadata/blueprints/components/expandable-section.yaml +115 -0
  93. package/skills/applying-slds/metadata/blueprints/components/expression.yaml +143 -0
  94. package/skills/applying-slds/metadata/blueprints/components/feeds.yaml +125 -0
  95. package/skills/applying-slds/metadata/blueprints/components/file-selector.yaml +154 -0
  96. package/skills/applying-slds/metadata/blueprints/components/files.yaml +119 -0
  97. package/skills/applying-slds/metadata/blueprints/components/form-element.yaml +145 -0
  98. package/skills/applying-slds/metadata/blueprints/components/global-header.yaml +120 -0
  99. package/skills/applying-slds/metadata/blueprints/components/global-navigation.yaml +100 -0
  100. package/skills/applying-slds/metadata/blueprints/components/icons.yaml +138 -0
  101. package/skills/applying-slds/metadata/blueprints/components/illustration.yaml +205 -0
  102. package/skills/applying-slds/metadata/blueprints/components/input.yaml +151 -0
  103. package/skills/applying-slds/metadata/blueprints/components/list-builder.yaml +127 -0
  104. package/skills/applying-slds/metadata/blueprints/components/lookups.yaml +132 -0
  105. package/skills/applying-slds/metadata/blueprints/components/map.yaml +118 -0
  106. package/skills/applying-slds/metadata/blueprints/components/menus.yaml +134 -0
  107. package/skills/applying-slds/metadata/blueprints/components/modals.yaml +152 -0
  108. package/skills/applying-slds/metadata/blueprints/components/notifications.yaml +88 -0
  109. package/skills/applying-slds/metadata/blueprints/components/page-headers.yaml +135 -0
  110. package/skills/applying-slds/metadata/blueprints/components/panels.yaml +149 -0
  111. package/skills/applying-slds/metadata/blueprints/components/path.yaml +154 -0
  112. package/skills/applying-slds/metadata/blueprints/components/picklist.yaml +125 -0
  113. package/skills/applying-slds/metadata/blueprints/components/pills.yaml +154 -0
  114. package/skills/applying-slds/metadata/blueprints/components/popovers.yaml +120 -0
  115. package/skills/applying-slds/metadata/blueprints/components/progress-bar.yaml +110 -0
  116. package/skills/applying-slds/metadata/blueprints/components/progress-indicator.yaml +133 -0
  117. package/skills/applying-slds/metadata/blueprints/components/progress-ring.yaml +102 -0
  118. package/skills/applying-slds/metadata/blueprints/components/prompt.yaml +126 -0
  119. package/skills/applying-slds/metadata/blueprints/components/publishers.yaml +178 -0
  120. package/skills/applying-slds/metadata/blueprints/components/radio-button-group.yaml +172 -0
  121. package/skills/applying-slds/metadata/blueprints/components/radio-group.yaml +112 -0
  122. package/skills/applying-slds/metadata/blueprints/components/rich-text-editor.yaml +135 -0
  123. package/skills/applying-slds/metadata/blueprints/components/scoped-notifications.yaml +188 -0
  124. package/skills/applying-slds/metadata/blueprints/components/scoped-tabs.yaml +97 -0
  125. package/skills/applying-slds/metadata/blueprints/components/select.yaml +127 -0
  126. package/skills/applying-slds/metadata/blueprints/components/setup-assistant.yaml +152 -0
  127. package/skills/applying-slds/metadata/blueprints/components/slider.yaml +111 -0
  128. package/skills/applying-slds/metadata/blueprints/components/spinners.yaml +135 -0
  129. package/skills/applying-slds/metadata/blueprints/components/split-view.yaml +112 -0
  130. package/skills/applying-slds/metadata/blueprints/components/summary-detail.yaml +103 -0
  131. package/skills/applying-slds/metadata/blueprints/components/tabs.yaml +138 -0
  132. package/skills/applying-slds/metadata/blueprints/components/textarea.yaml +116 -0
  133. package/skills/applying-slds/metadata/blueprints/components/tiles.yaml +108 -0
  134. package/skills/applying-slds/metadata/blueprints/components/timepicker.yaml +111 -0
  135. package/skills/applying-slds/metadata/blueprints/components/toast.yaml +154 -0
  136. package/skills/applying-slds/metadata/blueprints/components/tooltips.yaml +107 -0
  137. package/skills/applying-slds/metadata/blueprints/components/tree-grid.yaml +116 -0
  138. package/skills/applying-slds/metadata/blueprints/components/trees.yaml +116 -0
  139. package/skills/applying-slds/metadata/blueprints/components/trial-bar.yaml +112 -0
  140. package/skills/applying-slds/metadata/blueprints/components/vertical-navigation.yaml +130 -0
  141. package/skills/applying-slds/metadata/blueprints/components/vertical-tabs.yaml +140 -0
  142. package/skills/applying-slds/metadata/blueprints/components/visual-picker.yaml +150 -0
  143. package/skills/applying-slds/metadata/blueprints/components/welcome-mat.yaml +136 -0
  144. package/skills/applying-slds/metadata/hooks-index.json +6272 -0
  145. package/skills/applying-slds/metadata/icon-metadata.json +38466 -0
  146. package/skills/applying-slds/metadata/utilities-index.json +21912 -0
  147. package/skills/applying-slds/references/component-selection.md +112 -0
  148. package/skills/applying-slds/references/icons-decision-guide.md +124 -0
  149. package/skills/applying-slds/references/styling-decision-guide.md +228 -0
  150. package/skills/applying-slds/references/utilities-quick-ref.md +125 -0
  151. package/skills/applying-slds/scripts/search-blueprints.cjs +117 -0
  152. package/skills/applying-slds/scripts/search-hooks.cjs +139 -0
  153. package/skills/applying-slds/scripts/search-icons.cjs +174 -0
  154. package/skills/applying-slds/scripts/search-utilities.cjs +161 -0
  155. package/skills/building-mobile-apps/SKILL.md +0 -1
  156. package/skills/building-omnistudio-callable-apex/SKILL.md +0 -1
  157. package/skills/building-omnistudio-datamapper/SKILL.md +0 -1
  158. package/skills/building-omnistudio-flexcard/SKILL.md +0 -1
  159. package/skills/building-omnistudio-integration-procedure/SKILL.md +0 -1
  160. package/skills/building-omnistudio-omniscript/SKILL.md +0 -1
  161. package/skills/building-sf-integrations/SKILL.md +0 -1
  162. package/skills/configuring-connected-apps/SKILL.md +0 -1
  163. package/skills/connecting-datacloud/SKILL.md +0 -1
  164. package/skills/creating-b2b-commerce-store/SKILL.md +0 -1
  165. package/skills/debugging-apex-logs/SKILL.md +0 -1
  166. package/skills/deploying-metadata/SKILL.md +0 -1
  167. package/skills/deploying-omnistudio-datapacks/SKILL.md +0 -1
  168. package/skills/developing-agentforce/SKILL.md +0 -1
  169. package/skills/fetching-salesforce-docs/SKILL.md +0 -1
  170. package/skills/generating-custom-lightning-type/SKILL.md +17 -39
  171. package/skills/generating-custom-lightning-type/assets/primitive-types-and-constraints.md +41 -0
  172. package/skills/generating-custom-lightning-type/references/widget-rendition.md +124 -0
  173. package/skills/generating-lwc-components/SKILL.md +0 -1
  174. package/skills/generating-mermaid-diagrams/SKILL.md +0 -1
  175. package/skills/generating-visual-diagrams/SKILL.md +0 -1
  176. package/skills/handling-sf-data/SKILL.md +0 -1
  177. package/skills/harmonizing-datacloud/SKILL.md +0 -1
  178. package/skills/integrating-b2b-commerce-open-code-components/SKILL.md +0 -1
  179. package/skills/investigating-agentforce-architecture/README.md +156 -0
  180. package/skills/investigating-agentforce-architecture/SKILL.md +230 -0
  181. package/skills/investigating-agentforce-architecture/assets/cli/describe_sobject.yaml +16 -0
  182. package/skills/investigating-agentforce-architecture/assets/cli/describe_tooling_sobject.yaml +17 -0
  183. package/skills/investigating-agentforce-architecture/assets/cli/list_metadata_genaiprompttemplate.yaml +17 -0
  184. package/skills/investigating-agentforce-architecture/assets/cli/org_display.yaml +15 -0
  185. package/skills/investigating-agentforce-architecture/assets/cli/retrieve_genai_plugin.yaml +18 -0
  186. package/skills/investigating-agentforce-architecture/assets/cli/show_access_token.yaml +27 -0
  187. package/skills/investigating-agentforce-architecture/assets/mermaid/action_tree.mmd +20 -0
  188. package/skills/investigating-agentforce-architecture/assets/mermaid/data_flow.mmd +19 -0
  189. package/skills/investigating-agentforce-architecture/assets/mermaid/dependency_graph.mmd +19 -0
  190. package/skills/investigating-agentforce-architecture/assets/mermaid/invocation_sequence.mmd +20 -0
  191. package/skills/investigating-agentforce-architecture/assets/mermaid/planner_state.mmd +18 -0
  192. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_ids.soql +3 -0
  193. package/skills/investigating-agentforce-architecture/assets/soql/apex_class_bodies_by_names.soql +3 -0
  194. package/skills/investigating-agentforce-architecture/assets/soql/bot_definition_details.soql +3 -0
  195. package/skills/investigating-agentforce-architecture/assets/soql/bot_version_lookup.soql +4 -0
  196. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_by_ids.soql +3 -0
  197. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_ids_by_names.soql +3 -0
  198. package/skills/investigating-agentforce-architecture/assets/soql/flow_definition_view_by_durable_ids.soql +4 -0
  199. package/skills/investigating-agentforce-architecture/assets/soql/flow_metadata_by_id.soql +3 -0
  200. package/skills/investigating-agentforce-architecture/assets/soql/functions_by_plugins.soql +5 -0
  201. package/skills/investigating-agentforce-architecture/assets/soql/planner_attrs_by_parent_ids.soql +3 -0
  202. package/skills/investigating-agentforce-architecture/assets/soql/planner_bundle_functions.soql +3 -0
  203. package/skills/investigating-agentforce-architecture/assets/soql/planner_definition_by_agent_chain.soql +3 -0
  204. package/skills/investigating-agentforce-architecture/assets/soql/plugin_functions_by_plugin_ids.soql +3 -0
  205. package/skills/investigating-agentforce-architecture/assets/soql/plugin_instructions_by_plugin_ids.soql +3 -0
  206. package/skills/investigating-agentforce-architecture/assets/soql/plugins_by_planner.soql +4 -0
  207. package/skills/investigating-agentforce-architecture/references/architecture_sections.md +243 -0
  208. package/skills/investigating-agentforce-architecture/references/contract.json +244 -0
  209. package/skills/investigating-agentforce-architecture/references/soql_fields.md +512 -0
  210. package/skills/investigating-agentforce-architecture/scripts/_shared/__init__.py +1 -0
  211. package/skills/investigating-agentforce-architecture/scripts/_shared/fs_guard.py +329 -0
  212. package/skills/investigating-agentforce-architecture/scripts/_shared/paths.py +110 -0
  213. package/skills/investigating-agentforce-architecture/scripts/_shared/runtime.py +59 -0
  214. package/skills/investigating-agentforce-architecture/scripts/_shared/sql.py +10 -0
  215. package/skills/investigating-agentforce-architecture/scripts/cache_check.py +234 -0
  216. package/skills/investigating-agentforce-architecture/scripts/config.py +131 -0
  217. package/skills/investigating-agentforce-architecture/scripts/fetch_soql.py +689 -0
  218. package/skills/investigating-agentforce-architecture/scripts/finalize.py +295 -0
  219. package/skills/investigating-agentforce-architecture/scripts/main.py +2835 -0
  220. package/skills/investigating-agentforce-architecture/scripts/metadata_listing.py +265 -0
  221. package/skills/investigating-agentforce-architecture/scripts/parallel_retrieve.py +69 -0
  222. package/skills/investigating-agentforce-architecture/scripts/parse_bundle.py +215 -0
  223. package/skills/investigating-agentforce-architecture/scripts/parse_wave.py +845 -0
  224. package/skills/investigating-agentforce-architecture/scripts/probe_channels.py +302 -0
  225. package/skills/investigating-agentforce-architecture/scripts/render_architecture.py +1043 -0
  226. package/skills/investigating-agentforce-architecture/scripts/resolve_bot.py +255 -0
  227. package/skills/investigating-agentforce-architecture/scripts/resolve_invocation_target.py +130 -0
  228. package/skills/investigating-agentforce-architecture/scripts/rest_client.py +763 -0
  229. package/skills/investigating-agentforce-architecture/scripts/retrieve_planner.py +13 -0
  230. package/skills/investigating-agentforce-architecture/scripts/sf_cli.py +242 -0
  231. package/skills/investigating-agentforce-architecture/scripts/soql_loader.py +253 -0
  232. package/skills/investigating-agentforce-architecture/scripts/summarize_tree.py +143 -0
  233. package/skills/investigating-agentforce-architecture/scripts/tests/__init__.py +0 -0
  234. package/skills/investigating-agentforce-architecture/scripts/tests/_bootstrap.py +23 -0
  235. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/__init__.py +0 -0
  236. package/skills/investigating-agentforce-architecture/scripts/tests/fixtures/genai_payloads.py +400 -0
  237. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check.py +307 -0
  238. package/skills/investigating-agentforce-architecture/scripts/tests/test_cache_check_main.py +283 -0
  239. package/skills/investigating-agentforce-architecture/scripts/tests/test_config.py +115 -0
  240. package/skills/investigating-agentforce-architecture/scripts/tests/test_end_to_end_fixture.py +651 -0
  241. package/skills/investigating-agentforce-architecture/scripts/tests/test_finalize.py +278 -0
  242. package/skills/investigating-agentforce-architecture/scripts/tests/test_flow_children_inflation.py +582 -0
  243. package/skills/investigating-agentforce-architecture/scripts/tests/test_fs_guard.py +113 -0
  244. package/skills/investigating-agentforce-architecture/scripts/tests/test_iterative_wave_b.py +478 -0
  245. package/skills/investigating-agentforce-architecture/scripts/tests/test_main_pipeline.py +3359 -0
  246. package/skills/investigating-agentforce-architecture/scripts/tests/test_parallel_retrieve.py +131 -0
  247. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_bundle.py +400 -0
  248. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave.py +644 -0
  249. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_classifiers.py +224 -0
  250. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_helpers.py +380 -0
  251. package/skills/investigating-agentforce-architecture/scripts/tests/test_parse_wave_main.py +397 -0
  252. package/skills/investigating-agentforce-architecture/scripts/tests/test_per_branch_visited.py +244 -0
  253. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_channels.py +359 -0
  254. package/skills/investigating-agentforce-architecture/scripts/tests/test_probe_cli_recipes.py +185 -0
  255. package/skills/investigating-agentforce-architecture/scripts/tests/test_render_architecture.py +810 -0
  256. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_bot.py +203 -0
  257. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_creds.py +157 -0
  258. package/skills/investigating-agentforce-architecture/scripts/tests/test_resolve_invocation_target.py +145 -0
  259. package/skills/investigating-agentforce-architecture/scripts/tests/test_rest_client.py +1253 -0
  260. package/skills/investigating-agentforce-architecture/scripts/tests/test_runtime_override.py +100 -0
  261. package/skills/investigating-agentforce-architecture/scripts/tests/test_sf_cli.py +261 -0
  262. package/skills/investigating-agentforce-architecture/scripts/tests/test_signature_stamping.py +466 -0
  263. package/skills/investigating-agentforce-architecture/scripts/tests/test_soql_loader.py +501 -0
  264. package/skills/investigating-agentforce-architecture/scripts/tests/test_summarize_tree.py +241 -0
  265. package/skills/investigating-agentforce-architecture/scripts/tests/test_write_emit_ctx.py +480 -0
  266. package/skills/investigating-agentforce-architecture/tools/emit_env.py +157 -0
  267. package/skills/investigating-agentforce-architecture/tools/emit_result.py +262 -0
  268. package/skills/investigating-agentforce-architecture/tools/sanitize.py +33 -0
  269. package/skills/investigating-agentforce-architecture/tools/write_emit_ctx.py +332 -0
  270. package/skills/investigating-agentforce-d360/README.md +123 -0
  271. package/skills/investigating-agentforce-d360/SKILL.md +163 -0
  272. package/skills/investigating-agentforce-d360/assets/dc/app_generation.sql +51 -0
  273. package/skills/investigating-agentforce-d360/assets/dc/content_category.sql +44 -0
  274. package/skills/investigating-agentforce-d360/assets/dc/content_quality.sql +41 -0
  275. package/skills/investigating-agentforce-d360/assets/dc/discover_sessions.sql +36 -0
  276. package/skills/investigating-agentforce-d360/assets/dc/feedback.sql +47 -0
  277. package/skills/investigating-agentforce-d360/assets/dc/feedback_details.sql +38 -0
  278. package/skills/investigating-agentforce-d360/assets/dc/gateway_records.sql +45 -0
  279. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_llm.sql +50 -0
  280. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_metadata.sql +44 -0
  281. package/skills/investigating-agentforce-d360/assets/dc/gateway_request_tags.sql +42 -0
  282. package/skills/investigating-agentforce-d360/assets/dc/gateway_requests.sql +89 -0
  283. package/skills/investigating-agentforce-d360/assets/dc/gateway_responses.sql +43 -0
  284. package/skills/investigating-agentforce-d360/assets/dc/generations.sql +52 -0
  285. package/skills/investigating-agentforce-d360/assets/dc/interactions.sql +53 -0
  286. package/skills/investigating-agentforce-d360/assets/dc/messages.sql +53 -0
  287. package/skills/investigating-agentforce-d360/assets/dc/messaging_session.sql +37 -0
  288. package/skills/investigating-agentforce-d360/assets/dc/moment_interactions.sql +34 -0
  289. package/skills/investigating-agentforce-d360/assets/dc/moments.sql +39 -0
  290. package/skills/investigating-agentforce-d360/assets/dc/participants.sql +48 -0
  291. package/skills/investigating-agentforce-d360/assets/dc/sessions.sql +78 -0
  292. package/skills/investigating-agentforce-d360/assets/dc/steps.sql +64 -0
  293. package/skills/investigating-agentforce-d360/assets/dc/tag_associations.sql +46 -0
  294. package/skills/investigating-agentforce-d360/assets/dc/tag_definition_associations.sql +37 -0
  295. package/skills/investigating-agentforce-d360/assets/dc/tag_definitions.sql +50 -0
  296. package/skills/investigating-agentforce-d360/assets/dc/tags.sql +37 -0
  297. package/skills/investigating-agentforce-d360/assets/dc/telemetry_spans.sql +55 -0
  298. package/skills/investigating-agentforce-d360/references/artifacts.md +50 -0
  299. package/skills/investigating-agentforce-d360/references/dc_dmo_fields.md +823 -0
  300. package/skills/investigating-agentforce-d360/references/dc_pipeline_contract.md +608 -0
  301. package/skills/investigating-agentforce-d360/scripts/_shared/__init__.py +2 -0
  302. package/skills/investigating-agentforce-d360/scripts/_shared/cli_override.py +98 -0
  303. package/skills/investigating-agentforce-d360/scripts/_shared/fs_guard.py +334 -0
  304. package/skills/investigating-agentforce-d360/scripts/_shared/paths.py +155 -0
  305. package/skills/investigating-agentforce-d360/scripts/_shared/runtime.py +59 -0
  306. package/skills/investigating-agentforce-d360/scripts/_shared/sql.py +14 -0
  307. package/skills/investigating-agentforce-d360/scripts/assemble_dc.py +1624 -0
  308. package/skills/investigating-agentforce-d360/scripts/config.py +45 -0
  309. package/skills/investigating-agentforce-d360/scripts/dc.py +188 -0
  310. package/skills/investigating-agentforce-d360/scripts/discover_sessions.py +556 -0
  311. package/skills/investigating-agentforce-d360/scripts/fetch_dc.py +1045 -0
  312. package/skills/investigating-agentforce-d360/scripts/render_dc.py +1750 -0
  313. package/skills/investigating-agentforce-d360/scripts/resolve_session.py +264 -0
  314. package/skills/investigating-agentforce-d360/scripts/storage.py +92 -0
  315. package/skills/investigating-agentforce-d360/scripts/tests/__init__.py +0 -0
  316. package/skills/investigating-agentforce-d360/scripts/tests/_bootstrap.py +15 -0
  317. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/__init__.py +0 -0
  318. package/skills/investigating-agentforce-d360/scripts/tests/fixtures/synthetic_session.py +424 -0
  319. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_bootstrap_and_mode.py +115 -0
  320. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct.py +220 -0
  321. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_gateway_direct_integration.py +158 -0
  322. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_helpers.py +287 -0
  323. package/skills/investigating-agentforce-d360/scripts/tests/test_assemble_dc_integration.py +247 -0
  324. package/skills/investigating-agentforce-d360/scripts/tests/test_dc_and_resolve_session.py +433 -0
  325. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions.py +458 -0
  326. package/skills/investigating-agentforce-d360/scripts/tests/test_discover_sessions_grep_ci.py +193 -0
  327. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_helpers.py +266 -0
  328. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_identity.py +528 -0
  329. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_main.py +251 -0
  330. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall.py +229 -0
  331. package/skills/investigating-agentforce-d360/scripts/tests/test_fetch_dc_waterfall_full.py +283 -0
  332. package/skills/investigating-agentforce-d360/scripts/tests/test_identity_coherence.py +327 -0
  333. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_branches.py +256 -0
  334. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_gateway_direct.py +130 -0
  335. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_helpers.py +291 -0
  336. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_integration.py +220 -0
  337. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_planner_llm_calls.py +284 -0
  338. package/skills/investigating-agentforce-d360/scripts/tests/test_render_dc_show_prompts_gating.py +215 -0
  339. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_from_disk.py +100 -0
  340. package/skills/investigating-agentforce-d360/scripts/tests/test_resolve_session_main.py +149 -0
  341. package/skills/investigating-agentforce-d360/scripts/tests/test_runtime_override.py +104 -0
  342. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape.py +95 -0
  343. package/skills/investigating-agentforce-d360/scripts/tests/test_session_shape_dropped_by_stdm.py +85 -0
  344. package/skills/managing-managed-event-subscription/SKILL.md +152 -0
  345. package/skills/managing-managed-event-subscription/assets/managed-event-subscription-template.xml +20 -0
  346. package/skills/managing-managed-event-subscription/references/delete-guide.md +57 -0
  347. package/skills/managing-managed-event-subscription/references/topic-name-formats.md +26 -0
  348. package/skills/managing-managed-event-subscription/references/update-constraints.md +30 -0
  349. package/skills/modeling-omnistudio-epc-catalog/SKILL.md +0 -1
  350. package/skills/observing-agentforce/SKILL.md +0 -1
  351. package/skills/orchestrating-datacloud/SKILL.md +0 -1
  352. package/skills/preparing-datacloud/SKILL.md +0 -1
  353. package/skills/querying-soql/SKILL.md +0 -1
  354. package/skills/retrieving-datacloud/SKILL.md +0 -1
  355. package/skills/running-apex-tests/SKILL.md +0 -1
  356. package/skills/running-code-analyzer/SKILL.md +0 -1
  357. package/skills/segmenting-datacloud/SKILL.md +0 -1
  358. package/skills/testing-agentforce/SKILL.md +0 -1
  359. package/skills/uplifting-components-to-slds2/SKILL.md +3 -2
  360. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +30 -9
  361. package/skills/uplifting-components-to-slds2/references/examples.md +24 -6
  362. package/skills/validating-slds/SKILL.md +262 -0
  363. package/skills/validating-slds/references/quality-checks.md +308 -0
  364. package/skills/validating-slds/references/report-format.md +302 -0
  365. package/skills/validating-slds/scripts/analyze-quality.cjs +521 -0
@@ -0,0 +1,13 @@
1
+ """BFS graph builder — walks the cross-type reference graph.
2
+
3
+ (visited keys = (kind, canonical_name) tuples) + (MAX_DEPTH=5 cap
4
+ → _partial + PARTIAL_OK) land in Batches 2 and 3.
5
+ """
6
+ from __future__ import annotations
7
+
8
+
9
+ def build_fetch_graph() -> dict:
10
+ """TODO + BFS with per-kind visited tuples, cycle-safe,
11
+ MAX_DEPTH cap surfaces as _partial=true in the tree.
12
+ """
13
+ raise NotImplementedError("build_fetch_graph implements in P0 Batches 2+3")
@@ -0,0 +1,242 @@
1
+ """sf CLI recipe loader — reads assets/cli/*.yaml, runs argv via subprocess.
2
+
3
+ Security: YAML recipes are loaded via yaml.safe_load only; yaml.load is banned.
4
+ PyYAML's `yaml.load` deserializes arbitrary Python objects (including
5
+ constructor-injected code in some tags) and must never run on attacker-reachable
6
+ YAML. The recipes live inside this repo and are author-controlled, but we
7
+ still use `safe_load` as defence in depth — a compromised dist/ or a botched
8
+ copy-paste can't escalate to code execution.
9
+
10
+ the `_SAFE_LOADER` module-level binding is THE loader used everywhere
11
+ in this module; tests assert its identity against `yaml.safe_load`.
12
+
13
+ every stringification of a subprocess.CalledProcessError or related
14
+ exception is routed through `rest_client.redact_error` so stderr containing
15
+ an access token (rare but observed on some `sf` failure paths) cannot leak
16
+ into logs or RESULT blocks.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import os
22
+ import re
23
+ import subprocess
24
+ from pathlib import Path
25
+ from typing import Any, Dict
26
+
27
+ import yaml
28
+
29
+ from config import CLI_DIR
30
+ # hoisted `redact_text` to module-top alongside
31
+ # `redact_error`. Previously `_redact_subprocess_stderr` performed a lazy
32
+ # import of the module-private `_redact_text` on every call, which (a)
33
+ # tied this module to a name that could be renamed silently and (b) meant
34
+ # an ImportError from rest_client would surface only at redaction time —
35
+ # possibly in a frame whose locals held the raw, un-redacted stderr.
36
+ # Hoisting is safe: rest_client does not import sf_cli (no cycle).
37
+ from rest_client import redact_error, redact_text
38
+
39
+ # the ONE allowed loader for this module. Test asserts identity.
40
+ # NEVER change this to yaml.load (which can construct arbitrary Python
41
+ # objects via tag hooks).
42
+ _SAFE_LOADER = yaml.safe_load
43
+
44
+
45
+ class AuthRequired(RuntimeError):
46
+ """sf CLI returned a known auth-failure pattern.
47
+
48
+ Carries a redacted stderr snippet — any bearer token in stderr is scrubbed
49
+ via redact_error before reaching this message.
50
+ """
51
+
52
+
53
+ class SfCliError(RuntimeError):
54
+ """sf CLI returned non-zero exit or JSON status != 0.
55
+
56
+ Message is redacted before construction.
57
+ """
58
+
59
+
60
+ def _load_recipe(name: str) -> Dict[str, Any]:
61
+ """Read and parse a CLI recipe file with yaml.safe_load.
62
+
63
+ _SAFE_LOADER is bound to yaml.safe_load at module top; using it
64
+ here (instead of `yaml.safe_load` directly) makes the identity testable
65
+ and gives a single point to review if the loader ever changes.
66
+ """
67
+ path = CLI_DIR / f"{name}.yaml"
68
+ with path.open("r", encoding="utf-8") as fh:
69
+ recipe = _SAFE_LOADER(fh)
70
+ if not isinstance(recipe, dict):
71
+ raise SfCliError(
72
+ f"recipe {name!r} did not parse to a mapping (got {type(recipe).__name__})"
73
+ )
74
+ return recipe
75
+
76
+
77
+ def _sub(arg: str, params: Dict[str, str]) -> str:
78
+ """Substitute {{KEY}} placeholders in a single argv element.
79
+
80
+ Input validation: `params` values are expected to be already-validated
81
+ strings (SKILL.md phase 1 runs fs_guard on every user-supplied input).
82
+ We do not revalidate here because argv substitution is shell-escape-safe
83
+ by construction — subprocess.run with a list argv never invokes a shell.
84
+ """
85
+ out = arg
86
+ for key, value in params.items():
87
+ out = out.replace(f"{{{{{key}}}}}", value)
88
+ return out
89
+
90
+
91
+ def _parse_stdout_json(stdout: str) -> Dict[str, Any]:
92
+ """Parse sf CLI's --json stdout.
93
+
94
+ sf CLI emits `{"status": 0, "result": {...}}` on success; malformed
95
+ stdout (e.g. when the CLI crashes before JSON serialization) raises
96
+ SfCliError with a redacted message.
97
+ """
98
+ try:
99
+ return json.loads(stdout)
100
+ except json.JSONDecodeError as e:
101
+ # stdout could in theory contain a token on some crash paths;
102
+ # redact before surfacing.
103
+ raise SfCliError(
104
+ f"sf CLI stdout is not JSON: {redact_error(e)}"
105
+ ) from None
106
+
107
+
108
+ _AUTH_LINE_PREFIX_RE = re.compile(r"^(?:Error|Warning):\s") # @rule-suppress starter-sec-002 — re.compile, not python compile()
109
+
110
+
111
+ def _stderr_matches_auth(stderr: str, patterns) -> bool:
112
+ """Check if any auth-failure pattern appears on an Error:/Warning: line.
113
+
114
+ previously a substring scan across the entire stderr, which
115
+ false-positived on Node ESM warnings ("Warning: @gthoppae/sf-cli-
116
+ plugin-data360 is a linked ESM module") if they happened to echo an
117
+ auth pattern substring. Now we only match on lines starting with
118
+ `Error:` or `Warning:` followed by whitespace. This keeps every
119
+ true-positive we care about (real sf auth errors always surface as
120
+ `Error: NoOrgAuthenticationError ...`) while pruning the noise.
121
+
122
+ Matching rules:
123
+ * Line starts with `Error:<ws>` or `Warning:<ws>` (anchored at
124
+ start-of-line, so embedded-in-prose mentions don't match).
125
+ * Pattern is a substring of the REST of that line (after the
126
+ prefix). We strip only the prefix — we do not re-scope to word
127
+ boundaries, since sf auth tokens like `NoOrgAuthenticationError`
128
+ are always well-formed identifiers without adjacent alphanum.
129
+ * Empty stderr / empty patterns → no match.
130
+ """
131
+ if not stderr or not patterns:
132
+ return False
133
+ for line in stderr.splitlines():
134
+ m = _AUTH_LINE_PREFIX_RE.match(line)
135
+ if not m:
136
+ continue
137
+ tail = line[m.end():]
138
+ if any(p in tail for p in patterns):
139
+ return True
140
+ return False
141
+
142
+
143
+ def run_sf(name: str, **params: str) -> Dict[str, Any]:
144
+ """Execute a recipe-defined sf CLI command and return parsed stdout.
145
+
146
+ Steps:
147
+ 1. Load recipe (yaml.safe_load).
148
+ 2. Enforce required_params.
149
+ 3. Substitute {{KEY}} into argv.
150
+ 4. subprocess.run with list argv (no shell, no bash -c).
151
+ 5. Parse stdout JSON; check `status == 0`.
152
+ 6. On failure: classify as AuthRequired (stderr pattern match) or
153
+ SfCliError; every message is redacted via redact_error .
154
+
155
+ Exception semantics:
156
+ * AuthRequired — user needs to `sf org login`
157
+ * SfCliError — anything else (timeout, non-zero exit, JSON parse,
158
+ status != 0)
159
+
160
+ NEVER uses shell=True. NEVER calls log.exception(). NEVER echoes raw
161
+ stderr without redaction .
162
+ """
163
+ recipe = _load_recipe(name)
164
+
165
+ required = set(recipe.get("required_params") or [])
166
+ missing = required - params.keys()
167
+ if missing:
168
+ raise SfCliError(f"missing required params for {name!r}: {sorted(missing)}")
169
+
170
+ argv_template = recipe.get("argv") or []
171
+ if not isinstance(argv_template, list):
172
+ raise SfCliError(f"recipe {name!r} argv must be a list")
173
+
174
+ argv = [_sub(str(e), params) for e in argv_template]
175
+ timeout = int(recipe.get("timeout_seconds", 60))
176
+ auth_patterns = recipe.get("auth_required_stderr_patterns") or []
177
+
178
+ # SF_TEMP_SHOW_SECRETS=true is required for `sf org display --verbose
179
+ # --json` to emit `accessToken` instead of the literal redaction string
180
+ # `"[REDACTED] Use 'sf org auth show-access-token' to view"` introduced
181
+ # in sf CLI v2. Without it, downstream callers receive the redaction
182
+ # string as the bearer token and every Tooling/REST call returns
183
+ # INVALID_AUTH_HEADER 401. Set on every recipe — recipes that don't
184
+ # touch tokens are unaffected.
185
+ env = {**os.environ, "SF_TEMP_SHOW_SECRETS": "true"}
186
+ try:
187
+ cp = subprocess.run(
188
+ argv,
189
+ capture_output=True,
190
+ text=True,
191
+ timeout=timeout,
192
+ check=False,
193
+ env=env,
194
+ )
195
+ except subprocess.TimeoutExpired as e:
196
+ # timeout exceptions carry stderr via e.stderr — redact.
197
+ raise SfCliError(
198
+ f"sf CLI {name!r} timed out after {timeout}s: {redact_error(e)}"
199
+ ) from None
200
+ except (OSError, subprocess.SubprocessError) as e:
201
+ # any subprocess-layer error gets redacted.
202
+ raise SfCliError(
203
+ f"sf CLI {name!r} invocation failed: {redact_error(e)}"
204
+ ) from None
205
+
206
+ # Even on nonzero exit sf CLI usually still emits JSON on stdout; try
207
+ # to parse before deciding failure mode.
208
+ try:
209
+ data = _parse_stdout_json(cp.stdout) if cp.stdout else {}
210
+ except SfCliError:
211
+ data = {}
212
+
213
+ status_field = data.get("status", 0) if isinstance(data, dict) else 1
214
+ if cp.returncode != 0 or status_field != 0:
215
+ # stderr could contain a token on some failure paths; redact
216
+ # the ENTIRE string before deciding how to classify or what to show.
217
+ safe_stderr = _redact_subprocess_stderr(cp.stderr)
218
+ if _stderr_matches_auth(cp.stderr, auth_patterns):
219
+ raise AuthRequired(safe_stderr or "auth required")
220
+ # Include exit code + JSON status in the message (both are
221
+ # numeric/well-known, no token risk) plus the redacted stderr tail.
222
+ raise SfCliError(
223
+ f"sf CLI {name!r} failed "
224
+ f"(exit={cp.returncode}, json_status={status_field}): {safe_stderr}"
225
+ )
226
+
227
+ return data
228
+
229
+
230
+ def _redact_subprocess_stderr(stderr: str) -> str:
231
+ """run stderr text through the rest_client redaction regexes.
232
+
233
+ stderr may contain `Authorization: Bearer ...` or `accessToken=...` if
234
+ the sf CLI echoes a failing request; scrub before surfacing.
235
+
236
+ calls the public `redact_text` (imported at module
237
+ top). Previously imported the module-private `_redact_text` lazily on
238
+ every call — a rename could have silently broken redaction, and an
239
+ ImportError would have propagated through `run_sf` with the raw stderr
240
+ still live in the traceback's frame locals.
241
+ """
242
+ return redact_text(stderr or "")
@@ -0,0 +1,253 @@
1
+ """SOQL template loader — reads assets/soql/*.soql and substitutes {{PARAM}} values.
2
+
3
+ every value passed in via **params is regex-revalidated at the
4
+ substitution boundary via `fs_guard.validate_api_name`. This is defence
5
+ in depth — callers (SKILL.md phase 1, parse_bundle.extract_planner_name,
6
+ parse_wave.discovered_names) already validate at their own boundaries, but
7
+ centralizing the gate here guarantees that any future caller can't bypass
8
+ it by forgetting to validate first.
9
+
10
+ Why no escape function: `validate_api_name` restricts every substituted
11
+ string to `^[A-Za-z0-9_]+$` — the legal character set for Salesforce
12
+ DeveloperName / API-name identifiers. No quote, apostrophe, semicolon,
13
+ parenthesis, or whitespace can ever reach the SOQL builder, so there is
14
+ nothing to escape. Adding an escape step would be both dead code and a
15
+ false reassurance that unvalidated input is somehow safe.
16
+
17
+ `load_soql_in` adds support for `WHERE X IN (...)`
18
+ list placeholders. The BFS fan-out passes across Apex/Flow/Plugin/Function
19
+ discovery each need to batch-query by a list of ids or names; forcing
20
+ one SOQL call per element would blow up both the query-count budget and
21
+ the request-latency floor. The new function shares the `load_soql`
22
+ validation path (every element goes through `fs_guard.validate_api_name`)
23
+ so list inputs cannot widen the SOQL-injection surface — `'`, `;`, `--`,
24
+ whitespace, and every other SOQL metachar remain unreachable. The
25
+ existing `load_soql` signature is unchanged.
26
+
27
+ SOQL-string-substitution loader with validation at the substitution
28
+ boundary. Every value substituted into a `{{token}}` placeholder is
29
+ regex-validated before insertion.
30
+ """
31
+ from __future__ import annotations
32
+
33
+ from typing import Dict, List, Optional
34
+
35
+ from config import SOQL_DIR, fs_guard # fs_guard re-exported by config.py from _shared/
36
+
37
+
38
+ class SoqlParamError(ValueError):
39
+ """Raised when a parameter value fails revalidation at the substitution site.
40
+
41
+ Carries the failing key + a 20-char preview of the rejected value. Callers
42
+ (parse_bundle, parse_wave, main.py) catch this and add the key to
43
+ `_unresolved[]` with `reason=invalid-soql-param:<key>` so the pipeline
44
+ continues with `STATUS=PARTIAL_OK` rather than aborting on a single
45
+ malformed name.
46
+ """
47
+
48
+ def __init__(self, key: str, value, reason: str) -> None:
49
+ self.key = key
50
+ self.reason = reason
51
+ # Preview is bounded to 20 chars to avoid echoing arbitrarily-long
52
+ # attacker-controlled strings into logs.
53
+ try:
54
+ preview = str(value)[:20]
55
+ except Exception:
56
+ preview = "<unreprable>"
57
+ self.preview = preview
58
+ super().__init__(
59
+ f"SOQL param {key!r} rejected: {reason} (preview={preview!r})"
60
+ )
61
+
62
+
63
+ class SoqlTemplateNotFound(LookupError):
64
+ """Raised when `load_soql(name)` cannot locate a matching template file.
65
+
66
+ the message deliberately carries ONLY the requested
67
+ template name — never the absolute SOQL_DIR path. This prevents leaking
68
+ the filesystem layout of the skill install (which the attacker does not
69
+ otherwise need to know) via error messages that bubble up to logs or
70
+ RESULT blocks.
71
+
72
+ Distinct from `FileNotFoundError` so callers can tell "template missing"
73
+ apart from "filesystem I/O error" (permission denied, etc.) at the
74
+ except-clause layer.
75
+ """
76
+
77
+ def __init__(self, name: str) -> None:
78
+ # Preview bounds the echoed name at 60 chars. The validator forbids
79
+ # traversal characters anyway, but defence in depth against an
80
+ # arbitrarily-long attacker-controlled name ever reaching logs.
81
+ try:
82
+ preview = str(name)[:60]
83
+ except Exception:
84
+ preview = "<unreprable>"
85
+ self.name = preview
86
+ super().__init__(f"SOQL template not found: {preview!r}")
87
+
88
+
89
+ def load_soql(name: str, **params) -> str:
90
+ """Read assets/soql/<name>.soql and substitute {{PARAM}} placeholders.
91
+
92
+ every value in `params` is revalidated via
93
+ `fs_guard.validate_api_name` before being substituted. Invalid values
94
+ (non-string, empty, or containing any character outside [A-Za-z0-9_])
95
+ raise `SoqlParamError`.
96
+
97
+ `name` itself is validated via the same
98
+ `fs_guard.validate_api_name` regex BEFORE any filesystem access.
99
+ Without this, a caller that sources `name` from data (config file,
100
+ user argument, etc.) could read `SOQL_DIR/../../../tmp/pwn.soql` via
101
+ traversal. All SOQL template filenames in this skill match
102
+ `^[A-Za-z0-9_]+$` (e.g. `bot_version_lookup`, `plugins_by_planner`),
103
+ so the regex is the correct gate.
104
+
105
+ Substitution is single-pass: `str.replace` does not re-scan the output,
106
+ so a value that contains `{{OTHER}}` cannot re-trigger substitution of
107
+ a different key. This is verified by test_soql_loader.
108
+ """
109
+ # validate the template name BEFORE touching the filesystem.
110
+ # A raw `../../../etc/passwd` would otherwise resolve via read_text().
111
+ try:
112
+ fs_guard.validate_api_name(name, label="soql_template_name")
113
+ except fs_guard.ValidationError as e:
114
+ raise SoqlParamError("soql_template_name", name, e.reason) from None
115
+
116
+ # revalidate every SOQL param before substitution. No exceptions,
117
+ # no "this caller already validated" shortcuts. Defence in depth.
118
+ for key, value in params.items():
119
+ try:
120
+ fs_guard.validate_api_name(value, label=key)
121
+ except fs_guard.ValidationError as e:
122
+ raise SoqlParamError(key, value, e.reason) from None
123
+
124
+ # translate FileNotFoundError into SoqlTemplateNotFound so the
125
+ # absolute SOQL_DIR path never bleeds into caller-visible error text.
126
+ # We intentionally do NOT chain the original exception (`from None`) —
127
+ # the original carries the full path in `filename`/`strerror`.
128
+ try:
129
+ soql = (SOQL_DIR / f"{name}.soql").read_text()
130
+ except FileNotFoundError:
131
+ raise SoqlTemplateNotFound(name) from None
132
+
133
+ for key, value in params.items():
134
+ # Plain str.replace: single-pass by construction. No re-substitution
135
+ # of placeholders that appear inside a substituted value (see test).
136
+ soql = soql.replace(f"{{{{{key}}}}}", value)
137
+ return soql.strip()
138
+
139
+
140
+ def load_soql_in(
141
+ name: str,
142
+ *,
143
+ string_params: Optional[Dict[str, str]] = None,
144
+ list_params: Dict[str, List[str]],
145
+ ) -> str:
146
+ """Read assets/soql/<name>.soql and substitute both scalar and list placeholders.
147
+
148
+ the BFS fan-out builds `WHERE X IN (:list)` queries
149
+ (apex_class_bodies_by_names, flow_definition_by_ids, plugin_functions_
150
+ by_plugin_ids, etc.). The existing `load_soql` only handles scalar
151
+ string substitution; this sibling covers the list case without
152
+ touching that signature.
153
+
154
+ Parameters
155
+ ----------
156
+ name:
157
+ Template filename (without `.soql`). Validated via
158
+ `fs_guard.validate_api_name` BEFORE any filesystem access — same
159
+ defence-in-depth gate as `load_soql` .
160
+ string_params:
161
+ Optional scalar {{KEY}} → value map. Values revalidated through
162
+ `fs_guard.validate_api_name` — identical path to `load_soql`.
163
+ Useful for templates that mix a single scalar value with a list
164
+ in the same query. No active callers at present (retired with
165
+ `flow_definition_ids_by_name_with_ns.soql` on 2026-05-05); kept
166
+ on the signature as a stable extension point.
167
+ list_params:
168
+ Required. {{KEY}} → list-of-strings map. Each element is
169
+ revalidated through `fs_guard.validate_api_name`; any failure
170
+ aborts the whole call with `SoqlParamError(key=<list-key>, ...)`.
171
+ Empty lists raise — SOQL `WHERE X IN ()` is a syntax error in
172
+ Salesforce, fail fast rather than let the CLI surface an opaque
173
+ 500.
174
+
175
+ Rendering
176
+ ---------
177
+ Lists render as `','.join(f"'{v}'" for v in sorted(set(list_value)))`:
178
+ single-quoted, comma-separated, no surrounding parens. The `.soql`
179
+ template supplies the parens around the `{{PLACEHOLDER}}` so the
180
+ result reads `WHERE Id IN ('A','B','C')`.
181
+
182
+ Deduplication is load-bearing: SOQL tolerates dupes but they waste
183
+ the 100k-char query-length budget. Sort is for deterministic order —
184
+ stable cache keys and diffable test output.
185
+
186
+ Returns
187
+ -------
188
+ Substituted, `.strip()`-ed SOQL string.
189
+ """
190
+ # validate template name BEFORE touching the filesystem.
191
+ try:
192
+ fs_guard.validate_api_name(name, label="soql_template_name")
193
+ except fs_guard.ValidationError as e:
194
+ raise SoqlParamError("soql_template_name", name, e.reason) from None
195
+
196
+ string_params = string_params or {}
197
+
198
+ # / revalidate scalar params — same path as `load_soql`.
199
+ for key, value in string_params.items():
200
+ try:
201
+ fs_guard.validate_api_name(value, label=key)
202
+ except fs_guard.ValidationError as e:
203
+ raise SoqlParamError(key, value, e.reason) from None
204
+
205
+ # validate list params first (deterministic failure order — we
206
+ # want the first bad element, not a partially-rendered string).
207
+ rendered_lists: Dict[str, str] = {}
208
+ for key, list_value in list_params.items():
209
+ if not isinstance(list_value, list):
210
+ raise SoqlParamError(
211
+ key,
212
+ list_value,
213
+ f"list_params[{key}] must be a list, got {type(list_value).__name__}",
214
+ )
215
+ if not list_value:
216
+ # Salesforce `WHERE X IN ()` is a hard syntax error — fail fast
217
+ # at the loader, not at the CLI. Callers should branch on the
218
+ # empty-list case upstream (e.g. skip the whole SOQL) rather
219
+ # than rely on an empty-clause fallback.
220
+ raise SoqlParamError(
221
+ key, "[]", "empty-list-would-produce-invalid-soql",
222
+ )
223
+ # Dedupe + sort: cache-key stable, query-length budget sparing.
224
+ # `sorted(set(...))` raises TypeError on non-hashable elements;
225
+ # validate_api_name below will surface the real cause for
226
+ # non-strings, so gate on type FIRST.
227
+ for element in list_value:
228
+ try:
229
+ fs_guard.validate_api_name(element, label=key)
230
+ except fs_guard.ValidationError as e:
231
+ raise SoqlParamError(key, element, e.reason) from None
232
+ unique_sorted = sorted(set(list_value))
233
+ # Single-quote each element, comma-join. The `.soql` template
234
+ # supplies the surrounding parens around `{{KEY}}`.
235
+ rendered_lists[key] = ",".join(f"'{v}'" for v in unique_sorted)
236
+
237
+ # translate FileNotFoundError into SoqlTemplateNotFound. Same
238
+ # hygiene contract as `load_soql` — the absolute SOQL_DIR path must
239
+ # not bleed into caller-visible error text.
240
+ try:
241
+ soql = (SOQL_DIR / f"{name}.soql").read_text()
242
+ except FileNotFoundError:
243
+ raise SoqlTemplateNotFound(name) from None
244
+
245
+ # Single-pass substitution, same semantics as `load_soql`. Scalars
246
+ # first, lists second — the order is only relevant if a scalar value
247
+ # could contain a list-placeholder token, which `validate_api_name`
248
+ # forbids (no `{` or `}` in `[A-Za-z0-9_]+`).
249
+ for key, value in string_params.items():
250
+ soql = soql.replace(f"{{{{{key}}}}}", value)
251
+ for key, rendered in rendered_lists.items():
252
+ soql = soql.replace(f"{{{{{key}}}}}", rendered)
253
+ return soql.strip()
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env python3
2
+ """Render the declared action tree as a box-drawing summary.md.
3
+
4
+ Called by finalize.py (cold path) and cache_check.py (hit path when the
5
+ cached summary is missing). Mirrors the old agent Phase 7 render_tree()
6
+ logic verbatim.
7
+
8
+ Usage:
9
+ python3 summarize_tree.py <tree_json_path> <out_md_path> <built_at_utc>
10
+
11
+ Inputs:
12
+ argv[1] absolute path to declared_action_tree.json (or similar)
13
+ argv[2] output .summary.md path (overwritten atomically)
14
+ argv[3] ISO-8601 UTC built-at timestamp (e.g. 2026-04-25T14:00:00Z)
15
+
16
+ Outputs:
17
+ argv[2] rendered markdown
18
+ exit 0 success
19
+ exit 1 missing args, read failure, write failure
20
+ """
21
+ import json
22
+ import os
23
+ import pathlib
24
+ import sys
25
+
26
+ KC_ORDER = [
27
+ "BOT_DEFINITION", "TOPIC", "GEN_AI_FUNCTION",
28
+ "FLOW", "APEX", "PROMPT_TEMPLATE",
29
+ "STANDARD_ACTION", "UNKNOWN",
30
+ ]
31
+
32
+
33
+ def render_tree(node, prefix="", is_last=True, lines=None, agent_=None):
34
+ if lines is None:
35
+ lines = []
36
+ connector = "└── " if is_last else "├── "
37
+ kind = node.get("kind", "?")
38
+ api = node.get("api_name", "?")
39
+ elem = node.get("element_name")
40
+ if kind == "BOT_DEFINITION":
41
+ v = (agent_ or {}).get("version") or ""
42
+ label = f"[{kind}] {api}" + (f" ({v})" if v else "")
43
+ elif kind == "GEN_AI_FUNCTION":
44
+ unwraps = node.get("unwraps_to") or {}
45
+ uk = unwraps.get("kind")
46
+ un = unwraps.get("api_name")
47
+ arrow = f" → {uk}:{un}" if uk and un else ""
48
+ label = f"[{kind}] {api}{arrow}"
49
+ elif kind in ("STANDARD_ACTION", "UNKNOWN"):
50
+ # Canonical key is `invocation_type` (schema 3.1+); fall back to
51
+ # the two legacy keys so caches built by an older parse_wave still
52
+ # render the qualifier.
53
+ raw = (
54
+ node.get("invocation_type")
55
+ or node.get("raw_invocation_type")
56
+ or node.get("raw_action_type")
57
+ or ""
58
+ )
59
+ label = f"[{kind}] {api}" + (f" ({raw})" if raw else "")
60
+ else:
61
+ label = f"[{kind}] {api}"
62
+ if elem and kind != "BOT_DEFINITION":
63
+ label += f" — element:{elem}"
64
+ lines.append(prefix + connector + label)
65
+ children = node.get("children") or []
66
+ child_prefix = prefix + (" " if is_last else "│ ")
67
+ for i, c in enumerate(children):
68
+ render_tree(c, child_prefix, i == len(children) - 1, lines, agent_)
69
+ return lines
70
+
71
+
72
+ def main() -> int:
73
+ if len(sys.argv) != 4:
74
+ sys.stderr.write(
75
+ "summarize_tree.py: usage: python3 summarize_tree.py <tree_json_path> "
76
+ "<out_md_path> <built_at_utc>\n"
77
+ )
78
+ return 1
79
+
80
+ tree_path = pathlib.Path(sys.argv[1])
81
+ out_path = pathlib.Path(sys.argv[2])
82
+ built_at = sys.argv[3]
83
+
84
+ try:
85
+ tree = json.loads(tree_path.read_text())
86
+ except (OSError, json.JSONDecodeError) as e:
87
+ sys.stderr.write(f"summarize_tree.py: cannot read {tree_path}: {e}\n")
88
+ return 1
89
+
90
+ a = tree.get("agent", {}) or {}
91
+ kc = tree.get("_kind_counts", {}) or {}
92
+
93
+ lines = [
94
+ f"# {a.get('api_name', '?')} {a.get('version', '?')} — declared action tree",
95
+ "",
96
+ f"- **Bot ID:** `{a.get('bot_id', '?')}`",
97
+ f"- **Master label:** {a.get('master_label', '?')}",
98
+ f"- **Generation:** {a.get('generation', 'unknown')}",
99
+ f"- **Planner:** `{a.get('planner_name', '?')}` ({a.get('planner_type', '?')})",
100
+ f"- **Version auto-picked:** {a.get('_version_auto_picked', False)}",
101
+ f"- **Built at (UTC):** {built_at}",
102
+ f"- **Node count:** {tree.get('node_count', 0)}",
103
+ f"- **Depth:** {tree.get('depth', 0)}",
104
+ f"- **Partial:** {tree.get('_partial', False)}",
105
+ "",
106
+ "## Kind counts",
107
+ "",
108
+ ]
109
+ for k in KC_ORDER:
110
+ lines.append(f"- `{k}`: {kc.get(k, 0)}")
111
+
112
+ lines += [
113
+ "",
114
+ "## Declared action tree",
115
+ "",
116
+ "```",
117
+ ]
118
+ root = tree.get("root") or {"kind": "BOT_DEFINITION", "api_name": a.get("api_name", "?"), "children": []}
119
+ lines.extend(render_tree(root, "", True, None, a))
120
+ lines.append("```")
121
+
122
+ if tree.get("_unresolved"):
123
+ lines += ["", "## Unresolved", ""]
124
+ for u in tree["_unresolved"]:
125
+ lines.append(
126
+ f"- `{u.get('kind')}`/`{u.get('api_name')}` — {u.get('reason')}"
127
+ )
128
+
129
+ content = "\n".join(lines) + "\n"
130
+
131
+ try:
132
+ out_path.parent.mkdir(parents=True, exist_ok=True)
133
+ tmp = out_path.with_suffix(out_path.suffix + ".tmp")
134
+ tmp.write_text(content)
135
+ os.replace(tmp, out_path)
136
+ except OSError as e:
137
+ sys.stderr.write(f"summarize_tree.py: write failed: {e}\n")
138
+ return 1
139
+ return 0
140
+
141
+
142
+ if __name__ == "__main__":
143
+ sys.exit(main())
@@ -0,0 +1,23 @@
1
+ """Shared sys.path bootstrap for the test suite.
2
+
3
+ The scripts/ scripts expect to be imported as top-level modules (config,
4
+ soql_loader, rest_client, sf_cli) — that's how they behave when main.py
5
+ does `from config import ...`. Tests mirror that by inserting scripts/
6
+ onto sys.path.
7
+
8
+ fs_guard is now sourced via ``from config import fs_guard`` (config.py
9
+ re-exports it from the plugin _shared/ package). No tools/ entry on
10
+ sys.path is required — config.py's dev-fallback walks up to the repo's
11
+ ``plugins/investigating-agentforce-architecture/shared/`` when ``scripts/_shared/``
12
+ hasn't yet been mirrored by install.sh / the build script.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ _SCRIPTS_DIR = Path(__file__).resolve().parent.parent
20
+
21
+ s = str(_SCRIPTS_DIR)
22
+ if s not in sys.path:
23
+ sys.path.insert(0, s)