@tailor-platform/erp-kit 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +81 -12
  3. package/dist/cli.js +1070 -450
  4. package/package.json +11 -8
  5. package/schemas/module/model.yml +5 -0
  6. package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/SKILL.md +2 -2
  7. package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/SKILL.md +3 -3
  8. package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/SKILL.md +2 -2
  9. package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/SKILL.md +3 -3
  10. package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/SKILL.md +4 -4
  11. package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/SKILL.md +3 -3
  12. package/skills/{mock-scenario → erp-kit-mock-scenario}/SKILL.md +1 -1
  13. package/skills/{1-module-docs → erp-kit-module-1-docs}/SKILL.md +2 -2
  14. package/skills/{2-module-feature-breakdown → erp-kit-module-2-feature-breakdown}/SKILL.md +13 -9
  15. package/skills/erp-kit-module-2-feature-breakdown/references/naming.md +59 -0
  16. package/skills/{3-module-doc-review → erp-kit-module-3-doc-review}/SKILL.md +83 -25
  17. package/skills/erp-kit-module-4-tdd/SKILL.md +94 -0
  18. package/skills/erp-kit-module-4-tdd/references/cross-module-dependency.md +133 -0
  19. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/db-relations.md +5 -1
  20. package/skills/{4-module-tdd-implementation → erp-kit-module-4-tdd}/references/exports.md +1 -1
  21. package/skills/erp-kit-module-4-tdd/references/generated-code.md +32 -0
  22. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/SKILL.md +46 -44
  23. package/skills/erp-kit-module-5-impl-review/references/commands.md +62 -0
  24. package/skills/erp-kit-module-5-impl-review/references/errors.md +10 -0
  25. package/skills/{5-module-implementation-review → erp-kit-module-5-impl-review}/references/testing.md +1 -1
  26. package/skills/erp-kit-module-shared/SKILL.md +16 -0
  27. package/skills/erp-kit-module-shared/references/commands.md +203 -0
  28. package/skills/erp-kit-module-shared/references/errors.md +35 -0
  29. package/skills/erp-kit-module-shared/references/queries.md +168 -0
  30. package/skills/erp-kit-module-shared/references/structure.md +36 -0
  31. package/skills/{3-module-doc-review → erp-kit-module-shared}/references/testing.md +4 -3
  32. package/skills/erp-kit-update/SKILL.md +64 -0
  33. package/src/cli.doc.test.ts +65 -0
  34. package/src/cli.ts +3 -35
  35. package/src/commands/app/index.ts +3 -3
  36. package/src/commands/check.test.ts +1 -1
  37. package/src/commands/check.ts +2 -2
  38. package/src/commands/index.ts +73 -0
  39. package/src/commands/init.test.ts +22 -5
  40. package/src/commands/init.ts +25 -16
  41. package/src/commands/license.ts +193 -0
  42. package/src/commands/mock/index.ts +2 -2
  43. package/src/commands/mock/start.ts +1 -1
  44. package/src/commands/mock/validate.test.ts +1 -1
  45. package/src/commands/module/generate.ts +35 -0
  46. package/src/commands/module/index.ts +6 -4
  47. package/src/commands/module/list.test.ts +7 -12
  48. package/src/commands/module/list.ts +1 -1
  49. package/src/commands/scaffold-templates.ts +65 -0
  50. package/src/commands/scaffold.test.ts +92 -2
  51. package/src/commands/scaffold.ts +22 -2
  52. package/src/commands/sync-check.test.ts +60 -1
  53. package/src/commands/sync-check.ts +35 -2
  54. package/src/generator/generate-code.test.ts +200 -0
  55. package/src/generator/generate-code.ts +260 -0
  56. package/src/generator/parse-command-doc.test.ts +159 -0
  57. package/src/generator/parse-command-doc.ts +116 -0
  58. package/src/integration.test.ts +2 -2
  59. package/src/module.ts +6 -6
  60. package/src/modules/item-management/README.md +38 -0
  61. package/src/modules/item-management/command/activateItem.generated.ts +6 -0
  62. package/src/modules/item-management/command/activateItem.test.ts +76 -0
  63. package/src/modules/item-management/command/activateItem.ts +42 -0
  64. package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +6 -0
  65. package/src/modules/item-management/command/assignItemToTaxonomy.test.ts +88 -0
  66. package/src/modules/item-management/command/assignItemToTaxonomy.ts +63 -0
  67. package/src/modules/item-management/command/createItem.generated.ts +6 -0
  68. package/src/modules/item-management/command/createItem.test.ts +152 -0
  69. package/src/modules/item-management/command/createItem.ts +72 -0
  70. package/src/modules/item-management/command/createTaxonomyNode.generated.ts +6 -0
  71. package/src/modules/item-management/command/createTaxonomyNode.test.ts +126 -0
  72. package/src/modules/item-management/command/createTaxonomyNode.ts +70 -0
  73. package/src/modules/item-management/command/deactivateItem.generated.ts +6 -0
  74. package/src/modules/item-management/command/deactivateItem.test.ts +76 -0
  75. package/src/modules/item-management/command/deactivateItem.ts +42 -0
  76. package/src/modules/item-management/command/deleteItem.generated.ts +6 -0
  77. package/src/modules/item-management/command/deleteItem.test.ts +61 -0
  78. package/src/modules/item-management/command/deleteItem.ts +38 -0
  79. package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +6 -0
  80. package/src/modules/item-management/command/deleteTaxonomyNode.test.ts +73 -0
  81. package/src/modules/item-management/command/deleteTaxonomyNode.ts +50 -0
  82. package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +6 -0
  83. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +136 -0
  84. package/src/modules/item-management/command/moveTaxonomyNode.ts +85 -0
  85. package/src/modules/item-management/command/reactivateItem.generated.ts +6 -0
  86. package/src/modules/item-management/command/reactivateItem.test.ts +76 -0
  87. package/src/modules/item-management/command/reactivateItem.ts +42 -0
  88. package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +6 -0
  89. package/src/modules/item-management/command/removeItemFromTaxonomy.test.ts +43 -0
  90. package/src/modules/item-management/command/removeItemFromTaxonomy.ts +30 -0
  91. package/src/modules/item-management/command/updateItem.generated.ts +6 -0
  92. package/src/modules/item-management/command/updateItem.test.ts +178 -0
  93. package/src/modules/item-management/command/updateItem.ts +103 -0
  94. package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +6 -0
  95. package/src/modules/item-management/command/updateTaxonomyNode.test.ts +88 -0
  96. package/src/modules/item-management/command/updateTaxonomyNode.ts +62 -0
  97. package/src/modules/item-management/db/item.ts +47 -0
  98. package/src/modules/item-management/db/itemTaxonomyAssignment.ts +49 -0
  99. package/src/modules/item-management/db/taxonomyNode.ts +34 -0
  100. package/src/modules/item-management/docs/commands/ActivateItem.md +32 -0
  101. package/src/modules/item-management/docs/commands/AssignItemToTaxonomy.md +38 -0
  102. package/src/modules/item-management/docs/commands/CreateItem.md +44 -0
  103. package/src/modules/item-management/docs/commands/CreateTaxonomyNode.md +44 -0
  104. package/src/modules/item-management/docs/commands/DeactivateItem.md +34 -0
  105. package/src/modules/item-management/docs/commands/DeleteItem.md +35 -0
  106. package/src/modules/item-management/docs/commands/DeleteTaxonomyNode.md +39 -0
  107. package/src/modules/item-management/docs/commands/MoveTaxonomyNode.md +45 -0
  108. package/src/modules/item-management/docs/commands/ReactivateItem.md +34 -0
  109. package/src/modules/item-management/docs/commands/RemoveItemFromTaxonomy.md +30 -0
  110. package/src/modules/item-management/docs/commands/UpdateItem.md +55 -0
  111. package/src/modules/item-management/docs/commands/UpdateTaxonomyNode.md +36 -0
  112. package/src/modules/item-management/docs/features/item-lifecycle.md +60 -0
  113. package/src/modules/item-management/docs/features/item-taxonomy.md +65 -0
  114. package/src/modules/item-management/docs/models/ItemTaxonomyAssignment.md +36 -0
  115. package/src/modules/item-management/docs/models/TaxonomyNode.md +47 -0
  116. package/src/modules/item-management/docs/models/item.md +59 -0
  117. package/src/modules/item-management/docs/queries/CalculateNodeDepth.md +36 -0
  118. package/src/modules/item-management/docs/queries/CalculateSubtreeDepth.md +40 -0
  119. package/src/modules/item-management/docs/queries/DetectCircularReference.md +41 -0
  120. package/src/modules/item-management/docs/queries/GetItem.md +38 -0
  121. package/src/modules/item-management/docs/queries/GetItemTaxonomyAssignment.md +29 -0
  122. package/src/modules/item-management/docs/queries/GetTaxonomyNode.md +35 -0
  123. package/src/modules/item-management/docs/queries/GetTaxonomyNodeAssignments.md +29 -0
  124. package/src/modules/item-management/docs/queries/GetTaxonomyNodeChildren.md +29 -0
  125. package/src/modules/item-management/generated/enums.ts +9 -0
  126. package/src/modules/item-management/generated/kysely-tailordb.ts +62 -0
  127. package/src/modules/item-management/index.ts +53 -0
  128. package/src/modules/item-management/lib/_db_deps.ts +13 -0
  129. package/src/modules/item-management/lib/errors.generated.ts +117 -0
  130. package/src/modules/item-management/lib/permissions.generated.ts +17 -0
  131. package/src/modules/item-management/lib/types.ts +19 -0
  132. package/src/modules/item-management/module.ts +97 -0
  133. package/src/modules/item-management/query/calculateNodeDepth.generated.ts +5 -0
  134. package/src/modules/item-management/query/calculateNodeDepth.test.ts +56 -0
  135. package/src/modules/item-management/query/calculateNodeDepth.ts +28 -0
  136. package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +5 -0
  137. package/src/modules/item-management/query/calculateSubtreeDepth.test.ts +75 -0
  138. package/src/modules/item-management/query/calculateSubtreeDepth.ts +29 -0
  139. package/src/modules/item-management/query/detectCircularReference.generated.ts +5 -0
  140. package/src/modules/item-management/query/detectCircularReference.test.ts +61 -0
  141. package/src/modules/item-management/query/detectCircularReference.ts +32 -0
  142. package/src/modules/item-management/query/getItem.generated.ts +5 -0
  143. package/src/modules/item-management/query/getItem.test.ts +67 -0
  144. package/src/modules/item-management/query/getItem.ts +20 -0
  145. package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +5 -0
  146. package/src/modules/item-management/query/getItemTaxonomyAssignment.test.ts +25 -0
  147. package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +18 -0
  148. package/src/modules/item-management/query/getTaxonomyNode.generated.ts +5 -0
  149. package/src/modules/item-management/query/getTaxonomyNode.test.ts +47 -0
  150. package/src/modules/item-management/query/getTaxonomyNode.ts +18 -0
  151. package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +5 -0
  152. package/src/modules/item-management/query/getTaxonomyNodeAssignments.test.ts +25 -0
  153. package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +16 -0
  154. package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +5 -0
  155. package/src/modules/item-management/query/getTaxonomyNodeChildren.test.ts +34 -0
  156. package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +16 -0
  157. package/src/modules/item-management/tailor.config.ts +11 -0
  158. package/src/modules/item-management/testing/fixtures.ts +81 -0
  159. package/src/modules/primitives/command/activateCategory.generated.ts +6 -0
  160. package/src/modules/primitives/command/activateCategory.test.ts +11 -29
  161. package/src/modules/primitives/command/activateCategory.ts +27 -34
  162. package/src/modules/primitives/command/activateCurrency.generated.ts +6 -0
  163. package/src/modules/primitives/command/activateCurrency.test.ts +11 -29
  164. package/src/modules/primitives/command/activateCurrency.ts +27 -34
  165. package/src/modules/primitives/command/activateUnit.generated.ts +6 -0
  166. package/src/modules/primitives/command/activateUnit.test.ts +11 -15
  167. package/src/modules/primitives/command/activateUnit.ts +27 -34
  168. package/src/modules/primitives/command/createCategory.generated.ts +6 -0
  169. package/src/modules/primitives/command/createCategory.test.ts +27 -39
  170. package/src/modules/primitives/command/createCategory.ts +53 -62
  171. package/src/modules/primitives/command/createCurrency.generated.ts +6 -0
  172. package/src/modules/primitives/command/createCurrency.test.ts +78 -71
  173. package/src/modules/primitives/command/createCurrency.ts +43 -48
  174. package/src/modules/primitives/command/createExchangeRate.generated.ts +6 -0
  175. package/src/modules/primitives/command/createExchangeRate.test.ts +101 -100
  176. package/src/modules/primitives/command/createExchangeRate.ts +50 -59
  177. package/src/modules/primitives/command/createUnit.generated.ts +6 -0
  178. package/src/modules/primitives/command/createUnit.test.ts +92 -95
  179. package/src/modules/primitives/command/createUnit.ts +54 -57
  180. package/src/modules/primitives/command/deactivateCategory.generated.ts +6 -0
  181. package/src/modules/primitives/command/deactivateCategory.test.ts +27 -28
  182. package/src/modules/primitives/command/deactivateCategory.ts +43 -50
  183. package/src/modules/primitives/command/deactivateCurrency.generated.ts +6 -0
  184. package/src/modules/primitives/command/deactivateCurrency.test.ts +23 -38
  185. package/src/modules/primitives/command/deactivateCurrency.ts +31 -38
  186. package/src/modules/primitives/command/deactivateUnit.generated.ts +6 -0
  187. package/src/modules/primitives/command/deactivateUnit.test.ts +27 -23
  188. package/src/modules/primitives/command/deactivateUnit.ts +39 -49
  189. package/src/modules/primitives/command/setBaseCurrency.generated.ts +6 -0
  190. package/src/modules/primitives/command/setBaseCurrency.test.ts +40 -33
  191. package/src/modules/primitives/command/setBaseCurrency.ts +43 -50
  192. package/src/modules/primitives/command/setReferenceUnit.generated.ts +6 -0
  193. package/src/modules/primitives/command/setReferenceUnit.test.ts +39 -35
  194. package/src/modules/primitives/command/setReferenceUnit.ts +46 -59
  195. package/src/modules/primitives/db/unit.ts +13 -3
  196. package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -2
  197. package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -2
  198. package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -2
  199. package/src/modules/primitives/docs/commands/CreateCategory.md +1 -4
  200. package/src/modules/primitives/docs/commands/CreateCurrency.md +3 -4
  201. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +4 -5
  202. package/src/modules/primitives/docs/commands/CreateUnit.md +5 -5
  203. package/src/modules/primitives/docs/commands/DeactivateCategory.md +2 -3
  204. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +2 -3
  205. package/src/modules/primitives/docs/commands/DeactivateUnit.md +2 -3
  206. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +2 -3
  207. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +2 -3
  208. package/src/modules/primitives/docs/queries/ConvertAmount.md +3 -5
  209. package/src/modules/primitives/docs/queries/ConvertQuantity.md +3 -5
  210. package/src/modules/primitives/docs/queries/GetBaseCurrency.md +32 -0
  211. package/src/modules/primitives/docs/queries/GetCurrency.md +36 -0
  212. package/src/modules/primitives/docs/queries/GetUnit.md +36 -0
  213. package/src/modules/primitives/docs/queries/GetUoMCategory.md +36 -0
  214. package/src/modules/primitives/docs/queries/ListUnitsByCategory.md +26 -0
  215. package/src/modules/primitives/generated/kysely-tailordb.ts +24 -45
  216. package/src/modules/primitives/index.ts +15 -4
  217. package/src/modules/primitives/lib/errors.generated.ts +112 -0
  218. package/src/modules/primitives/{permissions.ts → lib/permissions.generated.ts} +9 -8
  219. package/src/modules/primitives/module.ts +37 -27
  220. package/src/modules/primitives/query/convertAmount.generated.ts +5 -0
  221. package/src/modules/primitives/query/convertAmount.test.ts +2 -2
  222. package/src/modules/primitives/query/convertAmount.ts +27 -28
  223. package/src/modules/primitives/query/convertQuantity.generated.ts +5 -0
  224. package/src/modules/primitives/query/convertQuantity.test.ts +6 -2
  225. package/src/modules/primitives/query/convertQuantity.ts +49 -57
  226. package/src/modules/primitives/query/getBaseCurrency.generated.ts +5 -0
  227. package/src/modules/primitives/query/getBaseCurrency.test.ts +28 -0
  228. package/src/modules/primitives/query/getBaseCurrency.ts +16 -0
  229. package/src/modules/primitives/query/getCurrency.generated.ts +5 -0
  230. package/src/modules/primitives/query/getCurrency.test.ts +47 -0
  231. package/src/modules/primitives/query/getCurrency.ts +18 -0
  232. package/src/modules/primitives/query/getUnit.generated.ts +5 -0
  233. package/src/modules/primitives/query/getUnit.test.ts +47 -0
  234. package/src/modules/primitives/query/getUnit.ts +18 -0
  235. package/src/modules/primitives/query/getUoMCategory.generated.ts +5 -0
  236. package/src/modules/primitives/query/getUoMCategory.test.ts +47 -0
  237. package/src/modules/primitives/query/getUoMCategory.ts +18 -0
  238. package/src/modules/primitives/query/listUnitsByCategory.generated.ts +5 -0
  239. package/src/modules/primitives/query/listUnitsByCategory.ts +16 -0
  240. package/src/modules/primitives/tailor.config.ts +3 -3
  241. package/src/modules/shared/defineCommand.test.ts +23 -10
  242. package/src/modules/shared/defineCommand.ts +23 -10
  243. package/src/modules/shared/internal.ts +1 -0
  244. package/src/modules/shared/requirePermission.test.ts +22 -21
  245. package/src/modules/shared/requirePermission.ts +8 -2
  246. package/src/modules/shared/result.ts +12 -0
  247. package/src/modules/testing/index.ts +36 -11
  248. package/src/modules/user-management/command/activateUser.generated.ts +6 -0
  249. package/src/modules/user-management/command/activateUser.test.ts +27 -27
  250. package/src/modules/user-management/command/activateUser.ts +40 -48
  251. package/src/modules/user-management/command/assignPermissionToRole.generated.ts +6 -0
  252. package/src/modules/user-management/command/assignPermissionToRole.test.ts +42 -43
  253. package/src/modules/user-management/command/assignPermissionToRole.ts +59 -62
  254. package/src/modules/user-management/command/assignRoleToUser.generated.ts +6 -0
  255. package/src/modules/user-management/command/assignRoleToUser.test.ts +70 -63
  256. package/src/modules/user-management/command/assignRoleToUser.ts +63 -66
  257. package/src/modules/user-management/command/createPermission.generated.ts +6 -0
  258. package/src/modules/user-management/command/createPermission.test.ts +45 -38
  259. package/src/modules/user-management/command/createPermission.ts +42 -46
  260. package/src/modules/user-management/command/createRole.generated.ts +6 -0
  261. package/src/modules/user-management/command/createRole.test.ts +30 -29
  262. package/src/modules/user-management/command/createRole.ts +33 -33
  263. package/src/modules/user-management/command/createUser.generated.ts +6 -0
  264. package/src/modules/user-management/command/createUser.test.ts +64 -42
  265. package/src/modules/user-management/command/createUser.ts +54 -56
  266. package/src/modules/user-management/command/deactivateUser.generated.ts +6 -0
  267. package/src/modules/user-management/command/deactivateUser.test.ts +27 -27
  268. package/src/modules/user-management/command/deactivateUser.ts +40 -48
  269. package/src/modules/user-management/command/logAuditEvent.generated.ts +6 -0
  270. package/src/modules/user-management/command/logAuditEvent.test.ts +50 -42
  271. package/src/modules/user-management/command/logAuditEvent.ts +25 -28
  272. package/src/modules/user-management/command/reactivateUser.generated.ts +6 -0
  273. package/src/modules/user-management/command/reactivateUser.test.ts +31 -27
  274. package/src/modules/user-management/command/reactivateUser.ts +40 -48
  275. package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +6 -0
  276. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +52 -51
  277. package/src/modules/user-management/command/revokePermissionFromRole.ts +60 -57
  278. package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +6 -0
  279. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +53 -48
  280. package/src/modules/user-management/command/revokeRoleFromUser.ts +58 -57
  281. package/src/modules/user-management/docs/commands/CreatePermission.md +2 -2
  282. package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
  283. package/src/modules/user-management/generated/enums.ts +11 -11
  284. package/src/modules/user-management/generated/kysely-tailordb.ts +27 -56
  285. package/src/modules/user-management/index.ts +2 -2
  286. package/src/modules/user-management/lib/errors.generated.ts +67 -0
  287. package/src/modules/user-management/{permissions.ts → lib/permissions.generated.ts} +8 -7
  288. package/src/modules/user-management/module.ts +22 -22
  289. package/src/modules/user-management/tailor.config.ts +3 -3
  290. package/src/schemas.ts +1 -1
  291. package/skills/1-module-docs/references/structure.md +0 -22
  292. package/skills/2-module-feature-breakdown/references/commands.md +0 -48
  293. package/skills/2-module-feature-breakdown/references/structure.md +0 -22
  294. package/skills/3-module-doc-review/references/commands.md +0 -54
  295. package/skills/3-module-doc-review/references/models.md +0 -29
  296. package/skills/4-module-tdd-implementation/SKILL.md +0 -74
  297. package/skills/4-module-tdd-implementation/references/commands.md +0 -45
  298. package/skills/4-module-tdd-implementation/references/errors.md +0 -7
  299. package/skills/4-module-tdd-implementation/references/models.md +0 -30
  300. package/skills/4-module-tdd-implementation/references/structure.md +0 -22
  301. package/skills/4-module-tdd-implementation/references/testing.md +0 -37
  302. package/skills/5-module-implementation-review/references/commands.md +0 -45
  303. package/skills/5-module-implementation-review/references/errors.md +0 -7
  304. package/skills/5-module-implementation-review/references/exports.md +0 -8
  305. package/skills/5-module-implementation-review/references/models.md +0 -30
  306. package/src/modules/primitives/lib/errors.ts +0 -138
  307. package/src/modules/user-management/lib/errors.ts +0 -81
  308. /package/skills/{app-compose-1-requirement-analysis → erp-kit-app-1-requirements}/references/structure.md +0 -0
  309. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-detailview.md +0 -0
  310. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-form.md +0 -0
  311. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/screen-listview.md +0 -0
  312. /package/skills/{app-compose-2-requirements-breakdown → erp-kit-app-2-breakdown}/references/structure.md +0 -0
  313. /package/skills/{app-compose-3-doc-review → erp-kit-app-3-doc-review}/references/structure.md +0 -0
  314. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/component.md +0 -0
  315. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-detailview.md +0 -0
  316. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-form.md +0 -0
  317. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/screen-listview.md +0 -0
  318. /package/skills/{app-compose-4-design-mock → erp-kit-app-4-design}/references/structure.md +0 -0
  319. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/component.md +0 -0
  320. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-detailview.md +0 -0
  321. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-form.md +0 -0
  322. /package/skills/{app-compose-5-design-mock-review → erp-kit-app-5-design-review}/references/screen-listview.md +0 -0
  323. /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/auth.md +0 -0
  324. /package/skills/{app-compose-6-implementation-spec → erp-kit-app-6-impl-spec}/references/structure.md +0 -0
  325. /package/skills/{2-module-feature-breakdown → erp-kit-module-4-tdd}/references/models.md +0 -0
