@markdy/cli 0.8.20 → 0.8.22

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 +14 -10
  2. package/dist/index.js +145 -4
  3. package/package.json +5 -4
package/README.md CHANGED
@@ -35,16 +35,20 @@ npx markdy render examples/showcase/bullet-reveal.markdy --out examples/xscene.h
35
35
  ## Commands
36
36
 
37
37
  ```bash
38
- markdy # launch a local browser playground on http://127.0.0.1:4242
39
- markdy lint scene.markdy
40
- markdy fmt scene.markdy
41
- markdy fmt scene.markdy --write
42
- markdy render scene.markdy --out dist/scene.html
43
- markdy explain scene.markdy
44
- markdy new explainer demo.markdy
45
- markdy docs
46
- markdy ai
47
- markdy check-all .
38
+ markdy # launch a local browser playground on http://127.0.0.1:4242
39
+ markdy lint scene.markdy # syntax validation
40
+ markdy lint scene.markdy --arch-rules # run Well-Architected governance & cycle rules
41
+ markdy fmt scene.markdy --write # format MarkdyScript file
42
+ markdy import flow.mmd --out scene.markdy # import Mermaid, draw.io, Compose, K8s, or Terraform
43
+ markdy diff v1.markdy v2.markdy # semantic AST diff summary table
44
+ markdy diff v1.markdy v2.markdy --evolution # generate animated migration scene
45
+ markdy share scene.markdy # create compressed playground link
46
+ markdy render scene.markdy --out dist/scene.html # export self-contained HTML preview
47
+ markdy explain scene.markdy # display AST structure and stats
48
+ markdy new demo.markdy # scaffold fresh starter scene
49
+ markdy docs # display docs and tutorial links
50
+ markdy ai # generate prompt context for LLMs
51
+ markdy check-all . --arch-rules # batch lint entire workspace
48
52
  ```
49
53
 
50
54
  ## Notes
package/dist/index.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { parse } from "@markdy/core";
4
+ import {
5
+ parse,
6
+ validateArchitecture,
7
+ resolveArchitectureConfig,
8
+ diffDiagramASTs,
9
+ compressMarkdyToUrlHash
10
+ } from "@markdy/core";
5
11
  import { createRequire } from "module";
6
12
  import { basename, dirname, extname, join, resolve, sep } from "path";
7
13
  import { fileURLToPath, pathToFileURL } from "url";
