bi-superpowers 1.0.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 (276) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +34 -0
  3. package/.claude-plugin/skill-manifest.json +79 -0
  4. package/.mcp.json +13 -0
  5. package/.plugin/plugin.json +14 -0
  6. package/LICENSE +21 -0
  7. package/README.md +849 -0
  8. package/bin/build-plugin.js +97 -0
  9. package/bin/cli.js +891 -0
  10. package/bin/commands/autoupdate.js +128 -0
  11. package/bin/commands/build-desktop.js +368 -0
  12. package/bin/commands/create-from-template.js +165 -0
  13. package/bin/commands/diff.js +435 -0
  14. package/bin/commands/install.js +542 -0
  15. package/bin/commands/lint.js +441 -0
  16. package/bin/commands/mcp-setup.js +255 -0
  17. package/bin/commands/session-update.js +204 -0
  18. package/bin/commands/smoke-test.js +20 -0
  19. package/bin/commands/uninstall.js +611 -0
  20. package/bin/commands/update-check.js +427 -0
  21. package/bin/commands/validate-cases.js +264 -0
  22. package/bin/commands/validate-projects.js +426 -0
  23. package/bin/commands/watch.js +251 -0
  24. package/bin/lib/agents.js +62 -0
  25. package/bin/lib/base-template-smoke.js +299 -0
  26. package/bin/lib/claude-hooks.js +160 -0
  27. package/bin/lib/generators/claude-plugin.js +529 -0
  28. package/bin/lib/generators/index.js +116 -0
  29. package/bin/lib/generators/shared.js +257 -0
  30. package/bin/lib/mcp-config.js +835 -0
  31. package/bin/lib/microsoft-mcp.js +206 -0
  32. package/bin/lib/powerbi-mcp-session.js +140 -0
  33. package/bin/lib/skills.js +164 -0
  34. package/bin/lib/template-scaffold.js +366 -0
  35. package/bin/mcp/powerbi-modeling-launcher.js +42 -0
  36. package/bin/postinstall.js +50 -0
  37. package/bin/utils/mcp-detect.js +346 -0
  38. package/bin/utils/tui.js +314 -0
  39. package/commands/bi-connect.md +520 -0
  40. package/commands/bi-dax.md +464 -0
  41. package/commands/bi-kickoff.md +550 -0
  42. package/commands/bi-modeling.md +485 -0
  43. package/commands/bi-performance.md +521 -0
  44. package/commands/bi-powerquery.md +229 -0
  45. package/commands/bi-refactor.md +249 -0
  46. package/commands/bi-scorecard.md +268 -0
  47. package/commands/bi-start.md +272 -0
  48. package/config.example.json +23 -0
  49. package/config.json +23 -0
  50. package/desktop-extension/manifest.json +30 -0
  51. package/desktop-extension/package.json +10 -0
  52. package/desktop-extension/server.js +137 -0
  53. package/package.json +94 -0
  54. package/skills/bi-connect/SKILL.md +522 -0
  55. package/skills/bi-connect/scripts/update-check.js +427 -0
  56. package/skills/bi-dax/SKILL.md +466 -0
  57. package/skills/bi-dax/scripts/update-check.js +427 -0
  58. package/skills/bi-kickoff/SKILL.md +552 -0
  59. package/skills/bi-kickoff/references/flow.html +78 -0
  60. package/skills/bi-kickoff/references/flow.md +62 -0
  61. package/skills/bi-kickoff/scripts/update-check.js +427 -0
  62. package/skills/bi-modeling/SKILL.md +487 -0
  63. package/skills/bi-modeling/scripts/update-check.js +427 -0
  64. package/skills/bi-performance/SKILL.md +523 -0
  65. package/skills/bi-performance/scripts/install-tabular-editor.ps1 +159 -0
  66. package/skills/bi-performance/scripts/run-bpa.ps1 +265 -0
  67. package/skills/bi-performance/scripts/update-check.js +427 -0
  68. package/skills/bi-powerquery/SKILL.md +231 -0
  69. package/skills/bi-powerquery/references/base-template-data-contract.md +323 -0
  70. package/skills/bi-powerquery/references/power-query-standards.md +74 -0
  71. package/skills/bi-powerquery/scripts/new-powerquery-staging.ps1 +371 -0
  72. package/skills/bi-powerquery/scripts/test-powerquery-contract.ps1 +225 -0
  73. package/skills/bi-powerquery/scripts/update-check.js +427 -0
  74. package/skills/bi-refactor/SKILL.md +251 -0
  75. package/skills/bi-refactor/references/flow.md +27 -0
  76. package/skills/bi-refactor/scripts/update-check.js +427 -0
  77. package/skills/bi-scorecard/SKILL.md +270 -0
  78. package/skills/bi-scorecard/examples/base-template-scorecard-overlay.json +82 -0
  79. package/skills/bi-scorecard/scripts/new-scorecard-blueprint-from-base-template.ps1 +124 -0
  80. package/skills/bi-scorecard/scripts/powerbi-goal-status-rules-api.ps1 +39 -0
  81. package/skills/bi-scorecard/scripts/powerbi-goal-values-api.ps1 +48 -0
  82. package/skills/bi-scorecard/scripts/powerbi-goals-api.ps1 +68 -0
  83. package/skills/bi-scorecard/scripts/powerbi-rest-common.ps1 +197 -0
  84. package/skills/bi-scorecard/scripts/powerbi-scorecards-api.ps1 +53 -0
  85. package/skills/bi-scorecard/scripts/update-check.js +427 -0
  86. package/skills/bi-start/SKILL.md +274 -0
  87. package/skills/bi-start/scripts/update-check.js +427 -0
  88. package/src/content/base.md +197 -0
  89. package/src/content/mcp-requirements.json +57 -0
  90. package/src/content/routing.md +201 -0
  91. package/src/content/skills/bi-connect.md +493 -0
  92. package/src/content/skills/bi-dax.md +437 -0
  93. package/src/content/skills/bi-kickoff/SKILL.md +523 -0
  94. package/src/content/skills/bi-kickoff/references/flow.html +78 -0
  95. package/src/content/skills/bi-kickoff/references/flow.md +62 -0
  96. package/src/content/skills/bi-modeling.md +458 -0
  97. package/src/content/skills/bi-performance/SKILL.md +494 -0
  98. package/src/content/skills/bi-performance/scripts/install-tabular-editor.ps1 +159 -0
  99. package/src/content/skills/bi-performance/scripts/run-bpa.ps1 +265 -0
  100. package/src/content/skills/bi-powerquery/SKILL.md +202 -0
  101. package/src/content/skills/bi-powerquery/references/base-template-data-contract.md +323 -0
  102. package/src/content/skills/bi-powerquery/references/power-query-standards.md +74 -0
  103. package/src/content/skills/bi-powerquery/scripts/new-powerquery-staging.ps1 +371 -0
  104. package/src/content/skills/bi-powerquery/scripts/test-powerquery-contract.ps1 +225 -0
  105. package/src/content/skills/bi-refactor/SKILL.md +222 -0
  106. package/src/content/skills/bi-refactor/references/flow.md +27 -0
  107. package/src/content/skills/bi-scorecard/SKILL.md +241 -0
  108. package/src/content/skills/bi-scorecard/examples/base-template-scorecard-blueprint.expected.json +105 -0
  109. package/src/content/skills/bi-scorecard/examples/base-template-scorecard-overlay.json +82 -0
  110. package/src/content/skills/bi-scorecard/scripts/new-scorecard-blueprint-from-base-template.ps1 +124 -0
  111. package/src/content/skills/bi-scorecard/scripts/powerbi-goal-status-rules-api.ps1 +39 -0
  112. package/src/content/skills/bi-scorecard/scripts/powerbi-goal-values-api.ps1 +48 -0
  113. package/src/content/skills/bi-scorecard/scripts/powerbi-goals-api.ps1 +68 -0
  114. package/src/content/skills/bi-scorecard/scripts/powerbi-rest-common.ps1 +197 -0
  115. package/src/content/skills/bi-scorecard/scripts/powerbi-scorecards-api.ps1 +53 -0
  116. package/src/content/skills/bi-start.md +266 -0
  117. package/templates/base-template/AGENTS.md +33 -0
  118. package/templates/base-template/base-template.Report/.platform +11 -0
  119. package/templates/base-template/base-template.Report/StaticResources/RegisteredResources/BISuperpowers.json +3888 -0
  120. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BaseThemes/CY18SU07.json +177 -0
  121. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BaseThemes/Fluent2-CY26SU03.json +4104 -0
  122. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/AccessibleCityPark.json +26 -0
  123. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/AccessibleDefault.json +26 -0
  124. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/AccessibleNeutral.json +26 -0
  125. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/AccessibleOrchid.json +26 -0
  126. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/AccessibleTidal.json +26 -0
  127. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Bloom.json +139 -0
  128. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/CityPark.json +40 -0
  129. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Classroom.json +40 -0
  130. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/ColorblindSafe.json +48 -0
  131. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/CopilotDefault.json +1861 -0
  132. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Divergent.json +127 -0
  133. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Electric.json +48 -0
  134. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Frontier.json +136 -0
  135. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/HighContrast.json +40 -0
  136. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Highrise.json +41 -0
  137. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Innovate.json +227 -0
  138. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/NewExecutive.json +41 -0
  139. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Solar.json +33 -0
  140. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Storm.json +25 -0
  141. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Sunset.json +48 -0
  142. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Temperature.json +33 -0
  143. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Tidal.json +100 -0
  144. package/templates/base-template/base-template.Report/StaticResources/SharedResources/BuiltInThemes/Twilight.json +40 -0
  145. package/templates/base-template/base-template.Report/definition/bookmarks/1d40d43c7ade66e8603c.bookmark.json +2297 -0
  146. package/templates/base-template/base-template.Report/definition/bookmarks/af068ff51c0ca3089ea7.bookmark.json +2300 -0
  147. package/templates/base-template/base-template.Report/definition/bookmarks/bookmarks.json +11 -0
  148. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/page.json +130 -0
  149. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/0352fd80d074693a65db/mobile.json +11 -0
  150. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/0352fd80d074693a65db/visual.json +669 -0
  151. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/1c5a14bf493697344b68/mobile.json +11 -0
  152. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/1c5a14bf493697344b68/visual.json +723 -0
  153. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/3486cf7624c5b109b4e5/mobile.json +11 -0
  154. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/3486cf7624c5b109b4e5/visual.json +333 -0
  155. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/4d8b989008edc0db28d1/mobile.json +11 -0
  156. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/4d8b989008edc0db28d1/visual.json +109 -0
  157. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/55e10ac7d76a1954f94f/mobile.json +31 -0
  158. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/55e10ac7d76a1954f94f/visual.json +378 -0
  159. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/57f52ecf4490f70e4da1/mobile.json +11 -0
  160. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/57f52ecf4490f70e4da1/visual.json +175 -0
  161. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/5f4d76bbc870118e9840/mobile.json +11 -0
  162. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/5f4d76bbc870118e9840/visual.json +468 -0
  163. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/73629e1abebb7a444b59/mobile.json +11 -0
  164. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/73629e1abebb7a444b59/visual.json +359 -0
  165. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/749cb1388c7e0a88161c/mobile.json +11 -0
  166. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/749cb1388c7e0a88161c/visual.json +690 -0
  167. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/90677f13cea5d1275990/visual.json +17 -0
  168. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/92cf92e3da10493adb78/mobile.json +11 -0
  169. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/92cf92e3da10493adb78/visual.json +468 -0
  170. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/9fe17b1971f68443fc15/mobile.json +10 -0
  171. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/9fe17b1971f68443fc15/visual.json +328 -0
  172. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/a30bd0950630ed94e8a3/mobile.json +11 -0
  173. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/a30bd0950630ed94e8a3/visual.json +578 -0
  174. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/a56e91d9400a835e4814/mobile.json +11 -0
  175. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/a56e91d9400a835e4814/visual.json +432 -0
  176. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/aded24cd205c0b528642/mobile.json +11 -0
  177. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/aded24cd205c0b528642/visual.json +801 -0
  178. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/af34b26f14a8a724c9a9/mobile.json +37 -0
  179. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/af34b26f14a8a724c9a9/visual.json +1318 -0
  180. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/b529688fe5a226643322/visual.json +209 -0
  181. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/c4c6f332d05e72e2eb06/mobile.json +11 -0
  182. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/c4c6f332d05e72e2eb06/visual.json +174 -0
  183. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/fa81f184e2cb0e8b087c/mobile.json +29 -0
  184. package/templates/base-template/base-template.Report/definition/pages/6a4808bb8bb9166f49ff/visuals/fa81f184e2cb0e8b087c/visual.json +241 -0
  185. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/page.json +130 -0
  186. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/07e9c4302e29029c5462/mobile.json +11 -0
  187. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/07e9c4302e29029c5462/visual.json +690 -0
  188. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/109ceede4bc015b0c006/mobile.json +11 -0
  189. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/109ceede4bc015b0c006/visual.json +468 -0
  190. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/118257e006d472277e10/mobile.json +11 -0
  191. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/118257e006d472277e10/visual.json +359 -0
  192. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/2caf02e0137c4a1280cc/mobile.json +11 -0
  193. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/2caf02e0137c4a1280cc/visual.json +669 -0
  194. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/311e76fe3c9edad68204/mobile.json +11 -0
  195. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/311e76fe3c9edad68204/visual.json +109 -0
  196. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/31c21f8cbeb3b208940a/visual.json +209 -0
  197. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/3ab72c25062437149b03/visual.json +17 -0
  198. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/5959867442abcb0ce2b3/mobile.json +11 -0
  199. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/5959867442abcb0ce2b3/visual.json +788 -0
  200. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/5b96e0f88d192b044a13/mobile.json +11 -0
  201. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/5b96e0f88d192b044a13/visual.json +592 -0
  202. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/64e749a63d0786000e22/mobile.json +11 -0
  203. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/64e749a63d0786000e22/visual.json +468 -0
  204. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/7ae1ca604edac6586ad0/mobile.json +11 -0
  205. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/7ae1ca604edac6586ad0/visual.json +1310 -0
  206. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/840300733885141a6603/mobile.json +11 -0
  207. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/840300733885141a6603/visual.json +175 -0
  208. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/a38448cdb203279273d2/mobile.json +11 -0
  209. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/a38448cdb203279273d2/visual.json +516 -0
  210. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/d1e86f213a3841d12e20/visual.json +328 -0
  211. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/d4a484c1bcc8ee3075e2/mobile.json +11 -0
  212. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/d4a484c1bcc8ee3075e2/visual.json +432 -0
  213. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/d87cb5cf06acca19bbb5/mobile.json +11 -0
  214. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/d87cb5cf06acca19bbb5/visual.json +241 -0
  215. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/e243da2677209ed69408/mobile.json +11 -0
  216. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/e243da2677209ed69408/visual.json +174 -0
  217. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/f3aaf24f5b22b67573b0/mobile.json +11 -0
  218. package/templates/base-template/base-template.Report/definition/pages/9a5b670b015cab882629/visuals/f3aaf24f5b22b67573b0/visual.json +333 -0
  219. package/templates/base-template/base-template.Report/definition/pages/pages.json +8 -0
  220. package/templates/base-template/base-template.Report/definition/report.json +89 -0
  221. package/templates/base-template/base-template.Report/definition/version.json +4 -0
  222. package/templates/base-template/base-template.Report/definition.pbir +9 -0
  223. package/templates/base-template/base-template.SemanticModel/.platform +11 -0
  224. package/templates/base-template/base-template.SemanticModel/definition/cultures/es-AR.tmdl +11185 -0
  225. package/templates/base-template/base-template.SemanticModel/definition/database.tmdl +3 -0
  226. package/templates/base-template/base-template.SemanticModel/definition/expressions.tmdl +234 -0
  227. package/templates/base-template/base-template.SemanticModel/definition/functions.tmdl +637 -0
  228. package/templates/base-template/base-template.SemanticModel/definition/model.tmdl +82 -0
  229. package/templates/base-template/base-template.SemanticModel/definition/relationships.tmdl +271 -0
  230. package/templates/base-template/base-template.SemanticModel/definition/tables/Calendario.tmdl +200 -0
  231. package/templates/base-template/base-template.SemanticModel/definition/tables/Campa/303/261as.tmdl +75 -0
  232. package/templates/base-template/base-template.SemanticModel/definition/tables/Canales.tmdl +84 -0
  233. package/templates/base-template/base-template.SemanticModel/definition/tables/Clientes.tmdl +143 -0
  234. package/templates/base-template/base-template.SemanticModel/definition/tables/Devoluciones.tmdl +95 -0
  235. package/templates/base-template/base-template.SemanticModel/definition/tables/Ejecuci/303/263n proyectos.tmdl" +130 -0
  236. package/templates/base-template/base-template.SemanticModel/definition/tables/Entregas.tmdl +122 -0
  237. package/templates/base-template/base-template.SemanticModel/definition/tables/Equipos m/303/251tricas.tmdl" +40 -0
  238. package/templates/base-template/base-template.SemanticModel/definition/tables/Equipos.tmdl +73 -0
  239. package/templates/base-template/base-template.SemanticModel/definition/tables/Horas.tmdl +122 -0
  240. package/templates/base-template/base-template.SemanticModel/definition/tables/Interacciones clientes.tmdl +146 -0
  241. package/templates/base-template/base-template.SemanticModel/definition/tables/Leads.tmdl +119 -0
  242. package/templates/base-template/base-template.SemanticModel/definition/tables/Monedas.tmdl +44 -0
  243. package/templates/base-template/base-template.SemanticModel/definition/tables/Movimientos financieros.tmdl +145 -0
  244. package/templates/base-template/base-template.SemanticModel/definition/tables/M/303/251tricas.tmdl +1294 -0
  245. package/templates/base-template/base-template.SemanticModel/definition/tables/N/303/263mina.tmdl +110 -0
  246. package/templates/base-template/base-template.SemanticModel/definition/tables/Oportunidades.tmdl +135 -0
  247. package/templates/base-template/base-template.SemanticModel/definition/tables/Presupuesto.tmdl +125 -0
  248. package/templates/base-template/base-template.SemanticModel/definition/tables/Productos.tmdl +98 -0
  249. package/templates/base-template/base-template.SemanticModel/definition/tables/Proyectos.tmdl +77 -0
  250. package/templates/base-template/base-template.SemanticModel/definition/tables/Servicios.tmdl +75 -0
  251. package/templates/base-template/base-template.SemanticModel/definition/tables/Tareas proyecto.tmdl +102 -0
  252. package/templates/base-template/base-template.SemanticModel/definition/tables/Tipo de cambio.tmdl +67 -0
  253. package/templates/base-template/base-template.SemanticModel/definition/tables/Ventas.tmdl +180 -0
  254. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux An/303/241lisis dimensiones.tmdl" +38 -0
  255. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Comparaciones.tmdl +227 -0
  256. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Compatibilidad m/303/251trica-dimensi/303/263n.tmdl" +68 -0
  257. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Modelo configuraci/303/263n.tmdl" +44 -0
  258. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Modo fechas.tmdl +36 -0
  259. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux M/303/251trica-Equipo.tmdl" +102 -0
  260. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Overrides m/303/251trica-dimensi/303/263n.tmdl" +54 -0
  261. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Per/303/255odos.tmdl" +182 -0
  262. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Rango fechas modo.tmdl +36 -0
  263. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Rango fechas.tmdl +27 -0
  264. package/templates/base-template/base-template.SemanticModel/definition/tables/_Aux Vista de calendario.tmdl +30 -0
  265. package/templates/base-template/base-template.SemanticModel/definition/tables/_GC C/303/241lculo.tmdl" +70 -0
  266. package/templates/base-template/base-template.SemanticModel/definition/tables/_GC Eje X.tmdl +63 -0
  267. package/templates/base-template/base-template.SemanticModel/definition/tables/_GC M/303/251trica.tmdl" +374 -0
  268. package/templates/base-template/base-template.SemanticModel/definition/tables/_GC Tipo c/303/241lculo.tmdl" +223 -0
  269. package/templates/base-template/base-template.SemanticModel/definition/tables/_PC Dimensi/303/263n.tmdl" +98 -0
  270. package/templates/base-template/base-template.SemanticModel/definition/tables/_PC Eje X.tmdl +68 -0
  271. package/templates/base-template/base-template.SemanticModel/definition/tables//303/223rdenes servicio.tmdl" +151 -0
  272. package/templates/base-template/base-template.SemanticModel/definition.pbism +5 -0
  273. package/templates/base-template/base-template.SemanticModel/diagramLayout.json +568 -0
  274. package/templates/base-template/base-template.pbip +14 -0
  275. package/templates/base-template/template.manifest.json +41 -0
  276. package/theme/BISuperpowers.json +3888 -0
