@tailor-platform/erp-kit 0.1.2 → 0.2.1

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