@@ -140,6 +146,13 @@ function formatValue(value) {
140
146
  var DEFAULT_PORT = 4242;
141
147
  var MARKDY_EXT = ".markdy";
142
148
  var PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
149
+ var compatModulePromise = null;
150
+ function loadCompatModule() {
151
+ if (!compatModulePromise) {
152
+ compatModulePromise = import("@markdy/compat");
153
+ }
154
+ return compatModulePromise;
155
+ }
143
156
  async function runCli(argv, io = defaultIo(), runtime = defaultRuntime()) {
144
157
  const parsed = parseArgv(argv);
145
158
  if (hasFlag(parsed, "help")) {
@@ -159,6 +172,12 @@ async function runCli(argv, io = defaultIo(), runtime = defaultRuntime()) {
159
172
  return renderCommand(parsed, io, runtime);
160
173
  case "explain":
161
174
  return explainCommand(parsed, io);
175
+ case "import":
176
+ return importCommand(parsed, io);
177
+ case "diff":
178
+ return diffCommand(parsed, io);
179
+ case "share":
180
+ return shareCommand(parsed, io);
162
181
  case "new":
163
182
  return newCommand(parsed, io);
164
183
  case "docs":
@@ -181,14 +200,50 @@ async function lintCommand(parsed, io) {
181
200
  return { exitCode: 1 };
182
201
  }
183
202
  const strict = hasFlag(parsed, "strict");
203
+ const checkArchRules = hasFlag(parsed, "arch-rules");
204
+ const configPath = getStringFlag(parsed, "config");
184
205
  const cache = /* @__PURE__ */ new Map();
185
206
  let warningCount = 0;
186
207
  let errorCount = 0;
208
+ let customRules;
209
+ if (configPath) {
210
+ try {
211
+ const raw = await readFile(resolve(process.cwd(), configPath), "utf-8");
212
+ const json = JSON.parse(raw);
213
+ customRules = resolveArchitectureConfig(json);
214
+ } catch (err) {
215
+ io.stderr(`markdy lint: failed to read config "${configPath}": ${err.message}`);
216
+ return { exitCode: 1 };
217
+ }
218
+ } else {
219
+ for (const defaultName of [".markdyrc.json", "markdy.config.json", ".markdyrc"]) {
220
+ try {
221
+ const candidate = resolve(process.cwd(), defaultName);
222
+ const raw = await readFile(candidate, "utf-8");
223
+ const json = JSON.parse(raw);
224
+ customRules = resolveArchitectureConfig(json);
225
+ break;
226
+ } catch {
227
+ }
228
+ }
229
+ }
187
230
  for (const file of files) {
188
231
  try {
189
232
  const scene = await loadSceneFromFile(file, cache);
190
233
  io.stdout(`OK ${file}`);
191
234
  warningCount += printWarnings(scene.ast.diagnostics, file, io);
235
+ if (checkArchRules || customRules) {
236
+ const violations = validateArchitecture(scene.ast, customRules);
237
+ for (const v of violations) {
238
+ if (v.severity === "error") {
239
+ errorCount++;
240
+ io.stderr(`ARCH_FAIL ${file}:${v.line ?? 1} [${v.ruleName}] ${v.message}`);
241
+ } else {
242
+ warningCount++;
243
+ io.stderr(`ARCH_WARN ${file}:${v.line ?? 1} [${v.ruleName}] ${v.message}`);
244
+ }
245
+ }
246
+ }
192
247
  } catch (error) {
193
248
  errorCount++;
194
249
  io.stderr(`FAIL ${file}`);
@@ -310,6 +365,89 @@ async function newCommand(parsed, io) {
310
365
  io.stdout(`Created ${resolvedTarget}`);
311
366
  return { exitCode: 0 };
312
367
  }
368
+ async function importCommand(parsed, io) {
369
+ const inputFile = parsed.positionals[0];
370
+ if (!inputFile) {
371
+ io.stderr("markdy import: expected an input file (e.g. diagram.mmd, docker-compose.yml, manifests.yaml, terraform.tfstate)");
372
+ return { exitCode: 1 };
373
+ }
374
+ const content = await readFile(resolve(inputFile), "utf8").catch((err) => {
375
+ io.stderr(`markdy import: failed to read ${inputFile}: ${describeError(err)}`);
376
+ return null;
377
+ });
378
+ if (content === null) return { exitCode: 1 };
379
+ const formatFlag = getStringFlag(parsed, "from")?.toLowerCase();
380
+ const ext = extname(inputFile).toLowerCase();
381
+ const title = basename(inputFile, extname(inputFile));
382
+ const compat = await loadCompatModule().catch((err) => {
383
+ io.stderr(`markdy import: failed to load @markdy/compat: ${describeError(err)}`);
384
+ return null;
385
+ });
386
+ if (!compat) return { exitCode: 1 };
387
+ let markdyCode;
388
+ if (formatFlag === "mermaid" || ext === ".mmd" || ext === ".mermaid") {
389
+ markdyCode = compat.transpileMermaidToMarkdy(content, title).code;
390
+ } else if (formatFlag === "compose" || (ext === ".yml" || ext === ".yaml") && (inputFile.includes("compose") || content.includes("services:"))) {
391
+ markdyCode = compat.transpileDockerComposeToMarkdy(content, title);
392
+ } else if (formatFlag === "k8s" || content.includes("apiVersion:") && content.includes("kind:")) {
393
+ markdyCode = compat.transpileKubernetesManifestsToMarkdy(content, title);
394
+ } else if (formatFlag === "terraform" || ext === ".tfstate" || (content.includes("terraform_version") || content.includes('"resources":'))) {
395
+ markdyCode = compat.transpileTerraformStateToMarkdy(content, title);
396
+ } else if (formatFlag === "drawio" || ext === ".drawio" || ext === ".xml" && content.includes("<mxCell")) {
397
+ markdyCode = compat.transpileDrawioToMarkdy(content, title).code;
398
+ } else {
399
+ markdyCode = compat.transpileMermaidToMarkdy(content, title).code;
400
+ }
401
+ const outPath = getStringFlag(parsed, "out");
402
+ if (outPath) {
403
+ const resolvedOut = resolve(outPath);
404
+ await writeFile(resolvedOut, markdyCode, "utf8");
405
+ io.stdout(`Wrote ${resolvedOut}`);
406
+ } else {
407
+ io.stdout(markdyCode);
408
+ }
409
+ return { exitCode: 0 };
410
+ }
411
+ async function diffCommand(parsed, io) {
412
+ const file1 = parsed.positionals[0];
413
+ const file2 = parsed.positionals[1];
414
+ if (!file1 || !file2) {
415
+ io.stderr("markdy diff: expected two .markdy files to compare (e.g. markdy diff before.markdy after.markdy)");
416
+ return { exitCode: 1 };
417
+ }
418
+ const scene1 = await loadSceneFromFile(file1).catch((err) => {
419
+ io.stderr(`markdy diff: ${describeError(err)}`);
420
+ return null;
421
+ });
422
+ const scene2 = await loadSceneFromFile(file2).catch((err) => {
423
+ io.stderr(`markdy diff: ${describeError(err)}`);
424
+ return null;
425
+ });
426
+ if (!scene1 || !scene2) return { exitCode: 1 };
427
+ const diffResult = diffDiagramASTs(scene1.ast, scene2.ast);
428
+ if (hasFlag(parsed, "evolution")) {
429
+ io.stdout(diffResult.evolutionMarkdyScript);
430
+ return { exitCode: 0 };
431
+ }
432
+ io.stdout(diffResult.summaryMarkdown);
433
+ return { exitCode: 0 };
434
+ }
435
+ async function shareCommand(parsed, io) {
436
+ const file = parsed.positionals[0];
437
+ if (!file) {
438
+ io.stderr("markdy share: expected a .markdy input file");
439
+ return { exitCode: 1 };
440
+ }
441
+ const scene = await loadSceneFromFile(file).catch((err) => {
442
+ io.stderr(`markdy share: ${describeError(err)}`);
443
+ return null;
444
+ });
445
+ if (!scene) return { exitCode: 1 };
446
+ const hash = await compressMarkdyToUrlHash(scene.source);
447
+ const shareUrl = `https://markdy.com/playground/#code=${hash}`;
448
+ io.stdout(shareUrl);
449
+ return { exitCode: 0 };
450
+ }
313
451
  async function docsCommand(parsed, io, runtime) {
314
452
  const docsUrl = "https://markdy.com";
315
453
  const links = [
@@ -692,14 +830,17 @@ function helpText() {
692
830
  "",
693
831
  "Usage:",
694
832
  " markdy",
695
- " markdy lint <file-or-dir> [--strict]",
833
+ " markdy lint <file-or-dir> [--strict] [--arch-rules]",
696
834
  " markdy fmt <file-or-dir> [--write | --check]",
697
835
  " markdy render <file.markdy> [--out file.html] [--port 4242] [--no-open]",
698
836
  " markdy explain <file.markdy> [--json]",
837
+ " markdy import <file> [--from compose|k8s|terraform|mermaid] [--out scene.markdy]",
838
+ " markdy diff <before.markdy> <after.markdy> [--evolution]",
839
+ " markdy share <file.markdy>",
699
840
  " markdy new [target.markdy] [--force]",
700
841
  " markdy docs [--open]",
701
842
  " markdy ai [--open]",
702
- " markdy check-all [dir] [--strict]"
843
+ " markdy check-all [dir] [--strict] [--arch-rules]"
703
844
  ].join("\n");
704
845
  }
705
846
  function parseArgv(argv) {
@@ -758,7 +899,7 @@ function parseArgv(argv) {
758
899
  return { command, positionals, flags };
759
900
  }
760
901
  function expectsValue(flag) {
761
- return flag === "out" || flag === "port";
902
+ return flag === "out" || flag === "port" || flag === "config";
762
903
  }
763
904
  function hasFlag(parsed, name) {
764
905
  return parsed.flags.get(name) === true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/cli",
3
- "version": "0.8.20",
3
+ "version": "0.8.22",
4
4
  "description": "First-party CLI for diagram-native MarkdyScript diagrams: lint, format, explain, render, and preview.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,9 +42,10 @@
42
42
  "access": "public"
43
43
  },
44
44
  "dependencies": {
45
- "@markdy/core": "0.8.20",
46
- "@markdy/renderer-dom": "0.8.20",
47
- "@markdy/stdlib-systems": "0.8.20"
45
+ "@markdy/compat": "0.8.22",
46
+ "@markdy/renderer-dom": "0.8.22",
47
+ "@markdy/stdlib-systems": "0.8.22",
48
+ "@markdy/core": "0.8.22"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@types/node": "^25.9.5",