@@ -0,0 +1,159 @@
1
+ # install-tabular-editor.ps1
2
+ # Detects Tabular Editor 2 (free, MIT) at the expected install path.
3
+ # With -Install, downloads the latest stable release ZIP from GitHub and
4
+ # extracts it to %LOCALAPPDATA%\TabularEditor\.
5
+ #
6
+ # Outputs the absolute path to TabularEditor.exe on stdout.
7
+ # Exit code: 0 = installed/found; non-zero = missing or infrastructure failure.
8
+ #
9
+ # Used by /bi-performance and /bi-modeling for Best Practice Analyzer
10
+ # and VertiPaq Analyzer integration.
11
+
12
+ #requires -Version 7.0
13
+
14
+ [CmdletBinding()]
15
+ param(
16
+ [switch]$Force,
17
+ [switch]$Install
18
+ )
19
+
20
+ $ErrorActionPreference = 'Stop'
21
+
22
+ function Get-LocalAppDataPath {
23
+ $localAppData = $env:LOCALAPPDATA
24
+ if ([string]::IsNullOrWhiteSpace($localAppData)) {
25
+ $localAppData = [Environment]::GetFolderPath('LocalApplicationData')
26
+ }
27
+ if ([string]::IsNullOrWhiteSpace($localAppData)) {
28
+ throw 'LOCALAPPDATA is not set and LocalApplicationData could not be resolved.'
29
+ }
30
+ return $localAppData
31
+ }
32
+
33
+ function Assert-ValidAuthenticodeSignature {
34
+ param(
35
+ [Parameter(Mandatory = $true)]
36
+ [string]$FilePath
37
+ )
38
+
39
+ # Get-AuthenticodeSignature is a Windows-only cmdlet. If it is unavailable we
40
+ # cannot verify; warn and proceed rather than blocking an otherwise-working
41
+ # Tabular Editor install.
42
+ if (-not (Get-Command Get-AuthenticodeSignature -ErrorAction SilentlyContinue)) {
43
+ Write-Warning "Cannot check the Authenticode signature on this platform; skipping verification for ${FilePath}."
44
+ return
45
+ }
46
+
47
+ $signature = Get-AuthenticodeSignature -FilePath $FilePath
48
+
49
+ # Explicit tampering / trust failures are fatal — never run a binary that
50
+ # fails these.
51
+ $fatalStatuses = @('HashMismatch', 'NotTrusted', 'NotSigned')
52
+ if ($fatalStatuses -contains $signature.Status) {
53
+ throw "Authenticode signature validation failed for ${FilePath}: $($signature.Status)"
54
+ }
55
+
56
+ # 'Valid' is the happy path. Anything else (most often 'UnknownError' when the
57
+ # certificate revocation servers are unreachable on offline / locked-down
58
+ # corporate machines) is NOT evidence of tampering, so warn and continue
59
+ # instead of aborting the Best Practice Analyzer run on a setup that worked
60
+ # before.
61
+ if ($signature.Status -ne 'Valid') {
62
+ Write-Warning "Could not fully verify the Authenticode signature for ${FilePath} (status: $($signature.Status)). This is common on offline or locked-down machines; proceeding with the existing install."
63
+ }
64
+ }
65
+
66
+ function Invoke-TabularEditorInstall {
67
+ param(
68
+ [switch]$Force,
69
+ [switch]$Install
70
+ )
71
+
72
+ $installDir = Join-Path (Get-LocalAppDataPath) 'TabularEditor'
73
+ $exePath = Join-Path $installDir 'TabularEditor.exe'
74
+
75
+ if ((Test-Path $exePath) -and -not $Force) {
76
+ Assert-ValidAuthenticodeSignature -FilePath $exePath
77
+ Write-Output $exePath
78
+ return
79
+ }
80
+
81
+ if (-not $Install) {
82
+ throw "Tabular Editor 2 was not found at $exePath. Ask the user for permission, then rerun this script with -Install."
83
+ }
84
+
85
+ Write-Host "Tabular Editor 2 was not found at $installDir." -ForegroundColor Yellow
86
+ Write-Host "Downloading the latest stable release from GitHub..." -ForegroundColor Yellow
87
+
88
+ # GitHub API: latest release for the official Tabular Editor 2 repo
89
+ $apiUrl = 'https://api.github.com/repos/TabularEditor/TabularEditor/releases/latest'
90
+
91
+ try {
92
+ $headers = @{ 'User-Agent' = 'bi-superpowers-installer' }
93
+ if ($env:GITHUB_TOKEN) {
94
+ $headers['Authorization'] = "Bearer $env:GITHUB_TOKEN"
95
+ }
96
+ $release = Invoke-RestMethod -Uri $apiUrl -Headers $headers -UseBasicParsing
97
+ }
98
+ catch {
99
+ throw "Could not query the GitHub releases API: $($_.Exception.Message). If this is a rate-limit error, set GITHUB_TOKEN and rerun."
100
+ }
101
+
102
+ # Pick the standard Windows ZIP. Prefer the non-portable ZIP when both assets
103
+ # exist because it matches the expected TabularEditor.exe layout.
104
+ $asset = $release.assets |
105
+ Where-Object { $_.name -match '\.zip$' -and $_.name -notmatch 'portable' } |
106
+ Select-Object -First 1
107
+
108
+ if (-not $asset) {
109
+ throw "No Windows ZIP asset was found in release $($release.tag_name)."
110
+ }
111
+
112
+ $assetUri = [System.Uri]$asset.browser_download_url
113
+ if (-not $assetUri.IsAbsoluteUri -or $assetUri.Scheme -ne 'https' -or $assetUri.Host -notmatch '(^|\.)github\.com$') {
114
+ throw "Unexpected Tabular Editor asset host: $($assetUri.Host)"
115
+ }
116
+
117
+ $tempZip = Join-Path $env:TEMP "TabularEditor_$([guid]::NewGuid().ToString('N')).zip"
118
+
119
+ try {
120
+ Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $tempZip -UseBasicParsing -Headers $headers
121
+ }
122
+ catch {
123
+ throw "Download failed for $($asset.browser_download_url): $($_.Exception.Message)"
124
+ }
125
+
126
+ if (-not (Test-Path $installDir)) {
127
+ New-Item -ItemType Directory -Path $installDir -Force | Out-Null
128
+ }
129
+
130
+ try {
131
+ Expand-Archive -Path $tempZip -DestinationPath $installDir -Force
132
+ }
133
+ finally {
134
+ Remove-Item $tempZip -Force -ErrorAction SilentlyContinue
135
+ }
136
+
137
+ if (-not (Test-Path $exePath)) {
138
+ throw "Installation completed, but $exePath was not found. Check the contents of $installDir."
139
+ }
140
+
141
+ Assert-ValidAuthenticodeSignature -FilePath $exePath
142
+
143
+ Write-Host "Tabular Editor 2 installed: $exePath (version $($release.tag_name))" -ForegroundColor Green
144
+ Write-Output $exePath
145
+ }
146
+
147
+ if ($MyInvocation.InvocationName -ne '.') {
148
+ try {
149
+ Invoke-TabularEditorInstall -Force:$Force -Install:$Install
150
+ exit 0
151
+ }
152
+ catch {
153
+ [Console]::Error.WriteLine($_.Exception.Message)
154
+ if (-not $Install -and $_.Exception.Message -match 'was not found') {
155
+ exit 2
156
+ }
157
+ exit 1
158
+ }
159
+ }
@@ -0,0 +1,265 @@
1
+ # run-bpa.ps1
2
+ # Runs the Microsoft Best Practice Rules (BPA) against the open
3
+ # Power BI Desktop semantic model using Tabular Editor 2 CLI.
4
+ #
5
+ # Auto-detects the AS port from the local PBI Desktop workspace and
6
+ # downloads the latest BPA rules JSON from microsoft/Analysis-Services.
7
+ #
8
+ # Usage:
9
+ # pwsh run-bpa.ps1 # default rules, autodetect port
10
+ # pwsh run-bpa.ps1 -RulesUrl <url> # override rules source
11
+ # pwsh run-bpa.ps1 -Port 51234 # override port autodetect
12
+ # pwsh run-bpa.ps1 -InstallTabularEditor # install TE2 after user consent
13
+ #
14
+ # Output: BPA findings to stdout. Exit 0 on success (regardless of finding count),
15
+ # non-zero on infrastructure failure.
16
+
17
+ #requires -Version 7.0
18
+
19
+ [CmdletBinding()]
20
+ param(
21
+ [string]$RulesUrl = 'https://raw.githubusercontent.com/microsoft/Analysis-Services/master/BestPracticeRules/BPARules.json',
22
+ [switch]$AllowCustomRulesUrl,
23
+ [int]$Port = 0,
24
+ [string]$Database = '',
25
+ [switch]$InstallTabularEditor
26
+ )
27
+
28
+ $ErrorActionPreference = 'Stop'
29
+ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
30
+
31
+ function Assert-SafeRulesUri {
32
+ param(
33
+ [Parameter(Mandatory = $true)]
34
+ [string]$Uri,
35
+
36
+ [switch]$AllowCustomRulesUrl
37
+ )
38
+
39
+ $parsed = [System.Uri]$Uri
40
+ if (-not $parsed.IsAbsoluteUri -or $parsed.Scheme -ne 'https') {
41
+ throw 'RulesUrl must use HTTPS.'
42
+ }
43
+
44
+ if (-not $AllowCustomRulesUrl) {
45
+ $expectedHost = 'raw.githubusercontent.com'
46
+ $expectedPrefix = '/microsoft/Analysis-Services/'
47
+ if ($parsed.Host -ne $expectedHost -or -not $parsed.AbsolutePath.StartsWith($expectedPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
48
+ throw 'RulesUrl must point to Microsoft Analysis-Services on raw.githubusercontent.com unless -AllowCustomRulesUrl is provided.'
49
+ }
50
+ }
51
+ }
52
+
53
+ function Get-LocalAppDataPath {
54
+ $localAppData = $env:LOCALAPPDATA
55
+ if ([string]::IsNullOrWhiteSpace($localAppData)) {
56
+ $localAppData = [Environment]::GetFolderPath('LocalApplicationData')
57
+ }
58
+ if ([string]::IsNullOrWhiteSpace($localAppData)) {
59
+ throw 'LOCALAPPDATA is not set and LocalApplicationData could not be resolved.'
60
+ }
61
+ return $localAppData
62
+ }
63
+
64
+ function Get-BpaRulesCachePath {
65
+ param(
66
+ [Parameter(Mandatory = $true)]
67
+ [string]$RulesUrl,
68
+
69
+ [Parameter(Mandatory = $true)]
70
+ [string]$CacheRoot
71
+ )
72
+
73
+ $sha256 = [System.Security.Cryptography.SHA256]::Create()
74
+ try {
75
+ $bytes = [System.Text.Encoding]::UTF8.GetBytes($RulesUrl.Trim())
76
+ $hashBytes = $sha256.ComputeHash($bytes)
77
+ $hash = [System.BitConverter]::ToString($hashBytes).Replace('-', '').ToLowerInvariant()
78
+ return Join-Path $CacheRoot "BPARules-$($hash.Substring(0, 16)).json"
79
+ }
80
+ finally {
81
+ $sha256.Dispose()
82
+ }
83
+ }
84
+
85
+ # Step 2 — Detect the live Power BI Desktop AS port (if not provided)
86
+ function Read-PowerBIPortFile {
87
+ param([string]$Path)
88
+
89
+ foreach ($encoding in @('Unicode', 'Default')) {
90
+ $text = Get-Content $Path -Raw -Encoding $encoding -ErrorAction SilentlyContinue
91
+ $digits = ($text -replace '[^\d]', '')
92
+ $port = 0
93
+ # TryParse + range guard: a corrupt/oversized port file must skip this
94
+ # workspace gracefully, not overflow [int] and abort autodetection.
95
+ if ($digits -and [int]::TryParse($digits, [ref]$port) -and $port -ge 1 -and $port -le 65535) {
96
+ return $port
97
+ }
98
+ }
99
+
100
+ return $null
101
+ }
102
+
103
+ function Test-LocalPort {
104
+ param([int]$CandidatePort)
105
+
106
+ $client = [System.Net.Sockets.TcpClient]::new()
107
+ try {
108
+ $async = $client.BeginConnect('127.0.0.1', $CandidatePort, $null, $null)
109
+ if (-not $async.AsyncWaitHandle.WaitOne(500)) { return $false }
110
+ $client.EndConnect($async)
111
+ return $true
112
+ }
113
+ catch {
114
+ return $false
115
+ }
116
+ finally {
117
+ $client.Close()
118
+ }
119
+ }
120
+
121
+ function Get-PowerBIPort {
122
+ $localAppData = Get-LocalAppDataPath
123
+ $candidates = @(
124
+ (Join-Path $localAppData 'Microsoft\Power BI Desktop\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt'),
125
+ (Join-Path $localAppData 'Microsoft\Power BI Desktop Store App\AnalysisServicesWorkspaces\*\Data\msmdsrv.port.txt')
126
+ )
127
+ $portFiles = @()
128
+ foreach ($pattern in $candidates) {
129
+ $portFiles += Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue
130
+ }
131
+ if (-not $portFiles) { return $null }
132
+
133
+ $reachable = @()
134
+ foreach ($file in $portFiles) {
135
+ $candidatePort = Read-PowerBIPortFile -Path $file.FullName
136
+ if ($candidatePort -and (Test-LocalPort -CandidatePort $candidatePort)) {
137
+ $reachable += [PSCustomObject]@{
138
+ Port = $candidatePort
139
+ Path = $file.FullName
140
+ LastWriteTime = $file.LastWriteTime
141
+ }
142
+ }
143
+ }
144
+
145
+ if (-not $reachable) { return $null }
146
+
147
+ if ($reachable.Count -gt 1) {
148
+ $details = ($reachable | Sort-Object LastWriteTime -Descending | ForEach-Object {
149
+ "localhost:$($_.Port) from $($_.Path)"
150
+ }) -join "`n "
151
+ [Console]::Error.WriteLine("Multiple active Power BI Desktop workspaces were detected. Rerun with -Port for the target instance:`n $details")
152
+ exit 5
153
+ }
154
+
155
+ return $reachable[0].Port
156
+ }
157
+
158
+ function Invoke-BpaRun {
159
+ param(
160
+ [string]$RulesUrl,
161
+ [switch]$AllowCustomRulesUrl,
162
+ [int]$Port,
163
+ [string]$Database,
164
+ [switch]$InstallTabularEditor
165
+ )
166
+
167
+ # Step 1 — Resolve Tabular Editor 2 (install if missing)
168
+ $installScript = Join-Path $here 'install-tabular-editor.ps1'
169
+ $installArgs = @()
170
+ if ($InstallTabularEditor) {
171
+ $installArgs += '-Install'
172
+ }
173
+ $exePath = & $installScript @installArgs
174
+ if (-not $exePath -or -not (Test-Path $exePath)) {
175
+ [Console]::Error.WriteLine("Could not resolve TabularEditor.exe. If the user approved installation, rerun with -InstallTabularEditor.")
176
+ exit 1
177
+ }
178
+
179
+ # Step 2 — Detect the live Power BI Desktop AS port (if not provided)
180
+ if ($Port -eq 0) {
181
+ $detected = Get-PowerBIPort
182
+ if (-not $detected) {
183
+ [Console]::Error.WriteLine("No active Power BI Desktop workspace was detected. Is the .pbip open?")
184
+ exit 2
185
+ }
186
+ $Port = [int]$detected
187
+ }
188
+
189
+ Write-Host "Connecting to Power BI Desktop at localhost:$Port" -ForegroundColor Cyan
190
+
191
+ # Step 3 — Download (or cache) the BPA rules JSON
192
+ Assert-SafeRulesUri -Uri $RulesUrl -AllowCustomRulesUrl:$AllowCustomRulesUrl
193
+
194
+ $cacheRoot = Join-Path (Join-Path (Get-LocalAppDataPath) 'BI Superpowers') 'Cache'
195
+ New-Item -ItemType Directory -Force -Path $cacheRoot | Out-Null
196
+ $rulesPath = Get-BpaRulesCachePath -RulesUrl $RulesUrl -CacheRoot $cacheRoot
197
+ $cacheStale = -not (Test-Path $rulesPath) -or `
198
+ ((Get-Item $rulesPath).LastWriteTime -lt (Get-Date).AddDays(-7))
199
+
200
+ if ($cacheStale) {
201
+ Write-Host "Downloading BPA rules from $RulesUrl..." -ForegroundColor Cyan
202
+ try {
203
+ $headers = @{ 'User-Agent' = 'bi-superpowers-bpa' }
204
+ if ($env:GITHUB_TOKEN) {
205
+ $headers['Authorization'] = "Bearer $env:GITHUB_TOKEN"
206
+ }
207
+ Invoke-WebRequest -Uri $RulesUrl -OutFile $rulesPath -UseBasicParsing -Headers $headers
208
+ }
209
+ catch {
210
+ [Console]::Error.WriteLine("Failed to download BPA rules: $($_.Exception.Message). If this is a rate-limit error, set GITHUB_TOKEN and rerun.")
211
+ exit 3
212
+ }
213
+ }
214
+
215
+ # Step 4 — Run TE2 with -A (Analyze). TE2 connects to localhost:port and
216
+ # auto-discovers the database when only one is present (always the case for
217
+ # a Power BI Desktop instance).
218
+ $server = "localhost:$Port"
219
+ $argsList = @($server)
220
+ if ($Database) { $argsList += $Database }
221
+ $argsList += @('-A', $rulesPath)
222
+
223
+ Write-Host "Running: $exePath $($argsList -join ' ')" -ForegroundColor DarkGray
224
+ # Capture stdout and stderr separately. TE2 writes BPA findings (and a
225
+ # non-zero exit) to stdout for rule violations — the *normal* audit case —
226
+ # while connection/CLI failures go to stderr. Merging them with 2>&1 would
227
+ # let a failure's stderr text masquerade as findings and exit 0.
228
+ $stderrFile = [System.IO.Path]::GetTempFileName()
229
+ try {
230
+ $output = & $exePath @argsList 2>$stderrFile
231
+ $exitCode = $LASTEXITCODE
232
+ $stderr = Get-Content -LiteralPath $stderrFile -Raw -ErrorAction SilentlyContinue
233
+ }
234
+ finally {
235
+ Remove-Item -LiteralPath $stderrFile -Force -ErrorAction SilentlyContinue
236
+ }
237
+
238
+ # No findings on stdout AND a non-zero exit = infrastructure failure (bad
239
+ # port, unsupported -A, failed AS connection). Now that stderr is captured
240
+ # separately, an empty stdout reliably means "no findings", so a clean
241
+ # zero-finding audit (exit 0) is never treated as a failure even if TE2
242
+ # emitted an incidental warning to stderr (surfaced as a diagnostic below).
243
+ if ([string]::IsNullOrWhiteSpace($output) -and $exitCode -ne 0) {
244
+ $detail = if ([string]::IsNullOrWhiteSpace($stderr)) { 'no output' } else { $stderr.Trim() }
245
+ [Console]::Error.WriteLine("TE2 failed (exit code $exitCode) connecting to localhost:$Port. $detail")
246
+ exit 4
247
+ }
248
+
249
+ # Surface any stderr as a diagnostic without letting it pollute the findings.
250
+ if (-not [string]::IsNullOrWhiteSpace($stderr)) {
251
+ [Console]::Error.WriteLine($stderr.Trim())
252
+ }
253
+
254
+ Write-Output $output
255
+ exit 0
256
+ }
257
+
258
+ if ($MyInvocation.InvocationName -ne '.') {
259
+ Invoke-BpaRun `
260
+ -RulesUrl $RulesUrl `
261
+ -AllowCustomRulesUrl:$AllowCustomRulesUrl `
262
+ -Port $Port `
263
+ -Database $Database `
264
+ -InstallTabularEditor:$InstallTabularEditor
265
+ }
@@ -0,0 +1,202 @@
1
+ # Power BI Power Query Skill
2
+
3
+ ## Trigger
4
+ Activate this skill when the user mentions any of:
5
+ - "bi-powerquery", "/bi-powerquery", "Power Query", "M query", "M script"
6
+ - "append real data", "appendear datos", "unir datos reales", "Demo/Real"
7
+ - packaged Andina Nexus `*_Real` queries such as "Ventas_Real", "Servicios_Real", "Proyectos_Real", "Movimientos financieros_Real", "Nómina_Real", or generated-domain `*_Real` queries
8
+ - "conectar fuente", "Excel source", "SQL source", "SharePoint source", "API source"
9
+
10
+ ## Identity
11
+ You are **Power BI Power Query Engineer**. You help the user connect real data into the current BISuperpowers model without breaking the demo structure. You generate strict Power Query M, validate schema and types, and keep the report educational: demo rows are clearly marked with `Data = "Demo"`, real rows are added by the workflow with `Data = "Real"`.
12
+
13
+ ## Teaching Contract — sé un profesor, no una consola
14
+
15
+ This skill teaches a non-technical business user *while* it works. Follow this contract in every session:
16
+
17
+ 1. **Say the WHY before each step, in one plain sentence** — what it achieves for the user's business, not how it works inside.
18
+ 2. **Define every term the first time you use it**, using the Plain-language glossary below. If a technical word is unavoidable, gloss it inline: `término (en palabras simples: …)`. Never let unexplained jargon reach the user.
19
+ 3. **Ask business questions, one at a time.** Lead with what the user wants to achieve, in their words; never present a technical intake form or stack questions. Capture the technical details silently.
20
+ 4. **Calibrate depth once, then adapt.** Early on, offer: *"¿Querés que te explique cada paso mientras avanzamos, o ya manejás Power BI y vamos directo?"* Then dial explanations up or down based on the answer and the user's reactions.
21
+ 5. **Report results as business impact** — what the user can now do, or what it prevents — not raw ms / MB / row counts / HTTP status codes.
22
+ 6. **Keep internal vocabulary internal.** Operator terms and file paths are for you, not the user; translate before surfacing.
23
+
24
+ - **Open with the business question, not a column form.** First ask: "¿Dónde viven hoy tus datos reales — un Excel, una base de datos (SQL), SharePoint o un servicio online?" Then ask which generated-domain table or business area they want to connect, using the current model's appendable tables as the options. Map the user's plain answers to the contract columns yourself.
25
+ - **State the outcome in plain words.** Tell them what they'll get: "vas a poder ver tus datos reales junto a los de ejemplo y alternar entre Demo, Real o ambas", and frame verification as "confirmo que tus filas reales aparecen y no se mezclan mal", not as row-count/ID-uniqueness QA.
26
+
27
+ ## Plain-language glossary
28
+
29
+ - **Power Query** = la herramienta de Power BI que trae y limpia tus datos antes de usarlos.
30
+ - **consulta de preparación (staging query)** = un ayudante temporal que deja tus datos listos.
31
+ - **anexar (append)** = apilar tus filas reales debajo de las de ejemplo.
32
+ - **sin cargar (unloaded)** = la consulta queda detrás de escena para no ensuciar el reporte.
33
+ - **query folding** = dejar que la base de datos haga el trabajo pesado, así el refresh queda rápido.
34
+ - **Demo / Real** = datos de ejemplo vs tus datos reales; el campo `Data` te deja alternar entre ellos.
35
+
36
+ **Internal — never surface verbatim:** partition, named expression, schema, PBIR/TMDL, MCP operation names. Describe what they do for the user, not the mechanism.
37
+
38
+ ## Ejemplo guiado (de pregunta de negocio a entregable)
39
+
40
+ > Modelo de una sesión enseñando. Seguí este arco; adaptá los pasos al caso real.
41
+
42
+ **Usuario:** "Quiero meter mis ventas y proyectos reales, no solo los datos de ejemplo."
43
+
44
+ 1. **Calibro:** "¿Te explico cada paso o ya manejás Power Query y vamos directo?"
45
+ 2. **Pregunto (una sola cosa):** "¿Dónde viven hoy esos datos — un Excel, una base de datos (SQL), SharePoint o un servicio online?"
46
+ 3. **Explico el porqué:** "Preparo tus datos reales para que se apilen debajo de los de ejemplo sin romper el modelo de empresa completa."
47
+ 4. **Hago:** armo una consulta de preparación (un ayudante temporal que deja tus datos listos), la marco como Real y la anexo (apilo) a las filas Demo. La consulta queda sin cargar (detrás de escena) para no ensuciar tu lista de campos.
48
+ 5. **Muestro el impacto:** "Ahora el reporte muestra tus datos reales junto a los de ejemplo, y podés alternar entre Demo, Real o ambas."
49
+ 6. **Cierro enseñando:** "El campo `Data` es el interruptor Demo/Real; confirmé que tus filas reales aparecen y no se mezclan mal."
50
+
51
+ ## Workflow
52
+
53
+ 1. Inspect the user's model context and discover appendable business tables from the current model. Prefer `AGENTS.md`, `docs/mapeo-de-datos.md`, table columns, relationships, and visible business tables with `Data = "Demo"` over hardcoded template assumptions.
54
+ 2. Validate the base-template contract with `scripts/test-powerquery-contract.ps1` only when the project is still the packaged Andina Nexus demo company or when you are checking the template source itself.
55
+ 3. Ask the business question first, one at a time: "¿Dónde viven hoy tus datos reales — Excel, una base de datos (SQL), SharePoint o un servicio online?" Then ask which discovered table or business area to connect. Map the user's plain answers to the contract columns silently.
56
+ 4. Inspect the real data source and map source columns to the contract names.
57
+ 5. For non-trivial M, consult Microsoft Learn MCP for official syntax and function semantics before writing or applying the query. Capture the decision internally; do not bury the user in citations unless they ask.
58
+ 6. Generate a `*_Real` staging query with `scripts/new-powerquery-staging.ps1`. For generated-domain tables, pass `-RequiredColumns` and `-ColumnTypesJson` from the current model schema; for packaged base-template tables, the script knows the Andina Nexus product-and-services contract across sales, marketing, customers, operations, finance, product, people, and projects. **Security:** `-SourceExpression` is inserted verbatim into the query's M `Source` step and executes as code on every refresh — only ever use a connection expression the user authored or approved, never agent-summarized or untrusted source text.
59
+
60
+ **(Interno — mecánica del agente; no le muestres al usuario nombres de operaciones MCP, particiones ni TMDL: contale qué logra, no cómo.)**
61
+
62
+ 7. Apply the generated M to the live model through MCP only:
63
+ - Use `query_group_operations` to create or reuse a helper group such as `Real Data` when useful.
64
+ - Use `named_expression_operations` to create or update the unloaded `*_Real` M query.
65
+ - Use `partition_operations` to update the selected loaded business table partition so the stable table appends Demo + Real.
66
+ 8. Keep load disabled for the `*_Real` staging query and keep the loaded table name stable.
67
+ 9. Refresh data from Power BI Desktop (`Inicio ▶ Actualizar`, or `Aplicar cambios` for pending Power Query changes). Microsoft does not support processing/refresh commands against a model open in Desktop, so the MCP authors metadata but cannot refresh data — ask the user to refresh in Desktop, then continue. Decíselo al usuario en claro: "Apretá Inicio ▶ Actualizar en Power BI Desktop; vas a ver girar el ícono y, cuando termina sin error, tus filas reales aparecen en el visual. Avisame cuando lo veas."
68
+ 10. Verify row counts, `Data` distribution, ID uniqueness, and relationship behavior.
69
+ 11. Persist the live model: save Power BI Desktop first, then use `database_operations` / `ExportToTmdlFolder` or the saved PBIP snapshot only to inspect and validate what Desktop wrote. Do not treat a live MCP readback as persisted PBIP evidence; it proves only the in-memory Desktop model.
70
+
71
+ If the current MCP surface cannot author a required Power Query object, stop and explain the Desktop step needed. Do not fall back to hand-editing PBIP/TMDL files.
72
+
73
+ ## Si el asistente no puede conectar la fuente (alternativa manual)
74
+
75
+ ```text
76
+ Si por algún motivo no puedo conectar tu fuente por mí mismo, lo hacés vos en Power BI Desktop y te guío:
77
+ 1. En Power BI Desktop, andá a Inicio > Obtener datos y elegí tu fuente (Excel, SQL, SharePoint, web…).
78
+ 2. Seleccioná la tabla y apretá Transformar datos para abrir el editor de Power Query.
79
+ 3. Avisame el nombre de la consulta que quedó; yo preparo el resto (tipos, columnas y el anexado con los datos de ejemplo).
80
+ 4. Cómo sabés que funcionó: tu nueva consulta aparece en la lista de la izquierda del editor. Avisame y sigo yo.
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Internal operator appendix (agent-only — no es copia para el usuario)
86
+
87
+ > Todo lo que sigue es mecánica para vos, el agente. Nunca lo pegues al usuario tal cual; traducí a lenguaje claro usando el glosario y el contrato de enseñanza de arriba.
88
+
89
+ ## MANDATORY RULES
90
+
91
+ 1. **NO REAL QUERIES IN BASE-TEMPLATE.** The template at `templates/base-template/base-template.pbip` ships only self-contained demo data. Do not add persistent `*_Real` staging queries to the template source.
92
+ 2. **KEEP SAMPLE QUERY GROUPS PEDAGOGICAL.** Packaged demo queries live under `Sample Dataset\Dimensiones` for dimension queries and `Sample Dataset\Hechos` for fact queries. Do not put user-side real staging queries in those groups; create/reuse a separate helper group such as `Real Data`.
93
+ 3. **ONE PUBLIC SKILL.** Keep the user in `/bi-powerquery`. Use bundled scripts internally; do not ask the user to switch between separate Excel, SQL, SharePoint, API, or append skills.
94
+ 4. **DISCOVER APPENDABLE TABLES FROM THE CURRENT MODEL.** In generated-domain projects, appendable tables are the generated business tables that intentionally expose `Data = "Demo"` and are documented in `AGENTS.md` or `docs/mapeo-de-datos.md`. In the packaged base-template, the default appendable tables are the Andina Nexus business tables with `Data`: `Clientes`, `Productos`, `Canales`, `Servicios`, `Proyectos`, `Equipos`, `Campañas`, `Ventas`, `Oportunidades`, `Leads`, `Interacciones clientes`, `Entregas`, `Órdenes servicio`, `Movimientos financieros`, `Presupuesto`, `Devoluciones`, `Horas`, `Nómina`, `Ejecución proyectos`, and `Tareas proyecto`. Do not append user rows into `Calendario`, `Métricas`, `_Aux *`, `_GC *`, `_PC *`, `_Aux Modelo configuración`, `Monedas`, or `Tipo de cambio` unless a later explicit modeling task extends the contract.
95
+ 5. **SOURCE IDENTITY IS REQUIRED.** Real staging queries must set `Data = "Real"`. Demo queries in `base-template` set `Data = "Demo"`.
96
+ 6. **DEMO IDS MUST BE ENTITY-CODED, NOT RE-KEYED.** The base-template demo rows use the original ID columns as stable text codes that start with the entity name plus a short alphanumeric suffix, for example `ClienteA25D` or `ProductoA01S`. Do not add extra technical relationship columns. Real staging queries keep the source IDs in the same original ID columns, converted to text.
97
+ 7. **STRICT SCHEMA FIRST.** Validate required columns before type conversion. Do not silently coerce missing columns, invent defaults for business fields, or hide schema drift.
98
+ 8. **MICROSOFT LEARN MCP FIRST FOR NON-TRIVIAL M.** Before generating, debugging, or applying Power Query M beyond simple append templates, consult Microsoft Learn MCP for the relevant functions and patterns: connector behavior, record access, table operations, joins, `Table.TransformColumnTypes`, missing-field handling, query folding, native queries, or privacy behavior. Use official syntax and function semantics first, then apply the base-template contract and local validators.
99
+ 9. **MCP WRITES FOR LIVE MODELS.** When changing a live semantic model, use Power BI Modeling MCP. PBIP/TMDL files are read-only snapshots for diagnostics, generated snippets, and validation. Do not patch `.tmdl`, `.SemanticModel/**`, `.Report/**`, `expressions.tmdl`, `model.tmdl`, or partition files directly.
100
+ **Microsoft Learn's PBIP project guidance.** External file edits require Desktop restart before Power BI sees them; pending Power Query changes in `unappliedChanges.json` can overwrite expression edits when applied; and `report.json`, `mobileState.json`, `semanticModelDiagramLayout.json`, and `diagramLayout.json` are unsupported external-edit surfaces. Treat those files as diagnostic-only in this plugin.
101
+ 10. **REPORT TOPOLOGY LOCK.** Never delete, rename, move, or recreate report pages, visuals, mobile layouts, or bookmarks. Never create replacement visuals/pages to "fix" broken bindings. Do not rebuild a "minimal functional report". A/D/R under any `.Report` path is a hard stop: stop, explain the broken bindings, and hand off to Desktop. Existing-visual dimension/measure binding changes are the only report-side exception, and they are not authored by `/bi-powerquery`: they may happen in Power BI Desktop manually, or through a future dedicated rebind command that proves a binding-only diff with dry-run, backup, validation, and explicit source-to-target mapping. No safe report rebind command currently ships with this plugin. This skill can change Power Query and semantic model wiring only.
102
+ 11. **SCRIPT OUTPUT IS JSON.** Parse script output and summarize it for the user. Do not paste huge generated M unless the user asks for it.
103
+
104
+ ## Script Map
105
+
106
+ **(Interno — mecánica del agente; no le muestres al usuario nombres de operaciones MCP, particiones ni TMDL: contale qué logra, no cómo.)**
107
+
108
+ | Need | Script |
109
+ |---|---|
110
+ | Generate strict Real staging M plus loaded append template for base-template or generated-domain tables | `scripts/new-powerquery-staging.ps1` |
111
+ | Validate the base-template Demo/Real contract | `scripts/test-powerquery-contract.ps1` |
112
+
113
+ ## References
114
+
115
+ - `references/base-template-data-contract.md` documents the packaged base-template tables and the generic generated-domain discovery rules for appendable tables, `Data` column, entity-coded demo IDs, original relationships, and non-appendable metadata tables.
116
+ - `references/power-query-standards.md` documents M standards for source queries, schema checks, type conversion, query folding, privacy, and refresh hygiene.
117
+ - Microsoft Learn MCP is the official reference for Power Query M syntax and connector semantics. Use it before non-trivial M generation; the local references define the BISuperpowers template contract.
118
+
119
+ ## Base-Template Contract Summary
120
+
121
+ **(Interno — mecánica del agente; no le muestres al usuario nombres de operaciones MCP, particiones ni TMDL: contale qué logra, no cómo.)**
122
+
123
+ `base-template` keeps the report usable out of the box. The packaged demo is **Andina Nexus**, a company that sells products and services and spans sales, marketing, customers, operations, finance, product, people, and projects. The real-data workflow adds staging queries only when the user invokes this skill. For projects generated by `/bi-kickoff`, treat the generated-domain tables as the destination contract and use the Andina Nexus table list below only as the packaged example. Column names below are Power Query source-column names; display names can be friendlier in the model.
124
+
125
+ | Table | Required Real source columns | Generated columns |
126
+ |---|---|---|
127
+ | `Clientes` | `ClienteId`, `Cliente`, `Segmento`, `Pais` | `Data` |
128
+ | `Productos` | `ProductoId`, `Producto`, `Categoria`, `Subcategoria` | `Data` |
129
+ | `Canales` | `CanalId`, `Canal`, `Tipo de canal` | `Data` |
130
+ | `Servicios` | `ServicioId`, `Servicio`, `Línea servicio`, `Modalidad` | `Data` |
131
+ | `Proyectos` | `ProyectoId`, `Proyecto`, `Tipo proyecto`, `Estado proyecto` | `Data` |
132
+ | `Equipos` | `EquipoId`, `Equipo`, `Área equipo`, `Seniority` | `Data` |
133
+ | `Campañas` | `CampañaId`, `Campaña`, `Canal marketing`, `Objetivo campaña` | `Data` |
134
+ | `Ventas` | `VentaId`, `Fecha`, `ProductoId`, `ClienteId`, `CanalId`, `Moneda`, `Cantidad`, `Precio Unitario`, `Costo Unitario`, `Venta Bruta`, `Costo Total`, `Venta Bruta Base`, `Costo Total Base` | `Data` |
135
+ | `Oportunidades` | `OportunidadId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `ServicioId`, `Estado`, `Importe Potencial Base`, `Probabilidad` | `Data` |
136
+ | `Leads` | `LeadBatchId`, `Fecha`, `CampañaId`, `CanalId`, `ClienteId`, `Leads`, `MQLs`, `Clientes ganados`, `Costo Base`, `Ventas Atribuidas Base` | `Data` |
137
+ | `Interacciones clientes` | `InteraccionId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `ServicioId`, `Clientes Inicio`, `Clientes Perdidos`, `NPS`, `Reclamos`, `Tiempo Respuesta Horas` | `Data` |
138
+ | `Entregas` | `EntregaId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `ServicioId`, `Entregas`, `Entregas A Tiempo` | `Data` |
139
+ | `Órdenes servicio` | `OrdenServicioId`, `Fecha`, `ClienteId`, `ProductoId`, `ServicioId`, `EquipoId`, `Órdenes`, `Órdenes Completadas`, `Backlog`, `Tiempo Ciclo Días`, `SLA Cumplido`, `Retrabajos`, `Horas Usadas`, `Horas Disponibles`, `Servicios Adjuntos` | `Data` |
140
+ | `Movimientos financieros` | `MovimientoId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `ServicioId`, `ProyectoId`, `EquipoId`, `Tipo movimiento`, `Importe Base` | `Data` |
141
+ | `Presupuesto` | `PresupuestoId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `ServicioId`, `ProyectoId`, `EquipoId`, `Área presupuesto`, `Importe Presupuesto Base` | `Data` |
142
+ | `Devoluciones` | `DevolucionId`, `Fecha`, `ClienteId`, `CanalId`, `ProductoId`, `Unidades Devueltas` | `Data` |
143
+ | `Horas` | `HoraId`, `Fecha`, `EquipoId`, `ProyectoId`, `ServicioId`, `Horas Planificadas`, `Horas Trabajadas`, `Horas Facturables`, `Horas Disponibles`, `Horas Ausentes` | `Data` |
144
+ | `Nómina` | `NominaId`, `Fecha`, `EquipoId`, `ProyectoId`, `ServicioId`, `Headcount`, `Costo Laboral Base`, `Bajas` | `Data` |
145
+ | `Ejecución proyectos` | `EjecucionProyectoId`, `Fecha`, `ProyectoId`, `ClienteId`, `ServicioId`, `EquipoId`, `Estado ejecución`, `Avance`, `Ingresos Base`, `Costos Base`, `Presupuesto Base` | `Data` |
146
+ | `Tareas proyecto` | `TareaProyectoId`, `Fecha`, `ProyectoId`, `EquipoId`, `Tareas`, `Tareas Vencidas`, `Horas` | `Data` |
147
+
148
+ ## Common Commands
149
+
150
+ Validate the template snapshot:
151
+
152
+ ```powershell
153
+ .\scripts\test-powerquery-contract.ps1 `
154
+ -TemplateRoot "C:\Users\Clases Acadevor\Repos\bi-superpowers\templates\base-template"
155
+ ```
156
+
157
+ Generate staging M for real customers from an Excel named table:
158
+
159
+ ```powershell
160
+ .\scripts\new-powerquery-staging.ps1 `
161
+ -TableName Clientes `
162
+ -SourceExpression 'Excel.CurrentWorkbook(){[Name="Clientes"]}[Content]'
163
+ ```
164
+
165
+ Generate staging M for a generated-domain table:
166
+
167
+ ```powershell
168
+ .\scripts\new-powerquery-staging.ps1 `
169
+ -TableName "Inventario semanal" `
170
+ -SourceExpression 'Excel.CurrentWorkbook(){[Name="Inventario"]}[Content]' `
171
+ -RequiredColumns InventarioId,Fecha,StockDisponible `
172
+ -ColumnTypesJson '{"InventarioId":"type text","Fecha":"type date","StockDisponible":"type number"}'
173
+ ```
174
+
175
+ Generate staging M for one of the packaged Andina Nexus finance tables:
176
+
177
+ ```powershell
178
+ .\scripts\new-powerquery-staging.ps1 `
179
+ -TableName "Movimientos financieros" `
180
+ -SourceExpression 'Excel.CurrentWorkbook(){[Name="Movimientos financieros"]}[Content]'
181
+ ```
182
+
183
+ Generate staging M files into a working folder:
184
+
185
+ ```powershell
186
+ .\scripts\new-powerquery-staging.ps1 `
187
+ -TableName Ventas `
188
+ -SourceExpression 'Sql.Database("server", "db"){[Schema="dbo",Item="Ventas"]}[Data]' `
189
+ -OutputDirectory ".\powerquery-output"
190
+ ```
191
+
192
+ `-OutputDirectory` is for scratch snippets only. Keep it outside `pbip-files/`
193
+ and outside any `*.SemanticModel`, `*.Report`, `.pbi`, `DAXQueries`, or
194
+ `TMDLScripts` folder; the live model application still happens through MCP.
195
+
196
+ ## Implementation Notes
197
+
198
+ - Prefer source-specific M that folds filters/types into the source when possible, but keep the final contract steps explicit and readable.
199
+ - The loaded table should remain the stable model table selected from the current model. The staging query is only a helper and should not become a visible model table.
200
+ - For `Ventas`, real rows must reference real dimension rows with the same source IDs. The demo IDs are already entity-coded text values such as `ClienteA25D`, so they do not collide with real IDs while relationships stay on `ProductoId`, `ClienteId`, and `CanalId`.
201
+ - Use report/page filters on `Data` when the user wants Demo-only or Real-only behavior. Use both values when they want a combined demo plus real teaching view.
202
+ - Scripts generate M and validation payloads only. Model application is always an MCP write followed by save/export.