mdan-cli 2.2.0 → 2.4.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 CHANGED
@@ -6,7 +6,7 @@ const { execSync } = require('child_process');
6
6
  const { intro, text, select, isCancel, cancel, outro, spinner } = require('@clack/prompts');
7
7
  const pc = require('picocolors');
8
8
 
9
- const VERSION = '2.2.0';
9
+ const VERSION = '2.4.0';
10
10
  const MDAN_DIR = path.resolve(__dirname, '..');
11
11
 
12
12
  // Colors
@@ -38,7 +38,7 @@ function showHelp() {
38
38
  console.log(`${colors.bold}USAGE${colors.nc}
39
39
  mdan <command> [options]
40
40
 
41
- ${colors.bold}COMMANDS${colors.nc}
41
+ ${colors.bold}COMMANDS${colors.nc}
42
42
  init [name] Create a new project
43
43
  attach [--rebuild] Add MDAN to existing project
44
44
  status Show project status
@@ -48,16 +48,20 @@ ${colors.bold}COMMANDS${colors.nc}
48
48
  agent [name] Show agent prompt
49
49
  oc Copy orchestrator prompt to clipboard
50
50
  skills List available skills
51
+ mcp [action] MCP config (init|validate|list)
52
+ prompt [action] Manage prompts (list|show <name>)
51
53
  version Show version
52
54
 
53
- ${colors.bold}EXAMPLES${colors.nc}
55
+ ${colors.bold}EXAMPLES${colors.nc}
54
56
  mdan init my-app # New project
55
57
  cd my-project && mdan attach # Existing project
56
58
  mdan attach --rebuild # Rebuild from scratch
59
+ mdan mcp init # Generate .mcp.json
60
+ mdan prompt list # List versioned prompts
57
61
 
58
- ${colors.bold}AGENTS${colors.nc}
62
+ ${colors.bold}AGENTS${colors.nc}
59
63
  product, architect, ux, dev, test, security, devops, doc
60
- `);
64
+ `);
61
65
  }
62
66
 
63
67
  async function cmdInit(initialName) {
@@ -100,20 +104,23 @@ async function cmdInit(initialName) {
100
104
  s.start(`Creating ${name} project structure...`);
101
105
 
102
106
  const dirs = [
103
- `${name}/.mdan/agents`,
104
- `${name}/.mdan/skills`,
107
+ `${name}/mdan/agents`,
108
+ `${name}/mdan/skills`,
105
109
  `${name}/mdan_output`,
106
110
  `${name}/.claude/skills`,
107
- `${name}/.github`
111
+ `${name}/.github`,
112
+ `${name}/tests/scenarios`,
113
+ `${name}/tests/evaluations`,
114
+ `${name}/templates/prompts`
108
115
  ];
109
116
 
110
117
  dirs.forEach(dir => fs.mkdirSync(dir, { recursive: true }));
111
118
 
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`);
119
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, `${name}/mdan/orchestrator.md`);
120
+ fs.copyFileSync(`${MDAN_DIR}/core/universal-envelope.md`, `${name}/mdan/universal-envelope.md`);
114
121
 
115
122
  fs.readdirSync(`${MDAN_DIR}/agents`).filter(f => f.endsWith('.md')).forEach(f => {
116
- fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `${name}/.mdan/agents/${f}`);
123
+ fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `${name}/mdan/agents/${f}`);
117
124
  });
118
125
 
