dotdog 0.7.1 → 0.8.1

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 (2) hide show
  1. package/dist/cli.js +63 -16
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3421,7 +3421,7 @@ function serve(dir = ".") {
3421
3421
  project: P(dag),
3422
3422
  nodes: N(dag).length,
3423
3423
  edges: E(dag).length,
3424
- version: dag.v || dag.version || "",
3424
+ version: dag.v || dag.version || (Array.isArray(dag) ? `${dag[0]}` : "unknown"),
3425
3425
  order: dag.o || [],
3426
3426
  cycles: dag.cy !== undefined ? dag.cy : null,
3427
3427
  savings: tk.sv || tk.savings_pct || 0,
@@ -3464,6 +3464,18 @@ function serve(dir = ".") {
3464
3464
  }
3465
3465
 
3466
3466
  // src/cli.ts
3467
+ function normalizeDag(dag) {
3468
+ if (Array.isArray(dag)) {
3469
+ const v3Nodes = (dag[2] || []).map((n, i) => {
3470
+ const props = n[2] || [];
3471
+ const states = n[3] || [];
3472
+ const edges = n[4] || [];
3473
+ return [i, n[0], n[1] === "p" ? "prediction" : "entity", "", props, states, edges];
3474
+ });
3475
+ return { v: dag[0], p: dag[1], n: v3Nodes, tk: dag[3] };
3476
+ }
3477
+ return dag;
3478
+ }
3467
3479
  function resolvePath2(p) {
3468
3480
  if (p.startsWith("~"))
3469
3481
  p = join3(homedir2(), p.slice(1));
@@ -3609,20 +3621,31 @@ program2.command("init <project>").option("-m, --minimal", "Only SPEC.dog + data
3609
3621
  console.log(source_default.bold(`
3610
3622
  Project "${p}" initialized. Fill in SPEC.dog then run spec validate.`));
3611
3623
  });
3612
- program2.command("list").action(() => {
3624
+ program2.command("list").option("--json", "Output project names as JSON").action((opts) => {
3625
+ const entries = [];
3613
3626
  for (const d of ["projects", "specs"]) {
3614
3627
  const dd = join3(process.cwd(), d);
3615
3628
  if (!existsSync3(dd))
3616
3629
  continue;
3617
3630
  const projects = readdirSync3(dd, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
3631
+ for (const p of projects) {
3632
+ const sp = join3(dd, p);
3633
+ const n = existsSync3(sp) ? readdirSync3(sp).filter((f) => f.endsWith(".dog")).length : 0;
3634
+ entries.push({ root: d, name: p, dogFiles: n });
3635
+ }
3636
+ }
3637
+ if (opts.json) {
3638
+ console.log(JSON.stringify(entries.map((e) => e.name)));
3639
+ return;
3640
+ }
3641
+ for (const d of ["projects", "specs"]) {
3642
+ const projects = entries.filter((e) => e.root === d);
3618
3643
  if (!projects.length)
3619
3644
  continue;
3620
3645
  console.log(source_default.bold(`
3621
3646
  ${d}/`));
3622
3647
  for (const p of projects) {
3623
- const sp = join3(dd, p);
3624
- const n = existsSync3(sp) ? readdirSync3(sp).filter((f) => f.endsWith(".dog")).length : 0;
3625
- console.log(` ${source_default.cyan(p)} : ${n} .dog files`);
3648
+ console.log(` ${source_default.cyan(p.name)} : ${p.dogFiles} .dog files`);
3626
3649
  }
3627
3650
  }
3628
3651
  });
@@ -3634,7 +3657,7 @@ ${s.length} sections`));
3634
3657
  for (const sec of s)
3635
3658
  console.log(` ${sec.heading.padEnd(30)} ${sec.content.length} chars`);
3636
3659
  });
3637
- program2.command("compile [dir]").option("-o, --output <file>").action((d = ".", opts) => {
3660
+ program2.command("compile [dir]").option("-o, --output <file>").option("--v3", "Ultra-compact v3 format (53% smaller than v2)").action((d = ".", opts) => {
3638
3661
  const dir = resolvePath2(d);
3639
3662
  const dirs = [join3(dir, "projects"), join3(dir, "specs"), dir];
3640
3663
  let found = false;
@@ -3784,16 +3807,36 @@ program2.command("compile [dir]").option("-o, --output <file>").action((d = ".",
3784
3807
  }
3785
3808
  v2nodes.push(entry);
3786
3809
  }
3787
- const dag = { v: 2, p, n: v2nodes };
3788
- const dagJson = JSON.stringify(dag);
3789
- const dagTokens = Math.round(Buffer.byteLength(dagJson, "utf-8") / 4);
3810
+ const outPath = opts.output || join3(pd, `${p}.dag`);
3811
+ const v2dag = { v: 2, p, n: v2nodes };
3812
+ const v2Json = JSON.stringify(v2dag);
3813
+ const dagTokens = Math.round(Buffer.byteLength(v2Json, "utf-8") / 4);
3790
3814
  const allSavingsPct = sourceTokens > 0 ? Math.round((1 - dagTokens / sourceTokens) * 1000) / 10 : 0;
3791
3815
  const contentSavingsPct = contentTokens > 0 ? Math.round((1 - dagTokens / contentTokens) * 1000) / 10 : 0;
3792
3816
  const savingsTokens = sourceTokens - dagTokens;
3793
- const outPath = opts.output || join3(pd, `${p}.dag`);
3794
3817
  const tokens = { m: "chars/4", st: sourceTokens, ct: contentTokens, dt: dagTokens, sv: allSavingsPct, cs: contentSavingsPct, saved: savingsTokens };
3795
- const report = { ...dag, tk: tokens };
3796
- writeFileSync2(outPath, JSON.stringify(report, null, 2));
3818
+ const v3dag = [3, p, v2nodes.map((n) => {
3819
+ const nd2 = nodes[n[0]];
3820
+ const tc = nd2.g === "prediction" ? "p" : "e";
3821
+ const st = nd2.s && nd2.s.length ? nd2.s : null;
3822
+ const ed = n[6] && n[6].length ? n[6] : null;
3823
+ const entry = [nd2.i, tc, n[4].length ? n[4] : null, st, ed];
3824
+ if (nd2.g === "prediction") {
3825
+ const f = [];
3826
+ if (nd2.cf != null)
3827
+ f.push(nd2.cf);
3828
+ if (nd2.tf)
3829
+ f.push(nd2.tf);
3830
+ if (f.length)
3831
+ entry.push(f);
3832
+ }
3833
+ return entry;
3834
+ }), tokens];
3835
+ const dag = opts.v3 ? v3dag : v2dag;
3836
+ const dagJson = JSON.stringify(dag);
3837
+ const report = opts.v3 ? [...dag, tokens] : { ...dag, tk: tokens };
3838
+ writeFileSync2(outPath, JSON.stringify(report, null, 2) + `
3839
+ `);
3797
3840
  console.log(source_default.green(` ✓ ${outPath}`));
3798
3841
  console.log(source_default.gray(` ${nodes.length} nodes, ${edges.length} edges, ${files.length} files`));
3799
3842
  console.log(source_default.gray(` ${sourceTokens} → ${dagTokens} tokens (${allSavingsPct}% savings, ${contentSavingsPct}% content-only, ${savingsTokens} saved)`));
@@ -3858,7 +3901,8 @@ program2.command("visualize [dir]").option("-s, --save").action((d = ".", opts)
3858
3901
  if (!existsSync3(dagFile))
3859
3902
  continue;
3860
3903
  const dag = JSON.parse(readFileSync3(dagFile, "utf-8"));
3861
- const nodes = dag.n || dag.nodes || [];
3904
+ const d2 = normalizeDag(dag);
3905
+ const nodes = d2.n || dag.nodes || [];
3862
3906
  const isV22 = (n) => Array.isArray(n) && typeof n[0] === "number";
3863
3907
  const nodeName2 = (n) => isV22(n) ? nodes[n[0]] ? nodes[n[0]][1] || String(n[0]) : String(n[0]) : n.i || n.id || "";
3864
3908
  const slug = (s) => s.replace(/\s+/g, "_").replace(/^[^a-zA-Z]+/, "n_");
@@ -4198,7 +4242,8 @@ program2.command("simulate <scenario>").description("Walk through a scenario, ch
4198
4242
  let entities = [], relationships = [];
4199
4243
  if (existsSync3(dagFile)) {
4200
4244
  const dag = JSON.parse(readFileSync3(dagFile, "utf-8"));
4201
- const simNodes = dag.n || dag.nodes || [];
4245
+ const d2 = normalizeDag(dag);
4246
+ const simNodes = d2.n || dag.nodes || [];
4202
4247
  const isV22 = (n) => Array.isArray(n) && typeof n[0] === "number";
4203
4248
  entities = simNodes.map((n) => (isV22(n) ? n[1] || String(n[0]) : n.i || n.id || "").toLowerCase());
4204
4249
  if (dag.e || dag.edges) {
@@ -4369,7 +4414,8 @@ program2.command("verify [dir]").description("Verify spec-code alignment. --init
4369
4414
  if (opts.init) {
4370
4415
  const entities = [];
4371
4416
  const props = new Map;
4372
- for (const node of dag.n || []) {
4417
+ const d3 = normalizeDag(dag);
4418
+ for (const node of d3.n || []) {
4373
4419
  const name = node[1] || String(node[0]);
4374
4420
  entities.push(name);
4375
4421
  if (node[4])
@@ -4719,7 +4765,8 @@ program2.command("badge [dir]").description("Generate dotdog-badge.svg (shields.
4719
4765
  console.log(source_default.red(` No .dag for ${p}. Run dotdog compile first.`));
4720
4766
  continue;
4721
4767
  }
4722
- const dag = JSON.parse(readFileSync3(dagFile, "utf-8"));
4768
+ const rawDag = JSON.parse(readFileSync3(dagFile, "utf-8"));
4769
+ const dag = normalizeDag(rawDag);
4723
4770
  const saved = dag.tk && dag.tk.saved ? dag.tk.saved : 0;
4724
4771
  const fmt = saved >= 1000 ? `${(saved / 1000).toFixed(1)}K` : `${saved}`;
4725
4772
  const label = "dotdog";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotdog",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
4
4
  "description": "CLI tool for structured software specifications. Validate .dog files, compile .dag graphs, query via MCP.",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",