diff --git a/src/SquidStd.Plugin/Extensions/SquidStdBootstrapPluginExtensions.cs b/src/SquidStd.Plugin/Extensions/SquidStdBootstrapPluginExtensions.cs
index 9de5732..e9fb85f 100644
--- a/src/SquidStd.Plugin/Extensions/SquidStdBootstrapPluginExtensions.cs
+++ b/src/SquidStd.Plugin/Extensions/SquidStdBootstrapPluginExtensions.cs
@@ -18,7 +18,8 @@ public static class SquidStdBootstrapPluginExtensions
/// across the whole set, and invokes for each plugin
/// in order against the bootstrap container. Must be called before the bootstrap starts, so
/// plugins can register configuration sections before the configuration is loaded. Relative
- /// directories are resolved against the bootstrap root directory. Any failure aborts startup:
+ /// directories are resolved against the bootstrap root directory and created when missing
+ /// (an empty directory yields no plugins). Any failure aborts startup:
/// loader problems raise and plugin exceptions
/// propagate unchanged. Plugin assemblies load into the default AssemblyLoadContext and are
/// fully trusted: there is no unloading and no version isolation. Note that plugin load
@@ -56,6 +57,10 @@ Action configure
? directory
: Path.Combine(bootstrap.Options.RootDirectory, directory);
+ // Managed like the bootstrap's DirectoriesConfig entries: a missing plugin
+ // directory is created (and simply yields no plugins), not treated as an error.
+ Directory.CreateDirectory(resolved);
+
plugins.AddRange(PluginAssemblyScanner.Scan(resolved));
}
diff --git a/src/SquidStd.Plugin/PluginCollectionBuilder.cs b/src/SquidStd.Plugin/PluginCollectionBuilder.cs
index 324601c..79d22ea 100644
--- a/src/SquidStd.Plugin/PluginCollectionBuilder.cs
+++ b/src/SquidStd.Plugin/PluginCollectionBuilder.cs
@@ -44,7 +44,8 @@ public PluginCollectionBuilder Add(ISquidStdPlugin plugin)
///
/// Adds a directory to scan for external plugin assemblies (*.dll, non-recursive).
- /// Relative paths are resolved against the bootstrap root directory.
+ /// Relative paths are resolved against the bootstrap root directory; a missing
+ /// directory is created and yields no plugins.
///
/// Absolute or root-relative directory path.
/// The same builder for chaining.
diff --git a/src/SquidStd.Plugin/README.md b/src/SquidStd.Plugin/README.md
index ec54bac..53e812d 100644
--- a/src/SquidStd.Plugin/README.md
+++ b/src/SquidStd.Plugin/README.md
@@ -40,6 +40,8 @@ await bootstrap.RunAsync();
can depend on an internal one and vice versa.
- `Configure` runs before the configuration load, so plugins can register their own configuration
sections and services ahead of time.
+- Plugin directories are managed like the other bootstrap directories: a missing directory is
+ created on the spot and simply yields no plugins.
- Each plugin receives a `PluginContext` populated with the standard keys: `PluginContextKeys.RootDirectory`
(the bootstrap root directory) and `PluginContextKeys.AppName` (the bootstrap app name).
diff --git a/tests/SquidStd.Tests/Plugin/SquidStdBootstrapPluginExtensionsTests.cs b/tests/SquidStd.Tests/Plugin/SquidStdBootstrapPluginExtensionsTests.cs
index f9e159f..55e38a8 100644
--- a/tests/SquidStd.Tests/Plugin/SquidStdBootstrapPluginExtensionsTests.cs
+++ b/tests/SquidStd.Tests/Plugin/SquidStdBootstrapPluginExtensionsTests.cs
@@ -93,6 +93,20 @@ public async Task UsePlugins_LoadsExternalPluginsFromRelativeDirectory()
Assert.Equal("tests.external", bootstrap.Container.Resolve(serviceKey: "plugin-marker"));
}
+ [Fact]
+ public async Task UsePlugins_MissingDirectory_IsCreatedAndYieldsNoPlugins()
+ {
+ using var root = new TempDirectory();
+
+ await using var bootstrap = SquidStdBootstrap.Create(
+ new SquidStdOptions { ConfigName = "plugintest", RootDirectory = root.Path }
+ );
+
+ bootstrap.UsePlugins(plugins => plugins.FromDirectory("plugins"));
+
+ Assert.True(Directory.Exists(root.Combine("plugins")));
+ }
+
[Fact]
public async Task UsePlugins_AfterStart_Throws()
{