mdboard 1.2.0 → 2.0.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.
Files changed (40) hide show
  1. package/bin.js +130 -44
  2. package/index.html +3321 -1195
  3. package/package.json +10 -11
  4. package/presets/kanban/api.json +91 -0
  5. package/presets/kanban/cli.json +69 -0
  6. package/presets/kanban/docs.json +29 -0
  7. package/presets/kanban/entities.json +128 -0
  8. package/presets/kanban/structure.json +15 -0
  9. package/presets/kanban/ui.json +86 -0
  10. package/presets/scrum/api.json +98 -0
  11. package/presets/scrum/cli.json +120 -0
  12. package/presets/scrum/docs.json +43 -0
  13. package/presets/scrum/entities.json +268 -0
  14. package/presets/scrum/structure.json +32 -0
  15. package/presets/scrum/ui.json +201 -0
  16. package/presets/shape-up/api.json +40 -0
  17. package/presets/shape-up/cli.json +44 -0
  18. package/presets/shape-up/docs.json +32 -0
  19. package/presets/shape-up/entities.json +140 -0
  20. package/presets/shape-up/structure.json +28 -0
  21. package/presets/shape-up/ui.json +114 -0
  22. package/src/cli/cli.js +338 -0
  23. package/src/cli/config.js +234 -0
  24. package/src/cli/init.js +175 -0
  25. package/src/cli/preset.js +849 -0
  26. package/src/cli/skill.js +417 -0
  27. package/src/cli/status.js +180 -0
  28. package/src/core/config.js +551 -0
  29. package/src/core/history.js +146 -0
  30. package/src/core/scanner.js +521 -0
  31. package/{workspace.js → src/core/workspace.js} +0 -15
  32. package/{yaml.js → src/core/yaml.js} +5 -1
  33. package/src/server/api.js +616 -0
  34. package/{server.js → src/server/server.js} +180 -132
  35. package/{watcher.js → src/server/watcher.js} +40 -9
  36. package/api.js +0 -752
  37. package/config.js +0 -73
  38. package/defaults.json +0 -43
  39. package/init.js +0 -109
  40. package/scanner.js +0 -491
