@mettlecast/domain-cli 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 (301) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +133 -0
  3. package/dist/builder/build-registry.d.ts +20 -0
  4. package/dist/builder/build-registry.js +178 -0
  5. package/dist/builder/build-types.d.ts +10 -0
  6. package/dist/builder/build-types.js +32 -0
  7. package/dist/builder/load-domain-module-config.d.ts +12 -0
  8. package/dist/builder/load-domain-module-config.js +34 -0
  9. package/dist/builder/load-module.d.ts +26 -0
  10. package/dist/builder/load-module.js +114 -0
  11. package/dist/builder/zod-to-json.d.ts +10 -0
  12. package/dist/builder/zod-to-json.js +15 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +338 -0
  15. package/dist/commands/add-api.d.ts +32 -0
  16. package/dist/commands/add-api.js +106 -0
  17. package/dist/commands/add-component.d.ts +15 -0
  18. package/dist/commands/add-component.js +78 -0
  19. package/dist/commands/add-domain.d.ts +21 -0
  20. package/dist/commands/add-domain.js +71 -0
  21. package/dist/commands/add-flow.d.ts +14 -0
  22. package/dist/commands/add-flow.js +108 -0
  23. package/dist/commands/add-hook.d.ts +15 -0
  24. package/dist/commands/add-hook.js +65 -0
  25. package/dist/commands/add-migration.d.ts +26 -0
  26. package/dist/commands/add-migration.js +93 -0
  27. package/dist/commands/add-module.d.ts +7 -0
  28. package/dist/commands/add-module.js +261 -0
  29. package/dist/commands/add-page.d.ts +13 -0
  30. package/dist/commands/add-page.js +170 -0
  31. package/dist/commands/add-seed-page.d.ts +5 -0
  32. package/dist/commands/add-seed-page.js +251 -0
  33. package/dist/commands/add-subscriber.d.ts +30 -0
  34. package/dist/commands/add-subscriber.js +87 -0
  35. package/dist/commands/add-test.d.ts +22 -0
  36. package/dist/commands/add-test.js +285 -0
  37. package/dist/commands/audit.d.ts +39 -0
  38. package/dist/commands/audit.js +171 -0
  39. package/dist/commands/build-catalog.d.ts +68 -0
  40. package/dist/commands/build-catalog.js +105 -0
  41. package/dist/commands/build-flows.d.ts +26 -0
  42. package/dist/commands/build-flows.js +174 -0
  43. package/dist/commands/build.d.ts +17 -0
  44. package/dist/commands/build.js +39 -0
  45. package/dist/commands/check-hashes.d.ts +38 -0
  46. package/dist/commands/check-hashes.js +97 -0
  47. package/dist/commands/create-project.d.ts +5 -0
  48. package/dist/commands/create-project.js +272 -0
  49. package/dist/commands/dev.d.ts +15 -0
  50. package/dist/commands/dev.js +27 -0
  51. package/dist/commands/doctor.d.ts +48 -0
  52. package/dist/commands/doctor.js +1301 -0
  53. package/dist/commands/explain.d.ts +34 -0
  54. package/dist/commands/explain.js +167 -0
  55. package/dist/commands/power-tune.d.ts +1 -0
  56. package/dist/commands/power-tune.js +87 -0
  57. package/dist/commands/reseed-page.d.ts +6 -0
  58. package/dist/commands/reseed-page.js +297 -0
  59. package/dist/commands/show-dns.d.ts +15 -0
  60. package/dist/commands/show-dns.js +101 -0
  61. package/dist/commands/show.d.ts +46 -0
  62. package/dist/commands/show.js +135 -0
  63. package/dist/commands/test.d.ts +18 -0
  64. package/dist/commands/test.js +56 -0
  65. package/dist/commands/upgrade-backend.d.ts +9 -0
  66. package/dist/commands/upgrade-backend.js +46 -0
  67. package/dist/commands/upgrade-event.d.ts +22 -0
  68. package/dist/commands/upgrade-event.js +111 -0
  69. package/dist/commands/upgrade-frontend.d.ts +5 -0
  70. package/dist/commands/upgrade-frontend.js +99 -0
  71. package/dist/commands/upgrade.d.ts +7 -0
  72. package/dist/commands/upgrade.js +495 -0
  73. package/dist/commands/validate.d.ts +30 -0
  74. package/dist/commands/validate.js +148 -0
  75. package/dist/runtime-stubs/hydrate-local-ctx.d.ts +49 -0
  76. package/dist/runtime-stubs/hydrate-local-ctx.js +45 -0
  77. package/dist/runtime-stubs/load-secrets.d.ts +11 -0
  78. package/dist/runtime-stubs/load-secrets.js +37 -0
  79. package/dist/server/api-server.d.ts +21 -0
  80. package/dist/server/api-server.js +14 -0
  81. package/dist/server/mount-routes.d.ts +19 -0
  82. package/dist/server/mount-routes.js +42 -0
  83. package/dist/templates/api-skeleton.d.ts +18 -0
  84. package/dist/templates/api-skeleton.js +69 -0
  85. package/dist/templates/claude-md.d.ts +4 -0
  86. package/dist/templates/claude-md.js +7 -0
  87. package/dist/templates/dashboard-pages/index.d.ts +101 -0
  88. package/dist/templates/dashboard-pages/index.js +22 -0
  89. package/dist/templates/domain-config.d.ts +10 -0
  90. package/dist/templates/domain-config.js +19 -0
  91. package/dist/templates/events-skeleton.d.ts +9 -0
  92. package/dist/templates/events-skeleton.js +23 -0
  93. package/dist/templates/patterns/api/create-with-event.d.ts +5 -0
  94. package/dist/templates/patterns/api/create-with-event.js +69 -0
  95. package/dist/templates/patterns/api/idempotent-mutation.d.ts +5 -0
  96. package/dist/templates/patterns/api/idempotent-mutation.js +99 -0
  97. package/dist/templates/patterns/api/paginated-list.d.ts +5 -0
  98. package/dist/templates/patterns/api/paginated-list.js +71 -0
  99. package/dist/templates/patterns/api/simple-crud.d.ts +6 -0
  100. package/dist/templates/patterns/api/simple-crud.js +95 -0
  101. package/dist/templates/patterns/api/system-admin.d.ts +5 -0
  102. package/dist/templates/patterns/api/system-admin.js +98 -0
  103. package/dist/templates/patterns/api/webhook-receiver-style.d.ts +5 -0
  104. package/dist/templates/patterns/api/webhook-receiver-style.js +125 -0
  105. package/dist/templates/patterns/component/index.d.ts +25 -0
  106. package/dist/templates/patterns/component/index.js +814 -0
  107. package/dist/templates/patterns/flow/autonomous-remediation.d.ts +5 -0
  108. package/dist/templates/patterns/flow/autonomous-remediation.js +98 -0
  109. package/dist/templates/patterns/flow/fan-out-reaction.d.ts +5 -0
  110. package/dist/templates/patterns/flow/fan-out-reaction.js +53 -0
  111. package/dist/templates/patterns/flow/human-in-loop.d.ts +5 -0
  112. package/dist/templates/patterns/flow/human-in-loop.js +90 -0
  113. package/dist/templates/patterns/flow/multi-step-orchestration.d.ts +5 -0
  114. package/dist/templates/patterns/flow/multi-step-orchestration.js +67 -0
  115. package/dist/templates/patterns/hook/index.d.ts +7 -0
  116. package/dist/templates/patterns/hook/index.js +378 -0
  117. package/dist/templates/patterns/migration/column-add.d.ts +5 -0
  118. package/dist/templates/patterns/migration/column-add.js +52 -0
  119. package/dist/templates/patterns/migration/event-store.d.ts +6 -0
  120. package/dist/templates/patterns/migration/event-store.js +97 -0
  121. package/dist/templates/patterns/migration/index.d.ts +5 -0
  122. package/dist/templates/patterns/migration/index.js +52 -0
  123. package/dist/templates/patterns/migration/rls-policy.d.ts +5 -0
  124. package/dist/templates/patterns/migration/rls-policy.js +78 -0
  125. package/dist/templates/patterns/migration/tenant-scoped-table.d.ts +6 -0
  126. package/dist/templates/patterns/migration/tenant-scoped-table.js +81 -0
  127. package/dist/templates/patterns/page/dashboard.d.ts +4 -0
  128. package/dist/templates/patterns/page/dashboard.js +164 -0
  129. package/dist/templates/patterns/page/detail.d.ts +4 -0
  130. package/dist/templates/patterns/page/detail.js +162 -0
  131. package/dist/templates/patterns/page/form.d.ts +4 -0
  132. package/dist/templates/patterns/page/form.js +192 -0
  133. package/dist/templates/patterns/page/index.d.ts +8 -0
  134. package/dist/templates/patterns/page/index.js +14 -0
  135. package/dist/templates/patterns/page/list.d.ts +4 -0
  136. package/dist/templates/patterns/page/list.js +138 -0
  137. package/dist/templates/patterns/page/settings.d.ts +4 -0
  138. package/dist/templates/patterns/page/settings.js +318 -0
  139. package/dist/templates/patterns/page/wizard.d.ts +4 -0
  140. package/dist/templates/patterns/page/wizard.js +293 -0
  141. package/dist/templates/patterns/subscriber/audit-relay.d.ts +5 -0
  142. package/dist/templates/patterns/subscriber/audit-relay.js +51 -0
  143. package/dist/templates/patterns/subscriber/cascade-deletion.d.ts +5 -0
  144. package/dist/templates/patterns/subscriber/cascade-deletion.js +60 -0
  145. package/dist/templates/patterns/subscriber/single-step-projection.d.ts +5 -0
  146. package/dist/templates/patterns/subscriber/single-step-projection.js +36 -0
  147. package/dist/templates/readme.d.ts +4 -0
  148. package/dist/templates/readme.js +7 -0
  149. package/dist/templates/subscriber-skeleton.d.ts +11 -0
  150. package/dist/templates/subscriber-skeleton.js +23 -0
  151. package/dist/utils/checksum.d.ts +18 -0
  152. package/dist/utils/checksum.js +39 -0
  153. package/dist/utils/file-helpers.d.ts +35 -0
  154. package/dist/utils/file-helpers.js +59 -0
  155. package/dist/utils/header-inject.d.ts +15 -0
  156. package/dist/utils/header-inject.js +93 -0
  157. package/dist/utils/install-file.d.ts +17 -0
  158. package/dist/utils/install-file.js +78 -0
  159. package/dist/utils/logger.d.ts +12 -0
  160. package/dist/utils/logger.js +24 -0
  161. package/dist/utils/manifest.d.ts +28 -0
  162. package/dist/utils/manifest.js +93 -0
  163. package/dist/utils/s3-fetch.d.ts +25 -0
  164. package/dist/utils/s3-fetch.js +52 -0
  165. package/dist/utils/scaffold-config.d.ts +95 -0
  166. package/dist/utils/scaffold-config.js +53 -0
  167. package/dist/utils/token-substitution.d.ts +35 -0
  168. package/dist/utils/token-substitution.js +52 -0
  169. package/dist/utils/zod-extractor.d.ts +10 -0
  170. package/dist/utils/zod-extractor.js +28 -0
  171. package/package.json +43 -0
  172. package/src/__tests__/build-registry.test.ts +115 -0
  173. package/src/__tests__/build-types.test.ts +100 -0
  174. package/src/__tests__/builder/walkDomainDir.test.ts +140 -0
  175. package/src/__tests__/commands/add-domain.test.ts +174 -0
  176. package/src/__tests__/commands/add-flow.test.ts +69 -0
  177. package/src/__tests__/commands/add-module.test.ts +207 -0
  178. package/src/__tests__/commands/build-flows.test.ts +154 -0
  179. package/src/__tests__/commands/check-hashes.test.ts +142 -0
  180. package/src/__tests__/commands/create-project.test.ts +712 -0
  181. package/src/__tests__/commands/upgrade.test.ts +762 -0
  182. package/src/__tests__/doctor.test.ts +291 -0
  183. package/src/__tests__/hydrate-local-ctx.test.ts +71 -0
  184. package/src/__tests__/load-domain-module-config.test.ts +10 -0
  185. package/src/__tests__/mount-routes.test.ts +100 -0
  186. package/src/__tests__/scaffold-src/part-a-layout.test.ts +46 -0
  187. package/src/__tests__/scripts/package-scaffold.test.ts +45 -0
  188. package/src/__tests__/utils/checksum.test.ts +90 -0
  189. package/src/__tests__/utils/header-inject.test.ts +89 -0
  190. package/src/__tests__/utils/install-file.test.ts +207 -0
  191. package/src/__tests__/utils/manifest.test.ts +229 -0
  192. package/src/__tests__/utils/token-substitution.test.ts +134 -0
  193. package/src/__tests__/validate.test.ts +18 -0
  194. package/src/builder/build-registry.ts +237 -0
  195. package/src/builder/build-types.ts +36 -0
  196. package/src/builder/load-domain-module-config.ts +43 -0
  197. package/src/builder/load-module.ts +133 -0
  198. package/src/builder/zod-to-json.ts +16 -0
  199. package/src/cli.ts +387 -0
  200. package/src/commands/add-api.ts +145 -0
  201. package/src/commands/add-component.ts +116 -0
  202. package/src/commands/add-domain.ts +100 -0
  203. package/src/commands/add-flow.ts +136 -0
  204. package/src/commands/add-hook.ts +100 -0
  205. package/src/commands/add-migration.ts +129 -0
  206. package/src/commands/add-module.ts +347 -0
  207. package/src/commands/add-page.ts +242 -0
  208. package/src/commands/add-seed-page.ts +338 -0
  209. package/src/commands/add-subscriber.ts +123 -0
  210. package/src/commands/add-test.ts +328 -0
  211. package/src/commands/audit.ts +217 -0
  212. package/src/commands/build-catalog.ts +181 -0
  213. package/src/commands/build-flows.ts +198 -0
  214. package/src/commands/build.ts +59 -0
  215. package/src/commands/check-hashes.ts +150 -0
  216. package/src/commands/create-project.ts +367 -0
  217. package/src/commands/dev.ts +47 -0
  218. package/src/commands/doctor.ts +1362 -0
  219. package/src/commands/explain.ts +209 -0
  220. package/src/commands/power-tune.ts +112 -0
  221. package/src/commands/reseed-page.ts +410 -0
  222. package/src/commands/show-dns.ts +139 -0
  223. package/src/commands/show.ts +179 -0
  224. package/src/commands/test.ts +91 -0
  225. package/src/commands/upgrade-backend.ts +72 -0
  226. package/src/commands/upgrade-event.ts +175 -0
  227. package/src/commands/upgrade-frontend.ts +135 -0
  228. package/src/commands/upgrade.ts +668 -0
  229. package/src/commands/validate.ts +208 -0
  230. package/src/runtime-stubs/hydrate-local-ctx.ts +102 -0
  231. package/src/runtime-stubs/load-secrets.ts +40 -0
  232. package/src/server/api-server.ts +34 -0
  233. package/src/server/mount-routes.ts +68 -0
  234. package/src/templates/api-skeleton.ts +72 -0
  235. package/src/templates/claude-md.ts +7 -0
  236. package/src/templates/dashboard-pages/account/api-keys.tsx +107 -0
  237. package/src/templates/dashboard-pages/account/audit-log.tsx +60 -0
  238. package/src/templates/dashboard-pages/account/index.ts +5 -0
  239. package/src/templates/dashboard-pages/account/members.tsx +107 -0
  240. package/src/templates/dashboard-pages/account/profile.tsx +53 -0
  241. package/src/templates/dashboard-pages/account/workspace-settings.tsx +64 -0
  242. package/src/templates/dashboard-pages/auth/accept-invitation.tsx +62 -0
  243. package/src/templates/dashboard-pages/auth/choose-org.tsx +66 -0
  244. package/src/templates/dashboard-pages/auth/forgot-password.tsx +69 -0
  245. package/src/templates/dashboard-pages/auth/index.ts +10 -0
  246. package/src/templates/dashboard-pages/auth/login.tsx +75 -0
  247. package/src/templates/dashboard-pages/auth/mfa-setup.tsx +56 -0
  248. package/src/templates/dashboard-pages/auth/mfa-verify.tsx +49 -0
  249. package/src/templates/dashboard-pages/auth/reset-password.tsx +64 -0
  250. package/src/templates/dashboard-pages/auth/sign-out.tsx +20 -0
  251. package/src/templates/dashboard-pages/auth/signup.tsx +71 -0
  252. package/src/templates/dashboard-pages/auth/verify-email.tsx +53 -0
  253. package/src/templates/dashboard-pages/index.ts +22 -0
  254. package/src/templates/dashboard-pages/shell/dashboard-home.tsx +41 -0
  255. package/src/templates/dashboard-pages/shell/forbidden.tsx +46 -0
  256. package/src/templates/dashboard-pages/shell/index.ts +5 -0
  257. package/src/templates/dashboard-pages/shell/maintenance.tsx +27 -0
  258. package/src/templates/dashboard-pages/shell/not-found.tsx +34 -0
  259. package/src/templates/dashboard-pages/shell/server-error.tsx +44 -0
  260. package/src/templates/domain-config.ts +20 -0
  261. package/src/templates/events-skeleton.ts +24 -0
  262. package/src/templates/patterns/api/create-with-event.ts +72 -0
  263. package/src/templates/patterns/api/idempotent-mutation.ts +102 -0
  264. package/src/templates/patterns/api/paginated-list.ts +74 -0
  265. package/src/templates/patterns/api/simple-crud.ts +100 -0
  266. package/src/templates/patterns/api/system-admin.ts +101 -0
  267. package/src/templates/patterns/api/webhook-receiver-style.ts +128 -0
  268. package/src/templates/patterns/component/index.ts +833 -0
  269. package/src/templates/patterns/flow/autonomous-remediation.ts +101 -0
  270. package/src/templates/patterns/flow/fan-out-reaction.ts +56 -0
  271. package/src/templates/patterns/flow/human-in-loop.ts +93 -0
  272. package/src/templates/patterns/flow/multi-step-orchestration.ts +70 -0
  273. package/src/templates/patterns/hook/index.ts +382 -0
  274. package/src/templates/patterns/migration/column-add.ts +54 -0
  275. package/src/templates/patterns/migration/event-store.ts +99 -0
  276. package/src/templates/patterns/migration/index.ts +54 -0
  277. package/src/templates/patterns/migration/rls-policy.ts +80 -0
  278. package/src/templates/patterns/migration/tenant-scoped-table.ts +83 -0
  279. package/src/templates/patterns/page/dashboard.ts +164 -0
  280. package/src/templates/patterns/page/detail.ts +162 -0
  281. package/src/templates/patterns/page/form.ts +192 -0
  282. package/src/templates/patterns/page/index.ts +17 -0
  283. package/src/templates/patterns/page/list.ts +138 -0
  284. package/src/templates/patterns/page/settings.ts +318 -0
  285. package/src/templates/patterns/page/wizard.ts +293 -0
  286. package/src/templates/patterns/subscriber/audit-relay.ts +54 -0
  287. package/src/templates/patterns/subscriber/cascade-deletion.ts +63 -0
  288. package/src/templates/patterns/subscriber/single-step-projection.ts +39 -0
  289. package/src/templates/readme.ts +7 -0
  290. package/src/templates/subscriber-skeleton.ts +28 -0
  291. package/src/utils/checksum.ts +43 -0
  292. package/src/utils/file-helpers.ts +91 -0
  293. package/src/utils/header-inject.ts +126 -0
  294. package/src/utils/install-file.ts +99 -0
  295. package/src/utils/logger.ts +26 -0
  296. package/src/utils/manifest.ts +137 -0
  297. package/src/utils/s3-fetch.ts +91 -0
  298. package/src/utils/scaffold-config.ts +172 -0
  299. package/src/utils/token-substitution.ts +80 -0
  300. package/src/utils/zod-extractor.ts +31 -0
  301. package/tsconfig.json +13 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Command to explain a rule ID (ESLint rule name or doctor check name).