@@ -0,0 +1,203 @@
1
+ # Command Implementation
2
+
3
+ ## Unified Pattern: `run` + generated shell
4
+
5
+ All commands follow the same pattern — export a `run` function, and the generated shell wraps it with `defineCommand`:
6
+
7
+ **Implementation file** (`command/myCommand.ts`):
8
+
9
+ ```typescript
10
+ export async function run(db: DB, input: MyCommandInput, ctx: CommandContext) {
11
+ // validate → query → mutate
12
+ return ok({ entity });
13
+ }
14
+ ```
15
+
16
+ **Generated shell** (`command/myCommand.generated.ts`):
17
+
18
+ ```typescript
19
+ export const myCommand = defineCommand(permissions.myCommand, run);
20
+ ```
21
+
22
+ ## Custom Fields (generic CF)
23
+
24
+ Commands that **write** (insert or update) into a table with user-extensible fields make `run` generic:
25
+
26
+ - Generic `CF extends Record<string, unknown>` on the `run` function
27
+ - Input type: `CreateXInput & CF` or `UpdateXInput & Partial<CF>`
28
+ - Destructure known fields, rest-spread custom fields
29
+ - Cast custom fields: `...(customFields as Record<string, unknown>)`
30
+
31
+ ```typescript
32
+ export async function run<CF extends Record<string, unknown>>(
33
+ db: DB,
34
+ input: CreateXInput & CF,
35
+ ctx: CommandContext,
36
+ ) {
37
+ const { name, ...customFields } = input;
38
+ await db
39
+ .insertInto("X")
40
+ .values({ ...(customFields as Record<string, unknown>), name })
41
+ .execute();
42
+ }
43
+ ```
44
+
45
+ ### module.ts wiring
46
+
47
+ Use instantiation expressions to lock `CF` to the module's custom field type:
48
+
49
+ ```typescript
50
+ const createXTyped = createX<FieldsToInsertable<F>>;
51
+ return { commands: { createX: createXTyped } };
52
+ ```
53
+
54
+ ### Rule: when to use generic CF
55
+
56
+ > If a model uses `fields?: F` for custom fields, **every command that writes those fields** must have a generic `CF`. This includes create, update, and any other write command. The determining factor is whether the command writes to a table with custom fields, not just whether it inserts new rows.
57
+
58
+ ## Config-Taking Commands
59
+
60
+ Commands that need external config (cross-module queries, settings) accept config as leading params before `(db, input, ctx)`:
61
+
62
+ ```typescript
63
+ export async function run<CF extends Record<string, unknown>>(
64
+ primitivesQueries: Pick<PrimitivesQueries, "getUnit">,
65
+ db: DB, input: CreateItemInput & CF, ctx: CommandContext,
66
+ ) { ... }
67
+ ```
68
+
69
+ module.ts wires with `.bind()` after instantiation:
70
+
71
+ ```typescript
72
+ const createItemTyped = createItem<FieldsToInsertable<IF>>;
73
+ return { commands: { createItem: createItemTyped.bind(null, queries) } };
74
+ ```
75
+
76
+ ## Command-Side Reads (CQRS Separation)
77
+
78
+ Following CQRS principles, commands own their reads for same-module data. Cross-module reads use injected query functions to preserve module boundaries.
79
+
80
+ ### Why
81
+
82
+ - **Query functions** use `ReadonlyDB` — shaped for API consumers, can't `forUpdate()`
83
+ - **Command reads** are shaped for enforcing invariants — need locking, may need different filters or joins
84
+ - **Independence** — query-side changes (pagination, field selection) must not affect command behavior
85
+ - **Module composition** — modules nest at multiple depths; parent modules inject child queries to maintain explicit dependency contracts
86
+
87
+ ### Rule
88
+
89
+ > **Same-module reads**: Commands inline their own `selectFrom()` calls using `db` (full `DB`). Never import or call query functions from `query/` within the same module — the command owns its reads and can apply locking.
90
+ >
91
+ > **Cross-module reads**: Commands receive query functions from other modules via dependency injection (leading params). This keeps module boundaries explicit and supports nested module composition.
92
+ >
93
+ > **Status preconditions**: Commands enforce status checks inline (e.g., "only DRAFT items can be deleted", "only ACTIVE users can be assigned roles"). Status filtering belongs to the command's invariant logic, not to query-side defaults. See [Status-Aware Query Rules](queries.md#status-aware-query-rules) for the read-side conventions.
94
+
95
+ ### Examples
96
+
97
+ **Correct** — same-module read inlined with locking:
98
+
99
+ ```typescript
100
+ // command/deactivateUnit.ts
101
+ const unit = await db
102
+ .selectFrom("Unit")
103
+ .selectAll()
104
+ .where("id", "=", input.unitId)
105
+ .forUpdate()
106
+ .executeTakeFirst();
107
+ if (!unit) return err(new UnitNotFoundError(input.unitId));
108
+ ```
109
+
110
+ **Correct** — cross-module read via injected query:
111
+
112
+ ```typescript
113
+ // command/createItem.ts (item-management validating a primitives entity)
114
+ export async function run<CF extends Record<string, unknown>>(
115
+ primitivesQueries: Pick<PrimitivesQueries, "getUnit">,
116
+ db: DB,
117
+ input: CreateItemInput & CF,
118
+ ctx: CommandContext,
119
+ ) {
120
+ const { unit } = await primitivesQueries.getUnit(db, { id: input.unitId }, ctx);
121
+ if (!unit?.isActive) return err(new UnitNotFoundError(input.unitId));
122
+ // ...
123
+ }
124
+ ```
125
+
126
+ **Wrong** — calling same-module query function:
127
+
128
+ ```typescript
129
+ // command/deactivateUnit.ts
130
+ import { getUnit } from "../query/getUnit"; // ← don't do this
131
+ const unit = await getUnit(db, { id: input.unitId }, ctx);
132
+ ```
133
+
134
+ ## Select Locking
135
+
136
+ Commands that read then write must lock rows with `forUpdate()` to prevent concurrent requests from causing race conditions.
137
+
138
+ ### When to lock
139
+
140
+ | Pattern | Lock? | Example |
141
+ | ----------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------- |
142
+ | Read a record, then update/delete it | Yes | `deactivateUnit`: reads unit, then sets `isActive = false` |
143
+ | Read a record to check uniqueness before insert | Yes | `createUnit`: checks symbol uniqueness, then inserts |
144
+ | Read multiple related records, then update them | Yes | `setReferenceUnit`: reads all units in category, recalculates factors |
145
+ | Read a record only for validation (no write to that record) | No | Reading a category to verify it exists before inserting a unit into a different table |
146
+ | Insert-only with no prior read | No | `logAuditEvent`: pure insert |
147
+
148
+ ### Rule
149
+
150
+ > Lock a `SELECT` when the command **writes to the same record it reads**, or when it **reads to enforce a uniqueness constraint** before inserting. The lock scope should be the narrowest set of rows needed — lock only the rows that participate in the read-then-write cycle.
151
+
152
+ ### Code pattern
153
+
154
+ Chain `.forUpdate()` on the select query:
155
+
156
+ ```typescript
157
+ const unit = await db
158
+ .selectFrom("Unit")
159
+ .selectAll()
160
+ .where("id", "=", input.unitId)
161
+ .forUpdate()
162
+ .executeTakeFirst();
163
+ ```
164
+
165
+ ### Batch locking
166
+
167
+ When a command reads multiple rows then updates them (e.g., `setReferenceUnit`), lock the entire set:
168
+
169
+ ```typescript
170
+ const units = await db
171
+ .selectFrom("Unit")
172
+ .selectAll()
173
+ .where("categoryId", "=", input.categoryId)
174
+ .forUpdate()
175
+ .execute();
176
+ ```
177
+
178
+ ## Implementation Considerations
179
+
180
+ - **Error handling**: Use `ok()` / `err()` from `shared/internal` — do not throw
181
+ - **Validation**: Check referenced entities exist before operating, return `err()` if not found
182
+
183
+ ## Conventions
184
+
185
+ - Input types: exported interfaces (`export interface MyFunctionInput`)
186
+ - Use `.executeTakeFirst()` for single results
187
+ - Include JSDoc: `/** Function: name \n Description */`
188
+
189
+ ## State Transitions
190
+
191
+ For commands that transition between statuses, accept `from?: string[]` with a default:
192
+
193
+ ```typescript
194
+ from?: string[]; // Default: ["ACTIVE"]
195
+
196
+ const validFromStatuses = input.from ?? ["ACTIVE"];
197
+ if (!validFromStatuses.includes(user.status)) {
198
+ return err(new InvalidStatusTransitionError(user.status, targetStatus));
199
+ }
200
+ ```
201
+
202
+ - Default `from` contains the base valid source status
203
+ - Parent modules can override to allow transitions from additional statuses
@@ -0,0 +1,35 @@
1
+ # Error Classes
2
+
3
+ Errors are **generated** from command/query documentation. Do not write `lib/errors.ts` manually.
4
+
5
+ ## How it works
6
+
7
+ 1. Write error scenarios in `docs/commands/*.md` as `CODE: Description` pairs
8
+ 2. Run `erp-kit module generate code` to produce `lib/errors.generated.ts`
9
+ 3. Import generated errors in your command implementations
10
+
11
+ ## Naming convention (in docs)
12
+
13
+ - Error code: `SCREAMING_SNAKE_CASE` (e.g., `DUPLICATE_SKU`)
14
+ - Generated class name: PascalCase + `Error` suffix (e.g., `DuplicateSkuError`)
15
+ - Generated via `createDomainError()` with contextual message
16
+
17
+ ## Error category naming rules
18
+
19
+ Use the **entity name** (not business-domain abbreviations) in error codes. Codes should be self-describing and consistent across modules.
20
+
21
+ | Category | Pattern | Example |
22
+ | ----------------- | --------------------------------------- | ------------------------------------ |
23
+ | Not found | `{ENTITY}_NOT_FOUND` | `UNIT_NOT_FOUND`, `ITEM_NOT_FOUND` |
24
+ | Duplicate | `DUPLICATE_{FIELD}` | `DUPLICATE_SKU`, `DUPLICATE_BARCODE` |
25
+ | Inactive / locked | `{FIELD}_LOCKED` or `{ENTITY}_INACTIVE` | `UOM_LOCKED` |
26
+ | Invalid state | `INVALID_{WHAT}` | `INVALID_STATE_TRANSITION` |
27
+ | No-op | `NO_FIELDS_TO_UPDATE` | `NO_FIELDS_TO_UPDATE` |
28
+
29
+ ### Cross-module foreign key errors
30
+
31
+ When a command validates an entity from another module (e.g., item-management checking that a Unit from primitives exists), use `{ENTITY}_NOT_FOUND` — the same pattern as same-module not-found errors. The error code reflects what's missing, not where it lives.
32
+
33
+ ### Idempotent error policy
34
+
35
+ If a command's outcome is already the current state (e.g., deactivating an already-inactive entity), return `ok()` — not an error. Only return errors for genuinely invalid operations.
@@ -0,0 +1,168 @@
1
+ # Query Implementation
2
+
3
+ ## Generated Query Shells
4
+
5
+ Run `erp-kit module generate code` to generate query shells from `docs/queries/*.md`. Each generated shell wraps your `run` function with `defineQuery`.
6
+
7
+ ### Generated shell example
8
+
9
+ ```typescript
10
+ // @generated — do not edit
11
+ import { defineQuery } from "../../shared/internal";
12
+ import { run } from "./getItem";
13
+
14
+ export const getItem = defineQuery(run);
15
+ ```
16
+
17
+ ### Hand-written implementation
18
+
19
+ ```typescript
20
+ import type { ReadonlyDB } from "../../shared/internal";
21
+ import type { DB } from "../generated/kysely-tailordb";
22
+
23
+ export type GetItemInput = { id: string } | { sku: string };
24
+
25
+ export async function run(db: ReadonlyDB<DB>, input: GetItemInput) {
26
+ let query = db.selectFrom("Item").selectAll();
27
+
28
+ if ("id" in input) {
29
+ query = query.where("id", "=", input.id);
30
+ } else {
31
+ query = query.where("sku", "=", input.sku);
32
+ }
33
+
34
+ const item = await query.executeTakeFirst();
35
+ return { item: item ?? null };
36
+ }
37
+ ```
38
+
39
+ ## Custom Queries
40
+
41
+ For queries beyond simple lookups (joins, aggregations, complex filters), write them by hand in `query/*.ts`:
42
+
43
+ ```typescript
44
+ import type { ReadonlyDB } from "../../shared/internal";
45
+ import type { DB } from "../generated/kysely-tailordb";
46
+
47
+ export interface ListActiveItemsByCategoryInput {
48
+ categoryId: string;
49
+ }
50
+
51
+ export async function run(db: ReadonlyDB<DB>, input: ListActiveItemsByCategoryInput) {
52
+ const items = await db
53
+ .selectFrom("Item")
54
+ .selectAll()
55
+ .where("categoryId", "=", input.categoryId)
56
+ .where("status", "=", "ACTIVE")
57
+ .execute();
58
+
59
+ return { items };
60
+ }
61
+ ```
62
+
63
+ ## Conventions
64
+
65
+ - `defineQuery` wraps all queries via generated shells
66
+ - `ReadonlyDB<DB>` ensures read-only access
67
+ - Get queries return `{ entity: T | null }` using `executeTakeFirst()`
68
+ - List queries return `{ entities: T[] }` using `execute()`
69
+ - Input types: `type` for union inputs (get), `interface` for single-shape inputs (list)
70
+ - `.generated.ts` files are always overwritten — never edit them
71
+
72
+ ## Status-Aware Query Rules
73
+
74
+ Models with a status lifecycle (Stateful models with `status` enum, or simple models with `isActive` bool) follow specific naming and filtering conventions. The core principle: **if a status filter is a business use case, put it in the query name. If it's a search parameter, put it in the input.**
75
+
76
+ ### Naming Convention
77
+
78
+ | Query type | Pattern | Returns |
79
+ | ------------------------------ | ------------------------------------ | ------------------------------------------- |
80
+ | **Get (single lookup)** | `get{Entity}` | Any status — caller already has a reference |
81
+ | **List (status-filtered)** | `list{Status}{Entities}` | Only records matching the named status |
82
+ | **List (unfiltered)** | `list{Entities}` | All records, all statuses |
83
+ | **Search (admin/exploratory)** | `search{Entities}({ status?, ... })` | Ad-hoc filtering via input params |
84
+
85
+ ### Examples
86
+
87
+ ```typescript
88
+ // Business use case — status baked into the name
89
+ export async function run(db: ReadonlyDB<DB>, input: ListActiveItemsByCategoryInput) {
90
+ const items = await db
91
+ .selectFrom("Item")
92
+ .selectAll()
93
+ .where("categoryId", "=", input.categoryId)
94
+ .where("status", "=", "ACTIVE")
95
+ .execute();
96
+ return { items };
97
+ }
98
+
99
+ // Unfiltered — no status filter, returns everything
100
+ export async function run(db: ReadonlyDB<DB>, input: ListItemsByCategoryInput) {
101
+ const items = await db
102
+ .selectFrom("Item")
103
+ .selectAll()
104
+ .where("categoryId", "=", input.categoryId)
105
+ .execute();
106
+ return { items };
107
+ }
108
+
109
+ // Single lookup — status-unaware, returns any status
110
+ export async function run(db: ReadonlyDB<DB>, input: GetItemInput) {
111
+ const item = await db
112
+ .selectFrom("Item")
113
+ .selectAll()
114
+ .where("id", "=", input.id)
115
+ .executeTakeFirst();
116
+ return { item: item ?? null };
117
+ }
118
+
119
+ // Admin/exploratory — parametric status filter
120
+ export interface SearchItemsInput {
121
+ status?: string;
122
+ categoryId?: string;
123
+ }
124
+
125
+ export async function run(db: ReadonlyDB<DB>, input: SearchItemsInput) {
126
+ let query = db.selectFrom("Item").selectAll();
127
+ if (input.status) {
128
+ query = query.where("status", "=", input.status);
129
+ }
130
+ if (input.categoryId) {
131
+ query = query.where("categoryId", "=", input.categoryId);
132
+ }
133
+ const items = await query.execute();
134
+ return { items };
135
+ }
136
+ ```
137
+
138
+ ### `isActive` (bool) Models
139
+
140
+ The same naming rules apply. For models using `isActive` instead of a `status` enum (e.g., Currency, Unit, UomCategory):
141
+
142
+ ```typescript
143
+ // Business use case — active units only
144
+ // listActiveUnitsByCategory
145
+ export async function run(db: ReadonlyDB<DB>, input: ListActiveUnitsByCategoryInput) {
146
+ const units = await db
147
+ .selectFrom("Unit")
148
+ .selectAll()
149
+ .where("categoryId", "=", input.categoryId)
150
+ .where("isActive", "=", true)
151
+ .execute();
152
+ return { units };
153
+ }
154
+ ```
155
+
156
+ ### Anti-patterns
157
+
158
+ - **Silent defaults**: `listItems()` returning only ACTIVE without the name saying so — hidden behavior violates the principle of least surprise
159
+ - **"listAll" prefix**: `listAllItems()` implies the unfiltered variant is the exception — use `listItems()` for unfiltered instead
160
+ - **Fat queries with many optional filters for business use cases**: prefer purpose-built queries with intent in the name over swiss-army-knife queries
161
+
162
+ ### Status preconditions in commands
163
+
164
+ Status checks that guard mutations (e.g., "only DRAFT items can be deleted") belong in commands, not queries. See [CQRS command-side read rule](commands.md#command-side-reads-cqrs-separation).
165
+
166
+ ## See Also
167
+
168
+ - [CQRS command-side read rule](commands.md#command-side-reads-cqrs-separation) — queries are read-side only; commands inline own reads
@@ -0,0 +1,36 @@
1
+ # Module Directory Structure
2
+
3
+ This structure is automatically scaffolded by `erp-kit module scaffold module <name>`.
4
+
5
+ ```
6
+ {module}/
7
+ ├── db/ # Database models (one file per model)
8
+ ├── executor/ # Async executors (record triggers, job functions)
9
+ ├── command/ # Domain commands + tests (*.test.ts co-located)
10
+ │ ├── *.ts # Hand-written business logic (run function)
11
+ │ ├── *.generated.ts # Generated command shells (do not edit)
12
+ │ └── *.test.ts # Tests
13
+ ├── query/ # Read-only query handlers
14
+ │ ├── *.ts # Hand-written custom queries
15
+ │ └── *.generated.ts # Generated get/list queries (do not edit)
16
+ ├── lib/
17
+ │ ├── errors.generated.ts # Generated error classes (do not edit)
18
+ │ ├── permissions.generated.ts # Generated permissions (do not edit)
19
+ │ └── types.ts # Hand-written types
20
+ ├── testing/ # Test fixtures and helpers
21
+ ├── generated/ # Auto-generated kysely types (do not edit)
22
+ ├── index.ts # Public exports
23
+ ├── tailor.config.ts # Module config and generators
24
+ └── module.ts # Module definition
25
+ ```
26
+
27
+ ## Rules
28
+
29
+ - `db/`: Only documentable model definitions, no helpers
30
+ - `executor/`: Async executors as factory functions (see executors.md)
31
+ - `command/`: Domain commands + co-located tests, no utilities
32
+ - `query/`: Read-only query handlers
33
+ - `lib/`: Generated errors/permissions + hand-written types
34
+ - `testing/`: Fixtures for tests only
35
+ - `.generated.ts` files are always overwritten — never edit them
36
+ - Run `pnpm generate` after modifying `db/` models
@@ -22,13 +22,14 @@ spies.select.mockReturnValueOnce(first).mockReturnValueOnce(second);
22
22
 
23
23
  ## Custom Fields Passthrough
24
24
 
25
- For `makeCreateX` factory commands, add one test verifying custom fields reach `.values()`:
25
+ For commands with generic `CF`, add one test verifying custom fields reach `.values()`:
26
26
 
27
- - Instantiate with concrete type: `makeCreateX<{ myField: string }>()`
27
+ - Import from `./createX.generated` (the generated shell that wires permissions)
28
+ - Pass extra fields in the input: `await createX(db, { name: "Test", myField: "value" }, ctx)`
28
29
  - Assert with `spies.values`: `expect(spies.values).toHaveBeenNthCalledWith(1, expect.objectContaining({ myField: "value" }))`
29
30
  - Use `toHaveBeenNthCalledWith(n, ...)` when multiple inserts occur (e.g., audit events)
30
31
 
31
- ## Fixtures (`src/testing/fixtures.ts`)
32
+ ## Fixtures (`testing/fixtures.ts`)
32
33
 
33
34
  - Import `Schema` from `lib/types` (not `Namespace` from generated code)
34
35
  - Pattern: `export const baseEntity = { ... } as const satisfies Entity<Schema>`
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: erp-kit-update
3
+ description: Route requirements changes to the correct erp-kit skill. Use when requirements change midway — adding features, modifying business flows, updating screens, changing module specs, or when unsure which erp-kit skill to re-run after a change.
4
+ ---
5
+
6
+ # Requirements Update Router
7
+
8
+ Route mid-workflow changes to the correct erp-kit skill. Dynamically discovers available skills — no hardcoded routing tables.
9
+
10
+ ## Workflow
11
+
12
+ ```
13
+ IDENTIFY → DISCOVER → ROUTE → CASCADE → INVOKE
14
+ ```
15
+
16
+ ## Phase 1: Identify
17
+
18
+ Ask the user:
19
+
20
+ 1. **What changed?** (free-text description of the change)
21
+ 2. **Where?** Determine domain from the target:
22
+ - `examples/<app>/` → **App workflow** (skills matching `erp-kit-app-*`)
23
+ - `modules/<module>/` → **Module workflow** (skills matching `erp-kit-module-*`)
24
+
25
+ If the user names a specific app or module, confirm the path. If ambiguous, check both directories.
26
+
27
+ ## Phase 2: Discover
28
+
29
+ Dynamically discover available erp-kit skills:
30
+
31
+ 1. Glob for `erp-kit-app-*/SKILL.md` or `erp-kit-module-*/SKILL.md` files (based on domain from Phase 1) in the skills directory
32
+ 2. Read each SKILL.md's frontmatter `name` and `description`
33
+ 3. Skip `erp-kit-module-shared` (not directly invocable)
34
+ 4. Sort skills by their numeric step (extract number from name, e.g. `erp-kit-app-2-breakdown` → step 2)
35
+
36
+ This builds the routing table at runtime — new or renamed skills are picked up automatically.
37
+
38
+ ## Phase 3: Route
39
+
40
+ Match the user's change description against each discovered skill's `description` field. Select the **earliest** skill whose description matches the type of change.
41
+
42
+ **When ambiguous:** Route to the earliest possible skill in the chain. Earlier skills cascade forward naturally.
43
+
44
+ ## Phase 4: Cascade
45
+
46
+ After identifying the entry point, warn about downstream skills that need re-running. Use the sorted skill list from Phase 2 — all skills with a higher step number than the routed skill are potential downstream candidates.
47
+
48
+ Show: "Starting from `<routed-skill>`. After this completes, you'll likely need to re-run: `<downstream-skills>`."
49
+
50
+ Only list downstream skills that are plausibly affected by the change.
51
+
52
+ ## Phase 5: Invoke
53
+
54
+ Use the `Skill` tool to invoke the routed skill. Pass the app or module name as the argument.
55
+
56
+ Example: `Skill: erp-kit-app-2-breakdown, args: "inventory-management"`
57
+
58
+ ## Multiple Changes
59
+
60
+ If the user describes multiple changes spanning different skills or domains:
61
+
62
+ 1. Group by domain (app vs module)
63
+ 2. Within each domain, route to the **earliest** affected skill
64
+ 3. The cascade will naturally cover later skills
@@ -0,0 +1,65 @@
1
+ import { describe, test, beforeAll } from "vitest";
2
+ import {
3
+ assertDocMatch,
4
+ initDocFile,
5
+ collectAllCommands,
6
+ type GenerateDocConfig,
7
+ type FileMapping,
8
+ } from "politty/docs";
9
+ import { mainCommand } from "./commands/index";
10
+
11
+ function capitalize(s: string): string {
12
+ return s.charAt(0).toUpperCase() + s.slice(1);
13
+ }
14
+
15
+ async function buildFilesFromCommand(): Promise<FileMapping> {
16
+ const allCommands = await collectAllCommands(mainCommand);
17
+ const files: FileMapping = {};
18
+
19
+ // Single-file full reference
20
+ files["docs/cli.md"] = { commands: [""], title: "CLI Reference" };
21
+
22
+ // Per-command sub-files
23
+ for (const [path, info] of allCommands) {
24
+ if (info.depth !== 2) continue;
25
+
26
+ const title =
27
+ info.subCommands.length > 0 ? `${capitalize(path)} Commands` : `${capitalize(path)} Command`;
28
+
29
+ files[`docs/cli/${path}.md`] = { commands: [path], title };
30
+ }
31
+
32
+ return files;
33
+ }
34
+
35
+ const files = await buildFilesFromCommand();
36
+ const targetCommands = Object.values(files).flatMap((f) =>
37
+ typeof f === "object" && "commands" in f ? f.commands : f,
38
+ );
39
+
40
+ const config: GenerateDocConfig = {
41
+ command: mainCommand,
42
+ files,
43
+ format: {
44
+ headingLevel: 2,
45
+ optionStyle: "table",
46
+ generateAnchors: true,
47
+ includeSubcommandDetails: true,
48
+ },
49
+ };
50
+
51
+ describe("CLI documentation", () => {
52
+ beforeAll(() => {
53
+ initDocFile(config);
54
+ });
55
+
56
+ for (const cmd of targetCommands) {
57
+ test(`${cmd || "root"}`, async () => {
58
+ await assertDocMatch({ ...config, targetCommands: [cmd] });
59
+ });
60
+ }
61
+
62
+ test("all", async () => {
63
+ await assertDocMatch(config);
64
+ });
65
+ });
package/src/cli.ts CHANGED
@@ -1,38 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { z } from "zod";
4
- import { defineCommand, runMain, arg } from "politty";
5
- import { runInit } from "./commands/init.js";
6
- import { mockCommand } from "./commands/mock/index.js";
7
- import { moduleCommand } from "./commands/module/index.js";
8
- import { appCommand } from "./commands/app/index.js";
3
+ import { runMain } from "politty";
4
+ import { mainCommand } from "./commands/index";
9
5
 
10
- const cwd = process.cwd();
11
-
12
- const initCommand = defineCommand({
13
- name: "init",
14
- description: "Set up consumer repo with framework skills",
15
- args: z.object({
16
- force: arg(z.boolean().default(false), {
17
- alias: "f",
18
- description: "Overwrite existing skills",
19
- }),
20
- }),
21
- run: (args) => {
22
- const exitCode = runInit(cwd, args.force);
23
- process.exit(exitCode);
24
- },
25
- });
26
-
27
- const main = defineCommand({
28
- name: "erp-kit",
29
- description: "ERP module framework CLI",
30
- subCommands: {
31
- module: moduleCommand,
32
- app: appCommand,
33
- mock: mockCommand,
34
- init: initCommand,
35
- },
36
- });
37
-
38
- void runMain(main);
6
+ void runMain(mainCommand);
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { defineCommand, arg } from "politty";
3
- import { runCheck } from "../check.js";
4
- import { runSyncCheck, formatSyncCheckReport } from "../sync-check.js";
5
- import { runScaffold, APP_TYPES, type ScaffoldType } from "../scaffold.js";
3
+ import { runCheck } from "../check";
4
+ import { runSyncCheck, formatSyncCheckReport } from "../sync-check";
5
+ import { runScaffold, APP_TYPES, type ScaffoldType } from "../scaffold";
6
6
 
7
7
  const cwd = process.cwd();
8
8
 
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { buildCheckTargets } from "./check.js";
2
+ import { buildCheckTargets } from "./check";
3
3
 
4
4
  describe("buildCheckTargets", () => {
5
5
  it("generates module targets when modulesRoot is set", () => {
@@ -1,5 +1,5 @@
1
- import { runMdschema } from "../mdschema.js";
2
- import { ALL_SCHEMAS } from "../schemas.js";
1
+ import { runMdschema } from "../mdschema";
2
+ import { ALL_SCHEMAS } from "../schemas";
3
3
  export interface CheckTarget {
4
4
  glob: string;
5
5
  schemaKey: string;