@@ -0,0 +1,234 @@
1
+ /**
2
+ * mdboard config — Interactive configuration wizard
3
+ *
4
+ * Guides the user through initial setup:
5
+ * Step 1: Methodology (preset selection)
6
+ * Step 2: Theme preference
7
+ * Step 3: Configuration overview (informational)
8
+ * Step 4: AI Skill installation
9
+ *
10
+ * Usage:
11
+ * mdboard config
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+ const readline = require('readline');
20
+ const configEngine = require('../core/config');
21
+ const { installSkill, AGENTS } = require('./skill');
22
+
23
+ var cwd = process.cwd();
24
+
25
+ /**
26
+ * Run the config wizard.
27
+ *
28
+ * @param {string[]} args - CLI arguments after 'config'
29
+ */
30
+ function run(args) {
31
+ var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
32
+
33
+ console.log('\n mdboard config — Setup wizard\n');
34
+
35
+ stepMethodology(rl);
36
+ }
37
+
38
+ // --- Step 1: Methodology ---
39
+
40
+ function stepMethodology(rl) {
41
+ // Read available presets
42
+ var presets = [];
43
+ try {
44
+ presets = fs.readdirSync(configEngine.PRESETS_DIR).filter(function (d) {
45
+ return fs.statSync(path.join(configEngine.PRESETS_DIR, d)).isDirectory();
46
+ });
47
+ } catch (e) { /* ignore */ }
48
+
49
+ console.log(' Step 1: Methodology\n');
50
+ for (var i = 0; i < presets.length; i++) {
51
+ console.log(' ' + (i + 1) + ') ' + presets[i]);
52
+ }
53
+ console.log('');
54
+
55
+ var currentPreset = configEngine.DEFAULT_PRESET;
56
+ try {
57
+ var cfg = configEngine.loadConfig(cwd, process.env.MDBOARD_CONFIG);
58
+ currentPreset = cfg._preset;
59
+ } catch (e) { /* ignore */ }
60
+
61
+ rl.question(' Choice [' + currentPreset + ']: ', function (answer) {
62
+ var choice = answer.trim();
63
+ var preset = currentPreset;
64
+
65
+ if (choice !== '') {
66
+ var idx = parseInt(choice, 10);
67
+ if (idx >= 1 && idx <= presets.length) {
68
+ preset = presets[idx - 1];
69
+ } else if (presets.includes(choice)) {
70
+ preset = choice;
71
+ } else {
72
+ console.log(' Preset "' + choice + '" not yet available. Using ' + currentPreset + '.');
73
+ }
74
+ }
75
+
76
+ // Set for the rest of the process
77
+ process.env.MDBOARD_PRESET = preset;
78
+
79
+ // Scaffold if project/ doesn't exist
80
+ var projectPath = path.join(cwd, 'project');
81
+ if (!fs.existsSync(projectPath)) {
82
+ console.log(' No project/ found — scaffolding with ' + preset + ' preset...');
83
+ try {
84
+ require('./init');
85
+ } catch (e) {
86
+ // init.js may call process.exit, so we catch and continue
87
+ }
88
+ } else {
89
+ console.log(' Methodology: ' + preset);
90
+ console.log(' project/ already exists — keeping existing data.\n');
91
+ }
92
+
93
+ stepTheme(rl, preset);
94
+ });
95
+ }
96
+
97
+ // --- Step 2: Theme ---
98
+
99
+ function stepTheme(rl, preset) {
100
+ console.log(' Step 2: Theme\n');
101
+ console.log(' 1) default Dark theme (built-in)');
102
+ console.log(' 2) light Light theme');
103
+ console.log(' 3) custom Edit project/mdboard.css\n');
104
+
105
+ rl.question(' Choice [default]: ', function (answer) {
106
+ var choice = answer.trim();
107
+ var theme = 'default';
108
+
109
+ if (choice === '2' || choice === 'light') {
110
+ theme = 'light';
111
+ } else if (choice === '3' || choice === 'custom') {
112
+ theme = 'custom';
113
+ }
114
+
115
+ // Save theme preference
116
+ if (theme !== 'default') {
117
+ var globalDir = path.join(os.homedir(), '.config', 'mdboard');
118
+ if (!fs.existsSync(globalDir)) {
119
+ fs.mkdirSync(globalDir, { recursive: true });
120
+ }
121
+ var configPath = path.join(globalDir, 'mdboard.json');
122
+ var globalCfg = {};
123
+ try { globalCfg = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch (e) { /* */ }
124
+ globalCfg.theme = theme;
125
+ fs.writeFileSync(configPath, JSON.stringify(globalCfg, null, 2) + '\n', 'utf-8');
126
+ console.log(' Theme saved: ' + theme + '\n');
127
+ } else {
128
+ console.log(' Theme: default\n');
129
+ }
130
+
131
+ stepConfiguration(rl, preset);
132
+ });
133
+ }
134
+
135
+ // --- Step 3: Configuration overview ---
136
+
137
+ function stepConfiguration(rl, preset) {
138
+ console.log(' Step 3: Configuration\n');
139
+
140
+ var cfg;
141
+ try {
142
+ cfg = configEngine.loadConfig(cwd, process.env.MDBOARD_CONFIG);
143
+ } catch (e) {
144
+ cfg = configEngine.loadConfig(null);
145
+ }
146
+
147
+ var types = configEngine.getEntityTypes(cfg);
148
+ var flat = configEngine.flattenHierarchy(cfg);
149
+ var hierarchy = flat.filter(function (e) { return !e.standalone; });
150
+ var chain = hierarchy.map(function (e) {
151
+ var entity = configEngine.getEntity(cfg, e.type);
152
+ return entity ? entity.singular : e.type;
153
+ }).join(' > ');
154
+
155
+ console.log(' Preset: ' + preset);
156
+ console.log(' Hierarchy: ' + chain);
157
+ console.log(' Entities: ' + types.join(', '));
158
+ console.log('');
159
+ console.log(' Config files (in presets/' + preset + '/):');
160
+ console.log(' entities.json Entity definitions, fields, statuses');
161
+ console.log(' structure.json Directory layout & hierarchy');
162
+ console.log(' cli.json CLI command config');
163
+ console.log(' api.json API endpoint config');
164
+ console.log(' ui.json Dashboard UI tabs & layout');
165
+ console.log(' docs.json Documentation & status report');
166
+ console.log('');
167
+ console.log(' Override any file by placing it in project/<file>.json');
168
+ console.log('');
169
+
170
+ stepSkill(rl);
171
+ }
172
+
173
+ // --- Step 4: AI Skill ---
174
+
175
+ function stepSkill(rl) {
176
+ console.log(' Step 4: AI Skill\n');
177
+ console.log(' Install an AI skill so your IDE/agent understands this project.\n');
178
+ console.log(' 1) Claude Code ~/.claude/skills/mdboard/SKILL.md');
179
+ console.log(' 2) Cursor .cursor/rules/mdboard.mdc');
180
+ console.log(' 3) Windsurf .windsurf/rules/mdboard.md');
181
+ console.log(' 4) VS Code .claude/skills/mdboard/SKILL.md');
182
+ console.log(' 5) Custom path');
183
+ console.log(' 6) Skip\n');
184
+
185
+ rl.question(' Choice [6]: ', function (answer) {
186
+ var choice = answer.trim() || '6';
187
+
188
+ if (choice === '6') {
189
+ finishWizard(rl);
190
+ return;
191
+ }
192
+
193
+ if (choice === '5') {
194
+ rl.question(' Path: ', function (pathAnswer) {
195
+ var p = path.resolve(pathAnswer.trim());
196
+ var fmt = p.endsWith('.mdc') ? 'mdc' : 'skill-md';
197
+ installSkill(cwd, p, fmt);
198
+ console.log(' Skill installed: ' + p);
199
+ finishWizard(rl);
200
+ });
201
+ return;
202
+ }
203
+
204
+ var agentKeys = ['claude-code', 'cursor', 'windsurf', 'vscode'];
205
+ var idx = parseInt(choice, 10) - 1;
206
+ if (idx < 0 || idx >= agentKeys.length) {
207
+ console.log(' Invalid choice — skipping.');
208
+ finishWizard(rl);
209
+ return;
210
+ }
211
+
212
+ var agent = AGENTS[agentKeys[idx]];
213
+ var fp = agent.path();
214
+ installSkill(cwd, fp, agent.format);
215
+ console.log(' Skill installed: ' + agent.label + ' → ' + fp);
216
+ finishWizard(rl);
217
+ });
218
+ }
219
+
220
+ // --- Done ---
221
+
222
+ function finishWizard(rl) {
223
+ console.log('\n Setup complete!\n');
224
+ console.log(' Next steps:');
225
+ console.log(' mdboard Start the dashboard');
226
+ console.log(' mdboard create ... Create your first entity');
227
+ console.log(' mdboard preset list Switch methodology or create a custom preset');
228
+ console.log(' mdboard skill Re-install skill anytime');
229
+ console.log('');
230
+ rl.close();
231
+ process.exit(0);
232
+ }
233
+
234
+ module.exports = { run };
@@ -0,0 +1,175 @@
1
+ /**
2
+ * mdboard init — Scaffold a new workspace with preset support
3
+ *
4
+ * Usage:
5
+ * mdboard init [name] [--preset shape-up]
6
+ *
7
+ * Creates the project/ directory structure based on the selected methodology preset.
8
+ * All directories and files are generated from the preset's structure.json config.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const readline = require('readline');
16
+ const configEngine = require('../core/config');
17
+
18
+ const cwd = process.cwd();
19
+ const projectPath = path.join(cwd, 'project');
20
+ const today = new Date().toISOString().split('T')[0];
21
+ const args = process.argv.slice(2).filter(a => a !== 'init');
22
+
23
+ // Parse flags
24
+ const preset = process.env.MDBOARD_PRESET || parseFlag('preset') || configEngine.DEFAULT_PRESET;
25
+ const nameArg = args.filter(a => !a.startsWith('--'))[0] || '';
26
+
27
+ function parseFlag(flag) {
28
+ for (let i = 0; i < args.length; i++) {
29
+ if (args[i] === '--' + flag && args[i + 1]) return args[i + 1];
30
+ }
31
+ return null;
32
+ }
33
+
34
+ // Check if project already exists
35
+ if (fs.existsSync(projectPath)) {
36
+ console.log('\n project/ already exists at ' + projectPath);
37
+ console.log(' Skipping init to avoid overwriting existing data.\n');
38
+ process.exit(0);
39
+ }
40
+
41
+ // Validate preset exists
42
+ const presetDir = path.join(configEngine.PRESETS_DIR, preset);
43
+ if (!fs.existsSync(presetDir)) {
44
+ const available = fs.readdirSync(configEngine.PRESETS_DIR).filter(d => {
45
+ return fs.statSync(path.join(configEngine.PRESETS_DIR, d)).isDirectory();
46
+ });
47
+ console.error('\n Error: unknown preset "' + preset + '"');
48
+ console.error(' Available presets: ' + available.join(', ') + '\n');
49
+ process.exit(1);
50
+ }
51
+
52
+ if (nameArg) {
53
+ scaffold(nameArg);
54
+ } else {
55
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
56
+ rl.question(' Workspace name: ', function (answer) {
57
+ rl.close();
58
+ const name = answer.trim() || path.basename(cwd);
59
+ scaffold(name);
60
+ });
61
+ }
62
+
63
+ function scaffold(name) {
64
+ // Load preset config
65
+ const cfg = configEngine.loadConfig(null);
66
+ const structure = cfg.structure || {};
67
+ const root = structure.root || 'project';
68
+ const hierarchy = structure.hierarchy || {};
69
+ const standalone = structure.standalone || {};
70
+
71
+ // Create root directory
72
+ fs.mkdirSync(projectPath, { recursive: true });
73
+
74
+ // Create hierarchy directories
75
+ createHierarchyDirs(projectPath, hierarchy);
76
+
77
+ // Create standalone directories
78
+ for (const [type, def] of Object.entries(standalone)) {
79
+ fs.mkdirSync(path.join(projectPath, def.dir), { recursive: true });
80
+ }
81
+
82
+ // Create archive directory if defined
83
+ if (structure.archive && structure.archive.dir) {
84
+ fs.mkdirSync(path.join(projectPath, structure.archive.dir), { recursive: true });
85
+ }
86
+
87
+ // PROJECT.md
88
+ fs.writeFileSync(path.join(projectPath, 'PROJECT.md'), '---\nname: "' + name + '"\ndescription: ""\ncreated: ' + today + '\nmethodology: ' + preset + '\n---\n\n# ' + name + '\n\n## Overview\n\nDescribe your project here.\n\n## Objectives\n\n-\n\n## Scope\n\n-\n', 'utf-8');
89
+
90
+ // mdboard.json — project config (preset + theme)
91
+ fs.writeFileSync(path.join(projectPath, 'mdboard.json'), JSON.stringify({ preset: preset }, null, 2) + '\n', 'utf-8');
92
+
93
+ // mdboard.css — example custom theme
94
+ fs.writeFileSync(path.join(projectPath, 'mdboard.css'), '/* mdboard custom theme\n Override any CSS variable from the dashboard.\n Example:\n :root {\n --bg: #FFFFFF;\n --surface: #F8F9FA;\n --text: #212529;\n --accent: #0969DA;\n }\n*/\n', 'utf-8');
95
+
96
+ // workspace.json
97
+ const wsPath = path.join(cwd, 'workspace.json');
98
+ if (!fs.existsSync(wsPath)) {
99
+ const slug = name.toLowerCase().replace(/\s+/g, '-');
100
+ fs.writeFileSync(wsPath, JSON.stringify({
101
+ name: name,
102
+ sources: [
103
+ { name: slug, label: name, path: '.', color: '#5B6EF5' }
104
+ ],
105
+ settings: {}
106
+ }, null, 2) + '\n', 'utf-8');
107
+ }
108
+
109
+ // Build directory listing for output
110
+ const dirs = collectDirs(hierarchy, standalone, structure.archive);
111
+
112
+ console.log('\n mdboard init — workspace "' + name + '" scaffolded');
113
+ console.log(' Preset: ' + preset + '\n');
114
+ console.log(' Created:');
115
+ console.log(' project/PROJECT.md');
116
+ for (const d of dirs) {
117
+ console.log(' project/' + d + '/');
118
+ }
119
+ console.log(' project/mdboard.css');
120
+ console.log(' workspace.json');
121
+
122
+ console.log('\n Next steps:');
123
+ console.log(' 1. Edit project/PROJECT.md with your project details');
124
+
125
+ // Show first create command example from hierarchy
126
+ const firstType = Object.keys(hierarchy)[0];
127
+ if (firstType) {
128
+ const entity = configEngine.getEntity(cfg, firstType);
129
+ if (entity) {
130
+ console.log(' 2. Create your first ' + entity.singular.toLowerCase() + ':');
131
+ console.log(' mdboard create ' + firstType + ' "My ' + entity.singular + '"');
132
+ }
133
+ }
134
+
135
+ console.log(' 3. Run `mdboard` to start the dashboard');
136
+ console.log('\n Other presets available:');
137
+ console.log(' mdboard preset list See all methodologies');
138
+ console.log(' mdboard preset create <name> Create a custom preset\n');
139
+ }
140
+
141
+ /**
142
+ * Recursively create directories for hierarchy entities.
143
+ */
144
+ function createHierarchyDirs(basePath, hierarchy) {
145
+ for (const [type, def] of Object.entries(hierarchy)) {
146
+ fs.mkdirSync(path.join(basePath, def.dir), { recursive: true });
147
+ // Don't recurse into children dirs here — they get created
148
+ // inside entity instance dirs when entities are created via CLI
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Collect directory names for output display.
154
+ */
155
+ function collectDirs(hierarchy, standalone, archive) {
156
+ const dirs = [];
157
+
158
+ function walkHierarchy(node) {
159
+ for (const [type, def] of Object.entries(node)) {
160
+ dirs.push(def.dir);
161
+ }
162
+ }
163
+
164
+ walkHierarchy(hierarchy);
165
+
166
+ for (const [type, def] of Object.entries(standalone || {})) {
167
+ dirs.push(def.dir);
168
+ }
169
+
170
+ if (archive && archive.dir) {
171
+ dirs.push(archive.dir);
172
+ }
173
+
174
+ return dirs;
175
+ }