modscape 2.0.2 → 2.0.3

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/src/table.js ADDED
@@ -0,0 +1,118 @@
1
+ import { Command } from 'commander';
2
+ import { readYaml, writeYaml, findTableById, outputError, outputWarn, outputOk } from './model-utils.js';
3
+
4
+ export function tableCommand() {
5
+ const cmd = new Command('table').description('Manage tables in a YAML model');
6
+
7
+ // list
8
+ cmd
9
+ .command('list <file>')
10
+ .description('List all tables in the model')
11
+ .option('--json', 'output as JSON')
12
+ .action((file, opts) => {
13
+ const data = readYaml(file);
14
+ const tables = (data.tables || []).map(t => ({ id: t.id, name: t.name }));
15
+ if (opts.json) {
16
+ console.log(JSON.stringify(tables));
17
+ } else {
18
+ if (tables.length === 0) {
19
+ console.log(' (no tables)');
20
+ } else {
21
+ tables.forEach(t => console.log(` ${t.id} ${t.name || ''}`));
22
+ }
23
+ }
24
+ });
25
+
26
+ // get
27
+ cmd
28
+ .command('get <file>')
29
+ .description('Get a table definition by ID')
30
+ .requiredOption('--id <id>', 'table ID')
31
+ .option('--json', 'output as JSON')
32
+ .action((file, opts) => {
33
+ const data = readYaml(file);
34
+ const table = findTableById(data, opts.id);
35
+ if (!table) return outputError(opts.json, `Table "${opts.id}" not found`);
36
+ if (opts.json) {
37
+ console.log(JSON.stringify(table));
38
+ } else {
39
+ console.log(JSON.stringify(table, null, 2));
40
+ }
41
+ });
42
+
43
+ // add
44
+ cmd
45
+ .command('add <file>')
46
+ .description('Add a new table to the model')
47
+ .requiredOption('--id <id>', 'table ID (snake_case)')
48
+ .requiredOption('--name <name>', 'conceptual name')
49
+ .option('--type <type>', 'appearance type (fact|dimension|mart|hub|link|satellite|table)')
50
+ .option('--logical-name <name>', 'logical (business) name')
51
+ .option('--physical-name <name>', 'physical (database) table name')
52
+ .option('--description <text>', 'conceptual description')
53
+ .option('--json', 'output as JSON')
54
+ .action((file, opts) => {
55
+ const data = readYaml(file);
56
+ if (findTableById(data, opts.id)) {
57
+ return outputError(opts.json, `Table "${opts.id}" already exists`, 'Use `table update` instead');
58
+ }
59
+ const table = { id: opts.id, name: opts.name };
60
+ if (opts.logicalName) table.logical_name = opts.logicalName;
61
+ if (opts.physicalName) table.physical_name = opts.physicalName;
62
+ if (opts.type) table.appearance = { type: opts.type };
63
+ if (opts.description) table.conceptual = { description: opts.description };
64
+ if (!data.tables) data.tables = [];
65
+ data.tables.push(table);
66
+ writeYaml(file, data);
67
+ outputOk(opts.json, 'add', 'table', opts.id);
68
+ });
69
+
70
+ // update
71
+ cmd
72
+ .command('update <file>')
73
+ .description('Update fields of an existing table')
74
+ .requiredOption('--id <id>', 'table ID to update')
75
+ .option('--name <name>', 'conceptual name')
76
+ .option('--type <type>', 'appearance type')
77
+ .option('--logical-name <name>', 'logical (business) name')
78
+ .option('--physical-name <name>', 'physical (database) table name')
79
+ .option('--description <text>', 'conceptual description')
80
+ .option('--json', 'output as JSON')
81
+ .action((file, opts) => {
82
+ const data = readYaml(file);
83
+ const table = findTableById(data, opts.id);
84
+ if (!table) return outputError(opts.json, `Table "${opts.id}" not found`, 'Use `table add` instead');
85
+ if (opts.name) table.name = opts.name;
86
+ if (opts.logicalName) table.logical_name = opts.logicalName;
87
+ if (opts.physicalName) table.physical_name = opts.physicalName;
88
+ if (opts.type) {
89
+ table.appearance = table.appearance || {};
90
+ table.appearance.type = opts.type;
91
+ }
92
+ if (opts.description) {
93
+ table.conceptual = table.conceptual || {};
94
+ table.conceptual.description = opts.description;
95
+ }
96
+ writeYaml(file, data);
97
+ outputOk(opts.json, 'update', 'table', opts.id);
98
+ });
99
+
100
+ // remove
101
+ cmd
102
+ .command('remove <file>')
103
+ .description('Remove a table from the model')
104
+ .requiredOption('--id <id>', 'table ID to remove')
105
+ .option('--json', 'output as JSON')
106
+ .action((file, opts) => {
107
+ const data = readYaml(file);
108
+ const before = (data.tables || []).length;
109
+ data.tables = (data.tables || []).filter(t => t.id !== opts.id);
110
+ if (data.tables.length === before) {
111
+ return outputWarn(opts.json, `Table "${opts.id}" not found, nothing removed`);
112
+ }
113
+ writeYaml(file, data);
114
+ outputOk(opts.json, 'remove', 'table', opts.id);
115
+ });
116
+
117
+ return cmd;
118
+ }
@@ -5,6 +5,19 @@ Start an interactive data modeling session.
5
5
  2. SECOND, analyze the existing `model.yaml` if it exists.
