diff --git a/scripts/lib/managed-extensions.py b/scripts/lib/managed-extensions.py index ba3a338..df2f3fa 100644 --- a/scripts/lib/managed-extensions.py +++ b/scripts/lib/managed-extensions.py @@ -344,10 +344,11 @@ class Manager: if not re.fullmatch(r'[a-z0-9@/_.-]+', package): raise ValueError('Invalid CLI package.') record = next((r for r in self.state['resources'] if r['client'] == client and r['name'] == entry['name']), None) - if record is not None: + installed = shutil.which(package) is not None + if record is not None and installed and not self.update: return - self.command(['npm', 'install', '--global', package]) - if not self.dry_run: + self.command(['npm', 'update' if record is not None and installed else 'install', '--global', package]) + if not self.dry_run and record is None: self.state['resources'].append(dict(client=client, name=entry['name'], kind='cli', package=package)) self.save() diff --git a/scripts/tests/test-managed-extensions.py b/scripts/tests/test-managed-extensions.py index e34b90b..2194a8d 100644 --- a/scripts/tests/test-managed-extensions.py +++ b/scripts/tests/test-managed-extensions.py @@ -272,6 +272,20 @@ class ManagedExtensionTests(unittest.TestCase): self.assertIn(['claude', 'plugin', 'install', 'qmd@qmd', '--scope', 'user'], self.commands) self.assertNotIn(['codex', 'mcp', 'add', 'qmd', '--', 'qmd', 'mcp'], self.commands) + def test_managed_mcporter_updates_when_requested(self): + self.manager().sync([mcporter()], ['codex'], {'MCPorter'}) + self.commands.clear() + with patch.object(extensions.shutil, 'which', return_value='mcporter'): + self.manager(update=True).sync([mcporter()], ['codex'], {'MCPorter'}) + self.assertEqual([['npm', 'update', '--global', 'mcporter']], self.commands) + + def test_managed_mcporter_reinstalls_when_command_is_missing(self): + self.manager().sync([mcporter()], ['codex'], {'MCPorter'}) + self.commands.clear() + with patch.object(extensions.shutil, 'which', return_value=None): + self.manager().sync([mcporter()], ['codex'], {'MCPorter'}) + self.assertEqual([['npm', 'install', '--global', 'mcporter']], self.commands) + def test_mcporter_replaces_direct_context7_plugin(self): self.manager().sync([mcp(), mcporter()], ['codex'], {'Context7', 'MCPorter'}) config = str(self.home / '.mcporter/mcporter.json')