3
+ * Maps rule IDs to descriptions and K-node references.
4
+ */
5
+
6
+ /**
7
+ * Options for the explain command.
8
+ */
9
+ export interface ExplainOptions {
10
+ /** The rule ID to explain (e.g., 'no-cross-domain-internal-import'). */
11
+ ruleId: string;
12
+ }
13
+
14
+ /**
15
+ * A rule explanation entry.
16
+ */
17
+ interface RuleExplanation {
18
+ ruleId: string;
19
+ title: string;
20
+ description: string;
21
+ severity: 'error' | 'warn';
22
+ category: 'convention' | 'security' | 'structure' | 'performance' | 'correctness';
23
+ kNodeRef: string;
24
+ fixHint: string;
25
+ exampleBad: string;
26
+ exampleGood: string;
27
+ }
28
+
29
+ /**
30
+ * Lookup table of rule IDs to explanations.
31
+ */
32
+ const RULE_EXPLANATIONS: Record<string, RuleExplanation> = {
33
+ 'no-cross-domain-internal-import': {
34
+ ruleId: 'no-cross-domain-internal-import',
35
+ title: 'No cross-domain internal imports',
36
+ description:
37
+ 'Domain code must never import from another domain directly. Cross-domain communication must use actions (ctx.actions.call), events (ctx.publish), or defined integrations.',
38
+ severity: 'error',
39
+ category: 'structure',
40
+ kNodeRef: 'K:convention:no-cross-domain-import',
41
+ fixHint: "Replace 'import { ... } from \"../../other-domain/...\"' with 'await ctx.actions.call(\"other-domain.action-id\", input)'.",
42
+ exampleBad: "import { doThing } from '../../payments/api/charge';",
43
+ exampleGood: "await ctx.actions.call('payments.charge', { amount: 100 });",
44
+ },
45
+ 'no-raw-aws-sdk': {
46
+ ruleId: 'no-raw-aws-sdk',
47
+ title: 'No raw AWS SDK imports',
48
+ description:
49
+ 'Domain code must never import @aws-sdk/* directly. Use ctx-provided helpers (ctx.db, ctx.publish, ctx.secrets, ctx.fetch) or define an integration.',
50
+ severity: 'error',
51
+ category: 'structure',
52
+ kNodeRef: 'K:convention:tier-1-foundations',
53
+ fixHint: "Use ctx.db for DynamoDB, ctx.secrets for Secrets Manager, ctx.fetch for HTTP calls. For other services, define an integration via defineIntegration.",
54
+ exampleBad: "import { S3Client } from '@aws-sdk/client-s3';",
55
+ exampleGood: "await ctx.integrations.s3.putObject({ Bucket, Key, Body });",
56
+ },
57
+ 'no-raw-http-server': {
58
+ ruleId: 'no-raw-http-server',
59
+ title: 'No raw HTTP server in domain code',
60
+ description:
61
+ 'Domain code must not create raw HTTP servers (express, fastify, etc.). All HTTP handling goes through defineApi which is wired to API Gateway by the scaffold.',
62
+ severity: 'error',
63
+ category: 'structure',
64
+ kNodeRef: 'K:convention:tier-1-foundations',
65
+ fixHint: 'Wrap your HTTP handler with defineApi({ ... }). The scaffold handles API Gateway wiring.',
66
+ exampleBad: "import express from 'express';\nconst app = express();",
67
+ exampleGood: "export const myApi = defineApi({ id: 'my-api', path: '/v1/my-api', ... });",
68
+ },
69
+ 'require-define-primitive': {
70
+ ruleId: 'require-define-primitive',
71
+ title: 'Require define primitive factories',
72
+ description:
73
+ 'All domain handlers must use the appropriate factory function: defineApi for HTTP APIs, defineSubscriber for event subscribers, defineAction for cross-domain actions, defineJob for background jobs.',
74
+ severity: 'error',
75
+ category: 'structure',
76
+ kNodeRef: 'K:runbook:add-domain',
77
+ fixHint: "Wrap your handler with the appropriate define* factory from @mettlecast/domain-runtime.",
78
+ exampleBad: "export const handler = async (event) => ({ statusCode: 200 });",
79
+ exampleGood: "export const myApi = defineApi({ id: 'my-api', ... });",
80
+ },
81
+ 'flow-domain-ownership': {
82
+ ruleId: 'flow-domain-ownership',
83
+ title: 'Flow domain ownership',
84
+ description:
85
+ 'Every flow must have an owningDomain. Root-level flows/ directory is deprecated. Flows belong inside domains/{owningDomain}/flows/.',
86
+ severity: 'error',
87
+ category: 'structure',
88
+ kNodeRef: 'K:convention:flow-vs-subscriber-rule',
89
+ fixHint: "Move the flow file to domains/<owningDomain>/flows/<id>.ts and set owningDomain in the defineFlow call.",
90
+ exampleBad: "// flows/onboarding.ts (root level — deprecated)",
91
+ exampleGood: "// domains/auth/flows/onboarding.ts\nexport const onboarding = defineFlow({ owningDomain: 'auth', ... });",
92
+ },
93
+ 'domain-configs-valid': {
94
+ ruleId: 'domain-configs-valid',
95
+ title: 'Domain configs are valid',
96
+ description:
97
+ 'Every domain directory under domains/ must contain a valid domain.config.ts file exported by defineDomain().',
98
+ severity: 'error',
99
+ category: 'structure',
100
+ kNodeRef: 'K:runbook:add-domain',
101
+ fixHint: "Run 'mc-domain-module add-domain <id>' to scaffold a missing domain.",
102
+ exampleBad: '// domains/payments/ — missing domain.config.ts',
103
+ exampleGood: "// domains/payments/domain.config.ts\nexport const domain = defineDomain({ id: 'payments', tenancy: 'required' });",
104
+ },
105
+ 'apis-have-versions': {
106
+ ruleId: 'apis-have-versions',
107
+ title: 'APIs declare versions',
108
+ description:
109
+ 'Every defineApi must have a versions field with at least one version entry. Versioning is mandatory for API evolution.',
110
+ severity: 'error',
111
+ category: 'correctness',
112
+ kNodeRef: 'K:runbook:add-domain',
113
+ fixHint: 'Add versions: { v1: { status, input, output, handler } } to your defineApi call.',
114
+ exampleBad: "defineApi({ id: 'my-api', path: '/v1/my-api' }) // missing versions",
115
+ exampleGood: "defineApi({ id: 'my-api', path: '/v1/my-api', versions: { v1: { ... } } });",
116
+ },
117
+ 'apis-have-tenancy': {
118
+ ruleId: 'apis-have-tenancy',
119
+ title: 'APIs declare tenancy',
120
+ description:
121
+ 'Every defineApi must declare its tenancy mode: required (tenant-scoped), none (tenant-agnostic like registration), or system (system-internal admin).',
122
+ severity: 'error',
123
+ category: 'correctness',
124
+ kNodeRef: 'K:convention:tier-1-foundations',
125
+ fixHint: "Add tenancy: 'required' | 'none' | 'system' to your defineApi call.",
126
+ exampleBad: "defineApi({ id: 'my-api', path: '/v1/my-api' }) // missing tenancy",
127
+ exampleGood: "defineApi({ id: 'my-api', path: '/v1/my-api', tenancy: 'required' });",
128
+ },
129
+ 'no-cross-domain-imports': {
130
+ ruleId: 'no-cross-domain-imports',
131
+ title: 'No cross-domain imports (doctor check)',
132
+ description:
133
+ 'The doctor command scans for import statements that reference files in other domain directories. These violate domain isolation.',
134
+ severity: 'error',
135
+ category: 'structure',
136
+ kNodeRef: 'K:convention:no-cross-domain-import',
137
+ fixHint: "Use domain actions or events to communicate between domains. See 'tib explain no-cross-domain-internal-import' for details.",
138
+ exampleBad: "import { charge } from '../../payments/api/charge';",
139
+ exampleGood: "await ctx.actions.call('payments.charge', { amount: 100 });",
140
+ },
141
+ 'scaffold-config-matches': {
142
+ ruleId: 'scaffold-config-matches',
143
+ title: 'Scaffold config matches on-disk domains',
144
+ description:
145
+ 'The .mc/scaffold-config.json domainIds array must match the directories present under domains/. Run add-domain to sync.',
146
+ severity: 'error',
147
+ category: 'structure',
148
+ kNodeRef: 'K:runbook:add-domain',
149
+ fixHint: "Run 'mc-domain-module add-domain <id>' for missing domains, or remove stale entries from .mc/scaffold-config.json.",
150
+ exampleBad: '// domains/ has "billing" but .mc/scaffold-config.json does not list it',
151
+ exampleGood: '// Run: mc-domain-module add-domain billing',
152
+ },
153
+ 'husky-hooks-installed': {
154
+ ruleId: 'husky-hooks-installed',
155
+ title: 'Husky git hooks are installed',
156
+ description:
157
+ 'The .husky/pre-commit hook should be present and executable. It enforces pre-commit checks like ESLint and commit message format.',
158
+ severity: 'warn',
159
+ category: 'structure',
160
+ kNodeRef: 'K:runbook:add-domain',
161
+ fixHint: "Run 'npx husky install' to set up git hooks.",
162
+ exampleBad: 'Missing .husky/pre-commit',
163
+ exampleGood: 'npx husky install # creates .husky/pre-commit',
164
+ },
165
+ };
166
+
167
+ /**
168
+ * Execute the explain command: look up a rule ID and print its explanation.
169
+ */
170
+ export function runExplain(opts: ExplainOptions): RuleExplanation {
171
+ const explanation = RULE_EXPLANATIONS[opts.ruleId];
172
+
173
+ if (!explanation) {
174
+ const available = Object.keys(RULE_EXPLANATIONS).join(', ');
175
+ throw new Error(
176
+ `Unknown rule ID "${opts.ruleId}".\n\nAvailable rules: ${available}`,
177
+ );
178
+ }
179
+
180
+ return explanation;
181
+ }
182
+
183
+ /**
184
+ * CLI entry point: prints the explanation in a human-readable format.
185
+ */
186
+ export function runExplainCli(opts: ExplainOptions): void {
187
+ const explanation = runExplain(opts);
188
+
189
+ // eslint-disable-next-line no-console
190
+ console.log(`
191
+ ╔══════════════════════════════════════════════════════════════╗
192
+ ║ ${explanation.ruleId}
193
+ ╚══════════════════════════════════════════════════════════════╝
194
+
195
+ Severity: ${explanation.severity.toUpperCase()}
196
+ Category: ${explanation.category}
197
+ Docs: ${explanation.kNodeRef}
198
+
199
+ ${explanation.description}
200
+
201
+ Fix: ${explanation.fixHint}
202
+
203
+ ❌ BAD:
204
+ ${explanation.exampleBad.split('\n').map((l) => ' ' + l).join('\n')}
205
+
206
+ ✅ GOOD:
207
+ ${explanation.exampleGood.split('\n').map((l) => ' ' + l).join('\n')}
208
+ `);
209
+ }
@@ -0,0 +1,112 @@
1
+ import { SFNClient, StartExecutionCommand, DescribeExecutionCommand } from '@aws-sdk/client-sfn';
2
+ import { readFileSync } from 'fs';
3
+ import path from 'path';
4
+ import { cliLogger } from '../utils/logger.js';
5
+
6
+ export async function runPowerTune(domain: string, primitive: string): Promise<void> {
7
+ // Load registry to find Lambda ARN
8
+ const registryPath = path.join(process.cwd(), '.tib', `${domain}-registry.json`);
9
+
10
+ let registry: { domain: { id: string }; functionArns?: Record<string, string> };
11
+ try {
12
+ registry = JSON.parse(readFileSync(registryPath, 'utf8'));
13
+ } catch {
14
+ cliLogger.error({}, `Registry not found at ${registryPath}. Run: tib build domains/${domain}`);
15
+ process.exit(1);
16
+ }
17
+
18
+ // Find Lambda ARN for the primitive
19
+ const lambdaArn = registry.functionArns?.[primitive];
20
+ if (!lambdaArn) {
21
+ cliLogger.error({}, `No Lambda ARN found for primitive '${primitive}' in domain '${domain}'.`);
22
+ cliLogger.error({}, `Available primitives: ${Object.keys(registry.functionArns ?? {}).join(', ')}`);
23
+ process.exit(1);
24
+ }
25
+
26
+ // Get state machine ARN
27
+ const stateMachineArn = process.env['POWER_TUNING_STATE_MACHINE_ARN'];
28
+ if (!stateMachineArn) {
29
+ cliLogger.error({}, 'POWER_TUNING_STATE_MACHINE_ARN environment variable not set.');
30
+ cliLogger.error({}, 'Deploy PowerTuningStack first and export the ARN.');
31
+ process.exit(1);
32
+ }
33
+
34
+ const sfnClient = new SFNClient({});
35
+
36
+ const input = JSON.stringify({
37
+ lambdaARN: lambdaArn,
38
+ powerValues: [128, 256, 512, 1024, 1536, 3008],
39
+ num: 10,
40
+ payload: '{}',
41
+ parallelInvocation: true,
42
+ strategy: 'cost',
43
+ });
44
+
45
+ cliLogger.info({ domain, primitive, lambdaArn }, 'Starting power tuning');
46
+
47
+ const startResult = await sfnClient.send(
48
+ new StartExecutionCommand({
49
+ stateMachineArn,
50
+ input,
51
+ name: `tib-power-tune-${domain}-${primitive}-${Date.now()}`,
52
+ })
53
+ );
54
+
55
+ const executionArn = startResult.executionArn!;
56
+ cliLogger.info({ executionArn }, 'Execution started');
57
+ cliLogger.info({}, 'Polling for results (this takes 1-3 minutes)...');
58
+
59
+ // Poll for completion
60
+ let attempts = 0;
61
+ const maxAttempts = 60; // 5 minutes at 5s intervals
62
+
63
+ while (attempts < maxAttempts) {
64
+ await new Promise(resolve => setTimeout(resolve, 5000));
65
+ attempts++;
66
+
67
+ const status = await sfnClient.send(new DescribeExecutionCommand({ executionArn }));
68
+
69
+ if (status.status === 'RUNNING') {
70
+ process.stdout.write('.');
71
+ continue;
72
+ }
73
+
74
+ process.stdout.write('\n');
75
+
76
+ if (status.status === 'SUCCEEDED') {
77
+ const output = JSON.parse(status.output ?? '{}');
78
+
79
+ cliLogger.info({}, '\n=== Power Tuning Results ===');
80
+ cliLogger.info({ domain, primitive }, 'Power tuning complete');
81
+
82
+ if (output.results && Array.isArray(output.results) && output.results.length > 0) {
83
+ const optimal = output.results.reduce((best: { power: number; cost: number; duration: number }, r: { power: number; cost: number; duration: number }) =>
84
+ r.cost < best.cost ? r : best
85
+ );
86
+
87
+ const baseline = output.results[0];
88
+ const costPercentChange = ((optimal.cost / baseline.cost - 1) * 100).toFixed(1);
89
+ const durationPercentChange = ((optimal.duration / baseline.duration - 1) * 100).toFixed(1);
90
+
91
+ cliLogger.info({ memory: optimal.power }, `Optimal memory size: ${optimal.power} MB`);
92
+ cliLogger.info({ change: costPercentChange }, `Expected cost change: ${costPercentChange}%`);
93
+ cliLogger.info({ change: durationPercentChange }, `Expected duration change: ${durationPercentChange}%`);
94
+ cliLogger.info({}, `To apply: update your domain registry config:`);
95
+ cliLogger.info({}, ` domains/${domain}/domain.config.ts → defaultDeployment.memory = ${optimal.power}`);
96
+ }
97
+
98
+ cliLogger.info({}, `Visualization: https://lambda-power-tuning.show/#${Buffer.from(JSON.stringify(output)).toString('base64')}`);
99
+ return;
100
+ }
101
+
102
+ cliLogger.error({ status }, `Execution failed with status: ${status.status}`);
103
+ if (status.cause) {
104
+ cliLogger.error({}, `Cause: ${status.cause}`);
105
+ }
106
+ process.exit(1);
107
+ }
108
+
109
+ cliLogger.error({}, 'Timeout: execution did not complete within 5 minutes.');
110
+ cliLogger.error({ executionArn }, `Check manually: aws stepfunctions describe-execution --execution-arn ${executionArn}`);
111
+ process.exit(1);
112
+ }