@metaobjectsdev/cli 1.0.0-rc.5 → 1.0.0-rc.7

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 (121) hide show
  1. package/dist/src/commands/docs.d.ts.map +1 -1
  2. package/dist/src/commands/docs.js +41 -9
  3. package/dist/src/commands/docs.js.map +1 -1
  4. package/dist/src/commands/gen.d.ts +5 -0
  5. package/dist/src/commands/gen.d.ts.map +1 -1
  6. package/dist/src/commands/gen.js +12 -1
  7. package/dist/src/commands/gen.js.map +1 -1
  8. package/dist/src/commands/init.d.ts.map +1 -1
  9. package/dist/src/commands/init.js +27 -3
  10. package/dist/src/commands/init.js.map +1 -1
  11. package/dist/src/commands/migrate.d.ts +5 -0
  12. package/dist/src/commands/migrate.d.ts.map +1 -1
  13. package/dist/src/commands/migrate.js +10 -4
  14. package/dist/src/commands/migrate.js.map +1 -1
  15. package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
  16. package/dist/src/commands/prompt-snapshot.js +2 -1
  17. package/dist/src/commands/prompt-snapshot.js.map +1 -1
  18. package/dist/src/commands/types.d.ts.map +1 -1
  19. package/dist/src/commands/types.js +12 -3
  20. package/dist/src/commands/types.js.map +1 -1
  21. package/dist/src/commands/upgrade.d.ts.map +1 -1
  22. package/dist/src/commands/upgrade.js +62 -4
  23. package/dist/src/commands/upgrade.js.map +1 -1
  24. package/dist/src/commands/verify.d.ts.map +1 -1
  25. package/dist/src/commands/verify.js +69 -21
  26. package/dist/src/commands/verify.js.map +1 -1
  27. package/dist/src/index.d.ts.map +1 -1
  28. package/dist/src/index.js +37 -18
  29. package/dist/src/index.js.map +1 -1
  30. package/dist/src/lib/advisory.d.ts +6 -0
  31. package/dist/src/lib/advisory.d.ts.map +1 -1
  32. package/dist/src/lib/advisory.js +13 -0
  33. package/dist/src/lib/advisory.js.map +1 -1
  34. package/dist/src/lib/anti-patterns.d.ts.map +1 -1
  35. package/dist/src/lib/anti-patterns.js +8 -4
  36. package/dist/src/lib/anti-patterns.js.map +1 -1
  37. package/dist/src/lib/args.d.ts +23 -12
  38. package/dist/src/lib/args.d.ts.map +1 -1
  39. package/dist/src/lib/args.js +33 -22
  40. package/dist/src/lib/args.js.map +1 -1
  41. package/dist/src/lib/authored-source.d.ts +22 -0
  42. package/dist/src/lib/authored-source.d.ts.map +1 -0
  43. package/dist/src/lib/authored-source.js +46 -0
  44. package/dist/src/lib/authored-source.js.map +1 -0
  45. package/dist/src/lib/base-url-advisory.d.ts +37 -0
  46. package/dist/src/lib/base-url-advisory.d.ts.map +1 -0
  47. package/dist/src/lib/base-url-advisory.js +168 -0
  48. package/dist/src/lib/base-url-advisory.js.map +1 -0
  49. package/dist/src/lib/codegen-drift.d.ts +8 -0
  50. package/dist/src/lib/codegen-drift.d.ts.map +1 -1
  51. package/dist/src/lib/codegen-drift.js +43 -10
  52. package/dist/src/lib/codegen-drift.js.map +1 -1
  53. package/dist/src/lib/detect-stack.d.ts +17 -0
  54. package/dist/src/lib/detect-stack.d.ts.map +1 -1
  55. package/dist/src/lib/detect-stack.js +64 -2
  56. package/dist/src/lib/detect-stack.js.map +1 -1
  57. package/dist/src/lib/docs-drift.d.ts.map +1 -1
  58. package/dist/src/lib/docs-drift.js +16 -7
  59. package/dist/src/lib/docs-drift.js.map +1 -1
  60. package/dist/src/lib/git-ignore.d.ts +19 -0
  61. package/dist/src/lib/git-ignore.d.ts.map +1 -1
  62. package/dist/src/lib/git-ignore.js +46 -3
  63. package/dist/src/lib/git-ignore.js.map +1 -1
  64. package/dist/src/lib/ignored-scaffold-check.d.ts +12 -0
  65. package/dist/src/lib/ignored-scaffold-check.d.ts.map +1 -0
  66. package/dist/src/lib/ignored-scaffold-check.js +75 -0
  67. package/dist/src/lib/ignored-scaffold-check.js.map +1 -0
  68. package/dist/src/lib/load-error.d.ts +46 -0
  69. package/dist/src/lib/load-error.d.ts.map +1 -0
  70. package/dist/src/lib/load-error.js +51 -0
  71. package/dist/src/lib/load-error.js.map +1 -0
  72. package/dist/src/lib/load-metaobjects-config.d.ts +13 -0
  73. package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
  74. package/dist/src/lib/load-metaobjects-config.js +54 -5
  75. package/dist/src/lib/load-metaobjects-config.js.map +1 -1
  76. package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -1
  77. package/dist/src/lib/manifest-ignored-check.js +19 -6
  78. package/dist/src/lib/manifest-ignored-check.js.map +1 -1
  79. package/dist/src/lib/output.d.ts +13 -1
  80. package/dist/src/lib/output.d.ts.map +1 -1
  81. package/dist/src/lib/output.js +26 -8
  82. package/dist/src/lib/output.js.map +1 -1
  83. package/dist/src/lib/rel-posix.d.ts +5 -0
  84. package/dist/src/lib/rel-posix.d.ts.map +1 -0
  85. package/dist/src/lib/rel-posix.js +23 -0
  86. package/dist/src/lib/rel-posix.js.map +1 -0
  87. package/dist/src/lib/replay-remedy.d.ts +2 -0
  88. package/dist/src/lib/replay-remedy.d.ts.map +1 -0
  89. package/dist/src/lib/replay-remedy.js +69 -0
  90. package/dist/src/lib/replay-remedy.js.map +1 -0
  91. package/dist/src/lib/wrangler.d.ts +17 -2
  92. package/dist/src/lib/wrangler.d.ts.map +1 -1
  93. package/dist/src/lib/wrangler.js +51 -2
  94. package/dist/src/lib/wrangler.js.map +1 -1
  95. package/package.json +10 -10
  96. package/src/commands/docs.ts +51 -12
  97. package/src/commands/gen.ts +14 -1
  98. package/src/commands/init.ts +27 -3
  99. package/src/commands/migrate.ts +12 -4
  100. package/src/commands/prompt-snapshot.ts +2 -1
  101. package/src/commands/types.ts +12 -3
  102. package/src/commands/upgrade.ts +69 -4
  103. package/src/commands/verify.ts +74 -23
  104. package/src/index.ts +37 -18
  105. package/src/lib/advisory.ts +15 -0
  106. package/src/lib/anti-patterns.ts +7 -5
  107. package/src/lib/args.ts +60 -39
  108. package/src/lib/authored-source.ts +45 -0
  109. package/src/lib/base-url-advisory.ts +178 -0
  110. package/src/lib/codegen-drift.ts +52 -9
  111. package/src/lib/detect-stack.ts +73 -2
  112. package/src/lib/docs-drift.ts +17 -7
  113. package/src/lib/git-ignore.ts +47 -3
  114. package/src/lib/ignored-scaffold-check.ts +78 -0
  115. package/src/lib/load-error.ts +89 -0
  116. package/src/lib/load-metaobjects-config.ts +53 -5
  117. package/src/lib/manifest-ignored-check.ts +21 -6
  118. package/src/lib/output.ts +36 -8
  119. package/src/lib/rel-posix.ts +25 -0
  120. package/src/lib/replay-remedy.ts +78 -0
  121. package/src/lib/wrangler.ts +52 -4
