@tailor-platform/erp-kit 0.2.1 → 0.2.2

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 (618) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -23
  3. package/dist/cli.mjs +1613 -0
  4. package/package.json +14 -13
  5. package/schemas/app-compose/story.yml +12 -0
  6. package/schemas/module/command.yml +9 -0
  7. package/schemas/module/module.yml +4 -0
  8. package/schemas/module/query.yml +9 -0
  9. package/skills/erp-kit-app-1-requirements/SKILL.md +4 -4
  10. package/skills/erp-kit-app-2-requirements-review/SKILL.md +102 -0
  11. package/skills/erp-kit-app-2-requirements-review/references/best-practices-check.md +66 -0
  12. package/skills/erp-kit-app-2-requirements-review/references/boundary-consistency-check.md +69 -0
  13. package/skills/erp-kit-app-2-requirements-review/references/requirements-report-format.md +25 -0
  14. package/skills/erp-kit-app-3-plan/SKILL.md +157 -0
  15. package/skills/erp-kit-app-3-plan/references/resolver-extraction.md +107 -0
  16. package/skills/erp-kit-app-3-plan/references/screen-extraction.md +74 -0
  17. package/skills/erp-kit-app-3-plan/references/story-extraction.md +86 -0
  18. package/skills/erp-kit-app-4-plan-review/SKILL.md +177 -0
  19. package/skills/erp-kit-app-4-plan-review/references/actor-flow-parity.md +73 -0
  20. package/skills/erp-kit-app-4-plan-review/references/business-flow-story-parity.md +86 -0
  21. package/skills/erp-kit-app-4-plan-review/references/orphan-detection.md +69 -0
  22. package/skills/erp-kit-app-4-plan-review/references/parity-report-format.md +52 -0
  23. package/skills/erp-kit-app-4-plan-review/references/story-resolver-parity.md +83 -0
  24. package/skills/erp-kit-app-4-plan-review/references/story-screen-parity.md +73 -0
  25. package/skills/{erp-kit-app-5-implementation → erp-kit-app-5-impl-backend}/SKILL.md +24 -68
  26. package/skills/erp-kit-app-5-impl-backend/references/app-config.md +38 -0
  27. package/skills/erp-kit-app-5-impl-backend/references/module-wiring.md +48 -0
  28. package/skills/erp-kit-app-5-impl-backend/references/resolver-patterns.md +68 -0
  29. package/skills/erp-kit-app-6-impl-frontend/SKILL.md +74 -0
  30. package/skills/{erp-kit-app-5-implementation/references/frontend.md → erp-kit-app-6-impl-frontend/references/pages.md} +8 -90
  31. package/skills/erp-kit-app-7-impl-review/SKILL.md +176 -0
  32. package/skills/erp-kit-app-7-impl-review/references/impl-parity-report-format.md +52 -0
  33. package/skills/erp-kit-app-7-impl-review/references/module-wiring-parity.md +84 -0
  34. package/skills/erp-kit-app-7-impl-review/references/resolver-doc-code-parity.md +86 -0
  35. package/skills/erp-kit-app-7-impl-review/references/screen-doc-code-parity.md +86 -0
  36. package/skills/erp-kit-module-1-requirements/SKILL.md +126 -0
  37. package/skills/erp-kit-module-1-requirements/references/boundary-analysis.md +51 -0
  38. package/skills/erp-kit-module-1-requirements/references/erp-research.md +57 -0
  39. package/skills/erp-kit-module-1-requirements/references/feature-doc.md +61 -0
  40. package/skills/erp-kit-module-2-requirements-review/SKILL.md +112 -0
  41. package/skills/erp-kit-module-2-requirements-review/references/best-practices-check.md +79 -0
  42. package/skills/erp-kit-module-2-requirements-review/references/boundary-consistency-check.md +70 -0
  43. package/skills/erp-kit-module-2-requirements-review/references/requirements-report-format.md +25 -0
  44. package/skills/erp-kit-module-3-plan/SKILL.md +107 -0
  45. package/skills/erp-kit-module-3-plan/references/command-extraction.md +87 -0
  46. package/skills/erp-kit-module-3-plan/references/model-extraction.md +72 -0
  47. package/skills/erp-kit-module-3-plan/references/query-extraction.md +59 -0
  48. package/skills/erp-kit-module-4-plan-review/SKILL.md +158 -0
  49. package/skills/erp-kit-module-4-plan-review/references/command-model-consistency.md +46 -0
  50. package/skills/erp-kit-module-4-plan-review/references/feature-command-parity.md +97 -0
  51. package/skills/erp-kit-module-4-plan-review/references/feature-model-parity.md +47 -0
  52. package/skills/erp-kit-module-4-plan-review/references/feature-query-parity.md +70 -0
  53. package/skills/erp-kit-module-4-plan-review/references/parity-report-format.md +52 -0
  54. package/skills/erp-kit-module-5-impl/SKILL.md +118 -0
  55. package/skills/erp-kit-module-5-impl/references/command-impl.md +68 -0
  56. package/skills/erp-kit-module-5-impl/references/exports.md +10 -0
  57. package/skills/erp-kit-module-5-impl/references/model-impl.md +45 -0
  58. package/skills/erp-kit-module-5-impl/references/query-impl.md +53 -0
  59. package/skills/erp-kit-module-6-impl-review/SKILL.md +187 -0
  60. package/skills/erp-kit-module-6-impl-review/references/command-doc-code-parity.md +92 -0
  61. package/skills/erp-kit-module-6-impl-review/references/command-doc-test-parity.md +93 -0
  62. package/skills/erp-kit-module-6-impl-review/references/error-implementation-parity.md +95 -0
  63. package/skills/{erp-kit-module-5-impl-review → erp-kit-module-6-impl-review}/references/errors.md +1 -1
  64. package/skills/erp-kit-module-6-impl-review/references/impl-parity-report-format.md +52 -0
  65. package/skills/erp-kit-module-6-impl-review/references/model-doc-code-parity.md +80 -0
  66. package/skills/erp-kit-module-shared/SKILL.md +1 -1
  67. package/skills/erp-kit-module-shared/references/commands.md +1 -1
  68. package/skills/erp-kit-module-shared/references/errors.md +12 -9
  69. package/skills/erp-kit-module-shared/references/queries.md +109 -36
  70. package/skills/erp-kit-module-shared/references/testing.md +10 -0
  71. package/skills/erp-kit-update/SKILL.md +2 -2
  72. package/src/app.ts +1 -1
  73. package/src/commands/check.ts +1 -1
  74. package/src/commands/index.ts +16 -5
  75. package/src/commands/init.test.ts +22 -69
  76. package/src/commands/init.ts +28 -115
  77. package/src/commands/lib/distribute.test.ts +126 -0
  78. package/src/commands/lib/distribute.ts +129 -0
  79. package/src/commands/parse-doc-test-cases.ts +55 -0
  80. package/src/commands/scaffold.test.ts +74 -33
  81. package/src/commands/scaffold.ts +54 -18
  82. package/src/commands/sync-check.test.ts +173 -0
  83. package/src/commands/sync-check.ts +103 -2
  84. package/src/commands/update.test.ts +87 -0
  85. package/src/commands/update.ts +41 -0
  86. package/src/generator/generate-code.test.ts +23 -12
  87. package/src/generator/generate-code.ts +22 -16
  88. package/src/integration.test.ts +1 -1
  89. package/src/module.ts +14 -97
  90. package/src/modules/item-management/README.md +8 -0
  91. package/src/modules/item-management/command/activateItem.generated.ts +1 -1
  92. package/src/modules/item-management/command/activateItem.test.ts +12 -18
  93. package/src/modules/item-management/command/activateItem.ts +9 -5
  94. package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +1 -1
  95. package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +10 -24
  96. package/src/modules/item-management/command/assignItemToTaxonomy.ts +19 -16
  97. package/src/modules/item-management/command/createItem.generated.ts +1 -1
  98. package/src/modules/item-management/command/createItem.test.ts +11 -11
  99. package/src/modules/item-management/command/createItem.ts +16 -7
  100. package/src/modules/item-management/command/createTaxonomyNode.generated.ts +1 -1
  101. package/src/modules/item-management/command/createTaxonomyNode.test.ts +9 -9
  102. package/src/modules/item-management/command/createTaxonomyNode.ts +33 -14
  103. package/src/modules/item-management/command/deactivateItem.generated.ts +1 -1
  104. package/src/modules/item-management/command/deactivateItem.test.ts +12 -18
  105. package/src/modules/item-management/command/deactivateItem.ts +9 -5
  106. package/src/modules/item-management/command/deleteItem.generated.ts +1 -1
  107. package/src/modules/item-management/command/deleteItem.test.ts +10 -16
  108. package/src/modules/item-management/command/deleteItem.ts +9 -5
  109. package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +1 -1
  110. package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +10 -16
  111. package/src/modules/item-management/command/deleteTaxonomyNode.ts +22 -12
  112. package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +1 -1
  113. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +10 -10
  114. package/src/modules/item-management/command/moveTaxonomyNode.ts +63 -19
  115. package/src/modules/item-management/command/reactivateItem.generated.ts +1 -1
  116. package/src/modules/item-management/command/reactivateItem.test.ts +12 -18
  117. package/src/modules/item-management/command/reactivateItem.ts +9 -5
  118. package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +1 -1
  119. package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +9 -16
  120. package/src/modules/item-management/command/removeItemFromTaxonomy.ts +11 -6
  121. package/src/modules/item-management/command/updateItem.generated.ts +1 -1
  122. package/src/modules/item-management/command/updateItem.test.ts +16 -16
  123. package/src/modules/item-management/command/updateItem.ts +11 -6
  124. package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +1 -1
  125. package/src/modules/item-management/command/updateTaxonomyNode.test.ts +14 -20
  126. package/src/modules/item-management/command/updateTaxonomyNode.ts +9 -6
  127. package/src/modules/item-management/docs/commands/ActivateItem.md +8 -0
  128. package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +7 -0
  129. package/src/modules/item-management/docs/commands/CreateItem.md +10 -0
  130. package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +9 -0
  131. package/src/modules/item-management/docs/commands/DeactivateItem.md +8 -0
  132. package/src/modules/item-management/docs/commands/DeleteItem.md +7 -0
  133. package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +7 -0
  134. package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +10 -0
  135. package/src/modules/item-management/docs/commands/ReactivateItem.md +8 -0
  136. package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +5 -0
  137. package/src/modules/item-management/docs/commands/UpdateItem.md +15 -0
  138. package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +9 -0
  139. package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +8 -0
  140. package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +7 -0
  141. package/src/modules/item-management/docs/queries/DetectCircularReference.md +9 -0
  142. package/src/modules/item-management/docs/queries/GetItem.md +9 -0
  143. package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +5 -0
  144. package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +7 -0
  145. package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +5 -0
  146. package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +6 -0
  147. package/src/modules/item-management/index.ts +0 -51
  148. package/src/modules/item-management/lib/errors.generated.ts +24 -24
  149. package/src/modules/item-management/lib/permissions.generated.ts +1 -1
  150. package/src/modules/item-management/lib/types.ts +1 -1
  151. package/src/modules/item-management/module.ts +1 -1
  152. package/src/modules/item-management/query/calculateNodeDepth.generated.ts +1 -1
  153. package/src/modules/item-management/query/calculateNodeDepth.test.ts +21 -6
  154. package/src/modules/item-management/query/calculateNodeDepth.ts +2 -2
  155. package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +1 -1
  156. package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +17 -5
  157. package/src/modules/item-management/query/calculateSubtreeDepth.ts +2 -2
  158. package/src/modules/item-management/query/detectCircularReference.generated.ts +1 -1
  159. package/src/modules/item-management/query/detectCircularReference.test.ts +25 -7
  160. package/src/modules/item-management/query/detectCircularReference.ts +4 -4
  161. package/src/modules/item-management/query/getItem.generated.ts +1 -1
  162. package/src/modules/item-management/query/getItem.test.ts +25 -7
  163. package/src/modules/item-management/query/getItem.ts +2 -2
  164. package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +1 -1
  165. package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +9 -3
  166. package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +2 -2
  167. package/src/modules/item-management/query/getTaxonomyNode.generated.ts +1 -1
  168. package/src/modules/item-management/query/getTaxonomyNode.test.ts +17 -5
  169. package/src/modules/item-management/query/getTaxonomyNode.ts +2 -2
  170. package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +1 -1
  171. package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +9 -3
  172. package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +2 -2
  173. package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +1 -1
  174. package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +13 -4
  175. package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +2 -2
  176. package/src/modules/item-management/tailor.config.ts +6 -4
  177. package/src/modules/item-management/tailor.d.ts +13 -0
  178. package/src/modules/primitives/README.md +8 -0
  179. package/src/modules/primitives/command/activateCategory.generated.ts +1 -1
  180. package/src/modules/primitives/command/activateCategory.test.ts +8 -18
  181. package/src/modules/primitives/command/activateCategory.ts +9 -5
  182. package/src/modules/primitives/command/activateCurrency.generated.ts +1 -1
  183. package/src/modules/primitives/command/activateCurrency.test.ts +8 -18
  184. package/src/modules/primitives/command/activateCurrency.ts +9 -5
  185. package/src/modules/primitives/command/activateUnit.generated.ts +1 -1
  186. package/src/modules/primitives/command/activateUnit.test.ts +8 -15
  187. package/src/modules/primitives/command/activateUnit.ts +9 -5
  188. package/src/modules/primitives/command/createCategory.generated.ts +1 -1
  189. package/src/modules/primitives/command/createCategory.test.ts +29 -44
  190. package/src/modules/primitives/command/createCategory.ts +9 -5
  191. package/src/modules/primitives/command/createCurrency.generated.ts +1 -1
  192. package/src/modules/primitives/command/createCurrency.test.ts +53 -78
  193. package/src/modules/primitives/command/createCurrency.ts +9 -6
  194. package/src/modules/primitives/command/createExchangeRate.generated.ts +1 -1
  195. package/src/modules/primitives/command/createExchangeRate.test.ts +59 -97
  196. package/src/modules/primitives/command/createExchangeRate.ts +13 -7
  197. package/src/modules/primitives/command/createUnit.generated.ts +1 -1
  198. package/src/modules/primitives/command/createUnit.test.ts +59 -90
  199. package/src/modules/primitives/command/createUnit.ts +9 -6
  200. package/src/modules/primitives/command/deactivateCategory.generated.ts +1 -1
  201. package/src/modules/primitives/command/deactivateCategory.test.ts +15 -33
  202. package/src/modules/primitives/command/deactivateCategory.ts +9 -5
  203. package/src/modules/primitives/command/deactivateCurrency.generated.ts +1 -1
  204. package/src/modules/primitives/command/deactivateCurrency.test.ts +12 -26
  205. package/src/modules/primitives/command/deactivateCurrency.ts +9 -5
  206. package/src/modules/primitives/command/deactivateUnit.generated.ts +1 -1
  207. package/src/modules/primitives/command/deactivateUnit.test.ts +15 -30
  208. package/src/modules/primitives/command/deactivateUnit.ts +14 -7
  209. package/src/modules/primitives/command/setBaseCurrency.generated.ts +1 -1
  210. package/src/modules/primitives/command/setBaseCurrency.test.ts +18 -40
  211. package/src/modules/primitives/command/setBaseCurrency.ts +15 -7
  212. package/src/modules/primitives/command/setReferenceUnit.generated.ts +1 -1
  213. package/src/modules/primitives/command/setReferenceUnit.test.ts +22 -44
  214. package/src/modules/primitives/command/setReferenceUnit.ts +21 -9
  215. package/src/modules/primitives/docs/commands/ActivateCategory.md +6 -0
  216. package/src/modules/primitives/docs/commands/ActivateCurrency.md +6 -0
  217. package/src/modules/primitives/docs/commands/ActivateUnit.md +6 -0
  218. package/src/modules/primitives/docs/commands/CreateCategory.md +6 -0
  219. package/src/modules/primitives/docs/commands/CreateCurrency.md +10 -0
  220. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +11 -0
  221. package/src/modules/primitives/docs/commands/CreateUnit.md +10 -0
  222. package/src/modules/primitives/docs/commands/DeactivateCategory.md +7 -0
  223. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +7 -0
  224. package/src/modules/primitives/docs/commands/DeactivateUnit.md +7 -0
  225. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +7 -0
  226. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +7 -0
  227. package/src/modules/primitives/docs/queries/ConvertAmount.md +14 -0
  228. package/src/modules/primitives/docs/queries/ConvertQuantity.md +13 -0
  229. package/src/modules/primitives/docs/queries/GetBaseCurrency.md +5 -0
  230. package/src/modules/primitives/docs/queries/GetCurrency.md +7 -0
  231. package/src/modules/primitives/docs/queries/GetUnit.md +7 -0
  232. package/src/modules/primitives/docs/queries/GetUoMCategory.md +7 -0
  233. package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +15 -5
  234. package/src/modules/primitives/index.ts +0 -49
  235. package/src/modules/primitives/lib/errors.generated.ts +23 -23
  236. package/src/modules/primitives/lib/permissions.generated.ts +1 -1
  237. package/src/modules/primitives/lib/types.ts +1 -1
  238. package/src/modules/primitives/module.ts +1 -1
  239. package/src/modules/primitives/query/convertAmount.generated.ts +1 -1
  240. package/src/modules/primitives/query/convertAmount.test.ts +110 -77
  241. package/src/modules/primitives/query/convertAmount.ts +61 -47
  242. package/src/modules/primitives/query/convertQuantity.generated.ts +1 -1
  243. package/src/modules/primitives/query/convertQuantity.test.ts +99 -69
  244. package/src/modules/primitives/query/convertQuantity.ts +12 -10
  245. package/src/modules/primitives/query/getBaseCurrency.generated.ts +1 -1
  246. package/src/modules/primitives/query/getBaseCurrency.test.ts +10 -4
  247. package/src/modules/primitives/query/getBaseCurrency.ts +2 -2
  248. package/src/modules/primitives/query/getCurrency.generated.ts +1 -1
  249. package/src/modules/primitives/query/getCurrency.test.ts +17 -5
  250. package/src/modules/primitives/query/getCurrency.ts +2 -2
  251. package/src/modules/primitives/query/getUnit.generated.ts +1 -1
  252. package/src/modules/primitives/query/getUnit.test.ts +17 -5
  253. package/src/modules/primitives/query/getUnit.ts +2 -2
  254. package/src/modules/primitives/query/getUoMCategory.generated.ts +1 -1
  255. package/src/modules/primitives/query/getUoMCategory.test.ts +17 -5
  256. package/src/modules/primitives/query/getUoMCategory.ts +2 -2
  257. package/src/modules/primitives/query/listUnitsByCategory.generated.ts +1 -1
  258. package/src/modules/primitives/query/listUnitsByCategory.test.ts +80 -0
  259. package/src/modules/primitives/query/listUnitsByCategory.ts +19 -3
  260. package/src/modules/primitives/tailor.config.ts +6 -4
  261. package/src/modules/primitives/tailor.d.ts +13 -0
  262. package/src/modules/product-management/README.md +52 -0
  263. package/src/modules/product-management/command/activateProduct.generated.ts +6 -0
  264. package/src/modules/product-management/command/activateProduct.test.ts +40 -0
  265. package/src/modules/product-management/command/activateProduct.ts +42 -0
  266. package/src/modules/product-management/command/assignProductToCategory.generated.ts +6 -0
  267. package/src/modules/product-management/command/assignProductToCategory.test.ts +90 -0
  268. package/src/modules/product-management/command/assignProductToCategory.ts +62 -0
  269. package/src/modules/product-management/command/createProduct.generated.ts +6 -0
  270. package/src/modules/product-management/command/createProduct.test.ts +149 -0
  271. package/src/modules/product-management/command/createProduct.ts +73 -0
  272. package/src/modules/product-management/command/createProductAttribute.generated.ts +6 -0
  273. package/src/modules/product-management/command/createProductAttribute.test.ts +70 -0
  274. package/src/modules/product-management/command/createProductAttribute.ts +53 -0
  275. package/src/modules/product-management/command/createProductAttributeValue.generated.ts +6 -0
  276. package/src/modules/product-management/command/createProductAttributeValue.test.ts +68 -0
  277. package/src/modules/product-management/command/createProductAttributeValue.ts +63 -0
  278. package/src/modules/product-management/command/createProductCategory.generated.ts +6 -0
  279. package/src/modules/product-management/command/createProductCategory.test.ts +135 -0
  280. package/src/modules/product-management/command/createProductCategory.ts +82 -0
  281. package/src/modules/product-management/command/deactivateProduct.generated.ts +6 -0
  282. package/src/modules/product-management/command/deactivateProduct.test.ts +40 -0
  283. package/src/modules/product-management/command/deactivateProduct.ts +42 -0
  284. package/src/modules/product-management/command/deleteProduct.generated.ts +6 -0
  285. package/src/modules/product-management/command/deleteProduct.test.ts +42 -0
  286. package/src/modules/product-management/command/deleteProduct.ts +42 -0
  287. package/src/modules/product-management/command/deleteProductAttribute.generated.ts +6 -0
  288. package/src/modules/product-management/command/deleteProductAttribute.test.ts +49 -0
  289. package/src/modules/product-management/command/deleteProductAttribute.ts +45 -0
  290. package/src/modules/product-management/command/deleteProductAttributeValue.generated.ts +6 -0
  291. package/src/modules/product-management/command/deleteProductAttributeValue.test.ts +71 -0
  292. package/src/modules/product-management/command/deleteProductAttributeValue.ts +68 -0
  293. package/src/modules/product-management/command/deleteProductCategory.generated.ts +6 -0
  294. package/src/modules/product-management/command/deleteProductCategory.test.ts +74 -0
  295. package/src/modules/product-management/command/deleteProductCategory.ts +53 -0
  296. package/src/modules/product-management/command/generateVariants.generated.ts +6 -0
  297. package/src/modules/product-management/command/generateVariants.test.ts +365 -0
  298. package/src/modules/product-management/command/generateVariants.ts +168 -0
  299. package/src/modules/product-management/command/moveProductCategory.generated.ts +6 -0
  300. package/src/modules/product-management/command/moveProductCategory.test.ts +170 -0
  301. package/src/modules/product-management/command/moveProductCategory.ts +124 -0
  302. package/src/modules/product-management/command/reactivateProduct.generated.ts +6 -0
  303. package/src/modules/product-management/command/reactivateProduct.test.ts +40 -0
  304. package/src/modules/product-management/command/reactivateProduct.ts +42 -0
  305. package/src/modules/product-management/command/removeProductFromCategory.generated.ts +6 -0
  306. package/src/modules/product-management/command/removeProductFromCategory.test.ts +42 -0
  307. package/src/modules/product-management/command/removeProductFromCategory.ts +32 -0
  308. package/src/modules/product-management/command/setProductAttributeAssignment.generated.ts +6 -0
  309. package/src/modules/product-management/command/setProductAttributeAssignment.test.ts +206 -0
  310. package/src/modules/product-management/command/setProductAttributeAssignment.ts +102 -0
  311. package/src/modules/product-management/command/updateProduct.generated.ts +6 -0
  312. package/src/modules/product-management/command/updateProduct.test.ts +168 -0
  313. package/src/modules/product-management/command/updateProduct.ts +95 -0
  314. package/src/modules/product-management/command/updateProductAttribute.generated.ts +6 -0
  315. package/src/modules/product-management/command/updateProductAttribute.test.ts +101 -0
  316. package/src/modules/product-management/command/updateProductAttribute.ts +68 -0
  317. package/src/modules/product-management/command/updateProductAttributeValue.generated.ts +6 -0
  318. package/src/modules/product-management/command/updateProductAttributeValue.test.ts +80 -0
  319. package/src/modules/product-management/command/updateProductAttributeValue.ts +58 -0
  320. package/src/modules/product-management/command/updateProductCategory.generated.ts +6 -0
  321. package/src/modules/product-management/command/updateProductCategory.test.ts +80 -0
  322. package/src/modules/product-management/command/updateProductCategory.ts +66 -0
  323. package/src/modules/product-management/db/product.ts +47 -0
  324. package/src/modules/product-management/db/productAttribute.ts +26 -0
  325. package/src/modules/product-management/db/productAttributeAssignment.ts +58 -0
  326. package/src/modules/product-management/db/productAttributeValue.ts +39 -0
  327. package/src/modules/product-management/db/productCategory.ts +34 -0
  328. package/src/modules/product-management/db/productCategoryAssignment.ts +49 -0
  329. package/src/modules/product-management/db/productVariant.ts +52 -0
  330. package/src/modules/product-management/docs/commands/ActivateProduct.md +39 -0
  331. package/src/modules/product-management/docs/commands/AssignProductToCategory.md +43 -0
  332. package/src/modules/product-management/docs/commands/CreateProduct.md +48 -0
  333. package/src/modules/product-management/docs/commands/CreateProductAttribute.md +39 -0
  334. package/src/modules/product-management/docs/commands/CreateProductAttributeValue.md +42 -0
  335. package/src/modules/product-management/docs/commands/CreateProductCategory.md +54 -0
  336. package/src/modules/product-management/docs/commands/DeactivateProduct.md +39 -0
  337. package/src/modules/product-management/docs/commands/DeleteProduct.md +42 -0
  338. package/src/modules/product-management/docs/commands/DeleteProductAttribute.md +39 -0
  339. package/src/modules/product-management/docs/commands/DeleteProductAttributeValue.md +42 -0
  340. package/src/modules/product-management/docs/commands/DeleteProductCategory.md +43 -0
  341. package/src/modules/product-management/docs/commands/GenerateVariants.md +68 -0
  342. package/src/modules/product-management/docs/commands/MoveProductCategory.md +54 -0
  343. package/src/modules/product-management/docs/commands/ReactivateProduct.md +38 -0
  344. package/src/modules/product-management/docs/commands/RemoveProductFromCategory.md +34 -0
  345. package/src/modules/product-management/docs/commands/SetProductAttributeAssignment.md +62 -0
  346. package/src/modules/product-management/docs/commands/UpdateProduct.md +61 -0
  347. package/src/modules/product-management/docs/commands/UpdateProductAttribute.md +46 -0
  348. package/src/modules/product-management/docs/commands/UpdateProductAttributeValue.md +47 -0
  349. package/src/modules/product-management/docs/commands/UpdateProductCategory.md +46 -0
  350. package/src/modules/product-management/docs/features/attribute-management.md +48 -0
  351. package/src/modules/product-management/docs/features/product-category.md +71 -0
  352. package/src/modules/product-management/docs/features/product-lifecycle.md +66 -0
  353. package/src/modules/product-management/docs/features/variant-generation.md +77 -0
  354. package/src/modules/product-management/docs/models/Product.md +58 -0
  355. package/src/modules/product-management/docs/models/ProductAttribute.md +37 -0
  356. package/src/modules/product-management/docs/models/ProductAttributeAssignment.md +41 -0
  357. package/src/modules/product-management/docs/models/ProductAttributeValue.md +40 -0
  358. package/src/modules/product-management/docs/models/ProductCategory.md +46 -0
  359. package/src/modules/product-management/docs/models/ProductCategoryAssignment.md +37 -0
  360. package/src/modules/product-management/docs/models/ProductVariant.md +41 -0
  361. package/src/modules/product-management/docs/queries/CalculateCategoryDepth.md +47 -0
  362. package/src/modules/product-management/docs/queries/DetectCategoryCircularReference.md +51 -0
  363. package/src/modules/product-management/docs/queries/GetProduct.md +42 -0
  364. package/src/modules/product-management/docs/queries/GetProductAttribute.md +42 -0
  365. package/src/modules/product-management/docs/queries/GetProductAttributeAssignment.md +34 -0
  366. package/src/modules/product-management/docs/queries/GetProductAttributeValue.md +40 -0
  367. package/src/modules/product-management/docs/queries/GetProductCategory.md +42 -0
  368. package/src/modules/product-management/docs/queries/GetProductCategoryAssignment.md +34 -0
  369. package/src/modules/product-management/docs/queries/GetProductVariant.md +41 -0
  370. package/src/modules/product-management/docs/queries/ListAttributeAssignmentsByAttribute.md +34 -0
  371. package/src/modules/product-management/docs/queries/ListCategoryAssignmentsByProduct.md +35 -0
  372. package/src/modules/product-management/docs/queries/ListProductAttributeAssignments.md +34 -0
  373. package/src/modules/product-management/docs/queries/ListProductAttributeValues.md +36 -0
  374. package/src/modules/product-management/docs/queries/ListProductCategoryAssignments.md +34 -0
  375. package/src/modules/product-management/docs/queries/ListProductCategoryChildren.md +34 -0
  376. package/src/modules/product-management/docs/queries/ListProductVariants.md +34 -0
  377. package/src/modules/product-management/generated/enums.ts +9 -0
  378. package/src/modules/product-management/generated/kysely-tailordb.ts +100 -0
  379. package/src/modules/product-management/index.ts +2 -0
  380. package/src/modules/product-management/lib/_db_deps.ts +17 -0
  381. package/src/modules/product-management/lib/errors.generated.ts +152 -0
  382. package/src/modules/product-management/lib/permissions.generated.ts +25 -0
  383. package/src/modules/product-management/lib/types.ts +51 -0
  384. package/src/modules/product-management/module.ts +201 -0
  385. package/src/modules/product-management/query/calculateCategoryDepth.generated.ts +5 -0
  386. package/src/modules/product-management/query/calculateCategoryDepth.test.ts +72 -0
  387. package/src/modules/product-management/query/calculateCategoryDepth.ts +37 -0
  388. package/src/modules/product-management/query/detectCategoryCircularReference.generated.ts +5 -0
  389. package/src/modules/product-management/query/detectCategoryCircularReference.test.ts +72 -0
  390. package/src/modules/product-management/query/detectCategoryCircularReference.ts +44 -0
  391. package/src/modules/product-management/query/getProduct.generated.ts +5 -0
  392. package/src/modules/product-management/query/getProduct.test.ts +59 -0
  393. package/src/modules/product-management/query/getProduct.ts +18 -0
  394. package/src/modules/product-management/query/getProductAttribute.generated.ts +5 -0
  395. package/src/modules/product-management/query/getProductAttribute.test.ts +59 -0
  396. package/src/modules/product-management/query/getProductAttribute.ts +18 -0
  397. package/src/modules/product-management/query/getProductAttributeAssignment.generated.ts +5 -0
  398. package/src/modules/product-management/query/getProductAttributeAssignment.test.ts +37 -0
  399. package/src/modules/product-management/query/getProductAttributeAssignment.ts +18 -0
  400. package/src/modules/product-management/query/getProductAttributeValue.generated.ts +5 -0
  401. package/src/modules/product-management/query/getProductAttributeValue.test.ts +31 -0
  402. package/src/modules/product-management/query/getProductAttributeValue.ts +16 -0
  403. package/src/modules/product-management/query/getProductCategory.generated.ts +5 -0
  404. package/src/modules/product-management/query/getProductCategory.test.ts +59 -0
  405. package/src/modules/product-management/query/getProductCategory.ts +18 -0
  406. package/src/modules/product-management/query/getProductCategoryAssignment.generated.ts +5 -0
  407. package/src/modules/product-management/query/getProductCategoryAssignment.test.ts +37 -0
  408. package/src/modules/product-management/query/getProductCategoryAssignment.ts +18 -0
  409. package/src/modules/product-management/query/getProductVariant.generated.ts +5 -0
  410. package/src/modules/product-management/query/getProductVariant.test.ts +43 -0
  411. package/src/modules/product-management/query/getProductVariant.ts +20 -0
  412. package/src/modules/product-management/query/listAttributeAssignmentsByAttribute.generated.ts +5 -0
  413. package/src/modules/product-management/query/listAttributeAssignmentsByAttribute.test.ts +31 -0
  414. package/src/modules/product-management/query/listAttributeAssignmentsByAttribute.ts +16 -0
  415. package/src/modules/product-management/query/listCategoryAssignmentsByProduct.generated.ts +5 -0
  416. package/src/modules/product-management/query/listCategoryAssignmentsByProduct.test.ts +31 -0
  417. package/src/modules/product-management/query/listCategoryAssignmentsByProduct.ts +16 -0
  418. package/src/modules/product-management/query/listProductAttributeAssignments.generated.ts +5 -0
  419. package/src/modules/product-management/query/listProductAttributeAssignments.test.ts +31 -0
  420. package/src/modules/product-management/query/listProductAttributeAssignments.ts +16 -0
  421. package/src/modules/product-management/query/listProductAttributeValues.generated.ts +5 -0
  422. package/src/modules/product-management/query/listProductAttributeValues.test.ts +31 -0
  423. package/src/modules/product-management/query/listProductAttributeValues.ts +17 -0
  424. package/src/modules/product-management/query/listProductCategoryAssignments.generated.ts +5 -0
  425. package/src/modules/product-management/query/listProductCategoryAssignments.test.ts +31 -0
  426. package/src/modules/product-management/query/listProductCategoryAssignments.ts +16 -0
  427. package/src/modules/product-management/query/listProductCategoryChildren.generated.ts +5 -0
  428. package/src/modules/product-management/query/listProductCategoryChildren.test.ts +31 -0
  429. package/src/modules/product-management/query/listProductCategoryChildren.ts +16 -0
  430. package/src/modules/product-management/query/listProductVariants.generated.ts +5 -0
  431. package/src/modules/product-management/query/listProductVariants.test.ts +31 -0
  432. package/src/modules/product-management/query/listProductVariants.ts +16 -0
  433. package/src/modules/product-management/tailor.config.ts +13 -0
  434. package/src/modules/product-management/tailor.d.ts +13 -0
  435. package/src/modules/product-management/testing/fixtures.ts +151 -0
  436. package/src/modules/user-management/README.md +9 -3
  437. package/src/modules/user-management/command/activateUser.generated.ts +1 -1
  438. package/src/modules/user-management/command/activateUser.test.ts +12 -65
  439. package/src/modules/user-management/command/activateUser.ts +5 -20
  440. package/src/modules/user-management/command/assignPermissionToRole.generated.ts +1 -1
  441. package/src/modules/user-management/command/assignPermissionToRole.test.ts +25 -60
  442. package/src/modules/user-management/command/assignPermissionToRole.ts +5 -24
  443. package/src/modules/user-management/command/assignRoleToUser.generated.ts +1 -1
  444. package/src/modules/user-management/command/assignRoleToUser.test.ts +35 -87
  445. package/src/modules/user-management/command/assignRoleToUser.ts +5 -24
  446. package/src/modules/user-management/command/createPermission.generated.ts +1 -1
  447. package/src/modules/user-management/command/createPermission.test.ts +23 -33
  448. package/src/modules/user-management/command/createPermission.ts +4 -5
  449. package/src/modules/user-management/command/createRole.generated.ts +1 -1
  450. package/src/modules/user-management/command/createRole.test.ts +17 -27
  451. package/src/modules/user-management/command/createRole.ts +4 -5
  452. package/src/modules/user-management/command/createUser.generated.ts +1 -1
  453. package/src/modules/user-management/command/createUser.test.ts +31 -118
  454. package/src/modules/user-management/command/createUser.ts +7 -25
  455. package/src/modules/user-management/command/deactivateUser.generated.ts +1 -1
  456. package/src/modules/user-management/command/deactivateUser.test.ts +12 -65
  457. package/src/modules/user-management/command/deactivateUser.ts +6 -21
  458. package/src/modules/user-management/command/reactivateUser.generated.ts +1 -1
  459. package/src/modules/user-management/command/reactivateUser.test.ts +13 -66
  460. package/src/modules/user-management/command/reactivateUser.ts +5 -20
  461. package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +1 -1
  462. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +24 -62
  463. package/src/modules/user-management/command/revokePermissionFromRole.ts +5 -24
  464. package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +1 -1
  465. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +24 -60
  466. package/src/modules/user-management/command/revokeRoleFromUser.ts +5 -24
  467. package/src/modules/user-management/docs/commands/ActivateUser.md +7 -0
  468. package/src/modules/user-management/docs/commands/AssignPermissionToRole.md +7 -0
  469. package/src/modules/user-management/docs/commands/AssignRoleToUser.md +9 -0
  470. package/src/modules/user-management/docs/commands/CreatePermission.md +12 -0
  471. package/src/modules/user-management/docs/commands/CreateRole.md +9 -0
  472. package/src/modules/user-management/docs/commands/CreateUser.md +11 -0
  473. package/src/modules/user-management/docs/commands/DeactivateUser.md +7 -0
  474. package/src/modules/user-management/docs/commands/ReactivateUser.md +7 -0
  475. package/src/modules/user-management/docs/commands/RevokePermissionFromRole.md +7 -0
  476. package/src/modules/user-management/docs/commands/RevokeRoleFromUser.md +7 -0
  477. package/src/modules/user-management/index.ts +0 -30
  478. package/src/modules/user-management/lib/errors.generated.ts +14 -14
  479. package/src/modules/user-management/lib/permissions.generated.ts +1 -2
  480. package/src/modules/user-management/lib/recomputeUserPermissions.ts +4 -3
  481. package/src/modules/user-management/lib/types.ts +1 -1
  482. package/src/modules/user-management/module.ts +2 -7
  483. package/src/modules/user-management/tailor.config.ts +6 -4
  484. package/src/modules/user-management/tailor.d.ts +13 -0
  485. package/src/modules/user-management/testing/fixtures.ts +1 -20
  486. package/src/schemas.ts +1 -1
  487. package/src/{modules/shared → shared}/defineCommand.test.ts +23 -7
  488. package/src/{modules/shared → shared}/defineCommand.ts +19 -10
  489. package/src/{modules/shared/internal.ts → shared/index.ts} +9 -1
  490. package/src/shared/pagination.test.ts +43 -0
  491. package/src/shared/pagination.ts +22 -0
  492. package/src/{modules/shared → shared}/types.ts +13 -0
  493. package/src/{modules/testing → testing}/index.ts +14 -7
  494. package/src/testing.ts +1 -1
  495. package/src/util.ts +8 -0
  496. package/templates/config/license.config.json +4 -0
  497. package/templates/scaffold/app/backend/.env.example +1 -0
  498. package/templates/scaffold/app/backend/__dot__gitignore +4 -0
  499. package/templates/scaffold/app/backend/eslint.config.js +32 -0
  500. package/templates/scaffold/app/backend/package.json +31 -0
  501. package/templates/scaffold/app/backend/seed/data/AuditEvent.jsonl +0 -0
  502. package/templates/scaffold/app/backend/seed/data/Permission.jsonl +0 -0
  503. package/templates/scaffold/app/backend/seed/data/Permission.schema.ts +20 -0
  504. package/templates/scaffold/app/backend/seed/data/Role.jsonl +0 -0
  505. package/templates/scaffold/app/backend/seed/data/Role.schema.ts +20 -0
  506. package/templates/scaffold/app/backend/seed/data/RolePermission.jsonl +0 -0
  507. package/templates/scaffold/app/backend/seed/data/RolePermission.schema.ts +24 -0
  508. package/templates/scaffold/app/backend/seed/data/User.jsonl +1 -0
  509. package/templates/scaffold/app/backend/seed/data/User.schema.ts +20 -0
  510. package/templates/scaffold/app/backend/seed/data/UserRole.jsonl +0 -0
  511. package/templates/scaffold/app/backend/seed/data/UserRole.schema.ts +24 -0
  512. package/templates/scaffold/app/backend/seed/data/_User.jsonl +1 -0
  513. package/templates/scaffold/app/backend/seed/data/_User.schema.ts +30 -0
  514. package/templates/scaffold/app/backend/seed/exec.mjs +659 -0
  515. package/templates/scaffold/app/backend/src/executors/permissionCreated.ts +3 -0
  516. package/templates/scaffold/app/backend/src/executors/permissionDeleted.ts +3 -0
  517. package/templates/scaffold/app/backend/src/generated/kysely-tailordb.ts +83 -0
  518. package/templates/scaffold/app/backend/src/modules.ts +9 -0
  519. package/templates/scaffold/app/backend/src/resolvers/createUser.ts +46 -0
  520. package/templates/scaffold/app/backend/tailor.config.ts +68 -0
  521. package/templates/scaffold/app/backend/tailor.d.ts +15 -0
  522. package/templates/scaffold/app/backend/tsconfig.json +19 -0
  523. package/templates/scaffold/app/docs/actors/.gitkeep +0 -0
  524. package/templates/scaffold/app/docs/business-flow/.gitkeep +0 -0
  525. package/templates/scaffold/app/docs/resolver/.gitkeep +0 -0
  526. package/templates/scaffold/app/docs/screen/.gitkeep +0 -0
  527. package/templates/scaffold/app/frontend/.env.example +2 -0
  528. package/templates/scaffold/app/frontend/__dot__gitignore +3 -0
  529. package/templates/scaffold/app/frontend/components.json +23 -0
  530. package/templates/scaffold/app/frontend/eslint.config.js +48 -0
  531. package/templates/scaffold/app/frontend/index.html +13 -0
  532. package/templates/scaffold/app/frontend/package.json +53 -0
  533. package/templates/scaffold/app/frontend/scripts/generate-graphql.mjs +6 -0
  534. package/templates/scaffold/app/frontend/src/App.tsx +58 -0
  535. package/templates/scaffold/app/frontend/src/components/composed/empty-state.tsx +26 -0
  536. package/templates/scaffold/app/frontend/src/components/composed/error-fallback.tsx +28 -0
  537. package/templates/scaffold/app/frontend/src/components/composed/loading.tsx +13 -0
  538. package/templates/scaffold/app/frontend/src/components/ui/badge.tsx +39 -0
  539. package/templates/scaffold/app/frontend/src/components/ui/button.tsx +60 -0
  540. package/templates/scaffold/app/frontend/src/components/ui/card.tsx +75 -0
  541. package/templates/scaffold/app/frontend/src/components/ui/form.tsx +152 -0
  542. package/templates/scaffold/app/frontend/src/components/ui/input.tsx +21 -0
  543. package/templates/scaffold/app/frontend/src/components/ui/label.tsx +21 -0
  544. package/templates/scaffold/app/frontend/src/components/ui/spinner.tsx +16 -0
  545. package/templates/scaffold/app/frontend/src/components/ui/table.tsx +90 -0
  546. package/templates/scaffold/app/frontend/src/graphql/generated/graphql-env.d.ts +103 -0
  547. package/templates/scaffold/app/frontend/src/graphql/generated/schema.graphql +1235 -0
  548. package/templates/scaffold/app/frontend/src/graphql/index.ts +15 -0
  549. package/templates/scaffold/app/frontend/src/index.css +5 -0
  550. package/templates/scaffold/app/frontend/src/lib/auth-client.ts +17 -0
  551. package/templates/scaffold/app/frontend/src/lib/utils.ts +6 -0
  552. package/templates/scaffold/app/frontend/src/main.tsx +10 -0
  553. package/templates/scaffold/app/frontend/src/pages/page.tsx +20 -0
  554. package/templates/scaffold/app/frontend/src/pages/user-management/page.tsx +19 -0
  555. package/templates/scaffold/app/frontend/src/pages/user-management/profile/page.tsx +97 -0
  556. package/templates/scaffold/app/frontend/src/pages/user-management/user/[id]/components/user-detail.tsx +58 -0
  557. package/templates/scaffold/app/frontend/src/pages/user-management/user/[id]/page.tsx +51 -0
  558. package/templates/scaffold/app/frontend/src/pages/user-management/user/components/users-table.tsx +101 -0
  559. package/templates/scaffold/app/frontend/src/pages/user-management/user/create/components/create-user-form.tsx +99 -0
  560. package/templates/scaffold/app/frontend/src/pages/user-management/user/create/page.tsx +19 -0
  561. package/templates/scaffold/app/frontend/src/pages/user-management/user/page.tsx +61 -0
  562. package/templates/scaffold/app/frontend/src/providers/graphql-provider.tsx +21 -0
  563. package/templates/scaffold/app/frontend/tsconfig.app.json +35 -0
  564. package/templates/scaffold/app/frontend/tsconfig.json +16 -0
  565. package/templates/scaffold/app/frontend/tsconfig.node.json +23 -0
  566. package/templates/scaffold/app/frontend/vite.config.ts +23 -0
  567. package/templates/scaffold/module/command/.gitkeep +0 -0
  568. package/templates/scaffold/module/db/.gitkeep +0 -0
  569. package/templates/scaffold/module/executor/.gitkeep +0 -0
  570. package/templates/scaffold/module/generated/.gitkeep +0 -0
  571. package/templates/scaffold/module/index.ts +2 -0
  572. package/templates/scaffold/module/lib/errors.ts +1 -0
  573. package/templates/scaffold/module/lib/types.ts +4 -0
  574. package/templates/scaffold/module/module.ts +7 -0
  575. package/templates/scaffold/module/permissions.ts +3 -0
  576. package/templates/scaffold/module/query/.gitkeep +0 -0
  577. package/templates/scaffold/module/tailor.config.ts +13 -0
  578. package/templates/scaffold/module/testing/fixtures.ts +1 -0
  579. package/templates/workflows/erp-kit-check.yml +37 -0
  580. package/dist/cli.js +0 -1654
  581. package/skills/erp-kit-app-2-breakdown/SKILL.md +0 -88
  582. package/skills/erp-kit-app-3-doc-review/SKILL.md +0 -112
  583. package/skills/erp-kit-app-4-impl-spec/SKILL.md +0 -116
  584. package/skills/erp-kit-app-5-implementation/references/backend.md +0 -232
  585. package/skills/erp-kit-module-1-docs/SKILL.md +0 -111
  586. package/skills/erp-kit-module-2-feature-breakdown/SKILL.md +0 -76
  587. package/skills/erp-kit-module-3-doc-review/SKILL.md +0 -294
  588. package/skills/erp-kit-module-4-tdd/SKILL.md +0 -94
  589. package/skills/erp-kit-module-4-tdd/references/exports.md +0 -8
  590. package/skills/erp-kit-module-5-impl-review/SKILL.md +0 -410
  591. package/src/commands/scaffold-templates.ts +0 -65
  592. package/src/modules/shared/index.ts +0 -1
  593. package/src/modules/user-management/command/logAuditEvent.generated.ts +0 -6
  594. package/src/modules/user-management/command/logAuditEvent.test.ts +0 -187
  595. package/src/modules/user-management/command/logAuditEvent.ts +0 -56
  596. package/src/modules/user-management/db/auditEvent.ts +0 -47
  597. package/src/modules/user-management/docs/commands/LogAuditEvent.md +0 -37
  598. package/src/modules/user-management/docs/features/audit-trail.md +0 -80
  599. package/src/modules/user-management/docs/models/AuditEvent.md +0 -36
  600. /package/skills/{erp-kit-module-2-feature-breakdown → erp-kit-module-3-plan}/references/naming.md +0 -0
  601. /package/skills/{erp-kit-module-4-tdd → erp-kit-module-5-impl}/references/cross-module-dependency.md +0 -0
  602. /package/skills/{erp-kit-module-4-tdd → erp-kit-module-5-impl}/references/db-relations.md +0 -0
  603. /package/skills/{erp-kit-module-4-tdd → erp-kit-module-5-impl}/references/generated-code.md +0 -0
  604. /package/skills/{erp-kit-module-4-tdd → erp-kit-module-5-impl}/references/models.md +0 -0
  605. /package/skills/{erp-kit-module-5-impl-review → erp-kit-module-6-impl-review}/references/commands.md +0 -0
  606. /package/skills/{erp-kit-module-5-impl-review → erp-kit-module-6-impl-review}/references/testing.md +0 -0
  607. /package/src/modules/{product-management → audit}/.gitkeep +0 -0
  608. /package/src/{modules/shared → shared}/createContext.test.ts +0 -0
  609. /package/src/{modules/shared → shared}/createContext.ts +0 -0
  610. /package/src/{modules/shared → shared}/definePermissions.test.ts +0 -0
  611. /package/src/{modules/shared → shared}/definePermissions.ts +0 -0
  612. /package/src/{modules/shared → shared}/defineQuery.test.ts +0 -0
  613. /package/src/{modules/shared → shared}/defineQuery.ts +0 -0
  614. /package/src/{modules/shared → shared}/entityTypes.ts +0 -0
  615. /package/src/{modules/shared → shared}/errors.ts +0 -0
  616. /package/src/{modules/shared → shared}/requirePermission.test.ts +0 -0
  617. /package/src/{modules/shared → shared}/requirePermission.ts +0 -0
  618. /package/src/{modules/shared → shared}/result.ts +0 -0
