@tailor-platform/erp-kit 0.0.1 → 0.1.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 (213) hide show
  1. package/README.md +196 -28
  2. package/dist/cli.js +894 -0
  3. package/package.json +65 -8
  4. package/rules/app-compose/backend/auth.md +78 -0
  5. package/rules/app-compose/frontend/auth.md +55 -0
  6. package/rules/app-compose/frontend/component.md +55 -0
  7. package/rules/app-compose/frontend/page.md +86 -0
  8. package/rules/app-compose/frontend/screen-detailview.md +112 -0
  9. package/rules/app-compose/frontend/screen-form.md +145 -0
  10. package/rules/app-compose/frontend/screen-listview.md +159 -0
  11. package/rules/app-compose/structure.md +32 -0
  12. package/rules/module-development/commands.md +54 -0
  13. package/rules/module-development/cross-module-type-injection.md +28 -0
  14. package/rules/module-development/dependency-modules.md +24 -0
  15. package/rules/module-development/errors.md +12 -0
  16. package/rules/module-development/executors.md +67 -0
  17. package/rules/module-development/exports.md +13 -0
  18. package/rules/module-development/models.md +34 -0
  19. package/rules/module-development/structure.md +27 -0
  20. package/rules/module-development/sync-vs-async-operations.md +83 -0
  21. package/rules/module-development/testing.md +43 -0
  22. package/rules/sdk-best-practices/db-relations.md +74 -0
  23. package/rules/sdk-best-practices/sdk-docs.md +14 -0
  24. package/schemas/app-compose/actors.yml +34 -0
  25. package/schemas/app-compose/business-flow.yml +50 -0
  26. package/schemas/app-compose/requirements.yml +33 -0
  27. package/schemas/app-compose/resolver.yml +47 -0
  28. package/schemas/app-compose/screen.yml +81 -0
  29. package/schemas/app-compose/story.yml +67 -0
  30. package/schemas/module/command.yml +52 -0
  31. package/schemas/module/feature.yml +58 -0
  32. package/schemas/module/model.yml +70 -0
  33. package/schemas/module/module.yml +50 -0
  34. package/skills/1-module-docs/SKILL.md +107 -0
  35. package/skills/2-module-feature-breakdown/SKILL.md +66 -0
  36. package/skills/3-module-doc-review/SKILL.md +230 -0
  37. package/skills/4-module-tdd-implementation/SKILL.md +56 -0
  38. package/skills/5-module-implementation-review/SKILL.md +400 -0
  39. package/skills/app-compose-1-requirement-analysis/SKILL.md +85 -0
  40. package/skills/app-compose-2-requirements-breakdown/SKILL.md +88 -0
  41. package/skills/app-compose-3-doc-review/SKILL.md +112 -0
  42. package/skills/app-compose-4-design-mock/SKILL.md +248 -0
  43. package/skills/app-compose-5-design-mock-review/SKILL.md +283 -0
  44. package/skills/app-compose-6-implementation-spec/SKILL.md +122 -0
  45. package/skills/mock-scenario/SKILL.md +118 -0
  46. package/src/app.ts +1 -0
  47. package/src/cli.ts +120 -0
  48. package/src/commands/check.test.ts +30 -0
  49. package/src/commands/check.ts +66 -0
  50. package/src/commands/init.test.ts +77 -0
  51. package/src/commands/init.ts +87 -0
  52. package/src/commands/mock/index.ts +53 -0
  53. package/src/commands/mock/start.ts +179 -0
  54. package/src/commands/mock/validate.test.ts +185 -0
  55. package/src/commands/mock/validate.ts +198 -0
  56. package/src/commands/scaffold.test.ts +76 -0
  57. package/src/commands/scaffold.ts +119 -0
  58. package/src/commands/sync-check.test.ts +125 -0
  59. package/src/commands/sync-check.ts +182 -0
  60. package/src/integration.test.ts +63 -0
  61. package/src/mdschema.ts +48 -0
  62. package/src/mockServer.ts +55 -0
  63. package/src/module.ts +86 -0
  64. package/src/modules/accounting/.gitkeep +0 -0
  65. package/src/modules/coa-management/.gitkeep +0 -0
  66. package/src/modules/inventory/.gitkeep +0 -0
  67. package/src/modules/manufacturing/.gitkeep +0 -0
  68. package/src/modules/primitives/README.md +39 -0
  69. package/src/modules/primitives/command/activateCategory.test.ts +75 -0
  70. package/src/modules/primitives/command/activateCategory.ts +50 -0
  71. package/src/modules/primitives/command/activateCurrency.test.ts +70 -0
  72. package/src/modules/primitives/command/activateCurrency.ts +50 -0
  73. package/src/modules/primitives/command/activateUnit.test.ts +53 -0
  74. package/src/modules/primitives/command/activateUnit.ts +50 -0
  75. package/src/modules/primitives/command/convertAmount.test.ts +275 -0
  76. package/src/modules/primitives/command/convertAmount.ts +126 -0
  77. package/src/modules/primitives/command/convertQuantity.test.ts +219 -0
  78. package/src/modules/primitives/command/convertQuantity.ts +73 -0
  79. package/src/modules/primitives/command/createCategory.test.ts +126 -0
  80. package/src/modules/primitives/command/createCategory.ts +89 -0
  81. package/src/modules/primitives/command/createCurrency.test.ts +191 -0
  82. package/src/modules/primitives/command/createCurrency.ts +77 -0
  83. package/src/modules/primitives/command/createExchangeRate.test.ts +216 -0
  84. package/src/modules/primitives/command/createExchangeRate.ts +91 -0
  85. package/src/modules/primitives/command/createUnit.test.ts +214 -0
  86. package/src/modules/primitives/command/createUnit.ts +88 -0
  87. package/src/modules/primitives/command/deactivateCategory.test.ts +97 -0
  88. package/src/modules/primitives/command/deactivateCategory.ts +62 -0
  89. package/src/modules/primitives/command/deactivateCurrency.test.ts +85 -0
  90. package/src/modules/primitives/command/deactivateCurrency.ts +55 -0
  91. package/src/modules/primitives/command/deactivateUnit.test.ts +78 -0
  92. package/src/modules/primitives/command/deactivateUnit.ts +62 -0
  93. package/src/modules/primitives/command/setBaseCurrency.test.ts +98 -0
  94. package/src/modules/primitives/command/setBaseCurrency.ts +74 -0
  95. package/src/modules/primitives/command/setReferenceUnit.test.ts +108 -0
  96. package/src/modules/primitives/command/setReferenceUnit.ts +84 -0
  97. package/src/modules/primitives/db/currency.ts +30 -0
  98. package/src/modules/primitives/db/exchangeRate.ts +28 -0
  99. package/src/modules/primitives/db/unit.ts +32 -0
  100. package/src/modules/primitives/db/uomCategory.ts +32 -0
  101. package/src/modules/primitives/docs/commands/ActivateCategory.md +34 -0
  102. package/src/modules/primitives/docs/commands/ActivateCurrency.md +33 -0
  103. package/src/modules/primitives/docs/commands/ActivateUnit.md +34 -0
  104. package/src/modules/primitives/docs/commands/ConvertAmount.md +50 -0
  105. package/src/modules/primitives/docs/commands/ConvertQuantity.md +43 -0
  106. package/src/modules/primitives/docs/commands/CreateCategory.md +44 -0
  107. package/src/modules/primitives/docs/commands/CreateCurrency.md +47 -0
  108. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +48 -0
  109. package/src/modules/primitives/docs/commands/CreateUnit.md +48 -0
  110. package/src/modules/primitives/docs/commands/DeactivateCategory.md +38 -0
  111. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +38 -0
  112. package/src/modules/primitives/docs/commands/DeactivateUnit.md +38 -0
  113. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +39 -0
  114. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +43 -0
  115. package/src/modules/primitives/docs/features/currency-definitions.md +55 -0
  116. package/src/modules/primitives/docs/features/exchange-rates.md +61 -0
  117. package/src/modules/primitives/docs/features/unit-conversion.md +66 -0
  118. package/src/modules/primitives/docs/features/uom-categories.md +52 -0
  119. package/src/modules/primitives/docs/models/Currency.md +45 -0
  120. package/src/modules/primitives/docs/models/ExchangeRate.md +33 -0
  121. package/src/modules/primitives/docs/models/Unit.md +46 -0
  122. package/src/modules/primitives/docs/models/UoMCategory.md +44 -0
  123. package/src/modules/primitives/generated/kysely-tailordb.ts +95 -0
  124. package/src/modules/primitives/index.ts +40 -0
  125. package/src/modules/primitives/lib/errors.ts +138 -0
  126. package/src/modules/primitives/lib/types.ts +20 -0
  127. package/src/modules/primitives/module.ts +66 -0
  128. package/src/modules/primitives/permissions.ts +18 -0
  129. package/src/modules/primitives/tailor.config.ts +11 -0
  130. package/src/modules/primitives/testing/fixtures.ts +161 -0
  131. package/src/modules/product-management/.gitkeep +0 -0
  132. package/src/modules/purchase/.gitkeep +0 -0
  133. package/src/modules/sales/.gitkeep +0 -0
  134. package/src/modules/shared/createContext.test.ts +39 -0
  135. package/src/modules/shared/createContext.ts +15 -0
  136. package/src/modules/shared/defineCommand.test.ts +42 -0
  137. package/src/modules/shared/defineCommand.ts +19 -0
  138. package/src/modules/shared/definePermissions.test.ts +146 -0
  139. package/src/modules/shared/definePermissions.ts +94 -0
  140. package/src/modules/shared/entityTypes.ts +15 -0
  141. package/src/modules/shared/errors.ts +22 -0
  142. package/src/modules/shared/index.ts +1 -0
  143. package/src/modules/shared/internal.ts +13 -0
  144. package/src/modules/shared/requirePermission.test.ts +47 -0
  145. package/src/modules/shared/requirePermission.ts +8 -0
  146. package/src/modules/shared/types.ts +4 -0
  147. package/src/modules/supplier-management/.gitkeep +0 -0
  148. package/src/modules/supplier-portal/.gitkeep +0 -0
  149. package/src/modules/testing/index.ts +120 -0
  150. package/src/modules/user-management/README.md +38 -0
  151. package/src/modules/user-management/command/activateUser.test.ts +112 -0
  152. package/src/modules/user-management/command/activateUser.ts +67 -0
  153. package/src/modules/user-management/command/assignPermissionToRole.test.ts +119 -0
  154. package/src/modules/user-management/command/assignPermissionToRole.ts +87 -0
  155. package/src/modules/user-management/command/assignRoleToUser.test.ts +162 -0
  156. package/src/modules/user-management/command/assignRoleToUser.ts +93 -0
  157. package/src/modules/user-management/command/createPermission.test.ts +143 -0
  158. package/src/modules/user-management/command/createPermission.ts +66 -0
  159. package/src/modules/user-management/command/createRole.test.ts +115 -0
  160. package/src/modules/user-management/command/createRole.ts +52 -0
  161. package/src/modules/user-management/command/createUser.test.ts +198 -0
  162. package/src/modules/user-management/command/createUser.ts +85 -0
  163. package/src/modules/user-management/command/deactivateUser.test.ts +112 -0
  164. package/src/modules/user-management/command/deactivateUser.ts +67 -0
  165. package/src/modules/user-management/command/logAuditEvent.test.ts +179 -0
  166. package/src/modules/user-management/command/logAuditEvent.ts +59 -0
  167. package/src/modules/user-management/command/reactivateUser.test.ts +115 -0
  168. package/src/modules/user-management/command/reactivateUser.ts +67 -0
  169. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +112 -0
  170. package/src/modules/user-management/command/revokePermissionFromRole.ts +81 -0
  171. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +112 -0
  172. package/src/modules/user-management/command/revokeRoleFromUser.ts +81 -0
  173. package/src/modules/user-management/db/auditEvent.ts +47 -0
  174. package/src/modules/user-management/db/permission.ts +31 -0
  175. package/src/modules/user-management/db/role.ts +28 -0
  176. package/src/modules/user-management/db/rolePermission.ts +44 -0
  177. package/src/modules/user-management/db/user.ts +38 -0
  178. package/src/modules/user-management/db/userRole.ts +44 -0
  179. package/src/modules/user-management/docs/commands/ActivateUser.md +36 -0
  180. package/src/modules/user-management/docs/commands/AssignPermissionToRole.md +39 -0
  181. package/src/modules/user-management/docs/commands/AssignRoleToUser.md +43 -0
  182. package/src/modules/user-management/docs/commands/CreatePermission.md +35 -0
  183. package/src/modules/user-management/docs/commands/CreateRole.md +35 -0
  184. package/src/modules/user-management/docs/commands/CreateUser.md +41 -0
  185. package/src/modules/user-management/docs/commands/DeactivateUser.md +38 -0
  186. package/src/modules/user-management/docs/commands/LogAuditEvent.md +37 -0
  187. package/src/modules/user-management/docs/commands/ReactivateUser.md +37 -0
  188. package/src/modules/user-management/docs/commands/RevokePermissionFromRole.md +40 -0
  189. package/src/modules/user-management/docs/commands/RevokeRoleFromUser.md +40 -0
  190. package/src/modules/user-management/docs/features/audit-trail.md +80 -0
  191. package/src/modules/user-management/docs/features/role-based-access-control.md +76 -0
  192. package/src/modules/user-management/docs/features/user-account-management.md +64 -0
  193. package/src/modules/user-management/docs/models/AuditEvent.md +34 -0
  194. package/src/modules/user-management/docs/models/Permission.md +31 -0
  195. package/src/modules/user-management/docs/models/Role.md +31 -0
  196. package/src/modules/user-management/docs/models/RolePermission.md +33 -0
  197. package/src/modules/user-management/docs/models/User.md +47 -0
  198. package/src/modules/user-management/docs/models/UserRole.md +34 -0
  199. package/src/modules/user-management/docs/plans/2026-01-30-flattened-permissions-design.md +52 -0
  200. package/src/modules/user-management/executor/recomputeOnRolePermissionChange.ts +61 -0
  201. package/src/modules/user-management/generated/enums.ts +24 -0
  202. package/src/modules/user-management/generated/kysely-tailordb.ts +112 -0
  203. package/src/modules/user-management/index.ts +32 -0
  204. package/src/modules/user-management/lib/errors.ts +81 -0
  205. package/src/modules/user-management/lib/recomputeUserPermissions.ts +53 -0
  206. package/src/modules/user-management/lib/types.ts +31 -0
  207. package/src/modules/user-management/module.ts +77 -0
  208. package/src/modules/user-management/permissions.ts +15 -0
  209. package/src/modules/user-management/tailor.config.ts +11 -0
  210. package/src/modules/user-management/testing/fixtures.ts +98 -0
  211. package/src/schemas.ts +25 -0
  212. package/src/testing.ts +10 -0
  213. package/src/util.ts +3 -0
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: mock-scenario
3
+ description: Scaffold a new Mockoon mock scenario with CRUD routes, error scenarios, and registry updates
4
+ ---
5
+
6
+ # Mock Scenario Scaffolder
7
+
8
+ Generate a complete Mockoon mock API config for a new provider scenario and wire it into the registry.
9
+
10
+ ## Workflow
11
+
12
+ ### Step 1: Identify target API
13
+
14
+ Ask the user for:
15
+
16
+ - **Provider name** (e.g. `stripe`, `github`, `twilio`)
17
+ - **Scenario name** (e.g. `admin-api`, `payments-api`, `product-sync`)
18
+ - **Key resources to mock** (e.g. customers, invoices, messages)
19
+ - **Scenario to test** (e.g. initial full sync, incremental sync, webhook-triggered update, error recovery)
20
+
21
+ ### Step 2: Research real API
22
+
23
+ Use web search to find:
24
+
25
+ - Base URL and API version
26
+ - Authentication scheme (API key, OAuth, bearer token)
27
+ - Endpoint patterns and HTTP methods
28
+ - Response shapes for the key resources
29
+ - Error response format (status codes, error body structure)
30
+ - Rate limit headers
31
+
32
+ ### Step 3: Generate Mockoon JSON
33
+
34
+ Create `mocks/{provider}/{scenario}/mock.json` following the Shopify admin-api mock as the canonical reference (`mocks/shopify/admin-api/mock.json`).
35
+
36
+ The config must include:
37
+
38
+ - **uuid**: a valid UUID v4
39
+ - **name**: Human-readable API name
40
+ - **endpointPrefix**: Match the real API's base path
41
+ - **port**: `3000` (overridden at runtime by the reverse proxy launcher)
42
+ - **hostname**: `0.0.0.0`
43
+ - **latency**: `0`
44
+ - **folders**: `[]`
45
+ - **rootChildren**: `[]`
46
+ - **proxyMode**: `false`
47
+ - **proxyHost**: `""`
48
+ - **proxyRemovePrefix**: `false`
49
+ - **tlsOptions**: `{ "enabled": false, "type": "CERT", "pfxPath": "", "certPath": "", "keyPath": "", "caPath": "", "passphrase": "" }`
50
+ - **cors**: `true`
51
+ - **headers**: `[{ "key": "Content-Type", "value": "application/json" }]`
52
+ - **proxyReqHeaders**: `[]`
53
+ - **proxyResHeaders**: `[]`
54
+ - **callbacks**: `[]`
55
+
56
+ All UUIDs (environment, routes, responses, data buckets) must be valid UUID v4 values.
57
+
58
+ **Data buckets** — for each stateful/CRUD resource:
59
+
60
+ - 2–3 seed records with realistic field values
61
+ - Use the `id` field matching Mockoon's `"id"` property for CRUD lookup
62
+
63
+ **Routes:**
64
+
65
+ 1. **CRUD routes** — for mutable resources:
66
+
67
+ ```json
68
+ {
69
+ "type": "crud",
70
+ "endpoint": "{resource}",
71
+ "responses": [
72
+ {
73
+ "label": "CRUD {Resource}",
74
+ "statusCode": 200,
75
+ "headers": [{ "key": "Content-Type", "value": "application/json" }],
76
+ "bodyType": "DATABUCKET",
77
+ "databucketID": "{resource-bucket-id}"
78
+ }
79
+ ]
80
+ }
81
+ ```
82
+
83
+ 2. **Static routes** — for read-only endpoints:
84
+ - List endpoints returning JSON arrays
85
+ - Single-item endpoints using `{{urlParam 'id'}}` interpolation
86
+
87
+ 3. **Error scenarios** — triggered via `X-Test-Scenario` header:
88
+ - `unauthorized` → 401
89
+ - `not-found` → 404
90
+ - `rate-limit` → 429 with `Retry-After` header
91
+ - `server-error` → 500
92
+
93
+ 4. **Catch-all** — `*` endpoint returning 500 when `X-Test-Scenario: server-error`
94
+
95
+ **Every route must have:** `type`, `documentation`, `responseMode: null`, `streamingMode: null`, `streamingInterval: 0`
96
+
97
+ **Every response must have:** `latency: 0`, `bodyType`, `databucketID`, `filePath: ""`, `sendFileAsBody: false`, `rules`, `rulesOperator: "OR"`, `disableTemplating: false`, `fallbackTo404: false`, `default`, `crudKey: "id"`, `callbacks: []`, a `Content-Type` header, and a non-empty `label`
98
+
99
+ **Every rule must have:** `target`, `modifier`, `value`, `operator`, `invert: false`
100
+
101
+ ### Step 4: Generate scenario README
102
+
103
+ Create `mocks/{provider}/{scenario}/README.md` with:
104
+
105
+ - Title and one-line description
106
+ - Quick start with both methods: `erp-kit mock start` (proxy) and direct `npx @mockoon/cli start`
107
+ - Endpoints table (method, path, scenarios)
108
+ - CRUD workflow example with curl commands using proxy URL (`http://localhost:3000/{provider}/{scenario}/...`)
109
+ - Error scenario examples
110
+ - Test data description
111
+
112
+ ### Step 5: Validate
113
+
114
+ ```bash
115
+ erp-kit mock validate
116
+ ```
117
+
118
+ Fix any failures before finishing.
package/src/app.ts ADDED
@@ -0,0 +1 @@
1
+ export { createContext } from "./modules/shared/createContext";
package/src/cli.ts ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { z } from "zod";
4
+ import { defineCommand, runMain, arg } from "politty";
5
+ import { runCheck } from "./commands/check.js";
6
+ import { runSyncCheck, formatSyncCheckReport } from "./commands/sync-check.js";
7
+ import { runScaffold, ALL_TYPES, isModuleType, type ScaffoldType } from "./commands/scaffold.js";
8
+ import { runInit } from "./commands/init.js";
9
+ import { mockCommand } from "./commands/mock/index.js";
10
+
11
+ const cwd = process.cwd();
12
+
13
+ const rootArgs = z.object({
14
+ modulesRoot: arg(z.string().optional(), {
15
+ alias: "m",
16
+ description: "Path to modules directory",
17
+ }),
18
+ appRoot: arg(z.string().optional(), {
19
+ alias: "a",
20
+ description: "Path to app-compose directory (apps/ or examples/)",
21
+ }),
22
+ });
23
+
24
+ function requireRoot(args: { modulesRoot?: string; appRoot?: string }) {
25
+ const paths = { modulesRoot: args.modulesRoot, appRoot: args.appRoot };
26
+ if (!paths.modulesRoot && !paths.appRoot) {
27
+ console.error("At least one of --modules-root or --app-root is required.");
28
+ process.exit(2);
29
+ }
30
+ return paths;
31
+ }
32
+
33
+ const checkCommand = defineCommand({
34
+ name: "check",
35
+ description: "Validate docs against schemas",
36
+ args: rootArgs,
37
+ run: async (args) => {
38
+ const paths = requireRoot(args);
39
+ const exitCode = await runCheck(paths, cwd);
40
+ process.exit(exitCode);
41
+ },
42
+ });
43
+
44
+ const syncCheckCommand = defineCommand({
45
+ name: "sync-check",
46
+ description: "Validate source <-> doc correspondence",
47
+ args: rootArgs,
48
+ run: async (args) => {
49
+ const paths = requireRoot(args);
50
+ const result = await runSyncCheck(paths, cwd);
51
+ console.log(formatSyncCheckReport(result));
52
+ process.exit(result.exitCode);
53
+ },
54
+ });
55
+
56
+ const scaffoldCommand = defineCommand({
57
+ name: "scaffold",
58
+ description: "Generate doc file from schema template",
59
+ args: rootArgs.extend({
60
+ type: arg(z.enum(ALL_TYPES as unknown as [string, ...string[]]), {
61
+ positional: true,
62
+ description: `Scaffold type (${ALL_TYPES.join(", ")})`,
63
+ }),
64
+ parent: arg(z.string(), {
65
+ positional: true,
66
+ description: "Parent name (module or app name)",
67
+ }),
68
+ name: arg(z.string().optional(), {
69
+ positional: true,
70
+ description: "Item name (required for most types)",
71
+ }),
72
+ }),
73
+ run: async (args) => {
74
+ const paths = requireRoot(args);
75
+ const root = isModuleType(args.type) ? paths.modulesRoot : paths.appRoot;
76
+ if (!root) {
77
+ console.error(
78
+ `--${isModuleType(args.type) ? "modules-root" : "app-root"} is required for scaffold type "${args.type}".`,
79
+ );
80
+ process.exit(2);
81
+ }
82
+ const exitCode = await runScaffold(
83
+ args.type as ScaffoldType,
84
+ args.parent,
85
+ args.name,
86
+ root,
87
+ cwd,
88
+ );
89
+ process.exit(exitCode);
90
+ },
91
+ });
92
+
93
+ const initCommand = defineCommand({
94
+ name: "init",
95
+ description: "Set up consumer repo (skills, rules)",
96
+ args: z.object({
97
+ force: arg(z.boolean().default(false), {
98
+ alias: "f",
99
+ description: "Overwrite existing skills and rules",
100
+ }),
101
+ }),
102
+ run: (args) => {
103
+ const exitCode = runInit(cwd, args.force);
104
+ process.exit(exitCode);
105
+ },
106
+ });
107
+
108
+ const main = defineCommand({
109
+ name: "erp-kit",
110
+ description: "Documentation validation and scaffolding tool",
111
+ subCommands: {
112
+ check: checkCommand,
113
+ "sync-check": syncCheckCommand,
114
+ scaffold: scaffoldCommand,
115
+ init: initCommand,
116
+ mock: mockCommand,
117
+ },
118
+ });
119
+
120
+ void runMain(main);
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildCheckTargets } from "./check.js";
3
+
4
+ describe("buildCheckTargets", () => {
5
+ it("generates module targets when modulesRoot is set", () => {
6
+ const targets = buildCheckTargets({ modulesRoot: "modules", appRoot: undefined });
7
+ expect(targets).toEqual([
8
+ { glob: "modules/[a-zA-Z]*/docs/features/*.md", schemaKey: "feature" },
9
+ { glob: "modules/[a-zA-Z]*/docs/commands/*.md", schemaKey: "command" },
10
+ { glob: "modules/[a-zA-Z]*/docs/models/*.md", schemaKey: "model" },
11
+ { glob: "modules/[a-zA-Z]*/README.md", schemaKey: "module" },
12
+ ]);
13
+ });
14
+
15
+ it("generates app-compose targets when appRoot is set", () => {
16
+ const targets = buildCheckTargets({ modulesRoot: undefined, appRoot: "apps" });
17
+ expect(targets[0].glob).toBe("apps/[a-zA-Z]*/README.md");
18
+ expect(targets).toHaveLength(6);
19
+ });
20
+
21
+ it("generates both when both roots are set", () => {
22
+ const targets = buildCheckTargets({ modulesRoot: "modules", appRoot: "examples" });
23
+ expect(targets).toHaveLength(10);
24
+ });
25
+
26
+ it("returns empty when neither root is set", () => {
27
+ const targets = buildCheckTargets({ modulesRoot: undefined, appRoot: undefined });
28
+ expect(targets).toHaveLength(0);
29
+ });
30
+ });
@@ -0,0 +1,66 @@
1
+ import { runMdschema } from "../mdschema.js";
2
+ import { ALL_SCHEMAS } from "../schemas.js";
3
+ export interface CheckTarget {
4
+ glob: string;
5
+ schemaKey: string;
6
+ }
7
+
8
+ export function buildCheckTargets(config: {
9
+ modulesRoot?: string;
10
+ appRoot?: string;
11
+ }): CheckTarget[] {
12
+ const targets: CheckTarget[] = [];
13
+
14
+ if (config.modulesRoot) {
15
+ const m = config.modulesRoot;
16
+ targets.push(
17
+ { glob: `${m}/[a-zA-Z]*/docs/features/*.md`, schemaKey: "feature" },
18
+ { glob: `${m}/[a-zA-Z]*/docs/commands/*.md`, schemaKey: "command" },
19
+ { glob: `${m}/[a-zA-Z]*/docs/models/*.md`, schemaKey: "model" },
20
+ { glob: `${m}/[a-zA-Z]*/README.md`, schemaKey: "module" },
21
+ );
22
+ }
23
+
24
+ if (config.appRoot) {
25
+ const a = config.appRoot;
26
+ targets.push(
27
+ { glob: `${a}/[a-zA-Z]*/README.md`, schemaKey: "requirements" },
28
+ { glob: `${a}/[a-zA-Z]*/docs/actors/*.md`, schemaKey: "actors" },
29
+ { glob: `${a}/[a-zA-Z]*/docs/business-flow/*/README.md`, schemaKey: "business-flow" },
30
+ { glob: `${a}/[a-zA-Z]*/docs/business-flow/*/story/*.md`, schemaKey: "story" },
31
+ { glob: `${a}/[a-zA-Z]*/docs/screen/*.md`, schemaKey: "screen" },
32
+ { glob: `${a}/[a-zA-Z]*/docs/resolver/*.md`, schemaKey: "resolver" },
33
+ );
34
+ }
35
+
36
+ return targets;
37
+ }
38
+
39
+ export async function runCheck(
40
+ config: { modulesRoot?: string; appRoot?: string },
41
+ cwd: string,
42
+ ): Promise<number> {
43
+ const targets = buildCheckTargets(config);
44
+
45
+ const results = await Promise.all(
46
+ targets.map(async (target) => {
47
+ const schemaPath = ALL_SCHEMAS[target.schemaKey];
48
+ if (!schemaPath) {
49
+ console.error(`Unknown schema key: ${target.schemaKey}`);
50
+ return 2;
51
+ }
52
+ const { exitCode, stdout, stderr } = await runMdschema(
53
+ ["check", target.glob, "--schema", schemaPath],
54
+ cwd,
55
+ );
56
+
57
+ if (stdout.trim()) console.log(stdout);
58
+ if (stderr.trim()) console.error(stderr);
59
+
60
+ return exitCode;
61
+ }),
62
+ );
63
+
64
+ if (results.includes(2)) return 2;
65
+ return results.some((code) => code !== 0) ? 1 : 0;
66
+ }
@@ -0,0 +1,77 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
5
+ import { runInit } from "./init.js";
6
+
7
+ describe("runInit", () => {
8
+ let tmpDir: string;
9
+
10
+ beforeEach(() => {
11
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "init-test-"));
12
+ });
13
+
14
+ afterEach(() => {
15
+ fs.rmSync(tmpDir, { recursive: true, force: true });
16
+ });
17
+
18
+ it("copies framework skills to .agents/skills/", () => {
19
+ runInit(tmpDir, false);
20
+ const skillPath = path.join(tmpDir, ".agents", "skills", "1-module-docs", "SKILL.md");
21
+ expect(fs.existsSync(skillPath)).toBe(true);
22
+ });
23
+
24
+ it("does not overwrite project-specific skills", () => {
25
+ const customSkillDir = path.join(tmpDir, ".agents", "skills", "my-custom-skill");
26
+ fs.mkdirSync(customSkillDir, { recursive: true });
27
+ fs.writeFileSync(path.join(customSkillDir, "SKILL.md"), "# Custom");
28
+ runInit(tmpDir, false);
29
+ const content = fs.readFileSync(path.join(customSkillDir, "SKILL.md"), "utf-8");
30
+ expect(content).toBe("# Custom");
31
+ });
32
+
33
+ it("does not overwrite existing framework skills", () => {
34
+ const skillDir = path.join(tmpDir, ".agents", "skills", "1-module-docs");
35
+ fs.mkdirSync(skillDir, { recursive: true });
36
+ fs.writeFileSync(path.join(skillDir, "SKILL.md"), "# Customized");
37
+ runInit(tmpDir, false);
38
+ const content = fs.readFileSync(path.join(skillDir, "SKILL.md"), "utf-8");
39
+ expect(content).toBe("# Customized");
40
+ });
41
+
42
+ it("overwrites existing framework skills with --force", () => {
43
+ const skillDir = path.join(tmpDir, ".agents", "skills", "1-module-docs");
44
+ fs.mkdirSync(skillDir, { recursive: true });
45
+ fs.writeFileSync(path.join(skillDir, "SKILL.md"), "# Customized");
46
+ runInit(tmpDir, true);
47
+ const content = fs.readFileSync(path.join(skillDir, "SKILL.md"), "utf-8");
48
+ expect(content).not.toBe("# Customized");
49
+ });
50
+
51
+ it("copies framework rules to .agents/rules/", () => {
52
+ runInit(tmpDir, false);
53
+ const rulePath = path.join(tmpDir, ".agents", "rules", "module-development", "structure.md");
54
+ expect(fs.existsSync(rulePath)).toBe(true);
55
+ // Also check nested subdirectory
56
+ const nestedRule = path.join(tmpDir, ".agents", "rules", "app-compose", "frontend", "auth.md");
57
+ expect(fs.existsSync(nestedRule)).toBe(true);
58
+ });
59
+
60
+ it("does not overwrite existing rules", () => {
61
+ const ruleDir = path.join(tmpDir, ".agents", "rules", "module-development");
62
+ fs.mkdirSync(ruleDir, { recursive: true });
63
+ fs.writeFileSync(path.join(ruleDir, "structure.md"), "# Custom Rule");
64
+ runInit(tmpDir, false);
65
+ const content = fs.readFileSync(path.join(ruleDir, "structure.md"), "utf-8");
66
+ expect(content).toBe("# Custom Rule");
67
+ });
68
+
69
+ it("overwrites existing rules with --force", () => {
70
+ const ruleDir = path.join(tmpDir, ".agents", "rules", "module-development");
71
+ fs.mkdirSync(ruleDir, { recursive: true });
72
+ fs.writeFileSync(path.join(ruleDir, "structure.md"), "# Custom Rule");
73
+ runInit(tmpDir, true);
74
+ const content = fs.readFileSync(path.join(ruleDir, "structure.md"), "utf-8");
75
+ expect(content).not.toBe("# Custom Rule");
76
+ });
77
+ });
@@ -0,0 +1,87 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import chalk from "chalk";
4
+ import { PACKAGE_ROOT } from "../util.js";
5
+
6
+ const SKILLS_SRC = path.join(PACKAGE_ROOT, "skills");
7
+ const RULES_SRC = path.join(PACKAGE_ROOT, "rules");
8
+
9
+ const FRAMEWORK_SKILLS = [
10
+ "1-module-docs",
11
+ "2-module-feature-breakdown",
12
+ "3-module-doc-review",
13
+ "4-module-tdd-implementation",
14
+ "5-module-implementation-review",
15
+ "app-compose-1-requirement-analysis",
16
+ "app-compose-2-requirements-breakdown",
17
+ "app-compose-3-doc-review",
18
+ "app-compose-4-design-mock",
19
+ "app-compose-5-design-mock-review",
20
+ "app-compose-6-implementation-spec",
21
+ "mock-scenario",
22
+ ];
23
+
24
+ export function runInit(cwd: string, force: boolean): number {
25
+ console.log(chalk.bold("erp-kit init\n"));
26
+
27
+ const skillsDest = path.join(cwd, ".agents", "skills");
28
+ let copiedCount = 0;
29
+ let skippedCount = 0;
30
+ for (const skill of FRAMEWORK_SKILLS) {
31
+ const srcSkill = path.join(SKILLS_SRC, skill, "SKILL.md");
32
+ const destDir = path.join(skillsDest, skill);
33
+ const destSkill = path.join(destDir, "SKILL.md");
34
+
35
+ if (!fs.existsSync(srcSkill)) continue;
36
+ if (!force && fs.existsSync(destSkill)) {
37
+ console.log(chalk.yellow(` Skipped ${skill}/SKILL.md (already exists)`));
38
+ skippedCount++;
39
+ continue;
40
+ }
41
+
42
+ fs.mkdirSync(destDir, { recursive: true });
43
+ fs.copyFileSync(srcSkill, destSkill);
44
+ copiedCount++;
45
+ }
46
+ console.log(chalk.green(` Copied ${copiedCount} framework skills to .agents/skills/`));
47
+ if (skippedCount > 0) {
48
+ console.log(
49
+ chalk.yellow(` Skipped ${skippedCount} existing skills (use --force to overwrite)`),
50
+ );
51
+ }
52
+
53
+ const rulesDest = path.join(cwd, ".agents", "rules");
54
+ let rulesCount = 0;
55
+ let rulesSkipped = 0;
56
+ if (fs.existsSync(RULES_SRC)) {
57
+ const copyRulesRecursive = (srcDir: string, destDir: string) => {
58
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
59
+ const srcPath = path.join(srcDir, entry.name);
60
+ const destPath = path.join(destDir, entry.name);
61
+ if (entry.isDirectory()) {
62
+ copyRulesRecursive(srcPath, destPath);
63
+ } else {
64
+ if (!force && fs.existsSync(destPath)) {
65
+ const rel = path.relative(rulesDest, destPath);
66
+ console.log(chalk.yellow(` Skipped rule ${rel} (already exists)`));
67
+ rulesSkipped++;
68
+ continue;
69
+ }
70
+ fs.mkdirSync(destDir, { recursive: true });
71
+ fs.copyFileSync(srcPath, destPath);
72
+ rulesCount++;
73
+ }
74
+ }
75
+ };
76
+ copyRulesRecursive(RULES_SRC, rulesDest);
77
+ }
78
+ console.log(chalk.green(` Copied ${rulesCount} framework rules to .agents/rules/`));
79
+ if (rulesSkipped > 0) {
80
+ console.log(
81
+ chalk.yellow(` Skipped ${rulesSkipped} existing rules (use --force to overwrite)`),
82
+ );
83
+ }
84
+
85
+ console.log(chalk.bold.green("\nDone! Run `erp-kit check` to validate your docs."));
86
+ return 0;
87
+ }
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+ import { defineCommand, arg } from "politty";
3
+ import { runMockStart } from "./start.js";
4
+ import { runMockValidate } from "./validate.js";
5
+
6
+ const startCommand = defineCommand({
7
+ name: "start",
8
+ description: "Start mock API servers with reverse proxy",
9
+ args: z.object({
10
+ mocksRoot: arg(z.string().default("./mocks"), {
11
+ description: "Path to mocks directory",
12
+ }),
13
+ port: arg(z.coerce.number().default(3000), {
14
+ alias: "p",
15
+ description: "Reverse proxy port",
16
+ }),
17
+ filter: arg(z.array(z.string()).default([]), {
18
+ positional: true,
19
+ description: "Filter by provider or provider/scenario",
20
+ }),
21
+ }),
22
+ run: async (args) => {
23
+ const exitCode = await runMockStart(args.mocksRoot, args.filter, args.port);
24
+ process.exit(exitCode);
25
+ },
26
+ });
27
+
28
+ const validateCommand = defineCommand({
29
+ name: "validate",
30
+ description: "Validate mock scenario configs",
31
+ args: z.object({
32
+ mocksRoot: arg(z.string().default("./mocks"), {
33
+ description: "Path to mocks directory",
34
+ }),
35
+ paths: arg(z.array(z.string()).default([]), {
36
+ positional: true,
37
+ description: "Specific scenario paths to validate",
38
+ }),
39
+ }),
40
+ run: async (args) => {
41
+ const exitCode = await runMockValidate(args.mocksRoot, args.paths);
42
+ process.exit(exitCode);
43
+ },
44
+ });
45
+
46
+ export const mockCommand = defineCommand({
47
+ name: "mock",
48
+ description: "Mock API server management",
49
+ subCommands: {
50
+ start: startCommand,
51
+ validate: validateCommand,
52
+ },
53
+ });