@metaobjectsdev/cli 1.0.13 → 1.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 (186) hide show
  1. package/README.md +32 -22
  2. package/dist/bin/meta.js +14 -2
  3. package/dist/bin/meta.js.map +1 -1
  4. package/dist/src/commands/docs.d.ts +1 -14
  5. package/dist/src/commands/docs.d.ts.map +1 -1
  6. package/dist/src/commands/docs.js +11 -835
  7. package/dist/src/commands/docs.js.map +1 -1
  8. package/dist/src/commands/eject.d.ts +24 -1
  9. package/dist/src/commands/eject.d.ts.map +1 -1
  10. package/dist/src/commands/eject.js +240 -69
  11. package/dist/src/commands/eject.js.map +1 -1
  12. package/dist/src/commands/gen.d.ts +7 -0
  13. package/dist/src/commands/gen.d.ts.map +1 -1
  14. package/dist/src/commands/gen.js +91 -21
  15. package/dist/src/commands/gen.js.map +1 -1
  16. package/dist/src/commands/generator.js +2 -2
  17. package/dist/src/commands/generator.js.map +1 -1
  18. package/dist/src/commands/init.d.ts +7 -0
  19. package/dist/src/commands/init.d.ts.map +1 -1
  20. package/dist/src/commands/init.js +71 -36
  21. package/dist/src/commands/init.js.map +1 -1
  22. package/dist/src/commands/migrate.d.ts +1 -1
  23. package/dist/src/commands/migrate.d.ts.map +1 -1
  24. package/dist/src/commands/migrate.js +224 -125
  25. package/dist/src/commands/migrate.js.map +1 -1
  26. package/dist/src/commands/types.d.ts.map +1 -1
  27. package/dist/src/commands/types.js +16 -2
  28. package/dist/src/commands/types.js.map +1 -1
  29. package/dist/src/commands/upgrade.d.ts.map +1 -1
  30. package/dist/src/commands/upgrade.js +4 -1
  31. package/dist/src/commands/upgrade.js.map +1 -1
  32. package/dist/src/commands/verify.d.ts.map +1 -1
  33. package/dist/src/commands/verify.js +189 -125
  34. package/dist/src/commands/verify.js.map +1 -1
  35. package/dist/src/index.d.ts.map +1 -1
  36. package/dist/src/index.js +59 -72
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/lib/agent-context-staleness.d.ts +8 -1
  39. package/dist/src/lib/agent-context-staleness.d.ts.map +1 -1
  40. package/dist/src/lib/agent-context-staleness.js +20 -5
  41. package/dist/src/lib/agent-context-staleness.js.map +1 -1
  42. package/dist/src/lib/agent-schema.d.ts +9 -0
  43. package/dist/src/lib/agent-schema.d.ts.map +1 -0
  44. package/dist/src/lib/agent-schema.js +61 -0
  45. package/dist/src/lib/agent-schema.js.map +1 -0
  46. package/dist/src/lib/allow.d.ts +6 -0
  47. package/dist/src/lib/allow.d.ts.map +1 -1
  48. package/dist/src/lib/allow.js +49 -5
  49. package/dist/src/lib/allow.js.map +1 -1
  50. package/dist/src/lib/anti-patterns.d.ts.map +1 -1
  51. package/dist/src/lib/anti-patterns.js +4 -2
  52. package/dist/src/lib/anti-patterns.js.map +1 -1
  53. package/dist/src/lib/args.d.ts +51 -10
  54. package/dist/src/lib/args.d.ts.map +1 -1
  55. package/dist/src/lib/args.js +44 -18
  56. package/dist/src/lib/args.js.map +1 -1
  57. package/dist/src/lib/catalog-listing.d.ts +21 -1
  58. package/dist/src/lib/catalog-listing.d.ts.map +1 -1
  59. package/dist/src/lib/catalog-listing.js +85 -16
  60. package/dist/src/lib/catalog-listing.js.map +1 -1
  61. package/dist/src/lib/catalog.d.ts +30 -0
  62. package/dist/src/lib/catalog.d.ts.map +1 -1
  63. package/dist/src/lib/catalog.js +40 -5
  64. package/dist/src/lib/catalog.js.map +1 -1
  65. package/dist/src/lib/codegen-drift.d.ts +31 -2
  66. package/dist/src/lib/codegen-drift.d.ts.map +1 -1
  67. package/dist/src/lib/codegen-drift.js +109 -4
  68. package/dist/src/lib/codegen-drift.js.map +1 -1
  69. package/dist/src/lib/collection-load-options.d.ts +1 -1
  70. package/dist/src/lib/collection-load-options.d.ts.map +1 -1
  71. package/dist/src/lib/collection-load-options.js +1 -0
  72. package/dist/src/lib/collection-load-options.js.map +1 -1
  73. package/dist/src/lib/config.d.ts +4 -2
  74. package/dist/src/lib/config.d.ts.map +1 -1
  75. package/dist/src/lib/config.js +4 -2
  76. package/dist/src/lib/config.js.map +1 -1
  77. package/dist/src/lib/decimal-precision-advisory.d.ts +10 -0
  78. package/dist/src/lib/decimal-precision-advisory.d.ts.map +1 -0
  79. package/dist/src/lib/decimal-precision-advisory.js +34 -0
  80. package/dist/src/lib/decimal-precision-advisory.js.map +1 -0
  81. package/dist/src/lib/deprecation-lint.d.ts.map +1 -1
  82. package/dist/src/lib/deprecation-lint.js +17 -26
  83. package/dist/src/lib/deprecation-lint.js.map +1 -1
  84. package/dist/src/lib/docs-removed.d.ts +13 -0
  85. package/dist/src/lib/docs-removed.d.ts.map +1 -0
  86. package/dist/src/lib/docs-removed.js +31 -0
  87. package/dist/src/lib/docs-removed.js.map +1 -0
  88. package/dist/src/lib/docs-site-eject.d.ts +13 -0
  89. package/dist/src/lib/docs-site-eject.d.ts.map +1 -0
  90. package/dist/src/lib/docs-site-eject.js +40 -0
  91. package/dist/src/lib/docs-site-eject.js.map +1 -0
  92. package/dist/src/lib/field-lint.d.ts +25 -0
  93. package/dist/src/lib/field-lint.d.ts.map +1 -0
  94. package/dist/src/lib/field-lint.js +102 -0
  95. package/dist/src/lib/field-lint.js.map +1 -0
  96. package/dist/src/lib/fk-index-advisory.d.ts +1 -2
  97. package/dist/src/lib/fk-index-advisory.d.ts.map +1 -1
  98. package/dist/src/lib/fk-index-advisory.js +4 -3
  99. package/dist/src/lib/fk-index-advisory.js.map +1 -1
  100. package/dist/src/lib/format.d.ts +7 -0
  101. package/dist/src/lib/format.d.ts.map +1 -1
  102. package/dist/src/lib/format.js +13 -0
  103. package/dist/src/lib/format.js.map +1 -1
  104. package/dist/src/lib/gen-migrate-advice.d.ts +3 -1
  105. package/dist/src/lib/gen-migrate-advice.d.ts.map +1 -1
  106. package/dist/src/lib/gen-migrate-advice.js +3 -4
  107. package/dist/src/lib/gen-migrate-advice.js.map +1 -1
  108. package/dist/src/lib/install-set.d.ts +10 -1
  109. package/dist/src/lib/install-set.d.ts.map +1 -1
  110. package/dist/src/lib/install-set.js +20 -2
  111. package/dist/src/lib/install-set.js.map +1 -1
  112. package/dist/src/lib/load-metaobjects-config.d.ts +13 -11
  113. package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
  114. package/dist/src/lib/load-metaobjects-config.js +53 -75
  115. package/dist/src/lib/load-metaobjects-config.js.map +1 -1
  116. package/dist/src/lib/manifest-ignored-check.d.ts +4 -1
  117. package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -1
  118. package/dist/src/lib/manifest-ignored-check.js +9 -4
  119. package/dist/src/lib/manifest-ignored-check.js.map +1 -1
  120. package/dist/src/lib/output.d.ts +15 -0
  121. package/dist/src/lib/output.d.ts.map +1 -1
  122. package/dist/src/lib/output.js +9 -1
  123. package/dist/src/lib/output.js.map +1 -1
  124. package/dist/src/lib/payload-field-tree.d.ts +4 -5
  125. package/dist/src/lib/payload-field-tree.d.ts.map +1 -1
  126. package/dist/src/lib/payload-field-tree.js +16 -31
  127. package/dist/src/lib/payload-field-tree.js.map +1 -1
  128. package/dist/src/lib/requirement-check.d.ts +7 -0
  129. package/dist/src/lib/requirement-check.d.ts.map +1 -1
  130. package/dist/src/lib/requirement-check.js +14 -7
  131. package/dist/src/lib/requirement-check.js.map +1 -1
  132. package/dist/src/lib/strict-args.d.ts +28 -0
  133. package/dist/src/lib/strict-args.d.ts.map +1 -0
  134. package/dist/src/lib/strict-args.js +49 -0
  135. package/dist/src/lib/strict-args.js.map +1 -0
  136. package/dist/src/lib/template-eject.d.ts +108 -0
  137. package/dist/src/lib/template-eject.d.ts.map +1 -0
  138. package/dist/src/lib/template-eject.js +438 -0
  139. package/dist/src/lib/template-eject.js.map +1 -0
  140. package/dist/src/lib/version.d.ts +9 -0
  141. package/dist/src/lib/version.d.ts.map +1 -1
  142. package/dist/src/lib/version.js +15 -0
  143. package/dist/src/lib/version.js.map +1 -1
  144. package/package.json +10 -10
  145. package/src/commands/docs.ts +11 -952
  146. package/src/commands/eject.ts +318 -73
  147. package/src/commands/gen.ts +94 -22
  148. package/src/commands/generator.ts +2 -2
  149. package/src/commands/init.ts +80 -38
  150. package/src/commands/migrate.ts +248 -117
  151. package/src/commands/types.ts +18 -2
  152. package/src/commands/upgrade.ts +5 -1
  153. package/src/commands/verify.ts +229 -136
  154. package/src/index.ts +59 -72
  155. package/src/lib/agent-context-staleness.ts +20 -5
  156. package/src/lib/agent-schema.ts +74 -0
  157. package/src/lib/allow.ts +57 -6
  158. package/src/lib/anti-patterns.ts +4 -2
  159. package/src/lib/args.ts +83 -28
  160. package/src/lib/catalog-listing.ts +105 -25
  161. package/src/lib/catalog.ts +51 -6
  162. package/src/lib/codegen-drift.ts +138 -3
  163. package/src/lib/collection-load-options.ts +2 -1
  164. package/src/lib/config.ts +5 -3
  165. package/src/lib/decimal-precision-advisory.ts +50 -0
  166. package/src/lib/deprecation-lint.ts +16 -24
  167. package/src/lib/docs-removed.ts +41 -0
  168. package/src/lib/docs-site-eject.ts +48 -0
  169. package/src/lib/field-lint.ts +122 -0
  170. package/src/lib/fk-index-advisory.ts +4 -4
  171. package/src/lib/format.ts +12 -0
  172. package/src/lib/gen-migrate-advice.ts +6 -4
  173. package/src/lib/install-set.ts +20 -1
  174. package/src/lib/load-metaobjects-config.ts +57 -78
  175. package/src/lib/manifest-ignored-check.ts +14 -10
  176. package/src/lib/output.ts +18 -1
  177. package/src/lib/payload-field-tree.ts +18 -35
  178. package/src/lib/requirement-check.ts +21 -6
  179. package/src/lib/strict-args.ts +52 -0
  180. package/src/lib/template-eject.ts +526 -0
  181. package/src/lib/version.ts +18 -0
  182. package/dist/src/lib/docs-drift.d.ts +0 -42
  183. package/dist/src/lib/docs-drift.d.ts.map +0 -1
  184. package/dist/src/lib/docs-drift.js +0 -264
  185. package/dist/src/lib/docs-drift.js.map +0 -1
  186. package/src/lib/docs-drift.ts +0 -312
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolve } from "node:path";
2
2
  import { log } from "./lib/log.js";