6
6
  3. Listen to the user's requirements and propose/apply changes to `model.yaml` strictly following the rules.
7
7
 
8
+ ## Mutation CLI — Use Before Editing YAML Directly
9
+
10
+ For targeted changes to tables, columns, relationships, lineage, or domains, **PREFER the mutation CLI commands** over editing YAML directly. CLI commands validate input and write atomically.
11
+
12
+ Recommended flow:
13
+ 1. Check existence: `modscape table get model.yaml --id <id> --json`
14
+ 2. Add or update: `modscape table add` / `modscape table update`
15
+ 3. After adding tables: `modscape layout model.yaml` to assign coordinates
16
+
17
+ See Section 13 of `.modscape/rules.md` for the full command reference.
18
+
19
+ Only edit YAML directly for complex nested fields not covered by CLI flags (e.g., `implementation`, `sampleData`, full `columns` definition).
20
+
8
21
  ## Appearance & Layout
9
22
  - **Appearance**: For new tables, include the `appearance: { type: "..." }` block.
10
23
  - **Layout**: When creating new entities, always assign initial `x` and `y` coordinates in the `layout` section. Position them logically near their related entities to avoid stacking.
@@ -13,6 +13,19 @@ When the user issues this command:
13
13
  2. ANALYZE `model.yaml` (if present).
14
14
  3. INTERACT with the user to gather requirements and update the model strictly following the rules.
15
15
 
16
+ ## Mutation CLI — Use Before Editing YAML Directly
17
+
18
+ For targeted changes to tables, columns, relationships, lineage, or domains, **PREFER the mutation CLI commands** over editing YAML directly. CLI commands validate input and write atomically.
19
+
20
+ Recommended flow:
21
+ 1. Check existence: `modscape table get model.yaml --id <id> --json`
22
+ 2. Add or update: `modscape table add` / `modscape table update`
23
+ 3. After adding tables: `modscape layout model.yaml` to assign coordinates
24
+
25
+ See Section 13 of `.modscape/rules.md` for the full command reference.
26
+
27
+ Only edit YAML directly for complex nested fields not covered by CLI flags (e.g., `implementation`, `sampleData`, full `columns` definition).
28
+
16
29
  ## Appearance & Layout
17
30
  - **Appearance**: When creating new tables, include the `appearance` block with an appropriate `type`.
18
31
  - **Layout**: For any new entity, assign logical `x` and `y` coordinates in the `layout` section to prevent overlapping and ensure a clean initial visualization.
@@ -11,6 +11,19 @@ BEFORE making any suggestions or changes, you MUST read and strictly follow the
11
11
 
12
12
  If a requested change violates these rules, warn the user.
13
13
 
14
+ ## Mutation CLI — Use Before Editing YAML Directly
15
+
16
+ For targeted changes to tables, columns, relationships, lineage, or domains, **PREFER the mutation CLI commands** over editing YAML directly. CLI commands validate input and write atomically.
17
+
18
+ Recommended flow:
19
+ 1. Check existence: `modscape table get model.yaml --id <id> --json`
20
+ 2. Add or update: `modscape table add` / `modscape table update`
21
+ 3. After adding tables: `modscape layout model.yaml` to assign coordinates
22
+
23
+ See Section 13 of `.modscape/rules.md` for the full command reference.
24
+
25
+ Only edit YAML directly for complex nested fields not covered by CLI flags (e.g., `implementation`, `sampleData`, full `columns` definition).
26
+
14
27
  ## 📁 Multi-file Awareness
15
28
  `modscape dev` supports pointing to a directory (e.g., `modscape dev samples/`).
16
29
  - **Switching Models**: Identify which YAML file you are editing from the directory.
@@ -592,7 +592,105 @@ modscape merge ./sales ./marketing -o combined.yaml
592
592
 
