@atlashub/smartstack-cli 4.80.0 → 5.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 (1008) hide show
  1. package/.documentation/agents.html +124 -585
  2. package/.documentation/ba-develop.html +852 -0
  3. package/.documentation/ba-skills.html +465 -0
  4. package/.documentation/business-analyse.html +385 -1570
  5. package/.documentation/cli-commands.html +162 -799
  6. package/.documentation/commands.html +902 -1338
  7. package/.documentation/css/styles.css +34 -1
  8. package/.documentation/efcore.html +161 -2599
  9. package/.documentation/gitflow.html +62 -105
  10. package/.documentation/hooks.html +94 -343
  11. package/.documentation/index.html +116 -385
  12. package/.documentation/init.html +217 -1566
  13. package/.documentation/installation.html +121 -1470
  14. package/.documentation/license.html +90 -450
  15. package/.documentation/ralph-loop.html +105 -602
  16. package/dist/index.js +9421 -79036
  17. package/dist/index.js.map +1 -1
  18. package/package.json +5 -20
  19. package/scripts/generate-docs/README.md +87 -0
  20. package/scripts/generate-docs/index.ts +175 -0
  21. package/scripts/generate-docs/lib/context-builder.ts +81 -0
  22. package/scripts/generate-docs/lib/handlebars-setup.ts +162 -0
  23. package/scripts/generate-docs/lib/markdown-parser.ts +86 -0
  24. package/scripts/generate-docs/lib/sidebar-builder.ts +80 -0
  25. package/scripts/generate-docs/lib/skill-parser.ts +171 -0
  26. package/scripts/generate-docs/lib/stats.ts +32 -0
  27. package/scripts/generate-docs/lib/version.ts +17 -0
  28. package/scripts/generate-docs/templates/layout.hbs +33 -0
  29. package/scripts/generate-docs/templates/pages/_generic.hbs +12 -0
  30. package/scripts/generate-docs/templates/pages/ba-develop.hbs +10 -0
  31. package/scripts/generate-docs/templates/pages/ba-skills.hbs +8 -0
  32. package/scripts/generate-docs/templates/pages/business-analyse.hbs +1 -0
  33. package/scripts/generate-docs/templates/pages/commands.hbs +13 -0
  34. package/scripts/generate-docs/templates/pages/gitflow.hbs +2164 -0
  35. package/scripts/generate-docs/templates/pages/index.hbs +5 -0
  36. package/scripts/generate-docs/templates/partials/breadcrumb.hbs +6 -0
  37. package/scripts/generate-docs/templates/partials/header.hbs +22 -0
  38. package/scripts/generate-docs/templates/partials/sidebar.hbs +32 -0
  39. package/scripts/generate-docs/templates/partials/skill-card.hbs +22 -0
  40. package/scripts/generate-docs/templates/partials/skill-grid.hbs +5 -0
  41. package/scripts/generate-docs/templates/partials/skill-table.hbs +18 -0
  42. package/scripts/generate-docs/templates/partials/stats-bar.hbs +20 -0
  43. package/scripts/test-migration-program-cs.mts +94 -0
  44. package/templates/agents/explore-codebase.md +2 -3
  45. package/templates/agents/explore-docs.md +5 -5
  46. package/templates/hooks/hooks.json +0 -9
  47. package/templates/project/Program.cs.template +17 -5
  48. package/templates/project/appsettings.json.template +208 -195
  49. package/templates/project/claude-md/api.CLAUDE.md.template +27 -2
  50. package/templates/project/patch-smartstack-theme.cjs.template +42 -0
  51. package/templates/scripts/statusline/README.md +47 -0
  52. package/templates/scripts/statusline/index.js +224 -0
  53. package/templates/skills/CLAUDE.md +235 -0
  54. package/templates/skills/ba-develop/SKILL.md +310 -0
  55. package/templates/skills/ba-develop/cli/compute-page-diff/__tests__/compute-page-diff.test.ts +177 -0
  56. package/templates/skills/ba-develop/cli/compute-page-diff/compute-diff.ts +51 -0
  57. package/templates/skills/ba-develop/cli/compute-page-diff/disk-drift.ts +55 -0
  58. package/templates/skills/ba-develop/cli/compute-page-diff/index.ts +89 -0
  59. package/templates/skills/ba-develop/cli/compute-page-diff/scan-pagespecs.ts +115 -0
  60. package/templates/skills/ba-develop/cli/compute-page-diff/types.ts +63 -0
  61. package/templates/skills/ba-develop/cli/compute-page-diff/validate.ts +20 -0
  62. package/templates/skills/ba-develop/cli/update-snapshot/__tests__/update-snapshot.test.ts +73 -0
  63. package/templates/skills/ba-develop/cli/update-snapshot/execute.ts +24 -0
  64. package/templates/skills/ba-develop/cli/update-snapshot/index.ts +61 -0
  65. package/templates/skills/ba-develop/cli/update-snapshot/types.ts +40 -0
  66. package/templates/skills/ba-develop/cli/update-snapshot/validate.ts +17 -0
  67. package/templates/skills/ba-develop/references/anti-patterns.md +101 -0
  68. package/templates/skills/ba-develop/references/auto-healing.md +191 -0
  69. package/templates/skills/ba-develop/references/commit-checkpoints.md +79 -0
  70. package/templates/skills/ba-develop/references/gates.md +380 -0
  71. package/templates/skills/ba-develop/references/output-contract.md +95 -0
  72. package/templates/skills/ba-develop/references/phases-detail.md +592 -0
  73. package/templates/skills/ba-develop-plan/SKILL.md +239 -0
  74. package/templates/skills/ba-develop-plan/cli/preflight-develop-plan/__tests__/validate.test.ts +225 -0
  75. package/templates/skills/ba-develop-plan/cli/preflight-develop-plan/index.ts +102 -0
  76. package/templates/skills/ba-develop-plan/cli/preflight-develop-plan/types.ts +121 -0
  77. package/templates/skills/ba-develop-plan/cli/preflight-develop-plan/validate.ts +261 -0
  78. package/templates/skills/business-analyse/_workflow/README.md +34 -0
  79. package/templates/skills/business-analyse/_workflow/ba-files.md +174 -0
  80. package/templates/skills/business-analyse/_workflow/code-discipline.md +104 -0
  81. package/templates/skills/business-analyse/_workflow/communication.md +63 -0
  82. package/templates/skills/business-analyse/_workflow/completeAuto-discipline.md +79 -0
  83. package/templates/skills/business-analyse/_workflow/context-documents.md +45 -0
  84. package/templates/skills/business-analyse/_workflow/doc-templates.md +318 -0
  85. package/templates/skills/business-analyse/audit-actors/SKILL.md +97 -0
  86. package/templates/skills/business-analyse/audit-cross-dimension/SKILL.md +127 -0
  87. package/templates/skills/business-analyse/audit-cross-ref-code/SKILL.md +119 -0
  88. package/templates/skills/business-analyse/audit-data-model/SKILL.md +343 -0
  89. package/templates/skills/business-analyse/audit-menu/SKILL.md +97 -0
  90. package/templates/skills/business-analyse/audit-prd/SKILL.md +479 -0
  91. package/templates/skills/business-analyse/audit-pre-dev/SKILL.md +135 -0
  92. package/templates/skills/business-analyse/audit-rbac/SKILL.md +93 -0
  93. package/templates/skills/business-analyse/audit-rules/SKILL.md +182 -0
  94. package/templates/skills/business-analyse/audit-screens/SKILL.md +169 -0
  95. package/templates/skills/business-analyse/audit-sections/SKILL.md +174 -0
  96. package/templates/skills/business-analyse/audit-use-cases/SKILL.md +245 -0
  97. package/templates/skills/business-analyse/create-actors/SKILL.md +129 -0
  98. package/templates/skills/business-analyse/create-ba-order/SKILL.md +182 -0
  99. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/__tests__/generate.test.ts +151 -0
  100. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/__tests__/graph.test.ts +173 -0
  101. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/generate.ts +273 -0
  102. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/graph.ts +193 -0
  103. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/index.ts +108 -0
  104. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/types.ts +106 -0
  105. package/templates/skills/business-analyse/create-ba-order/cli/create-ba-order/validate.ts +79 -0
  106. package/templates/skills/business-analyse/create-business-rules/SKILL.md +302 -0
  107. package/templates/skills/business-analyse/create-business-rules/levels/access-rules.md +105 -0
  108. package/templates/skills/business-analyse/create-business-rules/levels/elaborate.md +193 -0
  109. package/templates/skills/business-analyse/create-business-rules/levels/identify.md +157 -0
  110. package/templates/skills/business-analyse/create-business-rules/levels/link.md +86 -0
  111. package/templates/skills/business-analyse/create-data-model/SKILL.md +319 -0
  112. package/templates/skills/business-analyse/create-data-model/levels/attributes.md +130 -0
  113. package/templates/skills/business-analyse/create-data-model/levels/identify.md +100 -0
  114. package/templates/skills/business-analyse/create-data-model/levels/relationships.md +97 -0
  115. package/templates/skills/business-analyse/create-menu/SKILL.md +191 -0
  116. package/templates/skills/business-analyse/create-menu/levels/applications.md +85 -0
  117. package/templates/skills/business-analyse/create-menu/levels/modules.md +81 -0
  118. package/templates/skills/business-analyse/create-menu/levels/resources.md +75 -0
  119. package/templates/skills/business-analyse/create-menu/levels/sections.md +82 -0
  120. package/templates/skills/business-analyse/create-plan-development/SKILL.md +93 -0
  121. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/__tests__/graph.test.ts +271 -0
  122. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/__tests__/parse.test.ts +177 -0
  123. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/generate.ts +317 -0
  124. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/graph.ts +233 -0
  125. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/index.ts +106 -0
  126. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/parse.ts +346 -0
  127. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/types.ts +160 -0
  128. package/templates/skills/business-analyse/create-plan-development/cli/create-plan-development/validate.ts +118 -0
  129. package/templates/skills/business-analyse/create-prd/SKILL.md +228 -0
  130. package/templates/skills/business-analyse/create-rbac/SKILL.md +255 -0
  131. package/templates/skills/business-analyse/create-rbac/levels/detail.md +86 -0
  132. package/templates/skills/business-analyse/create-rbac/levels/discovery.md +63 -0
  133. package/templates/skills/business-analyse/create-rbac/levels/review.md +66 -0
  134. package/templates/skills/business-analyse/create-screen/SKILL.md +386 -0
  135. package/templates/skills/business-analyse/create-screen/levels/dashboard-screens.md +94 -0
  136. package/templates/skills/business-analyse/create-screen/levels/form-screens.md +142 -0
  137. package/templates/skills/business-analyse/create-screen/levels/home-screens.md +151 -0
  138. package/templates/skills/business-analyse/create-screen/levels/kanban-screens.md +86 -0
  139. package/templates/skills/business-analyse/create-screen/levels/list-screens.md +134 -0
  140. package/templates/skills/business-analyse/create-screen/references/post-check.md +101 -0
  141. package/templates/skills/business-analyse/create-screen/references/react-templates.md +252 -0
  142. package/templates/skills/business-analyse/create-screen/references/smartcomponents.md +419 -0
  143. package/templates/skills/business-analyse/create-screen/references/type-mapping.md +150 -0
  144. package/templates/skills/business-analyse/create-use-case/SKILL.md +347 -0
  145. package/templates/skills/business-analyse/create-use-case/levels/detail.md +136 -0
  146. package/templates/skills/business-analyse/create-use-case/levels/discovery.md +110 -0
  147. package/templates/skills/business-analyse/loop/SKILL.md +401 -0
  148. package/templates/skills/business-analyse/modeling-detail/SKILL.md +241 -0
  149. package/templates/skills/business-analyse/modeling-inventory/SKILL.md +174 -0
  150. package/templates/skills/business-analyse/reconcile-menu/SKILL.md +180 -0
  151. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/__tests__/clean.test.ts +266 -0
  152. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/__tests__/detect.test.ts +231 -0
  153. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/__tests__/scan.test.ts +154 -0
  154. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/clean.ts +319 -0
  155. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/detect.ts +256 -0
  156. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/index.ts +126 -0
  157. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/scan.ts +175 -0
  158. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/types.ts +136 -0
  159. package/templates/skills/business-analyse/reconcile-menu/cli/reconcile-menu/validate.ts +32 -0
  160. package/templates/skills/check-version/SKILL.md +196 -196
  161. package/templates/skills/cli-app-sync/SKILL.md +9 -9
  162. package/templates/skills/cli-app-sync/references/comparison-map.md +4 -4
  163. package/templates/skills/cli-app-sync/references/diff-entities.md +6 -6
  164. package/templates/skills/conventions/SKILL.md +64 -0
  165. package/templates/skills/dev-start/SKILL.md +190 -237
  166. package/templates/skills/development/SKILL.md +87 -0
  167. package/templates/skills/development/audit/SKILL.md +156 -0
  168. package/templates/skills/development/audit/routing-dynamic/SKILL.md +196 -0
  169. package/templates/skills/development/audit-dev-api/SKILL.md +331 -0
  170. package/templates/skills/development/audit-dev-api/cli/audit-dev-api/__tests__/end-to-end.test.ts +364 -0
  171. package/templates/skills/development/audit-dev-api/cli/audit-dev-api/audit.ts +646 -0
  172. package/templates/skills/development/audit-dev-api/cli/audit-dev-api/index.ts +140 -0
  173. package/templates/skills/development/audit-dev-api/cli/audit-dev-api/types.ts +158 -0
  174. package/templates/skills/development/audit-dev-api/cli/audit-dev-api/validate.ts +45 -0
  175. package/templates/skills/development/audit-dev-core/SKILL.md +182 -0
  176. package/templates/skills/development/audit-dev-data/SKILL.md +195 -0
  177. package/templates/skills/development/audit-dev-domain/SKILL.md +184 -0
  178. package/templates/skills/development/audit-dev-frontend/SKILL.md +530 -0
  179. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/__tests__/end-to-end.test.ts +202 -0
  180. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/apply.ts +31 -0
  181. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/audit.ts +734 -0
  182. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/index.ts +125 -0
  183. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/types.ts +165 -0
  184. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/validate.ts +36 -0
  185. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/__tests__/dev-ui-022.test.ts +193 -0
  186. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/apply.ts +374 -0
  187. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/audit.ts +1126 -0
  188. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/index.ts +141 -0
  189. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/types.ts +218 -0
  190. package/templates/skills/development/audit-dev-frontend/cli/audit-dev-frontend/validate.ts +80 -0
  191. package/templates/skills/development/audit-dev-tests/SKILL.md +82 -0
  192. package/templates/skills/development/audit-dev-tests/cli/audit-dev-tests/__tests__/audit.test.ts +220 -0
  193. package/templates/skills/development/audit-dev-tests/cli/audit-dev-tests/audit.ts +185 -0
  194. package/templates/skills/development/audit-dev-tests/cli/audit-dev-tests/index.ts +84 -0
  195. package/templates/skills/development/audit-dev-tests/cli/audit-dev-tests/types.ts +48 -0
  196. package/templates/skills/development/audit-dev-tests/cli/audit-dev-tests/validate.ts +36 -0
  197. package/templates/skills/development/audit-dev-wire/SKILL.md +144 -0
  198. package/templates/skills/development/audit-dev-wire/cli/audit-dev-wire/__tests__/audit.test.ts +171 -0
  199. package/templates/skills/development/audit-dev-wire/cli/audit-dev-wire/audit.ts +307 -0
  200. package/templates/skills/development/audit-dev-wire/cli/audit-dev-wire/index.ts +139 -0
  201. package/templates/skills/development/audit-dev-wire/cli/audit-dev-wire/types.ts +110 -0
  202. package/templates/skills/development/audit-dev-wire/cli/audit-dev-wire/validate.ts +16 -0
  203. package/templates/skills/development/backend/business-layer/SKILL.md +255 -0
  204. package/templates/skills/development/backend/business-layer/cli/scaffold-business/__tests__/generate.test.ts +254 -0
  205. package/templates/skills/development/backend/business-layer/cli/scaffold-business/generate.ts +842 -0
  206. package/templates/skills/development/backend/business-layer/cli/scaffold-business/index.ts +56 -0
  207. package/templates/skills/development/backend/business-layer/cli/scaffold-business/types.ts +112 -0
  208. package/templates/skills/development/backend/business-layer/cli/scaffold-business/validate.ts +24 -0
  209. package/templates/skills/development/backend/controller/SKILL.md +154 -0
  210. package/templates/skills/development/backend/controller/cli/scaffold-controller/__tests__/generate.test.ts +345 -0
  211. package/templates/skills/development/backend/controller/cli/scaffold-controller/generate.ts +280 -0
  212. package/templates/skills/development/backend/controller/cli/scaffold-controller/index.ts +49 -0
  213. package/templates/skills/development/backend/controller/cli/scaffold-controller/types.ts +72 -0
  214. package/templates/skills/development/backend/controller/cli/scaffold-controller/validate.ts +14 -0
  215. package/templates/skills/development/backend/core-seed/SKILL.md +177 -0
  216. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/__tests__/build-spec.test.ts +163 -0
  217. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/__tests__/generate.test.ts +330 -0
  218. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/__tests__/validate.test.ts +126 -0
  219. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/build-spec.ts +287 -0
  220. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/generate.ts +826 -0
  221. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/index.ts +188 -0
  222. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/types.ts +163 -0
  223. package/templates/skills/development/backend/core-seed/cli/scaffold-core-seed/validate.ts +129 -0
  224. package/templates/skills/development/backend/data-layer/SKILL.md +163 -0
  225. package/templates/skills/development/backend/data-layer/cli/scaffold-entity/__tests__/generate.test.ts +155 -0
  226. package/templates/skills/development/backend/data-layer/cli/scaffold-entity/generate.ts +232 -0
  227. package/templates/skills/development/backend/data-layer/cli/scaffold-entity/index.ts +34 -0
  228. package/templates/skills/development/backend/data-layer/cli/scaffold-entity/types.ts +60 -0
  229. package/templates/skills/development/backend/data-layer/cli/scaffold-entity/validate.ts +42 -0
  230. package/templates/skills/development/backend/data-layer/cli/scaffold-migration/execute.ts +13 -0
  231. package/templates/skills/development/backend/data-layer/cli/scaffold-migration/index.ts +25 -0
  232. package/templates/skills/development/backend/data-layer/cli/scaffold-migration/types.ts +11 -0
  233. package/templates/skills/development/backend/data-layer/cli/scaffold-migration/validate.ts +9 -0
  234. package/templates/skills/development/backend/screen-controller/SKILL.md +169 -0
  235. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/__tests__/generate.test.ts +329 -0
  236. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/__tests__/hub-views.test.ts +105 -0
  237. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/__tests__/parse-pagespec.test.ts +137 -0
  238. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/generate.ts +437 -0
  239. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/index.ts +108 -0
  240. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/parse-pagespec.ts +104 -0
  241. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/types.ts +101 -0
  242. package/templates/skills/development/backend/screen-controller/cli/scaffold-screen-controller/validate.ts +26 -0
  243. package/templates/skills/development/backend/seed-data/SKILL.md +91 -0
  244. package/templates/skills/development/backend/seed-data/cli/scaffold-seed/generate.ts +471 -0
  245. package/templates/skills/development/backend/seed-data/cli/scaffold-seed/index.ts +74 -0
  246. package/templates/skills/development/backend/seed-data/cli/scaffold-seed/types.ts +104 -0
  247. package/templates/skills/development/backend/seed-data/cli/scaffold-seed/validate.ts +63 -0
  248. package/templates/skills/development/backend/structure/SKILL.md +47 -0
  249. package/templates/skills/development/debug/SKILL.md +62 -0
  250. package/templates/skills/development/debug/audit-bug/SKILL.md +185 -0
  251. package/templates/skills/development/debug/backend/SKILL.md +114 -0
  252. package/templates/skills/development/debug/discuss-bug/SKILL.md +193 -0
  253. package/templates/skills/development/debug/fix-bug/SKILL.md +172 -0
  254. package/templates/skills/development/debug/frontend/SKILL.md +215 -0
  255. package/templates/skills/development/frontend/api-client/SKILL.md +158 -0
  256. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/__tests__/generate.test.ts +1180 -0
  257. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/__tests__/screen-strata-contract.test.ts +261 -0
  258. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/__tests__/url-parity.test.ts +201 -0
  259. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/generate.ts +875 -0
  260. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/index.ts +36 -0
  261. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/types.ts +251 -0
  262. package/templates/skills/development/frontend/api-client/cli/scaffold-api-client/validate.ts +10 -0
  263. package/templates/skills/development/frontend/auth/SKILL.md +77 -0
  264. package/templates/skills/development/frontend/auth/cli/scaffold-frontend-auth/generate.ts +306 -0
  265. package/templates/skills/development/frontend/auth/cli/scaffold-frontend-auth/index.ts +179 -0
  266. package/templates/skills/development/frontend/auth/cli/scaffold-frontend-auth/types.ts +22 -0
  267. package/templates/skills/development/frontend/auth/cli/scaffold-frontend-auth/validate.ts +37 -0
  268. package/templates/skills/development/frontend/component/SKILL.md +347 -0
  269. package/templates/skills/development/frontend/component/cli/scaffold-component/__tests__/generate.test.ts +1237 -0
  270. package/templates/skills/development/frontend/component/cli/scaffold-component/generate.ts +1923 -0
  271. package/templates/skills/development/frontend/component/cli/scaffold-component/index.ts +155 -0
  272. package/templates/skills/development/frontend/component/cli/scaffold-component/types.ts +290 -0
  273. package/templates/skills/development/frontend/component/cli/scaffold-component/validate.ts +16 -0
  274. package/templates/skills/development/frontend/component/cli/validate-page/__tests__/execute.test.ts +231 -0
  275. package/templates/skills/development/frontend/component/cli/validate-page/execute.ts +598 -0
  276. package/templates/skills/development/frontend/component/cli/validate-page/index.ts +88 -0
  277. package/templates/skills/development/frontend/component/cli/validate-page/types.ts +58 -0
  278. package/templates/skills/development/frontend/component/cli/validate-page/validate.ts +28 -0
  279. package/templates/skills/development/frontend/component/patterns/README.md +42 -0
  280. package/templates/skills/development/frontend/component/patterns/detail-page.md +133 -0
  281. package/templates/skills/development/frontend/component/patterns/entity-card.md +148 -0
  282. package/templates/skills/development/frontend/component/patterns/form-page.md +191 -0
  283. package/templates/skills/development/frontend/component/patterns/kanban-board.md +195 -0
  284. package/templates/skills/development/frontend/component/patterns/list-page.md +175 -0
  285. package/templates/skills/development/frontend/extension-config/SKILL.md +108 -0
  286. package/templates/skills/development/frontend/extension-config/cli/scaffold-extension-config/generate.ts +64 -0
  287. package/templates/skills/development/frontend/extension-config/cli/scaffold-extension-config/index.ts +70 -0
  288. package/templates/skills/development/frontend/extension-config/cli/scaffold-extension-config/types.ts +27 -0
  289. package/templates/skills/development/frontend/extension-config/cli/scaffold-extension-config/validate.ts +16 -0
  290. package/templates/skills/development/frontend/layout/SKILL.md +52 -0
  291. package/templates/skills/development/frontend/layout/cli/scaffold-layout/__tests__/generate.test.ts +66 -0
  292. package/templates/skills/development/frontend/layout/cli/scaffold-layout/generate.ts +175 -0
  293. package/templates/skills/development/frontend/layout/cli/scaffold-layout/index.ts +104 -0
  294. package/templates/skills/development/frontend/layout/cli/scaffold-layout/types.ts +17 -0
  295. package/templates/skills/development/frontend/layout/cli/scaffold-layout/validate.ts +37 -0
  296. package/templates/skills/development/frontend/routes/SKILL.md +152 -0
  297. package/templates/skills/development/frontend/routes/cli/aggregate-component-registry/generate.ts +216 -0
  298. package/templates/skills/development/frontend/routes/cli/aggregate-component-registry/index.ts +121 -0
  299. package/templates/skills/development/frontend/routes/cli/aggregate-component-registry/types.ts +69 -0
  300. package/templates/skills/development/frontend/routes/cli/aggregate-component-registry/validate.ts +23 -0
  301. package/templates/skills/development/frontend/routes/cli/scaffold-routes/__tests__/generate.test.ts +292 -0
  302. package/templates/skills/development/frontend/routes/cli/scaffold-routes/generate.ts +270 -0
  303. package/templates/skills/development/frontend/routes/cli/scaffold-routes/index.ts +42 -0
  304. package/templates/skills/development/frontend/routes/cli/scaffold-routes/types.ts +114 -0
  305. package/templates/skills/development/frontend/routes/cli/scaffold-routes/validate.ts +68 -0
  306. package/templates/skills/development/frontend/structure/SKILL.md +119 -0
  307. package/templates/skills/development/frontend/theme/SKILL.md +48 -0
  308. package/templates/skills/development/frontend/theme/cli/scaffold-theme/__tests__/generate.test.ts +53 -0
  309. package/templates/skills/development/frontend/theme/cli/scaffold-theme/generate.ts +129 -0
  310. package/templates/skills/development/frontend/theme/cli/scaffold-theme/index.ts +102 -0
  311. package/templates/skills/development/frontend/theme/cli/scaffold-theme/types.ts +48 -0
  312. package/templates/skills/development/frontend/theme/cli/scaffold-theme/validate.ts +37 -0
  313. package/templates/skills/development/frontend/ui-polish/SKILL.md +192 -0
  314. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/__tests__/r18.test.ts +153 -0
  315. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/__tests__/r19.test.ts +307 -0
  316. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/__tests__/r20.test.ts +167 -0
  317. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/__tests__/shared-scan.test.ts +262 -0
  318. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/apply.ts +580 -0
  319. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/audit.ts +825 -0
  320. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/index.ts +133 -0
  321. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/types.ts +121 -0
  322. package/templates/skills/development/frontend/ui-polish/cli/ui-polish/validate.ts +73 -0
  323. package/templates/skills/development/frontend/ui-polish/tokens.json +292 -0
  324. package/templates/skills/development/frontend/ui-primitives/SKILL.md +88 -0
  325. package/templates/skills/development/frontend/ui-primitives/cli/scaffold-ui-primitives/__tests__/generate.test.ts +158 -0
  326. package/templates/skills/development/frontend/ui-primitives/cli/scaffold-ui-primitives/generate.ts +345 -0
  327. package/templates/skills/development/frontend/ui-primitives/cli/scaffold-ui-primitives/index.ts +142 -0
  328. package/templates/skills/development/frontend/ui-primitives/cli/scaffold-ui-primitives/types.ts +27 -0
  329. package/templates/skills/development/frontend/ui-primitives/cli/scaffold-ui-primitives/validate.ts +37 -0
  330. package/templates/skills/development/run/SKILL.md +61 -0
  331. package/templates/skills/development/run/backend/SKILL.md +106 -0
  332. package/templates/skills/development/run/frontend/SKILL.md +116 -0
  333. package/templates/skills/development/smoke-test/SKILL.md +99 -0
  334. package/templates/skills/development/smoke-test/cli/run-smoke/execute.ts +424 -0
  335. package/templates/skills/development/smoke-test/cli/run-smoke/index.ts +75 -0
  336. package/templates/skills/development/smoke-test/cli/run-smoke/types.ts +100 -0
  337. package/templates/skills/development/testing/SKILL.md +148 -0
  338. package/templates/skills/development/testing/cli/scaffold-tests/generate.ts +530 -0
  339. package/templates/skills/development/testing/cli/scaffold-tests/index.ts +83 -0
  340. package/templates/skills/development/testing/cli/scaffold-tests/types.ts +51 -0
  341. package/templates/skills/development/testing/cli/scaffold-tests/validate.ts +33 -0
  342. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/__tests__/generate.test.ts +188 -0
  343. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/__tests__/parse-ac.test.ts +191 -0
  344. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/generate.ts +190 -0
  345. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/index.ts +138 -0
  346. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/parse-ac.ts +172 -0
  347. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/types.ts +104 -0
  348. package/templates/skills/development/testing/cli/scaffold-tests-from-ac/validate.ts +57 -0
  349. package/templates/skills/development/testing/cli/test-report/execute.ts +140 -0
  350. package/templates/skills/development/testing/cli/test-report/index.ts +96 -0
  351. package/templates/skills/development/testing/cli/test-report/types.ts +52 -0
  352. package/templates/skills/development/testing/cli/test-report/validate.ts +26 -0
  353. package/templates/skills/development/testing/fix-build/SKILL.md +81 -0
  354. package/templates/skills/development/testing/smoke-http/SKILL.md +123 -0
  355. package/templates/skills/development/testing/smoke-http/cli/smoke-http/execute.ts +129 -0
  356. package/templates/skills/development/testing/smoke-http/cli/smoke-http/index.ts +113 -0
  357. package/templates/skills/development/testing/smoke-http/cli/smoke-http/types.ts +62 -0
  358. package/templates/skills/development/testing/smoke-http/cli/smoke-http/validate.ts +36 -0
  359. package/templates/skills/development/testing/ui-test/SKILL.md +128 -0
  360. package/templates/skills/development/testing/ui-test/cli/build-manifest/generate.ts +129 -0
  361. package/templates/skills/development/testing/ui-test/cli/build-manifest/index.ts +80 -0
  362. package/templates/skills/development/testing/ui-test/cli/build-manifest/types.ts +72 -0
  363. package/templates/skills/development/testing/ui-test/cli/build-manifest/validate.ts +44 -0
  364. package/templates/skills/development/testing/ui-test/cli/run-ui-test/execute.ts +136 -0
  365. package/templates/skills/development/testing/ui-test/cli/run-ui-test/index.ts +75 -0
  366. package/templates/skills/development/testing/ui-test/cli/run-ui-test/lib/dev-browser-driver.ts +250 -0
  367. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/delete.js.hbs +83 -0
  368. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/detail.js.hbs +87 -0
  369. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/edit.js.hbs +91 -0
  370. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/form-submit.js.hbs +82 -0
  371. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/list.js.hbs +125 -0
  372. package/templates/skills/development/testing/ui-test/cli/run-ui-test/templates/permission-negative.js.hbs +65 -0
  373. package/templates/skills/development/testing/ui-test/cli/run-ui-test/types.ts +57 -0
  374. package/templates/skills/development/testing/ui-test/cli/run-ui-test/validate.ts +56 -0
  375. package/templates/skills/documentation/SKILL.md +168 -139
  376. package/templates/skills/documentation/cli/extract-doc/__tests__/forbidden.test.ts +136 -0
  377. package/templates/skills/documentation/cli/extract-doc/__tests__/overflow.test.ts +76 -0
  378. package/templates/skills/documentation/cli/extract-doc/__tests__/required.test.ts +147 -0
  379. package/templates/skills/documentation/cli/extract-doc/extract.ts +657 -0
  380. package/templates/skills/documentation/cli/extract-doc/index.ts +102 -0
  381. package/templates/skills/documentation/cli/extract-doc/types.ts +133 -0
  382. package/templates/skills/documentation/cli/extract-doc/validate.ts +35 -0
  383. package/templates/skills/documentation/cli/scaffold-doc/generate.ts +198 -0
  384. package/templates/skills/documentation/cli/scaffold-doc/index.ts +61 -0
  385. package/templates/skills/documentation/cli/scaffold-doc/types.ts +72 -0
  386. package/templates/skills/documentation/cli/scaffold-doc/validate.ts +33 -0
  387. package/templates/skills/documentation/data-schema.md +18 -38
  388. package/templates/skills/documentation/steps/step-01-scan.md +59 -113
  389. package/templates/skills/documentation/steps/step-02-generate.md +158 -231
  390. package/templates/skills/documentation/steps/step-03-validate.md +101 -280
  391. package/templates/skills/documentation/templates.md +403 -92
  392. package/templates/skills/efcore/SKILL.md +88 -308
  393. package/templates/skills/efcore/_shared.md +140 -0
  394. package/templates/skills/efcore/agents/create.md +69 -0
  395. package/templates/skills/efcore/agents/db-update.md +58 -0
  396. package/templates/skills/efcore/agents/list.md +35 -0
  397. package/templates/skills/efcore/agents/rebase-snapshot.md +50 -0
  398. package/templates/skills/efcore/agents/recreate-db.md +78 -0
  399. package/templates/skills/efcore/agents/squash.md +78 -0
  400. package/templates/skills/efcore/agents/status.md +35 -0
  401. package/templates/skills/efcore/cli/create/execute.ts +164 -0
  402. package/templates/skills/efcore/cli/create/index.ts +51 -0
  403. package/templates/skills/efcore/cli/create/types.ts +35 -0
  404. package/templates/skills/efcore/cli/create/validate.ts +29 -0
  405. package/templates/skills/efcore/cli/lib/detect-dbcontexts.ts +195 -0
  406. package/templates/skills/efcore/cli/lib/ef-runner.ts +144 -0
  407. package/templates/skills/efcore/cli/lib/migration-name.ts +56 -0
  408. package/templates/skills/efcore/cli/lib/parse-csproj-version.ts +45 -0
  409. package/templates/skills/efcore/cli/list/execute.ts +83 -0
  410. package/templates/skills/efcore/cli/list/index.ts +60 -0
  411. package/templates/skills/efcore/cli/list/types.ts +46 -0
  412. package/templates/skills/efcore/cli/list/validate.ts +20 -0
  413. package/templates/skills/efcore/cli/rebase-snapshot/execute.ts +21 -0
  414. package/templates/skills/efcore/cli/rebase-snapshot/index.ts +53 -0
  415. package/templates/skills/efcore/cli/rebase-snapshot/types.ts +20 -0
  416. package/templates/skills/efcore/cli/rebase-snapshot/validate.ts +25 -0
  417. package/templates/skills/efcore/cli/squash/execute.ts +298 -0
  418. package/templates/skills/efcore/cli/squash/index.ts +57 -0
  419. package/templates/skills/efcore/cli/squash/types.ts +38 -0
  420. package/templates/skills/efcore/cli/squash/validate.ts +30 -0
  421. package/templates/skills/efcore/cli/status/execute.ts +152 -0
  422. package/templates/skills/efcore/cli/status/index.ts +45 -0
  423. package/templates/skills/efcore/cli/status/types.ts +32 -0
  424. package/templates/skills/efcore/cli/status/validate.ts +20 -0
  425. package/templates/skills/external/context7/SKILL.md +121 -0
  426. package/templates/skills/external/dev-browser/SKILL.md +134 -0
  427. package/templates/skills/gitflow/SKILL.md +139 -392
  428. package/templates/skills/gitflow/_shared.md +24 -620
  429. package/templates/skills/gitflow/agents/abort.md +47 -0
  430. package/templates/skills/gitflow/agents/cleanup.md +50 -0
  431. package/templates/skills/gitflow/agents/commit.md +41 -0
  432. package/templates/skills/gitflow/agents/finish.md +47 -0
  433. package/templates/skills/gitflow/agents/init.md +41 -0
  434. package/templates/skills/gitflow/agents/merge.md +44 -0
  435. package/templates/skills/gitflow/agents/pr.md +39 -0
  436. package/templates/skills/gitflow/agents/start.md +42 -0
  437. package/templates/skills/gitflow/agents/status.md +39 -0
  438. package/templates/skills/gitflow/agents/sync.md +38 -0
  439. package/templates/skills/gitflow/cli/abort/execute.ts +61 -0
  440. package/templates/skills/gitflow/cli/abort/index.ts +116 -0
  441. package/templates/skills/gitflow/cli/abort/types.ts +21 -0
  442. package/templates/skills/gitflow/cli/abort/validate.ts +38 -0
  443. package/templates/skills/gitflow/cli/cleanup/execute.ts +107 -0
  444. package/templates/skills/gitflow/cli/cleanup/index.ts +122 -0
  445. package/templates/skills/gitflow/cli/cleanup/types.ts +26 -0
  446. package/templates/skills/gitflow/cli/cleanup/validate.ts +33 -0
  447. package/templates/skills/gitflow/cli/commit/execute.ts +146 -0
  448. package/templates/skills/gitflow/cli/commit/index.ts +77 -0
  449. package/templates/skills/gitflow/cli/commit/types.ts +36 -0
  450. package/templates/skills/gitflow/cli/commit/validate.ts +38 -0
  451. package/templates/skills/gitflow/cli/finish/execute.ts +127 -0
  452. package/templates/skills/gitflow/cli/finish/index.ts +106 -0
  453. package/templates/skills/gitflow/cli/finish/types.ts +25 -0
  454. package/templates/skills/gitflow/cli/finish/validate.ts +44 -0
  455. package/templates/skills/gitflow/cli/generate-msg/execute.ts +51 -0
  456. package/templates/skills/gitflow/cli/generate-msg/index.ts +73 -0
  457. package/templates/skills/gitflow/cli/generate-msg/types.ts +37 -0
  458. package/templates/skills/gitflow/cli/generate-msg/validate.ts +42 -0
  459. package/templates/skills/gitflow/cli/init/execute.ts +186 -0
  460. package/templates/skills/gitflow/cli/init/index.ts +127 -0
  461. package/templates/skills/gitflow/cli/init/types.ts +63 -0
  462. package/templates/skills/gitflow/cli/init/validate.ts +56 -0
  463. package/templates/skills/gitflow/cli/lib/branch.ts +83 -0
  464. package/templates/skills/gitflow/cli/lib/config.ts +149 -0
  465. package/templates/skills/gitflow/cli/lib/efcore.ts +136 -0
  466. package/templates/skills/gitflow/cli/lib/git.ts +212 -0
  467. package/templates/skills/gitflow/cli/lib/output.ts +49 -0
  468. package/templates/skills/gitflow/cli/lib/paths.ts +54 -0
  469. package/templates/skills/gitflow/cli/lib/platform.ts +44 -0
  470. package/templates/skills/gitflow/cli/lib/provider.ts +147 -0
  471. package/templates/skills/gitflow/cli/lib/types.ts +189 -0
  472. package/templates/skills/gitflow/cli/lib/version.ts +152 -0
  473. package/templates/skills/gitflow/cli/lib/worktree.ts +170 -0
  474. package/templates/skills/gitflow/cli/merge/execute.ts +93 -0
  475. package/templates/skills/gitflow/cli/merge/index.ts +109 -0
  476. package/templates/skills/gitflow/cli/merge/types.ts +24 -0
  477. package/templates/skills/gitflow/cli/merge/validate.ts +33 -0
  478. package/templates/skills/gitflow/cli/pr/execute.ts +131 -0
  479. package/templates/skills/gitflow/cli/pr/index.ts +115 -0
  480. package/templates/skills/gitflow/cli/pr/types.ts +27 -0
  481. package/templates/skills/gitflow/cli/pr/validate.ts +27 -0
  482. package/templates/skills/gitflow/cli/start/execute.ts +98 -0
  483. package/templates/skills/gitflow/cli/start/index.ts +75 -0
  484. package/templates/skills/gitflow/cli/start/types.ts +26 -0
  485. package/templates/skills/gitflow/cli/start/validate.ts +47 -0
  486. package/templates/skills/gitflow/cli/status/execute.ts +251 -0
  487. package/templates/skills/gitflow/cli/status/index.ts +154 -0
  488. package/templates/skills/gitflow/cli/status/types.ts +75 -0
  489. package/templates/skills/gitflow/cli/status/validate.ts +38 -0
  490. package/templates/skills/gitflow/cli/sync/execute.ts +84 -0
  491. package/templates/skills/gitflow/cli/sync/index.ts +75 -0
  492. package/templates/skills/gitflow/cli/sync/types.ts +25 -0
  493. package/templates/skills/gitflow/cli/sync/validate.ts +34 -0
  494. package/templates/skills/gitflow/commit-message.md +46 -0
  495. package/templates/skills/init/SKILL.md +54 -0
  496. package/templates/skills/lib/__tests__/canonical-hash.test.ts +45 -0
  497. package/templates/skills/lib/__tests__/page-spec-actions.test.ts +232 -0
  498. package/templates/skills/lib/canonical-hash.ts +40 -0
  499. package/templates/skills/lib/detector.ts +243 -0
  500. package/templates/skills/lib/dotnet.ts +238 -0
  501. package/templates/skills/lib/frontend-fixers.ts +151 -0
  502. package/templates/skills/lib/fs.ts +238 -0
  503. package/templates/skills/lib/git.ts +134 -0
  504. package/templates/skills/lib/graph.ts +138 -0
  505. package/templates/skills/lib/navroute-parser.ts +51 -0
  506. package/templates/skills/lib/output.ts +117 -0
  507. package/templates/skills/lib/page-spec-actions.ts +350 -0
  508. package/templates/skills/lib/skill-slug.ts +121 -0
  509. package/templates/skills/lib/string-utils.ts +140 -0
  510. package/templates/skills/lib/template-loader.ts +115 -0
  511. package/templates/skills/lib/url-conventions.ts +151 -0
  512. package/templates/skills/package.json +5 -0
  513. package/templates/skills/quick-search/SKILL.md +99 -99
  514. package/templates/skills/review/SKILL.md +56 -0
  515. package/templates/skills/smoke-generation/SKILL.md +18 -16
  516. package/templates/skills/ui-components/SKILL.md +136 -457
  517. package/templates/skills/upgrade/SKILL.md +36 -0
  518. package/templates/skills/utils/SKILL.md +45 -44
  519. package/templates/skills/utils/subcommands/test-web-config.md +152 -152
  520. package/templates/skills/utils/subcommands/test-web.md +123 -123
  521. package/templates/skills/validate-feature/SKILL.md +102 -101
  522. package/templates/skills/validate-feature/references/api-smoke-tests.md +140 -140
  523. package/templates/skills/validate-feature/references/db-validation-checks.md +180 -180
  524. package/templates/skills/validate-feature/steps/step-00-dependencies.md +121 -121
  525. package/templates/skills/validate-feature/steps/step-01-compile.md +39 -39
  526. package/templates/skills/validate-feature/steps/step-02-unit-tests.md +45 -45
  527. package/templates/skills/validate-feature/steps/step-03-integration-tests.md +53 -53
  528. package/templates/skills/validate-feature/steps/step-04-api-smoke.md +94 -94
  529. package/templates/skills/validate-feature/steps/step-05-db-validation.md +149 -149
  530. package/templates/skills/validation/conventions/SKILL.md +193 -0
  531. package/templates/skills/validation/conventions/cli/validate-conventions/execute.ts +368 -0
  532. package/templates/skills/validation/conventions/cli/validate-conventions/index.ts +67 -0
  533. package/templates/skills/validation/conventions/cli/validate-conventions/types.ts +91 -0
  534. package/templates/skills/validation/conventions/cli/validate-conventions/validate.ts +36 -0
  535. package/templates/skills/validation/cross-validate/SKILL.md +83 -0
  536. package/templates/skills/validation/cross-validate/cli/execute.ts +576 -0
  537. package/templates/skills/validation/cross-validate/cli/index.ts +87 -0
  538. package/templates/skills/validation/cross-validate/cli/types.ts +85 -0
  539. package/templates/skills/validation/cross-validate/cli/validate.ts +54 -0
  540. package/templates/skills/validation/eslint/SKILL.md +65 -0
  541. package/templates/skills/validation/eslint/cli/execute.ts +413 -0
  542. package/templates/skills/validation/eslint/cli/index.ts +102 -0
  543. package/templates/skills/validation/eslint/cli/types.ts +48 -0
  544. package/templates/skills/validation/eslint/cli/validate.ts +43 -0
  545. package/templates/skills/validation/project-inventory/SKILL.md +166 -0
  546. package/templates/skills/validation/project-inventory/cli/project-inventory/execute.ts +397 -0
  547. package/templates/skills/validation/project-inventory/cli/project-inventory/index.ts +80 -0
  548. package/templates/skills/validation/project-inventory/cli/project-inventory/types.ts +89 -0
  549. package/templates/skills/validation/project-inventory/cli/project-inventory/validate.ts +35 -0
  550. package/templates/skills/validation/readiness-report/SKILL.md +109 -0
  551. package/templates/skills/validation/readiness-report/cli/execute.ts +236 -0
  552. package/templates/skills/validation/readiness-report/cli/index.ts +234 -0
  553. package/templates/skills/validation/readiness-report/cli/types.ts +61 -0
  554. package/templates/skills/validation/readiness-report/cli/validate.ts +54 -0
  555. package/templates/skills/validation/roslyn/SKILL.md +103 -0
  556. package/templates/skills/validation/roslyn/cli/execute.ts +616 -0
  557. package/templates/skills/validation/roslyn/cli/index.ts +86 -0
  558. package/templates/skills/validation/roslyn/cli/types.ts +50 -0
  559. package/templates/skills/validation/roslyn/cli/validate.ts +43 -0
  560. package/.documentation/apex.html +0 -649
  561. package/dist/mcp-entry.mjs +0 -68888
  562. package/dist/mcp-entry.mjs.map +0 -1
  563. package/scripts/extract-api-endpoints.ts +0 -325
  564. package/scripts/extract-business-rules.ts +0 -440
  565. package/scripts/generate-doc-with-mock-ui.ts +0 -804
  566. package/templates/agents/ba-reader.md +0 -386
  567. package/templates/agents/ba-writer.md +0 -810
  568. package/templates/agents/efcore/migration.md +0 -204
  569. package/templates/agents/efcore/rebase-snapshot.md +0 -202
  570. package/templates/agents/efcore/squash.md +0 -269
  571. package/templates/agents/gitflow/abort.md +0 -45
  572. package/templates/agents/gitflow/cleanup.md +0 -107
  573. package/templates/agents/gitflow/commit.md +0 -236
  574. package/templates/agents/gitflow/exec.md +0 -48
  575. package/templates/agents/gitflow/finish.md +0 -146
  576. package/templates/agents/gitflow/init-clone.md +0 -199
  577. package/templates/agents/gitflow/init-detect.md +0 -137
  578. package/templates/agents/gitflow/init-validate.md +0 -225
  579. package/templates/agents/gitflow/init.md +0 -509
  580. package/templates/agents/gitflow/merge.md +0 -145
  581. package/templates/agents/gitflow/plan.md +0 -42
  582. package/templates/agents/gitflow/pr.md +0 -191
  583. package/templates/agents/gitflow/review.md +0 -49
  584. package/templates/agents/gitflow/start.md +0 -147
  585. package/templates/agents/gitflow/status.md +0 -95
  586. package/templates/agents/mcp-healthcheck.md +0 -163
  587. package/templates/hooks/docs-drift-check.md +0 -96
  588. package/templates/hooks/ef-migration-check.md +0 -139
  589. package/templates/hooks/mcp-check.md +0 -64
  590. package/templates/hooks/ralph-mcp-logger.sh +0 -46
  591. package/templates/mcp-scaffolding/component.tsx.hbs +0 -318
  592. package/templates/mcp-scaffolding/controller.cs.hbs +0 -118
  593. package/templates/mcp-scaffolding/entity-extension.cs.hbs +0 -239
  594. package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +0 -117
  595. package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +0 -133
  596. package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +0 -261
  597. package/templates/mcp-scaffolding/service-extension.cs.hbs +0 -53
  598. package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +0 -436
  599. package/templates/mcp-scaffolding/tests/entity.test.cs.hbs +0 -239
  600. package/templates/mcp-scaffolding/tests/repository.test.cs.hbs +0 -441
  601. package/templates/mcp-scaffolding/tests/security.test.cs.hbs +0 -442
  602. package/templates/mcp-scaffolding/tests/service.test.cs.hbs +0 -402
  603. package/templates/mcp-scaffolding/tests/validator.test.cs.hbs +0 -428
  604. package/templates/skills/_resources/config-safety.md +0 -61
  605. package/templates/skills/_resources/context-digest-template.md +0 -53
  606. package/templates/skills/_resources/doc-context-cache.md +0 -60
  607. package/templates/skills/_resources/docs-manifest-schema.md +0 -155
  608. package/templates/skills/_resources/formatting-guide.md +0 -124
  609. package/templates/skills/_resources/mcp-validate-documentation-spec.md +0 -181
  610. package/templates/skills/_shared.md +0 -228
  611. package/templates/skills/admin/SKILL.md +0 -48
  612. package/templates/skills/ai-prompt/SKILL.md +0 -171
  613. package/templates/skills/ai-prompt/references/ai-agent-modes.md +0 -89
  614. package/templates/skills/ai-prompt/references/eval-framework.md +0 -129
  615. package/templates/skills/ai-prompt/steps/step-00-init.md +0 -47
  616. package/templates/skills/ai-prompt/steps/step-01-implementation.md +0 -122
  617. package/templates/skills/apex/SKILL.md +0 -234
  618. package/templates/skills/apex/_shared.md +0 -197
  619. package/templates/skills/apex/references/analysis-methods.md +0 -178
  620. package/templates/skills/apex/references/challenge-questions.md +0 -359
  621. package/templates/skills/apex/references/checks/architecture-checks.sh +0 -154
  622. package/templates/skills/apex/references/checks/backend-checks.sh +0 -208
  623. package/templates/skills/apex/references/checks/frontend-checks.sh +0 -560
  624. package/templates/skills/apex/references/checks/infrastructure-checks.sh +0 -292
  625. package/templates/skills/apex/references/checks/security-checks.sh +0 -153
  626. package/templates/skills/apex/references/checks/seed-checks.sh +0 -610
  627. package/templates/skills/apex/references/code-generation.md +0 -412
  628. package/templates/skills/apex/references/core-seed-data.md +0 -1502
  629. package/templates/skills/apex/references/domain-events-pattern.md +0 -45
  630. package/templates/skills/apex/references/entity-hooks-pattern.md +0 -68
  631. package/templates/skills/apex/references/error-classification.md +0 -168
  632. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +0 -91
  633. package/templates/skills/apex/references/licensing-enforcement.md +0 -52
  634. package/templates/skills/apex/references/parallel-execution.md +0 -187
  635. package/templates/skills/apex/references/person-extension-pattern.md +0 -619
  636. package/templates/skills/apex/references/post-checks.md +0 -162
  637. package/templates/skills/apex/references/smartstack-api.md +0 -591
  638. package/templates/skills/apex/references/smartstack-frontend-compliance.md +0 -616
  639. package/templates/skills/apex/references/smartstack-frontend.md +0 -442
  640. package/templates/skills/apex/references/smartstack-layers.md +0 -551
  641. package/templates/skills/apex/steps/step-00-init.md +0 -405
  642. package/templates/skills/apex/steps/step-01-analyze.md +0 -210
  643. package/templates/skills/apex/steps/step-02-plan.md +0 -303
  644. package/templates/skills/apex/steps/step-03-execute.md +0 -194
  645. package/templates/skills/apex/steps/step-03a-layer0-domain.md +0 -144
  646. package/templates/skills/apex/steps/step-03b-layer1-seed.md +0 -339
  647. package/templates/skills/apex/steps/step-03c-layer2-backend.md +0 -401
  648. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +0 -562
  649. package/templates/skills/apex/steps/step-03e-layer4-devdata.md +0 -46
  650. package/templates/skills/apex/steps/step-04-examine.md +0 -404
  651. package/templates/skills/apex/steps/step-05-deep-review.md +0 -140
  652. package/templates/skills/apex/steps/step-06-resolve.md +0 -120
  653. package/templates/skills/apex/steps/step-07-tests.md +0 -271
  654. package/templates/skills/apex/steps/step-08-run-tests.md +0 -135
  655. package/templates/skills/apex-verify/SKILL.md +0 -110
  656. package/templates/skills/apex-verify/references/audit-rules.md +0 -50
  657. package/templates/skills/apex-verify/steps/step-00-init.md +0 -119
  658. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +0 -96
  659. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +0 -127
  660. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +0 -119
  661. package/templates/skills/apex-verify/steps/step-04-route-audit.md +0 -98
  662. package/templates/skills/apex-verify/steps/step-05-report.md +0 -110
  663. package/templates/skills/application/SKILL.md +0 -241
  664. package/templates/skills/application/references/application-roles-template.md +0 -228
  665. package/templates/skills/application/references/backend-controller-hierarchy.md +0 -68
  666. package/templates/skills/application/references/backend-entity-seeding.md +0 -73
  667. package/templates/skills/application/references/backend-seeding-and-dto-output.md +0 -83
  668. package/templates/skills/application/references/backend-table-prefix-mapping.md +0 -80
  669. package/templates/skills/application/references/backend-verification.md +0 -88
  670. package/templates/skills/application/references/contexts-cheatsheet.md +0 -86
  671. package/templates/skills/application/references/extensions-system.md +0 -158
  672. package/templates/skills/application/references/frontend-i18n-and-output.md +0 -67
  673. package/templates/skills/application/references/frontend-route-naming.md +0 -123
  674. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +0 -91
  675. package/templates/skills/application/references/frontend-verification.md +0 -158
  676. package/templates/skills/application/references/init-parameter-detection.md +0 -121
  677. package/templates/skills/application/references/migration-checklist-troubleshooting.md +0 -88
  678. package/templates/skills/application/references/nav-fallback-procedure.md +0 -198
  679. package/templates/skills/application/references/provider-template.md +0 -191
  680. package/templates/skills/application/references/roles-client-project-handling.md +0 -55
  681. package/templates/skills/application/references/roles-fallback-procedure.md +0 -144
  682. package/templates/skills/application/references/smartstack-provider.md +0 -118
  683. package/templates/skills/application/references/test-coverage-requirements.md +0 -213
  684. package/templates/skills/application/references/test-frontend.md +0 -73
  685. package/templates/skills/application/references/test-prerequisites.md +0 -72
  686. package/templates/skills/application/references/themes-db-driven.md +0 -484
  687. package/templates/skills/application/steps/step-00-init.md +0 -130
  688. package/templates/skills/application/steps/step-01-navigation.md +0 -170
  689. package/templates/skills/application/steps/step-02-permissions.md +0 -196
  690. package/templates/skills/application/steps/step-03-roles.md +0 -182
  691. package/templates/skills/application/steps/step-03b-provider.md +0 -134
  692. package/templates/skills/application/steps/step-04-backend.md +0 -174
  693. package/templates/skills/application/steps/step-05-frontend.md +0 -189
  694. package/templates/skills/application/steps/step-06-migration.md +0 -189
  695. package/templates/skills/application/steps/step-07-tests.md +0 -356
  696. package/templates/skills/application/steps/step-08-documentation.md +0 -137
  697. package/templates/skills/application/templates-backend.md +0 -463
  698. package/templates/skills/application/templates-frontend.md +0 -950
  699. package/templates/skills/application/templates-i18n.md +0 -520
  700. package/templates/skills/application/templates-seed.md +0 -1110
  701. package/templates/skills/audit-route/SKILL.md +0 -107
  702. package/templates/skills/audit-route/references/routing-pattern.md +0 -131
  703. package/templates/skills/audit-route/steps/step-00-init.md +0 -128
  704. package/templates/skills/audit-route/steps/step-01-inventory.md +0 -157
  705. package/templates/skills/audit-route/steps/step-02-conformity.md +0 -193
  706. package/templates/skills/audit-route/steps/step-03-report.md +0 -201
  707. package/templates/skills/business-analyse/SKILL.md +0 -252
  708. package/templates/skills/business-analyse/_shared.md +0 -276
  709. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +0 -560
  710. package/templates/skills/business-analyse/questionnaire/01-context.md +0 -43
  711. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +0 -111
  712. package/templates/skills/business-analyse/questionnaire/03-data-ui.md +0 -125
  713. package/templates/skills/business-analyse/questionnaire/04-risks-metrics.md +0 -6
  714. package/templates/skills/business-analyse/questionnaire/05-cross-module.md +0 -69
  715. package/templates/skills/business-analyse/questionnaire.md +0 -156
  716. package/templates/skills/business-analyse/react/application-viewer.md +0 -242
  717. package/templates/skills/business-analyse/react/components.md +0 -532
  718. package/templates/skills/business-analyse/react/i18n-template.md +0 -306
  719. package/templates/skills/business-analyse/react/schema.md +0 -831
  720. package/templates/skills/business-analyse/references/03-json-schemas.md +0 -221
  721. package/templates/skills/business-analyse/references/03-post-check-validation.md +0 -208
  722. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +0 -32
  723. package/templates/skills/business-analyse/references/04-cross-module-validation.md +0 -95
  724. package/templates/skills/business-analyse/references/04-file-allocation.md +0 -162
  725. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +0 -174
  726. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +0 -118
  727. package/templates/skills/business-analyse/references/acceptance-criteria.md +0 -164
  728. package/templates/skills/business-analyse/references/analysis-semantic-checks.md +0 -190
  729. package/templates/skills/business-analyse/references/canonical-json-formats.md +0 -204
  730. package/templates/skills/business-analyse/references/compilation-structure-cards.md +0 -297
  731. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +0 -124
  732. package/templates/skills/business-analyse/references/detection-strategies.md +0 -424
  733. package/templates/skills/business-analyse/references/domain-research-playbook.md +0 -234
  734. package/templates/skills/business-analyse/references/entity-architecture-decision.md +0 -240
  735. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +0 -166
  736. package/templates/skills/business-analyse/references/init-resume-logic.md +0 -70
  737. package/templates/skills/business-analyse/references/init-schema-deployment.md +0 -65
  738. package/templates/skills/business-analyse/references/module-completeness-challenge.md +0 -174
  739. package/templates/skills/business-analyse/references/multi-app-detection.md +0 -149
  740. package/templates/skills/business-analyse/references/naming-conventions.md +0 -253
  741. package/templates/skills/business-analyse/references/portal-classification.md +0 -52
  742. package/templates/skills/business-analyse/references/robustness-checks.md +0 -426
  743. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +0 -85
  744. package/templates/skills/business-analyse/references/ui-resource-cards.md +0 -259
  745. package/templates/skills/business-analyse/references/validation-checklist.md +0 -378
  746. package/templates/skills/business-analyse/schemas/application-schema.json +0 -481
  747. package/templates/skills/business-analyse/schemas/feature-schema.json +0 -53
  748. package/templates/skills/business-analyse/schemas/index-schema.json +0 -47
  749. package/templates/skills/business-analyse/schemas/project-schema.json +0 -481
  750. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +0 -245
  751. package/templates/skills/business-analyse/schemas/sections/discovery-schema.json +0 -80
  752. package/templates/skills/business-analyse/schemas/sections/handoff-schema.json +0 -82
  753. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +0 -70
  754. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +0 -567
  755. package/templates/skills/business-analyse/schemas/sections/validation-schema.json +0 -93
  756. package/templates/skills/business-analyse/schemas/shared/common-defs.json +0 -227
  757. package/templates/skills/business-analyse/steps/step-00-init.md +0 -463
  758. package/templates/skills/business-analyse/steps/step-01-cadrage.md +0 -901
  759. package/templates/skills/business-analyse/steps/step-02-structure.md +0 -315
  760. package/templates/skills/business-analyse/steps/step-03-specify.md +0 -986
  761. package/templates/skills/business-analyse/steps/step-04-consolidate.md +0 -928
  762. package/templates/skills/business-analyse/templates/tpl-frd.md +0 -168
  763. package/templates/skills/business-analyse/templates/tpl-handoff.md +0 -189
  764. package/templates/skills/business-analyse/templates/tpl-launch-displays.md +0 -59
  765. package/templates/skills/business-analyse/templates-frd.md +0 -476
  766. package/templates/skills/business-analyse/templates-react.md +0 -574
  767. package/templates/skills/business-analyse-design/SKILL.md +0 -101
  768. package/templates/skills/business-analyse-design/references/screens-post-check.md +0 -221
  769. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +0 -138
  770. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +0 -225
  771. package/templates/skills/business-analyse-design/references/spec-auto-inference.md +0 -117
  772. package/templates/skills/business-analyse-design/steps/step-01-screens.md +0 -108
  773. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +0 -155
  774. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +0 -189
  775. package/templates/skills/business-analyse-develop/SKILL.md +0 -250
  776. package/templates/skills/business-analyse-develop/references/category-completeness.md +0 -326
  777. package/templates/skills/business-analyse-develop/references/category-rules.md +0 -109
  778. package/templates/skills/business-analyse-develop/references/compact-loop.md +0 -478
  779. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +0 -132
  780. package/templates/skills/business-analyse-develop/references/init-resume-recovery.md +0 -183
  781. package/templates/skills/business-analyse-develop/references/module-transition.md +0 -246
  782. package/templates/skills/business-analyse-develop/references/multi-module-queue.md +0 -171
  783. package/templates/skills/business-analyse-develop/references/parallel-execution.md +0 -246
  784. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +0 -326
  785. package/templates/skills/business-analyse-develop/references/quality-gates.md +0 -160
  786. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +0 -140
  787. package/templates/skills/business-analyse-develop/references/report-template.md +0 -142
  788. package/templates/skills/business-analyse-develop/references/section-splitting.md +0 -439
  789. package/templates/skills/business-analyse-develop/references/task-transform-legacy.md +0 -256
  790. package/templates/skills/business-analyse-develop/references/team-orchestration.md +0 -547
  791. package/templates/skills/business-analyse-develop/steps/step-00-init.md +0 -242
  792. package/templates/skills/business-analyse-develop/steps/step-01-task.md +0 -182
  793. package/templates/skills/business-analyse-develop/steps/step-01-v4-execute.md +0 -139
  794. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +0 -216
  795. package/templates/skills/business-analyse-develop/steps/step-02-v4-verify.md +0 -176
  796. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +0 -107
  797. package/templates/skills/business-analyse-develop/steps/step-04-check.md +0 -419
  798. package/templates/skills/business-analyse-develop/steps/step-05-report.md +0 -137
  799. package/templates/skills/business-analyse-handoff/SKILL.md +0 -101
  800. package/templates/skills/business-analyse-handoff/references/acceptance-criteria.md +0 -318
  801. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +0 -211
  802. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +0 -47
  803. package/templates/skills/business-analyse-handoff/references/entity-canonicalization.md +0 -158
  804. package/templates/skills/business-analyse-handoff/references/entity-domain-mapping.md +0 -115
  805. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +0 -49
  806. package/templates/skills/business-analyse-handoff/references/handoff-file-templates.md +0 -197
  807. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +0 -142
  808. package/templates/skills/business-analyse-handoff/references/handoff-mappings.md +0 -108
  809. package/templates/skills/business-analyse-handoff/references/handoff-seeddata-generation.md +0 -314
  810. package/templates/skills/business-analyse-handoff/references/prd-generation.md +0 -362
  811. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +0 -125
  812. package/templates/skills/business-analyse-handoff/references/project-index-update.md +0 -98
  813. package/templates/skills/business-analyse-handoff/references/readiness-scoring.md +0 -95
  814. package/templates/skills/business-analyse-handoff/schemas/handoff-schema.json +0 -95
  815. package/templates/skills/business-analyse-handoff/steps/step-00-validate.md +0 -158
  816. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +0 -85
  817. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +0 -169
  818. package/templates/skills/business-analyse-handoff/templates/tpl-progress.md +0 -172
  819. package/templates/skills/business-analyse-html/SKILL.md +0 -89
  820. package/templates/skills/business-analyse-html/html/ba-interactive.html +0 -6134
  821. package/templates/skills/business-analyse-html/html/build-html.js +0 -137
  822. package/templates/skills/business-analyse-html/html/src/partials/cadrage-context.html +0 -34
  823. package/templates/skills/business-analyse-html/html/src/partials/cadrage-scope.html +0 -27
  824. package/templates/skills/business-analyse-html/html/src/partials/cadrage-stakeholders.html +0 -55
  825. package/templates/skills/business-analyse-html/html/src/partials/cadrage-success.html +0 -34
  826. package/templates/skills/business-analyse-html/html/src/partials/consol-datamodel.html +0 -8
  827. package/templates/skills/business-analyse-html/html/src/partials/consol-flows.html +0 -29
  828. package/templates/skills/business-analyse-html/html/src/partials/consol-interactions.html +0 -8
  829. package/templates/skills/business-analyse-html/html/src/partials/consol-permissions.html +0 -8
  830. package/templates/skills/business-analyse-html/html/src/partials/decomp-dependencies.html +0 -38
  831. package/templates/skills/business-analyse-html/html/src/partials/decomp-modules.html +0 -43
  832. package/templates/skills/business-analyse-html/html/src/partials/handoff-summary.html +0 -24
  833. package/templates/skills/business-analyse-html/html/src/partials/module-spec-container.html +0 -4
  834. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +0 -313
  835. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +0 -316
  836. package/templates/skills/business-analyse-html/html/src/scripts/03-render-cadrage.js +0 -162
  837. package/templates/skills/business-analyse-html/html/src/scripts/04-render-modules.js +0 -203
  838. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +0 -866
  839. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +0 -196
  840. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +0 -564
  841. package/templates/skills/business-analyse-html/html/src/scripts/07-render-handoff.js +0 -108
  842. package/templates/skills/business-analyse-html/html/src/scripts/08-editing.js +0 -137
  843. package/templates/skills/business-analyse-html/html/src/scripts/09-export.js +0 -138
  844. package/templates/skills/business-analyse-html/html/src/scripts/10-comments.js +0 -221
  845. package/templates/skills/business-analyse-html/html/src/scripts/11-review-panel.js +0 -167
  846. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +0 -162
  847. package/templates/skills/business-analyse-html/html/src/styles/01-variables.css +0 -38
  848. package/templates/skills/business-analyse-html/html/src/styles/02-layout.css +0 -216
  849. package/templates/skills/business-analyse-html/html/src/styles/03-navigation.css +0 -120
  850. package/templates/skills/business-analyse-html/html/src/styles/04-cards.css +0 -194
  851. package/templates/skills/business-analyse-html/html/src/styles/05-modules.css +0 -518
  852. package/templates/skills/business-analyse-html/html/src/styles/06-wireframes.css +0 -263
  853. package/templates/skills/business-analyse-html/html/src/styles/07-comments.css +0 -184
  854. package/templates/skills/business-analyse-html/html/src/styles/08-review-panel.css +0 -241
  855. package/templates/skills/business-analyse-html/html/src/styles/09-mockups-html.css +0 -220
  856. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +0 -73
  857. package/templates/skills/business-analyse-html/html/src/template.html +0 -449
  858. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +0 -144
  859. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +0 -143
  860. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +0 -442
  861. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +0 -139
  862. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +0 -283
  863. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +0 -199
  864. package/templates/skills/business-analyse-html/references/data-build.md +0 -215
  865. package/templates/skills/business-analyse-html/references/data-mapping.md +0 -452
  866. package/templates/skills/business-analyse-html/references/output-modes.md +0 -119
  867. package/templates/skills/business-analyse-html/references/wireframe-svg-style-guide.md +0 -335
  868. package/templates/skills/business-analyse-html/steps/step-01-collect.md +0 -140
  869. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +0 -76
  870. package/templates/skills/business-analyse-html/steps/step-03-render.md +0 -95
  871. package/templates/skills/business-analyse-html/steps/step-04-verify.md +0 -224
  872. package/templates/skills/business-analyse-quick/SKILL.md +0 -807
  873. package/templates/skills/business-analyse-quick/references/domain-heuristics.md +0 -172
  874. package/templates/skills/business-analyse-quick/references/prd-schema.md +0 -268
  875. package/templates/skills/business-analyse-review/SKILL.md +0 -71
  876. package/templates/skills/business-analyse-review/references/review-data-mapping.md +0 -367
  877. package/templates/skills/business-analyse-review/steps/step-00-init.md +0 -111
  878. package/templates/skills/business-analyse-review/steps/step-01-apply.md +0 -304
  879. package/templates/skills/business-analyse-status/SKILL.md +0 -136
  880. package/templates/skills/cc-agent/SKILL.md +0 -129
  881. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +0 -95
  882. package/templates/skills/cc-agent/references/agent-frontmatter.md +0 -213
  883. package/templates/skills/cc-agent/references/permission-modes.md +0 -102
  884. package/templates/skills/cc-agent/references/tools-reference.md +0 -144
  885. package/templates/skills/cc-agent/steps/step-00-init.md +0 -134
  886. package/templates/skills/cc-agent/steps/step-01-design.md +0 -186
  887. package/templates/skills/cc-agent/steps/step-02-generate.md +0 -131
  888. package/templates/skills/cc-agent/steps/step-03-validate.md +0 -130
  889. package/templates/skills/cc-agent/templates/agent-categorized.md +0 -67
  890. package/templates/skills/cc-agent/templates/agent-standalone.md +0 -56
  891. package/templates/skills/cc-agent/templates/agent-with-skills.md +0 -94
  892. package/templates/skills/cc-audit/SKILL.md +0 -108
  893. package/templates/skills/cc-audit/references/agent-checklist.md +0 -91
  894. package/templates/skills/cc-audit/references/hook-checklist.md +0 -110
  895. package/templates/skills/cc-audit/references/skill-checklist.md +0 -70
  896. package/templates/skills/cc-audit/steps/step-00-init.md +0 -98
  897. package/templates/skills/cc-audit/steps/step-01-scan.md +0 -142
  898. package/templates/skills/cc-audit/steps/step-02-analyze.md +0 -158
  899. package/templates/skills/cc-audit/steps/step-03-report.md +0 -142
  900. package/templates/skills/cc-skill/SKILL.md +0 -134
  901. package/templates/skills/cc-skill/references/best-practices.md +0 -167
  902. package/templates/skills/cc-skill/references/frontmatter-reference.md +0 -182
  903. package/templates/skills/cc-skill/references/skill-patterns.md +0 -199
  904. package/templates/skills/cc-skill/steps/step-00-init.md +0 -119
  905. package/templates/skills/cc-skill/steps/step-01-design.md +0 -199
  906. package/templates/skills/cc-skill/steps/step-02-generate.md +0 -145
  907. package/templates/skills/cc-skill/steps/step-03-steps.md +0 -151
  908. package/templates/skills/cc-skill/steps/step-04-validate.md +0 -124
  909. package/templates/skills/cc-skill/templates/skill-forked.md +0 -85
  910. package/templates/skills/cc-skill/templates/skill-progressive.md +0 -102
  911. package/templates/skills/cc-skill/templates/skill-simple.md +0 -75
  912. package/templates/skills/cc-skill/templates/step-template.md +0 -82
  913. package/templates/skills/controller/SKILL.md +0 -162
  914. package/templates/skills/controller/postman-templates.md +0 -614
  915. package/templates/skills/controller/references/controller-code-templates.md +0 -162
  916. package/templates/skills/controller/references/mcp-scaffold-workflow.md +0 -237
  917. package/templates/skills/controller/references/permission-sync-templates.md +0 -149
  918. package/templates/skills/controller/steps/step-00-init.md +0 -193
  919. package/templates/skills/controller/steps/step-01-analyze.md +0 -152
  920. package/templates/skills/controller/steps/step-02-plan.md +0 -176
  921. package/templates/skills/controller/steps/step-03-generate.md +0 -189
  922. package/templates/skills/controller/steps/step-04-perms.md +0 -80
  923. package/templates/skills/controller/steps/step-05-validate.md +0 -107
  924. package/templates/skills/controller/templates.md +0 -1556
  925. package/templates/skills/debug/SKILL.md +0 -70
  926. package/templates/skills/debug/references/team-protocol.md +0 -232
  927. package/templates/skills/debug/steps/step-00-init.md +0 -57
  928. package/templates/skills/debug/steps/step-01-analyze.md +0 -219
  929. package/templates/skills/debug/steps/step-02-resolve.md +0 -85
  930. package/templates/skills/efcore/references/database-operations.md +0 -66
  931. package/templates/skills/efcore/references/reset-operations.md +0 -81
  932. package/templates/skills/efcore/references/seed-methods.md +0 -86
  933. package/templates/skills/efcore/references/shared-init-functions.md +0 -250
  934. package/templates/skills/efcore/references/sql-objects-injection.md +0 -19
  935. package/templates/skills/efcore/references/troubleshooting.md +0 -81
  936. package/templates/skills/efcore/references/zero-downtime-patterns.md +0 -229
  937. package/templates/skills/explore/SKILL.md +0 -98
  938. package/templates/skills/feature-full/SKILL.md +0 -111
  939. package/templates/skills/feature-full/steps/step-00-init.md +0 -57
  940. package/templates/skills/feature-full/steps/step-01-implementation.md +0 -133
  941. package/templates/skills/gitflow/phases/abort.md +0 -189
  942. package/templates/skills/gitflow/phases/cleanup.md +0 -264
  943. package/templates/skills/gitflow/phases/status.md +0 -192
  944. package/templates/skills/gitflow/references/commit-message-generation.md +0 -58
  945. package/templates/skills/gitflow/references/commit-migration-validation.md +0 -53
  946. package/templates/skills/gitflow/references/finish-cleanup.md +0 -55
  947. package/templates/skills/gitflow/references/finish-version-bumping.md +0 -45
  948. package/templates/skills/gitflow/references/init-config-template.md +0 -143
  949. package/templates/skills/gitflow/references/init-environment-detection.md +0 -41
  950. package/templates/skills/gitflow/references/init-name-normalization.md +0 -118
  951. package/templates/skills/gitflow/references/init-questions.md +0 -193
  952. package/templates/skills/gitflow/references/init-structure-creation.md +0 -75
  953. package/templates/skills/gitflow/references/init-version-detection.md +0 -23
  954. package/templates/skills/gitflow/references/init-workspace-detection.md +0 -43
  955. package/templates/skills/gitflow/references/merge-ci-status.md +0 -36
  956. package/templates/skills/gitflow/references/merge-execution.md +0 -62
  957. package/templates/skills/gitflow/references/merge-pr-context.md +0 -76
  958. package/templates/skills/gitflow/references/plan-template.md +0 -69
  959. package/templates/skills/gitflow/references/pr-build-checks.md +0 -60
  960. package/templates/skills/gitflow/references/pr-generation.md +0 -58
  961. package/templates/skills/gitflow/references/start-branch-normalization.md +0 -28
  962. package/templates/skills/gitflow/references/start-efcore-preflight.md +0 -70
  963. package/templates/skills/gitflow/references/start-local-config.md +0 -113
  964. package/templates/skills/gitflow/references/start-worktree-creation.md +0 -50
  965. package/templates/skills/gitflow/references/sync-push-verify.md +0 -44
  966. package/templates/skills/gitflow/references/sync-rebase-conflicts.md +0 -38
  967. package/templates/skills/gitflow/steps/step-commit.md +0 -199
  968. package/templates/skills/gitflow/steps/step-finish.md +0 -147
  969. package/templates/skills/gitflow/steps/step-init.md +0 -230
  970. package/templates/skills/gitflow/steps/step-merge.md +0 -85
  971. package/templates/skills/gitflow/steps/step-plan.md +0 -151
  972. package/templates/skills/gitflow/steps/step-pr.md +0 -247
  973. package/templates/skills/gitflow/steps/step-start.md +0 -195
  974. package/templates/skills/gitflow/steps/step-sync.md +0 -161
  975. package/templates/skills/gitflow/templates/config.json +0 -72
  976. package/templates/skills/mcp/SKILL.md +0 -62
  977. package/templates/skills/mcp/steps/step-01-healthcheck.md +0 -108
  978. package/templates/skills/mcp/steps/step-02-tools.md +0 -73
  979. package/templates/skills/migrate/SKILL.md +0 -312
  980. package/templates/skills/migrate/references/v3.34-to-v3.46.md +0 -289
  981. package/templates/skills/notification/SKILL.md +0 -173
  982. package/templates/skills/refactor/SKILL.md +0 -56
  983. package/templates/skills/refactor/steps/step-01-discover.md +0 -60
  984. package/templates/skills/refactor/steps/step-02-execute.md +0 -67
  985. package/templates/skills/review-code/SKILL.md +0 -95
  986. package/templates/skills/review-code/references/clean-code-principles.md +0 -292
  987. package/templates/skills/review-code/references/code-quality-metrics.md +0 -174
  988. package/templates/skills/review-code/references/feedback-patterns.md +0 -149
  989. package/templates/skills/review-code/references/owasp-api-top10.md +0 -243
  990. package/templates/skills/review-code/references/security-checklist.md +0 -212
  991. package/templates/skills/review-code/references/smartstack-conventions.md +0 -568
  992. package/templates/skills/review-code/steps/step-01-smartstack.md +0 -96
  993. package/templates/skills/review-code/steps/step-02-detailed-review.md +0 -80
  994. package/templates/skills/review-code/steps/step-03-react.md +0 -44
  995. package/templates/skills/sketch/SKILL.md +0 -34
  996. package/templates/skills/ui-components/accessibility.md +0 -170
  997. package/templates/skills/ui-components/patterns/dashboard-chart.md +0 -327
  998. package/templates/skills/ui-components/patterns/data-table.md +0 -175
  999. package/templates/skills/ui-components/patterns/entity-card.md +0 -77
  1000. package/templates/skills/ui-components/patterns/grid-layout.md +0 -91
  1001. package/templates/skills/ui-components/patterns/kanban.md +0 -43
  1002. package/templates/skills/ui-components/references/component-catalog.md +0 -82
  1003. package/templates/skills/ui-components/responsive-guidelines.md +0 -278
  1004. package/templates/skills/ui-components/style-guide.md +0 -113
  1005. package/templates/skills/validate/SKILL.md +0 -181
  1006. package/templates/skills/workflow/SKILL.md +0 -196
  1007. package/templates/skills/workflow/steps/step-00-init.md +0 -57
  1008. package/templates/skills/workflow/steps/step-01-implementation.md +0 -84
