@metaobjectsdev/cli 1.0.2 → 1.0.4-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +13 -5
  2. package/dist/src/commands/deps.d.ts +3 -0
  3. package/dist/src/commands/deps.d.ts.map +1 -0
  4. package/dist/src/commands/deps.js +200 -0
  5. package/dist/src/commands/deps.js.map +1 -0
  6. package/dist/src/commands/docs.d.ts.map +1 -1
  7. package/dist/src/commands/docs.js +19 -3
  8. package/dist/src/commands/docs.js.map +1 -1
  9. package/dist/src/commands/eject.d.ts +2 -1
  10. package/dist/src/commands/eject.d.ts.map +1 -1
  11. package/dist/src/commands/eject.js +235 -75
  12. package/dist/src/commands/eject.js.map +1 -1
  13. package/dist/src/commands/gen.d.ts.map +1 -1
  14. package/dist/src/commands/gen.js +173 -30
  15. package/dist/src/commands/gen.js.map +1 -1
  16. package/dist/src/commands/init.d.ts +5 -9
  17. package/dist/src/commands/init.d.ts.map +1 -1
  18. package/dist/src/commands/init.js +92 -402
  19. package/dist/src/commands/init.js.map +1 -1
  20. package/dist/src/commands/migrate.d.ts.map +1 -1
  21. package/dist/src/commands/migrate.js +24 -19
  22. package/dist/src/commands/migrate.js.map +1 -1
  23. package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
  24. package/dist/src/commands/prompt-snapshot.js +2 -1
  25. package/dist/src/commands/prompt-snapshot.js.map +1 -1
  26. package/dist/src/commands/verify.d.ts.map +1 -1
  27. package/dist/src/commands/verify.js +186 -29
  28. package/dist/src/commands/verify.js.map +1 -1
  29. package/dist/src/index.d.ts.map +1 -1
  30. package/dist/src/index.js +97 -15
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/lib/advisory.d.ts +6 -0
  33. package/dist/src/lib/advisory.d.ts.map +1 -1
  34. package/dist/src/lib/advisory.js +10 -0
  35. package/dist/src/lib/advisory.js.map +1 -1
  36. package/dist/src/lib/agent-context-staleness.d.ts.map +1 -1
  37. package/dist/src/lib/agent-context-staleness.js +7 -1
  38. package/dist/src/lib/agent-context-staleness.js.map +1 -1
  39. package/dist/src/lib/args.d.ts +75 -3
  40. package/dist/src/lib/args.d.ts.map +1 -1
  41. package/dist/src/lib/args.js +54 -5
  42. package/dist/src/lib/args.js.map +1 -1
  43. package/dist/src/lib/catalog-listing.d.ts +92 -0
  44. package/dist/src/lib/catalog-listing.d.ts.map +1 -0
  45. package/dist/src/lib/catalog-listing.js +268 -0
  46. package/dist/src/lib/catalog-listing.js.map +1 -0
  47. package/dist/src/lib/catalog.d.ts +41 -0
  48. package/dist/src/lib/catalog.d.ts.map +1 -0
  49. package/dist/src/lib/catalog.js +89 -0
  50. package/dist/src/lib/catalog.js.map +1 -0
  51. package/dist/src/lib/collection-load-options.d.ts +13 -0
  52. package/dist/src/lib/collection-load-options.d.ts.map +1 -0
  53. package/dist/src/lib/collection-load-options.js +25 -0
  54. package/dist/src/lib/collection-load-options.js.map +1 -0
  55. package/dist/src/lib/dependency-sync.d.ts +170 -0
  56. package/dist/src/lib/dependency-sync.d.ts.map +1 -0
  57. package/dist/src/lib/dependency-sync.js +457 -0
  58. package/dist/src/lib/dependency-sync.js.map +1 -0
  59. package/dist/src/lib/install-set.d.ts +31 -0
  60. package/dist/src/lib/install-set.d.ts.map +1 -0
  61. package/dist/src/lib/install-set.js +103 -0
  62. package/dist/src/lib/install-set.js.map +1 -0
  63. package/dist/src/lib/library-eject.d.ts +79 -0
  64. package/dist/src/lib/library-eject.d.ts.map +1 -0
  65. package/dist/src/lib/library-eject.js +241 -0
  66. package/dist/src/lib/library-eject.js.map +1 -0
  67. package/dist/src/lib/library-listing.d.ts +63 -0
  68. package/dist/src/lib/library-listing.d.ts.map +1 -0
  69. package/dist/src/lib/library-listing.js +178 -0
  70. package/dist/src/lib/library-listing.js.map +1 -0
  71. package/dist/src/lib/library-prefix-advisory.d.ts +17 -0
  72. package/dist/src/lib/library-prefix-advisory.d.ts.map +1 -0
  73. package/dist/src/lib/library-prefix-advisory.js +67 -0
  74. package/dist/src/lib/library-prefix-advisory.js.map +1 -0
  75. package/dist/src/lib/load-metaobjects-config.d.ts +1 -2
  76. package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
  77. package/dist/src/lib/load-metaobjects-config.js +11 -17
  78. package/dist/src/lib/load-metaobjects-config.js.map +1 -1
  79. package/dist/src/lib/migrate-scope.d.ts +32 -1
  80. package/dist/src/lib/migrate-scope.d.ts.map +1 -1
  81. package/dist/src/lib/migrate-scope.js +51 -3
  82. package/dist/src/lib/migrate-scope.js.map +1 -1
  83. package/dist/src/lib/overlay-lint.d.ts +37 -0
  84. package/dist/src/lib/overlay-lint.d.ts.map +1 -0
  85. package/dist/src/lib/overlay-lint.js +102 -0
  86. package/dist/src/lib/overlay-lint.js.map +1 -0
  87. package/dist/src/lib/requirement-check.d.ts +26 -3
  88. package/dist/src/lib/requirement-check.d.ts.map +1 -1
  89. package/dist/src/lib/requirement-check.js +71 -13
  90. package/dist/src/lib/requirement-check.js.map +1 -1
  91. package/package.json +10 -10
  92. package/src/commands/deps.ts +231 -0
  93. package/src/commands/docs.ts +19 -3
  94. package/src/commands/eject.ts +304 -90
  95. package/src/commands/gen.ts +189 -29
  96. package/src/commands/init.ts +95 -407
  97. package/src/commands/migrate.ts +26 -16
  98. package/src/commands/prompt-snapshot.ts +2 -1
  99. package/src/commands/verify.ts +213 -30
  100. package/src/index.ts +97 -15
  101. package/src/lib/advisory.ts +13 -0
  102. package/src/lib/agent-context-staleness.ts +7 -1
  103. package/src/lib/args.ts +118 -8
  104. package/src/lib/catalog-listing.ts +369 -0
  105. package/src/lib/catalog.ts +107 -0
  106. package/src/lib/collection-load-options.ts +42 -0
  107. package/src/lib/dependency-sync.ts +656 -0
  108. package/src/lib/install-set.ts +116 -0
  109. package/src/lib/library-eject.ts +298 -0
  110. package/src/lib/library-listing.ts +258 -0
  111. package/src/lib/library-prefix-advisory.ts +78 -0
  112. package/src/lib/load-metaobjects-config.ts +15 -21
  113. package/src/lib/migrate-scope.ts +59 -4
  114. package/src/lib/overlay-lint.ts +129 -0
  115. package/src/lib/requirement-check.ts +100 -14