package/dist/cli.mjs ADDED
@@ -0,0 +1,1613 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { arg, defineCommand, runMain } from "politty";
4
+ import { z } from "zod";
5
+ import chalk from "chalk";
6
+ import fs, { existsSync, readFileSync, readdirSync } from "node:fs";
7
+ import path, { basename, dirname, join, relative, resolve } from "node:path";
8
+ import { execFile, execSync } from "node:child_process";
9
+ import fg from "fast-glob";
10
+ import { fromMarkdown } from "mdast-util-from-markdown";
11
+ import { toString } from "mdast-util-to-string";
12
+ import { createServer, request } from "node:http";
13
+ import { createServer as createServer$1 } from "node:net";
14
+ import { readFile, readdir, stat } from "node:fs/promises";
15
+ //#region src/util.ts
16
+ const PACKAGE_ROOT = path.resolve(import.meta.dirname, "..");
17
+ function readErpKitVersion() {
18
+ return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf-8")).version;
19
+ }
20
+ //#endregion
21
+ //#region src/commands/lib/distribute.ts
22
+ const SKILLS_SRC = path.join(PACKAGE_ROOT, "skills");
23
+ const WORKFLOWS_SRC = path.join(PACKAGE_ROOT, "templates", "workflows");
24
+ const CONFIG_SRC = path.join(PACKAGE_ROOT, "templates", "config");
25
+ function copyDirectoryRecursive(srcDir, destDir) {
26
+ let copied = 0;
27
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
28
+ const srcPath = path.join(srcDir, entry.name);
29
+ const destPath = path.join(destDir, entry.name);
30
+ if (entry.isDirectory()) copied += copyDirectoryRecursive(srcPath, destPath);
31
+ else {
32
+ fs.mkdirSync(destDir, { recursive: true });
33
+ fs.copyFileSync(srcPath, destPath);
34
+ copied++;
35
+ }
36
+ }
37
+ return copied;
38
+ }
39
+ function discoverFrameworkSkills() {
40
+ if (!fs.existsSync(SKILLS_SRC)) return [];
41
+ return fs.readdirSync(SKILLS_SRC, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("erp-kit-")).map((entry) => entry.name);
42
+ }
43
+ function copySkills(cwd) {
44
+ const skillsDest = path.join(cwd, ".agents", "skills");
45
+ let removed = 0;
46
+ if (fs.existsSync(skillsDest)) {
47
+ for (const entry of fs.readdirSync(skillsDest)) if (entry.startsWith("erp-kit-")) {
48
+ fs.rmSync(path.join(skillsDest, entry), {
49
+ recursive: true,
50
+ force: true
51
+ });
52
+ removed++;
53
+ }
54
+ }
55
+ let copied = 0;
56
+ for (const skill of discoverFrameworkSkills()) {
57
+ const srcSkillDir = path.join(SKILLS_SRC, skill);
58
+ if (!fs.existsSync(srcSkillDir)) continue;
59
+ const destDir = path.join(skillsDest, skill);
60
+ copied += copyDirectoryRecursive(srcSkillDir, destDir);
61
+ }
62
+ return {
63
+ copied,
64
+ removed
65
+ };
66
+ }
67
+ function copyWorkflows(cwd) {
68
+ const workflowsDest = path.join(cwd, ".github", "workflows");
69
+ if (!fs.existsSync(WORKFLOWS_SRC)) return {
70
+ copied: 0,
71
+ removed: 0
72
+ };
73
+ const workflowFiles = fs.readdirSync(WORKFLOWS_SRC).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
74
+ let copied = 0;
75
+ for (const file of workflowFiles) {
76
+ fs.mkdirSync(workflowsDest, { recursive: true });
77
+ fs.copyFileSync(path.join(WORKFLOWS_SRC, file), path.join(workflowsDest, file));
78
+ copied++;
79
+ }
80
+ return {
81
+ copied,
82
+ removed: 0
83
+ };
84
+ }
85
+ function copyConfigs(cwd) {
86
+ if (!fs.existsSync(CONFIG_SRC)) return {
87
+ copied: 0,
88
+ removed: 0
89
+ };
90
+ let copied = 0;
91
+ for (const entry of fs.readdirSync(CONFIG_SRC)) {
92
+ const destPath = path.join(cwd, entry);
93
+ fs.copyFileSync(path.join(CONFIG_SRC, entry), destPath);
94
+ copied++;
95
+ }
96
+ return {
97
+ copied,
98
+ removed: 0
99
+ };
100
+ }
101
+ function setupSymlink(cwd) {
102
+ const skillsDest = path.join(cwd, ".agents", "skills");
103
+ const claudeSkills = path.join(cwd, ".claude", "skills");
104
+ const relTarget = path.relative(path.dirname(claudeSkills), skillsDest);
105
+ let exists = false;
106
+ try {
107
+ fs.lstatSync(claudeSkills);
108
+ exists = true;
109
+ } catch {}
110
+ if (exists) {
111
+ if (fs.lstatSync(claudeSkills).isSymbolicLink()) {
112
+ if (fs.readlinkSync(claudeSkills) === relTarget) return "already_linked";
113
+ fs.unlinkSync(claudeSkills);
114
+ fs.symlinkSync(relTarget, claudeSkills);
115
+ return "linked";
116
+ }
117
+ return "skipped_directory";
118
+ }
119
+ fs.mkdirSync(path.dirname(claudeSkills), { recursive: true });
120
+ fs.symlinkSync(relTarget, claudeSkills);
121
+ return "linked";
122
+ }
123
+ function isAlreadyInitialized(cwd) {
124
+ const skillsDest = path.join(cwd, ".agents", "skills");
125
+ if (!fs.existsSync(skillsDest)) return false;
126
+ return fs.readdirSync(skillsDest).some((entry) => entry.startsWith("erp-kit-"));
127
+ }
128
+ //#endregion
129
+ //#region src/commands/init.ts
130
+ function runInit(cwd) {
131
+ console.log(chalk.bold("erp-kit init\n"));
132
+ if (isAlreadyInitialized(cwd)) {
133
+ console.log(chalk.yellow("Already initialized. Use `erp-kit update` to refresh framework resources."));
134
+ return 1;
135
+ }
136
+ const skills = copySkills(cwd);
137
+ console.log(chalk.green(` Copied ${skills.copied} skill files to .agents/skills/`));
138
+ const symlink = setupSymlink(cwd);
139
+ if (symlink === "linked") console.log(chalk.green(" .claude/skills -> .agents/skills/ (linked)"));
140
+ else if (symlink === "skipped_directory") console.log(chalk.yellow(" Skipped .claude/skills (directory exists, not a symlink)"));
141
+ const workflows = copyWorkflows(cwd);
142
+ if (workflows.copied > 0) console.log(chalk.green(` Copied ${workflows.copied} workflow(s) to .github/workflows/`));
143
+ const configs = copyConfigs(cwd);
144
+ if (configs.copied > 0) console.log(chalk.green(` Copied ${configs.copied} config file(s)`));
145
+ console.log(chalk.bold.green("\nDone! Run `erp-kit check` to validate your docs."));
146
+ return 0;
147
+ }
148
+ //#endregion
149
+ //#region src/commands/update.ts
150
+ const VALID_RESOURCES = ["skills", "workflows"];
151
+ function runUpdate(cwd, resources) {
152
+ console.log(chalk.bold("erp-kit update\n"));
153
+ const selected = resources.length === 0 ? new Set(VALID_RESOURCES) : new Set(resources.filter((r) => VALID_RESOURCES.includes(r)));
154
+ if (selected.size === 0) {
155
+ console.error(chalk.red(`Invalid resource. Valid: ${VALID_RESOURCES.join(", ")}`));
156
+ return 1;
157
+ }
158
+ if (selected.has("skills")) {
159
+ const result = copySkills(cwd);
160
+ if (result.removed > 0) console.log(chalk.green(` Removed ${result.removed} stale erp-kit-* skills`));
161
+ console.log(chalk.green(` Copied ${result.copied} skill files to .agents/skills/`));
162
+ }
163
+ if (selected.has("workflows")) {
164
+ const result = copyWorkflows(cwd);
165
+ if (result.copied > 0) console.log(chalk.green(` Copied ${result.copied} workflow(s) to .github/workflows/`));
166
+ }
167
+ console.log(chalk.bold.green("\nDone!"));
168
+ return 0;
169
+ }
170
+ //#endregion
171
+ //#region src/commands/license.ts
172
+ const licenseGroups = {
173
+ reciprocal: [
174
+ "APSL-1.0",
175
+ "APSL-1.1",
176
+ "APSL-1.2",
177
+ "APSL-2.0",
178
+ "CDDL-1.0",
179
+ "CDDL-1.1",
180
+ "CPL-1.0",
181
+ "EPL-1.0",
182
+ "EPL-2.0",
183
+ "FreeImage",
184
+ "IPL-1.0",
185
+ "MPL-1.0",
186
+ "MPL-1.1",
187
+ "MPL-2.0",
188
+ "Ruby"
189
+ ],
190
+ notice: [
191
+ "AFL-1.1",
192
+ "AFL-1.2",
193
+ "AFL-2.0",
194
+ "AFL-2.1",
195
+ "AFL-3.0",
196
+ "Apache-1.0",
197
+ "Apache-1.1",
198
+ "Apache-2.0",
199
+ "Artistic-1.0-cl8",
200
+ "Artistic-1.0-Perl",
201
+ "Artistic-1.0",
202
+ "Artistic-2.0",
203
+ "BSL-1.0",
204
+ "BSD-2-Clause-FreeBSD",
205
+ "BSD-2-Clause-NetBSD",
206
+ "BSD-2-Clause",
207
+ "BSD-3-Clause-Attribution",
208
+ "BSD-3-Clause-Clear",
209
+ "BSD-3-Clause-LBNL",
210
+ "BSD-3-Clause",
211
+ "BSD-4-Clause",
212
+ "BSD-4-Clause-UC",
213
+ "BSD-Protection",
214
+ "CC-BY-1.0",
215
+ "CC-BY-2.0",
216
+ "CC-BY-2.5",
217
+ "CC-BY-3.0",
218
+ "CC-BY-4.0",
219
+ "FTL",
220
+ "ISC",
221
+ "ImageMagick",
222
+ "Libpng",
223
+ "Lil-1.0",
224
+ "Linux-OpenIB",
225
+ "LPL-1.02",
226
+ "LPL-1.0",
227
+ "MS-PL",
228
+ "MIT",
229
+ "NCSA",
230
+ "OpenSSL",
231
+ "PHP-3.01",
232
+ "PHP-3.0",
233
+ "PIL",
234
+ "Python-2.0",
235
+ "Python-2.0-complete",
236
+ "PostgreSQL",
237
+ "SGI-B-1.0",
238
+ "SGI-B-1.1",
239
+ "SGI-B-2.0",
240
+ "Unicode-DFS-2015",
241
+ "Unicode-DFS-2016",
242
+ "Unicode-TOU",
243
+ "UPL-1.0",
244
+ "W3C-19980720",
245
+ "W3C-20150513",
246
+ "W3C",
247
+ "X11",
248
+ "Xnet",
249
+ "Zend-2.0",
250
+ "zlib-acknowledgement",
251
+ "Zlib",
252
+ "ZPL-1.1",
253
+ "ZPL-2.0",
254
+ "ZPL-2.1"
255
+ ],
256
+ unencumbered: [
257
+ "CC0-1.0",
258
+ "Unlicense",
259
+ "0BSD"
260
+ ]
261
+ };
262
+ function buildAllowSet(config) {
263
+ const set = /* @__PURE__ */ new Set();
264
+ for (const group of config.groups) for (const license of licenseGroups[group]) set.add(license);
265
+ if (config.allow) for (const license of config.allow) set.add(license);
266
+ if (config.deny) for (const license of config.deny) set.delete(license);
267
+ return set;
268
+ }
269
+ function isLicenseAllowed(licenseString, allowSet) {
270
+ if (/\s+(?:OR|AND)\s+/i.test(licenseString)) return licenseString.replace(/[()]/g, "").trim().split(/\s+(?:OR|AND)\s+/i).map((l) => l.trim()).filter((l) => l.length > 0).every((l) => allowSet.has(l));
271
+ return allowSet.has(licenseString);
272
+ }
273
+ function runLicenseList() {
274
+ for (const [group, licenses] of Object.entries(licenseGroups)) {
275
+ console.log(`${group} (${licenses.length} licenses):`);
276
+ for (const license of licenses) console.log(` ${license}`);
277
+ console.log();
278
+ }
279
+ return 0;
280
+ }
281
+ function runLicenseCheck(configPath) {
282
+ const raw = fs.readFileSync(configPath, "utf-8");
283
+ const config = JSON.parse(raw);
284
+ const validGroups = Object.keys(licenseGroups);
285
+ for (const group of config.groups) if (!validGroups.includes(group)) {
286
+ console.error(`Unknown license group: "${group}". Valid groups: ${validGroups.join(", ")}`);
287
+ return 2;
288
+ }
289
+ const allowSet = buildAllowSet(config);
290
+ console.log("Checking licenses...\n");
291
+ execSync("pnpm licenses list", { stdio: "inherit" });
292
+ const output = execSync("pnpm licenses list --json");
293
+ const licensesJson = JSON.parse(output.toString());
294
+ const violations = [];
295
+ for (const [license, packages] of Object.entries(licensesJson)) if (!isLicenseAllowed(license, allowSet)) for (const pkg of packages) violations.push({
296
+ package: pkg.name,
297
+ license
298
+ });
299
+ if (violations.length === 0) {
300
+ console.log("All licenses are allowed.");
301
+ return 0;
302
+ }
303
+ console.error("Found dependencies with disallowed licenses:\n");
304
+ for (const violation of violations) {
305
+ console.error(` - ${violation.package}`);
306
+ console.error(` License: ${violation.license}\n`);
307
+ }
308
+ return 1;
309
+ }
310
+ //#endregion
311
+ //#region src/mdschema.ts
312
+ const require = createRequire(import.meta.url);
313
+ function getMdschemaBin() {
314
+ const pkgPath = require.resolve("@jackchuka/mdschema/package.json");
315
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
316
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.mdschema;
317
+ if (!bin) throw new Error("Could not resolve mdschema binary from package.json bin field");
318
+ return path.join(path.dirname(pkgPath), bin);
319
+ }
320
+ function runMdschema(args, cwd) {
321
+ return new Promise((resolve) => {
322
+ execFile(getMdschemaBin(), args, {
323
+ encoding: "utf-8",
324
+ cwd,
325
+ timeout: 3e4
326
+ }, (error, stdout, stderr) => {
327
+ if (error) {
328
+ const execError = error;
329
+ resolve({
330
+ exitCode: execError.code === "ENOENT" ? 127 : execError.status ?? 1,
331
+ stdout: stdout ?? "",
332
+ stderr: stderr ?? ""
333
+ });
334
+ } else resolve({
335
+ exitCode: 0,
336
+ stdout: stdout ?? "",
337
+ stderr: stderr ?? ""
338
+ });
339
+ });
340
+ });
341
+ }
342
+ //#endregion
343
+ //#region src/schemas.ts
344
+ const SCHEMAS_ROOT = path.join(PACKAGE_ROOT, "schemas");
345
+ const MODULE_SCHEMAS = {
346
+ module: path.join(SCHEMAS_ROOT, "module", "module.yml"),
347
+ command: path.join(SCHEMAS_ROOT, "module", "command.yml"),
348
+ model: path.join(SCHEMAS_ROOT, "module", "model.yml"),
349
+ feature: path.join(SCHEMAS_ROOT, "module", "feature.yml"),
350
+ query: path.join(SCHEMAS_ROOT, "module", "query.yml")
351
+ };
352
+ const APP_COMPOSE_SCHEMAS = {
353
+ app: path.join(SCHEMAS_ROOT, "app-compose", "requirements.yml"),
354
+ actors: path.join(SCHEMAS_ROOT, "app-compose", "actors.yml"),
355
+ "business-flow": path.join(SCHEMAS_ROOT, "app-compose", "business-flow.yml"),
356
+ story: path.join(SCHEMAS_ROOT, "app-compose", "story.yml"),
357
+ screen: path.join(SCHEMAS_ROOT, "app-compose", "screen.yml"),
358
+ resolver: path.join(SCHEMAS_ROOT, "app-compose", "resolver.yml")
359
+ };
360
+ const ALL_SCHEMAS = {
361
+ ...MODULE_SCHEMAS,
362
+ ...APP_COMPOSE_SCHEMAS
363
+ };
364
+ //#endregion
365
+ //#region src/commands/check.ts
366
+ function buildCheckTargets(config) {
367
+ const targets = [];
368
+ if (config.modulesRoot) {
369
+ const m = config.modulesRoot;
370
+ targets.push({
371
+ glob: `${m}/[a-zA-Z]*/docs/features/*.md`,
372
+ schemaKey: "feature"
373
+ }, {
374
+ glob: `${m}/[a-zA-Z]*/docs/commands/*.md`,
375
+ schemaKey: "command"
376
+ }, {
377
+ glob: `${m}/[a-zA-Z]*/docs/models/*.md`,
378
+ schemaKey: "model"
379
+ }, {
380
+ glob: `${m}/[a-zA-Z]*/docs/queries/*.md`,
381
+ schemaKey: "query"
382
+ }, {
383
+ glob: `${m}/[a-zA-Z]*/README.md`,
384
+ schemaKey: "module"
385
+ });
386
+ }
387
+ if (config.appRoot) {
388
+ const a = config.appRoot;
389
+ targets.push({
390
+ glob: `${a}/[a-zA-Z]*/README.md`,
391
+ schemaKey: "app"
392
+ }, {
393
+ glob: `${a}/[a-zA-Z]*/docs/actors/*.md`,
394
+ schemaKey: "actors"
395
+ }, {
396
+ glob: `${a}/[a-zA-Z]*/docs/business-flow/*/README.md`,
397
+ schemaKey: "business-flow"
398
+ }, {
399
+ glob: `${a}/[a-zA-Z]*/docs/business-flow/*/story/*.md`,
400
+ schemaKey: "story"
401
+ }, {
402
+ glob: `${a}/[a-zA-Z]*/docs/screen/*.md`,
403
+ schemaKey: "screen"
404
+ }, {
405
+ glob: `${a}/[a-zA-Z]*/docs/resolver/*.md`,
406
+ schemaKey: "resolver"
407
+ });
408
+ }
409
+ return targets;
410
+ }
411
+ async function runCheck(config, cwd) {
412
+ const targets = buildCheckTargets(config);
413
+ const results = await Promise.all(targets.map(async (target) => {
414
+ const schemaPath = ALL_SCHEMAS[target.schemaKey];
415
+ if (!schemaPath) {
416
+ console.error(`Unknown schema key: ${target.schemaKey}`);
417
+ return 2;
418
+ }
419
+ const { exitCode, stdout, stderr } = await runMdschema([
420
+ "check",
421
+ target.glob,
422
+ "--schema",
423
+ schemaPath
424
+ ], cwd);
425
+ if (stdout.trim()) console.log(stdout);
426
+ if (stderr.trim()) console.error(stderr);
427
+ return exitCode;
428
+ }));
429
+ if (results.includes(2)) return 2;
430
+ return results.some((code) => code !== 0) ? 1 : 0;
431
+ }
432
+ //#endregion
433
+ //#region src/commands/parse-doc-test-cases.ts
434
+ function isHeading$1(node) {
435
+ return node.type === "heading";
436
+ }
437
+ function isList$1(node) {
438
+ return node.type === "list";
439
+ }
440
+ /**
441
+ * Parse test case descriptions from the `## Test Cases` section of a Markdown doc.
442
+ * Uses mdast to traverse the AST instead of string matching.
443
+ */
444
+ function parseTestCasesFromDoc(markdown) {
445
+ const tree = fromMarkdown(markdown);
446
+ const cases = [];
447
+ let collecting = false;
448
+ for (const node of tree.children) {
449
+ if (isHeading$1(node)) {
450
+ if (collecting) break;
451
+ if (node.depth === 2 && toString(node) === "Test Cases") {
452
+ collecting = true;
453
+ continue;
454
+ }
455
+ }
456
+ if (collecting && isList$1(node)) for (const item of node.children) {
457
+ const text = toString(item).trim();
458
+ if (text) cases.push(text);
459
+ }
460
+ }
461
+ return cases;
462
+ }
463
+ /**
464
+ * Parse `it("...")` descriptions from a test file using regex.
465
+ * Test files are not Markdown, so regex is appropriate here.
466
+ */
467
+ function parseItDescriptionsFromTest(content) {
468
+ const descriptions = [];
469
+ const regex = /\bit\(\s*["'`]([^"'`]+)["'`]/g;
470
+ let match;
471
+ while ((match = regex.exec(content)) !== null) descriptions.push(match[1]);
472
+ return descriptions;
473
+ }
474
+ //#endregion
475
+ //#region src/commands/sync-check.ts
476
+ function moduleCategories(root) {
477
+ return [
478
+ {
479
+ name: "command",
480
+ sourcePattern: `${root}/*/command/*.ts`,
481
+ docPattern: `${root}/*/docs/commands/*.md`,
482
+ exclusions: [/\.test\.ts$/, /\.generated\.ts$/]
483
+ },
484
+ {
485
+ name: "model",
486
+ sourcePattern: `${root}/*/db/*.ts`,
487
+ docPattern: `${root}/*/docs/models/*.md`,
488
+ exclusions: [/\.test\.ts$/, /^index\.ts$/]
489
+ },
490
+ {
491
+ name: "query",
492
+ sourcePattern: `${root}/*/query/*.ts`,
493
+ docPattern: `${root}/*/docs/queries/*.md`,
494
+ exclusions: [/\.test\.ts$/]
495
+ }
496
+ ];
497
+ }
498
+ function appComposeCategories(root) {
499
+ return [{
500
+ name: "resolver",
501
+ sourcePattern: `${root}/*/backend/src/modules/**/resolvers/*.ts`,
502
+ docPattern: `${root}/*/docs/resolver/*.md`,
503
+ exclusions: [/\.test\.ts$/, /^index\.ts$/]
504
+ }];
505
+ }
506
+ function shouldExclude(fileName, exclusions) {
507
+ return exclusions.some((pattern) => pattern.test(fileName));
508
+ }
509
+ async function runSyncCheck(config, cwd) {
510
+ const errors = [];
511
+ let totalSources = 0;
512
+ let totalDocs = 0;
513
+ const allCategories = [];
514
+ if (config.modulesRoot) allCategories.push(...moduleCategories(config.modulesRoot));
515
+ if (config.appRoot) allCategories.push(...appComposeCategories(config.appRoot));
516
+ for (const category of allCategories) {
517
+ const sources = await fg(category.sourcePattern, { cwd });
518
+ const docs = await fg(category.docPattern, { cwd });
519
+ const sourceBasenames = /* @__PURE__ */ new Map();
520
+ const docBasenames = /* @__PURE__ */ new Map();
521
+ for (const sourcePath of sources) {
522
+ if (shouldExclude(path.basename(sourcePath), category.exclusions)) continue;
523
+ let basename = path.basename(sourcePath, path.extname(sourcePath));
524
+ if (basename.endsWith(".generated")) basename = basename.slice(0, -10);
525
+ sourceBasenames.set(basename.toLowerCase(), sourcePath);
526
+ }
527
+ for (const docPath of docs) {
528
+ const basename = path.basename(docPath, path.extname(docPath));
529
+ docBasenames.set(basename.toLowerCase(), docPath);
530
+ }
531
+ for (const [basename, sourcePath] of sourceBasenames) if (!docBasenames.has(basename)) errors.push({
532
+ type: "missing-doc",
533
+ category: category.name,
534
+ sourcePath,
535
+ expectedBasename: basename
536
+ });
537
+ for (const [basename, docPath] of docBasenames) if (!sourceBasenames.has(basename)) errors.push({
538
+ type: "orphaned-doc",
539
+ category: category.name,
540
+ docPath,
541
+ expectedBasename: basename
542
+ });
543
+ totalSources += sourceBasenames.size;
544
+ totalDocs += docBasenames.size;
545
+ }
546
+ if (config.modulesRoot) {
547
+ const testCaseErrors = await runTestCaseSyncCheck(config.modulesRoot, cwd);
548
+ errors.push(...testCaseErrors);
549
+ }
550
+ return {
551
+ exitCode: errors.length > 0 ? 1 : 0,
552
+ errors,
553
+ summary: {
554
+ categoriesChecked: allCategories.length,
555
+ totalSources,
556
+ totalDocs
557
+ }
558
+ };
559
+ }
560
+ function testCaseCategories(root) {
561
+ return [{
562
+ name: "command-test-case",
563
+ docPattern: `${root}/*/docs/commands/*.md`,
564
+ testDir: "command"
565
+ }, {
566
+ name: "query-test-case",
567
+ docPattern: `${root}/*/docs/queries/*.md`,
568
+ testDir: "query"
569
+ }];
570
+ }
571
+ function toCamelCase(pascalCase) {
572
+ return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
573
+ }
574
+ async function runTestCaseSyncCheck(root, cwd) {
575
+ const errors = [];
576
+ const categories = testCaseCategories(root);
577
+ for (const category of categories) {
578
+ const docPaths = await fg(category.docPattern, { cwd });
579
+ for (const docPath of docPaths) {
580
+ const docFullPath = path.join(cwd, docPath);
581
+ const docTestCases = parseTestCasesFromDoc(fs.readFileSync(docFullPath, "utf-8"));
582
+ if (docTestCases.length === 0) continue;
583
+ const docBasename = path.basename(docPath, ".md");
584
+ const docsIndex = docPath.indexOf("/docs/");
585
+ if (docsIndex === -1) continue;
586
+ const modulePath = docPath.substring(0, docsIndex);
587
+ const testFileName = `${toCamelCase(docBasename)}.test.ts`;
588
+ const testPath = path.join(modulePath, category.testDir, testFileName);
589
+ const testFullPath = path.join(cwd, testPath);
590
+ if (!fs.existsSync(testFullPath)) continue;
591
+ const itDescriptions = parseItDescriptionsFromTest(fs.readFileSync(testFullPath, "utf-8"));
592
+ const docSet = new Set(docTestCases);
593
+ const testSet = new Set(itDescriptions);
594
+ for (const docCase of docSet) if (!testSet.has(docCase)) errors.push({
595
+ type: "missing-test-case",
596
+ category: category.name,
597
+ docPath,
598
+ sourcePath: testPath,
599
+ expectedBasename: docCase
600
+ });
601
+ for (const testCase of testSet) if (!docSet.has(testCase)) errors.push({
602
+ type: "extra-test-case",
603
+ category: category.name,
604
+ docPath,
605
+ sourcePath: testPath,
606
+ expectedBasename: testCase
607
+ });
608
+ }
609
+ }
610
+ return errors;
611
+ }
612
+ function formatSyncCheckReport(result) {
613
+ const lines = [];
614
+ lines.push(chalk.bold("docs-sync-check: Checking source-documentation correspondence...\n"));
615
+ if (result.errors.length > 0) {
616
+ lines.push(chalk.red.bold("Errors:\n"));
617
+ const byCategory = /* @__PURE__ */ new Map();
618
+ for (const error of result.errors) {
619
+ const existing = byCategory.get(error.category) ?? [];
620
+ existing.push(error);
621
+ byCategory.set(error.category, existing);
622
+ }
623
+ for (const [category, categoryErrors] of byCategory) {
624
+ lines.push(chalk.cyan(` Category: ${category}\n`));
625
+ for (const error of categoryErrors) {
626
+ if (error.type === "missing-doc") {
627
+ lines.push(` ${chalk.red(error.sourcePath)}`);
628
+ lines.push(` ${chalk.yellow("Missing documentation for:")} ${error.expectedBasename}`);
629
+ } else if (error.type === "orphaned-doc") {
630
+ lines.push(` ${chalk.red(error.docPath)}`);
631
+ lines.push(` ${chalk.yellow("Orphaned documentation:")} no source file for ${error.expectedBasename}`);
632
+ } else if (error.type === "missing-test-case") {
633
+ lines.push(` ${chalk.red(error.sourcePath)}`);
634
+ lines.push(` ${chalk.yellow("Missing test case:")} "${error.expectedBasename}" is in doc but not in test`);
635
+ } else if (error.type === "extra-test-case") {
636
+ lines.push(` ${chalk.red(error.sourcePath)}`);
637
+ lines.push(` ${chalk.yellow("Extra test case:")} "${error.expectedBasename}" is in test but not in doc`);
638
+ }
639
+ lines.push("");
640
+ }
641
+ }
642
+ } else lines.push(chalk.green("All source files have corresponding documentation.\n"));
643
+ lines.push(chalk.bold("Summary:"));
644
+ lines.push(` Categories checked: ${result.summary.categoriesChecked}`);
645
+ lines.push(` Source files: ${result.summary.totalSources}, Doc files: ${result.summary.totalDocs}`);
646
+ if (result.errors.length > 0) {
647
+ lines.push(chalk.red(` Errors: ${result.errors.length}`));
648
+ lines.push("");
649
+ lines.push(chalk.red.bold(`docs-sync-check failed with ${result.errors.length} error(s).`));
650
+ } else {
651
+ lines.push(chalk.green(" Errors: 0"));
652
+ lines.push("");
653
+ lines.push(chalk.green.bold("docs-sync-check passed."));
654
+ }
655
+ return lines.join("\n");
656
+ }
657
+ //#endregion
658
+ //#region src/commands/scaffold.ts
659
+ const MODULE_TYPES = [
660
+ "module",
661
+ "feature",
662
+ "command",
663
+ "model",
664
+ "query"
665
+ ];
666
+ const APP_TYPES = [
667
+ "app",
668
+ "actors",
669
+ "business-flow",
670
+ "story",
671
+ "screen",
672
+ "resolver"
673
+ ];
674
+ [...MODULE_TYPES, ...APP_TYPES];
675
+ const MODULE_DIR_MAP = {
676
+ feature: "docs/features",
677
+ command: "docs/commands",
678
+ model: "docs/models",
679
+ query: "docs/queries"
680
+ };
681
+ const APP_DIR_MAP = {
682
+ actors: "docs/actors",
683
+ "business-flow": "docs/business-flow",
684
+ screen: "docs/screen",
685
+ resolver: "docs/resolver"
686
+ };
687
+ function resolveScaffoldPath(type, parentName, name, root) {
688
+ if (type === "module" || type === "app") return path.join(root, parentName, "README.md");
689
+ if (!name) throw new Error(`Name is required for scaffold type "${type}"`);
690
+ if (type === "business-flow") return path.join(root, parentName, "docs/business-flow", name, "README.md");
691
+ if (type === "story") {
692
+ const parts = name.split("/");
693
+ if (parts.length !== 2) throw new Error(`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`);
694
+ return path.join(root, parentName, "docs/business-flow", parts[0], "story", `${parts[1]}.md`);
695
+ }
696
+ if (MODULE_DIR_MAP[type]) return path.join(root, parentName, MODULE_DIR_MAP[type], `${name}.md`);
697
+ if (APP_DIR_MAP[type]) return path.join(root, parentName, APP_DIR_MAP[type], `${name}.md`);
698
+ throw new Error(`Unknown scaffold type: ${type}`);
699
+ }
700
+ async function runScaffold(type, parentName, name, root, cwd) {
701
+ const outputPath = resolveScaffoldPath(type, parentName, name, root);
702
+ const absoluteOutput = path.resolve(cwd, outputPath);
703
+ if (fs.existsSync(absoluteOutput)) {
704
+ console.error(`File already exists: ${outputPath}`);
705
+ return 1;
706
+ }
707
+ const schemaPath = ALL_SCHEMAS[type];
708
+ if (!schemaPath) {
709
+ console.error(`No schema found for type: ${type}`);
710
+ return 2;
711
+ }
712
+ try {
713
+ fs.mkdirSync(path.dirname(absoluteOutput), { recursive: true });
714
+ } catch (err) {
715
+ console.error(`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`);
716
+ return 1;
717
+ }
718
+ const { exitCode, stdout, stderr } = await runMdschema([
719
+ "generate",
720
+ "--schema",
721
+ schemaPath,
722
+ "--output",
723
+ absoluteOutput
724
+ ], cwd);
725
+ if (stdout.trim()) console.log(stdout);
726
+ if (stderr.trim()) console.error(stderr);
727
+ if (exitCode !== 0) return exitCode;
728
+ if (type === "module") scaffoldModuleSrc(path.dirname(absoluteOutput), parentName);
729
+ if (type === "app") scaffoldAppSrc(path.dirname(absoluteOutput), parentName);
730
+ return exitCode;
731
+ }
732
+ function copyTemplateDir(srcDir, destDir, replacements, placeholderFiles) {
733
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
734
+ const srcPath = path.join(srcDir, entry.name);
735
+ const destName = entry.name === "__dot__gitignore" ? ".gitignore" : entry.name;
736
+ const destPath = path.join(destDir, destName);
737
+ if (entry.isDirectory()) {
738
+ fs.mkdirSync(destPath, { recursive: true });
739
+ copyTemplateDir(srcPath, destPath, replacements, placeholderFiles);
740
+ } else {
741
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
742
+ if (placeholderFiles.has(entry.name)) {
743
+ let content = fs.readFileSync(srcPath, "utf-8");
744
+ for (const [from, to] of Object.entries(replacements)) content = content.replaceAll(from, to);
745
+ fs.writeFileSync(destPath, content);
746
+ } else fs.copyFileSync(srcPath, destPath);
747
+ }
748
+ }
749
+ }
750
+ function scaffoldModuleSrc(moduleDir, moduleName) {
751
+ copyTemplateDir(path.join(PACKAGE_ROOT, "templates", "scaffold", "module"), moduleDir, { "template-module": moduleName }, new Set(["permissions.ts", "tailor.config.ts"]));
752
+ }
753
+ function scaffoldAppSrc(appDir, appName) {
754
+ const templateDir = path.join(PACKAGE_ROOT, "templates", "scaffold", "app");
755
+ const erpKitVersion = readErpKitVersion();
756
+ copyTemplateDir(templateDir, appDir, {
757
+ "template-app-frontend": `${appName}-frontend`,
758
+ "template-app-backend": appName,
759
+ "template-app": appName,
760
+ "\"workspace:*\"": `"${erpKitVersion}"`
761
+ }, new Set([
762
+ "package.json",
763
+ "tailor.config.ts",
764
+ "index.html"
765
+ ]));
766
+ }
767
+ //#endregion
768
+ //#region src/commands/module/list.ts
769
+ const MODULES_DIR = join(PACKAGE_ROOT, "src", "modules");
770
+ const EXCLUDED_DIRS = new Set(["shared", "testing"]);
771
+ function countFiles(dir, pattern, exclusions) {
772
+ if (!existsSync(dir)) return 0;
773
+ return readdirSync(dir).filter((f) => pattern.test(f) && !exclusions.some((p) => p.test(f))).length;
774
+ }
775
+ function listModules() {
776
+ if (!existsSync(MODULES_DIR)) return [];
777
+ return readdirSync(MODULES_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && !EXCLUDED_DIRS.has(d.name)).map((d) => {
778
+ const modDir = join(MODULES_DIR, d.name);
779
+ return {
780
+ name: d.name,
781
+ commands: countFiles(join(modDir, "command"), /\.ts$/, [/\.test\.ts$/]),
782
+ models: countFiles(join(modDir, "db"), /\.ts$/, [/\.test\.ts$/, /^index\.ts$/]),
783
+ features: countFiles(join(modDir, "docs", "features"), /\.md$/, [])
784
+ };
785
+ }).sort((a, b) => a.name.localeCompare(b.name));
786
+ }
787
+ function formatModuleList(modules) {
788
+ if (modules.length === 0) return "No modules found.";
789
+ const lines = [];
790
+ lines.push(chalk.bold("Modules:\n"));
791
+ const nameWidth = Math.max(...modules.map((m) => m.name.length), 4);
792
+ for (const mod of modules) {
793
+ const counts = [
794
+ `${mod.commands} commands`,
795
+ `${mod.models} models`,
796
+ `${mod.features} features`
797
+ ].join(", ");
798
+ lines.push(` ${mod.name.padEnd(nameWidth)} ${counts}`);
799
+ }
800
+ lines.push("");
801
+ lines.push(`${modules.length} modules`);
802
+ return lines.join("\n");
803
+ }
804
+ function runModuleList() {
805
+ const modules = listModules();
806
+ console.log(formatModuleList(modules));
807
+ return 0;
808
+ }
809
+ //#endregion
810
+ //#region src/generator/parse-command-doc.ts
811
+ function parseCommandDoc(fileName, markdown) {
812
+ const commandName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
813
+ const tree = fromMarkdown(markdown);
814
+ return {
815
+ commandName,
816
+ errors: parseErrorScenarios(tree),
817
+ externalDependencies: parseExternalDependencies(tree)
818
+ };
819
+ }
820
+ function errorCodeToClassName(code) {
821
+ return code.toLowerCase().split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("") + "Error";
822
+ }
823
+ function isHeading(node) {
824
+ return node.type === "heading";
825
+ }
826
+ function isList(node) {
827
+ return node.type === "list";
828
+ }
829
+ function getNodesUnderHeading(tree, headingText) {
830
+ const nodes = [];
831
+ let collecting = false;
832
+ for (const node of tree.children) {
833
+ if (isHeading(node)) {
834
+ if (collecting) break;
835
+ if (node.depth === 2 && toString(node) === headingText) {
836
+ collecting = true;
837
+ continue;
838
+ }
839
+ }
840
+ if (collecting) nodes.push(node);
841
+ }
842
+ return nodes;
843
+ }
844
+ const ERROR_PATTERN = /^([A-Z_]+):\s*(.+)$/;
845
+ function parseErrorScenarios(tree) {
846
+ const nodes = getNodesUnderHeading(tree, "Error Scenarios");
847
+ const errors = [];
848
+ for (const node of nodes) {
849
+ if (!isList(node)) continue;
850
+ for (const item of node.children) {
851
+ const text = toString(item);
852
+ const match = ERROR_PATTERN.exec(text);
853
+ if (match) errors.push({
854
+ code: match[1],
855
+ description: match[2].trim()
856
+ });
857
+ }
858
+ }
859
+ return errors;
860
+ }
861
+ const DEPENDENCY_PATTERN = /^([^:]+)::(.+)$/;
862
+ function parseExternalDependencies(tree) {
863
+ const nodes = getNodesUnderHeading(tree, "External Dependencies");
864
+ const deps = [];
865
+ for (const node of nodes) {
866
+ if (!isList(node)) continue;
867
+ for (const item of node.children) {
868
+ const firstChild = item.children[0];
869
+ if (firstChild?.type !== "paragraph") continue;
870
+ for (const inline of firstChild.children) if (inline.type === "link" || inline.type === "linkReference") {
871
+ const linkText = toString(inline);
872
+ const match = DEPENDENCY_PATTERN.exec(linkText);
873
+ if (match) deps.push({
874
+ module: match[1],
875
+ entity: match[2]
876
+ });
877
+ }
878
+ }
879
+ }
880
+ return deps;
881
+ }
882
+ //#endregion
883
+ //#region src/generator/generate-code.ts
884
+ function moduleNameToPrefix(moduleName) {
885
+ return moduleName.toUpperCase().replace(/-/g, "_");
886
+ }
887
+ function generateErrors(moduleName, docs) {
888
+ const seen = /* @__PURE__ */ new Set();
889
+ const lines = [];
890
+ const prefix = moduleNameToPrefix(moduleName);
891
+ for (const doc of docs) for (const error of doc.errors) {
892
+ if (seen.has(error.code)) continue;
893
+ seen.add(error.code);
894
+ const className = errorCodeToClassName(error.code);
895
+ const prefixedCode = `${prefix}_${error.code}`;
896
+ lines.push(`export const ${className} = createDomainError(`);
897
+ lines.push(` "${className}", "${prefixedCode}",`);
898
+ const escapedDesc = error.description.replace(/`/g, "'");
899
+ lines.push(` (identifier: string) => \`${escapedDesc}: \${identifier}\`,`);
900
+ lines.push(`);`);
901
+ lines.push(``);
902
+ }
903
+ if (lines.length === 0) return "";
904
+ return `// @generated — do not edit
905
+ import { createDomainError } from "../../../shared";
906
+
907
+ ${lines.join("\n")}`;
908
+ }
909
+ function generateCommandStub(doc) {
910
+ const inputType = `${doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1)}Input`;
911
+ return `import { ok, type CommandContext } from "../../../shared";
912
+ import type { Transaction } from "../generated/kysely-tailordb";
913
+
914
+ export interface ${inputType} {
915
+ // TODO: define input fields
916
+ }
917
+
918
+ export async function run(db: Transaction, input: ${inputType}, ctx: CommandContext) {
919
+ // TODO: implement
920
+ return ok({});
921
+ }
922
+ `;
923
+ }
924
+ function generateTestStub(doc) {
925
+ const pascal = doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1);
926
+ return `import { describe, expect, it } from "vitest";
927
+ import { createMockDb } from "../../../testing/index";
928
+ import type { CommandContext } from "../../../shared";
929
+ import type { Transaction } from "../generated/kysely-tailordb";
930
+ import { run, ${pascal}Input } from "./${doc.commandName}";
931
+
932
+ describe("${doc.commandName} - test scenario", () => {
933
+ const ctx: CommandContext = {
934
+ actorId: "test-actor",
935
+ permissions: ["TODO:${doc.commandName}"],
936
+ };
937
+
938
+ it("should be implemented", async () => {
939
+ const { db } = createMockDb<Transaction>();
940
+ const result = await run(db, {} as ${pascal}Input, ctx);
941
+ expect(result.ok).toBe(true);
942
+ });
943
+ });
944
+ `;
945
+ }
946
+ function generateCommandShell(doc) {
947
+ return [
948
+ `// @generated — do not edit`,
949
+ `import { defineCommand } from "../../../shared";`,
950
+ `import { permissions } from "../lib/permissions.generated";`,
951
+ `import { run } from "./${doc.commandName}";`,
952
+ ``,
953
+ `export const ${doc.commandName} = defineCommand(permissions.${doc.commandName}, run);`,
954
+ ``
955
+ ].join("\n");
956
+ }
957
+ function generateQueryShell(doc) {
958
+ return [
959
+ `// @generated — do not edit`,
960
+ `import { defineQuery } from "../../../shared";`,
961
+ `import { run } from "./${doc.commandName}";`,
962
+ ``,
963
+ `export const ${doc.commandName} = defineQuery(run);`,
964
+ ``
965
+ ].join("\n");
966
+ }
967
+ function generateQueryStub(doc) {
968
+ const inputType = `${doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1)}Input`;
969
+ return `import type { ReadonlyDB } from "../../../shared";
970
+ import type { DB } from "../generated/kysely-tailordb";
971
+
972
+ export interface ${inputType} {
973
+ // TODO: define input fields
974
+ }
975
+
976
+ export async function run(db: ReadonlyDB<DB>, input: ${inputType}) {
977
+ // TODO: implement
978
+ return {};
979
+ }
980
+ `;
981
+ }
982
+ function generateQueryTestStub(doc) {
983
+ const inputType = `${doc.commandName.charAt(0).toUpperCase() + doc.commandName.slice(1)}Input`;
984
+ return `import { describe, expect, it } from "vitest";
985
+ import { createMockDb } from "../../../testing/index";
986
+ import type { DB } from "../generated/kysely-tailordb";
987
+ import { run, type ${inputType} } from "./${doc.commandName}";
988
+
989
+ describe("${doc.commandName}", () => {
990
+ it("should be implemented", async () => {
991
+ const { db } = createMockDb<DB>();
992
+ const result = await run(db, {} as ${inputType});
993
+ expect(result).toBeDefined();
994
+ });
995
+ });
996
+ `;
997
+ }
998
+ function generatePermissions(moduleName, commandNames) {
999
+ return `// @generated — do not edit
1000
+ import { definePermissions } from "../../../shared";
1001
+
1002
+ export const { permissions, own, all } = definePermissions("${moduleName}", [
1003
+ ${[...commandNames].sort().map((name) => ` "${name}",`).join("\n")}
1004
+ ] as const);
1005
+ `;
1006
+ }
1007
+ function runGenerateCode(modulePath, moduleName) {
1008
+ const docsDir = path.join(modulePath, "docs", "commands");
1009
+ const libDir = path.join(modulePath, "lib");
1010
+ const commandDir = path.join(modulePath, "command");
1011
+ if (!fs.existsSync(docsDir)) {
1012
+ console.error(`No docs/commands/ directory found at ${docsDir}`);
1013
+ return 1;
1014
+ }
1015
+ const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
1016
+ if (mdFiles.length === 0) {
1017
+ console.error(`No command docs found in ${docsDir}`);
1018
+ return 1;
1019
+ }
1020
+ const parsedDocs = [];
1021
+ for (const file of mdFiles) {
1022
+ const content = fs.readFileSync(path.join(docsDir, file), "utf-8");
1023
+ const name = path.basename(file, ".md");
1024
+ parsedDocs.push(parseCommandDoc(name, content));
1025
+ }
1026
+ const queryDocsDir = path.join(modulePath, "docs", "queries");
1027
+ const allDocsForErrors = [...parsedDocs];
1028
+ if (fs.existsSync(queryDocsDir)) {
1029
+ const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
1030
+ for (const file of queryFiles) {
1031
+ const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
1032
+ const name = path.basename(file, ".md");
1033
+ allDocsForErrors.push(parseCommandDoc(name, content));
1034
+ }
1035
+ }
1036
+ let generated = 0;
1037
+ const errorsContent = generateErrors(moduleName, allDocsForErrors);
1038
+ if (errorsContent) {
1039
+ fs.mkdirSync(libDir, { recursive: true });
1040
+ const errorsFile = path.join(libDir, "errors.generated.ts");
1041
+ fs.writeFileSync(errorsFile, errorsContent);
1042
+ console.log(` wrote ${path.relative(modulePath, errorsFile)}`);
1043
+ generated++;
1044
+ }
1045
+ const permissionsContent = generatePermissions(moduleName, parsedDocs.map((d) => d.commandName));
1046
+ const permissionsFile = path.join(libDir, "permissions.generated.ts");
1047
+ fs.writeFileSync(permissionsFile, permissionsContent);
1048
+ console.log(` wrote ${path.relative(modulePath, permissionsFile)}`);
1049
+ generated++;
1050
+ fs.mkdirSync(commandDir, { recursive: true });
1051
+ for (const doc of parsedDocs) {
1052
+ const implFile = path.join(commandDir, `${doc.commandName}.ts`);
1053
+ const shellContent = generateCommandShell(doc);
1054
+ const shellFile = path.join(commandDir, `${doc.commandName}.generated.ts`);
1055
+ fs.writeFileSync(shellFile, shellContent);
1056
+ console.log(` wrote ${path.relative(modulePath, shellFile)}`);
1057
+ generated++;
1058
+ if (!fs.existsSync(implFile)) {
1059
+ fs.writeFileSync(implFile, generateCommandStub(doc));
1060
+ console.log(` scaffolded ${path.relative(modulePath, implFile)}`);
1061
+ }
1062
+ const testFile = path.join(commandDir, `${doc.commandName}.test.ts`);
1063
+ if (!fs.existsSync(testFile)) {
1064
+ fs.writeFileSync(testFile, generateTestStub(doc));
1065
+ console.log(` scaffolded ${path.relative(modulePath, testFile)}`);
1066
+ }
1067
+ }
1068
+ if (fs.existsSync(queryDocsDir)) {
1069
+ const queryFiles = fs.readdirSync(queryDocsDir).filter((f) => f.endsWith(".md"));
1070
+ if (queryFiles.length > 0) {
1071
+ const queryDir = path.join(modulePath, "query");
1072
+ fs.mkdirSync(queryDir, { recursive: true });
1073
+ for (const file of queryFiles) {
1074
+ const content = fs.readFileSync(path.join(queryDocsDir, file), "utf-8");
1075
+ const doc = parseCommandDoc(path.basename(file, ".md"), content);
1076
+ const shellFile = path.join(queryDir, `${doc.commandName}.generated.ts`);
1077
+ fs.writeFileSync(shellFile, generateQueryShell(doc));
1078
+ console.log(` wrote ${path.relative(modulePath, shellFile)}`);
1079
+ generated++;
1080
+ const implFile = path.join(queryDir, `${doc.commandName}.ts`);
1081
+ if (!fs.existsSync(implFile)) {
1082
+ fs.writeFileSync(implFile, generateQueryStub(doc));
1083
+ console.log(` scaffolded ${path.relative(modulePath, implFile)}`);
1084
+ }
1085
+ const testFile = path.join(queryDir, `${doc.commandName}.test.ts`);
1086
+ if (!fs.existsSync(testFile)) {
1087
+ fs.writeFileSync(testFile, generateQueryTestStub(doc));
1088
+ console.log(` scaffolded ${path.relative(modulePath, testFile)}`);
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+ console.log(`Generated ${generated} file(s) for ${moduleName}`);
1094
+ return 0;
1095
+ }
1096
+ //#endregion
1097
+ //#region src/commands/module/generate.ts
1098
+ const cwd$3 = process.cwd();
1099
+ const generateCommand = defineCommand({
1100
+ name: "generate",
1101
+ description: "Generate code from model definitions and docs",
1102
+ subCommands: { code: defineCommand({
1103
+ name: "code",
1104
+ description: "Generate errors, permissions, command shells, and query shells from docs",
1105
+ args: z.object({
1106
+ root: arg(z.string(), {
1107
+ alias: "r",
1108
+ description: "Path to modules directory"
1109
+ }),
1110
+ module: arg(z.string(), {
1111
+ positional: true,
1112
+ description: "Module name (e.g., primitives, item-management)"
1113
+ })
1114
+ }),
1115
+ run: (args) => {
1116
+ const modulePath = path.resolve(cwd$3, args.root, args.module);
1117
+ console.log(`Generating code for ${args.module}...`);
1118
+ const exitCode = runGenerateCode(modulePath, args.module);
1119
+ process.exit(exitCode);
1120
+ }
1121
+ }) }
1122
+ });
1123
+ //#endregion
1124
+ //#region src/commands/module/index.ts
1125
+ const cwd$2 = process.cwd();
1126
+ const rootArgs$1 = z.object({ root: arg(z.string(), {
1127
+ alias: "r",
1128
+ description: "Path to modules directory"
1129
+ }) });
1130
+ const listCommand = defineCommand({
1131
+ name: "list",
1132
+ description: "List available modules",
1133
+ run: () => {
1134
+ const exitCode = runModuleList();
1135
+ process.exit(exitCode);
1136
+ }
1137
+ });
1138
+ const checkCommand$1 = defineCommand({
1139
+ name: "check",
1140
+ description: "Validate module docs against schemas",
1141
+ args: rootArgs$1,
1142
+ run: async (args) => {
1143
+ const exitCode = await runCheck({ modulesRoot: args.root }, cwd$2);
1144
+ process.exit(exitCode);
1145
+ }
1146
+ });
1147
+ const syncCheckCommand$1 = defineCommand({
1148
+ name: "sync-check",
1149
+ description: "Validate source <-> doc correspondence",
1150
+ args: rootArgs$1,
1151
+ run: async (args) => {
1152
+ const result = await runSyncCheck({ modulesRoot: args.root }, cwd$2);
1153
+ console.log(formatSyncCheckReport(result));
1154
+ process.exit(result.exitCode);
1155
+ }
1156
+ });
1157
+ const scaffoldCommand$1 = defineCommand({
1158
+ name: "scaffold",
1159
+ description: "Generate module doc from schema template",
1160
+ args: rootArgs$1.extend({
1161
+ type: arg(z.enum(MODULE_TYPES), {
1162
+ positional: true,
1163
+ description: `Scaffold type (${MODULE_TYPES.join(", ")})`
1164
+ }),
1165
+ parent: arg(z.string(), {
1166
+ positional: true,
1167
+ description: "Module name"
1168
+ }),
1169
+ name: arg(z.string().optional(), {
1170
+ positional: true,
1171
+ description: "Item name (required for feature, command, model)"
1172
+ })
1173
+ }),
1174
+ run: async (args) => {
1175
+ const exitCode = await runScaffold(args.type, args.parent, args.name, args.root, cwd$2);
1176
+ process.exit(exitCode);
1177
+ }
1178
+ });
1179
+ const moduleCommand = defineCommand({
1180
+ name: "module",
1181
+ description: "Module management",
1182
+ subCommands: {
1183
+ list: listCommand,
1184
+ check: checkCommand$1,
1185
+ "sync-check": syncCheckCommand$1,
1186
+ scaffold: scaffoldCommand$1,
1187
+ generate: generateCommand
1188
+ }
1189
+ });
1190
+ //#endregion
1191
+ //#region src/commands/app/index.ts
1192
+ const cwd$1 = process.cwd();
1193
+ const rootArgs = z.object({ root: arg(z.string(), {
1194
+ alias: "r",
1195
+ description: "Path to app-compose directory"
1196
+ }) });
1197
+ const checkCommand = defineCommand({
1198
+ name: "check",
1199
+ description: "Validate app docs against schemas",
1200
+ args: rootArgs,
1201
+ run: async (args) => {
1202
+ const exitCode = await runCheck({ appRoot: args.root }, cwd$1);
1203
+ process.exit(exitCode);
1204
+ }
1205
+ });
1206
+ const syncCheckCommand = defineCommand({
1207
+ name: "sync-check",
1208
+ description: "Validate source <-> doc correspondence",
1209
+ args: rootArgs,
1210
+ run: async (args) => {
1211
+ const result = await runSyncCheck({ appRoot: args.root }, cwd$1);
1212
+ console.log(formatSyncCheckReport(result));
1213
+ process.exit(result.exitCode);
1214
+ }
1215
+ });
1216
+ const scaffoldCommand = defineCommand({
1217
+ name: "scaffold",
1218
+ description: "Generate app doc from schema template",
1219
+ args: rootArgs.extend({
1220
+ type: arg(z.enum(APP_TYPES), {
1221
+ positional: true,
1222
+ description: `Scaffold type (${APP_TYPES.join(", ")})`
1223
+ }),
1224
+ parent: arg(z.string(), {
1225
+ positional: true,
1226
+ description: "App name"
1227
+ }),
1228
+ name: arg(z.string().optional(), {
1229
+ positional: true,
1230
+ description: "Item name (required for most types)"
1231
+ })
1232
+ }),
1233
+ run: async (args) => {
1234
+ const exitCode = await runScaffold(args.type, args.parent, args.name, args.root, cwd$1);
1235
+ process.exit(exitCode);
1236
+ }
1237
+ });
1238
+ const appCommand = defineCommand({
1239
+ name: "app",
1240
+ description: "App-compose management",
1241
+ subCommands: {
1242
+ check: checkCommand,
1243
+ "sync-check": syncCheckCommand,
1244
+ scaffold: scaffoldCommand
1245
+ }
1246
+ });
1247
+ //#endregion
1248
+ //#region src/mockServer.ts
1249
+ /**
1250
+ * Start a Mockoon mock server from a mock.json file.
1251
+ * Returns a handle with the server URL and a stop function.
1252
+ */
1253
+ async function createMockServer(mockJsonPath, port) {
1254
+ const { MockoonServer, createLoggerInstance, listenServerEvents } = await import("@mockoon/commons-server");
1255
+ const resolvedPath = resolve(mockJsonPath);
1256
+ const environment = JSON.parse(readFileSync(resolvedPath, "utf-8"));
1257
+ if (port !== void 0) environment.port = port;
1258
+ const actualPort = environment.port;
1259
+ const logger = createLoggerInstance(null);
1260
+ const server = new MockoonServer(environment, {
1261
+ environmentDirectory: dirname(resolvedPath),
1262
+ enableAdminApi: false,
1263
+ disableTls: true,
1264
+ enableRandomLatency: false,
1265
+ maxTransactionLogs: 100,
1266
+ envVarsPrefix: "MOCKOON_"
1267
+ });
1268
+ listenServerEvents(server, environment, logger, false);
1269
+ await new Promise((resolve, reject) => {
1270
+ server.on("started", resolve);
1271
+ server.on("error", (errorCode, originalError) => {
1272
+ reject(originalError ?? /* @__PURE__ */ new Error(`Mockoon error: ${errorCode}`));
1273
+ });
1274
+ server.start();
1275
+ });
1276
+ return {
1277
+ url: `http://127.0.0.1:${actualPort}`,
1278
+ port: actualPort,
1279
+ stop: () => new Promise((resolve) => {
1280
+ server.on("stopped", resolve);
1281
+ server.stop();
1282
+ })
1283
+ };
1284
+ }
1285
+ //#endregion
1286
+ //#region src/commands/mock/start.ts
1287
+ function readdirSafe(dir) {
1288
+ try {
1289
+ return readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1290
+ } catch {
1291
+ return [];
1292
+ }
1293
+ }
1294
+ function discoverMocks(mocksDir, filters) {
1295
+ const mocks = [];
1296
+ for (const provider of readdirSafe(mocksDir)) {
1297
+ const providerDir = join(mocksDir, provider);
1298
+ for (const scenario of readdirSafe(providerDir)) {
1299
+ const mockPath = join(providerDir, scenario, "mock.json");
1300
+ if (!existsSync(mockPath)) continue;
1301
+ mocks.push({
1302
+ provider,
1303
+ scenario,
1304
+ mockPath
1305
+ });
1306
+ }
1307
+ }
1308
+ if (filters.length === 0) return mocks;
1309
+ return mocks.filter(({ provider, scenario }) => filters.some((f) => f === provider || f === `${provider}/${scenario}`));
1310
+ }
1311
+ function findFreePort() {
1312
+ return new Promise((resolve, reject) => {
1313
+ const srv = createServer$1();
1314
+ srv.listen(0, "127.0.0.1", () => {
1315
+ const addr = srv.address();
1316
+ const port = typeof addr === "object" && addr ? addr.port : 0;
1317
+ srv.close(() => resolve(port));
1318
+ });
1319
+ srv.on("error", reject);
1320
+ });
1321
+ }
1322
+ async function startMock(mock) {
1323
+ const port = await findFreePort();
1324
+ return {
1325
+ server: await createMockServer(mock.mockPath, port),
1326
+ route: `/${mock.provider}/${mock.scenario}`,
1327
+ provider: mock.provider,
1328
+ scenario: mock.scenario
1329
+ };
1330
+ }
1331
+ function createProxy(routeTable) {
1332
+ return createServer((req, res) => {
1333
+ const match = req.url?.match(/^\/([^/?]+)\/([^/?]+)(\/[^?]*)?(\?.*)?$/);
1334
+ if (!match) {
1335
+ res.writeHead(404, { "Content-Type": "application/json" });
1336
+ res.end(JSON.stringify({ error: "Not found. Use /{provider}/{scenario}/..." }));
1337
+ return;
1338
+ }
1339
+ const [, provider, scenario] = match;
1340
+ const key = `/${provider}/${scenario}`;
1341
+ const target = routeTable.get(key);
1342
+ if (!target) {
1343
+ res.writeHead(404, { "Content-Type": "application/json" });
1344
+ res.end(JSON.stringify({
1345
+ error: `Unknown route: ${key}`,
1346
+ available: [...routeTable.keys()]
1347
+ }));
1348
+ return;
1349
+ }
1350
+ const downstream = (req.url ?? "/").slice(key.length) || "/";
1351
+ const proxyReq = request({
1352
+ hostname: "127.0.0.1",
1353
+ port: target.server.port,
1354
+ path: downstream,
1355
+ method: req.method,
1356
+ headers: {
1357
+ ...req.headers,
1358
+ host: `127.0.0.1:${target.server.port}`
1359
+ }
1360
+ }, (proxyRes) => {
1361
+ res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
1362
+ proxyRes.pipe(res);
1363
+ });
1364
+ proxyReq.on("error", (err) => {
1365
+ if (!res.headersSent) res.writeHead(502, { "Content-Type": "application/json" });
1366
+ res.end(JSON.stringify({
1367
+ error: "Bad gateway",
1368
+ detail: err.message
1369
+ }));
1370
+ });
1371
+ req.pipe(proxyReq);
1372
+ });
1373
+ }
1374
+ async function runMockStart(mocksRoot, filters, port) {
1375
+ const mocksDir = resolve(mocksRoot);
1376
+ const mocks = discoverMocks(mocksDir, filters);
1377
+ if (mocks.length === 0) {
1378
+ console.error("No matching mocks found.");
1379
+ if (filters.length > 0) {
1380
+ console.error(`Filters: ${filters.join(", ")}`);
1381
+ console.error(`Available mocks are under: ${relative(process.cwd(), mocksDir)}/`);
1382
+ }
1383
+ return 1;
1384
+ }
1385
+ console.log(`Starting ${mocks.length} mock(s)...\n`);
1386
+ const running = [];
1387
+ for (const mock of mocks) {
1388
+ const info = await startMock(mock);
1389
+ running.push(info);
1390
+ }
1391
+ const routeTable = /* @__PURE__ */ new Map();
1392
+ for (const r of running) routeTable.set(r.route, r);
1393
+ console.log("Route table:");
1394
+ console.log("─".repeat(50));
1395
+ for (const [route, { server }] of routeTable) console.log(` ${route} \u2192 127.0.0.1:${server.port}`);
1396
+ console.log("─".repeat(50));
1397
+ const proxy = createProxy(routeTable);
1398
+ proxy.listen(port, () => {
1399
+ console.log(`\nReverse proxy listening on http://localhost:${port}`);
1400
+ console.log("Press Ctrl+C to stop all mocks.\n");
1401
+ });
1402
+ function shutdown() {
1403
+ console.log("\nShutting down...");
1404
+ proxy.close();
1405
+ for (const { server } of running) server.stop();
1406
+ process.exit(0);
1407
+ }
1408
+ process.on("SIGINT", shutdown);
1409
+ process.on("SIGTERM", shutdown);
1410
+ return new Promise(() => void 0);
1411
+ }
1412
+ //#endregion
1413
+ //#region src/commands/mock/validate.ts
1414
+ function pass(msg) {
1415
+ console.log(chalk.green(`\u2713 ${msg}`));
1416
+ }
1417
+ function fail(ctx, msg) {
1418
+ console.log(chalk.red(`\u2717 ${msg}`));
1419
+ ctx.failures++;
1420
+ }
1421
+ async function subdirs(dir) {
1422
+ const entries = await readdir(dir);
1423
+ const dirs = [];
1424
+ for (const entry of entries) if ((await stat(join(dir, entry))).isDirectory()) dirs.push(entry);
1425
+ return dirs.sort();
1426
+ }
1427
+ function checkStructure(ctx, entries, label) {
1428
+ if (entries.includes("README.md")) pass(`${label}: has README.md`);
1429
+ else fail(ctx, `${label}: missing README.md`);
1430
+ if (entries.includes("mock.json")) {
1431
+ pass(`${label}: has mock.json`);
1432
+ return true;
1433
+ }
1434
+ fail(ctx, `${label}: missing mock.json`);
1435
+ return false;
1436
+ }
1437
+ async function checkSchema(ctx, data, label) {
1438
+ const result = (await import("@mockoon/commons")).EnvironmentSchemaNoFix.validate(data, { abortEarly: false });
1439
+ if (!result.error) pass(`${label}: valid Mockoon schema`);
1440
+ else for (const detail of result.error.details) fail(ctx, `${label}: schema — ${detail.message}`);
1441
+ }
1442
+ function checkResponseQuality(ctx, data, label) {
1443
+ const routes = data.routes ?? [];
1444
+ for (const route of routes) for (const resp of route.responses ?? []) {
1445
+ const respLabel = `${label} \u2192 ${route.uuid}/${resp.uuid}`;
1446
+ if ((resp.headers ?? []).some((h) => h.key.toLowerCase() === "content-type")) pass(`${respLabel}: has Content-Type`);
1447
+ else fail(ctx, `${respLabel}: missing Content-Type header`);
1448
+ if (resp.label && resp.label.trim().length > 0) pass(`${respLabel}: has label "${resp.label}"`);
1449
+ else fail(ctx, `${respLabel}: missing or empty label`);
1450
+ }
1451
+ }
1452
+ function checkDatabucketRefs(ctx, data, label) {
1453
+ const bucketIds = new Set((data.data ?? []).map((d) => d.id));
1454
+ for (const route of data.routes ?? []) for (const resp of route.responses ?? []) if (resp.bodyType === "DATABUCKET") {
1455
+ const respLabel = `${label} \u2192 ${route.uuid}/${resp.uuid}`;
1456
+ if (resp.databucketID && bucketIds.has(resp.databucketID)) pass(`${respLabel}: databucket "${resp.databucketID}" exists`);
1457
+ else fail(ctx, `${respLabel}: databucketID "${resp.databucketID}" not found in data array`);
1458
+ }
1459
+ }
1460
+ async function discoverAllScenarios(mocksDir) {
1461
+ const scenarios = [];
1462
+ const providers = await subdirs(mocksDir);
1463
+ for (const provider of providers) {
1464
+ const providerDir = join(mocksDir, provider);
1465
+ for (const scenario of await subdirs(providerDir)) scenarios.push(`${provider}/${scenario}`);
1466
+ }
1467
+ return scenarios;
1468
+ }
1469
+ function resolveScenarioDir(arg) {
1470
+ const abs = resolve(arg);
1471
+ if (basename(abs) === "mock.json") return dirname(abs);
1472
+ return abs;
1473
+ }
1474
+ async function validateScenario(ctx, scenarioDir, mocksDir) {
1475
+ const label = relative(mocksDir, scenarioDir);
1476
+ console.log(chalk.bold(`\n\u2500\u2500 ${label} \u2500\u2500`));
1477
+ let entries;
1478
+ try {
1479
+ entries = await readdir(scenarioDir);
1480
+ } catch {
1481
+ fail(ctx, `${label}: directory not found`);
1482
+ return;
1483
+ }
1484
+ if (!checkStructure(ctx, entries, label)) return;
1485
+ const mockPath = join(scenarioDir, "mock.json");
1486
+ let data;
1487
+ try {
1488
+ data = JSON.parse(await readFile(mockPath, "utf-8"));
1489
+ } catch (err) {
1490
+ fail(ctx, `${label}: invalid JSON \u2014 ${err instanceof Error ? err.message : String(err)}`);
1491
+ return;
1492
+ }
1493
+ await checkSchema(ctx, data, label);
1494
+ checkResponseQuality(ctx, data, label);
1495
+ checkDatabucketRefs(ctx, data, label);
1496
+ }
1497
+ async function runMockValidate(mocksRoot, paths) {
1498
+ const ctx = { failures: 0 };
1499
+ const mocksDir = resolve(mocksRoot);
1500
+ const targets = paths.length > 0 ? paths.map(resolveScenarioDir) : (await discoverAllScenarios(mocksDir)).map((s) => join(mocksDir, s));
1501
+ if (targets.length === 0) {
1502
+ fail(ctx, "No scenarios found under mocks/");
1503
+ return 1;
1504
+ }
1505
+ console.log(chalk.bold("\nValidating mock configs...\n"));
1506
+ for (const target of targets) await validateScenario(ctx, target, mocksDir);
1507
+ console.log(chalk.bold("\n── summary ──"));
1508
+ if (ctx.failures === 0) console.log(chalk.green("✓ All checks passed"));
1509
+ else console.log(chalk.red(`\u2717 ${ctx.failures} check(s) failed`));
1510
+ return ctx.failures === 0 ? 0 : 1;
1511
+ }
1512
+ const mockCommand = defineCommand({
1513
+ name: "mock",
1514
+ description: "Mock API server management",
1515
+ subCommands: {
1516
+ start: defineCommand({
1517
+ name: "start",
1518
+ description: "Start mock API servers with reverse proxy",
1519
+ args: z.object({
1520
+ mocksRoot: arg(z.string().default("./mocks"), { description: "Path to mocks directory" }),
1521
+ port: arg(z.coerce.number().default(3e3), {
1522
+ alias: "p",
1523
+ description: "Reverse proxy port"
1524
+ }),
1525
+ filter: arg(z.array(z.string()).default([]), {
1526
+ positional: true,
1527
+ description: "Filter by provider or provider/scenario"
1528
+ })
1529
+ }),
1530
+ run: async (args) => {
1531
+ const exitCode = await runMockStart(args.mocksRoot, args.filter, args.port);
1532
+ process.exit(exitCode);
1533
+ }
1534
+ }),
1535
+ validate: defineCommand({
1536
+ name: "validate",
1537
+ description: "Validate mock scenario configs",
1538
+ args: z.object({
1539
+ mocksRoot: arg(z.string().default("./mocks"), { description: "Path to mocks directory" }),
1540
+ paths: arg(z.array(z.string()).default([]), {
1541
+ positional: true,
1542
+ description: "Specific scenario paths to validate"
1543
+ })
1544
+ }),
1545
+ run: async (args) => {
1546
+ const exitCode = await runMockValidate(args.mocksRoot, args.paths);
1547
+ process.exit(exitCode);
1548
+ }
1549
+ })
1550
+ }
1551
+ });
1552
+ //#endregion
1553
+ //#region src/commands/index.ts
1554
+ const cwd = process.cwd();
1555
+ //#endregion
1556
+ //#region src/cli.ts
1557
+ runMain(defineCommand({
1558
+ name: "erp-kit",
1559
+ description: "ERP module framework CLI",
1560
+ subCommands: {
1561
+ module: moduleCommand,
1562
+ app: appCommand,
1563
+ mock: mockCommand,
1564
+ init: defineCommand({
1565
+ name: "init",
1566
+ description: "First-time setup for a consumer repo",
1567
+ run: () => {
1568
+ const exitCode = runInit(cwd);
1569
+ process.exit(exitCode);
1570
+ }
1571
+ }),
1572
+ update: defineCommand({
1573
+ name: "update",
1574
+ description: "Refresh framework-managed resources (skills, workflows)",
1575
+ args: z.object({ resources: arg(z.array(z.string()).default([]), {
1576
+ positional: true,
1577
+ description: "Resources to update (skills, workflows). Defaults to all."
1578
+ }) }),
1579
+ run: (args) => {
1580
+ const exitCode = runUpdate(cwd, args.resources);
1581
+ process.exit(exitCode);
1582
+ }
1583
+ }),
1584
+ license: defineCommand({
1585
+ name: "license",
1586
+ description: "License management",
1587
+ subCommands: {
1588
+ check: defineCommand({
1589
+ name: "check",
1590
+ description: "Check dependency licenses against allowlist",
1591
+ args: z.object({ config: arg(z.string(), {
1592
+ alias: "c",
1593
+ description: "Path to license config JSON file"
1594
+ }) }),
1595
+ run: (args) => {
1596
+ const exitCode = runLicenseCheck(args.config);
1597
+ process.exit(exitCode);
1598
+ }
1599
+ }),
1600
+ list: defineCommand({
1601
+ name: "list",
1602
+ description: "List available license groups",
1603
+ run: () => {
1604
+ const exitCode = runLicenseList();
1605
+ process.exit(exitCode);
1606
+ }
1607
+ })
1608
+ }
1609
+ })
1610
+ }
1611
+ }));
1612
+ //#endregion
1613
+ export {};