anymorph 0.5.0 → 0.7.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 (3) hide show
  1. package/README.md +6 -16
  2. package/dist/index.js +218 -113
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,9 @@ anymorph workspaces
30
30
  anymorph init
31
31
  anymorph prepare ws_abc123
32
32
  anymorph validate 20260517T074200Z
33
- anymorph submit 20260517T074200Z
33
+ git add agent/runs content
34
+ git commit -m "chore(geo): publish run 20260517T074200Z"
35
+ git push
34
36
  ```
35
37
 
36
38
  ## Commands
@@ -187,21 +189,9 @@ anymorph validate 20260517T074200Z
187
189
  ```
188
190
 
189
191
  Validation does not update the backend. Backend sync happens from the tenant
190
- submission command after local artifacts pass validation.
191
-
192
- ### `anymorph submit <runId>`
193
-
194
- Submit validated local GEO run artifacts to Anymorph.
195
-
196
- ```bash
197
- anymorph submit 20260517T074200Z
198
- anymorph submit 20260517T074200Z --json
199
- ```
200
-
201
- The CLI reads `agent/runs/{runId}/manifest.json`, submits the manifest-declared
202
- artifact paths, and includes the current git commit SHA when available. Use
203
- `--workspace <workspaceId>` only when the workspace cannot be inferred from the
204
- run package.
192
+ repo GitHub webhook after `agent/runs/{runId}/actions.json` is pushed to
193
+ `main`. Any generated `content/*.mdx` files are materialized as CMS pages from
194
+ the same webhook path.
205
195
 
206
196
  ## How It Works
207
197
 
