@metaobjectsdev/cli 0.23.2 → 0.24.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 (89) hide show
  1. package/README.md +78 -0
  2. package/dist/src/commands/docs.d.ts.map +1 -1
  3. package/dist/src/commands/docs.js +148 -50
  4. package/dist/src/commands/docs.js.map +1 -1
  5. package/dist/src/commands/export.d.ts.map +1 -1
  6. package/dist/src/commands/export.js +28 -6
  7. package/dist/src/commands/export.js.map +1 -1
  8. package/dist/src/commands/gen.d.ts.map +1 -1
  9. package/dist/src/commands/gen.js +61 -22
  10. package/dist/src/commands/gen.js.map +1 -1
  11. package/dist/src/commands/init.d.ts +8 -0
  12. package/dist/src/commands/init.d.ts.map +1 -1
  13. package/dist/src/commands/init.js +142 -52
  14. package/dist/src/commands/init.js.map +1 -1
  15. package/dist/src/commands/migrate.d.ts +9 -2
  16. package/dist/src/commands/migrate.d.ts.map +1 -1
  17. package/dist/src/commands/migrate.js +399 -73
  18. package/dist/src/commands/migrate.js.map +1 -1
  19. package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
  20. package/dist/src/commands/prompt-snapshot.js +40 -18
  21. package/dist/src/commands/prompt-snapshot.js.map +1 -1
  22. package/dist/src/commands/upgrade.d.ts +2 -0
  23. package/dist/src/commands/upgrade.d.ts.map +1 -0
  24. package/dist/src/commands/upgrade.js +139 -0
  25. package/dist/src/commands/upgrade.js.map +1 -0
  26. package/dist/src/commands/verify.d.ts.map +1 -1
  27. package/dist/src/commands/verify.js +336 -48
  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 +46 -15
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/lib/allow.d.ts.map +1 -1
  33. package/dist/src/lib/allow.js +4 -0
  34. package/dist/src/lib/allow.js.map +1 -1
  35. package/dist/src/lib/args.d.ts +16 -2
  36. package/dist/src/lib/args.d.ts.map +1 -1
  37. package/dist/src/lib/args.js +19 -3
  38. package/dist/src/lib/args.js.map +1 -1
  39. package/dist/src/lib/codegen-drift.d.ts +8 -1
  40. package/dist/src/lib/codegen-drift.d.ts.map +1 -1
  41. package/dist/src/lib/codegen-drift.js +9 -1
  42. package/dist/src/lib/codegen-drift.js.map +1 -1
  43. package/dist/src/lib/detect-stack.d.ts +1 -1
  44. package/dist/src/lib/detect-stack.d.ts.map +1 -1
  45. package/dist/src/lib/detect-stack.js +22 -37
  46. package/dist/src/lib/detect-stack.js.map +1 -1
  47. package/dist/src/lib/load-metaobjects-config.d.ts +48 -1
  48. package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
  49. package/dist/src/lib/load-metaobjects-config.js +80 -1
  50. package/dist/src/lib/load-metaobjects-config.js.map +1 -1
  51. package/dist/src/lib/manifest-ignored-check.d.ts +12 -0
  52. package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -0
  53. package/dist/src/lib/manifest-ignored-check.js +74 -0
  54. package/dist/src/lib/manifest-ignored-check.js.map +1 -0
  55. package/dist/src/lib/migrate-scope.d.ts +49 -0
  56. package/dist/src/lib/migrate-scope.d.ts.map +1 -0
  57. package/dist/src/lib/migrate-scope.js +92 -0
  58. package/dist/src/lib/migrate-scope.js.map +1 -0
  59. package/dist/src/lib/output.d.ts +1 -1
  60. package/dist/src/lib/output.d.ts.map +1 -1
  61. package/dist/src/lib/output.js +9 -3
  62. package/dist/src/lib/output.js.map +1 -1
  63. package/dist/src/lib/requirement-check.d.ts.map +1 -1
  64. package/dist/src/lib/requirement-check.js +10 -57
  65. package/dist/src/lib/requirement-check.js.map +1 -1
  66. package/package.json +11 -11
  67. package/src/commands/docs.ts +158 -49
  68. package/src/commands/export.ts +30 -6
  69. package/src/commands/gen.ts +62 -22
  70. package/src/commands/init.ts +161 -58
  71. package/src/commands/migrate.ts +423 -75
  72. package/src/commands/prompt-snapshot.ts +43 -18
  73. package/src/commands/upgrade.ts +156 -0
  74. package/src/commands/verify.ts +373 -48
  75. package/src/index.ts +46 -15
  76. package/src/lib/allow.ts +4 -0
  77. package/src/lib/args.ts +35 -4
  78. package/src/lib/codegen-drift.ts +9 -0
  79. package/src/lib/detect-stack.ts +20 -33
  80. package/src/lib/load-metaobjects-config.ts +84 -2
  81. package/src/lib/manifest-ignored-check.ts +75 -0
  82. package/src/lib/migrate-scope.ts +102 -0
  83. package/src/lib/output.ts +8 -4
  84. package/src/lib/requirement-check.ts +10 -58
  85. package/dist/src/lib/verified-by-scan.d.ts +0 -20
  86. package/dist/src/lib/verified-by-scan.d.ts.map +0 -1
  87. package/dist/src/lib/verified-by-scan.js +0 -344
  88. package/dist/src/lib/verified-by-scan.js.map +0 -1
  89. package/src/lib/verified-by-scan.ts +0 -375