3
- import { cliVersion } from "./lib/version.js";
3
+ import { cliVersion, isVersionFlag } from "./lib/version.js";
4
4
  import { resolveFormat, isValidFormat, VALID_FORMATS, type OutputFormat } from "./lib/format.js";
5
5
  import { resolveCollection } from "@metaobjectsdev/sdk";
6
6
  import { GENERATOR_HELP } from "./commands/generator-help.js";
@@ -42,12 +42,11 @@ COMMANDS:
42
42
  types [query] Search the metadata vocabulary (types, subtypes, @attrs) by name or description
43
43
  export Flatten loaded metadata to one canonical JSON artifact
44
44
  fmt [<file>...] Rewrite metadata files into canonical form; --check lists drift, exits non-zero
45
- docs [<project-root>] --out <dir> Generate neutral metadata documentation (entity + template pages; --site for HTML site)
46
- verify Drift gate — subverbs: --templates / --db / --codegen / --docs / --deps (bare = --templates)
45
+ verify Drift gate — subverbs: --templates / --db / --codegen / --deps (bare = --templates)
47
46
  upgrade Rewrite retired metadata vocabulary (previews; --apply writes)
48
47
  prompt-snapshot Snapshot rendered template.* output; --check gates drift
49
48
  migrate Diff metadata vs live DB; emit migration SQL files