package/src/index.ts CHANGED
@@ -17,7 +17,7 @@ const VERSION = cliVersion();
17
17
  * human text. It is named ONCE and used by both the warning below and the help
18
18
  * text above, so the two cannot drift apart.
19
19
  */
20
- const FORMAT_AWARE_COMMANDS: readonly string[] = ["gen", "verify", "migrate", "types"];
20
+ const FORMAT_AWARE_COMMANDS: readonly string[] = ["gen", "verify", "migrate", "types", "deps", "eject"];
21
21
 
22
22
  const HELP_TEXT = `meta — MetaObjects CLI (v${VERSION})
23
23
 
@@ -32,10 +32,13 @@ COMMANDS:
32
32
  gen [<entity>...] Codegen TS targets from your declared metadata
33
33
  eject <generator> Copy a reference generator into codegen/generators/ to own it (any time after init)
34
34
  eject --list List every ejectable generator name, grouped by package
35
+ deps sync [<name>...] Resolve declared dependencies (path transport): sync the committed
36
+ snapshot + sha256 lock (.metaobjects/deps/, .metaobjects/deps.lock.json)
37
+ deps list One line per locked dependency: name, version, hash, node/package summary
35
38
  types [query] Search the metadata vocabulary (types, subtypes, @attrs) by name or description
36
39
  export Flatten loaded metadata to one canonical JSON artifact
37
40
  docs [<project-root>] --out <dir> Generate neutral metadata documentation (entity + template pages; --site for HTML site)
38
- verify Drift gate — subverbs: --templates / --db / --codegen / --docs (bare = --templates)
41
+ verify Drift gate — subverbs: --templates / --db / --codegen / --docs / --deps (bare = --templates)
39
42
  upgrade Rewrite retired metadata vocabulary (previews; --apply writes)
40
43
  prompt-snapshot Snapshot rendered template.* output; --check gates drift
41
44
  migrate Diff metadata vs live DB; emit migration SQL files
@@ -55,8 +58,14 @@ GLOBAL OPTIONS:
55
58
 
56
59
  GEN FLAGS:
57
60
  --dry-run Compute and print, don't write
58
- --list Print the generator registry (name, tier, options) and exit —
59
- no config or metadata required
61
+ --list Print the generator CATALOG (name, layer, tier, what it emits,
62
+ what to install) and exit. Codegen is opt-in: nothing runs until
63
+ you wire it, and this is where you choose. No config or metadata
64
+ required — add --format json for the same catalog, machine-readable.
65
+ --probe With --list: construct every catalog generator and dry-run it
66
+ against YOUR model, reporting how many files each would emit.
67
+ A count per generator beats any category label — and it
68
+ cannot go stale, because it runs the generators. Needs a project.
60
69
  --baseline <default|adopt|fresh>
61
70
  First-time-on-existing-file behavior. Default: refuse a file that
62
71
  cannot be proved to be generated output. "adopt" records the files
@@ -94,6 +103,10 @@ VERIFY FLAGS (ADR-0021 D2 — explicit subverbs; combine any; exit 1 on ANY drif
94
103
  run would emit; never reports an extra file, because docs.outDir
95
104
  holds hand-written documentation MetaObjects did not write.
96
105
  Needs metaobjects.config.ts; exit 2 if absent.
106
+ --deps Dependency drift — re-resolve each declared dependency and compare
107
+ its installed artifact's hash against .metaobjects/deps.lock.json
108
+ (the same check 'meta deps check' runs). Never part of the bare-verify
109
+ default — it needs the publisher reachable, which CI may not have.
97
110
  --db <url> Schema drift — live DB URL enables the schema-drift gate.
98
111
  Supports: file:, libsql:, postgres:, postgresql:. Omit to skip.
99
112
  D1 has no URL — use --dialect d1 / --d1 <binding> instead.
@@ -108,6 +121,8 @@ VERIFY FLAGS (ADR-0021 D2 — explicit subverbs; combine any; exit 1 on ANY drif
108
121
  --no-antipatterns Suppress the advisory "you hand-rolled what MetaObjects can
109
122
  model" pass (aggregate/currency/enum hints; warnings only)
110
123
  --no-requirement-lint Suppress the advisory requirement AUTHORING lint (not the gate)
124
+ --no-overlay-lint Suppress the advisory overlay-redeclaration AUTHORING lint
125
+ (never a gate — this lint can't fail the build)
111
126
  --limit <n|all> How many advisory lines TEXT output prints per section before
112
127
  it truncates (default 20). Never applies to --format
113
128
  toon/json, which carry every finding and every diagnostic.
@@ -147,8 +162,14 @@ USAGE:
147
162
 
148
163
  FLAGS:
149
164
  --dry-run Compute and print, don't write
150
- --list Print the generator registry (name, tier, options) and exit —
151
- no config or metadata required
165
+ --list Print the generator CATALOG (name, layer, tier, what it emits,
166
+ what to install) and exit. Codegen is opt-in: nothing runs until
167
+ you wire it, and this is where you choose. No config or metadata
168
+ required — add --format json for the same catalog, machine-readable.
169
+ --probe With --list: construct every catalog generator and dry-run it
170
+ against YOUR model, reporting how many files each would emit.
171
+ A count per generator beats any category label — and it cannot go
172
+ stale, because it runs the generators. Needs a project.
152
173
  --baseline <default|adopt|fresh>
153
174
  First-time-on-existing-file behavior. Default: refuse a file that
154
175
  cannot be proved to be generated output. "adopt" records the files
@@ -174,23 +195,65 @@ limit on the machine-readable result.
174
195
 
175
196
  NOTE: outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts
176
197
  `,