@@ -14,8 +14,8 @@ 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 { loadMetaobjectsConfig } from "../lib/load-metaobjects-config.js";
18
- import { loadMemory } from "@metaobjectsdev/sdk";
17
+ import { loadMemoryOptionsFrom, loadMetaobjectsConfig, resolveGenConfigDir } from "../lib/load-metaobjects-config.js";
18
+ import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
19
19
  import { TYPE_TEMPLATE, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT } from "@metaobjectsdev/metadata";
20
20
  import { render, ESCAPERS, type RenderFormat } from "@metaobjectsdev/render";
21
21
 
@@ -30,34 +30,59 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
30
30
  return 2;
31
31
  }
32
32
 
33
- // Best-effort load of metaobjects.config.ts to pick up consumer-supplied
34
- // providers. prompt-snapshot doesn't require codegen config; if it's absent
33
+ // Where the metadata lives is `resolveCollection`'s decision, not this
34
+ // command's `--check` is a drift GATE, so a project declaring `sources`
35
+ // elsewhere would otherwise gate against a stale `metaobjects/` (or report
36
+ // "no metaobjects/ found" for metadata it can see perfectly well). Discovery
37
+ // and load stay separate failure modes, the `meta gen` pattern: a broad catch
38
+ // around both reports a genuine ParseError as "no metadata found".
39
+ // `resolveCollection` raises ERR_COLLECTION_NOT_FOUND with its own message,
40
+ // replacing the hand-rolled ENOENT sniff that used to live here.
41
+ let collection;
42
+ try {
43
+ collection = await resolveCollection(cwd);
44
+ } catch (err) {
45
+ log.error((err as Error).message);
46
+ return 2;
47
+ }
48
+
49
+ // Everything project-relative below hangs off the DECLARING directory, not
50
+ // ambient cwd: `.metaobjects/snapshots/` is that config's own state, and the
51
+ // prompt text belongs to the same project as the metadata that references it.
52
+ // Identical to cwd for a run from the project root, which is the only
53
+ // invocation that worked before metadata sources were resolvable at all.
54
+ const projectRoot = collection.configDir;
55
+
56
+ // Best-effort load of metaobjects.config.ts to pick up consumer-supplied providers
57
+ // and shipped `libraries`. prompt-snapshot doesn't require codegen config; if it's absent
35
58
  // or invalid, fall back to defaults — the loader still works for any
36
59
  // metadata that only uses core+forge subtypes.
37
- let configProviders: NonNullable<Awaited<ReturnType<typeof loadMetaobjectsConfig>>["providers"]> | undefined;
60
+ let configLoadOptions: ReturnType<typeof loadMemoryOptionsFrom> = {};
38
61
  try {
39
- const forgeConfig = await loadMetaobjectsConfig(cwd);
40
- configProviders = forgeConfig.providers;
62
+ // `metaobjects.config.ts` gets its own nearest-ancestor walk, not `projectRoot`:
63
+ // it answers a different question from `.metaobjects/config.json` (design §4.6)
64
+ // and in a Maven- or pip-rooted monorepo the two legitimately sit in different
65
+ // directories, so reading it from the collection's would silently drop this
66
+ // package's providers (#326). Same path whenever the two files sit together.
67
+ configLoadOptions = loadMemoryOptionsFrom(
68
+ await loadMetaobjectsConfig(resolveGenConfigDir(cwd, projectRoot)),
69
+ );
41
70
  } catch {
42
- configProviders = undefined;
71
+ configLoadOptions = {};
43
72
  }
44
73
 
45
74
  let root;