50
- --version, -v Print version
49
+ --version, -v, -V Print version
51
50
  --help, -h Print this help
52
51
 
53
52
  GLOBAL OPTIONS:
@@ -81,8 +80,12 @@ GEN FLAGS:
81
80
  --limit <n|all> How many advisory lines TEXT output prints before it truncates
82
81
  (default 20). Never applies to --format toon/json, which
83
82
  carry every finding.
83
+ --generators <names> Narrow the run to these DECLARED generators, by stable name, comma-
84
+ separated (e.g. docs,api-docs to write only the documentation). A
85
+ name metaobjects.config.ts does not declare is refused: it never adds one.
84
86
  <entity> [<entity>] Positional filter on entity names
85
- (outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts)
87
+ (outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts. Every generator,
88
+ documentation included, is an entry in its \`generators\`; nothing else is generated.)
86
89
 
87
90
  EXPORT FLAGS:
88
91
  --out <file> Write output to a file (default: stdout)
@@ -102,26 +105,10 @@ FMT FLAGS:
102
105
  find-or-reuse behavior would otherwise silently fold a same-file
103
106
  base + overlay into one node, dropping the overlay marker.
104
107
 
105
- DOCS FLAGS:
106
- [<project-root>] PROJECT ROOT to resolve metadata from — the directory that CONTAINS
107
- your metadata, NOT the metadata directory. Passing it SCOPES the run
108
- (default: cwd)
109
- --out <dir>, -o Output directory for THIS run (default: ./docs/generated). It does
110
- NOT survive the run — set 'docs: { outDir }' in metaobjects.config.ts
111
- for a directory that persists, which is what 'meta verify --docs'
112
- measures. The default is a sub-directory on purpose: these pages are
113
- regenerated and overwritten, and 'docs/' itself is usually yours.
114
- --templates <dir> Project root to resolve adopter templates/ overrides (default: <project-root>)
115
- --prompts <dir> Extra dir holding prompt .mustache sources for --site (e.g. data/templates/)
116
-
117
108
  VERIFY FLAGS (ADR-0021 D2 — explicit subverbs; combine any; exit 1 on ANY drift):
118
109
  --templates Template/prompt {{field}}↔payload drift (the bare-verify default)
119
110
  --codegen Codegen drift — regenerate to a temp dir and diff the committed
120
- output (config outDir/targets). Needs metaobjects.config.ts; exit 2 if absent.
121
- --docs Docs drift — run 'meta docs' into a temp dir and diff the committed
122
- docs tree (docs.outDir). Reports a page that changed or that a fresh
123
- run would emit; never reports an extra file, because docs.outDir
124
- holds hand-written documentation MetaObjects did not write.
111
+ output (config outDir/targets), documentation included.
125
112
  Needs metaobjects.config.ts; exit 2 if absent.
126
113
  --deps Dependency drift — re-resolve each declared dependency and compare
127
114
  its installed artifact's hash against .metaobjects/deps.lock.json
@@ -135,18 +122,31 @@ VERIFY FLAGS (ADR-0021 D2 — explicit subverbs; combine any; exit 1 on ANY drif
135
122
  --allow <csv> Accepted for parity with 'migrate'; does NOT affect the
136
123
  verify drift gate (the gate fails on ANY detected change)
137
124
  --skip-schema Skip the schema-drift gate even when --db is present
125
+ --migration-format default|flyway
126
+ Migration file layout (default: default), as for 'meta migrate'.
127
+ Under 'flyway' the schema-drift gate leaves flyway_schema_history
128
+ out of the comparison, because Flyway owns it. Also settable
129
+ once as migrate.format in .metaobjects/config.json.
138
130
  --d1 <binding> D1 binding name from wrangler.toml (only with --dialect d1)
139
131
  --remote Target remote D1 instead of local (only with --dialect d1) —
140
132
  the ONLY way to verify the actual deployed D1 database
141
133
  --no-antipatterns Suppress the advisory "you hand-rolled what MetaObjects can
142
134
  model" pass (aggregate/currency/enum hints; warnings only)
143
135
  --no-requirement-lint Suppress the advisory requirement AUTHORING lint (not the gate)
136
+ --require-implementers
137
+ Fail (exit 1) when a live functional requirement has nothing
138
+ implementing it. That finding is a warning without this flag;
139
+ with it, the same WARN_REQUIREMENT_NOTHING_IMPLEMENTS code is
140
+ reported as an error. Also: META_REQUIRE_IMPLEMENTERS=1.
144
141
  --no-overlay-lint Suppress the advisory overlay-redeclaration AUTHORING lint
145
142
  (never a gate — this lint can't fail the build)
146
143
  --no-name-lint Suppress the advisory node-name AUTHORING lint (whitespace
147
144
  in a name; never a gate — this lint can't fail the build)
148
145
  --no-deprecation-lint Suppress the advisory deprecated-reference AUTHORING lint
149
146
  (never a gate — this lint can't fail the build)
147
+ --no-field-lint Suppress the advisory field AUTHORING lint (a reference
148
+ identity over a missing field; a duplicate field name —
149
+ never a gate; this lint can't fail the build)
150
150
  --limit <n|all> How many advisory lines TEXT output prints per section before
151
151
  it truncates (default 20). Never applies to --format
152
152
  toon/json, which carry every finding and every diagnostic.
@@ -175,9 +175,9 @@ MIGRATE FLAGS:
175
175
  --remote Target remote D1 instead of local (only with --dialect d1)
176
176
  --apply Run 'wrangler d1 migrations apply' after writing files
177
177
  --yes Skip the --remote --apply confirmation pause
178
- --dry-run Print SQL to stdout, don't write
178
+ --dry-run Print the SQL, don't write (json/toon: in the document's sql field)
179
179
 
180
- See https://metaobjects.com for docs.
180
+ See https://metaobjects.dev for docs.
181
181
  `;
182
182
 
183
183
  /** Focused per-subcommand usage slices shown by `<cmd> --help`. */
@@ -206,6 +206,9 @@ FLAGS:
206
206
  overwrites them with fresh output and re-baselines (discards edits).
207
207
  --no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
208
208
  --limit <n|all> Advisory lines TEXT output prints before truncating (default 20)
209
+ --generators <names> Narrow the run to these DECLARED generators, by stable name, comma-
210
+ separated (e.g. docs,api-docs to write only the documentation). A
211
+ name metaobjects.config.ts does not declare is refused: it never adds one.
209
212
  --format <toon|json|text> Output format (global flag; default toon off-TTY)
210
213
  <entity> [<entity>] Positional filter on entity names
211
214
  --help, -h Print this help
@@ -229,8 +232,9 @@ USAGE:
229
232
  meta eject <name>... Copy each generator into codegen/generators/<name>.ts
230
233
  meta eject --list List every ejectable generator name, grouped by package
231
234
 
232
- Codegen is opt-in — \`meta init\` wires nothing — so this is the door every generator
233
- you run comes through. Take several at once: the install set is CONSOLIDATED, so
235
+ Eject a generator to CHANGE what it emits. To run one as shipped, import its factory
236
+ from its package instead — \`meta gen --list\` prints the import line — and copy
237
+ nothing. Take several at once: the install set is CONSOLIDATED, so
234
238
  \`meta eject form hooks grid\` prints one install line, not three. An unknown name
235
239
  refuses the whole call and copies nothing.
236
240
 
@@ -243,9 +247,9 @@ FLAGS:
243
247
  --help, -h Print this help
244
248
 
245
249
  \`meta init\` copies NO generators — codegen is opt-in, and codegen/generators/
246
- starts empty on purpose. Everything you run comes through here (ADR-0034
247
- scaffold-and-own): the file is YOURS to edit, and \`meta gen\` runs your copy, not
248
- the package's. It prints the import line and the entry to add in
250
+ starts empty on purpose. An ejected file is YOURS to edit (ADR-0034
251
+ scaffold-and-own), and \`meta gen\` runs your copy, not the package's. Eject prints
252
+ the import line to swap in for the package one and the entry to add in
249
253
  metaobjects.config.ts, and never overwrites a file you already own without --force.
250
254
 
251
255
  Find names with \`meta gen --list\` — add --probe to see how many files each would
@@ -299,12 +303,8 @@ FLAGS:
299
303
  (a notice, exit code unchanged), so an edit is never invisible.
300
304
  --forbid-hand-edits With --codegen: fail (exit 1) on ANY hand edit inside generated
301
305
  code, for teams that want generated files untouchable.
302
- --docs Docs drift — run 'meta docs' to a temp dir and diff docs.outDir
303
- Needs metaobjects.config.ts; exit 2 if absent. A page the
304
- project GIT-IGNORES is exempt (docs.outDir is a directory, not
305
- a namespace MetaObjects owns); the count of exempt pages is
306
- reported either way, and a run where every page is ignored is
307
- refused rather than reported clean.
306
+ --docs Removed in 1.1.0: says so and exits 2. --codegen checks documentation
307
+ with everything else \`generators\` declares.
308
308
  --deps Dependency drift — re-resolve each declared dependency exactly as
309
309
  'meta deps check' does and compare its installed artifact's hash
310
310
  against .metaobjects/deps.lock.json. Exits 1 if any dependency has
@@ -329,6 +329,11 @@ FLAGS:
329
329
  --dialect sqlite|postgres|d1 Optional override (auto-detected from --db URL scheme)
330
330
  --allow <csv> Accepted for parity with 'migrate'; does NOT affect the drift gate
331
331
  --skip-schema Skip the schema-drift gate even when --db is present
332
+ --migration-format default|flyway
333
+ Migration file layout (default: default), as for 'meta migrate'.
334
+ Under 'flyway' the schema-drift gate leaves flyway_schema_history
335
+ out of the comparison, because Flyway owns it. Also settable
336
+ once as migrate.format in .metaobjects/config.json.
332
337
  --d1 <binding> D1 binding name from wrangler.toml (only with --dialect d1)
333
338
  --remote Target remote D1 instead of local (only with --dialect d1) —
334
339
  the ONLY way to verify the actual deployed D1 database
@@ -338,12 +343,20 @@ FLAGS:
338
343
  migrating a model onto a registered provider.
339
344
  --no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
340
345
  --no-requirement-lint Suppress the advisory requirement AUTHORING lint (not the gate)
346
+ --require-implementers
347
+ Fail (exit 1) when a live functional requirement has nothing
348
+ implementing it. That finding is a warning without this flag;
349
+ with it, the same WARN_REQUIREMENT_NOTHING_IMPLEMENTS code is
350
+ reported as an error. Also: META_REQUIRE_IMPLEMENTERS=1.
341
351
  --no-overlay-lint Suppress the advisory overlay-redeclaration AUTHORING lint —
342
352
  never a gate; this lint can't fail the build
343
353
  --no-name-lint Suppress the advisory node-name AUTHORING lint (whitespace in
344
354
  a name) — never a gate; this lint can't fail the build
345
355
  --no-deprecation-lint Suppress the advisory deprecated-reference AUTHORING lint —
346
356
  never a gate; this lint can't fail the build
357
+ --no-field-lint Suppress the advisory field AUTHORING lint (a reference
358
+ identity over a missing field; a duplicate field name) —
359
+ never a gate; this lint can't fail the build
347
360
  --limit <n|all> Advisory lines TEXT output prints PER SECTION before truncating
348
361
  (default 20; per-section so the authoring lint can never push
349
362
  the gate's own warnings off the end)
@@ -367,6 +380,9 @@ lint in its own section: names that are not addressable, prose slots holding one
367
380
  sentence twice, content written where no surface reads it. Warnings only — it can
368
381
  never fail the build. Opt out with --no-requirement-lint or META_NO_REQUIREMENT_LINT=1.
369
382
  The requirements GATE itself (dangling refs, link floor, levels) always runs.
383
+ One of its findings is a warning you can raise: a live functional requirement that
384
+ nothing implements fails the build under --require-implementers or
385
+ META_REQUIRE_IMPLEMENTERS=1. The diagnostic keeps its code; only its severity moves.
370
386
 
371
387
  verify also prints an ADVISORY overlay authoring lint, in its own section: a
372
388
  top-level declaration redeclared in two or more files where more than one
@@ -389,6 +405,12 @@ the deprecated node also carries @replacedBy, the finding names the replacement.
389
405
  A node referencing itself (a recursive FK, a same-entity passthrough) is not a
390
406
  finding. Warnings only — it can never fail the build. Opt out with
391
407
  --no-deprecation-lint or META_NO_DEPRECATION_LINT=1.
408
+
409
+ verify also prints an ADVISORY field authoring lint, in its own section. It reports
410
+ an identity.reference whose @fields names a field the object does not have
411
+ (inherited and overlaid fields count as present), and a field name declared more
412
+ than once in one object's children list. Both load with no error. Warnings only —
413
+ it can never fail the build. Opt out with --no-field-lint or META_NO_FIELD_LINT=1.
392
414
  `,
393
415
  export: `meta export — flatten loaded metadata to one canonical JSON artifact
394
416
 
@@ -431,42 +453,6 @@ skipped — not only when no base exists in the same file. A formatter never
431
453
  changes structure: even when a same-file base IS present, merging it with
432
454
  the overlay (what the ordinary loader would do) would drop the overlay
433
455
  marker from the output, so fmt refuses that too and leaves the file as-is.
434
- `,
435
- docs: `meta docs — generate neutral metadata documentation (entity + template pages)
436
-
437
- USAGE:
438
- meta docs [<project-root>] [flags]
439
-
440
- FLAGS:
441
- [<project-root>] PROJECT ROOT to resolve metadata from — the directory that CONTAINS
442
- your metadata, NOT the metadata directory itself. (The Python and C#
443
- 'docs' positionals mean the metadata dir; this one does not.) Passing
444
- it SCOPES the run to that directory's own sources; no ancestor
445
- .metaobjects/config.json is consulted. Omitted (default), the project
446
- is discovered by walking up.
447
- --out <dir>, -o Output directory for THIS run (default: ./docs/generated). It does
448
- NOT survive the run — set 'docs: { outDir }' in metaobjects.config.ts
449
- for a directory that persists, which is what 'meta verify --docs'
450
- measures. The default is a sub-directory on purpose: these pages are
451
- regenerated and overwritten, and 'docs/' itself is usually yours.
452
- --model Emit the markdown model surface (entity + template pages)
453
- --api Emit the markdown api surface (generated SDK reference)
454
- --requirements Emit the declared requirement ledger (requirements.md + .toon).
455
- Emits nothing when the project declares no requirement.* node.
456
- --agent Emit the agent surface — agent/schema.md (the physical schema),
457
- agent/ui.md (the generated forms and grids) and
458
- agent/requirements.md (the ledger plus a claimed-node index).
459
- Needs metaobjects.config.ts (the dialect and physical names come
460
- from it); each page is skipped when its tier has nothing to describe
461
- — agent/ui.md needs a UI generator (a form, hook or grid) in the
462
- suite, not merely objects that could have one.
463
- --metamodel Document the built-in metamodel vocabulary (no metadata needed)
464
- --site Generate the browsable HTML documentation site (<out>/site/)
465
- --scaffold-site Copy the site's templates + assets into codegen/docs-site/ to own (theme) them
466
- --templates <dir> Project root to resolve adopter templates/ overrides (default: <project-root>)
467
- --prompts <dir> Extra dir holding prompt .mustache sources (for --site) when they
468
- live outside the metadata sources or templates/ (e.g. data/templates/)
469
- --help, -h Print this help
470
456
  `,
471
457
  init: `meta init — scaffold a MetaObjects project in the current repo
472
458
 
@@ -604,7 +590,7 @@ export async function run(argv: string[]): Promise<number> {
604
590
  }
605
591
 
606
592
  // Intercept per-subcommand --help / -h before dispatching (mirrors migrate's own pattern).
607
- if (cmd !== undefined && cmd !== "--help" && cmd !== "-h" && cmd !== "--version" && cmd !== "-v") {
593
+ if (cmd !== undefined && cmd !== "--help" && cmd !== "-h" && !isVersionFlag(cmd)) {
608
594
  if (rest.includes("--help") || rest.includes("-h")) {
609
595
  const helpText = COMMAND_HELP[cmd];
610
596
  if (helpText !== undefined) {
@@ -651,6 +637,7 @@ export async function run(argv: string[]): Promise<number> {
651
637
  return 0;
652
638
  case "--version":
653
639
  case "-v":
640
+ case "-V":
654
641
  log.info(VERSION);
655
642
  return 0;
656
643
  case "init": {
@@ -6,25 +6,40 @@ import { join } from "node:path";
6
6
  // star one, so the root barrel now hands out the dependency manifest under this
7
7
  // name. `init.ts` already imports these from the subpath; this was the last site
8
8
  // reading them from the root.
9
- import { AGENT_CONTEXT_MANIFEST_PATH, agentContextStaleness, type Manifest } from "@metaobjectsdev/sdk/agent-context";
9
+ import { AGENT_CONTEXT_MANIFEST_PATH, agentContextStaleness, hashAgentContextRoot, resolveAgentContextRoot, type Manifest } from "@metaobjectsdev/sdk/agent-context";
10
+ import { docsRefForVersion } from "@metaobjectsdev/metadata";
10
11
  import { cliVersion } from "./version.js";
11
12
  import { log } from "./log.js";
12
13
 
14
+ /** The installed content's hash, or nothing when the content tree cannot be found (version compare then). */
15
+ export function currentContentHash(): { currentContentHash?: string } {
16
+ try {
17
+ return { currentContentHash: hashAgentContextRoot(resolveAgentContextRoot(), docsRefForVersion(cliVersion())) };
18
+ } catch {
19
+ return {};
20
+ }
21
+ }
22
+
13
23
  /**
14
24
  * Advisory: if a scaffolded MetaObjects agent context predates this CLI version,
15
25
  * print a one-line nudge to re-scaffold. Never throws, never blocks — an absent or
16
26
  * corrupt manifest is silently ignored (this is a reminder, not a gate).
27
+ *
28
+ * Returns the line it printed, so a command with a structured result can carry it
29
+ * there too; undefined when it said nothing.
17
30
  */
18
- export function warnIfAgentContextStale(cwd: string): void {
31
+ export function warnIfAgentContextStale(cwd: string): string | undefined {
19
32
  const p = join(cwd, AGENT_CONTEXT_MANIFEST_PATH);
20
33
  let manifest: Manifest | undefined;
21
34
  if (existsSync(p)) {
22
35
  try {
23
36
  manifest = JSON.parse(readFileSync(p, "utf8")) as Manifest;
24
37
  } catch {
25
- return; // unreadable/corrupt — say nothing
38
+ return undefined; // unreadable/corrupt — say nothing
26
39
  }
27
40
  }
28
- const msg = agentContextStaleness({ manifest, currentVersion: cliVersion() });
29
- if (msg !== null) log.warn(msg);
41
+ const msg = agentContextStaleness({ manifest, currentVersion: cliVersion(), ...currentContentHash() });
42
+ if (msg === null) return undefined;
43
+ log.warn(msg);
44
+ return msg;
30
45
  }
@@ -0,0 +1,74 @@
1
+ // The physical schema `agent/schema.md` describes (the `agent-docs` generator), built by the
2
+ // package that owns it, `migrate-ts`, with its own resolvers handed across. codegen-ts
3
+ // deliberately computes none of it (see agent-schema-input.ts there), so `meta gen` and
4
+ // `meta verify --codegen` build it here and hand it to the run as `RunGenOpts.agentSchema`;
5
+ // the runner calls it only when a declared generator asks.
6
+
7
+ import {
8
+ buildProjectionViews,
9
+ dbEmittingObjects,
10
+ DEFAULT_DIALECT,
11
+ missingDialectMessage,
12
+ } from "@metaobjectsdev/codegen-ts";
13
+ import type { AgentSchemaInput, Dialect, SchemaColumnLike } from "@metaobjectsdev/codegen-ts";
14
+ import { buildExpectedSchemaWithProvenance, columnTypeSql, qualifiedDbName } from "@metaobjectsdev/migrate-ts";
15
+ import type { MetaRoot } from "@metaobjectsdev/metadata";
16
+ import { describeError } from "./error-text.js";
17
+
18
+ /**
19
+ * The schema for `agent/schema.md`, or undefined, after saying why through `warn`, when the
20
+ * page is to be SKIPPED. A skipped page is not a silent pass: `meta verify --codegen` convicts
21
+ * a committed `agent/schema.md` that a fresh run no longer writes.
22
+ */
23
+ export function buildAgentSchemaInput(
24
+ root: MetaRoot,
25
+ configured: Dialect | undefined,
26
+ warn: (message: string) => void,
27
+ ): AgentSchemaInput | undefined {
28
+ // THE RUNNER'S OWN GUARD DECIDES THIS, not a default applied here. `DEFAULT_DIALECT` is
29
+ // inert: `meta gen` refuses a model that emits database code with no dialect declared, so a
30
+ // persisted model with no dialect is not a sqlite project. Documenting it as one would state
31
+ // an answer the toolchain never gave. A model with no persisted object needs no dialect and
32
+ // renders an empty schema page anyway, so the inert default is right for exactly the
33
+ // projects the guard lets through.
34
+ const dbEmitting = dbEmittingObjects(root.objects());
35
+ if (dbEmitting.length > 0 && configured === undefined) {
36
+ warn(`agent/schema.md skipped — ${missingDialectMessage(dbEmitting)}`);
37
+ return undefined;
38
+ }
39
+ // MySQL: MetaObjects does not own the schema, so there is no migrate-built schema to
40
+ // describe. The generated Drizzle tables are the description.
41
+ if (configured === "mysql") {
42
+ warn("agent/schema.md skipped — MetaObjects does not own a MySQL schema (the generated Drizzle tables describe it).");
43
+ return undefined;
44
+ }
45
+ const dialect = configured ?? DEFAULT_DIALECT;
46
+ try {
47
+ const built = buildExpectedSchemaWithProvenance(root, {
48
+ dialect,
49
+ // Views come from codegen-ts (migrate-ts never generates view DDL), exactly as
50
+ // `verify --db` threads them.
51
+ views: buildProjectionViews(root, { dialect }),
52
+ });
53
+ return {
54
+ dialect,
55
+ tables: built.snapshot.tables,
56
+ views: built.snapshot.views,
57
+ provenance: built.provenance,
58
+ // The structural `SchemaColumnLike` is migrate-ts's own ColumnDescriptor, narrowed to
59
+ // what the page reads; the cast hands the full descriptor back to the renderer that
60
+ // produced it.
61
+ columnType: (c: SchemaColumnLike) => columnTypeSql(c as never, dialect),
62
+ qualify: qualifiedDbName,
63
+ };
64
+ } catch (err) {
65
+ // A model the schema builder refuses is a real condition (a primary-key move, a duplicate
66
+ // physical name) that `meta migrate` reports properly. Documentation must not be the
67
+ // command that fails on it, so the schema page is skipped and the other pages still emit.
68
+ warn(
69
+ `agent/schema.md skipped — the expected schema could not be built ` +
70
+ `(${describeError(err)}). Run 'meta migrate' for the full diagnosis.`,
71
+ );
72
+ return undefined;
73
+ }
74
+ }
package/src/lib/allow.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  // both `meta migrate` and `meta verify --db`. Keeping a single copy avoids the
3
3
  // two commands drifting on which `--allow` tokens exist or how a change reads.
4
4
 
5
- import { allowOptionFor, suggestColumnRenames } from "@metaobjectsdev/migrate-ts";
6
- import type { AllowOptions, Change, ColumnRenameSuggestion } from "@metaobjectsdev/migrate-ts";
5
+ import { allowOptionFor, describeSqlType, isViewRecreateOnly, suggestColumnRenames } from "@metaobjectsdev/migrate-ts";
6
+ import type { AllowOptions, Change, ColumnRenameSuggestion, ViewChangeReason } from "@metaobjectsdev/migrate-ts";
7
7
  import type { BlockedEntry } from "./output.js";
8
8
 
9
9
  // Map CLI allow tokens → migrate-ts AllowOptions field names.
@@ -71,7 +71,7 @@ export function describeChange(c: Change): string {
71
71
  const from = c.fromDefault?.value ?? "none";
72
72
  const to = c.toDefault?.value ?? "none";
73
73
  const dflt = from === to && c.fromDefault?.kind === c.toDefault?.kind ? "" : `, default ${from} → ${to}`;
74
- return `${c.table}.${c.column} (${c.from.kind} → ${c.to.kind}${dflt})`;
74
+ return `${c.table}.${c.column} (${describeSqlType(c.from)} → ${describeSqlType(c.to)}${dflt})`;
75
75
  }
76
76
  case "change-column-nullable": return `${c.table}.${c.column} (${c.from ? "NULL" : "NOT NULL"} → ${c.to ? "NULL" : "NOT NULL"})`;
77
77
  case "change-column-default": return `${c.table}.${c.column}`;
@@ -84,9 +84,9 @@ export function describeChange(c: Change): string {
84
84
  return c.restore !== undefined
85
85
  ? `${c.table} check ${c.check} (${c.restore.expression})`
86
86
  : `${c.table} check ${c.check}`;
87
- case "create-view": return c.view.name;
88
- case "replace-view": return c.view.name;
89
- case "drop-view": return c.view;
87
+ case "create-view": return withViewReason(c.view.name, c.reason);
88
+ case "replace-view": return withViewReason(c.view.name, c.reason);
89
+ case "drop-view": return withViewReason(c.view, c.reason);
90
90
  }
91
91
  // Exhaustive: a new Change kind fails to compile here rather than printing a JSON dump
92
92
  // of the change object to a person (which is what a blocked drop-check used to do).
@@ -94,6 +94,57 @@ export function describeChange(c: Change): string {
94
94
  return unhandled;
95
95
  }
96
96
 
97
+ /**
98
+ * A view's name plus WHY the diff planned its change. A bare name was all a view change
99
+ * ever printed, so a view whose definition matched the metadata — dropped and recreated only
100
+ * because the migration alters a table it reads — read exactly like one that differed (D3).
101
+ */
102
+ function withViewReason(name: string, reason: ViewChangeReason | undefined): string {
103
+ return reason === undefined ? name : `${name} (${viewReasonText(reason)})`;
104
+ }
105
+
106
+ function viewReasonText(r: ViewChangeReason): string {
107
+ switch (r.kind) {
108
+ case "missing": return "declared by the metadata, not in the database";
109
+ case "undeclared": return "in the database, declared by no metadata object";
110
+ case "definition": {
111
+ const what = r.compared === "fingerprint"
112
+ ? "definition differs: the database view's fingerprint does not match the metadata's"
113
+ : r.firstDifference !== undefined
114
+ ? `definition text differs: metadata «${r.firstDifference.expected}» vs database «${r.firstDifference.actual}»`
115
+ : "definition text differs";
116
+ return recreatedAround(what, r.tables);
117
+ }
118
+ case "unfingerprinted":
119
+ return recreatedAround(
120
+ "the database view carries no MetaObjects fingerprint, so its definition cannot be compared",
121
+ r.tables,
122
+ );
123
+ case "unchanged":
124
+ return `definition matches the metadata; recreated because the migration alters ${tableList(r.tables)}`;
125
+ }
126
+ }
127
+
128
+ function recreatedAround(what: string, tables: readonly string[] | undefined): string {
129
+ return tables === undefined ? what : `${what}; recreated around the change to ${tableList(tables)}`;
130
+ }
131
+
132
+ function tableList(tables: readonly string[]): string {
133
+ return `${tables.length === 1 ? "table" : "tables"} ${tables.join(", ")}`;
134
+ }
135
+
136
+ /**
137
+ * One line naming the views a migration drops and recreates only because it alters a table
138
+ * they read. Their definitions match the metadata, and the migration needs the pair, but
139
+ * without this line the DROP VIEW / CREATE VIEW in the SQL reads like a view change (D3).
140
+ */
141
+ export function viewRecreateNote(changes: readonly Change[]): string | undefined {
142
+ const views = changes.flatMap((c) => c.kind === "create-view" && isViewRecreateOnly(c) ? [c.view.name] : []);
143
+ if (views.length === 0) return undefined;
144
+ return `${views.length} view(s) match the metadata and are recreated only because the migration ` +
145
+ `alters a table they read: ${views.join(", ")}`;
146
+ }
147
+
97
148
  /** The `--allow` token that unblocks `c`. migrate-ts picks the permission by what blocked the
98
149
  * change (a type change can be blocked by the auto-sequence default it carries), so this only
99
150
  * maps that permission back to its CLI token. */
@@ -202,8 +202,10 @@ function matchLine(line: string, isSql: boolean):
202
202
 
203
203
  const ADVICE: Record<AntiPatternFinding["rule"], string> = {
204
204
  "hand-rolled-aggregate":
205
- "you're computing an aggregate by hand — MetaObjects derives it: declare an " +
206
- "object.projection with an origin.aggregate child and call its generated query",
205
+ "you're computing an aggregate by hand — MetaObjects derives it: for grouped, " +
206
+ "served or reported figures declare an object.report (dimensions + measures, a " +
207
+ "read-only list route); an object.projection with an origin.aggregate child still " +
208
+ "fits a per-row figure on one entity read through its generated query",
207
209
  "money-float":
208
210
  "money handled as a float / hand-rolled minor units — MetaObjects has " +
209
211
  "field.currency (integer minor units stored, formatted client-side)",