177
- eject: `meta eject — copy a reference generator into your repo so you own it
198
+ eject: `meta eject — copy reference generators into your repo so you own them
178
199
 
179
200
  USAGE:
180
- meta eject <name> Copy generator <name> into codegen/generators/<name>.ts
201
+ meta eject <name>... Copy each generator into codegen/generators/<name>.ts
181
202
  meta eject --list List every ejectable generator name, grouped by package
182
203
 
204
+ Codegen is opt-in — \`meta init\` wires nothing — so this is the door every generator
205
+ you run comes through. Take several at once: the install set is CONSOLIDATED, so
206
+ \`meta eject form hooks grid\` prints one install line, not three. An unknown name
207
+ refuses the whole call and copies nothing.
208
+
183
209
  FLAGS:
184
210
  --list List ejectable generators instead of copying one
185
211
  --force Overwrite an already-ejected file (default: never clobber)
212
+ --format <toon|json|text> Output format (global flag). The structured form carries
213
+ each file's wire lines, the consolidated install set and the
214
+ config keys the ejected generators read.
186
215
  --help, -h Print this help
187
216
 
188
- \`meta init\` copies five generators (entity, queries, routes, barrel, names) into
189
- codegen/generators/ automatically (ADR-0034 scaffold-and-own). \`meta eject\` is
190
- the same operation for ANY generator — one you skipped at init time, a UI-tier
191
- generator like form/hooks/grid, or one a package gains later. It prints the
192
- import line to paste into metaobjects.config.ts, and it never overwrites a
193
- file you already own unless you pass --force.
217
+ \`meta init\` copies NO generators — codegen is opt-in, and codegen/generators/
218
+ starts empty on purpose. Everything you run comes through here (ADR-0034
219
+ scaffold-and-own): the file is YOURS to edit, and \`meta gen\` runs your copy, not
220
+ the package's. It prints the import line and the entry to add in
221
+ metaobjects.config.ts, and never overwrites a file you already own without --force.
222
+
223
+ Find names with \`meta gen --list\` — add --probe to see how many files each would
224
+ emit for your model.
225
+ `,
226
+ deps: `meta deps — sync a declared metadata dependency's committed snapshot
227
+
228
+ USAGE:
229
+ meta deps sync [<name>...] Resolve each declared dependency (path transport only in
230
+ this release — npm/python refuse by name), validate its
231
+ manifest + artifact, copy it into .metaobjects/deps/<name>/,
232
+ and pin it in .metaobjects/deps.lock.json. Naming one or more
233
+ <name>s syncs only those; bare 'sync' syncs everything
234
+ declared. A dependency removed from config is PRUNED from
235
+ the lock and its snapshot dir deleted, every run.
236
+ meta deps list One line per locked dependency: name, version, hash8,
237
+ node count, packages.
238
+ meta deps check Re-resolve each declared dependency (path transport, same
239
+ as sync steps 1-2) and compare its INSTALLED artifact's hash
240
+ against .metaobjects/deps.lock.json. Read-only — never
241
+ touches the lock or the snapshot. Exits 1 if any dependency
242
+ has drifted or cannot be resolved (ERR_DEPENDENCY_UPSTREAM_DRIFT).
243
+
244
+ FLAGS:
245
+ --dry-run Plan and report; write nothing (sync only)
246
+ --format <toon|json|text> Output format (global flag; default toon off-TTY)
247
+ --help, -h Print this help
248
+
249
+ Only the "path" transport resolves in this release — a "npm"/"python" dependency spec is
250
+ valid config (reserved for a future toolchain) but 'meta deps sync' refuses it by name:
251
+ "transport \`npm\` is not supported by this toolchain yet; use \`path\`".
252
+
253
+ The lock is the ONLY thing 'meta deps sync' writes to besides the snapshot directory —
254
+ sync never touches your own metadata files. 'meta deps check' compares the lock against
255
+ what is installed right now (the same check 'meta verify --deps' runs); 'meta deps sync'
256
+ only ever compares against what config DECLARES.
194
257
  `,
