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,371 @@
1
+ [CmdletBinding(DefaultParameterSetName = 'Inline')]
2
+ param(
3
+ [Parameter(Mandatory = $true)]
4
+ [string]$TableName,
5
+
6
+ [Parameter(Mandatory = $true, ParameterSetName = 'Inline')]
7
+ [string]$SourceExpression,
8
+
9
+ [Parameter(Mandatory = $true, ParameterSetName = 'File')]
10
+ [string]$SourceExpressionPath,
11
+
12
+ [string]$RealQueryName,
13
+
14
+ [string[]]$RequiredColumns,
15
+
16
+ [string[]]$ProjectionColumns,
17
+
18
+ [string]$ColumnTypesJson,
19
+
20
+ [string]$OutputDirectory
21
+ )
22
+
23
+ $ErrorActionPreference = 'Stop'
24
+ # Pin stdout to UTF-8 so generated M with accented column names round-trips when
25
+ # the caller (Node/MCP) decodes the pipe as UTF-8, regardless of console codepage.
26
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
27
+
28
+ function New-TableContract {
29
+ param(
30
+ [Parameter(Mandatory = $true)]
31
+ [string[]]$Columns,
32
+
33
+ [hashtable]$ColumnTypes = @{}
34
+ )
35
+
36
+ $types = @()
37
+ foreach ($column in $Columns) {
38
+ $typeExpression = if ($ColumnTypes.ContainsKey($column)) { $ColumnTypes[$column] } else { 'type text' }
39
+ $types += ,@($column, $typeExpression)
40
+ }
41
+
42
+ return [ordered]@{
43
+ RequiredColumns = $Columns
44
+ Types = $types
45
+ Projection = @('Data') + $Columns
46
+ }
47
+ }
48
+
49
+ function Get-TableContract {
50
+ param([string]$Name)
51
+
52
+ $types = @{
53
+ 'Fecha' = 'type date'
54
+ 'Cantidad' = 'Int64.Type'
55
+ 'Precio Unitario' = 'type number'
56
+ 'Costo Unitario' = 'type number'
57
+ 'Venta Bruta' = 'type number'
58
+ 'Costo Total' = 'type number'
59
+ 'Venta Bruta Base' = 'type number'
60
+ 'Costo Total Base' = 'type number'
61
+ 'Leads' = 'Int64.Type'
62
+ 'MQLs' = 'Int64.Type'
63
+ 'Clientes ganados' = 'Int64.Type'
64
+ 'Costo Base' = 'Int64.Type'
65
+ 'Ventas Atribuidas Base' = 'Int64.Type'
66
+ 'Importe Potencial Base' = 'Int64.Type'
67
+ 'Probabilidad' = 'type number'
68
+ 'Clientes Inicio' = 'Int64.Type'
69
+ 'Clientes Perdidos' = 'Int64.Type'
70
+ 'NPS' = 'Int64.Type'
71
+ 'Reclamos' = 'Int64.Type'
72
+ 'Tiempo Respuesta Horas' = 'type number'
73
+ 'Entregas' = 'Int64.Type'
74
+ 'Entregas A Tiempo' = 'Int64.Type'
75
+ 'Órdenes' = 'Int64.Type'
76
+ 'Órdenes Completadas' = 'Int64.Type'
77
+ 'Backlog' = 'Int64.Type'
78
+ 'Tiempo Ciclo Días' = 'Int64.Type'
79
+ 'SLA Cumplido' = 'Int64.Type'
80
+ 'Retrabajos' = 'Int64.Type'
81
+ 'Horas Usadas' = 'Int64.Type'
82
+ 'Horas Disponibles' = 'Int64.Type'
83
+ 'Servicios Adjuntos' = 'Int64.Type'
84
+ 'Importe Base' = 'Int64.Type'
85
+ 'Importe Presupuesto Base' = 'Int64.Type'
86
+ 'Unidades Devueltas' = 'Int64.Type'
87
+ 'Horas Planificadas' = 'Int64.Type'
88
+ 'Horas Trabajadas' = 'Int64.Type'
89
+ 'Horas Facturables' = 'Int64.Type'
90
+ 'Horas Ausentes' = 'Int64.Type'
91
+ 'Headcount' = 'Int64.Type'
92
+ 'Costo Laboral Base' = 'Int64.Type'
93
+ 'Bajas' = 'Int64.Type'
94
+ 'Avance' = 'type number'
95
+ 'Ingresos Base' = 'Int64.Type'
96
+ 'Costos Base' = 'Int64.Type'
97
+ 'Presupuesto Base' = 'Int64.Type'
98
+ 'Tareas' = 'Int64.Type'
99
+ 'Tareas Vencidas' = 'Int64.Type'
100
+ 'Horas' = 'Int64.Type'
101
+ }
102
+
103
+ switch ($Name) {
104
+ 'Clientes' { return New-TableContract -Columns @('ClienteId', 'Cliente', 'Segmento', 'Pais') -ColumnTypes $types }
105
+ 'Productos' { return New-TableContract -Columns @('ProductoId', 'Producto', 'Categoria', 'Subcategoria') -ColumnTypes $types }
106
+ 'Canales' { return New-TableContract -Columns @('CanalId', 'Canal', 'Tipo de canal') -ColumnTypes $types }
107
+ 'Servicios' { return New-TableContract -Columns @('ServicioId', 'Servicio', 'Línea servicio', 'Modalidad') -ColumnTypes $types }
108
+ 'Proyectos' { return New-TableContract -Columns @('ProyectoId', 'Proyecto', 'Tipo proyecto', 'Estado proyecto') -ColumnTypes $types }
109
+ 'Equipos' { return New-TableContract -Columns @('EquipoId', 'Equipo', 'Área equipo', 'Seniority') -ColumnTypes $types }
110
+ 'Campañas' { return New-TableContract -Columns @('CampañaId', 'Campaña', 'Canal marketing', 'Objetivo campaña') -ColumnTypes $types }
111
+ 'Ventas' { return New-TableContract -Columns @('VentaId', 'Fecha', 'ProductoId', 'ClienteId', 'CanalId', 'Moneda', 'Cantidad', 'Precio Unitario', 'Costo Unitario', 'Venta Bruta', 'Costo Total', 'Venta Bruta Base', 'Costo Total Base') -ColumnTypes $types }
112
+ 'Oportunidades' { return New-TableContract -Columns @('OportunidadId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'Estado', 'Importe Potencial Base', 'Probabilidad') -ColumnTypes $types }
113
+ 'Leads' { return New-TableContract -Columns @('LeadBatchId', 'Fecha', 'CampañaId', 'CanalId', 'ClienteId', 'Leads', 'MQLs', 'Clientes ganados', 'Costo Base', 'Ventas Atribuidas Base') -ColumnTypes $types }
114
+ 'Interacciones clientes' { return New-TableContract -Columns @('InteraccionId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'Clientes Inicio', 'Clientes Perdidos', 'NPS', 'Reclamos', 'Tiempo Respuesta Horas') -ColumnTypes $types }
115
+ 'Entregas' { return New-TableContract -Columns @('EntregaId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'Entregas', 'Entregas A Tiempo') -ColumnTypes $types }
116
+ 'Órdenes servicio' { return New-TableContract -Columns @('OrdenServicioId', 'Fecha', 'ClienteId', 'ProductoId', 'ServicioId', 'EquipoId', 'Órdenes', 'Órdenes Completadas', 'Backlog', 'Tiempo Ciclo Días', 'SLA Cumplido', 'Retrabajos', 'Horas Usadas', 'Horas Disponibles', 'Servicios Adjuntos') -ColumnTypes $types }
117
+ 'Movimientos financieros' { return New-TableContract -Columns @('MovimientoId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'ProyectoId', 'EquipoId', 'Tipo movimiento', 'Importe Base') -ColumnTypes $types }
118
+ 'Presupuesto' { return New-TableContract -Columns @('PresupuestoId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'ProyectoId', 'EquipoId', 'Área presupuesto', 'Importe Presupuesto Base') -ColumnTypes $types }
119
+ 'Devoluciones' { return New-TableContract -Columns @('DevolucionId', 'Fecha', 'ClienteId', 'CanalId', 'ProductoId', 'Unidades Devueltas') -ColumnTypes $types }
120
+ 'Horas' { return New-TableContract -Columns @('HoraId', 'Fecha', 'EquipoId', 'ProyectoId', 'ServicioId', 'Horas Planificadas', 'Horas Trabajadas', 'Horas Facturables', 'Horas Disponibles', 'Horas Ausentes') -ColumnTypes $types }
121
+ 'Nómina' { return New-TableContract -Columns @('NominaId', 'Fecha', 'EquipoId', 'ProyectoId', 'ServicioId', 'Headcount', 'Costo Laboral Base', 'Bajas') -ColumnTypes $types }
122
+ 'Ejecución proyectos' { return New-TableContract -Columns @('EjecucionProyectoId', 'Fecha', 'ProyectoId', 'ClienteId', 'ServicioId', 'EquipoId', 'Estado ejecución', 'Avance', 'Ingresos Base', 'Costos Base', 'Presupuesto Base') -ColumnTypes $types }
123
+ 'Tareas proyecto' { return New-TableContract -Columns @('TareaProyectoId', 'Fecha', 'ProyectoId', 'EquipoId', 'Tareas', 'Tareas Vencidas', 'Horas') -ColumnTypes $types }
124
+ }
125
+ }
126
+
127
+ function Assert-SafeMType {
128
+ param(
129
+ [Parameter(Mandatory = $true)]
130
+ [string]$TypeExpression,
131
+
132
+ [Parameter(Mandatory = $true)]
133
+ [string]$ColumnName
134
+ )
135
+
136
+ $allowed = @(
137
+ 'type text',
138
+ 'type nullable text',
139
+ 'type number',
140
+ 'type nullable number',
141
+ 'type date',
142
+ 'type nullable date',
143
+ 'type datetime',
144
+ 'type nullable datetime',
145
+ 'type logical',
146
+ 'type nullable logical',
147
+ 'Int64.Type',
148
+ 'Currency.Type',
149
+ 'Percentage.Type'
150
+ )
151
+
152
+ if ($allowed -notcontains $TypeExpression) {
153
+ throw "ColumnTypesJson contains unsupported M type '$TypeExpression' for column '$ColumnName'."
154
+ }
155
+ }
156
+
157
+ function Convert-ColumnList {
158
+ param([string[]]$Items)
159
+
160
+ $result = @()
161
+ foreach ($item in $Items) {
162
+ if ([string]::IsNullOrWhiteSpace($item)) {
163
+ continue
164
+ }
165
+
166
+ foreach ($part in ($item -split ',')) {
167
+ $trimmed = $part.Trim()
168
+ if (-not [string]::IsNullOrWhiteSpace($trimmed)) {
169
+ $result += $trimmed
170
+ }
171
+ }
172
+ }
173
+
174
+ return $result
175
+ }
176
+
177
+ function New-GeneratedTableContract {
178
+ param(
179
+ [Parameter(Mandatory = $true)]
180
+ [string]$Name,
181
+
182
+ [string[]]$RequiredColumns,
183
+
184
+ [string[]]$ProjectionColumns,
185
+
186
+ [string]$ColumnTypesJson
187
+ )
188
+
189
+ $normalizedRequiredColumns = Convert-ColumnList -Items $RequiredColumns
190
+ $normalizedProjectionColumns = Convert-ColumnList -Items $ProjectionColumns
191
+
192
+ if ($null -eq $normalizedRequiredColumns -or $normalizedRequiredColumns.Count -eq 0) {
193
+ throw "TableName '$Name' is not a bundled base-template table. Provide -RequiredColumns for generated-domain tables."
194
+ }
195
+
196
+ foreach ($column in $normalizedRequiredColumns) {
197
+ if ([string]::IsNullOrWhiteSpace($column)) {
198
+ throw 'RequiredColumns cannot contain blank values.'
199
+ }
200
+ }
201
+
202
+ $typeMap = @{}
203
+ if (-not [string]::IsNullOrWhiteSpace($ColumnTypesJson)) {
204
+ $parsed = ConvertFrom-Json -InputObject $ColumnTypesJson
205
+ foreach ($property in $parsed.PSObject.Properties) {
206
+ $typeExpression = [string]$property.Value
207
+ Assert-SafeMType -TypeExpression $typeExpression -ColumnName $property.Name
208
+ $typeMap[$property.Name] = $typeExpression
209
+ }
210
+ }
211
+
212
+ $types = @()
213
+ foreach ($column in $normalizedRequiredColumns) {
214
+ $typeExpression = if ($typeMap.ContainsKey($column)) { $typeMap[$column] } else { 'type text' }
215
+ $types += ,@($column, $typeExpression)
216
+ }
217
+
218
+ $projection = if ($null -eq $normalizedProjectionColumns -or $normalizedProjectionColumns.Count -eq 0) {
219
+ @('Data') + $normalizedRequiredColumns
220
+ } elseif ($normalizedProjectionColumns -contains 'Data') {
221
+ $normalizedProjectionColumns
222
+ } else {
223
+ @('Data') + $normalizedProjectionColumns
224
+ }
225
+
226
+ return [ordered]@{
227
+ RequiredColumns = $normalizedRequiredColumns
228
+ Types = $types
229
+ Projection = $projection
230
+ }
231
+ }
232
+
233
+ function Format-MTextList {
234
+ param([string[]]$Items)
235
+ $quoted = $Items | ForEach-Object { '"' + ($_ -replace '"', '""') + '"' }
236
+ return '{' + ($quoted -join ', ') + '}'
237
+ }
238
+
239
+ function Format-MTypePairs {
240
+ param([array]$Pairs)
241
+ $formatted = foreach ($pair in $Pairs) {
242
+ '{"' + ($pair[0] -replace '"', '""') + '", ' + $pair[1] + '}'
243
+ }
244
+ return '{' + ($formatted -join ', ') + '}'
245
+ }
246
+
247
+ function Assert-SafePowerQueryName {
248
+ param(
249
+ [Parameter(Mandatory = $true)]
250
+ [string]$Name,
251
+
252
+ [string]$ParameterName = 'Name'
253
+ )
254
+
255
+ if ($Name.Length -gt 80 -or $Name -notmatch '^[\p{L}][\p{L}0-9 _-]*$') {
256
+ throw "$ParameterName contains unsafe characters. Use letters, numbers, spaces, hyphen, or underscore, starting with a letter."
257
+ }
258
+ }
259
+
260
+ function Format-MQuotedIdentifier {
261
+ param([Parameter(Mandatory = $true)][string]$Name)
262
+ return '#"' + ($Name -replace '"', '""') + '"'
263
+ }
264
+
265
+ function Join-SafeChildFile {
266
+ param(
267
+ [Parameter(Mandatory = $true)][string]$Directory,
268
+ [Parameter(Mandatory = $true)][string]$FileName
269
+ )
270
+
271
+ $root = [System.IO.Path]::GetFullPath($Directory)
272
+ $candidate = [System.IO.Path]::GetFullPath((Join-Path $root $FileName))
273
+ $rootWithSeparator = $root.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar
274
+
275
+ if (-not $candidate.StartsWith($rootWithSeparator, [System.StringComparison]::OrdinalIgnoreCase)) {
276
+ throw "Output path escaped OutputDirectory: $FileName"
277
+ }
278
+
279
+ return $candidate
280
+ }
281
+
282
+ function Assert-SafeOutputDirectory {
283
+ param([Parameter(Mandatory = $true)][string]$Directory)
284
+
285
+ $fullPath = [System.IO.Path]::GetFullPath($Directory)
286
+ $segments = $fullPath -split '[\\/]+' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
287
+
288
+ foreach ($segment in $segments) {
289
+ if ($segment.EndsWith('.SemanticModel', [System.StringComparison]::OrdinalIgnoreCase) -or
290
+ $segment.EndsWith('.Report', [System.StringComparison]::OrdinalIgnoreCase) -or
291
+ [string]::Equals($segment, 'pbip-files', [System.StringComparison]::OrdinalIgnoreCase) -or
292
+ [string]::Equals($segment, '.pbi', [System.StringComparison]::OrdinalIgnoreCase) -or
293
+ [string]::Equals($segment, 'DAXQueries', [System.StringComparison]::OrdinalIgnoreCase) -or
294
+ [string]::Equals($segment, 'TMDLScripts', [System.StringComparison]::OrdinalIgnoreCase)) {
295
+ throw "OutputDirectory must be outside Power BI project artifact folders. Use a scratch folder such as '.\powerquery-output'."
296
+ }
297
+ }
298
+ }
299
+
300
+ if ($PSCmdlet.ParameterSetName -eq 'File') {
301
+ if (-not (Test-Path -LiteralPath $SourceExpressionPath)) {
302
+ throw "SourceExpressionPath not found: $SourceExpressionPath"
303
+ }
304
+ $SourceExpression = (Get-Content -LiteralPath $SourceExpressionPath -Raw).Trim()
305
+ }
306
+
307
+ if ([string]::IsNullOrWhiteSpace($RealQueryName)) {
308
+ $RealQueryName = "${TableName}_Real"
309
+ }
310
+ Assert-SafePowerQueryName -Name $TableName -ParameterName 'TableName'
311
+ Assert-SafePowerQueryName -Name $RealQueryName -ParameterName 'RealQueryName'
312
+
313
+ $contract = Get-TableContract -Name $TableName
314
+ if ($null -eq $contract) {
315
+ $contract = New-GeneratedTableContract -Name $TableName -RequiredColumns $RequiredColumns -ProjectionColumns $ProjectionColumns -ColumnTypesJson $ColumnTypesJson
316
+ }
317
+ $requiredColumns = Format-MTextList -Items $contract.RequiredColumns
318
+ $typePairs = Format-MTypePairs -Pairs $contract.Types
319
+ $projection = Format-MTextList -Items $contract.Projection
320
+
321
+ # SECURITY: $SourceExpression is inserted verbatim as the M `Source` step and
322
+ # runs as code on every model refresh — it is validated only as a connector
323
+ # expression by the caller, not sandboxed. Only ever pass a connection
324
+ # expression the user authored or approved; never agent-summarized or untrusted
325
+ # source text (see the bi-powerquery SKILL.md "untrusted source" rule).
326
+ $lines = [System.Collections.Generic.List[string]]::new()
327
+ $lines.Add('let')
328
+ $lines.Add(" Source = $SourceExpression,")
329
+ $lines.Add(" RequiredColumns = $requiredColumns,")
330
+ $lines.Add(' MissingColumns = List.Difference(RequiredColumns, Table.ColumnNames(Source)),')
331
+ $lines.Add(' AssertRequiredColumns = if List.Count(MissingColumns) = 0 then Source else error Error.Record("MissingColumns", "Source is missing required columns.", MissingColumns),')
332
+ $lines.Add(" Typed = Table.TransformColumnTypes(AssertRequiredColumns, $typePairs, `"en-US`"),")
333
+ $lines.Add(' AddData = if Table.HasColumns(Typed, "Data") then Table.TransformColumns(Typed, {{"Data", each "Real", type text}}) else Table.AddColumn(Typed, "Data", each "Real", type text),')
334
+
335
+ $lines.Add(" Result = Table.SelectColumns(AddData, $projection)")
336
+ $lines.Add('in')
337
+ $lines.Add(' Result')
338
+ $realQueryM = $lines -join [Environment]::NewLine
339
+
340
+ $realQueryRef = Format-MQuotedIdentifier -Name $RealQueryName
341
+ $loadedAppendM = @"
342
+ let
343
+ Demo = ExistingDemoResult,
344
+ Real = $realQueryRef,
345
+ Result = Table.Combine({Demo, Real})
346
+ in
347
+ Result
348
+ "@
349
+
350
+ $writtenFiles = @()
351
+ if (-not [string]::IsNullOrWhiteSpace($OutputDirectory)) {
352
+ Assert-SafeOutputDirectory -Directory $OutputDirectory
353
+ New-Item -ItemType Directory -Force -Path $OutputDirectory | Out-Null
354
+ $realPath = Join-SafeChildFile -Directory $OutputDirectory -FileName "$RealQueryName.m"
355
+ $appendPath = Join-SafeChildFile -Directory $OutputDirectory -FileName "${TableName}_append-template.m"
356
+ Set-Content -LiteralPath $realPath -Value $realQueryM -Encoding UTF8NoBOM
357
+ Set-Content -LiteralPath $appendPath -Value $loadedAppendM -Encoding UTF8NoBOM
358
+ $writtenFiles = @($realPath, $appendPath)
359
+ }
360
+
361
+ [ordered]@{
362
+ success = $true
363
+ tableName = $TableName
364
+ realQueryName = $RealQueryName
365
+ requiredColumns = $contract.RequiredColumns
366
+ generatedColumns = @('Data')
367
+ realQueryM = $realQueryM
368
+ loadedAppendM = $loadedAppendM
369
+ writtenFiles = $writtenFiles
370
+ } | ConvertTo-Json -Depth 8
371
+
@@ -0,0 +1,225 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [Parameter(Mandatory = $true)]
4
+ [string]$TemplateRoot
5
+ )
6
+
7
+ $ErrorActionPreference = 'Stop'
8
+ # Pin stdout to UTF-8 so validation JSON with accented paths (for example C:\Users\Jose)
9
+ # round-trips when the caller (Node/MCP) decodes the pipe as UTF-8.
10
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
11
+
12
+ function Resolve-DefinitionPath {
13
+ param([string]$Root)
14
+
15
+ $candidates = @(
16
+ (Join-Path $Root 'base-template.SemanticModel\definition'),
17
+ (Join-Path $Root 'definition'),
18
+ $Root
19
+ )
20
+
21
+ foreach ($candidate in $candidates) {
22
+ if ((Test-Path -LiteralPath (Join-Path $candidate 'relationships.tmdl')) -and
23
+ (Test-Path -LiteralPath (Join-Path $candidate 'tables'))) {
24
+ return (Resolve-Path -LiteralPath $candidate).Path
25
+ }
26
+ }
27
+
28
+ throw "Could not find a semantic model definition folder under: $Root"
29
+ }
30
+
31
+ function Add-Error {
32
+ param(
33
+ [System.Collections.Generic.List[object]]$Errors,
34
+ [string]$Table,
35
+ [string]$Message
36
+ )
37
+
38
+ $Errors.Add([ordered]@{ table = $Table; message = $Message })
39
+ }
40
+
41
+ function Test-Column {
42
+ param(
43
+ [string]$Text,
44
+ [string]$Table,
45
+ [string]$Column,
46
+ [bool]$RequireHidden,
47
+ [string]$DataType,
48
+ [System.Collections.Generic.List[object]]$Errors
49
+ )
50
+
51
+ $escaped = [regex]::Escape($Column)
52
+ $columnPattern = "(?ms)^\tcolumn '?$escaped'?\r?\n(?<block>.*?)(?=\r?\n\t(?:column|partition|hierarchy|measure|annotation|///)|\z)"
53
+ $match = [regex]::Match($Text, $columnPattern)
54
+ if (-not $match.Success) {
55
+ $anyColumnPattern = "(?ms)^\tcolumn '?[^'\r\n]+'?\r?\n(?<block>.*?)(?=\r?\n\t(?:column|partition|hierarchy|measure|annotation|///)|\z)"
56
+ foreach ($candidate in [regex]::Matches($Text, $anyColumnPattern)) {
57
+ if ($candidate.Groups['block'].Value -match "sourceColumn: $escaped(\r?\n|$)") {
58
+ $match = $candidate
59
+ break
60
+ }
61
+ }
62
+ }
63
+ if (-not $match.Success) {
64
+ Add-Error -Errors $Errors -Table $Table -Message "Missing column $Column"
65
+ return
66
+ }
67
+
68
+ $block = $match.Groups['block'].Value
69
+ if ($block -notmatch "sourceColumn: $escaped(\r?\n|$)") {
70
+ Add-Error -Errors $Errors -Table $Table -Message "Column $Column is missing sourceColumn $Column"
71
+ }
72
+
73
+ if ($RequireHidden -and $block -notmatch '(^|\r?\n)\t\tisHidden(\r?\n|$)') {
74
+ Add-Error -Errors $Errors -Table $Table -Message "Column $Column should be hidden"
75
+ }
76
+
77
+ if (-not [string]::IsNullOrWhiteSpace($DataType) -and $block -notmatch "dataType: $DataType") {
78
+ Add-Error -Errors $Errors -Table $Table -Message "Column $Column should have dataType $DataType"
79
+ }
80
+ }
81
+
82
+ $definitionPath = Resolve-DefinitionPath -Root $TemplateRoot
83
+ $tablesPath = Join-Path $definitionPath 'tables'
84
+ $errors = [System.Collections.Generic.List[object]]::new()
85
+ $campanas = 'Campa' + [char]0x00F1 + 'as'
86
+ $campanaId = 'Campa' + [char]0x00F1 + 'aId'
87
+ $ordenesServicio = [char]0x00D3 + 'rdenes servicio'
88
+ $nomina = 'N' + [char]0x00F3 + 'mina'
89
+ $ejecucionProyectos = 'Ejecuci' + [char]0x00F3 + 'n proyectos'
90
+ $appendableTables = @(
91
+ 'Clientes',
92
+ 'Productos',
93
+ 'Canales',
94
+ 'Servicios',
95
+ 'Proyectos',
96
+ 'Equipos',
97
+ $campanas,
98
+ 'Ventas',
99
+ 'Oportunidades',
100
+ 'Leads',
101
+ 'Interacciones clientes',
102
+ 'Entregas',
103
+ $ordenesServicio,
104
+ 'Movimientos financieros',
105
+ 'Presupuesto',
106
+ 'Devoluciones',
107
+ 'Horas',
108
+ $nomina,
109
+ $ejecucionProyectos,
110
+ 'Tareas proyecto'
111
+ )
112
+ $idColumns = [ordered]@{
113
+ Clientes = @('ClienteId')
114
+ Productos = @('ProductoId')
115
+ Canales = @('CanalId')
116
+ Servicios = @('ServicioId')
117
+ Proyectos = @('ProyectoId')
118
+ Equipos = @('EquipoId')
119
+ Ventas = @('VentaId', 'ProductoId', 'ClienteId', 'CanalId')
120
+ Oportunidades = @('OportunidadId', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId')
121
+ Leads = @('LeadBatchId', $campanaId, 'CanalId', 'ClienteId')
122
+ 'Interacciones clientes' = @('InteraccionId', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId')
123
+ Entregas = @('EntregaId', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId')
124
+ 'Movimientos financieros' = @('MovimientoId', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'ProyectoId', 'EquipoId')
125
+ Presupuesto = @('PresupuestoId', 'ClienteId', 'CanalId', 'ProductoId', 'ServicioId', 'ProyectoId', 'EquipoId')
126
+ Devoluciones = @('DevolucionId', 'ClienteId', 'CanalId', 'ProductoId')
127
+ Horas = @('HoraId', 'EquipoId', 'ProyectoId', 'ServicioId')
128
+ 'Tareas proyecto' = @('TareaProyectoId', 'ProyectoId', 'EquipoId')
129
+ }
130
+ $idColumns[$campanas] = @($campanaId)
131
+ $idColumns[$ordenesServicio] = @('OrdenServicioId', 'ClienteId', 'ProductoId', 'ServicioId', 'EquipoId')
132
+ $idColumns[$nomina] = @('NominaId', 'EquipoId', 'ProyectoId', 'ServicioId')
133
+ $idColumns[$ejecucionProyectos] = @('EjecucionProyectoId', 'ProyectoId', 'ClienteId', 'ServicioId', 'EquipoId')
134
+
135
+ foreach ($table in $appendableTables) {
136
+ $tablePath = Join-Path $tablesPath "$table.tmdl"
137
+ if (-not (Test-Path -LiteralPath $tablePath)) {
138
+ Add-Error -Errors $errors -Table $table -Message "Missing table TMDL file"
139
+ continue
140
+ }
141
+
142
+ $text = Get-Content -LiteralPath $tablePath -Raw -Encoding UTF8
143
+ Test-Column -Text $text -Table $table -Column 'Data' -RequireHidden:$false -DataType 'string' -Errors $errors
144
+
145
+ foreach ($column in $idColumns[$table]) {
146
+ Test-Column -Text $text -Table $table -Column $column -RequireHidden:$true -DataType 'string' -Errors $errors
147
+ }
148
+
149
+ if ($text -match 'DataKey') {
150
+ Add-Error -Errors $errors -Table $table -Message 'Unexpected DataKey column or expression found'
151
+ }
152
+
153
+ $usesEntityCodeFunction = $text -match 'EntityCode\s*=\s*\(entity as text, n as number\)'
154
+ $usesEntityCodeCall = $text -match 'EntityCode\("[A-Za-z]+",'
155
+ $usesEntityLiteralId = $text -match '"(?:Cliente|Producto|Canal|Servicio|Proyecto|Equipo|Campana)A[0-9]{2}[A-Z]"'
156
+ $usesExtensibleEntityCode = $text -match 'ToLetters\s*=\s*\(value as number\) as text' -and
157
+ $text -match '@ToLetters\(Number\.IntegerDivide\(Current, 26\) - 1\)' -and
158
+ $text -match 'BlockCode = ToLetters\(Number\.IntegerDivide\(CleanNumber - 1, 100\)\)'
159
+ $usesOldInlineDemoId = $text -match '"Demo" & Text\.PadStart\(Text\.From\(\['
160
+ $usesOldHelperDemoId = $text -match 'DemoId\([^,\r\n]+,\s*9\)'
161
+ $usesOldLiteralDemoId = $text -match '"Demo[0-9]{9}"'
162
+
163
+ if ($usesOldInlineDemoId -or $usesOldHelperDemoId -or $usesOldLiteralDemoId) {
164
+ Add-Error -Errors $errors -Table $table -Message 'Demo IDs should use entity-coded values such as ClienteA25D, not Demo#########'
165
+ }
166
+
167
+ if ($text -match 'IdDemo') {
168
+ Add-Error -Errors $errors -Table $table -Message 'Temporary entity-code columns should use *Codigo names, not *IdDemo'
169
+ }
170
+
171
+ if (-not ($usesEntityCodeCall -or $usesEntityLiteralId)) {
172
+ Add-Error -Errors $errors -Table $table -Message 'Demo IDs should use entity-coded values such as ClienteA25D'
173
+ }
174
+
175
+ if ($usesEntityCodeCall -and -not $usesEntityCodeFunction) {
176
+ Add-Error -Errors $errors -Table $table -Message 'EntityCode helper calls require the local EntityCode function'
177
+ }
178
+
179
+ if ($usesEntityCodeFunction -and -not $usesExtensibleEntityCode) {
180
+ Add-Error -Errors $errors -Table $table -Message 'EntityCode helper should use extensible alphabetic blocks so fact IDs stay unique past 2600 rows'
181
+ }
182
+ }
183
+
184
+ $relationshipsPath = Join-Path $definitionPath 'relationships.tmdl'
185
+ $relationships = Get-Content -LiteralPath $relationshipsPath -Raw -Encoding UTF8
186
+ $relationshipChecks = @(
187
+ 'Ventas_ProductoId_Productos_ProductoId',
188
+ 'fromColumn: Ventas.ProductoId',
189
+ 'toColumn: Productos.ProductoId',
190
+ 'Ventas_ClienteId_Clientes_ClienteId',
191
+ 'fromColumn: Ventas.ClienteId',
192
+ 'toColumn: Clientes.ClienteId',
193
+ 'Ventas_CanalId_Canales_CanalId',
194
+ 'fromColumn: Ventas.CanalId',
195
+ 'toColumn: Canales.CanalId',
196
+ 'Oportunidades_ServicioId_Servicios_ServicioId',
197
+ "Leads_${campanaId}_${campanas}_${campanaId}",
198
+ 'Interacciones clientes_ServicioId_Servicios_ServicioId',
199
+ 'Movimientos financieros_ProyectoId_Proyectos_ProyectoId',
200
+ 'Presupuesto_EquipoId_Equipos_EquipoId',
201
+ 'Horas_ProyectoId_Proyectos_ProyectoId',
202
+ "${ejecucionProyectos}_ProyectoId_Proyectos_ProyectoId",
203
+ 'Tareas proyecto_EquipoId_Equipos_EquipoId'
204
+ )
205
+
206
+ foreach ($check in $relationshipChecks) {
207
+ if (-not $relationships.Contains($check)) {
208
+ Add-Error -Errors $errors -Table 'relationships' -Message "Missing relationship contract text: $check"
209
+ }
210
+ }
211
+
212
+ if ($relationships -match 'DataKey') {
213
+ Add-Error -Errors $errors -Table 'relationships' -Message 'Relationships should use original ID columns, not DataKey columns'
214
+ }
215
+
216
+ $success = $errors.Count -eq 0
217
+ $result = [ordered]@{
218
+ success = $success
219
+ definitionPath = $definitionPath
220
+ appendableTables = $appendableTables
221
+ errors = @($errors)
222
+ }
223
+
224
+ $result | ConvertTo-Json -Depth 6
225
+ if (-not $success) { exit 1 }