dotdog 0.5.0 → 0.6.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/dist/cli.js CHANGED
@@ -2751,7 +2751,7 @@ function parseBlocks(lines, start, end) {
2751
2751
  let i = start;
2752
2752
  while (i < end) {
2753
2753
  const line = lines[i];
2754
- const entityMatch = line.match(/^###\s+Entity:\s*(.+)/);
2754
+ const entityMatch = line.match(/^#{3,5}\s+Entity:\s*(.+)/);
2755
2755
  if (entityMatch) {
2756
2756
  const result = parseStructuredBlock(lines, i, end, "entity", entityMatch[1]);
2757
2757
  if (result) {
@@ -2760,7 +2760,7 @@ function parseBlocks(lines, start, end) {
2760
2760
  continue;
2761
2761
  }
2762
2762
  }
2763
- const relMatch = line.match(/^###\s+Relationship:\s*(.+)/);
2763
+ const relMatch = line.match(/^#{3,5}\s+Relationship:\s*(.+)/);
2764
2764
  if (relMatch) {
2765
2765
  const result = parseStructuredBlock(lines, i, end, "relationship", relMatch[1]);
2766
2766
  if (result) {
@@ -2769,7 +2769,7 @@ function parseBlocks(lines, start, end) {
2769
2769
  continue;
2770
2770
  }
2771
2771
  }
2772
- const eventMatch = line.match(/^###\s+Event:\s*(.+)/);
2772
+ const eventMatch = line.match(/^#{3,5}\s+Event:\s*(.+)/);
2773
2773
  if (eventMatch) {
2774
2774
  const result = parseStructuredBlock(lines, i, end, "event", eventMatch[1]);
2775
2775
  if (result) {
@@ -2778,7 +2778,7 @@ function parseBlocks(lines, start, end) {
2778
2778
  continue;
2779
2779
  }
2780
2780
  }
2781
- const predMatch = line.match(/^###\s+Prediction:\s*(.+)/);
2781
+ const predMatch = line.match(/^#{3,5}\s+Prediction:\s*(.+)/);
2782
2782
  if (predMatch) {
2783
2783
  const result = parseStructuredBlock(lines, i, end, "prediction", predMatch[1]);
2784
2784
  if (result) {
@@ -2787,6 +2787,83 @@ function parseBlocks(lines, start, end) {
2787
2787
  continue;
2788
2788
  }
2789
2789
  }
2790
+ if (lines[i].startsWith("```")) {
2791
+ let yamlEnd = i + 1;
2792
+ while (yamlEnd < end && !lines[yamlEnd].startsWith("```"))
2793
+ yamlEnd++;
2794
+ if (yamlEnd < end) {
2795
+ const yamlContent = lines.slice(i + 1, yamlEnd);
2796
+ const yaml = parseSimpleYAML(yamlContent);
2797
+ if (yaml.prediction) {
2798
+ blocks.push({
2799
+ kind: "prediction",
2800
+ statement: yaml.prediction || "",
2801
+ description: yaml.description || "",
2802
+ trigger: yaml.trigger || "",
2803
+ timeframe: yaml.timeframe || "",
2804
+ confidence: yaml.confidence || 0,
2805
+ measurement: yaml.measurement || "",
2806
+ status: yaml.status || "pending",
2807
+ yaml,
2808
+ lineStart: i + 1,
2809
+ lineEnd: yamlEnd
2810
+ });
2811
+ i = yamlEnd + 1;
2812
+ continue;
2813
+ }
2814
+ if (yaml.entity) {
2815
+ blocks.push({
2816
+ kind: "entity",
2817
+ name: yaml.entity || "",
2818
+ description: yaml.description || "",
2819
+ type: yaml.type || "node",
2820
+ properties: {},
2821
+ states: Array.isArray(yaml.states) ? yaml.states : [],
2822
+ lifecycle: [],
2823
+ yaml,
2824
+ lineStart: i + 1,
2825
+ lineEnd: yamlEnd
2826
+ });
2827
+ i = yamlEnd + 1;
2828
+ continue;
2829
+ }
2830
+ if (yaml.relationship || yaml.verb) {
2831
+ blocks.push({
2832
+ kind: "relationship",
2833
+ source: yaml.source || "",
2834
+ target: yaml.target || "",
2835
+ verb: yaml.verb || "connects",
2836
+ description: yaml.description || "",
2837
+ cardinality: yaml.cardinality || "N:M",
2838
+ required: false,
2839
+ cascade: "none",
2840
+ invariants: [],
2841
+ yaml,
2842
+ lineStart: i + 1,
2843
+ lineEnd: yamlEnd
2844
+ });
2845
+ i = yamlEnd + 1;
2846
+ continue;
2847
+ }
2848
+ if (yaml.event) {
2849
+ blocks.push({
2850
+ kind: "event",
2851
+ name: yaml.event || "",
2852
+ trigger: yaml.trigger || "",
2853
+ payload: {},
2854
+ preconditions: [],
2855
+ postconditions: [],
2856
+ sideEffects: [],
2857
+ probability: null,
2858
+ yaml,
2859
+ lineStart: i + 1,
2860
+ lineEnd: yamlEnd
2861
+ });
2862
+ i = yamlEnd + 1;
2863
+ continue;
2864
+ }
2865
+ }
2866
+ }
2790
2867
  if (/^\|.+\|/.test(line) && i + 1 < end && /^\|[-| ]+\|/.test(lines[i + 1])) {
2791
2868
  const table = parseTable(lines, i, end);
2792
2869
  if (table) {
@@ -2795,8 +2872,46 @@ function parseBlocks(lines, start, end) {
2795
2872
  continue;
2796
2873
  }
2797
2874
  }
2875
+ if (lines[i].startsWith("```")) {
2876
+ let yamlEnd = i + 1;
2877
+ while (yamlEnd < end && !lines[yamlEnd].startsWith("```"))
2878
+ yamlEnd++;
2879
+ if (yamlEnd < end && yamlEnd > i + 1) {
2880
+ const yamlContent = lines.slice(i + 1, yamlEnd);
2881
+ const yaml = parseSimpleYAML(yamlContent);
2882
+ const key = yaml.prediction ? "prediction" : yaml.entity ? "entity" : yaml.event ? "event" : yaml.relationship || yaml.verb ? "relationship" : null;
2883
+ if (key) {
2884
+ if (key === "prediction") {
2885
+ blocks.push({ kind: "prediction", statement: yaml.prediction || "", description: yaml.description || "", trigger: yaml.trigger || "", timeframe: yaml.timeframe || "", confidence: yaml.confidence || 0, measurement: yaml.measurement || "", status: yaml.status || "pending", yaml, lineStart: i + 1, lineEnd: yamlEnd });
2886
+ } else if (key === "entity") {
2887
+ blocks.push({ kind: "entity", name: yaml.entity || "", description: yaml.description || "", type: yaml.type || "node", properties: {}, states: Array.isArray(yaml.states) ? yaml.states : [], lifecycle: [], yaml, lineStart: i + 1, lineEnd: yamlEnd });
2888
+ } else if (key === "event") {
2889
+ blocks.push({ kind: "event", name: yaml.event || "", trigger: yaml.trigger || "", payload: {}, preconditions: [], postconditions: [], sideEffects: [], probability: null, yaml, lineStart: i + 1, lineEnd: yamlEnd });
2890
+ } else if (key === "relationship") {
2891
+ blocks.push({ kind: "relationship", source: yaml.source || "", target: yaml.target || "", verb: yaml.verb || "connects", description: yaml.description || "", cardinality: yaml.cardinality || "N:M", required: false, cascade: "none", invariants: [], yaml, lineStart: i + 1, lineEnd: yamlEnd });
2892
+ }
2893
+ i = yamlEnd + 1;
2894
+ continue;
2895
+ }
2896
+ }
2897
+ }
2898
+ if (lines[i].startsWith("```")) {
2899
+ let fenceEnd = i + 1;
2900
+ while (fenceEnd < end && !lines[fenceEnd].startsWith("```"))
2901
+ fenceEnd++;
2902
+ const blockEnd = fenceEnd < end ? fenceEnd + 1 : end;
2903
+ blocks.push({
2904
+ kind: "prose",
2905
+ content: lines.slice(i, blockEnd).join(`
2906
+ `).trim(),
2907
+ lineStart: i + 1,
2908
+ lineEnd: blockEnd
2909
+ });
2910
+ i = blockEnd;
2911
+ continue;
2912
+ }
2798
2913
  const proseStart = i;
2799
- while (i < end && !isBlockStart(lines[i])) {
2914
+ while (i < end && !isBlockStart(lines[i]) && !lines[i].startsWith("```")) {
2800
2915
  i++;
2801
2916
  }
2802
2917
  const proseLines = lines.slice(proseStart, i).filter((l) => l.trim() !== "" || i === proseStart + 1);
@@ -2813,7 +2928,7 @@ function parseBlocks(lines, start, end) {
2813
2928
  return blocks;
2814
2929
  }
2815
2930
  function isBlockStart(line) {
2816
- return /^###\s+(Entity|Relationship|Event|Prediction):/.test(line) || /^\|.+\|/.test(line);
2931
+ return /^#{3,5}\s+(Entity|Relationship|Event|Prediction):/.test(line) || /^\|.+\|/.test(line);
2817
2932
  }
2818
2933
  function parseStructuredBlock(lines, start, end, kind, headerRest) {
2819
2934
  let i = start + 1;
@@ -3390,7 +3505,7 @@ program2.command("validate [dir]").action((d = ".") => {
3390
3505
  const missing = ["SPEC.dog", "constitution.dog", "data-model.dog"].filter((f) => !files.includes(f));
3391
3506
  const optional = ["COPY.dog", "plan.dog", "DESIGN-SYSTEM.dog", "INDEX.dog"].filter((f) => !files.includes(f));
3392
3507
  console.log(source_default.bold(`
3393
- ${p} : ${files.length} .dog files, ${100 - Math.round((missing.length * 3 + optional.length) / 20 * 100)}% complete`));
3508
+ ${p} : ${files.length} .dog files, ${Math.max(0, 100 - Math.round(missing.length * 3 / 20 * 100))}% complete`));
3394
3509
  for (const f of files)
3395
3510
  console.log(source_default.gray(` ${f}`));
3396
3511
  if (missing.length) {
@@ -3398,7 +3513,7 @@ program2.command("validate [dir]").action((d = ".") => {
3398
3513
  hasErrors = true;
3399
3514
  }
3400
3515
  if (optional.length)
3401
- console.log(source_default.yellow(` Missing optional: ${optional.join(", ")}`));
3516
+ console.log(source_default.gray(` Optional: ${optional.join(", ")} — not required for 100%`));
3402
3517
  }
3403
3518
  }
3404
3519
  if (!found)
@@ -3832,7 +3947,7 @@ Spec Analysis
3832
3947
  for (const f of missingReq)
3833
3948
  gaps.push(`\uD83D\uDD34 ${f}: Missing required file`);
3834
3949
  for (const f of missingOpt)
3835
- gaps.push(`\uD83D\uDFE1 ${f}: Missing optional file`);
3950
+ gaps.push(`ℹ️ ${f}: Optional file not present`);
3836
3951
  const entityNames = new Set(allEntities.map((e) => e.name));
3837
3952
  for (const e of allEntities) {
3838
3953
  if (!e.description || e.description.length < 10)
@@ -4469,8 +4584,19 @@ program2.command("resolve <name>").description("Mark a prediction as correct, wr
4469
4584
  if (block.kind === "prediction") {
4470
4585
  const b = block;
4471
4586
  if ((b.statement || b.name || "").toLowerCase().includes(name.toLowerCase())) {
4472
- const searchFor = `### Prediction: ${b.statement || b.name}`;
4473
- const headingIdx = content.indexOf(searchFor);
4587
+ let headingIdx = -1;
4588
+ const stmt = b.statement || b.name || "";
4589
+ for (const prefix of ["###", "####", "#####"]) {
4590
+ for (const fmt of [`${prefix} Prediction: ${stmt}`, `${prefix} ${stmt}`]) {
4591
+ const idx = content.indexOf(fmt);
4592
+ if (idx >= 0) {
4593
+ headingIdx = idx;
4594
+ break;
4595
+ }
4596
+ }
4597
+ if (headingIdx >= 0)
4598
+ break;
4599
+ }
4474
4600
  if (headingIdx >= 0) {
4475
4601
  const blockStart = content.indexOf("```yaml", headingIdx);
4476
4602
  const blockEnd = content.indexOf("```", blockStart + 7);
@@ -0,0 +1,39 @@
1
+ # DAO Governance
2
+
3
+ > Community governance starter. Members create proposals, vote, and execute approved treasury actions.
4
+
5
+ ## Product
6
+
7
+ A DAO governance workspace. Members join with voting power, submit proposals, cast votes, and execute passed proposals against a shared treasury.
8
+
9
+ ## What the User Sees
10
+
11
+ ### Screen: Proposal List
12
+
13
+ +------------------------------------------+
14
+ | DAO Proposals [New] |
15
+ |------------------------------------------|
16
+ | Upgrade Grants Program Active |
17
+ | Fund Security Audit Passed |
18
+ | Sponsor Builder Event Draft |
19
+ +------------------------------------------+
20
+
21
+ ### Screen: Proposal Detail
22
+
23
+ +------------------------------------------+
24
+ | Upgrade Grants Program |
25
+ |------------------------------------------|
26
+ | Status: Active |
27
+ | For: 62% Against: 18% Abstain: 20% |
28
+ | |
29
+ | [Vote For] [Vote Against] [Abstain] |
30
+ +------------------------------------------+
31
+
32
+ ## User Stories
33
+
34
+ | ID | Story | Pri | Acceptance |
35
+ |----|-------|-----|------------|
36
+ | US-01 | Member creates a proposal | P0 | Proposal appears as draft |
37
+ | US-02 | Member casts a vote | P0 | Vote is counted once |
38
+ | US-03 | Proposal reaches quorum | P0 | Status becomes passed or rejected |
39
+ | US-04 | Approved proposal executes | P1 | Treasury allocation is recorded |
@@ -0,0 +1,9 @@
1
+ # DAO Governance Constitution
2
+
3
+ ## Core Rules
4
+
5
+ 1. One member can cast one vote per proposal.
6
+ 2. Proposal execution requires both quorum and majority approval.
7
+ 3. Treasury allocations must reference an approved proposal.
8
+ 4. Proposal state changes are append-only and auditable.
9
+ 5. Voting power is read at the proposal snapshot block.
@@ -0,0 +1,134 @@
1
+ # DAO Governance Data Model
2
+
3
+ ## Entities
4
+
5
+ ### Entity: Member
6
+
7
+ A DAO participant with voting power.
8
+
9
+ ```yaml
10
+ entity: Member
11
+ type: entity
12
+ properties:
13
+ id:
14
+ type: string
15
+ required: true
16
+ wallet:
17
+ type: string
18
+ required: true
19
+ voting_power:
20
+ type: number
21
+ required: true
22
+ states: [active, delegated, suspended]
23
+ lifecycle: active → delegated → suspended
24
+ ```
25
+
26
+ ### Entity: Proposal
27
+
28
+ A governance item submitted for member voting.
29
+
30
+ ```yaml
31
+ entity: Proposal
32
+ type: entity
33
+ properties:
34
+ id:
35
+ type: string
36
+ required: true
37
+ title:
38
+ type: string
39
+ required: true
40
+ quorum_required:
41
+ type: number
42
+ required: true
43
+ status:
44
+ type: enum
45
+ required: true
46
+ states: [draft, active, passed, rejected, executed]
47
+ lifecycle: draft → active → passed → executed
48
+ ```
49
+
50
+ ### Entity: Vote
51
+
52
+ A member decision on a proposal.
53
+
54
+ ```yaml
55
+ entity: Vote
56
+ type: event
57
+ properties:
58
+ member_id:
59
+ type: string
60
+ required: true
61
+ proposal_id:
62
+ type: string
63
+ required: true
64
+ choice:
65
+ type: enum
66
+ required: true
67
+ weight:
68
+ type: number
69
+ required: true
70
+ states: [cast, counted]
71
+ lifecycle: cast → counted
72
+ ```
73
+
74
+ ### Entity: TreasuryAllocation
75
+
76
+ Funds allocated by an executed proposal.
77
+
78
+ ```yaml
79
+ entity: TreasuryAllocation
80
+ type: entity
81
+ properties:
82
+ id:
83
+ type: string
84
+ required: true
85
+ proposal_id:
86
+ type: string
87
+ required: true
88
+ amount:
89
+ type: number
90
+ required: true
91
+ recipient:
92
+ type: string
93
+ required: true
94
+ states: [approved, executed, canceled]
95
+ lifecycle: approved → executed
96
+ ```
97
+
98
+ ## Relationships
99
+
100
+ ### Relationship: Member → Proposal
101
+
102
+ ```yaml
103
+ relationship: Member → Proposal
104
+ verb: submits
105
+ cardinality: 1:N
106
+ required: true
107
+ ```
108
+
109
+ ### Relationship: Member → Vote
110
+
111
+ ```yaml
112
+ relationship: Member → Vote
113
+ verb: casts
114
+ cardinality: 1:N
115
+ required: true
116
+ ```
117
+
118
+ ### Relationship: Proposal → Vote
119
+
120
+ ```yaml
121
+ relationship: Proposal → Vote
122
+ verb: receives
123
+ cardinality: 1:N
124
+ required: true
125
+ ```
126
+
127
+ ### Relationship: Proposal → TreasuryAllocation
128
+
129
+ ```yaml
130
+ relationship: Proposal → TreasuryAllocation
131
+ verb: authorizes
132
+ cardinality: 1:1
133
+ required: false
134
+ ```
@@ -0,0 +1,8 @@
1
+ # SaaS Platform Constitution
2
+
3
+ ## Core Rules
4
+
5
+ 1. Organizations own their workspace data.
6
+ 2. Users access data only through organization membership.
7
+ 3. Plan limits must be enforced before billable usage is accepted.
8
+ 4. Subscription state controls access to paid features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotdog",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
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",
@@ -47,5 +47,8 @@
47
47
  "homepage": "https://specdog.github.io/dotdog",
48
48
  "bugs": {
49
49
  "url": "https://github.com/specdog/dotdog/issues"
50
+ },
51
+ "scripts": {
52
+ "lint": "bun run --check src/"
50
53
  }
51
54
  }