195
258
  verify: `meta verify — drift gate (templates / DB schema / codegen / migration replay)
196
259
 
@@ -210,6 +273,12 @@ FLAGS:
210
273
  a namespace MetaObjects owns); the count of exempt pages is
211
274
  reported either way, and a run where every page is ignored is
212
275
  refused rather than reported clean.
276
+ --deps Dependency drift — re-resolve each declared dependency exactly as
277
+ 'meta deps check' does and compare its installed artifact's hash
278
+ against .metaobjects/deps.lock.json. Exits 1 if any dependency has
279
+ drifted or cannot be resolved (ERR_DEPENDENCY_UPSTREAM_DRIFT). NEVER
280
+ part of the bare-verify default — it needs the publisher reachable,
281
+ which CI may not have.
213
282
  --db <url> Schema drift — live DB URL enables the schema-drift gate.
214
283
  Supports: file:, libsql:, postgres:, postgresql:
215
284
  D1 has no URL — use --dialect d1 / --d1 <binding> instead.
@@ -237,6 +306,8 @@ FLAGS:
237
306
  migrating a model onto a registered provider.
238
307
  --no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
239
308
  --no-requirement-lint Suppress the advisory requirement AUTHORING lint (not the gate)
309
+ --no-overlay-lint Suppress the advisory overlay-redeclaration AUTHORING lint —
310
+ never a gate; this lint can't fail the build
240
311
  --limit <n|all> Advisory lines TEXT output prints PER SECTION before truncating
241
312
  (default 20; per-section so the authoring lint can never push
242
313
  the gate's own warnings off the end)
@@ -258,6 +329,13 @@ lint in its own section: names that are not addressable, prose slots holding one
258
329
  sentence twice, content written where no surface reads it. Warnings only — it can
259
330
  never fail the build. Opt out with --no-requirement-lint or META_NO_REQUIREMENT_LINT=1.
260
331
  The requirements GATE itself (dangling refs, link floor, levels) always runs.
332
+
333
+ verify also prints an ADVISORY overlay authoring lint, in its own section: a
334
+ top-level declaration redeclared in two or more files where more than one
335
+ redeclaration lacks 'overlay: true' — today's default merge rule reuses it
336
+ silently, but the same unflagged redeclaration silently becomes a NEW object
337
+ the day the target is renamed or removed upstream. Warnings only — it can
338
+ never fail the build. Opt out with --no-overlay-lint or META_NO_OVERLAY_LINT=1.
261
339
  `,
