modscape 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yujikawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # <img src="./readme_assets/logo.svg" width="32" height="32" align="center" /> Modscape
2
+
3
+ Modscape is a YAML-driven data modeling visualizer. It helps data engineers and architects bridge the gap between conceptual, logical, and physical data models while maintaining sample data "stories".
4
+
5
+ [sample page](https://yujikawa.github.io/modscape/)
6
+
7
+ ## Features
8
+
9
+ - **YAML-First**: Define your entire data model in a single, simple YAML file.
10
+ - **Unified Sidebar**: A feature-rich sidebar accessible in both `dev` and `build` modes.
11
+ - **Tabs**: Switch between **Editor** (YAML) and **Entities** (Navigation).
12
+ - **Collapsible**: Collapse the sidebar to maximize your ER diagram viewing area.
13
+ - **Interactive ER Diagram**: Drag-and-drop entities to create the perfect layout.
14
+ - **Diagram Navigation**: Click entities in the sidebar to smoothly focus and zoom in on them.
15
+ - **Sandbox Mode**: Temporary in-memory editing in static builds. Try "What-if" modeling without affecting the source.
16
+ - **Layout Persistence**: Diagram positions (including **Domains**) are automatically saved to your YAML file in dev mode.
17
+ - **CLI-Driven Workflow**:
18
+ - `modscape dev`: Interactive editor with live updates.
19
+ - `modscape build`: Package your model into a standalone static site.
20
+
21
+ ## Installation
22
+
23
+ ### Prerequisites
24
+ - Node.js (v18 or higher)
25
+
26
+ ### Global Installation (via GitHub)
27
+ You can install Modscape directly from GitHub to use the `modscape` command anywhere:
28
+
29
+ ```bash
30
+ npm install -g https://github.com/yujikawa/modscape
31
+ ```
32
+
33
+ ### Local Setup (for Development)
34
+ ```bash
35
+ # Clone the repository
36
+ git clone https://github.com/yujikawa/modscape.git
37
+ cd modscape
38
+
39
+ # Install dependencies for both CLI and Visualizer
40
+ npm install
41
+ cd visualizer && npm install
42
+ cd ..
43
+
44
+ # Link the command to your system
45
+ npm link
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ### 0. Initialization (AI Agent Setup)
51
+ Scaffold project-specific modeling rules and configure your favorite AI agents (Gemini, Codex, Claude) to follow them.
52
+
53
+ ```bash
54
+ modscape init
55
+ ```
56
+ - Creates `.modscape/rules.md` as your project's "Source of Truth" for modeling.
57
+ - Generates agent-specific configurations (skills, prompts, commands) that point to these rules.
58
+ - **Why?**: This ensures AI agents generate YAML that perfectly matches your organization's standards.
59
+
60
+ ### 1. Development Mode (Interactive Editor)
61
+ Start a local session to edit your YAML and arrange entities.
62
+
63
+ ```bash
64
+ # Point to a directory to manage all models within it
65
+ modscape dev samples/
66
+
67
+ # Or point to a specific file
68
+ modscape dev my-model.yaml
69
+ ```
70
+ - Opens `http://localhost:5173` automatically.
71
+ - **Multi-file Support**: Switch between models using the dropdown in the sidebar.
72
+ - **Secure Routing**: Models are accessed via slugs (e.g., `?model=ecommerce`), keeping your local paths private.
73
+ - **Editor Tab**: Edit YAML with live updates to the diagram.
74
+ - **Entities Tab**: Search and quickly navigate to specific tables or domains.
75
+ - **Persistence**: Drag entities to save positions directly to the source YAML file.
76
+
77
+ ### 2. Static Site Build
78
+ Generate a standalone documentation site from your YAML model.
79
+
80
+ ```bash
81
+ modscape build my-model.yaml -o ./docs-site
82
+ ```
83
+ - Generates a `docs-site/` folder with a single-file visualizer.
84
+ - Includes the **Sandbox Mode**, allowing viewers to temporarily edit the model in their browser.
85
+ - Perfect for hosting on **GitHub Pages**, S3, or internal documentation portals.
86
+
87
+ ## YAML Schema Example
88
+
89
+ ```yaml
90
+ tables:
91
+ - id: hub_customer
92
+ name: HUB_CUSTOMER
93
+ appearance: # Optional: Visual style (icon and color)
94
+ type: "hub" # Predefined: hub, link, satellite, fact, dimension
95
+ icon: "🌐" # Optional: Emoji override
96
+ color: "#fbbf24" # Optional: Hex color override
97
+ conceptual:
98
+ description: "Business keys for unique customers."
99
+ tags: ["HUB", "PARTY"]
100
+ columns:
101
+ - id: hk_customer
102
+ logical: { name: "HK_CUSTOMER", type: "Binary", isPrimaryKey: true }
103
+ physical: { name: "HK_CUST", type: "BINARY(16)", constraints: ["NOT NULL"] }
104
+
105
+ domains:
106
+ - id: customer_domain
107
+ name: Customer Domain
108
+ tables: ["hub_customer"]
109
+ color: "rgba(59, 130, 246, 0.05)"
110
+
111
+ relationships:
112
+ - from: { table: hub_customer, column: hk_customer }
113
+ to: { table: sat_customer_crm, column: hk_customer }
114
+ type: "one-to-many"
115
+
116
+ layout: # Automatically managed by the visualizer or AI Agent
117
+ hub_customer: { x: 100, y: 100 }
118
+ customer_domain: { x: 50, y: 50, width: 600, height: 400 }
119
+ ```
120
+
121
+ ### Key Attributes
122
+ - **appearance**: Controls the node's visual identity. `type` sets a default icon and color (e.g., `hub` is Amber 🌐, `fact` is Red 📊). Use `icon` or `color` to override.
123
+ - **layout**: Stores (x, y) coordinates and dimensions. While the visualizer manages this automatically during drag-and-drop, AI Agents use this to arrange new entities logically.
124
+
125
+
126
+ ## Credits
127
+
128
+ Modscape is built upon several incredible open-source projects:
129
+
130
+ - **[React Flow](https://reactflow.dev/)**: Powering the interactive graph engine.
131
+ - **[Radix UI](https://www.radix-ui.com/)**: Providing accessible, high-quality primitives for the sidebar.
132
+ - **[Lucide](https://lucide.dev/)**: Beautiful, consistent iconography.
133
+ - **[shadcn/ui](https://ui.shadcn.com/)**: Design inspiration and component patterns.
134
+ - **[Express](https://expressjs.com/)**: Serving the development environment.
135
+
136
+ ## License
137
+ MIT
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "modscape",
3
+ "version": "0.1.0",
4
+ "description": "Modscape: A YAML-driven data modeling visualizer CLI",
5
+ "type": "module",
6
+ "bin": {
7
+ "modscape": "./src/index.js"
8
+ },
9
+ "files": [
10
+ "src",
11
+ "visualizer-dist",
12
+ "package.json",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "build-ui": "cd visualizer && npm run build && rm -rf ../visualizer-dist && mv dist ../visualizer-dist",
17
+ "dev": "node src/index.js dev"
18
+ },
19
+ "dependencies": {
20
+ "@inquirer/prompts": "^8.3.0",
21
+ "chokidar": "^5.0.0",
22
+ "commander": "^14.0.3",
23
+ "express": "^5.2.1",
24
+ "js-yaml": "^4.1.1",
25
+ "open": "^11.0.0"
26
+ }
27
+ }
package/src/build.js ADDED
@@ -0,0 +1,108 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import yaml from 'js-yaml';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ function scanFiles(inputPaths) {
9
+ const modelMap = new Map();
10
+ inputPaths.forEach(inputPath => {
11
+ const absolutePath = path.resolve(process.cwd(), inputPath);
12
+ if (!fs.existsSync(absolutePath)) {
13
+ console.warn(` ⚠️ Warning: Path not found: ${inputPath}`);
14
+ return;
15
+ }
16
+
17
+ const stats = fs.statSync(absolutePath);
18
+ if (stats.isDirectory()) {
19
+ const files = fs.readdirSync(absolutePath);
20
+ files.forEach(file => {
21
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
22
+ const slug = path.parse(file).name;
23
+ const fullPath = path.join(absolutePath, file);
24
+ if (modelMap.has(slug)) {
25
+ const parentName = path.basename(absolutePath);
26
+ modelMap.set(`${parentName}-${slug}`, fullPath);
27
+ } else {
28
+ modelMap.set(slug, fullPath);
29
+ }
30
+ }
31
+ });
32
+ } else if (stats.isFile() && (inputPath.endsWith('.yaml') || inputPath.endsWith('.yml'))) {
33
+ const slug = path.parse(absolutePath).name;
34
+ modelMap.set(slug, absolutePath);
35
+ }
36
+ });
37
+ return modelMap;
38
+ }
39
+
40
+ export async function build(paths, visualizerPath, outputDir) {
41
+ const modelMap = scanFiles(Array.isArray(paths) ? paths : [paths]);
42
+ const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
43
+ const distPath = path.resolve(__dirname, '../visualizer-dist');
44
+
45
+ if (modelMap.size === 0) {
46
+ console.error('\n ❌ Error: No YAML files found in the specified paths.');
47
+ return;
48
+ }
49
+
50
+ if (!fs.existsSync(distPath)) {
51
+ console.error('\n ❌ Error: visualizer-dist not found. Please run "npm run build-ui" first.\n');
52
+ return;
53
+ }
54
+
55
+ console.log(`\n 📦 Building static site with ${modelMap.size} model(s)...`);
56
+
57
+ // Create output dir and copy all files from visualizer-dist
58
+ if (!fs.existsSync(absoluteOutputDir)) {
59
+ fs.mkdirSync(absoluteOutputDir, { recursive: true });
60
+ }
61
+
62
+ // Copy recursive helper
63
+ const copyDir = (src, dest) => {
64
+ fs.mkdirSync(dest, { recursive: true });
65
+ const entries = fs.readdirSync(src, { withFileTypes: true });
66
+ for (let entry of entries) {
67
+ const srcPath = path.join(src, entry.name);
68
+ const destPath = path.join(dest, entry.name);
69
+ if (entry.isDirectory()) {
70
+ copyDir(srcPath, destPath);
71
+ } else {
72
+ fs.copyFileSync(srcPath, destPath);
73
+ }
74
+ }
75
+ };
76
+
77
+ copyDir(distPath, absoluteOutputDir);
78
+
79
+ // Prepare data for injection
80
+ const modelsData = [];
81
+ for (const [slug, absolutePath] of modelMap.entries()) {
82
+ try {
83
+ const content = fs.readFileSync(absolutePath, 'utf8');
84
+ const schema = yaml.load(content);
85
+ const name = slug.charAt(0).toUpperCase() + slug.slice(1).replace(/[-_]/g, ' ');
86
+ modelsData.push({ slug, name, schema });
87
+ } catch (e) {
88
+ console.warn(` ⚠️ Warning: Failed to load ${absolutePath}: ${e.message}`);
89
+ }
90
+ }
91
+
92
+ // Inject data into index.html
93
+ const indexPath = path.join(absoluteOutputDir, 'index.html');
94
+ let html = fs.readFileSync(indexPath, 'utf8');
95
+
96
+ const injectionData = {
97
+ isMultiFile: modelsData.length > 1,
98
+ models: modelsData
99
+ };
100
+
101
+ html = html.replace(
102
+ '</head>',
103
+ `<script>window.__MODSCAPE_DATA__ = ${JSON.stringify(injectionData)}; window.MODSCAPE_CLI_MODE = false;</script></head>`
104
+ );
105
+ fs.writeFileSync(indexPath, html, 'utf8');
106
+
107
+ console.log(`\n ✅ Build complete! Static site generated in: ${outputDir}`);
108
+ }
package/src/dev.js ADDED
@@ -0,0 +1,154 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import yaml from 'js-yaml';
5
+ import chokidar from 'chokidar';
6
+ import open from 'open';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+
11
+ function scanFiles(inputPaths) {
12
+ const modelMap = new Map();
13
+ inputPaths.forEach(inputPath => {
14
+ const absolutePath = path.resolve(process.cwd(), inputPath);
15
+ if (!fs.existsSync(absolutePath)) {
16
+ console.warn(` ⚠️ Warning: Path not found: ${inputPath}`);
17
+ return;
18
+ }
19
+
20
+ const stats = fs.statSync(absolutePath);
21
+ if (stats.isDirectory()) {
22
+ const files = fs.readdirSync(absolutePath);
23
+ files.forEach(file => {
24
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
25
+ const slug = path.parse(file).name;
26
+ const fullPath = path.join(absolutePath, file);
27
+ if (modelMap.has(slug)) {
28
+ // Collision handling: append folder name
29
+ const parentName = path.basename(absolutePath);
30
+ modelMap.set(`${parentName}-${slug}`, fullPath);
31
+ } else {
32
+ modelMap.set(slug, fullPath);
33
+ }
34
+ }
35
+ });
36
+ } else if (stats.isFile() && (inputPath.endsWith('.yaml') || inputPath.endsWith('.yml'))) {
37
+ const slug = path.parse(absolutePath).name;
38
+ modelMap.set(slug, absolutePath);
39
+ }
40
+ });
41
+ return modelMap;
42
+ }
43
+
44
+ export async function startDevServer(paths, visualizerPath) {
45
+ const app = express();
46
+ app.use(express.json());
47
+
48
+ const modelMap = scanFiles(Array.isArray(paths) ? paths : [paths]);
49
+ const distPath = path.resolve(__dirname, '../visualizer-dist');
50
+
51
+ if (modelMap.size === 0) {
52
+ console.error(`\n ❌ Error: No YAML files found in the specified paths.`);
53
+ return;
54
+ }
55
+
56
+ if (!fs.existsSync(distPath)) {
57
+ console.error(`\n ❌ Error: visualizer-dist not found at ${distPath}`);
58
+ console.log(' Please run "npm run build-ui" first.\n');
59
+ return;
60
+ }
61
+
62
+ // Helper to get absolute path from model slug
63
+ const getModelPath = (slug) => {
64
+ if (slug) return modelMap.get(slug);
65
+ // If no slug provided, return the first available model
66
+ return modelMap.values().next().value;
67
+ };
68
+
69
+ // API to list all available models
70
+ app.get('/api/files', (req, res) => {
71
+ const files = Array.from(modelMap.entries()).map(([slug, fullPath]) => ({
72
+ slug,
73
+ name: slug.charAt(0).toUpperCase() + slug.slice(1).replace(/[-_]/g, ' '),
74
+ path: path.relative(process.cwd(), fullPath)
75
+ }));
76
+ res.json(files);
77
+ });
78
+
79
+ // API to get current YAML data
80
+ app.get('/api/model', (req, res) => {
81
+ const modelPath = getModelPath(req.query.model);
82
+ if (!modelPath || !fs.existsSync(modelPath)) {
83
+ return res.status(404).json({ error: 'Model not found' });
84
+ }
85
+
86
+ try {
87
+ const content = fs.readFileSync(modelPath, 'utf8');
88
+ res.json(yaml.load(content));
89
+ } catch (e) {
90
+ res.status(500).json({ error: e.message });
91
+ }
92
+ });
93
+
94
+ // API to save layout changes
95
+ app.post('/api/layout', (req, res) => {
96
+ const modelPath = getModelPath(req.query.model);
97
+ if (!modelPath) return res.status(404).json({ error: 'Model not found' });
98
+
99
+ try {
100
+ const layout = req.body;
101
+ const content = fs.readFileSync(modelPath, 'utf8');
102
+ const data = yaml.load(content);
103
+ data.layout = layout;
104
+ fs.writeFileSync(modelPath, yaml.dump(data, { indent: 2, lineWidth: -1 }), 'utf8');
105
+ res.json({ success: true });
106
+ } catch (e) {
107
+ res.status(500).json({ error: e.message });
108
+ }
109
+ });
110
+
111
+ // API to save entire YAML content
112
+ app.post('/api/save-yaml', (req, res) => {
113
+ const modelPath = getModelPath(req.query.model);
114
+ if (!modelPath) return res.status(404).json({ error: 'Model not found' });
115
+
116
+ try {
117
+ const { yaml: yamlContent } = req.body;
118
+ yaml.load(yamlContent);
119
+ fs.writeFileSync(modelPath, yamlContent, 'utf8');
120
+ res.json({ success: true });
121
+ } catch (e) {
122
+ res.status(500).json({ error: e.message });
123
+ }
124
+ });
125
+
126
+ // Serve static files EXCEPT index.html
127
+ app.use(express.static(distPath, { index: false }));
128
+
129
+ // Intercept index.html to inject CLI_MODE flag
130
+ app.get('{/*path}', (req, res) => {
131
+ try {
132
+ let html = fs.readFileSync(path.join(distPath, 'index.html'), 'utf8');
133
+ html = html.replace(
134
+ '</head>',
135
+ '<script>window.MODSCAPE_CLI_MODE = true;</script></head>'
136
+ );
137
+ res.send(html);
138
+ } catch (e) {
139
+ res.status(500).send('Error loading visualizer');
140
+ }
141
+ });
142
+
143
+ const port = 5173;
144
+ app.listen(port, () => {
145
+ const url = `http://localhost:${port}`;
146
+ console.log(`\n 🚀 Modscape Visualizer running at: ${url}`);
147
+ console.log(` Watching ${modelMap.size} file(s)`);
148
+ open(url);
149
+ });
150
+
151
+ chokidar.watch(Array.from(modelMap.values())).on('change', (changedPath) => {
152
+ console.log(` File changed: ${path.relative(process.cwd(), changedPath)}. Please refresh the browser.`);
153
+ });
154
+ }
package/src/index.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { startDevServer } from './dev.js';
7
+ import { build } from './build.js';
8
+ import { initProject } from './init.js';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const VISUALIZER_PATH = path.resolve(__dirname, '../visualizer');
12
+ const program = new Command();
13
+
14
+ program
15
+ .name('modscape')
16
+ .description('A YAML-driven data modeling visualizer CLI')
17
+ .version('0.1.0');
18
+
19
+ program
20
+ .command('init')
21
+ .description('Initialize project with AI modeling rules')
22
+ .option('-g, --gemini', 'Scaffold for Gemini CLI')
23
+ .option('-x, --codex', 'Scaffold for Codex')
24
+ .option('-c, --claude', 'Scaffold for Claude Code')
25
+ .option('-a, --all', 'Scaffold for all agents')
26
+ .action((options) => {
27
+ initProject(options);
28
+ });
29
+
30
+ program
31
+ .command('dev')
32
+ .description('Start the development visualizer with local YAML files or directories')
33
+ .argument('<paths...>', 'paths to YAML model files or directories')
34
+ .action((paths) => {
35
+ startDevServer(paths, VISUALIZER_PATH);
36
+ });
37
+
38
+ program
39
+ .command('build')
40
+ .description('Build a static site from YAML models')
41
+ .argument('<paths...>', 'paths to YAML model files or directories')
42
+ .option('-o, --output <dir>', 'output directory', 'dist')
43
+ .action((paths, options) => {
44
+ build(paths, VISUALIZER_PATH, options.output);
45
+ });
46
+
47
+ program.parse();
package/src/init.js ADDED
@@ -0,0 +1,102 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ async function safeWriteFile(filePath, content) {
9
+ const absolutePath = path.resolve(process.cwd(), filePath);
10
+ const dir = path.dirname(absolutePath);
11
+
12
+ if (!fs.existsSync(dir)) {
13
+ fs.mkdirSync(dir, { recursive: true });
14
+ }
15
+
16
+ if (fs.existsSync(absolutePath)) {
17
+ const overwrite = await confirm({
18
+ message: `File ${filePath} already exists. Overwrite?`,
19
+ default: false,
20
+ });
21
+ if (!overwrite) {
22
+ console.log(` Skipping ${filePath}`);
23
+ return;
24
+ }
25
+ }
26
+
27
+ fs.writeFileSync(absolutePath, content, 'utf8');
28
+ console.log(` Created ${filePath}`);
29
+ }
30
+
31
+ export async function initProject(options = {}) {
32
+ console.log('\n 🛠️ ModMod Project Initialization\n');
33
+
34
+ try {
35
+ const agents = [];
36
+
37
+ // If options are provided via CLI flags, use them
38
+ if (options.all) {
39
+ agents.push('gemini', 'codex', 'claude');
40
+ } else if (options.gemini || options.codex || options.claude) {
41
+ if (options.gemini) agents.push('gemini');
42
+ if (options.codex) agents.push('codex');
43
+ if (options.claude) agents.push('claude');
44
+ } else {
45
+ // Otherwise, ask one by one (more robust than checkbox in some terminals)
46
+ console.log(' Please confirm which AI agents you want to scaffold for:\n');
47
+
48
+ if (await confirm({ message: 'Scaffold for Gemini CLI?', default: false })) {
49
+ agents.push('gemini');
50
+ }
51
+ if (await confirm({ message: 'Scaffold for Codex?', default: false })) {
52
+ agents.push('codex');
53
+ }
54
+ if (await confirm({ message: 'Scaffold for Claude Code?', default: false })) {
55
+ agents.push('claude');
56
+ }
57
+ }
58
+
59
+ if (agents.length === 0) {
60
+ console.log('\n ⚠️ No agents selected. Only ".modscape/rules.md" will be created.');
61
+ } else {
62
+ console.log(`\n Selected agents: ${agents.join(', ')}`);
63
+ }
64
+
65
+ console.log('\n Scaffolding modeling rules and commands...');
66
+
67
+ // 1. Create .modscape/rules.md
68
+ const rulesTemplatePath = path.join(__dirname, 'templates/rules.md');
69
+ const rulesTemplate = fs.readFileSync(rulesTemplatePath, 'utf8');
70
+ await safeWriteFile('.modscape/rules.md', rulesTemplate);
71
+
72
+ // 2. Create agent-specific files
73
+ if (agents.includes('gemini')) {
74
+ const skillTemplate = fs.readFileSync(path.join(__dirname, 'templates/gemini/SKILL.md'), 'utf8');
75
+ await safeWriteFile('.gemini/skills/modmod/SKILL.md', skillTemplate);
76
+
77
+ const commandTemplate = fs.readFileSync(path.join(__dirname, 'templates/gemini/command.toml'), 'utf8');
78
+ await safeWriteFile('.gemini/commands/modmod/modeling.toml', commandTemplate);
79
+ }
80
+
81
+ if (agents.includes('codex')) {
82
+ const promptTemplate = fs.readFileSync(path.join(__dirname, 'templates/codex/prompt.md'), 'utf8');
83
+ await safeWriteFile('.codex/prompts/modmod-modeling.md', promptTemplate);
84
+ }
85
+
86
+ if (agents.includes('claude')) {
87
+ const clauderules = fs.readFileSync(path.join(__dirname, 'templates/claude/clauderules'), 'utf8');
88
+ await safeWriteFile('.clauderules', clauderules);
89
+
90
+ const commandTemplate = fs.readFileSync(path.join(__dirname, 'templates/claude/command.md'), 'utf8');
91
+ await safeWriteFile('.claude/commands/modmod/modeling.md', commandTemplate);
92
+ }
93
+
94
+ console.log('\n ✅ Initialization complete! Customize ".modscape/rules.md" to match your project standards.\n');
95
+ } catch (error) {
96
+ if (error.name === 'ExitPromptError') {
97
+ console.log('\n Initialization cancelled by user.');
98
+ } else {
99
+ console.error('\n An error occurred during initialization:', error.message);
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,9 @@
1
+ You are a professional Data Modeler. Your primary directive is to manage `model.yaml`.
2
+
3
+ ## COMMAND: /modscape:modeling
4
+ When the user issues this command:
5
+ 1. READ `.modscape/rules.md` to understand project strategy and conventions.
6
+ 2. ANALYZE `model.yaml` (if present).
7
+ 3. INTERACT with the user to gather requirements and update the model strictly following the rules.
8
+
9
+ BEFORE making any suggestions or changes, you MUST read and strictly follow the rules defined in `.modscape/rules.md`.
@@ -0,0 +1,14 @@
1
+ # /modscape:modeling
2
+
3
+ Start an interactive data modeling session.
4
+
5
+ ## Instructions
6
+ 1. FIRST, read `.modscape/rules.md` to understand the project strategy, naming conventions, and YAML schema.
7
+ 2. SECOND, analyze the existing `model.yaml` if it exists.
8
+ 3. Listen to the user's requirements and propose/apply changes to `model.yaml` strictly following the rules.
9
+
10
+ ## Appearance & Layout
11
+ - **Appearance**: For new tables, include the `appearance: { type: "..." }` block.
12
+ - **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
+
14
+ Always prioritize consistency and project-specific standards.
@@ -0,0 +1,15 @@
1
+ # Data Modeling Instructions
2
+
3
+ You are a professional Data Modeler. Your primary directive is to manage `model.yaml`.
4
+
5
+ ## COMMAND: /modscape:modeling
6
+ When the user issues this command:
7
+ 1. READ `.modscape/rules.md` to understand project strategy and conventions.
8
+ 2. ANALYZE `model.yaml` (if present).
9
+ 3. INTERACT with the user to gather requirements and update the model strictly following the rules.
10
+
11
+ ## Appearance & Layout
12
+ - **Appearance**: When creating new tables, include the `appearance` block with an appropriate `type`.
13
+ - **Layout**: For any new entity, assign logical `x` and `y` coordinates in the `layout` section to prevent overlapping and ensure a clean initial visualization.
14
+
15
+ ALWAYS follow the rules defined in `.modscape/rules.md` for any modeling tasks.
@@ -0,0 +1,20 @@
1
+ # Data Modeling Expert
2
+
3
+ You are a professional Data Modeler. Your primary directive is to manage `model.yaml`.
4
+
5
+ BEFORE making any suggestions or changes, you MUST read and strictly follow the rules defined in `.modscape/rules.md`.
6
+
7
+ If a requested change violates these rules, warn the user.
8
+
9
+ ## 📁 Multi-file Awareness
10
+ `modscape dev` supports pointing to a directory (e.g., `modscape dev samples/`).
11
+ - **Switching Models**: Identify which YAML file you are editing from the directory.
12
+ - **Domain Separation**: Suggest splitting large models into multiple, domain-specific YAML files to improve organization.
13
+ - **Slug-based Access**: Be aware that the visualizer identifies models via slugs (filename without extension).
14
+
15
+ ## Layout & Appearance Management
16
+ - **Appearance**: When creating new tables, assign an appropriate `appearance.type` (e.g., `fact`, `hub`) to ensure correct visualization.
17
+ - **Layout**: You are responsible for the initial placement of new entities. Assign logical `x` and `y` coordinates in the `layout` section so they don't overlap existing nodes. The user will fine-tune the layout via the GUI.
18
+
19
+ ## Interactive Modeling
20
+ When the user wants to perform modeling tasks, ensure you are utilizing the strategy and conventions defined in the project rules. You can be triggered via the `/modscape:modeling` command which provides a dedicated workflow.
@@ -0,0 +1,14 @@
1
+ # /modscape:modeling command definition
2
+
3
+ description = "Start an interactive data modeling session using project rules"
4
+
5
+ [prompt]
6
+ instruction = """
7
+ You are now in **Modscape Modeling Mode**.
8
+
9
+ 1. FIRST, read `.modscape/rules.md` to understand the project strategy, naming conventions, and YAML schema.
10
+ 2. SECOND, analyze the existing `model.yaml` if it exists.
11
+ 3. Listen to the user's requirements and propose/apply changes to `model.yaml` strictly following the rules.
12
+
13
+ Stay in this mode until the task is complete. Always prioritize consistency and project-specific standards.
14
+ """