593
593
  ---
594
594
 
595
- ## 13. Project-Specific Rule Extensions
595
+ ## 13. Model Mutation CLI
596
+
597
+ Use the built-in mutation commands to **add, update, or remove individual entities** in a YAML model. These commands validate input and write atomically — safer than editing YAML directly.
598
+
599
+ **MUST** use these commands when making targeted changes. Only edit YAML directly for complex nested fields not covered by CLI flags (e.g., `implementation`, `sampleData`, `columns` full definition).
600
+
601
+ ### 13-1. Available Operations
602
+
603
+ | Resource | Operations |
604
+ |----------|-----------|
605
+ | `table` | `list` `get` `add` `update` `remove` |
606
+ | `column` | `add` `update` `remove` |
607
+ | `relationship` | `list` `add` `remove` |
608
+ | `lineage` | `list` `add` `remove` |
609
+ | `domain` | `list` `get` `add` `update` `remove` |
610
+ | `domain member` | `add` `remove` |
611
+
612
+ ### 13-2. Recommended AI Agent Flow
613
+
614
+ Before `add` or `update`, check existence with `get` or `list`:
615
+
616
+ ```bash
617
+ # 1. Check if table exists
618
+ modscape table get model.yaml --id fct_orders --json
619
+ # → found: use update / not found: use add
620
+
621
+ # 2a. Add new table
622
+ modscape table add model.yaml --id fct_orders --name "Orders" --type fact
623
+
624
+ # 2b. Update existing table
625
+ modscape table update model.yaml --id fct_orders --physical-name fct_sales_orders
626
+ ```
627
+
628
+ ### 13-3. CLI Flag Reference
629
+
630
+ **table add / update**
631
+ ```bash
632
+ modscape table add model.yaml \
633
+ --id <id> --name <name> \
634
+ [--type fact|dimension|mart|hub|link|satellite|table] \
635
+ [--logical-name <name>] [--physical-name <name>] \
636
+ [--description <text>] [--json]
637
+ ```
638
+
639
+ **column add / update**
640
+ ```bash
641
+ modscape column add model.yaml \
642
+ --table <tableId> --id <id> --name <name> \
643
+ [--type Int|String|Decimal|Date|Timestamp|Boolean] \
644
+ [--primary-key] [--foreign-key] \
645
+ [--physical-name <name>] [--physical-type <type>] [--json]
646
+ ```
647
+
648
+ **relationship add**
649
+ ```bash
650
+ modscape relationship add model.yaml \
651
+ --from <table.column> --to <table.column> \
652
+ --type one-to-one|one-to-many|many-to-one|many-to-many [--json]
653
+ ```
654
+
655
+ **lineage add**
656
+ ```bash
657
+ modscape lineage add model.yaml --from <tableId> --to <tableId> [--json]
658
+ ```
659
+
660
+ **domain add / update**
661
+ ```bash
662
+ modscape domain add model.yaml \
663
+ --id <id> --name <name> [--description <text>] [--color <color>] [--json]
664
+ ```
665
+
666
+ **domain member add / remove**
667
+ ```bash
668
+ modscape domain member add model.yaml --domain <domainId> --table <tableId> [--json]
669
+ modscape domain member remove model.yaml --domain <domainId> --table <tableId> [--json]
670
+ ```
671
+
672
+ ### 13-4. After Adding Tables
673
+
674
+ `table add` does **not** create layout coordinates. After adding tables, run:
675
+
676
+ ```bash
677
+ modscape layout model.yaml
678
+ ```
679
+
680
+ This assigns coordinates to all layout-less entries automatically.
681
+
682
+ ### 13-5. JSON Output for AI Pipelines
683
+
684
+ All commands support `--json` for machine-readable output:
685
+
686
+ ```json
687
+ { "ok": true, "action": "add", "resource": "table", "id": "fct_orders" }
688
+ { "ok": false, "error": "Table \"fct_orders\" already exists", "hint": "Use `table update` instead" }
689
+ ```
690
+
691
+ ---
692
+
693
+ ## 15. Project-Specific Rule Extensions
596
694
 
597
695
  A project MAY place a `.modscape/rules.custom.md` file to define rules that extend or override this base file.
598
696
 
@@ -626,7 +724,7 @@ A project MAY place a `.modscape/rules.custom.md` file to define rules that exte
626
724
 
627
725
  ---
628
726
 
629
- ## 14. Complete Example
727
+ ## 16. Complete Example
630
728
 
631
729
  ```yaml
632
730
  domains:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "visualizer",
3
3
  "private": true,
4
- "version": "2.0.2",
4
+ "version": "2.0.3",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",