@@ -0,0 +1,1237 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { generate } from '../generate.js'
3
+ import type { ScaffoldComponentInput, PageSpecMin } from '../types.js'
4
+
5
+ function fixture(overrides: Partial<ScaffoldComponentInput> = {}): ScaffoldComponentInput {
6
+ return {
7
+ module: 'crm',
8
+ appCode: 'TestV2',
9
+ entity: 'Contact',
10
+ section: 'directory',
11
+ views: ['list', 'detail', 'form'],
12
+ fields: [
13
+ { name: 'firstName', type: 'string', required: true },
14
+ { name: 'lastName', type: 'string', required: true },
15
+ { name: 'email', type: 'string', required: false },
16
+ ],
17
+ projectPath: '/web',
18
+ ...overrides,
19
+ }
20
+ }
21
+
22
+ describe('scaffold-component / generate', () => {
23
+ it('emits no local usePermissions stub anywhere in any page', () => {
24
+ const files = generate(fixture())
25
+ for (const file of files.filter((f) => f.path.endsWith('Page.tsx'))) {
26
+ expect(file.content).not.toMatch(/function\s+usePermissions\s*\(/)
27
+ expect(file.content).not.toMatch(/const\s+usePermissions\s*=/)
28
+ }
29
+ })
30
+
31
+ it('every page imports PageTemplate from @/components/ui/PageTemplate', () => {
32
+ const files = generate(fixture())
33
+ const pages = files.filter((f) => f.path.endsWith('Page.tsx'))
34
+ expect(pages.length).toBeGreaterThan(0)
35
+ for (const page of pages) {
36
+ expect(page.content).toMatch(/import\s*\{\s*PageTemplate\s*\}\s*from\s*['"]@\/components\/ui\/PageTemplate['"]/)
37
+ }
38
+ })
39
+
40
+ it('every permission="…" literal matches {module}.{section}.{action} (3 lowercase segments, no appCode prefix)', () => {
41
+ const files = generate(fixture())
42
+ const permRe = /permission\s*=\s*['"]([^'"]+)['"]/g
43
+ const validShape = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/
44
+ let matched = 0
45
+ for (const page of files.filter((f) => f.path.endsWith('Page.tsx'))) {
46
+ let m: RegExpExecArray | null
47
+ while ((m = permRe.exec(page.content)) !== null) {
48
+ matched++
49
+ expect(m[1]).toMatch(validShape)
50
+ expect(m[1].split('.').length).toBe(3)
51
+ expect(m[1].startsWith('crm.')).toBe(true)
52
+ }
53
+ }
54
+ expect(matched).toBeGreaterThan(0)
55
+ })
56
+
57
+ it('list / detail / form pages wrap content in <PageTemplate>', () => {
58
+ const files = generate(fixture())
59
+ // List uses plural ('ContactsListPage'); detail/form stay singular
60
+ // (matches the SmartStack project convention).
61
+ const expectedNames: Record<string, string> = {
62
+ List: 'ContactsListPage.tsx',
63
+ Detail: 'ContactDetailPage.tsx',
64
+ Form: 'ContactFormPage.tsx',
65
+ }
66
+ for (const [, fileName] of Object.entries(expectedNames)) {
67
+ const page = files.find((f) => f.path.endsWith(fileName))!
68
+ expect(page).toBeDefined()
69
+ expect(page.content).toMatch(/<PageTemplate\b/)
70
+ }
71
+ })
72
+
73
+ it('preserves PermissionGuard from @atlashub/smartstack on mutating actions', () => {
74
+ const files = generate(fixture())
75
+ const list = files.find((f) => f.path.endsWith('ContactsListPage.tsx'))!
76
+ expect(list.content).toMatch(/from\s+['"]@atlashub\/smartstack['"]/)
77
+ // Create button is wrapped
78
+ expect(list.content).toMatch(/<PermissionGuard\s+permission=['"]crm\.directory\.create['"]/)
79
+ })
80
+
81
+ it('generates hub pages (app-home / module-home / section-home) with PageTemplate + tokens', () => {
82
+ const files = generate(fixture({ views: ['app-home', 'module-home', 'section-home'] }))
83
+ const expectedFiles = ['ContactsAppHomePage.tsx', 'ContactsModuleHomePage.tsx', 'ContactsSectionHomePage.tsx']
84
+ for (const filename of expectedFiles) {
85
+ const page = files.find((f) => f.path.endsWith(filename))!
86
+ expect(page, `missing ${filename}`).toBeDefined()
87
+ // Wraps content in <PageTemplate>
88
+ expect(page.content).toMatch(/<PageTemplate\b/)
89
+ // Uses CSS tokens (no raw Tailwind colors)
90
+ expect(page.content).not.toMatch(/bg-white\b/)
91
+ expect(page.content).not.toMatch(/bg-(red|blue|green|yellow|gray|slate|zinc)-\d/)
92
+ // Permission check is 4-seg with appCode prefix
93
+ expect(page.content).toMatch(/<PermissionGuard\s+permission=['"]crm\.directory\.read['"]/)
94
+ // useTranslation namespace is the module
95
+ expect(page.content).toMatch(/useTranslation\(['"]crm['"]\)/)
96
+ // No hand-written legacy imports
97
+ expect(page.content).not.toMatch(/from\s+['"]@\/hooks\//)
98
+ expect(page.content).not.toMatch(/from\s+['"]@\/services\//)
99
+ }
100
+ })
101
+
102
+ it('emits 4 locale files per entity (fr/en/it/de)', () => {
103
+ const files = generate(fixture())
104
+ const locales = files.filter((f) => /\/i18n\/locales\/(fr|en|it|de)\/crm\.json$/.test(f.path))
105
+ expect(locales).toHaveLength(4)
106
+ for (const loc of locales) {
107
+ const parsed = JSON.parse(loc.content)
108
+ expect(parsed).toHaveProperty('contact')
109
+ expect(parsed.contact).toHaveProperty('list')
110
+ expect(parsed.contact).toHaveProperty('detail')
111
+ expect(parsed.contact).toHaveProperty('form')
112
+ }
113
+ })
114
+
115
+ it('isolates i18n keys when two entities share the same module', () => {
116
+ const filesA = generate(fixture({ entity: 'Contact', module: 'crm' }))
117
+ const filesB = generate(fixture({
118
+ entity: 'Company',
119
+ module: 'crm',
120
+ section: 'companies',
121
+ fields: [
122
+ { name: 'name', type: 'string', required: true },
123
+ { name: 'industry', type: 'string', required: false },
124
+ ],
125
+ }))
126
+ const frA = JSON.parse(filesA.find((f) => /fr\/crm\.json$/.test(f.path))!.content)
127
+ const frB = JSON.parse(filesB.find((f) => /fr\/crm\.json$/.test(f.path))!.content)
128
+
129
+ // Each entity has its own top-level key — no collision
130
+ expect(frA).toHaveProperty('contact')
131
+ expect(frB).toHaveProperty('company')
132
+ expect(frA.contact).toHaveProperty('list')
133
+ expect(frA.contact).toHaveProperty('detail')
134
+ expect(frB.company).toHaveProperty('list')
135
+ expect(frB.company).toHaveProperty('detail')
136
+
137
+ // Titles are entity-specific
138
+ expect(frA.contact.list.title).not.toBe(frB.company.list.title)
139
+ })
140
+
141
+ it('prefixes all t() calls with entity key in generated pages', () => {
142
+ const files = generate(fixture())
143
+ const pages = files.filter((f) => f.path.endsWith('Page.tsx'))
144
+ for (const page of pages) {
145
+ // Every t('...') call must start with the entity prefix 'contact.'
146
+ const tCalls = page.content.match(/t\('([^']+)'\)/g) ?? []
147
+ for (const call of tCalls) {
148
+ const key = call.match(/t\('([^']+)'\)/)![1]
149
+ expect(key.startsWith('contact.')).toBe(true)
150
+ }
151
+ // No bare t('list.'), t('form.'), t('detail.'), t('breadcrumb.') etc.
152
+ expect(page.content).not.toMatch(/t\('(?:list|form|detail|dashboard|home|breadcrumb)\./)
153
+ }
154
+ })
155
+
156
+ it('converts kebab-case section to camelCase for routes.* property access', () => {
157
+ // Regression guard for the REFERENTIELS class of crashes: when the
158
+ // section is multi-word kebab (e.g. `types-affaire`), the generated
159
+ // `navigate(routes.types-affaire.detail(id))` is parsed by JS as
160
+ // `routes.types - affaire.detail(id)` and throws ReferenceError at the
161
+ // first interaction. The fix mirrors what scaffold-routes already does
162
+ // (`kebabToCamel(entity.section)`) so both CLIs agree on the property
163
+ // name: `routes.typesAffaire`.
164
+ const files = generate(fixture({ section: 'types-affaire' }))
165
+ const pages = files.filter(f => f.path.endsWith('Page.tsx'))
166
+ for (const page of pages) {
167
+ // Camel form MUST appear wherever routes.<section>.<view>() is referenced.
168
+ if (page.content.includes('routes.')) {
169
+ expect(page.content, page.path).toMatch(/routes\.typesAffaire\./)
170
+ expect(page.content, page.path).not.toMatch(/routes\.types-affaire\./)
171
+ }
172
+ }
173
+ })
174
+
175
+ it('leaves single-word sections unchanged (camel of `contacts` is `contacts`)', () => {
176
+ const files = generate(fixture()) // section: 'directory'
177
+ const pages = files.filter(f => f.path.endsWith('Page.tsx'))
178
+ for (const page of pages) {
179
+ if (page.content.includes('routes.')) {
180
+ expect(page.content, page.path).toMatch(/routes\.directory\./)
181
+ }
182
+ }
183
+ })
184
+ })
185
+
186
+ // ─── Enriched mode (Phase 3a, post-pageSpec) ────────────────────────────
187
+
188
+ function pageSpecFixture(overrides: Partial<PageSpecMin> = {}): PageSpecMin {
189
+ return {
190
+ screenCode: 'SCR-CONTACT-LIST',
191
+ module: 'crm',
192
+ appCode: 'testv2',
193
+ section: 'directory',
194
+ entity: 'Contact',
195
+ view: 'list',
196
+ filePath: 'src/pages/testv2/crm/directory/ContactListPage.tsx',
197
+ permission: 'crm.directory.read',
198
+ actions: [
199
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
200
+ ],
201
+ columns: [
202
+ { key: 'firstName', labelKey: 'list.columns.firstName', sortable: true },
203
+ { key: 'lastName', labelKey: 'list.columns.lastName', sortable: true },
204
+ ],
205
+ i18nKeys: {
206
+ fr: {
207
+ 'list.title': 'Contacts métier',
208
+ 'list.subtitle': 'Annuaire CRM personnalisé',
209
+ 'list.create': 'Nouveau contact',
210
+ 'list.search': 'Filtrer…',
211
+ 'list.loading': 'Chargement…',
212
+ 'list.empty': 'Aucun contact à afficher',
213
+ 'list.columns.firstName': 'Prénom',
214
+ 'list.columns.lastName': 'Nom',
215
+ },
216
+ en: {
217
+ 'list.title': 'Business contacts',
218
+ 'list.subtitle': 'CRM custom directory',
219
+ 'list.create': 'New contact',
220
+ 'list.search': 'Filter…',
221
+ 'list.loading': 'Loading…',
222
+ 'list.empty': 'No contact to show',
223
+ 'list.columns.firstName': 'First name',
224
+ 'list.columns.lastName': 'Last name',
225
+ },
226
+ it: {
227
+ 'list.title': 'Contatti business',
228
+ 'list.subtitle': 'Rubrica CRM',
229
+ 'list.create': 'Nuovo contatto',
230
+ 'list.search': 'Filtra…',
231
+ 'list.loading': 'Caricamento…',
232
+ 'list.empty': 'Nessun contatto',
233
+ 'list.columns.firstName': 'Nome',
234
+ 'list.columns.lastName': 'Cognome',
235
+ },
236
+ de: {
237
+ 'list.title': 'Geschäftskontakte',
238
+ 'list.subtitle': 'Benutzerdefiniertes CRM-Verzeichnis',
239
+ 'list.create': 'Neuer Kontakt',
240
+ 'list.search': 'Filtern…',
241
+ 'list.loading': 'Wird geladen…',
242
+ 'list.empty': 'Kein Kontakt anzuzeigen',
243
+ 'list.columns.firstName': 'Vorname',
244
+ 'list.columns.lastName': 'Nachname',
245
+ },
246
+ },
247
+ needsRefinement: false,
248
+ specHash: 'a'.repeat(64),
249
+ ...overrides,
250
+ }
251
+ }
252
+
253
+ describe('scaffold-component / generate — enriched mode', () => {
254
+ it('uses pageSpec.i18nKeys verbatim instead of buildTranslations()', () => {
255
+ const files = generate(fixture({ pageSpec: pageSpecFixture() }))
256
+ const fr = files.find((f) => /\/i18n\/locales\/fr\/crm\.json$/.test(f.path))!
257
+ expect(fr).toBeDefined()
258
+ const parsed = JSON.parse(fr.content)
259
+ // The pageSpec catalogue overrides the legacy auto-generated dictionary —
260
+ // these are project-specific values that buildTranslations() would never
261
+ // produce on its own (humanize 'firstName' → 'First name', not 'Prénom métier').
262
+ expect(parsed.contact.list.title).toBe('Contacts métier')
263
+ expect(parsed.contact.list.subtitle).toBe('Annuaire CRM personnalisé')
264
+ expect(parsed.contact.list.create).toBe('Nouveau contact')
265
+ expect(parsed.contact.list.empty).toBe('Aucun contact à afficher')
266
+ expect(parsed.contact.list.columns.firstName).toBe('Prénom')
267
+ expect(parsed.contact.list.columns.lastName).toBe('Nom')
268
+ })
269
+
270
+ it('normalizes PascalCase field keys from pageSpec.i18nKeys to camelCase', () => {
271
+ const ps = pageSpecFixture({
272
+ i18nKeys: {
273
+ fr: {
274
+ 'list.title': 'Contacts',
275
+ 'list.columns.FirstName': 'Prénom',
276
+ 'list.columns.LastName': 'Nom',
277
+ 'form.fields.FirstName': 'Prénom',
278
+ 'form.fields.EstActif': 'Actif',
279
+ 'detail.fields.Code': 'Code',
280
+ 'list.filters.SecteurId': 'Secteur',
281
+ },
282
+ en: { 'list.title': 'Contacts', 'list.columns.FirstName': 'First name', 'form.fields.FirstName': 'First name', 'form.fields.EstActif': 'Active', 'detail.fields.Code': 'Code', 'list.filters.SecteurId': 'Sector' },
283
+ it: { 'list.title': 'Contatti', 'list.columns.FirstName': 'Nome', 'form.fields.FirstName': 'Nome', 'form.fields.EstActif': 'Attivo', 'detail.fields.Code': 'Codice', 'list.filters.SecteurId': 'Settore' },
284
+ de: { 'list.title': 'Kontakte', 'list.columns.FirstName': 'Vorname', 'form.fields.FirstName': 'Vorname', 'form.fields.EstActif': 'Aktiv', 'detail.fields.Code': 'Code', 'list.filters.SecteurId': 'Sektor' },
285
+ },
286
+ })
287
+ const files = generate(fixture({ pageSpec: ps }))
288
+ const fr = files.find((f) => /\/i18n\/locales\/fr\/crm\.json$/.test(f.path))!
289
+ const parsed = JSON.parse(fr.content)
290
+ // PascalCase field keys normalized to camelCase (matching t() calls)
291
+ expect(parsed.contact.list.columns.firstName).toBe('Prénom')
292
+ expect(parsed.contact.list.columns.lastName).toBe('Nom')
293
+ expect(parsed.contact.form.fields.firstName).toBe('Prénom')
294
+ expect(parsed.contact.form.fields.estActif).toBe('Actif')
295
+ expect(parsed.contact.detail.fields.code).toBe('Code')
296
+ expect(parsed.contact.list.filters.secteurId).toBe('Secteur')
297
+ // Non-field keys stay as-is
298
+ expect(parsed.contact.list.title).toBe('Contacts')
299
+ // PascalCase originals should NOT exist
300
+ expect(parsed.contact.list.columns.FirstName).toBeUndefined()
301
+ expect(parsed.contact.form.fields.FirstName).toBeUndefined()
302
+ })
303
+
304
+ it('produces parallel locale catalogues from pageSpec.i18nKeys (4 locales nested)', () => {
305
+ const files = generate(fixture({ pageSpec: pageSpecFixture() }))
306
+ for (const locale of ['fr', 'en', 'it', 'de'] as const) {
307
+ const file = files.find((f) => f.path === `web/TestV2-web/src/i18n/locales/${locale}/crm.json`)!
308
+ expect(file, `missing locale ${locale}`).toBeDefined()
309
+ const parsed = JSON.parse(file.content)
310
+ expect(parsed.contact).toBeDefined()
311
+ expect(parsed.contact.list).toBeDefined()
312
+ expect(parsed.contact.list.columns).toBeDefined()
313
+ expect(parsed.contact.list.columns.firstName).toBeDefined()
314
+ }
315
+ })
316
+
317
+ it('respects webRoot override for i18n file paths', () => {
318
+ const files = generate(fixture({ webRoot: 'web/custom-web', pageSpec: pageSpecFixture() }))
319
+ const fr = files.find((f) => f.path === 'web/custom-web/src/i18n/locales/fr/crm.json')
320
+ expect(fr, 'i18n file should be at web/custom-web/...').toBeDefined()
321
+ const wrongPath = files.find((f) => f.path === 'web/TestV2-web/src/i18n/locales/fr/crm.json')
322
+ expect(wrongPath, 'should NOT use default web/{appCode}-web path').toBeUndefined()
323
+ })
324
+
325
+ it('falls back to buildTranslations() (legacy) when pageSpec is absent', () => {
326
+ // No pageSpec → legacy dictionary outputs 'Contacts' (plural humanize) for list.title
327
+ const files = generate(fixture())
328
+ const fr = files.find((f) => /\/i18n\/locales\/fr\/crm\.json$/.test(f.path))!
329
+ const parsed = JSON.parse(fr.content)
330
+ expect(parsed.contact.list.title).toBe('Contacts')
331
+ // No 'subtitle' override from pageSpec — legacy "Gérer les contacts"
332
+ expect(parsed.contact.list.subtitle).toContain('Gérer')
333
+ })
334
+
335
+ it('legacy mode and enriched mode emit the same number of files (no schema drift)', () => {
336
+ const legacyFiles = generate(fixture()).map((f) => f.path).sort()
337
+ const enrichedFiles = generate(fixture({ pageSpec: pageSpecFixture() })).map((f) => f.path).sort()
338
+ // Enriched mode honours pageSpec.filePath as the page output target;
339
+ // legacy mode uses the derived `src/pages/{appCode}/{module}/{section}/...` path.
340
+ // The PATHS may differ (e.g. legacy folder name) but the FILE COUNT
341
+ // must stay identical so no view is silently dropped.
342
+ expect(enrichedFiles.length).toBe(legacyFiles.length)
343
+ })
344
+
345
+ it('honours pageSpec.filePath verbatim — output target matches the legacy folder', () => {
346
+ // pageSpec.filePath = 'src/pages/testv2/crm/directory/ContactListPage.tsx' (the
347
+ // fixture path, lifted as-is from the canonical PageSpec contract). Even when
348
+ // the project's actual folder layout differs from the derived
349
+ // `src/pages/{module}/{section}/...`, the scaffold MUST write at the path
350
+ // declared in the pageSpec — that's the contract's "source of truth" rule.
351
+ const ps = pageSpecFixture({ filePath: 'src/pages/legacy-budgeting/budgets/BudgetsListPage.tsx' })
352
+ const files = generate(fixture({
353
+ pageSpec: ps,
354
+ module: 'budgets',
355
+ entity: 'Budget',
356
+ section: 'budgets',
357
+ views: ['list'],
358
+ }))
359
+ const listPage = files.find((f) => f.path.endsWith('BudgetsListPage.tsx'))!
360
+ expect(listPage).toBeDefined()
361
+ expect(listPage.path).toBe('src/pages/legacy-budgeting/budgets/BudgetsListPage.tsx')
362
+ })
363
+
364
+ it('uses pageSpec.columns verbatim — does NOT pull legacy columns from spec.fields', () => {
365
+ // The data model can carry attributes the BA did NOT include in the list
366
+ // view (e.g. legacy `description` column). Without honouring pageSpec.columns
367
+ // the scaffold renders them as untranslated `LIST.COLUMNS.DESCRIPTION` headers.
368
+ const ps = pageSpecFixture({
369
+ columns: [
370
+ { key: 'code', labelKey: 'list.columns.code', sortable: true },
371
+ { key: 'label', labelKey: 'list.columns.label', sortable: true },
372
+ { key: 'initialAmount', labelKey: 'list.columns.initialAmount', sortable: true },
373
+ ],
374
+ })
375
+ const files = generate(fixture({
376
+ pageSpec: ps,
377
+ // Inject a legacy `description` field that the BA did NOT declare for this view.
378
+ fields: [
379
+ { name: 'code', type: 'string', required: true },
380
+ { name: 'label', type: 'string', required: true },
381
+ { name: 'description', type: 'string', required: false },
382
+ { name: 'initialAmount', type: 'number', required: true },
383
+ ],
384
+ module: 'budgets',
385
+ entity: 'Budget',
386
+ section: 'budgets',
387
+ views: ['list'],
388
+ }))
389
+ const list = files.find((f) => f.path.endsWith('ContactsListPage.tsx'))
390
+ ?? files.find((f) => /ListPage\.tsx$/.test(f.path))!
391
+ // The pageSpec-declared columns appear:
392
+ expect(list.content).toMatch(/key:\s*'code'/)
393
+ expect(list.content).toMatch(/key:\s*'label'/)
394
+ expect(list.content).toMatch(/key:\s*'initialAmount'/)
395
+ // The legacy `description` column does NOT appear (would surface as
396
+ // `LIST.COLUMNS.DESCRIPTION` in the rendered page header).
397
+ expect(list.content).not.toMatch(/key:\s*'description'/)
398
+ // labelKey from pageSpec is honoured (path verbatim, leaf normalised to
399
+ // camelCase by normalizeI18nKey — symmetric with the JSON catalogue which
400
+ // goes through normalizeI18nFieldKeys). camelCase leaves are idempotent
401
+ // so this test still passes verbatim.
402
+ expect(list.content).toMatch(/t\('budget\.list\.columns\.code'\)/)
403
+ expect(list.content).toMatch(/t\('budget\.list\.columns\.initialAmount'\)/)
404
+ })
405
+
406
+ it('renders a FilterBar when pageSpec.filters[] is non-empty', () => {
407
+ const ps = pageSpecFixture({
408
+ columns: [{ key: 'label', labelKey: 'list.columns.label', sortable: true }],
409
+ // Add filters: text + select + date-range — each produces a different control.
410
+ filters: [
411
+ { field: 'label', labelKey: 'list.filters.label', control: 'text' },
412
+ {
413
+ field: 'status',
414
+ labelKey: 'list.filters.status',
415
+ control: 'select',
416
+ options: ['active', 'archived'],
417
+ },
418
+ { field: 'startDate', labelKey: 'list.filters.startDate', control: 'date-range' },
419
+ ],
420
+ } as Parameters<typeof pageSpecFixture>[0])
421
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
422
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
423
+ // The component imports useState + useMemo (filter state).
424
+ expect(list.content).toMatch(/import\s*\{\s*useMemo,\s*useState\s*\}\s*from\s*'react'/)
425
+ // FilterBar container renders.
426
+ expect(list.content).toMatch(/flex\s+flex-wrap\s+items-end\s+gap-3/)
427
+ // Text filter input is wired:
428
+ expect(list.content).toMatch(/onFilterChange\(\s*'label'/)
429
+ // Select filter has its option set + "all" default:
430
+ expect(list.content).toMatch(/<option value="active">active<\/option>/)
431
+ expect(list.content).toMatch(/<option value="archived">archived<\/option>/)
432
+ expect(list.content).toMatch(/contact\.list\.filters\.all/)
433
+ // Date-range produces 2 date inputs:
434
+ expect(list.content).toMatch(/onFilterChange\('startDateFrom'/)
435
+ expect(list.content).toMatch(/onFilterChange\('startDateTo'/)
436
+ // DataTable consumes the filtered list.
437
+ expect(list.content).toMatch(/data=\{filtered\}/)
438
+ })
439
+
440
+ it('emits NO filter state imports when pageSpec.filters is empty', () => {
441
+ const ps = pageSpecFixture({ filters: [] } as Parameters<typeof pageSpecFixture>[0])
442
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
443
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
444
+ // No useMemo / useState import — the legacy template stays clean.
445
+ expect(list.content).not.toMatch(/import\s*\{\s*useMemo,\s*useState\s*\}\s*from\s*'react'/)
446
+ // No FilterBar container.
447
+ expect(list.content).not.toMatch(/flex\s+flex-wrap\s+items-end\s+gap-3/)
448
+ // DataTable still consumes a `filtered` slot — but it's a plain `data?.items ?? []` shorthand.
449
+ expect(list.content).toMatch(/data=\{filtered\}/)
450
+ expect(list.content).toMatch(/const filtered = data\?\.items \?\? \[\]/)
451
+ })
452
+
453
+ it('Fix #9 — normalises pagespec column labelKey leaf to match the JSON i18n catalogue (PascalCase → camelCase)', () => {
454
+ // The PRD sometimes emits PascalCase leaves (`list.columns.Code`,
455
+ // `list.columns.EstActif`). The JSON catalogue normalises them to
456
+ // camelCase via normalizeI18nFieldKeys (line ~1400). Without the
457
+ // symmetric normalisation on the TSX side, t('…Code') resolves nothing
458
+ // and the screen shows the raw key. This is THE reason every table
459
+ // column header rendered the literal key on REFERENTIELS.
460
+ const ps = pageSpecFixture({
461
+ columns: [
462
+ { key: 'code', labelKey: 'list.columns.Code', sortable: true },
463
+ { key: 'estActif', labelKey: 'list.columns.EstActif', sortable: true },
464
+ ],
465
+ })
466
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
467
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
468
+ // The leaf is camelCased in the t() call:
469
+ expect(list.content).toMatch(/t\('contact\.list\.columns\.code'\)/)
470
+ expect(list.content).toMatch(/t\('contact\.list\.columns\.estActif'\)/)
471
+ // Negative — the PascalCase leaf must NOT leak into the TSX:
472
+ expect(list.content).not.toMatch(/t\('contact\.list\.columns\.Code'\)/)
473
+ expect(list.content).not.toMatch(/t\('contact\.list\.columns\.EstActif'\)/)
474
+ })
475
+
476
+ it('Fix #9 — normalises pagespec filter labelKey leaf across every control type', () => {
477
+ // Same normalisation applied at the source — one pass on pageSpecFilters
478
+ // covers text, select, date-range and boolean labels + placeholders
479
+ // (6 substitution sites in renderFilterInput) without touching them.
480
+ const ps = pageSpecFixture({
481
+ filters: [
482
+ { field: 'code', labelKey: 'list.filters.Code', control: 'text' },
483
+ { field: 'status', labelKey: 'list.filters.Status', control: 'select', options: ['a'] },
484
+ { field: 'startDate', labelKey: 'list.filters.StartDate', control: 'date-range' },
485
+ { field: 'estActif', labelKey: 'list.filters.EstActif', control: 'boolean' },
486
+ ],
487
+ } as Parameters<typeof pageSpecFixture>[0])
488
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
489
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
490
+ // All four control types resolve the camelCased leaf:
491
+ expect(list.content).toMatch(/t\('contact\.list\.filters\.code'\)/)
492
+ expect(list.content).toMatch(/t\('contact\.list\.filters\.status'\)/)
493
+ expect(list.content).toMatch(/t\('contact\.list\.filters\.startDate'\)/)
494
+ expect(list.content).toMatch(/t\('contact\.list\.filters\.estActif'\)/)
495
+ // Negative — no PascalCase leaves leak into any of the 6 render sites:
496
+ expect(list.content).not.toMatch(/t\('contact\.list\.filters\.Code'\)/)
497
+ expect(list.content).not.toMatch(/t\('contact\.list\.filters\.Status'\)/)
498
+ expect(list.content).not.toMatch(/t\('contact\.list\.filters\.StartDate'\)/)
499
+ expect(list.content).not.toMatch(/t\('contact\.list\.filters\.EstActif'\)/)
500
+ })
501
+
502
+ it('Fix #9 — does NOT mangle action labelKey (parent not in I18N_FIELD_PARENTS)', () => {
503
+ // Action labels live under `list.<code>` / `form.actions.<code>` —
504
+ // their parent is NOT one of {fields, columns, filters, help}, so
505
+ // normalizeI18nFieldKeys leaves them alone in the JSON. The TSX must
506
+ // mirror that and keep them verbatim too, otherwise we would break
507
+ // kebab-case action codes like `toggle-actif`.
508
+ const ps = pageSpecFixture({
509
+ actions: [
510
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
511
+ { code: 'toggle-actif', scope: 'row', labelKey: 'list.toggle-actif', permission: 'crm.directory.update', variant: 'secondary' },
512
+ ],
513
+ })
514
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
515
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
516
+ // Action labels stay verbatim (2-seg path: parts.length < 3 → skip),
517
+ // and the kebab-case `toggle-actif` is preserved exactly:
518
+ expect(list.content).toMatch(/t\('contact\.list\.create'\)/)
519
+ expect(list.content).toMatch(/t\('contact\.list\.toggle-actif'\)/)
520
+ })
521
+
522
+ it('renders row-scope custom actions as icon buttons in the actions column', () => {
523
+ const ps = pageSpecFixture({
524
+ actions: [
525
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
526
+ { code: 'archive', scope: 'row', labelKey: 'list.archive', permission: 'crm.directory.update', variant: 'secondary' },
527
+ { code: 'duplicate', scope: 'row', labelKey: 'list.duplicate', permission: 'crm.directory.create', variant: 'secondary' },
528
+ ],
529
+ })
530
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
531
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
532
+ // Hooks for archive + duplicate imported alongside CRUD hooks
533
+ expect(list.content).toMatch(/import\s*\{\s*useContacts,\s*useDeleteContact, useArchiveContact, useDuplicateContact\s*\}/)
534
+ // Mutation declarations
535
+ expect(list.content).toMatch(/const archiveMutation = useArchiveContact\(\)/)
536
+ expect(list.content).toMatch(/const duplicateMutation = useDuplicateContact\(\)/)
537
+ // Handler functions wired to mutations
538
+ expect(list.content).toMatch(/const handleArchive = async \(item: Contact\) => \{[\s\S]*?archiveMutation\.mutateAsync\(item\.id\)/)
539
+ expect(list.content).toMatch(/const handleDuplicate = async \(item: Contact\) => \{[\s\S]*?duplicateMutation\.mutateAsync\(item\.id\)/)
540
+ // Icon imports include Archive + Copy (from the lucide map)
541
+ expect(list.content).toMatch(/Archive,/)
542
+ expect(list.content).toMatch(/Copy/)
543
+ // Row buttons rendered with PermissionGuard + 4-seg permission
544
+ expect(list.content).toMatch(/<PermissionGuard\s+permission="crm\.directory\.update">[\s\S]*?onClick=\{\(e\) => \{[\s\S]*?handleArchive\(item\)/)
545
+ expect(list.content).toMatch(/title=\{t\('contact\.list\.archive'\)\}/)
546
+ })
547
+
548
+ it('renders header-scope custom actions next to the Create button', () => {
549
+ const ps = pageSpecFixture({
550
+ actions: [
551
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
552
+ { code: 'export', scope: 'header', labelKey: 'list.export', permission: 'crm.directory.read', variant: 'secondary' },
553
+ ],
554
+ })
555
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
556
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
557
+ // The header actions are wrapped in a `<div className="flex items-center gap-2">` so multiple buttons fit
558
+ expect(list.content).toMatch(/actions=\{[\s\S]*?<div className="flex items-center gap-2">/)
559
+ // Export button uses the secondary variant style + Download icon
560
+ expect(list.content).toMatch(/\{t\('contact\.list\.export'\)\}/)
561
+ expect(list.content).toMatch(/Download/)
562
+ // Header action handler does NOT take an item parameter
563
+ expect(list.content).toMatch(/const handleExport = async \(\) => \{[\s\S]*?exportMutation\.mutateAsync\(\)/)
564
+ })
565
+
566
+ it('skips standard CRUD codes from custom actions iteration (no double-emit)', () => {
567
+ const ps = pageSpecFixture({
568
+ actions: [
569
+ // 'edit' and 'delete' are standard CRUD — already hardcoded; skip
570
+ { code: 'edit', scope: 'row', labelKey: 'list.edit', permission: 'crm.directory.update', variant: 'secondary' },
571
+ { code: 'delete', scope: 'row', labelKey: 'list.delete', permission: 'crm.directory.delete', variant: 'danger' },
572
+ // 'archive' is non-CRUD → renders
573
+ { code: 'archive', scope: 'row', labelKey: 'list.archive', permission: 'crm.directory.update', variant: 'secondary' },
574
+ ],
575
+ })
576
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
577
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
578
+ // No useEditContact import (CRUD codes are skipped from custom iteration)
579
+ expect(list.content).not.toMatch(/useEditContact/)
580
+ // archive IS imported as a custom action hook
581
+ expect(list.content).toMatch(/useArchiveContact/)
582
+ // useDeleteContact is the standard hardcoded import (appears in both
583
+ // import statement AND in `useDelete${e}()` call — 2 occurrences total).
584
+ // Custom-action iteration must NOT add a third reference.
585
+ expect(list.content.match(/useDeleteContact/g)?.length).toBe(2)
586
+ })
587
+
588
+ it('renders danger-variant row actions with error-bg on hover', () => {
589
+ const ps = pageSpecFixture({
590
+ actions: [
591
+ { code: 'reject', scope: 'row', labelKey: 'list.reject', permission: 'crm.directory.update', variant: 'danger' },
592
+ ],
593
+ })
594
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
595
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
596
+ // The danger variant uses error-bg for hover state (matches the legacy delete button styling)
597
+ expect(list.content).toMatch(/handleReject\(item\)[\s\S]*?className="p-1\.5 rounded hover:bg-\[var\(--error-bg\)\]/)
598
+ })
599
+
600
+ it('emits NO custom action artefacts when pageSpec.actions is empty (legacy)', () => {
601
+ // The fixture pageSpec has no actions[] beyond a default 'create' header
602
+ // (which is a standard CRUD code skipped from custom iteration).
603
+ const files = generate(fixture({ pageSpec: pageSpecFixture(), views: ['list'] }))
604
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
605
+ // No extra hook imports beyond the standard CRUD ones
606
+ expect(list.content).not.toMatch(/useArchive|useDuplicate|useApprove|useReject/)
607
+ // No mutation declarations beyond `deleteMutation`
608
+ expect(list.content).not.toMatch(/Mutation = use(?!Delete|Contacts)/)
609
+ })
610
+ })
611
+
612
+ describe('scaffold-component / generate — list page custom actions kind:navigate', () => {
613
+ it('emits a navigate(targetRoute) handler for a kind:navigate row action, no hook', () => {
614
+ const ps = pageSpecFixture({
615
+ actions: [
616
+ // The "Ouvrir" case — row click on the line should NAVIGATE to the
617
+ // detail page, not POST /{id}/open (which is what produces the 405).
618
+ {
619
+ code: 'open',
620
+ kind: 'navigate',
621
+ scope: 'row',
622
+ labelKey: 'list.open',
623
+ permission: 'crm.directory.read',
624
+ targetRoute: 'routes.directory.detail(item.id)',
625
+ },
626
+ ],
627
+ })
628
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
629
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
630
+ // NO hook import for the navigate action
631
+ expect(list.content).not.toMatch(/useOpenContact/)
632
+ // NO mutation declaration
633
+ expect(list.content).not.toMatch(/openMutation/)
634
+ // The handler navigates via React Router — synchronous, no mutateAsync
635
+ expect(list.content).toMatch(/const handleOpen = \(item: Contact\) => \{[\s\S]*?navigate\(routes\.directory\.detail\(item\.id\)\)/)
636
+ // The button still renders (PermissionGuard + onClick → handleOpen)
637
+ expect(list.content).toMatch(/handleOpen\(item\)/)
638
+ })
639
+
640
+ it('emits a navigate(targetRoute) handler for a kind:navigate header action', () => {
641
+ const ps = pageSpecFixture({
642
+ actions: [
643
+ {
644
+ code: 'openHistory',
645
+ kind: 'navigate',
646
+ scope: 'header',
647
+ labelKey: 'list.openHistory',
648
+ permission: 'crm.directory.read',
649
+ targetRoute: 'routes.directory.history()',
650
+ },
651
+ ],
652
+ })
653
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
654
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
655
+ // Synchronous handler — no `async`, no `await`
656
+ expect(list.content).toMatch(/const handleOpenHistory = \(\) => \{[\s\S]*?navigate\(routes\.directory\.history\(\)\)/)
657
+ expect(list.content).not.toMatch(/useOpenHistoryContact/)
658
+ expect(list.content).not.toMatch(/openHistoryMutation/)
659
+ })
660
+
661
+ it('mixes kind:api and kind:navigate actions on the same page', () => {
662
+ const ps = pageSpecFixture({
663
+ actions: [
664
+ // CRUD standard — skipped from custom iteration
665
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
666
+ // api-bound custom — needs hook + mutation
667
+ { code: 'archive', kind: 'api', scope: 'row', labelKey: 'list.archive', permission: 'crm.directory.update', variant: 'secondary' },
668
+ // navigate-bound custom — needs router only
669
+ { code: 'open', kind: 'navigate', scope: 'row', labelKey: 'list.open', permission: 'crm.directory.read', targetRoute: 'routes.directory.detail(item.id)' },
670
+ ],
671
+ })
672
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
673
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
674
+ // api action: hook + mutation
675
+ expect(list.content).toMatch(/useArchiveContact/)
676
+ expect(list.content).toMatch(/archiveMutation\.mutateAsync\(item\.id\)/)
677
+ // navigate action: NO hook, NO mutation, just navigate()
678
+ expect(list.content).not.toMatch(/useOpenContact/)
679
+ expect(list.content).not.toMatch(/openMutation/)
680
+ expect(list.content).toMatch(/handleOpen = \(item: Contact\) => \{[\s\S]*?navigate\(routes\.directory\.detail\(item\.id\)\)/)
681
+ // Both buttons reach the actions column
682
+ expect(list.content).toMatch(/handleArchive\(item\)/)
683
+ expect(list.content).toMatch(/handleOpen\(item\)/)
684
+ })
685
+
686
+ it('hook name follows endpoint when code != endpoint (override pattern)', () => {
687
+ // The BA wants the UI label "Sync from PCE" but the backend route is the
688
+ // legacy `sync-from-proconcept`. scaffold-api-client generates the hook
689
+ // named after the endpoint so the request URL and the React Query key
690
+ // stay aligned. scaffold-component MUST import the same name.
691
+ const ps = pageSpecFixture({
692
+ actions: [
693
+ {
694
+ code: 'syncFromPce',
695
+ kind: 'api',
696
+ scope: 'header',
697
+ labelKey: 'list.syncFromPce',
698
+ permission: 'crm.directory.update',
699
+ endpoint: 'sync-from-proconcept',
700
+ },
701
+ ],
702
+ })
703
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
704
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
705
+ // Hook name derived from endpoint (the URL is the source of truth)
706
+ expect(list.content).toMatch(/useSyncFromProconceptContact/)
707
+ expect(list.content).toMatch(/syncFromProconceptMutation = useSyncFromProconceptContact\(\)/)
708
+ expect(list.content).toMatch(/handleSyncFromPce = async \(\) => \{[\s\S]*?syncFromProconceptMutation\.mutateAsync\(\)/)
709
+ // The button label still uses the BA labelKey (camelCase) — UI label stays
710
+ // independent of the URL segment.
711
+ expect(list.content).toMatch(/\{t\('contact\.list\.syncFromPce'\)\}/)
712
+ })
713
+ })
714
+
715
+ describe('scaffold-component / generate — FK fields render as <EntityLookup>', () => {
716
+ it('emits <EntityLookup> for a same-module FK and imports the lookup hook', () => {
717
+ const files = generate(fixture({
718
+ entity: 'Employee',
719
+ module: 'hr',
720
+ section: 'employees',
721
+ views: ['form'],
722
+ fields: [
723
+ { name: 'firstName', type: 'string', required: true },
724
+ {
725
+ name: 'departmentId',
726
+ type: 'guid',
727
+ required: true,
728
+ fkTo: { entity: 'Department', module: 'hr' },
729
+ },
730
+ ],
731
+ }))
732
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
733
+ // Import surface — EntityLookup component + per-target lookup hook
734
+ expect(form.content).toMatch(/import \{ EntityLookup \} from '@\/components\/ui\/EntityLookup'/)
735
+ expect(form.content).toMatch(/import \{ useDepartmentLookup \} from '@\/features\/hr\/department\/hooks\/useDepartment'/)
736
+ // FK field is rendered as <EntityLookup>, not <input>
737
+ expect(form.content).toMatch(
738
+ /<EntityLookup\s+apiEndpoint="\/api\/hr\/departments\/lookup"\s+value=\{\(formData\.departmentId as string \| null\) \?\? null\}/,
739
+ )
740
+ expect(form.content).toMatch(/label=\{t\('employee\.form\.fields\.departmentId'\)\}/)
741
+ // Non-FK fields still get the plain <input>
742
+ expect(form.content).toMatch(/id="firstName"[\s\S]*?type="text"/)
743
+ // FK field never gets a plain <input> with name/id ending in "Id"
744
+ expect(form.content).not.toMatch(/<input[^>]*id="departmentId"/)
745
+ })
746
+
747
+ it('emits the required attribute on the EntityLookup when the FK is required', () => {
748
+ const files = generate(fixture({
749
+ entity: 'Employee',
750
+ module: 'hr',
751
+ section: 'employees',
752
+ views: ['form'],
753
+ fields: [
754
+ { name: 'mgrId', type: 'guid', required: true, fkTo: { entity: 'Manager', module: 'hr' } },
755
+ ],
756
+ }))
757
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
758
+ // The `required` attribute is present (no value — JSX boolean attribute shorthand)
759
+ expect(form.content).toMatch(/<EntityLookup[\s\S]*?\brequired\b[\s\S]*?disabled=\{isPending\}/)
760
+ })
761
+
762
+ it('omits the EntityLookup import when no FK is declared (legacy form unchanged)', () => {
763
+ const files = generate(fixture()) // no fkTo in fixture fields
764
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
765
+ expect(form.content).not.toMatch(/EntityLookup/)
766
+ expect(form.content).not.toMatch(/use[A-Z]\w*Lookup/)
767
+ })
768
+
769
+ it('honours a custom apiEndpoint override (cross-module / Core)', () => {
770
+ const files = generate(fixture({
771
+ entity: 'Demand',
772
+ module: 'requests',
773
+ section: 'demands',
774
+ views: ['form'],
775
+ fields: [
776
+ {
777
+ name: 'requesterUserId',
778
+ type: 'guid',
779
+ required: true,
780
+ fkTo: { entity: 'User', module: 'core', apiEndpoint: '/api/core/users/lookup' },
781
+ },
782
+ ],
783
+ }))
784
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
785
+ expect(form.content).toMatch(/apiEndpoint="\/api\/core\/users\/lookup"/)
786
+ expect(form.content).toMatch(/import \{ useUserLookup \} from '@\/features\/core\/user\/hooks\/useUser'/)
787
+ })
788
+
789
+ it('deduplicates lookup hook imports when multiple FKs target the same entity', () => {
790
+ const files = generate(fixture({
791
+ entity: 'Assignment',
792
+ module: 'hr',
793
+ section: 'assignments',
794
+ views: ['form'],
795
+ fields: [
796
+ { name: 'fromEmployeeId', type: 'guid', required: true, fkTo: { entity: 'Employee', module: 'hr' } },
797
+ { name: 'toEmployeeId', type: 'guid', required: true, fkTo: { entity: 'Employee', module: 'hr' } },
798
+ ],
799
+ }))
800
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
801
+ const matches = form.content.match(/import \{ useEmployeeLookup \}/g) ?? []
802
+ expect(matches.length).toBe(1)
803
+ // But both FK fields are rendered
804
+ expect(form.content).toMatch(/label=\{t\('assignment\.form\.fields\.fromEmployeeId'\)\}/)
805
+ expect(form.content).toMatch(/label=\{t\('assignment\.form\.fields\.toEmployeeId'\)\}/)
806
+ })
807
+
808
+ it('never emits a raw <input name="...Id"> or <select value={...Id}> when fkTo is present (DEV-UI-022 contract)', () => {
809
+ const files = generate(fixture({
810
+ entity: 'Order',
811
+ module: 'sales',
812
+ section: 'orders',
813
+ views: ['form'],
814
+ fields: [
815
+ { name: 'clientId', type: 'guid', required: true, fkTo: { entity: 'Client', module: 'crm' } },
816
+ ],
817
+ }))
818
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
819
+ // The patterns DEV-UI-022 will reject
820
+ expect(form.content).not.toMatch(/<input[^>]*\b(name|id)\s*=\s*["']clientId["']/)
821
+ expect(form.content).not.toMatch(/<select[^>]*value=\{[^}]*clientId/)
822
+ })
823
+ })
824
+
825
+ // ─── Form page custom actions ──────────────────────────────────────────
826
+
827
+ describe('scaffold-component / generate — form page custom actions', () => {
828
+ it('renders header-scoped custom actions on form page, guarded by isEdit', () => {
829
+ const ps = pageSpecFixture({
830
+ view: 'form',
831
+ filePath: 'src/pages/testv2/crm/directory/ContactFormPage.tsx',
832
+ actions: [
833
+ { code: 'create', scope: 'header', labelKey: 'form.create', permission: 'crm.directory.create', variant: 'primary' },
834
+ { code: 'duplicate', scope: 'header', labelKey: 'form.actions.duplicate', permission: 'crm.directory.create', variant: 'secondary' },
835
+ { code: 'toggle-actif', scope: 'header', labelKey: 'form.actions.toggle-actif', permission: 'crm.directory.execute', variant: 'secondary' },
836
+ ],
837
+ })
838
+ const files = generate(fixture({ pageSpec: ps, views: ['form'] }))
839
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
840
+ // Hook imports for api-bound actions (create is CRUD — skipped)
841
+ expect(form.content).toMatch(/useDuplicateContact/)
842
+ expect(form.content).toMatch(/useToggleActifContact/)
843
+ // Mutation declarations
844
+ expect(form.content).toMatch(/duplicateMutation = useDuplicateContact\(\)/)
845
+ expect(form.content).toMatch(/toggleActifMutation = useToggleActifContact\(\)/)
846
+ // Handlers use `id` from closure (not item parameter)
847
+ expect(form.content).toMatch(/const handleDuplicate = async \(\) => \{[\s\S]*?if \(id\) await duplicateMutation\.mutateAsync\(id\)/)
848
+ expect(form.content).toMatch(/const handleToggleActif = async \(\) => \{[\s\S]*?if \(id\) await toggleActifMutation\.mutateAsync\(id\)/)
849
+ // actions prop guarded by isEdit
850
+ expect(form.content).toMatch(/actions=\{isEdit \?/)
851
+ // PermissionGuard with 4-seg permission
852
+ expect(form.content).toMatch(/<PermissionGuard permission="crm\.directory\.create">[\s\S]*?handleDuplicate/)
853
+ expect(form.content).toMatch(/<PermissionGuard permission="crm\.directory\.execute">[\s\S]*?handleToggleActif/)
854
+ })
855
+
856
+ it('renders navigate-kind form action without hooks', () => {
857
+ const ps = pageSpecFixture({
858
+ view: 'form',
859
+ filePath: 'src/pages/testv2/crm/directory/ContactFormPage.tsx',
860
+ actions: [
861
+ {
862
+ code: 'openHistory',
863
+ kind: 'navigate',
864
+ scope: 'header',
865
+ labelKey: 'form.actions.openHistory',
866
+ permission: 'crm.directory.read',
867
+ targetRoute: 'routes.directory.history()',
868
+ },
869
+ ],
870
+ })
871
+ const files = generate(fixture({ pageSpec: ps, views: ['form'] }))
872
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
873
+ // NO hook import or mutation for navigate action
874
+ expect(form.content).not.toMatch(/useOpenHistoryContact/)
875
+ expect(form.content).not.toMatch(/openHistoryMutation/)
876
+ // Synchronous navigate handler
877
+ expect(form.content).toMatch(/const handleOpenHistory = \(\) => \{[\s\S]*?navigate\(routes\.directory\.history\(\)\)/)
878
+ })
879
+
880
+ it('emits NO action artefacts on form page when no custom actions (backward compat)', () => {
881
+ const files = generate(fixture({ views: ['form'] }))
882
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
883
+ // No custom hook imports
884
+ expect(form.content).not.toMatch(/useArchive|useDuplicate|useToggle/)
885
+ // No actions prop on PageTemplate
886
+ expect(form.content).not.toMatch(/actions=\{/)
887
+ // Standard save/cancel still present
888
+ expect(form.content).toMatch(/form\.cancel/)
889
+ expect(form.content).toMatch(/form\.submitCreate/)
890
+ })
891
+
892
+ it('ignores row-scoped actions on form page (forms have no rows)', () => {
893
+ const ps = pageSpecFixture({
894
+ view: 'form',
895
+ filePath: 'src/pages/testv2/crm/directory/ContactFormPage.tsx',
896
+ actions: [
897
+ { code: 'archive', scope: 'row', labelKey: 'list.archive', permission: 'crm.directory.update', variant: 'secondary' },
898
+ ],
899
+ })
900
+ const files = generate(fixture({ pageSpec: ps, views: ['form'] }))
901
+ const form = files.find((f) => /FormPage\.tsx$/.test(f.path))!
902
+ // Row-scoped action MUST NOT appear on the form page
903
+ expect(form.content).not.toMatch(/useArchiveContact/)
904
+ expect(form.content).not.toMatch(/handleArchive/)
905
+ expect(form.content).not.toMatch(/actions=\{/)
906
+ })
907
+ })
908
+
909
+ // ─── Detail page custom actions ────────────────────────────────────────
910
+
911
+ describe('scaffold-component / generate — detail page custom actions', () => {
912
+ it('renders custom header actions alongside the Edit button on detail page', () => {
913
+ const ps = pageSpecFixture({
914
+ view: 'detail',
915
+ filePath: 'src/pages/testv2/crm/directory/ContactDetailPage.tsx',
916
+ actions: [
917
+ { code: 'duplicate', scope: 'header', labelKey: 'detail.actions.duplicate', permission: 'crm.directory.create', variant: 'secondary' },
918
+ { code: 'toggle-actif', scope: 'header', labelKey: 'detail.actions.toggle-actif', permission: 'crm.directory.execute', variant: 'secondary' },
919
+ ],
920
+ })
921
+ const files = generate(fixture({ pageSpec: ps, views: ['detail'] }))
922
+ const detail = files.find((f) => /DetailPage\.tsx$/.test(f.path))!
923
+ // Hook imports for custom actions
924
+ expect(detail.content).toMatch(/useDuplicateContact/)
925
+ expect(detail.content).toMatch(/useToggleActifContact/)
926
+ // The Edit button is still present alongside custom actions
927
+ expect(detail.content).toMatch(/detail\.edit/)
928
+ // Custom action buttons appear inside the actions div
929
+ expect(detail.content).toMatch(/handleDuplicate/)
930
+ expect(detail.content).toMatch(/handleToggleActif/)
931
+ // Handlers use id from closure
932
+ expect(detail.content).toMatch(/const handleDuplicate = async \(\) => \{[\s\S]*?if \(id\) await duplicateMutation\.mutateAsync\(id\)/)
933
+ })
934
+
935
+ it('detail page with no custom actions still renders Edit button', () => {
936
+ const files = generate(fixture({ views: ['detail'] }))
937
+ const detail = files.find((f) => /DetailPage\.tsx$/.test(f.path))!
938
+ expect(detail.content).toMatch(/detail\.edit/)
939
+ expect(detail.content).not.toMatch(/useDuplicate|useToggle|useArchive/)
940
+ })
941
+ })
942
+
943
+ // ─── Heuristic: "open" row action without explicit kind → navigate ─────
944
+
945
+ describe('scaffold-component / generate — open action heuristic', () => {
946
+ it('treats row-scoped "open" as navigate when kind is omitted', () => {
947
+ const ps = pageSpecFixture({
948
+ actions: [
949
+ { code: 'create', scope: 'header', labelKey: 'list.create', permission: 'crm.directory.create', variant: 'primary' },
950
+ { code: 'open', scope: 'row', labelKey: 'list.actions.open', permission: 'crm.directory.read' },
951
+ ],
952
+ })
953
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
954
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
955
+ expect(list.content).not.toMatch(/useOpenContact/)
956
+ expect(list.content).not.toMatch(/openMutation/)
957
+ expect(list.content).toMatch(/handleOpen = \(item: Contact\) => \{[\s\S]*?navigate\(routes\.directory\.detail\(item\.id\)\)/)
958
+ })
959
+
960
+ it('respects explicit kind:api on "open" (override heuristic)', () => {
961
+ const ps = pageSpecFixture({
962
+ actions: [
963
+ { code: 'open', kind: 'api', scope: 'row', labelKey: 'list.actions.open', permission: 'crm.directory.execute' },
964
+ ],
965
+ })
966
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
967
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
968
+ expect(list.content).toMatch(/useOpenContact/)
969
+ expect(list.content).toMatch(/openMutation\.mutateAsync\(item\.id\)/)
970
+ })
971
+
972
+ it('row navigate fallback targets detail(item.id), not list()', () => {
973
+ const ps = pageSpecFixture({
974
+ actions: [
975
+ { code: 'view-report', kind: 'navigate', scope: 'row', labelKey: 'list.actions.view-report', permission: 'crm.directory.read' },
976
+ ],
977
+ })
978
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
979
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
980
+ expect(list.content).toMatch(/navigate\(routes\.directory\.detail\(item\.id\)\)/)
981
+ })
982
+ })
983
+
984
+ // ─── Filter q → cross-field search ────────────────────────────────────
985
+
986
+ describe('scaffold-component / generate — filter q cross-field search', () => {
987
+ it('generates Object.values search for filter field "q"', () => {
988
+ const ps = pageSpecFixture({
989
+ filters: [
990
+ { field: 'q', labelKey: 'list.filters.q', control: 'text' },
991
+ { field: 'estActif', labelKey: 'list.filters.estActif', control: 'boolean' },
992
+ ],
993
+ })
994
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
995
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
996
+ // Bug 4 fix — double-cast `as unknown as Record<string, unknown>` is the
997
+ // canonical TS idiom for crossing into a permissive index-signature shape
998
+ // when the source DTO is sealed.
999
+ expect(list.content).toMatch(/Object\.values\(item as unknown as Record<string, unknown>\)\.some/)
1000
+ expect(list.content).not.toMatch(/\(item as unknown as Record<string, unknown>\)\['q'\]/)
1001
+ })
1002
+
1003
+ it('Bug 4 — every "as Record<string, unknown>" cast is preceded by "as unknown"', () => {
1004
+ // Defence-in-depth: walk every emitted page and make sure the single-cast
1005
+ // `item as Record<string, unknown>` does NOT slip back in. The single cast
1006
+ // is rejected by TypeScript ("conversion may be a mistake") whenever the
1007
+ // DTO is sealed — which is the default for scaffold-api-client output.
1008
+ const ps = pageSpecFixture({
1009
+ filters: [
1010
+ { field: 'q', labelKey: 'list.filters.q', control: 'text' },
1011
+ { field: 'estActif', labelKey: 'list.filters.estActif', control: 'boolean' },
1012
+ { field: 'createdAt', labelKey: 'list.filters.createdAt', control: 'date-range' },
1013
+ { field: 'category', labelKey: 'list.filters.category', control: 'text' },
1014
+ ],
1015
+ })
1016
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
1017
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
1018
+ // No single-cast occurrence anywhere.
1019
+ expect(list.content).not.toMatch(/[^n] as Record<string, unknown>/)
1020
+ // Every double-cast occurrence is fully formed.
1021
+ const allCasts = (list.content.match(/as unknown as Record<string, unknown>/g) ?? []).length
1022
+ expect(allCasts).toBeGreaterThanOrEqual(3) // q + select + date-range + text
1023
+ })
1024
+ })
1025
+
1026
+ // ─── Bug 9 — fieldToCamel on headerField in DetailPage ───────────────
1027
+
1028
+ describe('scaffold-component / generate — Bug 9 (header field normalised to camelCase)', () => {
1029
+ // Reproducer: BA pagespec declared the detail header field as `Numero`
1030
+ // (PascalCase). The generated DemandeDetailPage.tsx emitted
1031
+ // `data.Numero` while the DTO serialises camelCase `numero` → TS2551
1032
+ // "Property 'Numero' does not exist on type 'DemandeDetailDto'. Did you mean
1033
+ // 'numero'?" (Typescript actually suggests the correct casing.)
1034
+ it('headerTitle uses camelCase field accessor even when BA field name is PascalCase', () => {
1035
+ const files = generate(fixture({
1036
+ fields: [
1037
+ { name: 'Numero', type: 'string', labelKey: 'detail.fields.numero' },
1038
+ { name: 'Status', type: 'string', labelKey: 'detail.fields.status' },
1039
+ ],
1040
+ views: ['detail'],
1041
+ }))
1042
+ const detail = files.find((f) => /DetailPage\.tsx$/.test(f.path))!
1043
+ expect(detail.content).toMatch(/data\.numero/)
1044
+ expect(detail.content).not.toMatch(/data\.Numero/)
1045
+ })
1046
+
1047
+ it('normalises kebab-style field names too (defence-in-depth)', () => {
1048
+ // Defensive : even though BA convention is camelCase or PascalCase, ensure
1049
+ // the normaliser handles odd shapes without leaking into the page.
1050
+ const files = generate(fixture({
1051
+ fields: [
1052
+ { name: 'CreatedAt', type: 'datetime', labelKey: 'detail.fields.createdAt' },
1053
+ ],
1054
+ views: ['detail'],
1055
+ }))
1056
+ const detail = files.find((f) => /DetailPage\.tsx$/.test(f.path))!
1057
+ expect(detail.content).toMatch(/data\.createdAt/)
1058
+ expect(detail.content).not.toMatch(/data\.CreatedAt/)
1059
+ })
1060
+ })
1061
+
1062
+ // ─── Icon mapping ─────────────────────────────────────────────────────
1063
+
1064
+ describe('scaffold-component / generate — action icon mapping', () => {
1065
+ it('maps "open" → ExternalLink, "set-availability" → CalendarCheck, unknown → ChevronRight', () => {
1066
+ const ps = pageSpecFixture({
1067
+ actions: [
1068
+ { code: 'open', scope: 'row', labelKey: 'list.actions.open', permission: 'crm.directory.read' },
1069
+ { code: 'set-availability', kind: 'api', scope: 'row', labelKey: 'list.actions.set-availability', permission: 'crm.directory.update' },
1070
+ { code: 'some-unknown-action', kind: 'api', scope: 'row', labelKey: 'list.actions.unknown', permission: 'crm.directory.execute' },
1071
+ ],
1072
+ })
1073
+ const files = generate(fixture({ pageSpec: ps, views: ['list'] }))
1074
+ const list = files.find((f) => /ListPage\.tsx$/.test(f.path))!
1075
+ expect(list.content).toMatch(/<ExternalLink className="w-4 h-4"/)
1076
+ expect(list.content).toMatch(/<CalendarCheck className="w-4 h-4"/)
1077
+ expect(list.content).toMatch(/<ChevronRight className="w-4 h-4"/)
1078
+ })
1079
+ })
1080
+
1081
+ // ─── Bug 7 — kanban view first-class ────────────────────────────────────
1082
+
1083
+ describe('scaffold-component / generate — Bug 7 (kanban view)', () => {
1084
+ it('emits KanbanPage when views includes "kanban"', () => {
1085
+ const files = generate(fixture({
1086
+ entity: 'Demande',
1087
+ section: 'demandes',
1088
+ fields: [
1089
+ { name: 'numero', type: 'string', required: true },
1090
+ { name: 'status', type: 'string', required: true },
1091
+ ],
1092
+ views: ['kanban'],
1093
+ kanbanConfig: {
1094
+ groupBy: 'status',
1095
+ columns: [
1096
+ { key: 'open', labelKey: 'kanban.columns.open' },
1097
+ { key: 'closed', labelKey: 'kanban.columns.closed' },
1098
+ ],
1099
+ },
1100
+ }))
1101
+ const kanban = files.find(f => /KanbanPage\.tsx$/.test(f.path))
1102
+ expect(kanban).toBeDefined()
1103
+ expect(kanban!.content).toMatch(/<PageTemplate/)
1104
+ expect(kanban!.content).toMatch(/use(Demandes|Contacts)\(\)/) // depends on plural
1105
+ expect(kanban!.content).toMatch(/KANBAN_COLUMNS/)
1106
+ })
1107
+
1108
+ it('groups items by the configured field (status by default)', () => {
1109
+ const files = generate(fixture({
1110
+ entity: 'Demande',
1111
+ section: 'demandes',
1112
+ fields: [
1113
+ { name: 'Numero', type: 'string', required: true }, // PascalCase from BA
1114
+ { name: 'Status', type: 'string', required: true },
1115
+ ],
1116
+ views: ['kanban'],
1117
+ kanbanConfig: {
1118
+ groupBy: 'Status',
1119
+ columns: [
1120
+ { key: 'open', labelKey: 'kanban.columns.open' },
1121
+ { key: 'closed', labelKey: 'kanban.columns.closed' },
1122
+ ],
1123
+ },
1124
+ }))
1125
+ const kanban = files.find(f => /KanbanPage\.tsx$/.test(f.path))!
1126
+ // groupBy field accessor MUST be camelCase to match the DTO shape.
1127
+ expect(kanban.content).toMatch(/\['status'\]/)
1128
+ expect(kanban.content).not.toMatch(/\['Status'\]/)
1129
+ // Each column key is preserved verbatim (status value, not field name).
1130
+ expect(kanban.content).toMatch(/key: 'open', labelKey: 'kanban\.columns\.open'/)
1131
+ expect(kanban.content).toMatch(/key: 'closed', labelKey: 'kanban\.columns\.closed'/)
1132
+ })
1133
+
1134
+ it('cards display titleField content', () => {
1135
+ const files = generate(fixture({
1136
+ entity: 'Demande',
1137
+ section: 'demandes',
1138
+ fields: [
1139
+ { name: 'numero', type: 'string', required: true },
1140
+ { name: 'status', type: 'string', required: true },
1141
+ ],
1142
+ views: ['kanban'],
1143
+ kanbanConfig: {
1144
+ groupBy: 'status',
1145
+ titleField: 'numero',
1146
+ columns: [{ key: 'open', labelKey: 'kanban.columns.open' }],
1147
+ },
1148
+ }))
1149
+ const kanban = files.find(f => /KanbanPage\.tsx$/.test(f.path))!
1150
+ expect(kanban.content).toMatch(/item\.numero/)
1151
+ })
1152
+
1153
+ it('throws when "kanban" is in views but kanbanConfig is missing', () => {
1154
+ expect(() => generate(fixture({
1155
+ entity: 'Demande',
1156
+ section: 'demandes',
1157
+ views: ['kanban'],
1158
+ // kanbanConfig intentionally missing
1159
+ }))).toThrow(/kanbanConfig is missing/i)
1160
+ })
1161
+
1162
+ it('emits drag handlers + move mutation import when dnd: true', () => {
1163
+ const files = generate(fixture({
1164
+ entity: 'Demande',
1165
+ section: 'demandes',
1166
+ fields: [
1167
+ { name: 'numero', type: 'string', required: true },
1168
+ { name: 'status', type: 'string', required: true },
1169
+ ],
1170
+ views: ['kanban'],
1171
+ kanbanConfig: {
1172
+ groupBy: 'status',
1173
+ dnd: true,
1174
+ columns: [{ key: 'open', labelKey: 'kanban.columns.open' }],
1175
+ },
1176
+ }))
1177
+ const kanban = files.find(f => /KanbanPage\.tsx$/.test(f.path))!
1178
+ expect(kanban.content).toMatch(/useMoveDemande/)
1179
+ expect(kanban.content).toMatch(/draggable/)
1180
+ expect(kanban.content).toMatch(/onDrop=/)
1181
+ })
1182
+ })
1183
+
1184
+ // ─── Bug 7 — reconduction view first-class ───────────────────────────────
1185
+
1186
+ describe('scaffold-component / generate — Bug 7 (reconduction view)', () => {
1187
+ it('emits ReconductionPage when views includes "reconduction"', () => {
1188
+ const files = generate(fixture({
1189
+ entity: 'Demande',
1190
+ section: 'demandes',
1191
+ fields: [{ name: 'numero', type: 'string', required: true }],
1192
+ views: ['reconduction'],
1193
+ }))
1194
+ const page = files.find(f => /ReconductionPage\.tsx$/.test(f.path))
1195
+ expect(page).toBeDefined()
1196
+ expect(page!.content).toMatch(/<PageTemplate/)
1197
+ expect(page!.content).toMatch(/useReconductDemandes/)
1198
+ })
1199
+
1200
+ it('reconduction button is disabled when no rows selected', () => {
1201
+ const files = generate(fixture({
1202
+ entity: 'Demande',
1203
+ section: 'demandes',
1204
+ fields: [{ name: 'numero', type: 'string', required: true }],
1205
+ views: ['reconduction'],
1206
+ }))
1207
+ const page = files.find(f => /ReconductionPage\.tsx$/.test(f.path))!
1208
+ expect(page.content).toMatch(/selectedIds\.length === 0/)
1209
+ })
1210
+
1211
+ it('emits refuse button + useRefuseReconduction hook when withRefuse: true', () => {
1212
+ const files = generate(fixture({
1213
+ entity: 'Demande',
1214
+ section: 'demandes',
1215
+ fields: [{ name: 'numero', type: 'string', required: true }],
1216
+ views: ['reconduction'],
1217
+ reconductionConfig: {
1218
+ actionLabelKey: 'reconduction.action.reconduct',
1219
+ withRefuse: true,
1220
+ },
1221
+ }))
1222
+ const page = files.find(f => /ReconductionPage\.tsx$/.test(f.path))!
1223
+ expect(page.content).toMatch(/useRefuseReconductionDemandes/)
1224
+ expect(page.content).toMatch(/handleRefuse/)
1225
+ })
1226
+
1227
+ it('does NOT emit refuse hook when withRefuse: false (default)', () => {
1228
+ const files = generate(fixture({
1229
+ entity: 'Demande',
1230
+ section: 'demandes',
1231
+ fields: [{ name: 'numero', type: 'string', required: true }],
1232
+ views: ['reconduction'],
1233
+ }))
1234
+ const page = files.find(f => /ReconductionPage\.tsx$/.test(f.path))!
1235
+ expect(page.content).not.toMatch(/useRefuseReconduction/)
1236
+ })
1237
+ })