multimodel-dev-os 2.8.0 → 2.8.1

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/README.md CHANGED
@@ -155,7 +155,7 @@ npx multimodel-dev-os@latest handoff build
155
155
  | **v2.5.0** | Repository Intelligence Command Center | ✅ Released |
156
156
  | **v2.6.0** | Real-Repo Onboarding & Adapter Sync | ✅ Released |
157
157
  | **v2.7.0** | Website, Demo & Distribution System | ✅ Released |
158
- | **v2.8.0** | Interactive TUI Dashboard & Plugin Hooks | ✅ Released |
158
+ | **v2.8.0 / v2.8.1** | Interactive TUI Dashboard & Plugin Hooks | ✅ Released |
159
159
  | **v3.0.0** | Unified Autonomous Co-Pilot Ecosystem | 🔮 Future |
160
160
 
161
161
  **[Full Roadmap →](https://rizvee.github.io/multimodel-dev-os/v2-roadmap)**
@@ -52,7 +52,8 @@ function parseArgs(args) {
52
52
  title: null,
53
53
  approved: false,
54
54
  intelligence: false,
55
- onboarding: false
55
+ onboarding: false,
56
+ listActions: false
56
57
  };
57
58
 
58
59
  for (let i = 0; i < args.length; i++) {
@@ -67,6 +68,8 @@ function parseArgs(args) {
67
68
  params.caveman = true;
68
69
  } else if (arg === '--dry-run' || arg === '-d') {
69
70
  params.dryRun = true;
71
+ } else if (arg === '--list-actions') {
72
+ params.listActions = true;
70
73
  } else if (arg === '--force' || arg === '-f') {
71
74
  params.force = true;
72
75
  } else if (arg === '--help' || arg === '-h') {
@@ -4407,28 +4410,33 @@ function handleDashboard(options) {
4407
4410
  ]
4408
4411
  };
4409
4412
 
4410
- if (!process.stdout.isTTY || !process.stdin.isTTY || options.dryRun) {
4411
- console.log(`\n🧠 \x1b[36mMultiModel Dev OS Command Center (Headless mode)\x1b[0m`);
4413
+ if (!process.stdout.isTTY || !process.stdin.isTTY || options.dryRun || options.listActions) {
4414
+ console.log(`\n📊 \x1b[36mMultiModel Dev OS Command Center (Headless/CI Preview)\x1b[0m`);
4415
+ console.log(`Target Workspace: \x1b[32m${options.target}\x1b[0m`);
4412
4416
  console.log('==================================================');
4417
+
4418
+ const targetFlag = options.target === process.cwd() ? '' : ` --target "${options.target}"`;
4419
+
4413
4420
  mainMenu.forEach(item => {
4414
4421
  if (item.action === 'command') {
4415
- console.log(` - ${item.name}: equivalent command: "npx multimodel-dev-os ${item.command}"`);
4422
+ console.log(` \x1b[33m•\x1b[0m ${item.name.padEnd(30)} \x1b[36mnpx multimodel-dev-os ${item.command}${targetFlag}\x1b[0m`);
4416
4423
  } else if (item.action === 'submenu') {
4417
- console.log(` - ${item.name}`);
4424
+ console.log(`\n \x1b[35m[${item.name.replace('...', '')}]\x1b[0m`);
4418
4425
  submenus[item.menu].forEach(sub => {
4419
4426
  if (sub.action === 'command') {
4420
- console.log(` └─ ${sub.name}: equivalent command: "npx multimodel-dev-os ${sub.command}"`);
4427
+ console.log(` └─ ${sub.name.padEnd(35)} \x1b[36mnpx multimodel-dev-os ${sub.command}${targetFlag}\x1b[0m`);
4421
4428
  }
4422
4429
  });
4423
4430
  }
4424
4431
  });
4425
- console.log('');
4432
+ console.log('\n\x1b[90m(Run with -t or --target to specify another workspace directory)\x1b[0m\n');
4426
4433
  return;
4427
4434
  }
4428
4435
 
4429
4436
  const runCommandWrapper = (cmdStr) => {
4430
4437
  console.clear();
4431
- console.log(`\n\x1b[36mRunning Command:\x1b[0m npx multimodel-dev-os ${cmdStr}`);
4438
+ const targetFlag = options.target === process.cwd() ? '' : ` --target "${options.target}"`;
4439
+ console.log(`\n\x1b[36mRunning Command:\x1b[0m npx multimodel-dev-os ${cmdStr}${targetFlag}`);
4432
4440
  console.log('--------------------------------------------------\n');
4433
4441
  try {
4434
4442
  const cliPath = join(sourceRoot, 'bin', 'multimodel-dev-os.js');
@@ -4473,6 +4481,9 @@ function getPluginsDir(targetDir) {
4473
4481
 
4474
4482
  function handlePluginList(options) {
4475
4483
  const pluginsDir = getPluginsDir(options.target);
4484
+ const rawRelPath = relative(process.cwd(), join(sourceRoot, '.ai', 'plugins', 'plugin.example.yaml')).replace(/\\/g, '/');
4485
+ const examplePath = rawRelPath.startsWith('.') ? rawRelPath : `./${rawRelPath}`;
4486
+
4476
4487
  if (!existsSync(pluginsDir)) {
4477
4488
  if (options.json) {
4478
4489
  console.log('[]');
@@ -4481,7 +4492,7 @@ function handlePluginList(options) {
4481
4492
  console.log(`\n🔌 \x1b[36mInstalled Plugins in: ${options.target}\x1b[0m`);
4482
4493
  console.log('==================================================');
4483
4494
  console.log(' No plugins installed. Try:');
4484
- console.log(' npx multimodel-dev-os plugin install .ai/plugins/plugin.example.yaml --approved');
4495
+ console.log(` npx multimodel-dev-os plugin install ${examplePath} --approved`);
4485
4496
  console.log('');
4486
4497
  return;
4487
4498
  }
@@ -4509,7 +4520,8 @@ function handlePluginList(options) {
4509
4520
  console.log(`\n🔌 \x1b[36mInstalled Plugins in: ${options.target} (${plugins.length})\x1b[0m`);
4510
4521
  console.log('==================================================');
4511
4522
  if (plugins.length === 0) {
4512
- console.log(' No plugins installed.');
4523
+ console.log(' No plugins installed. Try:');
4524
+ console.log(` npx multimodel-dev-os plugin install ${examplePath} --approved`);
4513
4525
  } else {
4514
4526
  plugins.forEach(p => {
4515
4527
  console.log(`\n\x1b[32m* ${p.name} (v${p.version || '1.0.0'})\x1b[0m [slug: \x1b[33m${p.slug}\x1b[0m]`);
@@ -4521,6 +4533,11 @@ function handlePluginList(options) {
4521
4533
  }
4522
4534
 
4523
4535
  function handlePluginShow(slug, options) {
4536
+ if (!/^[a-z0-9-_]+$/i.test(slug)) {
4537
+ console.error(`\x1b[31mError: Invalid plugin slug '${slug}'. Slugs must be alphanumeric with dashes or underscores only.\x1b[0m`);
4538
+ process.exit(1);
4539
+ }
4540
+
4524
4541
  const pluginsDir = getPluginsDir(options.target);
4525
4542
  let p = null;
4526
4543
  if (existsSync(pluginsDir)) {
@@ -4538,6 +4555,7 @@ function handlePluginShow(slug, options) {
4538
4555
 
4539
4556
  if (!p) {
4540
4557
  console.error(`\x1b[31mError: Plugin with slug '${slug}' is not installed.\x1b[0m`);
4558
+ console.error(` Run \x1b[36mplugin list\x1b[0m to see installed plugins, or validate a new plugin config using \x1b[36mplugin validate <path>\x1b[0m.`);
4541
4559
  process.exit(1);
4542
4560
  }
4543
4561
 
@@ -4586,13 +4604,14 @@ function handlePluginValidate(pluginPath, options) {
4586
4604
  }
4587
4605
 
4588
4606
  console.log(`\n📋 \x1b[34mValidating Plugin: ${pluginPath}\x1b[0m`);
4607
+ console.log('==================================================');
4589
4608
 
4590
4609
  let errors = 0;
4591
4610
  let plugin = null;
4592
4611
  try {
4593
4612
  plugin = parseYaml(readFileSync(fullPath, 'utf8'));
4594
4613
  } catch (e) {
4595
- console.error(` \x1b[31m✗ Failed to parse YAML: ${e.message}\x1b[0m`);
4614
+ console.error(` \x1b[31m✗ [SYNTAX] Failed to parse YAML: ${e.message}\x1b[0m`);
4596
4615
  errors++;
4597
4616
  }
4598
4617
 
@@ -4600,79 +4619,114 @@ function handlePluginValidate(pluginPath, options) {
4600
4619
  const reqKeys = ['name', 'slug', 'version', 'description', 'author'];
4601
4620
  reqKeys.forEach(k => {
4602
4621
  if (plugin[k] === undefined || plugin[k] === null) {
4603
- console.error(` \x1b[31m✗ Missing required key: ${k}\x1b[0m`);
4622
+ console.error(` \x1b[31m✗ [METADATA] Missing required key: ${k}\x1b[0m`);
4604
4623
  errors++;
4605
4624
  } else if (typeof plugin[k] !== 'string') {
4606
- console.error(` \x1b[31m✗ Key '${k}' must be a string (found: ${typeof plugin[k]})\x1b[0m`);
4625
+ console.error(` \x1b[31m✗ [METADATA] Key '${k}' must be a string (found: ${typeof plugin[k]})\x1b[0m`);
4607
4626
  errors++;
4627
+ } else if (k === 'slug') {
4628
+ if (!/^[a-z0-9-_]+$/i.test(plugin[k])) {
4629
+ console.error(` \x1b[31m✗ [METADATA] Key 'slug' must be alphanumeric with dashes or underscores only (found: "${plugin[k]}")\x1b[0m`);
4630
+ errors++;
4631
+ } else {
4632
+ console.log(` \x1b[32m✓ [METADATA] Key: slug ("${plugin[k]}")`);
4633
+ }
4608
4634
  } else {
4609
- console.log(` \x1b[32m✓\x1b[0m Key: ${k} ("${plugin[k]}")`);
4635
+ console.log(` \x1b[32m[METADATA] Key: ${k} ("${plugin[k]}")`);
4610
4636
  }
4611
4637
  });
4612
4638
 
4613
4639
  if (plugin.allowed_file_patterns !== undefined) {
4614
4640
  if (!Array.isArray(plugin.allowed_file_patterns)) {
4615
- console.error(` \x1b[31m✗ allowed_file_patterns must be an array\x1b[0m`);
4641
+ console.error(` \x1b[31m✗ [SAFETY] allowed_file_patterns must be an array\x1b[0m`);
4616
4642
  errors++;
4617
4643
  } else {
4618
4644
  plugin.allowed_file_patterns.forEach(pat => {
4619
4645
  if (typeof pat !== 'string') {
4620
- console.error(` \x1b[31m✗ allowed_file_patterns item must be a string: ${pat}\x1b[0m`);
4646
+ console.error(` \x1b[31m✗ [SAFETY] allowed_file_patterns item must be a string: ${pat}\x1b[0m`);
4647
+ errors++;
4648
+ return;
4649
+ }
4650
+ const normPattern = pat.replace(/\\/g, '/').trim();
4651
+ const isSafeSubdir = [
4652
+ '.ai/plugins/',
4653
+ '.ai/registries/',
4654
+ '.ai/templates/',
4655
+ '.ai/skills/',
4656
+ '.ai/checks/',
4657
+ '.ai/prompts/',
4658
+ '.ai/adapters/'
4659
+ ].some(prefix => normPattern.startsWith(prefix));
4660
+
4661
+ const hasTraversal = normPattern.includes('..') || normPattern.startsWith('/');
4662
+ const isBlacklisted = [
4663
+ '.env',
4664
+ '.npmrc',
4665
+ '.git/',
4666
+ 'node_modules/',
4667
+ 'package.json',
4668
+ 'package-lock.json'
4669
+ ].some(black => normPattern.includes(black));
4670
+
4671
+ if (!isSafeSubdir || hasTraversal || isBlacklisted) {
4672
+ console.error(` \x1b[31m✗ [SAFETY] File pattern '${pat}' violates safety boundaries (must reside under .ai/ or adapters/, contain no '..', and exclude blacklisted files)\x1b[0m`);
4621
4673
  errors++;
4622
4674
  }
4623
4675
  });
4624
- console.log(` \x1b[32m✓\x1b[0m allowed_file_patterns checked: ${plugin.allowed_file_patterns.length} items`);
4676
+ if (errors === 0) {
4677
+ console.log(` \x1b[32m✓ [SAFETY] allowed_file_patterns verified: ${plugin.allowed_file_patterns.length} items`);
4678
+ }
4625
4679
  }
4626
4680
  }
4627
4681
 
4628
4682
  if (plugin.denied_file_patterns !== undefined) {
4629
4683
  if (!Array.isArray(plugin.denied_file_patterns)) {
4630
- console.error(` \x1b[31m✗ denied_file_patterns must be an array\x1b[0m`);
4684
+ console.error(` \x1b[31m✗ [SAFETY] denied_file_patterns must be an array\x1b[0m`);
4631
4685
  errors++;
4632
4686
  } else {
4633
4687
  plugin.denied_file_patterns.forEach(pat => {
4634
4688
  if (typeof pat !== 'string') {
4635
- console.error(` \x1b[31m✗ denied_file_patterns item must be a string: ${pat}\x1b[0m`);
4689
+ console.error(` \x1b[31m✗ [SAFETY] denied_file_patterns item must be a string: ${pat}\x1b[0m`);
4636
4690
  errors++;
4637
4691
  }
4638
4692
  });
4639
- console.log(` \x1b[32m✓\x1b[0m denied_file_patterns checked: ${plugin.denied_file_patterns.length} items`);
4693
+ console.log(` \x1b[32m[SAFETY] denied_file_patterns verified: ${plugin.denied_file_patterns.length} items`);
4640
4694
  }
4641
4695
  }
4642
4696
 
4643
4697
  if (plugin.workflows !== undefined) {
4644
4698
  if (typeof plugin.workflows !== 'object' || Array.isArray(plugin.workflows)) {
4645
- console.error(` \x1b[31m✗ workflows must be an object\x1b[0m`);
4699
+ console.error(` \x1b[31m✗ [CAPABILITIES] workflows must be an object\x1b[0m`);
4646
4700
  errors++;
4647
4701
  } else {
4648
- console.log(` \x1b[32m✓\x1b[0m workflows object checked`);
4702
+ console.log(` \x1b[32m[CAPABILITIES] workflows verified`);
4649
4703
  }
4650
4704
  }
4651
4705
 
4652
4706
  if (plugin.templates !== undefined) {
4653
4707
  if (typeof plugin.templates !== 'object' || Array.isArray(plugin.templates)) {
4654
- console.error(` \x1b[31m✗ templates must be an object\x1b[0m`);
4708
+ console.error(` \x1b[31m✗ [CAPABILITIES] templates must be an object\x1b[0m`);
4655
4709
  errors++;
4656
4710
  } else {
4657
- console.log(` \x1b[32m✓\x1b[0m templates object checked`);
4711
+ console.log(` \x1b[32m[CAPABILITIES] templates verified`);
4658
4712
  }
4659
4713
  }
4660
4714
 
4661
4715
  if (plugin.adapters !== undefined) {
4662
4716
  if (typeof plugin.adapters !== 'object' || Array.isArray(plugin.adapters)) {
4663
- console.error(` \x1b[31m✗ adapters must be an object\x1b[0m`);
4717
+ console.error(` \x1b[31m✗ [CAPABILITIES] adapters must be an object\x1b[0m`);
4664
4718
  errors++;
4665
4719
  } else {
4666
- console.log(` \x1b[32m✓\x1b[0m adapters object checked`);
4720
+ console.log(` \x1b[32m[CAPABILITIES] adapters verified`);
4667
4721
  }
4668
4722
  }
4669
4723
 
4670
4724
  if (plugin.safety_notes !== undefined) {
4671
4725
  if (typeof plugin.safety_notes !== 'string') {
4672
- console.error(` \x1b[31m✗ safety_notes must be a string\x1b[0m`);
4726
+ console.error(` \x1b[31m✗ [SAFETY] safety_notes must be a string\x1b[0m`);
4673
4727
  errors++;
4674
4728
  } else {
4675
- console.log(` \x1b[32m✓\x1b[0m safety_notes checked`);
4729
+ console.log(` \x1b[32m[SAFETY] safety_notes verified`);
4676
4730
  }
4677
4731
  }
4678
4732
  }
@@ -4682,7 +4736,9 @@ function handlePluginValidate(pluginPath, options) {
4682
4736
  if (options && options.noExit) return false;
4683
4737
  process.exit(1);
4684
4738
  } else {
4685
- console.log(`\n\x1b[32m✔ Plugin '${plugin.slug || plugin.name}' is fully valid and compliant!\x1b[0m\n`);
4739
+ console.log(`\n\x1b[32m✔ Plugin '${plugin.slug || plugin.name}' is fully valid and compliant!\x1b[0m`);
4740
+ console.log(`\n\x1b[35mRecommended Next Command:\x1b[0m`);
4741
+ console.log(` npx multimodel-dev-os plugin install ${pluginPath} --approved\n`);
4686
4742
  if (options && options.noExit) return true;
4687
4743
  return true;
4688
4744
  }
@@ -4772,14 +4828,15 @@ function handlePluginInstall(pluginPath, options) {
4772
4828
  }
4773
4829
 
4774
4830
  if (!options.approved) {
4831
+ console.error(`\x1b[31mError: Plugin cannot be installed without explicit user approval. Pass the --approved flag.\x1b[0m`);
4775
4832
  console.log(`\n\x1b[33mPlanned Installation Actions:\x1b[0m`);
4776
4833
  filesToCopy.forEach(item => {
4777
4834
  const exists = existsSync(join(options.target, item.dest));
4778
4835
  const suffix = exists ? ' \x1b[33m(will overwrite)\x1b[0m' : '';
4779
4836
  console.log(` - \x1b[36m[WOULD COPY]\x1b[0m ${item.src} -> ${item.dest}${suffix}`);
4780
4837
  });
4781
- console.log(`\nRun with \x1b[32m--approved\x1b[0m to apply these changes.\n`);
4782
- return;
4838
+ console.error(`\n\x1b[31mError: Installation refused. Run with --approved to apply these changes.\x1b[0m\n`);
4839
+ process.exit(1);
4783
4840
  }
4784
4841
 
4785
4842
  filesToCopy.forEach(item => {
@@ -4799,7 +4856,22 @@ function handlePluginInstall(pluginPath, options) {
4799
4856
  console.log(` \x1b[32mCOPY:\x1b[0m ${item.dest}`);
4800
4857
  });
4801
4858
 
4802
- console.log(`\n\x1b[32m✔ Plugin '${plugin.name}' installed successfully!\x1b[0m\n`);
4859
+ console.log(`\n\x1b[32m✔ Plugin '${plugin.name}' installed successfully!\x1b[0m`);
4860
+ console.log(`\nSummary of actions:`);
4861
+ console.log(` - Manifest registered: .ai/plugins/${slug}.yaml`);
4862
+ const assetCount = filesToCopy.length - 1;
4863
+ console.log(` - Synced assets: ${assetCount} file(s)`);
4864
+
4865
+ console.log(`\n\x1b[35mRecommended Next Commands:\x1b[0m`);
4866
+ console.log(` • View plugin details: npx multimodel-dev-os plugin show ${slug}`);
4867
+ console.log(` • Audit plugin health: npx multimodel-dev-os plugin status --target .`);
4868
+ if (plugin.workflows) {
4869
+ const wfKeys = Object.keys(plugin.workflows);
4870
+ if (wfKeys.length > 0) {
4871
+ console.log(` • Run custom workflow: npx multimodel-dev-os workflow run ${wfKeys[0]}`);
4872
+ }
4873
+ }
4874
+ console.log('');
4803
4875
  }
4804
4876
 
4805
4877
  function handlePluginStatus(options) {
@@ -4849,6 +4921,15 @@ function handlePluginStatus(options) {
4849
4921
  console.log(` Status: \x1b[32mHealthy\x1b[0m (All ${presentCount}/${total} assets present)`);
4850
4922
  } else {
4851
4923
  console.log(` Status: \x1b[33mIncomplete\x1b[0m (${presentCount}/${total} assets present, ${missingCount} missing)`);
4924
+ console.log(` Missing Assets:`);
4925
+ p.allowed_file_patterns.forEach(pat => {
4926
+ const destPath = join(options.target, pat);
4927
+ if (!existsSync(destPath) || !statSync(destPath).isFile()) {
4928
+ console.log(` \x1b[31m✗\x1b[0m ${pat}`);
4929
+ }
4930
+ });
4931
+ console.log(` To fix: Reinstall the plugin or validate the configuration:`);
4932
+ console.log(` npx multimodel-dev-os plugin validate <path-to-plugin-source.yaml>`);
4852
4933
  }
4853
4934
  }
4854
4935
  } catch (e) {
@@ -32,7 +32,7 @@ export default {
32
32
  'license': 'https://opensource.org/licenses/MIT',
33
33
  'url': 'https://github.com/rizvee/multimodel-dev-os',
34
34
  'downloadUrl': 'https://www.npmjs.com/package/multimodel-dev-os',
35
- 'softwareVersion': '2.8.0',
35
+ 'softwareVersion': '2.8.1',
36
36
  'description': 'Portable, vendor-neutral AI Developer OS for multi-agent coding workflows.'
37
37
  })
38
38
  ]
package/docs/dashboard.md CHANGED
@@ -81,18 +81,20 @@ In automated environments (CI/CD pipelines, GitHub Actions) or when piped, the t
81
81
 
82
82
  To prevent execution from hanging indefinitely, the TUI automatically detects non-TTY environments:
83
83
  * **Interactive Mode**: Triggered when both `process.stdout.isTTY` and `process.stdin.isTTY` are true.
84
- * **Headless Fallback**: Triggered when run in non-interactive shells, or if `--dry-run` is passed. The dashboard immediately prints a structured list of all menu options along with their corresponding CLI execution strings and exits cleanly.
84
+ * **Headless Fallback**: Triggered when run in non-interactive shells, or if `--dry-run` or `--list-actions` is passed. The dashboard immediately prints a structured, grouped list of all menu options along with their corresponding CLI execution strings (including active target flags) and exits cleanly.
85
85
 
86
86
  Example headless output:
87
87
 
88
88
  ```txt
89
- 🧠 MultiModel Dev OS Command Center (Headless mode)
89
+ 📊 MultiModel Dev OS Command Center (Headless/CI Preview)
90
+ Target Workspace: /workspace
90
91
  ==================================================
91
- - Active Workspace Status: equivalent command: "npx multimodel-dev-os status"
92
- - Codebase Scan Analysis: equivalent command: "npx multimodel-dev-os scan"
93
- - Onboarding Operations...
94
- └─ Onboard: Analyze Repository: equivalent command: "npx multimodel-dev-os onboard analyze"
95
- └─ Onboard: Recommendation Summary: equivalent command: "npx multimodel-dev-os onboard recommend"
92
+ Active Workspace Status npx multimodel-dev-os status
93
+ Codebase Scan Analysis npx multimodel-dev-os scan
94
+
95
+ [Onboarding Operations]
96
+ └─ Onboard: Analyze Repository → npx multimodel-dev-os onboard analyze
97
+ └─ Onboard: Recommendation Summary → npx multimodel-dev-os onboard recommend
96
98
  ...
97
99
  ```
98
100
 
package/docs/faq.md CHANGED
@@ -97,7 +97,7 @@ The improvement proposal system enforces 12 strict safety checks including path
97
97
  It is a terminal-based operational menu (`npx multimodel-dev-os dashboard` or `ui`) that allows developers to run diagnostics, sync rules, build memory indexes, and check proposal status in a guided interface. It uses Node.js's native `readline` module for raw-keypress navigation, keeping execution zero-dependency and fast.
98
98
 
99
99
  **Will the TUI Dashboard hang in CI/CD pipelines?**
100
- No. The dashboard detects when stdin or stdout is non-interactive. In non-TTY environments, it prints a dry-run list of all options with their equivalent CLI commands and exits immediately, preventing pipelines from stalling.
100
+ No. The dashboard detects when stdin or stdout is non-interactive. In non-TTY environments, or when `--dry-run` or `--list-actions` is passed, it prints a grouped preview of all options with their equivalent CLI commands and exits immediately, preventing pipelines from stalling.
101
101
 
102
102
  **How secure is the Declarative Plugin system?**
103
103
  Extremely secure. Plugins are strictly configuration-based (YAML manifest files). They cannot run arbitrary bash commands, execute node scripts, download npm packages, or make network calls. File copies are restricted to whitelisted `.ai/` and `adapters/` directories, and blacklists protect files like `.env`, `.git/`, `package.json`, and source code folders. Overwriting existing files requires `--force` and automatically generates backups.
@@ -11,7 +11,7 @@ A plugin is defined by a single YAML or JSON file. The manifest schema structure
11
11
  ### Required Fields
12
12
 
13
13
  * **`name`** (string): The human-readable name of the plugin (e.g. `Git Integration Plugin`).
14
- * **`slug`** (string): A unique, URL-friendly slug identifier (e.g. `git-integration`). This slug determines the filename under `.ai/plugins/<slug>.yaml` after installation.
14
+ * **`slug`** (string): A unique, URL-friendly slug identifier. Must consist strictly of lowercase alphanumeric characters, dashes, or underscores (`/^[a-z0-9-_]+$/i`, e.g. `git-integration`). This slug determines the filename under `.ai/plugins/<slug>.yaml` after installation.
15
15
  * **`version`** (string): Semantic version string (e.g. `1.0.0`).
16
16
  * **`description`** (string): A brief summary of the capabilities introduced by the plugin.
17
17
  * **`author`** (string): The creator's name or team.
@@ -78,3 +78,12 @@ npx multimodel-dev-os@latest plugin status [--target <path>]
78
78
  When a plugin installation causes file conflicts:
79
79
  * By default, the installer aborts and prints a list of conflicting files.
80
80
  * Running with `--force` overwrites the destination files but automatically copies the existing file to `<filename>.bak` in the same directory.
81
+
82
+ ---
83
+
84
+ ## Validation & Safe Execution Gates
85
+
86
+ To prevent path traversal and enforce robust script auditing:
87
+ * **Alphanumeric Slug Constraints**: Slugs are validated against `/^[a-z0-9-_]+$/i` to block directory escapes when writing to `.ai/plugins/<slug>.yaml`.
88
+ * **Path Boundary checks**: The `plugin validate` CLI command automatically parses `allowed_file_patterns` to assert they fit within whitelisted `.ai/` and `adapters/` folders, checking that no `..` traversal or blacklisted files are referenced.
89
+ * **Non-zero CI exit codes**: If `plugin install` is called without the `--approved` flag, it prints planned actions and exits with **exit code 1** to abort scripting pipelines safely.
@@ -1,4 +1,4 @@
1
- # MultiModel Dev OS — Comprehensive AI Assistant Discoverability Guide (v2.8.0 Stable Release)
1
+ # MultiModel Dev OS — Comprehensive AI Assistant Discoverability Guide (v2.8.1 Stable Release)
2
2
 
3
3
  MultiModel Dev OS is a repository-level porting specification designed to align context and instructions across diverse developer tools and AI models.
4
4
 
@@ -1,4 +1,4 @@
1
- # MultiModel Dev OS (v2.8.0 Stable Release)
1
+ # MultiModel Dev OS (v2.8.1 Stable Release)
2
2
 
3
3
  Portable, vendor-neutral workspace configuration standard for multi-agent AI pair-programming workflows.
4
4
 
@@ -36,7 +36,7 @@ The installer restricts all plugin file copies to specific directories inside th
36
36
  * `.ai/prompts/`
37
37
  * `.ai/adapters/`
38
38
 
39
- If a plugin manifest attempts to write to paths outside of these folders (such as `src/`, `lib/`, or `tests/`), the validator immediately throws an error and aborts the installation.
39
+ If a plugin manifest attempts to write to paths outside of these folders (such as `src/`, `lib/`, or `tests/`), the validator immediately throws an error and aborts the installation. To guarantee safety, the validation phase restricts plugin `slug` parameters to alphanumeric characters with dashes and underscores only (`/^[a-z0-9-_]+$/i`), hard-blocking directory traversal patterns (`..`, `/`, `\`) to prevent manifest write escapes.
40
40
 
41
41
  ### 3. Blacklist Enforcement
42
42
  The system hard-blocks write operations to critical files or directories to prevent credentials exposure or project hijack:
@@ -7,7 +7,7 @@ This document outlines the development path, completed milestones, and future pl
7
7
  ## 1. Current Status
8
8
 
9
9
  > [!IMPORTANT]
10
- > **v2.7.0 is the active stable release** on the public npm registry. All features below marked ✅ are shipped and production-ready.
10
+ > **v2.8.1 is the active stable release** on the public npm registry. All features below marked ✅ are shipped and production-ready.
11
11
 
12
12
  ---
13
13
 
@@ -59,6 +59,13 @@ This document outlines the development path, completed milestones, and future pl
59
59
  - Created docs-first examples for key developer workflows
60
60
  - Updated sitemaps, model registries, and search indices
61
61
 
62
+ ### v2.8.0 / v2.8.1 — Interactive TUI Dashboard & Plugin Hooks ✅
63
+ - **Interactive TUI Dashboard**: Added `dashboard`/`ui` command launching a zero-dependency keyboard-interactive command center built with Node's native `readline` module.
64
+ - **Declarative Plugin Hooks**: Added `plugin` command suite (`list`, `show`, `validate`, `install`, `status`) and JSON schema to securely extend workspace templates, workflows, and skills.
65
+ - **Secure Plugin Installer**: Supports `--approved` execution gate, path whitelisting to `.ai/` and `adapters/` directories, and automatic conflict `.bak` backups.
66
+ - **Headless Fallback & CI Polish**: Polish dry-run outputs and added `--list-actions` parameter to prevent TUI hangs in CI.
67
+ - **Path Traversal Hardening**: Enforce alphanumeric slug checks (`/^[a-z0-9-_]+$/i`) and pattern validation bounds to block traversal vectors.
68
+
62
69
  ---
63
70
 
64
71
  ## 3. Publishing Workflow
@@ -66,7 +73,7 @@ This document outlines the development path, completed milestones, and future pl
66
73
  All releases follow this strict publishing checklist:
67
74
 
68
75
  1. Bump version in `package.json`
69
- 2. Run `npm run verify` (214+ assertions must pass)
76
+ 2. Run `npm run verify` (220+ assertions must pass)
70
77
  3. Run `npm run docs:build` to verify documentation
71
78
  4. Run `npm publish --dry-run` to review package hygiene
72
79
  5. Set `MMDO_ALLOW_PUBLISH=true` and publish:
@@ -76,12 +83,10 @@ All releases follow this strict publishing checklist:
76
83
 
77
84
  ---
78
85
 
79
- ## 4. Upcoming: v2.8.0 — Interactive Dashboard & Plugin Hooks
86
+ ## 4. Upcoming: v2.9.0 — Auto-Detection & Custom Adaptors
80
87
 
81
- * **Interactive TUI Status Dashboard**: Rich terminal UI for `status` and `workflow` commands
82
- * **Plugin Hook System**: Pre/post hooks for `init`, `scan`, `memory`, and `workflow` commands
83
- * **Custom Workflow Authoring**: User-defined workflow definitions beyond bundled registries
84
- * **Adapter Auto-Detection**: Detect installed tools and automatically recommend adapter setup
88
+ * **Adapter Auto-Detection**: Detect installed tools and automatically recommend adapter setup.
89
+ * **Custom Adapter Hookups**: Programmatic hooks allowing plugins to register physical adapter configurations dynamically.
85
90
 
86
91
  ---
87
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multimodel-dev-os",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "bin": {
5
5
  "multimodel-dev-os": "bin/multimodel-dev-os.js"
6
6
  },
@@ -11,7 +11,7 @@ param(
11
11
  [switch]$Help
12
12
  )
13
13
 
14
- $Version = "2.8.0"
14
+ $Version = "2.8.1"
15
15
  $RepoUrl = "https://raw.githubusercontent.com/rizvee/multimodel-dev-os/main"
16
16
 
17
17
  if ($Help) {
@@ -7,7 +7,7 @@ set -euo pipefail
7
7
  # --all (install all adapters)
8
8
  # --dry-run (show what would be created without creating)
9
9
 
10
- VERSION="2.8.0"
10
+ VERSION="2.8.1"
11
11
  REPO_URL="https://raw.githubusercontent.com/rizvee/multimodel-dev-os/main"
12
12
  CAVEMAN=false
13
13
  DRY_RUN=false
package/scripts/verify.js CHANGED
@@ -456,11 +456,99 @@ try {
456
456
  console.error(` ${RED}✗${NC} CLI help is missing scan, memory, status, workflow, or handoff commands`);
457
457
  fail++;
458
458
  }
459
+
460
+ if (helpOutput.includes('dashboard') && helpOutput.includes('ui') && helpOutput.includes('plugin')) {
461
+ console.log(` ${GREEN}✓${NC} CLI help includes dashboard, ui, and plugin commands`);
462
+ pass++;
463
+ } else {
464
+ console.error(` ${RED}✗${NC} CLI help is missing dashboard, ui, or plugin commands`);
465
+ fail++;
466
+ }
459
467
  } catch (e) {
460
468
  console.error(` ${RED}✗${NC} node bin/multimodel-dev-os.js --help failed: ${e.message}`);
461
469
  fail++;
462
470
  }
463
471
 
472
+ // --- v2.8.0 / v2.8.1 Dashboard & Plugin Tests ---
473
+ console.log('\nRunning TUI Dashboard & Plugin Pre-Flight Tests...');
474
+
475
+ // 1. Dashboard dry-run check
476
+ try {
477
+ const output = execSync('node bin/multimodel-dev-os.js dashboard --dry-run', { cwd: projectRoot, encoding: 'utf8' });
478
+ if (output.includes('Headless/CI Preview') && output.includes('npx multimodel-dev-os')) {
479
+ console.log(` ${GREEN}✓${NC} dashboard --dry-run executes successfully and displays headless preview`);
480
+ pass++;
481
+ } else {
482
+ console.error(` ${RED}✗${NC} dashboard --dry-run output is missing preview strings`);
483
+ fail++;
484
+ }
485
+ } catch (e) {
486
+ console.error(` ${RED}✗${NC} dashboard --dry-run execution failed: ${e.message}`);
487
+ fail++;
488
+ }
489
+
490
+ // 2. Dashboard list-actions check
491
+ try {
492
+ const output = execSync('node bin/multimodel-dev-os.js dashboard --list-actions', { cwd: projectRoot, encoding: 'utf8' });
493
+ if (output.includes('Headless/CI Preview') && output.includes('npx multimodel-dev-os')) {
494
+ console.log(` ${GREEN}✓${NC} dashboard --list-actions executes successfully and displays headless preview`);
495
+ pass++;
496
+ } else {
497
+ console.error(` ${RED}✗${NC} dashboard --list-actions output is missing preview strings`);
498
+ fail++;
499
+ }
500
+ } catch (e) {
501
+ console.error(` ${RED}✗${NC} dashboard --list-actions execution failed: ${e.message}`);
502
+ fail++;
503
+ }
504
+
505
+ // 3. Plugin validation check
506
+ try {
507
+ const output = execSync('node bin/multimodel-dev-os.js plugin validate .ai/plugins/plugin.example.yaml', { cwd: projectRoot, encoding: 'utf8' });
508
+ if (output.includes('fully valid and compliant')) {
509
+ console.log(` ${GREEN}✓${NC} plugin validate on example manifest passes successfully`);
510
+ pass++;
511
+ } else {
512
+ console.error(` ${RED}✗${NC} plugin validate on example manifest failed to report compliance`);
513
+ fail++;
514
+ }
515
+ } catch (e) {
516
+ console.error(` ${RED}✗${NC} plugin validate execution failed: ${e.message}`);
517
+ fail++;
518
+ }
519
+
520
+ // 4. Plugin install refusal check (no --approved, should exit with code 1)
521
+ try {
522
+ execSync('node bin/multimodel-dev-os.js plugin install .ai/plugins/plugin.example.yaml', { cwd: projectRoot, stdio: 'pipe' });
523
+ console.error(` ${RED}✗${NC} plugin install without --approved should have exited with code 1, but exited with 0`);
524
+ fail++;
525
+ } catch (e) {
526
+ if (e.status === 1) {
527
+ const stdErrOut = e.stderr ? e.stderr.toString() : '';
528
+ const stdOutOut = e.stdout ? e.stdout.toString() : '';
529
+ if (stdErrOut.includes('Installation refused') || stdOutOut.includes('Installation refused')) {
530
+ console.log(` ${GREEN}✓${NC} plugin install without --approved correctly refuses and exits with code 1`);
531
+ pass++;
532
+ } else {
533
+ console.error(` ${RED}✗${NC} plugin install without --approved exited with 1 but missing refusal message`);
534
+ fail++;
535
+ }
536
+ } else {
537
+ console.error(` ${RED}✗${NC} plugin install without --approved failed with unexpected code ${e.status}: ${e.message}`);
538
+ fail++;
539
+ }
540
+ }
541
+
542
+ // 5. Plugin status check
543
+ try {
544
+ execSync('node bin/multimodel-dev-os.js plugin status', { cwd: projectRoot, stdio: 'ignore' });
545
+ console.log(` ${GREEN}✓${NC} plugin status executes without crashing`);
546
+ pass++;
547
+ } catch (e) {
548
+ console.error(` ${RED}✗${NC} plugin status execution failed: ${e.message}`);
549
+ fail++;
550
+ }
551
+
464
552
  // Verify docs mention memory build
465
553
  try {
466
554
  const mdContent = readFileSync(join(projectRoot, 'docs', 'hash-compressed-memory.md'), 'utf8');