119
126
  fs.readdirSync(`${MDAN_DIR}/templates`).filter(f => f.endsWith('.md')).forEach(f => {
@@ -125,7 +132,7 @@ async function cmdInit(initialName) {
125
132
  if (fs.existsSync(skillsDir)) {
126
133
  fs.readdirSync(skillsDir).forEach(skill => {
127
134
  const src = `${skillsDir}/${skill}`;
128
- const dest1 = `${name}/.mdan/skills/${skill}`;
135
+ const dest1 = `${name}/mdan/skills/${skill}`;
129
136
  const dest2 = `${name}/.claude/skills/${skill}`;
130
137
  if (fs.statSync(src).isDirectory()) {
131
138
  fs.cpSync(src, dest1, { recursive: true });
@@ -136,13 +143,30 @@ async function cmdInit(initialName) {
136
143
 
137
144
  // Create .cursorrules
138
145
  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/';
146
+ cursorrules += '\n\n## CURSOR INSTRUCTIONS\nAgent files are in mdan/agents/\nSkills are in mdan/skills/';
140
147
  fs.writeFileSync(`${name}/.cursorrules`, cursorrules);
141
148
  fs.copyFileSync(`${name}/.cursorrules`, `${name}/.windsurfrules`);
142
149
  fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, `${name}/.github/copilot-instructions.md`);
143
150
 
144
151
  fs.writeFileSync(`${name}/README.md`, `# ${name}\n\n> Built with MDAN (${setupType} profile)\n`);
145
152
 
153
+ // Copy AGENTS.md and generate .mcp.json
154
+ if (fs.existsSync(`${MDAN_DIR}/AGENTS.md`)) {
155
+ fs.copyFileSync(`${MDAN_DIR}/AGENTS.md`, `${name}/AGENTS.md`);
156
+ }
157
+
158
+ const mcpConfig = {
159
+ mcpServers: { "mdan-memory": { command: "node", args: ["-e", "console.log('MDAN MCP')"] } },
160
+ metadata: { version: VERSION, framework: "mdan", generated: new Date().toISOString().split('T')[0] },
161
+ capabilities: {
162
+ scenarios: { enabled: true, test_paths: ["tests/scenarios/", "templates/tests/scenarios/"] },
163
+ evaluations: { enabled: true, eval_paths: ["tests/evaluations/", "templates/tests/evaluations/"] },
164
+ prompts: { enabled: true, prompt_paths: ["templates/prompts/"], registry: "templates/prompts.json" }
165
+ },
166
+ quality_gates: { min_test_coverage: 80, require_evaluations: true, require_scenarios: false }
167
+ };
168
+ fs.writeFileSync(`${name}/.mcp.json`, JSON.stringify(mcpConfig, null, 2));
169
+
146
170
  s.stop(pc.green(`Project ${name} initialized successfully!`));
147
171
 
148
172
  outro(
@@ -181,16 +205,19 @@ async function cmdAttach(rebuildMode) {
181
205
  ? `Preparing REBUILD environment for ${projectName}...`
182
206
  : `Attaching MDAN to ${projectName}...`);
183
207
 
184
- fs.mkdirSync('.mdan/agents', { recursive: true });
185
- fs.mkdirSync('.mdan/skills', { recursive: true });
208
+ fs.mkdirSync('mdan/agents', { recursive: true });
209
+ fs.mkdirSync('mdan/skills', { recursive: true });
186
210
  fs.mkdirSync('.claude/skills', { recursive: true });
187
211
  fs.mkdirSync('.github', { recursive: true });
212
+ fs.mkdirSync('tests/scenarios', { recursive: true });
213
+ fs.mkdirSync('tests/evaluations', { recursive: true });
214
+ fs.mkdirSync('templates/prompts', { recursive: true });
188
215
 
189
- fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, '.mdan/orchestrator.md');
190
- fs.copyFileSync(`${MDAN_DIR}/core/universal-envelope.md`, '.mdan/universal-envelope.md');
216
+ fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, 'mdan/orchestrator.md');
217
+ fs.copyFileSync(`${MDAN_DIR}/core/universal-envelope.md`, 'mdan/universal-envelope.md');
191
218
 
192
219
  fs.readdirSync(`${MDAN_DIR}/agents`).filter(f => f.endsWith('.md')).forEach(f => {
193
- fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `.mdan/agents/${f}`);
220
+ fs.copyFileSync(`${MDAN_DIR}/agents/${f}`, `mdan/agents/${f}`);
194
221
  });
195
222
 
196
223
  // Copy skills
@@ -199,7 +226,7 @@ async function cmdAttach(rebuildMode) {
199
226
  fs.readdirSync(skillsDir).forEach(skill => {
200
227
  const src = `${skillsDir}/${skill}`;
201
228
  if (fs.statSync(src).isDirectory()) {
202
- fs.cpSync(src, `.mdan/skills/${skill}`, { recursive: true });
229
+ fs.cpSync(src, `mdan/skills/${skill}`, { recursive: true });
203
230
  fs.cpSync(src, `.claude/skills/${skill}`, { recursive: true });
204
231
  }
205
232
  });
@@ -216,6 +243,23 @@ async function cmdAttach(rebuildMode) {
216
243
  fs.copyFileSync('.cursorrules', '.windsurfrules');
217
244
  fs.copyFileSync(`${MDAN_DIR}/core/orchestrator.md`, '.github/copilot-instructions.md');
218
245
 
246
+ // Copy AGENTS.md and generate .mcp.json
247
+ if (fs.existsSync(`${MDAN_DIR}/AGENTS.md`)) {
248
+ fs.copyFileSync(`${MDAN_DIR}/AGENTS.md`, 'AGENTS.md');
249
+ }
250
+
251
+ const mcpConfig = {
252
+ mcpServers: { "mdan-memory": { command: "node", args: ["-e", "console.log('MDAN MCP')"] } },
253
+ metadata: { version: VERSION, framework: "mdan", generated: new Date().toISOString().split('T')[0] },
254
+ capabilities: {
255
+ scenarios: { enabled: true, test_paths: ["tests/scenarios/", "templates/tests/scenarios/"] },
256
+ evaluations: { enabled: true, eval_paths: ["tests/evaluations/", "templates/tests/evaluations/"] },
257
+ prompts: { enabled: true, prompt_paths: ["templates/prompts/"], registry: "templates/prompts.json" }
258
+ },
259
+ quality_gates: { min_test_coverage: 80, require_evaluations: true, require_scenarios: false }
260
+ };
261
+ fs.writeFileSync('.mcp.json', JSON.stringify(mcpConfig, null, 2));
262
+
219
263
  s.stop(pc.green(`MDAN attached successfully!`));
220
264
 
221
265
  outro(
@@ -229,7 +273,7 @@ async function cmdAttach(rebuildMode) {
229
273
  }
230
274
 
231
275
  function cmdOc() {
232
- let orchFile = '.mdan/orchestrator.md';
276
+ let orchFile = 'mdan/orchestrator.md';
233
277
  if (!fs.existsSync(orchFile)) {
234
278
  orchFile = `${MDAN_DIR}/core/orchestrator.md`;
235
279
  }
@@ -262,10 +306,10 @@ function cmdOc() {
262
306
  }
263
307
 
264
308
  function cmdStatus() {
265
- if (fs.existsSync('.mdan/orchestrator.md')) {
309
+ if (fs.existsSync('mdan/orchestrator.md')) {
266
310
  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'));
311
+ if (fs.existsSync('mdan/STATUS.md')) {
312
+ console.log(fs.readFileSync('mdan/STATUS.md', 'utf8'));
269
313
  }
270
314
  } else {
271
315
  console.log(`${colors.yellow}No MDAN project here.${colors.nc}`);
@@ -350,15 +394,15 @@ function cmdModule(action, name) {
350
394
 
351
395
  console.log(`${colors.cyan}📦 Installing module: ${colors.bold}${name}${colors.nc}`);
352
396
 
353
- if (!fs.existsSync('.mdan')) {
354
- console.log(`${colors.yellow}⚠️ No .mdan folder found. Are you in an MDAN project?${colors.nc}`);
397
+ if (!fs.existsSync('mdan')) {
398
+ console.log(`${colors.yellow}⚠️ No mdan folder found. Are you in an MDAN project?${colors.nc}`);
355
399
  return;
356
400
  }
357
401
 
358
402
  // Copy agents
359
403
  if (fs.existsSync(`${moduleDir}/agents`)) {
360
404
  fs.readdirSync(`${moduleDir}/agents`).forEach(f => {
361
- fs.copyFileSync(`${moduleDir}/agents/${f}`, `.mdan/agents/${f}`);
405
+ fs.copyFileSync(`${moduleDir}/agents/${f}`, `mdan/agents/${f}`);
362
406
  console.log(`${colors.green} Added agent:${colors.nc} ${f}`);
363
407
  });
364
408
  }
@@ -430,6 +474,79 @@ function cmdSkills() {
430
474
  }
431
475
  }
432
476
 
477
+ function cmdMcp(action) {
478
+ if (!action || action === 'init') {
479
+ const mcpConfig = {
480
+ mcpServers: {
481
+ "mdan-memory": {
482
+ command: "node",
483
+ args: ["-e", "console.log(JSON.stringify({tools: []}))"]
484
+ }
485
+ },
486
+ metadata: {
487
+ version: VERSION,
488
+ framework: "mdan",
489
+ generated: new Date().toISOString().split('T')[0]
490
+ },
491
+ capabilities: {
492
+ scenarios: { enabled: true, test_paths: ["tests/scenarios/", "templates/tests/scenarios/"] },
493
+ evaluations: { enabled: true, eval_paths: ["tests/evaluations/", "templates/tests/evaluations/"] },
494
+ prompts: { enabled: true, prompt_paths: ["templates/prompts/"], registry: "templates/prompts.json" }
495
+ },
496
+ quality_gates: {
497
+ min_test_coverage: 80,
498
+ require_evaluations: true,
499
+ require_scenarios: false
500
+ }
501
+ };
502
+ fs.writeFileSync('.mcp.json', JSON.stringify(mcpConfig, null, 2));
503
+ console.log(`${colors.green}✅ .mcp.json created!${colors.nc}`);
504
+ console.log(' Configure your IDE to use MCP with this file.');
505
+ } else if (action === 'validate') {
506
+ if (fs.existsSync('.mcp.json')) {
507
+ try {
508
+ JSON.parse(fs.readFileSync('.mcp.json', 'utf8'));
509
+ console.log(`${colors.green}✅ .mcp.json is valid${colors.nc}`);
510
+ } catch (e) {
511
+ console.log(`${colors.red}❌ Invalid JSON: ${e.message}${colors.nc}`);
512
+ }
513
+ } else {
514
+ console.log(`${colors.yellow}⚠️ No .mcp.json found${colors.nc}`);
515
+ }
516
+ } else if (action === 'list') {
517
+ console.log(`${colors.cyan}MCP Tools:${colors.nc}`);
518
+ console.log(' - mdan-state: Read/write project state');
519
+ console.log(' - mdan-agents: List MDAN agents');
520
+ console.log(' - mdan-phases: Get phase information');
521
+ } else {
522
+ console.log('Usage: mdan mcp [init|validate|list]');
523
+ }
524
+ }
525
+
526
+ function cmdPrompt(action, name) {
527
+ const promptsDir = `${MDAN_DIR}/templates/prompts`;
528
+ if (!fs.existsSync(promptsDir)) {
529
+ console.log(`${colors.yellow}No prompts directory found${colors.nc}`);
530
+ return;
531
+ }
532
+
533
+ if (!action || action === 'list') {
534
+ console.log(`${colors.cyan}Available Prompts:${colors.nc}`);
535
+ fs.readdirSync(promptsDir).filter(f => f.endsWith('.yaml')).forEach(f => {
536
+ console.log(` ${f.replace('.yaml', '')}`);
537
+ });
538
+ } else if (action === 'show' && name) {
539
+ const file = `${promptsDir}/${name}.yaml`;
540
+ if (fs.existsSync(file)) {
541
+ console.log(fs.readFileSync(file, 'utf8'));
542
+ } else {
543
+ console.log(`${colors.red}Prompt not found: ${name}${colors.nc}`);
544
+ }
545
+ } else {
546
+ console.log('Usage: mdan prompt [list|show <name>]');
547
+ }
548
+ }
549
+
433
550
  // Main
434
551
  const [,, cmd, ...args] = process.argv;
435
552
 
@@ -494,6 +611,12 @@ async function main() {
494
611
  case 'skills':
495
612
  cmdSkills();
496
613
  break;
614
+ case 'mcp':
615
+ cmdMcp(args[0]);
616
+ break;
617
+ case 'prompt':
618
+ cmdPrompt(args[0], args[1]);
619
+ break;
497
620
  case 'version':
498
621
  case '-v':
499
622
  console.log(`MDAN v${VERSION}`);
package/cli/mdan.py CHANGED
@@ -7,7 +7,7 @@ import shutil
7
7
  import json
8
8
  from pathlib import Path
9
9
 
10
- VERSION = "2.2.0"
10
+ VERSION = "2.4.0"
11
11
  MDAN_DIR = Path(__file__).parent.parent
12
12
 
13
13
  # Colors
@@ -19,6 +19,7 @@ MAGENTA = "\033[0;35m"
19
19
  BOLD = "\033[1m"
20
20
  NC = "\033[0m"
21
21
 
22
+
22
23
  def banner():
23
24
  print(f"{CYAN}")
24
25
  print(" ███╗ ███╗██████╗ █████╗ ███╗ ██╗")
@@ -30,6 +31,7 @@ def banner():
30
31
  print(f"{NC}")
31
32
  print(f" {BOLD}Multi-Agent Development Agentic Network{NC} v{VERSION}\n")
32
33
 
34
+
33
35
  def show_help():
34
36
  banner()
35
37
  print(f"{BOLD}USAGE{NC}")
@@ -50,77 +52,90 @@ def show_help():
50
52
  print(f"{BOLD}AGENTS{NC}")
51
53
  print(" product, architect, ux, dev, test, security, devops, doc")
52
54
 
55
+
53
56
  def cmd_init(name="my-project"):
54
57
  print(f"{CYAN}🚀 Creating: {BOLD}{name}{NC}")
55
-
58
+
56
59
  dirs = [
57
- f"{name}/.mdan/agents", f"{name}/.mdan/skills", f"{name}/mdan_output",
58
- f"{name}/.claude/skills", f"{name}/.github"
60
+ f"{name}/mdan/agents",
61
+ f"{name}/mdan/skills",
62
+ f"{name}/mdan_output",
63
+ f"{name}/.claude/skills",
64
+ f"{name}/.github",
59
65
  ]
60
66
  for d in dirs:
61
67
  Path(d).mkdir(parents=True, exist_ok=True)
62
-
63
- shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", f"{name}/.mdan/")
64
- shutil.copy(f"{MDAN_DIR}/core/universal-envelope.md", f"{name}/.mdan/")
65
-
68
+
69
+ shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", f"{name}/mdan/")
70
+ shutil.copy(f"{MDAN_DIR}/core/universal-envelope.md", f"{name}/mdan/")
71
+
66
72
  for f in Path(f"{MDAN_DIR}/agents").glob("*.md"):
67
- shutil.copy(f, f"{name}/.mdan/agents/")
68
-
73
+ shutil.copy(f, f"{name}/mdan/agents/")
74
+
69
75
  for f in Path(f"{MDAN_DIR}/templates").glob("*.md"):
70
76
  shutil.copy(f, f"{name}/mdan_output/")
71
-
77
+
72
78
  skills_dir = Path(f"{MDAN_DIR}/skills")
73
79
  if skills_dir.exists():
74
80
  for s in skills_dir.iterdir():
75
81
  if s.is_dir():
76
- shutil.copytree(s, f"{name}/.mdan/skills/{s.name}", dirs_exist_ok=True)
77
- shutil.copytree(s, f"{name}/.claude/skills/{s.name}", dirs_exist_ok=True)
78
-
82
+ shutil.copytree(s, f"{name}/mdan/skills/{s.name}", dirs_exist_ok=True)
83
+ shutil.copytree(
84
+ s, f"{name}/.claude/skills/{s.name}", dirs_exist_ok=True
85
+ )
86
+
79
87
  cursorrules = Path(f"{MDAN_DIR}/core/orchestrator.md").read_text()
80
- cursorrules += "\n\n## CURSOR INSTRUCTIONS\nAgent files in .mdan/agents/\nSkills in .mdan/skills/"
88
+ cursorrules += "\n\n## CURSOR INSTRUCTIONS\nAgent files in mdan/agents/\nSkills in mdan/skills/"
81
89
  Path(f"{name}/.cursorrules").write_text(cursorrules)
82
90
  shutil.copy(f"{name}/.cursorrules", f"{name}/.windsurfrules")
83
- shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", f"{name}/.github/copilot-instructions.md")
91
+ shutil.copy(
92
+ f"{MDAN_DIR}/core/orchestrator.md", f"{name}/.github/copilot-instructions.md"
93
+ )
84
94
  Path(f"{name}/README.md").write_text(f"# {name}\n\n> Built with MDAN\n")
85
-
95
+
86
96
  print(f"{GREEN}✅ Created {name}/{NC}\n")
87
97
  print(f" {BOLD}Next:{NC} cursor {name}")
88
98
 
99
+
89
100
  def cmd_attach(rebuild=None):
90
101
  project = Path.cwd().name
91
-
102
+
92
103
  if rebuild == "--rebuild":
93
104
  print(f"{MAGENTA}🔄 REBUILD MODE: {BOLD}{project}{NC}")
94
105
  else:
95
106
  print(f"{CYAN}🔗 Attaching to: {BOLD}{project}{NC}")
96
-
97
- Path(".mdan/agents").mkdir(parents=True, exist_ok=True)
98
- Path(".mdan/skills").mkdir(parents=True, exist_ok=True)
107
+
108
+ Path("mdan/agents").mkdir(parents=True, exist_ok=True)
109
+ Path("mdan/skills").mkdir(parents=True, exist_ok=True)
99
110
  Path(".claude/skills").mkdir(parents=True, exist_ok=True)
100
111
  Path(".github").mkdir(parents=True, exist_ok=True)
101
-
102
- shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", ".mdan/")
103
- shutil.copy(f"{MDAN_DIR}/core/universal-envelope.md", ".mdan/")
104
-
112
+
113
+ shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", "mdan/")
114
+ shutil.copy(f"{MDAN_DIR}/core/universal-envelope.md", "mdan/")
115
+
105
116
  for f in Path(f"{MDAN_DIR}/agents").glob("*.md"):
106
- shutil.copy(f, ".mdan/agents/")
107
-
117
+ shutil.copy(f, "mdan/agents/")
118
+
108
119
  skills_dir = Path(f"{MDAN_DIR}/skills")
109
120
  if skills_dir.exists():
110
121
  for s in skills_dir.iterdir():
111
122
  if s.is_dir():
112
- shutil.copytree(s, f".mdan/skills/{s.name}", dirs_exist_ok=True)
123
+ shutil.copytree(s, f"mdan/skills/{s.name}", dirs_exist_ok=True)
113
124
  shutil.copytree(s, f".claude/skills/{s.name}", dirs_exist_ok=True)
114
-
125
+
115
126
  cursorrules = Path(f"{MDAN_DIR}/core/orchestrator.md").read_text()
116
127
  if rebuild == "--rebuild":
117
- cursorrules += "\n\n## REBUILD MODE\nAnalyze existing code then rewrite from scratch."
128
+ cursorrules += (
129
+ "\n\n## REBUILD MODE\nAnalyze existing code then rewrite from scratch."
130
+ )
118
131
  else:
119
- cursorrules += "\n\n## EXISTING PROJECT\nAnalyze codebase before making changes."
132
+ cursorrules += (
133
+ "\n\n## EXISTING PROJECT\nAnalyze codebase before making changes."
134
+ )
120
135
  Path(".cursorrules").write_text(cursorrules)
121
136
  shutil.copy(".cursorrules", ".windsurfrules")
122
137
  shutil.copy(f"{MDAN_DIR}/core/orchestrator.md", ".github/copilot-instructions.md")
123
-
138
+
124
139
  print(f"{GREEN}✅ MDAN ready!{NC}\n")
125
140
  print(f" {BOLD}Next:{NC} Run {CYAN}mdan oc{NC} to copy prompt")
126
141
  if rebuild == "--rebuild":
@@ -128,45 +143,63 @@ def cmd_attach(rebuild=None):
128
143
  else:
129
144
  print(f" Start: {CYAN}MDAN: Analyze this project{NC}")
130
145
 
146
+
131
147
  def cmd_oc():
132
148
  import subprocess
133
- orch_file = Path(".mdan/orchestrator.md")
149
+
150
+ orch_file = Path("mdan/orchestrator.md")
134
151
  if not orch_file.exists():
135
152
  orch_file = Path(f"{MDAN_DIR}/core/orchestrator.md")
136
-
153
+
137
154
  if orch_file.exists():
138
155
  content = orch_file.read_text()
139
156
  try:
140
157
  if sys.platform == "darwin":
141
- subprocess.run("pbcopy", universal_newlines=True, input=content, check=True)
158
+ subprocess.run(
159
+ "pbcopy", universal_newlines=True, input=content, check=True
160
+ )
142
161
  elif sys.platform == "win32":
143
- subprocess.run("clip", universal_newlines=True, input=content, check=True)
162
+ subprocess.run(
163
+ "clip", universal_newlines=True, input=content, check=True
164
+ )
144
165
  elif sys.platform == "linux":
145
166
  if shutil.which("xclip"):
146
- subprocess.run(["xclip", "-selection", "clipboard"], universal_newlines=True, input=content, check=True)
167
+ subprocess.run(
168
+ ["xclip", "-selection", "clipboard"],
169
+ universal_newlines=True,
170
+ input=content,
171
+ check=True,
172
+ )
147
173
  elif shutil.which("wl-copy"):
148
- subprocess.run(["wl-copy"], universal_newlines=True, input=content, check=True)
174
+ subprocess.run(
175
+ ["wl-copy"], universal_newlines=True, input=content, check=True
176
+ )
149
177
  else:
150
178
  raise Exception("No clipboard tool found")
151
179
  print(f"{GREEN}✅ Orchestrator prompt copied to clipboard!{NC}")
152
180
  print(" Paste it into Claude, ChatGPT, or your favorite LLM.")
153
181
  except Exception:
154
182
  print(content)
155
- print(f"\n{YELLOW}⚠️ Could not copy to clipboard automatically. Please copy the text above.{NC}")
183
+ print(
184
+ f"\n{YELLOW}⚠️ Could not copy to clipboard automatically. Please copy the text above.{NC}"
185
+ )
156
186
  else:
157
187
  print(f"{RED}Orchestrator file not found.{NC}")
158
188
 
189
+
159
190
  def cmd_status():
160
- if Path(".mdan/orchestrator.md").exists():
191
+ if Path("mdan/orchestrator.md").exists():
161
192
  print(f"{GREEN}✅ MDAN is active in this project{NC}")
162
- if Path(".mdan/STATUS.md").exists():
163
- print(Path(".mdan/STATUS.md").read_text())
193
+ if Path("mdan/STATUS.md").exists():
194
+ print(Path("mdan/STATUS.md").read_text())
164
195
  else:
165
196
  print(f"{YELLOW}No MDAN project here.{NC}")
166
197
  print(" Run: mdan init [name] or mdan attach")
167
198
 
199
+
168
200
  def cmd_phase(num, action=None):
169
201
  import subprocess
202
+
170
203
  phases = {
171
204
  "1": ("01-discover.md", "DISCOVER"),
172
205
  "discover": ("01-discover.md", "DISCOVER"),
@@ -177,43 +210,62 @@ def cmd_phase(num, action=None):
177
210
  "4": ("04-verify.md", "VERIFY"),
178
211
  "verify": ("04-verify.md", "VERIFY"),
179
212
  "5": ("05-ship.md", "SHIP"),
180
- "ship": ("05-ship.md", "SHIP")
213
+ "ship": ("05-ship.md", "SHIP"),
181
214
  }
182
-
215
+
183
216
  if num not in phases:
184
217
  print("Usage: mdan phase [1-5|name] [copy]")
185
218
  return
186
-
219
+
187
220
  file, name = phases[num]
188
221
  phase_file = Path(f"{MDAN_DIR}/phases/{file}")
189
-
222
+
190
223
  if phase_file.exists():
191
224
  content = phase_file.read_text()
192
225
  if action in ["copy", "-c"]:
193
226
  try:
194
227
  if sys.platform == "darwin":
195
- subprocess.run("pbcopy", universal_newlines=True, input=content, check=True)
228
+ subprocess.run(
229
+ "pbcopy", universal_newlines=True, input=content, check=True
230
+ )
196
231
  elif sys.platform == "win32":
197
- subprocess.run("clip", universal_newlines=True, input=content, check=True)
232
+ subprocess.run(
233
+ "clip", universal_newlines=True, input=content, check=True
234
+ )
198
235
  elif sys.platform == "linux":
199
236
  if shutil.which("xclip"):
200
- subprocess.run(["xclip", "-selection", "clipboard"], universal_newlines=True, input=content, check=True)
237
+ subprocess.run(
238
+ ["xclip", "-selection", "clipboard"],
239
+ universal_newlines=True,
240
+ input=content,
241
+ check=True,
242
+ )
201
243
  elif shutil.which("wl-copy"):
202
- subprocess.run(["wl-copy"], universal_newlines=True, input=content, check=True)
244
+ subprocess.run(
245
+ ["wl-copy"],
246
+ universal_newlines=True,
247
+ input=content,
248
+ check=True,
249
+ )
203
250
  else:
204
251
  raise Exception("No clipboard tool found")
205
252
  print(f"{GREEN}✅ Phase {name} prompt copied to clipboard!{NC}")
206
253
  print(" Paste it into your LLM to start the phase.")
207
254
  except Exception:
208
255
  print(content)
209
- print(f"\n{YELLOW}⚠️ Could not copy automatically. Please copy the text above.{NC}")
256
+ print(
257
+ f"\n{YELLOW}⚠️ Could not copy automatically. Please copy the text above.{NC}"
258
+ )
210
259
  else:
211
260
  print(f"{CYAN}{BOLD}Phase {name}{NC}")
212
261
  print(content)
213
- print(f"\n{YELLOW}Tip: Run '{CYAN}mdan phase {num} copy{YELLOW}' to copy this content to clipboard.{NC}")
262
+ print(
263
+ f"\n{YELLOW}Tip: Run '{CYAN}mdan phase {num} copy{YELLOW}' to copy this content to clipboard.{NC}"
264
+ )
214
265
  else:
215
266
  print(f"Phase file not found: {file}")
216
267
 
268
+
217
269
  def cmd_agent(name):
218
270
  file = Path(f"{MDAN_DIR}/agents/{name}.md")
219
271
  if file.exists():
@@ -221,6 +273,7 @@ def cmd_agent(name):
221
273
  else:
222
274
  print("Agents: product, architect, ux, dev, test, security, devops, doc")
223
275
 
276
+
224
277
  def cmd_skills():
225
278
  print(f"{CYAN}Skills:{NC}")
226
279
  skills_dir = Path(f"{MDAN_DIR}/skills")
@@ -230,10 +283,11 @@ def cmd_skills():
230
283
  else:
231
284
  print(" No skills installed")
232
285
 
286
+
233
287
  def main():
234
288
  args = sys.argv[1:]
235
289
  cmd = args[0] if args else "help"
236
-
290
+
237
291
  if cmd in ["help", "--help", "-h", None]:
238
292
  show_help()
239
293
  elif cmd == "init":
@@ -245,7 +299,9 @@ def main():
245
299
  elif cmd == "status":
246
300
  cmd_status()
247
301
  elif cmd == "phase":
248
- cmd_phase(args[1] if len(args) > 1 else None, args[2] if len(args) > 2 else None)
302
+ cmd_phase(
303
+ args[1] if len(args) > 1 else None, args[2] if len(args) > 2 else None
304
+ )
249
305
  elif cmd == "agent":
250
306
  cmd_agent(args[1] if len(args) > 1 else None)
251
307
  elif cmd == "skills":
@@ -255,5 +311,6 @@ def main():
255
311
  else:
256
312
  print(f"Unknown: {cmd}. Run: mdan help")
257
313
 
314
+
258
315
  if __name__ == "__main__":
259
316
  main()