262
340
  export: `meta export — flatten loaded metadata to one canonical JSON artifact
263
341
 
@@ -528,7 +606,11 @@ export async function run(argv: string[]): Promise<number> {
528
606
  }
529
607
  case "eject": {
530
608
  const { ejectCommand } = await import("./commands/eject.js");
531
- return ejectCommand(rest, cwd);
609
+ return ejectCommand(rest, cwd, fmt);
610
+ }
611
+ case "deps": {
612
+ const { depsCommand } = await import("./commands/deps.js");
613
+ return depsCommand(rest, cwd, fmt);
532
614
  }
533
615
  case "export": {
534
616
  const { exportCommand } = await import("./commands/export.js");
@@ -150,6 +150,19 @@ export function removedPropRows(findings: readonly RemovedPropFinding[]): Adviso
150
150
  }));
151
151
  }
152
152
 
153
+ /** FR-043 §3.5 findings in the same row shape. */
154
+ export function libraryPrefixRows(
155
+ findings: readonly { file: string; fqn: string; message: string }[],
156
+ ): AdvisoryFindingRow[] {
157
+ return findings.map((f) => ({
158
+ file: f.file,
159
+ line: 0,
160
+ rule: "library-prefix-unprovenanced",
161
+ construct: f.fqn,
162
+ message: f.message,
163
+ }));
164
+ }
165
+
153
166
  // ---------------------------------------------------------------------------
154
167
  // text output
155
168
  // ---------------------------------------------------------------------------
@@ -1,6 +1,12 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { AGENT_CONTEXT_MANIFEST_PATH, agentContextStaleness, type Manifest } from "@metaobjectsdev/sdk";
3
+ // The agent-context subpath, not the root barrel: `@metaobjectsdev/sdk` exports TWO
4
+ // types named `Manifest` — this one (the scaffolded agent context) via `export *`,
5
+ // and FR-023's `metaobjects.pkg.json` explicitly. An explicit export shadows a
6
+ // star one, so the root barrel now hands out the dependency manifest under this
7
+ // name. `init.ts` already imports these from the subpath; this was the last site
8
+ // reading them from the root.
9
+ import { AGENT_CONTEXT_MANIFEST_PATH, agentContextStaleness, type Manifest } from "@metaobjectsdev/sdk/agent-context";
4
10
  import { cliVersion } from "./version.js";
5
11
  import { log } from "./log.js";
6
12
 
package/src/lib/args.ts CHANGED
@@ -107,6 +107,12 @@ export interface GenFlags {
107
107
  /** ADR-0021 D3 — print the stable-name generator registry and exit without
108
108
  * running codegen. */
109
109
  list: boolean;
110
+ /**
111
+ * `--list` only: construct every catalog generator and dry-run it against THIS
112
+ * project's model, reporting a real file count per generator. Needs a project;
113
+ * plain `--list` deliberately does not (it describes the installed engine).
114
+ */
115
+ probe: boolean;
110
116
  /** Suppress the advisory anti-pattern (verify-as-teacher) pass. */
111
117
  noAntipatterns: boolean;
112
118
  /**
@@ -122,6 +128,7 @@ export const GEN_OPTIONS = {
122
128
  "dry-run": { type: "boolean", default: false },
123
129
  "baseline": { type: "string" },
124
130
  "list": { type: "boolean", default: false },
131
+ "probe": { type: "boolean", default: false },
125
132
  "no-antipatterns": { type: "boolean", default: false },
126
133
  "limit": { type: "string" },
127
134
  } as const;
@@ -149,6 +156,7 @@ export function parseGenArgs(argv: string[]): GenFlags {
149
156
  entities: positionals,
150
157
  baseline: (baselineRaw as "default" | "fresh" | "adopt" | undefined) ?? "default",
151
158
  list: !!values.list,
159
+ probe: !!values.probe,
152
160
  noAntipatterns: !!values["no-antipatterns"],
153
161
  // Throws on a bad value; the command layer reports it and exits 2, exactly as
154
162
  // it does for --baseline above.
@@ -296,7 +304,17 @@ export interface VerifyFlags {
296
304
  * read as that flag's opposite rather than as a replay depth.
297
305
  */
298
306
  replaySnapshot: boolean;
299
- /** Whether ANY explicit subverb flag (--templates/--db/--codegen/--docs/--replay*) was passed. */
307
+ /**
308
+ * Dependency drift (Task 15, FR-023) — re-resolve each declared dependency and
309
+ * compare its installed artifact's hash against `.metaobjects/deps.lock.json`
310
+ * (the same comparison `meta deps check` runs). Deliberately NEVER part of the
311
+ * bare-verify default, unlike --templates: it needs the publisher reachable,
312
+ * which CI may not have. It is still counted in `anyExplicit`, same as
313
+ * --codegen/--docs, so a lone `verify --deps` does not ALSO run the template
314
+ * gate.
315
+ */
316
+ deps: boolean;
317
+ /** Whether ANY explicit subverb flag (--templates/--db/--codegen/--docs/--deps/--replay*) was passed. */
300
318
  anyExplicit: boolean;
301
319
  /** Suppress the advisory anti-pattern (verify-as-teacher) pass. */
302
320
  noAntipatterns: boolean;
@@ -308,6 +326,16 @@ export interface VerifyFlags {
308
326
  * the half that CAN fail a build switched on. Same shape as --no-antipatterns.
309
327
  */
310
328
  noRequirementLint: boolean;
329
+ /**
330
+ * Suppress the advisory overlay AUTHORING lint (FR-023 §11.1 item 4) — the
331
+ * finding that an unflagged cross-file redeclaration works today only
332
+ * because the parser's default merge rule reuses the existing node by
333
+ * (type, resolutionKey); it silently becomes a NEW object the day the
334
+ * target is renamed or removed upstream. Same shape as
335
+ * --no-requirement-lint: mutes the advisory half only, never a gate (this
336
+ * lint has no gate half at all — it can never fail the build).
337
+ */
338
+ noOverlayLint: boolean;
311
339
  /**
312
340
  * ADR-0023 strict-attr load opt-OUT (#96). `verify` is strict-by-default — an
313
341
  * undeclared/typo'd own `@attr` fails verify (ERR_UNKNOWN_ATTR). `--lax`
@@ -342,10 +370,12 @@ export const VERIFY_OPTIONS = {
342
370
  templates: { type: "boolean", default: false },
343
371
  codegen: { type: "boolean", default: false },
344
372
  docs: { type: "boolean", default: false },
373
+ deps: { type: "boolean", default: false },
345
374
  replay: { type: "boolean", default: false },
346
375
  "replay-snapshot": { type: "boolean", default: false },
347
376
  "no-antipatterns": { type: "boolean", default: false },
348
377
  "no-requirement-lint": { type: "boolean", default: false },
378
+ "no-overlay-lint": { type: "boolean", default: false },
349
379
  lax: { type: "boolean", default: false },
350
380
  "d1": { type: "string" },
351
381
  "remote": { type: "boolean", default: false },
@@ -397,15 +427,17 @@ export function parseVerifyArgs(argv: string[]): VerifyFlags {
397
427
  const templates = !!values.templates;
398
428
  const codegen = !!values.codegen;
399
429
  const docs = !!values.docs;
430
+ const deps = !!values.deps;
400
431
  const replay = !!values.replay;
401
432
  const replaySnapshot = !!values["replay-snapshot"];
402
433
  // --db is itself an explicit subverb selector: passing a connection URL means
403
434
  // "run the schema-drift mode". So is `--dialect d1` (D1 has no --db connection
404
435
  // URL — see the `d1` field doc above). The replay flags are subverbs too, and
405
436
  // must be listed here or `meta verify --replay` would ALSO run the template gate
406
- // as the bare-verify default.
437
+ // as the bare-verify default. --deps joins the same list for the same reason —
438
+ // it must NOT also be part of that default (see the VerifyFlags doc on `deps`).
407
439
  const anyExplicit =
408
- templates || codegen || docs || values.db !== undefined || dialect === "d1" || replay || replaySnapshot;
440
+ templates || codegen || docs || deps || values.db !== undefined || dialect === "d1" || replay || replaySnapshot;
409
441
 
410
442
  return {
411
443
  prompts: values.prompts,
@@ -416,11 +448,13 @@ export function parseVerifyArgs(argv: string[]): VerifyFlags {
416
448
  templates,
417
449
  codegen,
418
450
  docs,
451
+ deps,
419
452
  replay,
420
453
  replaySnapshot,
421
454
  anyExplicit,
422
455
  noAntipatterns: !!values["no-antipatterns"],
423
456
  noRequirementLint: !!values["no-requirement-lint"],
457
+ noOverlayLint: !!values["no-overlay-lint"],
424
458
  lax: !!values.lax,
425
459
  d1: values.d1 as string | undefined,
426
460
  remote: !!values.remote,
@@ -576,8 +610,14 @@ export function parseMigrateArgs(argv: string[]): MigrateFlags {
576
610
  // ---------------------------------------------------------------------------
577
611
 
578
612
  export interface EjectFlags {
579
- /** The generator name to eject; undefined when only --list was given. */
580
- name: string | undefined;
613
+ /**
614
+ * The generator names to eject, in the order given. Empty when only --list was given.
615
+ *
616
+ * MANY, not one: choosing a client tier means `form hooks grid`, and three separate
617
+ * invocations produce three separate install lines for the same package. The
618
+ * consolidated install set is the point of taking them together.
619
+ */
620
+ names: string[];
581
621
  list: boolean;
582
622
  /** Overwrite an already-ejected file; default false — eject never clobbers. */
583
623
  force: boolean;
@@ -597,13 +637,83 @@ export function parseEjectArgs(argv: string[]): EjectFlags {
597
637
  allowPositionals: true,
598
638
  });
599
639
 
600
- if (positionals.length > 1) {
601
- throw new Error(`meta eject takes at most one generator name; got: ${positionals.join(", ")}`);
640
+ // `Set.add` returns the SET, which is always truthy — so the tempting
641
+ // `positionals.filter((n) => !seen.add(n))` never reports anything.
642
+ const seen = new Set<string>();
643
+ const duplicates: string[] = [];
644
+ for (const n of positionals) {
645
+ if (seen.has(n)) duplicates.push(n);
646
+ else seen.add(n);
647
+ }
648
+ if (duplicates.length > 0) {
649
+ throw new Error(
650
+ `meta eject: repeated generator name(s): ${[...new Set(duplicates)].join(", ")}`,
651
+ );
602
652
  }
603
653
 
604
654
  return {
605
- name: positionals[0],
655
+ names: positionals,
606
656
  list: !!values.list,
607
657
  force: !!values.force,
608
658
  };
609
659
  }
660
+
661
+ // ---------------------------------------------------------------------------
662
+ // deps flags — FR-023 Phase 1a Task 14
663
+ // ---------------------------------------------------------------------------
664
+
665
+ /** `meta deps`'s subcommand. `sync` and `list` are this task's; `check` is
666
+ * parsed (so the grammar and its usage errors are stable now) but not yet
667
+ * implemented — Task 15 wires its behavior. */
668
+ export type DepsSubverb = "sync" | "check" | "list";
669
+
670
+ const DEPS_SUBVERBS: readonly DepsSubverb[] = ["sync", "check", "list"];
671
+
672
+ export interface DepsFlags {
673
+ subverb: DepsSubverb;
674
+ /** Dependency names to narrow to — `sync`'s `[<name>…]`. Empty means "all
675
+ * declared dependencies." Accepted (and ignored) by every subverb rather
676
+ * than rejected outright: the positional grammar is one shape for all
677
+ * three, and only `sync` gives the list meaning today. */
678
+ names: string[];
679
+ /** `sync` only: plan and report, write nothing. */
680
+ dryRun: boolean;
681
+ }
682
+
683
+ /** The flag table `parseDepsArgs` parses. Exported so the help text can be
684
+ * gated against it. Deliberately carries no "format" key: `--format` is a
685
+ * GLOBAL flag `index.ts` strips from argv before any command's parser ever
686
+ * runs (see `MIGRATE_OPTIONS`'s "--migration-format, not --format" note
687
+ * above) — `meta deps` becomes format-aware by index.ts adding "deps" to
688
+ * `FORMAT_AWARE_COMMANDS` and passing the resolved `fmt` through, exactly as
689
+ * gen/verify/migrate do, not by re-declaring the flag here. */
690
+ export const DEPS_OPTIONS = {
691
+ "dry-run": { type: "boolean", default: false },
692
+ } as const;
693
+
694
+ export function parseDepsArgs(argv: string[]): DepsFlags {
695
+ const { values, positionals } = parseArgs({
696
+ args: argv,
697
+ options: DEPS_OPTIONS,
698
+ strict: true,
699
+ allowPositionals: true,
700
+ });
701
+
702
+ const [subverbRaw, ...names] = positionals;
703
+ if (subverbRaw === undefined) {
704
+ throw new Error(
705
+ "meta deps requires a subcommand: sync | check | list. Try `meta deps sync`.",
706
+ );
707
+ }
708
+ if (!DEPS_SUBVERBS.includes(subverbRaw as DepsSubverb)) {
709
+ throw new Error(
710
+ `meta deps: unknown subcommand "${subverbRaw}"; expected one of: ${DEPS_SUBVERBS.join(", ")}.`,
711
+ );
712
+ }
713
+
714
+ return {
715
+ subverb: subverbRaw as DepsSubverb,
716
+ names,
717
+ dryRun: !!values["dry-run"],
718
+ };
719
+ }