@@ -13,6 +13,55 @@ export function buildWranglerExecuteArgs(opts) {
13
13
  }
14
14
  return args;
15
15
  }
16
+ /**
17
+ * The reason to report when wrangler exits non-zero.
18
+ *
19
+ * **`--json` puts wrangler's error on STDOUT, and warnings on stderr.** This function
20
+ * exists because the runner used to report stderr alone, so a live D1 gate failing for
21
+ * want of a `CLOUDFLARE_API_TOKEN` told the adopter its cause was
22
+ * `▲ [WARNING] … "unsafe" fields are experimental` — a warning, named as the reason a
23
+ * schema gate failed, with the real cause discarded. Found by running an estate's own
24
+ * `verify:prod-schema` against production for the first time.
25
+ *
26
+ * Order: the structured error wrangler wrote, then stderr, then raw stdout, then the
27
+ * process's own message. Stderr is still consulted — a genuine wrangler failure that
28
+ * never reaches stdout must not be swallowed to fix the opposite mistake.
29
+ */
30
+ export function wranglerFailureReason(stdout, stderr, fallback) {
31
+ const structured = structuredWranglerError(stdout);
32
+ if (structured !== undefined)
33
+ return structured;
34
+ const err = stderr.trim();
35
+ if (err.length > 0)
36
+ return err;
37
+ const out = stdout.trim();
38
+ if (out.length > 0)
39
+ return out;
40
+ return fallback;
41
+ }
42
+ /** wrangler's two `--json` error shapes: `{error:{text}}` / `{error}` and the
43
+ * `[{success:false,error}]` execute envelope. Anything else reads as absent. */
44
+ function structuredWranglerError(stdout) {
45
+ let parsed;
46
+ try {
47
+ parsed = JSON.parse(stdout);
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ const node = Array.isArray(parsed) ? parsed[0] : parsed;
53
+ if (node === null || typeof node !== "object")
54
+ return undefined;
55
+ const { error } = node;
56
+ if (typeof error === "string" && error.trim().length > 0)
57
+ return error.trim();
58
+ if (error !== null && typeof error === "object") {
59
+ const { text } = error;
60
+ if (typeof text === "string" && text.trim().length > 0)
61
+ return text.trim();
62
+ }
63
+ return undefined;
64
+ }
16
65
  export const defaultWranglerRunner = async (args, cwd) => {
17
66
  try {
18
67
  const { stdout, stderr } = await execFile("wrangler", args, { cwd, maxBuffer: 16 * 1024 * 1024 });
@@ -23,8 +72,8 @@ export const defaultWranglerRunner = async (args, cwd) => {
23
72
  if (e.code === "ENOENT") {
24
73
  throw new Error(`wrangler not found on PATH; install it: 'npm i -D wrangler'`);
25
74
  }
26
- const stderr = e.stderr ?? "";
27
- throw new Error(`wrangler ${args.join(" ")} failed: ${stderr || e.message}`);
75
+ const reason = wranglerFailureReason(e.stdout ?? "", e.stderr ?? "", e.message);
76
+ throw new Error(`wrangler ${args.join(" ")} failed: ${reason}`);
28
77
  }
29
78
  };
30
79
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"wrangler.js","sourceRoot":"","sources":["../../../src/lib/wrangler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;AASvC,MAAM,UAAU,wBAAwB,CAAC,IAA4B;IACnE,MAAM,IAAI,GAAa;QACrB,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO;QAC7B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QACpC,QAAQ;QACR,WAAW,EAAE,IAAI,CAAC,OAAO;KAC1B,CAAC;IACF,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AASD,MAAM,CAAC,MAAM,qBAAqB,GAAmB,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IACvE,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;QAClG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAAmE,CAAC;QAC9E,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAa;IACtD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7D,OAAO,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"wrangler.js","sourceRoot":"","sources":["../../../src/lib/wrangler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/cli",
3
- "version": "1.0.0-rc.5",
3
+ "version": "1.0.0-rc.7",
4
4
  "description": "CLI for MetaObjects: scaffold, codegen, migrate, and drift-detection commands.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -51,15 +51,15 @@
51
51
  "@biomejs/js-api": "^0.7.0",
52
52
  "@biomejs/wasm-nodejs": "^1.9.4",
53
53
  "@libsql/kysely-libsql": "^0.4.0",
54
- "@metaobjectsdev/codegen-ts": "1.0.0-rc.5",
55
- "@metaobjectsdev/codegen-ts-react": "1.0.0-rc.5",
56
- "@metaobjectsdev/codegen-ts-tanstack": "1.0.0-rc.5",
57
- "@metaobjectsdev/docs-site": "1.0.0-rc.5",
58
- "@metaobjectsdev/metadata": "1.0.0-rc.5",
59
- "@metaobjectsdev/migrate-ts": "1.0.0-rc.5",
60
- "@metaobjectsdev/render": "1.0.0-rc.5",
61
- "@metaobjectsdev/runtime-ts": "1.0.0-rc.5",
62
- "@metaobjectsdev/sdk": "1.0.0-rc.5",
54
+ "@metaobjectsdev/codegen-ts": "1.0.0-rc.7",
55
+ "@metaobjectsdev/codegen-ts-react": "1.0.0-rc.7",
56
+ "@metaobjectsdev/codegen-ts-tanstack": "1.0.0-rc.7",
57
+ "@metaobjectsdev/docs-site": "1.0.0-rc.7",
58
+ "@metaobjectsdev/metadata": "1.0.0-rc.7",
59
+ "@metaobjectsdev/migrate-ts": "1.0.0-rc.7",
60
+ "@metaobjectsdev/render": "1.0.0-rc.7",
61
+ "@metaobjectsdev/runtime-ts": "1.0.0-rc.7",
62
+ "@metaobjectsdev/sdk": "1.0.0-rc.7",
63
63
  "@toon-format/toon": "^2.3.0",
64
64
  "jiti": "^2.4.0"
65
65
  },
@@ -29,6 +29,7 @@ import {
29
29
  import type {
30
30
  GenContext,
31
31
  EmittedFile,
32
+ Generator,
32
33
  ResolvedDocsConfig,
33
34
  DocsSurface,
34
35
  Dialect,
@@ -41,7 +42,7 @@ import {
41
42
  DEFAULT_COLUMN_NAMING_STRATEGY,
42
43
  renderCoreMetamodelDocs,
43
44
  } from "@metaobjectsdev/metadata";
44
- import { dbEmittingObjects, DEFAULT_DIALECT, missingDialectMessage } from "@metaobjectsdev/codegen-ts";
45
+ import { dbEmittingObjects, DEFAULT_DIALECT, DEFAULT_DOCS_DIR, missingDialectMessage, resolveGenerators, runEmitsHonoRoutes, runEmitsUiTier } from "@metaobjectsdev/codegen-ts";
45
46
  import type { MetaDataTypeProvider, MetaRoot } from "@metaobjectsdev/metadata";
46
47
  import { generateSite, SITE_TEMPLATE_NAMES, SITE_ASSET_NAMES, readSiteFile } from "@metaobjectsdev/docs-site";
47
48
  // The `agent` schema surface takes the physical schema as an ARGUMENT with its resolvers
@@ -53,6 +54,14 @@ import {
53
54
  qualifiedDbName,
54
55
  } from "@metaobjectsdev/migrate-ts";
55
56
  import type { AgentSchemaInput, SchemaColumnLike } from "@metaobjectsdev/codegen-ts";
57
+ import { reportLoadError } from "../lib/load-error.js";
58
+
59
+ /**
60
+ * Print a load failure with everything the loader's ADR-0009 envelope carried — the stable
61
+ * code, the file, the json path, and the loader's own next steps — instead of the bare
62
+ * `err.message` five commands used to print. See `lib/load-error.ts`.
63
+ */
64
+
56
65
 
57
66
  type DocsLayout = "flat" | "package";
58
67
 
@@ -202,9 +211,14 @@ function parseDocsArgs(argv: string[], cwd: string): DocsFlags {
202
211
  // cwd (mirrors how migrate/gen treat the working directory as the root).
203
212
  projectRoot: projectRoot ?? cwd,
204
213
  projectRootProvided: projectRoot !== undefined,
205
- // Default out dir, resolved against the project root below. In --metamodel
206
- // mode the renderer writes under <out>/metamodel/, default ./docs/metamodel.
207
- out: out ?? (wantMetamodel ? "./docs/metamodel" : "./docs"),
214
+ // Default out dir, resolved against the project root below. ONE spelling of the
215
+ // default, shared with resolveDocsConfig — two spellings is how a flag default and
216
+ // a config default come to disagree about where the pages are.
217
+ //
218
+ // --metamodel keeps its own sub-path: the renderer writes under <out>/metamodel/,
219
+ // and it is a different artifact (the metamodel reference, not this project's
220
+ // pages), so it is not swept into the generated-docs directory.
221
+ out: out ?? (wantMetamodel ? "./docs/metamodel" : DEFAULT_DOCS_DIR),
208
222
  // Default flat preserves today's single-package output (+ existing goldens).
209
223
  layout: layout ?? "flat",
210
224
  metamodel: wantMetamodel,
@@ -464,7 +478,7 @@ export async function docsCommand(
464
478
 
465
479
  // Merge the config `docs:` block with CLI overrides over documented defaults.
466
480
  // CLI --out/--layout only override when explicitly passed; surfaces/baseUrl
467
- // override whenever present. The resolver supplies defaults (outDir ./docs,
481
+ // override whenever present. The resolver supplies defaults (outDir ./docs/generated,
468
482
  // layout = fallback, surfaces = both) so config-less + flag-less runs are
469
483
  // unchanged.
470
484
  const cliOverrides: Partial<ResolvedDocsConfig> = {
@@ -505,7 +519,7 @@ export async function docsCommand(
505
519
  ...configLoadOptions,
506
520
  });
507
521
  } catch (err) {
508
- log.error(`docs: failed to load metadata: ${(err as Error).message}`);
522
+ reportLoadError(log, "docs: failed to load metadata", err);
509
523
  return 2;
510
524
  }
511
525
 
@@ -527,6 +541,29 @@ export async function docsCommand(
527
541
  pkMap: buildPkMap(root),
528
542
  relationMap: buildRelationMap(root),
529
543
  });
544
+ // The generator suite, with stable-name STRINGS resolved to the generators they name
545
+ // (ADR-0021 #1 allows `generators: ["routes-hono"]`). Both aggregations below read a
546
+ // generator MARKER, and a string carries none — so reading the raw array answered
547
+ // "no Hono routes / no UI tier" for a project that wires them by name, which is the
548
+ // same two-doors-one-question defect these flags exist to close. `meta gen` resolves
549
+ // the identical array through `normalizeConfig`.
550
+ //
551
+ // An unknown name throws (the message names it and lists the valid ones). `meta gen`
552
+ // fails on it; `meta docs` has never had to load the suite at all, so it degrades to
553
+ // the already-typed entries rather than becoming a new failure — and SAYS it did,
554
+ // because a page quietly losing a section is exactly what this whole fix is about.
555
+ let suite: Generator[];
556
+ try {
557
+ suite = resolveGenerators(loadedConfig?.generators ?? []);
558
+ } catch (err) {
559
+ log.warn(
560
+ `docs: could not resolve the generator suite (${(err as Error).message}) — the agent ` +
561
+ `pages fall back to the generators declared as values, so a tier wired by name may ` +
562
+ `be missing from them.`,
563
+ );
564
+ suite = (loadedConfig?.generators ?? []).filter((g): g is Generator => typeof g !== "string");
565
+ }
566
+
530
567
  const ctx: GenContext = {
531
568
  entities: root.objects(),
532
569
  loadedRoot: root,
@@ -541,12 +578,14 @@ export async function docsCommand(
541
578
  // surface. Aggregate it from the generator set exactly as the gen runner
542
579
  // does (a generator opts in via emitsHonoRoutes), so `meta docs` auto-detects
543
580
  // routesFileHono() rather than relying on a field users don't normally set.
544
- includeHonoRoutes:
545
- loadedConfig?.includeHonoRoutes ??
546
- (loadedConfig?.generators?.some(
547
- (g) => typeof g !== "string" && g.emitsHonoRoutes === true,
548
- ) ??
549
- false),
581
+ includeHonoRoutes: loadedConfig?.includeHonoRoutes ?? runEmitsHonoRoutes(suite),
582
+ // `agent/ui.md` describes forms, grids and the endpoints their hooks call. Whether
583
+ // any of that is emitted is a GENERATOR fact — the page's own predicate is
584
+ // metadata-only and answered "could a UI be generated?", so a project with
585
+ // `generators: []` got a confident page naming endpoints nothing serves. Aggregate
586
+ // it through the same helper the gen runner uses, so `meta docs` and `meta gen` —
587
+ // the two doors onto this page — cannot answer it differently.
588
+ includeUiTier: loadedConfig?.includeUiTier ?? runEmitsUiTier(suite),
550
589
  } as never,
551
590
  renderContext,
552
591
  projectRoot,
@@ -16,6 +16,14 @@ import {
16
16
  import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
17
17
  import { runGen, listGenerators } from "@metaobjectsdev/codegen-ts";
18
18
  import type { WriteStatus } from "@metaobjectsdev/codegen-ts";
19
+ import { reportLoadError } from "../lib/load-error.js";
20
+
21
+ /**
22
+ * Print a load failure with everything the loader's ADR-0009 envelope carried — the stable
23
+ * code, the file, the json path, and the loader's own next steps — instead of the bare
24
+ * `err.message` five commands used to print. See `lib/load-error.ts`.
25
+ */
26
+
19
27
 
20
28
  export function mapStatus(s: WriteStatus): GenFileStatus {
21
29
  switch (s) {
@@ -28,6 +36,10 @@ export function mapStatus(s: WriteStatus): GenFileStatus {
28
36
  case "unchanged":
29
37
  case "skipped": return "unchanged";
30
38
  case "refused": return "refused";
39
+ // --baseline=adopt — recorded as the merge base, nothing written. Its own word,
40
+ // for the same reason "overwrite" is not "new": the adopter has to know a manifest
41
+ // now exists to commit.
42
+ case "adopted": return "adopted";
31
43
  // FR-038 §8 — a generated file deleted because it is no longer generated.
32
44
  // Shown as its own outcome, not folded into "unchanged": a run summary that
33
45
  // lists writes but hides deletions is how a silent deletion happens.
@@ -114,7 +126,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
114
126
  ...loadMemoryOptionsFrom(forgeConfig),
115
127
  });
116
128
  } catch (err) {
117
- log.error(`failed to load metadata: ${(err as Error).message}`);
129
+ reportLoadError(log, "failed to load metadata", err);
118
130
  return 2;
119
131
  }
120
132
 
@@ -172,6 +184,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
172
184
  dryRun: cliConfig.dryRun,
173
185
  warnings: [],
174
186
  antiPatterns,
187
+ generatorCount: forgeConfig.generators?.length ?? 0,
175
188
  };
176
189
  const output =
177
190
  fmt === "toon" ? formatGenResultToon(genResult)
@@ -8,13 +8,14 @@ import {
8
8
  assemble, resolveAgentContextRoot, planScaffold,
9
9
  AGENT_CONTEXT_MANIFEST_PATH, type Manifest, type Stack,
10
10
  } from "@metaobjectsdev/sdk/agent-context";
11
- import { resolveStack } from "../lib/detect-stack.js";
11
+ import { assertKnownStackValues, resolveStack } from "../lib/detect-stack.js";
12
+ import { reportIgnoredScaffold } from "../lib/ignored-scaffold-check.js";
12
13
  import { parseInitArgs } from "../lib/args.js";
13
14
  import { log } from "../lib/log.js";
14
15
  import { cliVersion } from "../lib/version.js";
15
16
  import { declaredDependencyNames, type PackageManifest } from "../lib/package-manifest.js";
16
17
  import { findWranglerConfig, parseWranglerConfig } from "@metaobjectsdev/migrate-ts";
17
- import { readReferenceTemplate, type ReferenceGeneratorName } from "@metaobjectsdev/codegen-ts";
18
+ import { DEFAULT_DOCS_DIR, readReferenceTemplate, type ReferenceGeneratorName } from "@metaobjectsdev/codegen-ts";
18
19
 
19
20
  // ADR-0034 scaffold-and-own — `meta init` copies the codegen reference templates into
20
21
  // the consumer's repo so they OWN them; metaobjects.config.ts imports them locally.
@@ -138,7 +139,9 @@ export default defineConfig({
138
139
  barrel(),
139
140
  ],
140
141
  docs: {
141
- outDir: "./docs", // every surface lands here (run: meta docs)
142
+ outDir: "${DEFAULT_DOCS_DIR}", // every surface lands here (run: meta docs).
143
+ // A SUB-directory on purpose: these pages are regenerated
144
+ // and overwritten, and docs/ itself is usually yours.
142
145
  layout: "flat", // or "package" for multi-package models
143
146
  // surfaces defaults to ["model", "api", "requirements", "agent"] — all four.
144
147
  // Deliberately NOT narrowed here: the requirements and agent surfaces emit ZERO
@@ -600,6 +603,16 @@ async function writeConfigFile(opts: InitOptions, result: InitResult, agentDir:
600
603
  }
601
604
 
602
605
  export async function init(opts: InitOptions): Promise<InitResult> {
606
+ // Refused HERE, in the shared function, before anything is written — NOT at one CLI
607
+ // entry point. It was at `initCommand`'s arg parse only, and `meta agent-docs` calls
608
+ // this directly: `meta init --docs-only --server klingon` exited 2 with the message
609
+ // while `meta agent-docs --server klingon` exited 0, reported "11 files", and recorded
610
+ // `"servers": []`. That is the worse of the two doors to miss — `agent-docs` is the
611
+ // canonical redirect target for every language port, so the ports used the unguarded
612
+ // one. A non-empty override array suppresses both the prior manifest's stack AND
613
+ // detection, so a dropped value does not degrade, it inverts three statements in the
614
+ // generated context.
615
+ assertKnownStackValues({ servers: opts.servers ?? [], clients: opts.clients ?? [] });
603
616
  const result: InitResult = { created: [], preserved: [], removed: [], warnings: [] };
604
617
  const agentDir = join(opts.cwd, DEFAULT_METAOBJECTS_DIR);
605
618
  const metaobjectsDir = join(opts.cwd, DEFAULT_METADATA_DIR);
@@ -1135,6 +1148,9 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
1135
1148
  let flags;
1136
1149
  try {
1137
1150
  flags = parseInitArgs(args);
1151
+ // Also here, so a bad value is a USAGE error (exit 2) rather than the exit 1 an
1152
+ // init failure gets. `init()` refuses it too, which is what covers every other door.
1153
+ assertKnownStackValues({ servers: flags.servers ?? [], clients: flags.clients ?? [] });
1138
1154
  } catch (err) {
1139
1155
  log.error((err as Error).message);
1140
1156
  return 2;
@@ -1163,6 +1179,14 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
1163
1179
  }
1164
1180
 
1165
1181
  if (!flags.quiet) {
1182
+ // A count is not a guarantee that the files reached the repository. Many projects
1183
+ // git-ignore `.claude/` under a comment about credentials — a common and defensible
1184
+ // convention — and eight of the eleven files land there. Ignored, they exist only on
1185
+ // the machine that ran this: not committed, absent from CI and from a fresh clone,
1186
+ // invisible to a teammate, while `.agent-context.json` (which IS committed) tracks
1187
+ // them. That silently voids the whole downstream agent-context design, and the answer
1188
+ // was available from the outputs this command had just written.
1189
+ reportIgnoredScaffold(cwd, result.created);
1166
1190
  if (flags.docsOnly) {
1167
1191
  log.info(`Scaffolded the MetaObjects agent context (${result.created.length} files): .metaobjects/AGENTS.md + .claude/skills/metaobjects-*.`);
1168
1192
  for (const w of result.warnings) log.info(` ${w}`);
@@ -58,6 +58,14 @@ import {
58
58
  } from "../lib/wrangler.js";
59
59
  import { buildProjectionViews } from "@metaobjectsdev/codegen-ts";
60
60
  import { tokensToAllowOptions, describeChange } from "../lib/allow.js";
61
+ import { reportLoadError } from "../lib/load-error.js";
62
+
63
+ /**
64
+ * Print a load failure with everything the loader's ADR-0009 envelope carried — the stable
65
+ * code, the file, the json path, and the loader's own next steps — instead of the bare
66
+ * `err.message` five commands used to print. See `lib/load-error.ts`.
67
+ */
68
+
61
69
 
62
70
  export const MIGRATE_HELP_TEXT = `meta migrate — diff metadata vs live DB; emit migration SQL files
63
71
 
@@ -628,7 +636,7 @@ export async function migrateCommand(
628
636
  ...postgresLoadOptions,
629
637
  });
630
638
  } catch (err) {
631
- log.error(`failed to load metadata: ${(err as Error).message}`);
639
+ reportLoadError(log, "failed to load metadata", err);
632
640
  return 2;
633
641
  }
634
642
 
@@ -1056,7 +1064,7 @@ export async function runBaseline(
1056
1064
  ...baselineLoadOptions,
1057
1065
  });
1058
1066
  } catch (err) {
1059
- log.error(`migrate baseline: failed to load metadata: ${(err as Error).message}`);
1067
+ reportLoadError(log, "migrate baseline: failed to load metadata", err);
1060
1068
  return 2;
1061
1069
  }
1062
1070
  const baselineViews = buildProjectionViews(metadata, { dialect: config.dialect, columnNamingStrategy: baselineStrategy });
@@ -1195,7 +1203,7 @@ export async function runOfflineGenerate(
1195
1203
  ...offlineLoadOptions,
1196
1204
  });
1197
1205
  } catch (err) {
1198
- log.error(`migrate: failed to load metadata: ${(err as Error).message}`);
1206
+ reportLoadError(log, "migrate: failed to load metadata", err);
1199
1207
  return 2;
1200
1208
  }
1201
1209
 
@@ -1460,7 +1468,7 @@ async function runD1Migrate(
1460
1468
  ...d1LoadOptions,
1461
1469
  });
1462
1470
  } catch (err) {
1463
- log.error(`migrate: failed to load metadata: ${(err as Error).message}`);
1471
+ reportLoadError(log, "migrate: failed to load metadata", err);
1464
1472
  return 2;
1465
1473
  }
1466
1474
 
@@ -14,6 +14,7 @@ import { parsePromptSnapshotArgs } from "../lib/args.js";
14
14
  import { log } from "../lib/log.js";
15
15
  import { FileProvider } from "../lib/file-provider.js";
16
16
  import { snapshotPaths, unifiedDiff } from "../lib/snapshot.js";
17
+ import { reportLoadError } from "../lib/load-error.js";
17
18
  import { loadMemoryOptionsFrom, loadMetaobjectsConfig, resolveGenConfigDir } from "../lib/load-metaobjects-config.js";
18
19
  import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
19
20
  import { TYPE_TEMPLATE, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT } from "@metaobjectsdev/metadata";
@@ -78,7 +79,7 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
78
79
  ...configLoadOptions,
79
80
  });
80
81
  } catch (err) {
81
- log.error(`failed to load metadata: ${(err as Error).message}`);
82
+ reportLoadError(log, "failed to load metadata", err);
82
83
  return 1;
83
84
  }
84
85
 
@@ -20,8 +20,8 @@
20
20
  // The `--json` flag this once advertised in its own --help, twice, is not coming back: the
21
21
  // CLI rejects a bare `--json` before a command ever sees its args (index.ts, `formatAlias`),
22
22
  // on purpose — one global spelling for all three formats.
23
- import { composeRegistry, coreProviders, buildVocabularyCatalog } from "@metaobjectsdev/metadata";
24
- import { forgeTypesProvider } from "@metaobjectsdev/sdk";
23
+ import { composeRegistry, buildVocabularyCatalog } from "@metaobjectsdev/metadata";
24
+ import { defaultLoadMemoryProviders } from "@metaobjectsdev/sdk";
25
25
  import { log } from "../lib/log.js";
26
26
  import { emitStructured, type OutputFormat } from "../lib/format.js";
27
27
 
@@ -226,7 +226,16 @@ export async function typesCommand(args: string[], fmt: OutputFormat = "text"):
226
226
  // @sortable, @dbColumnType), `view.textarea` with none (no @rows), and the eight
227
227
  // documentation commonAttrs — @title among them — were absent entirely. This is the same
228
228
  // provider set the loader composes, so what this prints is what the loader accepts.
229
- const registry = composeRegistry([...coreProviders, forgeTypesProvider], { validate: true });
229
+ // `defaultLoadMemoryProviders` — THE composition, not a second spelling of it. This read
230
+ // `[...coreProviders]`, which is what that constant happens to equal today, so the claim
231
+ // above ("the same provider set the loader composes") was true by coincidence and the gate
232
+ // that pins the default composition against the canonical manifest had no jurisdiction
233
+ // here: add or drop a provider in the default set and `meta types` would silently print a
234
+ // vocabulary the loader does not accept, with nothing failing. `forgeTypesProvider` was
235
+ // once in this list, and printing it as registered vocabulary was part of how a TS-only
236
+ // registration read as cross-port: `meta types forge --all` listed 20 attrs no other port
237
+ // has. One composition, one door.
238
+ const registry = composeRegistry([...defaultLoadMemoryProviders], { validate: true });
230
239
  const catalog = buildVocabularyCatalog(registry);
231
240
 
232
241
  // Flatten the catalog into searchable entries.
@@ -21,7 +21,13 @@
21
21
  import { readFile, writeFile } from "node:fs/promises";
22
22
  import { extname, relative } from "node:path";
23
23
  import { resolveCollection } from "@metaobjectsdev/sdk";
24
- import { rewriteDocument } from "@metaobjectsdev/metadata";
24
+ import {
25
+ rewriteDocument,
26
+ composeRegistry,
27
+ coreProviders,
28
+ SUBTYPE_BASE,
29
+ retirementSuggestions,
30
+ } from "@metaobjectsdev/metadata";
25
31
  import { log } from "../lib/log.js";
26
32
 
27
33
  /** YAML authoring (ADR-0006). Rewritten by the `yaml`-backed arm, loaded on demand below. */
@@ -96,7 +102,37 @@ export async function upgradeCommand(args: string[], cwd: string): Promise<numbe
96
102
  ? (await import("@metaobjectsdev/metadata/vocabulary-rewrite-yaml")).rewriteYamlDocument
97
103
  : undefined;
98
104
 
99
- const opts = flags.maxVersion !== undefined ? { maxVersion: flags.maxVersion } : {};
105
+ // Which types have an ABSTRACT ANCHOR at `<type>.base`, derived from the core registry
106
+ // rather than listed here: `base` is an anchor exactly when the type registers some OTHER
107
+ // subtype for it to anchor. The rewriter is registry-free on purpose, so the caller — this
108
+ // command, which can build a registry — supplies the fact.
109
+ //
110
+ // Without it, `meta upgrade` answered "nothing to rewrite" on an estate whose
111
+ // `{"object.base": …}` the loader refuses (ERR_ABSTRACT_SUBTYPE_AUTHORED) — the change
112
+ // §10A of the 1.0 guide LEADS WITH. The one command whose job is "what does the new
113
+ // version need me to change?" said "nothing", and the adopter then hit the load failure.
114
+ const registry = composeRegistry(coreProviders);
115
+ // `allTypes()` yields one TypeId per registered type.subType pair, so take the distinct
116
+ // TYPE names off it.
117
+ const abstractAnchorTypes = [...new Set(registry.allTypes().map((id) => id.type))]
118
+ .filter((t) => {
119
+ const subs = registry.allSubTypesOf(t);
120
+ return subs.includes(SUBTYPE_BASE) && subs.some((sub) => sub !== SUBTYPE_BASE);
121
+ })
122
+ .sort();
123
+
124
+ const opts = {
125
+ abstractAnchorTypes,
126
+ ...(flags.maxVersion !== undefined ? { maxVersion: flags.maxVersion } : {}),
127
+ };
128
+
129
+ // One retirement RULE, however many occurrences it has. An estate hit this with 24
130
+ // `@forge*` attributes, and the per-occurrence rationale turned the output into ~96
131
+ // lines saying one thing four times over — the wall that buries the instruction it is
132
+ // made of, which is the exact failure `runGen`'s no-manifest refusal aggregates to
133
+ // avoid. Keyed by the rendered text, so two rules that happen to read identically
134
+ // collapse and two that differ anywhere do not.
135
+ const rules = new Map<string, { why: string; suggestions: string[]; subjects: Set<string> }>();
100
136
 
101
137
  for (const file of files) {
102
138
  const rel = relative(projectRoot, file);
@@ -120,10 +156,24 @@ export async function upgradeCommand(args: string[], cwd: string): Promise<numbe
120
156
  log.info(`\n${rel}`);
121
157
  for (const c of r.changes) log.info(` ${c.line}: @${c.from} → ${c.to}`);
122
158
  for (const f of r.refusals) {
159
+ // The occurrence line is per-line information: file, line, subject, the value found.
160
+ // The RATIONALE that goes with it is per RULE and prints once, after the listing —
161
+ // see `rules` below.
123
162
  log.warn(
124
- ` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision. ` +
125
- `Retired in ${f.since}. ${f.migration !== undefined ? `See ${f.migration}` : f.why}`,
163
+ ` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision ` +
164
+ `(no longer accepted as of ${f.since}).`,
126
165
  );
166
+ const key = `${f.why}\u0000${retirementSuggestions(f).join("\u0000")}`;
167
+ const rule = rules.get(key);
168
+ if (rule === undefined) {
169
+ rules.set(key, {
170
+ why: f.why,
171
+ suggestions: retirementSuggestions(f),
172
+ subjects: new Set([f.subject]),
173
+ });
174
+ } else {
175
+ rule.subjects.add(f.subject);
176
+ }
127
177
  }
128
178
 
129
179
  totalChanges += r.changes.length;
@@ -135,6 +185,21 @@ export async function upgradeCommand(args: string[], cwd: string): Promise<numbe
135
185
  }
136
186
 
137
187
  log.info("");
188
+
189
+ // The WHY and the options, once per rule. Both print, and the guide is not an
190
+ // alternative to them: this used to read `Retired in <since>. See <guide>`, dropping
191
+ // `why` whenever a guide existed — which is almost always — and dropping `replacedBy`
192
+ // unconditionally. For the `@forge*` entries that removed the only actionable sentence
193
+ // there is ("opt the provider in explicitly"). The subject list is what lets a reader
194
+ // connect the paragraph back to the lines above it now that it prints away from them.
195
+ for (const rule of rules.values()) {
196
+ const subjects = [...rule.subjects].sort().join(", ");
197
+ log.warn(`\n ${subjects} — why, and what to do:`);
198
+ log.warn(` ${rule.why}`);
199
+ for (const line of rule.suggestions) log.warn(` ${line}`);
200
+ }
201
+ if (rules.size > 0) log.info("");
202
+
138
203
  if (notChecked.length > 0) {
139
204
  log.warn(
140
205
  `${notChecked.length} file(s) could not be parsed and were NOT checked — fix these ` +