mdan-cli 2.2.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/cli/mdan.js ADDED
@@ -0,0 +1,505 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+ const { intro, text, select, isCancel, cancel, outro, spinner } = require('@clack/prompts');
7
+ const pc = require('picocolors');
8
+
9
+ const VERSION = '2.2.0';
10
+ const MDAN_DIR = path.resolve(__dirname, '..');
11
+
12
+ // Colors
13
+ const colors = {
14
+ red: '\x1b[0;31m',
15
+ green: '\x1b[0;32m',
16
+ yellow: '\x1b[1;33m',
17
+ cyan: '\x1b[0;36m',
18
+ magenta: '\x1b[0;35m',
19
+ bold: '\x1b[1m',
20
+ nc: '\x1b[0m'
21
+ };
22
+
23
+ function banner() {
24
+ console.log(`${colors.cyan}
25
+ ███╗ ███╗██████╗ █████╗ ███╗ ██╗
26
+ ████╗ ████║██╔══██╗██╔══██╗████╗ ██║
27
+ ██╔████╔██║██║ ██║███████║██╔██╗ ██║
28
+ ██║╚██╔╝██║██║ ██║██╔══██║██║╚██╗██║
29
+ ██║ ╚═╝ ██║██████╔╝██║ ██║██║ ╚████║
30
+ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
31
+ ${colors.nc}
32
+ ${colors.bold}Multi-Agent Development Agentic Network${colors.nc} v${VERSION}
33
+ `);
34
+ }
35
+
36
+ function showHelp() {
37
+ banner();
38
+ console.log(`${colors.bold}USAGE${colors.nc}
39
+ mdan <command> [options]
40
+
41
+ ${colors.bold}COMMANDS${colors.nc}
42
+ init [name] Create a new project
43
+ attach [--rebuild] Add MDAN to existing project
44
+ status Show project status
45
+ phase [1-5] Show phase guide
46
+ workflow [name] Show granular workflow
47
+ module add [name] Install a domain module (e.g., agile-scrum)
48
+ agent [name] Show agent prompt
49
+ oc Copy orchestrator prompt to clipboard
50
+ skills List available skills
51
+ version Show version
52
+
53
+ ${colors.bold}EXAMPLES${colors.nc}
54
+ mdan init my-app # New project
55
+ cd my-project && mdan attach # Existing project
56
+ mdan attach --rebuild # Rebuild from scratch
57
+
58
+ ${colors.bold}AGENTS${colors.nc}
59
+ product, architect, ux, dev, test, security, devops, doc
60
+ `);
61
+ }
62
+
63
+ async function cmdInit(initialName) {
64
+ intro(pc.bgCyan(pc.black(' MDAN v' + VERSION + ' - Initialization Wizard ')));
65
+
66
+ let name = initialName;
67
+
68
+ if (!name) {
69
+ const namePrompt = await text({
70
+ message: 'What is your project name?',
71
+ initialValue: 'my-project',
72
+ validate(value) {
73
+ if (value.length === 0) return 'Name is required!';
74
+ }
75
+ });
76
+
77
+ if (isCancel(namePrompt)) {
78
+ cancel('Operation cancelled.');
79
+ return process.exit(0);
80
+ }
81
+ name = namePrompt;
82
+ }
83
+
84
+ const setupType = await select({
85
+ message: 'What kind of project are you building?',
86
+ options: [
87
+ { value: 'standard', label: 'Standard Web/Mobile App', hint: 'Default 5-phase workflow' },
88
+ { value: 'micro', label: 'Micro Project', hint: 'Solo dev, single feature, simple' },
89
+ { value: 'api', label: 'API / SDK', hint: 'No UI, dev-focused' },
90
+ { value: 'product', label: 'Scale-up Product', hint: 'Multi-month, stakeholders' }
91
+ ]
92
+ });
93
+
94
+ if (isCancel(setupType)) {
95
+ cancel('Operation cancelled.');
96
+ return process.exit(0);
97
+ }
98
+
99
+ const s = spinner();
100
+ s.start(`Creating ${name} project structure...`);
101
+
102
+ const dirs = [
103
+ `${name}/.mdan/agents`,
104
+ `${name}/.mdan/skills`,
105
+ `${name}/mdan_output`,
106
+ `${name}/.claude/skills`,
107
+ `${name}/.github`
108
+ ];
109
+
110
+ dirs.forEach(dir => fs.mkdirSync(dir, { recursive: true }));
111
+
112
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, `${name}/.mdan/orchestrator.md`);
113
+ fs.copyFileSync(`${MDAN_DIR}/core/universal-envelope.md`, `${name}/.mdan/universal-envelope.md`);
114
+
115
+ fs.readdirSync(`${MDAN_DIR}/agents`).filter(f => f.endsWith('.md')).forEach(f => {
116
+ fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `${name}/.mdan/agents/${f}`);
117
+ });
118
+
119
+ fs.readdirSync(`${MDAN_DIR}/templates`).filter(f => f.endsWith('.md')).forEach(f => {
120
+ fs.copyFileSync(`${MDAN_DIR}/templates/${f}`, `${name}/mdan_output/${f}`);
121
+ });
122
+
123
+ // Copy skills
124
+ const skillsDir = `${MDAN_DIR}/skills`;
125
+ if (fs.existsSync(skillsDir)) {
126
+ fs.readdirSync(skillsDir).forEach(skill => {
127
+ const src = `${skillsDir}/${skill}`;
128
+ const dest1 = `${name}/.mdan/skills/${skill}`;
129
+ const dest2 = `${name}/.claude/skills/${skill}`;
130
+ if (fs.statSync(src).isDirectory()) {
131
+ fs.cpSync(src, dest1, { recursive: true });
132
+ fs.cpSync(src, dest2, { recursive: true });
133
+ }
134
+ });
135
+ }
136
+
137
+ // Create .cursorrules
138
+ let cursorrules = fs.readFileSync(`${MDAN_DIR}/core/orchestrator.md`, 'utf8');
139
+ cursorrules += '\n\n## CURSOR INSTRUCTIONS\nAgent files are in .mdan/agents/\nSkills are in .mdan/skills/';
140
+ fs.writeFileSync(`${name}/.cursorrules`, cursorrules);
141
+ fs.copyFileSync(`${name}/.cursorrules`, `${name}/.windsurfrules`);
142
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, `${name}/.github/copilot-instructions.md`);
143
+
144
+ fs.writeFileSync(`${name}/README.md`, `# ${name}\n\n> Built with MDAN (${setupType} profile)\n`);
145
+
146
+ s.stop(pc.green(`Project ${name} initialized successfully!`));
147
+
148
+ outro(
149
+ pc.bold('Next steps:\n') +
150
+ `1. ${pc.cyan(`cd ${name}`)}\n` +
151
+ `2. Open the folder in your IDE (Cursor, Windsurf, etc.)\n` +
152
+ `3. Or run ${pc.cyan('mdan oc')} and paste into your favorite LLM chat.`
153
+ );
154
+ }
155
+
156
+ async function cmdAttach(rebuildMode) {
157
+ intro(pc.bgMagenta(pc.black(' MDAN v' + VERSION + ' - Attach Wizard ')));
158
+
159
+ const projectName = path.basename(process.cwd());
160
+ let isRebuild = rebuildMode === '--rebuild';
161
+
162
+ if (!rebuildMode) {
163
+ const action = await select({
164
+ message: `How do you want to attach MDAN to ${pc.bold(projectName)}?`,
165
+ options: [
166
+ { value: 'attach', label: 'Attach normally', hint: 'Analyze existing codebase and add features' },
167
+ { value: 'rebuild', label: 'Rebuild Mode', hint: 'Analyze then rewrite EVERYTHING from scratch' }
168
+ ]
169
+ });
170
+
171
+ if (isCancel(action)) {
172
+ cancel('Operation cancelled.');
173
+ return process.exit(0);
174
+ }
175
+
176
+ isRebuild = action === 'rebuild';
177
+ }
178
+
179
+ const s = spinner();
180
+ s.start(isRebuild
181
+ ? `Preparing REBUILD environment for ${projectName}...`
182
+ : `Attaching MDAN to ${projectName}...`);
183
+
184
+ fs.mkdirSync('.mdan/agents', { recursive: true });
185
+ fs.mkdirSync('.mdan/skills', { recursive: true });
186
+ fs.mkdirSync('.claude/skills', { recursive: true });
187
+ fs.mkdirSync('.github', { recursive: true });
188
+
189
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, '.mdan/orchestrator.md');
190
+ fs.copyFileSync(`${MDAN_DIR}/core/universal-envelope.md`, '.mdan/universal-envelope.md');
191
+
192
+ fs.readdirSync(`${MDAN_DIR}/agents`).filter(f => f.endsWith('.md')).forEach(f => {
193
+ fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `.mdan/agents/${f}`);
194
+ });
195
+
196
+ // Copy skills
197
+ const skillsDir = `${MDAN_DIR}/skills`;
198
+ if (fs.existsSync(skillsDir)) {
199
+ fs.readdirSync(skillsDir).forEach(skill => {
200
+ const src = `${skillsDir}/${skill}`;
201
+ if (fs.statSync(src).isDirectory()) {
202
+ fs.cpSync(src, `.mdan/skills/${skill}`, { recursive: true });
203
+ fs.cpSync(src, `.claude/skills/${skill}`, { recursive: true });
204
+ }
205
+ });
206
+ }
207
+
208
+ // Create .cursorrules
209
+ let cursorrules = fs.readFileSync(`${MDAN_DIR}/core/orchestrator.md`, 'utf8');
210
+ if (isRebuild) {
211
+ cursorrules += '\n\n## REBUILD MODE\nAnalyze existing code then rewrite from scratch.';
212
+ } else {
213
+ cursorrules += '\n\n## EXISTING PROJECT\nAnalyze codebase before making changes.';
214
+ }
215
+ fs.writeFileSync('.cursorrules', cursorrules);
216
+ fs.copyFileSync('.cursorrules', '.windsurfrules');
217
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, '.github/copilot-instructions.md');
218
+
219
+ s.stop(pc.green(`MDAN attached successfully!`));
220
+
221
+ outro(
222
+ pc.bold('Next steps:\n') +
223
+ `1. Open this folder in your IDE (Cursor, Windsurf, etc.)\n` +
224
+ `2. Or run ${pc.cyan('mdan oc')} and paste the prompt into Claude/ChatGPT\n\n` +
225
+ (isRebuild
226
+ ? pc.magenta(`Start prompt: "MDAN REBUILD: Analyze and rewrite this project"`)
227
+ : pc.cyan(`Start prompt: "MDAN: Analyze this project and help me [your goal]"`))
228
+ );
229
+ }
230
+
231
+ function cmdOc() {
232
+ let orchFile = '.mdan/orchestrator.md';
233
+ if (!fs.existsSync(orchFile)) {
234
+ orchFile = `${MDAN_DIR}/core/orchestrator.md`;
235
+ }
236
+
237
+ if (fs.existsSync(orchFile)) {
238
+ const content = fs.readFileSync(orchFile, 'utf8');
239
+ try {
240
+ if (process.platform === 'darwin') {
241
+ execSync('pbcopy', { input: content });
242
+ } else if (process.platform === 'win32') {
243
+ execSync('clip', { input: content });
244
+ } else if (process.platform === 'linux') {
245
+ try {
246
+ execSync('xclip -selection clipboard', { input: content });
247
+ } catch (e) {
248
+ execSync('wl-copy', { input: content });
249
+ }
250
+ } else {
251
+ throw new Error('Unsupported platform');
252
+ }
253
+ console.log(`${colors.green}✅ Orchestrator prompt copied to clipboard!${colors.nc}`);
254
+ console.log(' Paste it into Claude, ChatGPT, or your favorite LLM.');
255
+ } catch (e) {
256
+ console.log(content);
257
+ console.log(`\n${colors.yellow}⚠️ Could not copy to clipboard automatically. Please copy the text above.${colors.nc}`);
258
+ }
259
+ } else {
260
+ console.log(`${colors.red}Orchestrator file not found.${colors.nc}`);
261
+ }
262
+ }
263
+
264
+ function cmdStatus() {
265
+ if (fs.existsSync('.mdan/orchestrator.md')) {
266
+ console.log(`${colors.green}✅ MDAN is active in this project${colors.nc}`);
267
+ if (fs.existsSync('.mdan/STATUS.md')) {
268
+ console.log(fs.readFileSync('.mdan/STATUS.md', 'utf8'));
269
+ }
270
+ } else {
271
+ console.log(`${colors.yellow}No MDAN project here.${colors.nc}`);
272
+ console.log(' Run: mdan init [name] or mdan attach');
273
+ }
274
+ }
275
+
276
+ function cmdPhase(num, action) {
277
+ const phases = {
278
+ '1': ['01-discover.md', 'DISCOVER'],
279
+ 'discover': ['01-discover.md', 'DISCOVER'],
280
+ '2': ['02-design.md', 'DESIGN'],
281
+ 'design': ['02-design.md', 'DESIGN'],
282
+ '3': ['03-build.md', 'BUILD'],
283
+ 'build': ['03-build.md', 'BUILD'],
284
+ '4': ['04-verify.md', 'VERIFY'],
285
+ 'verify': ['04-verify.md', 'VERIFY'],
286
+ '5': ['05-ship.md', 'SHIP'],
287
+ 'ship': ['05-ship.md', 'SHIP']
288
+ };
289
+
290
+ if (!phases[num]) {
291
+ console.log('Usage: mdan phase [1-5|name] [copy]');
292
+ return;
293
+ }
294
+
295
+ const [file, name] = phases[num];
296
+ const phaseFile = `${MDAN_DIR}/phases/${file}`;
297
+
298
+ if (fs.existsSync(phaseFile)) {
299
+ const content = fs.readFileSync(phaseFile, 'utf8');
300
+
301
+ if (action === 'copy' || action === '-c') {
302
+ try {
303
+ if (process.platform === 'darwin') {
304
+ execSync('pbcopy', { input: content });
305
+ } else if (process.platform === 'win32') {
306
+ execSync('clip', { input: content });
307
+ } else if (process.platform === 'linux') {
308
+ try {
309
+ execSync('xclip -selection clipboard', { input: content });
310
+ } catch (e) {
311
+ execSync('wl-copy', { input: content });
312
+ }
313
+ } else {
314
+ throw new Error('Unsupported platform');
315
+ }
316
+ console.log(`${colors.green}✅ Phase ${name} prompt copied to clipboard!${colors.nc}`);
317
+ console.log(' Paste it into your LLM to start the phase.');
318
+ } catch (e) {
319
+ console.log(content);
320
+ console.log(`\n${colors.yellow}⚠️ Could not copy automatically. Please copy the text above.${colors.nc}`);
321
+ }
322
+ } else {
323
+ console.log(`${colors.cyan}${colors.bold}Phase ${name}${colors.nc}`);
324
+ console.log(content);
325
+ console.log(`\n${colors.yellow}Tip: Run '${colors.cyan}mdan phase ${num} copy${colors.yellow}' to copy this content to clipboard.${colors.nc}`);
326
+ }
327
+ } else {
328
+ console.log(`${colors.red}Phase file not found: ${file}${colors.nc}`);
329
+ }
330
+ }
331
+
332
+ function cmdModule(action, name) {
333
+ if (action !== 'add' || !name) {
334
+ console.log('Usage: mdan module add [name]');
335
+ console.log('Available modules:');
336
+ if (fs.existsSync(`${MDAN_DIR}/modules`)) {
337
+ fs.readdirSync(`${MDAN_DIR}/modules`).forEach(m => {
338
+ const stat = fs.statSync(`${MDAN_DIR}/modules/${m}`);
339
+ if (stat.isDirectory()) console.log(` ${m}`);
340
+ });
341
+ }
342
+ return;
343
+ }
344
+
345
+ const moduleDir = `${MDAN_DIR}/modules/${name}`;
346
+ if (!fs.existsSync(moduleDir)) {
347
+ console.log(`${colors.red}Module not found: ${name}${colors.nc}`);
348
+ return;
349
+ }
350
+
351
+ console.log(`${colors.cyan}📦 Installing module: ${colors.bold}${name}${colors.nc}`);
352
+
353
+ if (!fs.existsSync('.mdan')) {
354
+ console.log(`${colors.yellow}⚠️ No .mdan folder found. Are you in an MDAN project?${colors.nc}`);
355
+ return;
356
+ }
357
+
358
+ // Copy agents
359
+ if (fs.existsSync(`${moduleDir}/agents`)) {
360
+ fs.readdirSync(`${moduleDir}/agents`).forEach(f => {
361
+ fs.copyFileSync(`${moduleDir}/agents/${f}`, `.mdan/agents/${f}`);
362
+ console.log(`${colors.green} Added agent:${colors.nc} ${f}`);
363
+ });
364
+ }
365
+
366
+ console.log(`\n${colors.green}✅ Module ${name} installed!${colors.nc}`);
367
+ }
368
+
369
+ function cmdWorkflow(name, action) {
370
+ if (!name) {
371
+ console.log('Usage: mdan workflow [name] [copy]');
372
+ console.log('Available workflows:');
373
+ if (fs.existsSync(`${MDAN_DIR}/workflows`)) {
374
+ fs.readdirSync(`${MDAN_DIR}/workflows`).forEach(f => {
375
+ if (f.endsWith('.md')) console.log(` ${f.replace('.md', '')}`);
376
+ });
377
+ }
378
+ return;
379
+ }
380
+
381
+ const workflowFile = `${MDAN_DIR}/workflows/${name}.md`;
382
+
383
+ if (fs.existsSync(workflowFile)) {
384
+ const content = fs.readFileSync(workflowFile, 'utf8');
385
+
386
+ if (action === 'copy' || action === '-c') {
387
+ try {
388
+ if (process.platform === 'darwin') {
389
+ execSync('pbcopy', { input: content });
390
+ } else if (process.platform === 'win32') {
391
+ execSync('clip', { input: content });
392
+ } else if (process.platform === 'linux') {
393
+ try {
394
+ execSync('xclip -selection clipboard', { input: content });
395
+ } catch (e) {
396
+ execSync('wl-copy', { input: content });
397
+ }
398
+ }
399
+ console.log(`${colors.green}✅ Workflow ${name} prompt copied to clipboard!${colors.nc}`);
400
+ } catch (e) {
401
+ console.log(content);
402
+ console.log(`\n${colors.yellow}⚠️ Could not copy automatically. Please copy the text above.${colors.nc}`);
403
+ }
404
+ } else {
405
+ console.log(`${colors.cyan}${colors.bold}Workflow: ${name}${colors.nc}`);
406
+ console.log(content);
407
+ console.log(`\n${colors.yellow}Tip: Run '${colors.cyan}mdan workflow ${name} copy${colors.yellow}' to copy this content to clipboard.${colors.nc}`);
408
+ }
409
+ } else {
410
+ console.log(`${colors.red}Workflow not found: ${name}${colors.nc}`);
411
+ }
412
+ }
413
+
414
+ function cmdAgent(name) {
415
+ const file = `${MDAN_DIR}/agents/${name}.md`;
416
+ if (fs.existsSync(file)) {
417
+ console.log(fs.readFileSync(file, 'utf8'));
418
+ } else {
419
+ console.log('Agents: product, architect, ux, dev, test, security, devops, doc');
420
+ }
421
+ }
422
+
423
+ function cmdSkills() {
424
+ console.log(`${colors.cyan}Skills:${colors.nc}`);
425
+ const skillsDir = `${MDAN_DIR}/skills`;
426
+ if (fs.existsSync(skillsDir)) {
427
+ fs.readdirSync(skillsDir).forEach(s => console.log(` ${s}`));
428
+ } else {
429
+ console.log(' No skills installed');
430
+ }
431
+ }
432
+
433
+ // Main
434
+ const [,, cmd, ...args] = process.argv;
435
+
436
+ async function main() {
437
+ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
438
+ if (!cmd) {
439
+ // Interactive Wizard Default
440
+ intro(pc.bgCyan(pc.black(' MDAN v' + VERSION + ' ')));
441
+ const action = await select({
442
+ message: 'What would you like to do?',
443
+ options: [
444
+ { value: 'init', label: 'Create a new project', hint: 'Start fresh' },
445
+ { value: 'attach', label: 'Attach to existing project', hint: 'Add MDAN to this folder' },
446
+ { value: 'help', label: 'Show Help', hint: 'See all commands' }
447
+ ]
448
+ });
449
+
450
+ if (isCancel(action)) {
451
+ cancel('Operation cancelled.');
452
+ return process.exit(0);
453
+ }
454
+
455
+ if (action === 'init') {
456
+ return cmdInit();
457
+ } else if (action === 'attach') {
458
+ return cmdAttach();
459
+ } else if (action === 'help') {
460
+ showHelp();
461
+ return;
462
+ }
463
+ } else {
464
+ showHelp();
465
+ }
466
+ return;
467
+ }
468
+
469
+ switch (cmd) {
470
+ case 'init':
471
+ await cmdInit(args[0]);
472
+ break;
473
+ case 'attach':
474
+ await cmdAttach(args[0]);
475
+ break;
476
+ case 'oc':
477
+ cmdOc();
478
+ break;
479
+ case 'status':
480
+ cmdStatus();
481
+ break;
482
+ case 'phase':
483
+ cmdPhase(args[0], args[1]);
484
+ break;
485
+ case 'module':
486
+ cmdModule(args[0], args[1]);
487
+ break;
488
+ case 'workflow':
489
+ cmdWorkflow(args[0], args[1]);
490
+ break;
491
+ case 'agent':
492
+ cmdAgent(args[0]);
493
+ break;
494
+ case 'skills':
495
+ cmdSkills();
496
+ break;
497
+ case 'version':
498
+ case '-v':
499
+ console.log(`MDAN v${VERSION}`);
500
+ break;
501
+ default:
502
+ console.log(`Unknown: ${cmd}. Run: mdan help`);
503
+ }
504
+ }
505
+ main().catch(console.error);