@metaobjectsdev/cli 0.23.1 → 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.
- package/README.md +78 -0
- package/dist/src/commands/docs.d.ts.map +1 -1
- package/dist/src/commands/docs.js +148 -50
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/export.d.ts.map +1 -1
- package/dist/src/commands/export.js +28 -6
- package/dist/src/commands/export.js.map +1 -1
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +61 -22
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/init.d.ts +8 -0
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +142 -52
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +9 -2
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +399 -73
- 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 +40 -18
- package/dist/src/commands/prompt-snapshot.js.map +1 -1
- package/dist/src/commands/upgrade.d.ts +2 -0
- package/dist/src/commands/upgrade.d.ts.map +1 -0
- package/dist/src/commands/upgrade.js +139 -0
- package/dist/src/commands/upgrade.js.map +1 -0
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +336 -48
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +46 -15
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/allow.d.ts.map +1 -1
- package/dist/src/lib/allow.js +4 -0
- package/dist/src/lib/allow.js.map +1 -1
- package/dist/src/lib/args.d.ts +16 -2
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +19 -3
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/codegen-drift.d.ts +8 -1
- package/dist/src/lib/codegen-drift.d.ts.map +1 -1
- package/dist/src/lib/codegen-drift.js +9 -1
- package/dist/src/lib/codegen-drift.js.map +1 -1
- package/dist/src/lib/detect-stack.d.ts +1 -1
- package/dist/src/lib/detect-stack.d.ts.map +1 -1
- package/dist/src/lib/detect-stack.js +22 -37
- package/dist/src/lib/detect-stack.js.map +1 -1
- package/dist/src/lib/load-metaobjects-config.d.ts +48 -1
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +80 -1
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/manifest-ignored-check.d.ts +12 -0
- package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -0
- package/dist/src/lib/manifest-ignored-check.js +74 -0
- package/dist/src/lib/manifest-ignored-check.js.map +1 -0
- package/dist/src/lib/migrate-scope.d.ts +49 -0
- package/dist/src/lib/migrate-scope.d.ts.map +1 -0
- package/dist/src/lib/migrate-scope.js +92 -0
- package/dist/src/lib/migrate-scope.js.map +1 -0
- package/dist/src/lib/output.d.ts +1 -1
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +9 -3
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/requirement-check.d.ts.map +1 -1
- package/dist/src/lib/requirement-check.js +10 -57
- package/dist/src/lib/requirement-check.js.map +1 -1
- package/package.json +11 -11
- package/src/commands/docs.ts +158 -49
- package/src/commands/export.ts +30 -6
- package/src/commands/gen.ts +62 -22
- package/src/commands/init.ts +161 -58
- package/src/commands/migrate.ts +423 -75
- package/src/commands/prompt-snapshot.ts +43 -18
- package/src/commands/upgrade.ts +156 -0
- package/src/commands/verify.ts +373 -48
- package/src/index.ts +46 -15
- package/src/lib/allow.ts +4 -0
- package/src/lib/args.ts +35 -4
- package/src/lib/codegen-drift.ts +9 -0
- package/src/lib/detect-stack.ts +20 -33
- package/src/lib/load-metaobjects-config.ts +84 -2
- package/src/lib/manifest-ignored-check.ts +75 -0
- package/src/lib/migrate-scope.ts +102 -0
- package/src/lib/output.ts +8 -4
- package/src/lib/requirement-check.ts +10 -58
- package/dist/src/lib/verified-by-scan.d.ts +0 -20
- package/dist/src/lib/verified-by-scan.d.ts.map +0 -1
- package/dist/src/lib/verified-by-scan.js +0 -344
- package/dist/src/lib/verified-by-scan.js.map +0 -1
- package/src/lib/verified-by-scan.ts +0 -375
package/src/commands/init.ts
CHANGED
|
@@ -36,7 +36,17 @@ const META_COMMON_JSON = JSON.stringify(
|
|
|
36
36
|
// ignore the per-target generated shadow with a narrow `*/src/generated/`
|
|
37
37
|
// pattern, then explicitly re-include `migrations/` and `config.json` so the
|
|
38
38
|
// tracked artifacts are never swept up even if a future broad pattern were added.
|
|
39
|
-
const METAOBJECTS_GITIGNORE_BODY =
|
|
39
|
+
const METAOBJECTS_GITIGNORE_BODY = `# The codegen merge base. The snapshot BODIES are a second full copy of all
|
|
40
|
+
# generated output — never commit those. \`.hashes.json\` is different: it is one
|
|
41
|
+
# hash per generated path, and it is the only thing that lets \`meta gen\` tell "this
|
|
42
|
+
# file is exactly what I wrote" from "somebody edited this" on a machine that did
|
|
43
|
+
# not generate it. Without it committed, every fresh clone and every CI runner has
|
|
44
|
+
# no merge base, and a hand-edited generated file cannot be recognised as such.
|
|
45
|
+
#
|
|
46
|
+
# The glob form matters: \`.gen-state/\` (a directory) would stop git descending, and
|
|
47
|
+
# the negation below could never take effect.
|
|
48
|
+
.gen-state/*
|
|
49
|
+
!.gen-state/.hashes.json
|
|
40
50
|
|
|
41
51
|
# Per-target codegen output routed under .metaobjects/<target>/ is regenerable
|
|
42
52
|
# (re-run \`meta gen\`); never commit it. The canonical output is your configured
|
|
@@ -127,6 +137,14 @@ export interface InitOptions {
|
|
|
127
137
|
wireRoot?: boolean;
|
|
128
138
|
/** Scaffold ONLY the agent-context (always-on + skills + root wiring), skipping the metaobjects/ project scaffold — for dropping context into an existing/polyglot repo. */
|
|
129
139
|
docsOnly?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Write ONLY `.metaobjects/config.json` — no TypeScript scaffold (metaobjects.config.ts,
|
|
142
|
+
* codegen/generators/, package.json edits, .gitignore, agent-context files, or a
|
|
143
|
+
* metaobjects/ directory). For a Maven- or pip-rooted project that needs the Node CLI
|
|
144
|
+
* (which owns `migrate` and `verify --db` under ADR-0015) to discover its metadata
|
|
145
|
+
* without acquiring a TypeScript project it will never use.
|
|
146
|
+
*/
|
|
147
|
+
configOnly?: boolean;
|
|
130
148
|
}
|
|
131
149
|
|
|
132
150
|
export interface InitResult {
|
|
@@ -192,7 +210,7 @@ function warnIfMonorepoSubdir(opts: InitOptions, result: InitResult): void {
|
|
|
192
210
|
* string[]s (or nothing) straight through is safe — no need to special-case an empty
|
|
193
211
|
* or absent prior.
|
|
194
212
|
*/
|
|
195
|
-
function stackForAgentContext(opts: InitOptions, prior: Manifest | undefined): Stack {
|
|
213
|
+
async function stackForAgentContext(opts: InitOptions, prior: Manifest | undefined): Promise<Stack> {
|
|
196
214
|
const hasOverride = (opts.servers?.length ?? 0) > 0 || (opts.clients?.length ?? 0) > 0;
|
|
197
215
|
const overrides = hasOverride
|
|
198
216
|
? { servers: opts.servers ?? [], clients: opts.clients ?? [] }
|
|
@@ -200,10 +218,27 @@ function stackForAgentContext(opts: InitOptions, prior: Manifest | undefined): S
|
|
|
200
218
|
return resolveStack(opts.cwd, overrides);
|
|
201
219
|
}
|
|
202
220
|
|
|
221
|
+
/** Writes `contents` to `path` (relative to `cwd`), unless `dryRun` — in which case
|
|
222
|
+
* the write is skipped entirely and the caller still records what WOULD have
|
|
223
|
+
* landed. Factors the mkdir+writeFile pair shared by every write site below. */
|
|
224
|
+
async function writeUnlessDryRun(cwd: string, dryRun: boolean, path: string, contents: string): Promise<void> {
|
|
225
|
+
if (dryRun) return;
|
|
226
|
+
const abs = join(cwd, path);
|
|
227
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
228
|
+
await writeFile(abs, contents, "utf8");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** "would be VERBED" during a dry run, plain VERBED otherwise — the one tense
|
|
232
|
+
* marker every reported write shares, so each call site states only its own
|
|
233
|
+
* past participle instead of writing out both tenses of the whole sentence. */
|
|
234
|
+
function verbed(dryRun: boolean, pastParticiple: string): string {
|
|
235
|
+
return dryRun ? `would be ${pastParticiple}` : pastParticiple;
|
|
236
|
+
}
|
|
237
|
+
|
|
203
238
|
async function writeAgentContext(opts: InitOptions, result: InitResult): Promise<void> {
|
|
204
239
|
warnIfMonorepoSubdir(opts, result);
|
|
205
240
|
const prior = await readManifest(opts.cwd);
|
|
206
|
-
const stack = stackForAgentContext(opts, prior);
|
|
241
|
+
const stack = await stackForAgentContext(opts, prior);
|
|
207
242
|
let assembled = assemble({ contentRoot: resolveAgentContextRoot(), stack });
|
|
208
243
|
if (opts.noSkills) assembled = assembled.filter((f) => !f.path.startsWith(".claude/skills/"));
|
|
209
244
|
|
|
@@ -226,32 +261,45 @@ async function writeAgentContext(opts: InitOptions, result: InitResult): Promise
|
|
|
226
261
|
: decision.writes;
|
|
227
262
|
const conflicts = opts.force ? [] : decision.conflicts;
|
|
228
263
|
|
|
264
|
+
// --print-only must win outright: a documented dry run must never write. Both
|
|
265
|
+
// callers of this function (`--docs-only` and `--refresh-docs`) return from
|
|
266
|
+
// `init()` ABOVE the full-scaffold path's own printOnly guard, so without this
|
|
267
|
+
// the dry run silently scaffolded for real — the same defect `--config-only`
|
|
268
|
+
// carried. The guard lives HERE rather than as a path list beside that one
|
|
269
|
+
// because this write set is dynamic (it depends on the resolved stack), and
|
|
270
|
+
// `decision` is already the complete plan: suppressing just the I/O reports
|
|
271
|
+
// exactly the paths a real run would touch, with no second list to drift.
|
|
272
|
+
const dryRun = opts.printOnly === true;
|
|
273
|
+
|
|
229
274
|
for (const w of writes) {
|
|
230
|
-
|
|
231
|
-
await mkdir(dirname(abs), { recursive: true });
|
|
232
|
-
await writeFile(abs, w.contents, "utf8");
|
|
275
|
+
await writeUnlessDryRun(opts.cwd, dryRun, w.path, w.contents);
|
|
233
276
|
result.created.push(w.path);
|
|
234
277
|
}
|
|
235
278
|
for (const c of conflicts) {
|
|
236
|
-
|
|
237
|
-
await mkdir(dirname(abs), { recursive: true });
|
|
238
|
-
await writeFile(abs, c.contents, "utf8");
|
|
279
|
+
await writeUnlessDryRun(opts.cwd, dryRun, c.newPath, c.contents);
|
|
239
280
|
result.created.push(c.newPath);
|
|
240
|
-
|
|
281
|
+
// Past tense only when it actually happened — a dry run that reports "written
|
|
282
|
+
// to <path>.new" is claiming an edit-preserving side effect the user can go
|
|
283
|
+
// look for and will not find.
|
|
284
|
+
result.warnings.push(
|
|
285
|
+
`${c.path} appears hand-edited; refreshed version ${verbed(dryRun, "written")} to ${c.newPath}`,
|
|
286
|
+
);
|
|
241
287
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
288
|
+
await writeUnlessDryRun(
|
|
289
|
+
opts.cwd, dryRun, AGENT_CONTEXT_MANIFEST_PATH,
|
|
290
|
+
JSON.stringify(decision.manifest, null, 2) + "\n",
|
|
291
|
+
);
|
|
292
|
+
result.created.push(AGENT_CONTEXT_MANIFEST_PATH);
|
|
245
293
|
|
|
246
294
|
for (const orphan of decision.removed) {
|
|
247
295
|
result.warnings.push(`${orphan} is no longer part of this stack; orphaned (safe to delete).`);
|
|
248
296
|
}
|
|
249
297
|
|
|
250
|
-
if (opts.wireRoot) await wireRootMemory(opts.cwd, result);
|
|
298
|
+
if (opts.wireRoot) await wireRootMemory(opts.cwd, result, dryRun);
|
|
251
299
|
}
|
|
252
300
|
|
|
253
301
|
const ROOT_IMPORT_LINE = "@.metaobjects/AGENTS.md";
|
|
254
|
-
async function wireRootMemory(cwd: string, result: InitResult): Promise<void> {
|
|
302
|
+
async function wireRootMemory(cwd: string, result: InitResult, dryRun = false): Promise<void> {
|
|
255
303
|
const claudePath = join(cwd, "CLAUDE.md");
|
|
256
304
|
const agentsPath = join(cwd, "AGENTS.md");
|
|
257
305
|
const claudeExists = await fileExists(claudePath);
|
|
@@ -259,8 +307,8 @@ async function wireRootMemory(cwd: string, result: InitResult): Promise<void> {
|
|
|
259
307
|
|
|
260
308
|
// If neither root memory file exists, create CLAUDE.md (Claude Code's canonical) with the import.
|
|
261
309
|
if (!claudeExists && !agentsExists) {
|
|
262
|
-
await
|
|
263
|
-
result.created.push(
|
|
310
|
+
await writeUnlessDryRun(cwd, dryRun, "CLAUDE.md", `# Project memory\n\n${ROOT_IMPORT_LINE}\n`);
|
|
311
|
+
result.created.push(`CLAUDE.md (${verbed(dryRun, "created")} with MetaObjects @import)`);
|
|
264
312
|
return;
|
|
265
313
|
}
|
|
266
314
|
// Otherwise append the import to whichever exist (idempotent — never double-add).
|
|
@@ -268,8 +316,11 @@ async function wireRootMemory(cwd: string, result: InitResult): Promise<void> {
|
|
|
268
316
|
if (!exists) continue;
|
|
269
317
|
const body = await readFile(path, "utf8");
|
|
270
318
|
if (body.includes(ROOT_IMPORT_LINE)) continue;
|
|
271
|
-
|
|
272
|
-
|
|
319
|
+
const target = path.endsWith("AGENTS.md") ? "AGENTS.md" : "CLAUDE.md";
|
|
320
|
+
await writeUnlessDryRun(cwd, dryRun, target, `${body.replace(/\n*$/, "\n")}\n${ROOT_IMPORT_LINE}\n`);
|
|
321
|
+
// Past tense only when it actually happened — this one mutates a file the user
|
|
322
|
+
// owns, so a dry run reporting it as done is the most misleading of the three.
|
|
323
|
+
result.warnings.push(`${verbed(dryRun, "wired")} ${ROOT_IMPORT_LINE} into ${target} so the MetaObjects context loads`);
|
|
273
324
|
}
|
|
274
325
|
}
|
|
275
326
|
|
|
@@ -294,6 +345,72 @@ async function writeOwnedGenerators(opts: InitOptions, result: InitResult): Prom
|
|
|
294
345
|
}
|
|
295
346
|
}
|
|
296
347
|
|
|
348
|
+
/**
|
|
349
|
+
* .metaobjects/config.json — write fresh defaults, or preserve+merge an existing
|
|
350
|
+
* valid config. Shared by the full scaffold and `--config-only` so the two paths
|
|
351
|
+
* cannot drift on the config's default content.
|
|
352
|
+
*/
|
|
353
|
+
async function writeConfigFile(opts: InitOptions, result: InitResult, agentDir: string, agentDirExists: boolean): Promise<void> {
|
|
354
|
+
const freshConfig = opts.d1
|
|
355
|
+
? ConfigSchema.parse({ ...DEFAULT_CONFIG, migrate: buildD1MigrateBlock(opts.cwd) })
|
|
356
|
+
: DEFAULT_CONFIG;
|
|
357
|
+
const writeFresh = (): Promise<void> =>
|
|
358
|
+
writeFile(join(agentDir, "config.json"), JSON.stringify(freshConfig, null, 2) + "\n", "utf8");
|
|
359
|
+
|
|
360
|
+
if (!agentDirExists) {
|
|
361
|
+
await writeFresh();
|
|
362
|
+
result.created.push(".metaobjects/config.json");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const configPath = join(agentDir, "config.json");
|
|
367
|
+
let priorContent: string | undefined;
|
|
368
|
+
try {
|
|
369
|
+
priorContent = await readFile(configPath, "utf8");
|
|
370
|
+
const parsed = ConfigSchema.parse(JSON.parse(priorContent));
|
|
371
|
+
const merged = ConfigSchema.parse({ ...DEFAULT_CONFIG, ...parsed });
|
|
372
|
+
// When a valid .metaobjects/config.json already exists and the user passes --force,
|
|
373
|
+
// we preserve the existing config and only re-scaffold support files. The --d1 flag
|
|
374
|
+
// only takes effect on fresh inits — retro-fitting D1 onto an existing project is
|
|
375
|
+
// the user's job (edit migrate.dialect and migrate.d1 in config.json directly).
|
|
376
|
+
await saveConfig(agentDir, merged);
|
|
377
|
+
result.preserved.push(".metaobjects/config.json");
|
|
378
|
+
return;
|
|
379
|
+
} catch {
|
|
380
|
+
if (priorContent === undefined) {
|
|
381
|
+
// The .metaobjects/ dir existed but config.json itself did not — a fresh write.
|
|
382
|
+
await writeFresh();
|
|
383
|
+
result.created.push(".metaobjects/config.json");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// In the full-scaffold path this is only reachable once the caller has
|
|
388
|
+
// already required --force (the exists-guard at the top of `init()`
|
|
389
|
+
// throws before writeConfigFile runs otherwise), so opts.force is always
|
|
390
|
+
// true there. `--config-only` calls this function directly with no such
|
|
391
|
+
// guard, so without this check it would silently destroy an existing,
|
|
392
|
+
// merely-unparseable config on every run — the one thing `--force` is
|
|
393
|
+
// supposed to gate.
|
|
394
|
+
if (!opts.force) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`existing .metaobjects/config.json exists but could not be parsed; refusing to overwrite it. ` +
|
|
397
|
+
`Use --force to replace it with defaults. Prior content:\n${priorContent}`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
log.warn("existing .metaobjects/config.json was invalid — writing fresh defaults. Prior content:");
|
|
401
|
+
log.warn(priorContent);
|
|
402
|
+
result.warnings.push("invalid .metaobjects/config.json replaced with defaults");
|
|
403
|
+
await writeFresh();
|
|
404
|
+
// F11 — matches the OTHER two `writeFresh()` call sites above: this IS a
|
|
405
|
+
// fresh write (a destructive one, replacing content that could not be
|
|
406
|
+
// parsed), not a no-op. Omitting this left it in neither `created` nor
|
|
407
|
+
// `preserved`, so the `--config-only` CLI summary (which keys on
|
|
408
|
+
// `result.created.includes(...)` alone) reported "already exists — left
|
|
409
|
+
// untouched" for a config it had just overwritten with defaults.
|
|
410
|
+
result.created.push(".metaobjects/config.json");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
297
414
|
export async function init(opts: InitOptions): Promise<InitResult> {
|
|
298
415
|
const result: InitResult = { created: [], preserved: [], warnings: [] };
|
|
299
416
|
const agentDir = join(opts.cwd, DEFAULT_METAOBJECTS_DIR);
|
|
@@ -309,6 +426,22 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
309
426
|
return result;
|
|
310
427
|
}
|
|
311
428
|
|
|
429
|
+
if (opts.configOnly) {
|
|
430
|
+
// --print-only must win outright: a documented dry run must never write, and
|
|
431
|
+
// this branch used to return ABOVE the printOnly guard the full-scaffold path
|
|
432
|
+
// uses below, so `--config-only --print-only` silently wrote the real file.
|
|
433
|
+
if (opts.printOnly) {
|
|
434
|
+
result.created.push(".metaobjects/config.json");
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
// Config only: write/preserve .metaobjects/config.json and nothing else — no
|
|
438
|
+
// metaobjects/ dir, no agent-context, no TypeScript scaffold. `agentDirExists` is
|
|
439
|
+
// captured before the mkdir below so an existing valid config is still preserved.
|
|
440
|
+
await mkdir(agentDir, { recursive: true });
|
|
441
|
+
await writeConfigFile(opts, result, agentDir, agentDirExists);
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
|
|
312
445
|
if (opts.refreshDocs && exists) {
|
|
313
446
|
// Refresh-only path: (re)write the agent-context docs and NOTHING else — never
|
|
314
447
|
// the project scaffold (metaobjects/, config.json, codegen/generators/,
|
|
@@ -362,45 +495,7 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
362
495
|
}
|
|
363
496
|
|
|
364
497
|
// .metaobjects/config.json
|
|
365
|
-
|
|
366
|
-
? ConfigSchema.parse({ ...DEFAULT_CONFIG, migrate: buildD1MigrateBlock(opts.cwd) })
|
|
367
|
-
: DEFAULT_CONFIG;
|
|
368
|
-
if (agentDirExists) {
|
|
369
|
-
const configPath = join(agentDir, "config.json");
|
|
370
|
-
let priorContent: string | undefined;
|
|
371
|
-
try {
|
|
372
|
-
priorContent = await readFile(configPath, "utf8");
|
|
373
|
-
const parsed = ConfigSchema.parse(JSON.parse(priorContent));
|
|
374
|
-
const merged = ConfigSchema.parse({ ...DEFAULT_CONFIG, ...parsed });
|
|
375
|
-
// When a valid .metaobjects/config.json already exists and the user passes --force,
|
|
376
|
-
// we preserve the existing config and only re-scaffold support files. The --d1 flag
|
|
377
|
-
// only takes effect on fresh inits — retro-fitting D1 onto an existing project is
|
|
378
|
-
// the user's job (edit migrate.dialect and migrate.d1 in config.json directly).
|
|
379
|
-
await saveConfig(agentDir, merged);
|
|
380
|
-
result.preserved.push(".metaobjects/config.json");
|
|
381
|
-
} catch {
|
|
382
|
-
if (priorContent !== undefined) {
|
|
383
|
-
log.warn("existing .metaobjects/config.json was invalid — writing fresh defaults. Prior content:");
|
|
384
|
-
log.warn(priorContent);
|
|
385
|
-
result.warnings.push("invalid .metaobjects/config.json replaced with defaults");
|
|
386
|
-
}
|
|
387
|
-
await writeFile(
|
|
388
|
-
join(agentDir, "config.json"),
|
|
389
|
-
JSON.stringify(freshConfig, null, 2) + "\n",
|
|
390
|
-
"utf8",
|
|
391
|
-
);
|
|
392
|
-
if (priorContent === undefined) {
|
|
393
|
-
result.created.push(".metaobjects/config.json");
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
} else {
|
|
397
|
-
await writeFile(
|
|
398
|
-
join(agentDir, "config.json"),
|
|
399
|
-
JSON.stringify(freshConfig, null, 2) + "\n",
|
|
400
|
-
"utf8",
|
|
401
|
-
);
|
|
402
|
-
result.created.push(".metaobjects/config.json");
|
|
403
|
-
}
|
|
498
|
+
await writeConfigFile(opts, result, agentDir, agentDirExists);
|
|
404
499
|
|
|
405
500
|
// .metaobjects/.gitignore
|
|
406
501
|
await writeFile(join(agentDir, ".gitignore"), METAOBJECTS_GITIGNORE_BODY, "utf8");
|
|
@@ -643,6 +738,7 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
|
|
|
643
738
|
noSkills: flags.noSkills,
|
|
644
739
|
wireRoot: flags.wireRoot,
|
|
645
740
|
docsOnly: flags.docsOnly,
|
|
741
|
+
configOnly: flags.configOnly,
|
|
646
742
|
});
|
|
647
743
|
|
|
648
744
|
if (flags.printOnly) {
|
|
@@ -656,6 +752,13 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
|
|
|
656
752
|
log.info(`Scaffolded the MetaObjects agent context (${result.created.length} files): .metaobjects/AGENTS.md + .claude/skills/metaobjects-*.`);
|
|
657
753
|
for (const w of result.warnings) log.info(` ${w}`);
|
|
658
754
|
log.info("Re-run --docs-only --refresh-docs to update; --no-wire-root to skip the root CLAUDE.md @import.");
|
|
755
|
+
} else if (flags.configOnly) {
|
|
756
|
+
if (result.created.includes(".metaobjects/config.json")) {
|
|
757
|
+
log.info("Wrote .metaobjects/config.json — declare your metadata sources there for the Node CLI (migrate, verify --db).");
|
|
758
|
+
} else {
|
|
759
|
+
log.info(".metaobjects/config.json already exists — left untouched.");
|
|
760
|
+
}
|
|
761
|
+
for (const w of result.warnings) log.warn(w);
|
|
659
762
|
} else {
|
|
660
763
|
log.info(nextStepsBlock());
|
|
661
764
|
// Surface any scaffold warnings (e.g. the #77 monorepo-subdir agent-context
|