@metaobjectsdev/cli 1.0.0-rc.4 → 1.0.0-rc.6
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.
- package/dist/src/commands/docs.d.ts.map +1 -1
- package/dist/src/commands/docs.js +41 -9
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/gen.d.ts +8 -0
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +17 -4
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +181 -35
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +6 -0
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +11 -5
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
- package/dist/src/commands/prompt-snapshot.js +2 -1
- package/dist/src/commands/prompt-snapshot.js.map +1 -1
- package/dist/src/commands/types.d.ts.map +1 -1
- package/dist/src/commands/types.js +12 -3
- package/dist/src/commands/types.js.map +1 -1
- package/dist/src/commands/upgrade.d.ts.map +1 -1
- package/dist/src/commands/upgrade.js +38 -4
- package/dist/src/commands/upgrade.js.map +1 -1
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +108 -30
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +55 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/advisory.d.ts +6 -0
- package/dist/src/lib/advisory.d.ts.map +1 -1
- package/dist/src/lib/advisory.js +13 -0
- package/dist/src/lib/advisory.js.map +1 -1
- package/dist/src/lib/anti-patterns.d.ts.map +1 -1
- package/dist/src/lib/anti-patterns.js +2 -4
- package/dist/src/lib/anti-patterns.js.map +1 -1
- package/dist/src/lib/args.d.ts +251 -12
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +119 -92
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/base-url-advisory.d.ts +37 -0
- package/dist/src/lib/base-url-advisory.d.ts.map +1 -0
- package/dist/src/lib/base-url-advisory.js +162 -0
- package/dist/src/lib/base-url-advisory.js.map +1 -0
- package/dist/src/lib/codegen-drift.d.ts +8 -0
- package/dist/src/lib/codegen-drift.d.ts.map +1 -1
- package/dist/src/lib/codegen-drift.js +43 -10
- package/dist/src/lib/codegen-drift.js.map +1 -1
- package/dist/src/lib/detect-stack.d.ts +17 -0
- package/dist/src/lib/detect-stack.d.ts.map +1 -1
- package/dist/src/lib/detect-stack.js +64 -2
- package/dist/src/lib/detect-stack.js.map +1 -1
- package/dist/src/lib/docs-drift.d.ts +12 -1
- package/dist/src/lib/docs-drift.d.ts.map +1 -1
- package/dist/src/lib/docs-drift.js +85 -16
- package/dist/src/lib/docs-drift.js.map +1 -1
- package/dist/src/lib/git-ignore.d.ts +59 -0
- package/dist/src/lib/git-ignore.d.ts.map +1 -0
- package/dist/src/lib/git-ignore.js +120 -0
- package/dist/src/lib/git-ignore.js.map +1 -0
- package/dist/src/lib/ignored-scaffold-check.d.ts +12 -0
- package/dist/src/lib/ignored-scaffold-check.d.ts.map +1 -0
- package/dist/src/lib/ignored-scaffold-check.js +75 -0
- package/dist/src/lib/ignored-scaffold-check.js.map +1 -0
- package/dist/src/lib/load-error.d.ts +46 -0
- package/dist/src/lib/load-error.d.ts.map +1 -0
- package/dist/src/lib/load-error.js +51 -0
- package/dist/src/lib/load-error.js.map +1 -0
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +65 -1
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -1
- package/dist/src/lib/manifest-ignored-check.js +23 -36
- package/dist/src/lib/manifest-ignored-check.js.map +1 -1
- package/dist/src/lib/output.d.ts +22 -1
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +39 -12
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/rel-posix.d.ts +5 -0
- package/dist/src/lib/rel-posix.d.ts.map +1 -0
- package/dist/src/lib/rel-posix.js +23 -0
- package/dist/src/lib/rel-posix.js.map +1 -0
- package/dist/src/lib/replay-remedy.d.ts +2 -0
- package/dist/src/lib/replay-remedy.d.ts.map +1 -0
- package/dist/src/lib/replay-remedy.js +69 -0
- package/dist/src/lib/replay-remedy.js.map +1 -0
- package/dist/src/lib/requirement-lint.d.ts.map +1 -1
- package/dist/src/lib/requirement-lint.js +25 -5
- package/dist/src/lib/requirement-lint.js.map +1 -1
- package/package.json +10 -10
- package/src/commands/docs.ts +51 -12
- package/src/commands/gen.ts +19 -4
- package/src/commands/init.ts +188 -39
- package/src/commands/migrate.ts +13 -5
- package/src/commands/prompt-snapshot.ts +2 -1
- package/src/commands/types.ts +12 -3
- package/src/commands/upgrade.ts +44 -4
- package/src/commands/verify.ts +120 -32
- package/src/index.ts +55 -8
- package/src/lib/advisory.ts +15 -0
- package/src/lib/anti-patterns.ts +2 -5
- package/src/lib/args.ts +154 -109
- package/src/lib/base-url-advisory.ts +173 -0
- package/src/lib/codegen-drift.ts +52 -9
- package/src/lib/detect-stack.ts +73 -2
- package/src/lib/docs-drift.ts +107 -17
- package/src/lib/git-ignore.ts +159 -0
- package/src/lib/ignored-scaffold-check.ts +78 -0
- package/src/lib/load-error.ts +89 -0
- package/src/lib/load-metaobjects-config.ts +58 -1
- package/src/lib/manifest-ignored-check.ts +25 -33
- package/src/lib/output.ts +64 -11
- package/src/lib/rel-posix.ts +25 -0
- package/src/lib/replay-remedy.ts +78 -0
- package/src/lib/requirement-lint.ts +26 -5
package/src/commands/verify.ts
CHANGED
|
@@ -12,12 +12,14 @@ import { parseVerifyArgs, type MigrateFlags } from "../lib/args.js";
|
|
|
12
12
|
import { log } from "../lib/log.js";
|
|
13
13
|
import { emitStructured, type OutputFormat } from "../lib/format.js";
|
|
14
14
|
import {
|
|
15
|
-
antiPatternRows, ranSection, skippedSection, warnCapped,
|
|
15
|
+
antiPatternRows, missingBaseUrlRows, ranSection, skippedSection, warnCapped,
|
|
16
16
|
type AdvisoryDiagnosticRow, type AdvisoryFindingRow, type AdvisorySection,
|
|
17
17
|
} from "../lib/advisory.js";
|
|
18
18
|
import { warnIfAgentContextStale } from "../lib/agent-context-staleness.js";
|
|
19
19
|
import { warnIfManifestIgnored } from "../lib/manifest-ignored-check.js";
|
|
20
20
|
import { scanSourceForAntiPatterns } from "../lib/anti-patterns.js";
|
|
21
|
+
import { scanForMissingBaseUrl, type BaseUrlFinding } from "../lib/base-url-advisory.js";
|
|
22
|
+
import { replayRemedy } from "../lib/replay-remedy.js";
|
|
21
23
|
import { FileProvider } from "../lib/file-provider.js";
|
|
22
24
|
import { derivePayloadFieldTree } from "../lib/payload-field-tree.js";
|
|
23
25
|
import { loadMemoryOptionsFrom, loadMetaobjectsConfig, resolveGenCollection, resolveGenConfigDir } from "../lib/load-metaobjects-config.js";
|
|
@@ -85,6 +87,7 @@ import {
|
|
|
85
87
|
REQUIREMENT_STATUSES,
|
|
86
88
|
} from "@metaobjectsdev/metadata";
|
|
87
89
|
import { verify, ERR_REQUIRED_SLOT_UNUSED, ERR_PARTIAL_UNRESOLVED } from "@metaobjectsdev/render";
|
|
90
|
+
import { describeLoadError, reportLoadError } from "../lib/load-error.js";
|
|
88
91
|
|
|
89
92
|
const DEFAULT_PROMPTS_DIR = "prompts";
|
|
90
93
|
|
|
@@ -250,7 +253,10 @@ export async function verifyCommand(
|
|
|
250
253
|
});
|
|
251
254
|
} catch (err) {
|
|
252
255
|
const msg = (err as Error).message;
|
|
253
|
-
|
|
256
|
+
// Everything the loader's envelope carried, not just the message: the stable code a
|
|
257
|
+
// CI job keys on, and the FILE + json path the message itself can never name.
|
|
258
|
+
const report = describeLoadError(err);
|
|
259
|
+
log.error(`failed to load metadata: ${report.text}`);
|
|
254
260
|
// Strict-load rejection (ADR-0023). Two different failures reach here and they need
|
|
255
261
|
// different advice:
|
|
256
262
|
//
|
|
@@ -263,8 +269,11 @@ export async function verifyCommand(
|
|
|
263
269
|
// retired attribute and the author gets a green `meta verify` over a value that now
|
|
264
270
|
// reaches nothing: a correct, loud failure converted into a quiet, wrong pass. That is
|
|
265
271
|
// strictly worse than the error it replaced.
|
|
266
|
-
|
|
267
|
-
|
|
272
|
+
// Off the REPORT, not the raw error a second time: `describeLoadError` has already
|
|
273
|
+
// decided what the envelope carried (and filtered empty entries), so re-reading the
|
|
274
|
+
// throwable would be a second answer to a question already answered above.
|
|
275
|
+
const code = report.code;
|
|
276
|
+
const suggestions = report.suggestions;
|
|
268
277
|
const strictHint =
|
|
269
278
|
"meta verify is strict (ADR-0023): every authored @attr must be declared. " +
|
|
270
279
|
"Fix: register the attr on a metadata provider, OR move arbitrary " +
|
|
@@ -280,6 +289,14 @@ export async function verifyCommand(
|
|
|
280
289
|
// report — only why there is none.
|
|
281
290
|
emitStructured({
|
|
282
291
|
error: `failed to load metadata: ${msg}`,
|
|
292
|
+
// ADR-0009 promises a stable `code`, and the docs name codes a CI job is meant to
|
|
293
|
+
// branch on — but this payload used to carry the message string and nothing else,
|
|
294
|
+
// so there was nothing to branch on. Each field is present only when the loader
|
|
295
|
+
// actually supplied it; absence here means the loader had none, never that it was
|
|
296
|
+
// dropped in transit.
|
|
297
|
+
...(report.code !== undefined ? { code: report.code } : {}),
|
|
298
|
+
...(report.files !== undefined ? { files: [...report.files] } : {}),
|
|
299
|
+
...(report.jsonPath !== undefined ? { jsonPath: report.jsonPath } : {}),
|
|
283
300
|
hint: suggestions !== undefined && suggestions.length > 0
|
|
284
301
|
? suggestions.join(" ")
|
|
285
302
|
: isStrictAttr ? strictHint : "fix the metadata error above and re-run",
|
|
@@ -404,12 +421,23 @@ export async function verifyCommand(
|
|
|
404
421
|
return 2;
|
|
405
422
|
}
|
|
406
423
|
|
|
407
|
-
// --dialect wins; else migrate's resolved dialect; else
|
|
408
|
-
//
|
|
409
|
-
|
|
424
|
+
// --dialect wins; else migrate's resolved dialect; else the project's DECLARED
|
|
425
|
+
// dialect from metaobjects.config.ts; else refuse. There is no --db to infer
|
|
426
|
+
// from, so guessing would replay a postgres chain through sqlite.
|
|
427
|
+
//
|
|
428
|
+
// The last fallback was missing, and its absence made one fact need two
|
|
429
|
+
// spellings: a TypeScript project declares `dialect: "postgres"` in
|
|
430
|
+
// metaobjects.config.ts, `meta gen`, `meta verify --codegen` and `meta migrate`
|
|
431
|
+
// all honour it, and `--replay` alone refused and demanded `migrate.dialect` in
|
|
432
|
+
// .metaobjects/config.json — the spelling a TypeScript project is least likely
|
|
433
|
+
// to have. migrate's own resolution still WINS where it has an answer, because
|
|
434
|
+
// it is the more specific statement about the chain being replayed.
|
|
435
|
+
const dialect: Dialect | undefined =
|
|
436
|
+
flags.dialect ?? migrateConfig.dialect ?? forgeConfig?.dialect;
|
|
410
437
|
if (dialect === undefined) {
|
|
411
438
|
log.error(
|
|
412
|
-
`meta verify --replay: no dialect — pass --dialect <postgres|sqlite>,
|
|
439
|
+
`meta verify --replay: no dialect — pass --dialect <postgres|sqlite>, ` +
|
|
440
|
+
`set 'dialect' in metaobjects.config.ts, or set migrate.dialect in .metaobjects/config.json`,
|
|
413
441
|
);
|
|
414
442
|
return 2;
|
|
415
443
|
}
|
|
@@ -438,11 +466,7 @@ export async function verifyCommand(
|
|
|
438
466
|
applied = await applyPending(engine.db, dir, { dryRun: false, dialect });
|
|
439
467
|
} catch (err) {
|
|
440
468
|
log.error(`meta verify --replay: ${(err as Error).message}`);
|
|
441
|
-
log.error(
|
|
442
|
-
`meta verify --replay: the committed chain does not apply to an empty database. ` +
|
|
443
|
-
`Applied migrations are immutable, so fix this with a NEW migration that creates the ` +
|
|
444
|
-
`missing object — not by editing a committed up.sql.`,
|
|
445
|
-
);
|
|
469
|
+
for (const line of replayRemedy(err)) log.error(`meta verify --replay: ${line}`);
|
|
446
470
|
return 1;
|
|
447
471
|
}
|
|
448
472
|
|
|
@@ -700,13 +724,33 @@ export async function verifyCommand(
|
|
|
700
724
|
antiPatternSection = skippedSection(`the scan failed: ${(err as Error).message}`);
|
|
701
725
|
return;
|
|
702
726
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
727
|
+
// F52 — the one gate that can see a provider silently missing its base. Folded into
|
|
728
|
+
// this pass rather than given its own, because it is the same KIND of finding (a
|
|
729
|
+
// teachable warning that never touches the exit code) and one --no-antipatterns
|
|
730
|
+
// should suppress the whole advisory tier, not half of it.
|
|
731
|
+
let baseUrl: BaseUrlFinding[] = [];
|
|
732
|
+
try {
|
|
733
|
+
baseUrl = scanForMissingBaseUrl(projectRoot, forgeConfig?.apiPrefix ?? "");
|
|
734
|
+
} catch {
|
|
735
|
+
// Same discipline as above: an advisory scan never breaks verify. The anti-pattern
|
|
736
|
+
// half already ran, so its rows are still reported rather than lost to this catch.
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
antiPatternSection = ranSection([...antiPatternRows(findings), ...missingBaseUrlRows(baseUrl)]);
|
|
740
|
+
if (findings.length > 0) {
|
|
741
|
+
log.warn(
|
|
742
|
+
`meta verify — ${findings.length} place(s) hand-roll what MetaObjects can model ` +
|
|
743
|
+
`(advisory — does not fail the build):`,
|
|
744
|
+
);
|
|
745
|
+
warnCapped(findings.map((f) => ` ${f.message}`), flags.limit, { structured });
|
|
746
|
+
}
|
|
747
|
+
if (baseUrl.length > 0) {
|
|
748
|
+
log.warn(
|
|
749
|
+
`meta verify — ${baseUrl.length} entity-fetcher provider(s) mounted with no baseUrl ` +
|
|
750
|
+
`while apiPrefix is "${forgeConfig?.apiPrefix ?? ""}" (advisory — does not fail the build):`,
|
|
751
|
+
);
|
|
752
|
+
warnCapped(baseUrl.map((f) => ` ${f.message}`), flags.limit, { structured });
|
|
753
|
+
}
|
|
710
754
|
}
|
|
711
755
|
|
|
712
756
|
// -- template (prompt / output) drift --------------------------------------
|
|
@@ -1189,11 +1233,11 @@ export async function verifyCommand(
|
|
|
1189
1233
|
strict: !flags.lax,
|
|
1190
1234
|
});
|
|
1191
1235
|
} catch (err) {
|
|
1192
|
-
|
|
1193
|
-
//
|
|
1194
|
-
//
|
|
1195
|
-
//
|
|
1196
|
-
|
|
1236
|
+
// The second door onto the same strict load, and it must carry the same provenance
|
|
1237
|
+
// as the first: the ADR-0009 code, the FILE and json path the message cannot name,
|
|
1238
|
+
// and the loader's own remedies. It printed a bare message plus a hand-rolled
|
|
1239
|
+
// suggestions read — the half-true rule this file's sibling comment warns about.
|
|
1240
|
+
reportLoadError(log, "verify --codegen: failed to load this package's metadata", err);
|
|
1197
1241
|
return 2;
|
|
1198
1242
|
}
|
|
1199
1243
|
}
|
|
@@ -1212,7 +1256,11 @@ export async function verifyCommand(
|
|
|
1212
1256
|
}
|
|
1213
1257
|
|
|
1214
1258
|
if (result.clean) {
|
|
1215
|
-
|
|
1259
|
+
// Says what it CHECKED, not more. Since 0.24.3 this gate asks whether each file's
|
|
1260
|
+
// GENERATED contribution is current — a hand edit `meta gen` preserves is exempt —
|
|
1261
|
+
// so "output is in sync with the metadata" claimed a stronger property than it had
|
|
1262
|
+
// verified, and did so over a file whose committed content contradicted the model.
|
|
1263
|
+
say("meta verify — every file's generated contribution is current (no codegen drift).");
|
|
1216
1264
|
return 0;
|
|
1217
1265
|
}
|
|
1218
1266
|
|
|
@@ -1220,15 +1268,32 @@ export async function verifyCommand(
|
|
|
1220
1268
|
`meta verify — codegen drift (${result.driftedFiles.length} file(s) differ from a fresh regen):`,
|
|
1221
1269
|
);
|
|
1222
1270
|
for (const line of result.lines) log.error(` ${line}`);
|
|
1223
|
-
|
|
1271
|
+
// The remedy is PER CONVICTION KIND, not one line for the whole gate. `meta gen`
|
|
1272
|
+
// fixes stale generated output; it cannot fix a hand-edited names artifact, because
|
|
1273
|
+
// it MERGES the edit and exits 0 — so prescribing it there was the unbreakable loop
|
|
1274
|
+
// the hand-edit exemption exists to end, reintroduced for the one file the exemption
|
|
1275
|
+
// deliberately excludes. Both lines print when the drift set holds both kinds.
|
|
1276
|
+
if (result.driftedFiles.length > result.handEditedNames.length) {
|
|
1277
|
+
log.error("Run 'meta gen' to regenerate, then commit the result.");
|
|
1278
|
+
}
|
|
1279
|
+
if (result.handEditedNames.length > 0) {
|
|
1280
|
+
log.error(
|
|
1281
|
+
"For the names artifact(s) above, 'meta gen' will NOT help — it preserves a hand " +
|
|
1282
|
+
"edit by design. Revert the edit (or delete the file and re-run 'meta gen' to " +
|
|
1283
|
+
"re-emit it), and keep constants of your own in a file of your own.",
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1224
1286
|
return 1;
|
|
1225
1287
|
}
|
|
1226
1288
|
|
|
1227
1289
|
// -- docs drift -------------------------------------------------------------
|
|
1228
1290
|
// Gated on --docs. Runs `meta docs` into a temp dir and diffs the committed docs
|
|
1229
|
-
// tree. See lib/docs-drift.ts for the
|
|
1230
|
-
//
|
|
1231
|
-
//
|
|
1291
|
+
// tree. See lib/docs-drift.ts for the deliberate differences from --codegen: a byte
|
|
1292
|
+
// difference IS drift here; a committed file a regen would not emit is reported ONLY
|
|
1293
|
+
// under `agent/` and only when it opens with the @generated marker (`docs.outDir` is
|
|
1294
|
+
// otherwise full of files MetaObjects did not write); and a page the project
|
|
1295
|
+
// GIT-IGNORES is exempt, because `docs.outDir` is a directory rather than a namespace
|
|
1296
|
+
// MetaObjects owns.
|
|
1232
1297
|
//
|
|
1233
1298
|
// It needs a config for the same reason `--codegen` does: `docs.outDir` says which
|
|
1234
1299
|
// tree to compare against, and the `agent` surface will not even materialise
|
|
@@ -1264,21 +1329,44 @@ export async function verifyCommand(
|
|
|
1264
1329
|
return 2;
|
|
1265
1330
|
}
|
|
1266
1331
|
|
|
1332
|
+
// What the gate did NOT check, on BOTH the passing and the failing line. A gate
|
|
1333
|
+
// that quietly compared two of 589 pages and reported "no drift" would be
|
|
1334
|
+
// indistinguishable from one that compared everything.
|
|
1335
|
+
const notChecked =
|
|
1336
|
+
result.ignoreReason !== undefined
|
|
1337
|
+
? `; .gitignore not consulted — ${result.ignoreReason} — so every page a fresh run emits was expected to be committed`
|
|
1338
|
+
: result.ignored > 0
|
|
1339
|
+
? `; ${result.ignored} git-ignored page(s) not checked`
|
|
1340
|
+
: "";
|
|
1341
|
+
|
|
1267
1342
|
if (result.clean) {
|
|
1268
1343
|
// The denominator is what was actually COMPARED, so the passing line and the
|
|
1269
1344
|
// failing line below count the same set. A gate whose two halves divide by
|
|
1270
1345
|
// different numbers is how `verify --templates` came to report seven templates
|
|
1271
1346
|
// vanishing between a red run and a green one.
|
|
1272
|
-
say(
|
|
1347
|
+
say(
|
|
1348
|
+
`meta verify — ${result.checked} docs page(s) match a fresh 'meta docs' ` +
|
|
1349
|
+
`(no docs drift${notChecked}).`,
|
|
1350
|
+
);
|
|
1273
1351
|
return 0;
|
|
1274
1352
|
}
|
|
1275
1353
|
|
|
1276
1354
|
log.error(
|
|
1277
1355
|
`meta verify — docs drift (${result.driftedFiles.length} of ${result.checked} page(s) ` +
|
|
1278
|
-
`differ from a fresh 'meta docs'):`,
|
|
1356
|
+
`differ from a fresh 'meta docs'${notChecked}):`,
|
|
1279
1357
|
);
|
|
1280
1358
|
for (const line of result.lines) log.error(` ${line}`);
|
|
1281
1359
|
log.error("Run 'meta docs' to regenerate, then commit the result.");
|
|
1360
|
+
// The one-command path to green, named at the point of conviction — a page reported
|
|
1361
|
+
// as uncommitted is the one case where regenerating is NOT the answer.
|
|
1362
|
+
if (result.lines.some((l) => l.startsWith("+ "))) {
|
|
1363
|
+
log.error(
|
|
1364
|
+
result.ignoreReason === undefined
|
|
1365
|
+
? "A page you deliberately do not commit is exempt once it is git-ignored."
|
|
1366
|
+
: `.gitignore was not consulted (${result.ignoreReason}), so a page you ` +
|
|
1367
|
+
`deliberately do not commit is reported above.`,
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1282
1370
|
return 1;
|
|
1283
1371
|
}
|
|
1284
1372
|
}
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,14 @@ GLOBAL OPTIONS:
|
|
|
55
55
|
|
|
56
56
|
GEN FLAGS:
|
|
57
57
|
--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
|
|
60
|
+
--baseline <default|adopt|fresh>
|
|
61
|
+
First-time-on-existing-file behavior. Default: refuse a file that
|
|
62
|
+
cannot be proved to be generated output. "adopt" records the files
|
|
63
|
+
you have as the merge base and writes NOTHING — the first run a
|
|
64
|
+
project with no committed .hashes.json can perform. "fresh"
|
|
65
|
+
overwrites them with fresh output and re-baselines (discards edits).
|
|
58
66
|
--no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
|
|
59
67
|
--limit <n|all> How many advisory lines TEXT output prints before it truncates
|
|
60
68
|
(default 20). Never applies to --format toon/json, which
|
|
@@ -69,7 +77,11 @@ DOCS FLAGS:
|
|
|
69
77
|
[<project-root>] PROJECT ROOT to resolve metadata from — the directory that CONTAINS
|
|
70
78
|
your metadata, NOT the metadata directory. Passing it SCOPES the run
|
|
71
79
|
(default: cwd)
|
|
72
|
-
--out <dir>, -o Output directory for
|
|
80
|
+
--out <dir>, -o Output directory for THIS run (default: ./docs/generated). It does
|
|
81
|
+
NOT survive the run — set 'docs: { outDir }' in metaobjects.config.ts
|
|
82
|
+
for a directory that persists, which is what 'meta verify --docs'
|
|
83
|
+
measures. The default is a sub-directory on purpose: these pages are
|
|
84
|
+
regenerated and overwritten, and 'docs/' itself is usually yours.
|
|
73
85
|
--templates <dir> Project root to resolve adopter templates/ overrides (default: <project-root>)
|
|
74
86
|
--prompts <dir> Extra dir holding prompt .mustache sources for --site (e.g. data/templates/)
|
|
75
87
|
|
|
@@ -127,7 +139,7 @@ ship in later sub-projects. See https://metaobjects.com for docs.
|
|
|
127
139
|
`;
|
|
128
140
|
|
|
129
141
|
/** Focused per-subcommand usage slices shown by `<cmd> --help`. */
|
|
130
|
-
const COMMAND_HELP: Record<string, string> = {
|
|
142
|
+
export const COMMAND_HELP: Record<string, string> = {
|
|
131
143
|
gen: `meta gen — codegen TS targets from your declared metadata
|
|
132
144
|
|
|
133
145
|
USAGE:
|
|
@@ -135,6 +147,14 @@ USAGE:
|
|
|
135
147
|
|
|
136
148
|
FLAGS:
|
|
137
149
|
--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
|
|
152
|
+
--baseline <default|adopt|fresh>
|
|
153
|
+
First-time-on-existing-file behavior. Default: refuse a file that
|
|
154
|
+
cannot be proved to be generated output. "adopt" records the files
|
|
155
|
+
you have as the merge base and writes NOTHING — the first run a
|
|
156
|
+
project with no committed .hashes.json can perform. "fresh"
|
|
157
|
+
overwrites them with fresh output and re-baselines (discards edits).
|
|
138
158
|
--no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
|
|
139
159
|
--limit <n|all> Advisory lines TEXT output prints before truncating (default 20)
|
|
140
160
|
--format <toon|json|text> Output format (global flag; default toon off-TTY)
|
|
@@ -179,10 +199,17 @@ USAGE:
|
|
|
179
199
|
|
|
180
200
|
FLAGS:
|
|
181
201
|
--templates Template/prompt {{field}}↔payload drift (default when bare)
|
|
182
|
-
--codegen Codegen drift —
|
|
202
|
+
--codegen Codegen drift — is the GENERATED contribution current? A hand
|
|
203
|
+
edit 'meta gen' preserves is not drift; a stale generated part is.
|
|
204
|
+
An <Entity>.names.ts artifact is the exception: it must match a
|
|
205
|
+
fresh regen exactly — a hand edit in it is itself the defect.
|
|
183
206
|
Needs metaobjects.config.ts; exit 2 if absent.
|
|
184
207
|
--docs Docs drift — run 'meta docs' to a temp dir and diff docs.outDir
|
|
185
|
-
Needs metaobjects.config.ts; exit 2 if absent.
|
|
208
|
+
Needs metaobjects.config.ts; exit 2 if absent. A page the
|
|
209
|
+
project GIT-IGNORES is exempt (docs.outDir is a directory, not
|
|
210
|
+
a namespace MetaObjects owns); the count of exempt pages is
|
|
211
|
+
reported either way, and a run where every page is ignored is
|
|
212
|
+
refused rather than reported clean.
|
|
186
213
|
--db <url> Schema drift — live DB URL enables the schema-drift gate.
|
|
187
214
|
Supports: file:, libsql:, postgres:, postgresql:
|
|
188
215
|
D1 has no URL — use --dialect d1 / --d1 <binding> instead.
|
|
@@ -204,6 +231,10 @@ FLAGS:
|
|
|
204
231
|
--d1 <binding> D1 binding name from wrangler.toml (only with --dialect d1)
|
|
205
232
|
--remote Target remote D1 instead of local (only with --dialect d1) —
|
|
206
233
|
the ONLY way to verify the actual deployed D1 database
|
|
234
|
+
--lax Load with the legacy OPEN attribute set instead of strict
|
|
235
|
+
(ADR-0023). Strict is the default: an undeclared or typo'd own
|
|
236
|
+
@attr fails verify with ERR_UNKNOWN_ATTR. Use only while
|
|
237
|
+
migrating a model onto a registered provider.
|
|
207
238
|
--no-antipatterns Suppress the advisory "hand-rolled what MetaObjects can model" pass
|
|
208
239
|
--no-requirement-lint Suppress the advisory requirement AUTHORING lint (not the gate)
|
|
209
240
|
--limit <n|all> Advisory lines TEXT output prints PER SECTION before truncating
|
|
@@ -249,7 +280,11 @@ FLAGS:
|
|
|
249
280
|
it SCOPES the run to that directory's own sources; no ancestor
|
|
250
281
|
.metaobjects/config.json is consulted. Omitted (default), the project
|
|
251
282
|
is discovered by walking up.
|
|
252
|
-
--out <dir>, -o Output directory for
|
|
283
|
+
--out <dir>, -o Output directory for THIS run (default: ./docs/generated). It does
|
|
284
|
+
NOT survive the run — set 'docs: { outDir }' in metaobjects.config.ts
|
|
285
|
+
for a directory that persists, which is what 'meta verify --docs'
|
|
286
|
+
measures. The default is a sub-directory on purpose: these pages are
|
|
287
|
+
regenerated and overwritten, and 'docs/' itself is usually yours.
|
|
253
288
|
--model Emit the markdown model surface (entity + template pages)
|
|
254
289
|
--api Emit the markdown api surface (generated SDK reference)
|
|
255
290
|
--requirements Emit the declared requirement ledger (requirements.md + .toon).
|
|
@@ -258,7 +293,9 @@ FLAGS:
|
|
|
258
293
|
agent/ui.md (the generated forms and grids) and
|
|
259
294
|
agent/requirements.md (the ledger plus a claimed-node index).
|
|
260
295
|
Needs metaobjects.config.ts (the dialect and physical names come
|
|
261
|
-
from it); each page is skipped when its tier has nothing to describe
|
|
296
|
+
from it); each page is skipped when its tier has nothing to describe
|
|
297
|
+
— agent/ui.md needs a UI generator (a form, hook or grid) in the
|
|
298
|
+
suite, not merely objects that could have one.
|
|
262
299
|
--metamodel Document the built-in metamodel vocabulary (no metadata needed)
|
|
263
300
|
--site Generate the browsable HTML documentation site (<out>/site/)
|
|
264
301
|
--scaffold-site Copy the site's templates + assets into codegen/docs-site/ to own (theme) them
|
|
@@ -274,6 +311,16 @@ USAGE:
|
|
|
274
311
|
|
|
275
312
|
FLAGS:
|
|
276
313
|
--refresh-docs Refresh .metaobjects/AGENTS.md + CLAUDE.md after CLI upgrades
|
|
314
|
+
--docs-only Write only the agent-context files — no metadata sources, no
|
|
315
|
+
codegen scaffold, no config. Pair with --refresh-docs to update
|
|
316
|
+
an existing project after upgrading.
|
|
317
|
+
--server <lang> Declare a server language for the agent context (repeatable):
|
|
318
|
+
typescript | java | kotlin | csharp | python
|
|
319
|
+
('node' is accepted as an alias for typescript; an unknown
|
|
320
|
+
value is refused, never dropped)
|
|
321
|
+
--client <fw> Declare a client framework for the agent context (repeatable):
|
|
322
|
+
react | tanstack | angular
|
|
323
|
+
--no-skills Skip the .claude/skills/ scaffold
|
|
277
324
|
--force Overwrite existing files
|
|
278
325
|
--quiet Suppress output
|
|
279
326
|
--print-only Print what would be written, don't write
|
|
@@ -289,8 +336,8 @@ USAGE:
|
|
|
289
336
|
meta agent-docs [--server <lang>]... [--client <fw>]... [--out <dir>] [flags]
|
|
290
337
|
|
|
291
338
|
FLAGS:
|
|
292
|
-
--server <lang> Server language (repeatable
|
|
293
|
-
--client <fw> Client framework (repeatable
|
|
339
|
+
--server <lang> Server language (repeatable): typescript|java|kotlin|csharp|python
|
|
340
|
+
--client <fw> Client framework (repeatable): react|tanstack|angular
|
|
294
341
|
--out <dir> Output directory (default: current directory)
|
|
295
342
|
--no-skills Skip .claude/skills/ scaffold
|
|
296
343
|
--no-wire-root Skip wiring root CLAUDE.md @import
|
package/src/lib/advisory.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { log } from "./log.js";
|
|
24
24
|
import type { AntiPatternFinding } from "./anti-patterns.js";
|
|
25
|
+
import type { BaseUrlFinding } from "./base-url-advisory.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* How many advisory lines TEXT output prints before it truncates.
|
|
@@ -119,6 +120,20 @@ export function antiPatternRows(findings: readonly AntiPatternFinding[]): Adviso
|
|
|
119
120
|
}));
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
* F52 findings in the same row shape, so the structured payload has ONE advisory list
|
|
125
|
+
* rather than a second one a consumer has to learn about. `rule` is the discriminator.
|
|
126
|
+
*/
|
|
127
|
+
export function missingBaseUrlRows(findings: readonly BaseUrlFinding[]): AdvisoryFindingRow[] {
|
|
128
|
+
return findings.map((f) => ({
|
|
129
|
+
file: f.file,
|
|
130
|
+
line: f.line,
|
|
131
|
+
rule: "missing-base-url",
|
|
132
|
+
construct: f.construct,
|
|
133
|
+
message: f.message,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
// ---------------------------------------------------------------------------
|
|
123
138
|
// text output
|
|
124
139
|
// ---------------------------------------------------------------------------
|
package/src/lib/anti-patterns.ts
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
// nag a legitimate one.
|
|
17
17
|
|
|
18
18
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
19
|
-
import { join
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { relPosix } from "./rel-posix.js";
|
|
20
21
|
|
|
21
22
|
export interface AntiPatternFinding {
|
|
22
23
|
file: string; // path relative to the scan root (posix-ish, sep-normalized)
|
|
@@ -220,10 +221,6 @@ function findingFor(file: string, lineNo: number, raw: string, isSql: boolean):
|
|
|
220
221
|
};
|
|
221
222
|
}
|
|
222
223
|
|
|
223
|
-
function relPosix(root: string, abs: string): string {
|
|
224
|
-
return relative(root, abs).split(sep).join("/");
|
|
225
|
-
}
|
|
226
|
-
|
|
227
224
|
function walk(dir: string, root: string, ignore: readonly RegExp[], acc: AntiPatternFinding[]): void {
|
|
228
225
|
let entries;
|
|
229
226
|
try {
|