package/dist/index.js CHANGED
@@ -12216,6 +12216,88 @@ var require_src = __commonJS({
12216
12216
  var cmsFrontmatter = require_cms_frontmatter();
12217
12217
  var cmsThemeTokens = require_cms_theme_tokens();
12218
12218
  var HOSTNAME_LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
12219
+ var CMS_MDX_COMPONENT_CONTRACTS2 = Object.freeze({
12220
+ Callout: Object.freeze({
12221
+ props: Object.freeze({
12222
+ tone: Object.freeze({
12223
+ allowedValues: Object.freeze(["info", "success", "definition"]),
12224
+ defaultValue: "info"
12225
+ })
12226
+ })
12227
+ }),
12228
+ CTA: Object.freeze({
12229
+ requiredProps: Object.freeze(["title", "description"]),
12230
+ reason: "CTA renders title + description side-by-side with the button; missing description leaves the column empty."
12231
+ })
12232
+ });
12233
+ function validateCmsMdxComponentContracts2(mdx) {
12234
+ const issues = [];
12235
+ checkRequiredComponentProps(mdx, issues);
12236
+ checkEnumComponentProps(mdx, issues);
12237
+ return issues;
12238
+ }
12239
+ function checkRequiredComponentProps(mdx, issues) {
12240
+ for (const [component, contract] of Object.entries(CMS_MDX_COMPONENT_CONTRACTS2)) {
12241
+ if (!Array.isArray(contract.requiredProps) || contract.requiredProps.length === 0) continue;
12242
+ for (const attrs of findComponentAttrs(mdx, component)) {
12243
+ for (const prop of contract.requiredProps) {
12244
+ if (!new RegExp(`\\b${escapeRegExp(prop)}\\s*=`).test(attrs)) {
12245
+ const trailer = contract.reason ? ` ${contract.reason}` : "";
12246
+ issues.push({
12247
+ code: "missing_required_prop",
12248
+ component,
12249
+ prop,
12250
+ message: `<${component}> is missing required prop "${prop}".${trailer}`
12251
+ });
12252
+ }
12253
+ }
12254
+ }
12255
+ }
12256
+ }
12257
+ function checkEnumComponentProps(mdx, issues) {
12258
+ for (const [component, contract] of Object.entries(CMS_MDX_COMPONENT_CONTRACTS2)) {
12259
+ if (!contract.props) continue;
12260
+ for (const attrs of findComponentAttrs(mdx, component)) {
12261
+ for (const [prop, propContract] of Object.entries(contract.props)) {
12262
+ if (!Array.isArray(propContract.allowedValues)) continue;
12263
+ const value = readStaticJsxStringProp(attrs, prop);
12264
+ if (value === null) continue;
12265
+ if (!propContract.allowedValues.includes(value)) {
12266
+ issues.push({
12267
+ code: "invalid_prop_value",
12268
+ component,
12269
+ prop,
12270
+ value,
12271
+ allowedValues: propContract.allowedValues,
12272
+ message: `<${component}> prop "${prop}" must be one of ${propContract.allowedValues.map((v) => `"${v}"`).join(", ")}; received "${value}".`
12273
+ });
12274
+ }
12275
+ }
12276
+ }
12277
+ }
12278
+ }
12279
+ function findComponentAttrs(mdx, component) {
12280
+ const tagPattern = new RegExp(`<${escapeRegExp(component)}\\b([^>]*?)\\s*/?>`, "gs");
12281
+ const attrs = [];
12282
+ let match = tagPattern.exec(mdx);
12283
+ while (match !== null) {
12284
+ attrs.push(match[1] ?? "");
12285
+ match = tagPattern.exec(mdx);
12286
+ }
12287
+ return attrs;
12288
+ }
12289
+ function readStaticJsxStringProp(attrs, prop) {
12290
+ const propPattern = new RegExp(
12291
+ `\\b${escapeRegExp(prop)}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|\\{\\s*["']([^"']*)["']\\s*\\})`,
12292
+ "s"
12293
+ );
12294
+ const match = propPattern.exec(attrs);
12295
+ if (!match) return null;
12296
+ return match[1] ?? match[2] ?? match[3] ?? null;
12297
+ }
12298
+ function escapeRegExp(value) {
12299
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12300
+ }
12219
12301
  function normalizePromptGenDomain2(value) {
12220
12302
  const domain = value.trim().toLowerCase();
12221
12303
  if (domain.length === 0 || domain.length > 253 || domain.includes("/") || domain.includes("\\") || domain.includes("?") || domain.includes("#") || domain.includes(":") || domain.includes("@") || domain.startsWith(".") || domain.endsWith(".") || domain.includes("..")) {
@@ -12254,6 +12336,7 @@ var require_src = __commonJS({
12254
12336
  generationMode: z.enum(["full", "lite"]).optional(),
12255
12337
  tierMix: PromptGenTierMixSchema.optional(),
12256
12338
  realizationMode: z.enum(["auto", "llm", "deterministic"]).optional(),
12339
+ brandProfile: z.record(z.string(), z.unknown()).optional(),
12257
12340
  additionalContext: z.string().trim().min(1).max(2e4).optional(),
12258
12341
  userRequest: z.string().trim().min(1).max(2e4).optional(),
12259
12342
  async: z.boolean().optional(),
@@ -13342,7 +13425,9 @@ var require_src = __commonJS({
13342
13425
  CMS_THEME_CONTRACT_TOKEN_KEYS: cmsThemeTokens.CMS_THEME_CONTRACT_TOKEN_KEYS,
13343
13426
  CMS_THEME_STATUS_DEFAULTS: cmsThemeTokens.CMS_THEME_STATUS_DEFAULTS,
13344
13427
  normalizeCmsThemeTokens: cmsThemeTokens.normalizeCmsThemeTokens,
13345
- validateCmsThemeTokens: cmsThemeTokens.validateCmsThemeTokens
13428
+ validateCmsThemeTokens: cmsThemeTokens.validateCmsThemeTokens,
13429
+ CMS_MDX_COMPONENT_CONTRACTS: CMS_MDX_COMPONENT_CONTRACTS2,
13430
+ validateCmsMdxComponentContracts: validateCmsMdxComponentContracts2
13346
13431
  };
13347
13432
  }
13348
13433
  });
@@ -13797,14 +13882,14 @@ var baseOpen = async (options) => {
13797
13882
  }
13798
13883
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
13799
13884
  if (options.wait) {
13800
- return new Promise((resolve3, reject) => {
13885
+ return new Promise((resolve4, reject) => {
13801
13886
  subprocess.once("error", reject);
13802
13887
  subprocess.once("close", (exitCode) => {
13803
13888
  if (!options.allowNonzeroExitCode && exitCode > 0) {
13804
13889
  reject(new Error(`Exited with code ${exitCode}`));
13805
13890
  return;
13806
13891
  }
13807
- resolve3(subprocess);
13892
+ resolve4(subprocess);
13808
13893
  });
13809
13894
  });
13810
13895
  }
@@ -15418,7 +15503,7 @@ async function loginCommand() {
15418
15503
  process.exit(1);
15419
15504
  }
15420
15505
  function sleep(ms) {
15421
- return new Promise((resolve3) => setTimeout(resolve3, ms));
15506
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
15422
15507
  }
15423
15508
 
15424
15509
  // src/lib/display.ts
@@ -15548,7 +15633,7 @@ async function checkCommand(domain, opts) {
15548
15633
  process.exit(1);
15549
15634
  }
15550
15635
  function sleep2(ms) {
15551
- return new Promise((resolve3) => setTimeout(resolve3, ms));
15636
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
15552
15637
  }
15553
15638
 
15554
15639
  // src/commands/status.ts
@@ -15674,6 +15759,8 @@ var CMS_THEME_CONTRACT_TOKEN_KEYS = import_index2.default.CMS_THEME_CONTRACT_TOK
15674
15759
  var CMS_THEME_STATUS_DEFAULTS = import_index2.default.CMS_THEME_STATUS_DEFAULTS;
15675
15760
  var normalizeCmsThemeTokens = import_index2.default.normalizeCmsThemeTokens;
15676
15761
  var validateCmsThemeTokens = import_index2.default.validateCmsThemeTokens;
15762
+ var CMS_MDX_COMPONENT_CONTRACTS = import_index2.default.CMS_MDX_COMPONENT_CONTRACTS;
15763
+ var validateCmsMdxComponentContracts = import_index2.default.validateCmsMdxComponentContracts;
15677
15764
  var reasonTagSchema = import_index2.default.reasonTagSchema;
15678
15765
  var reasonClassSchema = import_index2.default.reasonClassSchema;
15679
15766
  var REASON_TAG_CLASS = import_index2.default.REASON_TAG_CLASS;
@@ -15683,8 +15770,8 @@ var priorDecisionSchema = import_index2.default.priorDecisionSchema;
15683
15770
  var geoStrategyDecisionInputSchema = import_index2.default.geoStrategyDecisionInputSchema;
15684
15771
 
15685
15772
  // src/commands/geo.ts
15686
- import { readFile as readFile3 } from "node:fs/promises";
15687
- import { join as join6 } from "node:path";
15773
+ import { readFile as readFile3, readdir as readdir3, stat as stat4 } from "node:fs/promises";
15774
+ import { join as join6, relative as relative2, resolve as resolve3 } from "node:path";
15688
15775
 
15689
15776
  // src/geo/package-writer.ts
15690
15777
  import { mkdir, writeFile } from "node:fs/promises";
@@ -16377,15 +16464,6 @@ async function validateGeoRunArtifacts(input) {
16377
16464
  runDir
16378
16465
  };
16379
16466
  }
16380
- function normalizeSubmitArtifactPaths(runId, paths) {
16381
- const required = [
16382
- `agent/runs/${runId}/actions.json`,
16383
- `agent/runs/${runId}/rationale.md`,
16384
- `agent/runs/${runId}/status.json`
16385
- ];
16386
- const clean = Array.isArray(paths) ? paths.filter((path2) => hasText(path2)) : [];
16387
- return Array.from(/* @__PURE__ */ new Set([...clean, ...required]));
16388
- }
16389
16467
  function validateManifest(value, runId, errors) {
16390
16468
  if (value.schemaVersion !== 1) errors.push("manifest.json schemaVersion must be 1");
16391
16469
  if (value.runId !== runId) errors.push(`manifest.json runId must match ${runId}`);
@@ -16731,30 +16809,47 @@ Run from the tenant repo root. Writes agent/runs/{runId}/ with manifest,
16731
16809
  context, policy, intents, product catalog, crawl logs, and status files.`
16732
16810
  ).action(geoPrepareCommand)
16733
16811
  );
16734
- commands.push(
16735
- new Command("validate").argument("<runId>").description("Validate local GEO strategy run artifacts before pushing").option("--json", "Output as JSON").addHelpText(
16736
- "after",
16737
- `
16812
+ const validateCommand = new Command("validate").argument("[runId]").description("Validate local GEO strategy artifacts or CMS MDX").option("--json", "Output as JSON").addHelpText(
16813
+ "after",
16814
+ `
16738
16815
  Examples:
16739
16816
  $ anymorph validate 20260517T074200Z
16817
+ $ anymorph validate run 20260517T074200Z
16818
+ $ anymorph validate actions 20260517T074200Z
16819
+ $ anymorph validate mdx content/guide/page.mdx
16820
+ $ anymorph validate mdx "content/**/*.mdx"
16740
16821
  $ anymorph validate 20260517T074200Z --json
16741
16822
 
16742
16823
  Checks required run files, the shared actions.json contract, policy hash,
16743
- per-asset target rules, and commerce targetProductIds.`
16744
- ).action(geoValidateCommand)
16824
+ per-asset target rules, commerce targetProductIds, or CMS MDX component contracts.`
16825
+ ).action(async (runId, opts) => {
16826
+ if (!runId) {
16827
+ console.error(source_default.red("Missing runId. Use anymorph validate <runId> or anymorph validate mdx <file>."));
16828
+ process.exit(1);
16829
+ }
16830
+ await geoValidateCommand(runId, opts);
16831
+ });
16832
+ validateCommand.addCommand(
16833
+ new Command("run").argument("<runId>").description("Validate a local GEO run package").option("--json", "Output as JSON").action(geoValidateCommand)
16745
16834
  );
16746
- commands.push(
16747
- new Command("submit").argument("<runId>").description("Submit validated local GEO strategy artifacts to Anymorph").option("--workspace <workspaceId>", "Override workspace id for this submission").option("--json", "Output as JSON").addHelpText(
16835
+ validateCommand.addCommand(
16836
+ new Command("actions").argument("<runId>").description("Validate actions.json and related local GEO run artifacts").option("--json", "Output as JSON").action(geoValidateCommand)
16837
+ );
16838
+ validateCommand.addCommand(
16839
+ new Command("mdx").argument("<paths...>").description("Validate CMS MDX component contracts").option("--json", "Output as JSON").addHelpText(
16748
16840
  "after",
16749
16841
  `
16750
16842
  Examples:
16751
- $ anymorph submit 20260517T074200Z
16752
- $ anymorph submit 20260517T074200Z --workspace ws_abc123
16753
-
16754
- Submit after committing and pushing tenant repo artifacts. Backend reads the
16755
- pushed actions.json from the tenant repo and materializes the run.`
16756
- ).action(geoSubmitCommand)
16843
+ $ anymorph validate mdx content/guide/page.mdx
16844
+ $ anymorph validate mdx "content/**/*.mdx"
16845
+ $ anymorph validate mdx content --json`
16846
+ ).action(
16847
+ (paths, opts, command) => geoValidateMdxCommand(paths, {
16848
+ json: opts.json || Boolean(command.parent?.opts().json)
16849
+ })
16850
+ )
16757
16851
  );
16852
+ commands.push(validateCommand);
16758
16853
  commands.push(
16759
16854
  new Command("intents").argument("<runId>").description("Show the pre-fetched intents for a local GEO strategy run").option("--json", "Output as JSON").addHelpText(
16760
16855
  "after",
@@ -16881,7 +16976,9 @@ async function geoPrepareCommand(workspaceId, opts) {
16881
16976
  console.log(source_default.bold(" Next:"));
16882
16977
  console.log(source_default.dim(" codex"));
16883
16978
  console.log(source_default.dim(` anymorph validate ${pkg.runId}`));
16884
- console.log(source_default.dim(` anymorph submit ${pkg.runId}`));
16979
+ console.log(source_default.dim(" git add agent/runs content"));
16980
+ console.log(source_default.dim(` git commit -m "chore(geo): publish run ${pkg.runId}"`));
16981
+ console.log(source_default.dim(" git push"));
16885
16982
  console.log();
16886
16983
  }
16887
16984
  async function geoValidateCommand(runId, opts) {
@@ -16908,80 +17005,96 @@ async function geoValidateCommand(runId, opts) {
16908
17005
  console.error();
16909
17006
  process.exit(1);
16910
17007
  }
16911
- async function geoSubmitCommand(runId, opts) {
17008
+ async function geoValidateMdxCommand(paths, opts) {
17009
+ const json = opts.json || paths.includes("--json");
17010
+ const cleanPaths = paths.filter((path2) => path2 !== "--json");
16912
17011
  const repoPath = await resolveCurrentRepoRoot();
16913
- const validation = await validateGeoRunArtifacts({ repoPath, runId });
16914
- if (!validation.ok) {
16915
- if (opts.json) {
16916
- console.log(JSON.stringify({ ...validation, repoPath }, null, 2));
16917
- process.exit(1);
16918
- }
16919
- console.error(source_default.red(`
16920
- GEO run ${runId} is not valid
17012
+ const files = await resolveMdxInputFiles(repoPath, cleanPaths);
17013
+ const results = await Promise.all(
17014
+ files.map(async (file) => {
17015
+ const mdx = await readFile3(file, "utf8");
17016
+ const issues = validateCmsMdxComponentContracts(mdx);
17017
+ return {
17018
+ file: relative2(repoPath, file),
17019
+ ok: issues.length === 0,
17020
+ issues
17021
+ };
17022
+ })
17023
+ );
17024
+ const ok = results.every((result) => result.ok);
17025
+ if (json) {
17026
+ console.log(JSON.stringify({ ok, repoPath, files: results }, null, 2));
17027
+ process.exit(ok ? 0 : 1);
17028
+ }
17029
+ if (ok) {
17030
+ console.log(source_default.green(`
17031
+ ${results.length} CMS MDX file${results.length === 1 ? "" : "s"} valid.
16921
17032
  `));
16922
- for (const error of validation.errors) console.error(` - ${error}`);
16923
- console.error();
16924
- process.exit(1);
17033
+ return;
16925
17034
  }
16926
- const runDir = join6(repoPath, "agent", "runs", runId);
16927
- const [context, manifest] = await Promise.all([
16928
- readJsonRecord(join6(runDir, "context.json")),
16929
- readJsonRecord(join6(runDir, "manifest.json"))
16930
- ]);
16931
- const workspace = opts.workspace ?? stringValue(context.workspaceId) ?? stringValue(manifest.workspaceId) ?? stringValue(context.workspaceDomain) ?? stringValue(manifest.workspaceDomain);
16932
- if (!workspace) {
16933
- console.error(source_default.red("Could not infer workspace. Pass --workspace <workspaceId>."));
16934
- process.exit(1);
17035
+ console.error(source_default.red("\n MDX validation failed\n"));
17036
+ for (const result of results.filter((item) => !item.ok)) {
17037
+ console.error(` ${source_default.bold(result.file)}`);
17038
+ for (const issue of result.issues) console.error(` - ${issue.message}`);
16935
17039
  }
16936
- const git = await gitInfo(repoPath);
16937
- const artifactPaths = normalizeSubmitArtifactPaths(
16938
- runId,
16939
- manifest.allowedArtifactPaths
16940
- );
16941
- const body = parseCliRequest(
16942
- CliGeoSubmitRequestSchema,
16943
- {
16944
- workspace,
16945
- commitSha: git.headSha,
16946
- artifactPaths,
16947
- mode: context.mode ?? manifest.mode,
16948
- signalsSnapshot: {
16949
- runId,
16950
- source: "local-skillpack",
16951
- mode: context.mode ?? manifest.mode ?? null,
16952
- signals: context.signals ?? null
16953
- },
16954
- systemContext: context.systemContext ?? null
16955
- },
16956
- "Invalid GEO submit request"
16957
- );
16958
- const res = await apiRequest(
16959
- "POST",
16960
- `/api/cli/geo-strategy/runs/${encodeURIComponent(runId)}/submit`,
16961
- body
16962
- );
16963
- if (!res.ok) {
16964
- await printApiFailure(res, "Couldn't submit GEO strategy run", {
16965
- method: "POST",
16966
- path: `/api/cli/geo-strategy/runs/${encodeURIComponent(runId)}/submit`
16967
- });
16968
- process.exit(1);
17040
+ console.error();
17041
+ process.exit(1);
17042
+ }
17043
+ async function resolveMdxInputFiles(repoPath, inputs) {
17044
+ const files = /* @__PURE__ */ new Set();
17045
+ for (const input of inputs) {
17046
+ const matches = await expandMdxInput(repoPath, input);
17047
+ for (const file of matches) files.add(file);
16969
17048
  }
16970
- const payload = parseCliResponse(
16971
- CliGeoSubmitResponseSchema,
16972
- await res.json(),
16973
- "Invalid GEO submit response from backend"
16974
- );
16975
- if (opts.json) {
16976
- console.log(JSON.stringify({ ...payload, repoPath }, null, 2));
16977
- return;
17049
+ if (files.size === 0) {
17050
+ throw new Error("No MDX files matched.");
16978
17051
  }
16979
- console.log();
16980
- console.log(source_default.green.bold(` Submitted GEO run ${runId}`));
16981
- console.log(` ${source_default.bold("Workspace:")} ${workspace}`);
16982
- console.log(` ${source_default.bold("Status:")} ${payload.status}`);
16983
- if (payload.commitSha) console.log(` ${source_default.bold("Commit:")} ${payload.commitSha}`);
16984
- console.log();
17052
+ return [...files].sort();
17053
+ }
17054
+ async function expandMdxInput(repoPath, input) {
17055
+ if (input.includes("*")) return expandSimpleMdxGlob(repoPath, input);
17056
+ const absolute = resolve3(repoPath, input);
17057
+ const info = await stat4(absolute);
17058
+ if (info.isDirectory()) return walkMdxFiles(absolute);
17059
+ if (info.isFile() && absolute.endsWith(".mdx")) return [absolute];
17060
+ return [];
17061
+ }
17062
+ async function expandSimpleMdxGlob(repoPath, pattern) {
17063
+ const normalized = pattern.replaceAll("\\", "/");
17064
+ const prefix = normalized.split("*", 1)[0].replace(/[^/]*$/, "");
17065
+ const base = resolve3(repoPath, prefix || ".");
17066
+ const candidates = await walkMdxFiles(base);
17067
+ const regex2 = globToRegExp(normalized);
17068
+ return candidates.filter((file) => regex2.test(relative2(repoPath, file).replaceAll("\\", "/")));
17069
+ }
17070
+ async function walkMdxFiles(dir) {
17071
+ const entries = await readdir3(dir, { withFileTypes: true });
17072
+ const files = [];
17073
+ for (const entry of entries) {
17074
+ const path2 = join6(dir, entry.name);
17075
+ if (entry.isDirectory()) {
17076
+ files.push(...await walkMdxFiles(path2));
17077
+ } else if (entry.isFile() && entry.name.endsWith(".mdx")) {
17078
+ files.push(path2);
17079
+ }
17080
+ }
17081
+ return files;
17082
+ }
17083
+ function globToRegExp(pattern) {
17084
+ let source = "";
17085
+ for (let i = 0; i < pattern.length; i += 1) {
17086
+ const char = pattern[i];
17087
+ const next = pattern[i + 1];
17088
+ if (char === "*" && next === "*") {
17089
+ source += ".*";
17090
+ i += 1;
17091
+ } else if (char === "*") {
17092
+ source += "[^/]*";
17093
+ } else {
17094
+ source += char.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
17095
+ }
17096
+ }
17097
+ return new RegExp(`^${source}$`);
16985
17098
  }
16986
17099
  async function geoIntentsCommand(runId, opts) {
16987
17100
  const repoPath = await resolveCurrentRepoRoot();
@@ -17080,14 +17193,6 @@ async function readGeoIntentList(runDir) {
17080
17193
  };
17081
17194
  }
17082
17195
  }
17083
- async function readJsonRecord(path2) {
17084
- const raw = await readFile3(path2, "utf8");
17085
- const parsed = JSON.parse(raw);
17086
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17087
- throw new Error(`${path2} must contain a JSON object`);
17088
- }
17089
- return parsed;
17090
- }
17091
17196
  function stringValue(value) {
17092
17197
  return typeof value === "string" && value.length > 0 ? value : null;
17093
17198
  }
@@ -17148,14 +17253,14 @@ function printScaffoldError(prefix, err) {
17148
17253
 
17149
17254
  // src/index.ts
17150
17255
  var program2 = new Command();
17151
- program2.name("anymorph").description("Check AI visibility and run local GEO strategy workflows").version("0.5.0");
17256
+ program2.name("anymorph").description("Check AI visibility and run local GEO strategy workflows").version("0.7.0");
17152
17257
  program2.command("login").description("Sign in to your Anymorph account").addHelpText(
17153
17258
  "after",
17154
17259
  `
17155
17260
  Examples:
17156
17261
  $ anymorph login
17157
17262
 
17158
- Use this before authenticated commands such as workspaces, prepare, and submit.`
17263
+ Use this before authenticated commands such as workspaces and prepare.`
17159
17264
  ).action(loginCommand);
17160
17265
  program2.command("check <domain>").description("Run an AI visibility check for a domain").option("--json", "Output as JSON").addHelpText(
17161
17266
  "after",
@@ -17225,15 +17330,15 @@ Command usage:
17225
17330
  Must be run from the tenant repo root.
17226
17331
 
17227
17332
  anymorph validate <runId> [--json]
17228
- Validate local run files and actions.json before submit.
17333
+ Validate local run files and actions.json before pushing.
17229
17334
  Must be run from the tenant repo root.
17230
17335
 
17231
- anymorph submit <runId> [--workspace <workspaceId>] [--json]
17232
- Submit a validated, pushed local GEO run to Anymorph.
17336
+ anymorph validate mdx <paths...> [--json]
17337
+ Validate CMS MDX component contracts such as Callout tone values.
17233
17338
  Must be run from the tenant repo root.
17234
17339
 
17235
17340
  anymorph run-status <runId> [--json]
17236
- Show backend status for a submitted GEO run.
17341
+ Show backend sync status for a pushed GEO run.
17237
17342
 
17238
17343
  Use "anymorph <command> --help" for examples and option details.`
17239
17344
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anymorph",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Check your brand's AI visibility across ChatGPT, Perplexity, Gemini, and more",
5
5
  "type": "module",
6
6
  "private": false,