diff --git a/common/api/api_plugin_manager.cpp b/common/api/api_plugin_manager.cpp
index aaa4e76f5a..77f10d3f70 100644
--- a/common/api/api_plugin_manager.cpp
+++ b/common/api/api_plugin_manager.cpp
@@ -159,9 +159,42 @@ void API_PLUGIN_MANAGER::ReloadPlugins()
 }
 
 
+void API_PLUGIN_MANAGER::RecreatePluginEnvironment( const wxString& aIdentifier )
+{
+    if( !m_pluginsCache.contains( aIdentifier ) )
+        return;
+
+    const API_PLUGIN* plugin = m_pluginsCache.at( aIdentifier );
+    wxCHECK( plugin, /* void */ );
+
+    std::optional<wxString> env = PYTHON_MANAGER::GetPythonEnvironment( plugin->Identifier() );
+    wxCHECK( env.has_value(), /* void */ );
+
+    wxFileName envConfigPath( *env, wxS( "pyvenv.cfg" ) );
+    envConfigPath.MakeAbsolute();
+
+    if( envConfigPath.DirExists() && envConfigPath.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
+    {
+        wxLogTrace( traceApi,
+                    wxString::Format( "Manager: Removed existing Python environment at %s for %s",
+                                      envConfigPath.GetPath(), plugin->Identifier() ) );
+
+        JOB job;
+        job.type = JOB_TYPE::CREATE_ENV;
+        job.identifier = plugin->Identifier();
+        job.plugin_path = plugin->BasePath();
+        job.env_path = envConfigPath.GetPath();
+        m_jobs.emplace_back( job );
+
+        wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxID_ANY );
+        QueueEvent( evt );
+    }
+}
+
+
 std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxString& aIdentifier )
 {
-    if( !m_actionsCache.count( aIdentifier ) )
+    if( !m_actionsCache.contains( aIdentifier ) )
         return std::nullopt;
 
     return m_actionsCache.at( aIdentifier );
@@ -170,7 +203,7 @@ std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxStrin
 
 void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier )
 {
-    if( !m_actionsCache.count( aIdentifier ) )
+    if( !m_actionsCache.contains( aIdentifier ) )
         return;
 
     const PLUGIN_ACTION* action = m_actionsCache.at( aIdentifier );
diff --git a/include/api/api_plugin_manager.h b/include/api/api_plugin_manager.h
index 0ab9996702..1b34895a30 100644
--- a/include/api/api_plugin_manager.h
+++ b/include/api/api_plugin_manager.h
@@ -44,6 +44,8 @@ public:
 
     void ReloadPlugins();
 
+    void RecreatePluginEnvironment( const wxString& aIdentifier );
+
     void InvokeAction( const wxString& aIdentifier );
 
     std::optional<const PLUGIN_ACTION*> GetAction( const wxString& aIdentifier );
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
index 5e4e7bd0e5..60e541b506 100644
--- a/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.cpp
@@ -40,11 +40,70 @@
 
 #define GRID_CELL_MARGIN 4
 
+enum
+{
+    MYID_RECREATE_ENV = GRIDTRICKS_FIRST_CLIENT_ID
+};
+
+class PLUGINS_GRID_TRICKS : public GRID_TRICKS
+{
+public:
+    PLUGINS_GRID_TRICKS( WX_GRID* aGrid ) :
+        GRID_TRICKS( aGrid )
+    {}
+
+protected:
+    void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
+    void doPopupSelection( wxCommandEvent& event ) override;
+};
+
+
+void PLUGINS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
+{
+#ifdef KICAD_IPC_API
+    API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
+    wxString id = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
+                                        PANEL_PCBNEW_ACTION_PLUGINS::COLUMN_SETTINGS_IDENTIFIER );
+
+    if( std::optional<const PLUGIN_ACTION*> action = mgr.GetAction( id ) )
+    {
+        menu.Append( MYID_RECREATE_ENV, _( "Recreate Plugin Environment" ),
+                     _( "Recreate Plugin Environment" ) );
+        menu.AppendSeparator();
+    }
+#endif
+
+    GRID_TRICKS::showPopupMenu( menu, aEvent );
+}
+
+
+void PLUGINS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
+{
+    if( event.GetId() == MYID_RECREATE_ENV )
+    {
+#ifdef KICAD_IPC_API
+        API_PLUGIN_MANAGER& mgr = Pgm().GetPluginManager();
+        wxString id = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
+                                            PANEL_PCBNEW_ACTION_PLUGINS::COLUMN_SETTINGS_IDENTIFIER );
+
+        if( std::optional<const PLUGIN_ACTION*> action = mgr.GetAction( id ) )
+        {
+            mgr.RecreatePluginEnvironment( ( *action )->plugin.Identifier() );
+        }
+#endif
+    }
+    else
+    {
+        GRID_TRICKS::doPopupSelection( event );
+    }
+}
+
+
 PANEL_PCBNEW_ACTION_PLUGINS::PANEL_PCBNEW_ACTION_PLUGINS( wxWindow* aParent ) :
         PANEL_PCBNEW_ACTION_PLUGINS_BASE( aParent )
 {
     m_genericIcon = KiBitmapBundle( BITMAPS::puzzle_piece );
-    m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
+    m_grid->PushEventHandler( new PLUGINS_GRID_TRICKS( m_grid ) );
     m_grid->SetUseNativeColLabels();
 
     m_moveUpButton->SetBitmap( KiBitmapBundle( BITMAPS::small_up ) );
diff --git a/pcbnew/dialogs/panel_pcbnew_action_plugins.h b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
index 2036f27f88..fbb78b2dfd 100644
--- a/pcbnew/dialogs/panel_pcbnew_action_plugins.h
+++ b/pcbnew/dialogs/panel_pcbnew_action_plugins.h
@@ -20,8 +20,13 @@
 
 #include "panel_pcbnew_action_plugins_base.h"
 
+class PLUGINS_GRID_TRICKS;
+
+
 class PANEL_PCBNEW_ACTION_PLUGINS : public PANEL_PCBNEW_ACTION_PLUGINS_BASE
 {
+    friend class PLUGINS_GRID_TRICKS;
+
 public:
     PANEL_PCBNEW_ACTION_PLUGINS ( wxWindow* aParent );