46
75
  try {
47
- root = await loadMemory(cwd, {
48
- ...(configProviders !== undefined ? { providers: configProviders } : {}),
76
+ root = await loadMemory(collection.configDir, {
77
+ files: collection.files,
78
+ ...configLoadOptions,
49
79
  });
50
80
  } catch (err) {
51
- const msg = (err as Error).message;
52
- if (msg.includes("ENOENT") || msg.includes("no such") || msg.includes("cannot read")) {
53
- log.error(`no metaobjects/ found in ${cwd}; run 'meta init' to scaffold`);
54
- return 2;
55
- }
56
- log.error(`failed to load metadata: ${msg}`);
81
+ log.error(`failed to load metadata: ${(err as Error).message}`);
57
82
  return 1;
58
83
  }
59
84
 
60
- const promptsDir = join(cwd, flags.prompts ?? DEFAULT_PROMPTS_DIR);
85
+ const promptsDir = join(projectRoot, flags.prompts ?? DEFAULT_PROMPTS_DIR);
61
86
  const provider = new FileProvider(promptsDir);
62
87
 
63
88
  // ADR-0039: effective children — resolve rather than rely on root being unextended.
@@ -79,7 +104,7 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
79
104
  // Absent/typeless required attrs are a loader-schema concern, not ours.
80
105
  if (typeof textRef !== "string") continue;
81
106
 
82
- const { dir, payloadPath, snapPath } = snapshotPaths(cwd, tmpl.name);
107
+ const { dir, payloadPath, snapPath } = snapshotPaths(projectRoot, tmpl.name);
83
108
  if (!existsSync(payloadPath)) {
84
109
  log.info(`[${tmpl.name}] skipped — no payload at ${payloadPath}`);
85
110
  skipped++;
@@ -0,0 +1,156 @@
1
+ // server/typescript/packages/cli/src/commands/upgrade.ts
2
+ //
3
+ // `meta upgrade` — rewrite retired vocabulary in this project's metadata.
4
+ //
5
+ // DELIBERATELY NOT `meta migrate`. That command owns DATABASE SCHEMA (ADR-0015) and an
6
+ // adopter reading `migrate` expects DDL. Overloading it with a metadata rewrite would make
7
+ // the most destructive command in the toolchain ambiguous about what it touches.
8
+ //
9
+ // IT DOES NOT LOAD THE METADATA, and cannot. Once vocabulary is deregistered, metadata
10
+ // carrying it fails the load — which is exactly the state this command exists to repair. So
11
+ // it resolves the file SET through `resolveCollection` (the single authority on where
12
+ // metadata lives) and hands each file's RAW TEXT to the rewriter. See
13
+ // `metadata/src/vocabulary-rewrite.ts` for why that is the only workable shape.
14
+ //
15
+ // DRY-RUN BY DEFAULT, per this repo's convention for anything that edits committed files.
16
+ // `--apply` writes. Refusals exit NON-ZERO even when every mechanical change succeeded, so
17
+ // CI cannot mistake a partial upgrade for a finished one.
18
+
19
+ import { readFile, writeFile } from "node:fs/promises";
20
+ import { extname, relative } from "node:path";
21
+ import { resolveCollection } from "@metaobjectsdev/sdk";
22
+ import { rewriteDocument } from "@metaobjectsdev/metadata";
23
+ import { log } from "../lib/log.js";
24
+
25
+ /** Authoring formats the rewriter cannot edit — see `vocabulary-rewrite.ts` for why. */
26
+ const UNREWRITABLE_EXTENSIONS = new Set([".yaml", ".yml"]);
27
+
28
+ interface UpgradeFlags {
29
+ apply: boolean;
30
+ maxVersion?: string;
31
+ projectRoot?: string;
32
+ }
33
+
34
+ function parseArgs(argv: string[]): UpgradeFlags {
35
+ const flags: UpgradeFlags = { apply: false };
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const a = argv[i] as string;
38
+ if (a === "--apply") flags.apply = true;
39
+ else if (a === "--to") {
40
+ const v = argv[++i];
41
+ if (v === undefined) throw new Error("--to needs a version");
42
+ flags.maxVersion = v;
43
+ } else if (a.startsWith("--to=")) flags.maxVersion = a.slice("--to=".length);
44
+ else if (a === "--help" || a === "-h") throw new Error("__help__");
45
+ else if (!a.startsWith("-")) flags.projectRoot = a;
46
+ else throw new Error(`unknown option: ${a}`);
47
+ }
48
+ return flags;
49
+ }
50
+
51
+ export async function upgradeCommand(args: string[], cwd: string): Promise<number> {
52
+ let flags: UpgradeFlags;
53
+ try {
54
+ flags = parseArgs(args);
55
+ } catch (err) {
56
+ if ((err as Error).message === "__help__") {
57
+ log.info(
58
+ "meta upgrade [<project>] [--to <version>] [--apply]\n\n" +
59
+ " Rewrites retired metadata vocabulary. Previews by default; --apply writes.\n" +
60
+ " Retirements needing a human decision are REFUSED and listed with their guide.",
61
+ );
62
+ return 0;
63
+ }
64
+ log.error((err as Error).message);
65
+ return 2;
66
+ }
67
+
68
+ const projectRoot = flags.projectRoot ?? cwd;
69
+
70
+ let files: readonly string[];
71
+ try {
72
+ files = (await resolveCollection(projectRoot)).files;
73
+ } catch (err) {
74
+ log.error((err as Error).message);
75
+ return 1;
76
+ }
77
+
78
+ let totalChanges = 0;
79
+ let totalRefusals = 0;
80
+ let filesChanged = 0;
81
+ const skipped: string[] = [];
82
+
83
+ for (const file of files) {
84
+ const rel = relative(projectRoot, file);
85
+
86
+ // A file we cannot rewrite is NAMED, never passed over. Silently skipping it is the
87
+ // failure this command exists to prevent: the adopter runs the documented migration,
88
+ // reads "no retired vocabulary found", and ships metadata that does not load.
89
+ if (UNREWRITABLE_EXTENSIONS.has(extname(file).toLowerCase())) {
90
+ skipped.push(rel);
91
+ continue;
92
+ }
93
+
94
+ const before = await readFile(file, "utf8");
95
+ const r = rewriteDocument(before, {
96
+ ...(flags.maxVersion !== undefined ? { maxVersion: flags.maxVersion } : {}),
97
+ });
98
+ if (r.changes.length === 0 && r.refusals.length === 0) continue;
99
+
100
+ log.info(`\n${rel}`);
101
+ for (const c of r.changes) log.info(` ${c.line}: @${c.from} → ${c.to}`);
102
+ for (const f of r.refusals) {
103
+ log.warn(
104
+ ` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision. ` +
105
+ `Retired in ${f.since}. ${f.migration !== undefined ? `See ${f.migration}` : f.why}`,
106
+ );
107
+ }
108
+
109
+ totalChanges += r.changes.length;
110
+ totalRefusals += r.refusals.length;
111
+ if (r.changes.length > 0) {
112
+ filesChanged++;
113
+ if (flags.apply) await writeFile(file, r.text, "utf8");
114
+ }
115
+ }
116
+
117
+ log.info("");
118
+ if (skipped.length > 0) {
119
+ log.warn(
120
+ `${skipped.length} YAML file(s) cannot be rewritten automatically and were NOT ` +
121
+ `checked — migrate them by hand:\n ${skipped.join("\n ")}`,
122
+ );
123
+ }
124
+
125
+ if (totalChanges === 0 && totalRefusals === 0) {
126
+ log.info(
127
+ skipped.length > 0
128
+ ? "meta upgrade — no retired vocabulary found in the JSON metadata."
129
+ : "meta upgrade — no retired vocabulary found.",
130
+ );
131
+ } else if (totalChanges === 0) {
132
+ // Refusals only. Reporting "rewrote 0 declarations", or advertising `--apply`, both
133
+ // promise an action guaranteed to change nothing and bury the fact that the remaining
134
+ // work is entirely human.
135
+ log.info("meta upgrade — nothing here can be rewritten automatically.");
136
+ } else if (flags.apply) {
137
+ log.info(`meta upgrade — rewrote ${totalChanges} declaration(s) across ${filesChanged} file(s).`);
138
+ } else {
139
+ log.info(
140
+ `meta upgrade — ${totalChanges} declaration(s) in ${filesChanged} file(s) can be rewritten. ` +
141
+ `Re-run with --apply to write.`,
142
+ );
143
+ }
144
+
145
+ // Non-zero while ANY refusal stands, applied or not. A partial upgrade that exited 0 would
146
+ // let CI record the migration as done while metadata still fails to load. A file we could
147
+ // not read at all counts the same way, for the same reason.
148
+ if (totalRefusals > 0) {
149
+ log.error(
150
+ `${totalRefusals} declaration(s) need a human decision and were left untouched — ` +
151
+ `see the guides listed above.`,
152
+ );
153
+ return 1;
154
+ }
155
+ return skipped.length > 0 ? 1 : 0;
156
+ }