diff --git a/BotSharp.sln b/BotSharp.sln index ad95f29e8..3b6426304 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -157,518 +157,786 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.A2A", "src\In EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MultiTenancy", "src\Plugins\BotSharp.Plugin.MultiTenancy\BotSharp.Plugin.MultiTenancy.csproj", "{562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.AgentSkills", "src\Plugins\BotSharp.Plugin.AgentSkills\BotSharp.Plugin.AgentSkills.csproj", "{511BC47F-8640-4E5A-820F-662956911CFD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|x64.ActiveCfg = Debug|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|x64.Build.0 = Debug|Any CPU + {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {197885F1-2EB2-4709-B9AA-A777878D74B3}.Debug|x86.Build.0 = Debug|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|Any CPU.Build.0 = Release|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|x64.ActiveCfg = Release|Any CPU {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|x64.Build.0 = Release|Any CPU + {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|x86.ActiveCfg = Release|Any CPU + {197885F1-2EB2-4709-B9AA-A777878D74B3}.Release|x86.Build.0 = Release|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|Any CPU.Build.0 = Debug|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|x64.ActiveCfg = Debug|x64 {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|x64.Build.0 = Debug|x64 + {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|x86.ActiveCfg = Debug|Any CPU + {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Debug|x86.Build.0 = Debug|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|Any CPU.ActiveCfg = Release|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|Any CPU.Build.0 = Release|Any CPU {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|x64.ActiveCfg = Release|x64 {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|x64.Build.0 = Release|x64 + {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|x86.ActiveCfg = Release|Any CPU + {36F5CEBD-31A8-4BEF-8BAA-BAC4E63E4815}.Release|x86.Build.0 = Release|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|Any CPU.Build.0 = Debug|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|x64.ActiveCfg = Debug|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|x64.Build.0 = Debug|Any CPU + {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|x86.ActiveCfg = Debug|Any CPU + {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Debug|x86.Build.0 = Debug|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|Any CPU.ActiveCfg = Release|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|Any CPU.Build.0 = Release|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|x64.ActiveCfg = Release|Any CPU {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|x64.Build.0 = Release|Any CPU + {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|x86.ActiveCfg = Release|Any CPU + {07AD18C5-CE7B-495A-815F-170E93CCC42A}.Release|x86.Build.0 = Release|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|x64.ActiveCfg = Debug|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|x64.Build.0 = Debug|Any CPU + {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|x86.ActiveCfg = Debug|Any CPU + {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Debug|x86.Build.0 = Debug|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|Any CPU.Build.0 = Release|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|x64.ActiveCfg = Release|Any CPU {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|x64.Build.0 = Release|Any CPU + {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|x86.ActiveCfg = Release|Any CPU + {3EAB9CF3-0F47-4BFB-8BAC-8ADFF24AD899}.Release|x86.Build.0 = Release|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|Any CPU.Build.0 = Debug|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|x64.ActiveCfg = Debug|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|x64.Build.0 = Debug|Any CPU + {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|x86.ActiveCfg = Debug|Any CPU + {57806BAF-7736-425A-B499-13A2A2DF1E63}.Debug|x86.Build.0 = Debug|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|Any CPU.ActiveCfg = Release|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|Any CPU.Build.0 = Release|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|x64.ActiveCfg = Release|Any CPU {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|x64.Build.0 = Release|Any CPU + {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|x86.ActiveCfg = Release|Any CPU + {57806BAF-7736-425A-B499-13A2A2DF1E63}.Release|x86.Build.0 = Release|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|Any CPU.Build.0 = Debug|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|x64.ActiveCfg = Debug|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|x64.Build.0 = Debug|Any CPU + {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|x86.ActiveCfg = Debug|Any CPU + {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Debug|x86.Build.0 = Debug|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|Any CPU.ActiveCfg = Release|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|Any CPU.Build.0 = Release|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|x64.ActiveCfg = Release|Any CPU {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|x64.Build.0 = Release|Any CPU + {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|x86.ActiveCfg = Release|Any CPU + {68C7C9E9-496B-4004-A1F8-75FFB8C06C76}.Release|x86.Build.0 = Release|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|Any CPU.Build.0 = Debug|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|x64.ActiveCfg = Debug|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|x64.Build.0 = Debug|Any CPU + {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|x86.ActiveCfg = Debug|Any CPU + {2323A7A3-E938-488D-A57E-638638054BC4}.Debug|x86.Build.0 = Debug|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Release|Any CPU.ActiveCfg = Release|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Release|Any CPU.Build.0 = Release|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Release|x64.ActiveCfg = Release|Any CPU {2323A7A3-E938-488D-A57E-638638054BC4}.Release|x64.Build.0 = Release|Any CPU + {2323A7A3-E938-488D-A57E-638638054BC4}.Release|x86.ActiveCfg = Release|Any CPU + {2323A7A3-E938-488D-A57E-638638054BC4}.Release|x86.Build.0 = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|x64.ActiveCfg = Debug|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|x64.Build.0 = Debug|Any CPU + {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Debug|x86.Build.0 = Debug|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|Any CPU.Build.0 = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x64.ActiveCfg = Release|Any CPU {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x64.Build.0 = Release|Any CPU + {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x86.ActiveCfg = Release|Any CPU + {6D8D18A9-86D7-455E-81EC-9682C30AB7E7}.Release|x86.Build.0 = Release|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|x64.ActiveCfg = Debug|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|x64.Build.0 = Debug|Any CPU + {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Debug|x86.Build.0 = Debug|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|Any CPU.Build.0 = Release|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|x64.ActiveCfg = Release|Any CPU {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|x64.Build.0 = Release|Any CPU + {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|x86.ActiveCfg = Release|Any CPU + {FE2E6CC1-EB80-4518-B3A3-CB373EDA6A83}.Release|x86.Build.0 = Release|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|Any CPU.Build.0 = Debug|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|x64.ActiveCfg = Debug|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|x64.Build.0 = Debug|Any CPU + {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|x86.ActiveCfg = Debug|Any CPU + {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Debug|x86.Build.0 = Debug|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|Any CPU.ActiveCfg = Release|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|Any CPU.Build.0 = Release|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|x64.ActiveCfg = Release|Any CPU {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|x64.Build.0 = Release|Any CPU + {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|x86.ActiveCfg = Release|Any CPU + {0308FBFD-57EB-4709-9AE4-A80D516AD84D}.Release|x86.Build.0 = Release|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|x64.ActiveCfg = Debug|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|x64.Build.0 = Debug|Any CPU + {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|x86.ActiveCfg = Debug|Any CPU + {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Debug|x86.Build.0 = Debug|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|Any CPU.Build.0 = Release|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x64.ActiveCfg = Release|Any CPU {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x64.Build.0 = Release|Any CPU + {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x86.ActiveCfg = Release|Any CPU + {8300F66D-9EB8-438A-BF0F-70DFBE07D9DE}.Release|x86.Build.0 = Release|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x64.ActiveCfg = Debug|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x64.Build.0 = Debug|Any CPU + {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x86.ActiveCfg = Debug|Any CPU + {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Debug|x86.Build.0 = Debug|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|Any CPU.ActiveCfg = Release|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|Any CPU.Build.0 = Release|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x64.ActiveCfg = Release|Any CPU {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x64.Build.0 = Release|Any CPU + {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x86.ActiveCfg = Release|Any CPU + {7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}.Release|x86.Build.0 = Release|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|Any CPU.Build.0 = Debug|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|x64.ActiveCfg = Debug|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|x64.Build.0 = Debug|Any CPU + {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|x86.ActiveCfg = Debug|Any CPU + {46B7B54F-1425-4C9D-824A-9B826855D249}.Debug|x86.Build.0 = Debug|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|Any CPU.ActiveCfg = Release|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|Any CPU.Build.0 = Release|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.ActiveCfg = Release|Any CPU {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.Build.0 = Release|Any CPU + {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x86.ActiveCfg = Release|Any CPU + {46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x86.Build.0 = Release|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.ActiveCfg = Debug|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.Build.0 = Debug|Any CPU + {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x86.ActiveCfg = Debug|Any CPU + {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x86.Build.0 = Debug|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.Build.0 = Release|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.ActiveCfg = Release|Any CPU {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.Build.0 = Release|Any CPU + {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x86.ActiveCfg = Release|Any CPU + {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x86.Build.0 = Release|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.Build.0 = Debug|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.ActiveCfg = Debug|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.Build.0 = Debug|Any CPU + {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x86.ActiveCfg = Debug|Any CPU + {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x86.Build.0 = Debug|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.ActiveCfg = Release|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.Build.0 = Release|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.ActiveCfg = Release|Any CPU {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.Build.0 = Release|Any CPU + {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x86.ActiveCfg = Release|Any CPU + {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x86.Build.0 = Release|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|x64.ActiveCfg = Debug|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|x64.Build.0 = Debug|Any CPU + {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|x86.ActiveCfg = Debug|Any CPU + {298AC787-A104-414C-B114-82BE764FBD9C}.Debug|x86.Build.0 = Debug|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Release|Any CPU.Build.0 = Release|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Release|x64.ActiveCfg = Release|Any CPU {298AC787-A104-414C-B114-82BE764FBD9C}.Release|x64.Build.0 = Release|Any CPU + {298AC787-A104-414C-B114-82BE764FBD9C}.Release|x86.ActiveCfg = Release|Any CPU + {298AC787-A104-414C-B114-82BE764FBD9C}.Release|x86.Build.0 = Release|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|x64.ActiveCfg = Debug|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|x64.Build.0 = Debug|Any CPU + {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Debug|x86.Build.0 = Debug|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|Any CPU.Build.0 = Release|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|x64.ActiveCfg = Release|Any CPU {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|x64.Build.0 = Release|Any CPU + {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|x86.ActiveCfg = Release|Any CPU + {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C}.Release|x86.Build.0 = Release|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|x64.ActiveCfg = Debug|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|x64.Build.0 = Debug|Any CPU + {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Debug|x86.Build.0 = Debug|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|Any CPU.Build.0 = Release|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x64.ActiveCfg = Release|Any CPU {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x64.Build.0 = Release|Any CPU + {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x86.ActiveCfg = Release|Any CPU + {8BC29F8A-78D6-422C-B522-10687ADC38ED}.Release|x86.Build.0 = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|Any CPU.Build.0 = Debug|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|x64.ActiveCfg = Debug|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|x64.Build.0 = Debug|Any CPU + {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Debug|x86.Build.0 = Debug|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|Any CPU.ActiveCfg = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|Any CPU.Build.0 = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x64.ActiveCfg = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x64.Build.0 = Release|Any CPU + {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x86.ActiveCfg = Release|Any CPU + {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x86.Build.0 = Release|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|x64.ActiveCfg = Debug|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|x64.Build.0 = Debug|Any CPU + {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|x86.ActiveCfg = Debug|Any CPU + {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Debug|x86.Build.0 = Debug|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|Any CPU.Build.0 = Release|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|x64.ActiveCfg = Release|Any CPU {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|x64.Build.0 = Release|Any CPU + {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|x86.ActiveCfg = Release|Any CPU + {72CA059E-6AAA-406C-A1EB-A2243E652F5F}.Release|x86.Build.0 = Release|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x64.ActiveCfg = Debug|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x64.Build.0 = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x86.ActiveCfg = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x86.Build.0 = Debug|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|Any CPU.Build.0 = Release|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x64.ActiveCfg = Release|Any CPU {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x64.Build.0 = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x86.ActiveCfg = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x86.Build.0 = Release|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x64.ActiveCfg = Debug|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x64.Build.0 = Debug|Any CPU + {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x86.ActiveCfg = Debug|Any CPU + {E627F1E3-BE03-443A-83A2-86A855A278EB}.Debug|x86.Build.0 = Debug|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|Any CPU.Build.0 = Release|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x64.ActiveCfg = Release|Any CPU {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x64.Build.0 = Release|Any CPU + {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x86.ActiveCfg = Release|Any CPU + {E627F1E3-BE03-443A-83A2-86A855A278EB}.Release|x86.Build.0 = Release|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|Any CPU.Build.0 = Debug|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|x64.ActiveCfg = Debug|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|x64.Build.0 = Debug|Any CPU + {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|x86.ActiveCfg = Debug|Any CPU + {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Debug|x86.Build.0 = Debug|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|Any CPU.ActiveCfg = Release|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|Any CPU.Build.0 = Release|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|x64.ActiveCfg = Release|Any CPU {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|x64.Build.0 = Release|Any CPU + {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|x86.ActiveCfg = Release|Any CPU + {F06B22CB-B143-4680-8FFF-35B9E50E6C47}.Release|x86.Build.0 = Release|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|x64.ActiveCfg = Debug|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|x64.Build.0 = Debug|Any CPU + {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|x86.ActiveCfg = Debug|Any CPU + {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Debug|x86.Build.0 = Debug|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|Any CPU.Build.0 = Release|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|x64.ActiveCfg = Release|Any CPU {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|x64.Build.0 = Release|Any CPU + {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|x86.ActiveCfg = Release|Any CPU + {EDCD9C20-2D9D-4098-A16E-03F97B306CB8}.Release|x86.Build.0 = Release|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|x64.ActiveCfg = Debug|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|x64.Build.0 = Debug|Any CPU + {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|x86.ActiveCfg = Debug|Any CPU + {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Debug|x86.Build.0 = Debug|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|Any CPU.ActiveCfg = Release|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|Any CPU.Build.0 = Release|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|x64.ActiveCfg = Release|Any CPU {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|x64.Build.0 = Release|Any CPU + {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|x86.ActiveCfg = Release|Any CPU + {DCA18996-4D3A-4E98-BCD0-1FB77C59253E}.Release|x86.Build.0 = Release|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|Any CPU.Build.0 = Debug|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|x64.ActiveCfg = Debug|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|x64.Build.0 = Debug|Any CPU + {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|x86.ActiveCfg = Debug|Any CPU + {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Debug|x86.Build.0 = Debug|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|Any CPU.Build.0 = Release|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|x64.ActiveCfg = Release|Any CPU {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|x64.Build.0 = Release|Any CPU + {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|x86.ActiveCfg = Release|Any CPU + {5CA3335E-E6AD-46FD-B277-29BBC3A16500}.Release|x86.Build.0 = Release|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|Any CPU.Build.0 = Debug|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|x64.ActiveCfg = Debug|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|x64.Build.0 = Debug|Any CPU + {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|x86.ActiveCfg = Debug|Any CPU + {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Debug|x86.Build.0 = Debug|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|Any CPU.ActiveCfg = Release|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|Any CPU.Build.0 = Release|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|x64.ActiveCfg = Release|Any CPU {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|x64.Build.0 = Release|Any CPU + {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|x86.ActiveCfg = Release|Any CPU + {32D9E720-6FE6-4F29-94B1-B10B05BFAD75}.Release|x86.Build.0 = Release|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Debug|Any CPU.Build.0 = Debug|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Debug|x64.ActiveCfg = Debug|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Debug|x64.Build.0 = Debug|Any CPU + {D775DB67-A4B4-44E5-9144-522689590057}.Debug|x86.ActiveCfg = Debug|Any CPU + {D775DB67-A4B4-44E5-9144-522689590057}.Debug|x86.Build.0 = Debug|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Release|Any CPU.ActiveCfg = Release|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Release|Any CPU.Build.0 = Release|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Release|x64.ActiveCfg = Release|Any CPU {D775DB67-A4B4-44E5-9144-522689590057}.Release|x64.Build.0 = Release|Any CPU + {D775DB67-A4B4-44E5-9144-522689590057}.Release|x86.ActiveCfg = Release|Any CPU + {D775DB67-A4B4-44E5-9144-522689590057}.Release|x86.Build.0 = Release|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x64.ActiveCfg = Debug|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x64.Build.0 = Debug|Any CPU + {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x86.ActiveCfg = Debug|Any CPU + {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Debug|x86.Build.0 = Debug|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|Any CPU.Build.0 = Release|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x64.ActiveCfg = Release|Any CPU {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x64.Build.0 = Release|Any CPU + {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x86.ActiveCfg = Release|Any CPU + {267998C1-55C2-4ADC-8361-2CDFA5EA6D6C}.Release|x86.Build.0 = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|x64.ActiveCfg = Debug|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|x64.Build.0 = Debug|Any CPU + {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {289E25C8-63F1-4D52-9909-207724DB40CB}.Debug|x86.Build.0 = Debug|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|Any CPU.Build.0 = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.ActiveCfg = Release|Any CPU {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x64.Build.0 = Release|Any CPU + {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x86.ActiveCfg = Release|Any CPU + {289E25C8-63F1-4D52-9909-207724DB40CB}.Release|x86.Build.0 = Release|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x64.ActiveCfg = Debug|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x64.Build.0 = Debug|Any CPU + {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Debug|x86.Build.0 = Debug|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|Any CPU.Build.0 = Release|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x64.ActiveCfg = Release|Any CPU {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x64.Build.0 = Release|Any CPU + {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x86.ActiveCfg = Release|Any CPU + {CCF745F2-0C95-4ED0-983B-507C528B39EA}.Release|x86.Build.0 = Release|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x64.ActiveCfg = Debug|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x64.Build.0 = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x86.ActiveCfg = Debug|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Debug|x86.Build.0 = Debug|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|Any CPU.Build.0 = Release|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x64.ActiveCfg = Release|Any CPU {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x64.Build.0 = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x86.ActiveCfg = Release|Any CPU + {806A0B0E-FEFF-420E-B5B2-C9FCBF890A8C}.Release|x86.Build.0 = Release|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|Any CPU.Build.0 = Debug|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|x64.ActiveCfg = Debug|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|x64.Build.0 = Debug|Any CPU + {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|x86.ActiveCfg = Debug|Any CPU + {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Debug|x86.Build.0 = Debug|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|Any CPU.ActiveCfg = Release|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|Any CPU.Build.0 = Release|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|x64.ActiveCfg = Release|Any CPU {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|x64.Build.0 = Release|Any CPU + {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|x86.ActiveCfg = Release|Any CPU + {6406DC61-0F30-42E8-A1DB-B38CDF454273}.Release|x86.Build.0 = Release|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|x64.ActiveCfg = Debug|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|x64.Build.0 = Debug|Any CPU + {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|x86.ActiveCfg = Debug|Any CPU + {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Debug|x86.Build.0 = Debug|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|Any CPU.Build.0 = Release|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x64.ActiveCfg = Release|Any CPU {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x64.Build.0 = Release|Any CPU + {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x86.ActiveCfg = Release|Any CPU + {E04FBBEF-744E-4EF3-B634-42AD9F8B68B1}.Release|x86.Build.0 = Release|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x64.ActiveCfg = Debug|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x64.Build.0 = Debug|Any CPU + {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x86.ActiveCfg = Debug|Any CPU + {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Debug|x86.Build.0 = Debug|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|Any CPU.Build.0 = Release|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x64.ActiveCfg = Release|Any CPU {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x64.Build.0 = Release|Any CPU + {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x86.ActiveCfg = Release|Any CPU + {6507D336-3A4D-41D4-81C0-2B900173A5FE}.Release|x86.Build.0 = Release|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|x64.ActiveCfg = Debug|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|x64.Build.0 = Debug|Any CPU + {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|x86.ActiveCfg = Debug|Any CPU + {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Debug|x86.Build.0 = Debug|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|Any CPU.Build.0 = Release|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.ActiveCfg = Release|Any CPU {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x64.Build.0 = Release|Any CPU + {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x86.ActiveCfg = Release|Any CPU + {A72B3BEB-E14B-4917-BE44-97EAE4E122D2}.Release|x86.Build.0 = Release|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.ActiveCfg = Debug|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x64.Build.0 = Debug|Any CPU + {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Debug|x86.Build.0 = Debug|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|Any CPU.Build.0 = Release|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.ActiveCfg = Release|Any CPU {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x64.Build.0 = Release|Any CPU + {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x86.ActiveCfg = Release|Any CPU + {D6A99D4F-6248-419E-8A43-B38ADEBABA2C}.Release|x86.Build.0 = Release|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|x64.ActiveCfg = Debug|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|x64.Build.0 = Debug|Any CPU + {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|x86.ActiveCfg = Debug|Any CPU + {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Debug|x86.Build.0 = Debug|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|Any CPU.Build.0 = Release|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|x64.ActiveCfg = Release|Any CPU {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|x64.Build.0 = Release|Any CPU + {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|x86.ActiveCfg = Release|Any CPU + {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4}.Release|x86.Build.0 = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|x64.ActiveCfg = Debug|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|x64.Build.0 = Debug|Any CPU + {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|x86.ActiveCfg = Debug|Any CPU + {BF029B0A-768B-43A1-8D91-E70B95505716}.Debug|x86.Build.0 = Debug|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|Any CPU.Build.0 = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x64.ActiveCfg = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x64.Build.0 = Release|Any CPU + {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x86.ActiveCfg = Release|Any CPU + {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x86.Build.0 = Release|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|Any CPU.Build.0 = Debug|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x64.ActiveCfg = Debug|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x64.Build.0 = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x86.ActiveCfg = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x86.Build.0 = Debug|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|Any CPU.Build.0 = Release|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.ActiveCfg = Release|Any CPU {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.Build.0 = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x86.ActiveCfg = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x86.Build.0 = Release|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x64.ActiveCfg = Debug|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x64.Build.0 = Debug|Any CPU + {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x86.ActiveCfg = Debug|Any CPU + {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Debug|x86.Build.0 = Debug|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|Any CPU.Build.0 = Release|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.ActiveCfg = Release|Any CPU {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x64.Build.0 = Release|Any CPU + {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x86.ActiveCfg = Release|Any CPU + {EBFE97DA-D0BA-48BA-8B5D-083B60348D1D}.Release|x86.Build.0 = Release|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|Any CPU.Build.0 = Debug|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x64.ActiveCfg = Debug|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x64.Build.0 = Debug|Any CPU + {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x86.ActiveCfg = Debug|Any CPU + {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Debug|x86.Build.0 = Debug|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|Any CPU.ActiveCfg = Release|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|Any CPU.Build.0 = Release|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x64.ActiveCfg = Release|Any CPU {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x64.Build.0 = Release|Any CPU + {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x86.ActiveCfg = Release|Any CPU + {F57F4862-F8D4-44A1-AC12-5C131B5C9785}.Release|x86.Build.0 = Release|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|x64.ActiveCfg = Debug|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|x64.Build.0 = Debug|Any CPU + {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Debug|x86.Build.0 = Debug|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|Any CPU.Build.0 = Release|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x64.ActiveCfg = Release|Any CPU {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x64.Build.0 = Release|Any CPU + {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x86.ActiveCfg = Release|Any CPU + {6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x86.Build.0 = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|Any CPU.Build.0 = Debug|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x64.ActiveCfg = Debug|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x64.Build.0 = Debug|Any CPU + {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x86.Build.0 = Debug|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|Any CPU.ActiveCfg = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|Any CPU.Build.0 = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.ActiveCfg = Release|Any CPU {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.Build.0 = Release|Any CPU + {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x86.ActiveCfg = Release|Any CPU + {7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x86.Build.0 = Release|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x64.ActiveCfg = Debug|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x64.Build.0 = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x86.ActiveCfg = Debug|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Debug|x86.Build.0 = Debug|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|Any CPU.Build.0 = Release|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x64.ActiveCfg = Release|Any CPU {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x64.Build.0 = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x86.ActiveCfg = Release|Any CPU + {F812BAAE-5A7D-4DF7-8E71-70696B51C61F}.Release|x86.Build.0 = Release|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|Any CPU.Build.0 = Debug|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|x64.ActiveCfg = Debug|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|x64.Build.0 = Debug|Any CPU + {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|x86.ActiveCfg = Debug|Any CPU + {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Debug|x86.Build.0 = Debug|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|Any CPU.ActiveCfg = Release|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|Any CPU.Build.0 = Release|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x64.ActiveCfg = Release|Any CPU {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x64.Build.0 = Release|Any CPU + {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x86.ActiveCfg = Release|Any CPU + {AFD64412-4D6A-452E-82A2-79E5D8842E29}.Release|x86.Build.0 = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x64.ActiveCfg = Debug|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x64.Build.0 = Debug|Any CPU + {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x86.ActiveCfg = Debug|Any CPU + {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Debug|x86.Build.0 = Debug|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|Any CPU.Build.0 = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.ActiveCfg = Release|Any CPU {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x64.Build.0 = Release|Any CPU + {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x86.ActiveCfg = Release|Any CPU + {AF329442-B48E-4B48-A18A-1C869D1BA6F5}.Release|x86.Build.0 = Release|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x64.ActiveCfg = Debug|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x64.Build.0 = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x86.ActiveCfg = Debug|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Debug|x86.Build.0 = Debug|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|Any CPU.Build.0 = Release|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x64.ActiveCfg = Release|Any CPU {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x64.Build.0 = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x86.ActiveCfg = Release|Any CPU + {781F1465-365C-0F22-1775-25025DAFA4C7}.Release|x86.Build.0 = Release|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x64.ActiveCfg = Debug|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x64.Build.0 = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Debug|x86.Build.0 = Debug|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|Any CPU.Build.0 = Release|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.ActiveCfg = Release|Any CPU {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x64.Build.0 = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x86.ActiveCfg = Release|Any CPU + {8D2AD45F-836A-516F-DE6A-71443CEBB18A}.Release|x86.Build.0 = Release|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|Any CPU.Build.0 = Debug|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|x64.ActiveCfg = Debug|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|x64.Build.0 = Debug|Any CPU + {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|x86.ActiveCfg = Debug|Any CPU + {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Debug|x86.Build.0 = Debug|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|Any CPU.Build.0 = Release|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|x64.ActiveCfg = Release|Any CPU {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|x64.Build.0 = Release|Any CPU + {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|x86.ActiveCfg = Release|Any CPU + {C19D9AC1-97DD-8E65-E8DB-D295A095AA2D}.Release|x86.Build.0 = Release|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|Any CPU.Build.0 = Debug|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|x64.ActiveCfg = Debug|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|x64.Build.0 = Debug|Any CPU + {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|x86.ActiveCfg = Debug|Any CPU + {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Debug|x86.Build.0 = Debug|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|Any CPU.ActiveCfg = Release|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|Any CPU.Build.0 = Release|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|x64.ActiveCfg = Release|Any CPU {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|x64.Build.0 = Release|Any CPU + {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|x86.ActiveCfg = Release|Any CPU + {B268E2F0-060F-8466-7D81-ABA4D735CA59}.Release|x86.Build.0 = Release|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|x64.ActiveCfg = Debug|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|x64.Build.0 = Debug|Any CPU + {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|x86.ActiveCfg = Debug|Any CPU + {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Debug|x86.Build.0 = Debug|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|Any CPU.Build.0 = Release|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|x64.ActiveCfg = Release|Any CPU {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|x64.Build.0 = Release|Any CPU + {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|x86.ActiveCfg = Release|Any CPU + {970BE341-9AC8-99A5-6572-E703C1E02FCB}.Release|x86.Build.0 = Release|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|x64.ActiveCfg = Debug|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|x64.Build.0 = Debug|Any CPU + {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|x86.ActiveCfg = Debug|Any CPU + {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Debug|x86.Build.0 = Debug|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|Any CPU.Build.0 = Release|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|x64.ActiveCfg = Release|Any CPU {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|x64.Build.0 = Release|Any CPU + {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|x86.ActiveCfg = Release|Any CPU + {7D0DB012-9798-4BB9-B15B-A5B0B7B3B094}.Release|x86.Build.0 = Release|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|x64.ActiveCfg = Debug|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|x64.Build.0 = Debug|Any CPU + {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|x86.ActiveCfg = Debug|Any CPU + {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Debug|x86.Build.0 = Debug|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|Any CPU.Build.0 = Release|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|x64.ActiveCfg = Release|Any CPU {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|x64.Build.0 = Release|Any CPU + {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|x86.ActiveCfg = Release|Any CPU + {7C0C7D13-D161-4AB0-9C29-83A0F1FF990E}.Release|x86.Build.0 = Release|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|x64.ActiveCfg = Debug|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|x64.Build.0 = Debug|Any CPU + {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {B067B126-88CD-4282-BEEF-7369B64423EF}.Debug|x86.Build.0 = Debug|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|Any CPU.ActiveCfg = Release|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|Any CPU.Build.0 = Release|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.ActiveCfg = Release|Any CPU {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.Build.0 = Release|Any CPU + {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x86.ActiveCfg = Release|Any CPU + {B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x86.Build.0 = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.Build.0 = Debug|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.ActiveCfg = Debug|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.Build.0 = Debug|Any CPU + {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x86.ActiveCfg = Debug|Any CPU + {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x86.Build.0 = Debug|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.ActiveCfg = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.Build.0 = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.ActiveCfg = Release|Any CPU {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.Build.0 = Release|Any CPU + {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x86.ActiveCfg = Release|Any CPU + {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x86.Build.0 = Release|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x64.ActiveCfg = Debug|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x64.Build.0 = Debug|Any CPU + {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Debug|x86.Build.0 = Debug|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|Any CPU.Build.0 = Release|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x64.ActiveCfg = Release|Any CPU {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x64.Build.0 = Release|Any CPU + {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x86.ActiveCfg = Release|Any CPU + {FC63C875-E880-D8BB-B8B5-978AB7B62983}.Release|x86.Build.0 = Release|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|Any CPU.Build.0 = Debug|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|x64.ActiveCfg = Debug|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|x64.Build.0 = Debug|Any CPU + {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|x86.ActiveCfg = Debug|Any CPU + {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Debug|x86.Build.0 = Debug|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|Any CPU.ActiveCfg = Release|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|Any CPU.Build.0 = Release|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|x64.ActiveCfg = Release|Any CPU {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|x64.Build.0 = Release|Any CPU + {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|x86.ActiveCfg = Release|Any CPU + {242F2D93-FCCE-4982-8075-F3052ECCA92C}.Release|x86.Build.0 = Release|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|Any CPU.Build.0 = Debug|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|x64.ActiveCfg = Debug|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|x64.Build.0 = Debug|Any CPU + {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Debug|x86.Build.0 = Debug|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|Any CPU.ActiveCfg = Release|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|Any CPU.Build.0 = Release|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|x64.ActiveCfg = Release|Any CPU {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|x64.Build.0 = Release|Any CPU + {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|x86.ActiveCfg = Release|Any CPU + {E7C243B9-E751-B3B4-8F16-95C76CA90D31}.Release|x86.Build.0 = Release|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|x64.ActiveCfg = Debug|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|x64.Build.0 = Debug|Any CPU + {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Debug|x86.Build.0 = Debug|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|Any CPU.Build.0 = Release|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|x64.ActiveCfg = Release|Any CPU {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|x64.Build.0 = Release|Any CPU + {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|x86.ActiveCfg = Release|Any CPU + {394B858B-9C26-B977-A2DA-8CC7BE5914CB}.Release|x86.Build.0 = Release|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|Any CPU.Build.0 = Debug|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|x64.ActiveCfg = Debug|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|x64.Build.0 = Debug|Any CPU + {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|x86.ActiveCfg = Debug|Any CPU + {13223C71-9EAC-9835-28ED-5A4833E6F915}.Debug|x86.Build.0 = Debug|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|Any CPU.ActiveCfg = Release|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|Any CPU.Build.0 = Release|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.ActiveCfg = Release|Any CPU {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.Build.0 = Release|Any CPU + {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x86.ActiveCfg = Release|Any CPU + {13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x86.Build.0 = Release|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.Build.0 = Debug|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.ActiveCfg = Debug|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.Build.0 = Debug|Any CPU + {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x86.ActiveCfg = Debug|Any CPU + {E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x86.Build.0 = Debug|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.Build.0 = Release|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.ActiveCfg = Release|Any CPU {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.Build.0 = Release|Any CPU + {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x86.ActiveCfg = Release|Any CPU + {E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x86.Build.0 = Release|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|Any CPU.Build.0 = Debug|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|x64.ActiveCfg = Debug|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|x64.Build.0 = Debug|Any CPU + {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|x86.ActiveCfg = Debug|Any CPU + {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Debug|x86.Build.0 = Debug|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|Any CPU.ActiveCfg = Release|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|Any CPU.Build.0 = Release|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|x64.ActiveCfg = Release|Any CPU {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|x64.Build.0 = Release|Any CPU + {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|x86.ActiveCfg = Release|Any CPU + {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76}.Release|x86.Build.0 = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|x64.ActiveCfg = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|x64.Build.0 = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|x86.ActiveCfg = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Debug|x86.Build.0 = Debug|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|Any CPU.Build.0 = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|x64.ActiveCfg = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|x64.Build.0 = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|x86.ActiveCfg = Release|Any CPU + {511BC47F-8640-4E5A-820F-662956911CFD}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -745,6 +1013,7 @@ Global {13223C71-9EAC-9835-28ED-5A4833E6F915} = {53E7CD86-0D19-40D9-A0FA-AB4613837E89} {E8D01281-D52A-BFF4-33DB-E35D91754272} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749} {562DD0C6-DAC8-02CC-C1DD-D43DF186CE76} = {51AFE054-AE99-497D-A593-69BAEFB5106F} + {511BC47F-8640-4E5A-820F-662956911CFD} = {51AFE054-AE99-497D-A593-69BAEFB5106F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/Directory.Packages.props b/Directory.Packages.props index 1c198a828..fd492781b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -62,6 +62,7 @@ + diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/AgentSkillsPlugin.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/AgentSkillsPlugin.cs new file mode 100644 index 000000000..96d438913 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/AgentSkillsPlugin.cs @@ -0,0 +1,40 @@ +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Settings; +using BotSharp.Plugin.AgentSkills.Functions; +using Microsoft.Extensions.Configuration; + +namespace BotSharp.Plugin.AgentSkills; + +/// +/// Agent Skills plugin for BotSharp. +/// Enables AI agents to leverage reusable skills following the Agent Skills specification. +/// +public class AgentSkillsPlugin : IBotSharpPlugin +{ + public string Id => "a5b3e8c1-7d2f-4a9e-b6c4-8f5d1e2a3b4c"; + public string Name => "Agent Skills"; + public string Description => "Enables AI agents to leverage reusable skills following the Agent Skills specification (https://agentskills.io)."; + public string IconUrl => "https://raw.githubusercontent.com/SciSharp/BotSharp/master/docs/static/logos/BotSharp.png"; + public string[] AgentIds => []; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + // Register settings + services.AddScoped(provider => + { + var settingService = provider.GetRequiredService(); + return settingService.Bind("AgentSkills"); + }); + + // Register skill loader + services.AddScoped(); + + // Register hooks + services.AddScoped(); + + // Register function callbacks + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/BotSharp.Plugin.AgentSkills.csproj b/src/Plugins/BotSharp.Plugin.AgentSkills/BotSharp.Plugin.AgentSkills.csproj new file mode 100644 index 000000000..6cbf6656a --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/BotSharp.Plugin.AgentSkills.csproj @@ -0,0 +1,57 @@ + + + + $(TargetFramework) + enable + $(LangVersion) + $(BotSharpVersion) + $(GeneratePackageOnBuild) + $(GenerateDocumentationFile) + $(SolutionDir)packages + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Enums/UtilityName.cs new file mode 100644 index 000000000..a400b2a95 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Enums/UtilityName.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Plugin.AgentSkills.Enums; + +/// +/// Utility name constants for Agent Skills plugin. +/// +public class UtilityName +{ + public const string AgentSkills = "agent-skills"; +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ListSkillDirectoryFn.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ListSkillDirectoryFn.cs new file mode 100644 index 000000000..575d9b5c9 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ListSkillDirectoryFn.cs @@ -0,0 +1,107 @@ +namespace BotSharp.Plugin.AgentSkills.Functions; + +/// +/// Function that lists contents of a skill's directory. +/// +public class ListSkillDirectoryFn : IFunctionCallback +{ + public string Name => "skill-list_skill_directory"; + public string Indication => "Listing skill directory..."; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly BotSharpOptions _options; + + public ListSkillDirectoryFn( + IServiceProvider services, + ILogger logger, + BotSharpOptions options) + { + _services = services; + _logger = logger; + _options = options; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs, _options.JsonSerializerOptions); + var skillName = args?.SkillName; + var relativePath = args?.RelativePath; + + if (string.IsNullOrWhiteSpace(skillName)) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "Skill name is required." + }, _options.JsonSerializerOptions); + return false; + } + + try + { + var settings = _services.GetRequiredService(); + var loader = _services.GetRequiredService(); + + // Load skills and find the requested one + var state = loader.LoadSkills(settings); + var skill = state.GetSkill(skillName); + + if (skill is null) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Skill '{skillName}' not found." + }, _options.JsonSerializerOptions); + return false; + } + + var entries = loader.ListSkillDirectory(skill, relativePath).ToList(); + + message.Content = JsonSerializer.Serialize(new + { + success = true, + skill_name = skill.Name, + path = relativePath ?? "/", + entries = entries.Select(e => new + { + name = e.Name, + type = e.IsDirectory ? "directory" : "file", + size = e.Size + }) + }, _options.JsonSerializerOptions); + + return true; + } + catch (UnauthorizedAccessException ex) + { + _logger.LogWarning(ex, "Path traversal attempt for skill: {SkillName}", skillName); + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "Access denied: path traversal detected." + }, _options.JsonSerializerOptions); + return false; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to list skill directory: {SkillName}/{Path}", skillName, relativePath); + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Failed to list directory: {ex.Message}" + }, _options.JsonSerializerOptions); + return false; + } + } + + private class ListSkillDirectoryArgs + { + [JsonPropertyName("skill_name")] + public string? SkillName { get; set; } + + [JsonPropertyName("relative_path")] + public string? RelativePath { get; set; } + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFileFn.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFileFn.cs new file mode 100644 index 000000000..116b3378e --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFileFn.cs @@ -0,0 +1,132 @@ +namespace BotSharp.Plugin.AgentSkills.Functions; + +/// +/// Function that reads a file within a skill's directory. +/// +public class ReadSkillFileFn : IFunctionCallback +{ + public string Name => "skill-read_skill_file"; + public string Indication => "Reading skill file..."; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly BotSharpOptions _options; + + public ReadSkillFileFn( + IServiceProvider services, + ILogger logger, + BotSharpOptions options) + { + _services = services; + _logger = logger; + _options = options; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs, _options.JsonSerializerOptions); + var skillName = args?.SkillName; + var filePath = args?.FilePath; + + if (string.IsNullOrWhiteSpace(skillName)) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "Skill name is required." + }, _options.JsonSerializerOptions); + return false; + } + + if (string.IsNullOrWhiteSpace(filePath)) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "File path is required." + }, _options.JsonSerializerOptions); + return false; + } + + try + { + var settings = _services.GetRequiredService(); + var loader = _services.GetRequiredService(); + + // Load skills and find the requested one + var state = loader.LoadSkills(settings); + var skill = state.GetSkill(skillName); + + if (skill is null) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Skill '{skillName}' not found." + }, _options.JsonSerializerOptions); + return false; + } + + var content = loader.ReadSkillFile(skill, filePath); + + // Truncate if necessary + var maxSize = settings.MaxOutputSizeBytes; + var originalLength = content.Length; + var truncated = content.Length > maxSize; + if (truncated) + { + content = content.Substring(0, maxSize); + } + + message.Content = JsonSerializer.Serialize(new + { + success = true, + skill_name = skill.Name, + file_path = filePath, + content, + truncated, + total_length = originalLength + }, _options.JsonSerializerOptions); + + return true; + } + catch (UnauthorizedAccessException ex) + { + _logger.LogWarning(ex, "Path traversal attempt for skill: {SkillName}", skillName); + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "Access denied: path traversal detected." + }, _options.JsonSerializerOptions); + return false; + } + catch (FileNotFoundException) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"File not found: {filePath}" + }, _options.JsonSerializerOptions); + return false; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to read skill file: {SkillName}/{FilePath}", skillName, filePath); + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Failed to read file: {ex.Message}" + }, _options.JsonSerializerOptions); + return false; + } + } + + private class ReadSkillFileArgs + { + [JsonPropertyName("skill_name")] + public string? SkillName { get; set; } + + [JsonPropertyName("file_path")] + public string? FilePath { get; set; } + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFn.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFn.cs new file mode 100644 index 000000000..e6fe21c6f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Functions/ReadSkillFn.cs @@ -0,0 +1,88 @@ +namespace BotSharp.Plugin.AgentSkills.Functions; + +/// +/// Function that reads the full content of a skill's SKILL.md file. +/// +public class ReadSkillFn : IFunctionCallback +{ + public string Name => "skill-read_skill"; + public string Indication => "Reading skill instructions..."; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + private readonly BotSharpOptions _options; + + public ReadSkillFn( + IServiceProvider services, + ILogger logger, + BotSharpOptions options) + { + _services = services; + _logger = logger; + _options = options; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs, _options.JsonSerializerOptions); + var skillName = args?.SkillName; + + if (string.IsNullOrWhiteSpace(skillName)) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = "Skill name is required." + }, _options.JsonSerializerOptions); + return false; + } + + try + { + var settings = _services.GetRequiredService(); + var loader = _services.GetRequiredService(); + + // Load skills and find the requested one + var state = loader.LoadSkills(settings); + var skill = state.GetSkill(skillName); + + if (skill is null) + { + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Skill '{skillName}' not found. Available skills: {string.Join(", ", state.AllSkills.Select(s => s.Name))}" + }, _options.JsonSerializerOptions); + return false; + } + + var content = loader.ReadSkillContent(skill); + message.Content = JsonSerializer.Serialize(new + { + success = true, + skill_name = skill.Name, + source = skill.Source.ToString().ToLowerInvariant(), + path = skill.Path, + content + }, _options.JsonSerializerOptions); + + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to read skill: {SkillName}", skillName); + message.Content = JsonSerializer.Serialize(new + { + success = false, + error = $"Failed to read skill '{skillName}': {ex.Message}" + }, _options.JsonSerializerOptions); + return false; + } + } + + private class ReadSkillArgs + { + [JsonPropertyName("skill_name")] + public string? SkillName { get; set; } + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillHook.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillHook.cs new file mode 100644 index 000000000..1e970411f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillHook.cs @@ -0,0 +1,135 @@ +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Agents.Enums; +using BotSharp.Abstraction.Agents.Settings; + +namespace BotSharp.Plugin.AgentSkills.Hooks; + +public class AgentSkillHook : AgentHookBase +{ + public override string SelfId => "471ca181-375f-b16f-7134-5f868ecd31c6"; + + private readonly SkillLoader _skillLoader; + private readonly AgentSkillsSettings _options; + private SkillsState _state; + + public AgentSkillHook(IServiceProvider services, AgentSettings settings) + : base(services, settings) + { + _skillLoader = services.GetRequiredService(); + _options = services.GetRequiredService(); + _state = _skillLoader.LoadSkills(_options); + } + + public override bool OnInstructionLoaded(string template, IDictionary dict) + { + if (Agent.Type == AgentType.Routing || Agent.Type == AgentType.Planning) + { + return base.OnInstructionLoaded(template, dict); + } + + // Refresh skills if needed or if this is the first load + if (_state.AllSkills.Count == 0) + { + LoadSkills(_options); + } + + var skillsList = GenerateSkillsList(_state); + + var locations = $"- **User Skills**: `{_options.UserSkillsDir}`"; + if (_options.ProjectSkillsDir != null) + { + locations += Environment.NewLine + $"- **Project Skills**: `{_options.ProjectSkillsDir}`"; + } + dict["skills_locations"] = locations; + dict["skills_list"] = skillsList; + + return base.OnInstructionLoaded(template, dict); + } + + /// + /// Loads skills from configured directories. + /// + private void LoadSkills(AgentSkillsSettings settings) + { + var skills = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Load user-level skills + var userDir = settings.GetUserSkillsDirectory(); + if (Directory.Exists(userDir)) + { + foreach (var skill in _skillLoader.LoadSkillsFromDirectory(userDir, SkillSource.User)) + { + skills[skill.Name] = skill; + } + } + + // Load project-level skills (overrides user-level with same name) + var projectDir = settings.GetProjectSkillsDirectory(); + if (projectDir != null && Directory.Exists(projectDir)) + { + foreach (var skill in _skillLoader.LoadSkillsFromDirectory(projectDir, SkillSource.Project)) + { + skills[skill.Name] = skill; + } + } + + _state = new SkillsState + { + UserSkills = skills.Values.Where(s => s.Source == SkillSource.User).ToList(), + ProjectSkills = skills.Values.Where(s => s.Source == SkillSource.Project).ToList(), + LastRefreshed = DateTimeOffset.UtcNow + }; + } + + /// + /// Generates a formatted list of available skills. + /// + /// The current skills state. + /// The formatted skills list. + public static string GenerateSkillsList(SkillsState state) + { + var lines = new List(); + + // Group by source for clarity + if (state.ProjectSkills.Count > 0) + { + lines.Add("*Project Skills:*"); + foreach (var skill in state.ProjectSkills) + { + lines.Add(skill.ToDisplayString()); + } + } + + if (state.UserSkills.Count > 0) + { + // Filter out user skills that are overridden by project skills + var projectSkillNames = state.ProjectSkills + .Select(s => s.Name) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + var nonOverriddenUserSkills = state.UserSkills + .Where(s => !projectSkillNames.Contains(s.Name)) + .ToList(); + + if (nonOverriddenUserSkills.Count > 0) + { + if (lines.Count > 0) + { + lines.Add(""); + } + lines.Add("*User Skills:*"); + foreach (var skill in nonOverriddenUserSkills) + { + lines.Add(skill.ToDisplayString()); + } + } + } + + if (lines.Count == 0) + { + return "*No skills available.*"; + } + + return string.Join(Environment.NewLine, lines); + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillsUtilityHook.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillsUtilityHook.cs new file mode 100644 index 000000000..2d7d7f87c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Hooks/AgentSkillsUtilityHook.cs @@ -0,0 +1,45 @@ +using BotSharp.Abstraction.Agents; + +namespace BotSharp.Plugin.AgentSkills.Hooks; + +/// +/// Hook that adds Agent Skills utilities to agents. +/// +public class AgentSkillsUtilityHook : IAgentUtilityHook +{ + private static readonly string PREFIX = "skill-"; + private static readonly string READ_SKILL_FN = $"{PREFIX}read_skill"; + private static readonly string READ_SKILL_FILE_FN = $"{PREFIX}read_skill_file"; + private static readonly string LIST_SKILL_DIRECTORY_FN = $"{PREFIX}list_skill_directory"; + + public void AddUtilities(List utilities) + { + var utility = new AgentUtility + { + Category = "skill", + Name = UtilityName.AgentSkills, + Items = [ + new UtilityItem + { + FunctionName = READ_SKILL_FN, + TemplateName = $"{READ_SKILL_FN}.fn", + Description = "Reads the full content of a skill's SKILL.md file to get detailed instructions." + }, + new UtilityItem + { + FunctionName = READ_SKILL_FILE_FN, + TemplateName = $"{READ_SKILL_FILE_FN}.fn", + Description = "Reads a file within a skill's directory." + }, + new UtilityItem + { + FunctionName = LIST_SKILL_DIRECTORY_FN, + TemplateName = $"{LIST_SKILL_DIRECTORY_FN}.fn", + Description = "Lists the contents of a skill's directory." + } + ] + }; + + utilities.Add(utility); + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillLoader.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillLoader.cs new file mode 100644 index 000000000..643cf1704 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillLoader.cs @@ -0,0 +1,306 @@ +using Microsoft.Extensions.Logging.Abstractions; + +namespace BotSharp.Plugin.AgentSkills.Loading; + +/// +/// Discovers, validates, and loads skills from configured directories. +/// +public sealed class SkillLoader +{ + private readonly SkillParser _parser; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Optional logger instance. + public SkillLoader(ILogger? logger = null) + { + _parser = new SkillParser(); + _logger = logger ?? NullLogger.Instance; + } + + /// + /// Loads all skills from the configured directories. + /// + /// The skills settings containing directory paths. + /// The loaded skills state. + public SkillsState LoadSkills(AgentSkillsSettings settings) + { + var userSkills = new List(); + var projectSkills = new List(); + + // Load user-level skills + if (settings.EnableUserSkills) + { + var userSkillsDir = settings.GetUserSkillsDirectory(); + userSkills.AddRange(LoadSkillsFromDirectory(userSkillsDir, SkillSource.User)); + } + + // Load project-level skills + if (settings.EnableProjectSkills) + { + var projectSkillsDir = settings.GetProjectSkillsDirectory(); + if (projectSkillsDir is not null) + { + projectSkills.AddRange(LoadSkillsFromDirectory(projectSkillsDir, SkillSource.Project)); + } + } + + _logger.LogInformation( + "Loaded {UserCount} user skills and {ProjectCount} project skills", + userSkills.Count, + projectSkills.Count); + + return new SkillsState + { + UserSkills = userSkills, + ProjectSkills = projectSkills, + LastRefreshed = DateTimeOffset.UtcNow + }; + } + + /// + /// Loads a single skill by name. + /// + /// The name of the skill to load. + /// The skills settings containing directory paths. + /// The loaded skill metadata, or null if not found. + public SkillMetadata? LoadSkill(string skillName, AgentSkillsSettings settings) + { + // Check project-level first (takes precedence) + var projectSkillPath = settings.GetProjectSkillPath(skillName); + if (projectSkillPath is not null) + { + var skill = TryLoadSkill(projectSkillPath, SkillSource.Project); + if (skill is not null) + { + return skill; + } + } + + // Check user-level + var userSkillPath = settings.GetUserSkillPath(skillName); + return TryLoadSkill(userSkillPath, SkillSource.User); + } + + /// + /// Loads all skills from a specific directory. + /// + /// The directory containing skill subdirectories. + /// The source type for loaded skills. + /// Collection of successfully loaded skills. + public IEnumerable LoadSkillsFromDirectory(string skillsDirectory, SkillSource source) + { + if (!Directory.Exists(skillsDirectory)) + { + _logger.LogDebug("Skills directory does not exist: {Directory}", skillsDirectory); + yield break; + } + + var skillDirectories = Directory.GetDirectories(skillsDirectory); + + foreach (var skillDir in skillDirectories) + { + var skill = TryLoadSkill(skillDir, source); + if (skill is not null) + { + yield return skill; + } + } + } + + /// + /// Attempts to load a skill from a directory. + /// + /// The skill directory path. + /// The source type for the skill. + /// The loaded skill metadata, or null if loading fails. + private SkillMetadata? TryLoadSkill(string skillDirectory, SkillSource source) + { + var skillFilePath = Path.Combine(skillDirectory, SkillMetadata.SkillFileName); + + if (!File.Exists(skillFilePath)) + { + _logger.LogDebug("No SKILL.md found in: {Directory}", skillDirectory); + return null; + } + + // Security check: ensure the skill file is not a symlink pointing outside + if (IsSymbolicLink(skillFilePath)) + { + var realPath = GetRealPath(skillFilePath); + if (realPath is null || !IsPathSafe(realPath, skillDirectory)) + { + _logger.LogWarning( + "Skipping skill with potentially unsafe symlink: {Directory}", + skillDirectory); + return null; + } + } + + try + { + var skill = _parser.Parse(skillFilePath, source); + _logger.LogDebug("Loaded skill: {SkillName} from {Source}", skill.Name, source); + return skill; + } + catch (SkillParseException ex) + { + _logger.LogWarning("Failed to parse skill: {Error}", ex.Message); + return null; + } + catch (Exception ex) + { + _logger.LogError(ex, "Unexpected error loading skill from: {Directory}", skillDirectory); + return null; + } + } + + /// + /// Reads the full content of a SKILL.md file. + /// + /// The skill metadata. + /// The full content of the SKILL.md file. + /// Thrown if the file does not exist. + public string ReadSkillContent(SkillMetadata skill) + { + var skillFilePath = skill.SkillFilePath; + + if (!File.Exists(skillFilePath)) + { + throw new FileNotFoundException($"SKILL.md not found for skill '{skill.Name}'", skillFilePath); + } + + return File.ReadAllText(skillFilePath); + } + + /// + /// Reads a file within a skill directory. + /// + /// The skill metadata. + /// Relative path to the file within the skill directory. + /// The file content. + /// Thrown if path traversal is detected. + /// Thrown if the file does not exist. + public string ReadSkillFile(SkillMetadata skill, string relativePath) + { + var safePath = ResolveSafePath(skill.Path, relativePath); + if (safePath is null) + { + throw new UnauthorizedAccessException($"Path traversal attempt detected: {relativePath}"); + } + + if (!File.Exists(safePath)) + { + throw new FileNotFoundException($"File not found: {relativePath}", safePath); + } + + return File.ReadAllText(safePath); + } + + /// + /// Lists files in a skill directory. + /// + /// The skill metadata. + /// Optional relative path within the skill directory. + /// Collection of file and directory entries. + public IEnumerable ListSkillDirectory(SkillMetadata skill, string? relativePath = null) + { + var targetDir = skill.Path; + + if (!string.IsNullOrEmpty(relativePath)) + { + var safePath = ResolveSafePath(skill.Path, relativePath); + if (safePath is null) + { + throw new UnauthorizedAccessException($"Path traversal attempt detected: {relativePath}"); + } + targetDir = safePath; + } + + if (!Directory.Exists(targetDir)) + { + yield break; + } + + foreach (var dir in Directory.GetDirectories(targetDir)) + { + var name = Path.GetFileName(dir); + yield return new SkillDirectoryEntry { Name = name, IsDirectory = true }; + } + + foreach (var file in Directory.GetFiles(targetDir)) + { + var name = Path.GetFileName(file); + var size = new FileInfo(file).Length; + yield return new SkillDirectoryEntry { Name = name, IsDirectory = false, Size = size }; + } + } + + /// + /// Checks if a path is a symbolic link. + /// + private static bool IsSymbolicLink(string path) + { + try + { + var fileInfo = new FileInfo(path); + return fileInfo.LinkTarget != null; + } + catch + { + return false; + } + } + + /// + /// Gets the real path of a symbolic link. + /// + private static string? GetRealPath(string path) + { + try + { + var fileInfo = new FileInfo(path); + return fileInfo.LinkTarget != null ? Path.GetFullPath(fileInfo.LinkTarget) : Path.GetFullPath(path); + } + catch + { + return null; + } + } + + /// + /// Checks if a path is safe (within the base directory). + /// + private static bool IsPathSafe(string path, string baseDir) + { + var normalizedPath = Path.GetFullPath(path); + var normalizedBase = Path.GetFullPath(baseDir); + return normalizedPath.StartsWith(normalizedBase, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Resolves a relative path within a base directory safely. + /// + private static string? ResolveSafePath(string baseDir, string relativePath) + { + try + { + var combined = Path.Combine(baseDir, relativePath); + var fullPath = Path.GetFullPath(combined); + var normalizedBase = Path.GetFullPath(baseDir); + + if (!fullPath.StartsWith(normalizedBase, StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + return fullPath; + } + catch + { + return null; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillParser.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillParser.cs new file mode 100644 index 000000000..06fddae20 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillParser.cs @@ -0,0 +1,205 @@ +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace BotSharp.Plugin.AgentSkills.Loading; + +/// +/// Parses SKILL.md files to extract YAML frontmatter and create . +/// +public sealed class SkillParser +{ + private const string FrontmatterDelimiter = "---"; + + private readonly IDeserializer _yamlDeserializer; + + /// + /// Initializes a new instance of the class. + /// + public SkillParser() + { + _yamlDeserializer = new DeserializerBuilder() + .WithNamingConvention(HyphenatedNamingConvention.Instance) + .IgnoreUnmatchedProperties() + .Build(); + } + + /// + /// Parses a SKILL.md file and extracts the skill metadata. + /// + /// The path to the SKILL.md file. + /// The source location of the skill. + /// The parsed skill metadata. + /// Thrown when parsing fails. + public SkillMetadata Parse(string skillFilePath, SkillSource source) + { + // Validate the file exists and is within size limits + var fileValidation = SkillValidator.ValidateSkillFile(skillFilePath); + if (!fileValidation.IsValid) + { + throw new SkillParseException(skillFilePath, fileValidation.ErrorMessage!); + } + + var content = File.ReadAllText(skillFilePath); + var skillDirectory = Path.GetDirectoryName(skillFilePath)!; + var directoryName = Path.GetFileName(skillDirectory); + + return ParseContent(content, skillDirectory, directoryName, source); + } + + /// + /// Parses SKILL.md content and extracts the skill metadata. + /// + /// The content of the SKILL.md file. + /// The directory containing the skill. + /// The name of the skill directory. + /// The source location of the skill. + /// The parsed skill metadata. + /// Thrown when parsing fails. + public SkillMetadata ParseContent(string content, string skillDirectory, string directoryName, SkillSource source) + { + var frontmatter = ExtractFrontmatter(content); + if (frontmatter is null) + { + throw new SkillParseException(skillDirectory, "SKILL.md must have YAML frontmatter delimited by '---'."); + } + + SkillFrontmatter yamlData; + try + { + yamlData = _yamlDeserializer.Deserialize(frontmatter); + } + catch (Exception ex) + { + throw new SkillParseException(skillDirectory, $"Failed to parse YAML frontmatter: {ex.Message}", ex); + } + + // Validate required fields + if (string.IsNullOrWhiteSpace(yamlData.Name)) + { + throw new SkillParseException(skillDirectory, "Skill 'name' is required in frontmatter."); + } + + if (string.IsNullOrWhiteSpace(yamlData.Description)) + { + throw new SkillParseException(skillDirectory, "Skill 'description' is required in frontmatter."); + } + + // Validate name format + var nameValidation = SkillValidator.ValidateName(yamlData.Name); + if (!nameValidation.IsValid) + { + throw new SkillParseException(skillDirectory, nameValidation.ErrorMessage!); + } + + // Validate name matches directory + var matchValidation = SkillValidator.ValidateNameMatchesDirectory(yamlData.Name, directoryName); + if (!matchValidation.IsValid) + { + throw new SkillParseException(skillDirectory, matchValidation.ErrorMessage!); + } + + // Validate description length + var descValidation = SkillValidator.ValidateDescription(yamlData.Description); + if (!descValidation.IsValid) + { + throw new SkillParseException(skillDirectory, descValidation.ErrorMessage!); + } + + // Parse allowed tools + var allowedTools = AllowedTool.Parse(yamlData.AllowedTools); + + // Build metadata dictionary + Dictionary? metadata = null; + if (yamlData.Metadata is not null && yamlData.Metadata.Count > 0) + { + metadata = new Dictionary(yamlData.Metadata); + } + + return new SkillMetadata + { + Name = yamlData.Name, + Description = yamlData.Description, + Path = skillDirectory, + Source = source, + License = yamlData.License, + Compatibility = yamlData.Compatibility, + Metadata = metadata, + AllowedTools = allowedTools.Count > 0 ? allowedTools : null + }; + } + + /// + /// Extracts the YAML frontmatter from SKILL.md content. + /// + private static string? ExtractFrontmatter(string content) + { + if (string.IsNullOrWhiteSpace(content)) + { + return null; + } + + var lines = content.Split('\n'); + + // First line must be the frontmatter delimiter + if (lines.Length == 0 || lines[0].Trim() != FrontmatterDelimiter) + { + return null; + } + + // Find the closing delimiter + var frontmatterLines = new List(); + for (var i = 1; i < lines.Length; i++) + { + if (lines[i].Trim() == FrontmatterDelimiter) + { + return string.Join('\n', frontmatterLines); + } + frontmatterLines.Add(lines[i]); + } + + // No closing delimiter found + return null; + } + + /// + /// Internal class representing the YAML frontmatter structure. + /// + private sealed class SkillFrontmatter + { + public string? Name { get; set; } + public string? Description { get; set; } + public string? License { get; set; } + public string? Compatibility { get; set; } + public string? AllowedTools { get; set; } + public Dictionary? Metadata { get; set; } + } +} + +/// +/// Exception thrown when skill parsing fails. +/// +public sealed class SkillParseException : Exception +{ + /// + /// Gets the path to the skill that failed to parse. + /// + public string SkillPath { get; } + + /// + /// Initializes a new instance of the class. + /// + public SkillParseException(string skillPath, string message) + : base($"Failed to parse skill at '{skillPath}': {message}") + { + SkillPath = skillPath; + } + + /// + /// Initializes a new instance of the class. + /// + public SkillParseException(string skillPath, string message, Exception innerException) + : base($"Failed to parse skill at '{skillPath}': {message}", innerException) + { + SkillPath = skillPath; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillValidator.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillValidator.cs new file mode 100644 index 000000000..e8594ca3b --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Loading/SkillValidator.cs @@ -0,0 +1,133 @@ +namespace BotSharp.Plugin.AgentSkills.Loading; + +/// +/// Validates skill names and files according to the Agent Skills specification. +/// +public static partial class SkillValidator +{ + /// + /// Pattern for valid skill names: lowercase alphanumeric with hyphens, max 64 characters. + /// + private static readonly Regex SkillNamePatternRegex = new Regex( + @"^[a-z0-9][a-z0-9\-]{0,62}[a-z0-9]$|^[a-z0-9]$", + RegexOptions.Compiled); + + /// + /// Validates a skill name according to the Agent Skills specification. + /// + /// The skill name to validate. + /// A validation result indicating success or failure with error message. + public static SkillValidationResult ValidateName(string? name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return SkillValidationResult.Failure("Skill name cannot be null or empty."); + } + + if (name.Length > 64) + { + return SkillValidationResult.Failure($"Skill name exceeds maximum length of 64 characters. Actual: {name.Length}"); + } + + if (!SkillNamePatternRegex.IsMatch(name)) + { + return SkillValidationResult.Failure( + "Skill name must contain only lowercase letters, numbers, and hyphens. " + + "Must start and end with a letter or number."); + } + + return SkillValidationResult.Success(); + } + + /// + /// Validates that a skill name matches its directory name. + /// + /// The skill name from YAML frontmatter. + /// The directory name containing the skill. + /// A validation result indicating success or failure with error message. + public static SkillValidationResult ValidateNameMatchesDirectory(string skillName, string directoryName) + { + if (!string.Equals(skillName, directoryName, StringComparison.OrdinalIgnoreCase)) + { + return SkillValidationResult.Failure( + $"Skill name '{skillName}' does not match directory name '{directoryName}'."); + } + + return SkillValidationResult.Success(); + } + + /// + /// Validates a skill description. + /// + /// The description to validate. + /// A validation result indicating success or failure with error message. + public static SkillValidationResult ValidateDescription(string? description) + { + if (string.IsNullOrWhiteSpace(description)) + { + return SkillValidationResult.Failure("Skill description cannot be null or empty."); + } + + if (description.Length > 1024) + { + return SkillValidationResult.Failure( + $"Skill description exceeds maximum length of 1024 characters. Actual: {description.Length}"); + } + + return SkillValidationResult.Success(); + } + + /// + /// Validates that a SKILL.md file exists and is within size limits. + /// + /// The path to the SKILL.md file. + /// A validation result indicating success or failure with error message. + public static SkillValidationResult ValidateSkillFile(string skillFilePath) + { + if (!File.Exists(skillFilePath)) + { + return SkillValidationResult.Failure($"SKILL.md file not found at: {skillFilePath}"); + } + + var fileInfo = new FileInfo(skillFilePath); + if (fileInfo.Length > 10 * 1024 * 1024) // 10 MB limit + { + return SkillValidationResult.Failure( + $"SKILL.md file exceeds maximum size of 10 MB. Actual: {fileInfo.Length / (1024 * 1024):F2} MB"); + } + + return SkillValidationResult.Success(); + } +} + +/// +/// Represents the result of a skill validation operation. +/// +public readonly struct SkillValidationResult +{ + /// + /// Gets whether the validation was successful. + /// + public bool IsValid { get; } + + /// + /// Gets the error message if validation failed. + /// + public string? ErrorMessage { get; } + + private SkillValidationResult(bool isValid, string? errorMessage) + { + IsValid = isValid; + ErrorMessage = errorMessage; + } + + /// + /// Creates a successful validation result. + /// + public static SkillValidationResult Success() => new(true, null); + + /// + /// Creates a failed validation result with an error message. + /// + public static SkillValidationResult Failure(string errorMessage) => new(false, errorMessage); +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Models/AllowedTool.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/AllowedTool.cs new file mode 100644 index 000000000..09572d20b --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/AllowedTool.cs @@ -0,0 +1,63 @@ +namespace BotSharp.Plugin.AgentSkills.Models; + +/// +/// Represents an allowed tool specification from a skill's allowed-tools field. +/// Supports both exact matches and glob patterns. +/// +public sealed record AllowedTool +{ + /// + /// The tool name or pattern (e.g., "read_file" or "execute_*"). + /// + public string Name { get; init; } = string.Empty; + + /// + /// Whether this represents a glob pattern. + /// + public bool IsPattern { get; init; } + + private Regex? _regex; + + /// + /// Checks if the given tool name matches this allowed tool specification. + /// + /// The tool name to check. + /// True if the tool name matches; otherwise, false. + public bool Matches(string toolName) + { + if (!IsPattern) + { + return string.Equals(Name, toolName, StringComparison.OrdinalIgnoreCase); + } + + _regex ??= new Regex( + "^" + Regex.Escape(Name).Replace("\\*", ".*") + "$", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + + return _regex.IsMatch(toolName); + } + + /// + /// Parses a space-delimited allowed-tools string into individual instances. + /// + /// Space-delimited string of tool names/patterns. + /// Collection of parsed allowed tools. + public static IReadOnlyList Parse(string? allowedToolsString) + { + if (string.IsNullOrWhiteSpace(allowedToolsString)) + { + return []; + } + + var tools = new List(); + var parts = allowedToolsString.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (var part in parts) + { + var isPattern = part.Contains('*'); + tools.Add(new AllowedTool { Name = part, IsPattern = isPattern }); + } + + return tools; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillDirectoryEntry.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillDirectoryEntry.cs new file mode 100644 index 000000000..9560c84b8 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillDirectoryEntry.cs @@ -0,0 +1,22 @@ +namespace BotSharp.Plugin.AgentSkills.Models; + +/// +/// Represents an entry in a skill directory listing. +/// +public sealed record SkillDirectoryEntry +{ + /// + /// The name of the file or directory. + /// + public string Name { get; init; } = string.Empty; + + /// + /// Whether this is a directory. + /// + public bool IsDirectory { get; init; } + + /// + /// The file size in bytes (only for files). + /// + public long? Size { get; init; } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillMetadata.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillMetadata.cs new file mode 100644 index 000000000..3949c9ed4 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillMetadata.cs @@ -0,0 +1,79 @@ +namespace BotSharp.Plugin.AgentSkills.Models; + +/// +/// Represents the metadata of a skill parsed from SKILL.md YAML frontmatter. +/// Follows the Agent Skills specification: https://agentskills.io +/// +public sealed record SkillMetadata +{ + /// + /// Maximum allowed length for skill name. + /// + public const int MaxNameLength = 64; + + /// + /// Maximum allowed length for skill description. + /// + public const int MaxDescriptionLength = 1024; + + /// + /// Maximum file size for SKILL.md in bytes (10 MB). + /// + public const long MaxSkillFileSize = 10 * 1024 * 1024; + + /// + /// The standard skill definition filename. + /// + public const string SkillFileName = "SKILL.md"; + + /// + /// Required. Skill identifier (lowercase alphanumeric with hyphens, max 64 characters). + /// Must match the directory name. + /// + public string Name { get; init; } = string.Empty; + + /// + /// Required. Brief description of the skill's purpose (max 1024 characters). + /// + public string Description { get; init; } = string.Empty; + + /// + /// Absolute path to the skill directory containing SKILL.md. + /// + public string Path { get; init; } = string.Empty; + + /// + /// Whether the skill is from user-level or project-level location. + /// + public SkillSource Source { get; init; } + + /// + /// Optional. SPDX license identifier (e.g., "MIT", "Apache-2.0"). + /// + public string? License { get; init; } + + /// + /// Optional. Compatibility constraints (e.g., "vscode", "cursor", "any"). + /// + public string? Compatibility { get; init; } + + /// + /// Optional. Additional key-value metadata pairs. + /// + public IReadOnlyDictionary? Metadata { get; init; } + + /// + /// Optional. List of tools the skill is allowed to use. + /// + public IReadOnlyList? AllowedTools { get; init; } + + /// + /// Gets the full path to the SKILL.md file. + /// + public string SkillFilePath => System.IO.Path.Combine(Path, SkillFileName); + + /// + /// Returns a display string for the skill suitable for system prompts. + /// + public string ToDisplayString() => $"- **{Name}**: {Description}"; +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillSource.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillSource.cs new file mode 100644 index 000000000..a93de977c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillSource.cs @@ -0,0 +1,17 @@ +namespace BotSharp.Plugin.AgentSkills.Models; + +/// +/// Represents the source location of a skill. +/// +public enum SkillSource +{ + /// + /// User-level skill stored in ~/.botsharp/skills/ + /// + User, + + /// + /// Project-level skill stored in {project}/.botsharp/skills/ + /// + Project +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillsState.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillsState.cs new file mode 100644 index 000000000..333e74543 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Models/SkillsState.cs @@ -0,0 +1,62 @@ +namespace BotSharp.Plugin.AgentSkills.Models; + +/// +/// Represents the current state of loaded skills. +/// +public sealed class SkillsState +{ + /// + /// Gets or sets the collection of user-level skills. + /// + public IReadOnlyList UserSkills { get; init; } = []; + + /// + /// Gets or sets the collection of project-level skills. + /// + public IReadOnlyList ProjectSkills { get; init; } = []; + + /// + /// Gets the timestamp when skills were last loaded. + /// + public DateTimeOffset LastRefreshed { get; init; } = DateTimeOffset.UtcNow; + + /// + /// Gets all skills combined, with project skills taking precedence over user skills + /// when there are name conflicts. + /// + public IReadOnlyList AllSkills + { + get + { + var projectSkillNames = ProjectSkills.Select(s => s.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); + var userSkillsWithoutOverrides = UserSkills.Where(s => !projectSkillNames.Contains(s.Name)); + return [.. ProjectSkills, .. userSkillsWithoutOverrides]; + } + } + + /// + /// Gets a skill by name, checking project skills first, then user skills. + /// + /// The skill name to find. + /// The skill metadata if found; otherwise, null. + public SkillMetadata? GetSkill(string name) + { + return ProjectSkills.FirstOrDefault(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + ?? UserSkills.FirstOrDefault(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Gets skills filtered by source. + /// + /// The source to filter by. + /// Skills from the specified source. + public IReadOnlyList GetSkillsBySource(SkillSource source) + { + return source switch + { + SkillSource.User => UserSkills, + SkillSource.Project => ProjectSkills, + _ => [] + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Settings/AgentSkillsSettings.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Settings/AgentSkillsSettings.cs new file mode 100644 index 000000000..2e3781c3b --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Settings/AgentSkillsSettings.cs @@ -0,0 +1,112 @@ +namespace BotSharp.Plugin.AgentSkills.Settings; + +/// +/// Configuration settings for the Agent Skills plugin. +/// +public class AgentSkillsSettings +{ + /// + /// Enable user-level skills from ~/.botsharp/skills/ + /// + public bool EnableUserSkills { get; set; } = true; + + /// + /// Enable project-level skills from {project}/.botsharp/skills/ + /// + public bool EnableProjectSkills { get; set; } = true; + + /// + /// Override path for user skills directory. If null, uses default ~/.botsharp/skills/ + /// + public string? UserSkillsDir { get; set; } + + /// + /// Override path for project skills directory. If null, uses default {project}/.botsharp/skills/ + /// + public string? ProjectSkillsDir { get; set; } + + /// + /// Cache loaded skills in memory. + /// + public bool CacheSkills { get; set; } = true; + + /// + /// Validate skills on startup. + /// + public bool ValidateOnStartup { get; set; } = true; + + /// + /// Skills cache duration in seconds. + /// + public int SkillsCacheDurationSeconds { get; set; } = 300; + + /// + /// Enable read_skill tool to read full SKILL.md content. + /// + public bool EnableReadSkillTool { get; set; } = true; + + /// + /// Enable read_skill_file tool to read files in skill directories. + /// + public bool EnableReadFileTool { get; set; } = true; + + /// + /// Enable list_skill_directory tool to list skill directory contents. + /// + public bool EnableListDirectoryTool { get; set; } = true; + + /// + /// Maximum output size in bytes for skill content. + /// + public int MaxOutputSizeBytes { get; set; } = 50 * 1024; + + /// + /// Gets the resolved user skills directory path. + /// + public string GetUserSkillsDirectory() + { + if (!string.IsNullOrEmpty(UserSkillsDir)) + { + return UserSkillsDir; + } + + var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + return Path.Combine(homeDir, ".botsharp", "skills"); + } + + /// + /// Gets the resolved project skills directory path. + /// + /// The project root directory. + public string? GetProjectSkillsDirectory(string? projectRoot = null) + { + if (!string.IsNullOrEmpty(ProjectSkillsDir)) + { + return ProjectSkillsDir; + } + + if (string.IsNullOrEmpty(projectRoot)) + { + projectRoot = Directory.GetCurrentDirectory(); + } + + return Path.Combine(projectRoot, ".botsharp", "skills"); + } + + /// + /// Gets the path to a specific skill in user skills directory. + /// + public string GetUserSkillPath(string skillName) + { + return Path.Combine(GetUserSkillsDirectory(), skillName); + } + + /// + /// Gets the path to a specific skill in project skills directory. + /// + public string? GetProjectSkillPath(string skillName, string? projectRoot = null) + { + var projectSkillsDir = GetProjectSkillsDirectory(projectRoot); + return projectSkillsDir != null ? Path.Combine(projectSkillsDir, skillName) : null; + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/Using.cs b/src/Plugins/BotSharp.Plugin.AgentSkills/Using.cs new file mode 100644 index 000000000..73125f6ef --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/Using.cs @@ -0,0 +1,25 @@ +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Text; +global using System.Linq; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using System.Text.RegularExpressions; +global using System.Threading.Tasks; +global using BotSharp.Abstraction.Conversations; +global using BotSharp.Abstraction.Plugins; +global using BotSharp.Abstraction.Conversations.Models; +global using BotSharp.Abstraction.Functions; +global using BotSharp.Abstraction.Agents.Models; +global using BotSharp.Abstraction.Templating; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using BotSharp.Abstraction.Utilities; +global using BotSharp.Abstraction.Messaging; +global using BotSharp.Abstraction.Options; +global using BotSharp.Plugin.AgentSkills.Enums; +global using BotSharp.Plugin.AgentSkills.Hooks; +global using BotSharp.Plugin.AgentSkills.Loading; +global using BotSharp.Plugin.AgentSkills.Models; +global using BotSharp.Plugin.AgentSkills.Settings; diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/agent.json b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/agent.json new file mode 100644 index 000000000..b98cf0d84 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/agent.json @@ -0,0 +1,35 @@ +{ + "id": "471ca181-375f-b16f-7134-5f868ecd31c6", + "name": "Agent Skill", + "description": "You have access to a skills library that provides specialized capabilities and domain knowledge.", + "iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png", + "type": "task", + "createdDateTime": "2025-11-15T13:49:00Z", + "updatedDateTime": "2025-11-15T13:49:00Z", + "disabled": false, + "isPublic": true, + "profiles": [ "skill" ], + "utilities": [ + { + "Category": "skill", + "Name": "agent-skills", + "Items": [ + { + "FunctionName": "READ_SKILL_FN", + "TemplateName": "READ_SKILL_FN.fn", + "Description": "Reads the full content of a skill's SKILL.md file to get detailed instructions." + }, + { + "FunctionName": "READ_SKILL_FILE_FN", + "TemplateName": "READ_SKILL_FILE_FN.fn", + "Description": "Reads a file within a skill's directory." + }, + { + "FunctionName": "LIST_SKILL_DIRECTORY_FN", + "TemplateName": "LIST_SKILL_DIRECTORY_FN.fn", + "Description": "Lists the contents of a skill's directory." + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/instructions/instruction.liquid new file mode 100644 index 000000000..d2c012351 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/471ca181-375f-b16f-7134-5f868ecd31c6/instructions/instruction.liquid @@ -0,0 +1,54 @@ + + You have access to a skills library that provides specialized capabilities and domain knowledge. + + {skills_locations} + + **Available Skills:** + + {skills_list} + + --- + + ### How to Use Skills (Progressive Disclosure) - CRITICAL + + Skills follow a **progressive disclosure** pattern - you know they exist (name + description above), + but you **MUST read the full instructions before using them**. + + **MANDATORY Workflow:** + + 1. **Recognize when a skill applies**: Check if the user's task matches any skill's description above + 2. **Read the skill's full instructions FIRST**: Use `read_skill` tool to get the complete SKILL.md content + - This tells you exactly what scripts exist, their parameters, and how to use them + - **NEVER assume or guess script names, paths, or arguments** + 3. **Follow the skill's instructions precisely**: SKILL.md contains step-by-step workflows and examples + 4. **Execute scripts only after reading**: Use the exact script paths and argument formats from SKILL.md + + **IMPORTANT RULES:** + + ⚠️ **NEVER call `execute_skill_script` without first reading the skill with `read_skill`** + - You do NOT know what scripts exist in a skill until you read it + - You do NOT know the correct script arguments until you read the SKILL.md + - Guessing script names will fail - always read first + + ✅ **Correct Workflow Example:** + ``` + User: "Split this PDF into pages" + 1. Recognize: "split-pdf" skill matches this task + 2. Call: read_skill("split-pdf") → Get full instructions + 3. Learn: SKILL.md shows the actual script path and argument format + 4. Execute: Use the exact command format from SKILL.md + ``` + + ❌ **Wrong Workflow (DO NOT DO THIS):** + ``` + User: "Split this PDF into pages" + 1. Recognize: "split-pdf" skill matches this task + 2. Guess: execute_skill_script("split-pdf", "split_pdf.py", ...) ← WRONG! Never guess! + ``` + + **Skills are Self-Documenting:** + - Each SKILL.md tells you exactly what the skill does and how to use it + - The skill may contain Python scripts, config files, or reference docs + - Always use the exact paths and formats specified in SKILL.md + + Remember: **Read first, then execute.** This ensures you use skills correctly! diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-list_skill_directory.json b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-list_skill_directory.json new file mode 100644 index 000000000..6e5d35887 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-list_skill_directory.json @@ -0,0 +1,18 @@ +{ + "name": "skill-list_skill_directory", + "description": "Lists the contents of a skill's directory. Use this to discover what files and subdirectories are available within a skill.", + "parameters": { + "type": "object", + "properties": { + "skill_name": { + "type": "string", + "description": "The name of the skill to list." + }, + "relative_path": { + "type": "string", + "description": "Optional relative path to a subdirectory within the skill directory. If not provided, lists the root of the skill directory." + } + }, + "required": ["skill_name"] + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill.json b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill.json new file mode 100644 index 000000000..cea6f9a65 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill.json @@ -0,0 +1,14 @@ +{ + "name": "skill-read_skill", + "description": "Reads the full content of a skill's SKILL.md file to get detailed instructions. Use this when you need to understand how to apply a specific skill.", + "parameters": { + "type": "object", + "properties": { + "skill_name": { + "type": "string", + "description": "The name of the skill to read (e.g., 'web-research', 'code-review'). Use the skill names from the available skills list." + } + }, + "required": ["skill_name"] + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill_file.json b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill_file.json new file mode 100644 index 000000000..4be74cc35 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/skill-read_skill_file.json @@ -0,0 +1,18 @@ +{ + "name": "skill-read_skill_file", + "description": "Reads a file within a skill's directory. Use this to access supporting files like scripts, templates, or data files that a skill may include.", + "parameters": { + "type": "object", + "properties": { + "skill_name": { + "type": "string", + "description": "The name of the skill containing the file." + }, + "file_path": { + "type": "string", + "description": "The relative path to the file within the skill directory (e.g., 'scripts/helper.py', 'templates/prompt.txt')." + } + }, + "required": ["skill_name", "file_path"] + } +} diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-list_skill_directory.fn.liquid b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-list_skill_directory.fn.liquid new file mode 100644 index 000000000..16c38e506 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-list_skill_directory.fn.liquid @@ -0,0 +1 @@ +When you need to discover what files and subdirectories are available within a skill, use the skill-list_skill_directory function. This helps you understand the skill's structure before reading specific files. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill.fn.liquid b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill.fn.liquid new file mode 100644 index 000000000..d1d4b689d --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill.fn.liquid @@ -0,0 +1 @@ +When you need to understand how to apply a specific skill, use the skill-read_skill function to read the full SKILL.md content. This provides detailed instructions on when and how to use the skill. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill_file.fn.liquid b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill_file.fn.liquid new file mode 100644 index 000000000..4dcf3d796 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.AgentSkills/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/skill-read_skill_file.fn.liquid @@ -0,0 +1 @@ +When you need to access supporting files within a skill directory (such as scripts, templates, or data files), use the skill-read_skill_file function. Provide the skill name and the relative file path. \ No newline at end of file diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 9374d95fd..065bc9140 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -35,6 +35,7 @@ + @@ -91,6 +92,7 @@ + \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 39587b64e..5acc7fce3 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -1061,10 +1061,15 @@ "BotSharp.Plugin.PythonInterpreter", "BotSharp.Plugin.FuzzySharp", "BotSharp.Plugin.MMPEmbedding", - "BotSharp.Plugin.MultiTenancy" + "BotSharp.Plugin.MultiTenancy", + "BotSharp.Plugin.AgentSkills" ] }, - + "AgentSkills": { + "EnableUserSkills": false, + "EnableProjectSkills": true, + "ProjectSkillsDir": "C:\\workshop\\github\\BotSharp\\src\\WebStarter\\skills" + }, "TenantStore": { "Enabled": false, "Tenants": [ diff --git a/src/WebStarter/skills/code-review/SKILL.md b/src/WebStarter/skills/code-review/SKILL.md new file mode 100644 index 000000000..cd64cb557 --- /dev/null +++ b/src/WebStarter/skills/code-review/SKILL.md @@ -0,0 +1,94 @@ +--- +name: code-review +description: A skill for conducting thorough code reviews, identifying issues, suggesting improvements, and ensuring code quality standards are met. +license: MIT +compatibility: any +allowed-tools: read_file list_directory grep_search +metadata: + author: Maf.AgentSkills + version: 1.0.0 + category: development +--- + +# Code Review Skill + +This skill helps you conduct thorough and constructive code reviews. + +## When to Use + +Use this skill when: +- Reviewing pull requests or code changes +- Auditing code quality in a project +- Helping developers improve their code +- Checking for security vulnerabilities or bugs + +## Review Checklist + +Use the checklist in `templates/review-checklist.md` to ensure comprehensive coverage. + +### Categories + +1. **Correctness**: Does the code do what it's supposed to do? +2. **Security**: Are there any security vulnerabilities? +3. **Performance**: Are there performance concerns? +4. **Maintainability**: Is the code easy to understand and modify? +5. **Testing**: Is the code adequately tested? +6. **Documentation**: Is the code well-documented? + +## Instructions + +### 1. Understand Context + +Before reviewing: +- Understand the purpose of the change +- Read any related issue or ticket +- Know the project's coding standards + +### 2. Review Systematically + +Go through the code in this order: +1. **Architecture**: Does the overall approach make sense? +2. **Logic**: Is the logic correct and complete? +3. **Edge Cases**: Are edge cases handled? +4. **Error Handling**: Are errors handled appropriately? +5. **Style**: Does the code follow conventions? + +### 3. Provide Constructive Feedback + +For each issue found: +- Explain **what** the issue is +- Explain **why** it's a problem +- Suggest **how** to fix it +- Categorize severity (blocker, major, minor, suggestion) + +### 4. Output Format + +```markdown +# Code Review: [File/PR Name] + +## Summary +[Overall assessment: approve, request changes, or comment] + +## Critical Issues 🔴 +[Issues that must be fixed before merge] + +## Major Issues 🟠 +[Important issues that should be addressed] + +## Minor Issues 🟡 +[Nice-to-have improvements] + +## Suggestions 💡 +[Optional improvements for consideration] + +## Positive Highlights ✨ +[Things done well - always include some!] +``` + +## Best Practices + +- Be respectful and constructive +- Focus on the code, not the person +- Ask questions when unclear +- Acknowledge good patterns +- Suggest alternatives, don't just criticize diff --git a/src/WebStarter/skills/code-review/templates/review-checklist.md b/src/WebStarter/skills/code-review/templates/review-checklist.md new file mode 100644 index 000000000..724f43437 --- /dev/null +++ b/src/WebStarter/skills/code-review/templates/review-checklist.md @@ -0,0 +1,54 @@ +# Code Review Checklist + +Use this checklist during code reviews to ensure comprehensive coverage. + +## ✅ Correctness +- [ ] Code implements the intended functionality +- [ ] Logic is correct and handles all scenarios +- [ ] Edge cases are handled properly +- [ ] No off-by-one errors +- [ ] Null/undefined values are handled + +## 🔒 Security +- [ ] No SQL injection vulnerabilities +- [ ] No XSS vulnerabilities +- [ ] Input validation is performed +- [ ] Sensitive data is not logged +- [ ] Authentication/authorization is correct +- [ ] No hardcoded secrets or credentials + +## ⚡ Performance +- [ ] No unnecessary database queries (N+1 problem) +- [ ] Appropriate data structures used +- [ ] No memory leaks +- [ ] Caching used where appropriate +- [ ] Large operations are async/background + +## 🔧 Maintainability +- [ ] Code is readable and self-documenting +- [ ] Functions/methods are focused (single responsibility) +- [ ] No code duplication (DRY) +- [ ] Naming is clear and consistent +- [ ] Complex logic has comments +- [ ] Magic numbers are constants + +## 🧪 Testing +- [ ] Unit tests cover main functionality +- [ ] Edge cases have tests +- [ ] Tests are readable and maintainable +- [ ] Mocking is used appropriately +- [ ] Integration tests for critical paths + +## 📚 Documentation +- [ ] Public APIs are documented +- [ ] Complex algorithms have explanations +- [ ] README updated if needed +- [ ] Breaking changes documented +- [ ] Migration guide if applicable + +## 🎨 Style +- [ ] Follows project coding standards +- [ ] Consistent formatting +- [ ] No commented-out code +- [ ] No debug statements left +- [ ] Imports organized diff --git a/src/WebStarter/skills/skill-creator/SKILL.md b/src/WebStarter/skills/skill-creator/SKILL.md new file mode 100644 index 000000000..9d234b316 --- /dev/null +++ b/src/WebStarter/skills/skill-creator/SKILL.md @@ -0,0 +1,106 @@ +--- +name: skill-creator +description: A meta-skill that helps create new Agent Skills by providing templates, best practices, and validation guidance. +license: MIT +compatibility: any +metadata: + author: Maf.AgentSkills + version: 1.0.0 + category: meta +--- + +# Skill Creator + +This meta-skill helps you create new Agent Skills following the [Agent Skills specification](https://agentskills.io). + +## When to Use + +Use this skill when: +- Creating a new skill from scratch +- Converting existing documentation into a skill +- Validating an existing skill's structure +- Learning how to write effective skills + +## Skill Structure + +Every skill must have: + +``` +skill-name/ +├── SKILL.md # Required: skill definition with YAML frontmatter +├── templates/ # Optional: reusable templates +├── scripts/ # Optional: automation scripts +├── examples/ # Optional: usage examples +└── resources/ # Optional: additional resources +``` + +## SKILL.md Template + +```markdown +--- +name: my-skill-name +description: A brief description of what this skill does (max 1024 chars) +license: MIT +compatibility: any +allowed-tools: tool1 tool2 pattern_* +metadata: + author: Your Name + version: 1.0.0 + category: category-name +--- + +# Skill Title + +Brief introduction to the skill. + +## When to Use + +Describe scenarios when this skill should be applied. + +## Instructions + +Step-by-step instructions for applying the skill. + +### Step 1: [Name] +Details... + +### Step 2: [Name] +Details... + +## Output Format + +Describe expected output format if applicable. + +## Tips & Best Practices + +Additional guidance for effective use. +``` + +## Naming Rules + +Skill names must: +- Use only lowercase letters, numbers, and hyphens +- Start and end with a letter or number +- Be 1-64 characters long +- Match the directory name exactly + +**Valid**: `web-research`, `code-review`, `api-client-v2` +**Invalid**: `Web_Research`, `-invalid`, `skill.name` + +## Writing Effective Instructions + +1. **Be Specific**: Provide clear, actionable steps +2. **Use Examples**: Show input/output examples +3. **Structure Consistently**: Use headings and lists +4. **Include Templates**: Provide reusable templates +5. **Explain Why**: Help users understand the reasoning +6. **Handle Edge Cases**: Document exceptions and limitations + +## Best Practices + +- Keep skills focused on a single domain/task +- Make skills reusable across different contexts +- Include all necessary context in the skill +- Test skills with various scenarios +- Update skills based on user feedback +- Version skills when making breaking changes diff --git a/src/WebStarter/skills/split-pdf/SKILL.md b/src/WebStarter/skills/split-pdf/SKILL.md new file mode 100644 index 000000000..4c4b99aee --- /dev/null +++ b/src/WebStarter/skills/split-pdf/SKILL.md @@ -0,0 +1,67 @@ +--- +name: split-pdf +description: Split PDF files into separate single-page documents or extract specific page ranges. Use when you need to divide a PDF into multiple files, extract particular pages, or process PDF pages individually. Works with multi-page PDF documents. +license: MIT +--- + +# Split PDF + +将 PDF 文件拆分为多个单页文件或提取指定页面范围。 + +## 使用场景 + +- 将多页 PDF 拆分为独立的单页文件 +- 提取 PDF 的特定页面范围 +- 需要单独处理 PDF 各个页面时 + +## 使用方法 + +使用 `scripts/split-pdf.cs` 脚本进行 PDF 拆分: + +### 拆分所有页面 +```bash +dotnet scripts/split-pdf.cs input.pdf output-dir/ +``` + +### 拆分指定页面范围 +```bash +# 拆分第 1-5 页 +dotnet scripts/split-pdf.cs input.pdf output-dir/ 1-5 + +# 拆分第 10-20 页 +dotnet scripts/split-pdf.cs input.pdf output-dir/ 10-20 +``` + +### 示例 + +```bash +# 将 document.pdf 的所有页面拆分到 pages/ 目录 +dotnet scripts/split-pdf.cs document.pdf pages/ + +# 只提取前 3 页 +dotnet scripts/split-pdf.cs document.pdf output/ 1-3 +``` + +## 输出格式 + +拆分后的文件命名格式:`{原文件名}_page_{页码}.pdf` + +例如,拆分 `report.pdf` 后会生成: +- `report_page_001.pdf` +- `report_page_002.pdf` +- `report_page_003.pdf` +- ... + +## 依赖项 + +脚本使用以下 NuGet 包(已在脚本中声明): +- **PdfSharpCore 1.3.65** - PDF 操作核心库 +- **Spectre.Console 0.49.1** - 美化的控制台输出 + + +## 注意事项 + +- 页码从 1 开始计数 +- 如果指定的页面范围超出实际页数,会自动调整到有效范围 +- 输出目录不存在时会自动创建 +- 支持中文文件名和路径 diff --git a/src/WebStarter/skills/split-pdf/scripts/split-pdf.cs b/src/WebStarter/skills/split-pdf/scripts/split-pdf.cs new file mode 100644 index 000000000..451d67c54 --- /dev/null +++ b/src/WebStarter/skills/split-pdf/scripts/split-pdf.cs @@ -0,0 +1,112 @@ +#!/usr/bin/env dotnet +#:package PdfSharpCore@1.3.65 +#:package Spectre.Console@0.49.1 +#:property PublishAot=true + +using PdfSharpCore.Pdf; +using PdfSharpCore.Pdf.IO; +using Spectre.Console; +using System; +using System.IO; + +// ==================== 参数校验 ==================== +if (args.Length < 2) +{ + AnsiConsole.MarkupLine("[red]错误: 参数不足[/]"); + AnsiConsole.MarkupLine("[yellow]用法: dotnet split-pdf.cs <输出目录> [页面范围][/]"); + AnsiConsole.MarkupLine("[gray]示例: dotnet split-pdf.cs input.pdf ./output/[/]"); + AnsiConsole.MarkupLine("[gray] dotnet split-pdf.cs input.pdf ./output/ 1-5[/]"); + return 1; +} + +var pdfPath = args[0]; +var outputDir = args[1]; +var pageRange = args.Length >= 3 ? args[2] : null; + +// 验证 PDF 文件 +if (!File.Exists(pdfPath)) +{ + AnsiConsole.MarkupLine($"[red]错误: 文件不存在: {pdfPath}[/]"); + return 1; +} + +// 创建输出目录 +if (!Directory.Exists(outputDir)) +{ + Directory.CreateDirectory(outputDir); + AnsiConsole.MarkupLine($"[green]✓[/] 创建目录: {outputDir}"); +} + +// ==================== 拆分 PDF ==================== +try +{ + AnsiConsole.MarkupLine($"[cyan]📄 处理文件:[/] {Path.GetFileName(pdfPath)}"); + AnsiConsole.MarkupLine($"[cyan]📂 输出目录:[/] {outputDir}"); + + using var inputDocument = PdfReader.Open(pdfPath, PdfDocumentOpenMode.Import); + var totalPages = inputDocument.PageCount; + + // 解析页面范围 + int startPage = 1, endPage = totalPages; + if (!string.IsNullOrEmpty(pageRange)) + { + var parts = pageRange.Split('-'); + if (parts.Length == 2 && + int.TryParse(parts[0], out startPage) && + int.TryParse(parts[1], out endPage)) + { + startPage = Math.Max(1, Math.Min(startPage, totalPages)); + endPage = Math.Max(startPage, Math.Min(endPage, totalPages)); + } + else + { + AnsiConsole.MarkupLine($"[yellow]警告: 无效的页面范围 '{pageRange}',将拆分所有页面[/]"); + startPage = 1; + endPage = totalPages; + } + } + + AnsiConsole.MarkupLine($"[blue]ℹ️ 总页数:[/] {totalPages}"); + AnsiConsole.MarkupLine($"[blue]ℹ️ 拆分范围:[/] 第 {startPage} - {endPage} 页"); + Console.WriteLine(); + + var baseName = Path.GetFileNameWithoutExtension(pdfPath); + var savedCount = 0; + + await AnsiConsole.Progress() + .StartAsync(async ctx => + { + var task = ctx.AddTask("[green]拆分 PDF 页面[/]", maxValue: endPage - startPage + 1); + + for (int i = startPage; i <= endPage; i++) + { + task.Description = $"[green]拆分第 {i}/{endPage} 页[/]"; + + // 创建单页 PDF + using var outputDocument = new PdfDocument(); + outputDocument.AddPage(inputDocument.Pages[i - 1]); + + var outputPath = Path.Combine(outputDir, $"{baseName}_page_{i:D3}.pdf"); + outputDocument.Save(outputPath); + + savedCount++; + AnsiConsole.MarkupLine($" [gray]✓ 已保存: {Path.GetFileName(outputPath)}[/]"); + + task.Increment(1); + await Task.CompletedTask; + } + }); + + Console.WriteLine(); + AnsiConsole.MarkupLine($"[green]✅ 拆分完成![/]"); + AnsiConsole.MarkupLine($"[gray]已生成 {savedCount} 个 PDF 文件[/]"); + AnsiConsole.MarkupLine($"[gray]保存位置: {Path.GetFullPath(outputDir)}[/]"); + + return 0; +} +catch (Exception ex) +{ + AnsiConsole.MarkupLine($"[red]❌ 错误: {ex.Message}[/]"); + AnsiConsole.WriteException(ex); + return 1; +} diff --git a/src/WebStarter/skills/web-research/SKILL.md b/src/WebStarter/skills/web-research/SKILL.md new file mode 100644 index 000000000..c47edbdca --- /dev/null +++ b/src/WebStarter/skills/web-research/SKILL.md @@ -0,0 +1,87 @@ +--- +name: web-research +description: A skill for conducting comprehensive web research on any topic, synthesizing information from multiple sources into well-organized summaries. +license: MIT +compatibility: any +allowed-tools: read_file web_search fetch_url +metadata: + author: Maf.AgentSkills + version: 1.0.0 + category: research +--- + +# Web Research Skill + +This skill helps you conduct comprehensive web research on any topic. + +## When to Use + +Use this skill when the user asks you to: +- Research a topic thoroughly +- Find and summarize information from multiple sources +- Create a research report or literature review +- Compare different perspectives on a subject + +## Instructions + +### 1. Clarify the Research Scope + +Before starting, ensure you understand: +- The specific topic or question +- The depth of research required (quick overview vs. deep dive) +- Any specific sources or domains to focus on +- The desired output format + +### 2. Search Strategy + +1. **Start broad**: Use general search queries to understand the landscape +2. **Narrow down**: Use specific keywords based on initial findings +3. **Diversify sources**: Look for academic, industry, and news perspectives +4. **Verify facts**: Cross-reference important claims across sources + +### 3. Information Synthesis + +When synthesizing information: +- Organize by themes, not by source +- Highlight areas of consensus and disagreement +- Note the credibility and recency of sources +- Identify gaps in available information + +### 4. Output Format + +Structure your research report as: + +```markdown +# Research Report: [Topic] + +## Executive Summary +[2-3 sentence overview] + +## Key Findings +1. [Finding 1] +2. [Finding 2] +... + +## Detailed Analysis + +### [Theme 1] +[Analysis with citations] + +### [Theme 2] +[Analysis with citations] + +## Sources +- [Source 1]: [URL] +- [Source 2]: [URL] +... + +## Limitations & Further Research +[What wasn't covered, what needs more investigation] +``` + +## Tips + +- Always cite your sources +- Be transparent about the limitations of your research +- Distinguish between facts, expert opinions, and speculation +- Update findings if you discover contradictory information